blob: fcbc75ccbf2c994c52a2af69d0af93d05e5bd370 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
Anna Perel97b8c222012-01-18 10:08:14 +02002 * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/slab.h>
15#include <linux/kernel.h>
16#include <linux/device.h>
17#include <linux/usb/android_composite.h>
18#include <linux/spinlock.h>
19
Hemant Kumar1b820d52011-11-03 15:08:28 -070020#include <mach/usb_gadget_xport.h>
Ofir Cohena1c2a872011-12-14 10:26:34 +020021
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include "u_rmnet.h"
23#include "gadget_chips.h"
24
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025#define RMNET_NOTIFY_INTERVAL 5
26#define RMNET_MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification)
27
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028
29#define ACM_CTRL_DTR (1 << 0)
30
31/* TODO: use separate structures for data and
32 * control paths
33 */
34struct f_rmnet {
35 struct grmnet port;
36 int ifc_id;
37 u8 port_num;
38 atomic_t online;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -070039 atomic_t ctrl_online;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040 struct usb_composite_dev *cdev;
41
42 spinlock_t lock;
43
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044 /* usb eps*/
45 struct usb_ep *notify;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046 struct usb_request *notify_req;
47
48 /* control info */
49 struct list_head cpkt_resp_q;
50 atomic_t notify_count;
51 unsigned long cpkts_len;
52};
53
Anna Perel21515162012-02-02 20:50:02 +020054#define NR_RMNET_PORTS 3
Manu Gautam2b0234a2011-09-07 16:47:52 +053055static unsigned int nr_rmnet_ports;
Hemant Kumar1b820d52011-11-03 15:08:28 -070056static unsigned int no_ctrl_smd_ports;
Jack Pham427f6922011-11-23 19:42:00 -080057static unsigned int no_ctrl_hsic_ports;
Hemant Kumar1b820d52011-11-03 15:08:28 -070058static unsigned int no_data_bam_ports;
Ofir Cohena1c2a872011-12-14 10:26:34 +020059static unsigned int no_data_bam2bam_ports;
Jack Pham427f6922011-11-23 19:42:00 -080060static unsigned int no_data_hsic_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061static struct rmnet_ports {
Hemant Kumar1b820d52011-11-03 15:08:28 -070062 enum transport_type data_xport;
63 enum transport_type ctrl_xport;
64 unsigned data_xport_num;
65 unsigned ctrl_xport_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066 unsigned port_num;
67 struct f_rmnet *port;
Manu Gautam2b0234a2011-09-07 16:47:52 +053068} rmnet_ports[NR_RMNET_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069
70static struct usb_interface_descriptor rmnet_interface_desc = {
71 .bLength = USB_DT_INTERFACE_SIZE,
72 .bDescriptorType = USB_DT_INTERFACE,
73 .bNumEndpoints = 3,
74 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
75 .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
76 .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
77 /* .iInterface = DYNAMIC */
78};
79
80/* Full speed support */
81static struct usb_endpoint_descriptor rmnet_fs_notify_desc = {
82 .bLength = USB_DT_ENDPOINT_SIZE,
83 .bDescriptorType = USB_DT_ENDPOINT,
84 .bEndpointAddress = USB_DIR_IN,
85 .bmAttributes = USB_ENDPOINT_XFER_INT,
86 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
87 .bInterval = 1 << RMNET_NOTIFY_INTERVAL,
88};
89
90static struct usb_endpoint_descriptor rmnet_fs_in_desc = {
91 .bLength = USB_DT_ENDPOINT_SIZE,
92 .bDescriptorType = USB_DT_ENDPOINT,
93 .bEndpointAddress = USB_DIR_IN,
94 .bmAttributes = USB_ENDPOINT_XFER_BULK,
95 .wMaxPacketSize = __constant_cpu_to_le16(64),
96};
97
98static struct usb_endpoint_descriptor rmnet_fs_out_desc = {
99 .bLength = USB_DT_ENDPOINT_SIZE,
100 .bDescriptorType = USB_DT_ENDPOINT,
101 .bEndpointAddress = USB_DIR_OUT,
102 .bmAttributes = USB_ENDPOINT_XFER_BULK,
103 .wMaxPacketSize = __constant_cpu_to_le16(64),
104};
105
106static struct usb_descriptor_header *rmnet_fs_function[] = {
107 (struct usb_descriptor_header *) &rmnet_interface_desc,
108 (struct usb_descriptor_header *) &rmnet_fs_notify_desc,
109 (struct usb_descriptor_header *) &rmnet_fs_in_desc,
110 (struct usb_descriptor_header *) &rmnet_fs_out_desc,
111 NULL,
112};
113
114/* High speed support */
115static struct usb_endpoint_descriptor rmnet_hs_notify_desc = {
116 .bLength = USB_DT_ENDPOINT_SIZE,
117 .bDescriptorType = USB_DT_ENDPOINT,
118 .bEndpointAddress = USB_DIR_IN,
119 .bmAttributes = USB_ENDPOINT_XFER_INT,
120 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
121 .bInterval = RMNET_NOTIFY_INTERVAL + 4,
122};
123
124static struct usb_endpoint_descriptor rmnet_hs_in_desc = {
125 .bLength = USB_DT_ENDPOINT_SIZE,
126 .bDescriptorType = USB_DT_ENDPOINT,
127 .bEndpointAddress = USB_DIR_IN,
128 .bmAttributes = USB_ENDPOINT_XFER_BULK,
129 .wMaxPacketSize = __constant_cpu_to_le16(512),
130};
131
132static struct usb_endpoint_descriptor rmnet_hs_out_desc = {
133 .bLength = USB_DT_ENDPOINT_SIZE,
134 .bDescriptorType = USB_DT_ENDPOINT,
135 .bEndpointAddress = USB_DIR_OUT,
136 .bmAttributes = USB_ENDPOINT_XFER_BULK,
137 .wMaxPacketSize = __constant_cpu_to_le16(512),
138};
139
140static struct usb_descriptor_header *rmnet_hs_function[] = {
141 (struct usb_descriptor_header *) &rmnet_interface_desc,
142 (struct usb_descriptor_header *) &rmnet_hs_notify_desc,
143 (struct usb_descriptor_header *) &rmnet_hs_in_desc,
144 (struct usb_descriptor_header *) &rmnet_hs_out_desc,
145 NULL,
146};
147
148/* String descriptors */
149
150static struct usb_string rmnet_string_defs[] = {
151 [0].s = "RmNet",
152 { } /* end of list */
153};
154
155static struct usb_gadget_strings rmnet_string_table = {
156 .language = 0x0409, /* en-us */
157 .strings = rmnet_string_defs,
158};
159
160static struct usb_gadget_strings *rmnet_strings[] = {
161 &rmnet_string_table,
162 NULL,
163};
164
165/* ------- misc functions --------------------*/
166
167static inline struct f_rmnet *func_to_rmnet(struct usb_function *f)
168{
169 return container_of(f, struct f_rmnet, port.func);
170}
171
172static inline struct f_rmnet *port_to_rmnet(struct grmnet *r)
173{
174 return container_of(r, struct f_rmnet, port);
175}
176
177static struct usb_request *
178frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
179{
180 struct usb_request *req;
181
182 req = usb_ep_alloc_request(ep, flags);
183 if (!req)
184 return ERR_PTR(-ENOMEM);
185
186 req->buf = kmalloc(len, flags);
187 if (!req->buf) {
188 usb_ep_free_request(ep, req);
189 return ERR_PTR(-ENOMEM);
190 }
191
192 req->length = len;
193
194 return req;
195}
196
197void frmnet_free_req(struct usb_ep *ep, struct usb_request *req)
198{
199 kfree(req->buf);
200 usb_ep_free_request(ep, req);
201}
202
203static struct rmnet_ctrl_pkt *rmnet_alloc_ctrl_pkt(unsigned len, gfp_t flags)
204{
205 struct rmnet_ctrl_pkt *pkt;
206
207 pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
208 if (!pkt)
209 return ERR_PTR(-ENOMEM);
210
211 pkt->buf = kmalloc(len, flags);
212 if (!pkt->buf) {
213 kfree(pkt);
214 return ERR_PTR(-ENOMEM);
215 }
216 pkt->len = len;
217
218 return pkt;
219}
220
221static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
222{
223 kfree(pkt->buf);
224 kfree(pkt);
225}
226
227/* -------------------------------------------*/
228
Hemant Kumar1b820d52011-11-03 15:08:28 -0700229static int rmnet_gport_setup(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230{
Jack Pham427f6922011-11-23 19:42:00 -0800231 int ret;
232 int port_idx;
233 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234
Ofir Cohena1c2a872011-12-14 10:26:34 +0200235 pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u"
236 " smd ports: %u ctrl hsic ports: %u"
237 " nr_rmnet_ports: %u\n",
238 __func__, no_data_bam_ports, no_data_bam2bam_ports,
239 no_data_hsic_ports, no_ctrl_smd_ports,
240 no_ctrl_hsic_ports, nr_rmnet_ports);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241
Ofir Cohena1c2a872011-12-14 10:26:34 +0200242 if (no_data_bam_ports || no_data_bam2bam_ports) {
243 ret = gbam_setup(no_data_bam_ports,
244 no_data_bam2bam_ports);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700245 if (ret)
246 return ret;
247 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248
Hemant Kumar1b820d52011-11-03 15:08:28 -0700249 if (no_ctrl_smd_ports) {
250 ret = gsmd_ctrl_setup(no_ctrl_smd_ports);
251 if (ret)
252 return ret;
253 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254
Jack Pham427f6922011-11-23 19:42:00 -0800255 if (no_data_hsic_ports) {
256 port_idx = ghsic_data_setup(no_data_hsic_ports,
257 USB_GADGET_RMNET);
258 if (port_idx < 0)
259 return port_idx;
260 for (i = 0; i < nr_rmnet_ports; i++) {
261 if (rmnet_ports[i].data_xport ==
262 USB_GADGET_XPORT_HSIC) {
263 rmnet_ports[i].data_xport_num = port_idx;
264 port_idx++;
265 }
266 }
267 }
268
269 if (no_ctrl_hsic_ports) {
270 port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
271 USB_GADGET_RMNET);
272 if (port_idx < 0)
273 return port_idx;
274 for (i = 0; i < nr_rmnet_ports; i++) {
275 if (rmnet_ports[i].ctrl_xport ==
276 USB_GADGET_XPORT_HSIC) {
277 rmnet_ports[i].ctrl_xport_num = port_idx;
278 port_idx++;
279 }
280 }
281 }
282
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283 return 0;
284}
285
Manu Gautam2b0234a2011-09-07 16:47:52 +0530286static int gport_rmnet_connect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700288 int ret;
289 unsigned port_num;
290 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
291 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292
Hemant Kumar1b820d52011-11-03 15:08:28 -0700293 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
294 __func__, xport_to_str(cxport), xport_to_str(dxport),
295 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296
Hemant Kumar1b820d52011-11-03 15:08:28 -0700297 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
298 switch (cxport) {
299 case USB_GADGET_XPORT_SMD:
300 ret = gsmd_ctrl_connect(&dev->port, port_num);
301 if (ret) {
302 pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
303 __func__, ret);
304 return ret;
305 }
306 break;
Jack Pham427f6922011-11-23 19:42:00 -0800307 case USB_GADGET_XPORT_HSIC:
308 ret = ghsic_ctrl_connect(&dev->port, port_num);
309 if (ret) {
310 pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
311 __func__, ret);
312 return ret;
313 }
314 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700315 case USB_GADGET_XPORT_NONE:
316 break;
317 default:
318 pr_err("%s: Un-supported transport: %s\n", __func__,
319 xport_to_str(cxport));
320 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 }
322
Hemant Kumar1b820d52011-11-03 15:08:28 -0700323 port_num = rmnet_ports[dev->port_num].data_xport_num;
324 switch (dxport) {
325 case USB_GADGET_XPORT_BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200326 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200327 ret = gbam_connect(&dev->port, port_num,
Anna Perel21515162012-02-02 20:50:02 +0200328 dxport, port_num);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700329 if (ret) {
330 pr_err("%s: gbam_connect failed: err:%d\n",
331 __func__, ret);
332 gsmd_ctrl_disconnect(&dev->port, port_num);
333 return ret;
334 }
335 break;
Jack Pham427f6922011-11-23 19:42:00 -0800336 case USB_GADGET_XPORT_HSIC:
337 ret = ghsic_data_connect(&dev->port, port_num);
338 if (ret) {
339 pr_err("%s: ghsic_data_connect failed: err:%d\n",
340 __func__, ret);
341 ghsic_ctrl_disconnect(&dev->port, port_num);
342 return ret;
343 }
344 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700345 case USB_GADGET_XPORT_NONE:
346 break;
347 default:
348 pr_err("%s: Un-supported transport: %s\n", __func__,
349 xport_to_str(dxport));
350 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 }
352
353 return 0;
354}
355
Manu Gautam2b0234a2011-09-07 16:47:52 +0530356static int gport_rmnet_disconnect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700358 unsigned port_num;
359 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
360 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361
Hemant Kumar1b820d52011-11-03 15:08:28 -0700362 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
363 __func__, xport_to_str(cxport), xport_to_str(dxport),
364 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365
Hemant Kumar1b820d52011-11-03 15:08:28 -0700366 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
367 switch (cxport) {
368 case USB_GADGET_XPORT_SMD:
369 gsmd_ctrl_disconnect(&dev->port, port_num);
370 break;
Jack Pham427f6922011-11-23 19:42:00 -0800371 case USB_GADGET_XPORT_HSIC:
372 ghsic_ctrl_disconnect(&dev->port, port_num);
373 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700374 case USB_GADGET_XPORT_NONE:
375 break;
376 default:
377 pr_err("%s: Un-supported transport: %s\n", __func__,
378 xport_to_str(cxport));
379 return -ENODEV;
380 }
381
382 port_num = rmnet_ports[dev->port_num].data_xport_num;
383 switch (dxport) {
384 case USB_GADGET_XPORT_BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200385 case USB_GADGET_XPORT_BAM2BAM:
386 gbam_disconnect(&dev->port, port_num, dxport);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700387 break;
Jack Pham427f6922011-11-23 19:42:00 -0800388 case USB_GADGET_XPORT_HSIC:
389 ghsic_data_disconnect(&dev->port, port_num);
390 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700391 case USB_GADGET_XPORT_NONE:
392 break;
393 default:
394 pr_err("%s: Un-supported transport: %s\n", __func__,
395 xport_to_str(dxport));
396 return -ENODEV;
397 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398
399 return 0;
400}
401
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
403{
404 struct f_rmnet *dev = func_to_rmnet(f);
405
406 pr_debug("%s: portno:%d\n", __func__, dev->port_num);
407
408 if (gadget_is_dualspeed(c->cdev->gadget))
409 usb_free_descriptors(f->hs_descriptors);
410 usb_free_descriptors(f->descriptors);
411
412 frmnet_free_req(dev->notify, dev->notify_req);
413
Manu Gautamdd4222b2011-09-09 15:06:05 +0530414 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415}
416
Amit Blaye5bb35e2012-05-08 20:38:20 +0300417static void frmnet_suspend(struct usb_function *f)
418{
419 struct f_rmnet *dev = func_to_rmnet(f);
420 unsigned port_num;
421 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
422
423 pr_debug("%s: data xport: %s dev: %p portno: %d\n",
424 __func__, xport_to_str(dxport),
425 dev, dev->port_num);
426
427 port_num = rmnet_ports[dev->port_num].data_xport_num;
428 switch (dxport) {
429 case USB_GADGET_XPORT_BAM:
430 break;
431 case USB_GADGET_XPORT_BAM2BAM:
432 gbam_suspend(&dev->port, port_num, dxport);
433 break;
434 case USB_GADGET_XPORT_HSIC:
435 break;
436 case USB_GADGET_XPORT_NONE:
437 break;
438 default:
439 pr_err("%s: Un-supported transport: %s\n", __func__,
440 xport_to_str(dxport));
441 }
442}
443
444static void frmnet_resume(struct usb_function *f)
445{
446 struct f_rmnet *dev = func_to_rmnet(f);
447 unsigned port_num;
448 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
449
450 pr_debug("%s: data xport: %s dev: %p portno: %d\n",
451 __func__, xport_to_str(dxport),
452 dev, dev->port_num);
453
454 port_num = rmnet_ports[dev->port_num].data_xport_num;
455 switch (dxport) {
456 case USB_GADGET_XPORT_BAM:
457 break;
458 case USB_GADGET_XPORT_BAM2BAM:
459 gbam_resume(&dev->port, port_num, dxport);
460 break;
461 case USB_GADGET_XPORT_HSIC:
462 break;
463 case USB_GADGET_XPORT_NONE:
464 break;
465 default:
466 pr_err("%s: Un-supported transport: %s\n", __func__,
467 xport_to_str(dxport));
468 }
469}
470
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471static void frmnet_disable(struct usb_function *f)
472{
473 struct f_rmnet *dev = func_to_rmnet(f);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700474 unsigned long flags;
475 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476
477 pr_debug("%s: port#%d\n", __func__, dev->port_num);
478
479 usb_ep_disable(dev->notify);
Anna Perel97b8c222012-01-18 10:08:14 +0200480 dev->notify->driver_data = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700481
482 atomic_set(&dev->online, 0);
483
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700484 spin_lock_irqsave(&dev->lock, flags);
485 while (!list_empty(&dev->cpkt_resp_q)) {
486 cpkt = list_first_entry(&dev->cpkt_resp_q,
487 struct rmnet_ctrl_pkt, list);
488
489 list_del(&cpkt->list);
490 rmnet_free_ctrl_pkt(cpkt);
491 }
492 atomic_set(&dev->notify_count, 0);
493 spin_unlock_irqrestore(&dev->lock, flags);
494
Manu Gautam2b0234a2011-09-07 16:47:52 +0530495 gport_rmnet_disconnect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700496}
497
498static int
499frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
500{
501 struct f_rmnet *dev = func_to_rmnet(f);
502 struct usb_composite_dev *cdev = dev->cdev;
503 int ret;
504
505 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
506
507 if (dev->notify->driver_data) {
508 pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
509 usb_ep_disable(dev->notify);
510 }
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200511
512 ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
513 if (ret) {
514 dev->notify->desc = NULL;
515 ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
516 dev->notify->name, ret);
517 return ret;
518 }
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300519 ret = usb_ep_enable(dev->notify);
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200520
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 if (ret) {
522 pr_err("%s: usb ep#%s enable failed, err#%d\n",
523 __func__, dev->notify->name, ret);
524 return ret;
525 }
526 dev->notify->driver_data = dev;
527
Bar Weiner0fc137a2012-03-28 16:58:09 +0200528 if (!dev->port.in->driver_data) {
529 if (config_ep_by_speed(cdev->gadget, f, dev->port.in) ||
530 config_ep_by_speed(cdev->gadget, f, dev->port.out)) {
531 dev->port.in->desc = NULL;
532 dev->port.out->desc = NULL;
533 return -EINVAL;
534 }
535 ret = gport_rmnet_connect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536 }
537
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 atomic_set(&dev->online, 1);
539
540 return ret;
541}
542
543static void frmnet_ctrl_response_available(struct f_rmnet *dev)
544{
545 struct usb_request *req = dev->notify_req;
546 struct usb_cdc_notification *event;
547 unsigned long flags;
548 int ret;
549
550 pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num);
551
552 spin_lock_irqsave(&dev->lock, flags);
553 if (!atomic_read(&dev->online) || !req || !req->buf) {
554 spin_unlock_irqrestore(&dev->lock, flags);
555 return;
556 }
557
558 if (atomic_inc_return(&dev->notify_count) != 1) {
559 spin_unlock_irqrestore(&dev->lock, flags);
560 return;
561 }
562
563 event = req->buf;
564 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
565 | USB_RECIP_INTERFACE;
566 event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
567 event->wValue = cpu_to_le16(0);
568 event->wIndex = cpu_to_le16(dev->ifc_id);
569 event->wLength = cpu_to_le16(0);
570 spin_unlock_irqrestore(&dev->lock, flags);
571
572 ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
573 if (ret) {
574 atomic_dec(&dev->notify_count);
575 pr_debug("ep enqueue error %d\n", ret);
576 }
577}
578
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700579static void frmnet_connect(struct grmnet *gr)
580{
581 struct f_rmnet *dev;
582
583 if (!gr) {
584 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
585 return;
586 }
587
588 dev = port_to_rmnet(gr);
589
590 atomic_set(&dev->ctrl_online, 1);
591}
592
593static void frmnet_disconnect(struct grmnet *gr)
594{
595 struct f_rmnet *dev;
596 unsigned long flags;
597 struct usb_cdc_notification *event;
598 int status;
599 struct rmnet_ctrl_pkt *cpkt;
600
601 if (!gr) {
602 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
603 return;
604 }
605
606 dev = port_to_rmnet(gr);
607
608 atomic_set(&dev->ctrl_online, 0);
609
610 if (!atomic_read(&dev->online)) {
611 pr_debug("%s: nothing to do\n", __func__);
612 return;
613 }
614
615 usb_ep_fifo_flush(dev->notify);
616
617 event = dev->notify_req->buf;
618 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
619 | USB_RECIP_INTERFACE;
620 event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
621 event->wValue = cpu_to_le16(0);
622 event->wIndex = cpu_to_le16(dev->ifc_id);
623 event->wLength = cpu_to_le16(0);
624
Vamsi Krishna188078d2011-10-26 15:09:55 -0700625 status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700626 if (status < 0) {
627 if (!atomic_read(&dev->online))
628 return;
629 pr_err("%s: rmnet notify ep enqueue error %d\n",
630 __func__, status);
631 }
632
633 spin_lock_irqsave(&dev->lock, flags);
634 while (!list_empty(&dev->cpkt_resp_q)) {
635 cpkt = list_first_entry(&dev->cpkt_resp_q,
636 struct rmnet_ctrl_pkt, list);
637
638 list_del(&cpkt->list);
639 rmnet_free_ctrl_pkt(cpkt);
640 }
641 atomic_set(&dev->notify_count, 0);
642 spin_unlock_irqrestore(&dev->lock, flags);
643
644}
645
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700646static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700647frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648{
649 struct f_rmnet *dev;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700650 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651 unsigned long flags;
652
Hemant Kumarf60c0252011-11-03 12:37:07 -0700653 if (!gr || !buf) {
654 pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
655 __func__, gr, buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656 return -ENODEV;
657 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700658 cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
659 if (IS_ERR(cpkt)) {
660 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
661 return -ENOMEM;
662 }
663 memcpy(cpkt->buf, buf, len);
664 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700665
666 dev = port_to_rmnet(gr);
667
668 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
669
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700670 if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700671 rmnet_free_ctrl_pkt(cpkt);
672 return 0;
673 }
674
675 spin_lock_irqsave(&dev->lock, flags);
Chiranjeevi Velempati263e6c82011-11-11 23:07:36 +0530676 list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677 spin_unlock_irqrestore(&dev->lock, flags);
678
679 frmnet_ctrl_response_available(dev);
680
681 return 0;
682}
683
684static void
685frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req)
686{
687 struct f_rmnet *dev = req->context;
688 struct usb_composite_dev *cdev;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700689 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690
691 if (!dev) {
692 pr_err("%s: rmnet dev is null\n", __func__);
693 return;
694 }
695
696 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
697
698 cdev = dev->cdev;
699
Hemant Kumar1b820d52011-11-03 15:08:28 -0700700 if (dev->port.send_encap_cmd) {
701 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
702 dev->port.send_encap_cmd(port_num, req->buf, req->actual);
703 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700704}
705
706static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
707{
708 struct f_rmnet *dev = req->context;
709 int status = req->status;
710
711 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
712
713 switch (status) {
714 case -ECONNRESET:
715 case -ESHUTDOWN:
716 /* connection gone */
717 atomic_set(&dev->notify_count, 0);
718 break;
719 default:
720 pr_err("rmnet notify ep error %d\n", status);
721 /* FALLTHROUGH */
722 case 0:
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700723 if (!atomic_read(&dev->ctrl_online))
724 break;
725
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726 if (atomic_dec_and_test(&dev->notify_count))
727 break;
728
729 status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
730 if (status) {
731 atomic_dec(&dev->notify_count);
732 pr_debug("ep enqueue error %d\n", status);
733 }
734 break;
735 }
736}
737
738static int
739frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
740{
741 struct f_rmnet *dev = func_to_rmnet(f);
742 struct usb_composite_dev *cdev = dev->cdev;
743 struct usb_request *req = cdev->req;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700744 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700745 u16 w_index = le16_to_cpu(ctrl->wIndex);
746 u16 w_value = le16_to_cpu(ctrl->wValue);
747 u16 w_length = le16_to_cpu(ctrl->wLength);
748 int ret = -EOPNOTSUPP;
749
750 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
751
752 if (!atomic_read(&dev->online)) {
753 pr_debug("%s: usb cable is not connected\n", __func__);
754 return -ENOTCONN;
755 }
756
757 switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
758
759 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
760 | USB_CDC_SEND_ENCAPSULATED_COMMAND:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 ret = w_length;
762 req->complete = frmnet_cmd_complete;
763 req->context = dev;
764 break;
765
766
767 case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
768 | USB_CDC_GET_ENCAPSULATED_RESPONSE:
769 if (w_value)
770 goto invalid;
771 else {
772 unsigned len;
773 struct rmnet_ctrl_pkt *cpkt;
774
775 spin_lock(&dev->lock);
776 if (list_empty(&dev->cpkt_resp_q)) {
777 pr_err("ctrl resp queue empty "
778 " req%02x.%02x v%04x i%04x l%d\n",
779 ctrl->bRequestType, ctrl->bRequest,
780 w_value, w_index, w_length);
781 spin_unlock(&dev->lock);
782 goto invalid;
783 }
784
785 cpkt = list_first_entry(&dev->cpkt_resp_q,
786 struct rmnet_ctrl_pkt, list);
787 list_del(&cpkt->list);
788 spin_unlock(&dev->lock);
789
790 len = min_t(unsigned, w_length, cpkt->len);
791 memcpy(req->buf, cpkt->buf, len);
792 ret = len;
793
794 rmnet_free_ctrl_pkt(cpkt);
795 }
796 break;
797 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
798 | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
Hemant Kumar1b820d52011-11-03 15:08:28 -0700799 if (dev->port.notify_modem) {
800 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
801 dev->port.notify_modem(&dev->port, port_num, w_value);
802 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 ret = 0;
804
805 break;
806 default:
807
808invalid:
809 DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
810 ctrl->bRequestType, ctrl->bRequest,
811 w_value, w_index, w_length);
812 }
813
814 /* respond with data transfer or status phase? */
815 if (ret >= 0) {
816 VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
817 ctrl->bRequestType, ctrl->bRequest,
818 w_value, w_index, w_length);
819 req->zero = (ret < w_length);
820 req->length = ret;
821 ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
822 if (ret < 0)
823 ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
824 }
825
826 return ret;
827}
828
829static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
830{
831 struct f_rmnet *dev = func_to_rmnet(f);
832 struct usb_ep *ep;
833 struct usb_composite_dev *cdev = c->cdev;
834 int ret = -ENODEV;
835
836 dev->ifc_id = usb_interface_id(c, f);
837 if (dev->ifc_id < 0) {
838 pr_err("%s: unable to allocate ifc id, err:%d",
839 __func__, dev->ifc_id);
840 return dev->ifc_id;
841 }
842 rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
843
844 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
845 if (!ep) {
846 pr_err("%s: usb epin autoconfig failed\n", __func__);
847 return -ENODEV;
848 }
849 dev->port.in = ep;
850 ep->driver_data = cdev;
851
852 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
853 if (!ep) {
854 pr_err("%s: usb epout autoconfig failed\n", __func__);
855 ret = -ENODEV;
856 goto ep_auto_out_fail;
857 }
858 dev->port.out = ep;
859 ep->driver_data = cdev;
860
861 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
862 if (!ep) {
863 pr_err("%s: usb epnotify autoconfig failed\n", __func__);
864 ret = -ENODEV;
865 goto ep_auto_notify_fail;
866 }
867 dev->notify = ep;
868 ep->driver_data = cdev;
869
870 dev->notify_req = frmnet_alloc_req(ep,
Hemant Kumarfbf113d2011-09-16 18:24:45 -0700871 sizeof(struct usb_cdc_notification),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 GFP_KERNEL);
873 if (IS_ERR(dev->notify_req)) {
874 pr_err("%s: unable to allocate memory for notify req\n",
875 __func__);
876 ret = -ENOMEM;
877 goto ep_notify_alloc_fail;
878 }
879
880 dev->notify_req->complete = frmnet_notify_complete;
881 dev->notify_req->context = dev;
882
883 f->descriptors = usb_copy_descriptors(rmnet_fs_function);
884
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +0530885 if (!f->descriptors)
886 goto fail;
887
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700888 if (gadget_is_dualspeed(cdev->gadget)) {
889 rmnet_hs_in_desc.bEndpointAddress =
890 rmnet_fs_in_desc.bEndpointAddress;
891 rmnet_hs_out_desc.bEndpointAddress =
892 rmnet_fs_out_desc.bEndpointAddress;
893 rmnet_hs_notify_desc.bEndpointAddress =
894 rmnet_fs_notify_desc.bEndpointAddress;
895
896 /* copy descriptors, and track endpoint copies */
897 f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
898
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +0530899 if (!f->hs_descriptors)
900 goto fail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700901 }
902
903 pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
904 __func__, dev->port_num,
905 gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
906 dev->port.in->name, dev->port.out->name);
907
908 return 0;
909
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +0530910fail:
911 if (f->descriptors)
912 usb_free_descriptors(f->descriptors);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700913ep_notify_alloc_fail:
914 dev->notify->driver_data = NULL;
915 dev->notify = NULL;
916ep_auto_notify_fail:
917 dev->port.out->driver_data = NULL;
918 dev->port.out = NULL;
919ep_auto_out_fail:
920 dev->port.in->driver_data = NULL;
921 dev->port.in = NULL;
922
923 return ret;
924}
925
Manu Gautam2b0234a2011-09-07 16:47:52 +0530926static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700927{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700928 int status;
929 struct f_rmnet *dev;
930 struct usb_function *f;
931 unsigned long flags;
932
933 pr_debug("%s: usb config:%p\n", __func__, c);
934
Manu Gautam2b0234a2011-09-07 16:47:52 +0530935 if (portno >= nr_rmnet_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700936 pr_err("%s: supporting ports#%u port_id:%u", __func__,
Manu Gautam2b0234a2011-09-07 16:47:52 +0530937 nr_rmnet_ports, portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938 return -ENODEV;
939 }
940
941 if (rmnet_string_defs[0].id == 0) {
942 status = usb_string_id(c->cdev);
943 if (status < 0) {
944 pr_err("%s: failed to get string id, err:%d\n",
945 __func__, status);
946 return status;
947 }
948 rmnet_string_defs[0].id = status;
949 }
950
Manu Gautam2b0234a2011-09-07 16:47:52 +0530951 dev = rmnet_ports[portno].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952
953 spin_lock_irqsave(&dev->lock, flags);
954 dev->cdev = c->cdev;
955 f = &dev->port.func;
Vamsi Krishna188078d2011-10-26 15:09:55 -0700956 f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957 spin_unlock_irqrestore(&dev->lock, flags);
Vamsi Krishna188078d2011-10-26 15:09:55 -0700958 if (!f->name) {
959 pr_err("%s: cannot allocate memory for name\n", __func__);
960 return -ENOMEM;
961 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700962
963 f->strings = rmnet_strings;
964 f->bind = frmnet_bind;
965 f->unbind = frmnet_unbind;
966 f->disable = frmnet_disable;
967 f->set_alt = frmnet_set_alt;
968 f->setup = frmnet_setup;
Amit Blaye5bb35e2012-05-08 20:38:20 +0300969 f->suspend = frmnet_suspend;
970 f->resume = frmnet_resume;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700971 dev->port.send_cpkt_response = frmnet_send_cpkt_response;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700972 dev->port.disconnect = frmnet_disconnect;
973 dev->port.connect = frmnet_connect;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974
975 status = usb_add_function(c, f);
976 if (status) {
977 pr_err("%s: usb add function failed: %d\n",
978 __func__, status);
Manu Gautam2b0234a2011-09-07 16:47:52 +0530979 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 return status;
981 }
982
983 pr_debug("%s: complete\n", __func__);
984
985 return status;
986}
987
Manu Gautame3e897c2011-09-12 17:18:46 +0530988static void frmnet_cleanup(void)
989{
990 int i;
991
992 for (i = 0; i < nr_rmnet_ports; i++)
993 kfree(rmnet_ports[i].port);
994
995 nr_rmnet_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700996 no_ctrl_smd_ports = 0;
997 no_data_bam_ports = 0;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200998 no_data_bam2bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -0800999 no_ctrl_hsic_ports = 0;
1000 no_data_hsic_ports = 0;
Manu Gautame3e897c2011-09-12 17:18:46 +05301001}
1002
Hemant Kumar1b820d52011-11-03 15:08:28 -07001003static int frmnet_init_port(const char *ctrl_name, const char *data_name)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001004{
Hemant Kumar1b820d52011-11-03 15:08:28 -07001005 struct f_rmnet *dev;
1006 struct rmnet_ports *rmnet_port;
1007 int ret;
1008 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001009
Hemant Kumar1b820d52011-11-03 15:08:28 -07001010 if (nr_rmnet_ports >= NR_RMNET_PORTS) {
1011 pr_err("%s: Max-%d instances supported\n",
1012 __func__, NR_RMNET_PORTS);
Manu Gautame3e897c2011-09-12 17:18:46 +05301013 return -EINVAL;
1014 }
1015
Hemant Kumar1b820d52011-11-03 15:08:28 -07001016 pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
1017 __func__, nr_rmnet_ports, ctrl_name, data_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018
Hemant Kumar1b820d52011-11-03 15:08:28 -07001019 dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
1020 if (!dev) {
1021 pr_err("%s: Unable to allocate rmnet device\n", __func__);
1022 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001023 }
1024
Hemant Kumar1b820d52011-11-03 15:08:28 -07001025 dev->port_num = nr_rmnet_ports;
1026 spin_lock_init(&dev->lock);
1027 INIT_LIST_HEAD(&dev->cpkt_resp_q);
1028
1029 rmnet_port = &rmnet_ports[nr_rmnet_ports];
1030 rmnet_port->port = dev;
1031 rmnet_port->port_num = nr_rmnet_ports;
1032 rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
1033 rmnet_port->data_xport = str_to_xport(data_name);
1034
1035 switch (rmnet_port->ctrl_xport) {
1036 case USB_GADGET_XPORT_SMD:
1037 rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
1038 no_ctrl_smd_ports++;
1039 break;
Jack Pham427f6922011-11-23 19:42:00 -08001040 case USB_GADGET_XPORT_HSIC:
1041 rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
1042 no_ctrl_hsic_ports++;
1043 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001044 case USB_GADGET_XPORT_NONE:
1045 break;
1046 default:
1047 pr_err("%s: Un-supported transport: %u\n", __func__,
1048 rmnet_port->ctrl_xport);
1049 ret = -ENODEV;
1050 goto fail_probe;
1051 }
1052
1053 switch (rmnet_port->data_xport) {
1054 case USB_GADGET_XPORT_BAM:
1055 rmnet_port->data_xport_num = no_data_bam_ports;
1056 no_data_bam_ports++;
1057 break;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001058 case USB_GADGET_XPORT_BAM2BAM:
1059 rmnet_port->data_xport_num = no_data_bam2bam_ports;
1060 no_data_bam2bam_ports++;
1061 break;
Jack Pham427f6922011-11-23 19:42:00 -08001062 case USB_GADGET_XPORT_HSIC:
1063 rmnet_port->data_xport_num = no_data_hsic_ports;
1064 no_data_hsic_ports++;
1065 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001066 case USB_GADGET_XPORT_NONE:
1067 break;
1068 default:
1069 pr_err("%s: Un-supported transport: %u\n", __func__,
1070 rmnet_port->data_xport);
1071 ret = -ENODEV;
1072 goto fail_probe;
1073 }
1074 nr_rmnet_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001075
1076 return 0;
1077
1078fail_probe:
Manu Gautam2b0234a2011-09-07 16:47:52 +05301079 for (i = 0; i < nr_rmnet_ports; i++)
1080 kfree(rmnet_ports[i].port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001081
Hemant Kumar1b820d52011-11-03 15:08:28 -07001082 nr_rmnet_ports = 0;
1083 no_ctrl_smd_ports = 0;
1084 no_data_bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -08001085 no_ctrl_hsic_ports = 0;
1086 no_data_hsic_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001087
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001088 return ret;
1089}