blob: 8d4328623e6968317a9a1f82bd882c217101b3e4 [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"
43 " in packets more than %d bytes", r);
44 buf = krealloc(buf, r, GFP_KERNEL);
45 } else {
46 pr_err("diag: SDIO sending"
47 " in packets more than %d bytes", MAX_IN_BUF_SIZE);
48 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
72int diagfwd_connect_sdio(void)
73{
74 int err;
75
76 err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
77 N_MDM_READ);
78 if (err)
79 printk(KERN_ERR "diag: unable to alloc USB req on mdm ch");
80
81 driver->in_busy_sdio = 0;
82
83 /* Poll USB channel to check for data*/
84 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
85 /* Poll SDIO channel to check for data*/
86 queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
87 return 0;
88}
89
90int diagfwd_disconnect_sdio(void)
91{
92 driver->in_busy_sdio = 1;
93 usb_diag_free_req(driver->mdm_ch);
94 return 0;
95}
96
97int diagfwd_write_complete_sdio(void)
98{
99 driver->in_busy_sdio = 0;
100 APPEND_DEBUG('q');
101 queue_work(driver->diag_sdio_wq, &(driver->diag_read_sdio_work));
102 return 0;
103}
104
105int diagfwd_read_complete_sdio(void)
106{
107 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
108 return 0;
109}
110
111void diag_read_mdm_work_fn(struct work_struct *work)
112{
113 if (driver->sdio_ch) {
114 wait_event_interruptible(driver->wait_q, (sdio_write_avail
115 (driver->sdio_ch) >= driver->read_len_mdm));
116 if (driver->sdio_ch && driver->usb_buf_mdm_out &&
117 (driver->read_len_mdm > 0))
118 sdio_write(driver->sdio_ch, driver->usb_buf_mdm_out,
119 driver->read_len_mdm);
120 APPEND_DEBUG('x');
121 driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
122 driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
123 usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
124 APPEND_DEBUG('y');
125 }
126}
127
128static void diag_sdio_notify(void *ctxt, unsigned event)
129{
130 if (event == SDIO_EVENT_DATA_READ_AVAIL)
131 queue_work(driver->diag_sdio_wq,
132 &(driver->diag_read_sdio_work));
133
134 if (event == SDIO_EVENT_DATA_WRITE_AVAIL)
135 wake_up_interruptible(&driver->wait_q);
136}
137
138static int diag_sdio_probe(struct platform_device *pdev)
139{
140 int err;
141
142 err = sdio_open("SDIO_DIAG", &driver->sdio_ch, driver,
143 diag_sdio_notify);
144 if (err)
145 printk(KERN_INFO "DIAG could not open SDIO channel");
146 else {
147 printk(KERN_INFO "DIAG opened SDIO channel");
148 queue_work(driver->diag_sdio_wq, &(driver->diag_read_mdm_work));
149 }
150
151 return err;
152}
153
154static int diag_sdio_remove(struct platform_device *pdev)
155{
156 queue_work(driver->diag_sdio_wq, &(driver->diag_remove_sdio_work));
157 return 0;
158}
159
160static void diag_remove_sdio_work_fn(struct work_struct *work)
161{
162 pr_debug("\n diag: sdio remove called");
163 /*Disable SDIO channel to prevent further read/write */
164 driver->sdio_ch = NULL;
165}
166
167static int diagfwd_sdio_runtime_suspend(struct device *dev)
168{
169 dev_dbg(dev, "pm_runtime: suspending...\n");
170 return 0;
171}
172
173static int diagfwd_sdio_runtime_resume(struct device *dev)
174{
175 dev_dbg(dev, "pm_runtime: resuming...\n");
176 return 0;
177}
178
179static const struct dev_pm_ops diagfwd_sdio_dev_pm_ops = {
180 .runtime_suspend = diagfwd_sdio_runtime_suspend,
181 .runtime_resume = diagfwd_sdio_runtime_resume,
182};
183
184static struct platform_driver msm_sdio_ch_driver = {
185 .probe = diag_sdio_probe,
186 .remove = diag_sdio_remove,
187 .driver = {
188 .name = "SDIO_DIAG",
189 .owner = THIS_MODULE,
190 .pm = &diagfwd_sdio_dev_pm_ops,
191 },
192};
193
194void diagfwd_sdio_init(void)
195{
196 int ret;
197
198 driver->read_len_mdm = 0;
199 if (driver->buf_in_sdio == NULL)
200 driver->buf_in_sdio = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
201 if (driver->buf_in_sdio == NULL)
202 goto err;
203 if (driver->usb_buf_mdm_out == NULL)
204 driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
205 if (driver->usb_buf_mdm_out == NULL)
206 goto err;
207 if (driver->write_ptr_mdm == NULL)
208 driver->write_ptr_mdm = kzalloc(
209 sizeof(struct diag_request), GFP_KERNEL);
210 if (driver->write_ptr_mdm == NULL)
211 goto err;
212 if (driver->usb_read_mdm_ptr == NULL)
213 driver->usb_read_mdm_ptr = kzalloc(
214 sizeof(struct diag_request), GFP_KERNEL);
215 if (driver->usb_read_mdm_ptr == NULL)
216 goto err;
217 driver->diag_sdio_wq = create_singlethread_workqueue("diag_sdio_wq");
218#ifdef CONFIG_DIAG_OVER_USB
219 driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
220 diag_usb_legacy_notifier);
221 if (IS_ERR(driver->mdm_ch)) {
222 printk(KERN_ERR "Unable to open USB diag MDM channel\n");
223 goto err;
224 }
225 INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
226#endif
227 INIT_WORK(&(driver->diag_read_sdio_work), diag_read_sdio_work_fn);
228 INIT_WORK(&(driver->diag_remove_sdio_work), diag_remove_sdio_work_fn);
229 ret = platform_driver_register(&msm_sdio_ch_driver);
230 if (ret)
231 printk(KERN_INFO "DIAG could not register SDIO device");
232 else
233 printk(KERN_INFO "DIAG registered SDIO device");
234
235 return;
236err:
237 printk(KERN_INFO "\n Could not initialize diag buf for SDIO");
238 kfree(driver->buf_in_sdio);
239 kfree(driver->usb_buf_mdm_out);
240 kfree(driver->write_ptr_mdm);
241 kfree(driver->usb_read_mdm_ptr);
242 if (driver->diag_sdio_wq)
243 destroy_workqueue(driver->diag_sdio_wq);
244}
245
246void diagfwd_sdio_exit(void)
247{
248#ifdef CONFIG_DIAG_OVER_USB
249 if (driver->usb_connected)
250 usb_diag_free_req(driver->mdm_ch);
251#endif
252 platform_driver_unregister(&msm_sdio_ch_driver);
253#ifdef CONFIG_DIAG_OVER_USB
254 usb_diag_close(driver->mdm_ch);
255#endif
256 kfree(driver->buf_in_sdio);
257 kfree(driver->usb_buf_mdm_out);
258 kfree(driver->write_ptr_mdm);
259 kfree(driver->usb_read_mdm_ptr);
260 destroy_workqueue(driver->diag_sdio_wq);
261}