blob: bdcbdd7aef46295d0061ce011eaffad183499163 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * u_smd.c - utilities for USB gadget serial over smd
3 *
4 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
5 *
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;
74 struct work_struct connect_work;
75
76 /* At present, smd does not notify
77 * control bit change info from modem
78 */
79 struct work_struct update_modem_ctrl_sig;
80
81#define SMD_ACM_CTRL_DTR 0x01
82#define SMD_ACM_CTRL_RTS 0x02
83 unsigned cbits_to_modem;
84
85#define SMD_ACM_CTRL_DCD 0x01
86#define SMD_ACM_CTRL_DSR 0x02
87#define SMD_ACM_CTRL_BRK 0x04
88#define SMD_ACM_CTRL_RI 0x08
89 unsigned cbits_to_laptop;
90
91 /* pkt counters */
92 unsigned long nbytes_tomodem;
93 unsigned long nbytes_tolaptop;
94};
95
96static struct smd_portmaster {
97 struct mutex lock;
98 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -070099 struct platform_driver pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100} smd_ports[SMD_N_PORTS];
101static unsigned n_smd_ports;
102
103static void gsmd_free_req(struct usb_ep *ep, struct usb_request *req)
104{
105 kfree(req->buf);
106 usb_ep_free_request(ep, req);
107}
108
109static void gsmd_free_requests(struct usb_ep *ep, struct list_head *head)
110{
111 struct usb_request *req;
112
113 while (!list_empty(head)) {
114 req = list_entry(head->next, struct usb_request, list);
115 list_del(&req->list);
116 gsmd_free_req(ep, req);
117 }
118}
119
120static struct usb_request *
121gsmd_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
122{
123 struct usb_request *req;
124
125 req = usb_ep_alloc_request(ep, flags);
126 if (!req) {
127 pr_err("%s: usb alloc request failed\n", __func__);
128 return 0;
129 }
130
131 req->length = len;
132 req->buf = kmalloc(len, flags);
133 if (!req->buf) {
134 pr_err("%s: request buf allocation failed\n", __func__);
135 usb_ep_free_request(ep, req);
136 return 0;
137 }
138
139 return req;
140}
141
142static int gsmd_alloc_requests(struct usb_ep *ep, struct list_head *head,
143 int num, int size,
144 void (*cb)(struct usb_ep *ep, struct usb_request *))
145{
146 int i;
147 struct usb_request *req;
148
149 pr_debug("%s: ep:%p head:%p num:%d size:%d cb:%p", __func__,
150 ep, head, num, size, cb);
151
152 for (i = 0; i < num; i++) {
153 req = gsmd_alloc_req(ep, size, GFP_ATOMIC);
154 if (!req) {
155 pr_debug("%s: req allocated:%d\n", __func__, i);
156 return list_empty(head) ? -ENOMEM : 0;
157 }
158 req->complete = cb;
159 list_add(&req->list, head);
160 }
161
162 return 0;
163}
164
165static void gsmd_start_rx(struct gsmd_port *port)
166{
167 struct list_head *pool;
168 struct usb_ep *out;
169 int ret;
170
171 if (!port) {
172 pr_err("%s: port is null\n", __func__);
173 return;
174 }
175
176 spin_lock_irq(&port->port_lock);
177
178 if (!port->port_usb) {
179 pr_debug("%s: USB disconnected\n", __func__);
180 goto start_rx_end;
181 }
182
183 pool = &port->read_pool;
184 out = port->port_usb->out;
185
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700186 while (test_bit(CH_OPENED, &port->pi->flags) && !list_empty(pool)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187 struct usb_request *req;
188
189 req = list_entry(pool->next, struct usb_request, list);
190 list_del(&req->list);
191 req->length = SMD_RX_BUF_SIZE;
192
193 spin_unlock_irq(&port->port_lock);
194 ret = usb_ep_queue(out, req, GFP_KERNEL);
195 spin_lock_irq(&port->port_lock);
196 if (ret) {
197 pr_err("%s: usb ep out queue failed"
198 "port:%p, port#%d\n",
199 __func__, port, port->port_num);
200 list_add_tail(&req->list, pool);
201 break;
202 }
203 }
204start_rx_end:
205 spin_unlock_irq(&port->port_lock);
206}
207
208static void gsmd_rx_push(struct work_struct *w)
209{
210 struct gsmd_port *port = container_of(w, struct gsmd_port, push);
211 struct list_head *q;
212
213 pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
214
215 spin_lock_irq(&port->port_lock);
216
217 q = &port->read_queue;
218 while (!list_empty(q)) {
219 struct usb_request *req;
220 int avail;
221 struct smd_port_info *pi = port->pi;
222
223 req = list_first_entry(q, struct usb_request, list);
224
225 switch (req->status) {
226 case -ESHUTDOWN:
227 pr_debug("%s: req status shutdown portno#%d port:%p\n",
228 __func__, port->port_num, port);
229 goto rx_push_end;
230 default:
231 pr_warning("%s: port:%p port#%d"
232 " Unexpected Rx Status:%d\n", __func__,
233 port, port->port_num, req->status);
234 case 0:
235 /* normal completion */
236 break;
237 }
238
239 avail = smd_write_avail(pi->ch);
240 if (!avail)
241 goto rx_push_end;
242
243 if (req->actual) {
244 char *packet = req->buf;
245 unsigned size = req->actual;
246 unsigned n;
247 unsigned count;
248
249 n = port->n_read;
250 if (n) {
251 packet += n;
252 size -= n;
253 }
254
255 count = smd_write(pi->ch, packet, size);
256 if (count < 0) {
257 pr_err("%s: smd write failed err:%d\n",
258 __func__, count);
259 goto rx_push_end;
260 }
261
262 if (count != size) {
263 port->n_read += count;
264 goto rx_push_end;
265 }
266
267 port->nbytes_tomodem += count;
268 }
269
270 port->n_read = 0;
271 list_move(&req->list, &port->read_pool);
272 }
273
274rx_push_end:
275 spin_unlock_irq(&port->port_lock);
276
277 gsmd_start_rx(port);
278}
279
280static void gsmd_read_pending(struct gsmd_port *port)
281{
282 int avail;
283
284 if (!port || !port->pi->ch)
285 return;
286
287 /* passing null buffer discards the data */
288 while ((avail = smd_read_avail(port->pi->ch)))
289 smd_read(port->pi->ch, 0, avail);
290
291 return;
292}
293
294static void gsmd_tx_pull(struct work_struct *w)
295{
296 struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
297 struct list_head *pool = &port->write_pool;
298
299 pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
300 port, port->port_num, pool);
301
302 if (!port->port_usb) {
303 pr_debug("%s: usb is disconnected\n", __func__);
304 gsmd_read_pending(port);
305 return;
306 }
307
308 spin_lock_irq(&port->port_lock);
309 while (!list_empty(pool)) {
310 struct usb_request *req;
311 struct usb_ep *in = port->port_usb->in;
312 struct smd_port_info *pi = port->pi;
313 int avail;
314 int ret;
315
316 avail = smd_read_avail(pi->ch);
317 if (!avail)
318 break;
319
320 avail = avail > SMD_TX_BUF_SIZE ? SMD_TX_BUF_SIZE : avail;
321
322 req = list_entry(pool->next, struct usb_request, list);
323 list_del(&req->list);
324 req->length = smd_read(pi->ch, req->buf, avail);
325
326 spin_unlock_irq(&port->port_lock);
327 ret = usb_ep_queue(in, req, GFP_KERNEL);
328 spin_lock_irq(&port->port_lock);
329 if (ret) {
330 pr_err("%s: usb ep out queue failed"
331 "port:%p, port#%d err:%d\n",
332 __func__, port, port->port_num, ret);
333 /* could be usb disconnected */
334 if (!port->port_usb)
335 gsmd_free_req(in, req);
336 else
337 list_add(&req->list, pool);
338 goto tx_pull_end;
339 }
340
341 port->nbytes_tolaptop += req->length;
342 }
343
344tx_pull_end:
345 /* TBD: Check how code behaves on USB bus suspend */
346 if (port->port_usb && smd_read_avail(port->pi->ch) && !list_empty(pool))
347 queue_work(gsmd_wq, &port->pull);
348
349 spin_unlock_irq(&port->port_lock);
350
351 return;
352}
353
354static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
355{
356 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
358 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
359
360 if (!port) {
361 pr_err("%s: port is null\n", __func__);
362 return;
363 }
364
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700365 spin_lock(&port->port_lock);
366 if (!test_bit(CH_OPENED, &port->pi->flags)) {
367 gsmd_free_req(ep, req);
368 spin_unlock(&port->port_lock);
369 return;
370 }
371
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 list_add_tail(&req->list, &port->read_queue);
373 queue_work(gsmd_wq, &port->push);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700374 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375
376 return;
377}
378
379static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
380{
381 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382
383 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
384
385 if (!port) {
386 pr_err("%s: port is null\n", __func__);
387 return;
388 }
389
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700390 spin_lock(&port->port_lock);
391 if (!test_bit(CH_OPENED, &port->pi->flags)) {
392 gsmd_free_req(ep, req);
393 spin_unlock(&port->port_lock);
394 return;
395 }
396
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397 list_add(&req->list, &port->write_pool);
398
399 switch (req->status) {
400 default:
401 pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
402 __func__, port, port->port_num,
403 ep->name, req->status);
404 /* FALL THROUGH */
405 case 0:
406 queue_work(gsmd_wq, &port->pull);
407 break;
408
409 case -ESHUTDOWN:
410 /* disconnect */
411 pr_debug("%s: %s shutdown\n", __func__, ep->name);
412 gsmd_free_req(ep, req);
413 break;
414 }
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700415 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416
417 return;
418}
419
420static void gsmd_start_io(struct gsmd_port *port)
421{
422 int ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423
424 pr_debug("%s: port: %p\n", __func__, port);
425
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700426 spin_lock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427
428 if (!port->port_usb)
429 goto start_io_out;
430
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700431 smd_tiocmset_from_cb(port->pi->ch,
432 port->cbits_to_modem,
433 ~port->cbits_to_modem);
434
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 ret = gsmd_alloc_requests(port->port_usb->out,
436 &port->read_pool,
437 SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
438 gsmd_read_complete);
439 if (ret) {
440 pr_err("%s: unable to allocate out requests\n",
441 __func__);
442 goto start_io_out;
443 }
444
445 ret = gsmd_alloc_requests(port->port_usb->in,
446 &port->write_pool,
447 SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
448 gsmd_write_complete);
449 if (ret) {
450 gsmd_free_requests(port->port_usb->out, &port->read_pool);
451 pr_err("%s: unable to allocate IN requests\n",
452 __func__);
453 goto start_io_out;
454 }
455
456start_io_out:
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700457 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458
459 if (ret)
460 return;
461
462 gsmd_start_rx(port);
463}
464
465static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
466{
467 unsigned int acm_sig = 0;
468
469 /* should this needs to be in calling functions ??? */
470 uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
471
472 if (uart_sig & TIOCM_RI)
473 acm_sig |= SMD_ACM_CTRL_RI;
474 if (uart_sig & TIOCM_CD)
475 acm_sig |= SMD_ACM_CTRL_DCD;
476 if (uart_sig & TIOCM_DSR)
477 acm_sig |= SMD_ACM_CTRL_DSR;
478
479 return acm_sig;
480}
481
482static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
483{
484 unsigned int uart_sig = 0;
485
486 /* should this needs to be in calling functions ??? */
487 acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
488
489 if (acm_sig & SMD_ACM_CTRL_DTR)
490 uart_sig |= TIOCM_DTR;
491 if (acm_sig & SMD_ACM_CTRL_RTS)
492 uart_sig |= TIOCM_RTS;
493
494 return uart_sig;
495}
496
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700497
498static void gsmd_stop_io(struct gsmd_port *port)
499{
500 struct usb_ep *in;
501 struct usb_ep *out;
502 unsigned long flags;
503
504 spin_lock_irqsave(&port->port_lock, flags);
505 if (!port->port_usb) {
506 spin_unlock_irqrestore(&port->port_lock, flags);
507 return;
508 }
509 in = port->port_usb->in;
510 out = port->port_usb->out;
511 spin_unlock_irqrestore(&port->port_lock, flags);
512
513 usb_ep_fifo_flush(in);
514 usb_ep_fifo_flush(out);
515
516 spin_lock(&port->port_lock);
517 if (port->port_usb) {
518 gsmd_free_requests(out, &port->read_pool);
519 gsmd_free_requests(out, &port->read_queue);
520 gsmd_free_requests(in, &port->write_pool);
521 port->n_read = 0;
522 port->cbits_to_laptop = 0;
523 }
524
525 if (port->port_usb->send_modem_ctrl_bits)
526 port->port_usb->send_modem_ctrl_bits(
527 port->port_usb,
528 port->cbits_to_laptop);
529 spin_unlock(&port->port_lock);
530
531}
532
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533static void gsmd_notify(void *priv, unsigned event)
534{
535 struct gsmd_port *port = priv;
536 struct smd_port_info *pi = port->pi;
537 int i;
538
539 switch (event) {
540 case SMD_EVENT_DATA:
541 pr_debug("%s: Event data\n", __func__);
542 if (smd_read_avail(pi->ch))
543 queue_work(gsmd_wq, &port->pull);
544 if (smd_write_avail(pi->ch))
545 queue_work(gsmd_wq, &port->push);
546 break;
547 case SMD_EVENT_OPEN:
548 pr_debug("%s: Event Open\n", __func__);
549 set_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700550 gsmd_start_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 break;
552 case SMD_EVENT_CLOSE:
553 pr_debug("%s: Event Close\n", __func__);
554 clear_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700555 gsmd_stop_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 break;
557 case SMD_EVENT_STATUS:
558 i = smd_tiocmget(port->pi->ch);
559 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
560 if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
561 port->port_usb->send_modem_ctrl_bits(port->port_usb,
562 port->cbits_to_laptop);
563 break;
564 }
565}
566
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700567static void gsmd_connect_work(struct work_struct *w)
568{
569 struct gsmd_port *port;
570 struct smd_port_info *pi;
571 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572
573 port = container_of(w, struct gsmd_port, connect_work);
574 pi = port->pi;
575
576 pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
577
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700578 if (!test_bit(CH_READY, &pi->flags))
579 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700581 ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
582 &pi->ch, port, gsmd_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 if (ret) {
584 pr_err("%s: unable to open smd port:%s err:%d\n",
585 __func__, pi->name, ret);
586 return;
587 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700588}
589
590static void gsmd_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits)
591{
592 struct gsmd_port *port;
593 int temp;
594
595 if (portno >= n_smd_ports) {
596 pr_err("%s: invalid portno#%d\n", __func__, portno);
597 return;
598 }
599
600 if (!gser) {
601 pr_err("%s: gser is null\n", __func__);
602 return;
603 }
604
605 port = smd_ports[portno].port;
606
607 temp = convert_acm_sigs_to_uart(ctrl_bits);
608
609 if (temp == port->cbits_to_modem)
610 return;
611
612 port->cbits_to_modem = temp;
613
614 /* usb could send control signal before smd is ready */
615 if (!test_bit(CH_OPENED, &port->pi->flags))
616 return;
617
618 /* if DTR is high, update latest modem info to laptop */
619 if (port->cbits_to_modem & TIOCM_DTR) {
620 unsigned i;
621
622 i = smd_tiocmget(port->pi->ch);
623 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
624
625 if (gser->send_modem_ctrl_bits)
626 gser->send_modem_ctrl_bits(
627 port->port_usb,
628 port->cbits_to_laptop);
629 }
630
631 smd_tiocmset(port->pi->ch,
632 port->cbits_to_modem,
633 ~port->cbits_to_modem);
634}
635
636int gsmd_connect(struct gserial *gser, u8 portno)
637{
638 unsigned long flags;
639 int ret;
640 struct gsmd_port *port;
641
642 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
643
644 if (portno >= n_smd_ports) {
645 pr_err("%s: Invalid port no#%d", __func__, portno);
646 return -EINVAL;
647 }
648
649 if (!gser) {
650 pr_err("%s: gser is null\n", __func__);
651 return -EINVAL;
652 }
653
654 port = smd_ports[portno].port;
655
656 spin_lock_irqsave(&port->port_lock, flags);
657 port->port_usb = gser;
658 gser->notify_modem = gsmd_notify_modem;
659 port->nbytes_tomodem = 0;
660 port->nbytes_tolaptop = 0;
661 spin_unlock_irqrestore(&port->port_lock, flags);
662
663 ret = usb_ep_enable(gser->in, gser->in_desc);
664 if (ret) {
665 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
666 __func__, gser->in);
667 port->port_usb = 0;
668 return ret;
669 }
670 gser->in->driver_data = port;
671
672 ret = usb_ep_enable(gser->out, gser->out_desc);
673 if (ret) {
674 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
675 __func__, gser->out);
676 port->port_usb = 0;
677 gser->in->driver_data = 0;
678 return ret;
679 }
680 gser->out->driver_data = port;
681
682 queue_work(gsmd_wq, &port->connect_work);
683
684 return 0;
685}
686
687void gsmd_disconnect(struct gserial *gser, u8 portno)
688{
689 unsigned long flags;
690 struct gsmd_port *port;
691
692 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
693
694 if (portno >= n_smd_ports) {
695 pr_err("%s: invalid portno#%d\n", __func__, portno);
696 return;
697 }
698
699 if (!gser) {
700 pr_err("%s: gser is null\n", __func__);
701 return;
702 }
703
704 port = smd_ports[portno].port;
705
706 spin_lock_irqsave(&port->port_lock, flags);
707 port->port_usb = 0;
708 spin_unlock_irqrestore(&port->port_lock, flags);
709
710 /* disable endpoints, aborting down any active I/O */
711 usb_ep_disable(gser->out);
712 usb_ep_disable(gser->in);
713
714 spin_lock_irqsave(&port->port_lock, flags);
715 gsmd_free_requests(gser->out, &port->read_pool);
716 gsmd_free_requests(gser->out, &port->read_queue);
717 gsmd_free_requests(gser->in, &port->write_pool);
718 port->n_read = 0;
719 spin_unlock_irqrestore(&port->port_lock, flags);
720
721 if (!test_bit(CH_OPENED, &port->pi->flags))
722 return;
723
724 /* lower the dtr */
725 port->cbits_to_modem = 0;
726 smd_tiocmset(port->pi->ch,
727 port->cbits_to_modem,
728 ~port->cbits_to_modem);
729
730 smd_close(port->pi->ch);
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700731 clear_bit(CH_OPENED, &port->pi->flags);
732}
733
734#define SMD_CH_MAX_LEN 20
735static int gsmd_ch_probe(struct platform_device *pdev)
736{
737 struct gsmd_port *port;
738 struct smd_port_info *pi;
739 int i;
740 unsigned long flags;
741
742 pr_debug("%s: name:%s\n", __func__, pdev->name);
743
744 for (i = 0; i < n_smd_ports; i++) {
745 port = smd_ports[i].port;
746 pi = port->pi;
747
748 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
749 set_bit(CH_READY, &pi->flags);
750 spin_lock_irqsave(&port->port_lock, flags);
751 if (port->port_usb)
752 queue_work(gsmd_wq, &port->connect_work);
753 spin_unlock_irqrestore(&port->port_lock, flags);
754 break;
755 }
756 }
757 return 0;
758}
759
760static int gsmd_ch_remove(struct platform_device *pdev)
761{
762 struct gsmd_port *port;
763 struct smd_port_info *pi;
764 int i;
765
766 pr_debug("%s: name:%s\n", __func__, pdev->name);
767
768 for (i = 0; i < n_smd_ports; i++) {
769 port = smd_ports[i].port;
770 pi = port->pi;
771
772 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
773 clear_bit(CH_READY, &pi->flags);
774 clear_bit(CH_OPENED, &pi->flags);
775 smd_close(pi->ch);
776 break;
777 }
778 }
779 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700780}
781
782static void gsmd_port_free(int portno)
783{
784 struct gsmd_port *port = smd_ports[portno].port;
785
786 if (!port)
787 kfree(port);
788}
789
790static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
791{
792 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700793 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794
795 port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
796 if (!port)
797 return -ENOMEM;
798
799 port->port_num = portno;
800 port->pi = &smd_pi[portno];
801
802 spin_lock_init(&port->port_lock);
803
804 INIT_LIST_HEAD(&port->read_pool);
805 INIT_LIST_HEAD(&port->read_queue);
806 INIT_WORK(&port->push, gsmd_rx_push);
807
808 INIT_LIST_HEAD(&port->write_pool);
809 INIT_WORK(&port->pull, gsmd_tx_pull);
810
811 INIT_WORK(&port->connect_work, gsmd_connect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812
813 smd_ports[portno].port = port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700814 pdrv = &smd_ports[portno].pdrv;
815 pdrv->probe = gsmd_ch_probe;
816 pdrv->remove = gsmd_ch_remove;
817 pdrv->driver.name = port->pi->name;
818 pdrv->driver.owner = THIS_MODULE;
819 platform_driver_register(pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820
821 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
822
823 return 0;
824}
825
826#if defined(CONFIG_DEBUG_FS)
827static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
828 size_t count, loff_t *ppos)
829{
830 struct gsmd_port *port;
831 char *buf;
832 unsigned long flags;
833 int temp = 0;
834 int i;
835 int ret;
836
837 buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
838 if (!buf)
839 return -ENOMEM;
840
841 for (i = 0; i < n_smd_ports; i++) {
842 port = smd_ports[i].port;
843 spin_lock_irqsave(&port->port_lock, flags);
844 temp += scnprintf(buf + temp, 512 - temp,
845 "###PORT:%d###\n"
846 "nbytes_tolaptop: %lu\n"
847 "nbytes_tomodem: %lu\n"
848 "cbits_to_modem: %u\n"
849 "cbits_to_laptop: %u\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700850 "n_read: %u\n"
851 "CH_OPENED: %d\n"
852 "CH_READY: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700853 i, port->nbytes_tolaptop, port->nbytes_tomodem,
854 port->cbits_to_modem, port->cbits_to_laptop,
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700855 port->n_read,
856 test_bit(CH_OPENED, &port->pi->flags),
857 test_bit(CH_READY, &port->pi->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700858 spin_unlock_irqrestore(&port->port_lock, flags);
859 }
860
861 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
862
863 kfree(buf);
864
865 return ret;
866
867}
868
869static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
870 size_t count, loff_t *ppos)
871{
872 struct gsmd_port *port;
873 unsigned long flags;
874 int i;
875
876 for (i = 0; i < n_smd_ports; i++) {
877 port = smd_ports[i].port;
878
879 spin_lock_irqsave(&port->port_lock, flags);
880 port->nbytes_tolaptop = 0;
881 port->nbytes_tomodem = 0;
882 spin_unlock_irqrestore(&port->port_lock, flags);
883 }
884
885 return count;
886}
887
888static int debug_smd_open(struct inode *inode, struct file *file)
889{
890 return 0;
891}
892
893static const struct file_operations debug_gsmd_ops = {
894 .open = debug_smd_open,
895 .read = debug_smd_read_stats,
896 .write = debug_smd_reset_stats,
897};
898
899static void gsmd_debugfs_init(void)
900{
901 struct dentry *dent;
902
903 dent = debugfs_create_dir("usb_gsmd", 0);
904 if (IS_ERR(dent))
905 return;
906
907 debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
908}
909#else
910static void gsmd_debugfs_init(void) {}
911#endif
912
913int gsmd_setup(struct usb_gadget *g, unsigned count)
914{
915 struct usb_cdc_line_coding coding;
916 int ret;
917 int i;
918
919 pr_debug("%s: g:%p count: %d\n", __func__, g, count);
920
921 if (!count || count > SMD_N_PORTS) {
922 pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
923 __func__, count, g);
924 return -EINVAL;
925 }
926
927 coding.dwDTERate = cpu_to_le32(9600);
928 coding.bCharFormat = 8;
929 coding.bParityType = USB_CDC_NO_PARITY;
930 coding.bDataBits = USB_CDC_1_STOP_BITS;
931
932 gsmd_wq = create_singlethread_workqueue("k_gsmd");
933 if (!gsmd_wq) {
934 pr_err("%s: Unable to create workqueue gsmd_wq\n",
935 __func__);
936 return -ENOMEM;
937 }
938
939 for (i = 0; i < count; i++) {
940 mutex_init(&smd_ports[i].lock);
Manu Gautam4bd21422011-09-09 14:55:32 +0530941 n_smd_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942 ret = gsmd_port_alloc(i, &coding);
943 if (ret) {
Manu Gautam4bd21422011-09-09 14:55:32 +0530944 n_smd_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
946 goto free_smd_ports;
947 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700948 }
949
950 gsmd_debugfs_init();
951
952 return 0;
953free_smd_ports:
954 for (i = 0; i < n_smd_ports; i++)
955 gsmd_port_free(i);
956
957 destroy_workqueue(gsmd_wq);
958
959 return ret;
960}
961
962void gsmd_cleanup(struct usb_gadget *g, unsigned count)
963{
964 /* TBD */
965}