blob: 700d07f0b00bdf95d9cab1165298793f64161b6a [file] [log] [blame]
Amit Blayf9b352b2013-03-04 15:01:40 +02001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Anna Perela8c991d2012-04-09 16:44:46 +03002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#ifdef pr_fmt
14#undef pr_fmt
15#endif
16#define pr_fmt(fmt) "%s: " fmt, __func__
17
18#include <linux/kernel.h>
19#include <linux/interrupt.h>
20#include <linux/device.h>
21#include <linux/bitops.h>
22#include <linux/usb/gadget.h>
23
24#include <mach/bam_dmux.h>
Anna Perela8c991d2012-04-09 16:44:46 +030025#include <mach/usb_bam.h>
26
Amit Blayf9b352b2013-03-04 15:01:40 +020027#include "u_bam_data.h"
28
Anna Perela8c991d2012-04-09 16:44:46 +030029#define BAM2BAM_DATA_N_PORTS 1
30
31static struct workqueue_struct *bam_data_wq;
32static int n_bam2bam_data_ports;
33
34#define SPS_PARAMS_SPS_MODE BIT(5)
35#define SPS_PARAMS_TBE BIT(6)
36#define MSM_VENDOR_ID BIT(16)
37
Anna Perela8c991d2012-04-09 16:44:46 +030038struct bam_data_ch_info {
39 unsigned long flags;
40 unsigned id;
41
42 struct bam_data_port *port;
43 struct work_struct write_tobam_w;
44
45 struct usb_request *rx_req;
46 struct usb_request *tx_req;
47
Shimrit Malichi255b5342012-08-02 21:01:43 +030048 u32 src_pipe_idx;
49 u32 dst_pipe_idx;
Anna Perela8c991d2012-04-09 16:44:46 +030050 u8 connection_idx;
Amit Blayf9b352b2013-03-04 15:01:40 +020051
52 enum function_type func_type;
53 enum transport_type trans;
54 struct usb_bam_connect_ipa_params ipa_params;
Anna Perela8c991d2012-04-09 16:44:46 +030055};
56
57struct bam_data_port {
58 unsigned port_num;
59 struct data_port *port_usb;
60 struct bam_data_ch_info data_ch;
61
62 struct work_struct connect_w;
63 struct work_struct disconnect_w;
64};
65
66struct bam_data_port *bam2bam_data_ports[BAM2BAM_DATA_N_PORTS];
67
68/*------------data_path----------------------------*/
69
70static void bam_data_endless_rx_complete(struct usb_ep *ep,
71 struct usb_request *req)
72{
73 int status = req->status;
74
Amit Blay51bebe92012-12-25 18:48:10 +020075 pr_debug("%s: status: %d\n", __func__, status);
Anna Perela8c991d2012-04-09 16:44:46 +030076}
77
78static void bam_data_endless_tx_complete(struct usb_ep *ep,
79 struct usb_request *req)
80{
81 int status = req->status;
82
Amit Blay51bebe92012-12-25 18:48:10 +020083 pr_debug("%s: status: %d\n", __func__, status);
Anna Perela8c991d2012-04-09 16:44:46 +030084}
85
86static void bam_data_start_endless_rx(struct bam_data_port *port)
87{
88 struct bam_data_ch_info *d = &port->data_ch;
89 int status;
90
Amit Blay51bebe92012-12-25 18:48:10 +020091 if (!port->port_usb)
92 return;
93
Anna Perela8c991d2012-04-09 16:44:46 +030094 status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
95 if (status)
96 pr_err("error enqueuing transfer, %d\n", status);
97}
98
99static void bam_data_start_endless_tx(struct bam_data_port *port)
100{
101 struct bam_data_ch_info *d = &port->data_ch;
102 int status;
103
Amit Blay51bebe92012-12-25 18:48:10 +0200104 if (!port->port_usb)
105 return;
106
Anna Perela8c991d2012-04-09 16:44:46 +0300107 status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
108 if (status)
109 pr_err("error enqueuing transfer, %d\n", status);
110}
111
Amit Blay51bebe92012-12-25 18:48:10 +0200112static int bam_data_peer_reset_cb(void *param)
Anna Perela8c991d2012-04-09 16:44:46 +0300113{
Amit Blay51bebe92012-12-25 18:48:10 +0200114 struct bam_data_port *port = (struct bam_data_port *)param;
115 struct bam_data_ch_info *d;
116 int ret;
117 bool reenable_eps = false;
Anna Perela8c991d2012-04-09 16:44:46 +0300118
Amit Blay51bebe92012-12-25 18:48:10 +0200119 d = &port->data_ch;
Anna Perela8c991d2012-04-09 16:44:46 +0300120
Amit Blay51bebe92012-12-25 18:48:10 +0200121 pr_debug("%s: reset by peer\n", __func__);
122
123 /* Disable the relevant EPs if currently EPs are enabled */
124 if (port->port_usb && port->port_usb->in &&
125 port->port_usb->in->driver_data) {
126 usb_ep_disable(port->port_usb->out);
127 usb_ep_disable(port->port_usb->in);
128
129 port->port_usb->in->driver_data = NULL;
130 port->port_usb->out->driver_data = NULL;
131 reenable_eps = true;
Anna Perela8c991d2012-04-09 16:44:46 +0300132 }
Anna Perela8c991d2012-04-09 16:44:46 +0300133
Amit Blay51bebe92012-12-25 18:48:10 +0200134 /* Disable BAM */
135 msm_hw_bam_disable(1);
Anna Perela8c991d2012-04-09 16:44:46 +0300136
Amit Blay51bebe92012-12-25 18:48:10 +0200137 /* Reset BAM */
138 ret = usb_bam_reset();
139 if (ret) {
140 pr_err("%s: BAM reset failed %d\n", __func__, ret);
141 goto reenable_eps;
142 }
Anna Perela8c991d2012-04-09 16:44:46 +0300143
Amit Blay51bebe92012-12-25 18:48:10 +0200144 /* Enable BAM */
145 msm_hw_bam_disable(0);
146
147reenable_eps:
148 /* Re-Enable the relevant EPs, if EPs were originally enabled */
149 if (reenable_eps) {
150 ret = usb_ep_enable(port->port_usb->in);
151 if (ret) {
152 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
153 __func__, port->port_usb->in);
154 return ret;
155 }
156 port->port_usb->in->driver_data = port;
157
158 ret = usb_ep_enable(port->port_usb->out);
159 if (ret) {
160 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
161 __func__, port->port_usb->out);
162 port->port_usb->in->driver_data = 0;
163 return ret;
164 }
165 port->port_usb->out->driver_data = port;
166
167 bam_data_start_endless_rx(port);
168 bam_data_start_endless_tx(port);
169 }
170
171 /* Unregister the peer reset callback */
172 usb_bam_register_peer_reset_cb(d->connection_idx, NULL, NULL);
173
174 return 0;
Anna Perela8c991d2012-04-09 16:44:46 +0300175}
176
Amit Blayf9b352b2013-03-04 15:01:40 +0200177static void bam2bam_data_disconnect_work(struct work_struct *w)
178{
179 struct bam_data_port *port =
180 container_of(w, struct bam_data_port, disconnect_w);
181 struct bam_data_ch_info *d = &port->data_ch;
182 int ret;
183
184 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
185 if (d->func_type == USB_FUNC_ECM)
186 ecm_ipa_disconnect(d->ipa_params.priv);
187 ret = usb_bam_disconnect_ipa(d->connection_idx, &d->ipa_params);
188 if (ret)
189 pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
190 }
191}
192
Anna Perela8c991d2012-04-09 16:44:46 +0300193static void bam2bam_data_connect_work(struct work_struct *w)
194{
195 struct bam_data_port *port = container_of(w, struct bam_data_port,
196 connect_w);
197 struct bam_data_ch_info *d = &port->data_ch;
198 u32 sps_params;
199 int ret;
200
Amit Blay51bebe92012-12-25 18:48:10 +0200201 pr_debug("%s: Connect workqueue started", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300202
Amit Blayf9b352b2013-03-04 15:01:40 +0200203 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
204 d->ipa_params.client = IPA_CLIENT_USB_CONS;
205 d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
206 if (d->func_type == USB_FUNC_ECM) {
207 d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
208 d->ipa_params.priv = ecm_qc_get_ipa_priv();
209 }
210 ret = usb_bam_connect_ipa(&d->ipa_params);
211 if (ret) {
212 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
213 __func__, ret);
214 return;
215 }
Anna Perela8c991d2012-04-09 16:44:46 +0300216
Amit Blayf9b352b2013-03-04 15:01:40 +0200217 d->ipa_params.client = IPA_CLIENT_USB_PROD;
218 d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
219 if (d->func_type == USB_FUNC_ECM) {
220 d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
221 d->ipa_params.priv = ecm_qc_get_ipa_priv();
222 }
223 ret = usb_bam_connect_ipa(&d->ipa_params);
224 if (ret) {
225 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
226 __func__, ret);
227 return;
228 }
229 if (d->func_type == USB_FUNC_ECM) {
230 ret = ecm_ipa_connect(d->ipa_params.cons_clnt_hdl,
231 d->ipa_params.prod_clnt_hdl,
232 d->ipa_params.priv);
233 if (ret) {
234 pr_err("%s: failed to connect IPA: err:%d\n",
235 __func__, ret);
236 return;
237 }
238 }
239 } else { /* transport type is USB_GADGET_XPORT_BAM2BAM */
240 ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
241 &d->dst_pipe_idx);
242 if (ret) {
243 pr_err("usb_bam_connect failed: err:%d\n", ret);
244 return;
245 }
Anna Perela8c991d2012-04-09 16:44:46 +0300246 }
247
248 if (!port->port_usb) {
249 pr_err("port_usb is NULL");
250 return;
251 }
252
253 if (!port->port_usb->out) {
254 pr_err("port_usb->out (bulk out ep) is NULL");
255 return;
256 }
257
258 d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
259 if (!d->rx_req)
260 return;
261
262 d->rx_req->context = port;
263 d->rx_req->complete = bam_data_endless_rx_complete;
264 d->rx_req->length = 0;
265 sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
266 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
267 d->rx_req->udc_priv = sps_params;
268 d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
269 if (!d->tx_req)
270 return;
271
272 d->tx_req->context = port;
273 d->tx_req->complete = bam_data_endless_tx_complete;
274 d->tx_req->length = 0;
275 sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
276 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
277 d->tx_req->udc_priv = sps_params;
278
279 /* queue in & out requests */
280 bam_data_start_endless_rx(port);
281 bam_data_start_endless_tx(port);
282
Amit Blayf9b352b2013-03-04 15:01:40 +0200283 /* Register for peer reset callback if USB_GADGET_XPORT_BAM2BAM */
284 if (d->trans != USB_GADGET_XPORT_BAM2BAM_IPA) {
285 usb_bam_register_peer_reset_cb(d->connection_idx,
Amit Blay51bebe92012-12-25 18:48:10 +0200286 bam_data_peer_reset_cb, port);
287
Amit Blayf9b352b2013-03-04 15:01:40 +0200288 ret = usb_bam_client_ready(true);
289 if (ret) {
290 pr_err("%s: usb_bam_client_ready failed: err:%d\n",
Amit Blay51bebe92012-12-25 18:48:10 +0200291 __func__, ret);
Amit Blayf9b352b2013-03-04 15:01:40 +0200292 return;
293 }
Amit Blay51bebe92012-12-25 18:48:10 +0200294 }
295
296 pr_debug("%s: Connect workqueue done", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300297}
298
299static void bam2bam_data_port_free(int portno)
300{
301 kfree(bam2bam_data_ports[portno]);
302 bam2bam_data_ports[portno] = NULL;
303}
304
305static int bam2bam_data_port_alloc(int portno)
306{
307 struct bam_data_port *port = NULL;
308 struct bam_data_ch_info *d = NULL;
309
310 port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
311 if (!port)
312 return -ENOMEM;
313
314 port->port_num = portno;
315
316 INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
Amit Blayf9b352b2013-03-04 15:01:40 +0200317 INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
Anna Perela8c991d2012-04-09 16:44:46 +0300318
319 /* data ch */
320 d = &port->data_ch;
321 d->port = port;
322 bam2bam_data_ports[portno] = port;
323
Amit Blay51bebe92012-12-25 18:48:10 +0200324 pr_debug("port:%p portno:%d\n", port, portno);
Anna Perela8c991d2012-04-09 16:44:46 +0300325
326 return 0;
327}
328
329void bam_data_disconnect(struct data_port *gr, u8 port_num)
330{
331 struct bam_data_port *port;
Amit Blayf9b352b2013-03-04 15:01:40 +0200332 struct bam_data_ch_info *d;
Anna Perela8c991d2012-04-09 16:44:46 +0300333
Amit Blay51bebe92012-12-25 18:48:10 +0200334 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300335
336 if (port_num >= n_bam2bam_data_ports) {
337 pr_err("invalid bam2bam portno#%d\n", port_num);
338 return;
339 }
340
341 if (!gr) {
Amit Blayf9b352b2013-03-04 15:01:40 +0200342 pr_err("data port is null\n");
Anna Perela8c991d2012-04-09 16:44:46 +0300343 return;
344 }
345
346 port = bam2bam_data_ports[port_num];
347
Amit Blay51bebe92012-12-25 18:48:10 +0200348 if (port->port_usb && port->port_usb->in &&
349 port->port_usb->in->driver_data) {
350 /* disable endpoints */
351 usb_ep_disable(port->port_usb->out);
352 usb_ep_disable(port->port_usb->in);
Anna Perela8c991d2012-04-09 16:44:46 +0300353
Amit Blay51bebe92012-12-25 18:48:10 +0200354 port->port_usb->in->driver_data = NULL;
355 port->port_usb->out->driver_data = NULL;
356
357 port->port_usb = 0;
358 }
359
Amit Blayf9b352b2013-03-04 15:01:40 +0200360 d = &port->data_ch;
361 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
Lena Salman27b085c2013-03-13 08:38:25 +0200362 queue_work(bam_data_wq, &port->disconnect_w);
Amit Blayf9b352b2013-03-04 15:01:40 +0200363 else {
364 if (usb_bam_client_ready(false)) {
365 pr_err("%s: usb_bam_client_ready failed\n",
366 __func__);
367 }
368 }
Anna Perela8c991d2012-04-09 16:44:46 +0300369}
370
371int bam_data_connect(struct data_port *gr, u8 port_num,
Amit Blayf9b352b2013-03-04 15:01:40 +0200372 enum transport_type trans, u8 connection_idx, enum function_type func)
Anna Perela8c991d2012-04-09 16:44:46 +0300373{
374 struct bam_data_port *port;
375 struct bam_data_ch_info *d;
376 int ret;
377
Amit Blay51bebe92012-12-25 18:48:10 +0200378 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300379
380 if (port_num >= n_bam2bam_data_ports) {
381 pr_err("invalid portno#%d\n", port_num);
382 return -ENODEV;
383 }
384
385 if (!gr) {
Amit Blayf9b352b2013-03-04 15:01:40 +0200386 pr_err("data port is null\n");
Anna Perela8c991d2012-04-09 16:44:46 +0300387 return -ENODEV;
388 }
389
390 port = bam2bam_data_ports[port_num];
391
392 d = &port->data_ch;
393
394 ret = usb_ep_enable(gr->in);
395 if (ret) {
396 pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
397 return ret;
398 }
399 gr->in->driver_data = port;
400
401 ret = usb_ep_enable(gr->out);
402 if (ret) {
403 pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
404 gr->in->driver_data = 0;
405 return ret;
406 }
407 gr->out->driver_data = port;
408
409 port->port_usb = gr;
410
411 d->connection_idx = connection_idx;
412
Amit Blayf9b352b2013-03-04 15:01:40 +0200413 d->trans = trans;
414
415 if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
416 d->ipa_params.src_pipe = &(d->src_pipe_idx);
417 d->ipa_params.dst_pipe = &(d->dst_pipe_idx);
418 d->ipa_params.idx = connection_idx;
419 }
420
421 d->func_type = func;
422
Anna Perela8c991d2012-04-09 16:44:46 +0300423 queue_work(bam_data_wq, &port->connect_w);
424
425 return 0;
426}
427
428int bam_data_setup(unsigned int no_bam2bam_port)
429{
430 int i;
431 int ret;
432
Amit Blay51bebe92012-12-25 18:48:10 +0200433 pr_debug("requested %d BAM2BAM ports", no_bam2bam_port);
Anna Perela8c991d2012-04-09 16:44:46 +0300434
435 if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
436 pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
437 return -EINVAL;
438 }
439
440 bam_data_wq = alloc_workqueue("k_bam_data",
441 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
442 if (!bam_data_wq) {
443 pr_err("Failed to create workqueue\n");
444 return -ENOMEM;
445 }
446
447 for (i = 0; i < no_bam2bam_port; i++) {
448 n_bam2bam_data_ports++;
449 ret = bam2bam_data_port_alloc(i);
450 if (ret) {
451 n_bam2bam_data_ports--;
452 pr_err("Failed to alloc port:%d\n", i);
453 goto free_bam_ports;
454 }
455 }
456
457 return 0;
458
459free_bam_ports:
460 for (i = 0; i < n_bam2bam_data_ports; i++)
461 bam2bam_data_port_free(i);
462 destroy_workqueue(bam_data_wq);
463
464 return ret;
465}
466
Anna Perel557bf722012-09-20 11:16:35 +0300467static int bam_data_wake_cb(void *param)
468{
469 struct bam_data_port *port = (struct bam_data_port *)param;
470 struct data_port *d_port = port->port_usb;
471
Amit Blay51bebe92012-12-25 18:48:10 +0200472 pr_debug("%s: woken up by peer\n", __func__);
Anna Perel557bf722012-09-20 11:16:35 +0300473
474 if (!d_port) {
475 pr_err("FAILED: d_port == NULL");
476 return -ENODEV;
477 }
478
479 if (!d_port->cdev) {
480 pr_err("FAILED: d_port->cdev == NULL");
481 return -ENODEV;
482 }
483
484 if (!d_port->cdev->gadget) {
485 pr_err("FAILED: d_port->cdev->gadget == NULL");
486 return -ENODEV;
487 }
488
489 return usb_gadget_wakeup(d_port->cdev->gadget);
490}
491
492void bam_data_suspend(u8 port_num)
493{
494
495 struct bam_data_port *port;
496 struct bam_data_ch_info *d;
497
498 port = bam2bam_data_ports[port_num];
499 d = &port->data_ch;
500
Amit Blay51bebe92012-12-25 18:48:10 +0200501 pr_debug("%s: suspended port %d\n", __func__, port_num);
Anna Perel557bf722012-09-20 11:16:35 +0300502 usb_bam_register_wake_cb(d->connection_idx, bam_data_wake_cb, port);
503}
504
505void bam_data_resume(u8 port_num)
506{
507
508 struct bam_data_port *port;
509 struct bam_data_ch_info *d;
510
511 port = bam2bam_data_ports[port_num];
512 d = &port->data_ch;
513
Amit Blay51bebe92012-12-25 18:48:10 +0200514 pr_debug("%s: resumed port %d\n", __func__, port_num);
Anna Perel557bf722012-09-20 11:16:35 +0300515 usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
516}
517