blob: b57e4e390400482af44aa38eb8f7ae71aebe58cf [file] [log] [blame]
Pavankumar Kondeti7f047142013-07-15 15:16:25 +05301/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/interrupt.h>
15#include <linux/device.h>
16#include <linux/delay.h>
17#include <linux/slab.h>
18#include <linux/termios.h>
19#include <linux/debugfs.h>
20#include <linux/smux.h>
Pavankumar Kondeti7f047142013-07-15 15:16:25 +053021#include <linux/completion.h>
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +053022
23#include <mach/usb_gadget_xport.h>
24
25#define CH_OPENED 0
26#define CH_READY 1
Pavankumar Kondeti7f047142013-07-15 15:16:25 +053027#define CH_CONNECTED 2
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +053028
29static unsigned int num_ctrl_ports;
30
31static const char *ghsuart_ctrl_names[] = {
32 "SMUX_RMNET_CTL_HSUART"
33};
34
35struct ghsuart_ctrl_port {
36 /* port */
37 unsigned port_num;
38 /* gadget */
39 enum gadget_type gtype;
40 spinlock_t port_lock;
41 void *port_usb;
Pavankumar Kondeti7f047142013-07-15 15:16:25 +053042 struct completion close_complete;
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +053043 /* work queue*/
44 struct workqueue_struct *wq;
45 struct work_struct connect_w;
46 struct work_struct disconnect_w;
47 /*ctrl pkt response cb*/
48 int (*send_cpkt_response)(void *g, void *buf, size_t len);
49 void *ctxt;
50 unsigned int ch_id;
51 /* flow control bits */
52 unsigned long flags;
53 int (*send_pkt)(void *, void *, size_t actual);
54 /* Channel status */
55 unsigned long channel_sts;
56 /* control bits */
57 unsigned cbits_tomodem;
58 /* counters */
59 unsigned long to_modem;
60 unsigned long to_host;
61 unsigned long drp_cpkt_cnt;
62};
63
64static struct {
65 struct ghsuart_ctrl_port *port;
66 struct platform_driver pdrv;
67} ghsuart_ctrl_ports[NUM_HSUART_PORTS];
68
69static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual);
70
71static void smux_control_event(void *priv, int event_type, const void *metadata)
72{
73 struct grmnet *gr = NULL;
74 struct ghsuart_ctrl_port *port = priv;
75 void *buf;
76 unsigned long flags;
77 size_t len;
78
79 switch (event_type) {
Pavankumar Kondeti7f047142013-07-15 15:16:25 +053080 case SMUX_LOCAL_CLOSED:
81 clear_bit(CH_OPENED, &port->channel_sts);
82 complete(&port->close_complete);
83 break;
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +053084 case SMUX_CONNECTED:
85 spin_lock_irqsave(&port->port_lock, flags);
86 if (!port->port_usb) {
87 spin_unlock_irqrestore(&port->port_lock, flags);
88 return;
89 }
90 spin_unlock_irqrestore(&port->port_lock, flags);
Pavankumar Kondeti7f047142013-07-15 15:16:25 +053091 set_bit(CH_CONNECTED, &port->channel_sts);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +053092 if (port->gtype == USB_GADGET_RMNET) {
93 gr = port->port_usb;
94 if (gr && gr->connect)
95 gr->connect(gr);
96 }
97 break;
98 case SMUX_DISCONNECTED:
Pavankumar Kondeti7f047142013-07-15 15:16:25 +053099 clear_bit(CH_CONNECTED, &port->channel_sts);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530100 break;
101 case SMUX_READ_DONE:
102 len = ((struct smux_meta_read *)metadata)->len;
103 buf = ((struct smux_meta_read *)metadata)->buffer;
104 ghsuart_ctrl_receive(port, buf, len);
105 break;
106 case SMUX_READ_FAIL:
107 buf = ((struct smux_meta_read *)metadata)->buffer;
108 kfree(buf);
109 break;
110 case SMUX_WRITE_DONE:
111 case SMUX_WRITE_FAIL:
112 buf = ((struct smux_meta_write *)metadata)->buffer;
113 kfree(buf);
114 break;
115 case SMUX_LOW_WM_HIT:
116 case SMUX_HIGH_WM_HIT:
117 case SMUX_TIOCM_UPDATE:
118 break;
119 default:
120 pr_err("%s Event %d not supported\n", __func__, event_type);
121 };
122}
123
124static int rx_control_buffer(void *priv, void **pkt_priv, void **buffer,
125 int size)
126{
127 void *rx_buf;
128
129 rx_buf = kmalloc(size, GFP_KERNEL);
130 if (!rx_buf)
131 return -EAGAIN;
132 *buffer = rx_buf;
133 *pkt_priv = NULL;
134
135 return 0;
136}
137
138static int ghsuart_ctrl_receive(void *dev, void *buf, size_t actual)
139{
140 struct ghsuart_ctrl_port *port = dev;
141 int retval = 0;
142
143 pr_debug_ratelimited("%s: read complete bytes read: %d\n",
144 __func__, actual);
145
146 /* send it to USB here */
147 if (port && port->send_cpkt_response) {
148 retval = port->send_cpkt_response(port->port_usb, buf, actual);
149 port->to_host++;
150 }
151 kfree(buf);
152 return retval;
153}
154
155static int
156ghsuart_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
157{
158 void *cbuf;
159 struct ghsuart_ctrl_port *port;
160 int ret;
161
162 if (portno >= num_ctrl_ports) {
163 pr_err("%s: Invalid portno#%d\n", __func__, portno);
164 return -ENODEV;
165 }
166
167 port = ghsuart_ctrl_ports[portno].port;
168 if (!port) {
169 pr_err("%s: port is null\n", __func__);
170 return -ENODEV;
171 }
172 /* drop cpkt if ch is not open */
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530173 if (!test_bit(CH_CONNECTED, &port->channel_sts)) {
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530174 port->drp_cpkt_cnt++;
175 return 0;
176 }
177 cbuf = kmalloc(len, GFP_ATOMIC);
178 if (!cbuf)
179 return -ENOMEM;
180
181 memcpy(cbuf, buf, len);
182
183 pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
184
185 ret = msm_smux_write(port->ch_id, port, (void *)cbuf, len);
186 if (ret < 0) {
187 pr_err_ratelimited("%s: write error:%d\n",
188 __func__, ret);
189 port->drp_cpkt_cnt++;
190 kfree(cbuf);
191 return ret;
192 }
193 port->to_modem++;
194
195 return 0;
196}
197
198static void
199ghsuart_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
200{
201 struct ghsuart_ctrl_port *port;
202
203 if (portno >= num_ctrl_ports || !gptr) {
204 pr_err("%s: Invalid portno#%d\n", __func__, portno);
205 return;
206 }
207
208 port = ghsuart_ctrl_ports[portno].port;
209 if (!port) {
210 pr_err("%s: port is null\n", __func__);
211 return;
212 }
213
214 if (cbits == port->cbits_tomodem)
215 return;
216
217 port->cbits_tomodem = cbits;
218
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530219 if (!test_bit(CH_CONNECTED, &port->channel_sts))
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530220 return;
221
222 pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
223 /* Send the control bits to the Modem */
224 msm_smux_tiocm_set(port->ch_id, cbits, ~cbits);
225}
226
227static void ghsuart_ctrl_connect_w(struct work_struct *w)
228{
229 struct ghsuart_ctrl_port *port =
230 container_of(w, struct ghsuart_ctrl_port, connect_w);
231 int retval;
232
233 if (!port || !test_bit(CH_READY, &port->channel_sts))
234 return;
235
236 pr_debug("%s: port:%p\n", __func__, port);
237
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530238 if (test_bit(CH_OPENED, &port->channel_sts)) {
239 retval = wait_for_completion_timeout(
240 &port->close_complete, 3 * HZ);
241 if (retval == 0) {
242 pr_err("%s: smux close timedout\n", __func__);
243 return;
244 }
245 }
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530246 retval = msm_smux_open(port->ch_id, port->ctxt, smux_control_event,
247 rx_control_buffer);
248 if (retval < 0) {
249 pr_err(" %s smux_open failed\n", __func__);
250 return;
251 }
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530252 set_bit(CH_OPENED, &port->channel_sts);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530253
254}
255
256int ghsuart_ctrl_connect(void *gptr, int port_num)
257{
258 struct ghsuart_ctrl_port *port;
259 struct grmnet *gr;
260 unsigned long flags;
261
262 pr_debug("%s: port#%d\n", __func__, port_num);
263
264 if (port_num > num_ctrl_ports || !gptr) {
265 pr_err("%s: invalid portno#%d\n", __func__, port_num);
266 return -ENODEV;
267 }
268
269 port = ghsuart_ctrl_ports[port_num].port;
270 if (!port) {
271 pr_err("%s: port is null\n", __func__);
272 return -ENODEV;
273 }
274
275 spin_lock_irqsave(&port->port_lock, flags);
276
277 gr = gptr;
278 port->send_cpkt_response = gr->send_cpkt_response;
279 gr->send_encap_cmd = ghsuart_send_cpkt_tomodem;
280 gr->notify_modem = ghsuart_send_cbits_tomodem;
281
282 port->port_usb = gptr;
283 port->to_host = 0;
284 port->to_modem = 0;
285 port->drp_cpkt_cnt = 0;
286 spin_unlock_irqrestore(&port->port_lock, flags);
287
288 if (test_bit(CH_READY, &port->channel_sts))
289 queue_work(port->wq, &port->connect_w);
290
291 return 0;
292}
293
294static void ghsuart_ctrl_disconnect_w(struct work_struct *w)
295{
296 struct ghsuart_ctrl_port *port =
297 container_of(w, struct ghsuart_ctrl_port, disconnect_w);
298
299 if (!test_bit(CH_OPENED, &port->channel_sts))
300 return;
301
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530302 INIT_COMPLETION(port->close_complete);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530303 msm_smux_close(port->ch_id);
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530304 clear_bit(CH_CONNECTED, &port->channel_sts);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530305}
306
307void ghsuart_ctrl_disconnect(void *gptr, int port_num)
308{
Chiranjeevi Velempati95578cf2012-07-25 09:21:42 +0530309 struct ghsuart_ctrl_port *port;
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530310 struct grmnet *gr = NULL;
311 unsigned long flags;
312
313 pr_debug("%s: port#%d\n", __func__, port_num);
314
315 if (port_num > num_ctrl_ports) {
316 pr_err("%s: invalid portno#%d\n", __func__, port_num);
317 return;
318 }
319
Chiranjeevi Velempati95578cf2012-07-25 09:21:42 +0530320 port = ghsuart_ctrl_ports[port_num].port;
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530321
322 if (!gptr || !port) {
323 pr_err("%s: grmnet port is null\n", __func__);
324 return;
325 }
326
327 gr = gptr;
328
329 spin_lock_irqsave(&port->port_lock, flags);
330 gr->send_encap_cmd = 0;
331 gr->notify_modem = 0;
332 port->cbits_tomodem = 0;
333 port->port_usb = 0;
334 port->send_cpkt_response = 0;
335 spin_unlock_irqrestore(&port->port_lock, flags);
336
337 queue_work(port->wq, &port->disconnect_w);
338}
339
340static int ghsuart_ctrl_probe(struct platform_device *pdev)
341{
342 struct ghsuart_ctrl_port *port;
343 unsigned long flags;
344
345 pr_debug("%s: name:%s\n", __func__, pdev->name);
346
347 port = ghsuart_ctrl_ports[pdev->id].port;
348 set_bit(CH_READY, &port->channel_sts);
349
350 /* if usb is online, start read */
351 spin_lock_irqsave(&port->port_lock, flags);
352 if (port->port_usb)
353 queue_work(port->wq, &port->connect_w);
354 spin_unlock_irqrestore(&port->port_lock, flags);
355
356 return 0;
357}
358
359static int ghsuart_ctrl_remove(struct platform_device *pdev)
360{
361 struct ghsuart_ctrl_port *port;
362 struct grmnet *gr = NULL;
363 unsigned long flags;
364
365 pr_debug("%s: name:%s\n", __func__, pdev->name);
366
367 port = ghsuart_ctrl_ports[pdev->id].port;
368
369 spin_lock_irqsave(&port->port_lock, flags);
370 if (!port->port_usb) {
371 spin_unlock_irqrestore(&port->port_lock, flags);
372 goto not_ready;
373 }
374
375 gr = port->port_usb;
376
377 spin_unlock_irqrestore(&port->port_lock, flags);
378
379 if (gr && gr->disconnect)
380 gr->disconnect(gr);
381
382 clear_bit(CH_OPENED, &port->channel_sts);
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530383 clear_bit(CH_CONNECTED, &port->channel_sts);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530384not_ready:
385 clear_bit(CH_READY, &port->channel_sts);
386
387 return 0;
388}
389
390static void ghsuart_ctrl_port_free(int portno)
391{
392 struct ghsuart_ctrl_port *port = ghsuart_ctrl_ports[portno].port;
Chiranjeevi Velempati95578cf2012-07-25 09:21:42 +0530393 struct platform_driver *pdrv = &ghsuart_ctrl_ports[portno].pdrv;
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530394
395 destroy_workqueue(port->wq);
396 if (pdrv)
397 platform_driver_unregister(pdrv);
398 kfree(port);
399}
400
401static int ghsuart_ctrl_port_alloc(int portno, enum gadget_type gtype)
402{
403 struct ghsuart_ctrl_port *port;
404 struct platform_driver *pdrv;
405 int err;
406
407 port = kzalloc(sizeof(struct ghsuart_ctrl_port), GFP_KERNEL);
408 if (!port)
409 return -ENOMEM;
410
411 port->wq = create_singlethread_workqueue(ghsuart_ctrl_names[portno]);
412 if (!port->wq) {
413 pr_err("%s: Unable to create workqueue:%s\n",
414 __func__, ghsuart_ctrl_names[portno]);
415 kfree(port);
416 return -ENOMEM;
417 }
418
419 port->port_num = portno;
420 port->gtype = gtype;
421
422 spin_lock_init(&port->port_lock);
423
Pavankumar Kondeti7f047142013-07-15 15:16:25 +0530424 init_completion(&port->close_complete);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530425 INIT_WORK(&port->connect_w, ghsuart_ctrl_connect_w);
426 INIT_WORK(&port->disconnect_w, ghsuart_ctrl_disconnect_w);
427
428 port->ch_id = SMUX_USB_RMNET_CTL_0;
429 port->ctxt = port;
430 port->send_pkt = ghsuart_ctrl_receive;
431 ghsuart_ctrl_ports[portno].port = port;
432
433 pdrv = &ghsuart_ctrl_ports[portno].pdrv;
434 pdrv->probe = ghsuart_ctrl_probe;
435 pdrv->remove = ghsuart_ctrl_remove;
436 pdrv->driver.name = ghsuart_ctrl_names[portno];
437 pdrv->driver.owner = THIS_MODULE;
438
439 err = platform_driver_register(pdrv);
440 if (unlikely(err < 0))
441 return err;
442 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
443
444 return 0;
445}
446
447int ghsuart_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
448{
449 int first_port_id = num_ctrl_ports;
450 int total_num_ports = num_ports + num_ctrl_ports;
451 int i;
452 int ret = 0;
453
454 if (!num_ports || total_num_ports > NUM_HSUART_PORTS) {
455 pr_err("%s: Invalid num of ports count:%d\n",
456 __func__, num_ports);
457 return -EINVAL;
458 }
459
460 pr_debug("%s: requested ports:%d\n", __func__, num_ports);
461
462 for (i = first_port_id; i < (first_port_id + num_ports); i++) {
463
464 num_ctrl_ports++;
465 ret = ghsuart_ctrl_port_alloc(i, gtype);
466 if (ret) {
467 num_ctrl_ports--;
468 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
469 goto free_ports;
470 }
471 }
472
473 return first_port_id;
474
475free_ports:
476 for (i = first_port_id; i < num_ctrl_ports; i++)
477 ghsuart_ctrl_port_free(i);
478 num_ctrl_ports = first_port_id;
479 return ret;
480}
481
Tarun Gupta44ad2bb2013-09-30 18:01:58 +0530482#if defined(CONFIG_DEBUG_FS)
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530483#define DEBUG_BUF_SIZE 1024
484static ssize_t ghsuart_ctrl_read_stats(struct file *file, char __user *ubuf,
485 size_t count, loff_t *ppos)
486{
487 struct ghsuart_ctrl_port *port;
488 char *buf;
489 unsigned long flags;
490 int ret;
491 int i;
492 int temp = 0;
493
494 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
495 if (!buf)
496 return -ENOMEM;
497
498 for (i = 0; i < num_ctrl_ports; i++) {
499 port = ghsuart_ctrl_ports[i].port;
500 if (!port)
501 continue;
502 spin_lock_irqsave(&port->port_lock, flags);
503
504 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
505 "#PORT:%d port: %p\n"
506 "to_usbhost: %lu\n"
507 "to_modem: %lu\n"
508 "cpkt_drp_cnt: %lu\n"
509 "DTR: %s\n",
510 i, port,
511 port->to_host, port->to_modem,
512 port->drp_cpkt_cnt,
513 port->cbits_tomodem ? "HIGH" : "LOW");
514
515 spin_unlock_irqrestore(&port->port_lock, flags);
516 }
517
518 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
519
520 kfree(buf);
521
522 return ret;
523}
524
525static ssize_t ghsuart_ctrl_reset_stats(struct file *file,
526 const char __user *buf, size_t count, loff_t *ppos)
527{
528 struct ghsuart_ctrl_port *port;
529 int i;
530 unsigned long flags;
531
532 for (i = 0; i < num_ctrl_ports; i++) {
533 port = ghsuart_ctrl_ports[i].port;
534 if (!port)
535 continue;
536
537 spin_lock_irqsave(&port->port_lock, flags);
538 port->to_host = 0;
539 port->to_modem = 0;
540 port->drp_cpkt_cnt = 0;
541 spin_unlock_irqrestore(&port->port_lock, flags);
542 }
543 return count;
544}
545
546static const struct file_operations ghsuart_ctrl_stats_ops = {
547 .read = ghsuart_ctrl_read_stats,
548 .write = ghsuart_ctrl_reset_stats,
549};
550
551static struct dentry *ghsuart_ctrl_dent;
552static int ghsuart_ctrl_debugfs_init(void)
553{
554 struct dentry *ghsuart_ctrl_dfile;
555
556 ghsuart_ctrl_dent = debugfs_create_dir("ghsuart_ctrl_xport", 0);
557 if (!ghsuart_ctrl_dent || IS_ERR(ghsuart_ctrl_dent))
558 return -ENODEV;
559
560 ghsuart_ctrl_dfile =
561 debugfs_create_file("status", S_IRUGO | S_IWUSR,
Tarun Gupta44ad2bb2013-09-30 18:01:58 +0530562 ghsuart_ctrl_dent, 0, &ghsuart_ctrl_stats_ops);
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530563 if (!ghsuart_ctrl_dfile || IS_ERR(ghsuart_ctrl_dfile)) {
564 debugfs_remove(ghsuart_ctrl_dent);
565 ghsuart_ctrl_dent = NULL;
566 return -ENODEV;
567 }
568 return 0;
569}
570
571static void ghsuart_ctrl_debugfs_exit(void)
572{
573 debugfs_remove_recursive(ghsuart_ctrl_dent);
574}
Tarun Gupta44ad2bb2013-09-30 18:01:58 +0530575#else
576static int ghsuart_ctrl_debugfs_init(void) { return 0; }
577static void ghsuart_ctrl_debugfs_exit(void) {}
578#endif
Chiranjeevi Velempatia3cf1982012-05-15 18:15:26 +0530579
580static int __init ghsuart_ctrl_init(void)
581{
582 int ret;
583
584 ret = ghsuart_ctrl_debugfs_init();
585 if (ret) {
586 pr_debug("mode debugfs file is not available\n");
587 return ret;
588 }
589 return 0;
590}
591module_init(ghsuart_ctrl_init);
592
593static void __exit ghsuart_ctrl_exit(void)
594{
595 ghsuart_ctrl_debugfs_exit();
596}
597module_exit(ghsuart_ctrl_exit);
598
599MODULE_DESCRIPTION("HSUART control xport for RmNet");
600MODULE_LICENSE("GPL v2");