blob: 24b1659ed0eae34840f6e237577395246f1e8e44 [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>
Jack Phamcca03322012-06-14 19:00:31 -070019#include <linux/ratelimit.h>
Dixon Peterson32e70bb2011-12-16 13:26:45 -080020#include <linux/workqueue.h>
21#include <linux/pm_runtime.h>
22#include <linux/platform_device.h>
Shalabh Jainf7228dc2012-05-23 17:32:05 -070023#include <linux/smux.h>
Dixon Peterson32e70bb2011-12-16 13:26:45 -080024#include <asm/current.h>
25#ifdef CONFIG_DIAG_OVER_USB
26#include <mach/usbdiag.h>
27#endif
28#include "diagchar_hdlc.h"
29#include "diagmem.h"
30#include "diagchar.h"
31#include "diagfwd.h"
32#include "diagfwd_hsic.h"
Shalabh Jainf7228dc2012-05-23 17:32:05 -070033#include "diagfwd_smux.h"
Dixon Peterson32e70bb2011-12-16 13:26:45 -080034
Dixon Peterson938f8602012-08-17 20:02:57 -070035#define READ_HSIC_BUF_SIZE 2048
36
Dixon Peterson32e70bb2011-12-16 13:26:45 -080037static void diag_read_hsic_work_fn(struct work_struct *work)
38{
Dixon Peterson938f8602012-08-17 20:02:57 -070039 unsigned char *buf_in_hsic = NULL;
40 int num_reads_submitted = 0;
41 int err = 0;
42 int write_ptrs_available;
43
Dixon Peterson32e70bb2011-12-16 13:26:45 -080044 if (!driver->hsic_ch) {
45 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
46 return;
47 }
48
Dixon Peterson938f8602012-08-17 20:02:57 -070049 /*
50 * Determine the current number of available buffers for writing after
51 * reading from the HSIC has completed.
Dixon Peterson32e70bb2011-12-16 13:26:45 -080052 */
Dixon Peterson938f8602012-08-17 20:02:57 -070053 if (driver->logging_mode == MEMORY_DEVICE_MODE)
54 write_ptrs_available = driver->poolsize_hsic_write -
55 driver->num_hsic_buf_tbl_entries;
56 else
57 write_ptrs_available = driver->poolsize_hsic_write -
58 driver->count_hsic_write_pool;
Dixon Peterson32e70bb2011-12-16 13:26:45 -080059
60 /*
Dixon Peterson938f8602012-08-17 20:02:57 -070061 * Queue up a read on the HSIC for all available buffers in the
62 * pool, exhausting the pool.
Dixon Peterson32e70bb2011-12-16 13:26:45 -080063 */
Dixon Peterson938f8602012-08-17 20:02:57 -070064 do {
65 /*
66 * If no more write buffers are available,
67 * stop queuing reads
68 */
69 if (write_ptrs_available <= 0)
70 break;
71
72 write_ptrs_available--;
73
74 buf_in_hsic = diagmem_alloc(driver, READ_HSIC_BUF_SIZE,
75 POOL_TYPE_HSIC);
76 if (buf_in_hsic) {
77 /*
78 * Initiate the read from the hsic. The hsic read is
79 * asynchronous. Once the read is complete the read
80 * callback function will be called.
81 */
Dixon Peterson938f8602012-08-17 20:02:57 -070082 pr_debug("diag: read from HSIC\n");
83 num_reads_submitted++;
84 err = diag_bridge_read((char *)buf_in_hsic,
85 READ_HSIC_BUF_SIZE);
86 if (err) {
87 num_reads_submitted--;
88
89 /* Return the buffer to the pool */
90 diagmem_free(driver, buf_in_hsic,
91 POOL_TYPE_HSIC);
92
93 pr_err_ratelimited("diag: Error initiating HSIC read, err: %d\n",
94 err);
95 /*
96 * An error occurred, discontinue queuing
97 * reads
98 */
99 break;
100 }
101 }
102 } while (buf_in_hsic);
103
104 /*
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700105 * If there are read buffers available and for some reason the
106 * read was not queued, and if no unrecoverable error occurred
Dixon Peterson938f8602012-08-17 20:02:57 -0700107 * (-ENODEV is an unrecoverable error), then set up the next read
108 */
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700109 if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
110 (num_reads_submitted == 0) && (err != -ENODEV))
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700111 queue_work(driver->diag_bridge_wq,
112 &driver->diag_read_hsic_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800113}
114
115static void diag_hsic_read_complete_callback(void *ctxt, char *buf,
116 int buf_size, int actual_size)
117{
Dixon Peterson938f8602012-08-17 20:02:57 -0700118 int err = -2;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800119
120 if (!driver->hsic_ch) {
Dixon Peterson938f8602012-08-17 20:02:57 -0700121 /*
122 * The hsic channel is closed. Return the buffer to
123 * the pool. Do not send it on.
124 */
125 diagmem_free(driver, buf, POOL_TYPE_HSIC);
126 pr_debug("diag: In %s: driver->hsic_ch == 0, actual_size: %d\n",
127 __func__, actual_size);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800128 return;
129 }
130
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700131 /*
132 * Note that zero length is valid and still needs to be sent to
133 * the USB only when we are logging data to the USB
134 */
135 if ((actual_size > 0) ||
136 ((actual_size == 0) && (driver->logging_mode == USB_MODE))) {
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800137 if (!buf) {
Dixon Peterson938f8602012-08-17 20:02:57 -0700138 pr_err("diag: Out of diagmem for HSIC\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800139 } else {
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800140 /*
Dixon Peterson938f8602012-08-17 20:02:57 -0700141 * Send data in buf to be written on the
142 * appropriate device, e.g. USB MDM channel
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800143 */
Dixon Peterson938f8602012-08-17 20:02:57 -0700144 driver->write_len_mdm = actual_size;
145 err = diag_device_write((void *)buf, HSIC_DATA, NULL);
146 /* If an error, return buffer to the pool */
147 if (err) {
148 diagmem_free(driver, buf, POOL_TYPE_HSIC);
149 pr_err("diag: In %s, error calling diag_device_write, err: %d\n",
150 __func__, err);
151 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800152 }
153 } else {
Dixon Peterson938f8602012-08-17 20:02:57 -0700154 /*
155 * The buffer has an error status associated with it. Do not
156 * pass it on. Note that -ENOENT is sent when the diag bridge
157 * is closed.
158 */
159 diagmem_free(driver, buf, POOL_TYPE_HSIC);
160 pr_debug("diag: In %s: error status: %d\n", __func__,
161 actual_size);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800162 }
163
164 /*
165 * If for some reason there was no hsic data to write to the
166 * mdm channel, set up another read
167 */
Dixon Peterson938f8602012-08-17 20:02:57 -0700168 if (err &&
169 ((driver->logging_mode == MEMORY_DEVICE_MODE) ||
170 (driver->usb_mdm_connected && !driver->hsic_suspend))) {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700171 queue_work(driver->diag_bridge_wq,
172 &driver->diag_read_hsic_work);
Dixon Peterson938f8602012-08-17 20:02:57 -0700173 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800174}
175
176static void diag_hsic_write_complete_callback(void *ctxt, char *buf,
177 int buf_size, int actual_size)
178{
179 /* The write of the data to the HSIC bridge is complete */
180 driver->in_busy_hsic_write = 0;
181
182 if (!driver->hsic_ch) {
183 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
184 return;
185 }
186
187 if (actual_size < 0)
188 pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
189
Jack Phamb60775a2012-02-14 17:57:41 -0800190 if (driver->usb_mdm_connected)
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700191 queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
Jack Phamb60775a2012-02-14 17:57:41 -0800192}
193
194static int diag_hsic_suspend(void *ctxt)
195{
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700196 pr_debug("diag: hsic_suspend\n");
Dixon Peterson938f8602012-08-17 20:02:57 -0700197
198 /* Don't allow suspend if a write in the HSIC is in progress */
Jack Phamb60775a2012-02-14 17:57:41 -0800199 if (driver->in_busy_hsic_write)
200 return -EBUSY;
201
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700202 /* Don't allow suspend if in MEMORY_DEVICE_MODE */
203 if (driver->logging_mode == MEMORY_DEVICE_MODE)
204 return -EBUSY;
205
Jack Phamb60775a2012-02-14 17:57:41 -0800206 driver->hsic_suspend = 1;
207
208 return 0;
209}
210
211static void diag_hsic_resume(void *ctxt)
212{
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700213 pr_debug("diag: hsic_resume\n");
Jack Phamb60775a2012-02-14 17:57:41 -0800214 driver->hsic_suspend = 0;
215
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700216 if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
217 ((driver->logging_mode == MEMORY_DEVICE_MODE) ||
218 (driver->usb_mdm_connected)))
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700219 queue_work(driver->diag_bridge_wq,
220 &driver->diag_read_hsic_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800221}
222
223static struct diag_bridge_ops hsic_diag_bridge_ops = {
224 .ctxt = NULL,
225 .read_complete_cb = diag_hsic_read_complete_callback,
226 .write_complete_cb = diag_hsic_write_complete_callback,
Jack Phamb60775a2012-02-14 17:57:41 -0800227 .suspend = diag_hsic_suspend,
228 .resume = diag_hsic_resume,
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800229};
230
Jack Phamfbd22552012-09-07 17:29:08 -0700231static void diag_hsic_close(void)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800232{
233 if (driver->hsic_device_enabled) {
234 driver->hsic_ch = 0;
235 if (driver->hsic_device_opened) {
236 driver->hsic_device_opened = 0;
237 diag_bridge_close();
Jack Phamfbd22552012-09-07 17:29:08 -0700238 pr_debug("diag: %s: closed successfully\n", __func__);
239 } else {
240 pr_debug("diag: %s: already closed\n", __func__);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800241 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800242 } else {
Jack Phamfbd22552012-09-07 17:29:08 -0700243 pr_debug("diag: %s: HSIC device already removed\n", __func__);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700244 }
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700245}
246
247/* diagfwd_cancel_hsic is called to cancel outstanding read/writes */
248int diagfwd_cancel_hsic(void)
249{
250 int err;
251
Jack Phamfbd22552012-09-07 17:29:08 -0700252 mutex_lock(&driver->bridge_mutex);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700253 if (driver->hsic_device_enabled) {
254 if (driver->hsic_device_opened) {
255 driver->hsic_ch = 0;
256 driver->hsic_device_opened = 0;
257 diag_bridge_close();
258 err = diag_bridge_open(&hsic_diag_bridge_ops);
259 if (err) {
Dixon Peterson938f8602012-08-17 20:02:57 -0700260 pr_err("diag: HSIC channel open error: %d\n",
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700261 err);
262 } else {
Dixon Peterson938f8602012-08-17 20:02:57 -0700263 pr_debug("diag: opened HSIC channel\n");
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700264 driver->hsic_device_opened = 1;
265 driver->hsic_ch = 1;
266 }
267 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800268 }
269
Jack Phamfbd22552012-09-07 17:29:08 -0700270 mutex_unlock(&driver->bridge_mutex);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800271 return 0;
272}
273
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700274/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
275int diagfwd_connect_bridge(int process_cable)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800276{
277 int err;
278
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700279 pr_debug("diag: in %s\n", __func__);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800280
Jack Phamfbd22552012-09-07 17:29:08 -0700281 mutex_lock(&driver->bridge_mutex);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700282 /* If the usb cable is being connected */
283 if (process_cable) {
284 err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
285 N_MDM_READ);
286 if (err)
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700287 pr_err("diag: unable to alloc USB req on mdm"
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700288 " ch err:%d\n", err);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800289
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700290 driver->usb_mdm_connected = 1;
291 }
292
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700293 if (driver->hsic_device_enabled) {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700294 driver->in_busy_hsic_read_on_device = 0;
295 driver->in_busy_hsic_write = 0;
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700296 } else if (driver->diag_smux_enabled) {
297 driver->in_busy_smux = 0;
298 diagfwd_connect_smux();
Jack Phamfbd22552012-09-07 17:29:08 -0700299 mutex_unlock(&driver->bridge_mutex);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700300 return 0;
301 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800302
303 /* If the hsic (diag_bridge) platform device is not open */
304 if (driver->hsic_device_enabled) {
305 if (!driver->hsic_device_opened) {
306 err = diag_bridge_open(&hsic_diag_bridge_ops);
307 if (err) {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700308 pr_err("diag: HSIC channel open error: %d\n",
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800309 err);
310 } else {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700311 pr_debug("diag: opened HSIC channel\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800312 driver->hsic_device_opened = 1;
313 }
314 } else {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700315 pr_debug("diag: HSIC channel already open\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800316 }
317
318 /*
319 * Turn on communication over usb mdm and hsic, if the hsic
320 * device driver is enabled and opened
321 */
Jack Phamfbd22552012-09-07 17:29:08 -0700322 if (driver->hsic_device_opened) {
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800323 driver->hsic_ch = 1;
324
Jack Phamfbd22552012-09-07 17:29:08 -0700325 /* Poll USB mdm channel to check for data */
326 if (driver->logging_mode == USB_MODE)
327 queue_work(driver->diag_bridge_wq,
328 &driver->diag_read_mdm_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800329
Jack Phamfbd22552012-09-07 17:29:08 -0700330 /* Poll HSIC channel to check for data */
331 queue_work(driver->diag_bridge_wq,
332 &driver->diag_read_hsic_work);
333 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800334 } else {
335 /* The hsic device driver has not yet been enabled */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700336 pr_info("diag: HSIC channel not yet enabled\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800337 }
338
Jack Phamfbd22552012-09-07 17:29:08 -0700339 mutex_unlock(&driver->bridge_mutex);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800340 return 0;
341}
342
343/*
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700344 * diagfwd_disconnect_bridge is called when the USB mdm channel
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800345 * is disconnected
346 */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700347int diagfwd_disconnect_bridge(int process_cable)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800348{
Dixon Peterson938f8602012-08-17 20:02:57 -0700349 pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800350
Jack Phamfbd22552012-09-07 17:29:08 -0700351 mutex_lock(&driver->bridge_mutex);
352
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700353 /* If the usb cable is being disconnected */
354 if (process_cable) {
355 driver->usb_mdm_connected = 0;
356 usb_diag_free_req(driver->mdm_ch);
357 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800358
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700359 if (driver->hsic_device_enabled &&
360 driver->logging_mode != MEMORY_DEVICE_MODE) {
361 driver->in_busy_hsic_read_on_device = 1;
362 driver->in_busy_hsic_write = 1;
363 /* Turn off communication over usb mdm and hsic */
Jack Phamfbd22552012-09-07 17:29:08 -0700364 diag_hsic_close();
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700365 } else if (driver->diag_smux_enabled &&
366 driver->logging_mode == USB_MODE) {
367 driver->in_busy_smux = 1;
368 driver->lcid = LCID_INVALID;
369 driver->smux_connected = 0;
370 /* Turn off communication over usb mdm and smux */
371 msm_smux_close(LCID_VALID);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700372 }
Jack Phamfbd22552012-09-07 17:29:08 -0700373
374 mutex_unlock(&driver->bridge_mutex);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700375 return 0;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800376}
377
378/*
379 * diagfwd_write_complete_hsic is called after the asynchronous
380 * usb_diag_write() on mdm channel is complete
381 */
Dixon Peterson938f8602012-08-17 20:02:57 -0700382int diagfwd_write_complete_hsic(struct diag_request *diag_write_ptr)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800383{
Dixon Peterson938f8602012-08-17 20:02:57 -0700384 unsigned char *buf = (diag_write_ptr) ? diag_write_ptr->buf : NULL;
385
386 if (buf) {
387 /* Return buffers to their pools */
388 diagmem_free(driver, (unsigned char *)buf, POOL_TYPE_HSIC);
389 diagmem_free(driver, (unsigned char *)diag_write_ptr,
390 POOL_TYPE_HSIC_WRITE);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800391 }
392
Dixon Peterson938f8602012-08-17 20:02:57 -0700393 if (!driver->hsic_ch) {
394 pr_err("diag: In %s: driver->hsic_ch == 0\n", __func__);
395 return 0;
396 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800397
398 /* Read data from the hsic */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700399 queue_work(driver->diag_bridge_wq, &driver->diag_read_hsic_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800400
401 return 0;
402}
403
404/* Called after the asychronous usb_diag_read() on mdm channel is complete */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700405static int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800406{
407 /* The read of the usb driver on the mdm (not hsic) has completed */
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700408 driver->in_busy_hsic_read_on_device = 0;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800409 driver->read_len_mdm = diag_read_ptr->actual;
410
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700411 if (driver->diag_smux_enabled) {
412 diagfwd_read_complete_smux();
413 return 0;
414 }
415 /* If SMUX not enabled, check for HSIC */
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800416 if (!driver->hsic_ch) {
417 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
418 return 0;
419 }
420
421 /*
422 * The read of the usb driver on the mdm channel has completed.
423 * If there is no write on the hsic in progress, check if the
424 * read has data to pass on to the hsic. If so, pass the usb
425 * mdm data on to the hsic.
426 */
427 if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out &&
428 (driver->read_len_mdm > 0)) {
429
430 /*
431 * Initiate the hsic write. The hsic write is
432 * asynchronous. When complete the write
433 * complete callback function will be called
434 */
435 int err;
436 driver->in_busy_hsic_write = 1;
437 err = diag_bridge_write(driver->usb_buf_mdm_out,
438 driver->read_len_mdm);
439 if (err) {
Dixon Peterson938f8602012-08-17 20:02:57 -0700440 pr_err_ratelimited("diag: mdm data on hsic write err: %d\n",
Jack Phamcca03322012-06-14 19:00:31 -0700441 err);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800442 /*
443 * If the error is recoverable, then clear
444 * the write flag, so we will resubmit a
445 * write on the next frame. Otherwise, don't
446 * resubmit a write on the next frame.
447 */
Jack Phamcca03322012-06-14 19:00:31 -0700448 if ((-ENODEV) != err)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800449 driver->in_busy_hsic_write = 0;
450 }
451 }
452
453 /*
454 * If there is no write of the usb mdm data on the
455 * hsic channel
456 */
457 if (!driver->in_busy_hsic_write)
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700458 queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800459
460 return 0;
461}
462
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700463static void diagfwd_bridge_notifier(void *priv, unsigned event,
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800464 struct diag_request *d_req)
465{
466 switch (event) {
467 case USB_DIAG_CONNECT:
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700468 diagfwd_connect_bridge(1);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800469 break;
470 case USB_DIAG_DISCONNECT:
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700471 queue_work(driver->diag_bridge_wq,
472 &driver->diag_disconnect_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800473 break;
474 case USB_DIAG_READ_DONE:
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700475 queue_work(driver->diag_bridge_wq,
Jack Phamb60775a2012-02-14 17:57:41 -0800476 &driver->diag_usb_read_complete_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800477 break;
478 case USB_DIAG_WRITE_DONE:
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700479 if (driver->hsic_device_enabled)
Dixon Peterson938f8602012-08-17 20:02:57 -0700480 diagfwd_write_complete_hsic(d_req);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700481 else if (driver->diag_smux_enabled)
482 diagfwd_write_complete_smux();
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800483 break;
484 default:
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700485 pr_err("diag: in %s: Unknown event from USB diag:%u\n",
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800486 __func__, event);
487 break;
488 }
489}
490
Jack Phamb60775a2012-02-14 17:57:41 -0800491static void diag_usb_read_complete_fn(struct work_struct *w)
492{
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700493 diagfwd_read_complete_bridge(driver->usb_read_mdm_ptr);
Jack Phamb60775a2012-02-14 17:57:41 -0800494}
495
496static void diag_disconnect_work_fn(struct work_struct *w)
497{
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700498 diagfwd_disconnect_bridge(1);
Jack Phamb60775a2012-02-14 17:57:41 -0800499}
500
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800501static void diag_read_mdm_work_fn(struct work_struct *work)
502{
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700503 int ret;
504 if (driver->diag_smux_enabled) {
505 if (driver->lcid && driver->usb_buf_mdm_out &&
Ashay Jaiswal2d8daae2012-07-17 17:29:33 +0530506 (driver->read_len_mdm > 0) &&
507 driver->smux_connected) {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700508 ret = msm_smux_write(driver->lcid, NULL,
509 driver->usb_buf_mdm_out, driver->read_len_mdm);
510 if (ret)
511 pr_err("diag: writing to SMUX ch, r = %d,"
512 "lcid = %d\n", ret, driver->lcid);
513 }
514 driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
515 driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
516 usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
517 return;
518 }
519
520 /* if SMUX not enabled, check for HSIC */
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800521 if (!driver->hsic_ch) {
522 pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
523 return;
524 }
525
526 /*
527 * If there is no data being read from the usb mdm channel
528 * and there is no mdm channel data currently being written
529 * to the hsic
530 */
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700531 if (!driver->in_busy_hsic_read_on_device &&
532 !driver->in_busy_hsic_write) {
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800533 APPEND_DEBUG('x');
534
535 /* Setup the next read from usb mdm channel */
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700536 driver->in_busy_hsic_read_on_device = 1;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800537 driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
538 driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
539 usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
540 APPEND_DEBUG('y');
541 }
542
543 /*
544 * If for some reason there was no mdm channel read initiated,
545 * queue up the reading of data from the mdm channel
546 */
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700547 if (!driver->in_busy_hsic_read_on_device)
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700548 queue_work(driver->diag_bridge_wq,
549 &driver->diag_read_mdm_work);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800550}
551
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700552static int diag_hsic_probe(struct platform_device *pdev)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800553{
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700554 int err = 0;
555 pr_debug("diag: in %s\n", __func__);
Jack Phamfbd22552012-09-07 17:29:08 -0700556 if (!driver->hsic_inited) {
Dixon Peterson938f8602012-08-17 20:02:57 -0700557 diagmem_hsic_init(driver);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700558 INIT_WORK(&(driver->diag_read_hsic_work),
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700559 diag_read_hsic_work_fn);
Jack Phamfbd22552012-09-07 17:29:08 -0700560 driver->hsic_inited = 1;
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700561 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800562
Jack Phamfbd22552012-09-07 17:29:08 -0700563 mutex_lock(&driver->bridge_mutex);
564
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700565 /*
566 * The probe function was called after the usb was connected
567 * on the legacy channel OR ODL is turned on. Communication over usb
568 * mdm and hsic needs to be turned on.
569 */
570 if (driver->usb_mdm_connected || (driver->logging_mode ==
571 MEMORY_DEVICE_MODE)) {
Jack Phamfbd22552012-09-07 17:29:08 -0700572 if (driver->hsic_device_opened) {
573 /* should not happen. close it before re-opening */
574 pr_warn("diag: HSIC channel already opened in probe\n");
575 diag_bridge_close();
576 }
577
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700578 err = diag_bridge_open(&hsic_diag_bridge_ops);
579 if (err) {
580 pr_err("diag: could not open HSIC, err: %d\n", err);
581 driver->hsic_device_opened = 0;
Jack Phamfbd22552012-09-07 17:29:08 -0700582 mutex_unlock(&driver->bridge_mutex);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700583 return err;
584 }
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800585
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700586 pr_info("diag: opened HSIC channel\n");
587 driver->hsic_device_opened = 1;
588 driver->hsic_ch = 1;
Dixon Peterson938f8602012-08-17 20:02:57 -0700589
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700590 driver->in_busy_hsic_read_on_device = 0;
591 driver->in_busy_hsic_write = 0;
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700592
593 if (driver->usb_mdm_connected) {
594 /* Poll USB mdm channel to check for data */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700595 queue_work(driver->diag_bridge_wq,
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700596 &driver->diag_read_mdm_work);
597 }
598
599 /* Poll HSIC channel to check for data */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700600 queue_work(driver->diag_bridge_wq,
601 &driver->diag_read_hsic_work);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700602 }
603
Jack Phamfbd22552012-09-07 17:29:08 -0700604 /* The hsic (diag_bridge) platform device driver is enabled */
605 driver->hsic_device_enabled = 1;
606 mutex_unlock(&driver->bridge_mutex);
Dixon Petersonf79b66f2012-04-26 13:21:26 -0700607 return err;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800608}
609
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800610static int diag_hsic_remove(struct platform_device *pdev)
611{
Dixon Peterson938f8602012-08-17 20:02:57 -0700612 pr_debug("diag: %s called\n", __func__);
Jack Phamfbd22552012-09-07 17:29:08 -0700613 mutex_lock(&driver->bridge_mutex);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800614 diag_hsic_close();
Jack Phamfbd22552012-09-07 17:29:08 -0700615 driver->hsic_device_enabled = 0;
616 mutex_unlock(&driver->bridge_mutex);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800617 return 0;
618}
619
620static int diagfwd_hsic_runtime_suspend(struct device *dev)
621{
622 dev_dbg(dev, "pm_runtime: suspending...\n");
623 return 0;
624}
625
626static int diagfwd_hsic_runtime_resume(struct device *dev)
627{
628 dev_dbg(dev, "pm_runtime: resuming...\n");
629 return 0;
630}
631
632static const struct dev_pm_ops diagfwd_hsic_dev_pm_ops = {
633 .runtime_suspend = diagfwd_hsic_runtime_suspend,
634 .runtime_resume = diagfwd_hsic_runtime_resume,
635};
636
637static struct platform_driver msm_hsic_ch_driver = {
638 .probe = diag_hsic_probe,
639 .remove = diag_hsic_remove,
640 .driver = {
641 .name = "diag_bridge",
642 .owner = THIS_MODULE,
643 .pm = &diagfwd_hsic_dev_pm_ops,
644 },
645};
646
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700647void diagfwd_bridge_init(void)
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800648{
649 int ret;
650
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700651 pr_debug("diag: in %s\n", __func__);
652 driver->diag_bridge_wq = create_singlethread_workqueue(
653 "diag_bridge_wq");
654 driver->read_len_mdm = 0;
Dixon Peterson938f8602012-08-17 20:02:57 -0700655 driver->write_len_mdm = 0;
656 driver->num_hsic_buf_tbl_entries = 0;
Dixon Peterson5db2e3c2012-09-06 19:13:14 -0700657 spin_lock_init(&driver->hsic_spinlock);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700658 if (driver->usb_buf_mdm_out == NULL)
659 driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
660 GFP_KERNEL);
661 if (driver->usb_buf_mdm_out == NULL)
662 goto err;
Dixon Peterson938f8602012-08-17 20:02:57 -0700663 /* Only used by smux move to smux probe function */
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700664 if (driver->write_ptr_mdm == NULL)
665 driver->write_ptr_mdm = kzalloc(
666 sizeof(struct diag_request), GFP_KERNEL);
667 if (driver->write_ptr_mdm == NULL)
668 goto err;
669 if (driver->usb_read_mdm_ptr == NULL)
670 driver->usb_read_mdm_ptr = kzalloc(
671 sizeof(struct diag_request), GFP_KERNEL);
672 if (driver->usb_read_mdm_ptr == NULL)
673 goto err;
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800674
Dixon Peterson938f8602012-08-17 20:02:57 -0700675 if (driver->hsic_buf_tbl == NULL)
676 driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES *
677 sizeof(struct diag_write_device), GFP_KERNEL);
678 if (driver->hsic_buf_tbl == NULL)
679 goto err;
680
681 driver->count_hsic_pool = 0;
682 driver->count_hsic_write_pool = 0;
683
684 driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
685 driver->poolsize_hsic = N_MDM_WRITE;
686 driver->itemsize_hsic_write = sizeof(struct diag_request);
687 driver->poolsize_hsic_write = N_MDM_WRITE;
688
Jack Phamfbd22552012-09-07 17:29:08 -0700689 mutex_init(&driver->bridge_mutex);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700690#ifdef CONFIG_DIAG_OVER_USB
691 INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
692#endif
Jack Phamb60775a2012-02-14 17:57:41 -0800693 INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
694 INIT_WORK(&(driver->diag_usb_read_complete_work),
695 diag_usb_read_complete_fn);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800696#ifdef CONFIG_DIAG_OVER_USB
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700697 driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
698 diagfwd_bridge_notifier);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800699 if (IS_ERR(driver->mdm_ch)) {
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700700 pr_err("diag: Unable to open USB diag MDM channel\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800701 goto err;
702 }
703#endif
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700704 /* register HSIC device */
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800705 ret = platform_driver_register(&msm_hsic_ch_driver);
706 if (ret)
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700707 pr_err("diag: could not register HSIC device, ret: %d\n", ret);
708 /* register SMUX device */
709 ret = platform_driver_register(&msm_diagfwd_smux_driver);
710 if (ret)
711 pr_err("diag: could not register SMUX device, ret: %d\n", ret);
Dixon Peterson938f8602012-08-17 20:02:57 -0700712
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800713 return;
714err:
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700715 pr_err("diag: Could not initialize for bridge forwarding\n");
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800716 kfree(driver->usb_buf_mdm_out);
Dixon Peterson938f8602012-08-17 20:02:57 -0700717 kfree(driver->hsic_buf_tbl);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800718 kfree(driver->write_ptr_mdm);
719 kfree(driver->usb_read_mdm_ptr);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700720 if (driver->diag_bridge_wq)
721 destroy_workqueue(driver->diag_bridge_wq);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800722
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700723 return;
724}
725
726void diagfwd_bridge_exit(void)
727{
728 pr_debug("diag: in %s\n", __func__);
729
730 if (driver->hsic_device_enabled) {
731 diag_hsic_close();
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700732 driver->hsic_device_enabled = 0;
733 }
Jack Phamfbd22552012-09-07 17:29:08 -0700734 driver->hsic_inited = 0;
735 diagmem_exit(driver, POOL_TYPE_ALL);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700736 if (driver->diag_smux_enabled) {
737 driver->lcid = LCID_INVALID;
738 kfree(driver->buf_in_smux);
739 driver->diag_smux_enabled = 0;
740 }
741 platform_driver_unregister(&msm_hsic_ch_driver);
742 platform_driver_unregister(&msm_diagfwd_smux_driver);
743 /* destroy USB MDM specific variables */
744#ifdef CONFIG_DIAG_OVER_USB
745 if (driver->usb_mdm_connected)
746 usb_diag_free_req(driver->mdm_ch);
747 usb_diag_close(driver->mdm_ch);
748#endif
749 kfree(driver->usb_buf_mdm_out);
Dixon Peterson938f8602012-08-17 20:02:57 -0700750 kfree(driver->hsic_buf_tbl);
Shalabh Jainf7228dc2012-05-23 17:32:05 -0700751 kfree(driver->write_ptr_mdm);
752 kfree(driver->usb_read_mdm_ptr);
753 destroy_workqueue(driver->diag_bridge_wq);
Dixon Peterson32e70bb2011-12-16 13:26:45 -0800754}