blob: b12d74aa795ba5f695f27b5f653d0529c87ece21 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, 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#include <linux/kernel.h>
14#include <linux/interrupt.h>
15#include <linux/device.h>
16#include <linux/delay.h>
17#include <linux/slab.h>
18#include <linux/termios.h>
19#include <mach/msm_smd.h>
20#include <linux/netdevice.h>
21#include <mach/bam_dmux.h>
22#include <linux/debugfs.h>
23#include <linux/bitops.h>
24#include <linux/termios.h>
25
Ofir Cohena1c2a872011-12-14 10:26:34 +020026#include <mach/usb_gadget_xport.h>
27#include <mach/usb_bam.h>
28
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include "u_rmnet.h"
30
31#define BAM_N_PORTS 1
Ofir Cohena1c2a872011-12-14 10:26:34 +020032#define BAM2BAM_N_PORTS 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
34static struct workqueue_struct *gbam_wq;
35static int n_bam_ports;
Ofir Cohena1c2a872011-12-14 10:26:34 +020036static int n_bam2bam_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037static unsigned bam_ch_ids[] = { 8 };
38
Jack Phameffd4ae2011-08-03 16:49:36 -070039static const char *bam_ch_names[] = { "bam_dmux_ch_8" };
40
Vamsi Krishna84579552011-11-09 15:33:22 -080041#define BAM_PENDING_LIMIT 220
Vamsi Krishna8f24f252011-11-02 11:46:08 -070042#define BAM_MUX_TX_PKT_DROP_THRESHOLD 1000
Vamsi Krishna84579552011-11-09 15:33:22 -080043#define BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD 500
44#define BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD 300
Vamsi Krishna8f24f252011-11-02 11:46:08 -070045#define BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
47#define BAM_MUX_HDR 8
48
Vamsi Krishna8f24f252011-11-02 11:46:08 -070049#define BAM_MUX_RX_Q_SIZE 16
50#define BAM_MUX_TX_Q_SIZE 200
51#define BAM_MUX_RX_REQ_SIZE (2048 - BAM_MUX_HDR)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052
Vamsi Krishna8f24f252011-11-02 11:46:08 -070053unsigned int bam_mux_tx_pkt_drop_thld = BAM_MUX_TX_PKT_DROP_THRESHOLD;
54module_param(bam_mux_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055
Vamsi Krishna8f24f252011-11-02 11:46:08 -070056unsigned int bam_mux_rx_fctrl_en_thld = BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD;
57module_param(bam_mux_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058
Vamsi Krishna8f24f252011-11-02 11:46:08 -070059unsigned int bam_mux_rx_fctrl_support = BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT;
60module_param(bam_mux_rx_fctrl_support, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061
Vamsi Krishna8f24f252011-11-02 11:46:08 -070062unsigned int bam_mux_rx_fctrl_dis_thld = BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD;
63module_param(bam_mux_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064
Vamsi Krishna8f24f252011-11-02 11:46:08 -070065unsigned int bam_mux_tx_q_size = BAM_MUX_TX_Q_SIZE;
66module_param(bam_mux_tx_q_size, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067
Vamsi Krishna8f24f252011-11-02 11:46:08 -070068unsigned int bam_mux_rx_q_size = BAM_MUX_RX_Q_SIZE;
69module_param(bam_mux_rx_q_size, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070
Vamsi Krishna8f24f252011-11-02 11:46:08 -070071unsigned int bam_mux_rx_req_size = BAM_MUX_RX_REQ_SIZE;
72module_param(bam_mux_rx_req_size, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073
Jack Phameffd4ae2011-08-03 16:49:36 -070074#define BAM_CH_OPENED BIT(0)
75#define BAM_CH_READY BIT(1)
Ofir Cohena1c2a872011-12-14 10:26:34 +020076#define SPS_PARAMS_PIPE_ID_MASK (0x1F)
77#define SPS_PARAMS_SPS_MODE BIT(5)
78#define SPS_PARAMS_TBE BIT(6)
79#define MSM_VENDOR_ID BIT(16)
80
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081struct bam_ch_info {
Jack Phameffd4ae2011-08-03 16:49:36 -070082 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083 unsigned id;
84
85 struct list_head tx_idle;
86 struct sk_buff_head tx_skb_q;
87
88 struct list_head rx_idle;
89 struct sk_buff_head rx_skb_q;
90
91 struct gbam_port *port;
92 struct work_struct write_tobam_w;
93
Ofir Cohena1c2a872011-12-14 10:26:34 +020094 struct usb_request *rx_req;
95 struct usb_request *tx_req;
96
97 u8 src_pipe_idx;
98 u8 dst_pipe_idx;
99 u8 connection_idx;
100
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101 /* stats */
102 unsigned int pending_with_bam;
103 unsigned int tohost_drp_cnt;
104 unsigned int tomodem_drp_cnt;
105 unsigned int tx_len;
106 unsigned int rx_len;
107 unsigned long to_modem;
108 unsigned long to_host;
109};
110
111struct gbam_port {
112 unsigned port_num;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530113 spinlock_t port_lock_ul;
114 spinlock_t port_lock_dl;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115
116 struct grmnet *port_usb;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200117 struct grmnet *gr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118
119 struct bam_ch_info data_ch;
120
121 struct work_struct connect_w;
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800122 struct work_struct disconnect_w;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123};
124
125static struct bam_portmaster {
126 struct gbam_port *port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700127 struct platform_driver pdrv;
128} bam_ports[BAM_N_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129
Ofir Cohena1c2a872011-12-14 10:26:34 +0200130struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131static void gbam_start_rx(struct gbam_port *port);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200132static void gbam_start_endless_rx(struct gbam_port *port);
133static void gbam_start_endless_tx(struct gbam_port *port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134
135/*---------------misc functions---------------- */
136static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
137{
138 struct usb_request *req;
139
140 while (!list_empty(head)) {
141 req = list_entry(head->next, struct usb_request, list);
142 list_del(&req->list);
143 usb_ep_free_request(ep, req);
144 }
145}
146
147static int gbam_alloc_requests(struct usb_ep *ep, struct list_head *head,
148 int num,
149 void (*cb)(struct usb_ep *ep, struct usb_request *),
150 gfp_t flags)
151{
152 int i;
153 struct usb_request *req;
154
155 pr_debug("%s: ep:%p head:%p num:%d cb:%p", __func__,
156 ep, head, num, cb);
157
158 for (i = 0; i < num; i++) {
159 req = usb_ep_alloc_request(ep, flags);
160 if (!req) {
161 pr_debug("%s: req allocated:%d\n", __func__, i);
162 return list_empty(head) ? -ENOMEM : 0;
163 }
164 req->complete = cb;
165 list_add(&req->list, head);
166 }
167
168 return 0;
169}
170/*--------------------------------------------- */
171
172/*------------data_path----------------------------*/
173static void gbam_write_data_tohost(struct gbam_port *port)
174{
175 unsigned long flags;
176 struct bam_ch_info *d = &port->data_ch;
177 struct sk_buff *skb;
178 int ret;
179 struct usb_request *req;
180 struct usb_ep *ep;
181
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530182 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530184 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 return;
186 }
187
188 ep = port->port_usb->in;
189
190 while (!list_empty(&d->tx_idle)) {
191 skb = __skb_dequeue(&d->tx_skb_q);
192 if (!skb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530193 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 return;
195 }
196 req = list_first_entry(&d->tx_idle,
197 struct usb_request,
198 list);
199 req->context = skb;
200 req->buf = skb->data;
201 req->length = skb->len;
202
203 list_del(&req->list);
204
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530205 spin_unlock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 ret = usb_ep_queue(ep, req, GFP_ATOMIC);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530207 spin_lock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 if (ret) {
209 pr_err("%s: usb epIn failed\n", __func__);
210 list_add(&req->list, &d->tx_idle);
211 dev_kfree_skb_any(skb);
212 break;
213 }
214 d->to_host++;
215 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530216 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217}
218
219void gbam_data_recv_cb(void *p, struct sk_buff *skb)
220{
221 struct gbam_port *port = p;
222 struct bam_ch_info *d = &port->data_ch;
223 unsigned long flags;
224
225 if (!skb)
226 return;
227
228 pr_debug("%s: p:%p#%d d:%p skb_len:%d\n", __func__,
229 port, port->port_num, d, skb->len);
230
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530231 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530233 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234 dev_kfree_skb_any(skb);
235 return;
236 }
237
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700238 if (d->tx_skb_q.qlen > bam_mux_tx_pkt_drop_thld) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239 d->tohost_drp_cnt++;
240 if (printk_ratelimit())
241 pr_err("%s: tx pkt dropped: tx_drop_cnt:%u\n",
242 __func__, d->tohost_drp_cnt);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530243 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244 dev_kfree_skb_any(skb);
245 return;
246 }
247
248 __skb_queue_tail(&d->tx_skb_q, skb);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530249 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250
251 gbam_write_data_tohost(port);
252}
253
254void gbam_data_write_done(void *p, struct sk_buff *skb)
255{
256 struct gbam_port *port = p;
257 struct bam_ch_info *d = &port->data_ch;
258 unsigned long flags;
259
260 if (!skb)
261 return;
262
263 dev_kfree_skb_any(skb);
264
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530265 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266
267 d->pending_with_bam--;
268
269 pr_debug("%s: port:%p d:%p tom:%lu pbam:%u, pno:%d\n", __func__,
270 port, d, d->to_modem,
271 d->pending_with_bam, port->port_num);
272
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530273 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274
Vamsi Krishna84579552011-11-09 15:33:22 -0800275 queue_work(gbam_wq, &d->write_tobam_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276}
277
278static void gbam_data_write_tobam(struct work_struct *w)
279{
280 struct gbam_port *port;
281 struct bam_ch_info *d;
282 struct sk_buff *skb;
283 unsigned long flags;
284 int ret;
Vamsi Krishna84579552011-11-09 15:33:22 -0800285 int qlen;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286
287 d = container_of(w, struct bam_ch_info, write_tobam_w);
288 port = d->port;
289
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530290 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530292 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 return;
294 }
295
Vamsi Krishna84579552011-11-09 15:33:22 -0800296 while (d->pending_with_bam < BAM_PENDING_LIMIT) {
Vamsi Krishna2327c79152011-11-08 16:12:42 -0800297 skb = __skb_dequeue(&d->rx_skb_q);
Vamsi Krishna625c28e2011-12-16 22:34:49 -0800298 if (!skb)
299 break;
300
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 d->pending_with_bam++;
302 d->to_modem++;
303
304 pr_debug("%s: port:%p d:%p tom:%lu pbam:%u pno:%d\n", __func__,
305 port, d, d->to_modem, d->pending_with_bam,
306 port->port_num);
307
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530308 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309 ret = msm_bam_dmux_write(d->id, skb);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530310 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 if (ret) {
312 pr_debug("%s: write error:%d\n", __func__, ret);
313 d->pending_with_bam--;
314 d->to_modem--;
315 d->tomodem_drp_cnt++;
316 dev_kfree_skb_any(skb);
317 break;
318 }
319 }
Vamsi Krishna84579552011-11-09 15:33:22 -0800320
321 qlen = d->rx_skb_q.qlen;
322
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530323 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Vamsi Krishna84579552011-11-09 15:33:22 -0800324
325 if (qlen < BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD)
326 gbam_start_rx(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327}
328/*-------------------------------------------------------------*/
329
330static void gbam_epin_complete(struct usb_ep *ep, struct usb_request *req)
331{
332 struct gbam_port *port = ep->driver_data;
333 struct bam_ch_info *d;
334 struct sk_buff *skb = req->context;
335 int status = req->status;
336
337 switch (status) {
338 case 0:
339 /* successful completion */
340 case -ECONNRESET:
341 case -ESHUTDOWN:
342 /* connection gone */
343 break;
344 default:
345 pr_err("%s: data tx ep error %d\n",
346 __func__, status);
347 break;
348 }
349
350 dev_kfree_skb_any(skb);
351
352 if (!port)
353 return;
354
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530355 spin_lock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356 d = &port->data_ch;
357 list_add_tail(&req->list, &d->tx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530358 spin_unlock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
360 gbam_write_data_tohost(port);
361}
362
363static void
364gbam_epout_complete(struct usb_ep *ep, struct usb_request *req)
365{
366 struct gbam_port *port = ep->driver_data;
367 struct bam_ch_info *d = &port->data_ch;
368 struct sk_buff *skb = req->context;
369 int status = req->status;
370 int queue = 0;
371
372 switch (status) {
373 case 0:
374 skb_put(skb, req->actual);
375 queue = 1;
376 break;
377 case -ECONNRESET:
378 case -ESHUTDOWN:
379 /* cable disconnection */
380 dev_kfree_skb_any(skb);
381 req->buf = 0;
382 usb_ep_free_request(ep, req);
383 return;
384 default:
385 if (printk_ratelimit())
386 pr_err("%s: %s response error %d, %d/%d\n",
387 __func__, ep->name, status,
388 req->actual, req->length);
389 dev_kfree_skb_any(skb);
390 break;
391 }
392
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530393 spin_lock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 if (queue) {
395 __skb_queue_tail(&d->rx_skb_q, skb);
396 queue_work(gbam_wq, &d->write_tobam_w);
397 }
398
399 /* TODO: Handle flow control gracefully by having
400 * having call back mechanism from bam driver
401 */
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700402 if (bam_mux_rx_fctrl_support &&
Vamsi Krishna84579552011-11-09 15:33:22 -0800403 d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404
405 list_add_tail(&req->list, &d->rx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530406 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 return;
408 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530409 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700411 skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412 if (!skb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530413 spin_lock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414 list_add_tail(&req->list, &d->rx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530415 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 return;
417 }
418 skb_reserve(skb, BAM_MUX_HDR);
419
420 req->buf = skb->data;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700421 req->length = bam_mux_rx_req_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422 req->context = skb;
423
424 status = usb_ep_queue(ep, req, GFP_ATOMIC);
425 if (status) {
426 dev_kfree_skb_any(skb);
427
428 if (printk_ratelimit())
429 pr_err("%s: data rx enqueue err %d\n",
430 __func__, status);
431
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530432 spin_lock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433 list_add_tail(&req->list, &d->rx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530434 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 }
436}
437
Ofir Cohena1c2a872011-12-14 10:26:34 +0200438static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
439{
440 int status = req->status;
441
442 pr_debug("%s status: %d\n", __func__, status);
443}
444
445static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
446{
447 int status = req->status;
448
449 pr_debug("%s status: %d\n", __func__, status);
450}
451
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452static void gbam_start_rx(struct gbam_port *port)
453{
454 struct usb_request *req;
455 struct bam_ch_info *d;
456 struct usb_ep *ep;
457 unsigned long flags;
458 int ret;
459 struct sk_buff *skb;
460
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530461 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530463 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 return;
465 }
466
467 d = &port->data_ch;
468 ep = port->port_usb->out;
469
470 while (port->port_usb && !list_empty(&d->rx_idle)) {
Vamsi Krishna84579552011-11-09 15:33:22 -0800471
472 if (bam_mux_rx_fctrl_support &&
473 d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld)
474 break;
475
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476 req = list_first_entry(&d->rx_idle, struct usb_request, list);
477
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700478 skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700479 if (!skb)
480 break;
481 skb_reserve(skb, BAM_MUX_HDR);
482
483 list_del(&req->list);
484 req->buf = skb->data;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700485 req->length = bam_mux_rx_req_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 req->context = skb;
487
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530488 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489 ret = usb_ep_queue(ep, req, GFP_ATOMIC);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530490 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491 if (ret) {
492 dev_kfree_skb_any(skb);
493
494 if (printk_ratelimit())
495 pr_err("%s: rx queue failed\n", __func__);
496
497 if (port->port_usb)
498 list_add(&req->list, &d->rx_idle);
499 else
500 usb_ep_free_request(ep, req);
501 break;
502 }
503 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530504 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700505}
506
Ofir Cohena1c2a872011-12-14 10:26:34 +0200507static void gbam_start_endless_rx(struct gbam_port *port)
508{
509 struct bam_ch_info *d = &port->data_ch;
510 int status;
511
512 status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
513 if (status)
514 pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
515}
516
517static void gbam_start_endless_tx(struct gbam_port *port)
518{
519 struct bam_ch_info *d = &port->data_ch;
520 int status;
521
522 status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
523 if (status)
524 pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
525}
526
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527static void gbam_start_io(struct gbam_port *port)
528{
529 unsigned long flags;
530 struct usb_ep *ep;
531 int ret;
532 struct bam_ch_info *d;
533
534 pr_debug("%s: port:%p\n", __func__, port);
535
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530536 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530538 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 return;
540 }
541
542 d = &port->data_ch;
543 ep = port->port_usb->out;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700544 ret = gbam_alloc_requests(ep, &d->rx_idle, bam_mux_rx_q_size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545 gbam_epout_complete, GFP_ATOMIC);
546 if (ret) {
547 pr_err("%s: rx req allocation failed\n", __func__);
548 return;
549 }
550
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530551 spin_unlock_irqrestore(&port->port_lock_ul, flags);
552 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553 ep = port->port_usb->in;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700554 ret = gbam_alloc_requests(ep, &d->tx_idle, bam_mux_tx_q_size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 gbam_epin_complete, GFP_ATOMIC);
556 if (ret) {
557 pr_err("%s: tx req allocation failed\n", __func__);
558 gbam_free_requests(ep, &d->rx_idle);
559 return;
560 }
561
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530562 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563
564 /* queue out requests */
565 gbam_start_rx(port);
566}
567
Jeff Hugo1c4531c2011-08-02 14:55:37 -0600568static void gbam_notify(void *p, int event, unsigned long data)
569{
570 switch (event) {
571 case BAM_DMUX_RECEIVE:
572 gbam_data_recv_cb(p, (struct sk_buff *)(data));
573 break;
574 case BAM_DMUX_WRITE_DONE:
575 gbam_data_write_done(p, (struct sk_buff *)(data));
576 break;
577 }
578}
579
Ofir Cohena1c2a872011-12-14 10:26:34 +0200580static void gbam_free_buffers(struct gbam_port *port)
581{
582 struct sk_buff *skb;
583 unsigned long flags;
584 struct bam_ch_info *d;
585
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530586 spin_lock_irqsave(&port->port_lock_ul, flags);
587 spin_lock_irqsave(&port->port_lock_dl, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200588
589 if (!port || !port->port_usb)
590 goto free_buf_out;
591
592 d = &port->data_ch;
593
594 gbam_free_requests(port->port_usb->in, &d->tx_idle);
595 gbam_free_requests(port->port_usb->out, &d->rx_idle);
596
597 while ((skb = __skb_dequeue(&d->tx_skb_q)))
598 dev_kfree_skb_any(skb);
599
600 while ((skb = __skb_dequeue(&d->rx_skb_q)))
601 dev_kfree_skb_any(skb);
602
603free_buf_out:
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530604 spin_unlock_irqrestore(&port->port_lock_dl, flags);
605 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200606}
607
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800608static void gbam_disconnect_work(struct work_struct *w)
609{
610 struct gbam_port *port =
611 container_of(w, struct gbam_port, disconnect_w);
612 struct bam_ch_info *d = &port->data_ch;
613
614 if (!test_bit(BAM_CH_OPENED, &d->flags))
615 return;
616
617 msm_bam_dmux_close(d->id);
618 clear_bit(BAM_CH_OPENED, &d->flags);
619}
620
Ofir Cohena1c2a872011-12-14 10:26:34 +0200621static void gbam2bam_disconnect_work(struct work_struct *w)
622{
623 struct gbam_port *port =
624 container_of(w, struct gbam_port, disconnect_w);
625 unsigned long flags;
626
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530627 spin_lock_irqsave(&port->port_lock_ul, flags);
628 spin_lock_irqsave(&port->port_lock_dl, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200629 port->port_usb = 0;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530630 spin_unlock_irqrestore(&port->port_lock_dl, flags);
631 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200632
633 /* disable endpoints */
634 usb_ep_disable(port->gr->out);
635 usb_ep_disable(port->gr->in);
636
637}
638
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700639static void gbam_connect_work(struct work_struct *w)
640{
641 struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
642 struct bam_ch_info *d = &port->data_ch;
643 int ret;
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800644 unsigned long flags;
645
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530646 spin_lock_irqsave(&port->port_lock_ul, flags);
647 spin_lock_irqsave(&port->port_lock_dl, flags);
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800648 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530649 spin_unlock_irqrestore(&port->port_lock_dl, flags);
650 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800651 return;
652 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530653 spin_unlock_irqrestore(&port->port_lock_dl, flags);
654 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655
Jack Phameffd4ae2011-08-03 16:49:36 -0700656 if (!test_bit(BAM_CH_READY, &d->flags))
657 return;
658
Jeff Hugo1c4531c2011-08-02 14:55:37 -0600659 ret = msm_bam_dmux_open(d->id, port, gbam_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660 if (ret) {
661 pr_err("%s: unable open bam ch:%d err:%d\n",
662 __func__, d->id, ret);
663 return;
664 }
Jack Phameffd4ae2011-08-03 16:49:36 -0700665 set_bit(BAM_CH_OPENED, &d->flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700666
667 gbam_start_io(port);
668
669 pr_debug("%s: done\n", __func__);
670}
671
Ofir Cohena1c2a872011-12-14 10:26:34 +0200672static void gbam2bam_connect_work(struct work_struct *w)
Jack Phameffd4ae2011-08-03 16:49:36 -0700673{
Ofir Cohena1c2a872011-12-14 10:26:34 +0200674 struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
675 struct bam_ch_info *d = &port->data_ch;
676 u32 sps_params;
677 int ret;
Ofir Cohen4da266f2012-01-03 10:19:29 +0200678 unsigned long flags;
679
680 ret = usb_ep_enable(port->gr->in, port->gr->in_desc);
681 if (ret) {
682 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
683 __func__, port->gr->in);
684 return;
685 }
686 port->gr->in->driver_data = port;
687
688 ret = usb_ep_enable(port->gr->out, port->gr->out_desc);
689 if (ret) {
690 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
691 __func__, port->gr->out);
692 port->gr->in->driver_data = 0;
693 return;
694 }
695 port->gr->out->driver_data = port;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530696 spin_lock_irqsave(&port->port_lock_ul, flags);
697 spin_lock_irqsave(&port->port_lock_dl, flags);
Ofir Cohen4da266f2012-01-03 10:19:29 +0200698 port->port_usb = port->gr;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530699 spin_unlock_irqrestore(&port->port_lock_dl, flags);
700 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -0700701
Ofir Cohena1c2a872011-12-14 10:26:34 +0200702 ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
703 &d->dst_pipe_idx);
704 if (ret) {
705 pr_err("%s: usb_bam_connect failed: err:%d\n",
706 __func__, ret);
707 return;
708 }
Jack Phameffd4ae2011-08-03 16:49:36 -0700709
Ofir Cohena1c2a872011-12-14 10:26:34 +0200710 d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
711 if (!d->rx_req)
712 return;
Jack Phameffd4ae2011-08-03 16:49:36 -0700713
Ofir Cohena1c2a872011-12-14 10:26:34 +0200714 d->rx_req->context = port;
715 d->rx_req->complete = gbam_endless_rx_complete;
716 d->rx_req->length = 0;
717 sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
718 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
719 d->rx_req->udc_priv = sps_params;
720 d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
721 if (!d->tx_req)
722 return;
Jack Phameffd4ae2011-08-03 16:49:36 -0700723
Ofir Cohena1c2a872011-12-14 10:26:34 +0200724 d->tx_req->context = port;
725 d->tx_req->complete = gbam_endless_tx_complete;
726 d->tx_req->length = 0;
727 sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
728 MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
729 d->tx_req->udc_priv = sps_params;
Jack Phameffd4ae2011-08-03 16:49:36 -0700730
Ofir Cohena1c2a872011-12-14 10:26:34 +0200731 /* queue in & out requests */
732 gbam_start_endless_rx(port);
733 gbam_start_endless_tx(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700734
Ofir Cohena1c2a872011-12-14 10:26:34 +0200735 pr_debug("%s: done\n", __func__);
Jack Phameffd4ae2011-08-03 16:49:36 -0700736}
737
738/* BAM data channel ready, allow attempt to open */
739static int gbam_data_ch_probe(struct platform_device *pdev)
740{
741 struct gbam_port *port;
742 struct bam_ch_info *d;
743 int i;
744 unsigned long flags;
745
746 pr_debug("%s: name:%s\n", __func__, pdev->name);
747
748 for (i = 0; i < n_bam_ports; i++) {
749 port = bam_ports[i].port;
750 d = &port->data_ch;
751
752 if (!strncmp(bam_ch_names[i], pdev->name,
753 BAM_DMUX_CH_NAME_MAX_LEN)) {
754 set_bit(BAM_CH_READY, &d->flags);
755
756 /* if usb is online, try opening bam_ch */
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530757 spin_lock_irqsave(&port->port_lock_ul, flags);
758 spin_lock_irqsave(&port->port_lock_dl, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -0700759 if (port->port_usb)
760 queue_work(gbam_wq, &port->connect_w);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530761 spin_unlock_irqrestore(&port->port_lock_dl, flags);
762 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -0700763
764 break;
765 }
766 }
767
768 return 0;
769}
770
771/* BAM data channel went inactive, so close it */
772static int gbam_data_ch_remove(struct platform_device *pdev)
773{
774 struct gbam_port *port;
775 struct bam_ch_info *d;
776 struct usb_ep *ep_in = NULL;
777 struct usb_ep *ep_out = NULL;
778 unsigned long flags;
779 int i;
780
781 pr_debug("%s: name:%s\n", __func__, pdev->name);
782
783 for (i = 0; i < n_bam_ports; i++) {
784 if (!strncmp(bam_ch_names[i], pdev->name,
785 BAM_DMUX_CH_NAME_MAX_LEN)) {
786 port = bam_ports[i].port;
787 d = &port->data_ch;
788
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530789 spin_lock_irqsave(&port->port_lock_ul, flags);
790 spin_lock_irqsave(&port->port_lock_dl, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -0700791 if (port->port_usb) {
792 ep_in = port->port_usb->in;
793 ep_out = port->port_usb->out;
794 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530795 spin_unlock_irqrestore(&port->port_lock_dl, flags);
796 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -0700797
798 if (ep_in)
799 usb_ep_fifo_flush(ep_in);
800 if (ep_out)
801 usb_ep_fifo_flush(ep_out);
802
803 gbam_free_buffers(port);
804
805 msm_bam_dmux_close(d->id);
806
807 clear_bit(BAM_CH_READY, &d->flags);
808 clear_bit(BAM_CH_OPENED, &d->flags);
809 }
810 }
811
812 return 0;
813}
814
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700815static void gbam_port_free(int portno)
816{
817 struct gbam_port *port = bam_ports[portno].port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700818 struct platform_driver *pdrv = &bam_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819
Jack Phameffd4ae2011-08-03 16:49:36 -0700820 if (port) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700821 kfree(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700822 platform_driver_unregister(pdrv);
823 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700824}
825
Ofir Cohena1c2a872011-12-14 10:26:34 +0200826static void gbam2bam_port_free(int portno)
827{
828 struct gbam_port *port = bam2bam_ports[portno];
829
830 kfree(port);
831}
832
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700833static int gbam_port_alloc(int portno)
834{
835 struct gbam_port *port;
836 struct bam_ch_info *d;
Jack Phameffd4ae2011-08-03 16:49:36 -0700837 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838
839 port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
840 if (!port)
841 return -ENOMEM;
842
843 port->port_num = portno;
844
845 /* port initialization */
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530846 spin_lock_init(&port->port_lock_ul);
847 spin_lock_init(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700848 INIT_WORK(&port->connect_w, gbam_connect_work);
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800849 INIT_WORK(&port->disconnect_w, gbam_disconnect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700850
851 /* data ch */
852 d = &port->data_ch;
853 d->port = port;
854 INIT_LIST_HEAD(&d->tx_idle);
855 INIT_LIST_HEAD(&d->rx_idle);
856 INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);
857 skb_queue_head_init(&d->tx_skb_q);
858 skb_queue_head_init(&d->rx_skb_q);
859 d->id = bam_ch_ids[portno];
860
861 bam_ports[portno].port = port;
862
Jack Phameffd4ae2011-08-03 16:49:36 -0700863 pdrv = &bam_ports[portno].pdrv;
864 pdrv->probe = gbam_data_ch_probe;
865 pdrv->remove = gbam_data_ch_remove;
866 pdrv->driver.name = bam_ch_names[portno];
867 pdrv->driver.owner = THIS_MODULE;
868
869 platform_driver_register(pdrv);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200870 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
871
872 return 0;
873}
874
875static int gbam2bam_port_alloc(int portno)
876{
877 struct gbam_port *port;
878 struct bam_ch_info *d;
879
880 port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
881 if (!port)
882 return -ENOMEM;
883
884 port->port_num = portno;
885
886 /* port initialization */
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530887 spin_lock_init(&port->port_lock_ul);
888 spin_lock_init(&port->port_lock_dl);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200889
890 INIT_WORK(&port->connect_w, gbam2bam_connect_work);
891 INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
892
893 /* data ch */
894 d = &port->data_ch;
895 d->port = port;
896 bam2bam_ports[portno] = port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700897
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700898 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
899
900 return 0;
901}
902
903#if defined(CONFIG_DEBUG_FS)
904#define DEBUG_BUF_SIZE 1024
905static ssize_t gbam_read_stats(struct file *file, char __user *ubuf,
906 size_t count, loff_t *ppos)
907{
908 struct gbam_port *port;
909 struct bam_ch_info *d;
910 char *buf;
911 unsigned long flags;
912 int ret;
913 int i;
914 int temp = 0;
915
916 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
917 if (!buf)
918 return -ENOMEM;
919
920 for (i = 0; i < n_bam_ports; i++) {
921 port = bam_ports[i].port;
922 if (!port)
923 continue;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530924 spin_lock_irqsave(&port->port_lock_ul, flags);
925 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700926
927 d = &port->data_ch;
928
929 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
930 "#PORT:%d port:%p data_ch:%p#\n"
931 "dpkts_to_usbhost: %lu\n"
932 "dpkts_to_modem: %lu\n"
933 "dpkts_pwith_bam: %u\n"
934 "to_usbhost_dcnt: %u\n"
935 "tomodem__dcnt: %u\n"
936 "tx_buf_len: %u\n"
Vamsi Krishna84579552011-11-09 15:33:22 -0800937 "rx_buf_len: %u\n"
Jack Phameffd4ae2011-08-03 16:49:36 -0700938 "data_ch_open: %d\n"
939 "data_ch_ready: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940 i, port, &port->data_ch,
941 d->to_host, d->to_modem,
942 d->pending_with_bam,
943 d->tohost_drp_cnt, d->tomodem_drp_cnt,
Vamsi Krishna84579552011-11-09 15:33:22 -0800944 d->tx_skb_q.qlen, d->rx_skb_q.qlen,
Jack Phameffd4ae2011-08-03 16:49:36 -0700945 test_bit(BAM_CH_OPENED, &d->flags),
946 test_bit(BAM_CH_READY, &d->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700947
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530948 spin_unlock_irqrestore(&port->port_lock_dl, flags);
949 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950 }
951
952 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
953
954 kfree(buf);
955
956 return ret;
957}
958
959static ssize_t gbam_reset_stats(struct file *file, const char __user *buf,
960 size_t count, loff_t *ppos)
961{
962 struct gbam_port *port;
963 struct bam_ch_info *d;
964 int i;
965 unsigned long flags;
966
967 for (i = 0; i < n_bam_ports; i++) {
968 port = bam_ports[i].port;
969 if (!port)
970 continue;
971
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530972 spin_lock_irqsave(&port->port_lock_ul, flags);
973 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974
975 d = &port->data_ch;
976
977 d->to_host = 0;
978 d->to_modem = 0;
979 d->pending_with_bam = 0;
980 d->tohost_drp_cnt = 0;
981 d->tomodem_drp_cnt = 0;
982
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530983 spin_unlock_irqrestore(&port->port_lock_dl, flags);
984 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700985 }
986 return count;
987}
988
989const struct file_operations gbam_stats_ops = {
990 .read = gbam_read_stats,
991 .write = gbam_reset_stats,
992};
993
994static void gbam_debugfs_init(void)
995{
996 struct dentry *dent;
997 struct dentry *dfile;
998
999 dent = debugfs_create_dir("usb_rmnet", 0);
1000 if (IS_ERR(dent))
1001 return;
1002
1003 /* TODO: Implement cleanup function to remove created file */
1004 dfile = debugfs_create_file("status", 0444, dent, 0, &gbam_stats_ops);
1005 if (!dfile || IS_ERR(dfile))
1006 debugfs_remove(dent);
1007}
1008#else
1009static void gam_debugfs_init(void) { }
1010#endif
1011
Ofir Cohena1c2a872011-12-14 10:26:34 +02001012void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001013{
1014 struct gbam_port *port;
1015 unsigned long flags;
1016 struct bam_ch_info *d;
1017
1018 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
1019
Ofir Cohena1c2a872011-12-14 10:26:34 +02001020 if (trans == USB_GADGET_XPORT_BAM &&
1021 port_num >= n_bam_ports) {
1022 pr_err("%s: invalid bam portno#%d\n",
1023 __func__, port_num);
1024 return;
1025 }
1026
1027 if (trans == USB_GADGET_XPORT_BAM2BAM &&
1028 port_num >= n_bam2bam_ports) {
1029 pr_err("%s: invalid bam2bam portno#%d\n",
1030 __func__, port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001031 return;
1032 }
1033
1034 if (!gr) {
1035 pr_err("%s: grmnet port is null\n", __func__);
1036 return;
1037 }
Ofir Cohena1c2a872011-12-14 10:26:34 +02001038 if (trans == USB_GADGET_XPORT_BAM)
1039 port = bam_ports[port_num].port;
1040 else
1041 port = bam2bam_ports[port_num];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001042
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001043 d = &port->data_ch;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001044 port->gr = gr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001045
Ofir Cohena1c2a872011-12-14 10:26:34 +02001046 if (trans == USB_GADGET_XPORT_BAM) {
1047 gbam_free_buffers(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001048
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301049 spin_lock_irqsave(&port->port_lock_ul, flags);
1050 spin_lock_irqsave(&port->port_lock_dl, flags);
1051 port->port_usb = 0;
1052 spin_unlock_irqrestore(&port->port_lock_dl, flags);
1053 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001054
Ofir Cohena1c2a872011-12-14 10:26:34 +02001055 /* disable endpoints */
1056 usb_ep_disable(gr->out);
1057 usb_ep_disable(gr->in);
1058 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001059
Vamsi Krishna1ad076d2011-11-10 15:03:30 -08001060 queue_work(gbam_wq, &port->disconnect_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001061}
1062
Ofir Cohena1c2a872011-12-14 10:26:34 +02001063int gbam_connect(struct grmnet *gr, u8 port_num,
1064 enum transport_type trans, u8 connection_idx)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001065{
1066 struct gbam_port *port;
1067 struct bam_ch_info *d;
1068 int ret;
1069 unsigned long flags;
1070
1071 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
1072
Ofir Cohena1c2a872011-12-14 10:26:34 +02001073 if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) {
1074 pr_err("%s: invalid portno#%d\n", __func__, port_num);
1075 return -ENODEV;
1076 }
1077
1078 if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001079 pr_err("%s: invalid portno#%d\n", __func__, port_num);
1080 return -ENODEV;
1081 }
1082
1083 if (!gr) {
1084 pr_err("%s: grmnet port is null\n", __func__);
1085 return -ENODEV;
1086 }
1087
Ofir Cohena1c2a872011-12-14 10:26:34 +02001088 if (trans == USB_GADGET_XPORT_BAM)
1089 port = bam_ports[port_num].port;
1090 else
1091 port = bam2bam_ports[port_num];
1092
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001093 d = &port->data_ch;
1094
Ofir Cohena1c2a872011-12-14 10:26:34 +02001095 if (trans == USB_GADGET_XPORT_BAM) {
Ofir Cohen4da266f2012-01-03 10:19:29 +02001096 ret = usb_ep_enable(gr->in, gr->in_desc);
1097 if (ret) {
1098 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
1099 __func__, gr->in);
1100 return ret;
1101 }
1102 gr->in->driver_data = port;
1103
1104 ret = usb_ep_enable(gr->out, gr->out_desc);
1105 if (ret) {
1106 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
1107 __func__, gr->out);
1108 gr->in->driver_data = 0;
1109 return ret;
1110 }
1111 gr->out->driver_data = port;
1112
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301113 spin_lock_irqsave(&port->port_lock_ul, flags);
1114 spin_lock_irqsave(&port->port_lock_dl, flags);
1115 port->port_usb = gr;
Ofir Cohen4da266f2012-01-03 10:19:29 +02001116
Ofir Cohena1c2a872011-12-14 10:26:34 +02001117 d->to_host = 0;
1118 d->to_modem = 0;
1119 d->pending_with_bam = 0;
1120 d->tohost_drp_cnt = 0;
1121 d->tomodem_drp_cnt = 0;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301122 spin_unlock_irqrestore(&port->port_lock_dl, flags);
1123 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +02001124 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001125
Ofir Cohen4da266f2012-01-03 10:19:29 +02001126 if (trans == USB_GADGET_XPORT_BAM2BAM) {
1127 port->gr = gr;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001128 d->connection_idx = connection_idx;
Ofir Cohen4da266f2012-01-03 10:19:29 +02001129 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001130
1131 queue_work(gbam_wq, &port->connect_w);
1132
1133 return 0;
1134}
1135
Ofir Cohena1c2a872011-12-14 10:26:34 +02001136int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001137{
1138 int i;
1139 int ret;
1140
Ofir Cohena1c2a872011-12-14 10:26:34 +02001141 pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n",
1142 __func__, no_bam_port, no_bam2bam_port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001143
Ofir Cohena1c2a872011-12-14 10:26:34 +02001144 if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS
1145 || no_bam2bam_port > BAM2BAM_N_PORTS) {
1146 pr_err("%s: Invalid num of ports count:%d,%d\n",
1147 __func__, no_bam_port, no_bam2bam_port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001148 return -EINVAL;
1149 }
1150
1151 gbam_wq = alloc_workqueue("k_gbam", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
1152 if (!gbam_wq) {
1153 pr_err("%s: Unable to create workqueue gbam_wq\n",
1154 __func__);
1155 return -ENOMEM;
1156 }
1157
Ofir Cohena1c2a872011-12-14 10:26:34 +02001158 for (i = 0; i < no_bam_port; i++) {
Manu Gautamd59b5d32011-09-09 14:47:08 +05301159 n_bam_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001160 ret = gbam_port_alloc(i);
1161 if (ret) {
Manu Gautamd59b5d32011-09-09 14:47:08 +05301162 n_bam_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001163 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
1164 goto free_bam_ports;
1165 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166 }
1167
Ofir Cohena1c2a872011-12-14 10:26:34 +02001168 for (i = 0; i < no_bam2bam_port; i++) {
1169 n_bam2bam_ports++;
1170 ret = gbam2bam_port_alloc(i);
1171 if (ret) {
1172 n_bam2bam_ports--;
1173 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
1174 goto free_bam_ports;
1175 }
1176 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001177 gbam_debugfs_init();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001178 return 0;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001179
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001180free_bam_ports:
1181 for (i = 0; i < n_bam_ports; i++)
1182 gbam_port_free(i);
Ofir Cohena1c2a872011-12-14 10:26:34 +02001183 for (i = 0; i < n_bam2bam_ports; i++)
1184 gbam2bam_port_free(i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185 destroy_workqueue(gbam_wq);
1186
1187 return ret;
1188}