blob: 161634ebf134316bc91405b3ab2f6eb37d610914 [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;
Manu Gautamea8bd4f2013-04-05 13:54:27 +0530411 if (c->ch) {
412 smd_close(c->ch);
413 c->ch = NULL;
414 }
415
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200416 if (test_bit(CH_READY, &c->flags) ||
417 test_bit(CH_PREPARE_READY, &c->flags)) {
418 clear_bit(CH_PREPARE_READY, &c->flags);
419 pdrv = &ctrl_smd_ports[port->port_num].pdrv;
420 platform_driver_unregister(pdrv);
421 }
422}
423
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
425{
426 struct rmnet_ctrl_port *port;
427 unsigned long flags;
428 struct smd_ch_info *c;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700429 struct rmnet_ctrl_pkt *cpkt;
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200430 int clear_bits;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431
432 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
433
Manu Gautam2b0234a2011-09-07 16:47:52 +0530434 if (port_num >= n_rmnet_ctrl_ports) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 pr_err("%s: invalid portno#%d\n", __func__, port_num);
436 return;
437 }
438
439 if (!gr) {
440 pr_err("%s: grmnet port is null\n", __func__);
441 return;
442 }
443
Manu Gautam2b0234a2011-09-07 16:47:52 +0530444 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445 c = &port->ctrl_ch;
446
447 spin_lock_irqsave(&port->port_lock, flags);
448 port->port_usb = 0;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700449 gr->send_encap_cmd = 0;
450 gr->notify_modem = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451 c->cbits_tomodem = 0;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700452
453 while (!list_empty(&c->tx_q)) {
454 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
455
456 list_del(&cpkt->list);
457 free_rmnet_ctrl_pkt(cpkt);
458 }
459
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460 spin_unlock_irqrestore(&port->port_lock, flags);
461
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200462 if (test_and_clear_bit(CH_OPENED, &c->flags)) {
463 clear_bits = ~(c->cbits_tomodem | TIOCM_RTS);
Jack Phamc02d4812012-01-25 15:52:04 -0800464 /* send dtr zero */
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200465 smd_tiocmset(c->ch, c->cbits_tomodem, clear_bits);
466 }
Jack Phamc02d4812012-01-25 15:52:04 -0800467
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200468 queue_delayed_work(grmnet_ctrl_wq, &port->disconnect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469}
470
471#define SMD_CH_MAX_LEN 20
472static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev)
473{
474 struct rmnet_ctrl_port *port;
475 struct smd_ch_info *c;
476 int i;
477 unsigned long flags;
478
479 pr_debug("%s: name:%s\n", __func__, pdev->name);
480
Manu Gautam2b0234a2011-09-07 16:47:52 +0530481 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
482 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700483 c = &port->ctrl_ch;
484
485 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200486 clear_bit(CH_PREPARE_READY, &c->flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487 set_bit(CH_READY, &c->flags);
488
489 /* if usb is online, try opening smd_ch */
490 spin_lock_irqsave(&port->port_lock, flags);
491 if (port->port_usb)
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530492 queue_delayed_work(grmnet_ctrl_wq,
493 &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494 spin_unlock_irqrestore(&port->port_lock, flags);
495
496 break;
497 }
498 }
499
500 return 0;
501}
502
503static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev)
504{
505 struct rmnet_ctrl_port *port;
506 struct smd_ch_info *c;
507 int i;
508
509 pr_debug("%s: name:%s\n", __func__, pdev->name);
510
Manu Gautam2b0234a2011-09-07 16:47:52 +0530511 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
512 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 c = &port->ctrl_ch;
514
515 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
516 clear_bit(CH_READY, &c->flags);
517 clear_bit(CH_OPENED, &c->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800518 if (c->ch) {
519 smd_close(c->ch);
520 c->ch = NULL;
521 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522 break;
523 }
524 }
525
526 return 0;
527}
528
529
530static void grmnet_ctrl_smd_port_free(int portno)
531{
Manu Gautam2b0234a2011-09-07 16:47:52 +0530532 struct rmnet_ctrl_port *port = ctrl_smd_ports[portno].port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700533 struct platform_driver *pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534
Jack Phameffd4ae2011-08-03 16:49:36 -0700535 if (port) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536 kfree(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700537 platform_driver_unregister(pdrv);
538 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539}
540
541static int grmnet_ctrl_smd_port_alloc(int portno)
542{
543 struct rmnet_ctrl_port *port;
544 struct smd_ch_info *c;
545 struct platform_driver *pdrv;
546
547 port = kzalloc(sizeof(struct rmnet_ctrl_port), GFP_KERNEL);
548 if (!port)
549 return -ENOMEM;
550
551 port->port_num = portno;
552
553 spin_lock_init(&port->port_lock);
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530554 INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200555 INIT_DELAYED_WORK(&port->disconnect_w, grmnet_ctrl_smd_disconnect_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556
557 c = &port->ctrl_ch;
558 c->name = rmnet_ctrl_names[portno];
559 c->port = port;
560 init_waitqueue_head(&c->wait);
561 INIT_LIST_HEAD(&c->tx_q);
562 INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
563 INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
564
Manu Gautam2b0234a2011-09-07 16:47:52 +0530565 ctrl_smd_ports[portno].port = port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566
Manu Gautam2b0234a2011-09-07 16:47:52 +0530567 pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 pdrv->probe = grmnet_ctrl_smd_ch_probe;
569 pdrv->remove = grmnet_ctrl_smd_ch_remove;
570 pdrv->driver.name = c->name;
571 pdrv->driver.owner = THIS_MODULE;
572
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
574
575 return 0;
576}
577
578int gsmd_ctrl_setup(unsigned int count)
579{
580 int i;
581 int ret;
582
583 pr_debug("%s: requested ports:%d\n", __func__, count);
584
Manu Gautam2b0234a2011-09-07 16:47:52 +0530585 if (!count || count > NR_CTRL_SMD_PORTS) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586 pr_err("%s: Invalid num of ports count:%d\n",
587 __func__, count);
588 return -EINVAL;
589 }
590
591 grmnet_ctrl_wq = alloc_workqueue("gsmd_ctrl",
592 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
593 if (!grmnet_ctrl_wq) {
594 pr_err("%s: Unable to create workqueue grmnet_ctrl\n",
595 __func__);
596 return -ENOMEM;
597 }
598
599 for (i = 0; i < count; i++) {
Ofir Cohena1c2a872011-12-14 10:26:34 +0200600 n_rmnet_ctrl_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601 ret = grmnet_ctrl_smd_port_alloc(i);
602 if (ret) {
603 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
Ofir Cohena1c2a872011-12-14 10:26:34 +0200604 n_rmnet_ctrl_ports--;
Manu Gautam2b0234a2011-09-07 16:47:52 +0530605 goto free_ctrl_smd_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700607 }
608
609 return 0;
610
Manu Gautam2b0234a2011-09-07 16:47:52 +0530611free_ctrl_smd_ports:
612 for (i = 0; i < n_rmnet_ctrl_ports; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700613 grmnet_ctrl_smd_port_free(i);
614
615 destroy_workqueue(grmnet_ctrl_wq);
616
617 return ret;
618}
619
620#if defined(CONFIG_DEBUG_FS)
621#define DEBUG_BUF_SIZE 1024
622static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf,
623 size_t count, loff_t *ppos)
624{
625 struct rmnet_ctrl_port *port;
626 struct smd_ch_info *c;
627 char *buf;
628 unsigned long flags;
629 int ret;
630 int i;
631 int temp = 0;
632
633 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
634 if (!buf)
635 return -ENOMEM;
636
Manu Gautam2b0234a2011-09-07 16:47:52 +0530637 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
638 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700639 if (!port)
640 continue;
641 spin_lock_irqsave(&port->port_lock, flags);
642
643 c = &port->ctrl_ch;
644
645 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
646 "#PORT:%d port:%p ctrl_ch:%p#\n"
647 "to_usbhost: %lu\n"
648 "to_modem: %lu\n"
649 "DTR: %s\n"
650 "ch_open: %d\n"
651 "ch_ready: %d\n"
652 "read_avail: %d\n"
653 "write_avail:%d\n",
654 i, port, &port->ctrl_ch,
655 c->to_host, c->to_modem,
656 c->cbits_tomodem ? "HIGH" : "LOW",
657 test_bit(CH_OPENED, &c->flags),
658 test_bit(CH_READY, &c->flags),
Jack Phamc02d4812012-01-25 15:52:04 -0800659 c->ch ? smd_read_avail(c->ch) : 0,
660 c->ch ? smd_write_avail(c->ch) : 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700661
662 spin_unlock_irqrestore(&port->port_lock, flags);
663 }
664
665 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
666
667 kfree(buf);
668
669 return ret;
670}
671
672static ssize_t gsmd_ctrl_reset_stats(struct file *file, const char __user *buf,
673 size_t count, loff_t *ppos)
674{
675 struct rmnet_ctrl_port *port;
676 struct smd_ch_info *c;
677 int i;
678 unsigned long flags;
679
Manu Gautam2b0234a2011-09-07 16:47:52 +0530680 for (i = 0; i < n_rmnet_ctrl_ports; i++) {
681 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 if (!port)
683 continue;
684
685 spin_lock_irqsave(&port->port_lock, flags);
686
687 c = &port->ctrl_ch;
688
689 c->to_host = 0;
690 c->to_modem = 0;
691
692 spin_unlock_irqrestore(&port->port_lock, flags);
693 }
694 return count;
695}
696
697const struct file_operations gsmd_ctrl_stats_ops = {
698 .read = gsmd_ctrl_read_stats,
699 .write = gsmd_ctrl_reset_stats,
700};
701
702struct dentry *smd_ctrl_dent;
703struct dentry *smd_ctrl_dfile;
704static void gsmd_ctrl_debugfs_init(void)
705{
706 smd_ctrl_dent = debugfs_create_dir("usb_rmnet_ctrl_smd", 0);
707 if (IS_ERR(smd_ctrl_dent))
708 return;
709
710 smd_ctrl_dfile = debugfs_create_file("status", 0444, smd_ctrl_dent, 0,
711 &gsmd_ctrl_stats_ops);
712 if (!smd_ctrl_dfile || IS_ERR(smd_ctrl_dfile))
713 debugfs_remove(smd_ctrl_dent);
714}
715
716static void gsmd_ctrl_debugfs_exit(void)
717{
718 debugfs_remove(smd_ctrl_dfile);
719 debugfs_remove(smd_ctrl_dent);
720}
721
722#else
723static void gsmd_ctrl_debugfs_init(void) { }
724static void gsmd_ctrl_debugfs_exit(void) { }
725#endif
726
727static int __init gsmd_ctrl_init(void)
728{
729 gsmd_ctrl_debugfs_init();
730
731 return 0;
732}
733module_init(gsmd_ctrl_init);
734
735static void __exit gsmd_ctrl_exit(void)
736{
737 gsmd_ctrl_debugfs_exit();
738}
739module_exit(gsmd_ctrl_exit);
740MODULE_DESCRIPTION("smd control driver");
741MODULE_LICENSE("GPL v2");