blob: b2080b3c77494816dc1427493c29202b9dc7a0ad [file] [log] [blame]
Dixon Peterson32e70bb2011-12-16 13:26:45 -08001/* Copyright (c) 2012, 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_hsic.h"
31
32static void diag_read_hsic_work_fn(struct work_struct *work)
33{
34 if (!driver->hsic_ch) {
35 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
36 return;
37 }
38
39 /*
40 * If there is no hsic data being read from the hsic and there
41 * is no hsic data being written to the usb mdm channel
42 */
43 if (!driver->in_busy_hsic_read && !driver->in_busy_hsic_write_on_mdm) {
44 /*
45 * Initiate the read from the hsic. The hsic read is
46 * asynchronous. Once the read is complete the read
47 * callback function will be called.
48 */
49 int err;
50 driver->in_busy_hsic_read = 1;
51 APPEND_DEBUG('i');
52 err = diag_bridge_read((char *)driver->buf_in_hsic,
53 IN_BUF_SIZE);
54 if (err) {
55 pr_err("DIAG: Error initiating HSIC read, err: %d\n",
56 err);
57 /*
58 * If the error is recoverable, then clear
59 * the read flag, so we will resubmit a
60 * read on the next frame. Otherwise, don't
61 * resubmit a read on the next frame.
62 */
63 if ((-ESHUTDOWN) != err)
64 driver->in_busy_hsic_read = 0;
65 }
66 }
67
68 /*
69 * If for some reason there was no hsic data, set up
70 * the next read
71 */
72 if (!driver->in_busy_hsic_read)
73 queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
74}
75
76static void diag_hsic_read_complete_callback(void *ctxt, char *buf,
77 int buf_size, int actual_size)
78{
79 /* The read of the data from the HSIC bridge is complete */
80 driver->in_busy_hsic_read = 0;
81
82 if (!driver->hsic_ch) {
83 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
84 return;
85 }
86
87 APPEND_DEBUG('j');
88 if (actual_size > 0) {
89 if (!buf) {
90 pr_err("Out of diagmem for HSIC\n");
91 } else {
92 driver->write_ptr_mdm->length = actual_size;
93 /*
94 * Set flag to denote hsic data is currently
95 * being written to the usb mdm channel.
96 * driver->buf_in_hsic was given to
97 * diag_bridge_read(), so buf here should be
98 * driver->buf_in_hsic
99 */
100 driver->in_busy_hsic_write_on_mdm = 1;
101 diag_device_write((void *)buf, HSIC_DATA,
102 driver->write_ptr_mdm);
103 }
104 } else {
Jack Phamb60775a2012-02-14 17:57:41 -0800105 pr_debug("%s: actual_size: %d\n", __func__, actual_size);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800106 }
107
108 /*
109 * If for some reason there was no hsic data to write to the
110 * mdm channel, set up another read
111 */
Jack Phamb60775a2012-02-14 17:57:41 -0800112 if (!driver->in_busy_hsic_write_on_mdm &&
113 driver->usb_mdm_connected &&
114 !driver->hsic_suspend)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800115 queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
116}
117
118static void diag_hsic_write_complete_callback(void *ctxt, char *buf,
119 int buf_size, int actual_size)
120{
121 /* The write of the data to the HSIC bridge is complete */
122 driver->in_busy_hsic_write = 0;
123
124 if (!driver->hsic_ch) {
125 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
126 return;
127 }
128
129 if (actual_size < 0)
130 pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
131
Jack Phamb60775a2012-02-14 17:57:41 -0800132 if (driver->usb_mdm_connected)
133 queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
134}
135
136static int diag_hsic_suspend(void *ctxt)
137{
138 if (driver->in_busy_hsic_write)
139 return -EBUSY;
140
141 driver->hsic_suspend = 1;
142
143 return 0;
144}
145
146static void diag_hsic_resume(void *ctxt)
147{
148 driver->hsic_suspend = 0;
149
150 if (!driver->in_busy_hsic_write_on_mdm && driver->usb_mdm_connected)
151 queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800152}
153
154static struct diag_bridge_ops hsic_diag_bridge_ops = {
155 .ctxt = NULL,
156 .read_complete_cb = diag_hsic_read_complete_callback,
157 .write_complete_cb = diag_hsic_write_complete_callback,
Jack Phamb60775a2012-02-14 17:57:41 -0800158 .suspend = diag_hsic_suspend,
159 .resume = diag_hsic_resume,
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800160};
161
162static int diag_hsic_close(void)
163{
164 if (driver->hsic_device_enabled) {
165 driver->hsic_ch = 0;
166 if (driver->hsic_device_opened) {
167 driver->hsic_device_opened = 0;
168 diag_bridge_close();
169 }
170 pr_debug("DIAG in %s: closed successfully\n", __func__);
171 } else {
172 pr_debug("DIAG in %s: already closed\n", __func__);
173 }
174
175 return 0;
176}
177
178/* diagfwd_connect_hsic is called when the USB mdm channel is connected */
179static int diagfwd_connect_hsic(void)
180{
181 int err;
182
183 pr_debug("DIAG in %s\n", __func__);
184
185 err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE, N_MDM_READ);
186 if (err)
187 pr_err("DIAG: unable to alloc USB req on mdm ch err:%d\n", err);
188
189 driver->usb_mdm_connected = 1;
190 driver->in_busy_hsic_write_on_mdm = 0;
191 driver->in_busy_hsic_read_on_mdm = 0;
192 driver->in_busy_hsic_write = 0;
193 driver->in_busy_hsic_read = 0;
194
195 /* If the hsic (diag_bridge) platform device is not open */
196 if (driver->hsic_device_enabled) {
197 if (!driver->hsic_device_opened) {
198 err = diag_bridge_open(&hsic_diag_bridge_ops);
199 if (err) {
200 pr_err("DIAG: HSIC channel open error: %d\n",
201 err);
202 } else {
Jack Phamb60775a2012-02-14 17:57:41 -0800203 pr_debug("DIAG: opened HSIC channel\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800204 driver->hsic_device_opened = 1;
205 }
206 } else {
Jack Phamb60775a2012-02-14 17:57:41 -0800207 pr_debug("DIAG: HSIC channel already open\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800208 }
209
210 /*
211 * Turn on communication over usb mdm and hsic, if the hsic
212 * device driver is enabled and opened
213 */
214 if (driver->hsic_device_opened)
215 driver->hsic_ch = 1;
216
217 /* Poll USB mdm channel to check for data */
218 queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
219
220 /* Poll HSIC channel to check for data */
221 queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
222 } else {
223 /* The hsic device driver has not yet been enabled */
224 pr_info("DIAG: HSIC channel not yet enabled\n");
225 }
226
227 return 0;
228}
229
230/*
231 * diagfwd_disconnect_hsic is called when the USB mdm channel
232 * is disconnected
233 */
234static int diagfwd_disconnect_hsic(void)
235{
236 pr_debug("DIAG in %s\n", __func__);
237
238 driver->usb_mdm_connected = 0;
239 usb_diag_free_req(driver->mdm_ch);
240 driver->in_busy_hsic_write_on_mdm = 1;
241 driver->in_busy_hsic_read_on_mdm = 1;
242 driver->in_busy_hsic_write = 1;
243 driver->in_busy_hsic_read = 1;
244
245 /* Turn off communication over usb mdm and hsic */
Jack Phamb60775a2012-02-14 17:57:41 -0800246 return diag_hsic_close();
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800247}
248
249/*
250 * diagfwd_write_complete_hsic is called after the asynchronous
251 * usb_diag_write() on mdm channel is complete
252 */
253static int diagfwd_write_complete_hsic(void)
254{
255 /*
256 * Clear flag to denote that the write of the hsic data on the
257 * usb mdm channel is complete
258 */
259 driver->in_busy_hsic_write_on_mdm = 0;
260
261 if (!driver->hsic_ch) {
262 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
263 return 0;
264 }
265
266 APPEND_DEBUG('q');
267
268 /* Read data from the hsic */
269 queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
270
271 return 0;
272}
273
274/* Called after the asychronous usb_diag_read() on mdm channel is complete */
275static int diagfwd_read_complete_hsic(struct diag_request *diag_read_ptr)
276{
277 /* The read of the usb driver on the mdm (not hsic) has completed */
278 driver->in_busy_hsic_read_on_mdm = 0;
279 driver->read_len_mdm = diag_read_ptr->actual;
280
281 if (!driver->hsic_ch) {
282 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
283 return 0;
284 }
285
286 /*
287 * The read of the usb driver on the mdm channel has completed.
288 * If there is no write on the hsic in progress, check if the
289 * read has data to pass on to the hsic. If so, pass the usb
290 * mdm data on to the hsic.
291 */
292 if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out &&
293 (driver->read_len_mdm > 0)) {
294
295 /*
296 * Initiate the hsic write. The hsic write is
297 * asynchronous. When complete the write
298 * complete callback function will be called
299 */
300 int err;
301 driver->in_busy_hsic_write = 1;
302 err = diag_bridge_write(driver->usb_buf_mdm_out,
303 driver->read_len_mdm);
304 if (err) {
305 pr_err("DIAG: mdm data on hsic write err: %d\n", err);
306 /*
307 * If the error is recoverable, then clear
308 * the write flag, so we will resubmit a
309 * write on the next frame. Otherwise, don't
310 * resubmit a write on the next frame.
311 */
312 if ((-ESHUTDOWN) != err)
313 driver->in_busy_hsic_write = 0;
314 }
315 }
316
317 /*
318 * If there is no write of the usb mdm data on the
319 * hsic channel
320 */
321 if (!driver->in_busy_hsic_write)
322 queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
323
324 return 0;
325}
326
327static void diagfwd_hsic_notifier(void *priv, unsigned event,
328 struct diag_request *d_req)
329{
330 switch (event) {
331 case USB_DIAG_CONNECT:
332 diagfwd_connect_hsic();
333 break;
334 case USB_DIAG_DISCONNECT:
Jack Phamb60775a2012-02-14 17:57:41 -0800335 queue_work(driver->diag_hsic_wq, &driver->diag_disconnect_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800336 break;
337 case USB_DIAG_READ_DONE:
Jack Phamb60775a2012-02-14 17:57:41 -0800338 queue_work(driver->diag_hsic_wq,
339 &driver->diag_usb_read_complete_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800340 break;
341 case USB_DIAG_WRITE_DONE:
342 diagfwd_write_complete_hsic();
343 break;
344 default:
345 pr_err("DIAG in %s: Unknown event from USB diag:%u\n",
346 __func__, event);
347 break;
348 }
349}
350
Jack Phamb60775a2012-02-14 17:57:41 -0800351static void diag_usb_read_complete_fn(struct work_struct *w)
352{
353 diagfwd_read_complete_hsic(driver->usb_read_mdm_ptr);
354}
355
356static void diag_disconnect_work_fn(struct work_struct *w)
357{
358 diagfwd_disconnect_hsic();
359}
360
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800361static void diag_read_mdm_work_fn(struct work_struct *work)
362{
363 if (!driver->hsic_ch) {
364 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
365 return;
366 }
367
368 /*
369 * If there is no data being read from the usb mdm channel
370 * and there is no mdm channel data currently being written
371 * to the hsic
372 */
373 if (!driver->in_busy_hsic_read_on_mdm && !driver->in_busy_hsic_write) {
374 APPEND_DEBUG('x');
375
376 /* Setup the next read from usb mdm channel */
377 driver->in_busy_hsic_read_on_mdm = 1;
378 driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
379 driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
380 usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
381 APPEND_DEBUG('y');
382 }
383
384 /*
385 * If for some reason there was no mdm channel read initiated,
386 * queue up the reading of data from the mdm channel
387 */
388 if (!driver->in_busy_hsic_read_on_mdm)
389 queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
390}
391
392int diag_hsic_enable(void)
393{
394 pr_debug("DIAG in %s\n", __func__);
395
396 driver->read_len_mdm = 0;
397 if (driver->buf_in_hsic == NULL)
398 driver->buf_in_hsic = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
399 if (driver->buf_in_hsic == NULL)
400 goto err;
401 if (driver->usb_buf_mdm_out == NULL)
402 driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
403 if (driver->usb_buf_mdm_out == NULL)
404 goto err;
405 if (driver->write_ptr_mdm == NULL)
406 driver->write_ptr_mdm = kzalloc(
407 sizeof(struct diag_request), GFP_KERNEL);
408 if (driver->write_ptr_mdm == NULL)
409 goto err;
410 if (driver->usb_read_mdm_ptr == NULL)
411 driver->usb_read_mdm_ptr = kzalloc(
412 sizeof(struct diag_request), GFP_KERNEL);
413 if (driver->usb_read_mdm_ptr == NULL)
414 goto err;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800415#ifdef CONFIG_DIAG_OVER_USB
416 INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
417#endif
418 INIT_WORK(&(driver->diag_read_hsic_work), diag_read_hsic_work_fn);
419
420 driver->hsic_device_enabled = 1;
421
422 return 0;
423err:
424 pr_err("DIAG could not initialize buf for HSIC\n");
425 kfree(driver->buf_in_hsic);
426 kfree(driver->usb_buf_mdm_out);
427 kfree(driver->write_ptr_mdm);
428 kfree(driver->usb_read_mdm_ptr);
429 if (driver->diag_hsic_wq)
430 destroy_workqueue(driver->diag_hsic_wq);
431
432 return -ENOMEM;
433}
434
435static int diag_hsic_probe(struct platform_device *pdev)
436{
437 int err;
438
439 if (!driver->hsic_device_enabled) {
440 err = diag_hsic_enable();
441 if (err) {
442 pr_err("DIAG could not enable HSIC, err: %d\n", err);
443 return err;
444 }
445 }
446
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800447 /*
448 * The probe function was called after the usb was connected
449 * on the legacy channel. Communication over usb mdm and hsic
450 * needs to be turned on.
451 */
Jack Phamb60775a2012-02-14 17:57:41 -0800452 if (driver->usb_mdm_connected) {
453 /* The hsic (diag_bridge) platform device driver is enabled */
454 err = diag_bridge_open(&hsic_diag_bridge_ops);
455 if (err) {
456 pr_err("DIAG could not open HSIC, err: %d\n", err);
457 driver->hsic_device_opened = 0;
458 return err;
459 }
460
461 pr_debug("DIAG opened HSIC channel\n");
462 driver->hsic_device_opened = 1;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800463 driver->hsic_ch = 1;
464 driver->in_busy_hsic_write_on_mdm = 0;
465 driver->in_busy_hsic_read_on_mdm = 0;
466 driver->in_busy_hsic_write = 0;
467 driver->in_busy_hsic_read = 0;
468
469 /* Poll USB mdm channel to check for data */
470 queue_work(driver->diag_hsic_wq, &driver->diag_read_mdm_work);
471
472 /* Poll HSIC channel to check for data */
473 queue_work(driver->diag_hsic_wq, &driver->diag_read_hsic_work);
474 }
475
476 return err;
477}
478
479static int diag_hsic_remove(struct platform_device *pdev)
480{
Jack Phamb60775a2012-02-14 17:57:41 -0800481 pr_debug("DIAG: %s called\n", __func__);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800482 diag_hsic_close();
483 return 0;
484}
485
486static int diagfwd_hsic_runtime_suspend(struct device *dev)
487{
488 dev_dbg(dev, "pm_runtime: suspending...\n");
489 return 0;
490}
491
492static int diagfwd_hsic_runtime_resume(struct device *dev)
493{
494 dev_dbg(dev, "pm_runtime: resuming...\n");
495 return 0;
496}
497
498static const struct dev_pm_ops diagfwd_hsic_dev_pm_ops = {
499 .runtime_suspend = diagfwd_hsic_runtime_suspend,
500 .runtime_resume = diagfwd_hsic_runtime_resume,
501};
502
503static struct platform_driver msm_hsic_ch_driver = {
504 .probe = diag_hsic_probe,
505 .remove = diag_hsic_remove,
506 .driver = {
507 .name = "diag_bridge",
508 .owner = THIS_MODULE,
509 .pm = &diagfwd_hsic_dev_pm_ops,
510 },
511};
512
513
514void __init diagfwd_hsic_init(void)
515{
516 int ret;
517
518 pr_debug("DIAG in %s\n", __func__);
519
Jack Phamb60775a2012-02-14 17:57:41 -0800520 driver->diag_hsic_wq = create_singlethread_workqueue("diag_hsic_wq");
521 INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
522 INIT_WORK(&(driver->diag_usb_read_complete_work),
523 diag_usb_read_complete_fn);
524
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800525#ifdef CONFIG_DIAG_OVER_USB
526 driver->mdm_ch = usb_diag_open(DIAG_MDM, driver, diagfwd_hsic_notifier);
527 if (IS_ERR(driver->mdm_ch)) {
528 pr_err("DIAG Unable to open USB diag MDM channel\n");
529 goto err;
530 }
531#endif
532 ret = platform_driver_register(&msm_hsic_ch_driver);
533 if (ret)
534 pr_err("DIAG could not register HSIC device, ret: %d\n", ret);
535 else
536 driver->hsic_initialized = 1;
537
538 return;
539err:
540 pr_err("DIAG could not initialize for HSIC execution\n");
541}
542
543void __exit diagfwd_hsic_exit(void)
544{
545 pr_debug("DIAG in %s\n", __func__);
546
547 if (driver->hsic_initialized)
548 diag_hsic_close();
549
550#ifdef CONFIG_DIAG_OVER_USB
551 if (driver->usb_mdm_connected)
552 usb_diag_free_req(driver->mdm_ch);
553#endif
554 platform_driver_unregister(&msm_hsic_ch_driver);
555#ifdef CONFIG_DIAG_OVER_USB
556 usb_diag_close(driver->mdm_ch);
557#endif
558 kfree(driver->buf_in_hsic);
559 kfree(driver->usb_buf_mdm_out);
560 kfree(driver->write_ptr_mdm);
561 kfree(driver->usb_read_mdm_ptr);
562 destroy_workqueue(driver->diag_hsic_wq);
563
564 driver->hsic_device_enabled = 0;
565}