blob: b9348050075c4d30477c0c5fbef5f6331a208cb7 [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 {
68 if (diag_hsic[index].hsic_device_enabled) {
69 diag_hsic[index].in_busy_hsic_read_on_device = 0;
70 diag_hsic[index].in_busy_hsic_write = 0;
71 /* If the HSIC (diag_bridge) platform
72 * device is not open */
73 if (!diag_hsic[index].hsic_device_opened) {
74 hsic_diag_bridge_ops[index].ctxt =
75 (void *)(index);
76 err = diag_bridge_open(index,
77 &hsic_diag_bridge_ops[index]);
78 if (err) {
79 pr_err("diag: HSIC channel open error: %d\n",
80 err);
81 } else {
82 pr_debug("diag: opened HSIC channel\n");
83 diag_hsic[index].hsic_device_opened =
84 1;
85 }
Shalabh Jain737fca72012-11-14 21:53:43 -080086 } else {
Shalabh Jainb0037c02013-01-18 12:47:40 -080087 pr_debug("diag: HSIC channel already open\n");
Shalabh Jain737fca72012-11-14 21:53:43 -080088 }
Shalabh Jainb0037c02013-01-18 12:47:40 -080089 /*
90 * Turn on communication over usb mdm and HSIC,
91 * if the HSIC device driver is enabled
92 * and opened
93 */
94 if (diag_hsic[index].hsic_device_opened) {
95 diag_hsic[index].hsic_ch = 1;
96 /* Poll USB mdm channel to check for data */
97 if (driver->logging_mode == USB_MODE)
98 queue_work(diag_bridge[index].wq,
99 &diag_bridge[index].
100 diag_read_work);
101 /* Poll HSIC channel to check for data */
102 queue_work(diag_bridge[index].wq,
103 &diag_hsic[index].
104 diag_read_hsic_work);
105 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800106 }
107 }
108 mutex_unlock(&diag_bridge[index].bridge_mutex);
109}
110
111/*
112 * diagfwd_disconnect_bridge is called when the USB mdm channel
113 * is disconnected. So disconnect should happen for all bridges
114 */
115int diagfwd_disconnect_bridge(int process_cable)
116{
117 int i;
118 pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
119
120 for (i = 0; i < MAX_BRIDGES; i++) {
121 if (diag_bridge[i].enabled) {
122 mutex_lock(&diag_bridge[i].bridge_mutex);
123 /* If the usb cable is being disconnected */
124 if (process_cable) {
125 diag_bridge[i].usb_connected = 0;
126 usb_diag_free_req(diag_bridge[i].ch);
127 }
128
Shalabh Jainb0037c02013-01-18 12:47:40 -0800129 if (i == SMUX && driver->diag_smux_enabled &&
Shalabh Jain737fca72012-11-14 21:53:43 -0800130 driver->logging_mode == USB_MODE) {
131 driver->in_busy_smux = 1;
132 driver->lcid = LCID_INVALID;
133 driver->smux_connected = 0;
134 /* Turn off communication over usb and smux */
135 msm_smux_close(LCID_VALID);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800136 } else {
137 if (diag_hsic[i].hsic_device_enabled &&
138 driver->logging_mode !=
139 MEMORY_DEVICE_MODE) {
140 diag_hsic[i].
141 in_busy_hsic_read_on_device
142 = 1;
143 diag_hsic[i].in_busy_hsic_write = 1;
144 /* Turn off communication over usb
145 * and HSIC */
146 diag_hsic_close(i);
147 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800148 }
149 mutex_unlock(&diag_bridge[i].bridge_mutex);
150 }
151 }
152 return 0;
153}
154
155/* Called after the asychronous usb_diag_read() on mdm channel is complete */
156int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
157{
158 int index = (int)(diag_read_ptr->context);
159
160 /* The read of the usb on the mdm (not HSIC/SMUX) has completed */
161 diag_bridge[index].read_len = diag_read_ptr->actual;
162
163 if (index == SMUX) {
164 if (driver->diag_smux_enabled) {
165 diagfwd_read_complete_smux();
166 return 0;
167 } else {
168 pr_warning("diag: incorrect callback for smux\n");
169 }
170 }
171
172 /* If SMUX not enabled, check for HSIC */
Shalabh Jainb0037c02013-01-18 12:47:40 -0800173 diag_hsic[index].in_busy_hsic_read_on_device = 0;
174 if (!diag_hsic[index].hsic_ch) {
175 pr_err("DIAG in %s: hsic_ch == 0, ch %d\n", __func__, index);
Shalabh Jain737fca72012-11-14 21:53:43 -0800176 return 0;
177 }
178
179 /*
180 * The read of the usb driver on the mdm channel has completed.
181 * If there is no write on the HSIC in progress, check if the
182 * read has data to pass on to the HSIC. If so, pass the usb
183 * mdm data on to the HSIC.
184 */
Shalabh Jainb0037c02013-01-18 12:47:40 -0800185 if (!diag_hsic[index].in_busy_hsic_write &&
186 diag_bridge[index].usb_buf_out &&
187 (diag_bridge[index].read_len > 0)) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800188
189 /*
190 * Initiate the HSIC write. The HSIC write is
191 * asynchronous. When complete the write
192 * complete callback function will be called
193 */
194 int err;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800195 diag_hsic[index].in_busy_hsic_write = 1;
196 err = diag_bridge_write(index, diag_bridge[index].usb_buf_out,
197 diag_bridge[index].read_len);
Shalabh Jain737fca72012-11-14 21:53:43 -0800198 if (err) {
199 pr_err_ratelimited("diag: mdm data on HSIC write err: %d\n",
200 err);
201 /*
202 * If the error is recoverable, then clear
203 * the write flag, so we will resubmit a
204 * write on the next frame. Otherwise, don't
205 * resubmit a write on the next frame.
206 */
207 if ((-ENODEV) != err)
Shalabh Jainb0037c02013-01-18 12:47:40 -0800208 diag_hsic[index].in_busy_hsic_write = 0;
Shalabh Jain737fca72012-11-14 21:53:43 -0800209 }
210 }
211
212 /*
213 * If there is no write of the usb mdm data on the
214 * HSIC channel
215 */
Shalabh Jainb0037c02013-01-18 12:47:40 -0800216 if (!diag_hsic[index].in_busy_hsic_write)
217 queue_work(diag_bridge[index].wq,
218 &diag_bridge[index].diag_read_work);
Shalabh Jain737fca72012-11-14 21:53:43 -0800219
220 return 0;
221}
222
223static void diagfwd_bridge_notifier(void *priv, unsigned event,
224 struct diag_request *d_req)
225{
226 int index;
227
228 switch (event) {
229 case USB_DIAG_CONNECT:
230 diagfwd_connect_bridge(1);
231 break;
232 case USB_DIAG_DISCONNECT:
233 queue_work(driver->diag_wq,
234 &driver->diag_disconnect_work);
235 break;
236 case USB_DIAG_READ_DONE:
237 index = (int)(d_req->context);
238 queue_work(diag_bridge[index].wq,
239 &diag_bridge[index].usb_read_complete_work);
240 break;
241 case USB_DIAG_WRITE_DONE:
242 index = (int)(d_req->context);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800243 if (index == SMUX && driver->diag_smux_enabled)
Shalabh Jain737fca72012-11-14 21:53:43 -0800244 diagfwd_write_complete_smux();
Shalabh Jainb0037c02013-01-18 12:47:40 -0800245 else if (diag_hsic[index].hsic_device_enabled)
Dixon Petersonf90f3582013-01-26 18:14:17 -0800246 diagfwd_write_complete_hsic(d_req, index);
Shalabh Jain737fca72012-11-14 21:53:43 -0800247 break;
248 default:
249 pr_err("diag: in %s: Unknown event from USB diag:%u\n",
250 __func__, event);
251 break;
252 }
253}
254
255void diagfwd_bridge_init(int index)
256{
257 int ret;
258 unsigned char name[20];
259
Shalabh Jainb0037c02013-01-18 12:47:40 -0800260 if (index == HSIC) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800261 strlcpy(name, "hsic", sizeof(name));
Shalabh Jainb0037c02013-01-18 12:47:40 -0800262 } else if (index == HSIC_2) {
263 strlcpy(name, "hsic_2", sizeof(name));
264 } else if (index == SMUX) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800265 strlcpy(name, "smux", sizeof(name));
Shalabh Jainb0037c02013-01-18 12:47:40 -0800266 } else {
Dixon Petersonf90f3582013-01-26 18:14:17 -0800267 pr_err("diag: incorrect bridge init, instance: %d\n", index);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800268 return;
269 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800270
Shalabh Jainb0037c02013-01-18 12:47:40 -0800271 strlcpy(diag_bridge[index].name, name,
272 sizeof(diag_bridge[index].name));
Shalabh Jain737fca72012-11-14 21:53:43 -0800273 strlcat(name, "_diag_wq", sizeof(diag_bridge[index].name));
Shalabh Jainb0037c02013-01-18 12:47:40 -0800274 diag_bridge[index].id = index;
Shalabh Jain737fca72012-11-14 21:53:43 -0800275 diag_bridge[index].wq = create_singlethread_workqueue(name);
276 diag_bridge[index].read_len = 0;
277 diag_bridge[index].write_len = 0;
278 if (diag_bridge[index].usb_buf_out == NULL)
279 diag_bridge[index].usb_buf_out =
280 kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
281 if (diag_bridge[index].usb_buf_out == NULL)
282 goto err;
283 if (diag_bridge[index].usb_read_ptr == NULL)
284 diag_bridge[index].usb_read_ptr =
285 kzalloc(sizeof(struct diag_request), GFP_KERNEL);
286 if (diag_bridge[index].usb_read_ptr == NULL)
287 goto err;
288 if (diag_bridge[index].usb_read_ptr->context == NULL)
289 diag_bridge[index].usb_read_ptr->context =
290 kzalloc(sizeof(int), GFP_KERNEL);
291 if (diag_bridge[index].usb_read_ptr->context == NULL)
292 goto err;
293 mutex_init(&diag_bridge[index].bridge_mutex);
294
Shalabh Jainb0037c02013-01-18 12:47:40 -0800295 if (index == HSIC || index == HSIC_2) {
Shalabh Jain737fca72012-11-14 21:53:43 -0800296 INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
297 diag_usb_read_complete_hsic_fn);
298#ifdef CONFIG_DIAG_OVER_USB
299 INIT_WORK(&(diag_bridge[index].diag_read_work),
300 diag_read_usb_hsic_work_fn);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800301 if (index == HSIC)
302 diag_bridge[index].ch = usb_diag_open(DIAG_MDM,
303 (void *)index, diagfwd_bridge_notifier);
304 else if (index == HSIC_2)
305 diag_bridge[index].ch = usb_diag_open(DIAG_MDM2,
306 (void *)index, diagfwd_bridge_notifier);
Shalabh Jain737fca72012-11-14 21:53:43 -0800307 if (IS_ERR(diag_bridge[index].ch)) {
Shalabh Jainb0037c02013-01-18 12:47:40 -0800308 pr_err("diag: Unable to open USB MDM ch = %d\n", index);
Shalabh Jain737fca72012-11-14 21:53:43 -0800309 goto err;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800310 } else
311 diag_bridge[index].enabled = 1;
Shalabh Jain737fca72012-11-14 21:53:43 -0800312#endif
Shalabh Jain737fca72012-11-14 21:53:43 -0800313 } else if (index == SMUX) {
314 INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
315 diag_usb_read_complete_smux_fn);
316#ifdef CONFIG_DIAG_OVER_USB
317 INIT_WORK(&(diag_bridge[index].diag_read_work),
318 diag_read_usb_smux_work_fn);
319 diag_bridge[index].ch = usb_diag_open(DIAG_QSC, (void *)index,
320 diagfwd_bridge_notifier);
321 if (IS_ERR(diag_bridge[index].ch)) {
322 pr_err("diag: Unable to open USB diag QSC channel\n");
323 goto err;
Shalabh Jainb0037c02013-01-18 12:47:40 -0800324 } else
325 diag_bridge[index].enabled = 1;
Shalabh Jain737fca72012-11-14 21:53:43 -0800326#endif
327 ret = platform_driver_register(&msm_diagfwd_smux_driver);
328 if (ret)
329 pr_err("diag: could not register SMUX device, ret: %d\n",
330 ret);
331 }
332 return;
333err:
334 pr_err("diag: Could not initialize for bridge forwarding\n");
335 kfree(diag_bridge[index].usb_buf_out);
Shalabh Jainb0037c02013-01-18 12:47:40 -0800336 kfree(diag_hsic[index].hsic_buf_tbl);
Shalabh Jain737fca72012-11-14 21:53:43 -0800337 kfree(driver->write_ptr_mdm);
338 kfree(diag_bridge[index].usb_read_ptr);
339 if (diag_bridge[index].wq)
340 destroy_workqueue(diag_bridge[index].wq);
341 return;
342}
343
344void diagfwd_bridge_exit(void)
345{
346 int i;
347 pr_debug("diag: in %s\n", __func__);
348
Shalabh Jainb0037c02013-01-18 12:47:40 -0800349 for (i = 0; i < MAX_HSIC_CH; i++) {
350 if (diag_hsic[i].hsic_device_enabled) {
351 diag_hsic_close(i);
352 diag_hsic[i].hsic_device_enabled = 0;
353 diag_bridge[i].enabled = 0;
354 }
355 diag_hsic[i].hsic_inited = 0;
356 kfree(diag_hsic[i].hsic_buf_tbl);
Shalabh Jain737fca72012-11-14 21:53:43 -0800357 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800358 diagmem_exit(driver, POOL_TYPE_ALL);
359 if (driver->diag_smux_enabled) {
360 driver->lcid = LCID_INVALID;
361 kfree(driver->buf_in_smux);
362 driver->diag_smux_enabled = 0;
363 diag_bridge[SMUX].enabled = 0;
364 }
365 platform_driver_unregister(&msm_hsic_ch_driver);
366 platform_driver_unregister(&msm_diagfwd_smux_driver);
367 /* destroy USB MDM specific variables */
368 for (i = 0; i < MAX_BRIDGES; i++) {
369 if (diag_bridge[i].enabled) {
370#ifdef CONFIG_DIAG_OVER_USB
371 if (diag_bridge[i].usb_connected)
372 usb_diag_free_req(diag_bridge[i].ch);
373 usb_diag_close(diag_bridge[i].ch);
374#endif
375 kfree(diag_bridge[i].usb_buf_out);
376 kfree(diag_bridge[i].usb_read_ptr);
377 destroy_workqueue(diag_bridge[i].wq);
378 diag_bridge[i].enabled = 0;
379 }
380 }
Shalabh Jain737fca72012-11-14 21:53:43 -0800381 kfree(driver->write_ptr_mdm);
382}