diff options
author | Bill Pemberton <wfp5p@virginia.edu> | 2010-07-29 11:05:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 17:35:44 -0400 |
commit | 52af954599396e5945a895035525c703f2761b20 (patch) | |
tree | 73e5bba4974ec5640cde374e846033a3393392fa /drivers | |
parent | f283925fe9ef4ee75dc43e4c2bfbbd6b8a70bd0a (diff) |
USB: add USB serial ssu100 driver
Add support for the Quatech SSU-100 single port usb to serial device.
This driver is based on the ftdi_sio.c driver and the original
serqt_usb driver from Quatech.
Signed-off-by: Bill Pemberton <wfp5p@virginia.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/serial/ssu100.c | 698 |
3 files changed, 708 insertions, 0 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index bd8aab0ef1cf..916b2b6d765f 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig | |||
@@ -642,6 +642,15 @@ config USB_SERIAL_ZIO | |||
642 | To compile this driver as a module, choose M here: the | 642 | To compile this driver as a module, choose M here: the |
643 | module will be called zio. | 643 | module will be called zio. |
644 | 644 | ||
645 | config USB_SERIAL_SSU100 | ||
646 | tristate "USB Quatech SSU-100 Single Port Serial Driver" | ||
647 | help | ||
648 | Say Y here if you want to use the Quatech SSU-100 single | ||
649 | port usb to serial adapter. | ||
650 | |||
651 | To compile this driver as a module, choose M here: the | ||
652 | module will be called ssu100. | ||
653 | |||
645 | config USB_SERIAL_DEBUG | 654 | config USB_SERIAL_DEBUG |
646 | tristate "USB Debugging Device" | 655 | tristate "USB Debugging Device" |
647 | help | 656 | help |
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index e54c728c016e..40ebe17b6ea8 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile | |||
@@ -51,6 +51,7 @@ obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o | |||
51 | obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o | 51 | obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o |
52 | obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o | 52 | obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o |
53 | obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o | 53 | obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o |
54 | obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o | ||
54 | obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o | 55 | obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o |
55 | obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o | 56 | obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o |
56 | obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o | 57 | obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o |
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c new file mode 100644 index 000000000000..6e82d4f54bc8 --- /dev/null +++ b/drivers/usb/serial/ssu100.c | |||
@@ -0,0 +1,698 @@ | |||
1 | /* | ||
2 | * usb-serial driver for Quatech SSU-100 | ||
3 | * | ||
4 | * based on ftdi_sio.c and the original serqt_usb.c from Quatech | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/errno.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/tty.h> | ||
12 | #include <linux/tty_driver.h> | ||
13 | #include <linux/tty_flip.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/serial.h> | ||
16 | #include <linux/usb.h> | ||
17 | #include <linux/usb/serial.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | |||
20 | #define QT_OPEN_CLOSE_CHANNEL 0xca | ||
21 | #define QT_SET_GET_DEVICE 0xc2 | ||
22 | #define QT_SET_GET_REGISTER 0xc0 | ||
23 | #define QT_GET_SET_PREBUF_TRIG_LVL 0xcc | ||
24 | #define QT_SET_ATF 0xcd | ||
25 | #define QT_GET_SET_UART 0xc1 | ||
26 | #define QT_TRANSFER_IN 0xc0 | ||
27 | #define QT_HW_FLOW_CONTROL_MASK 0xc5 | ||
28 | #define QT_SW_FLOW_CONTROL_MASK 0xc6 | ||
29 | |||
30 | #define MODEM_CTL_REGISTER 0x04 | ||
31 | #define MODEM_STATUS_REGISTER 0x06 | ||
32 | |||
33 | |||
34 | #define SERIAL_LSR_OE 0x02 | ||
35 | #define SERIAL_LSR_PE 0x04 | ||
36 | #define SERIAL_LSR_FE 0x08 | ||
37 | #define SERIAL_LSR_BI 0x10 | ||
38 | |||
39 | #define SERIAL_LSR_TEMT 0x40 | ||
40 | |||
41 | #define SERIAL_MCR_DTR 0x01 | ||
42 | #define SERIAL_MCR_RTS 0x02 | ||
43 | #define SERIAL_MCR_LOOP 0x10 | ||
44 | |||
45 | #define SERIAL_MSR_CTS 0x10 | ||
46 | #define SERIAL_MSR_CD 0x80 | ||
47 | #define SERIAL_MSR_RI 0x40 | ||
48 | #define SERIAL_MSR_DSR 0x20 | ||
49 | #define SERIAL_MSR_MASK 0xf0 | ||
50 | |||
51 | #define SERIAL_CRTSCTS ((SERIAL_MCR_RTS << 8) | SERIAL_MSR_CTS) | ||
52 | |||
53 | #define SERIAL_8_DATA 0x03 | ||
54 | #define SERIAL_7_DATA 0x02 | ||
55 | #define SERIAL_6_DATA 0x01 | ||
56 | #define SERIAL_5_DATA 0x00 | ||
57 | |||
58 | #define SERIAL_ODD_PARITY 0X08 | ||
59 | #define SERIAL_EVEN_PARITY 0X18 | ||
60 | |||
61 | #define MAX_BAUD_RATE 460800 | ||
62 | |||
63 | #define ATC_DISABLED 0x00 | ||
64 | #define DUPMODE_BITS 0xc0 | ||
65 | #define RR_BITS 0x03 | ||
66 | #define LOOPMODE_BITS 0x41 | ||
67 | #define RS232_MODE 0x00 | ||
68 | #define RTSCTS_TO_CONNECTOR 0x40 | ||
69 | #define CLKS_X4 0x02 | ||
70 | #define FULLPWRBIT 0x00000080 | ||
71 | #define NEXT_BOARD_POWER_BIT 0x00000004 | ||
72 | |||
73 | static int debug = 1; | ||
74 | |||
75 | /* Version Information */ | ||
76 | #define DRIVER_VERSION "v0.1" | ||
77 | #define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver" | ||
78 | |||
79 | #define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */ | ||
80 | #define QUATECH_SSU100 0xC020 /* SSU100 */ | ||
81 | |||
82 | static const struct usb_device_id id_table[] = { | ||
83 | {USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)}, | ||
84 | {} /* Terminating entry */ | ||
85 | }; | ||
86 | |||
87 | MODULE_DEVICE_TABLE(usb, id_table); | ||
88 | |||
89 | |||
90 | static struct usb_driver ssu100_driver = { | ||
91 | .name = "ssu100", | ||
92 | .probe = usb_serial_probe, | ||
93 | .disconnect = usb_serial_disconnect, | ||
94 | .id_table = id_table, | ||
95 | .suspend = usb_serial_suspend, | ||
96 | .resume = usb_serial_resume, | ||
97 | .no_dynamic_id = 1, | ||
98 | .supports_autosuspend = 1, | ||
99 | }; | ||
100 | |||
101 | struct ssu100_port_private { | ||
102 | u8 shadowLSR; | ||
103 | u8 shadowMSR; | ||
104 | wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ | ||
105 | unsigned short max_packet_size; | ||
106 | }; | ||
107 | |||
108 | static void ssu100_release(struct usb_serial *serial) | ||
109 | { | ||
110 | struct ssu100_port_private *priv = usb_get_serial_port_data(*serial->port); | ||
111 | |||
112 | dbg("%s", __func__); | ||
113 | kfree(priv); | ||
114 | } | ||
115 | |||
116 | static inline int ssu100_control_msg(struct usb_device *dev, | ||
117 | u8 request, u16 data, u16 index) | ||
118 | { | ||
119 | return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | ||
120 | request, 0x40, data, index, | ||
121 | NULL, 0, 300); | ||
122 | } | ||
123 | |||
124 | static inline int ssu100_setdevice(struct usb_device *dev, u8 *data) | ||
125 | { | ||
126 | u16 x = ((u16)(data[1] << 8) | (u16)(data[0])); | ||
127 | |||
128 | return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0); | ||
129 | } | ||
130 | |||
131 | |||
132 | static inline int ssu100_getdevice(struct usb_device *dev, u8 *data) | ||
133 | { | ||
134 | return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), | ||
135 | QT_SET_GET_DEVICE, 0xc0, 0, 0, | ||
136 | data, 3, 300); | ||
137 | } | ||
138 | |||
139 | static inline int ssu100_getregister(struct usb_device *dev, | ||
140 | unsigned short uart, | ||
141 | unsigned short reg, | ||
142 | u8 *data) | ||
143 | { | ||
144 | return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), | ||
145 | QT_SET_GET_REGISTER, 0xc0, reg, | ||
146 | uart, data, sizeof(*data), 300); | ||
147 | |||
148 | } | ||
149 | |||
150 | |||
151 | static inline int ssu100_setregister(struct usb_device *dev, | ||
152 | unsigned short uart, | ||
153 | u16 data) | ||
154 | { | ||
155 | u16 value = (data << 8) | MODEM_CTL_REGISTER; | ||
156 | |||
157 | return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | ||
158 | QT_SET_GET_REGISTER, 0x40, value, uart, | ||
159 | NULL, 0, 300); | ||
160 | |||
161 | } | ||
162 | |||
163 | #define set_mctrl(dev, set) update_mctrl((dev), (set), 0) | ||
164 | #define clear_mctrl(dev, clear) update_mctrl((dev), 0, (clear)) | ||
165 | |||
166 | /* these do not deal with device that have more than 1 port */ | ||
167 | static inline int update_mctrl(struct usb_device *dev, unsigned int set, | ||
168 | unsigned int clear) | ||
169 | { | ||
170 | unsigned urb_value; | ||
171 | int result; | ||
172 | |||
173 | if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) { | ||
174 | dbg("%s - DTR|RTS not being set|cleared", __func__); | ||
175 | return 0; /* no change */ | ||
176 | } | ||
177 | |||
178 | clear &= ~set; /* 'set' takes precedence over 'clear' */ | ||
179 | urb_value = 0; | ||
180 | if (set & TIOCM_DTR) | ||
181 | urb_value |= SERIAL_MCR_DTR; | ||
182 | if (set & TIOCM_RTS) | ||
183 | urb_value |= SERIAL_MCR_RTS; | ||
184 | |||
185 | result = ssu100_setregister(dev, 0, urb_value); | ||
186 | if (result < 0) | ||
187 | dbg("%s Error from MODEM_CTRL urb", __func__); | ||
188 | |||
189 | return result; | ||
190 | } | ||
191 | |||
192 | static int ssu100_initdevice(struct usb_device *dev) | ||
193 | { | ||
194 | u8 *data; | ||
195 | int result = 0; | ||
196 | |||
197 | dbg("%s", __func__); | ||
198 | |||
199 | data = kzalloc(3, GFP_KERNEL); | ||
200 | if (!data) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | result = ssu100_getdevice(dev, data); | ||
204 | if (result < 0) { | ||
205 | dbg("%s - get_device failed %i", __func__, result); | ||
206 | goto out; | ||
207 | } | ||
208 | |||
209 | data[1] &= ~FULLPWRBIT; | ||
210 | |||
211 | result = ssu100_setdevice(dev, data); | ||
212 | if (result < 0) { | ||
213 | dbg("%s - setdevice failed %i", __func__, result); | ||
214 | goto out; | ||
215 | } | ||
216 | |||
217 | result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0); | ||
218 | if (result < 0) { | ||
219 | dbg("%s - set prebuffer level failed %i", __func__, result); | ||
220 | goto out; | ||
221 | } | ||
222 | |||
223 | result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0); | ||
224 | if (result < 0) { | ||
225 | dbg("%s - set ATFprebuffer level failed %i", __func__, result); | ||
226 | goto out; | ||
227 | } | ||
228 | |||
229 | result = ssu100_getdevice(dev, data); | ||
230 | if (result < 0) { | ||
231 | dbg("%s - get_device failed %i", __func__, result); | ||
232 | goto out; | ||
233 | } | ||
234 | |||
235 | data[0] &= ~(RR_BITS | DUPMODE_BITS); | ||
236 | data[0] |= CLKS_X4; | ||
237 | data[1] &= ~(LOOPMODE_BITS); | ||
238 | data[1] |= RS232_MODE; | ||
239 | |||
240 | result = ssu100_setdevice(dev, data); | ||
241 | if (result < 0) { | ||
242 | dbg("%s - setdevice failed %i", __func__, result); | ||
243 | goto out; | ||
244 | } | ||
245 | |||
246 | out: kfree(data); | ||
247 | return result; | ||
248 | |||
249 | } | ||
250 | |||
251 | |||
252 | static void ssu100_set_termios(struct tty_struct *tty, | ||
253 | struct usb_serial_port *port, | ||
254 | struct ktermios *old_termios) | ||
255 | { | ||
256 | struct usb_device *dev = port->serial->dev; | ||
257 | struct ktermios *termios = tty->termios; | ||
258 | u16 baud, divisor, remainder; | ||
259 | unsigned int cflag = termios->c_cflag; | ||
260 | u16 urb_value = 0; /* will hold the new flags */ | ||
261 | int result; | ||
262 | |||
263 | dbg("%s", __func__); | ||
264 | |||
265 | if (cflag & PARENB) { | ||
266 | if (cflag & PARODD) | ||
267 | urb_value |= SERIAL_ODD_PARITY; | ||
268 | else | ||
269 | urb_value |= SERIAL_EVEN_PARITY; | ||
270 | } | ||
271 | |||
272 | switch (cflag & CSIZE) { | ||
273 | case CS5: | ||
274 | urb_value |= SERIAL_5_DATA; | ||
275 | break; | ||
276 | case CS6: | ||
277 | urb_value |= SERIAL_6_DATA; | ||
278 | break; | ||
279 | case CS7: | ||
280 | urb_value |= SERIAL_7_DATA; | ||
281 | break; | ||
282 | default: | ||
283 | case CS8: | ||
284 | urb_value |= SERIAL_8_DATA; | ||
285 | break; | ||
286 | } | ||
287 | |||
288 | baud = tty_get_baud_rate(tty); | ||
289 | if (!baud) | ||
290 | baud = 9600; | ||
291 | |||
292 | dbg("%s - got baud = %d\n", __func__, baud); | ||
293 | |||
294 | |||
295 | divisor = MAX_BAUD_RATE / baud; | ||
296 | remainder = MAX_BAUD_RATE % baud; | ||
297 | if (((remainder * 2) >= baud) && (baud != 110)) | ||
298 | divisor++; | ||
299 | |||
300 | urb_value = urb_value << 8; | ||
301 | |||
302 | result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value); | ||
303 | if (result < 0) | ||
304 | dbg("%s - set uart failed", __func__); | ||
305 | |||
306 | if (cflag & CRTSCTS) | ||
307 | result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK, | ||
308 | SERIAL_CRTSCTS, 0); | ||
309 | else | ||
310 | result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK, | ||
311 | 0, 0); | ||
312 | if (result < 0) | ||
313 | dbg("%s - set HW flow control failed", __func__); | ||
314 | |||
315 | if (I_IXOFF(tty) || I_IXON(tty)) { | ||
316 | u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty))); | ||
317 | |||
318 | result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK, | ||
319 | x, 0); | ||
320 | } else | ||
321 | result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK, | ||
322 | 0, 0); | ||
323 | |||
324 | if (result < 0) | ||
325 | dbg("%s - set SW flow control failed", __func__); | ||
326 | |||
327 | } | ||
328 | |||
329 | |||
330 | static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) | ||
331 | { | ||
332 | struct usb_device *dev = port->serial->dev; | ||
333 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
334 | u8 *data; | ||
335 | int result; | ||
336 | |||
337 | dbg("%s - port %d", __func__, port->number); | ||
338 | |||
339 | data = kzalloc(2, GFP_KERNEL); | ||
340 | if (!data) | ||
341 | return -ENOMEM; | ||
342 | |||
343 | result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), | ||
344 | QT_OPEN_CLOSE_CHANNEL, | ||
345 | QT_TRANSFER_IN, 0x01, | ||
346 | 0, data, 2, 300); | ||
347 | if (result < 0) { | ||
348 | dbg("%s - open failed %i", __func__, result); | ||
349 | kfree(data); | ||
350 | return result; | ||
351 | } | ||
352 | |||
353 | priv->shadowLSR = data[0] & (SERIAL_LSR_OE | SERIAL_LSR_PE | | ||
354 | SERIAL_LSR_FE | SERIAL_LSR_BI); | ||
355 | |||
356 | priv->shadowMSR = data[1] & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | | ||
357 | SERIAL_MSR_RI | SERIAL_MSR_CD); | ||
358 | |||
359 | kfree(data); | ||
360 | |||
361 | /* set to 9600 */ | ||
362 | result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300); | ||
363 | if (result < 0) | ||
364 | dbg("%s - set uart failed", __func__); | ||
365 | |||
366 | if (tty) | ||
367 | ssu100_set_termios(tty, port, tty->termios); | ||
368 | |||
369 | return usb_serial_generic_open(tty, port); | ||
370 | } | ||
371 | |||
372 | static void ssu100_close(struct usb_serial_port *port) | ||
373 | { | ||
374 | dbg("%s", __func__); | ||
375 | usb_serial_generic_close(port); | ||
376 | } | ||
377 | |||
378 | static int get_serial_info(struct usb_serial_port *port, | ||
379 | struct serial_struct __user *retinfo) | ||
380 | { | ||
381 | struct serial_struct tmp; | ||
382 | |||
383 | if (!retinfo) | ||
384 | return -EFAULT; | ||
385 | |||
386 | memset(&tmp, 0, sizeof(tmp)); | ||
387 | tmp.line = port->serial->minor; | ||
388 | tmp.port = 0; | ||
389 | tmp.irq = 0; | ||
390 | tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; | ||
391 | tmp.xmit_fifo_size = port->bulk_out_size; | ||
392 | tmp.baud_base = 9600; | ||
393 | tmp.close_delay = 5*HZ; | ||
394 | tmp.closing_wait = 30*HZ; | ||
395 | |||
396 | if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) | ||
397 | return -EFAULT; | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int ssu100_ioctl(struct tty_struct *tty, struct file *file, | ||
402 | unsigned int cmd, unsigned long arg) | ||
403 | { | ||
404 | struct usb_serial_port *port = tty->driver_data; | ||
405 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
406 | |||
407 | dbg("%s cmd 0x%04x", __func__, cmd); | ||
408 | |||
409 | switch (cmd) { | ||
410 | case TIOCGSERIAL: | ||
411 | return get_serial_info(port, | ||
412 | (struct serial_struct __user *) arg); | ||
413 | |||
414 | case TIOCMIWAIT: | ||
415 | while (priv != NULL) { | ||
416 | u8 prevMSR = priv->shadowMSR & SERIAL_MSR_MASK; | ||
417 | interruptible_sleep_on(&priv->delta_msr_wait); | ||
418 | /* see if a signal did it */ | ||
419 | if (signal_pending(current)) | ||
420 | return -ERESTARTSYS; | ||
421 | else { | ||
422 | u8 diff = (priv->shadowMSR & SERIAL_MSR_MASK) ^ prevMSR; | ||
423 | if (!diff) | ||
424 | return -EIO; /* no change => error */ | ||
425 | |||
426 | /* Return 0 if caller wanted to know about | ||
427 | these bits */ | ||
428 | |||
429 | if (((arg & TIOCM_RNG) && (diff & SERIAL_MSR_RI)) || | ||
430 | ((arg & TIOCM_DSR) && (diff & SERIAL_MSR_DSR)) || | ||
431 | ((arg & TIOCM_CD) && (diff & SERIAL_MSR_CD)) || | ||
432 | ((arg & TIOCM_CTS) && (diff & SERIAL_MSR_CTS))) | ||
433 | return 0; | ||
434 | } | ||
435 | } | ||
436 | return 0; | ||
437 | |||
438 | default: | ||
439 | break; | ||
440 | } | ||
441 | |||
442 | dbg("%s arg not supported", __func__); | ||
443 | |||
444 | return -ENOIOCTLCMD; | ||
445 | } | ||
446 | |||
447 | static void ssu100_set_max_packet_size(struct usb_serial_port *port) | ||
448 | { | ||
449 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
450 | struct usb_serial *serial = port->serial; | ||
451 | struct usb_device *udev = serial->dev; | ||
452 | |||
453 | struct usb_interface *interface = serial->interface; | ||
454 | struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc; | ||
455 | |||
456 | unsigned num_endpoints; | ||
457 | int i; | ||
458 | |||
459 | num_endpoints = interface->cur_altsetting->desc.bNumEndpoints; | ||
460 | dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints); | ||
461 | |||
462 | for (i = 0; i < num_endpoints; i++) { | ||
463 | dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1, | ||
464 | interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize); | ||
465 | ep_desc = &interface->cur_altsetting->endpoint[i].desc; | ||
466 | } | ||
467 | |||
468 | /* set max packet size based on descriptor */ | ||
469 | priv->max_packet_size = ep_desc->wMaxPacketSize; | ||
470 | |||
471 | dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); | ||
472 | } | ||
473 | |||
474 | static int ssu100_attach(struct usb_serial *serial) | ||
475 | { | ||
476 | struct ssu100_port_private *priv; | ||
477 | struct usb_serial_port *port = *serial->port; | ||
478 | |||
479 | dbg("%s", __func__); | ||
480 | |||
481 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
482 | if (!priv) { | ||
483 | dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__, | ||
484 | sizeof(*priv)); | ||
485 | return -ENOMEM; | ||
486 | } | ||
487 | |||
488 | init_waitqueue_head(&priv->delta_msr_wait); | ||
489 | usb_set_serial_port_data(port, priv); | ||
490 | |||
491 | ssu100_set_max_packet_size(port); | ||
492 | |||
493 | return ssu100_initdevice(serial->dev); | ||
494 | } | ||
495 | |||
496 | static int ssu100_tiocmget(struct tty_struct *tty, struct file *file) | ||
497 | { | ||
498 | struct usb_serial_port *port = tty->driver_data; | ||
499 | struct usb_device *dev = port->serial->dev; | ||
500 | u8 *d; | ||
501 | int r; | ||
502 | |||
503 | dbg("%s\n", __func__); | ||
504 | |||
505 | d = kzalloc(2, GFP_KERNEL); | ||
506 | if (!d) | ||
507 | return -ENOMEM; | ||
508 | |||
509 | r = ssu100_getregister(dev, 0, MODEM_CTL_REGISTER, d); | ||
510 | if (r < 0) | ||
511 | goto mget_out; | ||
512 | |||
513 | r = ssu100_getregister(dev, 0, MODEM_STATUS_REGISTER, d+1); | ||
514 | if (r < 0) | ||
515 | goto mget_out; | ||
516 | |||
517 | r = (d[0] & SERIAL_MCR_DTR ? TIOCM_DTR : 0) | | ||
518 | (d[0] & SERIAL_MCR_RTS ? TIOCM_RTS : 0) | | ||
519 | (d[1] & SERIAL_MSR_CTS ? TIOCM_CTS : 0) | | ||
520 | (d[1] & SERIAL_MSR_CD ? TIOCM_CAR : 0) | | ||
521 | (d[1] & SERIAL_MSR_RI ? TIOCM_RI : 0) | | ||
522 | (d[1] & SERIAL_MSR_DSR ? TIOCM_DSR : 0); | ||
523 | |||
524 | mget_out: | ||
525 | kfree(d); | ||
526 | return r; | ||
527 | } | ||
528 | |||
529 | static int ssu100_tiocmset(struct tty_struct *tty, struct file *file, | ||
530 | unsigned int set, unsigned int clear) | ||
531 | { | ||
532 | struct usb_serial_port *port = tty->driver_data; | ||
533 | struct usb_device *dev = port->serial->dev; | ||
534 | |||
535 | dbg("%s\n", __func__); | ||
536 | return update_mctrl(dev, set, clear); | ||
537 | } | ||
538 | |||
539 | static void ssu100_dtr_rts(struct usb_serial_port *port, int on) | ||
540 | { | ||
541 | struct usb_device *dev = port->serial->dev; | ||
542 | |||
543 | dbg("%s\n", __func__); | ||
544 | |||
545 | mutex_lock(&port->serial->disc_mutex); | ||
546 | if (!port->serial->disconnected) { | ||
547 | /* Disable flow control */ | ||
548 | if (!on && | ||
549 | ssu100_setregister(dev, 0, 0) < 0) | ||
550 | dev_err(&port->dev, "error from flowcontrol urb\n"); | ||
551 | /* drop RTS and DTR */ | ||
552 | if (on) | ||
553 | set_mctrl(dev, TIOCM_DTR | TIOCM_RTS); | ||
554 | else | ||
555 | clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS); | ||
556 | } | ||
557 | mutex_unlock(&port->serial->disc_mutex); | ||
558 | } | ||
559 | |||
560 | static int ssu100_process_packet(struct tty_struct *tty, | ||
561 | struct usb_serial_port *port, | ||
562 | struct ssu100_port_private *priv, | ||
563 | char *packet, int len) | ||
564 | { | ||
565 | int i; | ||
566 | char flag; | ||
567 | char *ch; | ||
568 | |||
569 | dbg("%s - port %d", __func__, port->number); | ||
570 | |||
571 | if (len < 4) { | ||
572 | dbg("%s - malformed packet", __func__); | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | if ((packet[0] == 0x1b) && (packet[1] == 0x1b) && | ||
577 | ((packet[2] == 0x00) || (packet[2] == 0x01))) { | ||
578 | if (packet[2] == 0x00) | ||
579 | priv->shadowLSR = packet[3] & (SERIAL_LSR_OE | | ||
580 | SERIAL_LSR_PE | | ||
581 | SERIAL_LSR_FE | | ||
582 | SERIAL_LSR_BI); | ||
583 | |||
584 | if (packet[2] == 0x01) { | ||
585 | priv->shadowMSR = packet[3]; | ||
586 | wake_up_interruptible(&priv->delta_msr_wait); | ||
587 | } | ||
588 | |||
589 | len -= 4; | ||
590 | ch = packet + 4; | ||
591 | } else | ||
592 | ch = packet; | ||
593 | |||
594 | if (!len) | ||
595 | return 0; /* status only */ | ||
596 | |||
597 | if (port->port.console && port->sysrq) { | ||
598 | for (i = 0; i < len; i++, ch++) { | ||
599 | if (!usb_serial_handle_sysrq_char(tty, port, *ch)) | ||
600 | tty_insert_flip_char(tty, *ch, flag); | ||
601 | } | ||
602 | } else | ||
603 | tty_insert_flip_string_fixed_flag(tty, ch, flag, len); | ||
604 | |||
605 | return len; | ||
606 | } | ||
607 | |||
608 | static void ssu100_process_read_urb(struct urb *urb) | ||
609 | { | ||
610 | struct usb_serial_port *port = urb->context; | ||
611 | struct ssu100_port_private *priv = usb_get_serial_port_data(port); | ||
612 | char *data = (char *)urb->transfer_buffer; | ||
613 | struct tty_struct *tty; | ||
614 | int count = 0; | ||
615 | int i; | ||
616 | int len; | ||
617 | |||
618 | dbg("%s", __func__); | ||
619 | |||
620 | tty = tty_port_tty_get(&port->port); | ||
621 | if (!tty) | ||
622 | return; | ||
623 | |||
624 | for (i = 0; i < urb->actual_length; i += priv->max_packet_size) { | ||
625 | len = min_t(int, urb->actual_length - i, priv->max_packet_size); | ||
626 | count += ssu100_process_packet(tty, port, priv, &data[i], len); | ||
627 | } | ||
628 | |||
629 | if (count) | ||
630 | tty_flip_buffer_push(tty); | ||
631 | tty_kref_put(tty); | ||
632 | } | ||
633 | |||
634 | |||
635 | static struct usb_serial_driver ssu100_device = { | ||
636 | .driver = { | ||
637 | .owner = THIS_MODULE, | ||
638 | .name = "ssu100", | ||
639 | }, | ||
640 | .description = DRIVER_DESC, | ||
641 | .id_table = id_table, | ||
642 | .usb_driver = &ssu100_driver, | ||
643 | .num_ports = 1, | ||
644 | .bulk_in_size = 256, | ||
645 | .bulk_out_size = 256, | ||
646 | .open = ssu100_open, | ||
647 | .close = ssu100_close, | ||
648 | .attach = ssu100_attach, | ||
649 | .release = ssu100_release, | ||
650 | .dtr_rts = ssu100_dtr_rts, | ||
651 | .process_read_urb = ssu100_process_read_urb, | ||
652 | .tiocmget = ssu100_tiocmget, | ||
653 | .tiocmset = ssu100_tiocmset, | ||
654 | .ioctl = ssu100_ioctl, | ||
655 | .set_termios = ssu100_set_termios, | ||
656 | }; | ||
657 | |||
658 | static int __init ssu100_init(void) | ||
659 | { | ||
660 | int retval; | ||
661 | |||
662 | dbg("%s", __func__); | ||
663 | |||
664 | /* register with usb-serial */ | ||
665 | retval = usb_serial_register(&ssu100_device); | ||
666 | |||
667 | if (retval) | ||
668 | goto failed_usb_sio_register; | ||
669 | |||
670 | retval = usb_register(&ssu100_driver); | ||
671 | if (retval) | ||
672 | goto failed_usb_register; | ||
673 | |||
674 | printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" | ||
675 | DRIVER_DESC "\n"); | ||
676 | |||
677 | return 0; | ||
678 | |||
679 | failed_usb_register: | ||
680 | usb_serial_deregister(&ssu100_device); | ||
681 | failed_usb_sio_register: | ||
682 | return retval; | ||
683 | } | ||
684 | |||
685 | static void __exit ssu100_exit(void) | ||
686 | { | ||
687 | usb_deregister(&ssu100_driver); | ||
688 | usb_serial_deregister(&ssu100_device); | ||
689 | } | ||
690 | |||
691 | module_init(ssu100_init); | ||
692 | module_exit(ssu100_exit); | ||
693 | |||
694 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
695 | MODULE_LICENSE("GPL"); | ||
696 | |||
697 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
698 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||