blob: 081a09c492eae8c669361d92009c5cffce2d6084 [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;
65};
66
67struct bam_data_port *bam2bam_data_ports[BAM2BAM_DATA_N_PORTS];
68
69/*------------data_path----------------------------*/
70
71static void bam_data_endless_rx_complete(struct usb_ep *ep,
72 struct usb_request *req)
73{
74 int status = req->status;
75
Amit Blay51bebe92012-12-25 18:48:10 +020076 pr_debug("%s: status: %d\n", __func__, status);
Anna Perela8c991d2012-04-09 16:44:46 +030077}
78
79static void bam_data_endless_tx_complete(struct usb_ep *ep,
80 struct usb_request *req)
81{
82 int status = req->status;
83
Amit Blay51bebe92012-12-25 18:48:10 +020084 pr_debug("%s: status: %d\n", __func__, status);
Anna Perela8c991d2012-04-09 16:44:46 +030085}
86
87static void bam_data_start_endless_rx(struct bam_data_port *port)
88{
89 struct bam_data_ch_info *d = &port->data_ch;
90 int status;
91
Amit Blay51bebe92012-12-25 18:48:10 +020092 if (!port->port_usb)
93 return;
94
Lena Salman05b544f2013-05-13 15:49:10 +030095 pr_debug("%s: enqueue\n", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +030096 status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
97 if (status)
98 pr_err("error enqueuing transfer, %d\n", status);
99}
100
101static void bam_data_start_endless_tx(struct bam_data_port *port)
102{
103 struct bam_data_ch_info *d = &port->data_ch;
104 int status;
105
Amit Blay51bebe92012-12-25 18:48:10 +0200106 if (!port->port_usb)
107 return;
108
Lena Salman05b544f2013-05-13 15:49:10 +0300109 pr_debug("%s: enqueue\n", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300110 status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
111 if (status)
112 pr_err("error enqueuing transfer, %d\n", status);
113}
114
Lena Salman05b544f2013-05-13 15:49:10 +0300115static void bam_data_stop_endless_rx(struct bam_data_port *port)
116{
117 struct bam_data_ch_info *d = &port->data_ch;
118 int status;
119
120 if (!port->port_usb)
121 return;
122
123 pr_debug("%s: dequeue\n", __func__);
124 status = usb_ep_dequeue(port->port_usb->out, d->rx_req);
125 if (status)
126 pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
127
128}
129static void bam_data_stop_endless_tx(struct bam_data_port *port)
130{
131 struct bam_data_ch_info *d = &port->data_ch;
132 int status;
133
134 if (!port->port_usb)
135 return;
136
137 pr_debug("%s: dequeue\n", __func__);
138 status = usb_ep_dequeue(port->port_usb->in, d->tx_req);
139 if (status)
140 pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
141}
142
Amit Blay51bebe92012-12-25 18:48:10 +0200143static int bam_data_peer_reset_cb(void *param)
Anna Perela8c991d2012-04-09 16:44:46 +0300144{
Amit Blay51bebe92012-12-25 18:48:10 +0200145 struct bam_data_port *port = (struct bam_data_port *)param;
146 struct bam_data_ch_info *d;
147 int ret;
148 bool reenable_eps = false;
Anna Perela8c991d2012-04-09 16:44:46 +0300149
Amit Blay51bebe92012-12-25 18:48:10 +0200150 d = &port->data_ch;
Anna Perela8c991d2012-04-09 16:44:46 +0300151
Amit Blay51bebe92012-12-25 18:48:10 +0200152 pr_debug("%s: reset by peer\n", __func__);
153
154 /* Disable the relevant EPs if currently EPs are enabled */
155 if (port->port_usb && port->port_usb->in &&
156 port->port_usb->in->driver_data) {
157 usb_ep_disable(port->port_usb->out);
158 usb_ep_disable(port->port_usb->in);
159
160 port->port_usb->in->driver_data = NULL;
161 port->port_usb->out->driver_data = NULL;
162 reenable_eps = true;
Anna Perela8c991d2012-04-09 16:44:46 +0300163 }
Anna Perela8c991d2012-04-09 16:44:46 +0300164
Amit Blay51bebe92012-12-25 18:48:10 +0200165 /* Disable BAM */
166 msm_hw_bam_disable(1);
Anna Perela8c991d2012-04-09 16:44:46 +0300167
Amit Blay51bebe92012-12-25 18:48:10 +0200168 /* Reset BAM */
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200169 ret = usb_bam_a2_reset();
Amit Blay51bebe92012-12-25 18:48:10 +0200170 if (ret) {
171 pr_err("%s: BAM reset failed %d\n", __func__, ret);
172 goto reenable_eps;
173 }
Anna Perela8c991d2012-04-09 16:44:46 +0300174
Amit Blay51bebe92012-12-25 18:48:10 +0200175 /* Enable BAM */
176 msm_hw_bam_disable(0);
177
178reenable_eps:
179 /* Re-Enable the relevant EPs, if EPs were originally enabled */
180 if (reenable_eps) {
181 ret = usb_ep_enable(port->port_usb->in);
182 if (ret) {
183 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
184 __func__, port->port_usb->in);
185 return ret;
186 }
187 port->port_usb->in->driver_data = port;
188
189 ret = usb_ep_enable(port->port_usb->out);
190 if (ret) {
191 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
192 __func__, port->port_usb->out);
193 port->port_usb->in->driver_data = 0;
194 return ret;
195 }
196 port->port_usb->out->driver_data = port;
197
198 bam_data_start_endless_rx(port);
199 bam_data_start_endless_tx(port);
200 }
201
202 /* Unregister the peer reset callback */
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200203 usb_bam_register_peer_reset_cb(NULL, NULL);
Amit Blay51bebe92012-12-25 18:48:10 +0200204
205 return 0;
Anna Perela8c991d2012-04-09 16:44:46 +0300206}
207
Amit Blayf9b352b2013-03-04 15:01:40 +0200208static void bam2bam_data_disconnect_work(struct work_struct *w)
209{
210 struct bam_data_port *port =
211 container_of(w, struct bam_data_port, disconnect_w);
212 struct bam_data_ch_info *d = &port->data_ch;
213 int ret;
214
215 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
Lena Salmandf7e7992013-03-15 09:46:27 +0200216 if (d->func_type == USB_FUNC_MBIM)
217 teth_bridge_disconnect();
Amit Blayf9b352b2013-03-04 15:01:40 +0200218 if (d->func_type == USB_FUNC_ECM)
219 ecm_ipa_disconnect(d->ipa_params.priv);
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200220 ret = usb_bam_disconnect_ipa(&d->ipa_params);
Amit Blayf9b352b2013-03-04 15:01:40 +0200221 if (ret)
222 pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
223 }
224}
225
Anna Perela8c991d2012-04-09 16:44:46 +0300226static void bam2bam_data_connect_work(struct work_struct *w)
227{
228 struct bam_data_port *port = container_of(w, struct bam_data_port,
229 connect_w);
Lena Salmandf7e7992013-03-15 09:46:27 +0200230 struct teth_bridge_connect_params connect_params;
Anna Perela8c991d2012-04-09 16:44:46 +0300231 struct bam_data_ch_info *d = &port->data_ch;
Lena Salmandf7e7992013-03-15 09:46:27 +0200232 ipa_notify_cb usb_notify_cb;
233 void *priv;
Anna Perela8c991d2012-04-09 16:44:46 +0300234 u32 sps_params;
235 int ret;
236
Amit Blay51bebe92012-12-25 18:48:10 +0200237 pr_debug("%s: Connect workqueue started", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300238
Amit Blayf9b352b2013-03-04 15:01:40 +0200239 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
Lena Salmandf7e7992013-03-15 09:46:27 +0200240 if (d->func_type == USB_FUNC_MBIM) {
241 ret = teth_bridge_init(&usb_notify_cb, &priv);
242 if (ret) {
243 pr_err("%s:teth_bridge_init() failed\n",
244 __func__);
245 return;
246 }
247 d->ipa_params.notify = usb_notify_cb;
248 d->ipa_params.priv = priv;
249 d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
250 }
251
Lena Salmanabde35d2013-04-25 15:29:43 +0300252 d->ipa_params.client = IPA_CLIENT_USB_PROD;
253 d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
Amit Blayf9b352b2013-03-04 15:01:40 +0200254 if (d->func_type == USB_FUNC_ECM) {
Lena Salmanabde35d2013-04-25 15:29:43 +0300255 d->ipa_params.notify = ecm_qc_get_ipa_rx_cb();
Amit Blayf9b352b2013-03-04 15:01:40 +0200256 d->ipa_params.priv = ecm_qc_get_ipa_priv();
257 }
258 ret = usb_bam_connect_ipa(&d->ipa_params);
259 if (ret) {
260 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
261 __func__, ret);
262 return;
263 }
Anna Perela8c991d2012-04-09 16:44:46 +0300264
Lena Salmanabde35d2013-04-25 15:29:43 +0300265 d->ipa_params.client = IPA_CLIENT_USB_CONS;
266 d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
Amit Blayf9b352b2013-03-04 15:01:40 +0200267 if (d->func_type == USB_FUNC_ECM) {
Lena Salmanabde35d2013-04-25 15:29:43 +0300268 d->ipa_params.notify = ecm_qc_get_ipa_tx_cb();
Amit Blayf9b352b2013-03-04 15:01:40 +0200269 d->ipa_params.priv = ecm_qc_get_ipa_priv();
270 }
271 ret = usb_bam_connect_ipa(&d->ipa_params);
272 if (ret) {
273 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
274 __func__, ret);
275 return;
276 }
Lena Salmandf7e7992013-03-15 09:46:27 +0200277
278 if (d->func_type == USB_FUNC_MBIM) {
279 connect_params.ipa_usb_pipe_hdl =
280 d->ipa_params.prod_clnt_hdl;
281 connect_params.usb_ipa_pipe_hdl =
282 d->ipa_params.cons_clnt_hdl;
283 connect_params.tethering_mode =
284 TETH_TETHERING_MODE_MBIM;
285 ret = teth_bridge_connect(&connect_params);
286 if (ret) {
287 pr_err("%s:teth_bridge_connect() failed\n",
288 __func__);
289 return;
290 }
291 mbim_configure_params();
292 }
293
Amit Blayf9b352b2013-03-04 15:01:40 +0200294 if (d->func_type == USB_FUNC_ECM) {
295 ret = ecm_ipa_connect(d->ipa_params.cons_clnt_hdl,
296 d->ipa_params.prod_clnt_hdl,
297 d->ipa_params.priv);
298 if (ret) {
299 pr_err("%s: failed to connect IPA: err:%d\n",
300 __func__, ret);
301 return;
302 }
303 }
304 } else { /* transport type is USB_GADGET_XPORT_BAM2BAM */
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200305 ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
306 if (ret) {
307 pr_err("usb_bam_connect (src) failed: err:%d\n", ret);
308 return;
Anna Perela8c991d2012-04-09 16:44:46 +0300309 }
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200310 ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
311 if (ret) {
312 pr_err("usb_bam_connect (dst) failed: err:%d\n", ret);
313 return;
314 }
315}
Anna Perela8c991d2012-04-09 16:44:46 +0300316
317 if (!port->port_usb) {
318 pr_err("port_usb is NULL");
319 return;
320 }
321
322 if (!port->port_usb->out) {
323 pr_err("port_usb->out (bulk out ep) is NULL");
324 return;
325 }
326
327 d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
328 if (!d->rx_req)
329 return;
330
331 d->rx_req->context = port;
332 d->rx_req->complete = bam_data_endless_rx_complete;
333 d->rx_req->length = 0;
334 sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
335 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
336 d->rx_req->udc_priv = sps_params;
337 d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
338 if (!d->tx_req)
339 return;
340
341 d->tx_req->context = port;
342 d->tx_req->complete = bam_data_endless_tx_complete;
343 d->tx_req->length = 0;
344 sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
345 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
346 d->tx_req->udc_priv = sps_params;
347
348 /* queue in & out requests */
349 bam_data_start_endless_rx(port);
350 bam_data_start_endless_tx(port);
351
Amit Blayf9b352b2013-03-04 15:01:40 +0200352 /* Register for peer reset callback if USB_GADGET_XPORT_BAM2BAM */
353 if (d->trans != USB_GADGET_XPORT_BAM2BAM_IPA) {
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200354 usb_bam_register_peer_reset_cb(bam_data_peer_reset_cb, port);
Amit Blay51bebe92012-12-25 18:48:10 +0200355
Amit Blayf9b352b2013-03-04 15:01:40 +0200356 ret = usb_bam_client_ready(true);
357 if (ret) {
358 pr_err("%s: usb_bam_client_ready failed: err:%d\n",
Amit Blay51bebe92012-12-25 18:48:10 +0200359 __func__, ret);
Amit Blayf9b352b2013-03-04 15:01:40 +0200360 return;
361 }
Amit Blay51bebe92012-12-25 18:48:10 +0200362 }
363
364 pr_debug("%s: Connect workqueue done", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300365}
366
367static void bam2bam_data_port_free(int portno)
368{
369 kfree(bam2bam_data_ports[portno]);
370 bam2bam_data_ports[portno] = NULL;
371}
372
373static int bam2bam_data_port_alloc(int portno)
374{
375 struct bam_data_port *port = NULL;
376 struct bam_data_ch_info *d = NULL;
377
378 port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
379 if (!port)
380 return -ENOMEM;
381
382 port->port_num = portno;
383
384 INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
Amit Blayf9b352b2013-03-04 15:01:40 +0200385 INIT_WORK(&port->disconnect_w, bam2bam_data_disconnect_work);
Anna Perela8c991d2012-04-09 16:44:46 +0300386
387 /* data ch */
388 d = &port->data_ch;
389 d->port = port;
390 bam2bam_data_ports[portno] = port;
391
Amit Blay51bebe92012-12-25 18:48:10 +0200392 pr_debug("port:%p portno:%d\n", port, portno);
Anna Perela8c991d2012-04-09 16:44:46 +0300393
394 return 0;
395}
396
397void bam_data_disconnect(struct data_port *gr, u8 port_num)
398{
399 struct bam_data_port *port;
Amit Blayf9b352b2013-03-04 15:01:40 +0200400 struct bam_data_ch_info *d;
Anna Perela8c991d2012-04-09 16:44:46 +0300401
Amit Blay51bebe92012-12-25 18:48:10 +0200402 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300403
404 if (port_num >= n_bam2bam_data_ports) {
405 pr_err("invalid bam2bam portno#%d\n", port_num);
406 return;
407 }
408
409 if (!gr) {
Amit Blayf9b352b2013-03-04 15:01:40 +0200410 pr_err("data port is null\n");
Anna Perela8c991d2012-04-09 16:44:46 +0300411 return;
412 }
413
414 port = bam2bam_data_ports[port_num];
415
Amit Blay51bebe92012-12-25 18:48:10 +0200416 if (port->port_usb && port->port_usb->in &&
417 port->port_usb->in->driver_data) {
418 /* disable endpoints */
419 usb_ep_disable(port->port_usb->out);
420 usb_ep_disable(port->port_usb->in);
Anna Perela8c991d2012-04-09 16:44:46 +0300421
Amit Blay51bebe92012-12-25 18:48:10 +0200422 port->port_usb->in->driver_data = NULL;
423 port->port_usb->out->driver_data = NULL;
424
425 port->port_usb = 0;
426 }
427
Amit Blayf9b352b2013-03-04 15:01:40 +0200428 d = &port->data_ch;
429 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
Lena Salman27b085c2013-03-13 08:38:25 +0200430 queue_work(bam_data_wq, &port->disconnect_w);
Amit Blayf9b352b2013-03-04 15:01:40 +0200431 else {
432 if (usb_bam_client_ready(false)) {
433 pr_err("%s: usb_bam_client_ready failed\n",
434 __func__);
435 }
436 }
Anna Perela8c991d2012-04-09 16:44:46 +0300437}
438
439int bam_data_connect(struct data_port *gr, u8 port_num,
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200440 enum transport_type trans, u8 src_connection_idx,
441 u8 dst_connection_idx, enum function_type func)
Anna Perela8c991d2012-04-09 16:44:46 +0300442{
443 struct bam_data_port *port;
444 struct bam_data_ch_info *d;
445 int ret;
446
Amit Blay51bebe92012-12-25 18:48:10 +0200447 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300448
449 if (port_num >= n_bam2bam_data_ports) {
450 pr_err("invalid portno#%d\n", port_num);
451 return -ENODEV;
452 }
453
454 if (!gr) {
Amit Blayf9b352b2013-03-04 15:01:40 +0200455 pr_err("data port is null\n");
Anna Perela8c991d2012-04-09 16:44:46 +0300456 return -ENODEV;
457 }
458
459 port = bam2bam_data_ports[port_num];
460
461 d = &port->data_ch;
462
463 ret = usb_ep_enable(gr->in);
464 if (ret) {
465 pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
466 return ret;
467 }
468 gr->in->driver_data = port;
469
470 ret = usb_ep_enable(gr->out);
471 if (ret) {
472 pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
473 gr->in->driver_data = 0;
474 return ret;
475 }
476 gr->out->driver_data = port;
477
478 port->port_usb = gr;
479
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200480 d->src_connection_idx = src_connection_idx;
481 d->dst_connection_idx = dst_connection_idx;
Anna Perela8c991d2012-04-09 16:44:46 +0300482
Amit Blayf9b352b2013-03-04 15:01:40 +0200483 d->trans = trans;
Lena Salmandf7e7992013-03-15 09:46:27 +0200484 d->func_type = func;
Amit Blayf9b352b2013-03-04 15:01:40 +0200485
486 if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
487 d->ipa_params.src_pipe = &(d->src_pipe_idx);
488 d->ipa_params.dst_pipe = &(d->dst_pipe_idx);
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200489 d->ipa_params.src_idx = src_connection_idx;
490 d->ipa_params.dst_idx = dst_connection_idx;
Amit Blayf9b352b2013-03-04 15:01:40 +0200491 }
492
Anna Perela8c991d2012-04-09 16:44:46 +0300493 queue_work(bam_data_wq, &port->connect_w);
494
495 return 0;
496}
497
498int bam_data_setup(unsigned int no_bam2bam_port)
499{
500 int i;
501 int ret;
502
Amit Blay51bebe92012-12-25 18:48:10 +0200503 pr_debug("requested %d BAM2BAM ports", no_bam2bam_port);
Anna Perela8c991d2012-04-09 16:44:46 +0300504
505 if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
506 pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
507 return -EINVAL;
508 }
509
510 bam_data_wq = alloc_workqueue("k_bam_data",
511 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
512 if (!bam_data_wq) {
513 pr_err("Failed to create workqueue\n");
514 return -ENOMEM;
515 }
516
517 for (i = 0; i < no_bam2bam_port; i++) {
518 n_bam2bam_data_ports++;
519 ret = bam2bam_data_port_alloc(i);
520 if (ret) {
521 n_bam2bam_data_ports--;
522 pr_err("Failed to alloc port:%d\n", i);
523 goto free_bam_ports;
524 }
525 }
526
527 return 0;
528
529free_bam_ports:
530 for (i = 0; i < n_bam2bam_data_ports; i++)
531 bam2bam_data_port_free(i);
532 destroy_workqueue(bam_data_wq);
533
534 return ret;
535}
536
Anna Perel557bf722012-09-20 11:16:35 +0300537static int bam_data_wake_cb(void *param)
538{
539 struct bam_data_port *port = (struct bam_data_port *)param;
540 struct data_port *d_port = port->port_usb;
541
Amit Blay51bebe92012-12-25 18:48:10 +0200542 pr_debug("%s: woken up by peer\n", __func__);
Anna Perel557bf722012-09-20 11:16:35 +0300543
544 if (!d_port) {
545 pr_err("FAILED: d_port == NULL");
546 return -ENODEV;
547 }
548
549 if (!d_port->cdev) {
550 pr_err("FAILED: d_port->cdev == NULL");
551 return -ENODEV;
552 }
553
554 if (!d_port->cdev->gadget) {
555 pr_err("FAILED: d_port->cdev->gadget == NULL");
556 return -ENODEV;
557 }
558
559 return usb_gadget_wakeup(d_port->cdev->gadget);
560}
561
Lena Salman05b544f2013-05-13 15:49:10 +0300562static void bam_data_start(void *param, enum usb_bam_pipe_dir dir)
563{
564 struct bam_data_port *port = param;
565
566 if (dir == USB_TO_PEER_PERIPHERAL)
567 bam_data_start_endless_rx(port);
568 else
569 bam_data_start_endless_tx(port);
570}
571
572static void bam_data_stop(void *param, enum usb_bam_pipe_dir dir)
573{
574 struct bam_data_port *port = param;
575
576 if (dir == USB_TO_PEER_PERIPHERAL)
577 bam_data_stop_endless_rx(port);
578 else
579 bam_data_stop_endless_tx(port);
580}
581
Anna Perel557bf722012-09-20 11:16:35 +0300582void bam_data_suspend(u8 port_num)
583{
Anna Perel557bf722012-09-20 11:16:35 +0300584 struct bam_data_port *port;
585 struct bam_data_ch_info *d;
586
587 port = bam2bam_data_ports[port_num];
588 d = &port->data_ch;
589
Amit Blay51bebe92012-12-25 18:48:10 +0200590 pr_debug("%s: suspended port %d\n", __func__, port_num);
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200591 usb_bam_register_wake_cb(d->dst_connection_idx, bam_data_wake_cb, port);
Lena Salman05b544f2013-05-13 15:49:10 +0300592 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
593 usb_bam_register_start_stop_cbs(bam_data_start, bam_data_stop,
594 port);
595 usb_bam_suspend(&d->ipa_params);
596 }
Anna Perel557bf722012-09-20 11:16:35 +0300597}
598
599void bam_data_resume(u8 port_num)
600{
601
602 struct bam_data_port *port;
603 struct bam_data_ch_info *d;
604
605 port = bam2bam_data_ports[port_num];
606 d = &port->data_ch;
607
Amit Blay51bebe92012-12-25 18:48:10 +0200608 pr_debug("%s: resumed port %d\n", __func__, port_num);
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200609 usb_bam_register_wake_cb(d->dst_connection_idx, NULL, NULL);
Lena Salman05b544f2013-05-13 15:49:10 +0300610 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
611 usb_bam_resume(&d->ipa_params);
Anna Perel557bf722012-09-20 11:16:35 +0300612}