blob: 7d4e0d54f8a0a1361554383eed03707b1de843ae [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/slab.h>
14#include <linux/init.h>
15#include <linux/uaccess.h>
16#include <linux/diagchar.h>
17#include <linux/sched.h>
18#include <linux/err.h>
19#include <linux/workqueue.h>
20#include <linux/pm_runtime.h>
21#include <linux/platform_device.h>
22#include <asm/current.h>
23#ifdef CONFIG_DIAG_OVER_USB
24#include <mach/usbdiag.h>
25#endif
26#include "diagchar_hdlc.h"
27#include "diagmem.h"
28#include "diagchar.h"
29#include "diagfwd.h"
30#include "diagfwd_sdio.h"
31
32void __diag_sdio_send_req(void)
33{
34 int r = 0;
35 void *buf = driver->buf_in_sdio;
36
37 if (driver->sdio_ch && (!driver->in_busy_sdio)) {
38 r = sdio_read_avail(driver->sdio_ch);
39
40 if (r > IN_BUF_SIZE) {
41 if (r < MAX_IN_BUF_SIZE) {
42 pr_err("diag: SDIO sending"
Shalabh Jain5d9ba342011-08-10 13:51:54 -070043 " packets more than %d bytes\n", r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044 buf = krealloc(buf, r, GFP_KERNEL);
45 } else {
46 pr_err("diag: SDIO sending"
Shalabh Jain5d9ba342011-08-10 13:51:54 -070047 " in packets more than %d bytes\n", MAX_IN_BUF_SIZE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048 return;
49 }
50 }
51 if (r > 0) {
52 if (!buf)
53 printk(KERN_INFO "Out of diagmem for SDIO\n");
54 else {
55 APPEND_DEBUG('i');
56 sdio_read(driver->sdio_ch, buf, r);
Shalabh Jain482bf122011-12-06 03:54:47 -080057 if (((!driver->usb_connected) && (driver->
58 logging_mode == USB_MODE)) || (driver->
59 logging_mode == NO_LOGGING_MODE)) {
60 /* Drop the diag payload */
61 driver->in_busy_sdio = 0;
62 return;
63 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064 APPEND_DEBUG('j');
65 driver->write_ptr_mdm->length = r;
66 driver->in_busy_sdio = 1;
67 diag_device_write(buf, SDIO_DATA,
68 driver->write_ptr_mdm);
69 }
70 }
71 }
72}
73
74static void diag_read_sdio_work_fn(struct work_struct *work)
75{
76 __diag_sdio_send_req();
77}
78
Shalabh Jain5d9ba342011-08-10 13:51:54 -070079static void diag_sdio_notify(void *ctxt, unsigned event)
80{
81 if (event == SDIO_EVENT_DATA_READ_AVAIL)
82 queue_work(driver->diag_sdio_wq,
83 &(driver->diag_read_sdio_work));
84
85 if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
86 wake_up_interruptible(&driver->wait_q);
87}
88
89static int diag_sdio_close(void)
90{
91 queue_work(driver->diag_sdio_wq, &(driver->diag_close_sdio_work));
92 return 0;
93}
94
95static void diag_close_sdio_work_fn(struct work_struct *work)
96{
97 pr_debug("diag: sdio close called\n");
98 if (sdio_close(driver->sdio_ch))
99 pr_err("diag: could not close SDIO channel\n");
100 else
101 driver->sdio_ch = NULL; /* channel successfully closed */
102}
103
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104int diagfwd_connect_sdio(void)
105{
106 int err;
107
Jay Chokshi83b4f6132013-02-14 16:20:56 -0800108 err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_SDIO_WRITE,
109 N_MDM_SDIO_READ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110 if (err)
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700111 pr_err("diag: unable to alloc USB req on mdm ch\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112
113 driver->in_busy_sdio = 0;
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700114 if (!driver->sdio_ch) {
115 err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
116 diag_sdio_notify);
117 if (err)
118 pr_info("diag: could not open SDIO channel\n");
119 else
120 pr_info("diag: opened SDIO channel\n");
121 } else {
122 pr_info("diag: SDIO channel already open\n");
123 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124
125 /* Poll USB channel to check for data*/
126 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
127 /* Poll SDIO channel to check for data*/
128 queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
129 return 0;
130}
131
132int diagfwd_disconnect_sdio(void)
133{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 usb_diag_free_req(driver->mdm_ch);
Shalabh Jain69890aa2011-10-10 12:59:16 -0700135 if (driver->sdio_ch && (driver->logging_mode == USB_MODE)) {
136 driver->in_busy_sdio = 1;
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700137 diag_sdio_close();
Shalabh Jain69890aa2011-10-10 12:59:16 -0700138 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139 return 0;
140}
141
142int diagfwd_write_complete_sdio(void)
143{
144 driver->in_busy_sdio = 0;
145 APPEND_DEBUG('q');
146 queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
147 return 0;
148}
149
150int diagfwd_read_complete_sdio(void)
151{
152 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
153 return 0;
154}
155
156void diag_read_mdm_work_fn(struct work_struct *work)
157{
158 if (driver->sdio_ch) {
Shalabh Jain482bf122011-12-06 03:54:47 -0800159 wait_event_interruptible(driver->wait_q, ((sdio_write_avail
160 (driver->sdio_ch) >= driver->read_len_mdm) ||
161 !(driver->sdio_ch)));
162 if (!(driver->sdio_ch)) {
163 pr_alert("diag: sdio channel not valid");
164 return;
165 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 if (driver->sdio_ch && driver->usb_buf_mdm_out &&
167 (driver->read_len_mdm > 0))
168 sdio_write(driver->sdio_ch, driver->usb_buf_mdm_out,
169 driver->read_len_mdm);
170 APPEND_DEBUG('x');
171 driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
172 driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
173 usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
174 APPEND_DEBUG('y');
175 }
176}
177
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178static int diag_sdio_probe(struct platform_device *pdev)
179{
180 int err;
181
182 err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
183 diag_sdio_notify);
184 if (err)
185 printk(KERN_INFO "DIAG could not open SDIO channel");
186 else {
187 printk(KERN_INFO "DIAG opened SDIO channel");
188 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
189 }
190
191 return err;
192}
193
194static int diag_sdio_remove(struct platform_device *pdev)
195{
Shalabh Jain482bf122011-12-06 03:54:47 -0800196 pr_debug("\n diag: sdio remove called");
197 /* Disable SDIO channel to prevent further read/write */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 driver->sdio_ch = NULL;
Shalabh Jain482bf122011-12-06 03:54:47 -0800199 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200}
201
202static int diagfwd_sdio_runtime_suspend(struct device *dev)
203{
204 dev_dbg(dev, "pm_runtime: suspending...\n");
205 return 0;
206}
207
208static int diagfwd_sdio_runtime_resume(struct device *dev)
209{
210 dev_dbg(dev, "pm_runtime: resuming...\n");
211 return 0;
212}
213
214static const struct dev_pm_ops diagfwd_sdio_dev_pm_ops = {
215 .runtime_suspend = diagfwd_sdio_runtime_suspend,
216 .runtime_resume = diagfwd_sdio_runtime_resume,
217};
218
219static struct platform_driver msm_sdio_ch_driver = {
220 .probe = diag_sdio_probe,
221 .remove = diag_sdio_remove,
222 .driver = {
223 .name = "SDIO_DIAG",
224 .owner = THIS_MODULE,
225 .pm = &diagfwd_sdio_dev_pm_ops,
226 },
227};
228
229void diagfwd_sdio_init(void)
230{
231 int ret;
232
233 driver->read_len_mdm = 0;
234 if (driver->buf_in_sdio == NULL)
235 driver->buf_in_sdio = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
236 if (driver->buf_in_sdio == NULL)
237 goto err;
238 if (driver->usb_buf_mdm_out == NULL)
239 driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
240 if (driver->usb_buf_mdm_out == NULL)
241 goto err;
242 if (driver->write_ptr_mdm == NULL)
243 driver->write_ptr_mdm = kzalloc(
244 sizeof(struct diag_request), GFP_KERNEL);
245 if (driver->write_ptr_mdm == NULL)
246 goto err;
247 if (driver->usb_read_mdm_ptr == NULL)
248 driver->usb_read_mdm_ptr = kzalloc(
249 sizeof(struct diag_request), GFP_KERNEL);
250 if (driver->usb_read_mdm_ptr == NULL)
251 goto err;
252 driver->diag_sdio_wq = create_singlethread_workqueue("diag_sdio_wq");
253#ifdef CONFIG_DIAG_OVER_USB
254 driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
255 diag_usb_legacy_notifier);
256 if (IS_ERR(driver->mdm_ch)) {
257 printk(KERN_ERR "Unable to open USB diag MDM channel\n");
258 goto err;
259 }
260 INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
261#endif
262 INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn);
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700263 INIT_WORK(&(driver->diag_close_sdio_work), diag_close_sdio_work_fn);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 ret = platform_driver_register(&msm_sdio_ch_driver);
265 if (ret)
266 printk(KERN_INFO "DIAG could not register SDIO device");
267 else
268 printk(KERN_INFO "DIAG registered SDIO device");
269
270 return;
271err:
272 printk(KERN_INFO "\n Could not initialize diag buf for SDIO");
273 kfree(driver->buf_in_sdio);
274 kfree(driver->usb_buf_mdm_out);
275 kfree(driver->write_ptr_mdm);
276 kfree(driver->usb_read_mdm_ptr);
277 if (driver->diag_sdio_wq)
278 destroy_workqueue(driver->diag_sdio_wq);
279}
280
281void diagfwd_sdio_exit(void)
282{
283#ifdef CONFIG_DIAG_OVER_USB
284 if (driver->usb_connected)
285 usb_diag_free_req(driver->mdm_ch);
286#endif
287 platform_driver_unregister(&msm_sdio_ch_driver);
288#ifdef CONFIG_DIAG_OVER_USB
289 usb_diag_close(driver->mdm_ch);
290#endif
291 kfree(driver->buf_in_sdio);
292 kfree(driver->usb_buf_mdm_out);
293 kfree(driver->write_ptr_mdm);
294 kfree(driver->usb_read_mdm_ptr);
295 destroy_workqueue(driver->diag_sdio_wq);
296}