blob: c4a5ce4759da85df6de3aa8b724bcd4e69f49dd3 [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);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700366 if (!test_bit(CH_OPENED, &port->pi->flags) ||
367 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700368 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700369 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700370 return;
371 }
372
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 list_add_tail(&req->list, &port->read_queue);
374 queue_work(gsmd_wq, &port->push);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700375 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376
377 return;
378}
379
380static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
381{
382 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383
384 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
385
386 if (!port) {
387 pr_err("%s: port is null\n", __func__);
388 return;
389 }
390
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700391 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700392 if (!test_bit(CH_OPENED, &port->pi->flags) ||
393 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700394 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700395 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700396 return;
397 }
398
Vamsi Krishna396562a2011-08-25 11:39:09 -0700399 if (req->status)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400 pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
401 __func__, port, port->port_num,
402 ep->name, req->status);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403
Vamsi Krishna396562a2011-08-25 11:39:09 -0700404 list_add(&req->list, &port->write_pool);
405 queue_work(gsmd_wq, &port->pull);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700406 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
408 return;
409}
410
411static void gsmd_start_io(struct gsmd_port *port)
412{
413 int ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414
415 pr_debug("%s: port: %p\n", __func__, port);
416
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700417 spin_lock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418
419 if (!port->port_usb)
420 goto start_io_out;
421
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700422 smd_tiocmset_from_cb(port->pi->ch,
423 port->cbits_to_modem,
424 ~port->cbits_to_modem);
425
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426 ret = gsmd_alloc_requests(port->port_usb->out,
427 &port->read_pool,
428 SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
429 gsmd_read_complete);
430 if (ret) {
431 pr_err("%s: unable to allocate out requests\n",
432 __func__);
433 goto start_io_out;
434 }
435
436 ret = gsmd_alloc_requests(port->port_usb->in,
437 &port->write_pool,
438 SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
439 gsmd_write_complete);
440 if (ret) {
441 gsmd_free_requests(port->port_usb->out, &port->read_pool);
442 pr_err("%s: unable to allocate IN requests\n",
443 __func__);
444 goto start_io_out;
445 }
446
447start_io_out:
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700448 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449
450 if (ret)
451 return;
452
453 gsmd_start_rx(port);
454}
455
456static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
457{
458 unsigned int acm_sig = 0;
459
460 /* should this needs to be in calling functions ??? */
461 uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
462
463 if (uart_sig & TIOCM_RI)
464 acm_sig |= SMD_ACM_CTRL_RI;
465 if (uart_sig & TIOCM_CD)
466 acm_sig |= SMD_ACM_CTRL_DCD;
467 if (uart_sig & TIOCM_DSR)
468 acm_sig |= SMD_ACM_CTRL_DSR;
469
470 return acm_sig;
471}
472
473static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
474{
475 unsigned int uart_sig = 0;
476
477 /* should this needs to be in calling functions ??? */
478 acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
479
480 if (acm_sig & SMD_ACM_CTRL_DTR)
481 uart_sig |= TIOCM_DTR;
482 if (acm_sig & SMD_ACM_CTRL_RTS)
483 uart_sig |= TIOCM_RTS;
484
485 return uart_sig;
486}
487
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700488
489static void gsmd_stop_io(struct gsmd_port *port)
490{
491 struct usb_ep *in;
492 struct usb_ep *out;
493 unsigned long flags;
494
495 spin_lock_irqsave(&port->port_lock, flags);
496 if (!port->port_usb) {
497 spin_unlock_irqrestore(&port->port_lock, flags);
498 return;
499 }
500 in = port->port_usb->in;
501 out = port->port_usb->out;
502 spin_unlock_irqrestore(&port->port_lock, flags);
503
504 usb_ep_fifo_flush(in);
505 usb_ep_fifo_flush(out);
506
507 spin_lock(&port->port_lock);
508 if (port->port_usb) {
509 gsmd_free_requests(out, &port->read_pool);
510 gsmd_free_requests(out, &port->read_queue);
511 gsmd_free_requests(in, &port->write_pool);
512 port->n_read = 0;
513 port->cbits_to_laptop = 0;
514 }
515
516 if (port->port_usb->send_modem_ctrl_bits)
517 port->port_usb->send_modem_ctrl_bits(
518 port->port_usb,
519 port->cbits_to_laptop);
520 spin_unlock(&port->port_lock);
521
522}
523
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524static void gsmd_notify(void *priv, unsigned event)
525{
526 struct gsmd_port *port = priv;
527 struct smd_port_info *pi = port->pi;
528 int i;
529
530 switch (event) {
531 case SMD_EVENT_DATA:
532 pr_debug("%s: Event data\n", __func__);
533 if (smd_read_avail(pi->ch))
534 queue_work(gsmd_wq, &port->pull);
535 if (smd_write_avail(pi->ch))
536 queue_work(gsmd_wq, &port->push);
537 break;
538 case SMD_EVENT_OPEN:
539 pr_debug("%s: Event Open\n", __func__);
540 set_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700541 gsmd_start_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 break;
543 case SMD_EVENT_CLOSE:
544 pr_debug("%s: Event Close\n", __func__);
545 clear_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700546 gsmd_stop_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 break;
548 case SMD_EVENT_STATUS:
549 i = smd_tiocmget(port->pi->ch);
550 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
551 if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
552 port->port_usb->send_modem_ctrl_bits(port->port_usb,
553 port->cbits_to_laptop);
554 break;
555 }
556}
557
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558static void gsmd_connect_work(struct work_struct *w)
559{
560 struct gsmd_port *port;
561 struct smd_port_info *pi;
562 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563
564 port = container_of(w, struct gsmd_port, connect_work);
565 pi = port->pi;
566
567 pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
568
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700569 if (!test_bit(CH_READY, &pi->flags))
570 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700572 ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
573 &pi->ch, port, gsmd_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574 if (ret) {
575 pr_err("%s: unable to open smd port:%s err:%d\n",
576 __func__, pi->name, ret);
577 return;
578 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579}
580
581static void gsmd_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits)
582{
583 struct gsmd_port *port;
584 int temp;
585
586 if (portno >= n_smd_ports) {
587 pr_err("%s: invalid portno#%d\n", __func__, portno);
588 return;
589 }
590
591 if (!gser) {
592 pr_err("%s: gser is null\n", __func__);
593 return;
594 }
595
596 port = smd_ports[portno].port;
597
598 temp = convert_acm_sigs_to_uart(ctrl_bits);
599
600 if (temp == port->cbits_to_modem)
601 return;
602
603 port->cbits_to_modem = temp;
604
605 /* usb could send control signal before smd is ready */
606 if (!test_bit(CH_OPENED, &port->pi->flags))
607 return;
608
609 /* if DTR is high, update latest modem info to laptop */
610 if (port->cbits_to_modem & TIOCM_DTR) {
611 unsigned i;
612
613 i = smd_tiocmget(port->pi->ch);
614 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
615
616 if (gser->send_modem_ctrl_bits)
617 gser->send_modem_ctrl_bits(
618 port->port_usb,
619 port->cbits_to_laptop);
620 }
621
622 smd_tiocmset(port->pi->ch,
623 port->cbits_to_modem,
624 ~port->cbits_to_modem);
625}
626
627int gsmd_connect(struct gserial *gser, u8 portno)
628{
629 unsigned long flags;
630 int ret;
631 struct gsmd_port *port;
632
633 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
634
635 if (portno >= n_smd_ports) {
636 pr_err("%s: Invalid port no#%d", __func__, portno);
637 return -EINVAL;
638 }
639
640 if (!gser) {
641 pr_err("%s: gser is null\n", __func__);
642 return -EINVAL;
643 }
644
645 port = smd_ports[portno].port;
646
647 spin_lock_irqsave(&port->port_lock, flags);
648 port->port_usb = gser;
649 gser->notify_modem = gsmd_notify_modem;
650 port->nbytes_tomodem = 0;
651 port->nbytes_tolaptop = 0;
652 spin_unlock_irqrestore(&port->port_lock, flags);
653
654 ret = usb_ep_enable(gser->in, gser->in_desc);
655 if (ret) {
656 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
657 __func__, gser->in);
658 port->port_usb = 0;
659 return ret;
660 }
661 gser->in->driver_data = port;
662
663 ret = usb_ep_enable(gser->out, gser->out_desc);
664 if (ret) {
665 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
666 __func__, gser->out);
667 port->port_usb = 0;
668 gser->in->driver_data = 0;
669 return ret;
670 }
671 gser->out->driver_data = port;
672
673 queue_work(gsmd_wq, &port->connect_work);
674
675 return 0;
676}
677
678void gsmd_disconnect(struct gserial *gser, u8 portno)
679{
680 unsigned long flags;
681 struct gsmd_port *port;
682
683 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
684
685 if (portno >= n_smd_ports) {
686 pr_err("%s: invalid portno#%d\n", __func__, portno);
687 return;
688 }
689
690 if (!gser) {
691 pr_err("%s: gser is null\n", __func__);
692 return;
693 }
694
695 port = smd_ports[portno].port;
696
697 spin_lock_irqsave(&port->port_lock, flags);
698 port->port_usb = 0;
699 spin_unlock_irqrestore(&port->port_lock, flags);
700
701 /* disable endpoints, aborting down any active I/O */
702 usb_ep_disable(gser->out);
703 usb_ep_disable(gser->in);
704
705 spin_lock_irqsave(&port->port_lock, flags);
706 gsmd_free_requests(gser->out, &port->read_pool);
707 gsmd_free_requests(gser->out, &port->read_queue);
708 gsmd_free_requests(gser->in, &port->write_pool);
709 port->n_read = 0;
710 spin_unlock_irqrestore(&port->port_lock, flags);
711
712 if (!test_bit(CH_OPENED, &port->pi->flags))
713 return;
714
715 /* lower the dtr */
716 port->cbits_to_modem = 0;
717 smd_tiocmset(port->pi->ch,
718 port->cbits_to_modem,
719 ~port->cbits_to_modem);
720
721 smd_close(port->pi->ch);
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700722 clear_bit(CH_OPENED, &port->pi->flags);
723}
724
725#define SMD_CH_MAX_LEN 20
726static int gsmd_ch_probe(struct platform_device *pdev)
727{
728 struct gsmd_port *port;
729 struct smd_port_info *pi;
730 int i;
731 unsigned long flags;
732
733 pr_debug("%s: name:%s\n", __func__, pdev->name);
734
735 for (i = 0; i < n_smd_ports; i++) {
736 port = smd_ports[i].port;
737 pi = port->pi;
738
739 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
740 set_bit(CH_READY, &pi->flags);
741 spin_lock_irqsave(&port->port_lock, flags);
742 if (port->port_usb)
743 queue_work(gsmd_wq, &port->connect_work);
744 spin_unlock_irqrestore(&port->port_lock, flags);
745 break;
746 }
747 }
748 return 0;
749}
750
751static int gsmd_ch_remove(struct platform_device *pdev)
752{
753 struct gsmd_port *port;
754 struct smd_port_info *pi;
755 int i;
756
757 pr_debug("%s: name:%s\n", __func__, pdev->name);
758
759 for (i = 0; i < n_smd_ports; i++) {
760 port = smd_ports[i].port;
761 pi = port->pi;
762
763 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
764 clear_bit(CH_READY, &pi->flags);
765 clear_bit(CH_OPENED, &pi->flags);
766 smd_close(pi->ch);
767 break;
768 }
769 }
770 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700771}
772
773static void gsmd_port_free(int portno)
774{
775 struct gsmd_port *port = smd_ports[portno].port;
776
777 if (!port)
778 kfree(port);
779}
780
781static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
782{
783 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700784 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785
786 port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
787 if (!port)
788 return -ENOMEM;
789
790 port->port_num = portno;
791 port->pi = &smd_pi[portno];
792
793 spin_lock_init(&port->port_lock);
794
795 INIT_LIST_HEAD(&port->read_pool);
796 INIT_LIST_HEAD(&port->read_queue);
797 INIT_WORK(&port->push, gsmd_rx_push);
798
799 INIT_LIST_HEAD(&port->write_pool);
800 INIT_WORK(&port->pull, gsmd_tx_pull);
801
802 INIT_WORK(&port->connect_work, gsmd_connect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803
804 smd_ports[portno].port = port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700805 pdrv = &smd_ports[portno].pdrv;
806 pdrv->probe = gsmd_ch_probe;
807 pdrv->remove = gsmd_ch_remove;
808 pdrv->driver.name = port->pi->name;
809 pdrv->driver.owner = THIS_MODULE;
810 platform_driver_register(pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700811
812 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
813
814 return 0;
815}
816
817#if defined(CONFIG_DEBUG_FS)
818static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
819 size_t count, loff_t *ppos)
820{
821 struct gsmd_port *port;
822 char *buf;
823 unsigned long flags;
824 int temp = 0;
825 int i;
826 int ret;
827
828 buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
829 if (!buf)
830 return -ENOMEM;
831
832 for (i = 0; i < n_smd_ports; i++) {
833 port = smd_ports[i].port;
834 spin_lock_irqsave(&port->port_lock, flags);
835 temp += scnprintf(buf + temp, 512 - temp,
836 "###PORT:%d###\n"
837 "nbytes_tolaptop: %lu\n"
838 "nbytes_tomodem: %lu\n"
839 "cbits_to_modem: %u\n"
840 "cbits_to_laptop: %u\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700841 "n_read: %u\n"
Vamsi Krishna396562a2011-08-25 11:39:09 -0700842 "smd_read_avail: %d\n"
843 "smd_write_avail: %d\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700844 "CH_OPENED: %d\n"
845 "CH_READY: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700846 i, port->nbytes_tolaptop, port->nbytes_tomodem,
847 port->cbits_to_modem, port->cbits_to_laptop,
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700848 port->n_read,
Vamsi Krishna396562a2011-08-25 11:39:09 -0700849 smd_read_avail(port->pi->ch),
850 smd_write_avail(port->pi->ch),
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700851 test_bit(CH_OPENED, &port->pi->flags),
852 test_bit(CH_READY, &port->pi->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700853 spin_unlock_irqrestore(&port->port_lock, flags);
854 }
855
856 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
857
858 kfree(buf);
859
860 return ret;
861
862}
863
864static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
865 size_t count, loff_t *ppos)
866{
867 struct gsmd_port *port;
868 unsigned long flags;
869 int i;
870
871 for (i = 0; i < n_smd_ports; i++) {
872 port = smd_ports[i].port;
873
874 spin_lock_irqsave(&port->port_lock, flags);
875 port->nbytes_tolaptop = 0;
876 port->nbytes_tomodem = 0;
877 spin_unlock_irqrestore(&port->port_lock, flags);
878 }
879
880 return count;
881}
882
883static int debug_smd_open(struct inode *inode, struct file *file)
884{
885 return 0;
886}
887
888static const struct file_operations debug_gsmd_ops = {
889 .open = debug_smd_open,
890 .read = debug_smd_read_stats,
891 .write = debug_smd_reset_stats,
892};
893
894static void gsmd_debugfs_init(void)
895{
896 struct dentry *dent;
897
898 dent = debugfs_create_dir("usb_gsmd", 0);
899 if (IS_ERR(dent))
900 return;
901
902 debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
903}
904#else
905static void gsmd_debugfs_init(void) {}
906#endif
907
908int gsmd_setup(struct usb_gadget *g, unsigned count)
909{
910 struct usb_cdc_line_coding coding;
911 int ret;
912 int i;
913
914 pr_debug("%s: g:%p count: %d\n", __func__, g, count);
915
916 if (!count || count > SMD_N_PORTS) {
917 pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
918 __func__, count, g);
919 return -EINVAL;
920 }
921
922 coding.dwDTERate = cpu_to_le32(9600);
923 coding.bCharFormat = 8;
924 coding.bParityType = USB_CDC_NO_PARITY;
925 coding.bDataBits = USB_CDC_1_STOP_BITS;
926
927 gsmd_wq = create_singlethread_workqueue("k_gsmd");
928 if (!gsmd_wq) {
929 pr_err("%s: Unable to create workqueue gsmd_wq\n",
930 __func__);
931 return -ENOMEM;
932 }
933
934 for (i = 0; i < count; i++) {
935 mutex_init(&smd_ports[i].lock);
Manu Gautam4bd21422011-09-09 14:55:32 +0530936 n_smd_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700937 ret = gsmd_port_alloc(i, &coding);
938 if (ret) {
Manu Gautam4bd21422011-09-09 14:55:32 +0530939 n_smd_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
941 goto free_smd_ports;
942 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943 }
944
945 gsmd_debugfs_init();
946
947 return 0;
948free_smd_ports:
949 for (i = 0; i < n_smd_ports; i++)
950 gsmd_port_free(i);
951
952 destroy_workqueue(gsmd_wq);
953
954 return ret;
955}
956
957void gsmd_cleanup(struct usb_gadget *g, unsigned count)
958{
959 /* TBD */
960}