blob: d53020493a3e9370a3b7623a77accde40de0bd2a [file] [log] [blame]
Hardik Arya1c0a6162018-01-17 12:18:34 +05301/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
Hardik Aryad6da1cb2017-11-23 20:40:55 +05302 *
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/platform_device.h>
15#include <linux/device.h>
16#include <linux/err.h>
17#include <linux/sched.h>
18#include <linux/ratelimit.h>
19#include <linux/workqueue.h>
20#include <linux/pm_runtime.h>
21#include <linux/delay.h>
22#include <linux/atomic.h>
23#include <linux/diagchar.h>
24#include <linux/of.h>
25#include <linux/kmemleak.h>
26#include "diagchar.h"
27#include "diagfwd.h"
28#include "diagfwd_peripheral.h"
29#include "diagfwd_smd.h"
30#include "diag_ipc_logging.h"
31
32struct diag_smd_info smd_data[NUM_PERIPHERALS] = {
33 {
34 .peripheral = PERIPHERAL_MODEM,
35 .type = TYPE_DATA,
36 .name = "MODEM_DATA"
37 },
38 {
39 .peripheral = PERIPHERAL_LPASS,
40 .type = TYPE_DATA,
41 .name = "LPASS_DATA"
42 },
43 {
44 .peripheral = PERIPHERAL_WCNSS,
45 .type = TYPE_DATA,
46 .name = "WCNSS_DATA"
47 },
48 {
49 .peripheral = PERIPHERAL_SENSORS,
50 .type = TYPE_DATA,
51 .name = "SENSORS_DATA"
52 },
53 {
54 .peripheral = PERIPHERAL_WDSP,
55 .type = TYPE_DATA,
56 .name = "DIAG_DATA"
57 },
58 {
59 .peripheral = PERIPHERAL_CDSP,
60 .type = TYPE_DATA,
61 .name = "CDSP_DATA"
62 }
63};
64
65struct diag_smd_info smd_cntl[NUM_PERIPHERALS] = {
66 {
67 .peripheral = PERIPHERAL_MODEM,
68 .type = TYPE_CNTL,
69 .name = "MODEM_CNTL"
70 },
71 {
72 .peripheral = PERIPHERAL_LPASS,
73 .type = TYPE_CNTL,
74 .name = "LPASS_CNTL"
75 },
76 {
77 .peripheral = PERIPHERAL_WCNSS,
78 .type = TYPE_CNTL,
79 .name = "WCNSS_CNTL"
80 },
81 {
82 .peripheral = PERIPHERAL_SENSORS,
83 .type = TYPE_CNTL,
84 .name = "SENSORS_CNTL"
85 },
86 {
87 .peripheral = PERIPHERAL_WDSP,
88 .type = TYPE_CNTL,
89 .name = "DIAG_CTRL"
90 },
91 {
92 .peripheral = PERIPHERAL_CDSP,
93 .type = TYPE_CNTL,
94 .name = "CDSP_CNTL"
95 }
96};
97
98struct diag_smd_info smd_dci[NUM_PERIPHERALS] = {
99 {
100 .peripheral = PERIPHERAL_MODEM,
101 .type = TYPE_DCI,
102 .name = "MODEM_DCI"
103 },
104 {
105 .peripheral = PERIPHERAL_LPASS,
106 .type = TYPE_DCI,
107 .name = "LPASS_DCI"
108 },
109 {
110 .peripheral = PERIPHERAL_WCNSS,
111 .type = TYPE_DCI,
112 .name = "WCNSS_DCI"
113 },
114 {
115 .peripheral = PERIPHERAL_SENSORS,
116 .type = TYPE_DCI,
117 .name = "SENSORS_DCI"
118 },
119 {
120 .peripheral = PERIPHERAL_WDSP,
121 .type = TYPE_DCI,
122 .name = "DIAG_DCI_DATA"
123 },
124 {
125 .peripheral = PERIPHERAL_CDSP,
126 .type = TYPE_DCI,
127 .name = "CDSP_DCI"
128 }
129};
130
131struct diag_smd_info smd_cmd[NUM_PERIPHERALS] = {
132 {
133 .peripheral = PERIPHERAL_MODEM,
134 .type = TYPE_CMD,
135 .name = "MODEM_CMD"
136 },
137 {
138 .peripheral = PERIPHERAL_LPASS,
139 .type = TYPE_CMD,
140 .name = "LPASS_CMD"
141 },
142 {
143 .peripheral = PERIPHERAL_WCNSS,
144 .type = TYPE_CMD,
145 .name = "WCNSS_CMD"
146 },
147 {
148 .peripheral = PERIPHERAL_SENSORS,
149 .type = TYPE_CMD,
150 .name = "SENSORS_CMD"
151 },
152 {
153 .peripheral = PERIPHERAL_WDSP,
154 .type = TYPE_CMD,
155 .name = "DIAG_CMD"
156 },
157 {
158 .peripheral = PERIPHERAL_CDSP,
159 .type = TYPE_CMD,
160 .name = "CDSP_CMD"
161 }
162};
163
164struct diag_smd_info smd_dci_cmd[NUM_PERIPHERALS] = {
165 {
166 .peripheral = PERIPHERAL_MODEM,
167 .type = TYPE_DCI_CMD,
168 .name = "MODEM_DCI_CMD"
169 },
170 {
171 .peripheral = PERIPHERAL_LPASS,
172 .type = TYPE_DCI_CMD,
173 .name = "LPASS_DCI_CMD"
174 },
175 {
176 .peripheral = PERIPHERAL_WCNSS,
177 .type = TYPE_DCI_CMD,
178 .name = "WCNSS_DCI_CMD"
179 },
180 {
181 .peripheral = PERIPHERAL_SENSORS,
182 .type = TYPE_DCI_CMD,
183 .name = "SENSORS_DCI_CMD"
184 },
185 {
186 .peripheral = PERIPHERAL_WDSP,
187 .type = TYPE_DCI_CMD,
188 .name = "DIAG_DCI_CMD"
189 },
190 {
191 .peripheral = PERIPHERAL_CDSP,
192 .type = TYPE_DCI_CMD,
193 .name = "CDSP_DCI_CMD"
194 }
195};
196
197static void diag_state_open_smd(void *ctxt);
198static void diag_state_close_smd(void *ctxt);
199static void smd_notify(void *ctxt, unsigned int event);
200static int diag_smd_write(void *ctxt, unsigned char *buf, int len);
Hardik Arya1c0a6162018-01-17 12:18:34 +0530201static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len,
202 struct diagfwd_buf_t *fwd_buf);
Hardik Aryad6da1cb2017-11-23 20:40:55 +0530203static void diag_smd_queue_read(void *ctxt);
204
205static struct diag_peripheral_ops smd_ops = {
206 .open = diag_state_open_smd,
207 .close = diag_state_close_smd,
208 .write = diag_smd_write,
209 .read = diag_smd_read,
210 .queue_read = diag_smd_queue_read
211};
212
213static void diag_state_open_smd(void *ctxt)
214{
215 struct diag_smd_info *smd_info = NULL;
216
217 if (!ctxt)
218 return;
219
220 smd_info = (struct diag_smd_info *)(ctxt);
221 atomic_set(&smd_info->diag_state, 1);
222 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
223 "%s setting diag state to 1", smd_info->name);
224}
225
226static void diag_state_close_smd(void *ctxt)
227{
228 struct diag_smd_info *smd_info = NULL;
229
230 if (!ctxt)
231 return;
232
233 smd_info = (struct diag_smd_info *)(ctxt);
234 atomic_set(&smd_info->diag_state, 0);
235 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
236 "%s setting diag state to 0", smd_info->name);
237 wake_up_interruptible(&smd_info->read_wait_q);
238 flush_workqueue(smd_info->wq);
239}
240
241static int smd_channel_probe(struct platform_device *pdev, uint8_t type)
242{
243 int r = 0;
244 int index = -1;
245 const char *channel_name = NULL;
246 struct diag_smd_info *smd_info = NULL;
247
248 switch (pdev->id) {
249 case SMD_APPS_MODEM:
250 index = PERIPHERAL_MODEM;
251 break;
252 case SMD_APPS_QDSP:
253 index = PERIPHERAL_LPASS;
254 break;
255 case SMD_APPS_WCNSS:
256 index = PERIPHERAL_WCNSS;
257 break;
258 case SMD_APPS_DSPS:
259 index = PERIPHERAL_SENSORS;
260 break;
261 default:
262 pr_debug("diag: In %s Received probe for invalid index %d",
263 __func__, pdev->id);
264 return -EINVAL;
265 }
266
267 switch (type) {
268 case TYPE_DATA:
269 smd_info = &smd_data[index];
270 channel_name = "DIAG";
271 break;
272 case TYPE_CNTL:
273 smd_info = &smd_cntl[index];
274 channel_name = "DIAG_CNTL";
275 break;
276 case TYPE_CMD:
277 smd_info = &smd_cmd[index];
278 channel_name = "DIAG_CMD";
279 break;
280 case TYPE_DCI:
281 smd_info = &smd_dci[index];
282 channel_name = "DIAG_2";
283 break;
284 case TYPE_DCI_CMD:
285 smd_info = &smd_dci_cmd[index];
286 channel_name = "DIAG_2_CMD";
287 break;
288 default:
289 return -EINVAL;
290 }
291
292 if (index == PERIPHERAL_WCNSS && type == TYPE_DATA)
293 channel_name = "APPS_RIVA_DATA";
294 else if (index == PERIPHERAL_WCNSS && type == TYPE_CNTL)
295 channel_name = "APPS_RIVA_CTRL";
296
297 if (!channel_name || !smd_info)
298 return -EIO;
299
300 r = smd_named_open_on_edge(channel_name, pdev->id, &smd_info->hdl,
301 smd_info, smd_notify);
302
303 pm_runtime_set_active(&pdev->dev);
304 pm_runtime_enable(&pdev->dev);
305 pr_debug("diag: In %s, SMD port probed %s, id = %d, r = %d\n",
306 __func__, smd_info->name, pdev->id, r);
307
308 return 0;
309}
310
311static int smd_data_probe(struct platform_device *pdev)
312{
313 return smd_channel_probe(pdev, TYPE_DATA);
314}
315
316static int smd_cntl_probe(struct platform_device *pdev)
317{
318 return smd_channel_probe(pdev, TYPE_CNTL);
319}
320
321static int smd_cmd_probe(struct platform_device *pdev)
322{
323 return smd_channel_probe(pdev, TYPE_CMD);
324}
325
326static int smd_dci_probe(struct platform_device *pdev)
327{
328 return smd_channel_probe(pdev, TYPE_DCI);
329}
330
331static int smd_dci_cmd_probe(struct platform_device *pdev)
332{
333 return smd_channel_probe(pdev, TYPE_DCI_CMD);
334}
335
336static int smd_runtime_suspend(struct device *dev)
337{
338 dev_dbg(dev, "pm_runtime: suspending...\n");
339 return 0;
340}
341
342static int smd_runtime_resume(struct device *dev)
343{
344 dev_dbg(dev, "pm_runtime: resuming...\n");
345 return 0;
346}
347
348static const struct dev_pm_ops smd_dev_pm_ops = {
349 .runtime_suspend = smd_runtime_suspend,
350 .runtime_resume = smd_runtime_resume,
351};
352
353static struct platform_driver diag_smd_ch_driver = {
354 .probe = smd_data_probe,
355 .driver = {
356 .name = "DIAG",
357 .owner = THIS_MODULE,
358 .pm = &smd_dev_pm_ops,
359 },
360};
361
362static struct platform_driver diag_smd_lite_driver = {
363 .probe = smd_data_probe,
364 .driver = {
365 .name = "APPS_RIVA_DATA",
366 .owner = THIS_MODULE,
367 .pm = &smd_dev_pm_ops,
368 },
369};
370
371static struct platform_driver diag_smd_cntl_driver = {
372 .probe = smd_cntl_probe,
373 .driver = {
374 .name = "DIAG_CNTL",
375 .owner = THIS_MODULE,
376 .pm = &smd_dev_pm_ops,
377 },
378};
379
380static struct platform_driver diag_smd_lite_cntl_driver = {
381 .probe = smd_cntl_probe,
382 .driver = {
383 .name = "APPS_RIVA_CTRL",
384 .owner = THIS_MODULE,
385 .pm = &smd_dev_pm_ops,
386 },
387};
388
389static struct platform_driver diag_smd_lite_cmd_driver = {
390 .probe = smd_cmd_probe,
391 .driver = {
392 .name = "DIAG_CMD",
393 .owner = THIS_MODULE,
394 .pm = &smd_dev_pm_ops,
395 }
396};
397
398static struct platform_driver diag_smd_dci_driver = {
399 .probe = smd_dci_probe,
400 .driver = {
401 .name = "DIAG_2",
402 .owner = THIS_MODULE,
403 .pm = &smd_dev_pm_ops,
404 },
405};
406
407static struct platform_driver diag_smd_dci_cmd_driver = {
408 .probe = smd_dci_cmd_probe,
409 .driver = {
410 .name = "DIAG_2_CMD",
411 .owner = THIS_MODULE,
412 .pm = &smd_dev_pm_ops,
413 },
414};
415
416static void smd_open_work_fn(struct work_struct *work)
417{
418 struct diag_smd_info *smd_info = container_of(work,
419 struct diag_smd_info,
420 open_work);
421 if (!smd_info->inited)
422 return;
423
424 diagfwd_channel_open(smd_info->fwd_ctxt);
425 diagfwd_late_open(smd_info->fwd_ctxt);
426 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
427 smd_info->name);
428}
429
430static void smd_close_work_fn(struct work_struct *work)
431{
432 struct diag_smd_info *smd_info = container_of(work,
433 struct diag_smd_info,
434 close_work);
435 if (!smd_info->inited)
436 return;
437
438 diagfwd_channel_close(smd_info->fwd_ctxt);
439 wake_up_interruptible(&smd_info->read_wait_q);
440 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
441 smd_info->name);
442}
443
444static void smd_read_work_fn(struct work_struct *work)
445{
446 struct diag_smd_info *smd_info = container_of(work,
447 struct diag_smd_info,
448 read_work);
449 if (!smd_info->inited) {
450 diag_ws_release();
451 return;
452 }
453
454 diagfwd_channel_read(smd_info->fwd_ctxt);
455}
456
457static void diag_smd_late_init_work_fn(struct work_struct *work)
458{
459 struct diag_smd_info *smd_info = container_of(work,
460 struct diag_smd_info,
461 late_init_work);
462 if (!smd_info || !smd_info->hdl)
463 return;
464 diagfwd_channel_open(smd_info->fwd_ctxt);
465 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "smd late init p: %d t: %d\n",
466 smd_info->peripheral, smd_info->type);
467}
468
469static void diag_smd_queue_read(void *ctxt)
470{
471 struct diag_smd_info *smd_info = NULL;
472
473 if (!ctxt)
474 return;
475
476 smd_info = (struct diag_smd_info *)ctxt;
477 if (smd_info->inited && atomic_read(&smd_info->opened) &&
478 smd_info->hdl) {
479 wake_up_interruptible(&smd_info->read_wait_q);
480 queue_work(smd_info->wq, &(smd_info->read_work));
481 }
482}
483int diag_smd_check_state(void *ctxt)
484{
485 struct diag_smd_info *info = NULL;
486
487 if (!ctxt)
488 return 0;
489
490 info = (struct diag_smd_info *)ctxt;
491 return (int)(atomic_read(&info->diag_state));
492}
493void diag_smd_invalidate(void *ctxt, struct diagfwd_info *fwd_ctxt)
494{
495 struct diag_smd_info *smd_info = NULL;
496 void *prev = NULL;
497
498 if (!ctxt || !fwd_ctxt)
499 return;
500
501 smd_info = (struct diag_smd_info *)ctxt;
502 prev = smd_info->fwd_ctxt;
503 smd_info->fwd_ctxt = fwd_ctxt;
504 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s prev: %pK fwd_ctxt: %pK\n",
505 smd_info->name, prev, smd_info->fwd_ctxt);
506}
507
508static void __diag_smd_init(struct diag_smd_info *smd_info)
509{
510 char wq_name[DIAG_SMD_NAME_SZ + 10];
511
512 if (!smd_info)
513 return;
514
515 init_waitqueue_head(&smd_info->read_wait_q);
516 mutex_init(&smd_info->lock);
517 strlcpy(wq_name, "DIAG_SMD_", 10);
518 strlcat(wq_name, smd_info->name, sizeof(smd_info->name));
519 smd_info->wq = create_singlethread_workqueue(wq_name);
520 if (!smd_info->wq) {
521 pr_err("diag: In %s, unable to create workqueue for smd channel %s\n",
522 __func__, smd_info->name);
523 return;
524 }
525 INIT_WORK(&(smd_info->open_work), smd_open_work_fn);
526 INIT_WORK(&(smd_info->close_work), smd_close_work_fn);
527 INIT_WORK(&(smd_info->read_work), smd_read_work_fn);
528 INIT_WORK(&(smd_info->late_init_work), diag_smd_late_init_work_fn);
529 smd_info->fifo_size = 0;
530 smd_info->hdl = NULL;
531 smd_info->fwd_ctxt = NULL;
532 atomic_set(&smd_info->opened, 0);
533 atomic_set(&smd_info->diag_state, 0);
534
535 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s initialized fwd_ctxt: %pK\n",
536 smd_info->name, smd_info->fwd_ctxt);
537}
538
539int diag_smd_init(void)
540{
541 uint8_t peripheral;
542 struct diag_smd_info *smd_info = NULL;
543
544 for (peripheral = 0; peripheral < NUM_PERIPHERALS; peripheral++) {
545 if (peripheral == PERIPHERAL_WDSP)
546 continue;
547 smd_info = &smd_cntl[peripheral];
548 __diag_smd_init(smd_info);
549 diagfwd_cntl_register(TRANSPORT_SMD, smd_info->peripheral,
550 (void *)smd_info, &smd_ops,
551 &smd_info->fwd_ctxt);
552 smd_info->inited = 1;
553 __diag_smd_init(&smd_data[peripheral]);
554 __diag_smd_init(&smd_cmd[peripheral]);
555 __diag_smd_init(&smd_dci[peripheral]);
556 __diag_smd_init(&smd_dci_cmd[peripheral]);
557 }
558
559 platform_driver_register(&diag_smd_cntl_driver);
560 platform_driver_register(&diag_smd_lite_cntl_driver);
561 platform_driver_register(&diag_smd_ch_driver);
562 platform_driver_register(&diag_smd_lite_driver);
563 platform_driver_register(&diag_smd_lite_cmd_driver);
564 platform_driver_register(&diag_smd_dci_driver);
565 platform_driver_register(&diag_smd_dci_cmd_driver);
566
567 return 0;
568}
569
570static void smd_late_init(struct diag_smd_info *smd_info)
571{
572 struct diagfwd_info *fwd_info = NULL;
573
574 if (!smd_info)
575 return;
576
577 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s entering\n",
578 smd_info->name);
579
580 diagfwd_register(TRANSPORT_SMD, smd_info->peripheral, smd_info->type,
581 (void *)smd_info, &smd_ops, &smd_info->fwd_ctxt);
582 fwd_info = smd_info->fwd_ctxt;
583 smd_info->inited = 1;
584 /*
585 * The channel is already open by the probe call as a result of other
586 * peripheral. Inform the diag fwd layer that the channel is open.
587 */
588 if (atomic_read(&smd_info->opened))
589 queue_work(smd_info->wq, &(smd_info->late_init_work));
590
591 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
592 smd_info->name);
593}
594
595int diag_smd_init_peripheral(uint8_t peripheral)
596{
597 if (peripheral >= NUM_PERIPHERALS) {
598 pr_err("diag: In %s, invalid peripheral %d\n",
599 __func__, peripheral);
600 return -EINVAL;
601 }
602
603 smd_late_init(&smd_data[peripheral]);
604 smd_late_init(&smd_dci[peripheral]);
605 smd_late_init(&smd_cmd[peripheral]);
606 smd_late_init(&smd_dci_cmd[peripheral]);
607
608 return 0;
609}
610
611static void __diag_smd_exit(struct diag_smd_info *smd_info)
612{
613 if (!smd_info)
614 return;
615
616 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s entering\n",
617 smd_info->name);
618
619 diagfwd_deregister(smd_info->peripheral, smd_info->type,
620 (void *)smd_info);
621 smd_info->fwd_ctxt = NULL;
622 smd_info->hdl = NULL;
623 if (smd_info->wq)
624 destroy_workqueue(smd_info->wq);
625
626 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
627 smd_info->name);
628}
629
630void diag_smd_early_exit(void)
631{
632 int i = 0;
633
634 for (i = 0; i < NUM_PERIPHERALS; i++) {
635 if (i == PERIPHERAL_WDSP)
636 continue;
637 __diag_smd_exit(&smd_cntl[i]);
638 }
639
640 platform_driver_unregister(&diag_smd_cntl_driver);
641 platform_driver_unregister(&diag_smd_lite_cntl_driver);
642}
643
644void diag_smd_exit(void)
645{
646 int i = 0;
647
648 for (i = 0; i < NUM_PERIPHERALS; i++) {
649 if (i == PERIPHERAL_WDSP)
650 continue;
651 __diag_smd_exit(&smd_data[i]);
652 __diag_smd_exit(&smd_cmd[i]);
653 __diag_smd_exit(&smd_dci[i]);
654 __diag_smd_exit(&smd_dci_cmd[i]);
655 }
656
657 platform_driver_unregister(&diag_smd_ch_driver);
658 platform_driver_unregister(&diag_smd_lite_driver);
659 platform_driver_unregister(&diag_smd_lite_cmd_driver);
660 platform_driver_unregister(&diag_smd_dci_driver);
661 platform_driver_unregister(&diag_smd_dci_cmd_driver);
662}
663
664static int diag_smd_write_ext(struct diag_smd_info *smd_info,
665 unsigned char *buf, int len)
666{
667 int err = 0;
668 int offset = 0;
669 int write_len = 0;
670 int retry_count = 0;
671 int max_retries = 3;
672 uint8_t avail = 0;
673
674 if (!smd_info || !buf || len <= 0) {
675 pr_err_ratelimited("diag: In %s, invalid params, smd_info: %pK, buf: %pK, len: %d\n",
676 __func__, smd_info, buf, len);
677 return -EINVAL;
678 }
679
680 if (!smd_info->inited || !smd_info->hdl ||
681 !atomic_read(&smd_info->opened))
682 return -ENODEV;
683
684 mutex_lock(&smd_info->lock);
685 err = smd_write_start(smd_info->hdl, len);
686 if (err) {
687 pr_err_ratelimited("diag: In %s, error calling smd_write_start, peripheral: %d, err: %d\n",
688 __func__, smd_info->peripheral, err);
689 goto fail;
690 }
691
692 while (offset < len) {
693 retry_count = 0;
694 do {
695 if (smd_write_segment_avail(smd_info->hdl)) {
696 avail = 1;
697 break;
698 }
699 /*
700 * The channel maybe busy - the FIFO can be full. Retry
701 * after sometime. The value of 10000 was chosen
702 * emprically as the optimal value for the peripherals
703 * to read data from the SMD channel.
704 */
705 usleep_range(10000, 10100);
706 retry_count++;
707 } while (retry_count < max_retries);
708
709 if (!avail) {
710 err = -EAGAIN;
711 goto fail;
712 }
713
714 write_len = smd_write_segment(smd_info->hdl, buf + offset,
715 (len - offset));
716 offset += write_len;
717 write_len = 0;
718 }
719
720 err = smd_write_end(smd_info->hdl);
721 if (err) {
722 pr_err_ratelimited("diag: In %s, error calling smd_write_end, peripheral: %d, err: %d\n",
723 __func__, smd_info->peripheral, err);
724 goto fail;
725 }
726
727fail:
728 mutex_unlock(&smd_info->lock);
729 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
730 "%s wrote to channel, write_len: %d, err: %d\n",
731 smd_info->name, offset, err);
732 return err;
733}
734
735static int diag_smd_write(void *ctxt, unsigned char *buf, int len)
736{
737 int write_len = 0;
738 int retry_count = 0;
739 int max_retries = 3;
740 struct diag_smd_info *smd_info = NULL;
741
742 if (!ctxt || !buf)
743 return -EIO;
744
745 smd_info = (struct diag_smd_info *)ctxt;
746 if (!smd_info || !buf || len <= 0) {
747 pr_err_ratelimited("diag: In %s, invalid params, smd_info: %pK, buf: %pK, len: %d\n",
748 __func__, smd_info, buf, len);
749 return -EINVAL;
750 }
751
752 if (!smd_info->inited || !smd_info->hdl ||
753 !atomic_read(&smd_info->opened))
754 return -ENODEV;
755
756 if (len > smd_info->fifo_size)
757 return diag_smd_write_ext(smd_info, buf, len);
758
759 do {
760 mutex_lock(&smd_info->lock);
761 write_len = smd_write(smd_info->hdl, buf, len);
762 mutex_unlock(&smd_info->lock);
763 if (write_len == len)
764 break;
765 /*
766 * The channel maybe busy - the FIFO can be full. Retry after
767 * sometime. The value of 10000 was chosen emprically as the
768 * optimal value for the peripherals to read data from the SMD
769 * channel.
770 */
771 usleep_range(10000, 10100);
772 retry_count++;
773 } while (retry_count < max_retries);
774
775 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s wrote to channel, write_len: %d\n",
776 smd_info->name, write_len);
777
778 if (write_len != len)
779 return -ENOMEM;
780
781 return 0;
782}
783
Hardik Arya1c0a6162018-01-17 12:18:34 +0530784static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len,
785 struct diagfwd_buf_t *fwd_buf)
Hardik Aryad6da1cb2017-11-23 20:40:55 +0530786{
787 int pkt_len = 0;
788 int err = 0;
789 int total_recd_partial = 0;
790 int total_recd = 0;
791 uint8_t buf_full = 0;
792 unsigned char *temp_buf = NULL;
793 uint32_t read_len = 0;
794 struct diag_smd_info *smd_info = NULL;
795
Hardik Arya1c0a6162018-01-17 12:18:34 +0530796 if (!ctxt || !buf || buf_len <= 0 || !fwd_buf)
Hardik Aryad6da1cb2017-11-23 20:40:55 +0530797 return -EIO;
798
799 smd_info = (struct diag_smd_info *)ctxt;
800 if (!smd_info->hdl || !smd_info->inited ||
801 !atomic_read(&smd_info->opened))
802 return -EIO;
803
804 /*
805 * Always try to read the data if notification is received from smd
806 * In case if packet size is 0 release the wake source hold earlier
807 */
808 err = wait_event_interruptible(smd_info->read_wait_q,
809 (smd_info->hdl != NULL) &&
810 (atomic_read(&smd_info->opened) == 1));
811 if (err) {
812 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
813 return -ERESTARTSYS;
814 }
Hardik Arya1c0a6162018-01-17 12:18:34 +0530815 if (atomic_read(&fwd_buf->in_busy) == 0) {
816 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
817 "%s closing read thread. Buffer is already marked freed p: %d t: %d buf_num: %d\n",
818 smd_info->name, GET_BUF_PERIPHERAL(fwd_buf->ctxt),
819 GET_BUF_TYPE(fwd_buf->ctxt),
820 GET_BUF_NUM(fwd_buf->ctxt));
821 diag_ws_release();
822 return 0;
823 }
Hardik Aryad6da1cb2017-11-23 20:40:55 +0530824 /*
825 * Reset the buffers. Also release the wake source hold earlier.
826 */
827 if (atomic_read(&smd_info->diag_state) == 0) {
828 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
829 "%s closing read thread. diag state is closed\n",
830 smd_info->name);
831 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
832 return 0;
833 }
834
835 if (!smd_info->hdl || !atomic_read(&smd_info->opened)) {
836 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
837 "%s stopping read, hdl: %pK, opened: %d\n",
838 smd_info->name, smd_info->hdl,
839 atomic_read(&smd_info->opened));
840 goto fail_return;
841 }
842
843 do {
844 total_recd_partial = 0;
845 temp_buf = buf + total_recd;
846 pkt_len = smd_cur_packet_size(smd_info->hdl);
847 if (pkt_len <= 0)
848 break;
849
850 if (total_recd + pkt_len > buf_len) {
851 buf_full = 1;
852 break;
853 }
854
855 while (total_recd_partial < pkt_len) {
856 read_len = smd_read_avail(smd_info->hdl);
857 if (!read_len) {
858 err = wait_event_interruptible(smd_info->
859 read_wait_q,
860 ((atomic_read(&smd_info->opened)) &&
861 smd_read_avail(smd_info->hdl)));
862 if (err)
863 goto fail_return;
864
865 if (!smd_info->hdl ||
866 !atomic_read(&smd_info->opened)) {
867 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
868 "%s exiting from wait",
869 smd_info->name);
870 goto fail_return;
871 }
872 }
873
874 if (pkt_len < read_len)
875 goto fail_return;
876
877 smd_read(smd_info->hdl, temp_buf, read_len);
878 total_recd_partial += read_len;
879 total_recd += read_len;
880 temp_buf += read_len;
881 }
882 } while (pkt_len > 0);
883
884 if ((smd_info->type == TYPE_DATA && pkt_len) || buf_full)
885 err = queue_work(smd_info->wq, &(smd_info->read_work));
886
887 if (total_recd > 0) {
888 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s read total bytes: %d\n",
889 smd_info->name, total_recd);
890 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, total_recd);
891 } else {
892 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s error in read, err: %d\n",
893 smd_info->name, total_recd);
894 goto fail_return;
895 }
896 return 0;
897
898fail_return:
899 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
900 return -EINVAL;
901}
902
903static void smd_notify(void *ctxt, unsigned int event)
904{
905 struct diag_smd_info *smd_info = NULL;
906
907 smd_info = (struct diag_smd_info *)ctxt;
908 if (!smd_info)
909 return;
910
911 switch (event) {
912 case SMD_EVENT_OPEN:
913 atomic_set(&smd_info->opened, 1);
914 smd_info->fifo_size = smd_write_avail(smd_info->hdl);
915 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s channel opened\n",
916 smd_info->name);
917 queue_work(smd_info->wq, &(smd_info->open_work));
918 break;
919 case SMD_EVENT_CLOSE:
920 atomic_set(&smd_info->opened, 0);
921 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s channel closed\n",
922 smd_info->name);
923 queue_work(smd_info->wq, &(smd_info->close_work));
924 break;
925 case SMD_EVENT_DATA:
926 diag_ws_on_notify();
927 queue_work(smd_info->wq, &(smd_info->read_work));
928 break;
929 }
930
931 wake_up_interruptible(&smd_info->read_wait_q);
932}
933