blob: 767000c701492e264e8a64b7c577575fac0c2429 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Prolific PL2303 USB to serial adaptor driver
3 *
Greg Kroah-Hartman4d0dce32007-06-12 11:43:37 -07004 * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Copyright (C) 2003 IBM Corp.
6 *
7 * Original driver for 2.2.x by anonymous
8 *
Greg Kroah-Hartman4d0dce32007-06-12 11:43:37 -07009 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License version
11 * 2 as published by the Free Software Foundation.
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 *
Alan Cox3a0f43e2008-07-22 11:14:49 +010013 * See Documentation/usb/usb-serial.txt for more information on using this
14 * driver
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 */
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/kernel.h>
19#include <linux/errno.h>
20#include <linux/init.h>
21#include <linux/slab.h>
22#include <linux/tty.h>
23#include <linux/tty_driver.h>
24#include <linux/tty_flip.h>
25#include <linux/serial.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/spinlock.h>
Alan Cox3a0f43e2008-07-22 11:14:49 +010029#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/usb.h>
Greg Kroah-Hartmana9698882006-07-11 21:22:58 -070031#include <linux/usb/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include "pl2303.h"
33
34/*
35 * Version Information
36 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
38
39static int debug;
40
41#define PL2303_CLOSING_WAIT (30*HZ)
42
43#define PL2303_BUF_SIZE 1024
44#define PL2303_TMP_BUF_SIZE 1024
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046struct pl2303_buf {
47 unsigned int buf_size;
48 char *buf_buf;
49 char *buf_get;
50 char *buf_put;
51};
52
Németh Márton7d40d7e2010-01-10 15:34:24 +010053static const struct usb_device_id id_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
55 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
Peter Moulder3d861492006-06-19 22:47:49 +100056 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) },
58 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
Max Arnoldb483b6a2008-03-20 16:43:56 +070059 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
Steve Murphy4be2fa12008-05-23 23:39:05 +053060 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
Greg Kroah-Hartman727df352008-07-02 15:25:41 -050061 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
Masakazu Mokuno8a28dea2007-10-23 13:51:57 +090063 { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
65 { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
66 { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
67 { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
68 { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
Wang Jun58381712006-04-19 16:32:07 +080069 { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) },
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
71 { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
72 { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
73 { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
74 { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
75 { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
76 { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
77 { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) },
Luiz Fernando Capitulinoa8310f32005-11-17 09:47:32 -080078 { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) },
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) },
Luiz Fernando Capitulinoa8310f32005-11-17 09:47:32 -080080 { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) },
Andreas Loible7beb662007-08-24 01:51:11 +020081 { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) },
Alan Cox912299f2009-04-06 17:35:12 +010082 { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */
Peter Favrholdtacbb36f2005-04-18 17:39:32 -070083 { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
Christian Lindnerc6c27722006-02-01 14:10:52 +010084 { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) },
85 { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) },
Denis MONTERRAT6cceb052006-01-19 14:52:38 +010086 { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
Christian Lindnerc6c27722006-02-01 14:10:52 +010087 { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) },
Dick Streefland491b04c2006-03-01 00:53:33 -080088 { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
Matthew Meno3b928472006-06-21 15:25:53 -040089 { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
Kim Oldfieldb7aa94b2006-07-25 15:54:59 +100090 { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
Johannes Steingraeber8fd80132006-09-16 16:17:34 +020091 { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
YOSHIFUJI Hideaki2d94b982007-01-26 22:51:38 +090092 { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
Magnus Damm9e3285d2007-11-08 16:45:46 +090093 { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
Damien Stuartcc311ee2008-01-06 13:51:39 -050094 { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
Matthew Arnold7c992002008-12-13 22:42:53 +110095 { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
Mike Provencheraf4b8512008-12-16 14:30:14 -060096 { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
Gianpaolo Cugola8540d662009-06-05 22:57:52 +020097 { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
Khanh-Dang Nguyen Thu Lam49276562009-07-28 19:41:17 +020098 { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
Pawel Ludwikow35904e62009-08-27 14:15:50 +020099 { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 { } /* Terminating entry */
101};
102
Thiago Galesi372db8a2006-07-31 15:39:27 -0300103MODULE_DEVICE_TABLE(usb, id_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105static struct usb_driver pl2303_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 .name = "pl2303",
107 .probe = usb_serial_probe,
108 .disconnect = usb_serial_disconnect,
109 .id_table = id_table,
Sarah Sharpfcf9e552007-12-14 14:09:30 -0800110 .suspend = usb_serial_suspend,
111 .resume = usb_serial_resume,
Greg Kroah-Hartmanba9dc652005-11-16 13:41:28 -0800112 .no_dynamic_id = 1,
Sarah Sharpfcf9e552007-12-14 14:09:30 -0800113 .supports_autosuspend = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114};
115
116#define SET_LINE_REQUEST_TYPE 0x21
117#define SET_LINE_REQUEST 0x20
118
119#define SET_CONTROL_REQUEST_TYPE 0x21
120#define SET_CONTROL_REQUEST 0x22
121#define CONTROL_DTR 0x01
122#define CONTROL_RTS 0x02
123
124#define BREAK_REQUEST_TYPE 0x21
Alan Cox3a0f43e2008-07-22 11:14:49 +0100125#define BREAK_REQUEST 0x23
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126#define BREAK_ON 0xffff
127#define BREAK_OFF 0x0000
128
129#define GET_LINE_REQUEST_TYPE 0xa1
130#define GET_LINE_REQUEST 0x21
131
132#define VENDOR_WRITE_REQUEST_TYPE 0x40
133#define VENDOR_WRITE_REQUEST 0x01
134
135#define VENDOR_READ_REQUEST_TYPE 0xc0
136#define VENDOR_READ_REQUEST 0x01
137
138#define UART_STATE 0x08
139#define UART_STATE_TRANSIENT_MASK 0x74
140#define UART_DCD 0x01
141#define UART_DSR 0x02
142#define UART_BREAK_ERROR 0x04
143#define UART_RING 0x08
144#define UART_FRAME_ERROR 0x10
145#define UART_PARITY_ERROR 0x20
146#define UART_OVERRUN_ERROR 0x40
147#define UART_CTS 0x80
148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150enum pl2303_type {
151 type_0, /* don't know the difference between type 0 and */
152 type_1, /* type 1, until someone from prolific tells us... */
153 HX, /* HX version of the pl2303 chip */
154};
155
156struct pl2303_private {
157 spinlock_t lock;
158 struct pl2303_buf *buf;
159 int write_urb_in_use;
160 wait_queue_head_t delta_msr_wait;
161 u8 line_control;
162 u8 line_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 enum pl2303_type type;
164};
165
Thiago Galesi572d3132006-07-29 10:46:37 -0300166/*
167 * pl2303_buf_alloc
168 *
169 * Allocate a circular buffer and all associated memory.
170 */
171static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
172{
173 struct pl2303_buf *pb;
174
175 if (size == 0)
176 return NULL;
177
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800178 pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
Thiago Galesi572d3132006-07-29 10:46:37 -0300179 if (pb == NULL)
180 return NULL;
181
182 pb->buf_buf = kmalloc(size, GFP_KERNEL);
183 if (pb->buf_buf == NULL) {
184 kfree(pb);
185 return NULL;
186 }
187
188 pb->buf_size = size;
189 pb->buf_get = pb->buf_put = pb->buf_buf;
190
191 return pb;
192}
193
194/*
195 * pl2303_buf_free
196 *
197 * Free the buffer and all associated memory.
198 */
199static void pl2303_buf_free(struct pl2303_buf *pb)
200{
201 if (pb) {
202 kfree(pb->buf_buf);
203 kfree(pb);
204 }
205}
206
207/*
208 * pl2303_buf_clear
209 *
210 * Clear out all data in the circular buffer.
211 */
212static void pl2303_buf_clear(struct pl2303_buf *pb)
213{
214 if (pb != NULL)
215 pb->buf_get = pb->buf_put;
216 /* equivalent to a get of all data available */
217}
218
219/*
220 * pl2303_buf_data_avail
221 *
222 * Return the number of bytes of data available in the circular
223 * buffer.
224 */
225static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
226{
227 if (pb == NULL)
228 return 0;
229
Alan Cox3a0f43e2008-07-22 11:14:49 +0100230 return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size;
Thiago Galesi572d3132006-07-29 10:46:37 -0300231}
232
233/*
234 * pl2303_buf_space_avail
235 *
236 * Return the number of bytes of space available in the circular
237 * buffer.
238 */
239static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
240{
241 if (pb == NULL)
242 return 0;
243
Alan Cox3a0f43e2008-07-22 11:14:49 +0100244 return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size;
Thiago Galesi572d3132006-07-29 10:46:37 -0300245}
246
247/*
248 * pl2303_buf_put
249 *
250 * Copy data data from a user buffer and put it into the circular buffer.
251 * Restrict to the amount of space available.
252 *
253 * Return the number of bytes copied.
254 */
255static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
256 unsigned int count)
257{
258 unsigned int len;
259
260 if (pb == NULL)
261 return 0;
262
263 len = pl2303_buf_space_avail(pb);
264 if (count > len)
265 count = len;
266
267 if (count == 0)
268 return 0;
269
270 len = pb->buf_buf + pb->buf_size - pb->buf_put;
271 if (count > len) {
272 memcpy(pb->buf_put, buf, len);
273 memcpy(pb->buf_buf, buf+len, count - len);
274 pb->buf_put = pb->buf_buf + count - len;
275 } else {
276 memcpy(pb->buf_put, buf, count);
277 if (count < len)
278 pb->buf_put += count;
279 else /* count == len */
280 pb->buf_put = pb->buf_buf;
281 }
282
283 return count;
284}
285
286/*
287 * pl2303_buf_get
288 *
289 * Get data from the circular buffer and copy to the given buffer.
290 * Restrict to the amount of data available.
291 *
292 * Return the number of bytes copied.
293 */
294static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
295 unsigned int count)
296{
297 unsigned int len;
298
299 if (pb == NULL)
300 return 0;
301
302 len = pl2303_buf_data_avail(pb);
303 if (count > len)
304 count = len;
305
306 if (count == 0)
307 return 0;
308
309 len = pb->buf_buf + pb->buf_size - pb->buf_get;
310 if (count > len) {
311 memcpy(buf, pb->buf_get, len);
312 memcpy(buf+len, pb->buf_buf, count - len);
313 pb->buf_get = pb->buf_buf + count - len;
314 } else {
315 memcpy(buf, pb->buf_get, count);
316 if (count < len)
317 pb->buf_get += count;
318 else /* count == len */
319 pb->buf_get = pb->buf_buf;
320 }
321
322 return count;
323}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324
Sarah Sharpeb44da02007-12-14 14:08:00 -0800325static int pl2303_vendor_read(__u16 value, __u16 index,
326 struct usb_serial *serial, unsigned char *buf)
327{
328 int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
329 VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
330 value, index, buf, 1, 100);
331 dbg("0x%x:0x%x:0x%x:0x%x %d - %x", VENDOR_READ_REQUEST_TYPE,
332 VENDOR_READ_REQUEST, value, index, res, buf[0]);
333 return res;
334}
335
336static int pl2303_vendor_write(__u16 value, __u16 index,
337 struct usb_serial *serial)
338{
339 int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
340 VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
341 value, index, NULL, 0, 100);
342 dbg("0x%x:0x%x:0x%x:0x%x %d", VENDOR_WRITE_REQUEST_TYPE,
343 VENDOR_WRITE_REQUEST, value, index, res);
344 return res;
345}
346
Thiago Galesi372db8a2006-07-31 15:39:27 -0300347static int pl2303_startup(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
349 struct pl2303_private *priv;
350 enum pl2303_type type = type_0;
Sarah Sharp3e152502007-12-14 14:08:35 -0800351 unsigned char *buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 int i;
353
Sarah Sharp3e152502007-12-14 14:08:35 -0800354 buf = kmalloc(10, GFP_KERNEL);
355 if (buf == NULL)
356 return -ENOMEM;
357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 if (serial->dev->descriptor.bDeviceClass == 0x02)
359 type = type_0;
360 else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
361 type = HX;
362 else if (serial->dev->descriptor.bDeviceClass == 0x00)
363 type = type_1;
364 else if (serial->dev->descriptor.bDeviceClass == 0xFF)
365 type = type_1;
366 dbg("device type: %d", type);
367
368 for (i = 0; i < serial->num_ports; ++i) {
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100369 priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 if (!priv)
371 goto cleanup;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 spin_lock_init(&priv->lock);
373 priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
374 if (priv->buf == NULL) {
375 kfree(priv);
376 goto cleanup;
377 }
378 init_waitqueue_head(&priv->delta_msr_wait);
379 priv->type = type;
380 usb_set_serial_port_data(serial->port[i], priv);
381 }
Sarah Sharp3e152502007-12-14 14:08:35 -0800382
383 pl2303_vendor_read(0x8484, 0, serial, buf);
384 pl2303_vendor_write(0x0404, 0, serial);
385 pl2303_vendor_read(0x8484, 0, serial, buf);
386 pl2303_vendor_read(0x8383, 0, serial, buf);
387 pl2303_vendor_read(0x8484, 0, serial, buf);
388 pl2303_vendor_write(0x0404, 1, serial);
389 pl2303_vendor_read(0x8484, 0, serial, buf);
390 pl2303_vendor_read(0x8383, 0, serial, buf);
391 pl2303_vendor_write(0, 1, serial);
392 pl2303_vendor_write(1, 0, serial);
393 if (type == HX)
394 pl2303_vendor_write(2, 0x44, serial);
395 else
396 pl2303_vendor_write(2, 0x24, serial);
397
398 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 return 0;
400
401cleanup:
Sarah Sharp3e152502007-12-14 14:08:35 -0800402 kfree(buf);
Alan Cox3a0f43e2008-07-22 11:14:49 +0100403 for (--i; i >= 0; --i) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 priv = usb_get_serial_port_data(serial->port[i]);
405 pl2303_buf_free(priv->buf);
406 kfree(priv);
407 usb_set_serial_port_data(serial->port[i], NULL);
408 }
409 return -ENOMEM;
410}
411
Thiago Galesi372db8a2006-07-31 15:39:27 -0300412static int set_control_lines(struct usb_device *dev, u8 value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
414 int retval;
Alan Cox3a0f43e2008-07-22 11:14:49 +0100415
Thiago Galesi372db8a2006-07-31 15:39:27 -0300416 retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
417 SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
418 value, 0, NULL, 0, 100);
Harvey Harrison441b62c2008-03-03 16:08:34 -0800419 dbg("%s - value = %d, retval = %d", __func__, value, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 return retval;
421}
422
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423static void pl2303_send(struct usb_serial_port *port)
424{
425 int count, result;
426 struct pl2303_private *priv = usb_get_serial_port_data(port);
427 unsigned long flags;
428
Harvey Harrison441b62c2008-03-03 16:08:34 -0800429 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
431 spin_lock_irqsave(&priv->lock, flags);
432
433 if (priv->write_urb_in_use) {
434 spin_unlock_irqrestore(&priv->lock, flags);
435 return;
436 }
437
438 count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
Thiago Galesi372db8a2006-07-31 15:39:27 -0300439 port->bulk_out_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
441 if (count == 0) {
442 spin_unlock_irqrestore(&priv->lock, flags);
443 return;
444 }
445
446 priv->write_urb_in_use = 1;
447
448 spin_unlock_irqrestore(&priv->lock, flags);
449
Harvey Harrison441b62c2008-03-03 16:08:34 -0800450 usb_serial_debug_data(debug, &port->dev, __func__, count,
Thiago Galesi372db8a2006-07-31 15:39:27 -0300451 port->write_urb->transfer_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
453 port->write_urb->transfer_buffer_length = count;
454 port->write_urb->dev = port->serial->dev;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300455 result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 if (result) {
Thiago Galesi372db8a2006-07-31 15:39:27 -0300457 dev_err(&port->dev, "%s - failed submitting write urb,"
Harvey Harrison441b62c2008-03-03 16:08:34 -0800458 " error %d\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 priv->write_urb_in_use = 0;
Alan Cox3a0f43e2008-07-22 11:14:49 +0100460 /* TODO: reschedule pl2303_send */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 }
462
Pete Zaitcevcf2c7482006-05-22 21:58:49 -0700463 usb_serial_port_softint(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464}
465
Alan Cox95da3102008-07-22 11:09:07 +0100466static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port,
467 const unsigned char *buf, int count)
Thiago Galesi572d3132006-07-29 10:46:37 -0300468{
469 struct pl2303_private *priv = usb_get_serial_port_data(port);
470 unsigned long flags;
471
Harvey Harrison441b62c2008-03-03 16:08:34 -0800472 dbg("%s - port %d, %d bytes", __func__, port->number, count);
Thiago Galesi572d3132006-07-29 10:46:37 -0300473
474 if (!count)
475 return count;
476
477 spin_lock_irqsave(&priv->lock, flags);
478 count = pl2303_buf_put(priv->buf, buf, count);
479 spin_unlock_irqrestore(&priv->lock, flags);
480
481 pl2303_send(port);
482
483 return count;
484}
485
Alan Cox95da3102008-07-22 11:09:07 +0100486static int pl2303_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Alan Cox95da3102008-07-22 11:09:07 +0100488 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 struct pl2303_private *priv = usb_get_serial_port_data(port);
490 int room = 0;
491 unsigned long flags;
492
Harvey Harrison441b62c2008-03-03 16:08:34 -0800493 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
495 spin_lock_irqsave(&priv->lock, flags);
496 room = pl2303_buf_space_avail(priv->buf);
497 spin_unlock_irqrestore(&priv->lock, flags);
498
Harvey Harrison441b62c2008-03-03 16:08:34 -0800499 dbg("%s - returns %d", __func__, room);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 return room;
501}
502
Alan Cox95da3102008-07-22 11:09:07 +0100503static int pl2303_chars_in_buffer(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504{
Alan Cox95da3102008-07-22 11:09:07 +0100505 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 struct pl2303_private *priv = usb_get_serial_port_data(port);
507 int chars = 0;
508 unsigned long flags;
509
Harvey Harrison441b62c2008-03-03 16:08:34 -0800510 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 spin_lock_irqsave(&priv->lock, flags);
513 chars = pl2303_buf_data_avail(priv->buf);
514 spin_unlock_irqrestore(&priv->lock, flags);
515
Harvey Harrison441b62c2008-03-03 16:08:34 -0800516 dbg("%s - returns %d", __func__, chars);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 return chars;
518}
519
Alan Cox95da3102008-07-22 11:09:07 +0100520static void pl2303_set_termios(struct tty_struct *tty,
521 struct usb_serial_port *port, struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522{
523 struct usb_serial *serial = port->serial;
524 struct pl2303_private *priv = usb_get_serial_port_data(port);
525 unsigned long flags;
526 unsigned int cflag;
527 unsigned char *buf;
528 int baud;
529 int i;
530 u8 control;
Frank Schaefer25b82862009-08-18 20:15:07 +0200531 const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600,
532 4800, 7200, 9600, 14400, 19200, 28800, 38400,
533 57600, 115200, 230400, 460800, 614400,
534 921600, 1228800, 2457600, 3000000, 6000000 };
535 int baud_floor, baud_ceil;
536 int k;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Harvey Harrison441b62c2008-03-03 16:08:34 -0800538 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Alan Coxbf5e5832008-01-08 14:55:51 +0000540 /* The PL2303 is reported to lose bytes if you change
541 serial settings even to the same values as before. Thus
542 we actually need to filter in this specific case */
543
Alan Cox95da3102008-07-22 11:09:07 +0100544 if (!tty_termios_hw_change(tty->termios, old_termios))
Alan Coxbf5e5832008-01-08 14:55:51 +0000545 return;
546
Alan Cox95da3102008-07-22 11:09:07 +0100547 cflag = tty->termios->c_cflag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Thiago Galesi372db8a2006-07-31 15:39:27 -0300549 buf = kzalloc(7, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 if (!buf) {
Harvey Harrison441b62c2008-03-03 16:08:34 -0800551 dev_err(&port->dev, "%s - out of memory.\n", __func__);
Alan Coxa5b6f602008-04-08 17:16:06 +0100552 /* Report back no change occurred */
Alan Cox95da3102008-07-22 11:09:07 +0100553 *tty->termios = *old_termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return;
555 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Thiago Galesi372db8a2006-07-31 15:39:27 -0300557 i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
558 GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
559 0, 0, buf, 7, 100);
560 dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
561 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
563 if (cflag & CSIZE) {
564 switch (cflag & CSIZE) {
Alan Cox3a0f43e2008-07-22 11:14:49 +0100565 case CS5:
566 buf[6] = 5;
567 break;
568 case CS6:
569 buf[6] = 6;
570 break;
571 case CS7:
572 buf[6] = 7;
573 break;
574 default:
575 case CS8:
576 buf[6] = 8;
577 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
Harvey Harrison441b62c2008-03-03 16:08:34 -0800579 dbg("%s - data bits = %d", __func__, buf[6]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581
Frank Schaefer25b82862009-08-18 20:15:07 +0200582 /* For reference buf[0]:buf[3] baud rate value */
583 /* NOTE: Only the values defined in baud_sup are supported !
584 * => if unsupported values are set, the PL2303 seems to use
585 * 9600 baud (at least my PL2303X always does)
586 */
Alan Cox95da3102008-07-22 11:09:07 +0100587 baud = tty_get_baud_rate(tty);
Frank Schaefer25b82862009-08-18 20:15:07 +0200588 dbg("%s - baud requested = %d", __func__, baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 if (baud) {
Frank Schaefer25b82862009-08-18 20:15:07 +0200590 /* Set baudrate to nearest supported value */
591 for (k=0; k<ARRAY_SIZE(baud_sup); k++) {
592 if (baud_sup[k] / baud) {
593 baud_ceil = baud_sup[k];
594 if (k==0) {
595 baud = baud_ceil;
596 } else {
597 baud_floor = baud_sup[k-1];
598 if ((baud_ceil % baud)
599 > (baud % baud_floor))
600 baud = baud_floor;
601 else
602 baud = baud_ceil;
603 }
604 break;
605 }
606 }
607 if (baud > 1228800) {
608 /* type_0, type_1 only support up to 1228800 baud */
609 if (priv->type != HX)
610 baud = 1228800;
611 else if (baud > 6000000)
612 baud = 6000000;
613 }
614 dbg("%s - baud set = %d", __func__, baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 buf[0] = baud & 0xff;
616 buf[1] = (baud >> 8) & 0xff;
617 buf[2] = (baud >> 16) & 0xff;
618 buf[3] = (baud >> 24) & 0xff;
619 }
620
621 /* For reference buf[4]=0 is 1 stop bits */
622 /* For reference buf[4]=1 is 1.5 stop bits */
623 /* For reference buf[4]=2 is 2 stop bits */
624 if (cflag & CSTOPB) {
Frank Schaefer29cf1b72009-08-18 20:34:24 +0200625 /* NOTE: Comply with "real" UARTs / RS232:
626 * use 1.5 instead of 2 stop bits with 5 data bits
627 */
628 if ((cflag & CSIZE) == CS5) {
629 buf[4] = 1;
630 dbg("%s - stop bits = 1.5", __func__);
631 } else {
632 buf[4] = 2;
633 dbg("%s - stop bits = 2", __func__);
634 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 } else {
636 buf[4] = 0;
Harvey Harrison441b62c2008-03-03 16:08:34 -0800637 dbg("%s - stop bits = 1", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 }
639
640 if (cflag & PARENB) {
641 /* For reference buf[5]=0 is none parity */
642 /* For reference buf[5]=1 is odd parity */
643 /* For reference buf[5]=2 is even parity */
644 /* For reference buf[5]=3 is mark parity */
645 /* For reference buf[5]=4 is space parity */
646 if (cflag & PARODD) {
Frank Schaefer6dd81b42009-08-18 20:31:11 +0200647 if (cflag & CMSPAR) {
648 buf[5] = 3;
649 dbg("%s - parity = mark", __func__);
650 } else {
651 buf[5] = 1;
652 dbg("%s - parity = odd", __func__);
653 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 } else {
Frank Schaefer6dd81b42009-08-18 20:31:11 +0200655 if (cflag & CMSPAR) {
656 buf[5] = 4;
657 dbg("%s - parity = space", __func__);
658 } else {
659 buf[5] = 2;
660 dbg("%s - parity = even", __func__);
661 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
663 } else {
664 buf[5] = 0;
Harvey Harrison441b62c2008-03-03 16:08:34 -0800665 dbg("%s - parity = none", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 }
667
Thiago Galesi372db8a2006-07-31 15:39:27 -0300668 i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
669 SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
670 0, 0, buf, 7, 100);
671 dbg("0x21:0x20:0:0 %d", i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
673 /* change control lines if we are switching to or from B0 */
674 spin_lock_irqsave(&priv->lock, flags);
675 control = priv->line_control;
676 if ((cflag & CBAUD) == B0)
677 priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
678 else
679 priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
680 if (control != priv->line_control) {
681 control = priv->line_control;
682 spin_unlock_irqrestore(&priv->lock, flags);
683 set_control_lines(serial->dev, control);
684 } else {
685 spin_unlock_irqrestore(&priv->lock, flags);
686 }
Thiago Galesi372db8a2006-07-31 15:39:27 -0300687
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
689
Thiago Galesi372db8a2006-07-31 15:39:27 -0300690 i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
691 GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
692 0, 0, buf, 7, 100);
693 dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
695
696 if (cflag & CRTSCTS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 if (priv->type == HX)
Sarah Sharpeb44da02007-12-14 14:08:00 -0800698 pl2303_vendor_write(0x0, 0x61, serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 else
Sarah Sharpeb44da02007-12-14 14:08:00 -0800700 pl2303_vendor_write(0x0, 0x41, serial);
t.sefzick715f9522007-04-25 15:05:22 +0200701 } else {
Sarah Sharpeb44da02007-12-14 14:08:00 -0800702 pl2303_vendor_write(0x0, 0x0, serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 }
704
Frank Schaefer25b82862009-08-18 20:15:07 +0200705 /* Save resulting baud rate */
Alan Coxdf64c472007-10-15 20:54:47 +0100706 if (baud)
Alan Cox95da3102008-07-22 11:09:07 +0100707 tty_encode_baud_rate(tty, baud, baud);
Alan Coxdf64c472007-10-15 20:54:47 +0100708
Thiago Galesi372db8a2006-07-31 15:39:27 -0300709 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710}
711
Alan Cox335f8512009-06-11 12:26:29 +0100712static void pl2303_dtr_rts(struct usb_serial_port *port, int on)
Thiago Galesi572d3132006-07-29 10:46:37 -0300713{
714 struct pl2303_private *priv = usb_get_serial_port_data(port);
715 unsigned long flags;
Alan Cox335f8512009-06-11 12:26:29 +0100716 u8 control;
717
718 spin_lock_irqsave(&priv->lock, flags);
719 /* Change DTR and RTS */
720 if (on)
721 priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
722 else
723 priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
724 control = priv->line_control;
725 spin_unlock_irqrestore(&priv->lock, flags);
726 set_control_lines(port->serial->dev, control);
727}
728
729static void pl2303_close(struct usb_serial_port *port)
730{
731 struct pl2303_private *priv = usb_get_serial_port_data(port);
732 unsigned long flags;
Thiago Galesi572d3132006-07-29 10:46:37 -0300733
Harvey Harrison441b62c2008-03-03 16:08:34 -0800734 dbg("%s - port %d", __func__, port->number);
Thiago Galesi572d3132006-07-29 10:46:37 -0300735
Thiago Galesi572d3132006-07-29 10:46:37 -0300736 spin_lock_irqsave(&priv->lock, flags);
Thiago Galesi572d3132006-07-29 10:46:37 -0300737 /* clear out any remaining data in the buffer */
738 pl2303_buf_clear(priv->buf);
739 spin_unlock_irqrestore(&priv->lock, flags);
740
Thiago Galesi572d3132006-07-29 10:46:37 -0300741 /* shutdown our urbs */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800742 dbg("%s - shutting down urbs", __func__);
Thiago Galesi572d3132006-07-29 10:46:37 -0300743 usb_kill_urb(port->write_urb);
744 usb_kill_urb(port->read_urb);
745 usb_kill_urb(port->interrupt_in_urb);
746
Thiago Galesi572d3132006-07-29 10:46:37 -0300747}
748
Alan Coxa509a7e2009-09-19 13:13:26 -0700749static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750{
Alan Cox606d0992006-12-08 02:38:45 -0800751 struct ktermios tmp_termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 struct usb_serial *serial = port->serial;
753 struct pl2303_private *priv = usb_get_serial_port_data(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 int result;
755
Harvey Harrison441b62c2008-03-03 16:08:34 -0800756 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Dariusz M16948992005-07-28 18:06:13 +0200758 if (priv->type != HX) {
759 usb_clear_halt(serial->dev, port->write_urb->pipe);
760 usb_clear_halt(serial->dev, port->read_urb->pipe);
Sarah Sharp3e152502007-12-14 14:08:35 -0800761 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 /* reset upstream data pipes */
Sarah Sharpeb44da02007-12-14 14:08:00 -0800763 pl2303_vendor_write(8, 0, serial);
764 pl2303_vendor_write(9, 0, serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 }
766
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 /* Setup termios */
Alan Cox95da3102008-07-22 11:09:07 +0100768 if (tty)
769 pl2303_set_termios(tty, port, &tmp_termios);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Harvey Harrison441b62c2008-03-03 16:08:34 -0800771 dbg("%s - submitting read urb", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 port->read_urb->dev = serial->dev;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300773 result = usb_submit_urb(port->read_urb, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 if (result) {
Thiago Galesi372db8a2006-07-31 15:39:27 -0300775 dev_err(&port->dev, "%s - failed submitting read urb,"
Harvey Harrison441b62c2008-03-03 16:08:34 -0800776 " error %d\n", __func__, result);
Alan Cox335f8512009-06-11 12:26:29 +0100777 pl2303_close(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 return -EPROTO;
779 }
780
Harvey Harrison441b62c2008-03-03 16:08:34 -0800781 dbg("%s - submitting interrupt urb", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 port->interrupt_in_urb->dev = serial->dev;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300783 result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if (result) {
Thiago Galesi372db8a2006-07-31 15:39:27 -0300785 dev_err(&port->dev, "%s - failed submitting interrupt urb,"
Harvey Harrison441b62c2008-03-03 16:08:34 -0800786 " error %d\n", __func__, result);
Alan Cox335f8512009-06-11 12:26:29 +0100787 pl2303_close(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 return -EPROTO;
789 }
Alan Cox335f8512009-06-11 12:26:29 +0100790 port->port.drain_delay = 256;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 return 0;
792}
793
Alan Cox95da3102008-07-22 11:09:07 +0100794static int pl2303_tiocmset(struct tty_struct *tty, struct file *file,
Thiago Galesi372db8a2006-07-31 15:39:27 -0300795 unsigned int set, unsigned int clear)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
Alan Cox95da3102008-07-22 11:09:07 +0100797 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 struct pl2303_private *priv = usb_get_serial_port_data(port);
799 unsigned long flags;
800 u8 control;
801
Flavio Leitner6fdd8e82005-04-18 17:39:31 -0700802 if (!usb_get_intfdata(port->serial->interface))
803 return -ENODEV;
804
Thiago Galesi372db8a2006-07-31 15:39:27 -0300805 spin_lock_irqsave(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 if (set & TIOCM_RTS)
807 priv->line_control |= CONTROL_RTS;
808 if (set & TIOCM_DTR)
809 priv->line_control |= CONTROL_DTR;
810 if (clear & TIOCM_RTS)
811 priv->line_control &= ~CONTROL_RTS;
812 if (clear & TIOCM_DTR)
813 priv->line_control &= ~CONTROL_DTR;
814 control = priv->line_control;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300815 spin_unlock_irqrestore(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Thiago Galesi372db8a2006-07-31 15:39:27 -0300817 return set_control_lines(port->serial->dev, control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818}
819
Alan Cox95da3102008-07-22 11:09:07 +0100820static int pl2303_tiocmget(struct tty_struct *tty, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821{
Alan Cox95da3102008-07-22 11:09:07 +0100822 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 struct pl2303_private *priv = usb_get_serial_port_data(port);
824 unsigned long flags;
825 unsigned int mcr;
826 unsigned int status;
827 unsigned int result;
828
Harvey Harrison441b62c2008-03-03 16:08:34 -0800829 dbg("%s (%d)", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Flavio Leitner6fdd8e82005-04-18 17:39:31 -0700831 if (!usb_get_intfdata(port->serial->interface))
832 return -ENODEV;
833
Thiago Galesi372db8a2006-07-31 15:39:27 -0300834 spin_lock_irqsave(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 mcr = priv->line_control;
836 status = priv->line_status;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300837 spin_unlock_irqrestore(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0)
840 | ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0)
841 | ((status & UART_CTS) ? TIOCM_CTS : 0)
842 | ((status & UART_DSR) ? TIOCM_DSR : 0)
843 | ((status & UART_RING) ? TIOCM_RI : 0)
844 | ((status & UART_DCD) ? TIOCM_CD : 0);
845
Harvey Harrison441b62c2008-03-03 16:08:34 -0800846 dbg("%s - result = %x", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
848 return result;
849}
850
Alan Cox335f8512009-06-11 12:26:29 +0100851static int pl2303_carrier_raised(struct usb_serial_port *port)
852{
853 struct pl2303_private *priv = usb_get_serial_port_data(port);
854 if (priv->line_status & UART_DCD)
855 return 1;
856 return 0;
857}
858
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
860{
861 struct pl2303_private *priv = usb_get_serial_port_data(port);
862 unsigned long flags;
863 unsigned int prevstatus;
864 unsigned int status;
865 unsigned int changed;
866
Thiago Galesi372db8a2006-07-31 15:39:27 -0300867 spin_lock_irqsave(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 prevstatus = priv->line_status;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300869 spin_unlock_irqrestore(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
871 while (1) {
872 interruptible_sleep_on(&priv->delta_msr_wait);
873 /* see if a signal did it */
874 if (signal_pending(current))
875 return -ERESTARTSYS;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300876
877 spin_lock_irqsave(&priv->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 status = priv->line_status;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300879 spin_unlock_irqrestore(&priv->lock, flags);
880
Alan Cox3a0f43e2008-07-22 11:14:49 +0100881 changed = prevstatus ^ status;
Thiago Galesi372db8a2006-07-31 15:39:27 -0300882
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
884 ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
885 ((arg & TIOCM_CD) && (changed & UART_DCD)) ||
Alan Cox3a0f43e2008-07-22 11:14:49 +0100886 ((arg & TIOCM_CTS) && (changed & UART_CTS))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 return 0;
888 }
889 prevstatus = status;
890 }
891 /* NOTREACHED */
892 return 0;
893}
894
Alan Cox95da3102008-07-22 11:09:07 +0100895static int pl2303_ioctl(struct tty_struct *tty, struct file *file,
Thiago Galesi372db8a2006-07-31 15:39:27 -0300896 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897{
Alan Cox95da3102008-07-22 11:09:07 +0100898 struct usb_serial_port *port = tty->driver_data;
Harvey Harrison441b62c2008-03-03 16:08:34 -0800899 dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 switch (cmd) {
Alan Cox3a0f43e2008-07-22 11:14:49 +0100902 case TIOCMIWAIT:
903 dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
904 return wait_modem_info(port, arg);
905 default:
906 dbg("%s not supported = 0x%04x", __func__, cmd);
907 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 return -ENOIOCTLCMD;
910}
911
Alan Cox95da3102008-07-22 11:09:07 +0100912static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913{
Alan Cox95da3102008-07-22 11:09:07 +0100914 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 struct usb_serial *serial = port->serial;
916 u16 state;
917 int result;
918
Harvey Harrison441b62c2008-03-03 16:08:34 -0800919 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 if (break_state == 0)
922 state = BREAK_OFF;
923 else
924 state = BREAK_ON;
Alan Cox3a0f43e2008-07-22 11:14:49 +0100925 dbg("%s - turning break %s", __func__,
926 state == BREAK_OFF ? "off" : "on");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Thiago Galesi372db8a2006-07-31 15:39:27 -0300928 result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
929 BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
930 0, NULL, 0, 100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 if (result)
Harvey Harrison441b62c2008-03-03 16:08:34 -0800932 dbg("%s - error sending break = %d", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933}
934
Alan Sternf9c99bb2009-06-02 11:53:55 -0400935static void pl2303_release(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936{
937 int i;
938 struct pl2303_private *priv;
939
Harvey Harrison441b62c2008-03-03 16:08:34 -0800940 dbg("%s", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
942 for (i = 0; i < serial->num_ports; ++i) {
943 priv = usb_get_serial_port_data(serial->port[i]);
944 if (priv) {
945 pl2303_buf_free(priv->buf);
946 kfree(priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 }
Thiago Galesi372db8a2006-07-31 15:39:27 -0300948 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949}
950
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700951static void pl2303_update_line_status(struct usb_serial_port *port,
952 unsigned char *data,
953 unsigned int actual_length)
954{
955
956 struct pl2303_private *priv = usb_get_serial_port_data(port);
957 unsigned long flags;
958 u8 status_idx = UART_STATE;
Horst Schirmeier95f209f2005-07-28 15:32:20 +0200959 u8 length = UART_STATE + 1;
Thiago Galesi9c537612006-07-29 10:47:12 -0300960 u16 idv, idp;
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700961
Thiago Galesi9c537612006-07-29 10:47:12 -0300962 idv = le16_to_cpu(port->serial->dev->descriptor.idVendor);
963 idp = le16_to_cpu(port->serial->dev->descriptor.idProduct);
964
965
966 if (idv == SIEMENS_VENDOR_ID) {
967 if (idp == SIEMENS_PRODUCT_ID_X65 ||
968 idp == SIEMENS_PRODUCT_ID_SX1 ||
969 idp == SIEMENS_PRODUCT_ID_X75) {
970
971 length = 1;
972 status_idx = 0;
973 }
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700974 }
975
976 if (actual_length < length)
Luiz Fernando N. Capitulinoa009b752006-07-25 16:58:30 -0300977 return;
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700978
Alan Cox3a0f43e2008-07-22 11:14:49 +0100979 /* Save off the uart status for others to look at */
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700980 spin_lock_irqsave(&priv->lock, flags);
981 priv->line_status = data[status_idx];
982 spin_unlock_irqrestore(&priv->lock, flags);
Jason Wessel430eb0d2009-05-29 13:34:16 -0500983 if (priv->line_status & UART_BREAK_ERROR)
984 usb_serial_handle_break(port);
Thiago Galesi372db8a2006-07-31 15:39:27 -0300985 wake_up_interruptible(&priv->delta_msr_wait);
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700986}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
David Howells7d12e782006-10-05 14:55:46 +0100988static void pl2303_read_int_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
Ming Leicdc97792008-02-24 18:41:47 +0800990 struct usb_serial_port *port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 unsigned char *data = urb->transfer_buffer;
Flavio Leitner97bb13e2005-04-18 17:39:31 -0700992 unsigned int actual_length = urb->actual_length;
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -0700993 int status = urb->status;
994 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Harvey Harrison441b62c2008-03-03 16:08:34 -0800996 dbg("%s (%d)", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -0700998 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 case 0:
1000 /* success */
1001 break;
1002 case -ECONNRESET:
1003 case -ENOENT:
1004 case -ESHUTDOWN:
1005 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -08001006 dbg("%s - urb shutting down with status: %d", __func__,
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001007 status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 return;
1009 default:
Harvey Harrison441b62c2008-03-03 16:08:34 -08001010 dbg("%s - nonzero urb status received: %d", __func__,
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001011 status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 goto exit;
1013 }
1014
Harvey Harrison441b62c2008-03-03 16:08:34 -08001015 usb_serial_debug_data(debug, &port->dev, __func__,
Thiago Galesi372db8a2006-07-31 15:39:27 -03001016 urb->actual_length, urb->transfer_buffer);
1017
Flavio Leitner97bb13e2005-04-18 17:39:31 -07001018 pl2303_update_line_status(port, data, actual_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020exit:
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001021 retval = usb_submit_urb(urb, GFP_ATOMIC);
1022 if (retval)
Thiago Galesi372db8a2006-07-31 15:39:27 -03001023 dev_err(&urb->dev->dev,
1024 "%s - usb_submit_urb failed with result %d\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001025 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026}
1027
Alan Coxd4fc4a72009-07-09 13:36:58 +01001028static void pl2303_push_data(struct tty_struct *tty,
1029 struct usb_serial_port *port, struct urb *urb,
1030 u8 line_status)
1031{
1032 unsigned char *data = urb->transfer_buffer;
1033 /* get tty_flag from status */
1034 char tty_flag = TTY_NORMAL;
1035 /* break takes precedence over parity, */
1036 /* which takes precedence over framing errors */
1037 if (line_status & UART_BREAK_ERROR)
1038 tty_flag = TTY_BREAK;
1039 else if (line_status & UART_PARITY_ERROR)
1040 tty_flag = TTY_PARITY;
1041 else if (line_status & UART_FRAME_ERROR)
1042 tty_flag = TTY_FRAME;
1043 dbg("%s - tty_flag = %d", __func__, tty_flag);
1044
1045 tty_buffer_request_room(tty, urb->actual_length + 1);
1046 /* overrun is special, not associated with a char */
1047 if (line_status & UART_OVERRUN_ERROR)
1048 tty_insert_flip_char(tty, 0, TTY_OVERRUN);
Johan Hovold9388e2e2009-10-08 11:36:46 +02001049
1050 if (tty_flag == TTY_NORMAL && !(port->console && port->sysrq))
1051 tty_insert_flip_string(tty, data, urb->actual_length);
1052 else {
Alan Coxd4fc4a72009-07-09 13:36:58 +01001053 int i;
1054 for (i = 0; i < urb->actual_length; ++i)
1055 if (!usb_serial_handle_sysrq_char(tty, port, data[i]))
1056 tty_insert_flip_char(tty, data[i], tty_flag);
Johan Hovold9388e2e2009-10-08 11:36:46 +02001057 }
Alan Coxd4fc4a72009-07-09 13:36:58 +01001058 tty_flip_buffer_push(tty);
1059}
1060
David Howells7d12e782006-10-05 14:55:46 +01001061static void pl2303_read_bulk_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062{
Ming Leicdc97792008-02-24 18:41:47 +08001063 struct usb_serial_port *port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 struct pl2303_private *priv = usb_get_serial_port_data(port);
1065 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 int result;
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001068 int status = urb->status;
1069 u8 line_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
Harvey Harrison441b62c2008-03-03 16:08:34 -08001071 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001073 if (status) {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001074 dbg("%s - urb status = %d", __func__, status);
Alan Cox95da3102008-07-22 11:09:07 +01001075 if (!port->port.count) {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001076 dbg("%s - port is closed, exiting.", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 return;
1078 }
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001079 if (status == -EPROTO) {
Thiago Galesi372db8a2006-07-31 15:39:27 -03001080 /* PL2303 mysteriously fails with -EPROTO reschedule
1081 * the read */
1082 dbg("%s - caught -EPROTO, resubmitting the urb",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001083 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 urb->dev = port->serial->dev;
1085 result = usb_submit_urb(urb, GFP_ATOMIC);
1086 if (result)
Thiago Galesi372db8a2006-07-31 15:39:27 -03001087 dev_err(&urb->dev->dev, "%s - failed"
1088 " resubmitting read urb, error %d\n",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001089 __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 return;
1091 }
Harvey Harrison441b62c2008-03-03 16:08:34 -08001092 dbg("%s - unable to handle the error, exiting.", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 return;
1094 }
1095
Harvey Harrison441b62c2008-03-03 16:08:34 -08001096 usb_serial_debug_data(debug, &port->dev, __func__,
Alan Coxd4fc4a72009-07-09 13:36:58 +01001097 urb->actual_length, urb->transfer_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099 spin_lock_irqsave(&priv->lock, flags);
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001100 line_status = priv->line_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
1102 spin_unlock_irqrestore(&priv->lock, flags);
Thiago Galesi372db8a2006-07-31 15:39:27 -03001103 wake_up_interruptible(&priv->delta_msr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
Alan Cox4a90f092008-10-13 10:39:46 +01001105 tty = tty_port_tty_get(&port->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 if (tty && urb->actual_length) {
Alan Coxd4fc4a72009-07-09 13:36:58 +01001107 pl2303_push_data(tty, port, urb, line_status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 }
Alan Cox4a90f092008-10-13 10:39:46 +01001109 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 /* Schedule the next read _if_ we are still open */
Alan Cox95da3102008-07-22 11:09:07 +01001111 if (port->port.count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 urb->dev = port->serial->dev;
1113 result = usb_submit_urb(urb, GFP_ATOMIC);
1114 if (result)
Thiago Galesi372db8a2006-07-31 15:39:27 -03001115 dev_err(&urb->dev->dev, "%s - failed resubmitting"
Harvey Harrison441b62c2008-03-03 16:08:34 -08001116 " read urb, error %d\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 }
1118
1119 return;
1120}
1121
David Howells7d12e782006-10-05 14:55:46 +01001122static void pl2303_write_bulk_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123{
Ming Leicdc97792008-02-24 18:41:47 +08001124 struct usb_serial_port *port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 struct pl2303_private *priv = usb_get_serial_port_data(port);
1126 int result;
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001127 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
Harvey Harrison441b62c2008-03-03 16:08:34 -08001129 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001131 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 case 0:
1133 /* success */
1134 break;
1135 case -ECONNRESET:
1136 case -ENOENT:
1137 case -ESHUTDOWN:
1138 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -08001139 dbg("%s - urb shutting down with status: %d", __func__,
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001140 status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 priv->write_urb_in_use = 0;
1142 return;
1143 default:
1144 /* error in the urb, so we have to resubmit it */
Harvey Harrison441b62c2008-03-03 16:08:34 -08001145 dbg("%s - Overflow in write", __func__);
1146 dbg("%s - nonzero write bulk status received: %d", __func__,
Greg Kroah-Hartman461d6962007-06-15 15:44:13 -07001147 status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 port->write_urb->transfer_buffer_length = 1;
1149 port->write_urb->dev = port->serial->dev;
Thiago Galesi372db8a2006-07-31 15:39:27 -03001150 result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 if (result)
Thiago Galesi372db8a2006-07-31 15:39:27 -03001152 dev_err(&urb->dev->dev, "%s - failed resubmitting write"
Harvey Harrison441b62c2008-03-03 16:08:34 -08001153 " urb, error %d\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 else
1155 return;
1156 }
1157
1158 priv->write_urb_in_use = 0;
1159
1160 /* send any buffered data */
1161 pl2303_send(port);
1162}
1163
Thiago Galesi572d3132006-07-29 10:46:37 -03001164/* All of the device info needed for the PL2303 SIO serial converter */
1165static struct usb_serial_driver pl2303_device = {
1166 .driver = {
1167 .owner = THIS_MODULE,
1168 .name = "pl2303",
1169 },
1170 .id_table = id_table,
Johannes Hölzld9b1b782006-12-17 21:50:24 +01001171 .usb_driver = &pl2303_driver,
Thiago Galesi572d3132006-07-29 10:46:37 -03001172 .num_ports = 1,
1173 .open = pl2303_open,
1174 .close = pl2303_close,
Alan Cox335f8512009-06-11 12:26:29 +01001175 .dtr_rts = pl2303_dtr_rts,
1176 .carrier_raised = pl2303_carrier_raised,
Thiago Galesi572d3132006-07-29 10:46:37 -03001177 .write = pl2303_write,
1178 .ioctl = pl2303_ioctl,
1179 .break_ctl = pl2303_break_ctl,
1180 .set_termios = pl2303_set_termios,
1181 .tiocmget = pl2303_tiocmget,
1182 .tiocmset = pl2303_tiocmset,
1183 .read_bulk_callback = pl2303_read_bulk_callback,
1184 .read_int_callback = pl2303_read_int_callback,
1185 .write_bulk_callback = pl2303_write_bulk_callback,
1186 .write_room = pl2303_write_room,
1187 .chars_in_buffer = pl2303_chars_in_buffer,
1188 .attach = pl2303_startup,
Alan Sternf9c99bb2009-06-02 11:53:55 -04001189 .release = pl2303_release,
Thiago Galesi572d3132006-07-29 10:46:37 -03001190};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
Thiago Galesi372db8a2006-07-31 15:39:27 -03001192static int __init pl2303_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193{
1194 int retval;
Thiago Galesi372db8a2006-07-31 15:39:27 -03001195
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 retval = usb_serial_register(&pl2303_device);
1197 if (retval)
1198 goto failed_usb_serial_register;
1199 retval = usb_register(&pl2303_driver);
1200 if (retval)
1201 goto failed_usb_register;
Greg Kroah-Hartmanc197a8d2008-08-18 13:21:04 -07001202 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 return 0;
1204failed_usb_register:
1205 usb_serial_deregister(&pl2303_device);
1206failed_usb_serial_register:
1207 return retval;
1208}
1209
Thiago Galesi372db8a2006-07-31 15:39:27 -03001210static void __exit pl2303_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211{
Thiago Galesi372db8a2006-07-31 15:39:27 -03001212 usb_deregister(&pl2303_driver);
1213 usb_serial_deregister(&pl2303_device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214}
1215
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216module_init(pl2303_init);
1217module_exit(pl2303_exit);
1218
1219MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220MODULE_LICENSE("GPL");
1221
1222module_param(debug, bool, S_IRUGO | S_IWUSR);
1223MODULE_PARM_DESC(debug, "Debug enabled or not");
1224