blob: de54714d1260ba8b19d3a8c8a644eace8bdf0211 [file] [log] [blame]
David Brownell4d5a73d2008-06-19 18:18:40 -07001/*
2 * f_acm.c -- USB CDC serial (ACM) function driver
3 *
4 * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
5 * Copyright (C) 2008 by David Brownell
6 * Copyright (C) 2008 by Nokia Corporation
Michal Nazarewiczb97503f2009-10-28 16:57:30 +01007 * Copyright (C) 2009 by Samsung Electronics
Duy Truong790f06d2013-02-13 16:38:12 -08008 * Copyright (c) 2011 The Linux Foundation. All rights reserved.
Michal Nazarewicz54b83602012-01-13 15:05:16 +01009 * Author: Michal Nazarewicz (mina86@mina86.com)
David Brownell4d5a73d2008-06-19 18:18:40 -070010 *
11 * This software is distributed under the terms of the GNU General
12 * Public License ("GPL") as published by the Free Software Foundation,
13 * either version 2 of that License or (at your option) any later version.
14 */
15
16/* #define VERBOSE_DEBUG */
17
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018#include <linux/slab.h>
David Brownell4d5a73d2008-06-19 18:18:40 -070019#include <linux/kernel.h>
20#include <linux/device.h>
Anji jonnala92be1b42011-12-19 09:44:41 +053021#include <mach/usb_gadget_xport.h>
David Brownell4d5a73d2008-06-19 18:18:40 -070022
23#include "u_serial.h"
24#include "gadget_chips.h"
25
26
27/*
28 * This CDC ACM function support just wraps control functions and
29 * notifications around the generic serial-over-usb code.
30 *
31 * Because CDC ACM is standardized by the USB-IF, many host operating
32 * systems have drivers for it. Accordingly, ACM is the preferred
33 * interop solution for serial-port type connections. The control
34 * models are often not necessary, and in any case don't do much in
35 * this bare-bones implementation.
36 *
37 * Note that even MS-Windows has some support for ACM. However, that
38 * support is somewhat broken because when you use ACM in a composite
39 * device, having multiple interfaces confuses the poor OS. It doesn't
40 * seem to understand CDC Union descriptors. The new "association"
41 * descriptors (roughly equivalent to CDC Unions) may sometimes help.
42 */
43
David Brownell4d5a73d2008-06-19 18:18:40 -070044struct f_acm {
45 struct gserial port;
46 u8 ctrl_id, data_id;
47 u8 port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048 enum transport_type transport;
David Brownell4d5a73d2008-06-19 18:18:40 -070049
David Brownell1f1ba112008-08-06 18:49:57 -070050 u8 pending;
51
52 /* lock is mostly for pending and notify_req ... they get accessed
53 * by callbacks both from tty (open/close/break) under its spinlock,
54 * and notify_req.complete() which can't use that lock.
55 */
56 spinlock_t lock;
57
David Brownell4d5a73d2008-06-19 18:18:40 -070058 struct usb_ep *notify;
David Brownell1f1ba112008-08-06 18:49:57 -070059 struct usb_request *notify_req;
David Brownell4d5a73d2008-06-19 18:18:40 -070060
61 struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
David Brownell1f1ba112008-08-06 18:49:57 -070062
63 /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
David Brownell4d5a73d2008-06-19 18:18:40 -070064 u16 port_handshake_bits;
David Brownell1f1ba112008-08-06 18:49:57 -070065#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
66#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
67
68 /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
69 u16 serial_state;
70#define ACM_CTRL_OVERRUN (1 << 6)
71#define ACM_CTRL_PARITY (1 << 5)
72#define ACM_CTRL_FRAMING (1 << 4)
73#define ACM_CTRL_RI (1 << 3)
74#define ACM_CTRL_BRK (1 << 2)
75#define ACM_CTRL_DSR (1 << 1)
76#define ACM_CTRL_DCD (1 << 0)
David Brownell4d5a73d2008-06-19 18:18:40 -070077};
78
Anji jonnala92be1b42011-12-19 09:44:41 +053079static unsigned int no_acm_tty_ports;
80static unsigned int no_acm_sdio_ports;
81static unsigned int no_acm_smd_ports;
82static unsigned int nr_acm_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083
Anji jonnala92be1b42011-12-19 09:44:41 +053084static struct acm_port_info {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085 enum transport_type transport;
86 unsigned port_num;
87 unsigned client_port_num;
88} gacm_ports[GSERIAL_NO_PORTS];
89
David Brownell4d5a73d2008-06-19 18:18:40 -070090static inline struct f_acm *func_to_acm(struct usb_function *f)
91{
92 return container_of(f, struct f_acm, port.func);
93}
94
David Brownell1f1ba112008-08-06 18:49:57 -070095static inline struct f_acm *port_to_acm(struct gserial *p)
96{
97 return container_of(p, struct f_acm, port);
98}
99
Anji jonnala92be1b42011-12-19 09:44:41 +0530100static int acm_port_setup(struct usb_configuration *c)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101{
102 int ret = 0;
103
Anji jonnala92be1b42011-12-19 09:44:41 +0530104 pr_debug("%s: no_acm_tty_ports:%u no_acm_sdio_ports: %u nr_acm_ports:%u\n",
105 __func__, no_acm_tty_ports, no_acm_sdio_ports,
106 nr_acm_ports);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107
Anji jonnala92be1b42011-12-19 09:44:41 +0530108 if (no_acm_tty_ports)
109 ret = gserial_setup(c->cdev->gadget, no_acm_tty_ports);
110 if (no_acm_sdio_ports)
111 ret = gsdio_setup(c->cdev->gadget, no_acm_sdio_ports);
112 if (no_acm_smd_ports)
113 ret = gsmd_setup(c->cdev->gadget, no_acm_smd_ports);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114
115 return ret;
116}
117
Anji jonnala92be1b42011-12-19 09:44:41 +0530118static int acm_port_connect(struct f_acm *acm)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119{
120 unsigned port_num;
121
122 port_num = gacm_ports[acm->port_num].client_port_num;
123
124
125 pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_port_no:%d\n",
Hemant Kumar1b820d52011-11-03 15:08:28 -0700126 __func__, xport_to_str(acm->transport),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 acm, &acm->port, acm->port_num, port_num);
128
129 switch (acm->transport) {
Anji jonnala92be1b42011-12-19 09:44:41 +0530130 case USB_GADGET_XPORT_TTY:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 gserial_connect(&acm->port, port_num);
132 break;
Anji jonnala92be1b42011-12-19 09:44:41 +0530133 case USB_GADGET_XPORT_SDIO:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 gsdio_connect(&acm->port, port_num);
135 break;
Anji jonnala92be1b42011-12-19 09:44:41 +0530136 case USB_GADGET_XPORT_SMD:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137 gsmd_connect(&acm->port, port_num);
138 break;
139 default:
140 pr_err("%s: Un-supported transport: %s\n", __func__,
Hemant Kumar1b820d52011-11-03 15:08:28 -0700141 xport_to_str(acm->transport));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 return -ENODEV;
143 }
144
145 return 0;
146}
147
Anji jonnala92be1b42011-12-19 09:44:41 +0530148static int acm_port_disconnect(struct f_acm *acm)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149{
150 unsigned port_num;
151
152 port_num = gacm_ports[acm->port_num].client_port_num;
153
154 pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_pno:%d\n",
Hemant Kumar1b820d52011-11-03 15:08:28 -0700155 __func__, xport_to_str(acm->transport),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 acm, &acm->port, acm->port_num, port_num);
157
158 switch (acm->transport) {
Anji jonnala92be1b42011-12-19 09:44:41 +0530159 case USB_GADGET_XPORT_TTY:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 gserial_disconnect(&acm->port);
161 break;
Anji jonnala92be1b42011-12-19 09:44:41 +0530162 case USB_GADGET_XPORT_SDIO:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 gsdio_disconnect(&acm->port, port_num);
164 break;
Anji jonnala92be1b42011-12-19 09:44:41 +0530165 case USB_GADGET_XPORT_SMD:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 gsmd_disconnect(&acm->port, port_num);
167 break;
168 default:
169 pr_err("%s: Un-supported transport:%s\n", __func__,
Hemant Kumar1b820d52011-11-03 15:08:28 -0700170 xport_to_str(acm->transport));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 return -ENODEV;
172 }
173
174 return 0;
175}
David Brownell4d5a73d2008-06-19 18:18:40 -0700176/*-------------------------------------------------------------------------*/
177
178/* notification endpoint uses smallish and infrequent fixed-size messages */
179
180#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
David Brownell1f1ba112008-08-06 18:49:57 -0700181#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
David Brownell4d5a73d2008-06-19 18:18:40 -0700182
183/* interface and class descriptors: */
184
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100185static struct usb_interface_assoc_descriptor
186acm_iad_descriptor = {
187 .bLength = sizeof acm_iad_descriptor,
188 .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
189
190 /* .bFirstInterface = DYNAMIC, */
191 .bInterfaceCount = 2, // control + data
192 .bFunctionClass = USB_CLASS_COMM,
193 .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
Praveena Nadahally5c8db072010-09-10 23:05:03 +0530194 .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100195 /* .iFunction = DYNAMIC */
196};
197
198
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200199static struct usb_interface_descriptor acm_control_interface_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700200 .bLength = USB_DT_INTERFACE_SIZE,
201 .bDescriptorType = USB_DT_INTERFACE,
202 /* .bInterfaceNumber = DYNAMIC */
203 .bNumEndpoints = 1,
204 .bInterfaceClass = USB_CLASS_COMM,
205 .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
206 .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
207 /* .iInterface = DYNAMIC */
208};
209
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200210static struct usb_interface_descriptor acm_data_interface_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700211 .bLength = USB_DT_INTERFACE_SIZE,
212 .bDescriptorType = USB_DT_INTERFACE,
213 /* .bInterfaceNumber = DYNAMIC */
214 .bNumEndpoints = 2,
215 .bInterfaceClass = USB_CLASS_CDC_DATA,
216 .bInterfaceSubClass = 0,
217 .bInterfaceProtocol = 0,
218 /* .iInterface = DYNAMIC */
219};
220
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200221static struct usb_cdc_header_desc acm_header_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700222 .bLength = sizeof(acm_header_desc),
223 .bDescriptorType = USB_DT_CS_INTERFACE,
224 .bDescriptorSubType = USB_CDC_HEADER_TYPE,
Harvey Harrison551509d2009-02-11 14:11:36 -0800225 .bcdCDC = cpu_to_le16(0x0110),
David Brownell4d5a73d2008-06-19 18:18:40 -0700226};
227
228static struct usb_cdc_call_mgmt_descriptor
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200229acm_call_mgmt_descriptor = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700230 .bLength = sizeof(acm_call_mgmt_descriptor),
231 .bDescriptorType = USB_DT_CS_INTERFACE,
232 .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
233 .bmCapabilities = 0,
234 /* .bDataInterface = DYNAMIC */
235};
236
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200237static struct usb_cdc_acm_descriptor acm_descriptor = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700238 .bLength = sizeof(acm_descriptor),
239 .bDescriptorType = USB_DT_CS_INTERFACE,
240 .bDescriptorSubType = USB_CDC_ACM_TYPE,
David Brownell1f1ba112008-08-06 18:49:57 -0700241 .bmCapabilities = USB_CDC_CAP_LINE,
David Brownell4d5a73d2008-06-19 18:18:40 -0700242};
243
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200244static struct usb_cdc_union_desc acm_union_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700245 .bLength = sizeof(acm_union_desc),
246 .bDescriptorType = USB_DT_CS_INTERFACE,
247 .bDescriptorSubType = USB_CDC_UNION_TYPE,
248 /* .bMasterInterface0 = DYNAMIC */
249 /* .bSlaveInterface0 = DYNAMIC */
250};
251
252/* full speed support: */
253
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200254static struct usb_endpoint_descriptor acm_fs_notify_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700255 .bLength = USB_DT_ENDPOINT_SIZE,
256 .bDescriptorType = USB_DT_ENDPOINT,
257 .bEndpointAddress = USB_DIR_IN,
258 .bmAttributes = USB_ENDPOINT_XFER_INT,
Harvey Harrison551509d2009-02-11 14:11:36 -0800259 .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
David Brownell4d5a73d2008-06-19 18:18:40 -0700260 .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
261};
262
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200263static struct usb_endpoint_descriptor acm_fs_in_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700264 .bLength = USB_DT_ENDPOINT_SIZE,
265 .bDescriptorType = USB_DT_ENDPOINT,
266 .bEndpointAddress = USB_DIR_IN,
267 .bmAttributes = USB_ENDPOINT_XFER_BULK,
268};
269
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200270static struct usb_endpoint_descriptor acm_fs_out_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700271 .bLength = USB_DT_ENDPOINT_SIZE,
272 .bDescriptorType = USB_DT_ENDPOINT,
273 .bEndpointAddress = USB_DIR_OUT,
274 .bmAttributes = USB_ENDPOINT_XFER_BULK,
275};
276
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200277static struct usb_descriptor_header *acm_fs_function[] = {
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100278 (struct usb_descriptor_header *) &acm_iad_descriptor,
David Brownell4d5a73d2008-06-19 18:18:40 -0700279 (struct usb_descriptor_header *) &acm_control_interface_desc,
280 (struct usb_descriptor_header *) &acm_header_desc,
281 (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
282 (struct usb_descriptor_header *) &acm_descriptor,
283 (struct usb_descriptor_header *) &acm_union_desc,
284 (struct usb_descriptor_header *) &acm_fs_notify_desc,
285 (struct usb_descriptor_header *) &acm_data_interface_desc,
286 (struct usb_descriptor_header *) &acm_fs_in_desc,
287 (struct usb_descriptor_header *) &acm_fs_out_desc,
288 NULL,
289};
290
291/* high speed support: */
292
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200293static struct usb_endpoint_descriptor acm_hs_notify_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700294 .bLength = USB_DT_ENDPOINT_SIZE,
295 .bDescriptorType = USB_DT_ENDPOINT,
296 .bEndpointAddress = USB_DIR_IN,
297 .bmAttributes = USB_ENDPOINT_XFER_INT,
Harvey Harrison551509d2009-02-11 14:11:36 -0800298 .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
David Brownell4d5a73d2008-06-19 18:18:40 -0700299 .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
300};
301
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200302static struct usb_endpoint_descriptor acm_hs_in_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700303 .bLength = USB_DT_ENDPOINT_SIZE,
304 .bDescriptorType = USB_DT_ENDPOINT,
305 .bmAttributes = USB_ENDPOINT_XFER_BULK,
Harvey Harrison551509d2009-02-11 14:11:36 -0800306 .wMaxPacketSize = cpu_to_le16(512),
David Brownell4d5a73d2008-06-19 18:18:40 -0700307};
308
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200309static struct usb_endpoint_descriptor acm_hs_out_desc = {
David Brownell4d5a73d2008-06-19 18:18:40 -0700310 .bLength = USB_DT_ENDPOINT_SIZE,
311 .bDescriptorType = USB_DT_ENDPOINT,
312 .bmAttributes = USB_ENDPOINT_XFER_BULK,
Harvey Harrison551509d2009-02-11 14:11:36 -0800313 .wMaxPacketSize = cpu_to_le16(512),
David Brownell4d5a73d2008-06-19 18:18:40 -0700314};
315
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200316static struct usb_descriptor_header *acm_hs_function[] = {
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100317 (struct usb_descriptor_header *) &acm_iad_descriptor,
David Brownell4d5a73d2008-06-19 18:18:40 -0700318 (struct usb_descriptor_header *) &acm_control_interface_desc,
319 (struct usb_descriptor_header *) &acm_header_desc,
320 (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
321 (struct usb_descriptor_header *) &acm_descriptor,
322 (struct usb_descriptor_header *) &acm_union_desc,
323 (struct usb_descriptor_header *) &acm_hs_notify_desc,
324 (struct usb_descriptor_header *) &acm_data_interface_desc,
325 (struct usb_descriptor_header *) &acm_hs_in_desc,
326 (struct usb_descriptor_header *) &acm_hs_out_desc,
327 NULL,
328};
329
Sebastian Andrzej Siewior6fecfb02012-02-06 18:46:36 +0100330static struct usb_endpoint_descriptor acm_ss_in_desc = {
331 .bLength = USB_DT_ENDPOINT_SIZE,
332 .bDescriptorType = USB_DT_ENDPOINT,
333 .bmAttributes = USB_ENDPOINT_XFER_BULK,
334 .wMaxPacketSize = cpu_to_le16(1024),
335};
336
337static struct usb_endpoint_descriptor acm_ss_out_desc = {
338 .bLength = USB_DT_ENDPOINT_SIZE,
339 .bDescriptorType = USB_DT_ENDPOINT,
340 .bmAttributes = USB_ENDPOINT_XFER_BULK,
341 .wMaxPacketSize = cpu_to_le16(1024),
342};
343
344static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
345 .bLength = sizeof acm_ss_bulk_comp_desc,
346 .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
347};
348
349static struct usb_descriptor_header *acm_ss_function[] = {
350 (struct usb_descriptor_header *) &acm_iad_descriptor,
351 (struct usb_descriptor_header *) &acm_control_interface_desc,
352 (struct usb_descriptor_header *) &acm_header_desc,
353 (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
354 (struct usb_descriptor_header *) &acm_descriptor,
355 (struct usb_descriptor_header *) &acm_union_desc,
356 (struct usb_descriptor_header *) &acm_hs_notify_desc,
357 (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
358 (struct usb_descriptor_header *) &acm_data_interface_desc,
359 (struct usb_descriptor_header *) &acm_ss_in_desc,
360 (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
361 (struct usb_descriptor_header *) &acm_ss_out_desc,
362 (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
363 NULL,
364};
365
David Brownell4d5a73d2008-06-19 18:18:40 -0700366/* string descriptors: */
367
368#define ACM_CTRL_IDX 0
369#define ACM_DATA_IDX 1
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100370#define ACM_IAD_IDX 2
David Brownell4d5a73d2008-06-19 18:18:40 -0700371
372/* static strings, in UTF-8 */
373static struct usb_string acm_string_defs[] = {
374 [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
375 [ACM_DATA_IDX].s = "CDC ACM Data",
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100376 [ACM_IAD_IDX ].s = "CDC Serial",
David Brownell4d5a73d2008-06-19 18:18:40 -0700377 { /* ZEROES END LIST */ },
378};
379
380static struct usb_gadget_strings acm_string_table = {
381 .language = 0x0409, /* en-us */
382 .strings = acm_string_defs,
383};
384
385static struct usb_gadget_strings *acm_strings[] = {
386 &acm_string_table,
387 NULL,
388};
389
390/*-------------------------------------------------------------------------*/
391
392/* ACM control ... data handling is delegated to tty library code.
393 * The main task of this function is to activate and deactivate
394 * that code based on device state; track parameters like line
395 * speed, handshake state, and so on; and issue notifications.
396 */
397
398static void acm_complete_set_line_coding(struct usb_ep *ep,
399 struct usb_request *req)
400{
401 struct f_acm *acm = ep->driver_data;
402 struct usb_composite_dev *cdev = acm->port.func.config->cdev;
403
404 if (req->status != 0) {
405 DBG(cdev, "acm ttyGS%d completion, err %d\n",
406 acm->port_num, req->status);
407 return;
408 }
409
410 /* normal completion */
411 if (req->actual != sizeof(acm->port_line_coding)) {
412 DBG(cdev, "acm ttyGS%d short resp, len %d\n",
413 acm->port_num, req->actual);
414 usb_ep_set_halt(ep);
415 } else {
416 struct usb_cdc_line_coding *value = req->buf;
417
418 /* REVISIT: we currently just remember this data.
419 * If we change that, (a) validate it first, then
420 * (b) update whatever hardware needs updating,
421 * (c) worry about locking. This is information on
422 * the order of 9600-8-N-1 ... most of which means
423 * nothing unless we control a real RS232 line.
424 */
425 acm->port_line_coding = *value;
426 }
427}
428
429static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
430{
431 struct f_acm *acm = func_to_acm(f);
432 struct usb_composite_dev *cdev = f->config->cdev;
433 struct usb_request *req = cdev->req;
434 int value = -EOPNOTSUPP;
435 u16 w_index = le16_to_cpu(ctrl->wIndex);
436 u16 w_value = le16_to_cpu(ctrl->wValue);
437 u16 w_length = le16_to_cpu(ctrl->wLength);
438
439 /* composite driver infrastructure handles everything except
440 * CDC class messages; interface activation uses set_alt().
David Brownell1f1ba112008-08-06 18:49:57 -0700441 *
442 * Note CDC spec table 4 lists the ACM request profile. It requires
443 * encapsulated command support ... we don't handle any, and respond
444 * to them by stalling. Options include get/set/clear comm features
445 * (not that useful) and SEND_BREAK.
David Brownell4d5a73d2008-06-19 18:18:40 -0700446 */
447 switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
448
449 /* SET_LINE_CODING ... just read and save what the host sends */
450 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
451 | USB_CDC_REQ_SET_LINE_CODING:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 if (w_length != sizeof(struct usb_cdc_line_coding))
David Brownell4d5a73d2008-06-19 18:18:40 -0700453 goto invalid;
454
455 value = w_length;
456 cdev->gadget->ep0->driver_data = acm;
457 req->complete = acm_complete_set_line_coding;
458 break;
459
460 /* GET_LINE_CODING ... return what host sent, or initial value */
461 case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
462 | USB_CDC_REQ_GET_LINE_CODING:
David Brownell4d5a73d2008-06-19 18:18:40 -0700463
464 value = min_t(unsigned, w_length,
465 sizeof(struct usb_cdc_line_coding));
466 memcpy(req->buf, &acm->port_line_coding, value);
467 break;
468
469 /* SET_CONTROL_LINE_STATE ... save what the host sent */
470 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
471 | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
David Brownell4d5a73d2008-06-19 18:18:40 -0700472 value = 0;
473
474 /* FIXME we should not allow data to flow until the
David Brownell1f1ba112008-08-06 18:49:57 -0700475 * host sets the ACM_CTRL_DTR bit; and when it clears
David Brownell4d5a73d2008-06-19 18:18:40 -0700476 * that bit, we should return to that no-flow state.
477 */
478 acm->port_handshake_bits = w_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479 if (acm->port.notify_modem) {
480 unsigned port_num =
481 gacm_ports[acm->port_num].client_port_num;
482
483 acm->port.notify_modem(&acm->port, port_num, w_value);
484 }
David Brownell4d5a73d2008-06-19 18:18:40 -0700485 break;
486
487 default:
488invalid:
489 VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
490 ctrl->bRequestType, ctrl->bRequest,
491 w_value, w_index, w_length);
492 }
493
494 /* respond with data transfer or status phase? */
495 if (value >= 0) {
496 DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
497 acm->port_num, ctrl->bRequestType, ctrl->bRequest,
498 w_value, w_index, w_length);
499 req->zero = 0;
500 req->length = value;
501 value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
502 if (value < 0)
503 ERROR(cdev, "acm response on ttyGS%d, err %d\n",
504 acm->port_num, value);
505 }
506
507 /* device either stalls (value < 0) or reports success */
508 return value;
509}
510
511static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
512{
513 struct f_acm *acm = func_to_acm(f);
514 struct usb_composite_dev *cdev = f->config->cdev;
515
516 /* we know alt == 0, so this is an activation or a reset */
517
518 if (intf == acm->ctrl_id) {
David Brownell4d5a73d2008-06-19 18:18:40 -0700519 if (acm->notify->driver_data) {
520 VDBG(cdev, "reset acm control interface %d\n", intf);
521 usb_ep_disable(acm->notify);
522 } else {
523 VDBG(cdev, "init acm ctrl interface %d\n", intf);
David Brownell4d5a73d2008-06-19 18:18:40 -0700524 }
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200525 if (config_ep_by_speed(cdev->gadget, f, acm->notify))
526 return -EINVAL;
527
Tatyana Brokhman72c973d2011-06-28 16:33:48 +0300528 usb_ep_enable(acm->notify);
David Brownell4d5a73d2008-06-19 18:18:40 -0700529 acm->notify->driver_data = acm;
530
531 } else if (intf == acm->data_id) {
532 if (acm->port.in->driver_data) {
533 DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
Anji jonnala92be1b42011-12-19 09:44:41 +0530534 acm_port_disconnect(acm);
Tatyana Brokhmanea2a1df72011-06-28 16:33:50 +0300535 }
536 if (!acm->port.in->desc || !acm->port.out->desc) {
David Brownell4d5a73d2008-06-19 18:18:40 -0700537 DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
Tatyana Brokhmanea2a1df72011-06-28 16:33:50 +0300538 if (config_ep_by_speed(cdev->gadget, f,
539 acm->port.in) ||
540 config_ep_by_speed(cdev->gadget, f,
541 acm->port.out)) {
542 acm->port.in->desc = NULL;
543 acm->port.out->desc = NULL;
544 return -EINVAL;
545 }
David Brownell4d5a73d2008-06-19 18:18:40 -0700546 }
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200547 if (config_ep_by_speed(cdev->gadget, f,
548 acm->port.in) ||
549 config_ep_by_speed(cdev->gadget, f,
550 acm->port.out)) {
551 acm->port.in->desc = NULL;
552 acm->port.out->desc = NULL;
553 return -EINVAL;
554 }
555
Anji jonnala92be1b42011-12-19 09:44:41 +0530556 acm_port_connect(acm);
David Brownell4d5a73d2008-06-19 18:18:40 -0700557
558 } else
559 return -EINVAL;
560
561 return 0;
562}
563
564static void acm_disable(struct usb_function *f)
565{
566 struct f_acm *acm = func_to_acm(f);
567 struct usb_composite_dev *cdev = f->config->cdev;
568
569 DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
Anji jonnala92be1b42011-12-19 09:44:41 +0530570 acm_port_disconnect(acm);
David Brownell4d5a73d2008-06-19 18:18:40 -0700571 usb_ep_disable(acm->notify);
572 acm->notify->driver_data = NULL;
573}
574
575/*-------------------------------------------------------------------------*/
576
David Brownell1f1ba112008-08-06 18:49:57 -0700577/**
578 * acm_cdc_notify - issue CDC notification to host
579 * @acm: wraps host to be notified
580 * @type: notification type
581 * @value: Refer to cdc specs, wValue field.
582 * @data: data to be sent
583 * @length: size of data
584 * Context: irqs blocked, acm->lock held, acm_notify_req non-null
585 *
André Goddard Rosaaf901ca2009-11-14 13:09:05 -0200586 * Returns zero on success or a negative errno.
David Brownell1f1ba112008-08-06 18:49:57 -0700587 *
588 * See section 6.3.5 of the CDC 1.1 specification for information
589 * about the only notification we issue: SerialState change.
590 */
591static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
592 void *data, unsigned length)
593{
594 struct usb_ep *ep = acm->notify;
595 struct usb_request *req;
596 struct usb_cdc_notification *notify;
597 const unsigned len = sizeof(*notify) + length;
598 void *buf;
599 int status;
600
601 req = acm->notify_req;
602 acm->notify_req = NULL;
603 acm->pending = false;
604
605 req->length = len;
606 notify = req->buf;
607 buf = notify + 1;
608
609 notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
610 | USB_RECIP_INTERFACE;
611 notify->bNotificationType = type;
612 notify->wValue = cpu_to_le16(value);
613 notify->wIndex = cpu_to_le16(acm->ctrl_id);
614 notify->wLength = cpu_to_le16(length);
615 memcpy(buf, data, length);
616
David Brownelle50ae572008-11-12 11:35:13 -0800617 /* ep_queue() can complete immediately if it fills the fifo... */
618 spin_unlock(&acm->lock);
David Brownell1f1ba112008-08-06 18:49:57 -0700619 status = usb_ep_queue(ep, req, GFP_ATOMIC);
David Brownelle50ae572008-11-12 11:35:13 -0800620 spin_lock(&acm->lock);
621
David Brownell1f1ba112008-08-06 18:49:57 -0700622 if (status < 0) {
623 ERROR(acm->port.func.config->cdev,
624 "acm ttyGS%d can't notify serial state, %d\n",
625 acm->port_num, status);
626 acm->notify_req = req;
627 }
628
629 return status;
630}
631
632static int acm_notify_serial_state(struct f_acm *acm)
633{
634 struct usb_composite_dev *cdev = acm->port.func.config->cdev;
635 int status;
636
637 spin_lock(&acm->lock);
638 if (acm->notify_req) {
639 DBG(cdev, "acm ttyGS%d serial state %04x\n",
640 acm->port_num, acm->serial_state);
641 status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
642 0, &acm->serial_state, sizeof(acm->serial_state));
643 } else {
644 acm->pending = true;
645 status = 0;
646 }
647 spin_unlock(&acm->lock);
648 return status;
649}
650
651static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
652{
653 struct f_acm *acm = req->context;
654 u8 doit = false;
655
656 /* on this call path we do NOT hold the port spinlock,
657 * which is why ACM needs its own spinlock
658 */
659 spin_lock(&acm->lock);
660 if (req->status != -ESHUTDOWN)
661 doit = acm->pending;
662 acm->notify_req = req;
663 spin_unlock(&acm->lock);
664
665 if (doit)
666 acm_notify_serial_state(acm);
667}
668
669/* connect == the TTY link is open */
670
671static void acm_connect(struct gserial *port)
672{
673 struct f_acm *acm = port_to_acm(port);
674
675 acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
676 acm_notify_serial_state(acm);
677}
678
679static void acm_disconnect(struct gserial *port)
680{
681 struct f_acm *acm = port_to_acm(port);
682
683 acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
684 acm_notify_serial_state(acm);
685}
686
687static int acm_send_break(struct gserial *port, int duration)
688{
689 struct f_acm *acm = port_to_acm(port);
690 u16 state;
691
692 state = acm->serial_state;
693 state &= ~ACM_CTRL_BRK;
694 if (duration)
695 state |= ACM_CTRL_BRK;
696
697 acm->serial_state = state;
698 return acm_notify_serial_state(acm);
699}
700
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701static int acm_send_modem_ctrl_bits(struct gserial *port, int ctrl_bits)
702{
703 struct f_acm *acm = port_to_acm(port);
704
705 acm->serial_state = ctrl_bits;
706
707 return acm_notify_serial_state(acm);
708}
709
David Brownell1f1ba112008-08-06 18:49:57 -0700710/*-------------------------------------------------------------------------*/
711
David Brownell4d5a73d2008-06-19 18:18:40 -0700712/* ACM function driver setup/binding */
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200713static int
David Brownell4d5a73d2008-06-19 18:18:40 -0700714acm_bind(struct usb_configuration *c, struct usb_function *f)
715{
716 struct usb_composite_dev *cdev = c->cdev;
717 struct f_acm *acm = func_to_acm(f);
718 int status;
719 struct usb_ep *ep;
720
721 /* allocate instance-specific interface IDs, and patch descriptors */
722 status = usb_interface_id(c, f);
723 if (status < 0)
724 goto fail;
725 acm->ctrl_id = status;
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100726 acm_iad_descriptor.bFirstInterface = status;
David Brownell4d5a73d2008-06-19 18:18:40 -0700727
728 acm_control_interface_desc.bInterfaceNumber = status;
729 acm_union_desc .bMasterInterface0 = status;
730
731 status = usb_interface_id(c, f);
732 if (status < 0)
733 goto fail;
734 acm->data_id = status;
735
736 acm_data_interface_desc.bInterfaceNumber = status;
737 acm_union_desc.bSlaveInterface0 = status;
738 acm_call_mgmt_descriptor.bDataInterface = status;
739
740 status = -ENODEV;
741
742 /* allocate instance-specific endpoints */
743 ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
744 if (!ep)
745 goto fail;
746 acm->port.in = ep;
747 ep->driver_data = cdev; /* claim */
748
749 ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
750 if (!ep)
751 goto fail;
752 acm->port.out = ep;
753 ep->driver_data = cdev; /* claim */
754
755 ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
756 if (!ep)
757 goto fail;
758 acm->notify = ep;
759 ep->driver_data = cdev; /* claim */
760
David Brownell1f1ba112008-08-06 18:49:57 -0700761 /* allocate notification */
762 acm->notify_req = gs_alloc_req(ep,
763 sizeof(struct usb_cdc_notification) + 2,
764 GFP_KERNEL);
765 if (!acm->notify_req)
766 goto fail;
767
768 acm->notify_req->complete = acm_cdc_notify_complete;
769 acm->notify_req->context = acm;
770
Tatyana Brokhmanea2a1df72011-06-28 16:33:50 +0300771 /* copy descriptors */
David Brownell4d5a73d2008-06-19 18:18:40 -0700772 f->descriptors = usb_copy_descriptors(acm_fs_function);
David Brownell1f1ba112008-08-06 18:49:57 -0700773 if (!f->descriptors)
774 goto fail;
David Brownell4d5a73d2008-06-19 18:18:40 -0700775
David Brownell4d5a73d2008-06-19 18:18:40 -0700776 /* support all relevant hardware speeds... we expect that when
777 * hardware is dual speed, all bulk-capable endpoints work at
778 * both speeds
779 */
780 if (gadget_is_dualspeed(c->cdev->gadget)) {
781 acm_hs_in_desc.bEndpointAddress =
782 acm_fs_in_desc.bEndpointAddress;
783 acm_hs_out_desc.bEndpointAddress =
784 acm_fs_out_desc.bEndpointAddress;
785 acm_hs_notify_desc.bEndpointAddress =
786 acm_fs_notify_desc.bEndpointAddress;
787
Tatyana Brokhmanea2a1df72011-06-28 16:33:50 +0300788 /* copy descriptors */
David Brownell4d5a73d2008-06-19 18:18:40 -0700789 f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
Rajkumar Raghupathyc636cb42012-01-25 16:15:04 +0530790 if (!f->hs_descriptors)
791 goto fail;
David Brownell4d5a73d2008-06-19 18:18:40 -0700792 }
Sebastian Andrzej Siewior6fecfb02012-02-06 18:46:36 +0100793 if (gadget_is_superspeed(c->cdev->gadget)) {
794 acm_ss_in_desc.bEndpointAddress =
795 acm_fs_in_desc.bEndpointAddress;
796 acm_ss_out_desc.bEndpointAddress =
797 acm_fs_out_desc.bEndpointAddress;
798
799 /* copy descriptors, and track endpoint copies */
800 f->ss_descriptors = usb_copy_descriptors(acm_ss_function);
801 if (!f->ss_descriptors)
802 goto fail;
803 }
David Brownell4d5a73d2008-06-19 18:18:40 -0700804
David Brownell4d5a73d2008-06-19 18:18:40 -0700805 DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
806 acm->port_num,
Sebastian Andrzej Siewior6fecfb02012-02-06 18:46:36 +0100807 gadget_is_superspeed(c->cdev->gadget) ? "super" :
David Brownell4d5a73d2008-06-19 18:18:40 -0700808 gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
809 acm->port.in->name, acm->port.out->name,
810 acm->notify->name);
811 return 0;
812
813fail:
Rajkumar Raghupathyc636cb42012-01-25 16:15:04 +0530814 if (f->hs_descriptors)
815 usb_free_descriptors(f->hs_descriptors);
816 if (f->descriptors)
817 usb_free_descriptors(f->descriptors);
818
David Brownell1f1ba112008-08-06 18:49:57 -0700819 if (acm->notify_req)
820 gs_free_req(acm->notify, acm->notify_req);
821
David Brownell4d5a73d2008-06-19 18:18:40 -0700822 /* we might as well release our claims on endpoints */
823 if (acm->notify)
824 acm->notify->driver_data = NULL;
825 if (acm->port.out)
826 acm->port.out->driver_data = NULL;
827 if (acm->port.in)
828 acm->port.in->driver_data = NULL;
829
830 ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
831
832 return status;
833}
834
835static void
836acm_unbind(struct usb_configuration *c, struct usb_function *f)
837{
David Brownell1f1ba112008-08-06 18:49:57 -0700838 struct f_acm *acm = func_to_acm(f);
839
David Brownell4d5a73d2008-06-19 18:18:40 -0700840 if (gadget_is_dualspeed(c->cdev->gadget))
841 usb_free_descriptors(f->hs_descriptors);
Sebastian Andrzej Siewior6fecfb02012-02-06 18:46:36 +0100842 if (gadget_is_superspeed(c->cdev->gadget))
843 usb_free_descriptors(f->ss_descriptors);
David Brownell4d5a73d2008-06-19 18:18:40 -0700844 usb_free_descriptors(f->descriptors);
David Brownell1f1ba112008-08-06 18:49:57 -0700845 gs_free_req(acm->notify, acm->notify_req);
John Michelau677ba872010-11-08 18:05:37 -0600846 kfree(acm->port.func.name);
David Brownell1f1ba112008-08-06 18:49:57 -0700847 kfree(acm);
David Brownell4d5a73d2008-06-19 18:18:40 -0700848}
849
850/* Some controllers can't support CDC ACM ... */
851static inline bool can_support_cdc(struct usb_configuration *c)
852{
David Brownell4d5a73d2008-06-19 18:18:40 -0700853 /* everything else is *probably* fine ... */
854 return true;
855}
856
857/**
858 * acm_bind_config - add a CDC ACM function to a configuration
859 * @c: the configuration to support the CDC ACM instance
860 * @port_num: /dev/ttyGS* port this interface will use
861 * Context: single threaded during gadget setup
862 *
863 * Returns zero on success, else negative errno.
864 *
865 * Caller must have called @gserial_setup() with enough ports to
866 * handle all the ones it binds. Caller is also responsible
867 * for calling @gserial_cleanup() before module unload.
868 */
Michal Nazarewicz28824b12010-05-05 12:53:13 +0200869int acm_bind_config(struct usb_configuration *c, u8 port_num)
David Brownell4d5a73d2008-06-19 18:18:40 -0700870{
871 struct f_acm *acm;
872 int status;
873
874 if (!can_support_cdc(c))
875 return -EINVAL;
876
877 /* REVISIT might want instance-specific strings to help
878 * distinguish instances ...
879 */
880
881 /* maybe allocate device-global string IDs, and patch descriptors */
882 if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
883 status = usb_string_id(c->cdev);
884 if (status < 0)
885 return status;
886 acm_string_defs[ACM_CTRL_IDX].id = status;
887
888 acm_control_interface_desc.iInterface = status;
889
890 status = usb_string_id(c->cdev);
891 if (status < 0)
892 return status;
893 acm_string_defs[ACM_DATA_IDX].id = status;
894
895 acm_data_interface_desc.iInterface = status;
Michal Nazarewiczb97503f2009-10-28 16:57:30 +0100896
897 status = usb_string_id(c->cdev);
898 if (status < 0)
899 return status;
900 acm_string_defs[ACM_IAD_IDX].id = status;
901
902 acm_iad_descriptor.iFunction = status;
David Brownell4d5a73d2008-06-19 18:18:40 -0700903 }
904
905 /* allocate and initialize one new instance */
906 acm = kzalloc(sizeof *acm, GFP_KERNEL);
907 if (!acm)
908 return -ENOMEM;
909
David Brownell1f1ba112008-08-06 18:49:57 -0700910 spin_lock_init(&acm->lock);
911
David Brownell4d5a73d2008-06-19 18:18:40 -0700912 acm->port_num = port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700913 acm->transport = gacm_ports[port_num].transport;
David Brownell4d5a73d2008-06-19 18:18:40 -0700914
David Brownell1f1ba112008-08-06 18:49:57 -0700915 acm->port.connect = acm_connect;
916 acm->port.disconnect = acm_disconnect;
917 acm->port.send_break = acm_send_break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918 acm->port.send_modem_ctrl_bits = acm_send_modem_ctrl_bits;
David Brownell1f1ba112008-08-06 18:49:57 -0700919
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700920 acm->port.func.name = kasprintf(GFP_KERNEL, "acm%u", port_num + 1);
John Michelau677ba872010-11-08 18:05:37 -0600921 if (!acm->port.func.name) {
922 kfree(acm);
923 return -ENOMEM;
924 }
David Brownell4d5a73d2008-06-19 18:18:40 -0700925 acm->port.func.strings = acm_strings;
926 /* descriptors are per-instance copies */
927 acm->port.func.bind = acm_bind;
928 acm->port.func.unbind = acm_unbind;
929 acm->port.func.set_alt = acm_set_alt;
930 acm->port.func.setup = acm_setup;
931 acm->port.func.disable = acm_disable;
932
933 status = usb_add_function(c, &acm->port.func);
934 if (status)
935 kfree(acm);
936 return status;
937}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938
Anji jonnala92be1b42011-12-19 09:44:41 +0530939/**
940 * acm_init_port - bind a acm_port to its transport
941 */
942static int acm_init_port(int port_num, const char *name)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943{
Anji jonnala92be1b42011-12-19 09:44:41 +0530944 enum transport_type transport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945
Anji jonnala92be1b42011-12-19 09:44:41 +0530946 if (port_num >= GSERIAL_NO_PORTS)
947 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700948
Anji jonnala92be1b42011-12-19 09:44:41 +0530949 transport = str_to_xport(name);
950 pr_debug("%s, port:%d, transport:%s\n", __func__,
951 port_num, xport_to_str(transport));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952
Anji jonnala92be1b42011-12-19 09:44:41 +0530953 gacm_ports[port_num].transport = transport;
954 gacm_ports[port_num].port_num = port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955
Anji jonnala92be1b42011-12-19 09:44:41 +0530956 switch (transport) {
957 case USB_GADGET_XPORT_TTY:
958 gacm_ports[port_num].client_port_num = no_acm_tty_ports;
959 no_acm_tty_ports++;
960 break;
961 case USB_GADGET_XPORT_SDIO:
962 gacm_ports[port_num].client_port_num = no_acm_sdio_ports;
963 no_acm_sdio_ports++;
964 break;
965 case USB_GADGET_XPORT_SMD:
966 gacm_ports[port_num].client_port_num = no_acm_smd_ports;
967 no_acm_smd_ports++;
968 break;
969 default:
970 pr_err("%s: Un-supported transport transport: %u\n",
971 __func__, gacm_ports[port_num].transport);
972 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973 }
974
Anji jonnala92be1b42011-12-19 09:44:41 +0530975 nr_acm_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700976
977 return 0;
978}