blob: a5ceaff5f8e2dfdcdba63dd1c4b7f99739b45e07 [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);
Jack Pham1b236d12012-03-19 15:27:18 -0700212 struct smd_port_info *pi = port->pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 struct list_head *q;
214
215 pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
216
217 spin_lock_irq(&port->port_lock);
218
219 q = &port->read_queue;
Jack Pham1b236d12012-03-19 15:27:18 -0700220 while (pi->ch && !list_empty(q)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221 struct usb_request *req;
222 int avail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223
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;
Jack Pham1b236d12012-03-19 15:27:18 -0700299 struct smd_port_info *pi = port->pi;
300 struct usb_ep *in;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301
302 pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
303 port, port->port_num, pool);
304
Jack Pham1b236d12012-03-19 15:27:18 -0700305 spin_lock_irq(&port->port_lock);
306
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 if (!port->port_usb) {
308 pr_debug("%s: usb is disconnected\n", __func__);
Jack Pham1b236d12012-03-19 15:27:18 -0700309 spin_unlock_irq(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 gsmd_read_pending(port);
311 return;
312 }
313
Jack Pham1b236d12012-03-19 15:27:18 -0700314 in = port->port_usb->in;
315 while (pi->ch && !list_empty(pool)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 struct usb_request *req;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317 int avail;
318 int ret;
319
320 avail = smd_read_avail(pi->ch);
321 if (!avail)
322 break;
323
324 avail = avail > SMD_TX_BUF_SIZE ? SMD_TX_BUF_SIZE : avail;
325
326 req = list_entry(pool->next, struct usb_request, list);
327 list_del(&req->list);
328 req->length = smd_read(pi->ch, req->buf, avail);
329
330 spin_unlock_irq(&port->port_lock);
331 ret = usb_ep_queue(in, req, GFP_KERNEL);
332 spin_lock_irq(&port->port_lock);
333 if (ret) {
334 pr_err("%s: usb ep out queue failed"
335 "port:%p, port#%d err:%d\n",
336 __func__, port, port->port_num, ret);
337 /* could be usb disconnected */
338 if (!port->port_usb)
339 gsmd_free_req(in, req);
340 else
341 list_add(&req->list, pool);
342 goto tx_pull_end;
343 }
344
345 port->nbytes_tolaptop += req->length;
346 }
347
348tx_pull_end:
349 /* TBD: Check how code behaves on USB bus suspend */
350 if (port->port_usb && smd_read_avail(port->pi->ch) && !list_empty(pool))
351 queue_work(gsmd_wq, &port->pull);
352
353 spin_unlock_irq(&port->port_lock);
354
355 return;
356}
357
358static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
359{
360 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361
362 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
363
364 if (!port) {
365 pr_err("%s: port is null\n", __func__);
366 return;
367 }
368
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700369 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700370 if (!test_bit(CH_OPENED, &port->pi->flags) ||
371 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700372 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700373 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700374 return;
375 }
376
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 list_add_tail(&req->list, &port->read_queue);
378 queue_work(gsmd_wq, &port->push);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700379 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380
381 return;
382}
383
384static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
385{
386 struct gsmd_port *port = ep->driver_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387
388 pr_debug("%s: ep:%p port:%p\n", __func__, ep, port);
389
390 if (!port) {
391 pr_err("%s: port is null\n", __func__);
392 return;
393 }
394
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700395 spin_lock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700396 if (!test_bit(CH_OPENED, &port->pi->flags) ||
397 req->status == -ESHUTDOWN) {
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700398 spin_unlock(&port->port_lock);
Vamsi Krishna396562a2011-08-25 11:39:09 -0700399 gsmd_free_req(ep, req);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700400 return;
401 }
402
Vamsi Krishna396562a2011-08-25 11:39:09 -0700403 if (req->status)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404 pr_warning("%s: port:%p port#%d unexpected %s status %d\n",
405 __func__, port, port->port_num,
406 ep->name, req->status);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
Vamsi Krishna396562a2011-08-25 11:39:09 -0700408 list_add(&req->list, &port->write_pool);
409 queue_work(gsmd_wq, &port->pull);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700410 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
412 return;
413}
414
415static void gsmd_start_io(struct gsmd_port *port)
416{
417 int ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418
419 pr_debug("%s: port: %p\n", __func__, port);
420
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700421 spin_lock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422
423 if (!port->port_usb)
424 goto start_io_out;
425
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700426 smd_tiocmset_from_cb(port->pi->ch,
427 port->cbits_to_modem,
428 ~port->cbits_to_modem);
429
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430 ret = gsmd_alloc_requests(port->port_usb->out,
431 &port->read_pool,
432 SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE,
433 gsmd_read_complete);
434 if (ret) {
435 pr_err("%s: unable to allocate out requests\n",
436 __func__);
437 goto start_io_out;
438 }
439
440 ret = gsmd_alloc_requests(port->port_usb->in,
441 &port->write_pool,
442 SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE,
443 gsmd_write_complete);
444 if (ret) {
445 gsmd_free_requests(port->port_usb->out, &port->read_pool);
446 pr_err("%s: unable to allocate IN requests\n",
447 __func__);
448 goto start_io_out;
449 }
450
451start_io_out:
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700452 spin_unlock(&port->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453
454 if (ret)
455 return;
456
457 gsmd_start_rx(port);
458}
459
460static unsigned int convert_uart_sigs_to_acm(unsigned uart_sig)
461{
462 unsigned int acm_sig = 0;
463
464 /* should this needs to be in calling functions ??? */
465 uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR);
466
467 if (uart_sig & TIOCM_RI)
468 acm_sig |= SMD_ACM_CTRL_RI;
469 if (uart_sig & TIOCM_CD)
470 acm_sig |= SMD_ACM_CTRL_DCD;
471 if (uart_sig & TIOCM_DSR)
472 acm_sig |= SMD_ACM_CTRL_DSR;
473
474 return acm_sig;
475}
476
477static unsigned int convert_acm_sigs_to_uart(unsigned acm_sig)
478{
479 unsigned int uart_sig = 0;
480
481 /* should this needs to be in calling functions ??? */
482 acm_sig &= (SMD_ACM_CTRL_DTR | SMD_ACM_CTRL_RTS);
483
484 if (acm_sig & SMD_ACM_CTRL_DTR)
485 uart_sig |= TIOCM_DTR;
486 if (acm_sig & SMD_ACM_CTRL_RTS)
487 uart_sig |= TIOCM_RTS;
488
489 return uart_sig;
490}
491
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700492
493static void gsmd_stop_io(struct gsmd_port *port)
494{
495 struct usb_ep *in;
496 struct usb_ep *out;
497 unsigned long flags;
498
499 spin_lock_irqsave(&port->port_lock, flags);
500 if (!port->port_usb) {
501 spin_unlock_irqrestore(&port->port_lock, flags);
502 return;
503 }
504 in = port->port_usb->in;
505 out = port->port_usb->out;
506 spin_unlock_irqrestore(&port->port_lock, flags);
507
508 usb_ep_fifo_flush(in);
509 usb_ep_fifo_flush(out);
510
511 spin_lock(&port->port_lock);
512 if (port->port_usb) {
513 gsmd_free_requests(out, &port->read_pool);
514 gsmd_free_requests(out, &port->read_queue);
515 gsmd_free_requests(in, &port->write_pool);
516 port->n_read = 0;
517 port->cbits_to_laptop = 0;
518 }
519
520 if (port->port_usb->send_modem_ctrl_bits)
521 port->port_usb->send_modem_ctrl_bits(
522 port->port_usb,
523 port->cbits_to_laptop);
524 spin_unlock(&port->port_lock);
525
526}
527
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700528static void gsmd_notify(void *priv, unsigned event)
529{
530 struct gsmd_port *port = priv;
531 struct smd_port_info *pi = port->pi;
532 int i;
533
534 switch (event) {
535 case SMD_EVENT_DATA:
536 pr_debug("%s: Event data\n", __func__);
537 if (smd_read_avail(pi->ch))
538 queue_work(gsmd_wq, &port->pull);
539 if (smd_write_avail(pi->ch))
540 queue_work(gsmd_wq, &port->push);
541 break;
542 case SMD_EVENT_OPEN:
543 pr_debug("%s: Event Open\n", __func__);
544 set_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700545 gsmd_start_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700546 break;
547 case SMD_EVENT_CLOSE:
548 pr_debug("%s: Event Close\n", __func__);
549 clear_bit(CH_OPENED, &pi->flags);
Vamsi Krishna5e9346c2011-08-16 14:09:18 -0700550 gsmd_stop_io(port);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 break;
552 case SMD_EVENT_STATUS:
553 i = smd_tiocmget(port->pi->ch);
554 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
555 if (port->port_usb && port->port_usb->send_modem_ctrl_bits)
556 port->port_usb->send_modem_ctrl_bits(port->port_usb,
557 port->cbits_to_laptop);
558 break;
559 }
560}
561
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700562static void gsmd_connect_work(struct work_struct *w)
563{
564 struct gsmd_port *port;
565 struct smd_port_info *pi;
566 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700567
Eric Holmberge72b2d42012-01-23 13:45:45 -0700568 port = container_of(w, struct gsmd_port, connect_work.work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700569 pi = port->pi;
570
571 pr_debug("%s: port:%p port#%d\n", __func__, port, port->port_num);
572
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700573 if (!test_bit(CH_READY, &pi->flags))
574 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700576 ret = smd_named_open_on_edge(pi->name, SMD_APPS_MODEM,
577 &pi->ch, port, gsmd_notify);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578 if (ret) {
Eric Holmberge72b2d42012-01-23 13:45:45 -0700579 if (ret == -EAGAIN) {
580 /* port not ready - retry */
581 pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
582 __func__, pi->name, ret);
583 queue_delayed_work(gsmd_wq, &port->connect_work,
584 msecs_to_jiffies(250));
585 } else {
586 pr_err("%s: unable to open smd port:%s err:%d\n",
587 __func__, pi->name, ret);
588 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590}
591
Hemant Kumarf60c0252011-11-03 12:37:07 -0700592static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593{
594 struct gsmd_port *port;
595 int temp;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700596 struct gserial *gser = gptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597
598 if (portno >= n_smd_ports) {
599 pr_err("%s: invalid portno#%d\n", __func__, portno);
600 return;
601 }
602
603 if (!gser) {
604 pr_err("%s: gser is null\n", __func__);
605 return;
606 }
607
608 port = smd_ports[portno].port;
609
610 temp = convert_acm_sigs_to_uart(ctrl_bits);
611
612 if (temp == port->cbits_to_modem)
613 return;
614
615 port->cbits_to_modem = temp;
616
617 /* usb could send control signal before smd is ready */
618 if (!test_bit(CH_OPENED, &port->pi->flags))
619 return;
620
621 /* if DTR is high, update latest modem info to laptop */
622 if (port->cbits_to_modem & TIOCM_DTR) {
623 unsigned i;
624
625 i = smd_tiocmget(port->pi->ch);
626 port->cbits_to_laptop = convert_uart_sigs_to_acm(i);
627
628 if (gser->send_modem_ctrl_bits)
629 gser->send_modem_ctrl_bits(
630 port->port_usb,
631 port->cbits_to_laptop);
632 }
633
634 smd_tiocmset(port->pi->ch,
635 port->cbits_to_modem,
636 ~port->cbits_to_modem);
637}
638
639int gsmd_connect(struct gserial *gser, u8 portno)
640{
641 unsigned long flags;
642 int ret;
643 struct gsmd_port *port;
644
645 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
646
647 if (portno >= n_smd_ports) {
648 pr_err("%s: Invalid port no#%d", __func__, portno);
649 return -EINVAL;
650 }
651
652 if (!gser) {
653 pr_err("%s: gser is null\n", __func__);
654 return -EINVAL;
655 }
656
657 port = smd_ports[portno].port;
658
659 spin_lock_irqsave(&port->port_lock, flags);
660 port->port_usb = gser;
661 gser->notify_modem = gsmd_notify_modem;
662 port->nbytes_tomodem = 0;
663 port->nbytes_tolaptop = 0;
664 spin_unlock_irqrestore(&port->port_lock, flags);
665
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300666 ret = usb_ep_enable(gser->in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667 if (ret) {
668 pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
669 __func__, gser->in);
670 port->port_usb = 0;
671 return ret;
672 }
673 gser->in->driver_data = port;
674
Tatyana Brokhmancf709c12011-06-28 16:33:48 +0300675 ret = usb_ep_enable(gser->out);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700676 if (ret) {
677 pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
678 __func__, gser->out);
679 port->port_usb = 0;
680 gser->in->driver_data = 0;
681 return ret;
682 }
683 gser->out->driver_data = port;
684
Eric Holmberge72b2d42012-01-23 13:45:45 -0700685 queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686
687 return 0;
688}
689
690void gsmd_disconnect(struct gserial *gser, u8 portno)
691{
692 unsigned long flags;
693 struct gsmd_port *port;
694
695 pr_debug("%s: gserial:%p portno:%u\n", __func__, gser, portno);
696
697 if (portno >= n_smd_ports) {
698 pr_err("%s: invalid portno#%d\n", __func__, portno);
699 return;
700 }
701
702 if (!gser) {
703 pr_err("%s: gser is null\n", __func__);
704 return;
705 }
706
707 port = smd_ports[portno].port;
708
709 spin_lock_irqsave(&port->port_lock, flags);
710 port->port_usb = 0;
711 spin_unlock_irqrestore(&port->port_lock, flags);
712
713 /* disable endpoints, aborting down any active I/O */
714 usb_ep_disable(gser->out);
715 usb_ep_disable(gser->in);
716
717 spin_lock_irqsave(&port->port_lock, flags);
718 gsmd_free_requests(gser->out, &port->read_pool);
719 gsmd_free_requests(gser->out, &port->read_queue);
720 gsmd_free_requests(gser->in, &port->write_pool);
721 port->n_read = 0;
722 spin_unlock_irqrestore(&port->port_lock, flags);
723
Jack Phamc02d4812012-01-25 15:52:04 -0800724 if (test_and_clear_bit(CH_OPENED, &port->pi->flags)) {
725 /* lower the dtr */
726 port->cbits_to_modem = 0;
727 smd_tiocmset(port->pi->ch,
728 port->cbits_to_modem,
729 ~port->cbits_to_modem);
730 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731
Jack Phamc02d4812012-01-25 15:52:04 -0800732 if (port->pi->ch) {
733 smd_close(port->pi->ch);
734 port->pi->ch = NULL;
735 }
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700736}
737
738#define SMD_CH_MAX_LEN 20
739static int gsmd_ch_probe(struct platform_device *pdev)
740{
741 struct gsmd_port *port;
742 struct smd_port_info *pi;
743 int i;
744 unsigned long flags;
745
746 pr_debug("%s: name:%s\n", __func__, pdev->name);
747
748 for (i = 0; i < n_smd_ports; i++) {
749 port = smd_ports[i].port;
750 pi = port->pi;
751
752 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
753 set_bit(CH_READY, &pi->flags);
754 spin_lock_irqsave(&port->port_lock, flags);
755 if (port->port_usb)
Eric Holmberge72b2d42012-01-23 13:45:45 -0700756 queue_delayed_work(gsmd_wq, &port->connect_work,
757 msecs_to_jiffies(0));
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700758 spin_unlock_irqrestore(&port->port_lock, flags);
759 break;
760 }
761 }
762 return 0;
763}
764
765static int gsmd_ch_remove(struct platform_device *pdev)
766{
767 struct gsmd_port *port;
768 struct smd_port_info *pi;
769 int i;
770
771 pr_debug("%s: name:%s\n", __func__, pdev->name);
772
773 for (i = 0; i < n_smd_ports; i++) {
774 port = smd_ports[i].port;
775 pi = port->pi;
776
777 if (!strncmp(pi->name, pdev->name, SMD_CH_MAX_LEN)) {
778 clear_bit(CH_READY, &pi->flags);
779 clear_bit(CH_OPENED, &pi->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800780 if (pi->ch) {
781 smd_close(pi->ch);
782 pi->ch = NULL;
783 }
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700784 break;
785 }
786 }
787 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700788}
789
790static void gsmd_port_free(int portno)
791{
792 struct gsmd_port *port = smd_ports[portno].port;
793
794 if (!port)
795 kfree(port);
796}
797
798static int gsmd_port_alloc(int portno, struct usb_cdc_line_coding *coding)
799{
800 struct gsmd_port *port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700801 struct platform_driver *pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700802
803 port = kzalloc(sizeof(struct gsmd_port), GFP_KERNEL);
804 if (!port)
805 return -ENOMEM;
806
807 port->port_num = portno;
808 port->pi = &smd_pi[portno];
809
810 spin_lock_init(&port->port_lock);
811
812 INIT_LIST_HEAD(&port->read_pool);
813 INIT_LIST_HEAD(&port->read_queue);
814 INIT_WORK(&port->push, gsmd_rx_push);
815
816 INIT_LIST_HEAD(&port->write_pool);
817 INIT_WORK(&port->pull, gsmd_tx_pull);
818
Eric Holmberge72b2d42012-01-23 13:45:45 -0700819 INIT_DELAYED_WORK(&port->connect_work, gsmd_connect_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820
821 smd_ports[portno].port = port;
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700822 pdrv = &smd_ports[portno].pdrv;
823 pdrv->probe = gsmd_ch_probe;
824 pdrv->remove = gsmd_ch_remove;
825 pdrv->driver.name = port->pi->name;
826 pdrv->driver.owner = THIS_MODULE;
827 platform_driver_register(pdrv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700828
829 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
830
831 return 0;
832}
833
834#if defined(CONFIG_DEBUG_FS)
835static ssize_t debug_smd_read_stats(struct file *file, char __user *ubuf,
836 size_t count, loff_t *ppos)
837{
838 struct gsmd_port *port;
Jack Phamc02d4812012-01-25 15:52:04 -0800839 struct smd_port_info *pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700840 char *buf;
841 unsigned long flags;
842 int temp = 0;
843 int i;
844 int ret;
845
846 buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
847 if (!buf)
848 return -ENOMEM;
849
850 for (i = 0; i < n_smd_ports; i++) {
851 port = smd_ports[i].port;
Jack Phamc02d4812012-01-25 15:52:04 -0800852 pi = port->pi;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700853 spin_lock_irqsave(&port->port_lock, flags);
854 temp += scnprintf(buf + temp, 512 - temp,
855 "###PORT:%d###\n"
856 "nbytes_tolaptop: %lu\n"
857 "nbytes_tomodem: %lu\n"
858 "cbits_to_modem: %u\n"
859 "cbits_to_laptop: %u\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700860 "n_read: %u\n"
Vamsi Krishna396562a2011-08-25 11:39:09 -0700861 "smd_read_avail: %d\n"
862 "smd_write_avail: %d\n"
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700863 "CH_OPENED: %d\n"
864 "CH_READY: %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700865 i, port->nbytes_tolaptop, port->nbytes_tomodem,
866 port->cbits_to_modem, port->cbits_to_laptop,
Hemant Kumard3fb9bb2011-07-12 17:20:16 -0700867 port->n_read,
Jack Phamc02d4812012-01-25 15:52:04 -0800868 pi->ch ? smd_read_avail(pi->ch) : 0,
869 pi->ch ? smd_write_avail(pi->ch) : 0,
870 test_bit(CH_OPENED, &pi->flags),
871 test_bit(CH_READY, &pi->flags));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 spin_unlock_irqrestore(&port->port_lock, flags);
873 }
874
875 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
876
877 kfree(buf);
878
879 return ret;
880
881}
882
883static ssize_t debug_smd_reset_stats(struct file *file, const char __user *buf,
884 size_t count, loff_t *ppos)
885{
886 struct gsmd_port *port;
887 unsigned long flags;
888 int i;
889
890 for (i = 0; i < n_smd_ports; i++) {
891 port = smd_ports[i].port;
892
893 spin_lock_irqsave(&port->port_lock, flags);
894 port->nbytes_tolaptop = 0;
895 port->nbytes_tomodem = 0;
896 spin_unlock_irqrestore(&port->port_lock, flags);
897 }
898
899 return count;
900}
901
902static int debug_smd_open(struct inode *inode, struct file *file)
903{
904 return 0;
905}
906
907static const struct file_operations debug_gsmd_ops = {
908 .open = debug_smd_open,
909 .read = debug_smd_read_stats,
910 .write = debug_smd_reset_stats,
911};
912
913static void gsmd_debugfs_init(void)
914{
915 struct dentry *dent;
916
917 dent = debugfs_create_dir("usb_gsmd", 0);
918 if (IS_ERR(dent))
919 return;
920
921 debugfs_create_file("status", 0444, dent, 0, &debug_gsmd_ops);
922}
923#else
924static void gsmd_debugfs_init(void) {}
925#endif
926
927int gsmd_setup(struct usb_gadget *g, unsigned count)
928{
929 struct usb_cdc_line_coding coding;
930 int ret;
931 int i;
932
933 pr_debug("%s: g:%p count: %d\n", __func__, g, count);
934
935 if (!count || count > SMD_N_PORTS) {
936 pr_err("%s: Invalid num of ports count:%d gadget:%p\n",
937 __func__, count, g);
938 return -EINVAL;
939 }
940
941 coding.dwDTERate = cpu_to_le32(9600);
942 coding.bCharFormat = 8;
943 coding.bParityType = USB_CDC_NO_PARITY;
944 coding.bDataBits = USB_CDC_1_STOP_BITS;
945
946 gsmd_wq = create_singlethread_workqueue("k_gsmd");
947 if (!gsmd_wq) {
948 pr_err("%s: Unable to create workqueue gsmd_wq\n",
949 __func__);
950 return -ENOMEM;
951 }
952
953 for (i = 0; i < count; i++) {
954 mutex_init(&smd_ports[i].lock);
Manu Gautam4bd21422011-09-09 14:55:32 +0530955 n_smd_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956 ret = gsmd_port_alloc(i, &coding);
957 if (ret) {
Manu Gautam4bd21422011-09-09 14:55:32 +0530958 n_smd_ports--;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
960 goto free_smd_ports;
961 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700962 }
963
964 gsmd_debugfs_init();
965
966 return 0;
967free_smd_ports:
968 for (i = 0; i < n_smd_ports; i++)
969 gsmd_port_free(i);
970
971 destroy_workqueue(gsmd_wq);
972
973 return ret;
974}
975
976void gsmd_cleanup(struct usb_gadget *g, unsigned count)
977{
978 /* TBD */
979}