blob: 5a34679ad32369be455607fe89b4ca59a47b745a [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"},
Bar Weinered1059e2013-04-16 11:23:55 +030031 {"DATA39_CNTL"},
Bar Weiner6c817eb2013-04-15 20:52:18 +030032};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033static struct workqueue_struct *grmnet_ctrl_wq;
34
Bar Weiner6c817eb2013-04-15 20:52:18 +030035u8 online_clients;
36
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#define SMD_CH_MAX_LEN 20
38#define CH_OPENED 0
39#define CH_READY 1
Ido Shayevitzb84439b2013-01-08 10:31:50 +020040#define CH_PREPARE_READY 2
41
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042struct smd_ch_info {
43 struct smd_channel *ch;
44 char *name;
45 unsigned long flags;
46 wait_queue_head_t wait;
47 unsigned dtr;
48
49 struct list_head tx_q;
50 unsigned long tx_len;
51
52 struct work_struct read_w;
53 struct work_struct write_w;
54
55 struct rmnet_ctrl_port *port;
56
57 int cbits_tomodem;
58 /* stats */
59 unsigned long to_modem;
60 unsigned long to_host;
61};
62
63struct rmnet_ctrl_port {
64 struct smd_ch_info ctrl_ch;
65 unsigned int port_num;
66 struct grmnet *port_usb;
67
68 spinlock_t port_lock;
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +053069 struct delayed_work connect_w;
Ido Shayevitzb84439b2013-01-08 10:31:50 +020070 struct delayed_work disconnect_w;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071};
72
73static struct rmnet_ctrl_ports {
74 struct rmnet_ctrl_port *port;
75 struct platform_driver pdrv;
Bar Weiner6c817eb2013-04-15 20:52:18 +030076} ctrl_smd_ports[MAX_CTRL_PORT];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077
78
79/*---------------misc functions---------------- */
80
Manu Gautam2b0234a2011-09-07 16:47:52 +053081static struct rmnet_ctrl_pkt *alloc_rmnet_ctrl_pkt(unsigned len, gfp_t flags)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082{
83 struct rmnet_ctrl_pkt *pkt;
84
85 pkt = kzalloc(sizeof(struct rmnet_ctrl_pkt), flags);
86 if (!pkt)
87 return ERR_PTR(-ENOMEM);
88
89 pkt->buf = kmalloc(len, flags);
90 if (!pkt->buf) {
91 kfree(pkt);
92 return ERR_PTR(-ENOMEM);
93 }
Hemant Kumarf60c0252011-11-03 12:37:07 -070094
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 pkt->len = len;
96
97 return pkt;
98}
99
Manu Gautam2b0234a2011-09-07 16:47:52 +0530100static void free_rmnet_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101{
102 kfree(pkt->buf);
103 kfree(pkt);
104}
105
106/*--------------------------------------------- */
107
108/*---------------control/smd channel functions---------------- */
109
110static void grmnet_ctrl_smd_read_w(struct work_struct *w)
111{
112 struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
113 struct rmnet_ctrl_port *port = c->port;
114 int sz;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700115 size_t len;
116 void *buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117 unsigned long flags;
118
Jack Pham1b236d12012-03-19 15:27:18 -0700119 spin_lock_irqsave(&port->port_lock, flags);
120 while (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121 sz = smd_cur_packet_size(c->ch);
Jack Pham1b236d12012-03-19 15:27:18 -0700122 if (sz <= 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123 break;
124
125 if (smd_read_avail(c->ch) < sz)
126 break;
127
Jack Pham1b236d12012-03-19 15:27:18 -0700128 spin_unlock_irqrestore(&port->port_lock, flags);
129
Hemant Kumarf60c0252011-11-03 12:37:07 -0700130 buf = kmalloc(sz, GFP_KERNEL);
131 if (!buf)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 return;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700133
134 len = smd_read(c->ch, buf, sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135
136 /* send it to USB here */
137 spin_lock_irqsave(&port->port_lock, flags);
138 if (port->port_usb && port->port_usb->send_cpkt_response) {
Hemant Kumarf60c0252011-11-03 12:37:07 -0700139 port->port_usb->send_cpkt_response(port->port_usb,
140 buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141 c->to_host++;
142 }
Hemant Kumarf60c0252011-11-03 12:37:07 -0700143 kfree(buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 }
Jack Pham1b236d12012-03-19 15:27:18 -0700145 spin_unlock_irqrestore(&port->port_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146}
147
148static void grmnet_ctrl_smd_write_w(struct work_struct *w)
149{
150 struct smd_ch_info *c = container_of(w, struct smd_ch_info, write_w);
151 struct rmnet_ctrl_port *port = c->port;
152 unsigned long flags;
153 struct rmnet_ctrl_pkt *cpkt;
154 int ret;
155
156 spin_lock_irqsave(&port->port_lock, flags);
Jack Pham1b236d12012-03-19 15:27:18 -0700157 while (c->ch) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158 if (list_empty(&c->tx_q))
159 break;
160
161 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
162
163 if (smd_write_avail(c->ch) < cpkt->len)
164 break;
165
166 list_del(&cpkt->list);
167 spin_unlock_irqrestore(&port->port_lock, flags);
168 ret = smd_write(c->ch, cpkt->buf, cpkt->len);
169 spin_lock_irqsave(&port->port_lock, flags);
170 if (ret != cpkt->len) {
Hemant Kumarf60c0252011-11-03 12:37:07 -0700171 pr_err("%s: smd_write failed err:%d\n", __func__, ret);
Manu Gautam2b0234a2011-09-07 16:47:52 +0530172 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 break;
174 }
Manu Gautam2b0234a2011-09-07 16:47:52 +0530175 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176 c->to_modem++;
177 }
178 spin_unlock_irqrestore(&port->port_lock, flags);
179}
Bar Weiner6c817eb2013-04-15 20:52:18 +0300180static int is_legal_port_num(u8 portno)
181{
182 if (portno >= MAX_CTRL_PORT)
183 return false;
184 if (ctrl_smd_ports[portno].port == NULL)
185 return false;
186
187 return true;
188}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189
190static int
Hemant Kumarf60c0252011-11-03 12:37:07 -0700191grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno,
192 void *buf, size_t len)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193{
194 unsigned long flags;
195 struct rmnet_ctrl_port *port;
196 struct smd_ch_info *c;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700197 struct rmnet_ctrl_pkt *cpkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198
Bar Weiner6c817eb2013-04-15 20:52:18 +0300199 if (!is_legal_port_num(portno)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 pr_err("%s: Invalid portno#%d\n", __func__, portno);
201 return -ENODEV;
202 }
203
Hemant Kumarf60c0252011-11-03 12:37:07 -0700204 port = ctrl_smd_ports[portno].port;
205
206 cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC);
207 if (IS_ERR(cpkt)) {
208 pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
209 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 }
211
Hemant Kumarf60c0252011-11-03 12:37:07 -0700212 memcpy(cpkt->buf, buf, len);
213 cpkt->len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214
215 spin_lock_irqsave(&port->port_lock, flags);
216 c = &port->ctrl_ch;
217
218 /* drop cpkt if ch is not open */
219 if (!test_bit(CH_OPENED, &c->flags)) {
Manu Gautam2b0234a2011-09-07 16:47:52 +0530220 free_rmnet_ctrl_pkt(cpkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221 spin_unlock_irqrestore(&port->port_lock, flags);
222 return 0;
223 }
224
225 list_add_tail(&cpkt->list, &c->tx_q);
226 queue_work(grmnet_ctrl_wq, &c->write_w);
227 spin_unlock_irqrestore(&port->port_lock, flags);
228
229 return 0;
230}
231
Manu Gautam2b0234a2011-09-07 16:47:52 +0530232#define RMNET_CTRL_DTR 0x01
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233static void
Hemant Kumarf60c0252011-11-03 12:37:07 -0700234gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235{
236 struct rmnet_ctrl_port *port;
237 struct smd_ch_info *c;
238 int set_bits = 0;
239 int clear_bits = 0;
240 int temp = 0;
241
Bar Weiner6c817eb2013-04-15 20:52:18 +0300242 if (!is_legal_port_num(portno)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 pr_err("%s: Invalid portno#%d\n", __func__, portno);
244 return;
245 }
246
Hemant Kumarf60c0252011-11-03 12:37:07 -0700247 if (!gptr) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 pr_err("%s: grmnet is null\n", __func__);
249 return;
250 }
251
Manu Gautam2b0234a2011-09-07 16:47:52 +0530252 port = ctrl_smd_ports[portno].port;
253 cbits = cbits & RMNET_CTRL_DTR;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 c = &port->ctrl_ch;
255
256 /* host driver will only send DTR, but to have generic
257 * set and clear bit implementation using two separate
258 * checks
259 */
Manu Gautam2b0234a2011-09-07 16:47:52 +0530260 if (cbits & RMNET_CTRL_DTR)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261 set_bits |= TIOCM_DTR;
262 else
263 clear_bits |= TIOCM_DTR;
264
265 temp |= set_bits;
266 temp &= ~clear_bits;
267
268 if (temp == c->cbits_tomodem)
269 return;
270
271 c->cbits_tomodem = temp;
272
273 if (!test_bit(CH_OPENED, &c->flags))
274 return;
275
276 pr_debug("%s: ctrl_tomodem:%d ctrl_bits:%d setbits:%d clearbits:%d\n",
277 __func__, temp, cbits, set_bits, clear_bits);
278
279 smd_tiocmset(c->ch, set_bits, clear_bits);
280}
281
282static char *get_smd_event(unsigned event)
283{
284 switch (event) {
285 case SMD_EVENT_DATA:
286 return "DATA";
287 case SMD_EVENT_OPEN:
288 return "OPEN";
289 case SMD_EVENT_CLOSE:
290 return "CLOSE";
291 }
292
293 return "UNDEFINED";
294}
295
296static void grmnet_ctrl_smd_notify(void *p, unsigned event)
297{
298 struct rmnet_ctrl_port *port = p;
299 struct smd_ch_info *c = &port->ctrl_ch;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700300 struct rmnet_ctrl_pkt *cpkt;
301 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302
303 pr_debug("%s: EVENT_(%s)\n", __func__, get_smd_event(event));
304
305 switch (event) {
306 case SMD_EVENT_DATA:
307 if (smd_read_avail(c->ch))
308 queue_work(grmnet_ctrl_wq, &c->read_w);
309 if (smd_write_avail(c->ch))
310 queue_work(grmnet_ctrl_wq, &c->write_w);
311 break;
312 case SMD_EVENT_OPEN:
313 set_bit(CH_OPENED, &c->flags);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700314
315 if (port && port->port_usb && port->port_usb->connect)
316 port->port_usb->connect(port->port_usb);
317
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 break;
319 case SMD_EVENT_CLOSE:
320 clear_bit(CH_OPENED, &c->flags);
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700321
322 if (port && port->port_usb && port->port_usb->disconnect)
323 port->port_usb->disconnect(port->port_usb);
324
325 spin_lock_irqsave(&port->port_lock, flags);
326 while (!list_empty(&c->tx_q)) {
327 cpkt = list_first_entry(&c->tx_q,
328 struct rmnet_ctrl_pkt, list);
329
330 list_del(&cpkt->list);
331 free_rmnet_ctrl_pkt(cpkt);
332 }
333 spin_unlock_irqrestore(&port->port_lock, flags);
334
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335 break;
336 }
337}
338/*------------------------------------------------------------ */
339
340static void grmnet_ctrl_smd_connect_w(struct work_struct *w)
341{
342 struct rmnet_ctrl_port *port =
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530343 container_of(w, struct rmnet_ctrl_port, connect_w.work);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200344 struct rmnet_ctrl_ports *port_entry = &ctrl_smd_ports[port->port_num];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 struct smd_ch_info *c = &port->ctrl_ch;
346 unsigned long flags;
Amit Blayc2b01792012-07-11 18:37:35 +0300347 int set_bits = 0;
348 int clear_bits = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 int ret;
350
351 pr_debug("%s:\n", __func__);
352
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200353 if (!test_bit(CH_READY, &c->flags)) {
354 if (!test_bit(CH_PREPARE_READY, &c->flags)) {
355 set_bit(CH_PREPARE_READY, &c->flags);
356 platform_driver_register(&(port_entry->pdrv));
357 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 return;
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200359 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360
361 ret = smd_open(c->name, &c->ch, port, grmnet_ctrl_smd_notify);
362 if (ret) {
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530363 if (ret == -EAGAIN) {
364 /* port not ready - retry */
365 pr_debug("%s: SMD port not ready - rescheduling:%s err:%d\n",
366 __func__, c->name, ret);
367 queue_delayed_work(grmnet_ctrl_wq, &port->connect_w,
368 msecs_to_jiffies(250));
369 } else {
370 pr_err("%s: unable to open smd port:%s err:%d\n",
371 __func__, c->name, ret);
372 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 return;
374 }
375
Amit Blayc2b01792012-07-11 18:37:35 +0300376 set_bits = c->cbits_tomodem;
377 clear_bits = ~(c->cbits_tomodem | TIOCM_RTS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 spin_lock_irqsave(&port->port_lock, flags);
379 if (port->port_usb)
Amit Blayc2b01792012-07-11 18:37:35 +0300380 smd_tiocmset(c->ch, set_bits, clear_bits);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381 spin_unlock_irqrestore(&port->port_lock, flags);
382}
383
384int gsmd_ctrl_connect(struct grmnet *gr, int port_num)
385{
386 struct rmnet_ctrl_port *port;
387 struct smd_ch_info *c;
388 unsigned long flags;
389
390 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
391
Bar Weiner6c817eb2013-04-15 20:52:18 +0300392 if (!is_legal_port_num(port_num)) {
393 pr_err("%s: Invalid port_num#%d\n", __func__, port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 return -ENODEV;
395 }
396
397 if (!gr) {
398 pr_err("%s: grmnet port is null\n", __func__);
399 return -ENODEV;
400 }
401
Manu Gautam2b0234a2011-09-07 16:47:52 +0530402 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403 c = &port->ctrl_ch;
404
405 spin_lock_irqsave(&port->port_lock, flags);
406 port->port_usb = gr;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700407 gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem;
408 gr->notify_modem = gsmd_ctrl_send_cbits_tomodem;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 spin_unlock_irqrestore(&port->port_lock, flags);
410
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530411 queue_delayed_work(grmnet_ctrl_wq, &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412
413 return 0;
414}
415
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200416static void grmnet_ctrl_smd_disconnect_w(struct work_struct *w)
417{
418 struct rmnet_ctrl_port *port =
419 container_of(w, struct rmnet_ctrl_port,
420 disconnect_w.work);
421 struct smd_ch_info *c;
422 struct platform_driver *pdrv;
423
424 c = &port->ctrl_ch;
Manu Gautamea8bd4f2013-04-05 13:54:27 +0530425 if (c->ch) {
426 smd_close(c->ch);
427 c->ch = NULL;
428 }
429
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200430 if (test_bit(CH_READY, &c->flags) ||
431 test_bit(CH_PREPARE_READY, &c->flags)) {
432 clear_bit(CH_PREPARE_READY, &c->flags);
433 pdrv = &ctrl_smd_ports[port->port_num].pdrv;
434 platform_driver_unregister(pdrv);
435 }
436}
437
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num)
439{
440 struct rmnet_ctrl_port *port;
441 unsigned long flags;
442 struct smd_ch_info *c;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700443 struct rmnet_ctrl_pkt *cpkt;
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200444 int clear_bits;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445
446 pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
447
Bar Weiner6c817eb2013-04-15 20:52:18 +0300448 if (!is_legal_port_num(port_num)) {
449 pr_err("%s: Invalid port_num#%d\n", __func__, port_num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450 return;
451 }
452
453 if (!gr) {
454 pr_err("%s: grmnet port is null\n", __func__);
455 return;
456 }
457
Manu Gautam2b0234a2011-09-07 16:47:52 +0530458 port = ctrl_smd_ports[port_num].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700459 c = &port->ctrl_ch;
460
461 spin_lock_irqsave(&port->port_lock, flags);
462 port->port_usb = 0;
Hemant Kumarf60c0252011-11-03 12:37:07 -0700463 gr->send_encap_cmd = 0;
464 gr->notify_modem = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 c->cbits_tomodem = 0;
Vamsi Krishna9e9921a2011-10-04 16:09:31 -0700466
467 while (!list_empty(&c->tx_q)) {
468 cpkt = list_first_entry(&c->tx_q, struct rmnet_ctrl_pkt, list);
469
470 list_del(&cpkt->list);
471 free_rmnet_ctrl_pkt(cpkt);
472 }
473
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700474 spin_unlock_irqrestore(&port->port_lock, flags);
475
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200476 if (test_and_clear_bit(CH_OPENED, &c->flags)) {
477 clear_bits = ~(c->cbits_tomodem | TIOCM_RTS);
Jack Phamc02d4812012-01-25 15:52:04 -0800478 /* send dtr zero */
Bar Weiner3a55f8e2013-02-24 17:35:42 +0200479 smd_tiocmset(c->ch, c->cbits_tomodem, clear_bits);
480 }
Jack Phamc02d4812012-01-25 15:52:04 -0800481
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200482 queue_delayed_work(grmnet_ctrl_wq, &port->disconnect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700483}
484
485#define SMD_CH_MAX_LEN 20
486static int grmnet_ctrl_smd_ch_probe(struct platform_device *pdev)
487{
488 struct rmnet_ctrl_port *port;
489 struct smd_ch_info *c;
490 int i;
491 unsigned long flags;
492
493 pr_debug("%s: name:%s\n", __func__, pdev->name);
494
Bar Weiner6c817eb2013-04-15 20:52:18 +0300495 for (i = 0; i < MAX_CTRL_PORT; i++) {
496 if (!ctrl_smd_ports[i].port)
497 continue;
498
Manu Gautam2b0234a2011-09-07 16:47:52 +0530499 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500 c = &port->ctrl_ch;
501
502 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200503 clear_bit(CH_PREPARE_READY, &c->flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700504 set_bit(CH_READY, &c->flags);
505
506 /* if usb is online, try opening smd_ch */
507 spin_lock_irqsave(&port->port_lock, flags);
508 if (port->port_usb)
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530509 queue_delayed_work(grmnet_ctrl_wq,
510 &port->connect_w, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511 spin_unlock_irqrestore(&port->port_lock, flags);
512
513 break;
514 }
515 }
516
517 return 0;
518}
519
520static int grmnet_ctrl_smd_ch_remove(struct platform_device *pdev)
521{
522 struct rmnet_ctrl_port *port;
523 struct smd_ch_info *c;
524 int i;
525
526 pr_debug("%s: name:%s\n", __func__, pdev->name);
527
Bar Weiner6c817eb2013-04-15 20:52:18 +0300528 for (i = 0; i < MAX_CTRL_PORT; i++) {
529 if (!ctrl_smd_ports[i].port)
530 continue;
531
Manu Gautam2b0234a2011-09-07 16:47:52 +0530532 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533 c = &port->ctrl_ch;
534
535 if (!strncmp(c->name, pdev->name, SMD_CH_MAX_LEN)) {
536 clear_bit(CH_READY, &c->flags);
537 clear_bit(CH_OPENED, &c->flags);
Jack Phamc02d4812012-01-25 15:52:04 -0800538 if (c->ch) {
539 smd_close(c->ch);
540 c->ch = NULL;
541 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 break;
543 }
544 }
545
546 return 0;
547}
548
549
550static void grmnet_ctrl_smd_port_free(int portno)
551{
Manu Gautam2b0234a2011-09-07 16:47:52 +0530552 struct rmnet_ctrl_port *port = ctrl_smd_ports[portno].port;
Jack Phameffd4ae2011-08-03 16:49:36 -0700553 struct platform_driver *pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554
Jack Phameffd4ae2011-08-03 16:49:36 -0700555 if (port) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 kfree(port);
Jack Phameffd4ae2011-08-03 16:49:36 -0700557 platform_driver_unregister(pdrv);
558 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559}
560
561static int grmnet_ctrl_smd_port_alloc(int portno)
562{
563 struct rmnet_ctrl_port *port;
564 struct smd_ch_info *c;
565 struct platform_driver *pdrv;
566
567 port = kzalloc(sizeof(struct rmnet_ctrl_port), GFP_KERNEL);
568 if (!port)
569 return -ENOMEM;
570
571 port->port_num = portno;
572
573 spin_lock_init(&port->port_lock);
Chiranjeevi Velempati40608bb2012-01-27 14:09:47 +0530574 INIT_DELAYED_WORK(&port->connect_w, grmnet_ctrl_smd_connect_w);
Ido Shayevitzb84439b2013-01-08 10:31:50 +0200575 INIT_DELAYED_WORK(&port->disconnect_w, grmnet_ctrl_smd_disconnect_w);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576
577 c = &port->ctrl_ch;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300578 c->name = ctrl_names[portno / MAX_CTRL_PER_CLIENT]
579 [portno % MAX_CTRL_PER_CLIENT];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580 c->port = port;
581 init_waitqueue_head(&c->wait);
582 INIT_LIST_HEAD(&c->tx_q);
583 INIT_WORK(&c->read_w, grmnet_ctrl_smd_read_w);
584 INIT_WORK(&c->write_w, grmnet_ctrl_smd_write_w);
585
Manu Gautam2b0234a2011-09-07 16:47:52 +0530586 ctrl_smd_ports[portno].port = port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587
Manu Gautam2b0234a2011-09-07 16:47:52 +0530588 pdrv = &ctrl_smd_ports[portno].pdrv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 pdrv->probe = grmnet_ctrl_smd_ch_probe;
590 pdrv->remove = grmnet_ctrl_smd_ch_remove;
591 pdrv->driver.name = c->name;
592 pdrv->driver.owner = THIS_MODULE;
593
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594 pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
595
596 return 0;
597}
598
Bar Weiner6c817eb2013-04-15 20:52:18 +0300599int gsmd_ctrl_setup(enum ctrl_client client_num, unsigned int count,
600 u8 *first_port_idx)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601{
Bar Weiner6c817eb2013-04-15 20:52:18 +0300602 int i, start_port, allocated_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700603 int ret;
604
605 pr_debug("%s: requested ports:%d\n", __func__, count);
606
Pavankumar Kondeti40165c12013-05-28 12:11:27 +0530607 if (client_num >= NR_CTRL_CLIENTS) {
608 pr_err("%s: Invalid client:%d\n", __func__, client_num);
609 return -EINVAL;
610 }
611
Bar Weiner6c817eb2013-04-15 20:52:18 +0300612 if (!count || count > MAX_CTRL_PER_CLIENT) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700613 pr_err("%s: Invalid num of ports count:%d\n",
614 __func__, count);
615 return -EINVAL;
616 }
617
Bar Weiner6c817eb2013-04-15 20:52:18 +0300618 if (!online_clients) {
619 grmnet_ctrl_wq = alloc_workqueue("gsmd_ctrl",
620 WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
621 if (!grmnet_ctrl_wq) {
622 pr_err("%s: Unable to create workqueue grmnet_ctrl\n",
623 __func__);
624 return -ENOMEM;
625 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626 }
Bar Weiner6c817eb2013-04-15 20:52:18 +0300627 online_clients++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700628
Bar Weiner6c817eb2013-04-15 20:52:18 +0300629 start_port = MAX_CTRL_PER_CLIENT * client_num;
630 allocated_ports = 0;
631 for (i = start_port; i < count + start_port; i++) {
632 allocated_ports++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633 ret = grmnet_ctrl_smd_port_alloc(i);
634 if (ret) {
635 pr_err("%s: Unable to alloc port:%d\n", __func__, i);
Bar Weiner6c817eb2013-04-15 20:52:18 +0300636 allocated_ports--;
Manu Gautam2b0234a2011-09-07 16:47:52 +0530637 goto free_ctrl_smd_ports;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700639 }
Bar Weiner6c817eb2013-04-15 20:52:18 +0300640 if (first_port_idx)
641 *first_port_idx = start_port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642 return 0;
643
Manu Gautam2b0234a2011-09-07 16:47:52 +0530644free_ctrl_smd_ports:
Bar Weiner6c817eb2013-04-15 20:52:18 +0300645 for (i = 0; i < allocated_ports; i++)
646 grmnet_ctrl_smd_port_free(start_port + i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647
Bar Weiner6c817eb2013-04-15 20:52:18 +0300648
649 online_clients--;
650 if (!online_clients)
651 destroy_workqueue(grmnet_ctrl_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652
653 return ret;
654}
655
656#if defined(CONFIG_DEBUG_FS)
657#define DEBUG_BUF_SIZE 1024
658static ssize_t gsmd_ctrl_read_stats(struct file *file, char __user *ubuf,
659 size_t count, loff_t *ppos)
660{
661 struct rmnet_ctrl_port *port;
662 struct smd_ch_info *c;
663 char *buf;
664 unsigned long flags;
665 int ret;
666 int i;
667 int temp = 0;
668
669 buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
670 if (!buf)
671 return -ENOMEM;
672
Bar Weiner6c817eb2013-04-15 20:52:18 +0300673 for (i = 0; i < MAX_CTRL_PORT; i++) {
674 if (!ctrl_smd_ports[i].port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700675 continue;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300676 port = ctrl_smd_ports[i].port;
677
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678 spin_lock_irqsave(&port->port_lock, flags);
679
680 c = &port->ctrl_ch;
681
682 temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
683 "#PORT:%d port:%p ctrl_ch:%p#\n"
684 "to_usbhost: %lu\n"
685 "to_modem: %lu\n"
686 "DTR: %s\n"
687 "ch_open: %d\n"
688 "ch_ready: %d\n"
689 "read_avail: %d\n"
690 "write_avail:%d\n",
691 i, port, &port->ctrl_ch,
692 c->to_host, c->to_modem,
693 c->cbits_tomodem ? "HIGH" : "LOW",
694 test_bit(CH_OPENED, &c->flags),
695 test_bit(CH_READY, &c->flags),
Jack Phamc02d4812012-01-25 15:52:04 -0800696 c->ch ? smd_read_avail(c->ch) : 0,
697 c->ch ? smd_write_avail(c->ch) : 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700698
699 spin_unlock_irqrestore(&port->port_lock, flags);
700 }
701
702 ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
703
704 kfree(buf);
705
706 return ret;
707}
708
709static ssize_t gsmd_ctrl_reset_stats(struct file *file, const char __user *buf,
710 size_t count, loff_t *ppos)
711{
712 struct rmnet_ctrl_port *port;
713 struct smd_ch_info *c;
714 int i;
715 unsigned long flags;
716
Bar Weiner6c817eb2013-04-15 20:52:18 +0300717 for (i = 0; i < MAX_CTRL_PORT; i++) {
718 if (!ctrl_smd_ports[i].port)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719 continue;
Bar Weiner6c817eb2013-04-15 20:52:18 +0300720 port = ctrl_smd_ports[i].port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721
722 spin_lock_irqsave(&port->port_lock, flags);
723
724 c = &port->ctrl_ch;
725
726 c->to_host = 0;
727 c->to_modem = 0;
728
729 spin_unlock_irqrestore(&port->port_lock, flags);
730 }
731 return count;
732}
733
734const struct file_operations gsmd_ctrl_stats_ops = {
735 .read = gsmd_ctrl_read_stats,
736 .write = gsmd_ctrl_reset_stats,
737};
738
739struct dentry *smd_ctrl_dent;
740struct dentry *smd_ctrl_dfile;
741static void gsmd_ctrl_debugfs_init(void)
742{
743 smd_ctrl_dent = debugfs_create_dir("usb_rmnet_ctrl_smd", 0);
744 if (IS_ERR(smd_ctrl_dent))
745 return;
746
747 smd_ctrl_dfile = debugfs_create_file("status", 0444, smd_ctrl_dent, 0,
748 &gsmd_ctrl_stats_ops);
749 if (!smd_ctrl_dfile || IS_ERR(smd_ctrl_dfile))
750 debugfs_remove(smd_ctrl_dent);
751}
752
753static void gsmd_ctrl_debugfs_exit(void)
754{
755 debugfs_remove(smd_ctrl_dfile);
756 debugfs_remove(smd_ctrl_dent);
757}
758
759#else
760static void gsmd_ctrl_debugfs_init(void) { }
761static void gsmd_ctrl_debugfs_exit(void) { }
762#endif
763
764static int __init gsmd_ctrl_init(void)
765{
766 gsmd_ctrl_debugfs_init();
Bar Weiner6c817eb2013-04-15 20:52:18 +0300767 online_clients = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768
769 return 0;
770}
771module_init(gsmd_ctrl_init);
772
773static void __exit gsmd_ctrl_exit(void)
774{
775 gsmd_ctrl_debugfs_exit();
776}
777module_exit(gsmd_ctrl_exit);
778MODULE_DESCRIPTION("smd control driver");
779MODULE_LICENSE("GPL v2");