blob: 2dccca8a4d966f04ca824c897577741946701cec [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
Ido Shayevitz909ed382013-02-15 02:25:35 +02002 * Copyright (c) 2011-2013, The Linux Foundation. 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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017#include <linux/spinlock.h>
18
Hemant Kumar1b820d52011-11-03 15:08:28 -070019#include <mach/usb_gadget_xport.h>
Ofir Cohenfdecb602012-11-16 15:50:01 +020020#include <mach/usb_bam.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;
Bar Weiner0dae81b2013-02-14 13:53:54 +020057static unsigned int no_ctrl_qti_ports;
Jack Pham427f6922011-11-23 19:42:00 -080058static unsigned int no_ctrl_hsic_ports;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +053059static unsigned int no_ctrl_hsuart_ports;
Hemant Kumar1b820d52011-11-03 15:08:28 -070060static unsigned int no_data_bam_ports;
Ofir Cohena1c2a872011-12-14 10:26:34 +020061static unsigned int no_data_bam2bam_ports;
Jack Pham427f6922011-11-23 19:42:00 -080062static unsigned int no_data_hsic_ports;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +053063static unsigned int no_data_hsuart_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064static struct rmnet_ports {
Hemant Kumar1b820d52011-11-03 15:08:28 -070065 enum transport_type data_xport;
66 enum transport_type ctrl_xport;
67 unsigned data_xport_num;
68 unsigned ctrl_xport_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069 unsigned port_num;
70 struct f_rmnet *port;
Manu Gautam2b0234a2011-09-07 16:47:52 +053071} rmnet_ports[NR_RMNET_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072
73static struct usb_interface_descriptor rmnet_interface_desc = {
74 .bLength = USB_DT_INTERFACE_SIZE,
75 .bDescriptorType = USB_DT_INTERFACE,
76 .bNumEndpoints = 3,
77 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
78 .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
79 .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
80 /* .iInterface = DYNAMIC */
81};
82
83/* Full speed support */
84static struct usb_endpoint_descriptor rmnet_fs_notify_desc = {
85 .bLength = USB_DT_ENDPOINT_SIZE,
86 .bDescriptorType = USB_DT_ENDPOINT,
87 .bEndpointAddress = USB_DIR_IN,
88 .bmAttributes = USB_ENDPOINT_XFER_INT,
89 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
90 .bInterval = 1 << RMNET_NOTIFY_INTERVAL,
91};
92
93static struct usb_endpoint_descriptor rmnet_fs_in_desc = {
94 .bLength = USB_DT_ENDPOINT_SIZE,
95 .bDescriptorType = USB_DT_ENDPOINT,
96 .bEndpointAddress = USB_DIR_IN,
97 .bmAttributes = USB_ENDPOINT_XFER_BULK,
98 .wMaxPacketSize = __constant_cpu_to_le16(64),
99};
100
101static struct usb_endpoint_descriptor rmnet_fs_out_desc = {
102 .bLength = USB_DT_ENDPOINT_SIZE,
103 .bDescriptorType = USB_DT_ENDPOINT,
104 .bEndpointAddress = USB_DIR_OUT,
105 .bmAttributes = USB_ENDPOINT_XFER_BULK,
106 .wMaxPacketSize = __constant_cpu_to_le16(64),
107};
108
109static struct usb_descriptor_header *rmnet_fs_function[] = {
110 (struct usb_descriptor_header *) &rmnet_interface_desc,
111 (struct usb_descriptor_header *) &rmnet_fs_notify_desc,
112 (struct usb_descriptor_header *) &rmnet_fs_in_desc,
113 (struct usb_descriptor_header *) &rmnet_fs_out_desc,
114 NULL,
115};
116
117/* High speed support */
118static struct usb_endpoint_descriptor rmnet_hs_notify_desc = {
119 .bLength = USB_DT_ENDPOINT_SIZE,
120 .bDescriptorType = USB_DT_ENDPOINT,
121 .bEndpointAddress = USB_DIR_IN,
122 .bmAttributes = USB_ENDPOINT_XFER_INT,
123 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
124 .bInterval = RMNET_NOTIFY_INTERVAL + 4,
125};
126
127static struct usb_endpoint_descriptor rmnet_hs_in_desc = {
128 .bLength = USB_DT_ENDPOINT_SIZE,
129 .bDescriptorType = USB_DT_ENDPOINT,
130 .bEndpointAddress = USB_DIR_IN,
131 .bmAttributes = USB_ENDPOINT_XFER_BULK,
132 .wMaxPacketSize = __constant_cpu_to_le16(512),
133};
134
135static struct usb_endpoint_descriptor rmnet_hs_out_desc = {
136 .bLength = USB_DT_ENDPOINT_SIZE,
137 .bDescriptorType = USB_DT_ENDPOINT,
138 .bEndpointAddress = USB_DIR_OUT,
139 .bmAttributes = USB_ENDPOINT_XFER_BULK,
140 .wMaxPacketSize = __constant_cpu_to_le16(512),
141};
142
143static struct usb_descriptor_header *rmnet_hs_function[] = {
144 (struct usb_descriptor_header *) &rmnet_interface_desc,
145 (struct usb_descriptor_header *) &rmnet_hs_notify_desc,
146 (struct usb_descriptor_header *) &rmnet_hs_in_desc,
147 (struct usb_descriptor_header *) &rmnet_hs_out_desc,
148 NULL,
149};
150
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +0530151/* Super speed support */
152static struct usb_endpoint_descriptor rmnet_ss_notify_desc = {
Vijayavardhan Vennapusa724ae312012-11-20 17:36:21 +0530153 .bLength = USB_DT_ENDPOINT_SIZE,
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +0530154 .bDescriptorType = USB_DT_ENDPOINT,
155 .bEndpointAddress = USB_DIR_IN,
156 .bmAttributes = USB_ENDPOINT_XFER_INT,
157 .wMaxPacketSize = __constant_cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
158 .bInterval = RMNET_NOTIFY_INTERVAL + 4,
159};
160
161static struct usb_ss_ep_comp_descriptor rmnet_ss_notify_comp_desc = {
162 .bLength = sizeof rmnet_ss_notify_comp_desc,
163 .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
164
165 /* the following 3 values can be tweaked if necessary */
166 /* .bMaxBurst = 0, */
167 /* .bmAttributes = 0, */
168 .wBytesPerInterval = cpu_to_le16(RMNET_MAX_NOTIFY_SIZE),
169};
170
171static struct usb_endpoint_descriptor rmnet_ss_in_desc = {
Vijayavardhan Vennapusa724ae312012-11-20 17:36:21 +0530172 .bLength = USB_DT_ENDPOINT_SIZE,
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +0530173 .bDescriptorType = USB_DT_ENDPOINT,
174 .bEndpointAddress = USB_DIR_IN,
175 .bmAttributes = USB_ENDPOINT_XFER_BULK,
176 .wMaxPacketSize = __constant_cpu_to_le16(1024),
177};
178
179static struct usb_ss_ep_comp_descriptor rmnet_ss_in_comp_desc = {
180 .bLength = sizeof rmnet_ss_in_comp_desc,
181 .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
182
183 /* the following 2 values can be tweaked if necessary */
184 /* .bMaxBurst = 0, */
185 /* .bmAttributes = 0, */
186};
187
188static struct usb_endpoint_descriptor rmnet_ss_out_desc = {
Vijayavardhan Vennapusa724ae312012-11-20 17:36:21 +0530189 .bLength = USB_DT_ENDPOINT_SIZE,
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +0530190 .bDescriptorType = USB_DT_ENDPOINT,
191 .bEndpointAddress = USB_DIR_OUT,
192 .bmAttributes = USB_ENDPOINT_XFER_BULK,
193 .wMaxPacketSize = __constant_cpu_to_le16(1024),
194};
195
196static struct usb_ss_ep_comp_descriptor rmnet_ss_out_comp_desc = {
197 .bLength = sizeof rmnet_ss_out_comp_desc,
198 .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
199
200 /* the following 2 values can be tweaked if necessary */
201 /* .bMaxBurst = 0, */
202 /* .bmAttributes = 0, */
203};
204
205static struct usb_descriptor_header *rmnet_ss_function[] = {
206 (struct usb_descriptor_header *) &rmnet_interface_desc,
207 (struct usb_descriptor_header *) &rmnet_ss_notify_desc,
208 (struct usb_descriptor_header *) &rmnet_ss_notify_comp_desc,
209 (struct usb_descriptor_header *) &rmnet_ss_in_desc,
210 (struct usb_descriptor_header *) &rmnet_ss_in_comp_desc,
211 (struct usb_descriptor_header *) &rmnet_ss_out_desc,
212 (struct usb_descriptor_header *) &rmnet_ss_out_comp_desc,
213 NULL,
214};
215
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216/* String descriptors */
217
218static struct usb_string rmnet_string_defs[] = {
219 [0].s = "RmNet",
220 { } /* end of list */
221};
222
223static struct usb_gadget_strings rmnet_string_table = {
224 .language = 0x0409, /* en-us */
225 .strings = rmnet_string_defs,
226};
227
228static struct usb_gadget_strings *rmnet_strings[] = {
229 &rmnet_string_table,
230 NULL,
231};
232
Amit Blay2d4fb632012-05-29 18:05:38 +0300233static void frmnet_ctrl_response_available(struct f_rmnet *dev);
234
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235/* ------- misc functions --------------------*/
236
237static inline struct f_rmnet *func_to_rmnet(struct usb_function *f)
238{
239 return container_of(f, struct f_rmnet, port.func);
240}
241
242static inline struct f_rmnet *port_to_rmnet(struct grmnet *r)
243{
244 return container_of(r, struct f_rmnet, port);
245}
246
247static struct usb_request *
248frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
249{
250 struct usb_request *req;
251
252 req = usb_ep_alloc_request(ep, flags);
253 if (!req)
254 return ERR_PTR(-ENOMEM);
255
256 req->buf = kmalloc(len, flags);
257 if (!req->buf) {
258 usb_ep_free_request(ep, req);
259 return ERR_PTR(-ENOMEM);
260 }
261
262 req->length = len;
263
264 return req;
265}
266
267void frmnet_free_req(struct usb_ep *ep, struct usb_request *req)
268{
269 kfree(req->buf);
270 usb_ep_free_request(ep, req);
271}
272
273static struct rmnet_ctrl_pkt *rmnet_alloc_ctrl_pkt(unsigned len, gfp_t flags)
274{
275 struct rmnet_ctrl_pkt *pkt;
276
277 pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
278 if (!pkt)
279 return ERR_PTR(-ENOMEM);
280
281 pkt->buf = kmalloc(len, flags);
282 if (!pkt->buf) {
283 kfree(pkt);
284 return ERR_PTR(-ENOMEM);
285 }
286 pkt->len = len;
287
288 return pkt;
289}
290
291static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
292{
293 kfree(pkt->buf);
294 kfree(pkt);
295}
296
297/* -------------------------------------------*/
298
Hemant Kumar1b820d52011-11-03 15:08:28 -0700299static int rmnet_gport_setup(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300{
Jack Pham427f6922011-11-23 19:42:00 -0800301 int ret;
302 int port_idx;
303 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530305 pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u data hsuart ports: %u"
306 " smd ports: %u ctrl hsic ports: %u ctrl hsuart ports: %u"
Ofir Cohena1c2a872011-12-14 10:26:34 +0200307 " nr_rmnet_ports: %u\n",
308 __func__, no_data_bam_ports, no_data_bam2bam_ports,
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530309 no_data_hsic_ports, no_data_hsuart_ports, no_ctrl_smd_ports,
310 no_ctrl_hsic_ports, no_ctrl_hsuart_ports, nr_rmnet_ports);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311
Ofir Cohena1c2a872011-12-14 10:26:34 +0200312 if (no_data_bam_ports || no_data_bam2bam_ports) {
313 ret = gbam_setup(no_data_bam_ports,
314 no_data_bam2bam_ports);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700315 if (ret)
316 return ret;
317 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318
Hemant Kumar1b820d52011-11-03 15:08:28 -0700319 if (no_ctrl_smd_ports) {
320 ret = gsmd_ctrl_setup(no_ctrl_smd_ports);
321 if (ret)
322 return ret;
323 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324
Jack Pham427f6922011-11-23 19:42:00 -0800325 if (no_data_hsic_ports) {
326 port_idx = ghsic_data_setup(no_data_hsic_ports,
327 USB_GADGET_RMNET);
328 if (port_idx < 0)
329 return port_idx;
330 for (i = 0; i < nr_rmnet_ports; i++) {
331 if (rmnet_ports[i].data_xport ==
332 USB_GADGET_XPORT_HSIC) {
333 rmnet_ports[i].data_xport_num = port_idx;
334 port_idx++;
335 }
336 }
337 }
338
339 if (no_ctrl_hsic_ports) {
340 port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
341 USB_GADGET_RMNET);
342 if (port_idx < 0)
343 return port_idx;
344 for (i = 0; i < nr_rmnet_ports; i++) {
345 if (rmnet_ports[i].ctrl_xport ==
346 USB_GADGET_XPORT_HSIC) {
347 rmnet_ports[i].ctrl_xport_num = port_idx;
348 port_idx++;
349 }
350 }
351 }
352
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530353 if (no_data_hsuart_ports) {
354 port_idx = ghsuart_data_setup(no_data_hsuart_ports,
355 USB_GADGET_RMNET);
356 if (port_idx < 0)
357 return port_idx;
358 for (i = 0; i < nr_rmnet_ports; i++) {
359 if (rmnet_ports[i].data_xport ==
360 USB_GADGET_XPORT_HSUART) {
361 rmnet_ports[i].data_xport_num = port_idx;
362 port_idx++;
363 }
364 }
365 }
366
367 if (no_ctrl_hsuart_ports) {
368 port_idx = ghsuart_ctrl_setup(no_ctrl_hsuart_ports,
369 USB_GADGET_RMNET);
370 if (port_idx < 0)
371 return port_idx;
372 for (i = 0; i < nr_rmnet_ports; i++) {
373 if (rmnet_ports[i].ctrl_xport ==
374 USB_GADGET_XPORT_HSUART) {
375 rmnet_ports[i].ctrl_xport_num = port_idx;
376 port_idx++;
377 }
378 }
379 }
380
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381 return 0;
382}
383
Manu Gautam2b0234a2011-09-07 16:47:52 +0530384static int gport_rmnet_connect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700386 int ret;
387 unsigned port_num;
388 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
389 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Vijayavardhan Vennapusa768dfc02013-03-27 18:40:08 +0530390 int src_connection_idx = 0, dst_connection_idx = 0;
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200391 struct usb_gadget *gadget = dev->cdev->gadget;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392
Hemant Kumar1b820d52011-11-03 15:08:28 -0700393 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
394 __func__, xport_to_str(cxport), xport_to_str(dxport),
395 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396
Hemant Kumar1b820d52011-11-03 15:08:28 -0700397 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
398 switch (cxport) {
399 case USB_GADGET_XPORT_SMD:
400 ret = gsmd_ctrl_connect(&dev->port, port_num);
401 if (ret) {
402 pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
403 __func__, ret);
404 return ret;
405 }
406 break;
Bar Weiner0dae81b2013-02-14 13:53:54 +0200407 case USB_GADGET_XPORT_QTI:
408 ret = gqti_ctrl_connect(&dev->port);
409 if (ret) {
410 pr_err("%s: gqti_ctrl_connect failed: err:%d\n",
411 __func__, ret);
412 return ret;
413 }
414 break;
Jack Pham427f6922011-11-23 19:42:00 -0800415 case USB_GADGET_XPORT_HSIC:
416 ret = ghsic_ctrl_connect(&dev->port, port_num);
417 if (ret) {
418 pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
419 __func__, ret);
420 return ret;
421 }
422 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530423 case USB_GADGET_XPORT_HSUART:
424 ret = ghsuart_ctrl_connect(&dev->port, port_num);
425 if (ret) {
426 pr_err("%s: ghsuart_ctrl_connect failed: err:%d\n",
427 __func__, ret);
428 return ret;
429 }
430 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700431 case USB_GADGET_XPORT_NONE:
432 break;
433 default:
434 pr_err("%s: Un-supported transport: %s\n", __func__,
435 xport_to_str(cxport));
436 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 }
438
Hemant Kumar1b820d52011-11-03 15:08:28 -0700439 port_num = rmnet_ports[dev->port_num].data_xport_num;
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200440
Hemant Kumar1b820d52011-11-03 15:08:28 -0700441 switch (dxport) {
Ofir Cohena1c2a872011-12-14 10:26:34 +0200442 case USB_GADGET_XPORT_BAM2BAM:
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200443 src_connection_idx = usb_bam_get_connection_idx(gadget->name,
444 A2_P_BAM, USB_TO_PEER_PERIPHERAL, port_num);
445 dst_connection_idx = usb_bam_get_connection_idx(gadget->name,
446 A2_P_BAM, PEER_PERIPHERAL_TO_USB, port_num);
447 if (dst_connection_idx < 0 || src_connection_idx < 0) {
448 pr_err("%s: usb_bam_get_connection_idx failed\n",
449 __func__);
450 gsmd_ctrl_disconnect(&dev->port, port_num);
451 return ret;
452 }
Vijayavardhan Vennapusa768dfc02013-03-27 18:40:08 +0530453 case USB_GADGET_XPORT_BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200454 ret = gbam_connect(&dev->port, port_num,
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200455 dxport, src_connection_idx, dst_connection_idx);
456 if (ret) {
457 pr_err("%s: gbam_connect failed: err:%d\n",
458 __func__, ret);
459 gsmd_ctrl_disconnect(&dev->port, port_num);
460 return ret;
461 }
462 break;
463 case USB_GADGET_XPORT_BAM2BAM_IPA:
464 src_connection_idx = usb_bam_get_connection_idx(gadget->name,
465 IPA_P_BAM, USB_TO_PEER_PERIPHERAL, port_num);
466 dst_connection_idx = usb_bam_get_connection_idx(gadget->name,
467 IPA_P_BAM, PEER_PERIPHERAL_TO_USB, port_num);
468 if (dst_connection_idx < 0 || src_connection_idx < 0) {
469 pr_err("%s: usb_bam_get_connection_idx failed\n",
470 __func__);
471 gsmd_ctrl_disconnect(&dev->port, port_num);
472 return ret;
473 }
474 ret = gbam_connect(&dev->port, port_num,
475 dxport, src_connection_idx, dst_connection_idx);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700476 if (ret) {
477 pr_err("%s: gbam_connect failed: err:%d\n",
478 __func__, ret);
Bar Weiner0dae81b2013-02-14 13:53:54 +0200479 if (cxport == USB_GADGET_XPORT_QTI)
480 gqti_ctrl_disconnect(&dev->port);
481 else
482 gsmd_ctrl_disconnect(&dev->port, port_num);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700483 return ret;
484 }
485 break;
Jack Pham427f6922011-11-23 19:42:00 -0800486 case USB_GADGET_XPORT_HSIC:
487 ret = ghsic_data_connect(&dev->port, port_num);
488 if (ret) {
489 pr_err("%s: ghsic_data_connect failed: err:%d\n",
490 __func__, ret);
491 ghsic_ctrl_disconnect(&dev->port, port_num);
492 return ret;
493 }
494 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530495 case USB_GADGET_XPORT_HSUART:
496 ret = ghsuart_data_connect(&dev->port, port_num);
497 if (ret) {
498 pr_err("%s: ghsuart_data_connect failed: err:%d\n",
499 __func__, ret);
500 ghsuart_ctrl_disconnect(&dev->port, port_num);
501 return ret;
502 }
503 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700504 case USB_GADGET_XPORT_NONE:
505 break;
506 default:
507 pr_err("%s: Un-supported transport: %s\n", __func__,
508 xport_to_str(dxport));
509 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 }
511
512 return 0;
513}
514
Manu Gautam2b0234a2011-09-07 16:47:52 +0530515static int gport_rmnet_disconnect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700516{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700517 unsigned port_num;
518 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
519 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520
Hemant Kumar1b820d52011-11-03 15:08:28 -0700521 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
522 __func__, xport_to_str(cxport), xport_to_str(dxport),
523 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524
Hemant Kumar1b820d52011-11-03 15:08:28 -0700525 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
526 switch (cxport) {
527 case USB_GADGET_XPORT_SMD:
528 gsmd_ctrl_disconnect(&dev->port, port_num);
529 break;
Bar Weiner0dae81b2013-02-14 13:53:54 +0200530 case USB_GADGET_XPORT_QTI:
531 gqti_ctrl_disconnect(&dev->port);
532 break;
Jack Pham427f6922011-11-23 19:42:00 -0800533 case USB_GADGET_XPORT_HSIC:
534 ghsic_ctrl_disconnect(&dev->port, port_num);
535 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530536 case USB_GADGET_XPORT_HSUART:
537 ghsuart_ctrl_disconnect(&dev->port, port_num);
538 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700539 case USB_GADGET_XPORT_NONE:
540 break;
541 default:
542 pr_err("%s: Un-supported transport: %s\n", __func__,
543 xport_to_str(cxport));
544 return -ENODEV;
545 }
546
547 port_num = rmnet_ports[dev->port_num].data_xport_num;
548 switch (dxport) {
549 case USB_GADGET_XPORT_BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200550 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200551 case USB_GADGET_XPORT_BAM2BAM_IPA:
Ofir Cohen77848d62012-12-05 13:16:10 +0200552 gbam_disconnect(&dev->port, port_num, dxport);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700553 break;
Jack Pham427f6922011-11-23 19:42:00 -0800554 case USB_GADGET_XPORT_HSIC:
555 ghsic_data_disconnect(&dev->port, port_num);
556 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530557 case USB_GADGET_XPORT_HSUART:
558 ghsuart_data_disconnect(&dev->port, port_num);
559 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700560 case USB_GADGET_XPORT_NONE:
561 break;
562 default:
563 pr_err("%s: Un-supported transport: %s\n", __func__,
564 xport_to_str(dxport));
565 return -ENODEV;
566 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700567
568 return 0;
569}
570
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
572{
573 struct f_rmnet *dev = func_to_rmnet(f);
574
575 pr_debug("%s: portno:%d\n", __func__, dev->port_num);
576
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +0530577 if (gadget_is_superspeed(c->cdev->gadget))
578 usb_free_descriptors(f->ss_descriptors);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579 if (gadget_is_dualspeed(c->cdev->gadget))
580 usb_free_descriptors(f->hs_descriptors);
581 usb_free_descriptors(f->descriptors);
582
583 frmnet_free_req(dev->notify, dev->notify_req);
584
Manu Gautamdd4222b2011-09-09 15:06:05 +0530585 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586}
587
Ido Shayevitz909ed382013-02-15 02:25:35 +0200588static void frmnet_purge_responses(struct f_rmnet *dev)
589{
590 unsigned long flags;
591 struct rmnet_ctrl_pkt *cpkt;
592
593 pr_debug("%s: port#%d\n", __func__, dev->port_num);
594
595 spin_lock_irqsave(&dev->lock, flags);
596 while (!list_empty(&dev->cpkt_resp_q)) {
597 cpkt = list_first_entry(&dev->cpkt_resp_q,
598 struct rmnet_ctrl_pkt, list);
599
600 list_del(&cpkt->list);
601 rmnet_free_ctrl_pkt(cpkt);
602 }
603 atomic_set(&dev->notify_count, 0);
604 spin_unlock_irqrestore(&dev->lock, flags);
605}
606
Amit Blaye5bb35e2012-05-08 20:38:20 +0300607static void frmnet_suspend(struct usb_function *f)
608{
609 struct f_rmnet *dev = func_to_rmnet(f);
610 unsigned port_num;
611 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
612
613 pr_debug("%s: data xport: %s dev: %p portno: %d\n",
614 __func__, xport_to_str(dxport),
615 dev, dev->port_num);
616
Ido Shayevitz909ed382013-02-15 02:25:35 +0200617 frmnet_purge_responses(dev);
618
Amit Blaye5bb35e2012-05-08 20:38:20 +0300619 port_num = rmnet_ports[dev->port_num].data_xport_num;
620 switch (dxport) {
621 case USB_GADGET_XPORT_BAM:
622 break;
623 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200624 case USB_GADGET_XPORT_BAM2BAM_IPA:
Amit Blaye5bb35e2012-05-08 20:38:20 +0300625 gbam_suspend(&dev->port, port_num, dxport);
626 break;
627 case USB_GADGET_XPORT_HSIC:
628 break;
Mayank Rana9dc199a2012-08-28 10:22:07 +0530629 case USB_GADGET_XPORT_HSUART:
630 break;
Amit Blaye5bb35e2012-05-08 20:38:20 +0300631 case USB_GADGET_XPORT_NONE:
632 break;
633 default:
634 pr_err("%s: Un-supported transport: %s\n", __func__,
635 xport_to_str(dxport));
636 }
637}
638
639static void frmnet_resume(struct usb_function *f)
640{
641 struct f_rmnet *dev = func_to_rmnet(f);
642 unsigned port_num;
643 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
644
645 pr_debug("%s: data xport: %s dev: %p portno: %d\n",
646 __func__, xport_to_str(dxport),
647 dev, dev->port_num);
648
649 port_num = rmnet_ports[dev->port_num].data_xport_num;
650 switch (dxport) {
651 case USB_GADGET_XPORT_BAM:
652 break;
653 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200654 case USB_GADGET_XPORT_BAM2BAM_IPA:
Amit Blaye5bb35e2012-05-08 20:38:20 +0300655 gbam_resume(&dev->port, port_num, dxport);
656 break;
657 case USB_GADGET_XPORT_HSIC:
658 break;
Mayank Rana9dc199a2012-08-28 10:22:07 +0530659 case USB_GADGET_XPORT_HSUART:
660 break;
Amit Blaye5bb35e2012-05-08 20:38:20 +0300661 case USB_GADGET_XPORT_NONE:
662 break;
663 default:
664 pr_err("%s: Un-supported transport: %s\n", __func__,
665 xport_to_str(dxport));
666 }
667}
668
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669static void frmnet_disable(struct usb_function *f)
670{
671 struct f_rmnet *dev = func_to_rmnet(f);
672
673 pr_debug("%s: port#%d\n", __func__, dev->port_num);
674
675 usb_ep_disable(dev->notify);
Anna Perel97b8c222012-01-18 10:08:14 +0200676 dev->notify->driver_data = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677
678 atomic_set(&dev->online, 0);
679
Ido Shayevitz909ed382013-02-15 02:25:35 +0200680 frmnet_purge_responses(dev);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700681
Manu Gautam2b0234a2011-09-07 16:47:52 +0530682 gport_rmnet_disconnect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700683}
684
685static int
686frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
687{
688 struct f_rmnet *dev = func_to_rmnet(f);
689 struct usb_composite_dev *cdev = dev->cdev;
690 int ret;
Amit Blay2d4fb632012-05-29 18:05:38 +0300691 struct list_head *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692
693 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
694
695 if (dev->notify->driver_data) {
696 pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
697 usb_ep_disable(dev->notify);
698 }
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200699
700 ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
701 if (ret) {
702 dev->notify->desc = NULL;
703 ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
704 dev->notify->name, ret);
705 return ret;
706 }
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300707 ret = usb_ep_enable(dev->notify);
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200708
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700709 if (ret) {
710 pr_err("%s: usb ep#%s enable failed, err#%d\n",
711 __func__, dev->notify->name, ret);
712 return ret;
713 }
714 dev->notify->driver_data = dev;
715
Chiranjeevi Velempati502b1c82012-05-16 14:49:46 +0530716 if (!dev->port.in->desc || !dev->port.out->desc) {
Bar Weiner0fc137a2012-03-28 16:58:09 +0200717 if (config_ep_by_speed(cdev->gadget, f, dev->port.in) ||
718 config_ep_by_speed(cdev->gadget, f, dev->port.out)) {
719 dev->port.in->desc = NULL;
720 dev->port.out->desc = NULL;
721 return -EINVAL;
722 }
723 ret = gport_rmnet_connect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700724 }
725
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726 atomic_set(&dev->online, 1);
727
Amit Blay2d4fb632012-05-29 18:05:38 +0300728 /* In case notifications were aborted, but there are pending control
729 packets in the response queue, re-add the notifications */
730 list_for_each(cpkt, &dev->cpkt_resp_q)
731 frmnet_ctrl_response_available(dev);
732
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733 return ret;
734}
735
736static void frmnet_ctrl_response_available(struct f_rmnet *dev)
737{
738 struct usb_request *req = dev->notify_req;
739 struct usb_cdc_notification *event;
740 unsigned long flags;
741 int ret;
Anna Perelf3af59d2012-08-12 15:28:30 +0300742 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700743
744 pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num);
745
746 spin_lock_irqsave(&dev->lock, flags);
747 if (!atomic_read(&dev->online) || !req || !req->buf) {
748 spin_unlock_irqrestore(&dev->lock, flags);
749 return;
750 }
751
752 if (atomic_inc_return(&dev->notify_count) != 1) {
753 spin_unlock_irqrestore(&dev->lock, flags);
754 return;
755 }
756
757 event = req->buf;
758 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
759 | USB_RECIP_INTERFACE;
760 event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
761 event->wValue = cpu_to_le16(0);
762 event->wIndex = cpu_to_le16(dev->ifc_id);
763 event->wLength = cpu_to_le16(0);
764 spin_unlock_irqrestore(&dev->lock, flags);
765
766 ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
767 if (ret) {
Anna Perelf3af59d2012-08-12 15:28:30 +0300768 spin_lock_irqsave(&dev->lock, flags);
Ido Shayevitz909ed382013-02-15 02:25:35 +0200769 if (!list_empty(&dev->cpkt_resp_q)) {
770 atomic_dec(&dev->notify_count);
771 cpkt = list_first_entry(&dev->cpkt_resp_q,
Anna Perelf3af59d2012-08-12 15:28:30 +0300772 struct rmnet_ctrl_pkt, list);
Anna Perelf3af59d2012-08-12 15:28:30 +0300773 list_del(&cpkt->list);
774 rmnet_free_ctrl_pkt(cpkt);
775 }
776 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700777 pr_debug("ep enqueue error %d\n", ret);
778 }
779}
780
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700781static void frmnet_connect(struct grmnet *gr)
782{
783 struct f_rmnet *dev;
784
785 if (!gr) {
786 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
787 return;
788 }
789
790 dev = port_to_rmnet(gr);
791
792 atomic_set(&dev->ctrl_online, 1);
793}
794
795static void frmnet_disconnect(struct grmnet *gr)
796{
797 struct f_rmnet *dev;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700798 struct usb_cdc_notification *event;
799 int status;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700800
801 if (!gr) {
802 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
803 return;
804 }
805
806 dev = port_to_rmnet(gr);
807
808 atomic_set(&dev->ctrl_online, 0);
809
810 if (!atomic_read(&dev->online)) {
811 pr_debug("%s: nothing to do\n", __func__);
812 return;
813 }
814
815 usb_ep_fifo_flush(dev->notify);
816
817 event = dev->notify_req->buf;
818 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
819 | USB_RECIP_INTERFACE;
820 event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
821 event->wValue = cpu_to_le16(0);
822 event->wIndex = cpu_to_le16(dev->ifc_id);
823 event->wLength = cpu_to_le16(0);
824
Vamsi Krishna188078d2011-10-26 15:09:55 -0700825 status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700826 if (status < 0) {
827 if (!atomic_read(&dev->online))
828 return;
829 pr_err("%s: rmnet notify ep enqueue error %d\n",
830 __func__, status);
831 }
832
Ido Shayevitz909ed382013-02-15 02:25:35 +0200833 frmnet_purge_responses(dev);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700834}
835
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700836static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700837frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838{
839 struct f_rmnet *dev;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700840 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700841 unsigned long flags;
842
Hemant Kumarf60c0252011-11-03 12:37:07 -0700843 if (!gr || !buf) {
844 pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
845 __func__, gr, buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700846 return -ENODEV;
847 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700848 cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
849 if (IS_ERR(cpkt)) {
850 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
851 return -ENOMEM;
852 }
853 memcpy(cpkt->buf, buf, len);
854 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855
856 dev = port_to_rmnet(gr);
857
858 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
859
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700860 if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861 rmnet_free_ctrl_pkt(cpkt);
862 return 0;
863 }
864
865 spin_lock_irqsave(&dev->lock, flags);
Chiranjeevi Velempati263e6c82011-11-11 23:07:36 +0530866 list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867 spin_unlock_irqrestore(&dev->lock, flags);
868
869 frmnet_ctrl_response_available(dev);
870
871 return 0;
872}
873
874static void
875frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req)
876{
877 struct f_rmnet *dev = req->context;
878 struct usb_composite_dev *cdev;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700879 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880
881 if (!dev) {
882 pr_err("%s: rmnet dev is null\n", __func__);
883 return;
884 }
885
886 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
887
888 cdev = dev->cdev;
889
Hemant Kumar1b820d52011-11-03 15:08:28 -0700890 if (dev->port.send_encap_cmd) {
891 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
892 dev->port.send_encap_cmd(port_num, req->buf, req->actual);
893 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700894}
895
896static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
897{
898 struct f_rmnet *dev = req->context;
899 int status = req->status;
Anna Perelf3af59d2012-08-12 15:28:30 +0300900 unsigned long flags;
901 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902
903 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
904
905 switch (status) {
906 case -ECONNRESET:
907 case -ESHUTDOWN:
908 /* connection gone */
909 atomic_set(&dev->notify_count, 0);
910 break;
911 default:
912 pr_err("rmnet notify ep error %d\n", status);
913 /* FALLTHROUGH */
914 case 0:
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700915 if (!atomic_read(&dev->ctrl_online))
916 break;
917
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918 if (atomic_dec_and_test(&dev->notify_count))
919 break;
920
921 status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
922 if (status) {
Anna Perelf3af59d2012-08-12 15:28:30 +0300923 spin_lock_irqsave(&dev->lock, flags);
Ido Shayevitz909ed382013-02-15 02:25:35 +0200924 if (!list_empty(&dev->cpkt_resp_q)) {
925 atomic_dec(&dev->notify_count);
926 cpkt = list_first_entry(&dev->cpkt_resp_q,
Anna Perelf3af59d2012-08-12 15:28:30 +0300927 struct rmnet_ctrl_pkt, list);
Anna Perelf3af59d2012-08-12 15:28:30 +0300928 list_del(&cpkt->list);
929 rmnet_free_ctrl_pkt(cpkt);
930 }
931 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700932 pr_debug("ep enqueue error %d\n", status);
933 }
934 break;
935 }
936}
937
938static int
939frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
940{
941 struct f_rmnet *dev = func_to_rmnet(f);
942 struct usb_composite_dev *cdev = dev->cdev;
943 struct usb_request *req = cdev->req;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700944 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 u16 w_index = le16_to_cpu(ctrl->wIndex);
946 u16 w_value = le16_to_cpu(ctrl->wValue);
947 u16 w_length = le16_to_cpu(ctrl->wLength);
948 int ret = -EOPNOTSUPP;
949
950 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
951
952 if (!atomic_read(&dev->online)) {
953 pr_debug("%s: usb cable is not connected\n", __func__);
954 return -ENOTCONN;
955 }
956
957 switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
958
959 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
960 | USB_CDC_SEND_ENCAPSULATED_COMMAND:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961 ret = w_length;
962 req->complete = frmnet_cmd_complete;
963 req->context = dev;
964 break;
965
966
967 case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
968 | USB_CDC_GET_ENCAPSULATED_RESPONSE:
969 if (w_value)
970 goto invalid;
971 else {
972 unsigned len;
973 struct rmnet_ctrl_pkt *cpkt;
974
975 spin_lock(&dev->lock);
976 if (list_empty(&dev->cpkt_resp_q)) {
977 pr_err("ctrl resp queue empty "
978 " req%02x.%02x v%04x i%04x l%d\n",
979 ctrl->bRequestType, ctrl->bRequest,
980 w_value, w_index, w_length);
981 spin_unlock(&dev->lock);
982 goto invalid;
983 }
984
985 cpkt = list_first_entry(&dev->cpkt_resp_q,
986 struct rmnet_ctrl_pkt, list);
987 list_del(&cpkt->list);
988 spin_unlock(&dev->lock);
989
990 len = min_t(unsigned, w_length, cpkt->len);
991 memcpy(req->buf, cpkt->buf, len);
992 ret = len;
993
994 rmnet_free_ctrl_pkt(cpkt);
995 }
996 break;
997 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
998 | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
Hemant Kumar1b820d52011-11-03 15:08:28 -0700999 if (dev->port.notify_modem) {
1000 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
1001 dev->port.notify_modem(&dev->port, port_num, w_value);
1002 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001003 ret = 0;
1004
1005 break;
1006 default:
1007
1008invalid:
1009 DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
1010 ctrl->bRequestType, ctrl->bRequest,
1011 w_value, w_index, w_length);
1012 }
1013
1014 /* respond with data transfer or status phase? */
1015 if (ret >= 0) {
1016 VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
1017 ctrl->bRequestType, ctrl->bRequest,
1018 w_value, w_index, w_length);
1019 req->zero = (ret < w_length);
1020 req->length = ret;
1021 ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
1022 if (ret < 0)
1023 ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
1024 }
1025
1026 return ret;
1027}
1028
1029static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
1030{
1031 struct f_rmnet *dev = func_to_rmnet(f);
1032 struct usb_ep *ep;
1033 struct usb_composite_dev *cdev = c->cdev;
1034 int ret = -ENODEV;
1035
1036 dev->ifc_id = usb_interface_id(c, f);
1037 if (dev->ifc_id < 0) {
1038 pr_err("%s: unable to allocate ifc id, err:%d",
1039 __func__, dev->ifc_id);
1040 return dev->ifc_id;
1041 }
1042 rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
1043
1044 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
1045 if (!ep) {
1046 pr_err("%s: usb epin autoconfig failed\n", __func__);
1047 return -ENODEV;
1048 }
1049 dev->port.in = ep;
1050 ep->driver_data = cdev;
1051
1052 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
1053 if (!ep) {
1054 pr_err("%s: usb epout autoconfig failed\n", __func__);
1055 ret = -ENODEV;
1056 goto ep_auto_out_fail;
1057 }
1058 dev->port.out = ep;
1059 ep->driver_data = cdev;
1060
1061 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
1062 if (!ep) {
1063 pr_err("%s: usb epnotify autoconfig failed\n", __func__);
1064 ret = -ENODEV;
1065 goto ep_auto_notify_fail;
1066 }
1067 dev->notify = ep;
1068 ep->driver_data = cdev;
1069
1070 dev->notify_req = frmnet_alloc_req(ep,
Hemant Kumarfbf113d2011-09-16 18:24:45 -07001071 sizeof(struct usb_cdc_notification),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001072 GFP_KERNEL);
1073 if (IS_ERR(dev->notify_req)) {
1074 pr_err("%s: unable to allocate memory for notify req\n",
1075 __func__);
1076 ret = -ENOMEM;
1077 goto ep_notify_alloc_fail;
1078 }
1079
1080 dev->notify_req->complete = frmnet_notify_complete;
1081 dev->notify_req->context = dev;
1082
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301083 ret = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001084 f->descriptors = usb_copy_descriptors(rmnet_fs_function);
1085
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301086 if (!f->descriptors)
1087 goto fail;
1088
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001089 if (gadget_is_dualspeed(cdev->gadget)) {
1090 rmnet_hs_in_desc.bEndpointAddress =
1091 rmnet_fs_in_desc.bEndpointAddress;
1092 rmnet_hs_out_desc.bEndpointAddress =
1093 rmnet_fs_out_desc.bEndpointAddress;
1094 rmnet_hs_notify_desc.bEndpointAddress =
1095 rmnet_fs_notify_desc.bEndpointAddress;
1096
1097 /* copy descriptors, and track endpoint copies */
1098 f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
1099
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301100 if (!f->hs_descriptors)
1101 goto fail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001102 }
1103
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301104 if (gadget_is_superspeed(cdev->gadget)) {
1105 rmnet_ss_in_desc.bEndpointAddress =
1106 rmnet_fs_in_desc.bEndpointAddress;
1107 rmnet_ss_out_desc.bEndpointAddress =
1108 rmnet_fs_out_desc.bEndpointAddress;
1109 rmnet_ss_notify_desc.bEndpointAddress =
1110 rmnet_fs_notify_desc.bEndpointAddress;
1111
1112 /* copy descriptors, and track endpoint copies */
1113 f->ss_descriptors = usb_copy_descriptors(rmnet_ss_function);
1114
1115 if (!f->ss_descriptors)
1116 goto fail;
1117 }
1118
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001119 pr_info("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
1120 __func__, dev->port_num,
1121 gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
1122 dev->port.in->name, dev->port.out->name);
1123
1124 return 0;
1125
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301126fail:
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301127 if (f->ss_descriptors)
1128 usb_free_descriptors(f->ss_descriptors);
1129 if (f->hs_descriptors)
1130 usb_free_descriptors(f->hs_descriptors);
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301131 if (f->descriptors)
1132 usb_free_descriptors(f->descriptors);
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301133 if (dev->notify_req)
1134 frmnet_free_req(dev->notify, dev->notify_req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001135ep_notify_alloc_fail:
1136 dev->notify->driver_data = NULL;
1137 dev->notify = NULL;
1138ep_auto_notify_fail:
1139 dev->port.out->driver_data = NULL;
1140 dev->port.out = NULL;
1141ep_auto_out_fail:
1142 dev->port.in->driver_data = NULL;
1143 dev->port.in = NULL;
1144
1145 return ret;
1146}
1147
Manu Gautam2b0234a2011-09-07 16:47:52 +05301148static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001149{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001150 int status;
1151 struct f_rmnet *dev;
1152 struct usb_function *f;
1153 unsigned long flags;
1154
1155 pr_debug("%s: usb config:%p\n", __func__, c);
1156
Manu Gautam2b0234a2011-09-07 16:47:52 +05301157 if (portno >= nr_rmnet_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001158 pr_err("%s: supporting ports#%u port_id:%u", __func__,
Manu Gautam2b0234a2011-09-07 16:47:52 +05301159 nr_rmnet_ports, portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001160 return -ENODEV;
1161 }
1162
1163 if (rmnet_string_defs[0].id == 0) {
1164 status = usb_string_id(c->cdev);
1165 if (status < 0) {
1166 pr_err("%s: failed to get string id, err:%d\n",
1167 __func__, status);
1168 return status;
1169 }
1170 rmnet_string_defs[0].id = status;
1171 }
1172
Manu Gautam2b0234a2011-09-07 16:47:52 +05301173 dev = rmnet_ports[portno].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001174
1175 spin_lock_irqsave(&dev->lock, flags);
1176 dev->cdev = c->cdev;
1177 f = &dev->port.func;
Vamsi Krishna188078d2011-10-26 15:09:55 -07001178 f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001179 spin_unlock_irqrestore(&dev->lock, flags);
Vamsi Krishna188078d2011-10-26 15:09:55 -07001180 if (!f->name) {
1181 pr_err("%s: cannot allocate memory for name\n", __func__);
1182 return -ENOMEM;
1183 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001184
1185 f->strings = rmnet_strings;
1186 f->bind = frmnet_bind;
1187 f->unbind = frmnet_unbind;
1188 f->disable = frmnet_disable;
1189 f->set_alt = frmnet_set_alt;
1190 f->setup = frmnet_setup;
Amit Blaye5bb35e2012-05-08 20:38:20 +03001191 f->suspend = frmnet_suspend;
1192 f->resume = frmnet_resume;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001193 dev->port.send_cpkt_response = frmnet_send_cpkt_response;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -07001194 dev->port.disconnect = frmnet_disconnect;
1195 dev->port.connect = frmnet_connect;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001196
1197 status = usb_add_function(c, f);
1198 if (status) {
1199 pr_err("%s: usb add function failed: %d\n",
1200 __func__, status);
Manu Gautam2b0234a2011-09-07 16:47:52 +05301201 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001202 return status;
1203 }
1204
1205 pr_debug("%s: complete\n", __func__);
1206
1207 return status;
1208}
1209
Manu Gautame3e897c2011-09-12 17:18:46 +05301210static void frmnet_cleanup(void)
1211{
1212 int i;
1213
1214 for (i = 0; i < nr_rmnet_ports; i++)
1215 kfree(rmnet_ports[i].port);
1216
1217 nr_rmnet_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001218 no_ctrl_smd_ports = 0;
Bar Weiner0dae81b2013-02-14 13:53:54 +02001219 no_ctrl_qti_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001220 no_data_bam_ports = 0;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001221 no_data_bam2bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -08001222 no_ctrl_hsic_ports = 0;
1223 no_data_hsic_ports = 0;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301224 no_ctrl_hsuart_ports = 0;
1225 no_data_hsuart_ports = 0;
Manu Gautame3e897c2011-09-12 17:18:46 +05301226}
1227
Hemant Kumar1b820d52011-11-03 15:08:28 -07001228static int frmnet_init_port(const char *ctrl_name, const char *data_name)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001229{
Hemant Kumar1b820d52011-11-03 15:08:28 -07001230 struct f_rmnet *dev;
1231 struct rmnet_ports *rmnet_port;
1232 int ret;
1233 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001234
Hemant Kumar1b820d52011-11-03 15:08:28 -07001235 if (nr_rmnet_ports >= NR_RMNET_PORTS) {
1236 pr_err("%s: Max-%d instances supported\n",
1237 __func__, NR_RMNET_PORTS);
Manu Gautame3e897c2011-09-12 17:18:46 +05301238 return -EINVAL;
1239 }
1240
Hemant Kumar1b820d52011-11-03 15:08:28 -07001241 pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
1242 __func__, nr_rmnet_ports, ctrl_name, data_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001243
Hemant Kumar1b820d52011-11-03 15:08:28 -07001244 dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
1245 if (!dev) {
1246 pr_err("%s: Unable to allocate rmnet device\n", __func__);
1247 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001248 }
1249
Hemant Kumar1b820d52011-11-03 15:08:28 -07001250 dev->port_num = nr_rmnet_ports;
1251 spin_lock_init(&dev->lock);
1252 INIT_LIST_HEAD(&dev->cpkt_resp_q);
1253
1254 rmnet_port = &rmnet_ports[nr_rmnet_ports];
1255 rmnet_port->port = dev;
1256 rmnet_port->port_num = nr_rmnet_ports;
1257 rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
1258 rmnet_port->data_xport = str_to_xport(data_name);
1259
1260 switch (rmnet_port->ctrl_xport) {
1261 case USB_GADGET_XPORT_SMD:
1262 rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
1263 no_ctrl_smd_ports++;
1264 break;
Bar Weiner0dae81b2013-02-14 13:53:54 +02001265 case USB_GADGET_XPORT_QTI:
1266 rmnet_port->ctrl_xport_num = no_ctrl_qti_ports;
1267 no_ctrl_qti_ports++;
1268 break;
Jack Pham427f6922011-11-23 19:42:00 -08001269 case USB_GADGET_XPORT_HSIC:
1270 rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
1271 no_ctrl_hsic_ports++;
1272 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301273 case USB_GADGET_XPORT_HSUART:
1274 rmnet_port->ctrl_xport_num = no_ctrl_hsuart_ports;
1275 no_ctrl_hsuart_ports++;
1276 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001277 case USB_GADGET_XPORT_NONE:
1278 break;
1279 default:
1280 pr_err("%s: Un-supported transport: %u\n", __func__,
1281 rmnet_port->ctrl_xport);
1282 ret = -ENODEV;
1283 goto fail_probe;
1284 }
1285
1286 switch (rmnet_port->data_xport) {
1287 case USB_GADGET_XPORT_BAM:
1288 rmnet_port->data_xport_num = no_data_bam_ports;
1289 no_data_bam_ports++;
1290 break;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001291 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +02001292 case USB_GADGET_XPORT_BAM2BAM_IPA:
Ofir Cohena1c2a872011-12-14 10:26:34 +02001293 rmnet_port->data_xport_num = no_data_bam2bam_ports;
1294 no_data_bam2bam_ports++;
1295 break;
Jack Pham427f6922011-11-23 19:42:00 -08001296 case USB_GADGET_XPORT_HSIC:
1297 rmnet_port->data_xport_num = no_data_hsic_ports;
1298 no_data_hsic_ports++;
1299 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301300 case USB_GADGET_XPORT_HSUART:
1301 rmnet_port->data_xport_num = no_data_hsuart_ports;
1302 no_data_hsuart_ports++;
1303 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001304 case USB_GADGET_XPORT_NONE:
1305 break;
1306 default:
1307 pr_err("%s: Un-supported transport: %u\n", __func__,
1308 rmnet_port->data_xport);
1309 ret = -ENODEV;
1310 goto fail_probe;
1311 }
1312 nr_rmnet_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001313
1314 return 0;
1315
1316fail_probe:
Manu Gautam2b0234a2011-09-07 16:47:52 +05301317 for (i = 0; i < nr_rmnet_ports; i++)
1318 kfree(rmnet_ports[i].port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001319
Hemant Kumar1b820d52011-11-03 15:08:28 -07001320 nr_rmnet_ports = 0;
1321 no_ctrl_smd_ports = 0;
Bar Weiner0dae81b2013-02-14 13:53:54 +02001322 no_ctrl_qti_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001323 no_data_bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -08001324 no_ctrl_hsic_ports = 0;
1325 no_data_hsic_ports = 0;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301326 no_ctrl_hsuart_ports = 0;
1327 no_data_hsuart_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001328
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001329 return ret;
1330}