diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/class/cdc-acm.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 942 |
1 files changed, 942 insertions, 0 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c new file mode 100644 index 000000000000..6d1f9b6aecff --- /dev/null +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -0,0 +1,942 @@ | |||
1 | /* | ||
2 | * cdc-acm.c | ||
3 | * | ||
4 | * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de> | ||
5 | * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> | ||
6 | * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com> | ||
7 | * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> | ||
8 | * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name> | ||
9 | * | ||
10 | * USB Abstract Control Model driver for USB modems and ISDN adapters | ||
11 | * | ||
12 | * Sponsored by SuSE | ||
13 | * | ||
14 | * ChangeLog: | ||
15 | * v0.9 - thorough cleaning, URBification, almost a rewrite | ||
16 | * v0.10 - some more cleanups | ||
17 | * v0.11 - fixed flow control, read error doesn't stop reads | ||
18 | * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced | ||
19 | * v0.13 - added termios, added hangup | ||
20 | * v0.14 - sized down struct acm | ||
21 | * v0.15 - fixed flow control again - characters could be lost | ||
22 | * v0.16 - added code for modems with swapped data and control interfaces | ||
23 | * v0.17 - added new style probing | ||
24 | * v0.18 - fixed new style probing for devices with more configurations | ||
25 | * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan) | ||
26 | * v0.20 - switched to probing on interface (rather than device) class | ||
27 | * v0.21 - revert to probing on device for devices with multiple configs | ||
28 | * v0.22 - probe only the control interface. if usbcore doesn't choose the | ||
29 | * config we want, sysadmin changes bConfigurationValue in sysfs. | ||
30 | * v0.23 - use softirq for rx processing, as needed by tty layer | ||
31 | * v0.24 - change probe method to evaluate CDC union descriptor | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * This program is free software; you can redistribute it and/or modify | ||
36 | * it under the terms of the GNU General Public License as published by | ||
37 | * the Free Software Foundation; either version 2 of the License, or | ||
38 | * (at your option) any later version. | ||
39 | * | ||
40 | * This program is distributed in the hope that it will be useful, | ||
41 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
42 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
43 | * GNU General Public License for more details. | ||
44 | * | ||
45 | * You should have received a copy of the GNU General Public License | ||
46 | * along with this program; if not, write to the Free Software | ||
47 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
48 | */ | ||
49 | |||
50 | #undef DEBUG | ||
51 | |||
52 | #include <linux/kernel.h> | ||
53 | #include <linux/errno.h> | ||
54 | #include <linux/init.h> | ||
55 | #include <linux/slab.h> | ||
56 | #include <linux/tty.h> | ||
57 | #include <linux/tty_driver.h> | ||
58 | #include <linux/tty_flip.h> | ||
59 | #include <linux/module.h> | ||
60 | #include <linux/smp_lock.h> | ||
61 | #include <asm/uaccess.h> | ||
62 | #include <linux/usb.h> | ||
63 | #include <linux/usb_cdc.h> | ||
64 | #include <asm/byteorder.h> | ||
65 | #include <asm/unaligned.h> | ||
66 | |||
67 | #include "cdc-acm.h" | ||
68 | |||
69 | /* | ||
70 | * Version Information | ||
71 | */ | ||
72 | #define DRIVER_VERSION "v0.23" | ||
73 | #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik" | ||
74 | #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" | ||
75 | |||
76 | static struct usb_driver acm_driver; | ||
77 | static struct tty_driver *acm_tty_driver; | ||
78 | static struct acm *acm_table[ACM_TTY_MINORS]; | ||
79 | |||
80 | static DECLARE_MUTEX(open_sem); | ||
81 | |||
82 | #define ACM_READY(acm) (acm && acm->dev && acm->used) | ||
83 | |||
84 | /* | ||
85 | * Functions for ACM control messages. | ||
86 | */ | ||
87 | |||
88 | static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) | ||
89 | { | ||
90 | int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), | ||
91 | request, USB_RT_ACM, value, | ||
92 | acm->control->altsetting[0].desc.bInterfaceNumber, | ||
93 | buf, len, 5000); | ||
94 | dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); | ||
95 | return retval < 0 ? retval : 0; | ||
96 | } | ||
97 | |||
98 | /* devices aren't required to support these requests. | ||
99 | * the cdc acm descriptor tells whether they do... | ||
100 | */ | ||
101 | #define acm_set_control(acm, control) \ | ||
102 | acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) | ||
103 | #define acm_set_line(acm, line) \ | ||
104 | acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) | ||
105 | #define acm_send_break(acm, ms) \ | ||
106 | acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) | ||
107 | |||
108 | /* | ||
109 | * Interrupt handlers for various ACM device responses | ||
110 | */ | ||
111 | |||
112 | /* control interface reports status changes with "interrupt" transfers */ | ||
113 | static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs) | ||
114 | { | ||
115 | struct acm *acm = urb->context; | ||
116 | struct usb_cdc_notification *dr = urb->transfer_buffer; | ||
117 | unsigned char *data; | ||
118 | int newctrl; | ||
119 | int status; | ||
120 | |||
121 | switch (urb->status) { | ||
122 | case 0: | ||
123 | /* success */ | ||
124 | break; | ||
125 | case -ECONNRESET: | ||
126 | case -ENOENT: | ||
127 | case -ESHUTDOWN: | ||
128 | /* this urb is terminated, clean up */ | ||
129 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); | ||
130 | return; | ||
131 | default: | ||
132 | dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); | ||
133 | goto exit; | ||
134 | } | ||
135 | |||
136 | if (!ACM_READY(acm)) | ||
137 | goto exit; | ||
138 | |||
139 | data = (unsigned char *)(dr + 1); | ||
140 | switch (dr->bNotificationType) { | ||
141 | |||
142 | case USB_CDC_NOTIFY_NETWORK_CONNECTION: | ||
143 | |||
144 | dbg("%s network", dr->wValue ? "connected to" : "disconnected from"); | ||
145 | break; | ||
146 | |||
147 | case USB_CDC_NOTIFY_SERIAL_STATE: | ||
148 | |||
149 | newctrl = le16_to_cpu(get_unaligned((__le16 *) data)); | ||
150 | |||
151 | if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { | ||
152 | dbg("calling hangup"); | ||
153 | tty_hangup(acm->tty); | ||
154 | } | ||
155 | |||
156 | acm->ctrlin = newctrl; | ||
157 | |||
158 | dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", | ||
159 | acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', | ||
160 | acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-', | ||
161 | acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', | ||
162 | acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); | ||
163 | |||
164 | break; | ||
165 | |||
166 | default: | ||
167 | dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", | ||
168 | dr->bNotificationType, dr->wIndex, | ||
169 | dr->wLength, data[0], data[1]); | ||
170 | break; | ||
171 | } | ||
172 | exit: | ||
173 | status = usb_submit_urb (urb, GFP_ATOMIC); | ||
174 | if (status) | ||
175 | err ("%s - usb_submit_urb failed with result %d", | ||
176 | __FUNCTION__, status); | ||
177 | } | ||
178 | |||
179 | /* data interface returns incoming bytes, or we got unthrottled */ | ||
180 | static void acm_read_bulk(struct urb *urb, struct pt_regs *regs) | ||
181 | { | ||
182 | struct acm *acm = urb->context; | ||
183 | dbg("Entering acm_read_bulk with status %d\n", urb->status); | ||
184 | |||
185 | if (!ACM_READY(acm)) | ||
186 | return; | ||
187 | |||
188 | if (urb->status) | ||
189 | dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status); | ||
190 | |||
191 | /* calling tty_flip_buffer_push() in_irq() isn't allowed */ | ||
192 | tasklet_schedule(&acm->bh); | ||
193 | } | ||
194 | |||
195 | static void acm_rx_tasklet(unsigned long _acm) | ||
196 | { | ||
197 | struct acm *acm = (void *)_acm; | ||
198 | struct urb *urb = acm->readurb; | ||
199 | struct tty_struct *tty = acm->tty; | ||
200 | unsigned char *data = urb->transfer_buffer; | ||
201 | int i = 0; | ||
202 | dbg("Entering acm_rx_tasklet"); | ||
203 | |||
204 | if (urb->actual_length > 0 && !acm->throttle) { | ||
205 | for (i = 0; i < urb->actual_length && !acm->throttle; i++) { | ||
206 | /* if we insert more than TTY_FLIPBUF_SIZE characters, | ||
207 | * we drop them. */ | ||
208 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) { | ||
209 | tty_flip_buffer_push(tty); | ||
210 | } | ||
211 | tty_insert_flip_char(tty, data[i], 0); | ||
212 | } | ||
213 | dbg("Handed %d bytes to tty layer", i+1); | ||
214 | tty_flip_buffer_push(tty); | ||
215 | } | ||
216 | |||
217 | spin_lock(&acm->throttle_lock); | ||
218 | if (acm->throttle) { | ||
219 | dbg("Throtteling noticed"); | ||
220 | memmove(data, data + i, urb->actual_length - i); | ||
221 | urb->actual_length -= i; | ||
222 | acm->resubmit_to_unthrottle = 1; | ||
223 | spin_unlock(&acm->throttle_lock); | ||
224 | return; | ||
225 | } | ||
226 | spin_unlock(&acm->throttle_lock); | ||
227 | |||
228 | urb->actual_length = 0; | ||
229 | urb->dev = acm->dev; | ||
230 | |||
231 | i = usb_submit_urb(urb, GFP_ATOMIC); | ||
232 | if (i) | ||
233 | dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i); | ||
234 | } | ||
235 | |||
236 | /* data interface wrote those outgoing bytes */ | ||
237 | static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) | ||
238 | { | ||
239 | struct acm *acm = (struct acm *)urb->context; | ||
240 | dbg("Entering acm_write_bulk with status %d\n", urb->status); | ||
241 | |||
242 | if (!ACM_READY(acm)) | ||
243 | goto out; | ||
244 | |||
245 | if (urb->status) | ||
246 | dbg("nonzero write bulk status received: %d", urb->status); | ||
247 | |||
248 | schedule_work(&acm->work); | ||
249 | out: | ||
250 | acm->ready_for_write = 1; | ||
251 | } | ||
252 | |||
253 | static void acm_softint(void *private) | ||
254 | { | ||
255 | struct acm *acm = private; | ||
256 | dbg("Entering acm_softint.\n"); | ||
257 | |||
258 | if (!ACM_READY(acm)) | ||
259 | return; | ||
260 | tty_wakeup(acm->tty); | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * TTY handlers | ||
265 | */ | ||
266 | |||
267 | static int acm_tty_open(struct tty_struct *tty, struct file *filp) | ||
268 | { | ||
269 | struct acm *acm; | ||
270 | int rv = -EINVAL; | ||
271 | dbg("Entering acm_tty_open.\n"); | ||
272 | |||
273 | down(&open_sem); | ||
274 | |||
275 | acm = acm_table[tty->index]; | ||
276 | if (!acm || !acm->dev) | ||
277 | goto err_out; | ||
278 | else | ||
279 | rv = 0; | ||
280 | |||
281 | tty->driver_data = acm; | ||
282 | acm->tty = tty; | ||
283 | |||
284 | |||
285 | |||
286 | if (acm->used++) { | ||
287 | goto done; | ||
288 | } | ||
289 | |||
290 | acm->ctrlurb->dev = acm->dev; | ||
291 | if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { | ||
292 | dbg("usb_submit_urb(ctrl irq) failed"); | ||
293 | goto bail_out; | ||
294 | } | ||
295 | |||
296 | acm->readurb->dev = acm->dev; | ||
297 | if (usb_submit_urb(acm->readurb, GFP_KERNEL)) { | ||
298 | dbg("usb_submit_urb(read bulk) failed"); | ||
299 | goto bail_out_and_unlink; | ||
300 | } | ||
301 | |||
302 | if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS)) | ||
303 | goto full_bailout; | ||
304 | |||
305 | /* force low_latency on so that our tty_push actually forces the data through, | ||
306 | otherwise it is scheduled, and with high data rates data can get lost. */ | ||
307 | tty->low_latency = 1; | ||
308 | |||
309 | done: | ||
310 | err_out: | ||
311 | up(&open_sem); | ||
312 | return rv; | ||
313 | |||
314 | full_bailout: | ||
315 | usb_kill_urb(acm->readurb); | ||
316 | bail_out_and_unlink: | ||
317 | usb_kill_urb(acm->ctrlurb); | ||
318 | bail_out: | ||
319 | acm->used--; | ||
320 | up(&open_sem); | ||
321 | return -EIO; | ||
322 | } | ||
323 | |||
324 | static void acm_tty_close(struct tty_struct *tty, struct file *filp) | ||
325 | { | ||
326 | struct acm *acm = tty->driver_data; | ||
327 | |||
328 | if (!acm || !acm->used) | ||
329 | return; | ||
330 | |||
331 | down(&open_sem); | ||
332 | if (!--acm->used) { | ||
333 | if (acm->dev) { | ||
334 | acm_set_control(acm, acm->ctrlout = 0); | ||
335 | usb_kill_urb(acm->ctrlurb); | ||
336 | usb_kill_urb(acm->writeurb); | ||
337 | usb_kill_urb(acm->readurb); | ||
338 | } else { | ||
339 | tty_unregister_device(acm_tty_driver, acm->minor); | ||
340 | acm_table[acm->minor] = NULL; | ||
341 | usb_free_urb(acm->ctrlurb); | ||
342 | usb_free_urb(acm->readurb); | ||
343 | usb_free_urb(acm->writeurb); | ||
344 | kfree(acm); | ||
345 | } | ||
346 | } | ||
347 | up(&open_sem); | ||
348 | } | ||
349 | |||
350 | static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) | ||
351 | { | ||
352 | struct acm *acm = tty->driver_data; | ||
353 | int stat; | ||
354 | dbg("Entering acm_tty_write to write %d bytes,\n", count); | ||
355 | |||
356 | if (!ACM_READY(acm)) | ||
357 | return -EINVAL; | ||
358 | if (!acm->ready_for_write) | ||
359 | return 0; | ||
360 | if (!count) | ||
361 | return 0; | ||
362 | |||
363 | count = (count > acm->writesize) ? acm->writesize : count; | ||
364 | |||
365 | dbg("Get %d bytes...", count); | ||
366 | memcpy(acm->write_buffer, buf, count); | ||
367 | dbg(" Successfully copied.\n"); | ||
368 | |||
369 | acm->writeurb->transfer_buffer_length = count; | ||
370 | acm->writeurb->dev = acm->dev; | ||
371 | |||
372 | acm->ready_for_write = 0; | ||
373 | stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC); | ||
374 | if (stat < 0) { | ||
375 | dbg("usb_submit_urb(write bulk) failed"); | ||
376 | acm->ready_for_write = 1; | ||
377 | return stat; | ||
378 | } | ||
379 | |||
380 | return count; | ||
381 | } | ||
382 | |||
383 | static int acm_tty_write_room(struct tty_struct *tty) | ||
384 | { | ||
385 | struct acm *acm = tty->driver_data; | ||
386 | if (!ACM_READY(acm)) | ||
387 | return -EINVAL; | ||
388 | return !acm->ready_for_write ? 0 : acm->writesize; | ||
389 | } | ||
390 | |||
391 | static int acm_tty_chars_in_buffer(struct tty_struct *tty) | ||
392 | { | ||
393 | struct acm *acm = tty->driver_data; | ||
394 | if (!ACM_READY(acm)) | ||
395 | return -EINVAL; | ||
396 | return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0; | ||
397 | } | ||
398 | |||
399 | static void acm_tty_throttle(struct tty_struct *tty) | ||
400 | { | ||
401 | struct acm *acm = tty->driver_data; | ||
402 | if (!ACM_READY(acm)) | ||
403 | return; | ||
404 | spin_lock_bh(&acm->throttle_lock); | ||
405 | acm->throttle = 1; | ||
406 | spin_unlock_bh(&acm->throttle_lock); | ||
407 | } | ||
408 | |||
409 | static void acm_tty_unthrottle(struct tty_struct *tty) | ||
410 | { | ||
411 | struct acm *acm = tty->driver_data; | ||
412 | if (!ACM_READY(acm)) | ||
413 | return; | ||
414 | spin_lock_bh(&acm->throttle_lock); | ||
415 | acm->throttle = 0; | ||
416 | spin_unlock_bh(&acm->throttle_lock); | ||
417 | if (acm->resubmit_to_unthrottle) { | ||
418 | acm->resubmit_to_unthrottle = 0; | ||
419 | acm_read_bulk(acm->readurb, NULL); | ||
420 | } | ||
421 | } | ||
422 | |||
423 | static void acm_tty_break_ctl(struct tty_struct *tty, int state) | ||
424 | { | ||
425 | struct acm *acm = tty->driver_data; | ||
426 | if (!ACM_READY(acm)) | ||
427 | return; | ||
428 | if (acm_send_break(acm, state ? 0xffff : 0)) | ||
429 | dbg("send break failed"); | ||
430 | } | ||
431 | |||
432 | static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) | ||
433 | { | ||
434 | struct acm *acm = tty->driver_data; | ||
435 | |||
436 | if (!ACM_READY(acm)) | ||
437 | return -EINVAL; | ||
438 | |||
439 | return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | | ||
440 | (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | | ||
441 | (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | | ||
442 | (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | | ||
443 | (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | | ||
444 | TIOCM_CTS; | ||
445 | } | ||
446 | |||
447 | static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, | ||
448 | unsigned int set, unsigned int clear) | ||
449 | { | ||
450 | struct acm *acm = tty->driver_data; | ||
451 | unsigned int newctrl; | ||
452 | |||
453 | if (!ACM_READY(acm)) | ||
454 | return -EINVAL; | ||
455 | |||
456 | newctrl = acm->ctrlout; | ||
457 | set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); | ||
458 | clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); | ||
459 | |||
460 | newctrl = (newctrl & ~clear) | set; | ||
461 | |||
462 | if (acm->ctrlout == newctrl) | ||
463 | return 0; | ||
464 | return acm_set_control(acm, acm->ctrlout = newctrl); | ||
465 | } | ||
466 | |||
467 | static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) | ||
468 | { | ||
469 | struct acm *acm = tty->driver_data; | ||
470 | |||
471 | if (!ACM_READY(acm)) | ||
472 | return -EINVAL; | ||
473 | |||
474 | return -ENOIOCTLCMD; | ||
475 | } | ||
476 | |||
477 | static __u32 acm_tty_speed[] = { | ||
478 | 0, 50, 75, 110, 134, 150, 200, 300, 600, | ||
479 | 1200, 1800, 2400, 4800, 9600, 19200, 38400, | ||
480 | 57600, 115200, 230400, 460800, 500000, 576000, | ||
481 | 921600, 1000000, 1152000, 1500000, 2000000, | ||
482 | 2500000, 3000000, 3500000, 4000000 | ||
483 | }; | ||
484 | |||
485 | static __u8 acm_tty_size[] = { | ||
486 | 5, 6, 7, 8 | ||
487 | }; | ||
488 | |||
489 | static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old) | ||
490 | { | ||
491 | struct acm *acm = tty->driver_data; | ||
492 | struct termios *termios = tty->termios; | ||
493 | struct usb_cdc_line_coding newline; | ||
494 | int newctrl = acm->ctrlout; | ||
495 | |||
496 | if (!ACM_READY(acm)) | ||
497 | return; | ||
498 | |||
499 | newline.dwDTERate = cpu_to_le32p(acm_tty_speed + | ||
500 | (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); | ||
501 | newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; | ||
502 | newline.bParityType = termios->c_cflag & PARENB ? | ||
503 | (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; | ||
504 | newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; | ||
505 | |||
506 | acm->clocal = ((termios->c_cflag & CLOCAL) != 0); | ||
507 | |||
508 | if (!newline.dwDTERate) { | ||
509 | newline.dwDTERate = acm->line.dwDTERate; | ||
510 | newctrl &= ~ACM_CTRL_DTR; | ||
511 | } else newctrl |= ACM_CTRL_DTR; | ||
512 | |||
513 | if (newctrl != acm->ctrlout) | ||
514 | acm_set_control(acm, acm->ctrlout = newctrl); | ||
515 | |||
516 | if (memcmp(&acm->line, &newline, sizeof newline)) { | ||
517 | memcpy(&acm->line, &newline, sizeof newline); | ||
518 | dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), | ||
519 | newline.bCharFormat, newline.bParityType, | ||
520 | newline.bDataBits); | ||
521 | acm_set_line(acm, &acm->line); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * USB probe and disconnect routines. | ||
527 | */ | ||
528 | |||
529 | static int acm_probe (struct usb_interface *intf, | ||
530 | const struct usb_device_id *id) | ||
531 | { | ||
532 | struct usb_cdc_union_desc *union_header = NULL; | ||
533 | char *buffer = intf->altsetting->extra; | ||
534 | int buflen = intf->altsetting->extralen; | ||
535 | struct usb_interface *control_interface; | ||
536 | struct usb_interface *data_interface; | ||
537 | struct usb_endpoint_descriptor *epctrl; | ||
538 | struct usb_endpoint_descriptor *epread; | ||
539 | struct usb_endpoint_descriptor *epwrite; | ||
540 | struct usb_device *usb_dev = interface_to_usbdev(intf); | ||
541 | struct acm *acm; | ||
542 | int minor; | ||
543 | int ctrlsize,readsize; | ||
544 | u8 *buf; | ||
545 | u8 ac_management_function = 0; | ||
546 | u8 call_management_function = 0; | ||
547 | int call_interface_num = -1; | ||
548 | int data_interface_num; | ||
549 | unsigned long quirks; | ||
550 | |||
551 | /* handle quirks deadly to normal probing*/ | ||
552 | quirks = (unsigned long)id->driver_info; | ||
553 | if (quirks == NO_UNION_NORMAL) { | ||
554 | data_interface = usb_ifnum_to_if(usb_dev, 1); | ||
555 | control_interface = usb_ifnum_to_if(usb_dev, 0); | ||
556 | goto skip_normal_probe; | ||
557 | } | ||
558 | |||
559 | /* normal probing*/ | ||
560 | if (!buffer) { | ||
561 | err("Wierd descriptor references\n"); | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
565 | if (!buflen) { | ||
566 | if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { | ||
567 | dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n"); | ||
568 | buflen = intf->cur_altsetting->endpoint->extralen; | ||
569 | buffer = intf->cur_altsetting->endpoint->extra; | ||
570 | } else { | ||
571 | err("Zero length descriptor references\n"); | ||
572 | return -EINVAL; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | while (buflen > 0) { | ||
577 | if (buffer [1] != USB_DT_CS_INTERFACE) { | ||
578 | err("skipping garbage\n"); | ||
579 | goto next_desc; | ||
580 | } | ||
581 | |||
582 | switch (buffer [2]) { | ||
583 | case USB_CDC_UNION_TYPE: /* we've found it */ | ||
584 | if (union_header) { | ||
585 | err("More than one union descriptor, skipping ..."); | ||
586 | goto next_desc; | ||
587 | } | ||
588 | union_header = (struct usb_cdc_union_desc *) | ||
589 | buffer; | ||
590 | break; | ||
591 | case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ | ||
592 | break; /* for now we ignore it */ | ||
593 | case USB_CDC_HEADER_TYPE: /* maybe check version */ | ||
594 | break; /* for now we ignore it */ | ||
595 | case USB_CDC_ACM_TYPE: | ||
596 | ac_management_function = buffer[3]; | ||
597 | break; | ||
598 | case USB_CDC_CALL_MANAGEMENT_TYPE: | ||
599 | call_management_function = buffer[3]; | ||
600 | call_interface_num = buffer[4]; | ||
601 | if ((call_management_function & 3) != 3) | ||
602 | err("This device cannot do calls on its own. It is no modem."); | ||
603 | break; | ||
604 | |||
605 | default: | ||
606 | err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]); | ||
607 | break; | ||
608 | } | ||
609 | next_desc: | ||
610 | buflen -= buffer[0]; | ||
611 | buffer += buffer[0]; | ||
612 | } | ||
613 | |||
614 | if (!union_header) { | ||
615 | if (call_interface_num > 0) { | ||
616 | dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n"); | ||
617 | data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); | ||
618 | control_interface = intf; | ||
619 | } else { | ||
620 | dev_dbg(&intf->dev,"No union descriptor, giving up\n"); | ||
621 | return -ENODEV; | ||
622 | } | ||
623 | } else { | ||
624 | control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); | ||
625 | data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); | ||
626 | if (!control_interface || !data_interface) { | ||
627 | dev_dbg(&intf->dev,"no interfaces\n"); | ||
628 | return -ENODEV; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | if (data_interface_num != call_interface_num) | ||
633 | dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n"); | ||
634 | |||
635 | skip_normal_probe: | ||
636 | |||
637 | /*workaround for switched interfaces */ | ||
638 | if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { | ||
639 | if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { | ||
640 | struct usb_interface *t; | ||
641 | dev_dbg(&intf->dev,"Your device has switched interfaces.\n"); | ||
642 | |||
643 | t = control_interface; | ||
644 | control_interface = data_interface; | ||
645 | data_interface = t; | ||
646 | } else { | ||
647 | return -EINVAL; | ||
648 | } | ||
649 | } | ||
650 | |||
651 | if (usb_interface_claimed(data_interface)) { /* valid in this context */ | ||
652 | dev_dbg(&intf->dev,"The data interface isn't available\n"); | ||
653 | return -EBUSY; | ||
654 | } | ||
655 | |||
656 | |||
657 | if (data_interface->cur_altsetting->desc.bNumEndpoints < 2) | ||
658 | return -EINVAL; | ||
659 | |||
660 | epctrl = &control_interface->cur_altsetting->endpoint[0].desc; | ||
661 | epread = &data_interface->cur_altsetting->endpoint[0].desc; | ||
662 | epwrite = &data_interface->cur_altsetting->endpoint[1].desc; | ||
663 | |||
664 | |||
665 | /* workaround for switched endpoints */ | ||
666 | if ((epread->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) { | ||
667 | /* descriptors are swapped */ | ||
668 | struct usb_endpoint_descriptor *t; | ||
669 | dev_dbg(&intf->dev,"The data interface has switched endpoints\n"); | ||
670 | |||
671 | t = epread; | ||
672 | epread = epwrite; | ||
673 | epwrite = t; | ||
674 | } | ||
675 | dbg("interfaces are valid"); | ||
676 | for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); | ||
677 | |||
678 | if (minor == ACM_TTY_MINORS) { | ||
679 | err("no more free acm devices"); | ||
680 | return -ENODEV; | ||
681 | } | ||
682 | |||
683 | if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { | ||
684 | dev_dbg(&intf->dev, "out of memory (acm kmalloc)\n"); | ||
685 | goto alloc_fail; | ||
686 | } | ||
687 | memset(acm, 0, sizeof(struct acm)); | ||
688 | |||
689 | ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); | ||
690 | readsize = le16_to_cpu(epread->wMaxPacketSize); | ||
691 | acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); | ||
692 | acm->control = control_interface; | ||
693 | acm->data = data_interface; | ||
694 | acm->minor = minor; | ||
695 | acm->dev = usb_dev; | ||
696 | acm->ctrl_caps = ac_management_function; | ||
697 | acm->ctrlsize = ctrlsize; | ||
698 | acm->readsize = readsize; | ||
699 | acm->bh.func = acm_rx_tasklet; | ||
700 | acm->bh.data = (unsigned long) acm; | ||
701 | INIT_WORK(&acm->work, acm_softint, acm); | ||
702 | spin_lock_init(&acm->throttle_lock); | ||
703 | acm->ready_for_write = 1; | ||
704 | |||
705 | buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); | ||
706 | if (!buf) { | ||
707 | dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n"); | ||
708 | goto alloc_fail2; | ||
709 | } | ||
710 | acm->ctrl_buffer = buf; | ||
711 | |||
712 | buf = usb_buffer_alloc(usb_dev, readsize, GFP_KERNEL, &acm->read_dma); | ||
713 | if (!buf) { | ||
714 | dev_dbg(&intf->dev, "out of memory (read buffer alloc)\n"); | ||
715 | goto alloc_fail3; | ||
716 | } | ||
717 | acm->read_buffer = buf; | ||
718 | |||
719 | buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma); | ||
720 | if (!buf) { | ||
721 | dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); | ||
722 | goto alloc_fail4; | ||
723 | } | ||
724 | acm->write_buffer = buf; | ||
725 | |||
726 | acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); | ||
727 | if (!acm->ctrlurb) { | ||
728 | dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); | ||
729 | goto alloc_fail5; | ||
730 | } | ||
731 | acm->readurb = usb_alloc_urb(0, GFP_KERNEL); | ||
732 | if (!acm->readurb) { | ||
733 | dev_dbg(&intf->dev, "out of memory (readurb kmalloc)\n"); | ||
734 | goto alloc_fail6; | ||
735 | } | ||
736 | acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); | ||
737 | if (!acm->writeurb) { | ||
738 | dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n"); | ||
739 | goto alloc_fail7; | ||
740 | } | ||
741 | |||
742 | usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), | ||
743 | acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); | ||
744 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||
745 | acm->ctrlurb->transfer_dma = acm->ctrl_dma; | ||
746 | |||
747 | usb_fill_bulk_urb(acm->readurb, usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress), | ||
748 | acm->read_buffer, readsize, acm_read_bulk, acm); | ||
749 | acm->readurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; | ||
750 | acm->readurb->transfer_dma = acm->read_dma; | ||
751 | |||
752 | usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), | ||
753 | acm->write_buffer, acm->writesize, acm_write_bulk, acm); | ||
754 | acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; | ||
755 | acm->writeurb->transfer_dma = acm->write_dma; | ||
756 | |||
757 | dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); | ||
758 | |||
759 | acm_set_control(acm, acm->ctrlout); | ||
760 | |||
761 | acm->line.dwDTERate = cpu_to_le32(9600); | ||
762 | acm->line.bDataBits = 8; | ||
763 | acm_set_line(acm, &acm->line); | ||
764 | |||
765 | usb_driver_claim_interface(&acm_driver, data_interface, acm); | ||
766 | |||
767 | tty_register_device(acm_tty_driver, minor, &intf->dev); | ||
768 | |||
769 | acm_table[minor] = acm; | ||
770 | usb_set_intfdata (intf, acm); | ||
771 | return 0; | ||
772 | |||
773 | alloc_fail7: | ||
774 | usb_free_urb(acm->readurb); | ||
775 | alloc_fail6: | ||
776 | usb_free_urb(acm->ctrlurb); | ||
777 | alloc_fail5: | ||
778 | usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); | ||
779 | alloc_fail4: | ||
780 | usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma); | ||
781 | alloc_fail3: | ||
782 | usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); | ||
783 | alloc_fail2: | ||
784 | kfree(acm); | ||
785 | alloc_fail: | ||
786 | return -ENOMEM; | ||
787 | } | ||
788 | |||
789 | static void acm_disconnect(struct usb_interface *intf) | ||
790 | { | ||
791 | struct acm *acm = usb_get_intfdata (intf); | ||
792 | struct usb_device *usb_dev = interface_to_usbdev(intf); | ||
793 | |||
794 | if (!acm || !acm->dev) { | ||
795 | dbg("disconnect on nonexisting interface"); | ||
796 | return; | ||
797 | } | ||
798 | |||
799 | down(&open_sem); | ||
800 | acm->dev = NULL; | ||
801 | usb_set_intfdata (intf, NULL); | ||
802 | |||
803 | usb_kill_urb(acm->ctrlurb); | ||
804 | usb_kill_urb(acm->readurb); | ||
805 | usb_kill_urb(acm->writeurb); | ||
806 | |||
807 | flush_scheduled_work(); /* wait for acm_softint */ | ||
808 | |||
809 | usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); | ||
810 | usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma); | ||
811 | usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); | ||
812 | |||
813 | usb_driver_release_interface(&acm_driver, acm->data); | ||
814 | |||
815 | if (!acm->used) { | ||
816 | tty_unregister_device(acm_tty_driver, acm->minor); | ||
817 | acm_table[acm->minor] = NULL; | ||
818 | usb_free_urb(acm->ctrlurb); | ||
819 | usb_free_urb(acm->readurb); | ||
820 | usb_free_urb(acm->writeurb); | ||
821 | kfree(acm); | ||
822 | up(&open_sem); | ||
823 | return; | ||
824 | } | ||
825 | |||
826 | up(&open_sem); | ||
827 | |||
828 | if (acm->tty) | ||
829 | tty_hangup(acm->tty); | ||
830 | } | ||
831 | |||
832 | /* | ||
833 | * USB driver structure. | ||
834 | */ | ||
835 | |||
836 | static struct usb_device_id acm_ids[] = { | ||
837 | /* quirky and broken devices */ | ||
838 | { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ | ||
839 | .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ | ||
840 | }, | ||
841 | /* control interfaces with various AT-command sets */ | ||
842 | { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, | ||
843 | USB_CDC_ACM_PROTO_AT_V25TER) }, | ||
844 | { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, | ||
845 | USB_CDC_ACM_PROTO_AT_PCCA101) }, | ||
846 | { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, | ||
847 | USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, | ||
848 | { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, | ||
849 | USB_CDC_ACM_PROTO_AT_GSM) }, | ||
850 | { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, | ||
851 | USB_CDC_ACM_PROTO_AT_3G ) }, | ||
852 | { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, | ||
853 | USB_CDC_ACM_PROTO_AT_CDMA) }, | ||
854 | |||
855 | /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ | ||
856 | { } | ||
857 | }; | ||
858 | |||
859 | MODULE_DEVICE_TABLE (usb, acm_ids); | ||
860 | |||
861 | static struct usb_driver acm_driver = { | ||
862 | .owner = THIS_MODULE, | ||
863 | .name = "cdc_acm", | ||
864 | .probe = acm_probe, | ||
865 | .disconnect = acm_disconnect, | ||
866 | .id_table = acm_ids, | ||
867 | }; | ||
868 | |||
869 | /* | ||
870 | * TTY driver structures. | ||
871 | */ | ||
872 | |||
873 | static struct tty_operations acm_ops = { | ||
874 | .open = acm_tty_open, | ||
875 | .close = acm_tty_close, | ||
876 | .write = acm_tty_write, | ||
877 | .write_room = acm_tty_write_room, | ||
878 | .ioctl = acm_tty_ioctl, | ||
879 | .throttle = acm_tty_throttle, | ||
880 | .unthrottle = acm_tty_unthrottle, | ||
881 | .chars_in_buffer = acm_tty_chars_in_buffer, | ||
882 | .break_ctl = acm_tty_break_ctl, | ||
883 | .set_termios = acm_tty_set_termios, | ||
884 | .tiocmget = acm_tty_tiocmget, | ||
885 | .tiocmset = acm_tty_tiocmset, | ||
886 | }; | ||
887 | |||
888 | /* | ||
889 | * Init / exit. | ||
890 | */ | ||
891 | |||
892 | static int __init acm_init(void) | ||
893 | { | ||
894 | int retval; | ||
895 | acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); | ||
896 | if (!acm_tty_driver) | ||
897 | return -ENOMEM; | ||
898 | acm_tty_driver->owner = THIS_MODULE, | ||
899 | acm_tty_driver->driver_name = "acm", | ||
900 | acm_tty_driver->name = "ttyACM", | ||
901 | acm_tty_driver->devfs_name = "usb/acm/", | ||
902 | acm_tty_driver->major = ACM_TTY_MAJOR, | ||
903 | acm_tty_driver->minor_start = 0, | ||
904 | acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, | ||
905 | acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, | ||
906 | acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, | ||
907 | acm_tty_driver->init_termios = tty_std_termios; | ||
908 | acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
909 | tty_set_operations(acm_tty_driver, &acm_ops); | ||
910 | |||
911 | retval = tty_register_driver(acm_tty_driver); | ||
912 | if (retval) { | ||
913 | put_tty_driver(acm_tty_driver); | ||
914 | return retval; | ||
915 | } | ||
916 | |||
917 | retval = usb_register(&acm_driver); | ||
918 | if (retval) { | ||
919 | tty_unregister_driver(acm_tty_driver); | ||
920 | put_tty_driver(acm_tty_driver); | ||
921 | return retval; | ||
922 | } | ||
923 | |||
924 | info(DRIVER_VERSION ":" DRIVER_DESC); | ||
925 | |||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | static void __exit acm_exit(void) | ||
930 | { | ||
931 | usb_deregister(&acm_driver); | ||
932 | tty_unregister_driver(acm_tty_driver); | ||
933 | put_tty_driver(acm_tty_driver); | ||
934 | } | ||
935 | |||
936 | module_init(acm_init); | ||
937 | module_exit(acm_exit); | ||
938 | |||
939 | MODULE_AUTHOR( DRIVER_AUTHOR ); | ||
940 | MODULE_DESCRIPTION( DRIVER_DESC ); | ||
941 | MODULE_LICENSE("GPL"); | ||
942 | |||