blob: cbcf5ac76b3582f91980a4f28cef18b06f8d54ca [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
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
28struct rmnet_descs {
29 struct usb_endpoint_descriptor *in;
30 struct usb_endpoint_descriptor *out;
31 struct usb_endpoint_descriptor *notify;
32};
33
34#define ACM_CTRL_DTR (1 << 0)
35
36/* TODO: use separate structures for data and
37 * control paths
38 */
39struct f_rmnet {
40 struct grmnet port;
41 int ifc_id;
42 u8 port_num;
43 atomic_t online;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -070044 atomic_t ctrl_online;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045 struct usb_composite_dev *cdev;
46
47 spinlock_t lock;
48
49 /* usb descriptors */
50 struct rmnet_descs fs;
51 struct rmnet_descs hs;
52
53 /* usb eps*/
54 struct usb_ep *notify;
55 struct usb_endpoint_descriptor *notify_desc;
56 struct usb_request *notify_req;
57
58 /* control info */
59 struct list_head cpkt_resp_q;
60 atomic_t notify_count;
61 unsigned long cpkts_len;
62};
63
Manu Gautam2b0234a2011-09-07 16:47:52 +053064#define NR_RMNET_PORTS 1
65static unsigned int nr_rmnet_ports;
Hemant Kumar1b820d52011-11-03 15:08:28 -070066static unsigned int no_ctrl_smd_ports;
Jack Pham427f6922011-11-23 19:42:00 -080067static unsigned int no_ctrl_hsic_ports;
Hemant Kumar1b820d52011-11-03 15:08:28 -070068static unsigned int no_data_bam_ports;
Ofir Cohena1c2a872011-12-14 10:26:34 +020069static unsigned int no_data_bam2bam_ports;
Jack Pham427f6922011-11-23 19:42:00 -080070static unsigned int no_data_hsic_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071static struct rmnet_ports {
Hemant Kumar1b820d52011-11-03 15:08:28 -070072 enum transport_type data_xport;
73 enum transport_type ctrl_xport;
74 unsigned data_xport_num;
75 unsigned ctrl_xport_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076 unsigned port_num;
77 struct f_rmnet *port;
Manu Gautam2b0234a2011-09-07 16:47:52 +053078} rmnet_ports[NR_RMNET_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079
80static struct usb_interface_descriptor rmnet_interface_desc = {
81 .bLength = USB_DT_INTERFACE_SIZE,
82 .bDescriptorType = USB_DT_INTERFACE,
83 .bNumEndpoints = 3,
84 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
85 .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
86 .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
87 /* .iInterface = DYNAMIC */
88};
89
90/* Full speed support */
91static struct usb_endpoint_descriptor rmnet_fs_notify_desc = {
92 .bLength = USB_DT_ENDPOINT_SIZE,
93 .bDescriptorType = USB_DT_ENDPOINT,
94 .bEndpointAddress = USB_DIR_IN,
95 .bmAttributes = USB_ENDPOINT_XFER_INT,
96 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
97 .bInterval = 1 << RMNET_NOTIFY_INTERVAL,
98};
99
100static struct usb_endpoint_descriptor rmnet_fs_in_desc = {
101 .bLength = USB_DT_ENDPOINT_SIZE,
102 .bDescriptorType = USB_DT_ENDPOINT,
103 .bEndpointAddress = USB_DIR_IN,
104 .bmAttributes = USB_ENDPOINT_XFER_BULK,
105 .wMaxPacketSize = __constant_cpu_to_le16(64),
106};
107
108static struct usb_endpoint_descriptor rmnet_fs_out_desc = {
109 .bLength = USB_DT_ENDPOINT_SIZE,
110 .bDescriptorType = USB_DT_ENDPOINT,
111 .bEndpointAddress = USB_DIR_OUT,
112 .bmAttributes = USB_ENDPOINT_XFER_BULK,
113 .wMaxPacketSize = __constant_cpu_to_le16(64),
114};
115
116static struct usb_descriptor_header *rmnet_fs_function[] = {
117 (struct usb_descriptor_header *) &rmnet_interface_desc,
118 (struct usb_descriptor_header *) &rmnet_fs_notify_desc,
119 (struct usb_descriptor_header *) &rmnet_fs_in_desc,
120 (struct usb_descriptor_header *) &rmnet_fs_out_desc,
121 NULL,
122};
123
124/* High speed support */
125static struct usb_endpoint_descriptor rmnet_hs_notify_desc = {
126 .bLength = USB_DT_ENDPOINT_SIZE,
127 .bDescriptorType = USB_DT_ENDPOINT,
128 .bEndpointAddress = USB_DIR_IN,
129 .bmAttributes = USB_ENDPOINT_XFER_INT,
130 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
131 .bInterval = RMNET_NOTIFY_INTERVAL + 4,
132};
133
134static struct usb_endpoint_descriptor rmnet_hs_in_desc = {
135 .bLength = USB_DT_ENDPOINT_SIZE,
136 .bDescriptorType = USB_DT_ENDPOINT,
137 .bEndpointAddress = USB_DIR_IN,
138 .bmAttributes = USB_ENDPOINT_XFER_BULK,
139 .wMaxPacketSize = __constant_cpu_to_le16(512),
140};
141
142static struct usb_endpoint_descriptor rmnet_hs_out_desc = {
143 .bLength = USB_DT_ENDPOINT_SIZE,
144 .bDescriptorType = USB_DT_ENDPOINT,
145 .bEndpointAddress = USB_DIR_OUT,
146 .bmAttributes = USB_ENDPOINT_XFER_BULK,
147 .wMaxPacketSize = __constant_cpu_to_le16(512),
148};
149
150static struct usb_descriptor_header *rmnet_hs_function[] = {
151 (struct usb_descriptor_header *) &rmnet_interface_desc,
152 (struct usb_descriptor_header *) &rmnet_hs_notify_desc,
153 (struct usb_descriptor_header *) &rmnet_hs_in_desc,
154 (struct usb_descriptor_header *) &rmnet_hs_out_desc,
155 NULL,
156};
157
158/* String descriptors */
159
160static struct usb_string rmnet_string_defs[] = {
161 [0].s = "RmNet",
162 { } /* end of list */
163};
164
165static struct usb_gadget_strings rmnet_string_table = {
166 .language = 0x0409, /* en-us */
167 .strings = rmnet_string_defs,
168};
169
170static struct usb_gadget_strings *rmnet_strings[] = {
171 &rmnet_string_table,
172 NULL,
173};
174
175/* ------- misc functions --------------------*/
176
177static inline struct f_rmnet *func_to_rmnet(struct usb_function *f)
178{
179 return container_of(f, struct f_rmnet, port.func);
180}
181
182static inline struct f_rmnet *port_to_rmnet(struct grmnet *r)
183{
184 return container_of(r, struct f_rmnet, port);
185}
186
187static struct usb_request *
188frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
189{
190 struct usb_request *req;
191
192 req = usb_ep_alloc_request(ep, flags);
193 if (!req)
194 return ERR_PTR(-ENOMEM);
195
196 req->buf = kmalloc(len, flags);
197 if (!req->buf) {
198 usb_ep_free_request(ep, req);
199 return ERR_PTR(-ENOMEM);
200 }
201
202 req->length = len;
203
204 return req;
205}
206
207void frmnet_free_req(struct usb_ep *ep, struct usb_request *req)
208{
209 kfree(req->buf);
210 usb_ep_free_request(ep, req);
211}
212
213static struct rmnet_ctrl_pkt *rmnet_alloc_ctrl_pkt(unsigned len, gfp_t flags)
214{
215 struct rmnet_ctrl_pkt *pkt;
216
217 pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
218 if (!pkt)
219 return ERR_PTR(-ENOMEM);
220
221 pkt->buf = kmalloc(len, flags);
222 if (!pkt->buf) {
223 kfree(pkt);
224 return ERR_PTR(-ENOMEM);
225 }
226 pkt->len = len;
227
228 return pkt;
229}
230
231static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
232{
233 kfree(pkt->buf);
234 kfree(pkt);
235}
236
237/* -------------------------------------------*/
238
Hemant Kumar1b820d52011-11-03 15:08:28 -0700239static int rmnet_gport_setup(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240{
Jack Pham427f6922011-11-23 19:42:00 -0800241 int ret;
242 int port_idx;
243 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244
Ofir Cohena1c2a872011-12-14 10:26:34 +0200245 pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u"
246 " smd ports: %u ctrl hsic ports: %u"
247 " nr_rmnet_ports: %u\n",
248 __func__, no_data_bam_ports, no_data_bam2bam_ports,
249 no_data_hsic_ports, no_ctrl_smd_ports,
250 no_ctrl_hsic_ports, nr_rmnet_ports);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251
Ofir Cohena1c2a872011-12-14 10:26:34 +0200252 if (no_data_bam_ports || no_data_bam2bam_ports) {
253 ret = gbam_setup(no_data_bam_ports,
254 no_data_bam2bam_ports);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700255 if (ret)
256 return ret;
257 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258
Hemant Kumar1b820d52011-11-03 15:08:28 -0700259 if (no_ctrl_smd_ports) {
260 ret = gsmd_ctrl_setup(no_ctrl_smd_ports);
261 if (ret)
262 return ret;
263 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264
Jack Pham427f6922011-11-23 19:42:00 -0800265 if (no_data_hsic_ports) {
266 port_idx = ghsic_data_setup(no_data_hsic_ports,
267 USB_GADGET_RMNET);
268 if (port_idx < 0)
269 return port_idx;
270 for (i = 0; i < nr_rmnet_ports; i++) {
271 if (rmnet_ports[i].data_xport ==
272 USB_GADGET_XPORT_HSIC) {
273 rmnet_ports[i].data_xport_num = port_idx;
274 port_idx++;
275 }
276 }
277 }
278
279 if (no_ctrl_hsic_ports) {
280 port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
281 USB_GADGET_RMNET);
282 if (port_idx < 0)
283 return port_idx;
284 for (i = 0; i < nr_rmnet_ports; i++) {
285 if (rmnet_ports[i].ctrl_xport ==
286 USB_GADGET_XPORT_HSIC) {
287 rmnet_ports[i].ctrl_xport_num = port_idx;
288 port_idx++;
289 }
290 }
291 }
292
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 return 0;
294}
295
Manu Gautam2b0234a2011-09-07 16:47:52 +0530296static int gport_rmnet_connect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700298 int ret;
299 unsigned port_num;
300 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
301 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302
Hemant Kumar1b820d52011-11-03 15:08:28 -0700303 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
304 __func__, xport_to_str(cxport), xport_to_str(dxport),
305 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306
Hemant Kumar1b820d52011-11-03 15:08:28 -0700307 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
308 switch (cxport) {
309 case USB_GADGET_XPORT_SMD:
310 ret = gsmd_ctrl_connect(&dev->port, port_num);
311 if (ret) {
312 pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
313 __func__, ret);
314 return ret;
315 }
316 break;
Jack Pham427f6922011-11-23 19:42:00 -0800317 case USB_GADGET_XPORT_HSIC:
318 ret = ghsic_ctrl_connect(&dev->port, port_num);
319 if (ret) {
320 pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
321 __func__, ret);
322 return ret;
323 }
324 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700325 case USB_GADGET_XPORT_NONE:
326 break;
327 default:
328 pr_err("%s: Un-supported transport: %s\n", __func__,
329 xport_to_str(cxport));
330 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 }
332
Hemant Kumar1b820d52011-11-03 15:08:28 -0700333 port_num = rmnet_ports[dev->port_num].data_xport_num;
334 switch (dxport) {
335 case USB_GADGET_XPORT_BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200336 case USB_GADGET_XPORT_BAM2BAM:
337 /* currently only one connection (idx 0)
338 is supported */
339 ret = gbam_connect(&dev->port, port_num,
340 dxport, 0);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700341 if (ret) {
342 pr_err("%s: gbam_connect failed: err:%d\n",
343 __func__, ret);
344 gsmd_ctrl_disconnect(&dev->port, port_num);
345 return ret;
346 }
347 break;
Jack Pham427f6922011-11-23 19:42:00 -0800348 case USB_GADGET_XPORT_HSIC:
349 ret = ghsic_data_connect(&dev->port, port_num);
350 if (ret) {
351 pr_err("%s: ghsic_data_connect failed: err:%d\n",
352 __func__, ret);
353 ghsic_ctrl_disconnect(&dev->port, port_num);
354 return ret;
355 }
356 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700357 case USB_GADGET_XPORT_NONE:
358 break;
359 default:
360 pr_err("%s: Un-supported transport: %s\n", __func__,
361 xport_to_str(dxport));
362 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363 }
364
365 return 0;
366}
367
Manu Gautam2b0234a2011-09-07 16:47:52 +0530368static int gport_rmnet_disconnect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700370 unsigned port_num;
371 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
372 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373
Hemant Kumar1b820d52011-11-03 15:08:28 -0700374 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
375 __func__, xport_to_str(cxport), xport_to_str(dxport),
376 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377
Hemant Kumar1b820d52011-11-03 15:08:28 -0700378 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
379 switch (cxport) {
380 case USB_GADGET_XPORT_SMD:
381 gsmd_ctrl_disconnect(&dev->port, port_num);
382 break;
Jack Pham427f6922011-11-23 19:42:00 -0800383 case USB_GADGET_XPORT_HSIC:
384 ghsic_ctrl_disconnect(&dev->port, port_num);
385 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700386 case USB_GADGET_XPORT_NONE:
387 break;
388 default:
389 pr_err("%s: Un-supported transport: %s\n", __func__,
390 xport_to_str(cxport));
391 return -ENODEV;
392 }
393
394 port_num = rmnet_ports[dev->port_num].data_xport_num;
395 switch (dxport) {
396 case USB_GADGET_XPORT_BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200397 case USB_GADGET_XPORT_BAM2BAM:
398 gbam_disconnect(&dev->port, port_num, dxport);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700399 break;
Jack Pham427f6922011-11-23 19:42:00 -0800400 case USB_GADGET_XPORT_HSIC:
401 ghsic_data_disconnect(&dev->port, port_num);
402 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700403 case USB_GADGET_XPORT_NONE:
404 break;
405 default:
406 pr_err("%s: Un-supported transport: %s\n", __func__,
407 xport_to_str(dxport));
408 return -ENODEV;
409 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410
411 return 0;
412}
413
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
415{
416 struct f_rmnet *dev = func_to_rmnet(f);
417
418 pr_debug("%s: portno:%d\n", __func__, dev->port_num);
419
420 if (gadget_is_dualspeed(c->cdev->gadget))
421 usb_free_descriptors(f->hs_descriptors);
422 usb_free_descriptors(f->descriptors);
423
424 frmnet_free_req(dev->notify, dev->notify_req);
425
Manu Gautamdd4222b2011-09-09 15:06:05 +0530426 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427}
428
429static void frmnet_disable(struct usb_function *f)
430{
431 struct f_rmnet *dev = func_to_rmnet(f);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700432 unsigned long flags;
433 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434
435 pr_debug("%s: port#%d\n", __func__, dev->port_num);
436
437 usb_ep_disable(dev->notify);
438
439 atomic_set(&dev->online, 0);
440
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700441 spin_lock_irqsave(&dev->lock, flags);
442 while (!list_empty(&dev->cpkt_resp_q)) {
443 cpkt = list_first_entry(&dev->cpkt_resp_q,
444 struct rmnet_ctrl_pkt, list);
445
446 list_del(&cpkt->list);
447 rmnet_free_ctrl_pkt(cpkt);
448 }
449 atomic_set(&dev->notify_count, 0);
450 spin_unlock_irqrestore(&dev->lock, flags);
451
Manu Gautam2b0234a2011-09-07 16:47:52 +0530452 gport_rmnet_disconnect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453}
454
455static int
456frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
457{
458 struct f_rmnet *dev = func_to_rmnet(f);
459 struct usb_composite_dev *cdev = dev->cdev;
460 int ret;
461
462 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
463
464 if (dev->notify->driver_data) {
465 pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
466 usb_ep_disable(dev->notify);
467 }
468 dev->notify_desc = ep_choose(cdev->gadget,
469 dev->hs.notify,
470 dev->fs.notify);
471 ret = usb_ep_enable(dev->notify, dev->notify_desc);
472 if (ret) {
473 pr_err("%s: usb ep#%s enable failed, err#%d\n",
474 __func__, dev->notify->name, ret);
475 return ret;
476 }
477 dev->notify->driver_data = dev;
478
479 if (dev->port.in->driver_data) {
480 pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
Manu Gautam2b0234a2011-09-07 16:47:52 +0530481 gport_rmnet_disconnect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482 }
483
484 dev->port.in_desc = ep_choose(cdev->gadget,
485 dev->hs.in, dev->fs.in);
486 dev->port.out_desc = ep_choose(cdev->gadget,
487 dev->hs.out, dev->fs.out);
488
Manu Gautam2b0234a2011-09-07 16:47:52 +0530489 ret = gport_rmnet_connect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490
491 atomic_set(&dev->online, 1);
492
493 return ret;
494}
495
496static void frmnet_ctrl_response_available(struct f_rmnet *dev)
497{
498 struct usb_request *req = dev->notify_req;
499 struct usb_cdc_notification *event;
500 unsigned long flags;
501 int ret;
502
503 pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num);
504
505 spin_lock_irqsave(&dev->lock, flags);
506 if (!atomic_read(&dev->online) || !req || !req->buf) {
507 spin_unlock_irqrestore(&dev->lock, flags);
508 return;
509 }
510
511 if (atomic_inc_return(&dev->notify_count) != 1) {
512 spin_unlock_irqrestore(&dev->lock, flags);
513 return;
514 }
515
516 event = req->buf;
517 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
518 | USB_RECIP_INTERFACE;
519 event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
520 event->wValue = cpu_to_le16(0);
521 event->wIndex = cpu_to_le16(dev->ifc_id);
522 event->wLength = cpu_to_le16(0);
523 spin_unlock_irqrestore(&dev->lock, flags);
524
525 ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
526 if (ret) {
527 atomic_dec(&dev->notify_count);
528 pr_debug("ep enqueue error %d\n", ret);
529 }
530}
531
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700532static void frmnet_connect(struct grmnet *gr)
533{
534 struct f_rmnet *dev;
535
536 if (!gr) {
537 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
538 return;
539 }
540
541 dev = port_to_rmnet(gr);
542
543 atomic_set(&dev->ctrl_online, 1);
544}
545
546static void frmnet_disconnect(struct grmnet *gr)
547{
548 struct f_rmnet *dev;
549 unsigned long flags;
550 struct usb_cdc_notification *event;
551 int status;
552 struct rmnet_ctrl_pkt *cpkt;
553
554 if (!gr) {
555 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
556 return;
557 }
558
559 dev = port_to_rmnet(gr);
560
561 atomic_set(&dev->ctrl_online, 0);
562
563 if (!atomic_read(&dev->online)) {
564 pr_debug("%s: nothing to do\n", __func__);
565 return;
566 }
567
568 usb_ep_fifo_flush(dev->notify);
569
570 event = dev->notify_req->buf;
571 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
572 | USB_RECIP_INTERFACE;
573 event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
574 event->wValue = cpu_to_le16(0);
575 event->wIndex = cpu_to_le16(dev->ifc_id);
576 event->wLength = cpu_to_le16(0);
577
Vamsi Krishna188078d2011-10-26 15:09:55 -0700578 status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700579 if (status < 0) {
580 if (!atomic_read(&dev->online))
581 return;
582 pr_err("%s: rmnet notify ep enqueue error %d\n",
583 __func__, status);
584 }
585
586 spin_lock_irqsave(&dev->lock, flags);
587 while (!list_empty(&dev->cpkt_resp_q)) {
588 cpkt = list_first_entry(&dev->cpkt_resp_q,
589 struct rmnet_ctrl_pkt, list);
590
591 list_del(&cpkt->list);
592 rmnet_free_ctrl_pkt(cpkt);
593 }
594 atomic_set(&dev->notify_count, 0);
595 spin_unlock_irqrestore(&dev->lock, flags);
596
597}
598
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700600frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601{
602 struct f_rmnet *dev;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700603 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604 unsigned long flags;
605
Hemant Kumarf60c0252011-11-03 12:37:07 -0700606 if (!gr || !buf) {
607 pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
608 __func__, gr, buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700609 return -ENODEV;
610 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700611 cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
612 if (IS_ERR(cpkt)) {
613 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
614 return -ENOMEM;
615 }
616 memcpy(cpkt->buf, buf, len);
617 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618
619 dev = port_to_rmnet(gr);
620
621 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
622
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700623 if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624 rmnet_free_ctrl_pkt(cpkt);
625 return 0;
626 }
627
628 spin_lock_irqsave(&dev->lock, flags);
Chiranjeevi Velempati263e6c82011-11-11 23:07:36 +0530629 list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630 spin_unlock_irqrestore(&dev->lock, flags);
631
632 frmnet_ctrl_response_available(dev);
633
634 return 0;
635}
636
637static void
638frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req)
639{
640 struct f_rmnet *dev = req->context;
641 struct usb_composite_dev *cdev;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700642 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643
644 if (!dev) {
645 pr_err("%s: rmnet dev is null\n", __func__);
646 return;
647 }
648
649 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
650
651 cdev = dev->cdev;
652
Hemant Kumar1b820d52011-11-03 15:08:28 -0700653 if (dev->port.send_encap_cmd) {
654 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
655 dev->port.send_encap_cmd(port_num, req->buf, req->actual);
656 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657}
658
659static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
660{
661 struct f_rmnet *dev = req->context;
662 int status = req->status;
663
664 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
665
666 switch (status) {
667 case -ECONNRESET:
668 case -ESHUTDOWN:
669 /* connection gone */
670 atomic_set(&dev->notify_count, 0);
671 break;
672 default:
673 pr_err("rmnet notify ep error %d\n", status);
674 /* FALLTHROUGH */
675 case 0:
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700676 if (!atomic_read(&dev->ctrl_online))
677 break;
678
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679 if (atomic_dec_and_test(&dev->notify_count))
680 break;
681
682 status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
683 if (status) {
684 atomic_dec(&dev->notify_count);
685 pr_debug("ep enqueue error %d\n", status);
686 }
687 break;
688 }
689}
690
691static int
692frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
693{
694 struct f_rmnet *dev = func_to_rmnet(f);
695 struct usb_composite_dev *cdev = dev->cdev;
696 struct usb_request *req = cdev->req;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700697 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700698 u16 w_index = le16_to_cpu(ctrl->wIndex);
699 u16 w_value = le16_to_cpu(ctrl->wValue);
700 u16 w_length = le16_to_cpu(ctrl->wLength);
701 int ret = -EOPNOTSUPP;
702
703 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
704
705 if (!atomic_read(&dev->online)) {
706 pr_debug("%s: usb cable is not connected\n", __func__);
707 return -ENOTCONN;
708 }
709
710 switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
711
712 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
713 | USB_CDC_SEND_ENCAPSULATED_COMMAND:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714 ret = w_length;
715 req->complete = frmnet_cmd_complete;
716 req->context = dev;
717 break;
718
719
720 case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
721 | USB_CDC_GET_ENCAPSULATED_RESPONSE:
722 if (w_value)
723 goto invalid;
724 else {
725 unsigned len;
726 struct rmnet_ctrl_pkt *cpkt;
727
728 spin_lock(&dev->lock);
729 if (list_empty(&dev->cpkt_resp_q)) {
730 pr_err("ctrl resp queue empty "
731 " req%02x.%02x v%04x i%04x l%d\n",
732 ctrl->bRequestType, ctrl->bRequest,
733 w_value, w_index, w_length);
734 spin_unlock(&dev->lock);
735 goto invalid;
736 }
737
738 cpkt = list_first_entry(&dev->cpkt_resp_q,
739 struct rmnet_ctrl_pkt, list);
740 list_del(&cpkt->list);
741 spin_unlock(&dev->lock);
742
743 len = min_t(unsigned, w_length, cpkt->len);
744 memcpy(req->buf, cpkt->buf, len);
745 ret = len;
746
747 rmnet_free_ctrl_pkt(cpkt);
748 }
749 break;
750 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
751 | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
Hemant Kumar1b820d52011-11-03 15:08:28 -0700752 if (dev->port.notify_modem) {
753 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
754 dev->port.notify_modem(&dev->port, port_num, w_value);
755 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 ret = 0;
757
758 break;
759 default:
760
761invalid:
762 DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
763 ctrl->bRequestType, ctrl->bRequest,
764 w_value, w_index, w_length);
765 }
766
767 /* respond with data transfer or status phase? */
768 if (ret >= 0) {
769 VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
770 ctrl->bRequestType, ctrl->bRequest,
771 w_value, w_index, w_length);
772 req->zero = (ret < w_length);
773 req->length = ret;
774 ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
775 if (ret < 0)
776 ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
777 }
778
779 return ret;
780}
781
782static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
783{
784 struct f_rmnet *dev = func_to_rmnet(f);
785 struct usb_ep *ep;
786 struct usb_composite_dev *cdev = c->cdev;
787 int ret = -ENODEV;
788
789 dev->ifc_id = usb_interface_id(c, f);
790 if (dev->ifc_id < 0) {
791 pr_err("%s: unable to allocate ifc id, err:%d",
792 __func__, dev->ifc_id);
793 return dev->ifc_id;
794 }
795 rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
796
797 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
798 if (!ep) {
799 pr_err("%s: usb epin autoconfig failed\n", __func__);
800 return -ENODEV;
801 }
802 dev->port.in = ep;
803 ep->driver_data = cdev;
804
805 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
806 if (!ep) {
807 pr_err("%s: usb epout autoconfig failed\n", __func__);
808 ret = -ENODEV;
809 goto ep_auto_out_fail;
810 }
811 dev->port.out = ep;
812 ep->driver_data = cdev;
813
814 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
815 if (!ep) {
816 pr_err("%s: usb epnotify autoconfig failed\n", __func__);
817 ret = -ENODEV;
818 goto ep_auto_notify_fail;
819 }
820 dev->notify = ep;
821 ep->driver_data = cdev;
822
823 dev->notify_req = frmnet_alloc_req(ep,
Hemant Kumarfbf113d2011-09-16 18:24:45 -0700824 sizeof(struct usb_cdc_notification),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700825 GFP_KERNEL);
826 if (IS_ERR(dev->notify_req)) {
827 pr_err("%s: unable to allocate memory for notify req\n",
828 __func__);
829 ret = -ENOMEM;
830 goto ep_notify_alloc_fail;
831 }
832
833 dev->notify_req->complete = frmnet_notify_complete;
834 dev->notify_req->context = dev;
835
836 f->descriptors = usb_copy_descriptors(rmnet_fs_function);
837
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +0530838 if (!f->descriptors)
839 goto fail;
840
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700841 dev->fs.in = usb_find_endpoint(rmnet_fs_function,
842 f->descriptors,
843 &rmnet_fs_in_desc);
844 dev->fs.out = usb_find_endpoint(rmnet_fs_function,
845 f->descriptors,
846 &rmnet_fs_out_desc);
847 dev->fs.notify = usb_find_endpoint(rmnet_fs_function,
848 f->descriptors,
849 &rmnet_fs_notify_desc);
850
851 if (gadget_is_dualspeed(cdev->gadget)) {
852 rmnet_hs_in_desc.bEndpointAddress =
853 rmnet_fs_in_desc.bEndpointAddress;
854 rmnet_hs_out_desc.bEndpointAddress =
855 rmnet_fs_out_desc.bEndpointAddress;
856 rmnet_hs_notify_desc.bEndpointAddress =
857 rmnet_fs_notify_desc.bEndpointAddress;
858
859 /* copy descriptors, and track endpoint copies */
860 f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
861
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +0530862 if (!f->hs_descriptors)
863 goto fail;
864
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700865 dev->hs.in = usb_find_endpoint(rmnet_hs_function,
866 f->hs_descriptors, &rmnet_hs_in_desc);
867 dev->hs.out = usb_find_endpoint(rmnet_hs_function,
868 f->hs_descriptors, &rmnet_hs_out_desc);
869 dev->hs.notify = usb_find_endpoint(rmnet_hs_function,
870 f->hs_descriptors, &rmnet_hs_notify_desc);
871 }
872
873 pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
874 __func__, dev->port_num,
875 gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
876 dev->port.in->name, dev->port.out->name);
877
878 return 0;
879
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +0530880fail:
881 if (f->descriptors)
882 usb_free_descriptors(f->descriptors);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883ep_notify_alloc_fail:
884 dev->notify->driver_data = NULL;
885 dev->notify = NULL;
886ep_auto_notify_fail:
887 dev->port.out->driver_data = NULL;
888 dev->port.out = NULL;
889ep_auto_out_fail:
890 dev->port.in->driver_data = NULL;
891 dev->port.in = NULL;
892
893 return ret;
894}
895
Manu Gautam2b0234a2011-09-07 16:47:52 +0530896static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700897{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700898 int status;
899 struct f_rmnet *dev;
900 struct usb_function *f;
901 unsigned long flags;
902
903 pr_debug("%s: usb config:%p\n", __func__, c);
904
Manu Gautam2b0234a2011-09-07 16:47:52 +0530905 if (portno >= nr_rmnet_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700906 pr_err("%s: supporting ports#%u port_id:%u", __func__,
Manu Gautam2b0234a2011-09-07 16:47:52 +0530907 nr_rmnet_ports, portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700908 return -ENODEV;
909 }
910
911 if (rmnet_string_defs[0].id == 0) {
912 status = usb_string_id(c->cdev);
913 if (status < 0) {
914 pr_err("%s: failed to get string id, err:%d\n",
915 __func__, status);
916 return status;
917 }
918 rmnet_string_defs[0].id = status;
919 }
920
Manu Gautam2b0234a2011-09-07 16:47:52 +0530921 dev = rmnet_ports[portno].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700922
923 spin_lock_irqsave(&dev->lock, flags);
924 dev->cdev = c->cdev;
925 f = &dev->port.func;
Vamsi Krishna188078d2011-10-26 15:09:55 -0700926 f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700927 spin_unlock_irqrestore(&dev->lock, flags);
Vamsi Krishna188078d2011-10-26 15:09:55 -0700928 if (!f->name) {
929 pr_err("%s: cannot allocate memory for name\n", __func__);
930 return -ENOMEM;
931 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700932
933 f->strings = rmnet_strings;
934 f->bind = frmnet_bind;
935 f->unbind = frmnet_unbind;
936 f->disable = frmnet_disable;
937 f->set_alt = frmnet_set_alt;
938 f->setup = frmnet_setup;
939 dev->port.send_cpkt_response = frmnet_send_cpkt_response;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700940 dev->port.disconnect = frmnet_disconnect;
941 dev->port.connect = frmnet_connect;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942
943 status = usb_add_function(c, f);
944 if (status) {
945 pr_err("%s: usb add function failed: %d\n",
946 __func__, status);
Manu Gautam2b0234a2011-09-07 16:47:52 +0530947 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700948 return status;
949 }
950
951 pr_debug("%s: complete\n", __func__);
952
953 return status;
954}
955
Manu Gautame3e897c2011-09-12 17:18:46 +0530956static void frmnet_cleanup(void)
957{
958 int i;
959
960 for (i = 0; i < nr_rmnet_ports; i++)
961 kfree(rmnet_ports[i].port);
962
963 nr_rmnet_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700964 no_ctrl_smd_ports = 0;
965 no_data_bam_ports = 0;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200966 no_data_bam2bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -0800967 no_ctrl_hsic_ports = 0;
968 no_data_hsic_ports = 0;
Manu Gautame3e897c2011-09-12 17:18:46 +0530969}
970
Hemant Kumar1b820d52011-11-03 15:08:28 -0700971static int frmnet_init_port(const char *ctrl_name, const char *data_name)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700973 struct f_rmnet *dev;
974 struct rmnet_ports *rmnet_port;
975 int ret;
976 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700977
Hemant Kumar1b820d52011-11-03 15:08:28 -0700978 if (nr_rmnet_ports >= NR_RMNET_PORTS) {
979 pr_err("%s: Max-%d instances supported\n",
980 __func__, NR_RMNET_PORTS);
Manu Gautame3e897c2011-09-12 17:18:46 +0530981 return -EINVAL;
982 }
983
Hemant Kumar1b820d52011-11-03 15:08:28 -0700984 pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
985 __func__, nr_rmnet_ports, ctrl_name, data_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700986
Hemant Kumar1b820d52011-11-03 15:08:28 -0700987 dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
988 if (!dev) {
989 pr_err("%s: Unable to allocate rmnet device\n", __func__);
990 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991 }
992
Hemant Kumar1b820d52011-11-03 15:08:28 -0700993 dev->port_num = nr_rmnet_ports;
994 spin_lock_init(&dev->lock);
995 INIT_LIST_HEAD(&dev->cpkt_resp_q);
996
997 rmnet_port = &rmnet_ports[nr_rmnet_ports];
998 rmnet_port->port = dev;
999 rmnet_port->port_num = nr_rmnet_ports;
1000 rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
1001 rmnet_port->data_xport = str_to_xport(data_name);
1002
1003 switch (rmnet_port->ctrl_xport) {
1004 case USB_GADGET_XPORT_SMD:
1005 rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
1006 no_ctrl_smd_ports++;
1007 break;
Jack Pham427f6922011-11-23 19:42:00 -08001008 case USB_GADGET_XPORT_HSIC:
1009 rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
1010 no_ctrl_hsic_ports++;
1011 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001012 case USB_GADGET_XPORT_NONE:
1013 break;
1014 default:
1015 pr_err("%s: Un-supported transport: %u\n", __func__,
1016 rmnet_port->ctrl_xport);
1017 ret = -ENODEV;
1018 goto fail_probe;
1019 }
1020
1021 switch (rmnet_port->data_xport) {
1022 case USB_GADGET_XPORT_BAM:
1023 rmnet_port->data_xport_num = no_data_bam_ports;
1024 no_data_bam_ports++;
1025 break;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001026 case USB_GADGET_XPORT_BAM2BAM:
1027 rmnet_port->data_xport_num = no_data_bam2bam_ports;
1028 no_data_bam2bam_ports++;
1029 break;
Jack Pham427f6922011-11-23 19:42:00 -08001030 case USB_GADGET_XPORT_HSIC:
1031 rmnet_port->data_xport_num = no_data_hsic_ports;
1032 no_data_hsic_ports++;
1033 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001034 case USB_GADGET_XPORT_NONE:
1035 break;
1036 default:
1037 pr_err("%s: Un-supported transport: %u\n", __func__,
1038 rmnet_port->data_xport);
1039 ret = -ENODEV;
1040 goto fail_probe;
1041 }
1042 nr_rmnet_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001043
1044 return 0;
1045
1046fail_probe:
Manu Gautam2b0234a2011-09-07 16:47:52 +05301047 for (i = 0; i < nr_rmnet_ports; i++)
1048 kfree(rmnet_ports[i].port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001049
Hemant Kumar1b820d52011-11-03 15:08:28 -07001050 nr_rmnet_ports = 0;
1051 no_ctrl_smd_ports = 0;
1052 no_data_bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -08001053 no_ctrl_hsic_ports = 0;
1054 no_data_hsic_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001055
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001056 return ret;
1057}