blob: d9b40811392179c717076f0404293ff0c5386f14 [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 */
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700263 dbg("%s - urb shutting down with status: %d", __FUNCTION__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 return;
265 default:
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700266 dbg("%s - nonzero urb status received: %d", __FUNCTION__, 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
283 newctrl = le16_to_cpu(get_unaligned((__le16 *) data));
284
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",
Greg Kroah-Hartman185d4052007-07-18 10:58:02 -0700310 __FUNCTION__, 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;
446 struct acm_wb *wb = (struct acm_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 Neukum1365baf72007-10-12 17:24:28 +0200495
496 mutex_lock(&acm->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 if (acm->used++) {
Oliver Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-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 Neukum1365baf72007-10-12 17:24:28 +0200536 usb_autopm_put_interface(acm->control);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 acm->used--;
Oliver Neukum1365baf72007-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 Neukum1365baf72007-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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 char *buffer = intf->altsetting->extra;
808 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;
884
885 default:
886 err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
887 break;
888 }
889next_desc:
890 buflen -= buffer[0];
891 buffer += buffer[0];
892 }
893
894 if (!union_header) {
895 if (call_interface_num > 0) {
Joe Perches898eb712007-10-18 03:06:30 -0700896 dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
898 control_interface = intf;
899 } else {
Joe Perches898eb712007-10-18 03:06:30 -0700900 dev_dbg(&intf->dev,"No union descriptor, giving up\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 return -ENODEV;
902 }
903 } else {
904 control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
905 data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
906 if (!control_interface || !data_interface) {
Joe Perches898eb712007-10-18 03:06:30 -0700907 dev_dbg(&intf->dev,"no interfaces\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 return -ENODEV;
909 }
910 }
911
912 if (data_interface_num != call_interface_num)
Joe Perchesdc0d5c12007-12-17 11:40:18 -0800913 dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
915skip_normal_probe:
916
917 /*workaround for switched interfaces */
918 if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
919 if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
920 struct usb_interface *t;
Joe Perches898eb712007-10-18 03:06:30 -0700921 dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
923 t = control_interface;
924 control_interface = data_interface;
925 data_interface = t;
926 } else {
927 return -EINVAL;
928 }
929 }
Alan Stern74da5d62007-08-02 13:29:10 -0400930
931 /* Accept probe requests only for the control interface */
932 if (intf != control_interface)
933 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
935 if (usb_interface_claimed(data_interface)) { /* valid in this context */
Joe Perches898eb712007-10-18 03:06:30 -0700936 dev_dbg(&intf->dev,"The data interface isn't available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 return -EBUSY;
938 }
939
940
941 if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
942 return -EINVAL;
943
944 epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
945 epread = &data_interface->cur_altsetting->endpoint[0].desc;
946 epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
947
948
949 /* workaround for switched endpoints */
Luiz Fernando N. Capitulino45aea702006-10-26 13:02:48 -0300950 if (!usb_endpoint_dir_in(epread)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 /* descriptors are swapped */
952 struct usb_endpoint_descriptor *t;
Joe Perches898eb712007-10-18 03:06:30 -0700953 dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 t = epread;
956 epread = epwrite;
957 epwrite = t;
958 }
959 dbg("interfaces are valid");
960 for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
961
962 if (minor == ACM_TTY_MINORS) {
963 err("no more free acm devices");
964 return -ENODEV;
965 }
966
Oliver Neukum46f116e2005-10-24 22:42:35 +0200967 if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) {
Joe Perches898eb712007-10-18 03:06:30 -0700968 dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 goto alloc_fail;
970 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971
972 ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
Oliver Neukum86478942006-05-13 22:50:47 +0200973 readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2);
David Engrafe4cf3aa2008-03-20 10:01:34 +0100974 acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 acm->control = control_interface;
976 acm->data = data_interface;
977 acm->minor = minor;
978 acm->dev = usb_dev;
979 acm->ctrl_caps = ac_management_function;
980 acm->ctrlsize = ctrlsize;
981 acm->readsize = readsize;
Oliver Neukum86478942006-05-13 22:50:47 +0200982 acm->rx_buflimit = num_rx_buf;
David Kubicek61a87ad2005-11-01 18:51:34 +0100983 acm->urb_task.func = acm_rx_tasklet;
984 acm->urb_task.data = (unsigned long) acm;
David Howellsc4028952006-11-22 14:57:56 +0000985 INIT_WORK(&acm->work, acm_softint);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 spin_lock_init(&acm->throttle_lock);
Oliver Neukum884b6002005-04-21 21:28:02 +0200987 spin_lock_init(&acm->write_lock);
David Kubicek61a87ad2005-11-01 18:51:34 +0100988 spin_lock_init(&acm->read_lock);
Oliver Neukum1365baf72007-10-12 17:24:28 +0200989 mutex_init(&acm->mutex);
Oliver Neukum884b6002005-04-21 21:28:02 +0200990 acm->write_ready = 1;
David Kubicek61a87ad2005-11-01 18:51:34 +0100991 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
993 buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
994 if (!buf) {
Joe Perches898eb712007-10-18 03:06:30 -0700995 dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 goto alloc_fail2;
997 }
998 acm->ctrl_buffer = buf;
999
Oliver Neukum884b6002005-04-21 21:28:02 +02001000 if (acm_write_buffers_alloc(acm) < 0) {
Joe Perches898eb712007-10-18 03:06:30 -07001001 dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 goto alloc_fail4;
1003 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
1005 acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
1006 if (!acm->ctrlurb) {
Joe Perches898eb712007-10-18 03:06:30 -07001007 dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 goto alloc_fail5;
1009 }
Oliver Neukum86478942006-05-13 22:50:47 +02001010 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001011 struct acm_ru *rcv = &(acm->ru[i]);
1012
1013 if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
Joe Perches898eb712007-10-18 03:06:30 -07001014 dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001015 goto alloc_fail7;
1016 }
1017
1018 rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1019 rcv->instance = acm;
1020 }
Oliver Neukum86478942006-05-13 22:50:47 +02001021 for (i = 0; i < num_rx_buf; i++) {
David Kubicek61a87ad2005-11-01 18:51:34 +01001022 struct acm_rb *buf = &(acm->rb[i]);
1023
David Kubicek61a87ad2005-11-01 18:51:34 +01001024 if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
Joe Perches898eb712007-10-18 03:06:30 -07001025 dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
David Kubicek61a87ad2005-11-01 18:51:34 +01001026 goto alloc_fail7;
1027 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 }
David Engrafe4cf3aa2008-03-20 10:01:34 +01001029 for(i = 0; i < ACM_NW; i++)
1030 {
1031 struct acm_wb *snd = &(acm->wb[i]);
1032
1033 if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
1034 dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)");
1035 goto alloc_fail7;
1036 }
1037
1038 usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
1039 NULL, acm->writesize, acm_write_bulk, snd);
1040 snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1041 snd->instance = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 }
1043
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001044 usb_set_intfdata (intf, acm);
1045
1046 i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
1047 if (i < 0)
1048 goto alloc_fail8;
1049
1050 if (cfd) { /* export the country data */
1051 acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
1052 if (!acm->country_codes)
1053 goto skip_countries;
1054 acm->country_code_size = cfd->bLength - 4;
1055 memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
1056 acm->country_rel_date = cfd->iCountryCodeRelDate;
1057
1058 i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
1059 if (i < 0) {
1060 kfree(acm->country_codes);
1061 goto skip_countries;
1062 }
1063
1064 i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
1065 if (i < 0) {
1066 kfree(acm->country_codes);
1067 goto skip_countries;
1068 }
1069 }
1070
1071skip_countries:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
1073 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
1074 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1075 acm->ctrlurb->transfer_dma = acm->ctrl_dma;
1076
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
1078
1079 acm_set_control(acm, acm->ctrlout);
1080
1081 acm->line.dwDTERate = cpu_to_le32(9600);
1082 acm->line.bDataBits = 8;
1083 acm_set_line(acm, &acm->line);
1084
1085 usb_driver_claim_interface(&acm_driver, data_interface, acm);
1086
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001087 usb_get_intf(control_interface);
1088 tty_register_device(acm_tty_driver, minor, &control_interface->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
1090 acm_table[minor] = acm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001092 return 0;
1093alloc_fail8:
David Engrafe4cf3aa2008-03-20 10:01:34 +01001094 for (i = 0; i < ACM_NW; i++)
1095 usb_free_urb(acm->wb[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096alloc_fail7:
Oliver Neukum86478942006-05-13 22:50:47 +02001097 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001098 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001099 for (i = 0; i < num_rx_buf; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001100 usb_free_urb(acm->ru[i].urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 usb_free_urb(acm->ctrlurb);
1102alloc_fail5:
Oliver Neukum884b6002005-04-21 21:28:02 +02001103 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104alloc_fail4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
1106alloc_fail2:
1107 kfree(acm);
1108alloc_fail:
1109 return -ENOMEM;
1110}
1111
Oliver Neukum1365baf72007-10-12 17:24:28 +02001112static void stop_data_traffic(struct acm *acm)
1113{
1114 int i;
1115
1116 tasklet_disable(&acm->urb_task);
1117
1118 usb_kill_urb(acm->ctrlurb);
David Engrafe4cf3aa2008-03-20 10:01:34 +01001119 for(i = 0; i < ACM_NW; i++)
1120 usb_kill_urb(acm->wb[i].urb);
Oliver Neukum1365baf72007-10-12 17:24:28 +02001121 for (i = 0; i < acm->rx_buflimit; i++)
1122 usb_kill_urb(acm->ru[i].urb);
1123
1124 INIT_LIST_HEAD(&acm->filled_read_bufs);
1125 INIT_LIST_HEAD(&acm->spare_read_bufs);
1126
1127 tasklet_enable(&acm->urb_task);
1128
1129 cancel_work_sync(&acm->work);
1130}
1131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132static void acm_disconnect(struct usb_interface *intf)
1133{
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001134 struct acm *acm = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 struct usb_device *usb_dev = interface_to_usbdev(intf);
David Kubicek61a87ad2005-11-01 18:51:34 +01001136 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
1138 if (!acm || !acm->dev) {
1139 dbg("disconnect on nonexisting interface");
1140 return;
1141 }
1142
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001143 mutex_lock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001144 if (!usb_get_intfdata(intf)) {
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001145 mutex_unlock(&open_mutex);
Oliver Neukum86067eea2006-01-08 12:39:13 +01001146 return;
1147 }
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001148 if (acm->country_codes){
Alan Stern74da5d62007-08-02 13:29:10 -04001149 device_remove_file(&acm->control->dev,
1150 &dev_attr_wCountryCodes);
1151 device_remove_file(&acm->control->dev,
1152 &dev_attr_iCountryCodeRelDate);
Oliver Neukumc4cabd22007-02-27 15:28:55 +01001153 }
Alan Stern74da5d62007-08-02 13:29:10 -04001154 device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 acm->dev = NULL;
Oliver Neukum86067eea2006-01-08 12:39:13 +01001156 usb_set_intfdata(acm->control, NULL);
1157 usb_set_intfdata(acm->data, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Oliver Neukum1365baf72007-10-12 17:24:28 +02001159 stop_data_traffic(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
Oliver Neukum884b6002005-04-21 21:28:02 +02001161 acm_write_buffers_free(acm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
Oliver Neukum86478942006-05-13 22:50:47 +02001163 for (i = 0; i < acm->rx_buflimit; i++)
David Kubicek61a87ad2005-11-01 18:51:34 +01001164 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165
Oliver Neukum86067eea2006-01-08 12:39:13 +01001166 usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
1168 if (!acm->used) {
brian@murphy.dk83ef3442005-06-29 16:53:29 -07001169 acm_tty_unregister(acm);
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001170 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 return;
1172 }
1173
Arjan van de Ven4186ecf2006-01-11 15:55:29 +01001174 mutex_unlock(&open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
1176 if (acm->tty)
1177 tty_hangup(acm->tty);
1178}
1179
Oliver Neukum1365baf72007-10-12 17:24:28 +02001180static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1181{
1182 struct acm *acm = usb_get_intfdata(intf);
1183
1184 if (acm->susp_count++)
1185 return 0;
1186 /*
1187 we treat opened interfaces differently,
1188 we must guard against open
1189 */
1190 mutex_lock(&acm->mutex);
1191
1192 if (acm->used)
1193 stop_data_traffic(acm);
1194
1195 mutex_unlock(&acm->mutex);
1196 return 0;
1197}
1198
1199static int acm_resume(struct usb_interface *intf)
1200{
1201 struct acm *acm = usb_get_intfdata(intf);
1202 int rv = 0;
1203
1204 if (--acm->susp_count)
1205 return 0;
1206
1207 mutex_lock(&acm->mutex);
1208 if (acm->used) {
1209 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1210 if (rv < 0)
1211 goto err_out;
1212
1213 tasklet_schedule(&acm->urb_task);
1214 }
1215
1216err_out:
1217 mutex_unlock(&acm->mutex);
1218 return rv;
1219}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220/*
1221 * USB driver structure.
1222 */
1223
1224static struct usb_device_id acm_ids[] = {
1225 /* quirky and broken devices */
1226 { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
1227 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1228 },
Andrey Arapovb0e2a702007-07-04 17:11:42 +02001229 { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
1230 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1231 },
Masahito Omote8753e652005-07-29 12:17:25 -07001232 { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
1233 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1234 },
Chris Malley91a9c922006-10-03 10:08:28 +01001235 { USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
1236 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1237 },
Oliver Neukum86478942006-05-13 22:50:47 +02001238 { USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
1239 .driver_info = SINGLE_RX_URB, /* firmware bug */
1240 },
Oliver Neukum3dd2ae82006-06-23 09:14:17 +02001241 { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
1242 .driver_info = SINGLE_RX_URB, /* firmware bug */
1243 },
Oliver Neukum9be84562007-02-12 08:50:03 +01001244 { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
1245 .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
1246 },
1247
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 /* control interfaces with various AT-command sets */
1249 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1250 USB_CDC_ACM_PROTO_AT_V25TER) },
1251 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1252 USB_CDC_ACM_PROTO_AT_PCCA101) },
1253 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1254 USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
1255 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1256 USB_CDC_ACM_PROTO_AT_GSM) },
1257 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1258 USB_CDC_ACM_PROTO_AT_3G ) },
1259 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
1260 USB_CDC_ACM_PROTO_AT_CDMA) },
1261
1262 /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
1263 { }
1264};
1265
1266MODULE_DEVICE_TABLE (usb, acm_ids);
1267
1268static struct usb_driver acm_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 .name = "cdc_acm",
1270 .probe = acm_probe,
1271 .disconnect = acm_disconnect,
Oliver Neukum1365baf72007-10-12 17:24:28 +02001272 .suspend = acm_suspend,
1273 .resume = acm_resume,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 .id_table = acm_ids,
Oliver Neukum1365baf72007-10-12 17:24:28 +02001275 .supports_autosuspend = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276};
1277
1278/*
1279 * TTY driver structures.
1280 */
1281
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001282static const struct tty_operations acm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 .open = acm_tty_open,
1284 .close = acm_tty_close,
1285 .write = acm_tty_write,
1286 .write_room = acm_tty_write_room,
1287 .ioctl = acm_tty_ioctl,
1288 .throttle = acm_tty_throttle,
1289 .unthrottle = acm_tty_unthrottle,
1290 .chars_in_buffer = acm_tty_chars_in_buffer,
1291 .break_ctl = acm_tty_break_ctl,
1292 .set_termios = acm_tty_set_termios,
1293 .tiocmget = acm_tty_tiocmget,
1294 .tiocmset = acm_tty_tiocmset,
1295};
1296
1297/*
1298 * Init / exit.
1299 */
1300
1301static int __init acm_init(void)
1302{
1303 int retval;
1304 acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
1305 if (!acm_tty_driver)
1306 return -ENOMEM;
1307 acm_tty_driver->owner = THIS_MODULE,
1308 acm_tty_driver->driver_name = "acm",
1309 acm_tty_driver->name = "ttyACM",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 acm_tty_driver->major = ACM_TTY_MAJOR,
1311 acm_tty_driver->minor_start = 0,
1312 acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
1313 acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001314 acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 acm_tty_driver->init_termios = tty_std_termios;
1316 acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1317 tty_set_operations(acm_tty_driver, &acm_ops);
1318
1319 retval = tty_register_driver(acm_tty_driver);
1320 if (retval) {
1321 put_tty_driver(acm_tty_driver);
1322 return retval;
1323 }
1324
1325 retval = usb_register(&acm_driver);
1326 if (retval) {
1327 tty_unregister_driver(acm_tty_driver);
1328 put_tty_driver(acm_tty_driver);
1329 return retval;
1330 }
1331
1332 info(DRIVER_VERSION ":" DRIVER_DESC);
1333
1334 return 0;
1335}
1336
1337static void __exit acm_exit(void)
1338{
1339 usb_deregister(&acm_driver);
1340 tty_unregister_driver(acm_tty_driver);
1341 put_tty_driver(acm_tty_driver);
1342}
1343
1344module_init(acm_init);
1345module_exit(acm_exit);
1346
1347MODULE_AUTHOR( DRIVER_AUTHOR );
1348MODULE_DESCRIPTION( DRIVER_DESC );
1349MODULE_LICENSE("GPL");
1350