blob: caccadecd70476d3d37630d30b9ecc0ef782c5fe [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;
Eric Holmberg0c55b742011-09-29 10:56:08 -0600169 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170 int ret;
171
172 if (!port) {
173 pr_err("%s: port is null\n", __func__);
174 return;
175 }
176
Eric Holmberg0c55b742011-09-29 10:56:08 -0600177 spin_lock_irqsave(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178
179 if (!port->port_usb) {
180 pr_debug("%s: USB disconnected\n", __func__);
181 goto start_rx_end;
182 }
183
184 pool = &port->read_pool;
185 out = port->port_usb->out;
186
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700187 while (test_bit(CH_OPENED, &port->pi->flags) && !list_empty(pool)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188 struct usb_request *req;
189
190 req = list_entry(pool->next, struct usb_request, list);
191 list_del(&req->list);
192 req->length = SMD_RX_BUF_SIZE;
193
Eric Holmberg0c55b742011-09-29 10:56:08 -0600194 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 ret = usb_ep_queue(out, req, GFP_KERNEL);
Eric Holmberg0c55b742011-09-29 10:56:08 -0600196 spin_lock_irqsave(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 if (ret) {
198 pr_err("%s: usb ep out queue failed"
199 "port:%p, port#%d\n",
200 __func__, port, port->port_num);
201 list_add_tail(&req->list, pool);
202 break;
203 }
204 }
205start_rx_end:
Eric Holmberg0c55b742011-09-29 10:56:08 -0600206 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207}
208
209static void gsmd_rx_push(struct work_struct *w)
210{
211 struct gsmd_port *port = container_of(w, struct gsmd_port, push);
212 struct list_head *q;
213
214 pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
215
216 spin_lock_irq(&port->port_lock);
217
218 q = &port->read_queue;
219 while (!list_empty(q)) {
220 struct usb_request *req;
221 int avail;
222 struct smd_port_info *pi = port->pi;
223
224 req = list_first_entry(q, struct usb_request, list);
225
226 switch (req->status) {
227 case -ESHUTDOWN:
228 pr_debug("%s: req status shutdown portno#%d port:%p\n",
229 __func__, port->port_num, port);
230 goto rx_push_end;
231 default:
232 pr_warning("%s: port:%p port#%d"
233 " Unexpected Rx Status:%d\n", __func__,
234 port, port->port_num, req->status);
235 case 0:
236 /* normal completion */
237 break;
238 }
239
240 avail = smd_write_avail(pi->ch);
241 if (!avail)
242 goto rx_push_end;
243
244 if (req->actual) {
245 char *packet = req->buf;
246 unsigned size = req->actual;
247 unsigned n;
248 unsigned count;
249
250 n = port->n_read;
251 if (n) {
252 packet += n;
253 size -= n;
254 }
255
256 count = smd_write(pi->ch, packet, size);
257 if (count < 0) {
258 pr_err("%s: smd write failed err:%d\n",
259 __func__, count);
260 goto rx_push_end;
261 }
262
263 if (count != size) {
264 port->n_read += count;
265 goto rx_push_end;
266 }
267
268 port->nbytes_tomodem += count;
269 }
270
271 port->n_read = 0;
272 list_move(&req->list, &port->read_pool);
273 }
274
275rx_push_end:
276 spin_unlock_irq(&port->port_lock);
277
278 gsmd_start_rx(port);
279}
280
281static void gsmd_read_pending(struct gsmd_port *port)
282{
283 int avail;
284
285 if (!port || !port->pi->ch)
286 return;
287
288 /* passing null buffer discards the data */
289 while ((avail = smd_read_avail(port->pi->ch)))
290 smd_read(port->pi->ch, 0, avail);
291
292 return;
293}
294
295static void gsmd_tx_pull(struct work_struct *w)
296{
297 struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
298 struct list_head *pool = &port->write_pool;
299
300 pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
301 port, port->port_num, pool);
302
303 if (!port->port_usb) {
304 pr_debug("%s: usb is disconnected\n", __func__);
305 gsmd_read_pending(port);
306 return;
307 }
308
309 spin_lock_irq(&port->port_lock);
310 while (!list_empty(pool)) {
311 struct usb_request *req;
312 struct usb_ep *in = port->port_usb->in;
313 struct smd_port_info *pi = port->pi;
314 int avail;
315 int ret;
316
317 avail = smd_read_avail(pi->ch);
318 if (!avail)
319 break;
320
321 avail = avail > SMD_TX_BUF_SIZE ? SMD_TX_BUF_SIZE : avail;
322
323 req = list_entry(pool->next, struct usb_request, list);
324 list_del(&req->list);
325 req->length = smd_read(pi->ch, req->buf, avail);
326
327 spin_unlock_irq(&port->port_lock);
328 ret = usb_ep_queue(in, req, GFP_KERNEL);
329 spin_lock_irq(&port->port_lock);
330 if (ret) {
331 pr_err("%s: usb ep out queue failed"
332 "port:%p, port#%d err:%d\n",
333 __func__, port, port->port_num, ret);
334 /* could be usb disconnected */
335 if (!port->port_usb)
336 gsmd_free_req(in, req);
337 else
338 list_add(&req->list, pool);
339 goto tx_pull_end;
340 }
341
342 port->nbytes_tolaptop += req->length;
343 }
344
345tx_pull_end:
346 /* TBD: Check how code behaves on USB bus suspend */
347 if (port->port_usb && smd_read_avail(port->pi->ch) && !list_empty(pool))
348 queue_work(gsmd_wq, &port->pull);
349
350 spin_unlock_irq(&port->port_lock);
351
352 return;
353}
354
355static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
356{
357 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358
359 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
360
361 if (!port) {
362 pr_err("%s: port is null\n", __func__);
363 return;
364 }
365
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700366 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700367 if (!test_bit(CH_OPENED, &port->pi->flags) ||
368 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700369 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700370 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700371 return;
372 }
373
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374 list_add_tail(&req->list, &port->read_queue);
375 queue_work(gsmd_wq, &port->push);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700376 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377
378 return;
379}
380
381static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
382{
383 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384
385 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
386
387 if (!port) {
388 pr_err("%s: port is null\n", __func__);
389 return;
390 }
391
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700392 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700393 if (!test_bit(CH_OPENED, &port->pi->flags) ||
394 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700395 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700396 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700397 return;
398 }
399
Vamsi Krishna396562a2011-08-25 11:39:09 -0700400 if (req->status)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
402 __func__, port, port->port_num,
403 ep->name, req->status);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404
Vamsi Krishna396562a2011-08-25 11:39:09 -0700405 list_add(&req->list, &port->write_pool);
406 queue_work(gsmd_wq, &port->pull);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700407 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408
409 return;
410}
411
412static void gsmd_start_io(struct gsmd_port *port)
413{
414 int ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415
416 pr_debug("%s: port: %p\n", __func__, port);
417
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700418 spin_lock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419
420 if (!port->port_usb)
421 goto start_io_out;
422
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700423 smd_tiocmset_from_cb(port->pi->ch,
424 port->cbits_to_modem,
425 ~port->cbits_to_modem);
426
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427 ret = gsmd_alloc_requests(port->port_usb->out,
428 &port->read_pool,
429 SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
430 gsmd_read_complete);
431 if (ret) {
432 pr_err("%s: unable to allocate out requests\n",
433 __func__);
434 goto start_io_out;
435 }
436
437 ret = gsmd_alloc_requests(port->port_usb->in,
438 &port->write_pool,
439 SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
440 gsmd_write_complete);
441 if (ret) {
442 gsmd_free_requests(port->port_usb->out, &port->read_pool);
443 pr_err("%s: unable to allocate IN requests\n",
444 __func__);
445 goto start_io_out;
446 }
447
448start_io_out:
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700449 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450
451 if (ret)
452 return;
453
454 gsmd_start_rx(port);
455}
456
457static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
458{
459 unsigned int acm_sig = 0;
460
461 /* should this needs to be in calling functions ??? */
462 uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
463
464 if (uart_sig & TIOCM_RI)
465 acm_sig |= SMD_ACM_CTRL_RI;
466 if (uart_sig & TIOCM_CD)
467 acm_sig |= SMD_ACM_CTRL_DCD;
468 if (uart_sig & TIOCM_DSR)
469 acm_sig |= SMD_ACM_CTRL_DSR;
470
471 return acm_sig;
472}
473
474static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
475{
476 unsigned int uart_sig = 0;
477
478 /* should this needs to be in calling functions ??? */
479 acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
480
481 if (acm_sig & SMD_ACM_CTRL_DTR)
482 uart_sig |= TIOCM_DTR;
483 if (acm_sig & SMD_ACM_CTRL_RTS)
484 uart_sig |= TIOCM_RTS;
485
486 return uart_sig;
487}
488
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700489
490static void gsmd_stop_io(struct gsmd_port *port)
491{
492 struct usb_ep *in;
493 struct usb_ep *out;
494 unsigned long flags;
495
496 spin_lock_irqsave(&port->port_lock, flags);
497 if (!port->port_usb) {
498 spin_unlock_irqrestore(&port->port_lock, flags);
499 return;
500 }
501 in = port->port_usb->in;
502 out = port->port_usb->out;
503 spin_unlock_irqrestore(&port->port_lock, flags);
504
505 usb_ep_fifo_flush(in);
506 usb_ep_fifo_flush(out);
507
508 spin_lock(&port->port_lock);
509 if (port->port_usb) {
510 gsmd_free_requests(out, &port->read_pool);
511 gsmd_free_requests(out, &port->read_queue);
512 gsmd_free_requests(in, &port->write_pool);
513 port->n_read = 0;
514 port->cbits_to_laptop = 0;
515 }
516
517 if (port->port_usb->send_modem_ctrl_bits)
518 port->port_usb->send_modem_ctrl_bits(
519 port->port_usb,
520 port->cbits_to_laptop);
521 spin_unlock(&port->port_lock);
522
523}
524
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525static void gsmd_notify(void *priv, unsigned event)
526{
527 struct gsmd_port *port = priv;
528 struct smd_port_info *pi = port->pi;
529 int i;
530
531 switch (event) {
532 case SMD_EVENT_DATA:
533 pr_debug("%s: Event data\n", __func__);
534 if (smd_read_avail(pi->ch))
535 queue_work(gsmd_wq, &port->pull);
536 if (smd_write_avail(pi->ch))
537 queue_work(gsmd_wq, &port->push);
538 break;
539 case SMD_EVENT_OPEN:
540 pr_debug("%s: Event Open\n", __func__);
541 set_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700542 gsmd_start_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543 break;
544 case SMD_EVENT_CLOSE:
545 pr_debug("%s: Event Close\n", __func__);
546 clear_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700547 gsmd_stop_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 break;
549 case SMD_EVENT_STATUS:
550 i = smd_tiocmget(port->pi->ch);
551 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
552 if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
553 port->port_usb->send_modem_ctrl_bits(port->port_usb,
554 port->cbits_to_laptop);
555 break;
556 }
557}
558
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559static void gsmd_connect_work(struct work_struct *w)
560{
561 struct gsmd_port *port;
562 struct smd_port_info *pi;
563 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700564
565 port = container_of(w, struct gsmd_port, connect_work);
566 pi = port->pi;
567
568 pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
569
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700570 if (!test_bit(CH_READY, &pi->flags))
571 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700573 ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
574 &pi->ch, port, gsmd_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575 if (ret) {
576 pr_err("%s: unable to open smd port:%s err:%d\n",
577 __func__, pi->name, ret);
578 return;
579 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580}
581
Hemant Kumarf60c0252011-11-03 12:37:07 -0700582static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583{
584 struct gsmd_port *port;
585 int temp;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700586 struct gserial *gser = gptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587
588 if (portno >= n_smd_ports) {
589 pr_err("%s: invalid portno#%d\n", __func__, portno);
590 return;
591 }
592
593 if (!gser) {
594 pr_err("%s: gser is null\n", __func__);
595 return;
596 }
597
598 port = smd_ports[portno].port;
599
600 temp = convert_acm_sigs_to_uart(ctrl_bits);
601
602 if (temp == port->cbits_to_modem)
603 return;
604
605 port->cbits_to_modem = temp;
606
607 /* usb could send control signal before smd is ready */
608 if (!test_bit(CH_OPENED, &port->pi->flags))
609 return;
610
611 /* if DTR is high, update latest modem info to laptop */
612 if (port->cbits_to_modem & TIOCM_DTR) {
613 unsigned i;
614
615 i = smd_tiocmget(port->pi->ch);
616 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
617
618 if (gser->send_modem_ctrl_bits)
619 gser->send_modem_ctrl_bits(
620 port->port_usb,
621 port->cbits_to_laptop);
622 }
623
624 smd_tiocmset(port->pi->ch,
625 port->cbits_to_modem,
626 ~port->cbits_to_modem);
627}
628
629int gsmd_connect(struct gserial *gser, u8 portno)
630{
631 unsigned long flags;
632 int ret;
633 struct gsmd_port *port;
634
635 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
636
637 if (portno >= n_smd_ports) {
638 pr_err("%s: Invalid port no#%d", __func__, portno);
639 return -EINVAL;
640 }
641
642 if (!gser) {
643 pr_err("%s: gser is null\n", __func__);
644 return -EINVAL;
645 }
646
647 port = smd_ports[portno].port;
648
649 spin_lock_irqsave(&port->port_lock, flags);
650 port->port_usb = gser;
651 gser->notify_modem = gsmd_notify_modem;
652 port->nbytes_tomodem = 0;
653 port->nbytes_tolaptop = 0;
654 spin_unlock_irqrestore(&port->port_lock, flags);
655
656 ret = usb_ep_enable(gser->in, gser->in_desc);
657 if (ret) {
658 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
659 __func__, gser->in);
660 port->port_usb = 0;
661 return ret;
662 }
663 gser->in->driver_data = port;
664
665 ret = usb_ep_enable(gser->out, gser->out_desc);
666 if (ret) {
667 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
668 __func__, gser->out);
669 port->port_usb = 0;
670 gser->in->driver_data = 0;
671 return ret;
672 }
673 gser->out->driver_data = port;
674
675 queue_work(gsmd_wq, &port->connect_work);
676
677 return 0;
678}
679
680void gsmd_disconnect(struct gserial *gser, u8 portno)
681{
682 unsigned long flags;
683 struct gsmd_port *port;
684
685 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
686
687 if (portno >= n_smd_ports) {
688 pr_err("%s: invalid portno#%d\n", __func__, portno);
689 return;
690 }
691
692 if (!gser) {
693 pr_err("%s: gser is null\n", __func__);
694 return;
695 }
696
697 port = smd_ports[portno].port;
698
699 spin_lock_irqsave(&port->port_lock, flags);
700 port->port_usb = 0;
701 spin_unlock_irqrestore(&port->port_lock, flags);
702
703 /* disable endpoints, aborting down any active I/O */
704 usb_ep_disable(gser->out);
705 usb_ep_disable(gser->in);
706
707 spin_lock_irqsave(&port->port_lock, flags);
708 gsmd_free_requests(gser->out, &port->read_pool);
709 gsmd_free_requests(gser->out, &port->read_queue);
710 gsmd_free_requests(gser->in, &port->write_pool);
711 port->n_read = 0;
712 spin_unlock_irqrestore(&port->port_lock, flags);
713
714 if (!test_bit(CH_OPENED, &port->pi->flags))
715 return;
716
717 /* lower the dtr */
718 port->cbits_to_modem = 0;
719 smd_tiocmset(port->pi->ch,
720 port->cbits_to_modem,
721 ~port->cbits_to_modem);
722
723 smd_close(port->pi->ch);
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700724 clear_bit(CH_OPENED, &port->pi->flags);
725}
726
727#define SMD_CH_MAX_LEN 20
728static int gsmd_ch_probe(struct platform_device *pdev)
729{
730 struct gsmd_port *port;
731 struct smd_port_info *pi;
732 int i;
733 unsigned long flags;
734
735 pr_debug("%s: name:%s\n", __func__, pdev->name);
736
737 for (i = 0; i < n_smd_ports; i++) {
738 port = smd_ports[i].port;
739 pi = port->pi;
740
741 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
742 set_bit(CH_READY, &pi->flags);
743 spin_lock_irqsave(&port->port_lock, flags);
744 if (port->port_usb)
745 queue_work(gsmd_wq, &port->connect_work);
746 spin_unlock_irqrestore(&port->port_lock, flags);
747 break;
748 }
749 }
750 return 0;
751}
752
753static int gsmd_ch_remove(struct platform_device *pdev)
754{
755 struct gsmd_port *port;
756 struct smd_port_info *pi;
757 int i;
758
759 pr_debug("%s: name:%s\n", __func__, pdev->name);
760
761 for (i = 0; i < n_smd_ports; i++) {
762 port = smd_ports[i].port;
763 pi = port->pi;
764
765 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
766 clear_bit(CH_READY, &pi->flags);
767 clear_bit(CH_OPENED, &pi->flags);
768 smd_close(pi->ch);
769 break;
770 }
771 }
772 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700773}
774
775static void gsmd_port_free(int portno)
776{
777 struct gsmd_port *port = smd_ports[portno].port;
778
779 if (!port)
780 kfree(port);
781}
782
783static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
784{
785 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700786 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700787
788 port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
789 if (!port)
790 return -ENOMEM;
791
792 port->port_num = portno;
793 port->pi = &smd_pi[portno];
794
795 spin_lock_init(&port->port_lock);
796
797 INIT_LIST_HEAD(&port->read_pool);
798 INIT_LIST_HEAD(&port->read_queue);
799 INIT_WORK(&port->push, gsmd_rx_push);
800
801 INIT_LIST_HEAD(&port->write_pool);
802 INIT_WORK(&port->pull, gsmd_tx_pull);
803
804 INIT_WORK(&port->connect_work, gsmd_connect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700805
806 smd_ports[portno].port = port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700807 pdrv = &smd_ports[portno].pdrv;
808 pdrv->probe = gsmd_ch_probe;
809 pdrv->remove = gsmd_ch_remove;
810 pdrv->driver.name = port->pi->name;
811 pdrv->driver.owner = THIS_MODULE;
812 platform_driver_register(pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813
814 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
815
816 return 0;
817}
818
819#if defined(CONFIG_DEBUG_FS)
820static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
821 size_t count, loff_t *ppos)
822{
823 struct gsmd_port *port;
824 char *buf;
825 unsigned long flags;
826 int temp = 0;
827 int i;
828 int ret;
829
830 buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
831 if (!buf)
832 return -ENOMEM;
833
834 for (i = 0; i < n_smd_ports; i++) {
835 port = smd_ports[i].port;
836 spin_lock_irqsave(&port->port_lock, flags);
837 temp += scnprintf(buf + temp, 512 - temp,
838 "###PORT:%d###\n"
839 "nbytes_tolaptop: %lu\n"
840 "nbytes_tomodem: %lu\n"
841 "cbits_to_modem: %u\n"
842 "cbits_to_laptop: %u\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700843 "n_read: %u\n"
Vamsi Krishna396562a2011-08-25 11:39:09 -0700844 "smd_read_avail: %d\n"
845 "smd_write_avail: %d\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700846 "CH_OPENED: %d\n"
847 "CH_READY: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700848 i, port->nbytes_tolaptop, port->nbytes_tomodem,
849 port->cbits_to_modem, port->cbits_to_laptop,
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700850 port->n_read,
Vamsi Krishna396562a2011-08-25 11:39:09 -0700851 smd_read_avail(port->pi->ch),
852 smd_write_avail(port->pi->ch),
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700853 test_bit(CH_OPENED, &port->pi->flags),
854 test_bit(CH_READY, &port->pi->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855 spin_unlock_irqrestore(&port->port_lock, flags);
856 }
857
858 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
859
860 kfree(buf);
861
862 return ret;
863
864}
865
866static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
867 size_t count, loff_t *ppos)
868{
869 struct gsmd_port *port;
870 unsigned long flags;
871 int i;
872
873 for (i = 0; i < n_smd_ports; i++) {
874 port = smd_ports[i].port;
875
876 spin_lock_irqsave(&port->port_lock, flags);
877 port->nbytes_tolaptop = 0;
878 port->nbytes_tomodem = 0;
879 spin_unlock_irqrestore(&port->port_lock, flags);
880 }
881
882 return count;
883}
884
885static int debug_smd_open(struct inode *inode, struct file *file)
886{
887 return 0;
888}
889
890static const struct file_operations debug_gsmd_ops = {
891 .open = debug_smd_open,
892 .read = debug_smd_read_stats,
893 .write = debug_smd_reset_stats,
894};
895
896static void gsmd_debugfs_init(void)
897{
898 struct dentry *dent;
899
900 dent = debugfs_create_dir("usb_gsmd", 0);
901 if (IS_ERR(dent))
902 return;
903
904 debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
905}
906#else
907static void gsmd_debugfs_init(void) {}
908#endif
909
910int gsmd_setup(struct usb_gadget *g, unsigned count)
911{
912 struct usb_cdc_line_coding coding;
913 int ret;
914 int i;
915
916 pr_debug("%s: g:%p count: %d\n", __func__, g, count);
917
918 if (!count || count > SMD_N_PORTS) {
919 pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
920 __func__, count, g);
921 return -EINVAL;
922 }
923
924 coding.dwDTERate = cpu_to_le32(9600);
925 coding.bCharFormat = 8;
926 coding.bParityType = USB_CDC_NO_PARITY;
927 coding.bDataBits = USB_CDC_1_STOP_BITS;
928
929 gsmd_wq = create_singlethread_workqueue("k_gsmd");
930 if (!gsmd_wq) {
931 pr_err("%s: Unable to create workqueue gsmd_wq\n",
932 __func__);
933 return -ENOMEM;
934 }
935
936 for (i = 0; i < count; i++) {
937 mutex_init(&smd_ports[i].lock);
Manu Gautam4bd21422011-09-09 14:55:32 +0530938 n_smd_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 ret = gsmd_port_alloc(i, &coding);
940 if (ret) {
Manu Gautam4bd21422011-09-09 14:55:32 +0530941 n_smd_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
943 goto free_smd_ports;
944 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700945 }
946
947 gsmd_debugfs_init();
948
949 return 0;
950free_smd_ports:
951 for (i = 0; i < n_smd_ports; i++)
952 gsmd_port_free(i);
953
954 destroy_workqueue(gsmd_wq);
955
956 return ret;
957}
958
959void gsmd_cleanup(struct usb_gadget *g, unsigned count)
960{
961 /* TBD */
962}