blob: e74b2e1655853d2efdabaaefbc7411d4d8b11a0b [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;
Eric Holmberge72b2d42012-01-23 13:45:45 -070074 struct delayed_work connect_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075
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;
Vamsi Krishnab015f0c2012-03-16 14:14:41 -0700248 int count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249
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
Eric Holmberge72b2d42012-01-23 13:45:45 -0700565 port = container_of(w, struct gsmd_port, connect_work.work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566 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) {
Eric Holmberge72b2d42012-01-23 13:45:45 -0700576 if (ret == -EAGAIN) {
577 /* port not ready - retry */
578 pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
579 __func__, pi->name, ret);
580 queue_delayed_work(gsmd_wq, &port->connect_work,
581 msecs_to_jiffies(250));
582 } else {
583 pr_err("%s: unable to open smd port:%s err:%d\n",
584 __func__, pi->name, ret);
585 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587}
588
Hemant Kumarf60c0252011-11-03 12:37:07 -0700589static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590{
591 struct gsmd_port *port;
592 int temp;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700593 struct gserial *gser = gptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594
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
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300663 ret = usb_ep_enable(gser->in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664 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
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300672 ret = usb_ep_enable(gser->out);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 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
Eric Holmberge72b2d42012-01-23 13:45:45 -0700682 queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700683
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
Jack Phamc02d4812012-01-25 15:52:04 -0800721 if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
722 /* lower the dtr */
723 port->cbits_to_modem = 0;
724 smd_tiocmset(port->pi->ch,
725 port->cbits_to_modem,
726 ~port->cbits_to_modem);
727 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700728
Jack Phamc02d4812012-01-25 15:52:04 -0800729 if (port->pi->ch) {
730 smd_close(port->pi->ch);
731 port->pi->ch = NULL;
732 }
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700733}
734
735#define SMD_CH_MAX_LEN 20
736static int gsmd_ch_probe(struct platform_device *pdev)
737{
738 struct gsmd_port *port;
739 struct smd_port_info *pi;
740 int i;
741 unsigned long flags;
742
743 pr_debug("%s: name:%s\n", __func__, pdev->name);
744
745 for (i = 0; i < n_smd_ports; i++) {
746 port = smd_ports[i].port;
747 pi = port->pi;
748
749 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
750 set_bit(CH_READY, &pi->flags);
751 spin_lock_irqsave(&port->port_lock, flags);
752 if (port->port_usb)
Eric Holmberge72b2d42012-01-23 13:45:45 -0700753 queue_delayed_work(gsmd_wq, &port->connect_work,
754 msecs_to_jiffies(0));
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700755 spin_unlock_irqrestore(&port->port_lock, flags);
756 break;
757 }
758 }
759 return 0;
760}
761
762static int gsmd_ch_remove(struct platform_device *pdev)
763{
764 struct gsmd_port *port;
765 struct smd_port_info *pi;
766 int i;
767
768 pr_debug("%s: name:%s\n", __func__, pdev->name);
769
770 for (i = 0; i < n_smd_ports; i++) {
771 port = smd_ports[i].port;
772 pi = port->pi;
773
774 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
775 clear_bit(CH_READY, &pi->flags);
776 clear_bit(CH_OPENED, &pi->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800777 if (pi->ch) {
778 smd_close(pi->ch);
779 pi->ch = NULL;
780 }
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700781 break;
782 }
783 }
784 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785}
786
787static void gsmd_port_free(int portno)
788{
789 struct gsmd_port *port = smd_ports[portno].port;
790
791 if (!port)
792 kfree(port);
793}
794
795static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
796{
797 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700798 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700799
800 port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
801 if (!port)
802 return -ENOMEM;
803
804 port->port_num = portno;
805 port->pi = &smd_pi[portno];
806
807 spin_lock_init(&port->port_lock);
808
809 INIT_LIST_HEAD(&port->read_pool);
810 INIT_LIST_HEAD(&port->read_queue);
811 INIT_WORK(&port->push, gsmd_rx_push);
812
813 INIT_LIST_HEAD(&port->write_pool);
814 INIT_WORK(&port->pull, gsmd_tx_pull);
815
Eric Holmberge72b2d42012-01-23 13:45:45 -0700816 INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700817
818 smd_ports[portno].port = port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700819 pdrv = &smd_ports[portno].pdrv;
820 pdrv->probe = gsmd_ch_probe;
821 pdrv->remove = gsmd_ch_remove;
822 pdrv->driver.name = port->pi->name;
823 pdrv->driver.owner = THIS_MODULE;
824 platform_driver_register(pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700825
826 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
827
828 return 0;
829}
830
831#if defined(CONFIG_DEBUG_FS)
832static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
833 size_t count, loff_t *ppos)
834{
835 struct gsmd_port *port;
Jack Phamc02d4812012-01-25 15:52:04 -0800836 struct smd_port_info *pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700837 char *buf;
838 unsigned long flags;
839 int temp = 0;
840 int i;
841 int ret;
842
843 buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
844 if (!buf)
845 return -ENOMEM;
846
847 for (i = 0; i < n_smd_ports; i++) {
848 port = smd_ports[i].port;
Jack Phamc02d4812012-01-25 15:52:04 -0800849 pi = port->pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700850 spin_lock_irqsave(&port->port_lock, flags);
851 temp += scnprintf(buf + temp, 512 - temp,
852 "###PORT:%d###\n"
853 "nbytes_tolaptop: %lu\n"
854 "nbytes_tomodem: %lu\n"
855 "cbits_to_modem: %u\n"
856 "cbits_to_laptop: %u\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700857 "n_read: %u\n"
Vamsi Krishna396562a2011-08-25 11:39:09 -0700858 "smd_read_avail: %d\n"
859 "smd_write_avail: %d\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700860 "CH_OPENED: %d\n"
861 "CH_READY: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700862 i, port->nbytes_tolaptop, port->nbytes_tomodem,
863 port->cbits_to_modem, port->cbits_to_laptop,
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700864 port->n_read,
Jack Phamc02d4812012-01-25 15:52:04 -0800865 pi->ch ? smd_read_avail(pi->ch) : 0,
866 pi->ch ? smd_write_avail(pi->ch) : 0,
867 test_bit(CH_OPENED, &pi->flags),
868 test_bit(CH_READY, &pi->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869 spin_unlock_irqrestore(&port->port_lock, flags);
870 }
871
872 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
873
874 kfree(buf);
875
876 return ret;
877
878}
879
880static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
881 size_t count, loff_t *ppos)
882{
883 struct gsmd_port *port;
884 unsigned long flags;
885 int i;
886
887 for (i = 0; i < n_smd_ports; i++) {
888 port = smd_ports[i].port;
889
890 spin_lock_irqsave(&port->port_lock, flags);
891 port->nbytes_tolaptop = 0;
892 port->nbytes_tomodem = 0;
893 spin_unlock_irqrestore(&port->port_lock, flags);
894 }
895
896 return count;
897}
898
899static int debug_smd_open(struct inode *inode, struct file *file)
900{
901 return 0;
902}
903
904static const struct file_operations debug_gsmd_ops = {
905 .open = debug_smd_open,
906 .read = debug_smd_read_stats,
907 .write = debug_smd_reset_stats,
908};
909
910static void gsmd_debugfs_init(void)
911{
912 struct dentry *dent;
913
914 dent = debugfs_create_dir("usb_gsmd", 0);
915 if (IS_ERR(dent))
916 return;
917
918 debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
919}
920#else
921static void gsmd_debugfs_init(void) {}
922#endif
923
924int gsmd_setup(struct usb_gadget *g, unsigned count)
925{
926 struct usb_cdc_line_coding coding;
927 int ret;
928 int i;
929
930 pr_debug("%s: g:%p count: %d\n", __func__, g, count);
931
932 if (!count || count > SMD_N_PORTS) {
933 pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
934 __func__, count, g);
935 return -EINVAL;
936 }
937
938 coding.dwDTERate = cpu_to_le32(9600);
939 coding.bCharFormat = 8;
940 coding.bParityType = USB_CDC_NO_PARITY;
941 coding.bDataBits = USB_CDC_1_STOP_BITS;
942
943 gsmd_wq = create_singlethread_workqueue("k_gsmd");
944 if (!gsmd_wq) {
945 pr_err("%s: Unable to create workqueue gsmd_wq\n",
946 __func__);
947 return -ENOMEM;
948 }
949
950 for (i = 0; i < count; i++) {
951 mutex_init(&smd_ports[i].lock);
Manu Gautam4bd21422011-09-09 14:55:32 +0530952 n_smd_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953 ret = gsmd_port_alloc(i, &coding);
954 if (ret) {
Manu Gautam4bd21422011-09-09 14:55:32 +0530955 n_smd_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
957 goto free_smd_ports;
958 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 }
960
961 gsmd_debugfs_init();
962
963 return 0;
964free_smd_ports:
965 for (i = 0; i < n_smd_ports; i++)
966 gsmd_port_free(i);
967
968 destroy_workqueue(gsmd_wq);
969
970 return ret;
971}
972
973void gsmd_cleanup(struct usb_gadget *g, unsigned count)
974{
975 /* TBD */
976}