blob: 4bd7f39ba37b3d34d602d7a451fc5b4158860976 [file] [log] [blame]
Chiranjeevi Velempatia06b2232013-01-04 10:10:52 +05301/* Copyright (c) 2011-2013, Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Shimrit Malichi194fe122012-07-25 13:50:41 +030027#include <linux/usb/msm_hsusb.h>
Ofir Cohena1c2a872011-12-14 10:26:34 +020028#include <mach/usb_bam.h>
29
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include "u_rmnet.h"
31
32#define BAM_N_PORTS 1
Anna Perel21515162012-02-02 20:50:02 +020033#define BAM2BAM_N_PORTS 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
35static struct workqueue_struct *gbam_wq;
36static int n_bam_ports;
Ofir Cohena1c2a872011-12-14 10:26:34 +020037static int n_bam2bam_ports;
Vijayavardhan Vennapusa08c31252011-12-21 13:02:49 +053038static unsigned n_tx_req_queued;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039static unsigned bam_ch_ids[] = { 8 };
40
Jack Phameffd4ae2011-08-03 16:49:36 -070041static const char *bam_ch_names[] = { "bam_dmux_ch_8" };
42
Vamsi Krishna84579552011-11-09 15:33:22 -080043#define BAM_PENDING_LIMIT 220
Vamsi Krishna8f24f252011-11-02 11:46:08 -070044#define BAM_MUX_TX_PKT_DROP_THRESHOLD 1000
Vamsi Krishna84579552011-11-09 15:33:22 -080045#define BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD 500
46#define BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD 300
Vamsi Krishna8f24f252011-11-02 11:46:08 -070047#define BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048
49#define BAM_MUX_HDR 8
50
Vamsi Krishna8f24f252011-11-02 11:46:08 -070051#define BAM_MUX_RX_Q_SIZE 16
52#define BAM_MUX_TX_Q_SIZE 200
Manu Gautam15203302012-09-26 11:12:54 +053053#define BAM_MUX_RX_REQ_SIZE 2048 /* Must be 1KB aligned */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054
Vijayavardhan Vennapusa08c31252011-12-21 13:02:49 +053055#define DL_INTR_THRESHOLD 20
56
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053057static unsigned int bam_pending_limit = BAM_PENDING_LIMIT;
58module_param(bam_pending_limit, uint, S_IRUGO | S_IWUSR);
59
60static unsigned int bam_mux_tx_pkt_drop_thld = BAM_MUX_TX_PKT_DROP_THRESHOLD;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070061module_param(bam_mux_tx_pkt_drop_thld, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053063static unsigned int bam_mux_rx_fctrl_en_thld = BAM_MUX_RX_PKT_FCTRL_EN_TSHOLD;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070064module_param(bam_mux_rx_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053066static unsigned int bam_mux_rx_fctrl_support = BAM_MUX_RX_PKT_FLOW_CTRL_SUPPORT;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070067module_param(bam_mux_rx_fctrl_support, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053069static unsigned int bam_mux_rx_fctrl_dis_thld = BAM_MUX_RX_PKT_FCTRL_DIS_TSHOLD;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070070module_param(bam_mux_rx_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053072static unsigned int bam_mux_tx_q_size = BAM_MUX_TX_Q_SIZE;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070073module_param(bam_mux_tx_q_size, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053075static unsigned int bam_mux_rx_q_size = BAM_MUX_RX_Q_SIZE;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070076module_param(bam_mux_rx_q_size, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053078static unsigned int bam_mux_rx_req_size = BAM_MUX_RX_REQ_SIZE;
Vamsi Krishna8f24f252011-11-02 11:46:08 -070079module_param(bam_mux_rx_req_size, uint, S_IRUGO | S_IWUSR);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +053081static unsigned int dl_intr_threshold = DL_INTR_THRESHOLD;
Vijayavardhan Vennapusa08c31252011-12-21 13:02:49 +053082module_param(dl_intr_threshold, uint, S_IRUGO | S_IWUSR);
83
Jack Phameffd4ae2011-08-03 16:49:36 -070084#define BAM_CH_OPENED BIT(0)
85#define BAM_CH_READY BIT(1)
Ofir Cohena1c2a872011-12-14 10:26:34 +020086
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087struct bam_ch_info {
Jack Phameffd4ae2011-08-03 16:49:36 -070088 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089 unsigned id;
90
91 struct list_head tx_idle;
92 struct sk_buff_head tx_skb_q;
93
94 struct list_head rx_idle;
95 struct sk_buff_head rx_skb_q;
96
97 struct gbam_port *port;
98 struct work_struct write_tobam_w;
Vijayavardhan Vennapusa929e5792011-12-12 17:34:53 +053099 struct work_struct write_tohost_w;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100
Ofir Cohena1c2a872011-12-14 10:26:34 +0200101 struct usb_request *rx_req;
102 struct usb_request *tx_req;
103
Shimrit Malichi255b5342012-08-02 21:01:43 +0300104 u32 src_pipe_idx;
105 u32 dst_pipe_idx;
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200106 u8 src_connection_idx;
107 u8 dst_connection_idx;
Ofir Cohenfdecb602012-11-16 15:50:01 +0200108 enum transport_type trans;
Ofir Cohen77848d62012-12-05 13:16:10 +0200109 struct usb_bam_connect_ipa_params ipa_params;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200110
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 /* stats */
112 unsigned int pending_with_bam;
113 unsigned int tohost_drp_cnt;
114 unsigned int tomodem_drp_cnt;
115 unsigned int tx_len;
116 unsigned int rx_len;
117 unsigned long to_modem;
118 unsigned long to_host;
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +0530119 unsigned int rx_flow_control_disable;
120 unsigned int rx_flow_control_enable;
121 unsigned int rx_flow_control_triggered;
122 unsigned int max_num_pkts_pending_with_bam;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123};
124
125struct gbam_port {
126 unsigned port_num;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530127 spinlock_t port_lock_ul;
128 spinlock_t port_lock_dl;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129
130 struct grmnet *port_usb;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200131 struct grmnet *gr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132
133 struct bam_ch_info data_ch;
134
135 struct work_struct connect_w;
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800136 struct work_struct disconnect_w;
Bar Weiner84e1ba02013-07-18 09:08:03 +0300137 struct work_struct suspend_w;
138 struct work_struct resume_w;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139};
140
141static struct bam_portmaster {
142 struct gbam_port *port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700143 struct platform_driver pdrv;
144} bam_ports[BAM_N_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145
Ofir Cohena1c2a872011-12-14 10:26:34 +0200146struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147static void gbam_start_rx(struct gbam_port *port);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200148static void gbam_start_endless_rx(struct gbam_port *port);
149static void gbam_start_endless_tx(struct gbam_port *port);
Amit Blay94525352012-12-24 11:23:27 +0200150static int gbam_peer_reset_cb(void *param);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151
152/*---------------misc functions---------------- */
153static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
154{
155 struct usb_request *req;
156
157 while (!list_empty(head)) {
158 req = list_entry(head->next, struct usb_request, list);
159 list_del(&req->list);
160 usb_ep_free_request(ep, req);
161 }
162}
163
164static int gbam_alloc_requests(struct usb_ep *ep, struct list_head *head,
165 int num,
166 void (*cb)(struct usb_ep *ep, struct usb_request *),
167 gfp_t flags)
168{
169 int i;
170 struct usb_request *req;
171
172 pr_debug("%s: ep:%p head:%p num:%d cb:%p", __func__,
173 ep, head, num, cb);
174
175 for (i = 0; i < num; i++) {
176 req = usb_ep_alloc_request(ep, flags);
177 if (!req) {
178 pr_debug("%s: req allocated:%d\n", __func__, i);
179 return list_empty(head) ? -ENOMEM : 0;
180 }
181 req->complete = cb;
182 list_add(&req->list, head);
183 }
184
185 return 0;
186}
187/*--------------------------------------------- */
188
189/*------------data_path----------------------------*/
Chiranjeevi Velempatie5105922012-01-19 12:25:26 +0530190static void gbam_write_data_tohost(struct gbam_port *port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191{
192 unsigned long flags;
Chiranjeevi Velempatie5105922012-01-19 12:25:26 +0530193 struct bam_ch_info *d = &port->data_ch;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 struct sk_buff *skb;
195 int ret;
196 struct usb_request *req;
197 struct usb_ep *ep;
198
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530199 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530201 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 return;
203 }
204
205 ep = port->port_usb->in;
206
207 while (!list_empty(&d->tx_idle)) {
208 skb = __skb_dequeue(&d->tx_skb_q);
209 if (!skb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530210 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 return;
212 }
213 req = list_first_entry(&d->tx_idle,
214 struct usb_request,
215 list);
216 req->context = skb;
217 req->buf = skb->data;
218 req->length = skb->len;
Vijayavardhan Vennapusa08c31252011-12-21 13:02:49 +0530219 n_tx_req_queued++;
220 if (n_tx_req_queued == dl_intr_threshold) {
221 req->no_interrupt = 0;
222 n_tx_req_queued = 0;
223 } else {
224 req->no_interrupt = 1;
225 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226
Rajkumar Raghupathyf05df312013-06-04 17:57:49 +0530227 /* Send ZLP in case packet length is multiple of maxpacksize */
228 req->zero = 1;
229
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 list_del(&req->list);
231
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530232 spin_unlock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233 ret = usb_ep_queue(ep, req, GFP_ATOMIC);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530234 spin_lock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 if (ret) {
Chiranjeevi Velempatia06b2232013-01-04 10:10:52 +0530236 pr_err("%s: usb epIn failed with %d\n", __func__, ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237 list_add(&req->list, &d->tx_idle);
238 dev_kfree_skb_any(skb);
239 break;
240 }
241 d->to_host++;
242 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530243 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244}
245
Chiranjeevi Velempatie5105922012-01-19 12:25:26 +0530246static void gbam_write_data_tohost_w(struct work_struct *w)
247{
248 struct bam_ch_info *d;
249 struct gbam_port *port;
250
251 d = container_of(w, struct bam_ch_info, write_tohost_w);
252 port = d->port;
253
254 gbam_write_data_tohost(port);
255}
256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257void gbam_data_recv_cb(void *p, struct sk_buff *skb)
258{
259 struct gbam_port *port = p;
260 struct bam_ch_info *d = &port->data_ch;
261 unsigned long flags;
262
263 if (!skb)
264 return;
265
266 pr_debug("%s: p:%p#%d d:%p skb_len:%d\n", __func__,
267 port, port->port_num, d, skb->len);
268
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530269 spin_lock_irqsave(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530271 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 dev_kfree_skb_any(skb);
273 return;
274 }
275
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700276 if (d->tx_skb_q.qlen > bam_mux_tx_pkt_drop_thld) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 d->tohost_drp_cnt++;
278 if (printk_ratelimit())
279 pr_err("%s: tx pkt dropped: tx_drop_cnt:%u\n",
280 __func__, d->tohost_drp_cnt);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530281 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 dev_kfree_skb_any(skb);
283 return;
284 }
285
286 __skb_queue_tail(&d->tx_skb_q, skb);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530287 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
Chiranjeevi Velempatie5105922012-01-19 12:25:26 +0530289 gbam_write_data_tohost(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290}
291
292void gbam_data_write_done(void *p, struct sk_buff *skb)
293{
294 struct gbam_port *port = p;
295 struct bam_ch_info *d = &port->data_ch;
296 unsigned long flags;
297
298 if (!skb)
299 return;
300
301 dev_kfree_skb_any(skb);
302
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530303 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
305 d->pending_with_bam--;
306
307 pr_debug("%s: port:%p d:%p tom:%lu pbam:%u, pno:%d\n", __func__,
308 port, d, d->to_modem,
309 d->pending_with_bam, port->port_num);
310
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530311 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312
Vamsi Krishna84579552011-11-09 15:33:22 -0800313 queue_work(gbam_wq, &d->write_tobam_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314}
315
316static void gbam_data_write_tobam(struct work_struct *w)
317{
318 struct gbam_port *port;
319 struct bam_ch_info *d;
320 struct sk_buff *skb;
321 unsigned long flags;
322 int ret;
Vamsi Krishna84579552011-11-09 15:33:22 -0800323 int qlen;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324
325 d = container_of(w, struct bam_ch_info, write_tobam_w);
326 port = d->port;
327
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530328 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530330 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 return;
332 }
333
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +0530334 while (d->pending_with_bam < bam_pending_limit) {
Vamsi Krishna2327c79152011-11-08 16:12:42 -0800335 skb = __skb_dequeue(&d->rx_skb_q);
Vamsi Krishna625c28e2011-12-16 22:34:49 -0800336 if (!skb)
337 break;
338
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 d->pending_with_bam++;
340 d->to_modem++;
341
342 pr_debug("%s: port:%p d:%p tom:%lu pbam:%u pno:%d\n", __func__,
343 port, d, d->to_modem, d->pending_with_bam,
344 port->port_num);
345
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530346 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700347 ret = msm_bam_dmux_write(d->id, skb);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530348 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 if (ret) {
350 pr_debug("%s: write error:%d\n", __func__, ret);
351 d->pending_with_bam--;
352 d->to_modem--;
353 d->tomodem_drp_cnt++;
354 dev_kfree_skb_any(skb);
355 break;
356 }
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +0530357 if (d->pending_with_bam > d->max_num_pkts_pending_with_bam)
358 d->max_num_pkts_pending_with_bam = d->pending_with_bam;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359 }
Vamsi Krishna84579552011-11-09 15:33:22 -0800360
361 qlen = d->rx_skb_q.qlen;
362
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530363 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Vamsi Krishna84579552011-11-09 15:33:22 -0800364
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +0530365 if (qlen < bam_mux_rx_fctrl_dis_thld) {
366 if (d->rx_flow_control_triggered) {
367 d->rx_flow_control_disable++;
368 d->rx_flow_control_triggered = 0;
369 }
Vamsi Krishna84579552011-11-09 15:33:22 -0800370 gbam_start_rx(port);
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +0530371 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372}
373/*-------------------------------------------------------------*/
374
375static void gbam_epin_complete(struct usb_ep *ep, struct usb_request *req)
376{
377 struct gbam_port *port = ep->driver_data;
378 struct bam_ch_info *d;
379 struct sk_buff *skb = req->context;
380 int status = req->status;
381
382 switch (status) {
383 case 0:
384 /* successful completion */
Manu Gautam06277e42013-04-01 15:07:29 +0530385 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700386 case -ECONNRESET:
387 case -ESHUTDOWN:
388 /* connection gone */
Manu Gautam06277e42013-04-01 15:07:29 +0530389 dev_kfree_skb_any(skb);
390 usb_ep_free_request(ep, req);
391 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 default:
393 pr_err("%s: data tx ep error %d\n",
394 __func__, status);
395 break;
396 }
397
398 dev_kfree_skb_any(skb);
399
400 if (!port)
401 return;
402
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530403 spin_lock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404 d = &port->data_ch;
405 list_add_tail(&req->list, &d->tx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530406 spin_unlock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
Vijayavardhan Vennapusa929e5792011-12-12 17:34:53 +0530408 queue_work(gbam_wq, &d->write_tohost_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409}
410
411static void
412gbam_epout_complete(struct usb_ep *ep, struct usb_request *req)
413{
414 struct gbam_port *port = ep->driver_data;
415 struct bam_ch_info *d = &port->data_ch;
416 struct sk_buff *skb = req->context;
417 int status = req->status;
418 int queue = 0;
419
420 switch (status) {
421 case 0:
422 skb_put(skb, req->actual);
423 queue = 1;
424 break;
425 case -ECONNRESET:
426 case -ESHUTDOWN:
427 /* cable disconnection */
428 dev_kfree_skb_any(skb);
429 req->buf = 0;
430 usb_ep_free_request(ep, req);
431 return;
432 default:
433 if (printk_ratelimit())
434 pr_err("%s: %s response error %d, %d/%d\n",
435 __func__, ep->name, status,
436 req->actual, req->length);
437 dev_kfree_skb_any(skb);
438 break;
439 }
440
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530441 spin_lock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 if (queue) {
443 __skb_queue_tail(&d->rx_skb_q, skb);
444 queue_work(gbam_wq, &d->write_tobam_w);
445 }
446
447 /* TODO: Handle flow control gracefully by having
448 * having call back mechanism from bam driver
449 */
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700450 if (bam_mux_rx_fctrl_support &&
Vamsi Krishna84579552011-11-09 15:33:22 -0800451 d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld) {
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +0530452 if (!d->rx_flow_control_triggered) {
453 d->rx_flow_control_triggered = 1;
454 d->rx_flow_control_enable++;
455 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700456 list_add_tail(&req->list, &d->rx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530457 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 return;
459 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530460 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700462 skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 if (!skb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530464 spin_lock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 list_add_tail(&req->list, &d->rx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530466 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 return;
468 }
469 skb_reserve(skb, BAM_MUX_HDR);
470
471 req->buf = skb->data;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700472 req->length = bam_mux_rx_req_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 req->context = skb;
474
475 status = usb_ep_queue(ep, req, GFP_ATOMIC);
476 if (status) {
477 dev_kfree_skb_any(skb);
478
479 if (printk_ratelimit())
480 pr_err("%s: data rx enqueue err %d\n",
481 __func__, status);
482
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530483 spin_lock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484 list_add_tail(&req->list, &d->rx_idle);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530485 spin_unlock(&port->port_lock_ul);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 }
487}
488
Ofir Cohena1c2a872011-12-14 10:26:34 +0200489static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
490{
491 int status = req->status;
492
493 pr_debug("%s status: %d\n", __func__, status);
494}
495
496static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
497{
498 int status = req->status;
499
500 pr_debug("%s status: %d\n", __func__, status);
501}
502
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503static void gbam_start_rx(struct gbam_port *port)
504{
505 struct usb_request *req;
506 struct bam_ch_info *d;
507 struct usb_ep *ep;
508 unsigned long flags;
509 int ret;
510 struct sk_buff *skb;
511
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530512 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530514 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515 return;
516 }
517
518 d = &port->data_ch;
519 ep = port->port_usb->out;
520
521 while (port->port_usb && !list_empty(&d->rx_idle)) {
Vamsi Krishna84579552011-11-09 15:33:22 -0800522
523 if (bam_mux_rx_fctrl_support &&
524 d->rx_skb_q.qlen >= bam_mux_rx_fctrl_en_thld)
525 break;
526
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 req = list_first_entry(&d->rx_idle, struct usb_request, list);
528
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700529 skb = alloc_skb(bam_mux_rx_req_size + BAM_MUX_HDR, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 if (!skb)
531 break;
532 skb_reserve(skb, BAM_MUX_HDR);
533
534 list_del(&req->list);
535 req->buf = skb->data;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700536 req->length = bam_mux_rx_req_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 req->context = skb;
538
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530539 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 ret = usb_ep_queue(ep, req, GFP_ATOMIC);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530541 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 if (ret) {
543 dev_kfree_skb_any(skb);
544
545 if (printk_ratelimit())
Chiranjeevi Velempatia06b2232013-01-04 10:10:52 +0530546 pr_err("%s: rx queue failed %d\n",
547 __func__, ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548
549 if (port->port_usb)
550 list_add(&req->list, &d->rx_idle);
551 else
552 usb_ep_free_request(ep, req);
553 break;
554 }
555 }
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530556 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557}
558
Ofir Cohena1c2a872011-12-14 10:26:34 +0200559static void gbam_start_endless_rx(struct gbam_port *port)
560{
561 struct bam_ch_info *d = &port->data_ch;
562 int status;
563
Lena Salmana12bc322013-07-02 15:04:42 +0300564 spin_lock(&port->port_lock_ul);
Lena Salman05b544f2013-05-13 15:49:10 +0300565 if (!port->port_usb) {
Lena Salmana12bc322013-07-02 15:04:42 +0300566 spin_unlock(&port->port_lock_ul);
Lena Salman05b544f2013-05-13 15:49:10 +0300567 pr_err("%s: port->port_usb is NULL", __func__);
Amit Blay94525352012-12-24 11:23:27 +0200568 return;
Lena Salman05b544f2013-05-13 15:49:10 +0300569 }
Amit Blay94525352012-12-24 11:23:27 +0200570
Lena Salman05b544f2013-05-13 15:49:10 +0300571 pr_debug("%s: enqueue\n", __func__);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200572 status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
573 if (status)
574 pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
Lena Salmana12bc322013-07-02 15:04:42 +0300575 spin_unlock(&port->port_lock_ul);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200576}
577
578static void gbam_start_endless_tx(struct gbam_port *port)
579{
580 struct bam_ch_info *d = &port->data_ch;
581 int status;
582
Lena Salmana12bc322013-07-02 15:04:42 +0300583 spin_lock(&port->port_lock_dl);
Lena Salman05b544f2013-05-13 15:49:10 +0300584 if (!port->port_usb) {
Lena Salmana12bc322013-07-02 15:04:42 +0300585 spin_unlock(&port->port_lock_dl);
Lena Salman05b544f2013-05-13 15:49:10 +0300586 pr_err("%s: port->port_usb is NULL", __func__);
Amit Blay94525352012-12-24 11:23:27 +0200587 return;
Lena Salman05b544f2013-05-13 15:49:10 +0300588 }
Amit Blay94525352012-12-24 11:23:27 +0200589
Lena Salman05b544f2013-05-13 15:49:10 +0300590 pr_debug("%s: enqueue\n", __func__);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200591 status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
592 if (status)
593 pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
Lena Salmana12bc322013-07-02 15:04:42 +0300594 spin_unlock(&port->port_lock_dl);
595
Ofir Cohena1c2a872011-12-14 10:26:34 +0200596}
597
Lena Salman05b544f2013-05-13 15:49:10 +0300598static void gbam_stop_endless_rx(struct gbam_port *port)
599{
600 struct bam_ch_info *d = &port->data_ch;
601 int status;
602
Lena Salmana12bc322013-07-02 15:04:42 +0300603 spin_lock(&port->port_lock_ul);
Lena Salman05b544f2013-05-13 15:49:10 +0300604 if (!port->port_usb) {
Lena Salmana12bc322013-07-02 15:04:42 +0300605 spin_unlock(&port->port_lock_ul);
Lena Salman05b544f2013-05-13 15:49:10 +0300606 pr_err("%s: port->port_usb is NULL", __func__);
607 return;
608 }
609 pr_debug("%s: dequeue\n", __func__);
610
611 status = usb_ep_dequeue(port->port_usb->out, d->rx_req);
612 if (status)
613 pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
Lena Salmana12bc322013-07-02 15:04:42 +0300614 spin_unlock(&port->port_lock_ul);
Lena Salman05b544f2013-05-13 15:49:10 +0300615}
Lena Salmana12bc322013-07-02 15:04:42 +0300616
Lena Salman05b544f2013-05-13 15:49:10 +0300617static void gbam_stop_endless_tx(struct gbam_port *port)
618{
619 struct bam_ch_info *d = &port->data_ch;
620 int status;
621
Lena Salmana12bc322013-07-02 15:04:42 +0300622 spin_lock(&port->port_lock_dl);
Lena Salman05b544f2013-05-13 15:49:10 +0300623 if (!port->port_usb) {
Lena Salmana12bc322013-07-02 15:04:42 +0300624 spin_unlock(&port->port_lock_dl);
Lena Salman05b544f2013-05-13 15:49:10 +0300625 pr_err("%s: port->port_usb is NULL", __func__);
626 return;
627 }
628
629 pr_debug("%s: dequeue\n", __func__);
630 status = usb_ep_dequeue(port->port_usb->in, d->tx_req);
631 if (status)
632 pr_err("%s: error dequeuing transfer, %d\n", __func__, status);
Lena Salmana12bc322013-07-02 15:04:42 +0300633 spin_unlock(&port->port_lock_dl);
Lena Salman05b544f2013-05-13 15:49:10 +0300634}
635
636static void gbam_start(void *param, enum usb_bam_pipe_dir dir)
637{
638 struct gbam_port *port = param;
639
640 if (dir == USB_TO_PEER_PERIPHERAL)
641 gbam_start_endless_rx(port);
642 else
643 gbam_start_endless_tx(port);
644}
645
646static void gbam_stop(void *param, enum usb_bam_pipe_dir dir)
647{
648 struct gbam_port *port = param;
649
650 if (dir == USB_TO_PEER_PERIPHERAL)
651 gbam_stop_endless_rx(port);
652 else
653 gbam_stop_endless_tx(port);
654}
655
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656static void gbam_start_io(struct gbam_port *port)
657{
658 unsigned long flags;
659 struct usb_ep *ep;
660 int ret;
661 struct bam_ch_info *d;
662
663 pr_debug("%s: port:%p\n", __func__, port);
664
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530665 spin_lock_irqsave(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700666 if (!port->port_usb) {
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530667 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668 return;
669 }
670
671 d = &port->data_ch;
672 ep = port->port_usb->out;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700673 ret = gbam_alloc_requests(ep, &d->rx_idle, bam_mux_rx_q_size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674 gbam_epout_complete, GFP_ATOMIC);
675 if (ret) {
676 pr_err("%s: rx req allocation failed\n", __func__);
Manu Gautam24823342013-06-13 16:33:49 +0530677 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678 return;
679 }
680
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530681 spin_unlock_irqrestore(&port->port_lock_ul, flags);
682 spin_lock_irqsave(&port->port_lock_dl, flags);
Chiranjeevi Velempati44d02982013-02-17 22:09:08 +0530683 if (!port->port_usb) {
684 gbam_free_requests(ep, &d->rx_idle);
685 spin_unlock_irqrestore(&port->port_lock_dl, flags);
686 return;
687 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700688 ep = port->port_usb->in;
Vamsi Krishna8f24f252011-11-02 11:46:08 -0700689 ret = gbam_alloc_requests(ep, &d->tx_idle, bam_mux_tx_q_size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690 gbam_epin_complete, GFP_ATOMIC);
691 if (ret) {
692 pr_err("%s: tx req allocation failed\n", __func__);
693 gbam_free_requests(ep, &d->rx_idle);
Manu Gautam24823342013-06-13 16:33:49 +0530694 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 return;
696 }
697
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530698 spin_unlock_irqrestore(&port->port_lock_dl, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699
700 /* queue out requests */
701 gbam_start_rx(port);
702}
703
Jeff Hugo1c4531c2011-08-02 14:55:37 -0600704static void gbam_notify(void *p, int event, unsigned long data)
705{
706 switch (event) {
707 case BAM_DMUX_RECEIVE:
708 gbam_data_recv_cb(p, (struct sk_buff *)(data));
709 break;
710 case BAM_DMUX_WRITE_DONE:
711 gbam_data_write_done(p, (struct sk_buff *)(data));
712 break;
713 }
714}
715
Ofir Cohena1c2a872011-12-14 10:26:34 +0200716static void gbam_free_buffers(struct gbam_port *port)
717{
718 struct sk_buff *skb;
719 unsigned long flags;
720 struct bam_ch_info *d;
721
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530722 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -0800723 spin_lock(&port->port_lock_dl);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200724
725 if (!port || !port->port_usb)
726 goto free_buf_out;
727
728 d = &port->data_ch;
729
730 gbam_free_requests(port->port_usb->in, &d->tx_idle);
731 gbam_free_requests(port->port_usb->out, &d->rx_idle);
732
733 while ((skb = __skb_dequeue(&d->tx_skb_q)))
734 dev_kfree_skb_any(skb);
735
736 while ((skb = __skb_dequeue(&d->rx_skb_q)))
737 dev_kfree_skb_any(skb);
738
739free_buf_out:
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -0800740 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530741 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200742}
743
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800744static void gbam_disconnect_work(struct work_struct *w)
745{
746 struct gbam_port *port =
747 container_of(w, struct gbam_port, disconnect_w);
748 struct bam_ch_info *d = &port->data_ch;
749
750 if (!test_bit(BAM_CH_OPENED, &d->flags))
751 return;
752
753 msm_bam_dmux_close(d->id);
754 clear_bit(BAM_CH_OPENED, &d->flags);
755}
756
Ofir Cohenfdecb602012-11-16 15:50:01 +0200757static void gbam2bam_disconnect_work(struct work_struct *w)
758{
Shimrit Malichi419fdac2013-01-16 14:35:31 +0200759 struct gbam_port *port =
760 container_of(w, struct gbam_port, disconnect_w);
Ofir Cohenfdecb602012-11-16 15:50:01 +0200761 struct bam_ch_info *d = &port->data_ch;
762 int ret;
763
764 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200765 ret = usb_bam_disconnect_ipa(&d->ipa_params);
Ofir Cohenfdecb602012-11-16 15:50:01 +0200766 if (ret)
767 pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
768 __func__, ret);
Lena Salman7c0e9792013-06-20 14:36:54 +0300769 teth_bridge_disconnect();
Ofir Cohenfdecb602012-11-16 15:50:01 +0200770 }
771}
772
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700773static void gbam_connect_work(struct work_struct *w)
774{
775 struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
776 struct bam_ch_info *d = &port->data_ch;
777 int ret;
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800778 unsigned long flags;
779
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530780 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -0800781 spin_lock(&port->port_lock_dl);
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800782 if (!port->port_usb) {
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -0800783 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530784 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Vamsi Krishna1ad076d2011-11-10 15:03:30 -0800785 return;
786 }
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -0800787 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +0530788 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700789
Jack Phameffd4ae2011-08-03 16:49:36 -0700790 if (!test_bit(BAM_CH_READY, &d->flags))
791 return;
792
Jeff Hugo1c4531c2011-08-02 14:55:37 -0600793 ret = msm_bam_dmux_open(d->id, port, gbam_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794 if (ret) {
795 pr_err("%s: unable open bam ch:%d err:%d\n",
796 __func__, d->id, ret);
797 return;
798 }
Jack Phameffd4ae2011-08-03 16:49:36 -0700799 set_bit(BAM_CH_OPENED, &d->flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700800
801 gbam_start_io(port);
802
803 pr_debug("%s: done\n", __func__);
804}
805
Ofir Cohena1c2a872011-12-14 10:26:34 +0200806static void gbam2bam_connect_work(struct work_struct *w)
Jack Phameffd4ae2011-08-03 16:49:36 -0700807{
Ofir Cohena1c2a872011-12-14 10:26:34 +0200808 struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
Shimrit Malichi085a3d22013-03-10 10:30:08 +0200809 struct teth_bridge_connect_params connect_params;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200810 struct bam_ch_info *d = &port->data_ch;
811 u32 sps_params;
Shimrit Malichi085a3d22013-03-10 10:30:08 +0200812 ipa_notify_cb usb_notify_cb;
813 void *priv;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200814 int ret;
Shimrit Malichi841fe172013-04-23 11:52:19 +0300815 unsigned long flags;
Jack Phameffd4ae2011-08-03 16:49:36 -0700816
Ofir Cohenfdecb602012-11-16 15:50:01 +0200817 if (d->trans == USB_GADGET_XPORT_BAM2BAM) {
Bar Weiner189bb3c2013-06-09 14:24:56 +0300818 usb_bam_reset_complete();
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200819 ret = usb_bam_connect(d->src_connection_idx, &d->src_pipe_idx);
Ofir Cohenfdecb602012-11-16 15:50:01 +0200820 if (ret) {
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200821 pr_err("%s: usb_bam_connect (src) failed: err:%d\n",
822 __func__, ret);
823 return;
824 }
825 ret = usb_bam_connect(d->dst_connection_idx, &d->dst_pipe_idx);
826 if (ret) {
827 pr_err("%s: usb_bam_connect (dst) failed: err:%d\n",
Ofir Cohenfdecb602012-11-16 15:50:01 +0200828 __func__, ret);
829 return;
830 }
831 } else if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
Shimrit Malichi085a3d22013-03-10 10:30:08 +0200832 ret = teth_bridge_init(&usb_notify_cb, &priv);
833 if (ret) {
834 pr_err("%s:teth_bridge_init() failed\n", __func__);
835 return;
836 }
837 d->ipa_params.notify = usb_notify_cb;
838 d->ipa_params.priv = priv;
839 d->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
840
Lena Salmanabde35d2013-04-25 15:29:43 +0300841 d->ipa_params.client = IPA_CLIENT_USB_PROD;
842 d->ipa_params.dir = USB_TO_PEER_PERIPHERAL;
Ofir Cohen77848d62012-12-05 13:16:10 +0200843 ret = usb_bam_connect_ipa(&d->ipa_params);
Ofir Cohenfdecb602012-11-16 15:50:01 +0200844 if (ret) {
845 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
846 __func__, ret);
847 return;
848 }
849
Lena Salmanabde35d2013-04-25 15:29:43 +0300850 d->ipa_params.client = IPA_CLIENT_USB_CONS;
851 d->ipa_params.dir = PEER_PERIPHERAL_TO_USB;
Ofir Cohen77848d62012-12-05 13:16:10 +0200852 ret = usb_bam_connect_ipa(&d->ipa_params);
Ofir Cohenfdecb602012-11-16 15:50:01 +0200853 if (ret) {
854 pr_err("%s: usb_bam_connect_ipa failed: err:%d\n",
855 __func__, ret);
856 return;
857 }
Shimrit Malichi085a3d22013-03-10 10:30:08 +0200858
859 connect_params.ipa_usb_pipe_hdl = d->ipa_params.prod_clnt_hdl;
860 connect_params.usb_ipa_pipe_hdl = d->ipa_params.cons_clnt_hdl;
861 connect_params.tethering_mode = TETH_TETHERING_MODE_RMNET;
862 ret = teth_bridge_connect(&connect_params);
863 if (ret) {
864 pr_err("%s:teth_bridge_connect() failed\n", __func__);
865 return;
866 }
Ofir Cohena1c2a872011-12-14 10:26:34 +0200867 }
Jack Phameffd4ae2011-08-03 16:49:36 -0700868
Shimrit Malichi841fe172013-04-23 11:52:19 +0300869 spin_lock_irqsave(&port->port_lock_ul, flags);
870 spin_lock(&port->port_lock_dl);
871 if (!port->port_usb) {
872 pr_debug("%s: usb cable is disconnected, exiting\n", __func__);
873 spin_unlock(&port->port_lock_dl);
874 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200875 return;
Shimrit Malichi841fe172013-04-23 11:52:19 +0300876 }
877 d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_ATOMIC);
878 if (!d->rx_req) {
879 spin_unlock(&port->port_lock_dl);
880 spin_unlock_irqrestore(&port->port_lock_ul, flags);
881 pr_err("%s: out of memory\n", __func__);
882 return;
883 }
Jack Phameffd4ae2011-08-03 16:49:36 -0700884
Ofir Cohena1c2a872011-12-14 10:26:34 +0200885 d->rx_req->context = port;
886 d->rx_req->complete = gbam_endless_rx_complete;
887 d->rx_req->length = 0;
Bar Weiner8475d632013-06-13 11:29:49 +0300888 d->rx_req->no_interrupt = 1;
Ido Shayevitzd1cb16c2012-03-28 18:57:47 +0200889 sps_params = (MSM_SPS_MODE | d->src_pipe_idx |
890 MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200891 d->rx_req->udc_priv = sps_params;
Shimrit Malichi841fe172013-04-23 11:52:19 +0300892
893 d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_ATOMIC);
894 spin_unlock(&port->port_lock_dl);
895 spin_unlock_irqrestore(&port->port_lock_ul, flags);
896 if (!d->tx_req) {
897 pr_err("%s: out of memory\n", __func__);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200898 return;
Shimrit Malichi841fe172013-04-23 11:52:19 +0300899 }
Jack Phameffd4ae2011-08-03 16:49:36 -0700900
Ofir Cohena1c2a872011-12-14 10:26:34 +0200901 d->tx_req->context = port;
902 d->tx_req->complete = gbam_endless_tx_complete;
903 d->tx_req->length = 0;
Bar Weiner8475d632013-06-13 11:29:49 +0300904 d->tx_req->no_interrupt = 1;
Ido Shayevitzd1cb16c2012-03-28 18:57:47 +0200905 sps_params = (MSM_SPS_MODE | d->dst_pipe_idx |
906 MSM_VENDOR_ID) & ~MSM_IS_FINITE_TRANSFER;
Ofir Cohena1c2a872011-12-14 10:26:34 +0200907 d->tx_req->udc_priv = sps_params;
Jack Phameffd4ae2011-08-03 16:49:36 -0700908
Ofir Cohena1c2a872011-12-14 10:26:34 +0200909 /* queue in & out requests */
910 gbam_start_endless_rx(port);
911 gbam_start_endless_tx(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700912
Amit Blay94525352012-12-24 11:23:27 +0200913 if (d->trans == USB_GADGET_XPORT_BAM2BAM && port->port_num == 0) {
914 /* Register for peer reset callback */
Shimrit Malichidbf43d72013-03-16 03:32:27 +0200915 usb_bam_register_peer_reset_cb(gbam_peer_reset_cb, port);
Amit Blay94525352012-12-24 11:23:27 +0200916
917 ret = usb_bam_client_ready(true);
918 if (ret) {
919 pr_err("%s: usb_bam_client_ready failed: err:%d\n",
920 __func__, ret);
921 return;
922 }
923 }
924
Ofir Cohena1c2a872011-12-14 10:26:34 +0200925 pr_debug("%s: done\n", __func__);
Jack Phameffd4ae2011-08-03 16:49:36 -0700926}
927
Bar Weiner84e1ba02013-07-18 09:08:03 +0300928static int gbam_wake_cb(void *param)
929{
930 struct gbam_port *port = (struct gbam_port *)param;
931 struct bam_ch_info *d;
932 struct f_rmnet *dev;
933
934 dev = port_to_rmnet(port->gr);
935 d = &port->data_ch;
936
937 pr_debug("%s: woken up by peer\n", __func__);
938
939 return usb_gadget_wakeup(dev->cdev->gadget);
940}
941
942static void gbam2bam_suspend_work(struct work_struct *w)
943{
944 struct gbam_port *port = container_of(w, struct gbam_port, suspend_w);
945 struct bam_ch_info *d = &port->data_ch;
946
947 pr_debug("%s: suspend work started\n", __func__);
948
949 usb_bam_register_wake_cb(d->dst_connection_idx, gbam_wake_cb, port);
950 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
951 usb_bam_register_start_stop_cbs(gbam_start, gbam_stop, port);
952 usb_bam_suspend(&d->ipa_params);
953 }
954}
955
956static void gbam2bam_resume_work(struct work_struct *w)
957{
958 struct gbam_port *port = container_of(w, struct gbam_port, resume_w);
959 struct bam_ch_info *d = &port->data_ch;
960
961 pr_debug("%s: resume work started\n", __func__);
962
963 usb_bam_register_wake_cb(d->dst_connection_idx, NULL, NULL);
964 if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA)
965 usb_bam_resume(&d->ipa_params);
966}
967
Amit Blay94525352012-12-24 11:23:27 +0200968static int gbam_peer_reset_cb(void *param)
969{
970 struct gbam_port *port = (struct gbam_port *)param;
971 struct bam_ch_info *d;
972 struct f_rmnet *dev;
973 struct usb_gadget *gadget;
974 int ret;
975 bool reenable_eps = false;
976
977 dev = port_to_rmnet(port->gr);
978 d = &port->data_ch;
979
980 gadget = dev->cdev->gadget;
981
982 pr_debug("%s: reset by peer\n", __func__);
983
984 /* Disable the relevant EPs if currently EPs are enabled */
985 if (port->port_usb && port->port_usb->in &&
986 port->port_usb->in->driver_data) {
987 usb_ep_disable(port->port_usb->out);
988 usb_ep_disable(port->port_usb->in);
989
990 port->port_usb->in->driver_data = NULL;
991 port->port_usb->out->driver_data = NULL;
992 reenable_eps = true;
993 }
994
995 /* Disable BAM */
996 msm_hw_bam_disable(1);
997
998 /* Reset BAM */
Bar Weiner189bb3c2013-06-09 14:24:56 +0300999 ret = usb_bam_a2_reset(0);
Amit Blay94525352012-12-24 11:23:27 +02001000 if (ret) {
1001 pr_err("%s: BAM reset failed %d\n", __func__, ret);
1002 goto reenable_eps;
1003 }
1004
1005 /* Enable BAM */
1006 msm_hw_bam_disable(0);
1007
1008reenable_eps:
1009 /* Re-Enable the relevant EPs, if EPs were originally enabled */
1010 if (reenable_eps) {
1011 ret = usb_ep_enable(port->port_usb->in);
1012 if (ret) {
1013 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
1014 __func__, port->port_usb->in);
1015 return ret;
1016 }
1017 port->port_usb->in->driver_data = port;
1018
1019 ret = usb_ep_enable(port->port_usb->out);
1020 if (ret) {
1021 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
1022 __func__, port->port_usb->out);
1023 port->port_usb->in->driver_data = 0;
1024 return ret;
1025 }
1026 port->port_usb->out->driver_data = port;
1027
1028 gbam_start_endless_rx(port);
1029 gbam_start_endless_tx(port);
1030 }
1031
1032 /* Unregister the peer reset callback */
1033 if (d->trans == USB_GADGET_XPORT_BAM2BAM && port->port_num == 0)
Shimrit Malichidbf43d72013-03-16 03:32:27 +02001034 usb_bam_register_peer_reset_cb(NULL, NULL);
Amit Blay94525352012-12-24 11:23:27 +02001035
1036 return 0;
1037}
1038
Jack Phameffd4ae2011-08-03 16:49:36 -07001039/* BAM data channel ready, allow attempt to open */
1040static int gbam_data_ch_probe(struct platform_device *pdev)
1041{
1042 struct gbam_port *port;
1043 struct bam_ch_info *d;
1044 int i;
1045 unsigned long flags;
1046
1047 pr_debug("%s: name:%s\n", __func__, pdev->name);
1048
1049 for (i = 0; i < n_bam_ports; i++) {
1050 port = bam_ports[i].port;
1051 d = &port->data_ch;
1052
1053 if (!strncmp(bam_ch_names[i], pdev->name,
1054 BAM_DMUX_CH_NAME_MAX_LEN)) {
1055 set_bit(BAM_CH_READY, &d->flags);
1056
1057 /* if usb is online, try opening bam_ch */
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301058 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001059 spin_lock(&port->port_lock_dl);
Jack Phameffd4ae2011-08-03 16:49:36 -07001060 if (port->port_usb)
1061 queue_work(gbam_wq, &port->connect_w);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001062 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301063 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -07001064
1065 break;
1066 }
1067 }
1068
1069 return 0;
1070}
1071
1072/* BAM data channel went inactive, so close it */
1073static int gbam_data_ch_remove(struct platform_device *pdev)
1074{
1075 struct gbam_port *port;
1076 struct bam_ch_info *d;
1077 struct usb_ep *ep_in = NULL;
1078 struct usb_ep *ep_out = NULL;
1079 unsigned long flags;
1080 int i;
1081
1082 pr_debug("%s: name:%s\n", __func__, pdev->name);
1083
1084 for (i = 0; i < n_bam_ports; i++) {
1085 if (!strncmp(bam_ch_names[i], pdev->name,
1086 BAM_DMUX_CH_NAME_MAX_LEN)) {
1087 port = bam_ports[i].port;
1088 d = &port->data_ch;
1089
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301090 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001091 spin_lock(&port->port_lock_dl);
Jack Phameffd4ae2011-08-03 16:49:36 -07001092 if (port->port_usb) {
1093 ep_in = port->port_usb->in;
1094 ep_out = port->port_usb->out;
1095 }
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001096 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301097 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Jack Phameffd4ae2011-08-03 16:49:36 -07001098
1099 if (ep_in)
1100 usb_ep_fifo_flush(ep_in);
1101 if (ep_out)
1102 usb_ep_fifo_flush(ep_out);
1103
1104 gbam_free_buffers(port);
1105
1106 msm_bam_dmux_close(d->id);
1107
Vamsi Krishna7658bd12012-01-13 10:32:00 -08001108 /* bam dmux will free all pending skbs */
1109 d->pending_with_bam = 0;
1110
Jack Phameffd4ae2011-08-03 16:49:36 -07001111 clear_bit(BAM_CH_READY, &d->flags);
1112 clear_bit(BAM_CH_OPENED, &d->flags);
1113 }
1114 }
1115
1116 return 0;
1117}
1118
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001119static void gbam_port_free(int portno)
1120{
1121 struct gbam_port *port = bam_ports[portno].port;
Jack Phameffd4ae2011-08-03 16:49:36 -07001122 struct platform_driver *pdrv = &bam_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001123
Jack Phameffd4ae2011-08-03 16:49:36 -07001124 if (port) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001125 kfree(port);
Jack Phameffd4ae2011-08-03 16:49:36 -07001126 platform_driver_unregister(pdrv);
1127 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001128}
1129
Ofir Cohena1c2a872011-12-14 10:26:34 +02001130static void gbam2bam_port_free(int portno)
1131{
1132 struct gbam_port *port = bam2bam_ports[portno];
1133
1134 kfree(port);
1135}
1136
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001137static int gbam_port_alloc(int portno)
1138{
1139 struct gbam_port *port;
1140 struct bam_ch_info *d;
Jack Phameffd4ae2011-08-03 16:49:36 -07001141 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001142
1143 port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
1144 if (!port)
1145 return -ENOMEM;
1146
1147 port->port_num = portno;
1148
1149 /* port initialization */
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301150 spin_lock_init(&port->port_lock_ul);
1151 spin_lock_init(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001152 INIT_WORK(&port->connect_w, gbam_connect_work);
Vamsi Krishna1ad076d2011-11-10 15:03:30 -08001153 INIT_WORK(&port->disconnect_w, gbam_disconnect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001154
1155 /* data ch */
1156 d = &port->data_ch;
1157 d->port = port;
1158 INIT_LIST_HEAD(&d->tx_idle);
1159 INIT_LIST_HEAD(&d->rx_idle);
1160 INIT_WORK(&d->write_tobam_w, gbam_data_write_tobam);
Chiranjeevi Velempatie5105922012-01-19 12:25:26 +05301161 INIT_WORK(&d->write_tohost_w, gbam_write_data_tohost_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001162 skb_queue_head_init(&d->tx_skb_q);
1163 skb_queue_head_init(&d->rx_skb_q);
1164 d->id = bam_ch_ids[portno];
1165
1166 bam_ports[portno].port = port;
1167
Jack Phameffd4ae2011-08-03 16:49:36 -07001168 pdrv = &bam_ports[portno].pdrv;
1169 pdrv->probe = gbam_data_ch_probe;
1170 pdrv->remove = gbam_data_ch_remove;
1171 pdrv->driver.name = bam_ch_names[portno];
1172 pdrv->driver.owner = THIS_MODULE;
1173
1174 platform_driver_register(pdrv);
Ofir Cohena1c2a872011-12-14 10:26:34 +02001175 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
1176
1177 return 0;
1178}
1179
1180static int gbam2bam_port_alloc(int portno)
1181{
1182 struct gbam_port *port;
1183 struct bam_ch_info *d;
1184
1185 port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
1186 if (!port)
1187 return -ENOMEM;
1188
1189 port->port_num = portno;
1190
1191 /* port initialization */
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301192 spin_lock_init(&port->port_lock_ul);
1193 spin_lock_init(&port->port_lock_dl);
Ofir Cohena1c2a872011-12-14 10:26:34 +02001194
1195 INIT_WORK(&port->connect_w, gbam2bam_connect_work);
Ofir Cohenfdecb602012-11-16 15:50:01 +02001196 INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
Bar Weiner84e1ba02013-07-18 09:08:03 +03001197 INIT_WORK(&port->suspend_w, gbam2bam_suspend_work);
1198 INIT_WORK(&port->resume_w, gbam2bam_resume_work);
Ofir Cohena1c2a872011-12-14 10:26:34 +02001199
1200 /* data ch */
1201 d = &port->data_ch;
1202 d->port = port;
1203 bam2bam_ports[portno] = port;
Jack Phameffd4ae2011-08-03 16:49:36 -07001204
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001205 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
1206
1207 return 0;
1208}
1209
1210#if defined(CONFIG_DEBUG_FS)
1211#define DEBUG_BUF_SIZE 1024
1212static ssize_t gbam_read_stats(struct file *file, char __user *ubuf,
1213 size_t count, loff_t *ppos)
1214{
1215 struct gbam_port *port;
1216 struct bam_ch_info *d;
1217 char *buf;
1218 unsigned long flags;
1219 int ret;
1220 int i;
1221 int temp = 0;
1222
1223 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
1224 if (!buf)
1225 return -ENOMEM;
1226
1227 for (i = 0; i < n_bam_ports; i++) {
1228 port = bam_ports[i].port;
1229 if (!port)
1230 continue;
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301231 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001232 spin_lock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001233
1234 d = &port->data_ch;
1235
1236 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
1237 "#PORT:%d port:%p data_ch:%p#\n"
1238 "dpkts_to_usbhost: %lu\n"
1239 "dpkts_to_modem: %lu\n"
1240 "dpkts_pwith_bam: %u\n"
1241 "to_usbhost_dcnt: %u\n"
1242 "tomodem__dcnt: %u\n"
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +05301243 "rx_flow_control_disable_count: %u\n"
1244 "rx_flow_control_enable_count: %u\n"
1245 "rx_flow_control_triggered: %u\n"
1246 "max_num_pkts_pending_with_bam: %u\n"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001247 "tx_buf_len: %u\n"
Vamsi Krishna84579552011-11-09 15:33:22 -08001248 "rx_buf_len: %u\n"
Jack Phameffd4ae2011-08-03 16:49:36 -07001249 "data_ch_open: %d\n"
1250 "data_ch_ready: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001251 i, port, &port->data_ch,
1252 d->to_host, d->to_modem,
1253 d->pending_with_bam,
1254 d->tohost_drp_cnt, d->tomodem_drp_cnt,
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +05301255 d->rx_flow_control_disable,
1256 d->rx_flow_control_enable,
1257 d->rx_flow_control_triggered,
1258 d->max_num_pkts_pending_with_bam,
Vamsi Krishna84579552011-11-09 15:33:22 -08001259 d->tx_skb_q.qlen, d->rx_skb_q.qlen,
Jack Phameffd4ae2011-08-03 16:49:36 -07001260 test_bit(BAM_CH_OPENED, &d->flags),
1261 test_bit(BAM_CH_READY, &d->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001262
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001263 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301264 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001265 }
1266
1267 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
1268
1269 kfree(buf);
1270
1271 return ret;
1272}
1273
1274static ssize_t gbam_reset_stats(struct file *file, const char __user *buf,
1275 size_t count, loff_t *ppos)
1276{
1277 struct gbam_port *port;
1278 struct bam_ch_info *d;
1279 int i;
1280 unsigned long flags;
1281
1282 for (i = 0; i < n_bam_ports; i++) {
1283 port = bam_ports[i].port;
1284 if (!port)
1285 continue;
1286
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301287 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001288 spin_lock(&port->port_lock_dl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001289
1290 d = &port->data_ch;
1291
1292 d->to_host = 0;
1293 d->to_modem = 0;
1294 d->pending_with_bam = 0;
1295 d->tohost_drp_cnt = 0;
1296 d->tomodem_drp_cnt = 0;
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +05301297 d->rx_flow_control_disable = 0;
1298 d->rx_flow_control_enable = 0;
1299 d->rx_flow_control_triggered = 0;
1300 d->max_num_pkts_pending_with_bam = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001301
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001302 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301303 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001304 }
1305 return count;
1306}
1307
1308const struct file_operations gbam_stats_ops = {
1309 .read = gbam_read_stats,
1310 .write = gbam_reset_stats,
1311};
1312
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301313struct dentry *gbam_dent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001314static void gbam_debugfs_init(void)
1315{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001316 struct dentry *dfile;
1317
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301318 gbam_dent = debugfs_create_dir("usb_rmnet", 0);
1319 if (!gbam_dent || IS_ERR(gbam_dent))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001320 return;
1321
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301322 dfile = debugfs_create_file("status", 0444, gbam_dent, 0,
1323 &gbam_stats_ops);
1324 if (!dfile || IS_ERR(dfile)) {
1325 debugfs_remove(gbam_dent);
1326 gbam_dent = NULL;
1327 return;
1328 }
1329}
1330static void gbam_debugfs_remove(void)
1331{
1332 debugfs_remove_recursive(gbam_dent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001333}
1334#else
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301335static inline void gbam_debugfs_init(void) {}
1336static inline void gbam_debugfs_remove(void) {}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001337#endif
1338
Ofir Cohen77848d62012-12-05 13:16:10 +02001339void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001340{
1341 struct gbam_port *port;
1342 unsigned long flags;
1343 struct bam_ch_info *d;
1344
1345 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
1346
Ofir Cohena1c2a872011-12-14 10:26:34 +02001347 if (trans == USB_GADGET_XPORT_BAM &&
1348 port_num >= n_bam_ports) {
1349 pr_err("%s: invalid bam portno#%d\n",
1350 __func__, port_num);
1351 return;
1352 }
1353
Ofir Cohenfdecb602012-11-16 15:50:01 +02001354 if ((trans == USB_GADGET_XPORT_BAM2BAM ||
1355 trans == USB_GADGET_XPORT_BAM2BAM_IPA) &&
Ofir Cohena1c2a872011-12-14 10:26:34 +02001356 port_num >= n_bam2bam_ports) {
1357 pr_err("%s: invalid bam2bam portno#%d\n",
1358 __func__, port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001359 return;
1360 }
1361
1362 if (!gr) {
1363 pr_err("%s: grmnet port is null\n", __func__);
1364 return;
1365 }
Ofir Cohena1c2a872011-12-14 10:26:34 +02001366 if (trans == USB_GADGET_XPORT_BAM)
1367 port = bam_ports[port_num].port;
1368 else
1369 port = bam2bam_ports[port_num];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001370
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001371 d = &port->data_ch;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001372 port->gr = gr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001373
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001374 if (trans == USB_GADGET_XPORT_BAM)
Ofir Cohena1c2a872011-12-14 10:26:34 +02001375 gbam_free_buffers(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001376
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301377 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001378 spin_lock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301379 port->port_usb = 0;
Vijayavardhan Vennapusa08c31252011-12-21 13:02:49 +05301380 n_tx_req_queued = 0;
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001381 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301382 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001383
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001384 /* disable endpoints */
1385 usb_ep_disable(gr->out);
1386 usb_ep_disable(gr->in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001387
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001388 gr->in->driver_data = NULL;
1389 gr->out->driver_data = NULL;
1390
Ofir Cohenfdecb602012-11-16 15:50:01 +02001391 if (trans == USB_GADGET_XPORT_BAM ||
1392 trans == USB_GADGET_XPORT_BAM2BAM_IPA)
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001393 queue_work(gbam_wq, &port->disconnect_w);
Amit Blay94525352012-12-24 11:23:27 +02001394 else if (trans == USB_GADGET_XPORT_BAM2BAM) {
1395 if (port_num == 0) {
1396 if (usb_bam_client_ready(false)) {
1397 pr_err("%s: usb_bam_client_ready failed\n",
1398 __func__);
1399 }
1400 }
1401 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001402}
1403
Ofir Cohena1c2a872011-12-14 10:26:34 +02001404int gbam_connect(struct grmnet *gr, u8 port_num,
Shimrit Malichidbf43d72013-03-16 03:32:27 +02001405 enum transport_type trans, u8 src_connection_idx,
1406 u8 dst_connection_idx)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001407{
1408 struct gbam_port *port;
1409 struct bam_ch_info *d;
1410 int ret;
1411 unsigned long flags;
1412
1413 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
1414
Ofir Cohena1c2a872011-12-14 10:26:34 +02001415 if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) {
1416 pr_err("%s: invalid portno#%d\n", __func__, port_num);
1417 return -ENODEV;
1418 }
1419
Ofir Cohenfdecb602012-11-16 15:50:01 +02001420 if ((trans == USB_GADGET_XPORT_BAM2BAM ||
1421 trans == USB_GADGET_XPORT_BAM2BAM_IPA)
1422 && port_num >= n_bam2bam_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001423 pr_err("%s: invalid portno#%d\n", __func__, port_num);
1424 return -ENODEV;
1425 }
1426
1427 if (!gr) {
1428 pr_err("%s: grmnet port is null\n", __func__);
1429 return -ENODEV;
1430 }
1431
Ofir Cohena1c2a872011-12-14 10:26:34 +02001432 if (trans == USB_GADGET_XPORT_BAM)
1433 port = bam_ports[port_num].port;
1434 else
1435 port = bam2bam_ports[port_num];
1436
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001437 d = &port->data_ch;
1438
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001439 ret = usb_ep_enable(gr->in);
1440 if (ret) {
1441 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
1442 __func__, gr->in);
1443 return ret;
1444 }
1445 gr->in->driver_data = port;
Ofir Cohen4da266f2012-01-03 10:19:29 +02001446
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001447 ret = usb_ep_enable(gr->out);
1448 if (ret) {
1449 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
1450 __func__, gr->out);
1451 gr->in->driver_data = 0;
1452 return ret;
1453 }
1454 gr->out->driver_data = port;
Ofir Cohen4da266f2012-01-03 10:19:29 +02001455
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301456 spin_lock_irqsave(&port->port_lock_ul, flags);
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001457 spin_lock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301458 port->port_usb = gr;
Ofir Cohen4da266f2012-01-03 10:19:29 +02001459
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001460 if (trans == USB_GADGET_XPORT_BAM) {
Ofir Cohena1c2a872011-12-14 10:26:34 +02001461 d->to_host = 0;
1462 d->to_modem = 0;
1463 d->pending_with_bam = 0;
1464 d->tohost_drp_cnt = 0;
1465 d->tomodem_drp_cnt = 0;
Vijayavardhan Vennapusa2001b032014-01-07 13:11:51 +05301466 d->rx_flow_control_disable = 0;
1467 d->rx_flow_control_enable = 0;
1468 d->rx_flow_control_triggered = 0;
1469 d->max_num_pkts_pending_with_bam = 0;
Ido Shayevitzeb8d8312012-09-10 11:13:11 +03001470 }
1471
Jeff Ohlstein6f8c5fc2012-01-17 13:08:39 -08001472 spin_unlock(&port->port_lock_dl);
Vijayavardhan Vennapusaa8808532012-01-09 15:24:02 +05301473 spin_unlock_irqrestore(&port->port_lock_ul, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001474
Ofir Cohen4da266f2012-01-03 10:19:29 +02001475 if (trans == USB_GADGET_XPORT_BAM2BAM) {
1476 port->gr = gr;
Shimrit Malichidbf43d72013-03-16 03:32:27 +02001477 d->src_connection_idx = src_connection_idx;
1478 d->dst_connection_idx = dst_connection_idx;
Ofir Cohenfdecb602012-11-16 15:50:01 +02001479 } else if (trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
Ofir Cohenfdecb602012-11-16 15:50:01 +02001480 port->gr = gr;
Ofir Cohen77848d62012-12-05 13:16:10 +02001481 d->ipa_params.src_pipe = &(d->src_pipe_idx);
1482 d->ipa_params.dst_pipe = &(d->dst_pipe_idx);
Shimrit Malichidbf43d72013-03-16 03:32:27 +02001483 d->ipa_params.src_idx = src_connection_idx;
1484 d->ipa_params.dst_idx = dst_connection_idx;
Ofir Cohen4da266f2012-01-03 10:19:29 +02001485 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001486
Ofir Cohenfdecb602012-11-16 15:50:01 +02001487 d->trans = trans;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001488 queue_work(gbam_wq, &port->connect_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001489 return 0;
1490}
1491
Ofir Cohena1c2a872011-12-14 10:26:34 +02001492int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001493{
1494 int i;
1495 int ret;
1496
Ofir Cohena1c2a872011-12-14 10:26:34 +02001497 pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n",
1498 __func__, no_bam_port, no_bam2bam_port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001499
Ofir Cohena1c2a872011-12-14 10:26:34 +02001500 if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS
1501 || no_bam2bam_port > BAM2BAM_N_PORTS) {
1502 pr_err("%s: Invalid num of ports count:%d,%d\n",
1503 __func__, no_bam_port, no_bam2bam_port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001504 return -EINVAL;
1505 }
1506
1507 gbam_wq = alloc_workqueue("k_gbam", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
1508 if (!gbam_wq) {
1509 pr_err("%s: Unable to create workqueue gbam_wq\n",
1510 __func__);
1511 return -ENOMEM;
1512 }
1513
Ofir Cohena1c2a872011-12-14 10:26:34 +02001514 for (i = 0; i < no_bam_port; i++) {
Manu Gautamd59b5d32011-09-09 14:47:08 +05301515 n_bam_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001516 ret = gbam_port_alloc(i);
1517 if (ret) {
Manu Gautamd59b5d32011-09-09 14:47:08 +05301518 n_bam_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001519 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
1520 goto free_bam_ports;
1521 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001522 }
1523
Ofir Cohena1c2a872011-12-14 10:26:34 +02001524 for (i = 0; i < no_bam2bam_port; i++) {
1525 n_bam2bam_ports++;
1526 ret = gbam2bam_port_alloc(i);
1527 if (ret) {
1528 n_bam2bam_ports--;
1529 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
1530 goto free_bam_ports;
1531 }
1532 }
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301533
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001534 gbam_debugfs_init();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535 return 0;
Ofir Cohena1c2a872011-12-14 10:26:34 +02001536
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001537free_bam_ports:
1538 for (i = 0; i < n_bam_ports; i++)
1539 gbam_port_free(i);
Ofir Cohena1c2a872011-12-14 10:26:34 +02001540 for (i = 0; i < n_bam2bam_ports; i++)
1541 gbam2bam_port_free(i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001542 destroy_workqueue(gbam_wq);
1543
1544 return ret;
1545}
Amit Blaye5bb35e2012-05-08 20:38:20 +03001546
Tarun Gupta44ad2bb2013-09-30 18:01:58 +05301547void gbam_cleanup(void)
1548{
1549 gbam_debugfs_remove();
1550}
1551
Amit Blaye5bb35e2012-05-08 20:38:20 +03001552void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
1553{
1554 struct gbam_port *port;
1555 struct bam_ch_info *d;
1556
Ofir Cohenfdecb602012-11-16 15:50:01 +02001557 if (trans != USB_GADGET_XPORT_BAM2BAM &&
1558 trans != USB_GADGET_XPORT_BAM2BAM_IPA)
Amit Blaye5bb35e2012-05-08 20:38:20 +03001559 return;
1560
1561 port = bam2bam_ports[port_num];
1562 d = &port->data_ch;
1563
1564 pr_debug("%s: suspended port %d\n", __func__, port_num);
1565
Bar Weiner84e1ba02013-07-18 09:08:03 +03001566 queue_work(gbam_wq, &port->suspend_w);
Amit Blaye5bb35e2012-05-08 20:38:20 +03001567}
1568
1569void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
1570{
1571 struct gbam_port *port;
1572 struct bam_ch_info *d;
1573
Ofir Cohenfdecb602012-11-16 15:50:01 +02001574 if (trans != USB_GADGET_XPORT_BAM2BAM &&
1575 trans != USB_GADGET_XPORT_BAM2BAM_IPA)
Amit Blaye5bb35e2012-05-08 20:38:20 +03001576 return;
1577
1578 port = bam2bam_ports[port_num];
1579 d = &port->data_ch;
1580
1581 pr_debug("%s: resumed port %d\n", __func__, port_num);
1582
Bar Weiner84e1ba02013-07-18 09:08:03 +03001583 queue_work(gbam_wq, &port->resume_w);
Amit Blaye5bb35e2012-05-08 20:38:20 +03001584}