blob: 27433a7ee913a56f2bbf307844e687427bd9cf77 [file] [log] [blame]
Hardik Aryad6da1cb2017-11-23 20:40:55 +05301/* Copyright (c) 2015-2017, The Linux Foundation. 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/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);
201static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len);
202static void diag_smd_queue_read(void *ctxt);
203
204static struct diag_peripheral_ops smd_ops = {
205 .open = diag_state_open_smd,
206 .close = diag_state_close_smd,
207 .write = diag_smd_write,
208 .read = diag_smd_read,
209 .queue_read = diag_smd_queue_read
210};
211
212static void diag_state_open_smd(void *ctxt)
213{
214 struct diag_smd_info *smd_info = NULL;
215
216 if (!ctxt)
217 return;
218
219 smd_info = (struct diag_smd_info *)(ctxt);
220 atomic_set(&smd_info->diag_state, 1);
221 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
222 "%s setting diag state to 1", smd_info->name);
223}
224
225static void diag_state_close_smd(void *ctxt)
226{
227 struct diag_smd_info *smd_info = NULL;
228
229 if (!ctxt)
230 return;
231
232 smd_info = (struct diag_smd_info *)(ctxt);
233 atomic_set(&smd_info->diag_state, 0);
234 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
235 "%s setting diag state to 0", smd_info->name);
236 wake_up_interruptible(&smd_info->read_wait_q);
237 flush_workqueue(smd_info->wq);
238}
239
240static int smd_channel_probe(struct platform_device *pdev, uint8_t type)
241{
242 int r = 0;
243 int index = -1;
244 const char *channel_name = NULL;
245 struct diag_smd_info *smd_info = NULL;
246
247 switch (pdev->id) {
248 case SMD_APPS_MODEM:
249 index = PERIPHERAL_MODEM;
250 break;
251 case SMD_APPS_QDSP:
252 index = PERIPHERAL_LPASS;
253 break;
254 case SMD_APPS_WCNSS:
255 index = PERIPHERAL_WCNSS;
256 break;
257 case SMD_APPS_DSPS:
258 index = PERIPHERAL_SENSORS;
259 break;
260 default:
261 pr_debug("diag: In %s Received probe for invalid index %d",
262 __func__, pdev->id);
263 return -EINVAL;
264 }
265
266 switch (type) {
267 case TYPE_DATA:
268 smd_info = &smd_data[index];
269 channel_name = "DIAG";
270 break;
271 case TYPE_CNTL:
272 smd_info = &smd_cntl[index];
273 channel_name = "DIAG_CNTL";
274 break;
275 case TYPE_CMD:
276 smd_info = &smd_cmd[index];
277 channel_name = "DIAG_CMD";
278 break;
279 case TYPE_DCI:
280 smd_info = &smd_dci[index];
281 channel_name = "DIAG_2";
282 break;
283 case TYPE_DCI_CMD:
284 smd_info = &smd_dci_cmd[index];
285 channel_name = "DIAG_2_CMD";
286 break;
287 default:
288 return -EINVAL;
289 }
290
291 if (index == PERIPHERAL_WCNSS && type == TYPE_DATA)
292 channel_name = "APPS_RIVA_DATA";
293 else if (index == PERIPHERAL_WCNSS && type == TYPE_CNTL)
294 channel_name = "APPS_RIVA_CTRL";
295
296 if (!channel_name || !smd_info)
297 return -EIO;
298
299 r = smd_named_open_on_edge(channel_name, pdev->id, &smd_info->hdl,
300 smd_info, smd_notify);
301
302 pm_runtime_set_active(&pdev->dev);
303 pm_runtime_enable(&pdev->dev);
304 pr_debug("diag: In %s, SMD port probed %s, id = %d, r = %d\n",
305 __func__, smd_info->name, pdev->id, r);
306
307 return 0;
308}
309
310static int smd_data_probe(struct platform_device *pdev)
311{
312 return smd_channel_probe(pdev, TYPE_DATA);
313}
314
315static int smd_cntl_probe(struct platform_device *pdev)
316{
317 return smd_channel_probe(pdev, TYPE_CNTL);
318}
319
320static int smd_cmd_probe(struct platform_device *pdev)
321{
322 return smd_channel_probe(pdev, TYPE_CMD);
323}
324
325static int smd_dci_probe(struct platform_device *pdev)
326{
327 return smd_channel_probe(pdev, TYPE_DCI);
328}
329
330static int smd_dci_cmd_probe(struct platform_device *pdev)
331{
332 return smd_channel_probe(pdev, TYPE_DCI_CMD);
333}
334
335static int smd_runtime_suspend(struct device *dev)
336{
337 dev_dbg(dev, "pm_runtime: suspending...\n");
338 return 0;
339}
340
341static int smd_runtime_resume(struct device *dev)
342{
343 dev_dbg(dev, "pm_runtime: resuming...\n");
344 return 0;
345}
346
347static const struct dev_pm_ops smd_dev_pm_ops = {
348 .runtime_suspend = smd_runtime_suspend,
349 .runtime_resume = smd_runtime_resume,
350};
351
352static struct platform_driver diag_smd_ch_driver = {
353 .probe = smd_data_probe,
354 .driver = {
355 .name = "DIAG",
356 .owner = THIS_MODULE,
357 .pm = &smd_dev_pm_ops,
358 },
359};
360
361static struct platform_driver diag_smd_lite_driver = {
362 .probe = smd_data_probe,
363 .driver = {
364 .name = "APPS_RIVA_DATA",
365 .owner = THIS_MODULE,
366 .pm = &smd_dev_pm_ops,
367 },
368};
369
370static struct platform_driver diag_smd_cntl_driver = {
371 .probe = smd_cntl_probe,
372 .driver = {
373 .name = "DIAG_CNTL",
374 .owner = THIS_MODULE,
375 .pm = &smd_dev_pm_ops,
376 },
377};
378
379static struct platform_driver diag_smd_lite_cntl_driver = {
380 .probe = smd_cntl_probe,
381 .driver = {
382 .name = "APPS_RIVA_CTRL",
383 .owner = THIS_MODULE,
384 .pm = &smd_dev_pm_ops,
385 },
386};
387
388static struct platform_driver diag_smd_lite_cmd_driver = {
389 .probe = smd_cmd_probe,
390 .driver = {
391 .name = "DIAG_CMD",
392 .owner = THIS_MODULE,
393 .pm = &smd_dev_pm_ops,
394 }
395};
396
397static struct platform_driver diag_smd_dci_driver = {
398 .probe = smd_dci_probe,
399 .driver = {
400 .name = "DIAG_2",
401 .owner = THIS_MODULE,
402 .pm = &smd_dev_pm_ops,
403 },
404};
405
406static struct platform_driver diag_smd_dci_cmd_driver = {
407 .probe = smd_dci_cmd_probe,
408 .driver = {
409 .name = "DIAG_2_CMD",
410 .owner = THIS_MODULE,
411 .pm = &smd_dev_pm_ops,
412 },
413};
414
415static void smd_open_work_fn(struct work_struct *work)
416{
417 struct diag_smd_info *smd_info = container_of(work,
418 struct diag_smd_info,
419 open_work);
420 if (!smd_info->inited)
421 return;
422
423 diagfwd_channel_open(smd_info->fwd_ctxt);
424 diagfwd_late_open(smd_info->fwd_ctxt);
425 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
426 smd_info->name);
427}
428
429static void smd_close_work_fn(struct work_struct *work)
430{
431 struct diag_smd_info *smd_info = container_of(work,
432 struct diag_smd_info,
433 close_work);
434 if (!smd_info->inited)
435 return;
436
437 diagfwd_channel_close(smd_info->fwd_ctxt);
438 wake_up_interruptible(&smd_info->read_wait_q);
439 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
440 smd_info->name);
441}
442
443static void smd_read_work_fn(struct work_struct *work)
444{
445 struct diag_smd_info *smd_info = container_of(work,
446 struct diag_smd_info,
447 read_work);
448 if (!smd_info->inited) {
449 diag_ws_release();
450 return;
451 }
452
453 diagfwd_channel_read(smd_info->fwd_ctxt);
454}
455
456static void diag_smd_late_init_work_fn(struct work_struct *work)
457{
458 struct diag_smd_info *smd_info = container_of(work,
459 struct diag_smd_info,
460 late_init_work);
461 if (!smd_info || !smd_info->hdl)
462 return;
463 diagfwd_channel_open(smd_info->fwd_ctxt);
464 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "smd late init p: %d t: %d\n",
465 smd_info->peripheral, smd_info->type);
466}
467
468static void diag_smd_queue_read(void *ctxt)
469{
470 struct diag_smd_info *smd_info = NULL;
471
472 if (!ctxt)
473 return;
474
475 smd_info = (struct diag_smd_info *)ctxt;
476 if (smd_info->inited && atomic_read(&smd_info->opened) &&
477 smd_info->hdl) {
478 wake_up_interruptible(&smd_info->read_wait_q);
479 queue_work(smd_info->wq, &(smd_info->read_work));
480 }
481}
482int diag_smd_check_state(void *ctxt)
483{
484 struct diag_smd_info *info = NULL;
485
486 if (!ctxt)
487 return 0;
488
489 info = (struct diag_smd_info *)ctxt;
490 return (int)(atomic_read(&info->diag_state));
491}
492void diag_smd_invalidate(void *ctxt, struct diagfwd_info *fwd_ctxt)
493{
494 struct diag_smd_info *smd_info = NULL;
495 void *prev = NULL;
496
497 if (!ctxt || !fwd_ctxt)
498 return;
499
500 smd_info = (struct diag_smd_info *)ctxt;
501 prev = smd_info->fwd_ctxt;
502 smd_info->fwd_ctxt = fwd_ctxt;
503 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s prev: %pK fwd_ctxt: %pK\n",
504 smd_info->name, prev, smd_info->fwd_ctxt);
505}
506
507static void __diag_smd_init(struct diag_smd_info *smd_info)
508{
509 char wq_name[DIAG_SMD_NAME_SZ + 10];
510
511 if (!smd_info)
512 return;
513
514 init_waitqueue_head(&smd_info->read_wait_q);
515 mutex_init(&smd_info->lock);
516 strlcpy(wq_name, "DIAG_SMD_", 10);
517 strlcat(wq_name, smd_info->name, sizeof(smd_info->name));
518 smd_info->wq = create_singlethread_workqueue(wq_name);
519 if (!smd_info->wq) {
520 pr_err("diag: In %s, unable to create workqueue for smd channel %s\n",
521 __func__, smd_info->name);
522 return;
523 }
524 INIT_WORK(&(smd_info->open_work), smd_open_work_fn);
525 INIT_WORK(&(smd_info->close_work), smd_close_work_fn);
526 INIT_WORK(&(smd_info->read_work), smd_read_work_fn);
527 INIT_WORK(&(smd_info->late_init_work), diag_smd_late_init_work_fn);
528 smd_info->fifo_size = 0;
529 smd_info->hdl = NULL;
530 smd_info->fwd_ctxt = NULL;
531 atomic_set(&smd_info->opened, 0);
532 atomic_set(&smd_info->diag_state, 0);
533
534 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s initialized fwd_ctxt: %pK\n",
535 smd_info->name, smd_info->fwd_ctxt);
536}
537
538int diag_smd_init(void)
539{
540 uint8_t peripheral;
541 struct diag_smd_info *smd_info = NULL;
542
543 for (peripheral = 0; peripheral < NUM_PERIPHERALS; peripheral++) {
544 if (peripheral == PERIPHERAL_WDSP)
545 continue;
546 smd_info = &smd_cntl[peripheral];
547 __diag_smd_init(smd_info);
548 diagfwd_cntl_register(TRANSPORT_SMD, smd_info->peripheral,
549 (void *)smd_info, &smd_ops,
550 &smd_info->fwd_ctxt);
551 smd_info->inited = 1;
552 __diag_smd_init(&smd_data[peripheral]);
553 __diag_smd_init(&smd_cmd[peripheral]);
554 __diag_smd_init(&smd_dci[peripheral]);
555 __diag_smd_init(&smd_dci_cmd[peripheral]);
556 }
557
558 platform_driver_register(&diag_smd_cntl_driver);
559 platform_driver_register(&diag_smd_lite_cntl_driver);
560 platform_driver_register(&diag_smd_ch_driver);
561 platform_driver_register(&diag_smd_lite_driver);
562 platform_driver_register(&diag_smd_lite_cmd_driver);
563 platform_driver_register(&diag_smd_dci_driver);
564 platform_driver_register(&diag_smd_dci_cmd_driver);
565
566 return 0;
567}
568
569static void smd_late_init(struct diag_smd_info *smd_info)
570{
571 struct diagfwd_info *fwd_info = NULL;
572
573 if (!smd_info)
574 return;
575
576 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s entering\n",
577 smd_info->name);
578
579 diagfwd_register(TRANSPORT_SMD, smd_info->peripheral, smd_info->type,
580 (void *)smd_info, &smd_ops, &smd_info->fwd_ctxt);
581 fwd_info = smd_info->fwd_ctxt;
582 smd_info->inited = 1;
583 /*
584 * The channel is already open by the probe call as a result of other
585 * peripheral. Inform the diag fwd layer that the channel is open.
586 */
587 if (atomic_read(&smd_info->opened))
588 queue_work(smd_info->wq, &(smd_info->late_init_work));
589
590 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
591 smd_info->name);
592}
593
594int diag_smd_init_peripheral(uint8_t peripheral)
595{
596 if (peripheral >= NUM_PERIPHERALS) {
597 pr_err("diag: In %s, invalid peripheral %d\n",
598 __func__, peripheral);
599 return -EINVAL;
600 }
601
602 smd_late_init(&smd_data[peripheral]);
603 smd_late_init(&smd_dci[peripheral]);
604 smd_late_init(&smd_cmd[peripheral]);
605 smd_late_init(&smd_dci_cmd[peripheral]);
606
607 return 0;
608}
609
610static void __diag_smd_exit(struct diag_smd_info *smd_info)
611{
612 if (!smd_info)
613 return;
614
615 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s entering\n",
616 smd_info->name);
617
618 diagfwd_deregister(smd_info->peripheral, smd_info->type,
619 (void *)smd_info);
620 smd_info->fwd_ctxt = NULL;
621 smd_info->hdl = NULL;
622 if (smd_info->wq)
623 destroy_workqueue(smd_info->wq);
624
625 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s exiting\n",
626 smd_info->name);
627}
628
629void diag_smd_early_exit(void)
630{
631 int i = 0;
632
633 for (i = 0; i < NUM_PERIPHERALS; i++) {
634 if (i == PERIPHERAL_WDSP)
635 continue;
636 __diag_smd_exit(&smd_cntl[i]);
637 }
638
639 platform_driver_unregister(&diag_smd_cntl_driver);
640 platform_driver_unregister(&diag_smd_lite_cntl_driver);
641}
642
643void diag_smd_exit(void)
644{
645 int i = 0;
646
647 for (i = 0; i < NUM_PERIPHERALS; i++) {
648 if (i == PERIPHERAL_WDSP)
649 continue;
650 __diag_smd_exit(&smd_data[i]);
651 __diag_smd_exit(&smd_cmd[i]);
652 __diag_smd_exit(&smd_dci[i]);
653 __diag_smd_exit(&smd_dci_cmd[i]);
654 }
655
656 platform_driver_unregister(&diag_smd_ch_driver);
657 platform_driver_unregister(&diag_smd_lite_driver);
658 platform_driver_unregister(&diag_smd_lite_cmd_driver);
659 platform_driver_unregister(&diag_smd_dci_driver);
660 platform_driver_unregister(&diag_smd_dci_cmd_driver);
661}
662
663static int diag_smd_write_ext(struct diag_smd_info *smd_info,
664 unsigned char *buf, int len)
665{
666 int err = 0;
667 int offset = 0;
668 int write_len = 0;
669 int retry_count = 0;
670 int max_retries = 3;
671 uint8_t avail = 0;
672
673 if (!smd_info || !buf || len <= 0) {
674 pr_err_ratelimited("diag: In %s, invalid params, smd_info: %pK, buf: %pK, len: %d\n",
675 __func__, smd_info, buf, len);
676 return -EINVAL;
677 }
678
679 if (!smd_info->inited || !smd_info->hdl ||
680 !atomic_read(&smd_info->opened))
681 return -ENODEV;
682
683 mutex_lock(&smd_info->lock);
684 err = smd_write_start(smd_info->hdl, len);
685 if (err) {
686 pr_err_ratelimited("diag: In %s, error calling smd_write_start, peripheral: %d, err: %d\n",
687 __func__, smd_info->peripheral, err);
688 goto fail;
689 }
690
691 while (offset < len) {
692 retry_count = 0;
693 do {
694 if (smd_write_segment_avail(smd_info->hdl)) {
695 avail = 1;
696 break;
697 }
698 /*
699 * The channel maybe busy - the FIFO can be full. Retry
700 * after sometime. The value of 10000 was chosen
701 * emprically as the optimal value for the peripherals
702 * to read data from the SMD channel.
703 */
704 usleep_range(10000, 10100);
705 retry_count++;
706 } while (retry_count < max_retries);
707
708 if (!avail) {
709 err = -EAGAIN;
710 goto fail;
711 }
712
713 write_len = smd_write_segment(smd_info->hdl, buf + offset,
714 (len - offset));
715 offset += write_len;
716 write_len = 0;
717 }
718
719 err = smd_write_end(smd_info->hdl);
720 if (err) {
721 pr_err_ratelimited("diag: In %s, error calling smd_write_end, peripheral: %d, err: %d\n",
722 __func__, smd_info->peripheral, err);
723 goto fail;
724 }
725
726fail:
727 mutex_unlock(&smd_info->lock);
728 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
729 "%s wrote to channel, write_len: %d, err: %d\n",
730 smd_info->name, offset, err);
731 return err;
732}
733
734static int diag_smd_write(void *ctxt, unsigned char *buf, int len)
735{
736 int write_len = 0;
737 int retry_count = 0;
738 int max_retries = 3;
739 struct diag_smd_info *smd_info = NULL;
740
741 if (!ctxt || !buf)
742 return -EIO;
743
744 smd_info = (struct diag_smd_info *)ctxt;
745 if (!smd_info || !buf || len <= 0) {
746 pr_err_ratelimited("diag: In %s, invalid params, smd_info: %pK, buf: %pK, len: %d\n",
747 __func__, smd_info, buf, len);
748 return -EINVAL;
749 }
750
751 if (!smd_info->inited || !smd_info->hdl ||
752 !atomic_read(&smd_info->opened))
753 return -ENODEV;
754
755 if (len > smd_info->fifo_size)
756 return diag_smd_write_ext(smd_info, buf, len);
757
758 do {
759 mutex_lock(&smd_info->lock);
760 write_len = smd_write(smd_info->hdl, buf, len);
761 mutex_unlock(&smd_info->lock);
762 if (write_len == len)
763 break;
764 /*
765 * The channel maybe busy - the FIFO can be full. Retry after
766 * sometime. The value of 10000 was chosen emprically as the
767 * optimal value for the peripherals to read data from the SMD
768 * channel.
769 */
770 usleep_range(10000, 10100);
771 retry_count++;
772 } while (retry_count < max_retries);
773
774 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s wrote to channel, write_len: %d\n",
775 smd_info->name, write_len);
776
777 if (write_len != len)
778 return -ENOMEM;
779
780 return 0;
781}
782
783static int diag_smd_read(void *ctxt, unsigned char *buf, int buf_len)
784{
785 int pkt_len = 0;
786 int err = 0;
787 int total_recd_partial = 0;
788 int total_recd = 0;
789 uint8_t buf_full = 0;
790 unsigned char *temp_buf = NULL;
791 uint32_t read_len = 0;
792 struct diag_smd_info *smd_info = NULL;
793
794 if (!ctxt || !buf || buf_len <= 0)
795 return -EIO;
796
797 smd_info = (struct diag_smd_info *)ctxt;
798 if (!smd_info->hdl || !smd_info->inited ||
799 !atomic_read(&smd_info->opened))
800 return -EIO;
801
802 /*
803 * Always try to read the data if notification is received from smd
804 * In case if packet size is 0 release the wake source hold earlier
805 */
806 err = wait_event_interruptible(smd_info->read_wait_q,
807 (smd_info->hdl != NULL) &&
808 (atomic_read(&smd_info->opened) == 1));
809 if (err) {
810 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
811 return -ERESTARTSYS;
812 }
813
814 /*
815 * Reset the buffers. Also release the wake source hold earlier.
816 */
817 if (atomic_read(&smd_info->diag_state) == 0) {
818 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
819 "%s closing read thread. diag state is closed\n",
820 smd_info->name);
821 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
822 return 0;
823 }
824
825 if (!smd_info->hdl || !atomic_read(&smd_info->opened)) {
826 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
827 "%s stopping read, hdl: %pK, opened: %d\n",
828 smd_info->name, smd_info->hdl,
829 atomic_read(&smd_info->opened));
830 goto fail_return;
831 }
832
833 do {
834 total_recd_partial = 0;
835 temp_buf = buf + total_recd;
836 pkt_len = smd_cur_packet_size(smd_info->hdl);
837 if (pkt_len <= 0)
838 break;
839
840 if (total_recd + pkt_len > buf_len) {
841 buf_full = 1;
842 break;
843 }
844
845 while (total_recd_partial < pkt_len) {
846 read_len = smd_read_avail(smd_info->hdl);
847 if (!read_len) {
848 err = wait_event_interruptible(smd_info->
849 read_wait_q,
850 ((atomic_read(&smd_info->opened)) &&
851 smd_read_avail(smd_info->hdl)));
852 if (err)
853 goto fail_return;
854
855 if (!smd_info->hdl ||
856 !atomic_read(&smd_info->opened)) {
857 DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
858 "%s exiting from wait",
859 smd_info->name);
860 goto fail_return;
861 }
862 }
863
864 if (pkt_len < read_len)
865 goto fail_return;
866
867 smd_read(smd_info->hdl, temp_buf, read_len);
868 total_recd_partial += read_len;
869 total_recd += read_len;
870 temp_buf += read_len;
871 }
872 } while (pkt_len > 0);
873
874 if ((smd_info->type == TYPE_DATA && pkt_len) || buf_full)
875 err = queue_work(smd_info->wq, &(smd_info->read_work));
876
877 if (total_recd > 0) {
878 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s read total bytes: %d\n",
879 smd_info->name, total_recd);
880 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, total_recd);
881 } else {
882 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s error in read, err: %d\n",
883 smd_info->name, total_recd);
884 goto fail_return;
885 }
886 return 0;
887
888fail_return:
889 diagfwd_channel_read_done(smd_info->fwd_ctxt, buf, 0);
890 return -EINVAL;
891}
892
893static void smd_notify(void *ctxt, unsigned int event)
894{
895 struct diag_smd_info *smd_info = NULL;
896
897 smd_info = (struct diag_smd_info *)ctxt;
898 if (!smd_info)
899 return;
900
901 switch (event) {
902 case SMD_EVENT_OPEN:
903 atomic_set(&smd_info->opened, 1);
904 smd_info->fifo_size = smd_write_avail(smd_info->hdl);
905 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s channel opened\n",
906 smd_info->name);
907 queue_work(smd_info->wq, &(smd_info->open_work));
908 break;
909 case SMD_EVENT_CLOSE:
910 atomic_set(&smd_info->opened, 0);
911 DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "%s channel closed\n",
912 smd_info->name);
913 queue_work(smd_info->wq, &(smd_info->close_work));
914 break;
915 case SMD_EVENT_DATA:
916 diag_ws_on_notify();
917 queue_work(smd_info->wq, &(smd_info->read_work));
918 break;
919 }
920
921 wake_up_interruptible(&smd_info->read_wait_q);
922}
923