blob: 63c34043b4d91e0bfb2864672704be838f518fef [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
David Kubicek61a87ad2005-11-01 18:51:34 +01009 * Copyright (c) 2005 David Kubicek <dave@awk.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * USB Abstract Control Model driver for USB modems and ISDN adapters
12 *
13 * Sponsored by SuSE
14 *
15 * ChangeLog:
16 * v0.9 - thorough cleaning, URBification, almost a rewrite
17 * v0.10 - some more cleanups
18 * v0.11 - fixed flow control, read error doesn't stop reads
19 * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
20 * v0.13 - added termios, added hangup
21 * v0.14 - sized down struct acm
22 * v0.15 - fixed flow control again - characters could be lost
23 * v0.16 - added code for modems with swapped data and control interfaces
24 * v0.17 - added new style probing
25 * v0.18 - fixed new style probing for devices with more configurations
26 * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
27 * v0.20 - switched to probing on interface (rather than device) class
28 * v0.21 - revert to probing on device for devices with multiple configs
29 * v0.22 - probe only the control interface. if usbcore doesn't choose the
30 * config we want, sysadmin changes bConfigurationValue in sysfs.
31 * v0.23 - use softirq for rx processing, as needed by tty layer
32 * v0.24 - change probe method to evaluate CDC union descriptor
David Kubicek61a87ad2005-11-01 18:51:34 +010033 * v0.25 - downstream tasks paralelized to maximize throughput
David Engrafe4cf3aa2008-03-20 10:01:34 +010034 * v0.26 - multiple write urbs, writesize increased
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 */
36
37/*
38 * This program is free software; you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation; either version 2 of the License, or
41 * (at your option) any later version.
42 *
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 * GNU General Public License for more details.
47 *
48 * You should have received a copy of the GNU General Public License
49 * along with this program; if not, write to the Free Software
50 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
51 */
52
53#undef DEBUG
54
55#include <linux/kernel.h>
56#include <linux/errno.h>
57#include <linux/init.h>
58#include <linux/slab.h>
59#include <linux/tty.h>
60#include <linux/tty_driver.h>
61#include <linux/tty_flip.h>
62#include <linux/module.h>
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010063#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#include <asm/uaccess.h>
65#include <linux/usb.h>
David Brownella8c28f22006-06-13 09:57:47 -070066#include <linux/usb/cdc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <asm/byteorder.h>
68#include <asm/unaligned.h>
David Kubicek61a87ad2005-11-01 18:51:34 +010069#include <linux/list.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71#include "cdc-acm.h"
72
73/*
74 * Version Information
75 */
David Engrafe4cf3aa2008-03-20 10:01:34 +010076#define DRIVER_VERSION "v0.26"
David Kubicek61a87ad2005-11-01 18:51:34 +010077#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
79
80static struct usb_driver acm_driver;
81static struct tty_driver *acm_tty_driver;
82static struct acm *acm_table[ACM_TTY_MINORS];
83
Arjan van de Ven4186ecf2006-01-11 15:55:29 +010084static DEFINE_MUTEX(open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
86#define ACM_READY(acm) (acm && acm->dev && acm->used)
87
88/*
89 * Functions for ACM control messages.
90 */
91
92static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
93{
94 int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
95 request, USB_RT_ACM, value,
96 acm->control->altsetting[0].desc.bInterfaceNumber,
97 buf, len, 5000);
98 dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
99 return retval < 0 ? retval : 0;
100}
101
102/* devices aren't required to support these requests.
103 * the cdc acm descriptor tells whether they do...
104 */
105#define acm_set_control(acm, control) \
106 acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
107#define acm_set_line(acm, line) \
108 acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
109#define acm_send_break(acm, ms) \
110 acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
111
112/*
Oliver Neukum884b6002005-04-21 21:28:02 +0200113 * Write buffer management.
114 * All of these assume proper locks taken by the caller.
115 */
116
117static int acm_wb_alloc(struct acm *acm)
118{
119 int i, wbn;
120 struct acm_wb *wb;
121
David Engrafe4cf3aa2008-03-20 10:01:34 +0100122 wbn = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200123 i = 0;
124 for (;;) {
125 wb = &acm->wb[wbn];
126 if (!wb->use) {
127 wb->use = 1;
128 return wbn;
129 }
Oliver Neukum86478942006-05-13 22:50:47 +0200130 wbn = (wbn + 1) % ACM_NW;
131 if (++i >= ACM_NW)
Oliver Neukum884b6002005-04-21 21:28:02 +0200132 return -1;
133 }
134}
135
Oliver Neukum884b6002005-04-21 21:28:02 +0200136static int acm_wb_is_avail(struct acm *acm)
137{
138 int i, n;
139
Oliver Neukum86478942006-05-13 22:50:47 +0200140 n = ACM_NW;
141 for (i = 0; i < ACM_NW; i++) {
142 n -= acm->wb[i].use;
Oliver Neukum884b6002005-04-21 21:28:02 +0200143 }
144 return n;
145}
146
147static inline int acm_wb_is_used(struct acm *acm, int wbn)
148{
149 return acm->wb[wbn].use;
150}
151
152/*
153 * Finish write.
154 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100155static void acm_write_done(struct acm *acm, struct acm_wb *wb)
Oliver Neukum884b6002005-04-21 21:28:02 +0200156{
157 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200158
159 spin_lock_irqsave(&acm->write_lock, flags);
160 acm->write_ready = 1;
David Engrafe4cf3aa2008-03-20 10:01:34 +0100161 wb->use = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200162 spin_unlock_irqrestore(&acm->write_lock, flags);
163}
164
165/*
166 * Poke write.
167 */
David Engrafe4cf3aa2008-03-20 10:01:34 +0100168static int acm_write_start(struct acm *acm, int wbn)
Oliver Neukum884b6002005-04-21 21:28:02 +0200169{
170 unsigned long flags;
Oliver Neukum884b6002005-04-21 21:28:02 +0200171 struct acm_wb *wb;
172 int rc;
173
174 spin_lock_irqsave(&acm->write_lock, flags);
175 if (!acm->dev) {
176 spin_unlock_irqrestore(&acm->write_lock, flags);
177 return -ENODEV;
178 }
179
180 if (!acm->write_ready) {
181 spin_unlock_irqrestore(&acm->write_lock, flags);
182 return 0; /* A white lie */
183 }
184
Oliver Neukum884b6002005-04-21 21:28:02 +0200185 if (!acm_wb_is_used(acm, wbn)) {
186 spin_unlock_irqrestore(&acm->write_lock, flags);
187 return 0;
188 }
189 wb = &acm->wb[wbn];
190
David Engrafe4cf3aa2008-03-20 10:01:34 +0100191 if(acm_wb_is_avail(acm) <= 1)
192 acm->write_ready = 0;
Oliver Neukum884b6002005-04-21 21:28:02 +0200193 spin_unlock_irqrestore(&acm->write_lock, flags);
194
David Engrafe4cf3aa2008-03-20 10:01:34 +0100195 wb->urb->transfer_buffer = wb->buf;
196 wb->urb->transfer_dma = wb->dmah;
197 wb->urb->transfer_buffer_length = wb->len;
198 wb->urb->dev = acm->dev;
Oliver Neukum884b6002005-04-21 21:28:02 +0200199
David Engrafe4cf3aa2008-03-20 10:01:34 +0100200 if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200201 dbg("usb_submit_urb(write bulk) failed: %d", rc);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100202 acm_write_done(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200203 }
204 return rc;
205}
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100206/*
207 * attributes exported through sysfs
208 */
209static ssize_t show_caps
210(struct device *dev, struct device_attribute *attr, char *buf)
211{
212 struct usb_interface *intf = to_usb_interface(dev);
213 struct acm *acm = usb_get_intfdata(intf);
Oliver Neukum884b6002005-04-21 21:28:02 +0200214
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100215 return sprintf(buf, "%d", acm->ctrl_caps);
216}
217static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
218
219static ssize_t show_country_codes
220(struct device *dev, struct device_attribute *attr, char *buf)
221{
222 struct usb_interface *intf = to_usb_interface(dev);
223 struct acm *acm = usb_get_intfdata(intf);
224
225 memcpy(buf, acm->country_codes, acm->country_code_size);
226 return acm->country_code_size;
227}
228
229static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
230
231static ssize_t show_country_rel_date
232(struct device *dev, struct device_attribute *attr, char *buf)
233{
234 struct usb_interface *intf = to_usb_interface(dev);
235 struct acm *acm = usb_get_intfdata(intf);
236
237 return sprintf(buf, "%d", acm->country_rel_date);
238}
239
240static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
Oliver Neukum884b6002005-04-21 21:28:02 +0200241/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 * Interrupt handlers for various ACM device responses
243 */
244
245/* control interface reports status changes with "interrupt" transfers */
David Howells7d12e782006-10-05 14:55:46 +0100246static void acm_ctrl_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247{
248 struct acm *acm = urb->context;
249 struct usb_cdc_notification *dr = urb->transfer_buffer;
250 unsigned char *data;
251 int newctrl;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700252 int retval;
253 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700255 switch (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 case 0:
257 /* success */
258 break;
259 case -ECONNRESET:
260 case -ENOENT:
261 case -ESHUTDOWN:
262 /* this urb is terminated, clean up */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800263 dbg("%s - urb shutting down with status: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 return;
265 default:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800266 dbg("%s - nonzero urb status received: %d", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 goto exit;
268 }
269
270 if (!ACM_READY(acm))
271 goto exit;
272
273 data = (unsigned char *)(dr + 1);
274 switch (dr->bNotificationType) {
275
276 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
277
278 dbg("%s network", dr->wValue ? "connected to" : "disconnected from");
279 break;
280
281 case USB_CDC_NOTIFY_SERIAL_STATE:
282
Harvey Harrisona5abdea2008-04-29 01:03:40 -0700283 newctrl = get_unaligned_le16(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
285 if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
286 dbg("calling hangup");
287 tty_hangup(acm->tty);
288 }
289
290 acm->ctrlin = newctrl;
291
292 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
293 acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
294 acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
295 acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
296 acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
297
298 break;
299
300 default:
301 dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
302 dr->bNotificationType, dr->wIndex,
303 dr->wLength, data[0], data[1]);
304 break;
305 }
306exit:
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700307 retval = usb_submit_urb (urb, GFP_ATOMIC);
308 if (retval)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 err ("%s - usb_submit_urb failed with result %d",
Harvey Harrison441b62c2008-03-03 16:08:34 -0800310 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311}
312
313/* data interface returns incoming bytes, or we got unthrottled */
David Howells7d12e782006-10-05 14:55:46 +0100314static void acm_read_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{
David Kubicek61a87ad2005-11-01 18:51:34 +0100316 struct acm_rb *buf;
317 struct acm_ru *rcv = urb->context;
318 struct acm *acm = rcv->instance;
Oliver Neukum86478942006-05-13 22:50:47 +0200319 int status = urb->status;
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700320
321 dbg("Entering acm_read_bulk with status %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323 if (!ACM_READY(acm))
324 return;
325
Oliver Neukum86478942006-05-13 22:50:47 +0200326 if (status)
Joe Perches898eb712007-10-18 03:06:30 -0700327 dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
David Kubicek61a87ad2005-11-01 18:51:34 +0100329 buf = rcv->buffer;
330 buf->size = urb->actual_length;
331
Oliver Neukum86478942006-05-13 22:50:47 +0200332 if (likely(status == 0)) {
333 spin_lock(&acm->read_lock);
334 list_add_tail(&rcv->list, &acm->spare_read_urbs);
335 list_add_tail(&buf->list, &acm->filled_read_bufs);
336 spin_unlock(&acm->read_lock);
337 } else {
338 /* we drop the buffer due to an error */
339 spin_lock(&acm->read_lock);
340 list_add_tail(&rcv->list, &acm->spare_read_urbs);
341 list_add(&buf->list, &acm->spare_read_bufs);
342 spin_unlock(&acm->read_lock);
343 /* nevertheless the tasklet must be kicked unconditionally
344 so the queue cannot dry up */
345 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100346 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347}
348
349static void acm_rx_tasklet(unsigned long _acm)
350{
351 struct acm *acm = (void *)_acm;
David Kubicek61a87ad2005-11-01 18:51:34 +0100352 struct acm_rb *buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 struct tty_struct *tty = acm->tty;
David Kubicek61a87ad2005-11-01 18:51:34 +0100354 struct acm_ru *rcv;
Jarek Poplawski762f0072006-10-06 07:23:11 +0200355 unsigned long flags;
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100356 unsigned char throttled;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 dbg("Entering acm_rx_tasklet");
358
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100359 if (!ACM_READY(acm))
360 return;
361
Oliver Neukum834dbca2007-03-06 10:47:04 +0100362 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100363 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100364 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100365 if (throttled)
David Kubicek61a87ad2005-11-01 18:51:34 +0100366 return;
367
368next_buffer:
Jarek Poplawski762f0072006-10-06 07:23:11 +0200369 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100370 if (list_empty(&acm->filled_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200371 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100372 goto urbs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 }
David Kubicek61a87ad2005-11-01 18:51:34 +0100374 buf = list_entry(acm->filled_read_bufs.next,
375 struct acm_rb, list);
376 list_del(&buf->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200377 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100378
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200379 dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100380
Alan Cox33f0f882006-01-09 20:54:13 -0800381 tty_buffer_request_room(tty, buf->size);
Oliver Neukum834dbca2007-03-06 10:47:04 +0100382 spin_lock_irqsave(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100383 throttled = acm->throttle;
Oliver Neukum834dbca2007-03-06 10:47:04 +0100384 spin_unlock_irqrestore(&acm->throttle_lock, flags);
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100385 if (!throttled)
Alan Cox33f0f882006-01-09 20:54:13 -0800386 tty_insert_flip_string(tty, buf->base, buf->size);
David Kubicek61a87ad2005-11-01 18:51:34 +0100387 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100389 if (throttled) {
390 dbg("Throttling noticed");
Jarek Poplawski762f0072006-10-06 07:23:11 +0200391 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100392 list_add(&buf->list, &acm->filled_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200393 spin_unlock_irqrestore(&acm->read_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 return;
395 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
Jarek Poplawski762f0072006-10-06 07:23:11 +0200397 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100398 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200399 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100400 goto next_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
David Kubicek61a87ad2005-11-01 18:51:34 +0100402urbs:
403 while (!list_empty(&acm->spare_read_bufs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200404 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100405 if (list_empty(&acm->spare_read_urbs)) {
Jarek Poplawski762f0072006-10-06 07:23:11 +0200406 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100407 return;
408 }
409 rcv = list_entry(acm->spare_read_urbs.next,
410 struct acm_ru, list);
411 list_del(&rcv->list);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200412 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100413
414 buf = list_entry(acm->spare_read_bufs.next,
415 struct acm_rb, list);
416 list_del(&buf->list);
417
418 rcv->buffer = buf;
419
420 usb_fill_bulk_urb(rcv->urb, acm->dev,
421 acm->rx_endpoint,
422 buf->base,
423 acm->readsize,
424 acm_read_bulk, rcv);
425 rcv->urb->transfer_dma = buf->dma;
426 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
427
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200428 dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
David Kubicek61a87ad2005-11-01 18:51:34 +0100429
430 /* This shouldn't kill the driver as unsuccessful URBs are returned to the
431 free-urbs-pool and resubmited ASAP */
432 if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
433 list_add(&buf->list, &acm->spare_read_bufs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200434 spin_lock_irqsave(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100435 list_add(&rcv->list, &acm->spare_read_urbs);
Jarek Poplawski762f0072006-10-06 07:23:11 +0200436 spin_unlock_irqrestore(&acm->read_lock, flags);
David Kubicek61a87ad2005-11-01 18:51:34 +0100437 return;
438 }
439 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440}
441
442/* data interface wrote those outgoing bytes */
David Howells7d12e782006-10-05 14:55:46 +0100443static void acm_write_bulk(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444{
David Engrafe4cf3aa2008-03-20 10:01:34 +0100445 struct acm *acm;
Ming Leicdc97792008-02-24 18:41:47 +0800446 struct acm_wb *wb = urb->context;
Oliver Neukum884b6002005-04-21 21:28:02 +0200447
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200448 dbg("Entering acm_write_bulk with status %d", urb->status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
David Engrafe4cf3aa2008-03-20 10:01:34 +0100450 acm = wb->instance;
451 acm_write_done(acm, wb);
Oliver Neukum884b6002005-04-21 21:28:02 +0200452 if (ACM_READY(acm))
453 schedule_work(&acm->work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454}
455
David Howellsc4028952006-11-22 14:57:56 +0000456static void acm_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
David Howellsc4028952006-11-22 14:57:56 +0000458 struct acm *acm = container_of(work, struct acm, work);
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200459 dbg("Entering acm_softint.");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460
461 if (!ACM_READY(acm))
462 return;
463 tty_wakeup(acm->tty);
464}
465
466/*
467 * TTY handlers
468 */
469
470static int acm_tty_open(struct tty_struct *tty, struct file *filp)
471{
472 struct acm *acm;
473 int rv = -EINVAL;
David Kubicek61a87ad2005-11-01 18:51:34 +0100474 int i;
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200475 dbg("Entering acm_tty_open.");
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100476
477 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
479 acm = acm_table[tty->index];
480 if (!acm || !acm->dev)
481 goto err_out;
482 else
483 rv = 0;
484
David Engraf28d1dfa2008-03-20 10:53:52 +0100485 set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 tty->driver_data = acm;
487 acm->tty = tty;
488
David Kubicek61a87ad2005-11-01 18:51:34 +0100489 /* force low_latency on so that our tty_push actually forces the data through,
490 otherwise it is scheduled, and with high data rates data can get lost. */
491 tty->low_latency = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Oliver Neukum94409cc2008-02-11 15:22:29 +0100493 if (usb_autopm_get_interface(acm->control) < 0)
494 goto early_bail;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200495
496 mutex_lock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 if (acm->used++) {
Oliver Neukum1365baf2007-10-12 17:24:28 +0200498 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 goto done;
500 }
501
Oliver Neukum1365baf2007-10-12 17:24:28 +0200502
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 acm->ctrlurb->dev = acm->dev;
504 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
505 dbg("usb_submit_urb(ctrl irq) failed");
506 goto bail_out;
507 }
508
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100509 if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
510 (acm->ctrl_caps & USB_CDC_CAP_LINE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 goto full_bailout;
512
David Kubicek61a87ad2005-11-01 18:51:34 +0100513 INIT_LIST_HEAD(&acm->spare_read_urbs);
514 INIT_LIST_HEAD(&acm->spare_read_bufs);
515 INIT_LIST_HEAD(&acm->filled_read_bufs);
Oliver Neukum86478942006-05-13 22:50:47 +0200516 for (i = 0; i < acm->rx_buflimit; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100517 list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
518 }
Oliver Neukum86478942006-05-13 22:50:47 +0200519 for (i = 0; i < acm->rx_buflimit; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +0100520 list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
521 }
522
Oliver Neukumca79b7b2007-02-12 08:41:35 +0100523 acm->throttle = 0;
524
David Kubicek61a87ad2005-11-01 18:51:34 +0100525 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
527done:
528err_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200529 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100530 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return rv;
532
533full_bailout:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 usb_kill_urb(acm->ctrlurb);
535bail_out:
Oliver Neukum1365baf2007-10-12 17:24:28 +0200536 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 acm->used--;
Oliver Neukum1365baf2007-10-12 17:24:28 +0200538 mutex_unlock(&acm->mutex);
Oliver Neukum94409cc2008-02-11 15:22:29 +0100539early_bail:
540 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 return -EIO;
542}
543
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700544static void acm_tty_unregister(struct acm *acm)
545{
Oliver Neukum86478942006-05-13 22:50:47 +0200546 int i,nr;
David Kubicek61a87ad2005-11-01 18:51:34 +0100547
Oliver Neukum86478942006-05-13 22:50:47 +0200548 nr = acm->rx_buflimit;
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700549 tty_unregister_device(acm_tty_driver, acm->minor);
550 usb_put_intf(acm->control);
551 acm_table[acm->minor] = NULL;
552 usb_free_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100553 for (i = 0; i < ACM_NW; i++)
554 usb_free_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200555 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100556 usb_free_urb(acm->ru[i].urb);
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100557 kfree(acm->country_codes);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700558 kfree(acm);
559}
560
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561static void acm_tty_close(struct tty_struct *tty, struct file *filp)
562{
563 struct acm *acm = tty->driver_data;
Oliver Neukum86478942006-05-13 22:50:47 +0200564 int i,nr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
566 if (!acm || !acm->used)
567 return;
568
Oliver Neukum86478942006-05-13 22:50:47 +0200569 nr = acm->rx_buflimit;
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100570 mutex_lock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 if (!--acm->used) {
572 if (acm->dev) {
573 acm_set_control(acm, acm->ctrlout = 0);
574 usb_kill_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100575 for (i = 0; i < ACM_NW; i++)
576 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum86478942006-05-13 22:50:47 +0200577 for (i = 0; i < nr; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +0100578 usb_kill_urb(acm->ru[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200579 usb_autopm_put_interface(acm->control);
brian@murphy.dk83ef3442005-06-29 16:53:29 -0700580 } else
581 acm_tty_unregister(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
Arjan van de Ven4186ecf2006-01-11 15:55:29 +0100583 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584}
585
586static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
587{
588 struct acm *acm = tty->driver_data;
589 int stat;
Oliver Neukum884b6002005-04-21 21:28:02 +0200590 unsigned long flags;
591 int wbn;
592 struct acm_wb *wb;
593
Oliver Neukum3dd2ae82006-06-23 09:14:17 +0200594 dbg("Entering acm_tty_write to write %d bytes,", count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596 if (!ACM_READY(acm))
597 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 if (!count)
599 return 0;
600
Oliver Neukum884b6002005-04-21 21:28:02 +0200601 spin_lock_irqsave(&acm->write_lock, flags);
602 if ((wbn = acm_wb_alloc(acm)) < 0) {
603 spin_unlock_irqrestore(&acm->write_lock, flags);
Oliver Neukum884b6002005-04-21 21:28:02 +0200604 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 }
Oliver Neukum884b6002005-04-21 21:28:02 +0200606 wb = &acm->wb[wbn];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
Oliver Neukum884b6002005-04-21 21:28:02 +0200608 count = (count > acm->writesize) ? acm->writesize : count;
609 dbg("Get %d bytes...", count);
610 memcpy(wb->buf, buf, count);
611 wb->len = count;
612 spin_unlock_irqrestore(&acm->write_lock, flags);
613
David Engrafe4cf3aa2008-03-20 10:01:34 +0100614 if ((stat = acm_write_start(acm, wbn)) < 0)
Oliver Neukum884b6002005-04-21 21:28:02 +0200615 return stat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 return count;
617}
618
619static int acm_tty_write_room(struct tty_struct *tty)
620{
621 struct acm *acm = tty->driver_data;
622 if (!ACM_READY(acm))
623 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200624 /*
625 * Do not let the line discipline to know that we have a reserve,
626 * or it might get too enthusiastic.
627 */
628 return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629}
630
631static int acm_tty_chars_in_buffer(struct tty_struct *tty)
632{
633 struct acm *acm = tty->driver_data;
634 if (!ACM_READY(acm))
635 return -EINVAL;
Oliver Neukum884b6002005-04-21 21:28:02 +0200636 /*
637 * This is inaccurate (overcounts), but it works.
638 */
Oliver Neukum86478942006-05-13 22:50:47 +0200639 return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640}
641
642static void acm_tty_throttle(struct tty_struct *tty)
643{
644 struct acm *acm = tty->driver_data;
645 if (!ACM_READY(acm))
646 return;
647 spin_lock_bh(&acm->throttle_lock);
648 acm->throttle = 1;
649 spin_unlock_bh(&acm->throttle_lock);
650}
651
652static void acm_tty_unthrottle(struct tty_struct *tty)
653{
654 struct acm *acm = tty->driver_data;
655 if (!ACM_READY(acm))
656 return;
657 spin_lock_bh(&acm->throttle_lock);
658 acm->throttle = 0;
659 spin_unlock_bh(&acm->throttle_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100660 tasklet_schedule(&acm->urb_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661}
662
663static void acm_tty_break_ctl(struct tty_struct *tty, int state)
664{
665 struct acm *acm = tty->driver_data;
666 if (!ACM_READY(acm))
667 return;
668 if (acm_send_break(acm, state ? 0xffff : 0))
669 dbg("send break failed");
670}
671
672static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
673{
674 struct acm *acm = tty->driver_data;
675
676 if (!ACM_READY(acm))
677 return -EINVAL;
678
679 return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
680 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
681 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
682 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
683 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
684 TIOCM_CTS;
685}
686
687static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
688 unsigned int set, unsigned int clear)
689{
690 struct acm *acm = tty->driver_data;
691 unsigned int newctrl;
692
693 if (!ACM_READY(acm))
694 return -EINVAL;
695
696 newctrl = acm->ctrlout;
697 set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
698 clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
699
700 newctrl = (newctrl & ~clear) | set;
701
702 if (acm->ctrlout == newctrl)
703 return 0;
704 return acm_set_control(acm, acm->ctrlout = newctrl);
705}
706
707static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
708{
709 struct acm *acm = tty->driver_data;
710
711 if (!ACM_READY(acm))
712 return -EINVAL;
713
714 return -ENOIOCTLCMD;
715}
716
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100717static const __u32 acm_tty_speed[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 0, 50, 75, 110, 134, 150, 200, 300, 600,
719 1200, 1800, 2400, 4800, 9600, 19200, 38400,
720 57600, 115200, 230400, 460800, 500000, 576000,
721 921600, 1000000, 1152000, 1500000, 2000000,
722 2500000, 3000000, 3500000, 4000000
723};
724
Arjan van de Ven4c4c9432005-11-29 09:43:42 +0100725static const __u8 acm_tty_size[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 5, 6, 7, 8
727};
728
Alan Cox606d0992006-12-08 02:38:45 -0800729static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730{
731 struct acm *acm = tty->driver_data;
Alan Cox606d0992006-12-08 02:38:45 -0800732 struct ktermios *termios = tty->termios;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 struct usb_cdc_line_coding newline;
734 int newctrl = acm->ctrlout;
735
736 if (!ACM_READY(acm))
737 return;
738
739 newline.dwDTERate = cpu_to_le32p(acm_tty_speed +
740 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0));
741 newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
742 newline.bParityType = termios->c_cflag & PARENB ?
743 (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
744 newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
745
746 acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
747
748 if (!newline.dwDTERate) {
749 newline.dwDTERate = acm->line.dwDTERate;
750 newctrl &= ~ACM_CTRL_DTR;
751 } else newctrl |= ACM_CTRL_DTR;
752
753 if (newctrl != acm->ctrlout)
754 acm_set_control(acm, acm->ctrlout = newctrl);
755
756 if (memcmp(&acm->line, &newline, sizeof newline)) {
757 memcpy(&acm->line, &newline, sizeof newline);
758 dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
759 newline.bCharFormat, newline.bParityType,
760 newline.bDataBits);
761 acm_set_line(acm, &acm->line);
762 }
763}
764
765/*
766 * USB probe and disconnect routines.
767 */
768
Oliver Neukum884b6002005-04-21 21:28:02 +0200769/* Little helper: write buffers free */
770static void acm_write_buffers_free(struct acm *acm)
771{
772 int i;
773 struct acm_wb *wb;
774
Oliver Neukum86478942006-05-13 22:50:47 +0200775 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200776 usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
777 }
778}
779
780/* Little helper: write buffers allocate */
781static int acm_write_buffers_alloc(struct acm *acm)
782{
783 int i;
784 struct acm_wb *wb;
785
Oliver Neukum86478942006-05-13 22:50:47 +0200786 for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
Oliver Neukum884b6002005-04-21 21:28:02 +0200787 wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
788 &wb->dmah);
789 if (!wb->buf) {
790 while (i != 0) {
791 --i;
792 --wb;
793 usb_buffer_free(acm->dev, acm->writesize,
794 wb->buf, wb->dmah);
795 }
796 return -ENOMEM;
797 }
798 }
799 return 0;
800}
801
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802static int acm_probe (struct usb_interface *intf,
803 const struct usb_device_id *id)
804{
805 struct usb_cdc_union_desc *union_header = NULL;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100806 struct usb_cdc_country_functional_desc *cfd = NULL;
David Brownellc6dbf552008-04-13 14:00:44 -0700807 unsigned char *buffer = intf->altsetting->extra;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 int buflen = intf->altsetting->extralen;
809 struct usb_interface *control_interface;
810 struct usb_interface *data_interface;
811 struct usb_endpoint_descriptor *epctrl;
812 struct usb_endpoint_descriptor *epread;
813 struct usb_endpoint_descriptor *epwrite;
814 struct usb_device *usb_dev = interface_to_usbdev(intf);
815 struct acm *acm;
816 int minor;
817 int ctrlsize,readsize;
818 u8 *buf;
819 u8 ac_management_function = 0;
820 u8 call_management_function = 0;
821 int call_interface_num = -1;
822 int data_interface_num;
823 unsigned long quirks;
Oliver Neukum86478942006-05-13 22:50:47 +0200824 int num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100825 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Oliver Neukum86478942006-05-13 22:50:47 +0200827 /* normal quirks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 quirks = (unsigned long)id->driver_info;
Oliver Neukum86478942006-05-13 22:50:47 +0200829 num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
830
831 /* handle quirks deadly to normal probing*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 if (quirks == NO_UNION_NORMAL) {
833 data_interface = usb_ifnum_to_if(usb_dev, 1);
834 control_interface = usb_ifnum_to_if(usb_dev, 0);
835 goto skip_normal_probe;
836 }
837
838 /* normal probing*/
839 if (!buffer) {
Joe Perches898eb712007-10-18 03:06:30 -0700840 err("Weird descriptor references\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return -EINVAL;
842 }
843
844 if (!buflen) {
845 if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
Joe Perches898eb712007-10-18 03:06:30 -0700846 dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 buflen = intf->cur_altsetting->endpoint->extralen;
848 buffer = intf->cur_altsetting->endpoint->extra;
849 } else {
850 err("Zero length descriptor references\n");
851 return -EINVAL;
852 }
853 }
854
855 while (buflen > 0) {
856 if (buffer [1] != USB_DT_CS_INTERFACE) {
857 err("skipping garbage\n");
858 goto next_desc;
859 }
860
861 switch (buffer [2]) {
862 case USB_CDC_UNION_TYPE: /* we've found it */
863 if (union_header) {
864 err("More than one union descriptor, skipping ...");
865 goto next_desc;
866 }
867 union_header = (struct usb_cdc_union_desc *)
868 buffer;
869 break;
Oliver Neukumc4cabd22007-02-27 15:28:55 +0100870 case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
871 cfd = (struct usb_cdc_country_functional_desc *)buffer;
872 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 case USB_CDC_HEADER_TYPE: /* maybe check version */
874 break; /* for now we ignore it */
875 case USB_CDC_ACM_TYPE:
876 ac_management_function = buffer[3];
877 break;
878 case USB_CDC_CALL_MANAGEMENT_TYPE:
879 call_management_function = buffer[3];
880 call_interface_num = buffer[4];
881 if ((call_management_function & 3) != 3)
882 err("This device cannot do calls on its own. It is no modem.");
883 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 default:
David Brownellc6dbf552008-04-13 14:00:44 -0700885 /* there are LOTS more CDC descriptors that
886 * could legitimately be found here.
887 */
888 dev_dbg(&intf->dev, "Ignoring descriptor: "
889 "type %02x, length %d\n",
890 buffer[2], buffer[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 break;
892 }
893next_desc:
894 buflen -= buffer[0];
895 buffer += buffer[0];
896 }
897
898 if (!union_header) {
899 if (call_interface_num > 0) {
Joe Perches898eb712007-10-18 03:06:30 -0700900 dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
902 control_interface = intf;
903 } else {
Joe Perches898eb712007-10-18 03:06:30 -0700904 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 return -ENODEV;
906 }
907 } else {
908 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
909 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
910 if (!control_interface || !data_interface) {
Joe Perches898eb712007-10-18 03:06:30 -0700911 dev_dbg(&intf->dev,"no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 return -ENODEV;
913 }
914 }
915
916 if (data_interface_num != call_interface_num)
Joe Perchesdc0d5c12007-12-17 11:40:18 -0800917 dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
919skip_normal_probe:
920
921 /*workaround for switched interfaces */
922 if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
923 if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
924 struct usb_interface *t;
Joe Perches898eb712007-10-18 03:06:30 -0700925 dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
927 t = control_interface;
928 control_interface = data_interface;
929 data_interface = t;
930 } else {
931 return -EINVAL;
932 }
933 }
Alan Stern74da5d62007-08-02 13:29:10 -0400934
935 /* Accept probe requests only for the control interface */
936 if (intf != control_interface)
937 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
939 if (usb_interface_claimed(data_interface)) { /* valid in this context */
Joe Perches898eb712007-10-18 03:06:30 -0700940 dev_dbg(&intf->dev,"The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 return -EBUSY;
942 }
943
944
945 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
946 return -EINVAL;
947
948 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
949 epread = &data_interface->cur_altsetting->endpoint[0].desc;
950 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
951
952
953 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -0300954 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 /* descriptors are swapped */
956 struct usb_endpoint_descriptor *t;
Joe Perches898eb712007-10-18 03:06:30 -0700957 dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
959 t = epread;
960 epread = epwrite;
961 epwrite = t;
962 }
963 dbg("interfaces are valid");
964 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
965
966 if (minor == ACM_TTY_MINORS) {
967 err("no more free acm devices");
968 return -ENODEV;
969 }
970
Oliver Neukum46f116e2005-10-24 22:42:35 +0200971 if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
Joe Perches898eb712007-10-18 03:06:30 -0700972 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 goto alloc_fail;
974 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Oliver Neukum86478942006-05-13 22:50:47 +0200977 readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100978 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 acm->control = control_interface;
980 acm->data = data_interface;
981 acm->minor = minor;
982 acm->dev = usb_dev;
983 acm->ctrl_caps = ac_management_function;
984 acm->ctrlsize = ctrlsize;
985 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +0200986 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100987 acm->urb_task.func = acm_rx_tasklet;
988 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +0000989 INIT_WORK(&acm->work, acm_softint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +0200991 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100992 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf2007-10-12 17:24:28 +0200993 mutex_init(&acm->mutex);
Oliver Neukum884b6002005-04-21 21:28:02 +0200994 acm->write_ready = 1;
David Kubicek61a87ad2005-11-01 18:51:34 +0100995 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
997 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
998 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -0700999 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 goto alloc_fail2;
1001 }
1002 acm->ctrl_buffer = buf;
1003
Oliver Neukum884b6002005-04-21 21:28:02 +02001004 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001005 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 goto alloc_fail4;
1007 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
1009 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1010 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001011 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 goto alloc_fail5;
1013 }
Oliver Neukum86478942006-05-13 22:50:47 +02001014 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001015 struct acm_ru *rcv = &(acm->ru[i]);
1016
1017 if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
Joe Perches898eb712007-10-18 03:06:30 -07001018 dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001019 goto alloc_fail7;
1020 }
1021
1022 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1023 rcv->instance = acm;
1024 }
Oliver Neukum86478942006-05-13 22:50:47 +02001025 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001026 struct acm_rb *buf = &(acm->rb[i]);
1027
David Kubicek61a87ad2005-11-01 18:51:34 +01001028 if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
Joe Perches898eb712007-10-18 03:06:30 -07001029 dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001030 goto alloc_fail7;
1031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 }
David Engrafe4cf3aa2008-03-20 10:01:34 +01001033 for(i = 0; i < ACM_NW; i++)
1034 {
1035 struct acm_wb *snd = &(acm->wb[i]);
1036
1037 if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1038 dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
1039 goto alloc_fail7;
1040 }
1041
1042 usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1043 NULL, acm->writesize, acm_write_bulk, snd);
1044 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1045 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 }
1047
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001048 usb_set_intfdata (intf, acm);
1049
1050 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1051 if (i < 0)
1052 goto alloc_fail8;
1053
1054 if (cfd) { /* export the country data */
1055 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1056 if (!acm->country_codes)
1057 goto skip_countries;
1058 acm->country_code_size = cfd->bLength - 4;
1059 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1060 acm->country_rel_date = cfd->iCountryCodeRelDate;
1061
1062 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1063 if (i < 0) {
1064 kfree(acm->country_codes);
1065 goto skip_countries;
1066 }
1067
1068 i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1069 if (i < 0) {
1070 kfree(acm->country_codes);
1071 goto skip_countries;
1072 }
1073 }
1074
1075skip_countries:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1077 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
1078 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1079 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1082
1083 acm_set_control(acm, acm->ctrlout);
1084
1085 acm->line.dwDTERate = cpu_to_le32(9600);
1086 acm->line.bDataBits = 8;
1087 acm_set_line(acm, &acm->line);
1088
1089 usb_driver_claim_interface(&acm_driver, data_interface, acm);
1090
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001091 usb_get_intf(control_interface);
1092 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
1094 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001096 return 0;
1097alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001098 for (i = 0; i < ACM_NW; i++)
1099 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100alloc_fail7:
Oliver Neukum86478942006-05-13 22:50:47 +02001101 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001102 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001103 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001104 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 usb_free_urb(acm->ctrlurb);
1106alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001107 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1110alloc_fail2:
1111 kfree(acm);
1112alloc_fail:
1113 return -ENOMEM;
1114}
1115
Oliver Neukum1365baf2007-10-12 17:24:28 +02001116static void stop_data_traffic(struct acm *acm)
1117{
1118 int i;
1119
1120 tasklet_disable(&acm->urb_task);
1121
1122 usb_kill_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001123 for(i = 0; i < ACM_NW; i++)
1124 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf2007-10-12 17:24:28 +02001125 for (i = 0; i < acm->rx_buflimit; i++)
1126 usb_kill_urb(acm->ru[i].urb);
1127
1128 INIT_LIST_HEAD(&acm->filled_read_bufs);
1129 INIT_LIST_HEAD(&acm->spare_read_bufs);
1130
1131 tasklet_enable(&acm->urb_task);
1132
1133 cancel_work_sync(&acm->work);
1134}
1135
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136static void acm_disconnect(struct usb_interface *intf)
1137{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001138 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 struct usb_device *usb_dev = interface_to_usbdev(intf);
David Kubicek61a87ad2005-11-01 18:51:34 +01001140 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141
1142 if (!acm || !acm->dev) {
1143 dbg("disconnect on nonexisting interface");
1144 return;
1145 }
1146
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001147 mutex_lock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001148 if (!usb_get_intfdata(intf)) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001149 mutex_unlock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001150 return;
1151 }
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001152 if (acm->country_codes){
Alan Stern74da5d62007-08-02 13:29:10 -04001153 device_remove_file(&acm->control->dev,
1154 &dev_attr_wCountryCodes);
1155 device_remove_file(&acm->control->dev,
1156 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001157 }
Alan Stern74da5d62007-08-02 13:29:10 -04001158 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001160 usb_set_intfdata(acm->control, NULL);
1161 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
Oliver Neukum1365baf2007-10-12 17:24:28 +02001163 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
Oliver Neukum884b6002005-04-21 21:28:02 +02001165 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001167 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001168 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
Oliver Neukum86067eea2006-01-08 12:39:13 +01001170 usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
1172 if (!acm->used) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001173 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001174 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 return;
1176 }
1177
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001178 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
1180 if (acm->tty)
1181 tty_hangup(acm->tty);
1182}
1183
Oliver Neukum1365baf2007-10-12 17:24:28 +02001184static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1185{
1186 struct acm *acm = usb_get_intfdata(intf);
1187
1188 if (acm->susp_count++)
1189 return 0;
1190 /*
1191 we treat opened interfaces differently,
1192 we must guard against open
1193 */
1194 mutex_lock(&acm->mutex);
1195
1196 if (acm->used)
1197 stop_data_traffic(acm);
1198
1199 mutex_unlock(&acm->mutex);
1200 return 0;
1201}
1202
1203static int acm_resume(struct usb_interface *intf)
1204{
1205 struct acm *acm = usb_get_intfdata(intf);
1206 int rv = 0;
1207
1208 if (--acm->susp_count)
1209 return 0;
1210
1211 mutex_lock(&acm->mutex);
1212 if (acm->used) {
1213 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1214 if (rv < 0)
1215 goto err_out;
1216
1217 tasklet_schedule(&acm->urb_task);
1218 }
1219
1220err_out:
1221 mutex_unlock(&acm->mutex);
1222 return rv;
1223}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224/*
1225 * USB driver structure.
1226 */
1227
1228static struct usb_device_id acm_ids[] = {
1229 /* quirky and broken devices */
1230 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1231 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1232 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001233 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1234 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1235 },
Masahito Omote8753e652005-07-29 12:17:25 -07001236 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1237 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1238 },
Chris Malley91a9c922006-10-03 10:08:28 +01001239 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1240 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1241 },
Oliver Neukum86478942006-05-13 22:50:47 +02001242 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1243 .driver_info = SINGLE_RX_URB, /* firmware bug */
1244 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001245 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1246 .driver_info = SINGLE_RX_URB, /* firmware bug */
1247 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001248 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1249 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1250 },
Iain McFarlane6149ed52008-05-04 00:13:49 +01001251 { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
1252 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1253 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 /* control interfaces with various AT-command sets */
1256 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1257 USB_CDC_ACM_PROTO_AT_V25TER) },
1258 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1259 USB_CDC_ACM_PROTO_AT_PCCA101) },
1260 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1261 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1262 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1263 USB_CDC_ACM_PROTO_AT_GSM) },
1264 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1265 USB_CDC_ACM_PROTO_AT_3G ) },
1266 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1267 USB_CDC_ACM_PROTO_AT_CDMA) },
1268
1269 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1270 { }
1271};
1272
1273MODULE_DEVICE_TABLE (usb, acm_ids);
1274
1275static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 .name = "cdc_acm",
1277 .probe = acm_probe,
1278 .disconnect = acm_disconnect,
Oliver Neukum1365baf2007-10-12 17:24:28 +02001279 .suspend = acm_suspend,
1280 .resume = acm_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 .id_table = acm_ids,
Oliver Neukum1365baf2007-10-12 17:24:28 +02001282 .supports_autosuspend = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283};
1284
1285/*
1286 * TTY driver structures.
1287 */
1288
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001289static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 .open = acm_tty_open,
1291 .close = acm_tty_close,
1292 .write = acm_tty_write,
1293 .write_room = acm_tty_write_room,
1294 .ioctl = acm_tty_ioctl,
1295 .throttle = acm_tty_throttle,
1296 .unthrottle = acm_tty_unthrottle,
1297 .chars_in_buffer = acm_tty_chars_in_buffer,
1298 .break_ctl = acm_tty_break_ctl,
1299 .set_termios = acm_tty_set_termios,
1300 .tiocmget = acm_tty_tiocmget,
1301 .tiocmset = acm_tty_tiocmset,
1302};
1303
1304/*
1305 * Init / exit.
1306 */
1307
1308static int __init acm_init(void)
1309{
1310 int retval;
1311 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1312 if (!acm_tty_driver)
1313 return -ENOMEM;
1314 acm_tty_driver->owner = THIS_MODULE,
1315 acm_tty_driver->driver_name = "acm",
1316 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 acm_tty_driver->major = ACM_TTY_MAJOR,
1318 acm_tty_driver->minor_start = 0,
1319 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1320 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001321 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 acm_tty_driver->init_termios = tty_std_termios;
1323 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1324 tty_set_operations(acm_tty_driver, &acm_ops);
1325
1326 retval = tty_register_driver(acm_tty_driver);
1327 if (retval) {
1328 put_tty_driver(acm_tty_driver);
1329 return retval;
1330 }
1331
1332 retval = usb_register(&acm_driver);
1333 if (retval) {
1334 tty_unregister_driver(acm_tty_driver);
1335 put_tty_driver(acm_tty_driver);
1336 return retval;
1337 }
1338
1339 info(DRIVER_VERSION ":" DRIVER_DESC);
1340
1341 return 0;
1342}
1343
1344static void __exit acm_exit(void)
1345{
1346 usb_deregister(&acm_driver);
1347 tty_unregister_driver(acm_tty_driver);
1348 put_tty_driver(acm_tty_driver);
1349}
1350
1351module_init(acm_init);
1352module_exit(acm_exit);
1353
1354MODULE_AUTHOR( DRIVER_AUTHOR );
1355MODULE_DESCRIPTION( DRIVER_DESC );
1356MODULE_LICENSE("GPL");
1357