blob: 4b0266575489b0b01a3b99effa7836f4106d15a6 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * u_smd.c - utilities for USB gadget serial over smd
3 *
Duy Truong790f06d2013-02-13 16:38:12 -08004 * Copyright (c) 2011, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 *
6 * This code also borrows from drivers/usb/gadget/u_serial.c, which is
7 * Copyright (C) 2000 - 2003 Al Borchers (alborchers@steinerpoint.com)
8 * Copyright (C) 2008 David Brownell
9 * Copyright (C) 2008 by Nokia Corporation
10 * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
11 * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 and
15 * only version 2 as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22#include <linux/kernel.h>
23#include <linux/interrupt.h>
24#include <linux/device.h>
25#include <linux/delay.h>
26#include <linux/slab.h>
27#include <linux/termios.h>
28#include <mach/msm_smd.h>
29#include <linux/debugfs.h>
30
31#include "u_serial.h"
32
33#define SMD_RX_QUEUE_SIZE 8
34#define SMD_RX_BUF_SIZE 2048
35
36#define SMD_TX_QUEUE_SIZE 8
37#define SMD_TX_BUF_SIZE 2048
38
39static struct workqueue_struct *gsmd_wq;
40
41#define SMD_N_PORTS 2
42#define CH_OPENED 0
Hemant Kumard3fb9bb2011-07-12 17:20:16 -070043#define CH_READY 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044struct smd_port_info {
45 struct smd_channel *ch;
46 char *name;
47 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048};
49
50struct smd_port_info smd_pi[SMD_N_PORTS] = {
51 {
52 .name = "DS",
53 },
54 {
55 .name = "UNUSED",
56 },
57};
58
59struct gsmd_port {
60 unsigned port_num;
61 spinlock_t port_lock;
62
63 unsigned n_read;
64 struct list_head read_pool;
65 struct list_head read_queue;
66 struct work_struct push;
67
68 struct list_head write_pool;
69 struct work_struct pull;
70
71 struct gserial *port_usb;
72
73 struct smd_port_info *pi;
Eric Holmberge72b2d42012-01-23 13:45:45 -070074 struct delayed_work connect_work;
Manu Gautam7b561ea2012-11-09 17:28:01 +053075 struct work_struct disconnect_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076
77 /* At present, smd does not notify
78 * control bit change info from modem
79 */
80 struct work_struct update_modem_ctrl_sig;
81
82#define SMD_ACM_CTRL_DTR 0x01
83#define SMD_ACM_CTRL_RTS 0x02
84 unsigned cbits_to_modem;
85
86#define SMD_ACM_CTRL_DCD 0x01
87#define SMD_ACM_CTRL_DSR 0x02
88#define SMD_ACM_CTRL_BRK 0x04
89#define SMD_ACM_CTRL_RI 0x08
90 unsigned cbits_to_laptop;
91
92 /* pkt counters */
93 unsigned long nbytes_tomodem;
94 unsigned long nbytes_tolaptop;
95};
96
97static struct smd_portmaster {
98 struct mutex lock;
99 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700100 struct platform_driver pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101} smd_ports[SMD_N_PORTS];
102static unsigned n_smd_ports;
103
104static void gsmd_free_req(struct usb_ep *ep, struct usb_request *req)
105{
106 kfree(req->buf);
107 usb_ep_free_request(ep, req);
108}
109
110static void gsmd_free_requests(struct usb_ep *ep, struct list_head *head)
111{
112 struct usb_request *req;
113
114 while (!list_empty(head)) {
115 req = list_entry(head->next, struct usb_request, list);
116 list_del(&req->list);
117 gsmd_free_req(ep, req);
118 }
119}
120
121static struct usb_request *
122gsmd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
123{
124 struct usb_request *req;
125
126 req = usb_ep_alloc_request(ep, flags);
127 if (!req) {
128 pr_err("%s: usb alloc request failed\n", __func__);
129 return 0;
130 }
131
132 req->length = len;
133 req->buf = kmalloc(len, flags);
134 if (!req->buf) {
135 pr_err("%s: request buf allocation failed\n", __func__);
136 usb_ep_free_request(ep, req);
137 return 0;
138 }
139
140 return req;
141}
142
143static int gsmd_alloc_requests(struct usb_ep *ep, struct list_head *head,
144 int num, int size,
145 void (*cb)(struct usb_ep *ep, struct usb_request *))
146{
147 int i;
148 struct usb_request *req;
149
150 pr_debug("%s: ep:%p head:%p num:%d size:%d cb:%p", __func__,
151 ep, head, num, size, cb);
152
153 for (i = 0; i < num; i++) {
154 req = gsmd_alloc_req(ep, size, GFP_ATOMIC);
155 if (!req) {
156 pr_debug("%s: req allocated:%d\n", __func__, i);
157 return list_empty(head) ? -ENOMEM : 0;
158 }
159 req->complete = cb;
160 list_add(&req->list, head);
161 }
162
163 return 0;
164}
165
166static void gsmd_start_rx(struct gsmd_port *port)
167{
168 struct list_head *pool;
169 struct usb_ep *out;
Eric Holmberg0c55b742011-09-29 10:56:08 -0600170 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 int ret;
172
173 if (!port) {
174 pr_err("%s: port is null\n", __func__);
175 return;
176 }
177
Eric Holmberg0c55b742011-09-29 10:56:08 -0600178 spin_lock_irqsave(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179
180 if (!port->port_usb) {
181 pr_debug("%s: USB disconnected\n", __func__);
182 goto start_rx_end;
183 }
184
185 pool = &port->read_pool;
186 out = port->port_usb->out;
187
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700188 while (test_bit(CH_OPENED, &port->pi->flags) && !list_empty(pool)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 struct usb_request *req;
190
191 req = list_entry(pool->next, struct usb_request, list);
192 list_del(&req->list);
193 req->length = SMD_RX_BUF_SIZE;
194
Eric Holmberg0c55b742011-09-29 10:56:08 -0600195 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 ret = usb_ep_queue(out, req, GFP_KERNEL);
Eric Holmberg0c55b742011-09-29 10:56:08 -0600197 spin_lock_irqsave(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 if (ret) {
199 pr_err("%s: usb ep out queue failed"
200 "port:%p, port#%d\n",
201 __func__, port, port->port_num);
202 list_add_tail(&req->list, pool);
203 break;
204 }
205 }
206start_rx_end:
Eric Holmberg0c55b742011-09-29 10:56:08 -0600207 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208}
209
210static void gsmd_rx_push(struct work_struct *w)
211{
212 struct gsmd_port *port = container_of(w, struct gsmd_port, push);
Jack Pham1b236d12012-03-19 15:27:18 -0700213 struct smd_port_info *pi = port->pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 struct list_head *q;
215
216 pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
217
218 spin_lock_irq(&port->port_lock);
219
220 q = &port->read_queue;
Jack Pham1b236d12012-03-19 15:27:18 -0700221 while (pi->ch && !list_empty(q)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 struct usb_request *req;
223 int avail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224
225 req = list_first_entry(q, struct usb_request, list);
226
227 switch (req->status) {
228 case -ESHUTDOWN:
229 pr_debug("%s: req status shutdown portno#%d port:%p\n",
230 __func__, port->port_num, port);
231 goto rx_push_end;
232 default:
233 pr_warning("%s: port:%p port#%d"
234 " Unexpected Rx Status:%d\n", __func__,
235 port, port->port_num, req->status);
236 case 0:
237 /* normal completion */
238 break;
239 }
240
241 avail = smd_write_avail(pi->ch);
242 if (!avail)
243 goto rx_push_end;
244
245 if (req->actual) {
246 char *packet = req->buf;
247 unsigned size = req->actual;
248 unsigned n;
Vamsi Krishnab015f0c2012-03-16 14:14:41 -0700249 int count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250
251 n = port->n_read;
252 if (n) {
253 packet += n;
254 size -= n;
255 }
256
257 count = smd_write(pi->ch, packet, size);
258 if (count < 0) {
259 pr_err("%s: smd write failed err:%d\n",
260 __func__, count);
261 goto rx_push_end;
262 }
263
264 if (count != size) {
265 port->n_read += count;
266 goto rx_push_end;
267 }
268
269 port->nbytes_tomodem += count;
270 }
271
272 port->n_read = 0;
273 list_move(&req->list, &port->read_pool);
274 }
275
276rx_push_end:
277 spin_unlock_irq(&port->port_lock);
278
279 gsmd_start_rx(port);
280}
281
282static void gsmd_read_pending(struct gsmd_port *port)
283{
284 int avail;
285
286 if (!port || !port->pi->ch)
287 return;
288
289 /* passing null buffer discards the data */
290 while ((avail = smd_read_avail(port->pi->ch)))
291 smd_read(port->pi->ch, 0, avail);
292
293 return;
294}
295
296static void gsmd_tx_pull(struct work_struct *w)
297{
298 struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
299 struct list_head *pool = &port->write_pool;
Jack Pham1b236d12012-03-19 15:27:18 -0700300 struct smd_port_info *pi = port->pi;
301 struct usb_ep *in;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302
303 pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
304 port, port->port_num, pool);
305
Jack Pham1b236d12012-03-19 15:27:18 -0700306 spin_lock_irq(&port->port_lock);
307
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 if (!port->port_usb) {
309 pr_debug("%s: usb is disconnected\n", __func__);
Jack Pham1b236d12012-03-19 15:27:18 -0700310 spin_unlock_irq(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 gsmd_read_pending(port);
312 return;
313 }
314
Jack Pham1b236d12012-03-19 15:27:18 -0700315 in = port->port_usb->in;
316 while (pi->ch && !list_empty(pool)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317 struct usb_request *req;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 int avail;
319 int ret;
320
321 avail = smd_read_avail(pi->ch);
322 if (!avail)
323 break;
324
325 avail = avail > SMD_TX_BUF_SIZE ? SMD_TX_BUF_SIZE : avail;
326
327 req = list_entry(pool->next, struct usb_request, list);
328 list_del(&req->list);
329 req->length = smd_read(pi->ch, req->buf, avail);
330
331 spin_unlock_irq(&port->port_lock);
332 ret = usb_ep_queue(in, req, GFP_KERNEL);
333 spin_lock_irq(&port->port_lock);
334 if (ret) {
335 pr_err("%s: usb ep out queue failed"
336 "port:%p, port#%d err:%d\n",
337 __func__, port, port->port_num, ret);
338 /* could be usb disconnected */
339 if (!port->port_usb)
340 gsmd_free_req(in, req);
341 else
342 list_add(&req->list, pool);
343 goto tx_pull_end;
344 }
345
346 port->nbytes_tolaptop += req->length;
347 }
348
349tx_pull_end:
350 /* TBD: Check how code behaves on USB bus suspend */
351 if (port->port_usb && smd_read_avail(port->pi->ch) && !list_empty(pool))
352 queue_work(gsmd_wq, &port->pull);
353
354 spin_unlock_irq(&port->port_lock);
355
356 return;
357}
358
359static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
360{
361 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362
363 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
364
365 if (!port) {
366 pr_err("%s: port is null\n", __func__);
367 return;
368 }
369
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700370 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700371 if (!test_bit(CH_OPENED, &port->pi->flags) ||
372 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700373 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700374 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700375 return;
376 }
377
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 list_add_tail(&req->list, &port->read_queue);
379 queue_work(gsmd_wq, &port->push);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700380 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381
382 return;
383}
384
385static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
386{
387 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388
389 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
390
391 if (!port) {
392 pr_err("%s: port is null\n", __func__);
393 return;
394 }
395
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700396 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700397 if (!test_bit(CH_OPENED, &port->pi->flags) ||
398 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700399 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700400 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700401 return;
402 }
403
Vamsi Krishna396562a2011-08-25 11:39:09 -0700404 if (req->status)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405 pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
406 __func__, port, port->port_num,
407 ep->name, req->status);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408
Vamsi Krishna396562a2011-08-25 11:39:09 -0700409 list_add(&req->list, &port->write_pool);
410 queue_work(gsmd_wq, &port->pull);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700411 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412
413 return;
414}
415
416static void gsmd_start_io(struct gsmd_port *port)
417{
418 int ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419
420 pr_debug("%s: port: %p\n", __func__, port);
421
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700422 spin_lock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423
424 if (!port->port_usb)
425 goto start_io_out;
426
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700427 smd_tiocmset_from_cb(port->pi->ch,
428 port->cbits_to_modem,
429 ~port->cbits_to_modem);
430
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431 ret = gsmd_alloc_requests(port->port_usb->out,
432 &port->read_pool,
433 SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
434 gsmd_read_complete);
435 if (ret) {
436 pr_err("%s: unable to allocate out requests\n",
437 __func__);
438 goto start_io_out;
439 }
440
441 ret = gsmd_alloc_requests(port->port_usb->in,
442 &port->write_pool,
443 SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
444 gsmd_write_complete);
445 if (ret) {
446 gsmd_free_requests(port->port_usb->out, &port->read_pool);
447 pr_err("%s: unable to allocate IN requests\n",
448 __func__);
449 goto start_io_out;
450 }
451
452start_io_out:
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700453 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454
455 if (ret)
456 return;
457
458 gsmd_start_rx(port);
459}
460
461static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
462{
463 unsigned int acm_sig = 0;
464
465 /* should this needs to be in calling functions ??? */
466 uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
467
468 if (uart_sig & TIOCM_RI)
469 acm_sig |= SMD_ACM_CTRL_RI;
470 if (uart_sig & TIOCM_CD)
471 acm_sig |= SMD_ACM_CTRL_DCD;
472 if (uart_sig & TIOCM_DSR)
473 acm_sig |= SMD_ACM_CTRL_DSR;
474
475 return acm_sig;
476}
477
478static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
479{
480 unsigned int uart_sig = 0;
481
482 /* should this needs to be in calling functions ??? */
483 acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
484
485 if (acm_sig & SMD_ACM_CTRL_DTR)
486 uart_sig |= TIOCM_DTR;
487 if (acm_sig & SMD_ACM_CTRL_RTS)
488 uart_sig |= TIOCM_RTS;
489
490 return uart_sig;
491}
492
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700493
494static void gsmd_stop_io(struct gsmd_port *port)
495{
496 struct usb_ep *in;
497 struct usb_ep *out;
498 unsigned long flags;
499
500 spin_lock_irqsave(&port->port_lock, flags);
501 if (!port->port_usb) {
502 spin_unlock_irqrestore(&port->port_lock, flags);
503 return;
504 }
505 in = port->port_usb->in;
506 out = port->port_usb->out;
507 spin_unlock_irqrestore(&port->port_lock, flags);
508
509 usb_ep_fifo_flush(in);
510 usb_ep_fifo_flush(out);
511
512 spin_lock(&port->port_lock);
513 if (port->port_usb) {
514 gsmd_free_requests(out, &port->read_pool);
515 gsmd_free_requests(out, &port->read_queue);
516 gsmd_free_requests(in, &port->write_pool);
517 port->n_read = 0;
518 port->cbits_to_laptop = 0;
519 }
520
521 if (port->port_usb->send_modem_ctrl_bits)
522 port->port_usb->send_modem_ctrl_bits(
523 port->port_usb,
524 port->cbits_to_laptop);
525 spin_unlock(&port->port_lock);
526
527}
528
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529static void gsmd_notify(void *priv, unsigned event)
530{
531 struct gsmd_port *port = priv;
532 struct smd_port_info *pi = port->pi;
533 int i;
534
535 switch (event) {
536 case SMD_EVENT_DATA:
537 pr_debug("%s: Event data\n", __func__);
538 if (smd_read_avail(pi->ch))
539 queue_work(gsmd_wq, &port->pull);
540 if (smd_write_avail(pi->ch))
541 queue_work(gsmd_wq, &port->push);
542 break;
543 case SMD_EVENT_OPEN:
544 pr_debug("%s: Event Open\n", __func__);
545 set_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700546 gsmd_start_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 break;
548 case SMD_EVENT_CLOSE:
549 pr_debug("%s: Event Close\n", __func__);
550 clear_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700551 gsmd_stop_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552 break;
553 case SMD_EVENT_STATUS:
554 i = smd_tiocmget(port->pi->ch);
555 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
556 if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
557 port->port_usb->send_modem_ctrl_bits(port->port_usb,
558 port->cbits_to_laptop);
559 break;
560 }
561}
562
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563static void gsmd_connect_work(struct work_struct *w)
564{
565 struct gsmd_port *port;
566 struct smd_port_info *pi;
567 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568
Eric Holmberge72b2d42012-01-23 13:45:45 -0700569 port = container_of(w, struct gsmd_port, connect_work.work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700570 pi = port->pi;
571
572 pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
573
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700574 if (!test_bit(CH_READY, &pi->flags))
575 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700577 ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
578 &pi->ch, port, gsmd_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579 if (ret) {
Eric Holmberge72b2d42012-01-23 13:45:45 -0700580 if (ret == -EAGAIN) {
581 /* port not ready - retry */
582 pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
583 __func__, pi->name, ret);
584 queue_delayed_work(gsmd_wq, &port->connect_work,
585 msecs_to_jiffies(250));
586 } else {
587 pr_err("%s: unable to open smd port:%s err:%d\n",
588 __func__, pi->name, ret);
589 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591}
592
Manu Gautam7b561ea2012-11-09 17:28:01 +0530593static void gsmd_disconnect_work(struct work_struct *w)
594{
595 struct gsmd_port *port;
596 struct smd_port_info *pi;
597
598 port = container_of(w, struct gsmd_port, disconnect_work);
599 pi = port->pi;
600
601 pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
602
603 smd_close(port->pi->ch);
604 port->pi->ch = NULL;
605}
606
Hemant Kumarf60c0252011-11-03 12:37:07 -0700607static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608{
609 struct gsmd_port *port;
610 int temp;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700611 struct gserial *gser = gptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700612
613 if (portno >= n_smd_ports) {
614 pr_err("%s: invalid portno#%d\n", __func__, portno);
615 return;
616 }
617
618 if (!gser) {
619 pr_err("%s: gser is null\n", __func__);
620 return;
621 }
622
623 port = smd_ports[portno].port;
624
625 temp = convert_acm_sigs_to_uart(ctrl_bits);
626
627 if (temp == port->cbits_to_modem)
628 return;
629
630 port->cbits_to_modem = temp;
631
632 /* usb could send control signal before smd is ready */
633 if (!test_bit(CH_OPENED, &port->pi->flags))
634 return;
635
636 /* if DTR is high, update latest modem info to laptop */
637 if (port->cbits_to_modem & TIOCM_DTR) {
638 unsigned i;
639
640 i = smd_tiocmget(port->pi->ch);
641 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
642
643 if (gser->send_modem_ctrl_bits)
644 gser->send_modem_ctrl_bits(
645 port->port_usb,
646 port->cbits_to_laptop);
647 }
648
649 smd_tiocmset(port->pi->ch,
650 port->cbits_to_modem,
651 ~port->cbits_to_modem);
652}
653
654int gsmd_connect(struct gserial *gser, u8 portno)
655{
656 unsigned long flags;
657 int ret;
658 struct gsmd_port *port;
659
660 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
661
662 if (portno >= n_smd_ports) {
663 pr_err("%s: Invalid port no#%d", __func__, portno);
664 return -EINVAL;
665 }
666
667 if (!gser) {
668 pr_err("%s: gser is null\n", __func__);
669 return -EINVAL;
670 }
671
672 port = smd_ports[portno].port;
673
674 spin_lock_irqsave(&port->port_lock, flags);
675 port->port_usb = gser;
676 gser->notify_modem = gsmd_notify_modem;
677 port->nbytes_tomodem = 0;
678 port->nbytes_tolaptop = 0;
679 spin_unlock_irqrestore(&port->port_lock, flags);
680
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300681 ret = usb_ep_enable(gser->in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 if (ret) {
683 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
684 __func__, gser->in);
685 port->port_usb = 0;
686 return ret;
687 }
688 gser->in->driver_data = port;
689
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300690 ret = usb_ep_enable(gser->out);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691 if (ret) {
692 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
693 __func__, gser->out);
694 port->port_usb = 0;
695 gser->in->driver_data = 0;
696 return ret;
697 }
698 gser->out->driver_data = port;
699
Eric Holmberge72b2d42012-01-23 13:45:45 -0700700 queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701
702 return 0;
703}
704
705void gsmd_disconnect(struct gserial *gser, u8 portno)
706{
707 unsigned long flags;
708 struct gsmd_port *port;
709
710 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
711
712 if (portno >= n_smd_ports) {
713 pr_err("%s: invalid portno#%d\n", __func__, portno);
714 return;
715 }
716
717 if (!gser) {
718 pr_err("%s: gser is null\n", __func__);
719 return;
720 }
721
722 port = smd_ports[portno].port;
723
724 spin_lock_irqsave(&port->port_lock, flags);
725 port->port_usb = 0;
726 spin_unlock_irqrestore(&port->port_lock, flags);
727
728 /* disable endpoints, aborting down any active I/O */
729 usb_ep_disable(gser->out);
Chiranjeevi Velempati4fd1c162012-08-01 09:41:22 +0530730 gser->out->driver_data = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731 usb_ep_disable(gser->in);
Chiranjeevi Velempati4fd1c162012-08-01 09:41:22 +0530732 gser->in->driver_data = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733
734 spin_lock_irqsave(&port->port_lock, flags);
735 gsmd_free_requests(gser->out, &port->read_pool);
736 gsmd_free_requests(gser->out, &port->read_queue);
737 gsmd_free_requests(gser->in, &port->write_pool);
738 port->n_read = 0;
739 spin_unlock_irqrestore(&port->port_lock, flags);
740
Jack Phamc02d4812012-01-25 15:52:04 -0800741 if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
742 /* lower the dtr */
743 port->cbits_to_modem = 0;
744 smd_tiocmset(port->pi->ch,
745 port->cbits_to_modem,
746 ~port->cbits_to_modem);
747 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748
Manu Gautam7b561ea2012-11-09 17:28:01 +0530749 if (port->pi->ch)
750 queue_work(gsmd_wq, &port->disconnect_work);
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700751}
752
753#define SMD_CH_MAX_LEN 20
754static int gsmd_ch_probe(struct platform_device *pdev)
755{
756 struct gsmd_port *port;
757 struct smd_port_info *pi;
758 int i;
759 unsigned long flags;
760
761 pr_debug("%s: name:%s\n", __func__, pdev->name);
762
763 for (i = 0; i < n_smd_ports; i++) {
764 port = smd_ports[i].port;
765 pi = port->pi;
766
767 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
768 set_bit(CH_READY, &pi->flags);
769 spin_lock_irqsave(&port->port_lock, flags);
770 if (port->port_usb)
Eric Holmberge72b2d42012-01-23 13:45:45 -0700771 queue_delayed_work(gsmd_wq, &port->connect_work,
772 msecs_to_jiffies(0));
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700773 spin_unlock_irqrestore(&port->port_lock, flags);
774 break;
775 }
776 }
777 return 0;
778}
779
780static int gsmd_ch_remove(struct platform_device *pdev)
781{
782 struct gsmd_port *port;
783 struct smd_port_info *pi;
784 int i;
785
786 pr_debug("%s: name:%s\n", __func__, pdev->name);
787
788 for (i = 0; i < n_smd_ports; i++) {
789 port = smd_ports[i].port;
790 pi = port->pi;
791
792 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
793 clear_bit(CH_READY, &pi->flags);
794 clear_bit(CH_OPENED, &pi->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800795 if (pi->ch) {
796 smd_close(pi->ch);
797 pi->ch = NULL;
798 }
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700799 break;
800 }
801 }
802 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803}
804
805static void gsmd_port_free(int portno)
806{
807 struct gsmd_port *port = smd_ports[portno].port;
808
809 if (!port)
810 kfree(port);
811}
812
813static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
814{
815 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700816 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700817
818 port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
819 if (!port)
820 return -ENOMEM;
821
822 port->port_num = portno;
823 port->pi = &smd_pi[portno];
824
825 spin_lock_init(&port->port_lock);
826
827 INIT_LIST_HEAD(&port->read_pool);
828 INIT_LIST_HEAD(&port->read_queue);
829 INIT_WORK(&port->push, gsmd_rx_push);
830
831 INIT_LIST_HEAD(&port->write_pool);
832 INIT_WORK(&port->pull, gsmd_tx_pull);
833
Eric Holmberge72b2d42012-01-23 13:45:45 -0700834 INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
Manu Gautam7b561ea2012-11-09 17:28:01 +0530835 INIT_WORK(&port->disconnect_work, gsmd_disconnect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700836
837 smd_ports[portno].port = port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700838 pdrv = &smd_ports[portno].pdrv;
839 pdrv->probe = gsmd_ch_probe;
840 pdrv->remove = gsmd_ch_remove;
841 pdrv->driver.name = port->pi->name;
842 pdrv->driver.owner = THIS_MODULE;
843 platform_driver_register(pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844
845 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
846
847 return 0;
848}
849
850#if defined(CONFIG_DEBUG_FS)
851static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
852 size_t count, loff_t *ppos)
853{
854 struct gsmd_port *port;
Jack Phamc02d4812012-01-25 15:52:04 -0800855 struct smd_port_info *pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700856 char *buf;
857 unsigned long flags;
858 int temp = 0;
859 int i;
860 int ret;
861
862 buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
863 if (!buf)
864 return -ENOMEM;
865
866 for (i = 0; i < n_smd_ports; i++) {
867 port = smd_ports[i].port;
Jack Phamc02d4812012-01-25 15:52:04 -0800868 pi = port->pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869 spin_lock_irqsave(&port->port_lock, flags);
870 temp += scnprintf(buf + temp, 512 - temp,
871 "###PORT:%d###\n"
872 "nbytes_tolaptop: %lu\n"
873 "nbytes_tomodem: %lu\n"
874 "cbits_to_modem: %u\n"
875 "cbits_to_laptop: %u\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700876 "n_read: %u\n"
Vamsi Krishna396562a2011-08-25 11:39:09 -0700877 "smd_read_avail: %d\n"
878 "smd_write_avail: %d\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700879 "CH_OPENED: %d\n"
880 "CH_READY: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700881 i, port->nbytes_tolaptop, port->nbytes_tomodem,
882 port->cbits_to_modem, port->cbits_to_laptop,
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700883 port->n_read,
Jack Phamc02d4812012-01-25 15:52:04 -0800884 pi->ch ? smd_read_avail(pi->ch) : 0,
885 pi->ch ? smd_write_avail(pi->ch) : 0,
886 test_bit(CH_OPENED, &pi->flags),
887 test_bit(CH_READY, &pi->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700888 spin_unlock_irqrestore(&port->port_lock, flags);
889 }
890
891 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
892
893 kfree(buf);
894
895 return ret;
896
897}
898
899static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
900 size_t count, loff_t *ppos)
901{
902 struct gsmd_port *port;
903 unsigned long flags;
904 int i;
905
906 for (i = 0; i < n_smd_ports; i++) {
907 port = smd_ports[i].port;
908
909 spin_lock_irqsave(&port->port_lock, flags);
910 port->nbytes_tolaptop = 0;
911 port->nbytes_tomodem = 0;
912 spin_unlock_irqrestore(&port->port_lock, flags);
913 }
914
915 return count;
916}
917
918static int debug_smd_open(struct inode *inode, struct file *file)
919{
920 return 0;
921}
922
923static const struct file_operations debug_gsmd_ops = {
924 .open = debug_smd_open,
925 .read = debug_smd_read_stats,
926 .write = debug_smd_reset_stats,
927};
928
929static void gsmd_debugfs_init(void)
930{
931 struct dentry *dent;
932
933 dent = debugfs_create_dir("usb_gsmd", 0);
934 if (IS_ERR(dent))
935 return;
936
937 debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
938}
939#else
940static void gsmd_debugfs_init(void) {}
941#endif
942
943int gsmd_setup(struct usb_gadget *g, unsigned count)
944{
945 struct usb_cdc_line_coding coding;
946 int ret;
947 int i;
948
949 pr_debug("%s: g:%p count: %d\n", __func__, g, count);
950
951 if (!count || count > SMD_N_PORTS) {
952 pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
953 __func__, count, g);
954 return -EINVAL;
955 }
956
957 coding.dwDTERate = cpu_to_le32(9600);
958 coding.bCharFormat = 8;
959 coding.bParityType = USB_CDC_NO_PARITY;
960 coding.bDataBits = USB_CDC_1_STOP_BITS;
961
962 gsmd_wq = create_singlethread_workqueue("k_gsmd");
963 if (!gsmd_wq) {
964 pr_err("%s: Unable to create workqueue gsmd_wq\n",
965 __func__);
966 return -ENOMEM;
967 }
968
969 for (i = 0; i < count; i++) {
970 mutex_init(&smd_ports[i].lock);
Manu Gautam4bd21422011-09-09 14:55:32 +0530971 n_smd_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972 ret = gsmd_port_alloc(i, &coding);
973 if (ret) {
Manu Gautam4bd21422011-09-09 14:55:32 +0530974 n_smd_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700975 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
976 goto free_smd_ports;
977 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700978 }
979
980 gsmd_debugfs_init();
981
982 return 0;
983free_smd_ports:
984 for (i = 0; i < n_smd_ports; i++)
985 gsmd_port_free(i);
986
987 destroy_workqueue(gsmd_wq);
988
989 return ret;
990}
991
992void gsmd_cleanup(struct usb_gadget *g, unsigned count)
993{
994 /* TBD */
995}