blob: b828531135a127f7c471a95a56061a5cbb137775 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +05302 * Copyright (c) 2011-2014, 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;
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +053050 unsigned long notify_count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051 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;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300304 u8 base;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530306 pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u data hsuart ports: %u"
307 " smd ports: %u ctrl hsic ports: %u ctrl hsuart ports: %u"
Ofir Cohena1c2a872011-12-14 10:26:34 +0200308 " nr_rmnet_ports: %u\n",
309 __func__, no_data_bam_ports, no_data_bam2bam_ports,
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530310 no_data_hsic_ports, no_data_hsuart_ports, no_ctrl_smd_ports,
311 no_ctrl_hsic_ports, no_ctrl_hsuart_ports, nr_rmnet_ports);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312
Ofir Cohena1c2a872011-12-14 10:26:34 +0200313 if (no_data_bam_ports || no_data_bam2bam_ports) {
314 ret = gbam_setup(no_data_bam_ports,
315 no_data_bam2bam_ports);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700316 if (ret)
317 return ret;
318 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319
Hemant Kumar1b820d52011-11-03 15:08:28 -0700320 if (no_ctrl_smd_ports) {
Bar Weiner6c817eb2013-04-15 20:52:18 +0300321 ret = gsmd_ctrl_setup(FRMNET_CTRL_CLIENT,
322 no_ctrl_smd_ports, &base);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700323 if (ret)
324 return ret;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300325 for (i = 0; i < nr_rmnet_ports; i++)
326 if (rmnet_ports[i].port)
327 rmnet_ports[i].port->port_num += base;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700328 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329
Jack Pham427f6922011-11-23 19:42:00 -0800330 if (no_data_hsic_ports) {
331 port_idx = ghsic_data_setup(no_data_hsic_ports,
332 USB_GADGET_RMNET);
333 if (port_idx < 0)
334 return port_idx;
335 for (i = 0; i < nr_rmnet_ports; i++) {
336 if (rmnet_ports[i].data_xport ==
337 USB_GADGET_XPORT_HSIC) {
338 rmnet_ports[i].data_xport_num = port_idx;
339 port_idx++;
340 }
341 }
342 }
343
344 if (no_ctrl_hsic_ports) {
345 port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
346 USB_GADGET_RMNET);
347 if (port_idx < 0)
348 return port_idx;
349 for (i = 0; i < nr_rmnet_ports; i++) {
350 if (rmnet_ports[i].ctrl_xport ==
351 USB_GADGET_XPORT_HSIC) {
352 rmnet_ports[i].ctrl_xport_num = port_idx;
353 port_idx++;
354 }
355 }
356 }
357
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530358 if (no_data_hsuart_ports) {
359 port_idx = ghsuart_data_setup(no_data_hsuart_ports,
360 USB_GADGET_RMNET);
361 if (port_idx < 0)
362 return port_idx;
363 for (i = 0; i < nr_rmnet_ports; i++) {
364 if (rmnet_ports[i].data_xport ==
365 USB_GADGET_XPORT_HSUART) {
366 rmnet_ports[i].data_xport_num = port_idx;
367 port_idx++;
368 }
369 }
370 }
371
372 if (no_ctrl_hsuart_ports) {
373 port_idx = ghsuart_ctrl_setup(no_ctrl_hsuart_ports,
374 USB_GADGET_RMNET);
375 if (port_idx < 0)
376 return port_idx;
377 for (i = 0; i < nr_rmnet_ports; i++) {
378 if (rmnet_ports[i].ctrl_xport ==
379 USB_GADGET_XPORT_HSUART) {
380 rmnet_ports[i].ctrl_xport_num = port_idx;
381 port_idx++;
382 }
383 }
384 }
385
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700386 return 0;
387}
388
Manu Gautam2b0234a2011-09-07 16:47:52 +0530389static int gport_rmnet_connect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700391 int ret;
392 unsigned port_num;
393 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
394 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Vijayavardhan Vennapusa768dfc02013-03-27 18:40:08 +0530395 int src_connection_idx = 0, dst_connection_idx = 0;
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200396 struct usb_gadget *gadget = dev->cdev->gadget;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397
Hemant Kumar1b820d52011-11-03 15:08:28 -0700398 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
399 __func__, xport_to_str(cxport), xport_to_str(dxport),
400 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401
Hemant Kumar1b820d52011-11-03 15:08:28 -0700402 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
403 switch (cxport) {
404 case USB_GADGET_XPORT_SMD:
405 ret = gsmd_ctrl_connect(&dev->port, port_num);
406 if (ret) {
407 pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
408 __func__, ret);
409 return ret;
410 }
411 break;
Bar Weiner0dae81b2013-02-14 13:53:54 +0200412 case USB_GADGET_XPORT_QTI:
413 ret = gqti_ctrl_connect(&dev->port);
414 if (ret) {
415 pr_err("%s: gqti_ctrl_connect failed: err:%d\n",
416 __func__, ret);
417 return ret;
418 }
419 break;
Jack Pham427f6922011-11-23 19:42:00 -0800420 case USB_GADGET_XPORT_HSIC:
421 ret = ghsic_ctrl_connect(&dev->port, port_num);
422 if (ret) {
423 pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
424 __func__, ret);
425 return ret;
426 }
427 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530428 case USB_GADGET_XPORT_HSUART:
429 ret = ghsuart_ctrl_connect(&dev->port, port_num);
430 if (ret) {
431 pr_err("%s: ghsuart_ctrl_connect failed: err:%d\n",
432 __func__, ret);
433 return ret;
434 }
435 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700436 case USB_GADGET_XPORT_NONE:
437 break;
438 default:
439 pr_err("%s: Un-supported transport: %s\n", __func__,
440 xport_to_str(cxport));
441 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 }
443
Hemant Kumar1b820d52011-11-03 15:08:28 -0700444 port_num = rmnet_ports[dev->port_num].data_xport_num;
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200445
Hemant Kumar1b820d52011-11-03 15:08:28 -0700446 switch (dxport) {
Ofir Cohena1c2a872011-12-14 10:26:34 +0200447 case USB_GADGET_XPORT_BAM2BAM:
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200448 src_connection_idx = usb_bam_get_connection_idx(gadget->name,
449 A2_P_BAM, USB_TO_PEER_PERIPHERAL, port_num);
450 dst_connection_idx = usb_bam_get_connection_idx(gadget->name,
451 A2_P_BAM, PEER_PERIPHERAL_TO_USB, port_num);
452 if (dst_connection_idx < 0 || src_connection_idx < 0) {
453 pr_err("%s: usb_bam_get_connection_idx failed\n",
454 __func__);
455 gsmd_ctrl_disconnect(&dev->port, port_num);
456 return ret;
457 }
Vijayavardhan Vennapusa768dfc02013-03-27 18:40:08 +0530458 case USB_GADGET_XPORT_BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200459 ret = gbam_connect(&dev->port, port_num,
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200460 dxport, src_connection_idx, dst_connection_idx);
461 if (ret) {
462 pr_err("%s: gbam_connect failed: err:%d\n",
463 __func__, ret);
464 gsmd_ctrl_disconnect(&dev->port, port_num);
465 return ret;
466 }
467 break;
468 case USB_GADGET_XPORT_BAM2BAM_IPA:
469 src_connection_idx = usb_bam_get_connection_idx(gadget->name,
470 IPA_P_BAM, USB_TO_PEER_PERIPHERAL, port_num);
471 dst_connection_idx = usb_bam_get_connection_idx(gadget->name,
472 IPA_P_BAM, PEER_PERIPHERAL_TO_USB, port_num);
473 if (dst_connection_idx < 0 || src_connection_idx < 0) {
474 pr_err("%s: usb_bam_get_connection_idx failed\n",
475 __func__);
476 gsmd_ctrl_disconnect(&dev->port, port_num);
477 return ret;
478 }
479 ret = gbam_connect(&dev->port, port_num,
480 dxport, src_connection_idx, dst_connection_idx);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700481 if (ret) {
482 pr_err("%s: gbam_connect failed: err:%d\n",
483 __func__, ret);
Bar Weiner0dae81b2013-02-14 13:53:54 +0200484 if (cxport == USB_GADGET_XPORT_QTI)
485 gqti_ctrl_disconnect(&dev->port);
486 else
487 gsmd_ctrl_disconnect(&dev->port, port_num);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700488 return ret;
489 }
490 break;
Jack Pham427f6922011-11-23 19:42:00 -0800491 case USB_GADGET_XPORT_HSIC:
492 ret = ghsic_data_connect(&dev->port, port_num);
493 if (ret) {
494 pr_err("%s: ghsic_data_connect failed: err:%d\n",
495 __func__, ret);
496 ghsic_ctrl_disconnect(&dev->port, port_num);
497 return ret;
498 }
499 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530500 case USB_GADGET_XPORT_HSUART:
501 ret = ghsuart_data_connect(&dev->port, port_num);
502 if (ret) {
503 pr_err("%s: ghsuart_data_connect failed: err:%d\n",
504 __func__, ret);
505 ghsuart_ctrl_disconnect(&dev->port, port_num);
506 return ret;
507 }
508 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700509 case USB_GADGET_XPORT_NONE:
510 break;
511 default:
512 pr_err("%s: Un-supported transport: %s\n", __func__,
513 xport_to_str(dxport));
514 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515 }
516
517 return 0;
518}
519
Manu Gautam2b0234a2011-09-07 16:47:52 +0530520static int gport_rmnet_disconnect(struct f_rmnet *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521{
Hemant Kumar1b820d52011-11-03 15:08:28 -0700522 unsigned port_num;
523 enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
524 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525
Hemant Kumar1b820d52011-11-03 15:08:28 -0700526 pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
527 __func__, xport_to_str(cxport), xport_to_str(dxport),
528 dev, dev->port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529
Hemant Kumar1b820d52011-11-03 15:08:28 -0700530 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
531 switch (cxport) {
532 case USB_GADGET_XPORT_SMD:
533 gsmd_ctrl_disconnect(&dev->port, port_num);
534 break;
Bar Weiner0dae81b2013-02-14 13:53:54 +0200535 case USB_GADGET_XPORT_QTI:
536 gqti_ctrl_disconnect(&dev->port);
537 break;
Jack Pham427f6922011-11-23 19:42:00 -0800538 case USB_GADGET_XPORT_HSIC:
539 ghsic_ctrl_disconnect(&dev->port, port_num);
540 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530541 case USB_GADGET_XPORT_HSUART:
542 ghsuart_ctrl_disconnect(&dev->port, port_num);
543 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700544 case USB_GADGET_XPORT_NONE:
545 break;
546 default:
547 pr_err("%s: Un-supported transport: %s\n", __func__,
548 xport_to_str(cxport));
549 return -ENODEV;
550 }
551
552 port_num = rmnet_ports[dev->port_num].data_xport_num;
553 switch (dxport) {
554 case USB_GADGET_XPORT_BAM:
Ofir Cohena1c2a872011-12-14 10:26:34 +0200555 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200556 case USB_GADGET_XPORT_BAM2BAM_IPA:
Ofir Cohen77848d62012-12-05 13:16:10 +0200557 gbam_disconnect(&dev->port, port_num, dxport);
Hemant Kumar1b820d52011-11-03 15:08:28 -0700558 break;
Jack Pham427f6922011-11-23 19:42:00 -0800559 case USB_GADGET_XPORT_HSIC:
560 ghsic_data_disconnect(&dev->port, port_num);
561 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +0530562 case USB_GADGET_XPORT_HSUART:
563 ghsuart_data_disconnect(&dev->port, port_num);
564 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700565 case USB_GADGET_XPORT_NONE:
566 break;
567 default:
568 pr_err("%s: Un-supported transport: %s\n", __func__,
569 xport_to_str(dxport));
570 return -ENODEV;
571 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572
573 return 0;
574}
575
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
577{
578 struct f_rmnet *dev = func_to_rmnet(f);
579
580 pr_debug("%s: portno:%d\n", __func__, dev->port_num);
581
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +0530582 if (gadget_is_superspeed(c->cdev->gadget))
583 usb_free_descriptors(f->ss_descriptors);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700584 if (gadget_is_dualspeed(c->cdev->gadget))
585 usb_free_descriptors(f->hs_descriptors);
Arve Hjønnevåg6ef3aa42013-04-18 18:58:11 -0700586 usb_free_descriptors(f->fs_descriptors);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587
588 frmnet_free_req(dev->notify, dev->notify_req);
589
Manu Gautamdd4222b2011-09-09 15:06:05 +0530590 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591}
592
Ido Shayevitz909ed382013-02-15 02:25:35 +0200593static void frmnet_purge_responses(struct f_rmnet *dev)
594{
595 unsigned long flags;
596 struct rmnet_ctrl_pkt *cpkt;
597
598 pr_debug("%s: port#%d\n", __func__, dev->port_num);
599
600 spin_lock_irqsave(&dev->lock, flags);
601 while (!list_empty(&dev->cpkt_resp_q)) {
602 cpkt = list_first_entry(&dev->cpkt_resp_q,
603 struct rmnet_ctrl_pkt, list);
604
605 list_del(&cpkt->list);
606 rmnet_free_ctrl_pkt(cpkt);
607 }
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530608 dev->notify_count = 0;
Ido Shayevitz909ed382013-02-15 02:25:35 +0200609 spin_unlock_irqrestore(&dev->lock, flags);
610}
611
Amit Blaye5bb35e2012-05-08 20:38:20 +0300612static void frmnet_suspend(struct usb_function *f)
613{
614 struct f_rmnet *dev = func_to_rmnet(f);
615 unsigned port_num;
616 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
617
618 pr_debug("%s: data xport: %s dev: %p portno: %d\n",
619 __func__, xport_to_str(dxport),
620 dev, dev->port_num);
621
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530622 usb_ep_fifo_flush(dev->notify);
Ido Shayevitz909ed382013-02-15 02:25:35 +0200623 frmnet_purge_responses(dev);
624
Amit Blaye5bb35e2012-05-08 20:38:20 +0300625 port_num = rmnet_ports[dev->port_num].data_xport_num;
626 switch (dxport) {
627 case USB_GADGET_XPORT_BAM:
628 break;
629 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200630 case USB_GADGET_XPORT_BAM2BAM_IPA:
Amit Blaye5bb35e2012-05-08 20:38:20 +0300631 gbam_suspend(&dev->port, port_num, dxport);
632 break;
633 case USB_GADGET_XPORT_HSIC:
634 break;
Mayank Rana9dc199a2012-08-28 10:22:07 +0530635 case USB_GADGET_XPORT_HSUART:
636 break;
Amit Blaye5bb35e2012-05-08 20:38:20 +0300637 case USB_GADGET_XPORT_NONE:
638 break;
639 default:
640 pr_err("%s: Un-supported transport: %s\n", __func__,
641 xport_to_str(dxport));
642 }
643}
644
645static void frmnet_resume(struct usb_function *f)
646{
647 struct f_rmnet *dev = func_to_rmnet(f);
648 unsigned port_num;
649 enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
650
651 pr_debug("%s: data xport: %s dev: %p portno: %d\n",
652 __func__, xport_to_str(dxport),
653 dev, dev->port_num);
654
655 port_num = rmnet_ports[dev->port_num].data_xport_num;
656 switch (dxport) {
657 case USB_GADGET_XPORT_BAM:
658 break;
659 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +0200660 case USB_GADGET_XPORT_BAM2BAM_IPA:
Amit Blaye5bb35e2012-05-08 20:38:20 +0300661 gbam_resume(&dev->port, port_num, dxport);
662 break;
663 case USB_GADGET_XPORT_HSIC:
664 break;
Mayank Rana9dc199a2012-08-28 10:22:07 +0530665 case USB_GADGET_XPORT_HSUART:
666 break;
Amit Blaye5bb35e2012-05-08 20:38:20 +0300667 case USB_GADGET_XPORT_NONE:
668 break;
669 default:
670 pr_err("%s: Un-supported transport: %s\n", __func__,
671 xport_to_str(dxport));
672 }
673}
674
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700675static void frmnet_disable(struct usb_function *f)
676{
677 struct f_rmnet *dev = func_to_rmnet(f);
678
679 pr_debug("%s: port#%d\n", __func__, dev->port_num);
680
681 usb_ep_disable(dev->notify);
Anna Perel97b8c222012-01-18 10:08:14 +0200682 dev->notify->driver_data = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700683
684 atomic_set(&dev->online, 0);
685
Ido Shayevitz909ed382013-02-15 02:25:35 +0200686 frmnet_purge_responses(dev);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700687
Manu Gautam2b0234a2011-09-07 16:47:52 +0530688 gport_rmnet_disconnect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689}
690
691static int
692frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
693{
694 struct f_rmnet *dev = func_to_rmnet(f);
695 struct usb_composite_dev *cdev = dev->cdev;
696 int ret;
Amit Blay2d4fb632012-05-29 18:05:38 +0300697 struct list_head *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700698
699 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
700
701 if (dev->notify->driver_data) {
702 pr_debug("%s: reset port:%d\n", __func__, dev->port_num);
703 usb_ep_disable(dev->notify);
704 }
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200705
706 ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
707 if (ret) {
708 dev->notify->desc = NULL;
709 ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
710 dev->notify->name, ret);
711 return ret;
712 }
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300713 ret = usb_ep_enable(dev->notify);
Tatyana Brokhman31ac3522011-06-28 15:33:50 +0200714
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715 if (ret) {
716 pr_err("%s: usb ep#%s enable failed, err#%d\n",
717 __func__, dev->notify->name, ret);
718 return ret;
719 }
720 dev->notify->driver_data = dev;
721
Chiranjeevi Velempati502b1c82012-05-16 14:49:46 +0530722 if (!dev->port.in->desc || !dev->port.out->desc) {
Bar Weiner0fc137a2012-03-28 16:58:09 +0200723 if (config_ep_by_speed(cdev->gadget, f, dev->port.in) ||
724 config_ep_by_speed(cdev->gadget, f, dev->port.out)) {
725 dev->port.in->desc = NULL;
726 dev->port.out->desc = NULL;
727 return -EINVAL;
728 }
729 ret = gport_rmnet_connect(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700730 }
731
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732 atomic_set(&dev->online, 1);
733
Amit Blay2d4fb632012-05-29 18:05:38 +0300734 /* In case notifications were aborted, but there are pending control
735 packets in the response queue, re-add the notifications */
736 list_for_each(cpkt, &dev->cpkt_resp_q)
737 frmnet_ctrl_response_available(dev);
738
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700739 return ret;
740}
741
742static void frmnet_ctrl_response_available(struct f_rmnet *dev)
743{
744 struct usb_request *req = dev->notify_req;
745 struct usb_cdc_notification *event;
746 unsigned long flags;
747 int ret;
Anna Perelf3af59d2012-08-12 15:28:30 +0300748 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700749
750 pr_debug("%s:dev:%p portno#%d\n", __func__, dev, dev->port_num);
751
752 spin_lock_irqsave(&dev->lock, flags);
753 if (!atomic_read(&dev->online) || !req || !req->buf) {
754 spin_unlock_irqrestore(&dev->lock, flags);
755 return;
756 }
757
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530758 if (++dev->notify_count != 1) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759 spin_unlock_irqrestore(&dev->lock, flags);
760 return;
761 }
762
763 event = req->buf;
764 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
765 | USB_RECIP_INTERFACE;
766 event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
767 event->wValue = cpu_to_le16(0);
768 event->wIndex = cpu_to_le16(dev->ifc_id);
769 event->wLength = cpu_to_le16(0);
770 spin_unlock_irqrestore(&dev->lock, flags);
771
772 ret = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
773 if (ret) {
Anna Perelf3af59d2012-08-12 15:28:30 +0300774 spin_lock_irqsave(&dev->lock, flags);
Ido Shayevitz909ed382013-02-15 02:25:35 +0200775 if (!list_empty(&dev->cpkt_resp_q)) {
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530776 if (dev->notify_count > 0)
777 dev->notify_count--;
778 else {
779 pr_debug("%s: Invalid notify_count=%lu to decrement\n",
780 __func__, dev->notify_count);
781 spin_unlock_irqrestore(&dev->lock, flags);
782 return;
783 }
Ido Shayevitz909ed382013-02-15 02:25:35 +0200784 cpkt = list_first_entry(&dev->cpkt_resp_q,
Anna Perelf3af59d2012-08-12 15:28:30 +0300785 struct rmnet_ctrl_pkt, list);
Anna Perelf3af59d2012-08-12 15:28:30 +0300786 list_del(&cpkt->list);
787 rmnet_free_ctrl_pkt(cpkt);
788 }
789 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700790 pr_debug("ep enqueue error %d\n", ret);
791 }
792}
793
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700794static void frmnet_connect(struct grmnet *gr)
795{
796 struct f_rmnet *dev;
797
798 if (!gr) {
799 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
800 return;
801 }
802
803 dev = port_to_rmnet(gr);
804
805 atomic_set(&dev->ctrl_online, 1);
806}
807
808static void frmnet_disconnect(struct grmnet *gr)
809{
810 struct f_rmnet *dev;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700811 struct usb_cdc_notification *event;
812 int status;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700813
814 if (!gr) {
815 pr_err("%s: Invalid grmnet:%p\n", __func__, gr);
816 return;
817 }
818
819 dev = port_to_rmnet(gr);
820
821 atomic_set(&dev->ctrl_online, 0);
822
823 if (!atomic_read(&dev->online)) {
824 pr_debug("%s: nothing to do\n", __func__);
825 return;
826 }
827
828 usb_ep_fifo_flush(dev->notify);
829
830 event = dev->notify_req->buf;
831 event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
832 | USB_RECIP_INTERFACE;
833 event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
834 event->wValue = cpu_to_le16(0);
835 event->wIndex = cpu_to_le16(dev->ifc_id);
836 event->wLength = cpu_to_le16(0);
837
Vamsi Krishna188078d2011-10-26 15:09:55 -0700838 status = usb_ep_queue(dev->notify, dev->notify_req, GFP_ATOMIC);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700839 if (status < 0) {
840 if (!atomic_read(&dev->online))
841 return;
842 pr_err("%s: rmnet notify ep enqueue error %d\n",
843 __func__, status);
844 }
845
Ido Shayevitz909ed382013-02-15 02:25:35 +0200846 frmnet_purge_responses(dev);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700847}
848
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700849static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700850frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851{
852 struct f_rmnet *dev;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700853 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700854 unsigned long flags;
855
Hemant Kumarf60c0252011-11-03 12:37:07 -0700856 if (!gr || !buf) {
857 pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
858 __func__, gr, buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859 return -ENODEV;
860 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700861 cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
862 if (IS_ERR(cpkt)) {
863 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
864 return -ENOMEM;
865 }
866 memcpy(cpkt->buf, buf, len);
867 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700868
869 dev = port_to_rmnet(gr);
870
871 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
872
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700873 if (!atomic_read(&dev->online) || !atomic_read(&dev->ctrl_online)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700874 rmnet_free_ctrl_pkt(cpkt);
875 return 0;
876 }
877
878 spin_lock_irqsave(&dev->lock, flags);
Chiranjeevi Velempati263e6c82011-11-11 23:07:36 +0530879 list_add_tail(&cpkt->list, &dev->cpkt_resp_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880 spin_unlock_irqrestore(&dev->lock, flags);
881
882 frmnet_ctrl_response_available(dev);
883
884 return 0;
885}
886
887static void
888frmnet_cmd_complete(struct usb_ep *ep, struct usb_request *req)
889{
890 struct f_rmnet *dev = req->context;
891 struct usb_composite_dev *cdev;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700892 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700893
894 if (!dev) {
895 pr_err("%s: rmnet dev is null\n", __func__);
896 return;
897 }
898
899 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
900
901 cdev = dev->cdev;
902
Hemant Kumar1b820d52011-11-03 15:08:28 -0700903 if (dev->port.send_encap_cmd) {
904 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
905 dev->port.send_encap_cmd(port_num, req->buf, req->actual);
906 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700907}
908
909static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
910{
911 struct f_rmnet *dev = req->context;
912 int status = req->status;
Anna Perelf3af59d2012-08-12 15:28:30 +0300913 unsigned long flags;
914 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700915
916 pr_debug("%s: dev:%p port#%d\n", __func__, dev, dev->port_num);
917
918 switch (status) {
919 case -ECONNRESET:
920 case -ESHUTDOWN:
921 /* connection gone */
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530922 spin_lock_irqsave(&dev->lock, flags);
923 dev->notify_count = 0;
924 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700925 break;
926 default:
927 pr_err("rmnet notify ep error %d\n", status);
928 /* FALLTHROUGH */
929 case 0:
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700930 if (!atomic_read(&dev->ctrl_online))
931 break;
932
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530933 spin_lock_irqsave(&dev->lock, flags);
934 if (dev->notify_count > 0) {
935 dev->notify_count--;
936 if (dev->notify_count == 0) {
937 spin_unlock_irqrestore(&dev->lock, flags);
938 break;
939 }
940 } else {
941 pr_debug("%s: Invalid notify_count=%lu to decrement\n",
942 __func__, dev->notify_count);
943 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700944 break;
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530945 }
946 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700947
948 status = usb_ep_queue(dev->notify, req, GFP_ATOMIC);
949 if (status) {
Anna Perelf3af59d2012-08-12 15:28:30 +0300950 spin_lock_irqsave(&dev->lock, flags);
Ido Shayevitz909ed382013-02-15 02:25:35 +0200951 if (!list_empty(&dev->cpkt_resp_q)) {
ChandanaKishori Chiluveru4d694c22014-01-23 15:00:19 +0530952 if (dev->notify_count > 0)
953 dev->notify_count--;
954 else {
955 pr_err("%s: Invalid notify_count=%lu to decrement\n",
956 __func__, dev->notify_count);
957 spin_unlock_irqrestore(&dev->lock,
958 flags);
959 break;
960 }
Ido Shayevitz909ed382013-02-15 02:25:35 +0200961 cpkt = list_first_entry(&dev->cpkt_resp_q,
Anna Perelf3af59d2012-08-12 15:28:30 +0300962 struct rmnet_ctrl_pkt, list);
Anna Perelf3af59d2012-08-12 15:28:30 +0300963 list_del(&cpkt->list);
964 rmnet_free_ctrl_pkt(cpkt);
965 }
966 spin_unlock_irqrestore(&dev->lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700967 pr_debug("ep enqueue error %d\n", status);
968 }
969 break;
970 }
971}
972
973static int
974frmnet_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
975{
976 struct f_rmnet *dev = func_to_rmnet(f);
977 struct usb_composite_dev *cdev = dev->cdev;
978 struct usb_request *req = cdev->req;
Hemant Kumar1b820d52011-11-03 15:08:28 -0700979 unsigned port_num;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 u16 w_index = le16_to_cpu(ctrl->wIndex);
981 u16 w_value = le16_to_cpu(ctrl->wValue);
982 u16 w_length = le16_to_cpu(ctrl->wLength);
983 int ret = -EOPNOTSUPP;
984
985 pr_debug("%s:dev:%p port#%d\n", __func__, dev, dev->port_num);
986
987 if (!atomic_read(&dev->online)) {
988 pr_debug("%s: usb cable is not connected\n", __func__);
989 return -ENOTCONN;
990 }
991
992 switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
993
994 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
995 | USB_CDC_SEND_ENCAPSULATED_COMMAND:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700996 ret = w_length;
997 req->complete = frmnet_cmd_complete;
998 req->context = dev;
999 break;
1000
1001
1002 case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
1003 | USB_CDC_GET_ENCAPSULATED_RESPONSE:
1004 if (w_value)
1005 goto invalid;
1006 else {
1007 unsigned len;
1008 struct rmnet_ctrl_pkt *cpkt;
1009
1010 spin_lock(&dev->lock);
1011 if (list_empty(&dev->cpkt_resp_q)) {
1012 pr_err("ctrl resp queue empty "
1013 " req%02x.%02x v%04x i%04x l%d\n",
1014 ctrl->bRequestType, ctrl->bRequest,
1015 w_value, w_index, w_length);
1016 spin_unlock(&dev->lock);
1017 goto invalid;
1018 }
1019
1020 cpkt = list_first_entry(&dev->cpkt_resp_q,
1021 struct rmnet_ctrl_pkt, list);
1022 list_del(&cpkt->list);
1023 spin_unlock(&dev->lock);
1024
1025 len = min_t(unsigned, w_length, cpkt->len);
1026 memcpy(req->buf, cpkt->buf, len);
1027 ret = len;
1028
1029 rmnet_free_ctrl_pkt(cpkt);
1030 }
1031 break;
1032 case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
1033 | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
Hemant Kumar1b820d52011-11-03 15:08:28 -07001034 if (dev->port.notify_modem) {
1035 port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
1036 dev->port.notify_modem(&dev->port, port_num, w_value);
1037 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001038 ret = 0;
1039
1040 break;
1041 default:
1042
1043invalid:
1044 DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
1045 ctrl->bRequestType, ctrl->bRequest,
1046 w_value, w_index, w_length);
1047 }
1048
1049 /* respond with data transfer or status phase? */
1050 if (ret >= 0) {
1051 VDBG(cdev, "rmnet req%02x.%02x v%04x i%04x l%d\n",
1052 ctrl->bRequestType, ctrl->bRequest,
1053 w_value, w_index, w_length);
1054 req->zero = (ret < w_length);
1055 req->length = ret;
1056 ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
1057 if (ret < 0)
1058 ERROR(cdev, "rmnet ep0 enqueue err %d\n", ret);
1059 }
1060
1061 return ret;
1062}
1063
1064static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
1065{
1066 struct f_rmnet *dev = func_to_rmnet(f);
1067 struct usb_ep *ep;
1068 struct usb_composite_dev *cdev = c->cdev;
1069 int ret = -ENODEV;
1070
1071 dev->ifc_id = usb_interface_id(c, f);
1072 if (dev->ifc_id < 0) {
1073 pr_err("%s: unable to allocate ifc id, err:%d",
1074 __func__, dev->ifc_id);
1075 return dev->ifc_id;
1076 }
1077 rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
1078
1079 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
1080 if (!ep) {
1081 pr_err("%s: usb epin autoconfig failed\n", __func__);
1082 return -ENODEV;
1083 }
1084 dev->port.in = ep;
1085 ep->driver_data = cdev;
1086
1087 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
1088 if (!ep) {
1089 pr_err("%s: usb epout autoconfig failed\n", __func__);
1090 ret = -ENODEV;
1091 goto ep_auto_out_fail;
1092 }
1093 dev->port.out = ep;
1094 ep->driver_data = cdev;
1095
1096 ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
1097 if (!ep) {
1098 pr_err("%s: usb epnotify autoconfig failed\n", __func__);
1099 ret = -ENODEV;
1100 goto ep_auto_notify_fail;
1101 }
1102 dev->notify = ep;
1103 ep->driver_data = cdev;
1104
1105 dev->notify_req = frmnet_alloc_req(ep,
Hemant Kumarfbf113d2011-09-16 18:24:45 -07001106 sizeof(struct usb_cdc_notification),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107 GFP_KERNEL);
1108 if (IS_ERR(dev->notify_req)) {
1109 pr_err("%s: unable to allocate memory for notify req\n",
1110 __func__);
1111 ret = -ENOMEM;
1112 goto ep_notify_alloc_fail;
1113 }
1114
1115 dev->notify_req->complete = frmnet_notify_complete;
1116 dev->notify_req->context = dev;
1117
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301118 ret = -ENOMEM;
Arve Hjønnevåg6ef3aa42013-04-18 18:58:11 -07001119 f->fs_descriptors = usb_copy_descriptors(rmnet_fs_function);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001120
Arve Hjønnevåg6ef3aa42013-04-18 18:58:11 -07001121 if (!f->fs_descriptors)
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301122 goto fail;
1123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001124 if (gadget_is_dualspeed(cdev->gadget)) {
1125 rmnet_hs_in_desc.bEndpointAddress =
1126 rmnet_fs_in_desc.bEndpointAddress;
1127 rmnet_hs_out_desc.bEndpointAddress =
1128 rmnet_fs_out_desc.bEndpointAddress;
1129 rmnet_hs_notify_desc.bEndpointAddress =
1130 rmnet_fs_notify_desc.bEndpointAddress;
1131
1132 /* copy descriptors, and track endpoint copies */
1133 f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
1134
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301135 if (!f->hs_descriptors)
1136 goto fail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001137 }
1138
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301139 if (gadget_is_superspeed(cdev->gadget)) {
1140 rmnet_ss_in_desc.bEndpointAddress =
1141 rmnet_fs_in_desc.bEndpointAddress;
1142 rmnet_ss_out_desc.bEndpointAddress =
1143 rmnet_fs_out_desc.bEndpointAddress;
1144 rmnet_ss_notify_desc.bEndpointAddress =
1145 rmnet_fs_notify_desc.bEndpointAddress;
1146
1147 /* copy descriptors, and track endpoint copies */
1148 f->ss_descriptors = usb_copy_descriptors(rmnet_ss_function);
1149
1150 if (!f->ss_descriptors)
1151 goto fail;
1152 }
1153
Mayank Ranace1aaa52013-06-12 12:29:54 +05301154 pr_debug("%s: RmNet(%d) %s Speed, IN:%s OUT:%s\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001155 __func__, dev->port_num,
1156 gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
1157 dev->port.in->name, dev->port.out->name);
1158
1159 return 0;
1160
Rajkumar Raghupathy42ec8da2011-10-21 18:58:53 +05301161fail:
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301162 if (f->ss_descriptors)
1163 usb_free_descriptors(f->ss_descriptors);
1164 if (f->hs_descriptors)
1165 usb_free_descriptors(f->hs_descriptors);
Arve Hjønnevåg6ef3aa42013-04-18 18:58:11 -07001166 if (f->fs_descriptors)
1167 usb_free_descriptors(f->fs_descriptors);
Pavankumar Kondeti6f94bc92012-08-03 09:34:32 +05301168 if (dev->notify_req)
1169 frmnet_free_req(dev->notify, dev->notify_req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001170ep_notify_alloc_fail:
1171 dev->notify->driver_data = NULL;
1172 dev->notify = NULL;
1173ep_auto_notify_fail:
1174 dev->port.out->driver_data = NULL;
1175 dev->port.out = NULL;
1176ep_auto_out_fail:
1177 dev->port.in->driver_data = NULL;
1178 dev->port.in = NULL;
1179
1180 return ret;
1181}
1182
Manu Gautam2b0234a2011-09-07 16:47:52 +05301183static int frmnet_bind_config(struct usb_configuration *c, unsigned portno)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001184{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185 int status;
1186 struct f_rmnet *dev;
1187 struct usb_function *f;
1188 unsigned long flags;
1189
1190 pr_debug("%s: usb config:%p\n", __func__, c);
1191
Manu Gautam2b0234a2011-09-07 16:47:52 +05301192 if (portno >= nr_rmnet_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001193 pr_err("%s: supporting ports#%u port_id:%u", __func__,
Manu Gautam2b0234a2011-09-07 16:47:52 +05301194 nr_rmnet_ports, portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001195 return -ENODEV;
1196 }
1197
1198 if (rmnet_string_defs[0].id == 0) {
1199 status = usb_string_id(c->cdev);
1200 if (status < 0) {
1201 pr_err("%s: failed to get string id, err:%d\n",
1202 __func__, status);
1203 return status;
1204 }
1205 rmnet_string_defs[0].id = status;
1206 }
1207
Manu Gautam2b0234a2011-09-07 16:47:52 +05301208 dev = rmnet_ports[portno].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001209
1210 spin_lock_irqsave(&dev->lock, flags);
1211 dev->cdev = c->cdev;
1212 f = &dev->port.func;
Vamsi Krishna188078d2011-10-26 15:09:55 -07001213 f->name = kasprintf(GFP_ATOMIC, "rmnet%d", portno);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001214 spin_unlock_irqrestore(&dev->lock, flags);
Vamsi Krishna188078d2011-10-26 15:09:55 -07001215 if (!f->name) {
1216 pr_err("%s: cannot allocate memory for name\n", __func__);
1217 return -ENOMEM;
1218 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001219
1220 f->strings = rmnet_strings;
1221 f->bind = frmnet_bind;
1222 f->unbind = frmnet_unbind;
1223 f->disable = frmnet_disable;
1224 f->set_alt = frmnet_set_alt;
1225 f->setup = frmnet_setup;
Amit Blaye5bb35e2012-05-08 20:38:20 +03001226 f->suspend = frmnet_suspend;
1227 f->resume = frmnet_resume;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001228 dev->port.send_cpkt_response = frmnet_send_cpkt_response;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -07001229 dev->port.disconnect = frmnet_disconnect;
1230 dev->port.connect = frmnet_connect;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001231
1232 status = usb_add_function(c, f);
1233 if (status) {
1234 pr_err("%s: usb add function failed: %d\n",
1235 __func__, status);
Manu Gautam2b0234a2011-09-07 16:47:52 +05301236 kfree(f->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001237 return status;
1238 }
1239
1240 pr_debug("%s: complete\n", __func__);
1241
1242 return status;
1243}
1244
Manu Gautame3e897c2011-09-12 17:18:46 +05301245static void frmnet_cleanup(void)
1246{
1247 int i;
1248
1249 for (i = 0; i < nr_rmnet_ports; i++)
1250 kfree(rmnet_ports[i].port);
1251
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301252 gbam_cleanup();
Manu Gautame3e897c2011-09-12 17:18:46 +05301253 nr_rmnet_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001254 no_ctrl_smd_ports = 0;
Bar Weiner0dae81b2013-02-14 13:53:54 +02001255 no_ctrl_qti_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001256 no_data_bam_ports = 0;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001257 no_data_bam2bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -08001258 no_ctrl_hsic_ports = 0;
1259 no_data_hsic_ports = 0;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301260 no_ctrl_hsuart_ports = 0;
1261 no_data_hsuart_ports = 0;
Manu Gautame3e897c2011-09-12 17:18:46 +05301262}
1263
Hemant Kumarc2b17782013-02-03 15:56:29 -08001264static int frmnet_init_port(const char *ctrl_name, const char *data_name,
1265 const char *port_name)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001266{
Hemant Kumar1b820d52011-11-03 15:08:28 -07001267 struct f_rmnet *dev;
1268 struct rmnet_ports *rmnet_port;
1269 int ret;
1270 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001271
Hemant Kumar1b820d52011-11-03 15:08:28 -07001272 if (nr_rmnet_ports >= NR_RMNET_PORTS) {
1273 pr_err("%s: Max-%d instances supported\n",
1274 __func__, NR_RMNET_PORTS);
Manu Gautame3e897c2011-09-12 17:18:46 +05301275 return -EINVAL;
1276 }
1277
Hemant Kumar1b820d52011-11-03 15:08:28 -07001278 pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
1279 __func__, nr_rmnet_ports, ctrl_name, data_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001280
Hemant Kumar1b820d52011-11-03 15:08:28 -07001281 dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
1282 if (!dev) {
1283 pr_err("%s: Unable to allocate rmnet device\n", __func__);
1284 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001285 }
1286
Hemant Kumar1b820d52011-11-03 15:08:28 -07001287 dev->port_num = nr_rmnet_ports;
1288 spin_lock_init(&dev->lock);
1289 INIT_LIST_HEAD(&dev->cpkt_resp_q);
1290
1291 rmnet_port = &rmnet_ports[nr_rmnet_ports];
1292 rmnet_port->port = dev;
1293 rmnet_port->port_num = nr_rmnet_ports;
1294 rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
1295 rmnet_port->data_xport = str_to_xport(data_name);
1296
1297 switch (rmnet_port->ctrl_xport) {
1298 case USB_GADGET_XPORT_SMD:
1299 rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
1300 no_ctrl_smd_ports++;
1301 break;
Bar Weiner0dae81b2013-02-14 13:53:54 +02001302 case USB_GADGET_XPORT_QTI:
1303 rmnet_port->ctrl_xport_num = no_ctrl_qti_ports;
1304 no_ctrl_qti_ports++;
1305 break;
Jack Pham427f6922011-11-23 19:42:00 -08001306 case USB_GADGET_XPORT_HSIC:
Hemant Kumarc2b17782013-02-03 15:56:29 -08001307 ghsic_ctrl_set_port_name(port_name, ctrl_name);
Jack Pham427f6922011-11-23 19:42:00 -08001308 rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
1309 no_ctrl_hsic_ports++;
1310 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301311 case USB_GADGET_XPORT_HSUART:
1312 rmnet_port->ctrl_xport_num = no_ctrl_hsuart_ports;
1313 no_ctrl_hsuart_ports++;
1314 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001315 case USB_GADGET_XPORT_NONE:
1316 break;
1317 default:
1318 pr_err("%s: Un-supported transport: %u\n", __func__,
1319 rmnet_port->ctrl_xport);
1320 ret = -ENODEV;
1321 goto fail_probe;
1322 }
1323
1324 switch (rmnet_port->data_xport) {
1325 case USB_GADGET_XPORT_BAM:
1326 rmnet_port->data_xport_num = no_data_bam_ports;
1327 no_data_bam_ports++;
1328 break;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001329 case USB_GADGET_XPORT_BAM2BAM:
Ofir Cohenfdecb602012-11-16 15:50:01 +02001330 case USB_GADGET_XPORT_BAM2BAM_IPA:
Ofir Cohena1c2a872011-12-14 10:26:34 +02001331 rmnet_port->data_xport_num = no_data_bam2bam_ports;
1332 no_data_bam2bam_ports++;
1333 break;
Jack Pham427f6922011-11-23 19:42:00 -08001334 case USB_GADGET_XPORT_HSIC:
Hemant Kumarc2b17782013-02-03 15:56:29 -08001335 ghsic_data_set_port_name(port_name, data_name);
Jack Pham427f6922011-11-23 19:42:00 -08001336 rmnet_port->data_xport_num = no_data_hsic_ports;
1337 no_data_hsic_ports++;
1338 break;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301339 case USB_GADGET_XPORT_HSUART:
1340 rmnet_port->data_xport_num = no_data_hsuart_ports;
1341 no_data_hsuart_ports++;
1342 break;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001343 case USB_GADGET_XPORT_NONE:
1344 break;
1345 default:
1346 pr_err("%s: Un-supported transport: %u\n", __func__,
1347 rmnet_port->data_xport);
1348 ret = -ENODEV;
1349 goto fail_probe;
1350 }
1351 nr_rmnet_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001352
1353 return 0;
1354
1355fail_probe:
Manu Gautam2b0234a2011-09-07 16:47:52 +05301356 for (i = 0; i < nr_rmnet_ports; i++)
1357 kfree(rmnet_ports[i].port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001358
Hemant Kumar1b820d52011-11-03 15:08:28 -07001359 nr_rmnet_ports = 0;
1360 no_ctrl_smd_ports = 0;
Bar Weiner0dae81b2013-02-14 13:53:54 +02001361 no_ctrl_qti_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001362 no_data_bam_ports = 0;
Jack Pham427f6922011-11-23 19:42:00 -08001363 no_ctrl_hsic_ports = 0;
1364 no_data_hsic_ports = 0;
Vijayavardhan Vennapusaeb8d2392012-04-03 18:58:49 +05301365 no_ctrl_hsuart_ports = 0;
1366 no_data_hsuart_ports = 0;
Hemant Kumar1b820d52011-11-03 15:08:28 -07001367
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001368 return ret;
1369}