blob: ae5917588d45864a2137a8d3663a331796abe9e3 [file] [log] [blame]
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -07001/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -07002 *
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/delay.h>
20#include <linux/workqueue.h>
21#include <linux/pm_runtime.h>
22#include <linux/platform_device.h>
23#include <linux/spinlock.h>
24#include <linux/ratelimit.h>
25#include "diagchar.h"
26#include "diagfwd.h"
27#include "diag_mux.h"
28#include "diag_usb.h"
29#include "diag_memorydevice.h"
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -070030#include "diag_pcie.h"
Manoj Prabhu B571cf422017-08-08 19:01:41 +053031#include "diagfwd_peripheral.h"
32#include "diag_ipc_logging.h"
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -070033
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -070034#ifdef CONFIG_DIAG_OVER_PCIE
35#define diag_mux_register_ops diag_pcie_register_ops
36#else
37#define diag_mux_register_ops diag_usb_register_ops
38#endif
39
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -070040struct diag_mux_state_t *diag_mux;
41static struct diag_logger_t usb_logger;
42static struct diag_logger_t md_logger;
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -070043static struct diag_logger_t pcie_logger;
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -070044
45static struct diag_logger_ops usb_log_ops = {
46 .open = diag_usb_connect_all,
47 .close = diag_usb_disconnect_all,
48 .queue_read = diag_usb_queue_read,
49 .write = diag_usb_write,
50 .close_peripheral = NULL
51};
52
53static struct diag_logger_ops md_log_ops = {
54 .open = diag_md_open_all,
55 .close = diag_md_close_all,
56 .queue_read = NULL,
57 .write = diag_md_write,
58 .close_peripheral = diag_md_close_peripheral,
59};
60
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -070061#ifdef CONFIG_DIAG_OVER_PCIE
62static struct diag_logger_ops pcie_log_ops = {
63 .open = diag_pcie_connect_all,
64 .close = diag_pcie_disconnect_all,
65 .queue_read = NULL,
66 .write = diag_pcie_write,
67 .close_peripheral = NULL
68};
69#endif
70
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -070071int diag_mux_init(void)
72{
73 diag_mux = kzalloc(sizeof(struct diag_mux_state_t),
74 GFP_KERNEL);
75 if (!diag_mux)
76 return -ENOMEM;
77 kmemleak_not_leak(diag_mux);
78
79 usb_logger.mode = DIAG_USB_MODE;
80 usb_logger.log_ops = &usb_log_ops;
81
82 md_logger.mode = DIAG_MEMORY_DEVICE_MODE;
83 md_logger.log_ops = &md_log_ops;
84 diag_md_init();
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -070085#ifdef CONFIG_DIAG_OVER_PCIE
86 pcie_logger.mode = DIAG_PCIE_MODE;
87 pcie_logger.log_ops = &pcie_log_ops;
88 diag_mux->pcie_ptr = &pcie_logger;
89#endif
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -070090 /*
91 * Set USB logging as the default logger. This is the mode
92 * Diag should be in when it initializes.
93 */
94 diag_mux->usb_ptr = &usb_logger;
95 diag_mux->md_ptr = &md_logger;
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -070096 switch (driver->transport_set) {
97 case DIAG_ROUTE_TO_PCIE:
98 diag_mux->logger = &pcie_logger;
99 diag_mux->mode = DIAG_PCIE_MODE;
100 break;
101 case DIAG_ROUTE_TO_USB:
102 default:
103 diag_mux->logger = &usb_logger;
104 diag_mux->mode = DIAG_USB_MODE;
105 break;
106 }
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700107 diag_mux->mux_mask = 0;
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700108 return 0;
109}
110
111void diag_mux_exit(void)
112{
113 kfree(diag_mux);
114}
115
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700116#ifdef CONFIG_DIAG_OVER_PCIE
117int diag_pcie_register_ops(int proc, int ctx, struct diag_mux_ops *ops)
118{
119 int err = 0;
120
121 if (!ops)
122 return -EINVAL;
123
124 if (proc < 0 || proc >= NUM_MUX_PROC)
125 return 0;
126
127 pcie_logger.ops[proc] = ops;
128 err = diag_pcie_register(proc, ctx, ops);
129 if (err) {
Mohit Aggarwalb5f9b8f2018-12-02 17:39:24 -0800130 driver->transport_set = DIAG_ROUTE_TO_USB;
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700131 diag_mux->logger = &usb_logger;
132 diag_mux->mode = DIAG_USB_MODE;
133 usb_logger.ops[proc] = ops;
134 err = diag_usb_register(proc, ctx, ops);
135 if (err) {
136 pr_err("diag: MUX: unable to register usb operations for proc: %d, err: %d\n",
137 proc, err);
138 return err;
139 }
140 pr_err("diag: MUX: unable to register pcie operations for proc: %d, err: %d\n",
141 proc, err);
142 }
143 return 0;
144}
145#else
146int diag_usb_register_ops(int proc, int ctx, struct diag_mux_ops *ops)
147{
148 int err = 0;
149
150 if (!ops)
151 return -EINVAL;
152
153 if (proc < 0 || proc >= NUM_MUX_PROC)
154 return 0;
155 usb_logger.ops[proc] = ops;
156 err = diag_usb_register(proc, ctx, ops);
157 if (err) {
158 pr_err("diag: MUX: unable to register usb operations for proc: %d, err: %d\n",
159 proc, err);
160 return err;
161 }
162 return 0;
163}
164#endif
165
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700166int diag_mux_register(int proc, int ctx, struct diag_mux_ops *ops)
167{
168 int err = 0;
169
170 if (!ops)
171 return -EINVAL;
172
173 if (proc < 0 || proc >= NUM_MUX_PROC)
174 return 0;
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700175 err = diag_mux_register_ops(proc, ctx, ops);
176 if (err)
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700177 return err;
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700178 md_logger.ops[proc] = ops;
179 err = diag_md_register(proc, ctx, ops);
180 if (err) {
181 pr_err("diag: MUX: unable to register md operations for proc: %d, err: %d\n",
182 proc, err);
183 return err;
184 }
185
186 return 0;
187}
188
189int diag_mux_queue_read(int proc)
190{
191 struct diag_logger_t *logger = NULL;
192
193 if (proc < 0 || proc >= NUM_MUX_PROC)
194 return -EINVAL;
195 if (!diag_mux)
196 return -EIO;
197
198 if (diag_mux->mode == DIAG_MULTI_MODE)
199 logger = diag_mux->usb_ptr;
200 else
201 logger = diag_mux->logger;
202
203 if (logger && logger->log_ops && logger->log_ops->queue_read)
204 return logger->log_ops->queue_read(proc);
205
206 return 0;
207}
208
209int diag_mux_write(int proc, unsigned char *buf, int len, int ctx)
210{
211 struct diag_logger_t *logger = NULL;
212 int peripheral;
213
214 if (proc < 0 || proc >= NUM_MUX_PROC)
215 return -EINVAL;
216 if (!diag_mux)
217 return -EIO;
218
Manoj Prabhu B571cf422017-08-08 19:01:41 +0530219 peripheral = diag_md_get_peripheral(ctx);
220 if (peripheral < 0) {
221 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
222 "diag:%s:%d invalid peripheral = %d\n",
223 __func__, __LINE__, peripheral);
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700224 return -EINVAL;
Manoj Prabhu B571cf422017-08-08 19:01:41 +0530225 }
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700226
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700227 if (MD_PERIPHERAL_MASK(peripheral) & diag_mux->mux_mask) {
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700228 logger = diag_mux->md_ptr;
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700229 } else {
230 switch (driver->transport_set) {
231 case DIAG_ROUTE_TO_PCIE:
232 logger = diag_mux->pcie_ptr;
233 break;
234 case DIAG_ROUTE_TO_USB:
235 default:
236 logger = diag_mux->usb_ptr;
237 break;
238 }
239 }
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700240
241 if (logger && logger->log_ops && logger->log_ops->write)
242 return logger->log_ops->write(proc, buf, len, ctx);
243 return 0;
244}
245
246int diag_mux_close_peripheral(int proc, uint8_t peripheral)
247{
248 struct diag_logger_t *logger = NULL;
249
250 if (proc < 0 || proc >= NUM_MUX_PROC)
251 return -EINVAL;
252 /* Peripheral should account for Apps data as well */
Manoj Prabhu B571cf422017-08-08 19:01:41 +0530253 if (peripheral > NUM_PERIPHERALS) {
254 if (!driver->num_pd_session)
255 return -EINVAL;
256 if (peripheral > NUM_MD_SESSIONS)
257 return -EINVAL;
258 }
259
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700260 if (!diag_mux)
261 return -EIO;
262
263 if (MD_PERIPHERAL_MASK(peripheral) & diag_mux->mux_mask)
264 logger = diag_mux->md_ptr;
265 else
266 logger = diag_mux->logger;
267
268 if (logger && logger->log_ops && logger->log_ops->close_peripheral)
269 return logger->log_ops->close_peripheral(proc, peripheral);
270 return 0;
271}
272
273int diag_mux_switch_logging(int *req_mode, int *peripheral_mask)
274{
275 unsigned int new_mask = 0;
276
277 if (!req_mode)
278 return -EINVAL;
279
Manoj Prabhu B571cf422017-08-08 19:01:41 +0530280 if (*peripheral_mask <= 0 ||
281 (*peripheral_mask > (DIAG_CON_ALL | DIAG_CON_UPD_ALL))) {
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700282 pr_err("diag: mask %d in %s\n", *peripheral_mask, __func__);
283 return -EINVAL;
284 }
285
286 switch (*req_mode) {
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700287 case DIAG_PCIE_MODE:
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700288 case DIAG_USB_MODE:
289 new_mask = ~(*peripheral_mask) & diag_mux->mux_mask;
290 if (new_mask != DIAG_CON_NONE)
291 *req_mode = DIAG_MULTI_MODE;
Manoj Prabhu B85088ba2017-12-06 11:52:02 +0530292 if (new_mask == DIAG_CON_ALL)
293 *req_mode = DIAG_MEMORY_DEVICE_MODE;
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700294 break;
295 case DIAG_MEMORY_DEVICE_MODE:
296 new_mask = (*peripheral_mask) | diag_mux->mux_mask;
297 if (new_mask != DIAG_CON_ALL)
298 *req_mode = DIAG_MULTI_MODE;
299 break;
300 default:
301 pr_err("diag: Invalid mode %d in %s\n", *req_mode, __func__);
302 return -EINVAL;
303 }
304
305 switch (diag_mux->mode) {
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700306 case DIAG_PCIE_MODE:
307 if (*req_mode == DIAG_MEMORY_DEVICE_MODE) {
308 diag_mux->pcie_ptr->log_ops->close();
309 diag_mux->logger = diag_mux->md_ptr;
310 diag_mux->md_ptr->log_ops->open();
311 } else if (*req_mode == DIAG_MULTI_MODE) {
312 diag_mux->md_ptr->log_ops->open();
313 diag_mux->logger = NULL;
314 }
315 break;
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700316 case DIAG_USB_MODE:
317 if (*req_mode == DIAG_MEMORY_DEVICE_MODE) {
318 diag_mux->usb_ptr->log_ops->close();
319 diag_mux->logger = diag_mux->md_ptr;
320 diag_mux->md_ptr->log_ops->open();
321 } else if (*req_mode == DIAG_MULTI_MODE) {
322 diag_mux->md_ptr->log_ops->open();
323 diag_mux->logger = NULL;
324 }
325 break;
326 case DIAG_MEMORY_DEVICE_MODE:
327 if (*req_mode == DIAG_USB_MODE) {
328 diag_mux->md_ptr->log_ops->close();
329 diag_mux->logger = diag_mux->usb_ptr;
330 diag_mux->usb_ptr->log_ops->open();
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700331 } else if (*req_mode == DIAG_PCIE_MODE) {
332 diag_mux->md_ptr->log_ops->close();
333 diag_mux->logger = diag_mux->pcie_ptr;
334 diag_mux->pcie_ptr->log_ops->open();
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700335 } else if (*req_mode == DIAG_MULTI_MODE) {
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700336 if (driver->transport_set == DIAG_ROUTE_TO_PCIE)
337 diag_mux->pcie_ptr->log_ops->open();
338 else
339 diag_mux->usb_ptr->log_ops->open();
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700340 diag_mux->logger = NULL;
341 }
342 break;
343 case DIAG_MULTI_MODE:
344 if (*req_mode == DIAG_USB_MODE) {
345 diag_mux->md_ptr->log_ops->close();
346 diag_mux->logger = diag_mux->usb_ptr;
Sreelakshmi Gownipalli75c91e12018-10-16 16:54:43 -0700347 } else if (*req_mode == DIAG_PCIE_MODE) {
348 diag_mux->md_ptr->log_ops->close();
349 diag_mux->logger = diag_mux->pcie_ptr;
Sreelakshmi Gownipallicb8893d2016-10-19 16:02:34 -0700350 } else if (*req_mode == DIAG_MEMORY_DEVICE_MODE) {
351 diag_mux->usb_ptr->log_ops->close();
352 diag_mux->logger = diag_mux->md_ptr;
353 }
354 break;
355 }
356 diag_mux->mode = *req_mode;
357 diag_mux->mux_mask = new_mask;
358 *peripheral_mask = new_mask;
359 return 0;
360}