blob: 475f5badf2b207805b38874c0604cdc89ee745d7 [file] [log] [blame]
Shalabh Jainb0037c02013-01-18 12:47:40 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Shalabh Jain737fca72012-11-14 21:53:43 -08002 *
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/delay.h>
15#include <linux/diagchar.h>
16#include <linux/kmemleak.h>
17#include <linux/err.h>
18#include <linux/workqueue.h>
19#include <linux/ratelimit.h>
20#include <linux/platform_device.h>
21#include <linux/smux.h>
22#ifdef CONFIG_DIAG_OVER_USB
23#include <mach/usbdiag.h>
24#endif
25#include "diagchar.h"
26#include "diagmem.h"
27#include "diagfwd_cntl.h"
28#include "diagfwd_smux.h"
29#include "diagfwd_hsic.h"
30#include "diag_masks.h"
31#include "diagfwd_bridge.h"
32
33struct diag_bridge_dev *diag_bridge;
34
35/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
36int diagfwd_connect_bridge(int process_cable)
37{
38 int i;
39
40 pr_debug("diag: in %s\n", __func__);
41
42 for (i = 0; i < MAX_BRIDGES; i++)
43 if (diag_bridge[i].enabled)
44 connect_bridge(process_cable, i);
45 return 0;
46}
47
48void connect_bridge(int process_cable, int index)
49{
50 int err;
51
52 mutex_lock(&diag_bridge[index].bridge_mutex);
53 /* If the usb cable is being connected */
54 if (process_cable) {
55 err = usb_diag_alloc_req(diag_bridge[index].ch, N_MDM_WRITE,
56 N_MDM_READ);
57 if (err)
Shalabh Jainb0037c02013-01-18 12:47:40 -080058 pr_err("diag: unable to alloc USB req for ch %d err:%d\n",
59 index, err);
Shalabh Jain737fca72012-11-14 21:53:43 -080060
61 diag_bridge[index].usb_connected = 1;
62 }
63
64 if (index == SMUX && driver->diag_smux_enabled) {
65 driver->in_busy_smux = 0;
66 diagfwd_connect_smux();
Shalabh Jainb0037c02013-01-18 12:47:40 -080067 } else {
Dixon Petersonf2d449c2013-02-01 18:02:20 -080068 if (diag_hsic[index].hsic_device_enabled &&
69 (driver->logging_mode != MEMORY_DEVICE_MODE ||
70 diag_hsic[index].hsic_data_requested)) {
Shalabh Jainb0037c02013-01-18 12:47:40 -080071 diag_hsic[index].in_busy_hsic_read_on_device = 0;
72 diag_hsic[index].in_busy_hsic_write = 0;
73 /* If the HSIC (diag_bridge) platform
74 * device is not open */
75 if (!diag_hsic[index].hsic_device_opened) {
76 hsic_diag_bridge_ops[index].ctxt =
77 (void *)(index);
78 err = diag_bridge_open(index,
79 &hsic_diag_bridge_ops[index]);
80 if (err) {
81 pr_err("diag: HSIC channel open error: %d\n",
82 err);
83 } else {
84 pr_debug("diag: opened HSIC channel\n");
85 diag_hsic[index].hsic_device_opened =
86 1;
87 }
Shalabh Jain737fca72012-11-14 21:53:43 -080088 } else {
Shalabh Jainb0037c02013-01-18 12:47:40 -080089 pr_debug("diag: HSIC channel already open\n");
Shalabh Jain737fca72012-11-14 21:53:43 -080090 }
Shalabh Jainb0037c02013-01-18 12:47:40 -080091 /*
92 * Turn on communication over usb mdm and HSIC,
93 * if the HSIC device driver is enabled
94 * and opened
95 */
96 if (diag_hsic[index].hsic_device_opened) {
97 diag_hsic[index].hsic_ch = 1;
98 /* Poll USB mdm channel to check for data */
99 if (driver->logging_mode == USB_MODE)
100 queue_work(diag_bridge[index].wq,
101 &diag_bridge[index].
102 diag_read_work);
103 /* Poll HSIC channel to check for data */
104 queue_work(diag_bridge[index].wq,
105 &diag_hsic[index].
106 diag_read_hsic_work);
107 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800108 }
109 }
110 mutex_unlock(&diag_bridge[index].bridge_mutex);
111}
112
113/*
114 * diagfwd_disconnect_bridge is called when the USB mdm channel
115 * is disconnected. So disconnect should happen for all bridges
116 */
117int diagfwd_disconnect_bridge(int process_cable)
118{
119 int i;
120 pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
121
122 for (i = 0; i < MAX_BRIDGES; i++) {
123 if (diag_bridge[i].enabled) {
124 mutex_lock(&diag_bridge[i].bridge_mutex);
125 /* If the usb cable is being disconnected */
126 if (process_cable) {
127 diag_bridge[i].usb_connected = 0;
128 usb_diag_free_req(diag_bridge[i].ch);
129 }
130
Dixon Petersonf2d449c2013-02-01 18:02:20 -0800131 if (i == SMUX) {
132 if (driver->diag_smux_enabled &&
Shalabh Jain737fca72012-11-14 21:53:43 -0800133 driver->logging_mode == USB_MODE) {
Dixon Petersonf2d449c2013-02-01 18:02:20 -0800134 driver->in_busy_smux = 1;
135 driver->lcid = LCID_INVALID;
136 driver->smux_connected = 0;
137 /*
138 * Turn off communication over usb
139 * and smux
140 */
141 msm_smux_close(LCID_VALID);
142 }
Shalabh Jainb0037c02013-01-18 12:47:40 -0800143 } else {
144 if (diag_hsic[i].hsic_device_enabled &&
Dixon Petersonf2d449c2013-02-01 18:02:20 -0800145 (driver->logging_mode != MEMORY_DEVICE_MODE
146 || !diag_hsic[i].hsic_data_requested)) {
Shalabh Jainb0037c02013-01-18 12:47:40 -0800147 diag_hsic[i].
Dixon Petersonf2d449c2013-02-01 18:02:20 -0800148 in_busy_hsic_read_on_device = 1;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800149 diag_hsic[i].in_busy_hsic_write = 1;
150 /* Turn off communication over usb
151 * and HSIC */
152 diag_hsic_close(i);
153 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800154 }
155 mutex_unlock(&diag_bridge[i].bridge_mutex);
156 }
157 }
158 return 0;
159}
160
161/* Called after the asychronous usb_diag_read() on mdm channel is complete */
162int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
163{
164 int index = (int)(diag_read_ptr->context);
165
166 /* The read of the usb on the mdm (not HSIC/SMUX) has completed */
167 diag_bridge[index].read_len = diag_read_ptr->actual;
168
169 if (index == SMUX) {
170 if (driver->diag_smux_enabled) {
171 diagfwd_read_complete_smux();
172 return 0;
173 } else {
174 pr_warning("diag: incorrect callback for smux\n");
175 }
176 }
177
178 /* If SMUX not enabled, check for HSIC */
Shalabh Jainb0037c02013-01-18 12:47:40 -0800179 diag_hsic[index].in_busy_hsic_read_on_device = 0;
180 if (!diag_hsic[index].hsic_ch) {
181 pr_err("DIAG in %s: hsic_ch == 0, ch %d\n", __func__, index);
Shalabh Jain737fca72012-11-14 21:53:43 -0800182 return 0;
183 }
184
185 /*
186 * The read of the usb driver on the mdm channel has completed.
187 * If there is no write on the HSIC in progress, check if the
188 * read has data to pass on to the HSIC. If so, pass the usb
189 * mdm data on to the HSIC.
190 */
Shalabh Jainb0037c02013-01-18 12:47:40 -0800191 if (!diag_hsic[index].in_busy_hsic_write &&
192 diag_bridge[index].usb_buf_out &&
193 (diag_bridge[index].read_len > 0)) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800194
195 /*
196 * Initiate the HSIC write. The HSIC write is
197 * asynchronous. When complete the write
198 * complete callback function will be called
199 */
200 int err;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800201 diag_hsic[index].in_busy_hsic_write = 1;
202 err = diag_bridge_write(index, diag_bridge[index].usb_buf_out,
203 diag_bridge[index].read_len);
Shalabh Jain737fca72012-11-14 21:53:43 -0800204 if (err) {
205 pr_err_ratelimited("diag: mdm data on HSIC write err: %d\n",
206 err);
207 /*
208 * If the error is recoverable, then clear
209 * the write flag, so we will resubmit a
210 * write on the next frame. Otherwise, don't
211 * resubmit a write on the next frame.
212 */
213 if ((-ENODEV) != err)
Shalabh Jainb0037c02013-01-18 12:47:40 -0800214 diag_hsic[index].in_busy_hsic_write = 0;
Shalabh Jain737fca72012-11-14 21:53:43 -0800215 }
216 }
217
218 /*
219 * If there is no write of the usb mdm data on the
220 * HSIC channel
221 */
Shalabh Jainb0037c02013-01-18 12:47:40 -0800222 if (!diag_hsic[index].in_busy_hsic_write)
223 queue_work(diag_bridge[index].wq,
224 &diag_bridge[index].diag_read_work);
Shalabh Jain737fca72012-11-14 21:53:43 -0800225
226 return 0;
227}
228
229static void diagfwd_bridge_notifier(void *priv, unsigned event,
230 struct diag_request *d_req)
231{
232 int index;
233
234 switch (event) {
235 case USB_DIAG_CONNECT:
236 diagfwd_connect_bridge(1);
237 break;
238 case USB_DIAG_DISCONNECT:
239 queue_work(driver->diag_wq,
240 &driver->diag_disconnect_work);
241 break;
242 case USB_DIAG_READ_DONE:
243 index = (int)(d_req->context);
244 queue_work(diag_bridge[index].wq,
245 &diag_bridge[index].usb_read_complete_work);
246 break;
247 case USB_DIAG_WRITE_DONE:
248 index = (int)(d_req->context);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800249 if (index == SMUX && driver->diag_smux_enabled)
Shalabh Jain737fca72012-11-14 21:53:43 -0800250 diagfwd_write_complete_smux();
Shalabh Jainb0037c02013-01-18 12:47:40 -0800251 else if (diag_hsic[index].hsic_device_enabled)
Dixon Petersonf90f3582013-01-26 18:14:17 -0800252 diagfwd_write_complete_hsic(d_req, index);
Shalabh Jain737fca72012-11-14 21:53:43 -0800253 break;
254 default:
255 pr_err("diag: in %s: Unknown event from USB diag:%u\n",
256 __func__, event);
257 break;
258 }
259}
260
261void diagfwd_bridge_init(int index)
262{
263 int ret;
264 unsigned char name[20];
265
Shalabh Jainb0037c02013-01-18 12:47:40 -0800266 if (index == HSIC) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800267 strlcpy(name, "hsic", sizeof(name));
Shalabh Jainb0037c02013-01-18 12:47:40 -0800268 } else if (index == HSIC_2) {
269 strlcpy(name, "hsic_2", sizeof(name));
270 } else if (index == SMUX) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800271 strlcpy(name, "smux", sizeof(name));
Shalabh Jainb0037c02013-01-18 12:47:40 -0800272 } else {
Dixon Petersonf90f3582013-01-26 18:14:17 -0800273 pr_err("diag: incorrect bridge init, instance: %d\n", index);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800274 return;
275 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800276
Shalabh Jainb0037c02013-01-18 12:47:40 -0800277 strlcpy(diag_bridge[index].name, name,
278 sizeof(diag_bridge[index].name));
Shalabh Jain737fca72012-11-14 21:53:43 -0800279 strlcat(name, "_diag_wq", sizeof(diag_bridge[index].name));
Shalabh Jainb0037c02013-01-18 12:47:40 -0800280 diag_bridge[index].id = index;
Shalabh Jain737fca72012-11-14 21:53:43 -0800281 diag_bridge[index].wq = create_singlethread_workqueue(name);
282 diag_bridge[index].read_len = 0;
283 diag_bridge[index].write_len = 0;
284 if (diag_bridge[index].usb_buf_out == NULL)
285 diag_bridge[index].usb_buf_out =
286 kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
287 if (diag_bridge[index].usb_buf_out == NULL)
288 goto err;
289 if (diag_bridge[index].usb_read_ptr == NULL)
290 diag_bridge[index].usb_read_ptr =
291 kzalloc(sizeof(struct diag_request), GFP_KERNEL);
292 if (diag_bridge[index].usb_read_ptr == NULL)
293 goto err;
294 if (diag_bridge[index].usb_read_ptr->context == NULL)
295 diag_bridge[index].usb_read_ptr->context =
296 kzalloc(sizeof(int), GFP_KERNEL);
297 if (diag_bridge[index].usb_read_ptr->context == NULL)
298 goto err;
299 mutex_init(&diag_bridge[index].bridge_mutex);
300
Shalabh Jainb0037c02013-01-18 12:47:40 -0800301 if (index == HSIC || index == HSIC_2) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800302 INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
303 diag_usb_read_complete_hsic_fn);
304#ifdef CONFIG_DIAG_OVER_USB
305 INIT_WORK(&(diag_bridge[index].diag_read_work),
306 diag_read_usb_hsic_work_fn);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800307 if (index == HSIC)
308 diag_bridge[index].ch = usb_diag_open(DIAG_MDM,
309 (void *)index, diagfwd_bridge_notifier);
310 else if (index == HSIC_2)
311 diag_bridge[index].ch = usb_diag_open(DIAG_MDM2,
312 (void *)index, diagfwd_bridge_notifier);
Shalabh Jain737fca72012-11-14 21:53:43 -0800313 if (IS_ERR(diag_bridge[index].ch)) {
Shalabh Jainb0037c02013-01-18 12:47:40 -0800314 pr_err("diag: Unable to open USB MDM ch = %d\n", index);
Shalabh Jain737fca72012-11-14 21:53:43 -0800315 goto err;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800316 } else
317 diag_bridge[index].enabled = 1;
Shalabh Jain737fca72012-11-14 21:53:43 -0800318#endif
Shalabh Jain737fca72012-11-14 21:53:43 -0800319 } else if (index == SMUX) {
320 INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
321 diag_usb_read_complete_smux_fn);
322#ifdef CONFIG_DIAG_OVER_USB
323 INIT_WORK(&(diag_bridge[index].diag_read_work),
324 diag_read_usb_smux_work_fn);
325 diag_bridge[index].ch = usb_diag_open(DIAG_QSC, (void *)index,
326 diagfwd_bridge_notifier);
327 if (IS_ERR(diag_bridge[index].ch)) {
328 pr_err("diag: Unable to open USB diag QSC channel\n");
329 goto err;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800330 } else
331 diag_bridge[index].enabled = 1;
Shalabh Jain737fca72012-11-14 21:53:43 -0800332#endif
333 ret = platform_driver_register(&msm_diagfwd_smux_driver);
334 if (ret)
335 pr_err("diag: could not register SMUX device, ret: %d\n",
336 ret);
337 }
338 return;
339err:
340 pr_err("diag: Could not initialize for bridge forwarding\n");
341 kfree(diag_bridge[index].usb_buf_out);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800342 kfree(diag_hsic[index].hsic_buf_tbl);
Shalabh Jain737fca72012-11-14 21:53:43 -0800343 kfree(driver->write_ptr_mdm);
344 kfree(diag_bridge[index].usb_read_ptr);
345 if (diag_bridge[index].wq)
346 destroy_workqueue(diag_bridge[index].wq);
347 return;
348}
349
350void diagfwd_bridge_exit(void)
351{
352 int i;
353 pr_debug("diag: in %s\n", __func__);
354
Shalabh Jainb0037c02013-01-18 12:47:40 -0800355 for (i = 0; i < MAX_HSIC_CH; i++) {
356 if (diag_hsic[i].hsic_device_enabled) {
357 diag_hsic_close(i);
358 diag_hsic[i].hsic_device_enabled = 0;
359 diag_bridge[i].enabled = 0;
360 }
361 diag_hsic[i].hsic_inited = 0;
362 kfree(diag_hsic[i].hsic_buf_tbl);
Shalabh Jain737fca72012-11-14 21:53:43 -0800363 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800364 diagmem_exit(driver, POOL_TYPE_ALL);
365 if (driver->diag_smux_enabled) {
366 driver->lcid = LCID_INVALID;
367 kfree(driver->buf_in_smux);
368 driver->diag_smux_enabled = 0;
369 diag_bridge[SMUX].enabled = 0;
370 }
371 platform_driver_unregister(&msm_hsic_ch_driver);
372 platform_driver_unregister(&msm_diagfwd_smux_driver);
373 /* destroy USB MDM specific variables */
374 for (i = 0; i < MAX_BRIDGES; i++) {
375 if (diag_bridge[i].enabled) {
376#ifdef CONFIG_DIAG_OVER_USB
377 if (diag_bridge[i].usb_connected)
378 usb_diag_free_req(diag_bridge[i].ch);
379 usb_diag_close(diag_bridge[i].ch);
380#endif
381 kfree(diag_bridge[i].usb_buf_out);
382 kfree(diag_bridge[i].usb_read_ptr);
383 destroy_workqueue(diag_bridge[i].wq);
384 diag_bridge[i].enabled = 0;
385 }
386 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800387 kfree(driver->write_ptr_mdm);
388}