blob: f3873aad0c37c9f0a76e5459e37230ab3d06a2c7 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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);
57 APPEND_DEBUG('j');
58 driver->write_ptr_mdm->length = r;
59 driver->in_busy_sdio = 1;
60 diag_device_write(buf, SDIO_DATA,
61 driver->write_ptr_mdm);
62 }
63 }
64 }
65}
66
67static void diag_read_sdio_work_fn(struct work_struct *work)
68{
69 __diag_sdio_send_req();
70}
71
Shalabh Jain5d9ba342011-08-10 13:51:54 -070072static void diag_sdio_notify(void *ctxt, unsigned event)
73{
74 if (event == SDIO_EVENT_DATA_READ_AVAIL)
75 queue_work(driver->diag_sdio_wq,
76 &(driver->diag_read_sdio_work));
77
78 if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
79 wake_up_interruptible(&driver->wait_q);
80}
81
82static int diag_sdio_close(void)
83{
84 queue_work(driver->diag_sdio_wq, &(driver->diag_close_sdio_work));
85 return 0;
86}
87
88static void diag_close_sdio_work_fn(struct work_struct *work)
89{
90 pr_debug("diag: sdio close called\n");
91 if (sdio_close(driver->sdio_ch))
92 pr_err("diag: could not close SDIO channel\n");
93 else
94 driver->sdio_ch = NULL; /* channel successfully closed */
95}
96
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097int diagfwd_connect_sdio(void)
98{
99 int err;
100
101 err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
102 N_MDM_READ);
103 if (err)
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700104 pr_err("diag: unable to alloc USB req on mdm ch\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105
106 driver->in_busy_sdio = 0;
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700107 if (!driver->sdio_ch) {
108 err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
109 diag_sdio_notify);
110 if (err)
111 pr_info("diag: could not open SDIO channel\n");
112 else
113 pr_info("diag: opened SDIO channel\n");
114 } else {
115 pr_info("diag: SDIO channel already open\n");
116 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117
118 /* Poll USB channel to check for data*/
119 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
120 /* Poll SDIO channel to check for data*/
121 queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
122 return 0;
123}
124
125int diagfwd_disconnect_sdio(void)
126{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 usb_diag_free_req(driver->mdm_ch);
Shalabh Jain69890aa2011-10-10 12:59:16 -0700128 if (driver->sdio_ch && (driver->logging_mode == USB_MODE)) {
129 driver->in_busy_sdio = 1;
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700130 diag_sdio_close();
Shalabh Jain69890aa2011-10-10 12:59:16 -0700131 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 return 0;
133}
134
135int diagfwd_write_complete_sdio(void)
136{
137 driver->in_busy_sdio = 0;
138 APPEND_DEBUG('q');
139 queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
140 return 0;
141}
142
143int diagfwd_read_complete_sdio(void)
144{
145 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
146 return 0;
147}
148
149void diag_read_mdm_work_fn(struct work_struct *work)
150{
151 if (driver->sdio_ch) {
152 wait_event_interruptible(driver->wait_q, (sdio_write_avail
153 (driver->sdio_ch) >= driver->read_len_mdm));
154 if (driver->sdio_ch && driver->usb_buf_mdm_out &&
155 (driver->read_len_mdm > 0))
156 sdio_write(driver->sdio_ch, driver->usb_buf_mdm_out,
157 driver->read_len_mdm);
158 APPEND_DEBUG('x');
159 driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
160 driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
161 usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
162 APPEND_DEBUG('y');
163 }
164}
165
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166static int diag_sdio_probe(struct platform_device *pdev)
167{
168 int err;
169
170 err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
171 diag_sdio_notify);
172 if (err)
173 printk(KERN_INFO "DIAG could not open SDIO channel");
174 else {
175 printk(KERN_INFO "DIAG opened SDIO channel");
176 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
177 }
178
179 return err;
180}
181
182static int diag_sdio_remove(struct platform_device *pdev)
183{
184 queue_work(driver->diag_sdio_wq, &(driver->diag_remove_sdio_work));
185 return 0;
186}
187
188static void diag_remove_sdio_work_fn(struct work_struct *work)
189{
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700190 pr_debug("diag: sdio remove called\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 /*Disable SDIO channel to prevent further read/write */
192 driver->sdio_ch = NULL;
193}
194
195static int diagfwd_sdio_runtime_suspend(struct device *dev)
196{
197 dev_dbg(dev, "pm_runtime: suspending...\n");
198 return 0;
199}
200
201static int diagfwd_sdio_runtime_resume(struct device *dev)
202{
203 dev_dbg(dev, "pm_runtime: resuming...\n");
204 return 0;
205}
206
207static const struct dev_pm_ops diagfwd_sdio_dev_pm_ops = {
208 .runtime_suspend = diagfwd_sdio_runtime_suspend,
209 .runtime_resume = diagfwd_sdio_runtime_resume,
210};
211
212static struct platform_driver msm_sdio_ch_driver = {
213 .probe = diag_sdio_probe,
214 .remove = diag_sdio_remove,
215 .driver = {
216 .name = "SDIO_DIAG",
217 .owner = THIS_MODULE,
218 .pm = &diagfwd_sdio_dev_pm_ops,
219 },
220};
221
222void diagfwd_sdio_init(void)
223{
224 int ret;
225
226 driver->read_len_mdm = 0;
227 if (driver->buf_in_sdio == NULL)
228 driver->buf_in_sdio = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
229 if (driver->buf_in_sdio == NULL)
230 goto err;
231 if (driver->usb_buf_mdm_out == NULL)
232 driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
233 if (driver->usb_buf_mdm_out == NULL)
234 goto err;
235 if (driver->write_ptr_mdm == NULL)
236 driver->write_ptr_mdm = kzalloc(
237 sizeof(struct diag_request), GFP_KERNEL);
238 if (driver->write_ptr_mdm == NULL)
239 goto err;
240 if (driver->usb_read_mdm_ptr == NULL)
241 driver->usb_read_mdm_ptr = kzalloc(
242 sizeof(struct diag_request), GFP_KERNEL);
243 if (driver->usb_read_mdm_ptr == NULL)
244 goto err;
245 driver->diag_sdio_wq = create_singlethread_workqueue("diag_sdio_wq");
246#ifdef CONFIG_DIAG_OVER_USB
247 driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
248 diag_usb_legacy_notifier);
249 if (IS_ERR(driver->mdm_ch)) {
250 printk(KERN_ERR "Unable to open USB diag MDM channel\n");
251 goto err;
252 }
253 INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
254#endif
255 INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn);
256 INIT_WORK(&(driver->diag_remove_sdio_work), diag_remove_sdio_work_fn);
Shalabh Jain5d9ba342011-08-10 13:51:54 -0700257 INIT_WORK(&(driver->diag_close_sdio_work), diag_close_sdio_work_fn);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 ret = platform_driver_register(&msm_sdio_ch_driver);
259 if (ret)
260 printk(KERN_INFO "DIAG could not register SDIO device");
261 else
262 printk(KERN_INFO "DIAG registered SDIO device");
263
264 return;
265err:
266 printk(KERN_INFO "\n Could not initialize diag buf for SDIO");
267 kfree(driver->buf_in_sdio);
268 kfree(driver->usb_buf_mdm_out);
269 kfree(driver->write_ptr_mdm);
270 kfree(driver->usb_read_mdm_ptr);
271 if (driver->diag_sdio_wq)
272 destroy_workqueue(driver->diag_sdio_wq);
273}
274
275void diagfwd_sdio_exit(void)
276{
277#ifdef CONFIG_DIAG_OVER_USB
278 if (driver->usb_connected)
279 usb_diag_free_req(driver->mdm_ch);
280#endif
281 platform_driver_unregister(&msm_sdio_ch_driver);
282#ifdef CONFIG_DIAG_OVER_USB
283 usb_diag_close(driver->mdm_ch);
284#endif
285 kfree(driver->buf_in_sdio);
286 kfree(driver->usb_buf_mdm_out);
287 kfree(driver->write_ptr_mdm);
288 kfree(driver->usb_read_mdm_ptr);
289 destroy_workqueue(driver->diag_sdio_wq);
290}