blob: 3cc0419c54338bd3a3dd9c319fef7721389da06e [file] [log] [blame]
Anna Perela8c991d2012-04-09 16:44:46 +03001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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>
25#include <mach/usb_gadget_xport.h>
26#include <mach/usb_bam.h>
27
28#define BAM2BAM_DATA_N_PORTS 1
29
30static struct workqueue_struct *bam_data_wq;
31static int n_bam2bam_data_ports;
32
33#define SPS_PARAMS_SPS_MODE BIT(5)
34#define SPS_PARAMS_TBE BIT(6)
35#define MSM_VENDOR_ID BIT(16)
36
37struct data_port {
Anna Perel557bf722012-09-20 11:16:35 +030038 struct usb_composite_dev *cdev;
Anna Perela8c991d2012-04-09 16:44:46 +030039 struct usb_ep *in;
40 struct usb_ep *out;
41};
42
43struct bam_data_ch_info {
44 unsigned long flags;
45 unsigned id;
46
47 struct bam_data_port *port;
48 struct work_struct write_tobam_w;
49
50 struct usb_request *rx_req;
51 struct usb_request *tx_req;
52
Shimrit Malichi255b5342012-08-02 21:01:43 +030053 u32 src_pipe_idx;
54 u32 dst_pipe_idx;
Anna Perela8c991d2012-04-09 16:44:46 +030055 u8 connection_idx;
56};
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
Anna Perela8c991d2012-04-09 16:44:46 +030095 status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
96 if (status)
97 pr_err("error enqueuing transfer, %d\n", status);
98}
99
100static void bam_data_start_endless_tx(struct bam_data_port *port)
101{
102 struct bam_data_ch_info *d = &port->data_ch;
103 int status;
104
Amit Blay51bebe92012-12-25 18:48:10 +0200105 if (!port->port_usb)
106 return;
107
Anna Perela8c991d2012-04-09 16:44:46 +0300108 status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
109 if (status)
110 pr_err("error enqueuing transfer, %d\n", status);
111}
112
Amit Blay51bebe92012-12-25 18:48:10 +0200113static int bam_data_peer_reset_cb(void *param)
Anna Perela8c991d2012-04-09 16:44:46 +0300114{
Amit Blay51bebe92012-12-25 18:48:10 +0200115 struct bam_data_port *port = (struct bam_data_port *)param;
116 struct bam_data_ch_info *d;
117 int ret;
118 bool reenable_eps = false;
Anna Perela8c991d2012-04-09 16:44:46 +0300119
Amit Blay51bebe92012-12-25 18:48:10 +0200120 d = &port->data_ch;
Anna Perela8c991d2012-04-09 16:44:46 +0300121
Amit Blay51bebe92012-12-25 18:48:10 +0200122 pr_debug("%s: reset by peer\n", __func__);
123
124 /* Disable the relevant EPs if currently EPs are enabled */
125 if (port->port_usb && port->port_usb->in &&
126 port->port_usb->in->driver_data) {
127 usb_ep_disable(port->port_usb->out);
128 usb_ep_disable(port->port_usb->in);
129
130 port->port_usb->in->driver_data = NULL;
131 port->port_usb->out->driver_data = NULL;
132 reenable_eps = true;
Anna Perela8c991d2012-04-09 16:44:46 +0300133 }
Anna Perela8c991d2012-04-09 16:44:46 +0300134
Amit Blay51bebe92012-12-25 18:48:10 +0200135 /* Disable BAM */
136 msm_hw_bam_disable(1);
Anna Perela8c991d2012-04-09 16:44:46 +0300137
Amit Blay51bebe92012-12-25 18:48:10 +0200138 /* Reset BAM */
139 ret = usb_bam_reset();
140 if (ret) {
141 pr_err("%s: BAM reset failed %d\n", __func__, ret);
142 goto reenable_eps;
143 }
Anna Perela8c991d2012-04-09 16:44:46 +0300144
Amit Blay51bebe92012-12-25 18:48:10 +0200145 /* Enable BAM */
146 msm_hw_bam_disable(0);
147
148reenable_eps:
149 /* Re-Enable the relevant EPs, if EPs were originally enabled */
150 if (reenable_eps) {
151 ret = usb_ep_enable(port->port_usb->in);
152 if (ret) {
153 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
154 __func__, port->port_usb->in);
155 return ret;
156 }
157 port->port_usb->in->driver_data = port;
158
159 ret = usb_ep_enable(port->port_usb->out);
160 if (ret) {
161 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
162 __func__, port->port_usb->out);
163 port->port_usb->in->driver_data = 0;
164 return ret;
165 }
166 port->port_usb->out->driver_data = port;
167
168 bam_data_start_endless_rx(port);
169 bam_data_start_endless_tx(port);
170 }
171
172 /* Unregister the peer reset callback */
173 usb_bam_register_peer_reset_cb(d->connection_idx, NULL, NULL);
174
175 return 0;
Anna Perela8c991d2012-04-09 16:44:46 +0300176}
177
178static void bam2bam_data_connect_work(struct work_struct *w)
179{
180 struct bam_data_port *port = container_of(w, struct bam_data_port,
181 connect_w);
182 struct bam_data_ch_info *d = &port->data_ch;
183 u32 sps_params;
184 int ret;
185
Amit Blay51bebe92012-12-25 18:48:10 +0200186 pr_debug("%s: Connect workqueue started", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300187
188 ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
189 &d->dst_pipe_idx);
190 d->src_pipe_idx = 11;
191 d->dst_pipe_idx = 10;
192
193 if (ret) {
194 pr_err("usb_bam_connect failed: err:%d\n", ret);
195 return;
196 }
197
198 if (!port->port_usb) {
199 pr_err("port_usb is NULL");
200 return;
201 }
202
203 if (!port->port_usb->out) {
204 pr_err("port_usb->out (bulk out ep) is NULL");
205 return;
206 }
207
208 d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
209 if (!d->rx_req)
210 return;
211
212 d->rx_req->context = port;
213 d->rx_req->complete = bam_data_endless_rx_complete;
214 d->rx_req->length = 0;
215 sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
216 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
217 d->rx_req->udc_priv = sps_params;
218 d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
219 if (!d->tx_req)
220 return;
221
222 d->tx_req->context = port;
223 d->tx_req->complete = bam_data_endless_tx_complete;
224 d->tx_req->length = 0;
225 sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
226 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
227 d->tx_req->udc_priv = sps_params;
228
229 /* queue in & out requests */
230 bam_data_start_endless_rx(port);
231 bam_data_start_endless_tx(port);
232
Amit Blay51bebe92012-12-25 18:48:10 +0200233 /* Register for peer reset callback */
234 usb_bam_register_peer_reset_cb(d->connection_idx,
235 bam_data_peer_reset_cb, port);
236
237 ret = usb_bam_client_ready(true);
238 if (ret) {
239 pr_err("%s: usb_bam_client_ready failed: err:%d\n",
240 __func__, ret);
241 return;
242 }
243
244 pr_debug("%s: Connect workqueue done", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300245}
246
247static void bam2bam_data_port_free(int portno)
248{
249 kfree(bam2bam_data_ports[portno]);
250 bam2bam_data_ports[portno] = NULL;
251}
252
253static int bam2bam_data_port_alloc(int portno)
254{
255 struct bam_data_port *port = NULL;
256 struct bam_data_ch_info *d = NULL;
257
258 port = kzalloc(sizeof(struct bam_data_port), GFP_KERNEL);
259 if (!port)
260 return -ENOMEM;
261
262 port->port_num = portno;
263
264 INIT_WORK(&port->connect_w, bam2bam_data_connect_work);
Anna Perela8c991d2012-04-09 16:44:46 +0300265
266 /* data ch */
267 d = &port->data_ch;
268 d->port = port;
269 bam2bam_data_ports[portno] = port;
270
Amit Blay51bebe92012-12-25 18:48:10 +0200271 pr_debug("port:%p portno:%d\n", port, portno);
Anna Perela8c991d2012-04-09 16:44:46 +0300272
273 return 0;
274}
275
276void bam_data_disconnect(struct data_port *gr, u8 port_num)
277{
278 struct bam_data_port *port;
Anna Perela8c991d2012-04-09 16:44:46 +0300279
Amit Blay51bebe92012-12-25 18:48:10 +0200280 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300281
282 if (port_num >= n_bam2bam_data_ports) {
283 pr_err("invalid bam2bam portno#%d\n", port_num);
284 return;
285 }
286
287 if (!gr) {
288 pr_err("mbim data port is null\n");
289 return;
290 }
291
292 port = bam2bam_data_ports[port_num];
293
Amit Blay51bebe92012-12-25 18:48:10 +0200294 if (port->port_usb && port->port_usb->in &&
295 port->port_usb->in->driver_data) {
296 /* disable endpoints */
297 usb_ep_disable(port->port_usb->out);
298 usb_ep_disable(port->port_usb->in);
Anna Perela8c991d2012-04-09 16:44:46 +0300299
Amit Blay51bebe92012-12-25 18:48:10 +0200300 port->port_usb->in->driver_data = NULL;
301 port->port_usb->out->driver_data = NULL;
302
303 port->port_usb = 0;
304 }
305
306 if (usb_bam_client_ready(false))
307 pr_err("%s: usb_bam_client_ready failed\n", __func__);
Anna Perela8c991d2012-04-09 16:44:46 +0300308}
309
310int bam_data_connect(struct data_port *gr, u8 port_num,
311 u8 connection_idx)
312{
313 struct bam_data_port *port;
314 struct bam_data_ch_info *d;
315 int ret;
316
Amit Blay51bebe92012-12-25 18:48:10 +0200317 pr_debug("dev:%p port#%d\n", gr, port_num);
Anna Perela8c991d2012-04-09 16:44:46 +0300318
319 if (port_num >= n_bam2bam_data_ports) {
320 pr_err("invalid portno#%d\n", port_num);
321 return -ENODEV;
322 }
323
324 if (!gr) {
325 pr_err("mbim data port is null\n");
326 return -ENODEV;
327 }
328
329 port = bam2bam_data_ports[port_num];
330
331 d = &port->data_ch;
332
333 ret = usb_ep_enable(gr->in);
334 if (ret) {
335 pr_err("usb_ep_enable failed eptype:IN ep:%p", gr->in);
336 return ret;
337 }
338 gr->in->driver_data = port;
339
340 ret = usb_ep_enable(gr->out);
341 if (ret) {
342 pr_err("usb_ep_enable failed eptype:OUT ep:%p", gr->out);
343 gr->in->driver_data = 0;
344 return ret;
345 }
346 gr->out->driver_data = port;
347
348 port->port_usb = gr;
349
350 d->connection_idx = connection_idx;
351
352 queue_work(bam_data_wq, &port->connect_w);
353
354 return 0;
355}
356
357int bam_data_setup(unsigned int no_bam2bam_port)
358{
359 int i;
360 int ret;
361
Amit Blay51bebe92012-12-25 18:48:10 +0200362 pr_debug("requested %d BAM2BAM ports", no_bam2bam_port);
Anna Perela8c991d2012-04-09 16:44:46 +0300363
364 if (!no_bam2bam_port || no_bam2bam_port > BAM2BAM_DATA_N_PORTS) {
365 pr_err("Invalid num of ports count:%d\n", no_bam2bam_port);
366 return -EINVAL;
367 }
368
369 bam_data_wq = alloc_workqueue("k_bam_data",
370 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
371 if (!bam_data_wq) {
372 pr_err("Failed to create workqueue\n");
373 return -ENOMEM;
374 }
375
376 for (i = 0; i < no_bam2bam_port; i++) {
377 n_bam2bam_data_ports++;
378 ret = bam2bam_data_port_alloc(i);
379 if (ret) {
380 n_bam2bam_data_ports--;
381 pr_err("Failed to alloc port:%d\n", i);
382 goto free_bam_ports;
383 }
384 }
385
386 return 0;
387
388free_bam_ports:
389 for (i = 0; i < n_bam2bam_data_ports; i++)
390 bam2bam_data_port_free(i);
391 destroy_workqueue(bam_data_wq);
392
393 return ret;
394}
395
Anna Perel557bf722012-09-20 11:16:35 +0300396static int bam_data_wake_cb(void *param)
397{
398 struct bam_data_port *port = (struct bam_data_port *)param;
399 struct data_port *d_port = port->port_usb;
400
Amit Blay51bebe92012-12-25 18:48:10 +0200401 pr_debug("%s: woken up by peer\n", __func__);
Anna Perel557bf722012-09-20 11:16:35 +0300402
403 if (!d_port) {
404 pr_err("FAILED: d_port == NULL");
405 return -ENODEV;
406 }
407
408 if (!d_port->cdev) {
409 pr_err("FAILED: d_port->cdev == NULL");
410 return -ENODEV;
411 }
412
413 if (!d_port->cdev->gadget) {
414 pr_err("FAILED: d_port->cdev->gadget == NULL");
415 return -ENODEV;
416 }
417
418 return usb_gadget_wakeup(d_port->cdev->gadget);
419}
420
421void bam_data_suspend(u8 port_num)
422{
423
424 struct bam_data_port *port;
425 struct bam_data_ch_info *d;
426
427 port = bam2bam_data_ports[port_num];
428 d = &port->data_ch;
429
Amit Blay51bebe92012-12-25 18:48:10 +0200430 pr_debug("%s: suspended port %d\n", __func__, port_num);
Anna Perel557bf722012-09-20 11:16:35 +0300431 usb_bam_register_wake_cb(d->connection_idx, bam_data_wake_cb, port);
432}
433
434void bam_data_resume(u8 port_num)
435{
436
437 struct bam_data_port *port;
438 struct bam_data_ch_info *d;
439
440 port = bam2bam_data_ports[port_num];
441 d = &port->data_ch;
442
Amit Blay51bebe92012-12-25 18:48:10 +0200443 pr_debug("%s: resumed port %d\n", __func__, port_num);
Anna Perel557bf722012-09-20 11:16:35 +0300444 usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
445}
446