blob: 581777976a7147ddff7dc039fb90dd450c0141b5 [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
Anna Perel21515162012-02-02 20:50:02 +020027#define NR_CTRL_SMD_PORTS 3
Manu Gautam2b0234a2011-09-07 16:47:52 +053028static int n_rmnet_ctrl_ports;
Anna Perel21515162012-02-02 20:50:02 +020029static char *rmnet_ctrl_names[] = {"DATA40_CNTL", "DATA39_CNTL", "DATA38_CNTL"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030static struct workqueue_struct *grmnet_ctrl_wq;
31
32#define SMD_CH_MAX_LEN 20
33#define CH_OPENED 0
34#define CH_READY 1
Ido Shayevitzb84439b2013-01-08 10:31:50 +020035#define CH_PREPARE_READY 2
36
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037struct smd_ch_info {
38 struct smd_channel *ch;
39 char *name;
40 unsigned long flags;
41 wait_queue_head_t wait;
42 unsigned dtr;
43
44 struct list_head tx_q;
45 unsigned long tx_len;
46
47 struct work_struct read_w;
48 struct work_struct write_w;
49
50 struct rmnet_ctrl_port *port;
51
52 int cbits_tomodem;
53 /* stats */
54 unsigned long to_modem;
55 unsigned long to_host;
56};
57
58struct rmnet_ctrl_port {
59 struct smd_ch_info ctrl_ch;
60 unsigned int port_num;
61 struct grmnet *port_usb;
62
63 spinlock_t port_lock;
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +053064 struct delayed_work connect_w;
Ido Shayevitzb84439b2013-01-08 10:31:50 +020065 struct delayed_work disconnect_w;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066};
67
68static struct rmnet_ctrl_ports {
69 struct rmnet_ctrl_port *port;
70 struct platform_driver pdrv;
Manu Gautam2b0234a2011-09-07 16:47:52 +053071} ctrl_smd_ports[NR_CTRL_SMD_PORTS];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072
73
74/*---------------misc functions---------------- */
75
Manu Gautam2b0234a2011-09-07 16:47:52 +053076static struct rmnet_ctrl_pkt *alloc_rmnet_ctrl_pkt(unsigned len, gfp_t flags)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077{
78 struct rmnet_ctrl_pkt *pkt;
79
80 pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
81 if (!pkt)
82 return ERR_PTR(-ENOMEM);
83
84 pkt->buf = kmalloc(len, flags);
85 if (!pkt->buf) {
86 kfree(pkt);
87 return ERR_PTR(-ENOMEM);
88 }
Hemant Kumarf60c0252011-11-03 12:37:07 -070089
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090 pkt->len = len;
91
92 return pkt;
93}
94
Manu Gautam2b0234a2011-09-07 16:47:52 +053095static void free_rmnet_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096{
97 kfree(pkt->buf);
98 kfree(pkt);
99}
100
101/*--------------------------------------------- */
102
103/*---------------control/smd channel functions---------------- */
104
105static void grmnet_ctrl_smd_read_w(struct work_struct *w)
106{
107 struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
108 struct rmnet_ctrl_port *port = c->port;
109 int sz;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700110 size_t len;
111 void *buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 unsigned long flags;
113
Jack Pham1b236d12012-03-19 15:27:18 -0700114 spin_lock_irqsave(&port->port_lock, flags);
115 while (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 sz = smd_cur_packet_size(c->ch);
Jack Pham1b236d12012-03-19 15:27:18 -0700117 if (sz <= 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118 break;
119
120 if (smd_read_avail(c->ch) < sz)
121 break;
122
Jack Pham1b236d12012-03-19 15:27:18 -0700123 spin_unlock_irqrestore(&port->port_lock, flags);
124
Hemant Kumarf60c0252011-11-03 12:37:07 -0700125 buf = kmalloc(sz, GFP_KERNEL);
126 if (!buf)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 return;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700128
129 len = smd_read(c->ch, buf, sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130
131 /* send it to USB here */
132 spin_lock_irqsave(&port->port_lock, flags);
133 if (port->port_usb && port->port_usb->send_cpkt_response) {
Hemant Kumarf60c0252011-11-03 12:37:07 -0700134 port->port_usb->send_cpkt_response(port->port_usb,
135 buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136 c->to_host++;
137 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700138 kfree(buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139 }
Jack Pham1b236d12012-03-19 15:27:18 -0700140 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141}
142
143static void grmnet_ctrl_smd_write_w(struct work_struct *w)
144{
145 struct smd_ch_info *c = container_of(w, struct smd_ch_info, write_w);
146 struct rmnet_ctrl_port *port = c->port;
147 unsigned long flags;
148 struct rmnet_ctrl_pkt *cpkt;
149 int ret;
150
151 spin_lock_irqsave(&port->port_lock, flags);
Jack Pham1b236d12012-03-19 15:27:18 -0700152 while (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153 if (list_empty(&c->tx_q))
154 break;
155
156 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
157
158 if (smd_write_avail(c->ch) < cpkt->len)
159 break;
160
161 list_del(&cpkt->list);
162 spin_unlock_irqrestore(&port->port_lock, flags);
163 ret = smd_write(c->ch, cpkt->buf, cpkt->len);
164 spin_lock_irqsave(&port->port_lock, flags);
165 if (ret != cpkt->len) {
Hemant Kumarf60c0252011-11-03 12:37:07 -0700166 pr_err("%s: smd_write failed err:%d\n", __func__, ret);
Manu Gautam2b0234a2011-09-07 16:47:52 +0530167 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 break;
169 }
Manu Gautam2b0234a2011-09-07 16:47:52 +0530170 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 c->to_modem++;
172 }
173 spin_unlock_irqrestore(&port->port_lock, flags);
174}
175
176static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700177grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno,
178 void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179{
180 unsigned long flags;
181 struct rmnet_ctrl_port *port;
182 struct smd_ch_info *c;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700183 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184
Manu Gautam2b0234a2011-09-07 16:47:52 +0530185 if (portno >= n_rmnet_ctrl_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 pr_err("%s: Invalid portno#%d\n", __func__, portno);
187 return -ENODEV;
188 }
189
Hemant Kumarf60c0252011-11-03 12:37:07 -0700190 port = ctrl_smd_ports[portno].port;
191
192 cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC);
193 if (IS_ERR(cpkt)) {
194 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
195 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 }
197
Hemant Kumarf60c0252011-11-03 12:37:07 -0700198 memcpy(cpkt->buf, buf, len);
199 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200
201 spin_lock_irqsave(&port->port_lock, flags);
202 c = &port->ctrl_ch;
203
204 /* drop cpkt if ch is not open */
205 if (!test_bit(CH_OPENED, &c->flags)) {
Manu Gautam2b0234a2011-09-07 16:47:52 +0530206 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 spin_unlock_irqrestore(&port->port_lock, flags);
208 return 0;
209 }
210
211 list_add_tail(&cpkt->list, &c->tx_q);
212 queue_work(grmnet_ctrl_wq, &c->write_w);
213 spin_unlock_irqrestore(&port->port_lock, flags);
214
215 return 0;
216}
217
Manu Gautam2b0234a2011-09-07 16:47:52 +0530218#define RMNET_CTRL_DTR 0x01
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219static void
Hemant Kumarf60c0252011-11-03 12:37:07 -0700220gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221{
222 struct rmnet_ctrl_port *port;
223 struct smd_ch_info *c;
224 int set_bits = 0;
225 int clear_bits = 0;
226 int temp = 0;
227
Manu Gautam2b0234a2011-09-07 16:47:52 +0530228 if (portno >= n_rmnet_ctrl_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229 pr_err("%s: Invalid portno#%d\n", __func__, portno);
230 return;
231 }
232
Hemant Kumarf60c0252011-11-03 12:37:07 -0700233 if (!gptr) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234 pr_err("%s: grmnet is null\n", __func__);
235 return;
236 }
237
Manu Gautam2b0234a2011-09-07 16:47:52 +0530238 port = ctrl_smd_ports[portno].port;
239 cbits = cbits & RMNET_CTRL_DTR;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240 c = &port->ctrl_ch;
241
242 /* host driver will only send DTR, but to have generic
243 * set and clear bit implementation using two separate
244 * checks
245 */
Manu Gautam2b0234a2011-09-07 16:47:52 +0530246 if (cbits & RMNET_CTRL_DTR)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 set_bits |= TIOCM_DTR;
248 else
249 clear_bits |= TIOCM_DTR;
250
251 temp |= set_bits;
252 temp &= ~clear_bits;
253
254 if (temp == c->cbits_tomodem)
255 return;
256
257 c->cbits_tomodem = temp;
258
259 if (!test_bit(CH_OPENED, &c->flags))
260 return;
261
262 pr_debug("%s: ctrl_tomodem:%d ctrl_bits:%d setbits:%d clearbits:%d\n",
263 __func__, temp, cbits, set_bits, clear_bits);
264
265 smd_tiocmset(c->ch, set_bits, clear_bits);
266}
267
268static char *get_smd_event(unsigned event)
269{
270 switch (event) {
271 case SMD_EVENT_DATA:
272 return "DATA";
273 case SMD_EVENT_OPEN:
274 return "OPEN";
275 case SMD_EVENT_CLOSE:
276 return "CLOSE";
277 }
278
279 return "UNDEFINED";
280}
281
282static void grmnet_ctrl_smd_notify(void *p, unsigned event)
283{
284 struct rmnet_ctrl_port *port = p;
285 struct smd_ch_info *c = &port->ctrl_ch;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700286 struct rmnet_ctrl_pkt *cpkt;
287 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
289 pr_debug("%s: EVENT_(%s)\n", __func__, get_smd_event(event));
290
291 switch (event) {
292 case SMD_EVENT_DATA:
293 if (smd_read_avail(c->ch))
294 queue_work(grmnet_ctrl_wq, &c->read_w);
295 if (smd_write_avail(c->ch))
296 queue_work(grmnet_ctrl_wq, &c->write_w);
297 break;
298 case SMD_EVENT_OPEN:
299 set_bit(CH_OPENED, &c->flags);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700300
301 if (port && port->port_usb && port->port_usb->connect)
302 port->port_usb->connect(port->port_usb);
303
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304 break;
305 case SMD_EVENT_CLOSE:
306 clear_bit(CH_OPENED, &c->flags);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700307
308 if (port && port->port_usb && port->port_usb->disconnect)
309 port->port_usb->disconnect(port->port_usb);
310
311 spin_lock_irqsave(&port->port_lock, flags);
312 while (!list_empty(&c->tx_q)) {
313 cpkt = list_first_entry(&c->tx_q,
314 struct rmnet_ctrl_pkt, list);
315
316 list_del(&cpkt->list);
317 free_rmnet_ctrl_pkt(cpkt);
318 }
319 spin_unlock_irqrestore(&port->port_lock, flags);
320
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 break;
322 }
323}
324/*------------------------------------------------------------ */
325
326static void grmnet_ctrl_smd_connect_w(struct work_struct *w)
327{
328 struct rmnet_ctrl_port *port =
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530329 container_of(w, struct rmnet_ctrl_port, connect_w.work);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200330 struct rmnet_ctrl_ports *port_entry = &ctrl_smd_ports[port->port_num];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 struct smd_ch_info *c = &port->ctrl_ch;
332 unsigned long flags;
Amit Blayc2b01792012-07-11 18:37:35 +0300333 int set_bits = 0;
334 int clear_bits = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335 int ret;
336
337 pr_debug("%s:\n", __func__);
338
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200339 if (!test_bit(CH_READY, &c->flags)) {
340 if (!test_bit(CH_PREPARE_READY, &c->flags)) {
341 set_bit(CH_PREPARE_READY, &c->flags);
342 platform_driver_register(&(port_entry->pdrv));
343 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344 return;
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200345 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346
347 ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify);
348 if (ret) {
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530349 if (ret == -EAGAIN) {
350 /* port not ready - retry */
351 pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
352 __func__, c->name, ret);
353 queue_delayed_work(grmnet_ctrl_wq, &port->connect_w,
354 msecs_to_jiffies(250));
355 } else {
356 pr_err("%s: unable to open smd port:%s err:%d\n",
357 __func__, c->name, ret);
358 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359 return;
360 }
361
Amit Blayc2b01792012-07-11 18:37:35 +0300362 set_bits = c->cbits_tomodem;
363 clear_bits = ~(c->cbits_tomodem | TIOCM_RTS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 spin_lock_irqsave(&port->port_lock, flags);
365 if (port->port_usb)
Amit Blayc2b01792012-07-11 18:37:35 +0300366 smd_tiocmset(c->ch, set_bits, clear_bits);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367 spin_unlock_irqrestore(&port->port_lock, flags);
368}
369
370int gsmd_ctrl_connect(struct grmnet *gr, int port_num)
371{
372 struct rmnet_ctrl_port *port;
373 struct smd_ch_info *c;
374 unsigned long flags;
375
376 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
377
Manu Gautam2b0234a2011-09-07 16:47:52 +0530378 if (port_num >= n_rmnet_ctrl_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 pr_err("%s: invalid portno#%d\n", __func__, port_num);
380 return -ENODEV;
381 }
382
383 if (!gr) {
384 pr_err("%s: grmnet port is null\n", __func__);
385 return -ENODEV;
386 }
387
Manu Gautam2b0234a2011-09-07 16:47:52 +0530388 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 c = &port->ctrl_ch;
390
391 spin_lock_irqsave(&port->port_lock, flags);
392 port->port_usb = gr;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700393 gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem;
394 gr->notify_modem = gsmd_ctrl_send_cbits_tomodem;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 spin_unlock_irqrestore(&port->port_lock, flags);
396
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530397 queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398
399 return 0;
400}
401
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200402static void grmnet_ctrl_smd_disconnect_w(struct work_struct *w)
403{
404 struct rmnet_ctrl_port *port =
405 container_of(w, struct rmnet_ctrl_port,
406 disconnect_w.work);
407 struct smd_ch_info *c;
408 struct platform_driver *pdrv;
409
410 c = &port->ctrl_ch;
411 if (test_bit(CH_READY, &c->flags) ||
412 test_bit(CH_PREPARE_READY, &c->flags)) {
413 clear_bit(CH_PREPARE_READY, &c->flags);
414 pdrv = &ctrl_smd_ports[port->port_num].pdrv;
415 platform_driver_unregister(pdrv);
416 }
417}
418
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
420{
421 struct rmnet_ctrl_port *port;
422 unsigned long flags;
423 struct smd_ch_info *c;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700424 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425
426 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
427
Manu Gautam2b0234a2011-09-07 16:47:52 +0530428 if (port_num >= n_rmnet_ctrl_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 pr_err("%s: invalid portno#%d\n", __func__, port_num);
430 return;
431 }
432
433 if (!gr) {
434 pr_err("%s: grmnet port is null\n", __func__);
435 return;
436 }
437
Manu Gautam2b0234a2011-09-07 16:47:52 +0530438 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700439 c = &port->ctrl_ch;
440
441 spin_lock_irqsave(&port->port_lock, flags);
442 port->port_usb = 0;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700443 gr->send_encap_cmd = 0;
444 gr->notify_modem = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445 c->cbits_tomodem = 0;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700446
447 while (!list_empty(&c->tx_q)) {
448 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
449
450 list_del(&cpkt->list);
451 free_rmnet_ctrl_pkt(cpkt);
452 }
453
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454 spin_unlock_irqrestore(&port->port_lock, flags);
455
Jack Phamc02d4812012-01-25 15:52:04 -0800456 if (test_and_clear_bit(CH_OPENED, &c->flags))
457 /* send dtr zero */
458 smd_tiocmset(c->ch, c->cbits_tomodem, ~c->cbits_tomodem);
459
460 if (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461 smd_close(c->ch);
Jack Phamc02d4812012-01-25 15:52:04 -0800462 c->ch = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 }
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200464
465 queue_delayed_work(grmnet_ctrl_wq, &port->disconnect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466}
467
468#define SMD_CH_MAX_LEN 20
469static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev)
470{
471 struct rmnet_ctrl_port *port;
472 struct smd_ch_info *c;
473 int i;
474 unsigned long flags;
475
476 pr_debug("%s: name:%s\n", __func__, pdev->name);
477
Manu Gautam2b0234a2011-09-07 16:47:52 +0530478 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
479 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700480 c = &port->ctrl_ch;
481
482 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200483 clear_bit(CH_PREPARE_READY, &c->flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484 set_bit(CH_READY, &c->flags);
485
486 /* if usb is online, try opening smd_ch */
487 spin_lock_irqsave(&port->port_lock, flags);
488 if (port->port_usb)
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530489 queue_delayed_work(grmnet_ctrl_wq,
490 &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491 spin_unlock_irqrestore(&port->port_lock, flags);
492
493 break;
494 }
495 }
496
497 return 0;
498}
499
500static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev)
501{
502 struct rmnet_ctrl_port *port;
503 struct smd_ch_info *c;
504 int i;
505
506 pr_debug("%s: name:%s\n", __func__, pdev->name);
507
Manu Gautam2b0234a2011-09-07 16:47:52 +0530508 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
509 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 c = &port->ctrl_ch;
511
512 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
513 clear_bit(CH_READY, &c->flags);
514 clear_bit(CH_OPENED, &c->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800515 if (c->ch) {
516 smd_close(c->ch);
517 c->ch = NULL;
518 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 break;
520 }
521 }
522
523 return 0;
524}
525
526
527static void grmnet_ctrl_smd_port_free(int portno)
528{
Manu Gautam2b0234a2011-09-07 16:47:52 +0530529 struct rmnet_ctrl_port *port = ctrl_smd_ports[portno].port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700530 struct platform_driver *pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531
Jack Phameffd4ae2011-08-03 16:49:36 -0700532 if (port) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533 kfree(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700534 platform_driver_unregister(pdrv);
535 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536}
537
538static int grmnet_ctrl_smd_port_alloc(int portno)
539{
540 struct rmnet_ctrl_port *port;
541 struct smd_ch_info *c;
542 struct platform_driver *pdrv;
543
544 port = kzalloc(sizeof(struct rmnet_ctrl_port), GFP_KERNEL);
545 if (!port)
546 return -ENOMEM;
547
548 port->port_num = portno;
549
550 spin_lock_init(&port->port_lock);
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530551 INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200552 INIT_DELAYED_WORK(&port->disconnect_w, grmnet_ctrl_smd_disconnect_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553
554 c = &port->ctrl_ch;
555 c->name = rmnet_ctrl_names[portno];
556 c->port = port;
557 init_waitqueue_head(&c->wait);
558 INIT_LIST_HEAD(&c->tx_q);
559 INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
560 INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
561
Manu Gautam2b0234a2011-09-07 16:47:52 +0530562 ctrl_smd_ports[portno].port = port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563
Manu Gautam2b0234a2011-09-07 16:47:52 +0530564 pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700565 pdrv->probe = grmnet_ctrl_smd_ch_probe;
566 pdrv->remove = grmnet_ctrl_smd_ch_remove;
567 pdrv->driver.name = c->name;
568 pdrv->driver.owner = THIS_MODULE;
569
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700570 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
571
572 return 0;
573}
574
575int gsmd_ctrl_setup(unsigned int count)
576{
577 int i;
578 int ret;
579
580 pr_debug("%s: requested ports:%d\n", __func__, count);
581
Manu Gautam2b0234a2011-09-07 16:47:52 +0530582 if (!count || count > NR_CTRL_SMD_PORTS) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 pr_err("%s: Invalid num of ports count:%d\n",
584 __func__, count);
585 return -EINVAL;
586 }
587
588 grmnet_ctrl_wq = alloc_workqueue("gsmd_ctrl",
589 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
590 if (!grmnet_ctrl_wq) {
591 pr_err("%s: Unable to create workqueue grmnet_ctrl\n",
592 __func__);
593 return -ENOMEM;
594 }
595
596 for (i = 0; i < count; i++) {
Ofir Cohena1c2a872011-12-14 10:26:34 +0200597 n_rmnet_ctrl_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 ret = grmnet_ctrl_smd_port_alloc(i);
599 if (ret) {
600 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200601 n_rmnet_ctrl_ports--;
Manu Gautam2b0234a2011-09-07 16:47:52 +0530602 goto free_ctrl_smd_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700603 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604 }
605
606 return 0;
607
Manu Gautam2b0234a2011-09-07 16:47:52 +0530608free_ctrl_smd_ports:
609 for (i = 0; i < n_rmnet_ctrl_ports; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700610 grmnet_ctrl_smd_port_free(i);
611
612 destroy_workqueue(grmnet_ctrl_wq);
613
614 return ret;
615}
616
617#if defined(CONFIG_DEBUG_FS)
618#define DEBUG_BUF_SIZE 1024
619static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf,
620 size_t count, loff_t *ppos)
621{
622 struct rmnet_ctrl_port *port;
623 struct smd_ch_info *c;
624 char *buf;
625 unsigned long flags;
626 int ret;
627 int i;
628 int temp = 0;
629
630 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
631 if (!buf)
632 return -ENOMEM;
633
Manu Gautam2b0234a2011-09-07 16:47:52 +0530634 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
635 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636 if (!port)
637 continue;
638 spin_lock_irqsave(&port->port_lock, flags);
639
640 c = &port->ctrl_ch;
641
642 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
643 "#PORT:%d port:%p ctrl_ch:%p#\n"
644 "to_usbhost: %lu\n"
645 "to_modem: %lu\n"
646 "DTR: %s\n"
647 "ch_open: %d\n"
648 "ch_ready: %d\n"
649 "read_avail: %d\n"
650 "write_avail:%d\n",
651 i, port, &port->ctrl_ch,
652 c->to_host, c->to_modem,
653 c->cbits_tomodem ? "HIGH" : "LOW",
654 test_bit(CH_OPENED, &c->flags),
655 test_bit(CH_READY, &c->flags),
Jack Phamc02d4812012-01-25 15:52:04 -0800656 c->ch ? smd_read_avail(c->ch) : 0,
657 c->ch ? smd_write_avail(c->ch) : 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658
659 spin_unlock_irqrestore(&port->port_lock, flags);
660 }
661
662 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
663
664 kfree(buf);
665
666 return ret;
667}
668
669static ssize_t gsmd_ctrl_reset_stats(struct file *file, const char __user *buf,
670 size_t count, loff_t *ppos)
671{
672 struct rmnet_ctrl_port *port;
673 struct smd_ch_info *c;
674 int i;
675 unsigned long flags;
676
Manu Gautam2b0234a2011-09-07 16:47:52 +0530677 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
678 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679 if (!port)
680 continue;
681
682 spin_lock_irqsave(&port->port_lock, flags);
683
684 c = &port->ctrl_ch;
685
686 c->to_host = 0;
687 c->to_modem = 0;
688
689 spin_unlock_irqrestore(&port->port_lock, flags);
690 }
691 return count;
692}
693
694const struct file_operations gsmd_ctrl_stats_ops = {
695 .read = gsmd_ctrl_read_stats,
696 .write = gsmd_ctrl_reset_stats,
697};
698
699struct dentry *smd_ctrl_dent;
700struct dentry *smd_ctrl_dfile;
701static void gsmd_ctrl_debugfs_init(void)
702{
703 smd_ctrl_dent = debugfs_create_dir("usb_rmnet_ctrl_smd", 0);
704 if (IS_ERR(smd_ctrl_dent))
705 return;
706
707 smd_ctrl_dfile = debugfs_create_file("status", 0444, smd_ctrl_dent, 0,
708 &gsmd_ctrl_stats_ops);
709 if (!smd_ctrl_dfile || IS_ERR(smd_ctrl_dfile))
710 debugfs_remove(smd_ctrl_dent);
711}
712
713static void gsmd_ctrl_debugfs_exit(void)
714{
715 debugfs_remove(smd_ctrl_dfile);
716 debugfs_remove(smd_ctrl_dent);
717}
718
719#else
720static void gsmd_ctrl_debugfs_init(void) { }
721static void gsmd_ctrl_debugfs_exit(void) { }
722#endif
723
724static int __init gsmd_ctrl_init(void)
725{
726 gsmd_ctrl_debugfs_init();
727
728 return 0;
729}
730module_init(gsmd_ctrl_init);
731
732static void __exit gsmd_ctrl_exit(void)
733{
734 gsmd_ctrl_debugfs_exit();
735}
736module_exit(gsmd_ctrl_exit);
737MODULE_DESCRIPTION("smd control driver");
738MODULE_LICENSE("GPL v2");