blob: 9b824bbb367c0f65b925bed5fe020a7240352113 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
Ido Shayevitzb84439b2013-01-08 10:31:50 +02002 * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/interrupt.h>
16#include <linux/device.h>
17#include <linux/delay.h>
18#include <linux/slab.h>
19#include <linux/termios.h>
20#include <mach/msm_smd.h>
21#include <linux/debugfs.h>
22#include <linux/bitops.h>
23#include <linux/termios.h>
24
25#include "u_rmnet.h"
26
Bar Weiner6c817eb2013-04-15 20:52:18 +030027#define MAX_CTRL_PER_CLIENT 3
28#define MAX_CTRL_PORT (MAX_CTRL_PER_CLIENT * NR_CTRL_CLIENTS)
29static char *ctrl_names[NR_CTRL_CLIENTS][MAX_CTRL_PER_CLIENT] = {
30 {"DATA40_CNTL", "DATA39_CNTL", "DATA38_CNTL"},
31};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032static struct workqueue_struct *grmnet_ctrl_wq;
33
Bar Weiner6c817eb2013-04-15 20:52:18 +030034u8 online_clients;
35
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036#define SMD_CH_MAX_LEN 20
37#define CH_OPENED 0
38#define CH_READY 1
Ido Shayevitzb84439b2013-01-08 10:31:50 +020039#define CH_PREPARE_READY 2
40
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041struct smd_ch_info {
42 struct smd_channel *ch;
43 char *name;
44 unsigned long flags;
45 wait_queue_head_t wait;
46 unsigned dtr;
47
48 struct list_head tx_q;
49 unsigned long tx_len;
50
51 struct work_struct read_w;
52 struct work_struct write_w;
53
54 struct rmnet_ctrl_port *port;
55
56 int cbits_tomodem;
57 /* stats */
58 unsigned long to_modem;
59 unsigned long to_host;
60};
61
62struct rmnet_ctrl_port {
63 struct smd_ch_info ctrl_ch;
64 unsigned int port_num;
65 struct grmnet *port_usb;
66
67 spinlock_t port_lock;
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +053068 struct delayed_work connect_w;
Ido Shayevitzb84439b2013-01-08 10:31:50 +020069 struct delayed_work disconnect_w;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070};
71
72static struct rmnet_ctrl_ports {
73 struct rmnet_ctrl_port *port;
74 struct platform_driver pdrv;
Bar Weiner6c817eb2013-04-15 20:52:18 +030075} ctrl_smd_ports[MAX_CTRL_PORT];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076
77
78/*---------------misc functions---------------- */
79
Manu Gautam2b0234a2011-09-07 16:47:52 +053080static struct rmnet_ctrl_pkt *alloc_rmnet_ctrl_pkt(unsigned len, gfp_t flags)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081{
82 struct rmnet_ctrl_pkt *pkt;
83
84 pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
85 if (!pkt)
86 return ERR_PTR(-ENOMEM);
87
88 pkt->buf = kmalloc(len, flags);
89 if (!pkt->buf) {
90 kfree(pkt);
91 return ERR_PTR(-ENOMEM);
92 }
Hemant Kumarf60c0252011-11-03 12:37:07 -070093
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094 pkt->len = len;
95
96 return pkt;
97}
98
Manu Gautam2b0234a2011-09-07 16:47:52 +053099static void free_rmnet_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100{
101 kfree(pkt->buf);
102 kfree(pkt);
103}
104
105/*--------------------------------------------- */
106
107/*---------------control/smd channel functions---------------- */
108
109static void grmnet_ctrl_smd_read_w(struct work_struct *w)
110{
111 struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
112 struct rmnet_ctrl_port *port = c->port;
113 int sz;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700114 size_t len;
115 void *buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 unsigned long flags;
117
Jack Pham1b236d12012-03-19 15:27:18 -0700118 spin_lock_irqsave(&port->port_lock, flags);
119 while (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120 sz = smd_cur_packet_size(c->ch);
Jack Pham1b236d12012-03-19 15:27:18 -0700121 if (sz <= 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 break;
123
124 if (smd_read_avail(c->ch) < sz)
125 break;
126
Jack Pham1b236d12012-03-19 15:27:18 -0700127 spin_unlock_irqrestore(&port->port_lock, flags);
128
Hemant Kumarf60c0252011-11-03 12:37:07 -0700129 buf = kmalloc(sz, GFP_KERNEL);
130 if (!buf)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 return;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700132
133 len = smd_read(c->ch, buf, sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134
135 /* send it to USB here */
136 spin_lock_irqsave(&port->port_lock, flags);
137 if (port->port_usb && port->port_usb->send_cpkt_response) {
Hemant Kumarf60c0252011-11-03 12:37:07 -0700138 port->port_usb->send_cpkt_response(port->port_usb,
139 buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 c->to_host++;
141 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700142 kfree(buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 }
Jack Pham1b236d12012-03-19 15:27:18 -0700144 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145}
146
147static void grmnet_ctrl_smd_write_w(struct work_struct *w)
148{
149 struct smd_ch_info *c = container_of(w, struct smd_ch_info, write_w);
150 struct rmnet_ctrl_port *port = c->port;
151 unsigned long flags;
152 struct rmnet_ctrl_pkt *cpkt;
153 int ret;
154
155 spin_lock_irqsave(&port->port_lock, flags);
Jack Pham1b236d12012-03-19 15:27:18 -0700156 while (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 if (list_empty(&c->tx_q))
158 break;
159
160 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
161
162 if (smd_write_avail(c->ch) < cpkt->len)
163 break;
164
165 list_del(&cpkt->list);
166 spin_unlock_irqrestore(&port->port_lock, flags);
167 ret = smd_write(c->ch, cpkt->buf, cpkt->len);
168 spin_lock_irqsave(&port->port_lock, flags);
169 if (ret != cpkt->len) {
Hemant Kumarf60c0252011-11-03 12:37:07 -0700170 pr_err("%s: smd_write failed err:%d\n", __func__, ret);
Manu Gautam2b0234a2011-09-07 16:47:52 +0530171 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 break;
173 }
Manu Gautam2b0234a2011-09-07 16:47:52 +0530174 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 c->to_modem++;
176 }
177 spin_unlock_irqrestore(&port->port_lock, flags);
178}
Bar Weiner6c817eb2013-04-15 20:52:18 +0300179static int is_legal_port_num(u8 portno)
180{
181 if (portno >= MAX_CTRL_PORT)
182 return false;
183 if (ctrl_smd_ports[portno].port == NULL)
184 return false;
185
186 return true;
187}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188
189static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700190grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno,
191 void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192{
193 unsigned long flags;
194 struct rmnet_ctrl_port *port;
195 struct smd_ch_info *c;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700196 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197
Bar Weiner6c817eb2013-04-15 20:52:18 +0300198 if (!is_legal_port_num(portno)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 pr_err("%s: Invalid portno#%d\n", __func__, portno);
200 return -ENODEV;
201 }
202
Hemant Kumarf60c0252011-11-03 12:37:07 -0700203 port = ctrl_smd_ports[portno].port;
204
205 cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC);
206 if (IS_ERR(cpkt)) {
207 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
208 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 }
210
Hemant Kumarf60c0252011-11-03 12:37:07 -0700211 memcpy(cpkt->buf, buf, len);
212 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213
214 spin_lock_irqsave(&port->port_lock, flags);
215 c = &port->ctrl_ch;
216
217 /* drop cpkt if ch is not open */
218 if (!test_bit(CH_OPENED, &c->flags)) {
Manu Gautam2b0234a2011-09-07 16:47:52 +0530219 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220 spin_unlock_irqrestore(&port->port_lock, flags);
221 return 0;
222 }
223
224 list_add_tail(&cpkt->list, &c->tx_q);
225 queue_work(grmnet_ctrl_wq, &c->write_w);
226 spin_unlock_irqrestore(&port->port_lock, flags);
227
228 return 0;
229}
230
Manu Gautam2b0234a2011-09-07 16:47:52 +0530231#define RMNET_CTRL_DTR 0x01
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232static void
Hemant Kumarf60c0252011-11-03 12:37:07 -0700233gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234{
235 struct rmnet_ctrl_port *port;
236 struct smd_ch_info *c;
237 int set_bits = 0;
238 int clear_bits = 0;
239 int temp = 0;
240
Bar Weiner6c817eb2013-04-15 20:52:18 +0300241 if (!is_legal_port_num(portno)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 pr_err("%s: Invalid portno#%d\n", __func__, portno);
243 return;
244 }
245
Hemant Kumarf60c0252011-11-03 12:37:07 -0700246 if (!gptr) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 pr_err("%s: grmnet is null\n", __func__);
248 return;
249 }
250
Manu Gautam2b0234a2011-09-07 16:47:52 +0530251 port = ctrl_smd_ports[portno].port;
252 cbits = cbits & RMNET_CTRL_DTR;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 c = &port->ctrl_ch;
254
255 /* host driver will only send DTR, but to have generic
256 * set and clear bit implementation using two separate
257 * checks
258 */
Manu Gautam2b0234a2011-09-07 16:47:52 +0530259 if (cbits & RMNET_CTRL_DTR)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 set_bits |= TIOCM_DTR;
261 else
262 clear_bits |= TIOCM_DTR;
263
264 temp |= set_bits;
265 temp &= ~clear_bits;
266
267 if (temp == c->cbits_tomodem)
268 return;
269
270 c->cbits_tomodem = temp;
271
272 if (!test_bit(CH_OPENED, &c->flags))
273 return;
274
275 pr_debug("%s: ctrl_tomodem:%d ctrl_bits:%d setbits:%d clearbits:%d\n",
276 __func__, temp, cbits, set_bits, clear_bits);
277
278 smd_tiocmset(c->ch, set_bits, clear_bits);
279}
280
281static char *get_smd_event(unsigned event)
282{
283 switch (event) {
284 case SMD_EVENT_DATA:
285 return "DATA";
286 case SMD_EVENT_OPEN:
287 return "OPEN";
288 case SMD_EVENT_CLOSE:
289 return "CLOSE";
290 }
291
292 return "UNDEFINED";
293}
294
295static void grmnet_ctrl_smd_notify(void *p, unsigned event)
296{
297 struct rmnet_ctrl_port *port = p;
298 struct smd_ch_info *c = &port->ctrl_ch;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700299 struct rmnet_ctrl_pkt *cpkt;
300 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301
302 pr_debug("%s: EVENT_(%s)\n", __func__, get_smd_event(event));
303
304 switch (event) {
305 case SMD_EVENT_DATA:
306 if (smd_read_avail(c->ch))
307 queue_work(grmnet_ctrl_wq, &c->read_w);
308 if (smd_write_avail(c->ch))
309 queue_work(grmnet_ctrl_wq, &c->write_w);
310 break;
311 case SMD_EVENT_OPEN:
312 set_bit(CH_OPENED, &c->flags);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700313
314 if (port && port->port_usb && port->port_usb->connect)
315 port->port_usb->connect(port->port_usb);
316
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317 break;
318 case SMD_EVENT_CLOSE:
319 clear_bit(CH_OPENED, &c->flags);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700320
321 if (port && port->port_usb && port->port_usb->disconnect)
322 port->port_usb->disconnect(port->port_usb);
323
324 spin_lock_irqsave(&port->port_lock, flags);
325 while (!list_empty(&c->tx_q)) {
326 cpkt = list_first_entry(&c->tx_q,
327 struct rmnet_ctrl_pkt, list);
328
329 list_del(&cpkt->list);
330 free_rmnet_ctrl_pkt(cpkt);
331 }
332 spin_unlock_irqrestore(&port->port_lock, flags);
333
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334 break;
335 }
336}
337/*------------------------------------------------------------ */
338
339static void grmnet_ctrl_smd_connect_w(struct work_struct *w)
340{
341 struct rmnet_ctrl_port *port =
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530342 container_of(w, struct rmnet_ctrl_port, connect_w.work);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200343 struct rmnet_ctrl_ports *port_entry = &ctrl_smd_ports[port->port_num];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344 struct smd_ch_info *c = &port->ctrl_ch;
345 unsigned long flags;
Amit Blayc2b01792012-07-11 18:37:35 +0300346 int set_bits = 0;
347 int clear_bits = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 int ret;
349
350 pr_debug("%s:\n", __func__);
351
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200352 if (!test_bit(CH_READY, &c->flags)) {
353 if (!test_bit(CH_PREPARE_READY, &c->flags)) {
354 set_bit(CH_PREPARE_READY, &c->flags);
355 platform_driver_register(&(port_entry->pdrv));
356 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357 return;
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200358 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
360 ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify);
361 if (ret) {
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530362 if (ret == -EAGAIN) {
363 /* port not ready - retry */
364 pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
365 __func__, c->name, ret);
366 queue_delayed_work(grmnet_ctrl_wq, &port->connect_w,
367 msecs_to_jiffies(250));
368 } else {
369 pr_err("%s: unable to open smd port:%s err:%d\n",
370 __func__, c->name, ret);
371 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 return;
373 }
374
Amit Blayc2b01792012-07-11 18:37:35 +0300375 set_bits = c->cbits_tomodem;
376 clear_bits = ~(c->cbits_tomodem | TIOCM_RTS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 spin_lock_irqsave(&port->port_lock, flags);
378 if (port->port_usb)
Amit Blayc2b01792012-07-11 18:37:35 +0300379 smd_tiocmset(c->ch, set_bits, clear_bits);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 spin_unlock_irqrestore(&port->port_lock, flags);
381}
382
383int gsmd_ctrl_connect(struct grmnet *gr, int port_num)
384{
385 struct rmnet_ctrl_port *port;
386 struct smd_ch_info *c;
387 unsigned long flags;
388
389 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
390
Bar Weiner6c817eb2013-04-15 20:52:18 +0300391 if (!is_legal_port_num(port_num)) {
392 pr_err("%s: Invalid port_num#%d\n", __func__, port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393 return -ENODEV;
394 }
395
396 if (!gr) {
397 pr_err("%s: grmnet port is null\n", __func__);
398 return -ENODEV;
399 }
400
Manu Gautam2b0234a2011-09-07 16:47:52 +0530401 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402 c = &port->ctrl_ch;
403
404 spin_lock_irqsave(&port->port_lock, flags);
405 port->port_usb = gr;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700406 gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem;
407 gr->notify_modem = gsmd_ctrl_send_cbits_tomodem;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408 spin_unlock_irqrestore(&port->port_lock, flags);
409
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530410 queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
412 return 0;
413}
414
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200415static void grmnet_ctrl_smd_disconnect_w(struct work_struct *w)
416{
417 struct rmnet_ctrl_port *port =
418 container_of(w, struct rmnet_ctrl_port,
419 disconnect_w.work);
420 struct smd_ch_info *c;
421 struct platform_driver *pdrv;
422
423 c = &port->ctrl_ch;
Manu Gautamea8bd4f2013-04-05 13:54:27 +0530424 if (c->ch) {
425 smd_close(c->ch);
426 c->ch = NULL;
427 }
428
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200429 if (test_bit(CH_READY, &c->flags) ||
430 test_bit(CH_PREPARE_READY, &c->flags)) {
431 clear_bit(CH_PREPARE_READY, &c->flags);
432 pdrv = &ctrl_smd_ports[port->port_num].pdrv;
433 platform_driver_unregister(pdrv);
434 }
435}
436
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
438{
439 struct rmnet_ctrl_port *port;
440 unsigned long flags;
441 struct smd_ch_info *c;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700442 struct rmnet_ctrl_pkt *cpkt;
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200443 int clear_bits;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700444
445 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
446
Bar Weiner6c817eb2013-04-15 20:52:18 +0300447 if (!is_legal_port_num(port_num)) {
448 pr_err("%s: Invalid port_num#%d\n", __func__, port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 return;
450 }
451
452 if (!gr) {
453 pr_err("%s: grmnet port is null\n", __func__);
454 return;
455 }
456
Manu Gautam2b0234a2011-09-07 16:47:52 +0530457 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 c = &port->ctrl_ch;
459
460 spin_lock_irqsave(&port->port_lock, flags);
461 port->port_usb = 0;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700462 gr->send_encap_cmd = 0;
463 gr->notify_modem = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 c->cbits_tomodem = 0;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700465
466 while (!list_empty(&c->tx_q)) {
467 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
468
469 list_del(&cpkt->list);
470 free_rmnet_ctrl_pkt(cpkt);
471 }
472
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 spin_unlock_irqrestore(&port->port_lock, flags);
474
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200475 if (test_and_clear_bit(CH_OPENED, &c->flags)) {
476 clear_bits = ~(c->cbits_tomodem | TIOCM_RTS);
Jack Phamc02d4812012-01-25 15:52:04 -0800477 /* send dtr zero */
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200478 smd_tiocmset(c->ch, c->cbits_tomodem, clear_bits);
479 }
Jack Phamc02d4812012-01-25 15:52:04 -0800480
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200481 queue_delayed_work(grmnet_ctrl_wq, &port->disconnect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482}
483
484#define SMD_CH_MAX_LEN 20
485static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev)
486{
487 struct rmnet_ctrl_port *port;
488 struct smd_ch_info *c;
489 int i;
490 unsigned long flags;
491
492 pr_debug("%s: name:%s\n", __func__, pdev->name);
493
Bar Weiner6c817eb2013-04-15 20:52:18 +0300494 for (i = 0; i < MAX_CTRL_PORT; i++) {
495 if (!ctrl_smd_ports[i].port)
496 continue;
497
Manu Gautam2b0234a2011-09-07 16:47:52 +0530498 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700499 c = &port->ctrl_ch;
500
501 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200502 clear_bit(CH_PREPARE_READY, &c->flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503 set_bit(CH_READY, &c->flags);
504
505 /* if usb is online, try opening smd_ch */
506 spin_lock_irqsave(&port->port_lock, flags);
507 if (port->port_usb)
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530508 queue_delayed_work(grmnet_ctrl_wq,
509 &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 spin_unlock_irqrestore(&port->port_lock, flags);
511
512 break;
513 }
514 }
515
516 return 0;
517}
518
519static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev)
520{
521 struct rmnet_ctrl_port *port;
522 struct smd_ch_info *c;
523 int i;
524
525 pr_debug("%s: name:%s\n", __func__, pdev->name);
526
Bar Weiner6c817eb2013-04-15 20:52:18 +0300527 for (i = 0; i < MAX_CTRL_PORT; i++) {
528 if (!ctrl_smd_ports[i].port)
529 continue;
530
Manu Gautam2b0234a2011-09-07 16:47:52 +0530531 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532 c = &port->ctrl_ch;
533
534 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
535 clear_bit(CH_READY, &c->flags);
536 clear_bit(CH_OPENED, &c->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800537 if (c->ch) {
538 smd_close(c->ch);
539 c->ch = NULL;
540 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 break;
542 }
543 }
544
545 return 0;
546}
547
548
549static void grmnet_ctrl_smd_port_free(int portno)
550{
Manu Gautam2b0234a2011-09-07 16:47:52 +0530551 struct rmnet_ctrl_port *port = ctrl_smd_ports[portno].port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700552 struct platform_driver *pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553
Jack Phameffd4ae2011-08-03 16:49:36 -0700554 if (port) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 kfree(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700556 platform_driver_unregister(pdrv);
557 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558}
559
560static int grmnet_ctrl_smd_port_alloc(int portno)
561{
562 struct rmnet_ctrl_port *port;
563 struct smd_ch_info *c;
564 struct platform_driver *pdrv;
565
566 port = kzalloc(sizeof(struct rmnet_ctrl_port), GFP_KERNEL);
567 if (!port)
568 return -ENOMEM;
569
570 port->port_num = portno;
571
572 spin_lock_init(&port->port_lock);
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530573 INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200574 INIT_DELAYED_WORK(&port->disconnect_w, grmnet_ctrl_smd_disconnect_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575
576 c = &port->ctrl_ch;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300577 c->name = ctrl_names[portno / MAX_CTRL_PER_CLIENT]
578 [portno % MAX_CTRL_PER_CLIENT];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579 c->port = port;
580 init_waitqueue_head(&c->wait);
581 INIT_LIST_HEAD(&c->tx_q);
582 INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
583 INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
584
Manu Gautam2b0234a2011-09-07 16:47:52 +0530585 ctrl_smd_ports[portno].port = port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586
Manu Gautam2b0234a2011-09-07 16:47:52 +0530587 pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700588 pdrv->probe = grmnet_ctrl_smd_ch_probe;
589 pdrv->remove = grmnet_ctrl_smd_ch_remove;
590 pdrv->driver.name = c->name;
591 pdrv->driver.owner = THIS_MODULE;
592
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
594
595 return 0;
596}
597
Bar Weiner6c817eb2013-04-15 20:52:18 +0300598int gsmd_ctrl_setup(enum ctrl_client client_num, unsigned int count,
599 u8 *first_port_idx)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600{
Bar Weiner6c817eb2013-04-15 20:52:18 +0300601 int i, start_port, allocated_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700602 int ret;
603
604 pr_debug("%s: requested ports:%d\n", __func__, count);
605
Bar Weiner6c817eb2013-04-15 20:52:18 +0300606 if (!count || count > MAX_CTRL_PER_CLIENT) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607 pr_err("%s: Invalid num of ports count:%d\n",
608 __func__, count);
609 return -EINVAL;
610 }
611
Bar Weiner6c817eb2013-04-15 20:52:18 +0300612 if (!online_clients) {
613 grmnet_ctrl_wq = alloc_workqueue("gsmd_ctrl",
614 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
615 if (!grmnet_ctrl_wq) {
616 pr_err("%s: Unable to create workqueue grmnet_ctrl\n",
617 __func__);
618 return -ENOMEM;
619 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700620 }
Bar Weiner6c817eb2013-04-15 20:52:18 +0300621 online_clients++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622
Bar Weiner6c817eb2013-04-15 20:52:18 +0300623 start_port = MAX_CTRL_PER_CLIENT * client_num;
624 allocated_ports = 0;
625 for (i = start_port; i < count + start_port; i++) {
626 allocated_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700627 ret = grmnet_ctrl_smd_port_alloc(i);
628 if (ret) {
629 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
Bar Weiner6c817eb2013-04-15 20:52:18 +0300630 allocated_ports--;
Manu Gautam2b0234a2011-09-07 16:47:52 +0530631 goto free_ctrl_smd_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700632 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633 }
Bar Weiner6c817eb2013-04-15 20:52:18 +0300634 if (first_port_idx)
635 *first_port_idx = start_port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636 return 0;
637
Manu Gautam2b0234a2011-09-07 16:47:52 +0530638free_ctrl_smd_ports:
Bar Weiner6c817eb2013-04-15 20:52:18 +0300639 for (i = 0; i < allocated_ports; i++)
640 grmnet_ctrl_smd_port_free(start_port + i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641
Bar Weiner6c817eb2013-04-15 20:52:18 +0300642
643 online_clients--;
644 if (!online_clients)
645 destroy_workqueue(grmnet_ctrl_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700646
647 return ret;
648}
649
650#if defined(CONFIG_DEBUG_FS)
651#define DEBUG_BUF_SIZE 1024
652static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf,
653 size_t count, loff_t *ppos)
654{
655 struct rmnet_ctrl_port *port;
656 struct smd_ch_info *c;
657 char *buf;
658 unsigned long flags;
659 int ret;
660 int i;
661 int temp = 0;
662
663 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
664 if (!buf)
665 return -ENOMEM;
666
Bar Weiner6c817eb2013-04-15 20:52:18 +0300667 for (i = 0; i < MAX_CTRL_PORT; i++) {
668 if (!ctrl_smd_ports[i].port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 continue;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300670 port = ctrl_smd_ports[i].port;
671
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700672 spin_lock_irqsave(&port->port_lock, flags);
673
674 c = &port->ctrl_ch;
675
676 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
677 "#PORT:%d port:%p ctrl_ch:%p#\n"
678 "to_usbhost: %lu\n"
679 "to_modem: %lu\n"
680 "DTR: %s\n"
681 "ch_open: %d\n"
682 "ch_ready: %d\n"
683 "read_avail: %d\n"
684 "write_avail:%d\n",
685 i, port, &port->ctrl_ch,
686 c->to_host, c->to_modem,
687 c->cbits_tomodem ? "HIGH" : "LOW",
688 test_bit(CH_OPENED, &c->flags),
689 test_bit(CH_READY, &c->flags),
Jack Phamc02d4812012-01-25 15:52:04 -0800690 c->ch ? smd_read_avail(c->ch) : 0,
691 c->ch ? smd_write_avail(c->ch) : 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692
693 spin_unlock_irqrestore(&port->port_lock, flags);
694 }
695
696 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
697
698 kfree(buf);
699
700 return ret;
701}
702
703static ssize_t gsmd_ctrl_reset_stats(struct file *file, const char __user *buf,
704 size_t count, loff_t *ppos)
705{
706 struct rmnet_ctrl_port *port;
707 struct smd_ch_info *c;
708 int i;
709 unsigned long flags;
710
Bar Weiner6c817eb2013-04-15 20:52:18 +0300711 for (i = 0; i < MAX_CTRL_PORT; i++) {
712 if (!ctrl_smd_ports[i].port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700713 continue;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300714 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715
716 spin_lock_irqsave(&port->port_lock, flags);
717
718 c = &port->ctrl_ch;
719
720 c->to_host = 0;
721 c->to_modem = 0;
722
723 spin_unlock_irqrestore(&port->port_lock, flags);
724 }
725 return count;
726}
727
728const struct file_operations gsmd_ctrl_stats_ops = {
729 .read = gsmd_ctrl_read_stats,
730 .write = gsmd_ctrl_reset_stats,
731};
732
733struct dentry *smd_ctrl_dent;
734struct dentry *smd_ctrl_dfile;
735static void gsmd_ctrl_debugfs_init(void)
736{
737 smd_ctrl_dent = debugfs_create_dir("usb_rmnet_ctrl_smd", 0);
738 if (IS_ERR(smd_ctrl_dent))
739 return;
740
741 smd_ctrl_dfile = debugfs_create_file("status", 0444, smd_ctrl_dent, 0,
742 &gsmd_ctrl_stats_ops);
743 if (!smd_ctrl_dfile || IS_ERR(smd_ctrl_dfile))
744 debugfs_remove(smd_ctrl_dent);
745}
746
747static void gsmd_ctrl_debugfs_exit(void)
748{
749 debugfs_remove(smd_ctrl_dfile);
750 debugfs_remove(smd_ctrl_dent);
751}
752
753#else
754static void gsmd_ctrl_debugfs_init(void) { }
755static void gsmd_ctrl_debugfs_exit(void) { }
756#endif
757
758static int __init gsmd_ctrl_init(void)
759{
760 gsmd_ctrl_debugfs_init();
Bar Weiner6c817eb2013-04-15 20:52:18 +0300761 online_clients = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762
763 return 0;
764}
765module_init(gsmd_ctrl_init);
766
767static void __exit gsmd_ctrl_exit(void)
768{
769 gsmd_ctrl_debugfs_exit();
770}
771module_exit(gsmd_ctrl_exit);
772MODULE_DESCRIPTION("smd control driver");
773MODULE_LICENSE("GPL v2");