blob: b31560569aed5a88f3335bab6a1f8fcd4d8360f3 [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;
Shimrit Malichidbf43d72013-03-16 03:32:27 +020050 u8 src_connection_idx;
51 u8 dst_connection_idx;
Amit Blayf9b352b2013-03-04 15:01:40 +020052
53 enum function_type func_type;
54 enum transport_type trans;
55 struct usb_bam_connect_ipa_params ipa_params;
Anna Perela8c991d2012-04-09 16:44:46 +030056};
57
58struct bam_data_port {
59 unsigned port_num;
60 struct data_port *port_usb;
61 struct bam_data_ch_info data_ch;
62
63 struct work_struct connect_w;
64 struct work_struct disconnect_w;
Bar Weiner84e1ba02013-07-18 09:08:03 +030065 struct work_struct suspend_w;
66 struct work_struct resume_w;
Anna Perela8c991d2012-04-09 16:44:46 +030067};
68
69struct bam_data_port *bam2bam_data_ports[BAM2BAM_DATA_N_PORTS];
70
Bar Weiner84e1ba02013-07-18 09:08:03 +030071static void bam2bam_data_suspend_work(struct work_struct *w);
72static void bam2bam_data_resume_work(struct work_struct *w);
73
Anna Perela8c991d2012-04-09 16:44:46 +030074/*------------data_path----------------------------*/
75
76static void bam_data_endless_rx_complete(struct usb_ep *ep,
77 struct usb_request *req)
78{
79 int status = req->status;
80
Amit Blay51bebe92012-12-25 18:48:10 +020081 pr_debug("%s: status: %d\n", __func__, status);
Anna Perela8c991d2012-04-09 16:44:46 +030082}
83
84static void bam_data_endless_tx_complete(struct usb_ep *ep,
85 struct usb_request *req)
86{
87 int status = req->status;
88
Amit Blay51bebe92012-12-25 18:48:10 +020089 pr_debug("%s: status: %d\n", __func__, status);
Anna Perela8c991d2012-04-09 16:44:46 +030090}
91
92static void bam_data_start_endless_rx(struct bam_data_port *port)
93{
94 struct bam_data_ch_info *d = &port->data_ch;
95 int status;
96
Amit Blay51bebe92012-12-25 18:48:10 +020097 if (!port->port_usb)
98 return;
99
Lena Salman05b544f2013-05-13 15:49:10 +0300100 pr_debug("%s: enqueue\n", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300101 status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
102 if (status)
103 pr_err("error enqueuing transfer, %d\n", status);
104}
105
106static void bam_data_start_endless_tx(struct bam_data_port *port)
107{
108 struct bam_data_ch_info *d = &port->data_ch;
109 int status;
110
Amit Blay51bebe92012-12-25 18:48:10 +0200111 if (!port->port_usb)
112 return;
113
Lena Salman05b544f2013-05-13 15:49:10 +0300114 pr_debug("%s: enqueue\n", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300115 status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
116 if (status)
117 pr_err("error enqueuing transfer, %d\n", status);
118}
119
Lena Salman05b544f2013-05-13 15:49:10 +0300120static void bam_data_stop_endless_rx(struct bam_data_port *port)
121{
122 struct bam_data_ch_info *d = &port->data_ch;
123 int status;
124
125 if (!port->port_usb)
126 return;
127
128 pr_debug("%s: dequeue\n", __func__);
129 status = usb_ep_dequeue(port->port_usb->out, d->rx_req);
130 if (status)
131 pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
132
133}
134static void bam_data_stop_endless_tx(struct bam_data_port *port)
135{
136 struct bam_data_ch_info *d = &port->data_ch;
137 int status;
138
139 if (!port->port_usb)
140 return;
141
142 pr_debug("%s: dequeue\n", __func__);
143 status = usb_ep_dequeue(port->port_usb->in, d->tx_req);
144 if (status)
145 pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
146}
147
Amit Blay51bebe92012-12-25 18:48:10 +0200148static int bam_data_peer_reset_cb(void *param)
Anna Perela8c991d2012-04-09 16:44:46 +0300149{
Amit Blay51bebe92012-12-25 18:48:10 +0200150 struct bam_data_port *port = (struct bam_data_port *)param;
151 struct bam_data_ch_info *d;
152 int ret;
Anna Perela8c991d2012-04-09 16:44:46 +0300153
Amit Blay51bebe92012-12-25 18:48:10 +0200154 d = &port->data_ch;
Anna Perela8c991d2012-04-09 16:44:46 +0300155
Amit Blay51bebe92012-12-25 18:48:10 +0200156 pr_debug("%s: reset by peer\n", __func__);
157
Amit Blay51bebe92012-12-25 18:48:10 +0200158 /* Disable BAM */
159 msm_hw_bam_disable(1);
Anna Perela8c991d2012-04-09 16:44:46 +0300160
Amit Blay51bebe92012-12-25 18:48:10 +0200161 /* Reset BAM */
Bar Weiner189bb3c2013-06-09 14:24:56 +0300162 ret = usb_bam_a2_reset(0);
Amit Blay51bebe92012-12-25 18:48:10 +0200163 if (ret) {
164 pr_err("%s: BAM reset failed %d\n", __func__, ret);
Bar Weiner189bb3c2013-06-09 14:24:56 +0300165 return ret;
Amit Blay51bebe92012-12-25 18:48:10 +0200166 }
Anna Perela8c991d2012-04-09 16:44:46 +0300167
Amit Blay51bebe92012-12-25 18:48:10 +0200168 /* Enable BAM */
169 msm_hw_bam_disable(0);
170
Amit Blay51bebe92012-12-25 18:48:10 +0200171 /* Unregister the peer reset callback */
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200172 usb_bam_register_peer_reset_cb(NULL, NULL);
Amit Blay51bebe92012-12-25 18:48:10 +0200173
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);
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200187 ret = usb_bam_disconnect_ipa(&d->ipa_params);
Amit Blayf9b352b2013-03-04 15:01:40 +0200188 if (ret)
189 pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
Lena Salman7c0e9792013-06-20 14:36:54 +0300190 if (d->func_type == USB_FUNC_MBIM)
191 teth_bridge_disconnect();
192
Amit Blayf9b352b2013-03-04 15:01:40 +0200193 }
194}
195
Anna Perela8c991d2012-04-09 16:44:46 +0300196static void bam2bam_data_connect_work(struct work_struct *w)
197{
198 struct bam_data_port *port = container_of(w, struct bam_data_port,
199 connect_w);
Lena Salmandf7e7992013-03-15 09:46:27 +0200200 struct teth_bridge_connect_params connect_params;
Anna Perela8c991d2012-04-09 16:44:46 +0300201 struct bam_data_ch_info *d = &port->data_ch;
Lena Salmandf7e7992013-03-15 09:46:27 +0200202 ipa_notify_cb usb_notify_cb;
203 void *priv;
Anna Perela8c991d2012-04-09 16:44:46 +0300204 u32 sps_params;
205 int ret;
206
Amit Blay51bebe92012-12-25 18:48:10 +0200207 pr_debug("%s: Connect workqueue started", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300208
Amit Blayf9b352b2013-03-04 15:01:40 +0200209 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
Lena Salmandf7e7992013-03-15 09:46:27 +0200210 if (d->func_type == USB_FUNC_MBIM) {
211 ret = teth_bridge_init(&usb_notify_cb, &priv);
212 if (ret) {
213 pr_err("%s:teth_bridge_init() failed\n",
214 __func__);
215 return;
216 }
217 d->ipa_params.notify = usb_notify_cb;
218 d->ipa_params.priv = priv;
219 d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
220 }
221
Lena Salmanabde35d2013-04-25 15:29:43 +0300222 d->ipa_params.client = IPA_CLIENT_USB_PROD;
223 d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
Amit Blayf9b352b2013-03-04 15:01:40 +0200224 if (d->func_type == USB_FUNC_ECM) {
Lena Salmanabde35d2013-04-25 15:29:43 +0300225 d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
Amit Blayf9b352b2013-03-04 15:01:40 +0200226 d->ipa_params.priv = ecm_qc_get_ipa_priv();
227 }
228 ret = usb_bam_connect_ipa(&d->ipa_params);
229 if (ret) {
230 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
231 __func__, ret);
232 return;
233 }
Anna Perela8c991d2012-04-09 16:44:46 +0300234
Lena Salmanabde35d2013-04-25 15:29:43 +0300235 d->ipa_params.client = IPA_CLIENT_USB_CONS;
236 d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
Amit Blayf9b352b2013-03-04 15:01:40 +0200237 if (d->func_type == USB_FUNC_ECM) {
Lena Salmanabde35d2013-04-25 15:29:43 +0300238 d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
Amit Blayf9b352b2013-03-04 15:01:40 +0200239 d->ipa_params.priv = ecm_qc_get_ipa_priv();
240 }
241 ret = usb_bam_connect_ipa(&d->ipa_params);
242 if (ret) {
243 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
244 __func__, ret);
245 return;
246 }
Lena Salmandf7e7992013-03-15 09:46:27 +0200247
248 if (d->func_type == USB_FUNC_MBIM) {
249 connect_params.ipa_usb_pipe_hdl =
250 d->ipa_params.prod_clnt_hdl;
251 connect_params.usb_ipa_pipe_hdl =
252 d->ipa_params.cons_clnt_hdl;
253 connect_params.tethering_mode =
254 TETH_TETHERING_MODE_MBIM;
255 ret = teth_bridge_connect(&connect_params);
256 if (ret) {
257 pr_err("%s:teth_bridge_connect() failed\n",
258 __func__);
259 return;
260 }
261 mbim_configure_params();
262 }
263
Amit Blayf9b352b2013-03-04 15:01:40 +0200264 if (d->func_type == USB_FUNC_ECM) {
265 ret = ecm_ipa_connect(d->ipa_params.cons_clnt_hdl,
266 d->ipa_params.prod_clnt_hdl,
267 d->ipa_params.priv);
268 if (ret) {
269 pr_err("%s: failed to connect IPA: err:%d\n",
270 __func__, ret);
271 return;
272 }
273 }
274 } else { /* transport type is USB_GADGET_XPORT_BAM2BAM */
Bar Weinerd94bcfa2013-06-25 03:37:24 +0300275 usb_bam_reset_complete();
276 ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
277 if (ret) {
278 pr_err("usb_bam_connect (src) failed: err:%d\n", ret);
279 return;
280 }
281 ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
282 if (ret) {
283 pr_err("usb_bam_connect (dst) failed: err:%d\n", ret);
284 return;
285 }
Anna Perela8c991d2012-04-09 16:44:46 +0300286 }
287
288 if (!port->port_usb) {
289 pr_err("port_usb is NULL");
290 return;
291 }
292
293 if (!port->port_usb->out) {
294 pr_err("port_usb->out (bulk out ep) is NULL");
295 return;
296 }
297
298 d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
299 if (!d->rx_req)
300 return;
301
302 d->rx_req->context = port;
303 d->rx_req->complete = bam_data_endless_rx_complete;
304 d->rx_req->length = 0;
Bar Weiner8475d632013-06-13 11:29:49 +0300305 d->rx_req->no_interrupt = 1;
Anna Perela8c991d2012-04-09 16:44:46 +0300306 sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
307 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
308 d->rx_req->udc_priv = sps_params;
309 d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
310 if (!d->tx_req)
311 return;
312
313 d->tx_req->context = port;
314 d->tx_req->complete = bam_data_endless_tx_complete;
315 d->tx_req->length = 0;
Bar Weiner8475d632013-06-13 11:29:49 +0300316 d->tx_req->no_interrupt = 1;
Anna Perela8c991d2012-04-09 16:44:46 +0300317 sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
318 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
319 d->tx_req->udc_priv = sps_params;
320
321 /* queue in & out requests */
322 bam_data_start_endless_rx(port);
323 bam_data_start_endless_tx(port);
324
Amit Blayf9b352b2013-03-04 15:01:40 +0200325 /* Register for peer reset callback if USB_GADGET_XPORT_BAM2BAM */
326 if (d->trans != USB_GADGET_XPORT_BAM2BAM_IPA) {
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200327 usb_bam_register_peer_reset_cb(bam_data_peer_reset_cb, port);
Amit Blay51bebe92012-12-25 18:48:10 +0200328
Amit Blayf9b352b2013-03-04 15:01:40 +0200329 ret = usb_bam_client_ready(true);
330 if (ret) {
331 pr_err("%s: usb_bam_client_ready failed: err:%d\n",
Amit Blay51bebe92012-12-25 18:48:10 +0200332 __func__, ret);
Amit Blayf9b352b2013-03-04 15:01:40 +0200333 return;
334 }
Amit Blay51bebe92012-12-25 18:48:10 +0200335 }
336
337 pr_debug("%s: Connect workqueue done", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300338}
339
340static void bam2bam_data_port_free(int portno)
341{
342 kfree(bam2bam_data_ports[portno]);
343 bam2bam_data_ports[portno] = NULL;
344}
345
346static int bam2bam_data_port_alloc(int portno)
347{
348 struct bam_data_port *port = NULL;
349 struct bam_data_ch_info *d = NULL;
350
351 port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
352 if (!port)
353 return -ENOMEM;
354
355 port->port_num = portno;
356
357 INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
Amit Blayf9b352b2013-03-04 15:01:40 +0200358 INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
Bar Weiner84e1ba02013-07-18 09:08:03 +0300359 INIT_WORK(&port->suspend_w, bam2bam_data_suspend_work);
360 INIT_WORK(&port->resume_w, bam2bam_data_resume_work);
Anna Perela8c991d2012-04-09 16:44:46 +0300361
362 /* data ch */
363 d = &port->data_ch;
364 d->port = port;
365 bam2bam_data_ports[portno] = port;
366
Amit Blay51bebe92012-12-25 18:48:10 +0200367 pr_debug("port:%p portno:%d\n", port, portno);
Anna Perela8c991d2012-04-09 16:44:46 +0300368
369 return 0;
370}
371
372void bam_data_disconnect(struct data_port *gr, u8 port_num)
373{
374 struct bam_data_port *port;
Amit Blayf9b352b2013-03-04 15:01:40 +0200375 struct bam_data_ch_info *d;
Anna Perela8c991d2012-04-09 16:44:46 +0300376
Amit Blay51bebe92012-12-25 18:48:10 +0200377 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300378
379 if (port_num >= n_bam2bam_data_ports) {
380 pr_err("invalid bam2bam portno#%d\n", port_num);
381 return;
382 }
383
384 if (!gr) {
Amit Blayf9b352b2013-03-04 15:01:40 +0200385 pr_err("data port is null\n");
Anna Perela8c991d2012-04-09 16:44:46 +0300386 return;
387 }
388
389 port = bam2bam_data_ports[port_num];
390
Amit Blay51bebe92012-12-25 18:48:10 +0200391 if (port->port_usb && port->port_usb->in &&
392 port->port_usb->in->driver_data) {
393 /* disable endpoints */
394 usb_ep_disable(port->port_usb->out);
395 usb_ep_disable(port->port_usb->in);
Anna Perela8c991d2012-04-09 16:44:46 +0300396
Amit Blay51bebe92012-12-25 18:48:10 +0200397 port->port_usb->in->driver_data = NULL;
398 port->port_usb->out->driver_data = NULL;
399
400 port->port_usb = 0;
401 }
402
Amit Blayf9b352b2013-03-04 15:01:40 +0200403 d = &port->data_ch;
404 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
Lena Salman27b085c2013-03-13 08:38:25 +0200405 queue_work(bam_data_wq, &port->disconnect_w);
Amit Blayf9b352b2013-03-04 15:01:40 +0200406 else {
407 if (usb_bam_client_ready(false)) {
408 pr_err("%s: usb_bam_client_ready failed\n",
409 __func__);
410 }
411 }
Anna Perela8c991d2012-04-09 16:44:46 +0300412}
413
414int bam_data_connect(struct data_port *gr, u8 port_num,
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200415 enum transport_type trans, u8 src_connection_idx,
416 u8 dst_connection_idx, enum function_type func)
Anna Perela8c991d2012-04-09 16:44:46 +0300417{
418 struct bam_data_port *port;
419 struct bam_data_ch_info *d;
420 int ret;
421
Amit Blay51bebe92012-12-25 18:48:10 +0200422 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300423
424 if (port_num >= n_bam2bam_data_ports) {
425 pr_err("invalid portno#%d\n", port_num);
426 return -ENODEV;
427 }
428
429 if (!gr) {
Amit Blayf9b352b2013-03-04 15:01:40 +0200430 pr_err("data port is null\n");
Anna Perela8c991d2012-04-09 16:44:46 +0300431 return -ENODEV;
432 }
433
434 port = bam2bam_data_ports[port_num];
435
436 d = &port->data_ch;
437
438 ret = usb_ep_enable(gr->in);
439 if (ret) {
440 pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
441 return ret;
442 }
443 gr->in->driver_data = port;
444
445 ret = usb_ep_enable(gr->out);
446 if (ret) {
447 pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
448 gr->in->driver_data = 0;
449 return ret;
450 }
451 gr->out->driver_data = port;
452
453 port->port_usb = gr;
454
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200455 d->src_connection_idx = src_connection_idx;
456 d->dst_connection_idx = dst_connection_idx;
Anna Perela8c991d2012-04-09 16:44:46 +0300457
Amit Blayf9b352b2013-03-04 15:01:40 +0200458 d->trans = trans;
Lena Salmandf7e7992013-03-15 09:46:27 +0200459 d->func_type = func;
Amit Blayf9b352b2013-03-04 15:01:40 +0200460
461 if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
462 d->ipa_params.src_pipe = &(d->src_pipe_idx);
463 d->ipa_params.dst_pipe = &(d->dst_pipe_idx);
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200464 d->ipa_params.src_idx = src_connection_idx;
465 d->ipa_params.dst_idx = dst_connection_idx;
Amit Blayf9b352b2013-03-04 15:01:40 +0200466 }
467
Anna Perela8c991d2012-04-09 16:44:46 +0300468 queue_work(bam_data_wq, &port->connect_w);
469
470 return 0;
471}
472
Bar Weiner189bb3c2013-06-09 14:24:56 +0300473int bam_data_destroy(unsigned int no_bam2bam_port)
474{
475 struct bam_data_ch_info *d;
476 struct bam_data_port *port;
477
478 port = bam2bam_data_ports[no_bam2bam_port];
479 d = &port->data_ch;
480
481 pr_debug("bam_data_destroy: Freeing ports\n");
482 bam2bam_data_port_free(no_bam2bam_port);
483 if (bam_data_wq)
484 destroy_workqueue(bam_data_wq);
485 bam_data_wq = NULL;
486
487 return 0;
488}
489
Anna Perela8c991d2012-04-09 16:44:46 +0300490int bam_data_setup(unsigned int no_bam2bam_port)
491{
492 int i;
493 int ret;
494
Amit Blay51bebe92012-12-25 18:48:10 +0200495 pr_debug("requested %d BAM2BAM ports", no_bam2bam_port);
Anna Perela8c991d2012-04-09 16:44:46 +0300496
497 if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
498 pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
499 return -EINVAL;
500 }
501
Bar Weiner189bb3c2013-06-09 14:24:56 +0300502 if (bam_data_wq) {
503 pr_debug("bam_data is already setup");
504 return 0;
505 }
506
Anna Perela8c991d2012-04-09 16:44:46 +0300507 bam_data_wq = alloc_workqueue("k_bam_data",
508 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
509 if (!bam_data_wq) {
510 pr_err("Failed to create workqueue\n");
511 return -ENOMEM;
512 }
513
514 for (i = 0; i < no_bam2bam_port; i++) {
515 n_bam2bam_data_ports++;
516 ret = bam2bam_data_port_alloc(i);
517 if (ret) {
518 n_bam2bam_data_ports--;
519 pr_err("Failed to alloc port:%d\n", i);
520 goto free_bam_ports;
521 }
522 }
523
524 return 0;
525
526free_bam_ports:
527 for (i = 0; i < n_bam2bam_data_ports; i++)
528 bam2bam_data_port_free(i);
529 destroy_workqueue(bam_data_wq);
530
531 return ret;
532}
533
Anna Perel557bf722012-09-20 11:16:35 +0300534static int bam_data_wake_cb(void *param)
535{
536 struct bam_data_port *port = (struct bam_data_port *)param;
537 struct data_port *d_port = port->port_usb;
538
Amit Blay51bebe92012-12-25 18:48:10 +0200539 pr_debug("%s: woken up by peer\n", __func__);
Anna Perel557bf722012-09-20 11:16:35 +0300540
541 if (!d_port) {
542 pr_err("FAILED: d_port == NULL");
543 return -ENODEV;
544 }
545
546 if (!d_port->cdev) {
547 pr_err("FAILED: d_port->cdev == NULL");
548 return -ENODEV;
549 }
550
551 if (!d_port->cdev->gadget) {
552 pr_err("FAILED: d_port->cdev->gadget == NULL");
553 return -ENODEV;
554 }
555
556 return usb_gadget_wakeup(d_port->cdev->gadget);
557}
558
Lena Salman05b544f2013-05-13 15:49:10 +0300559static void bam_data_start(void *param, enum usb_bam_pipe_dir dir)
560{
561 struct bam_data_port *port = param;
562
563 if (dir == USB_TO_PEER_PERIPHERAL)
564 bam_data_start_endless_rx(port);
565 else
566 bam_data_start_endless_tx(port);
567}
568
569static void bam_data_stop(void *param, enum usb_bam_pipe_dir dir)
570{
571 struct bam_data_port *port = param;
572
573 if (dir == USB_TO_PEER_PERIPHERAL)
574 bam_data_stop_endless_rx(port);
575 else
576 bam_data_stop_endless_tx(port);
577}
578
Anna Perel557bf722012-09-20 11:16:35 +0300579void bam_data_suspend(u8 port_num)
580{
Anna Perel557bf722012-09-20 11:16:35 +0300581 struct bam_data_port *port;
582 struct bam_data_ch_info *d;
583
584 port = bam2bam_data_ports[port_num];
585 d = &port->data_ch;
586
Amit Blay51bebe92012-12-25 18:48:10 +0200587 pr_debug("%s: suspended port %d\n", __func__, port_num);
Bar Weiner84e1ba02013-07-18 09:08:03 +0300588
589 queue_work(bam_data_wq, &port->suspend_w);
Anna Perel557bf722012-09-20 11:16:35 +0300590}
591
592void bam_data_resume(u8 port_num)
593{
594
595 struct bam_data_port *port;
596 struct bam_data_ch_info *d;
597
598 port = bam2bam_data_ports[port_num];
599 d = &port->data_ch;
600
Amit Blay51bebe92012-12-25 18:48:10 +0200601 pr_debug("%s: resumed port %d\n", __func__, port_num);
Bar Weiner84e1ba02013-07-18 09:08:03 +0300602
603 queue_work(bam_data_wq, &port->resume_w);
604}
605
606static void bam2bam_data_suspend_work(struct work_struct *w)
607{
608 struct bam_data_port *port =
609 container_of(w, struct bam_data_port, suspend_w);
610 struct bam_data_ch_info *d = &port->data_ch;
611
612 pr_debug("%s: suspend work started\n", __func__);
613
614 usb_bam_register_wake_cb(d->dst_connection_idx, bam_data_wake_cb, port);
615 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
616 usb_bam_register_start_stop_cbs(bam_data_start, bam_data_stop,
617 port);
618 usb_bam_suspend(&d->ipa_params);
619 }
620}
621
622static void bam2bam_data_resume_work(struct work_struct *w)
623{
624 struct bam_data_port *port =
625 container_of(w, struct bam_data_port, resume_w);
626 struct bam_data_ch_info *d = &port->data_ch;
627
628 pr_debug("%s: resume work started\n", __func__);
629
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200630 usb_bam_register_wake_cb(d->dst_connection_idx, NULL, NULL);
Lena Salman05b544f2013-05-13 15:49:10 +0300631 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
632 usb_bam_resume(&d->ipa_params);
Anna Perel557bf722012-09-20 11:16:35 +0300633}