blob: 242c66e6e90e0fd7c7a1624a37991b9f4650cb45 [file] [log] [blame]
Eric Holmberg8ed30f22012-05-10 19:16:51 -06001/* drivers/tty/smux_test.c
2 *
3 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15#include <linux/debugfs.h>
16#include <linux/list.h>
17#include <linux/ctype.h>
18#include <linux/jiffies.h>
19#include <linux/slab.h>
20#include <linux/delay.h>
21#include <linux/completion.h>
22#include <linux/termios.h>
23#include <linux/smux.h>
24#include "smux_private.h"
25
26#define DEBUG_BUFMAX 4096
27
28/**
29 * Unit test assertion for logging test cases.
30 *
31 * @a lval
32 * @b rval
33 * @cmp comparison operator
34 *
35 * Assertion fails if (@a cmp @b) is not true which then
36 * logs the function and line number where the error occurred
37 * along with the values of @a and @b.
38 *
39 * Assumes that the following local variables exist:
40 * @buf - buffer to write failure message to
41 * @i - number of bytes written to buffer
42 * @max - maximum size of the buffer
43 * @failed - set to true if test fails
44 */
45#define UT_ASSERT_INT(a, cmp, b) \
46 if (!((a)cmp(b))) { \
47 i += scnprintf(buf + i, max - i, \
48 "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
49 __func__, __LINE__, \
50 a, b); \
51 failed = 1; \
52 break; \
53 } \
54 do {} while (0)
55
56#define UT_ASSERT_PTR(a, cmp, b) \
57 if (!((a)cmp(b))) { \
58 i += scnprintf(buf + i, max - i, \
59 "%s:%d Fail: " #a "(%p) " #cmp " " #b "(%p)\n", \
60 __func__, __LINE__, \
61 a, b); \
62 failed = 1; \
63 break; \
64 } \
65 do {} while (0)
66
67#define UT_ASSERT_UINT(a, cmp, b) \
68 if (!((a)cmp(b))) { \
69 i += scnprintf(buf + i, max - i, \
70 "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
71 __func__, __LINE__, \
72 a, b); \
73 failed = 1; \
74 break; \
75 } \
76 do {} while (0)
77
78static unsigned char test_array[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55,
79 89, 144, 233};
80
81/* Used for mapping local to remote TIOCM signals */
82struct tiocm_test_vector {
83 uint32_t input;
84 uint32_t set_old;
85 uint32_t set_new;
86 uint32_t clr_old;
87};
88
89/**
90 * Allocates a new buffer for SMUX for every call.
91 */
92int get_rx_buffer(void *priv, void **pkt_priv, void **buffer, int size)
93{
94 void *rx_buf;
95
96 rx_buf = kmalloc(size, GFP_ATOMIC);
97 *pkt_priv = (void *)0x1234;
98 *buffer = rx_buf;
99
100 return 0;
101}
102
103/* Test vector for packet tests. */
104struct test_vector {
105 const char *data;
106 const unsigned len;
107};
108
109/* Mock object metadata for SMUX_READ_DONE event */
110struct mock_read_event {
111 struct list_head list;
112 struct smux_meta_read meta;
113};
114
115/* Mock object metadata for SMUX_WRITE_DONE event */
116struct mock_write_event {
117 struct list_head list;
118 struct smux_meta_write meta;
119};
120
121/* Mock object for all SMUX callback events */
122struct smux_mock_callback {
123 int cb_count;
124 struct completion cb_completion;
125 spinlock_t lock;
126
127 /* status changes */
128 int event_connected;
129 int event_disconnected;
130 int event_disconnected_ssr;
131 int event_low_wm;
132 int event_high_wm;
133
134 /* TIOCM changes */
135 int event_tiocm;
136 struct smux_meta_tiocm tiocm_meta;
137
138 /* read event data */
139 int event_read_done;
140 int event_read_failed;
141 struct list_head read_events;
142
143 /* write event data */
144 int event_write_done;
145 int event_write_failed;
146 struct list_head write_events;
147};
148
149/**
150 * Initialize mock callback data. Only call once.
151 *
152 * @cb Mock callback data
153 */
154void mock_cb_data_init(struct smux_mock_callback *cb)
155{
156 init_completion(&cb->cb_completion);
157 spin_lock_init(&cb->lock);
158 INIT_LIST_HEAD(&cb->read_events);
159 INIT_LIST_HEAD(&cb->write_events);
160}
161
162/**
163 * Reset mock callback data to default values.
164 *
165 * @cb Mock callback data
166 *
167 * All packets are freed and counters reset to zero.
168 */
169void mock_cb_data_reset(struct smux_mock_callback *cb)
170{
171 cb->cb_count = 0;
172 INIT_COMPLETION(cb->cb_completion);
173 cb->event_connected = 0;
174 cb->event_disconnected = 0;
175 cb->event_disconnected_ssr = 0;
176 cb->event_low_wm = 0;
177 cb->event_high_wm = 0;
178 cb->event_tiocm = 0;
179 cb->tiocm_meta.tiocm_old = 0;
180 cb->tiocm_meta.tiocm_new = 0;
181
182 cb->event_read_done = 0;
183 cb->event_read_failed = 0;
184 while (!list_empty(&cb->read_events)) {
185 struct mock_read_event *meta;
186 meta = list_first_entry(&cb->read_events,
187 struct mock_read_event,
188 list);
189 kfree(meta->meta.buffer);
190 list_del(&meta->list);
191 kfree(meta);
192 }
193
194 cb->event_write_done = 0;
195 cb->event_write_failed = 0;
196 while (!list_empty(&cb->write_events)) {
197 struct mock_write_event *meta;
198 meta = list_first_entry(&cb->write_events,
199 struct mock_write_event,
200 list);
201 list_del(&meta->list);
202 kfree(meta);
203 }
204}
205
206/**
207 * Dump the values of the mock callback data for debug purposes.
208 *
209 * @cb Mock callback data
210 * @buf Print buffer
211 * @max Maximum number of characters to print
212 *
213 * @returns Number of characters added to buffer
214 */
215static int mock_cb_data_print(const struct smux_mock_callback *cb,
216 char *buf, int max)
217{
218 int i = 0;
219
220 i += scnprintf(buf + i, max - i,
221 "\tcb_count=%d\n"
222 "\tcb_completion.done=%d\n"
223 "\tevent_connected=%d\n"
224 "\tevent_disconnected=%d\n"
225 "\tevent_disconnected_ssr=%d\n"
226 "\tevent_low_wm=%d\n"
227 "\tevent_high_wm=%d\n"
228 "\tevent_tiocm=%d\n"
229 "\tevent_read_done=%d\n"
230 "\tevent_read_failed=%d\n"
231 "\tread_events=%d\n"
232 "\tevent_write_done=%d\n"
233 "\tevent_write_failed=%d\n"
234 "\twrite_events=%d\n",
235 cb->cb_count,
236 cb->cb_completion.done,
237 cb->event_connected,
238 cb->event_disconnected,
239 cb->event_disconnected_ssr,
240 cb->event_low_wm,
241 cb->event_high_wm,
242 cb->event_tiocm,
243 cb->event_read_done,
244 cb->event_read_failed,
245 !list_empty(&cb->read_events),
246 cb->event_write_done,
247 cb->event_write_failed,
248 list_empty(&cb->write_events)
249 );
250
251 return i;
252}
253
254/**
255 * Mock object event callback. Used to logs events for analysis in the unit
256 * tests.
257 */
258void smux_mock_cb(void *priv, int event, const void *metadata)
259{
260 struct smux_mock_callback *cb_data_ptr;
261 struct mock_write_event *write_event_meta;
262 struct mock_read_event *read_event_meta;
263 unsigned long flags;
264
265 cb_data_ptr = (struct smux_mock_callback *)priv;
266 if (cb_data_ptr == NULL) {
267 pr_err("%s: invalid private data\n", __func__);
268 return;
269 }
270
271 spin_lock_irqsave(&cb_data_ptr->lock, flags);
272 switch (event) {
273 case SMUX_CONNECTED:
274 ++cb_data_ptr->event_connected;
275 break;
276
277 case SMUX_DISCONNECTED:
278 ++cb_data_ptr->event_disconnected;
279 cb_data_ptr->event_disconnected_ssr =
280 ((struct smux_meta_disconnected *)metadata)->is_ssr;
281 break;
282
283 case SMUX_READ_DONE:
284 ++cb_data_ptr->event_read_done;
285 read_event_meta = kmalloc(sizeof(struct mock_read_event),
286 GFP_ATOMIC);
287 if (read_event_meta) {
288 read_event_meta->meta =
289 *(struct smux_meta_read *)metadata;
290 list_add_tail(&read_event_meta->list,
291 &cb_data_ptr->read_events);
292 }
293 break;
294
295 case SMUX_READ_FAIL:
296 ++cb_data_ptr->event_read_failed;
297 read_event_meta = kmalloc(sizeof(struct mock_read_event),
298 GFP_ATOMIC);
299 if (read_event_meta) {
300 read_event_meta->meta =
301 *(struct smux_meta_read *)metadata;
302 list_add_tail(&read_event_meta->list,
303 &cb_data_ptr->read_events);
304 }
305 break;
306
307 case SMUX_WRITE_DONE:
308 ++cb_data_ptr->event_write_done;
309 write_event_meta = kmalloc(sizeof(struct mock_write_event),
310 GFP_ATOMIC);
311 if (write_event_meta) {
312 write_event_meta->meta =
313 *(struct smux_meta_write *)metadata;
314 list_add_tail(&write_event_meta->list,
315 &cb_data_ptr->write_events);
316 }
317 break;
318
319 case SMUX_WRITE_FAIL:
320 ++cb_data_ptr->event_write_failed;
321 write_event_meta = kmalloc(sizeof(struct mock_write_event),
322 GFP_ATOMIC);
323 if (write_event_meta) {
324 write_event_meta->meta =
325 *(struct smux_meta_write *)metadata;
326 list_add_tail(&write_event_meta->list,
327 &cb_data_ptr->write_events);
328 }
329 break;
330
331 case SMUX_LOW_WM_HIT:
332 ++cb_data_ptr->event_low_wm;
333 break;
334
335 case SMUX_HIGH_WM_HIT:
336 ++cb_data_ptr->event_high_wm;
337 break;
338
339 case SMUX_TIOCM_UPDATE:
340 ++cb_data_ptr->event_tiocm;
341 cb_data_ptr->tiocm_meta = *(struct smux_meta_tiocm *)metadata;
342 break;
343
344 default:
345 pr_err("%s: unknown event %d\n", __func__, event);
346 };
347
348 ++cb_data_ptr->cb_count;
349 complete(&cb_data_ptr->cb_completion);
350 spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
351}
352
353/**
354 * Test Read/write usage.
355 *
356 * @buf Output buffer for failure/status messages
357 * @max Size of @buf
358 * @vectors Test vector data (must end with NULL item)
359 * @name Name of the test case for failure messages
360 *
361 * Perform a sanity test consisting of opening a port, writing test packet(s),
362 * reading the response(s), and closing the port.
363 *
364 * The port should already be configured to use either local or remote
365 * loopback.
366 */
367static int smux_ut_basic_core(char *buf, int max,
368 const struct test_vector *vectors,
369 const char *name)
370{
371 int i = 0;
372 int failed = 0;
373 static struct smux_mock_callback cb_data;
374 static int cb_initialized;
375 int ret;
376
377 if (!cb_initialized)
378 mock_cb_data_init(&cb_data);
379
380 mock_cb_data_reset(&cb_data);
381 while (!failed) {
382 struct mock_write_event *write_event;
383 struct mock_read_event *read_event;
384
385 /* open port */
386 ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
387 get_rx_buffer);
388 UT_ASSERT_INT(ret, ==, 0);
389 UT_ASSERT_INT(
390 (int)wait_for_completion_timeout(
391 &cb_data.cb_completion, HZ), >, 0);
392 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
393 UT_ASSERT_INT(cb_data.event_connected, ==, 1);
394 mock_cb_data_reset(&cb_data);
395
396 /* write, read, and verify the test vector data */
397 for (; vectors->data != NULL; ++vectors) {
398 const char *test_data = vectors->data;
399 const unsigned test_len = vectors->len;
400
401 i += scnprintf(buf + i, max - i,
402 "Writing vector %p len %d\n",
403 test_data, test_len);
404
405 /* write data */
406 msm_smux_write(SMUX_TEST_LCID, (void *)0xCAFEFACE,
407 test_data, test_len);
408 UT_ASSERT_INT(ret, ==, 0);
409 UT_ASSERT_INT(
410 (int)wait_for_completion_timeout(
411 &cb_data.cb_completion, HZ), >, 0);
412
413 /* wait for write and echo'd read to complete */
414 INIT_COMPLETION(cb_data.cb_completion);
415 if (cb_data.cb_count < 2)
416 UT_ASSERT_INT(
417 (int)wait_for_completion_timeout(
418 &cb_data.cb_completion, HZ),
419 >, 0);
420
421 UT_ASSERT_INT(cb_data.cb_count, >=, 1);
422 UT_ASSERT_INT(cb_data.event_write_done, ==, 1);
423 UT_ASSERT_INT(list_empty(&cb_data.write_events), ==, 0);
424
425 write_event = list_first_entry(&cb_data.write_events,
426 struct mock_write_event, list);
427 UT_ASSERT_PTR(write_event->meta.pkt_priv, ==,
428 (void *)0xCAFEFACE);
429 UT_ASSERT_PTR(write_event->meta.buffer, ==,
430 (void *)test_data);
431 UT_ASSERT_INT(write_event->meta.len, ==, test_len);
432
433 /* verify read event */
434 UT_ASSERT_INT(cb_data.event_read_done, ==, 1);
435 UT_ASSERT_INT(list_empty(&cb_data.read_events), ==, 0);
436 read_event = list_first_entry(&cb_data.read_events,
437 struct mock_read_event, list);
438 UT_ASSERT_PTR(read_event->meta.pkt_priv, ==,
439 (void *)0x1234);
440 UT_ASSERT_PTR(read_event->meta.buffer, !=, NULL);
441
442 if (read_event->meta.len != test_len ||
443 memcmp(read_event->meta.buffer,
444 test_data, test_len)) {
445 /* data mismatch */
446 char linebuff[80];
447
448 hex_dump_to_buffer(test_data, test_len,
449 16, 1, linebuff, sizeof(linebuff), 1);
450 i += scnprintf(buf + i, max - i,
451 "Expected:\n%s\n\n", linebuff);
452
453 hex_dump_to_buffer(read_event->meta.buffer,
454 read_event->meta.len,
455 16, 1, linebuff, sizeof(linebuff), 1);
456 i += scnprintf(buf + i, max - i,
457 "Actual:\n%s\n", linebuff);
458 failed = 1;
459 break;
460 }
461 mock_cb_data_reset(&cb_data);
462 }
463
464 /* close port */
465 ret = msm_smux_close(SMUX_TEST_LCID);
466 UT_ASSERT_INT(ret, ==, 0);
467 UT_ASSERT_INT(
468 (int)wait_for_completion_timeout(
469 &cb_data.cb_completion, HZ),
470 >, 0);
471 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
472 UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
473 UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
474 break;
475 }
476
477 if (!failed) {
478 i += scnprintf(buf + i, max - i, "\tOK\n");
479 } else {
480 pr_err("%s: Failed\n", name);
481 i += scnprintf(buf + i, max - i, "\tFailed\n");
482 i += mock_cb_data_print(&cb_data, buf + i, max - i);
483 msm_smux_close(SMUX_TEST_LCID);
484 }
485
486 mock_cb_data_reset(&cb_data);
487 return i;
488}
489
490/**
491 * Verify Basic Local Loopback Support
492 *
493 * Perform a sanity test consisting of opening a port in local loopback
494 * mode and writing a packet and reading the echo'd packet back.
495 */
496static int smux_ut_basic(char *buf, int max)
497{
498 const struct test_vector test_data[] = {
499 {"hello\0world\n", sizeof("hello\0world\n")},
500 {0, 0},
501 };
502 int i = 0;
503 int failed = 0;
504 int ret;
505
506 i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
507 while (!failed) {
508 /* enable loopback mode */
509 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
510 SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
511 UT_ASSERT_INT(ret, ==, 0);
512
513 i += smux_ut_basic_core(buf + i, max - i, test_data, __func__);
514 break;
515 }
516
517 if (failed) {
518 pr_err("%s: Failed\n", __func__);
519 i += scnprintf(buf + i, max - i, "\tFailed\n");
520 }
521 return i;
522}
523
524/**
525 * Verify Basic Remote Loopback Support
526 *
527 * Perform a sanity test consisting of opening a port in remote loopback
528 * mode and writing a packet and reading the echo'd packet back.
529 */
530static int smux_ut_remote_basic(char *buf, int max)
531{
532 const struct test_vector test_data[] = {
533 {"hello\0world\n", sizeof("hello\0world\n")},
534 {0, 0},
535 };
536 int i = 0;
537 int failed = 0;
538 int ret;
539
540 i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
541 while (!failed) {
542 /* enable remote mode */
543 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
544 SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
545 UT_ASSERT_INT(ret, ==, 0);
546
547 i += smux_ut_basic_core(buf + i, max - i, test_data, __func__);
548 break;
549 }
550
551 if (failed) {
552 pr_err("%s: Failed\n", __func__);
553 i += scnprintf(buf + i, max - i, "\tFailed\n");
554 }
555 return i;
556}
557
558/**
559 * Fill test pattern into provided buffer including an optional
560 * redzone 16 bytes before and 16 bytes after the buffer.
561 *
562 * buf ---------
563 * redzone
564 * --------- <- returned pointer
565 * data
566 * --------- <- returned pointer + len
567 * redzone
568 * ---------
569 *
570 * @buf Pointer to the buffer of size len or len+32 (redzone)
571 * @len Length of the *data* buffer (excluding 32-byte redzone)
572 * @redzone If true, adds redzone data
573 *
574 * @returns pointer to buffer (buf + 16 if redzone enabled)
575 */
576uint8_t *test_pattern_fill(char *buf, int len, int redzone)
577{
578 void *ret;
579 uint8_t ch;
580
581 ret = buf;
582 if (redzone) {
583 memset((char *)buf, 0xAB, 16);
584 memset((char *)buf + len, 0xBA, 16);
585 ret += 16;
586 }
587
588 /* fill with test pattern */
589 for (ch = 0; len > 0; --len, ++ch)
590 *buf++ = (char)ch;
591
592 return ret;
593}
594
595/**
596 * Verify test pattern generated by test_pattern_fill.
597 *
598 * @buf_ptr Pointer to buffer pointer
599 * @len Length of the *data* buffer (excluding 32-byte redzone)
600 * @redzone If true, verifies redzone and adjusts *buf_ptr
601 * @errmsg Buffer for error message
602 * @errmsg_max Size of error message buffer
603 *
604 * @returns 0 for success; length of error message otherwise
605 */
606unsigned test_pattern_verify(char **buf_ptr, int len, int redzone,
607 char *errmsg, int errmsg_max)
608{
609 int n;
610 int i = 0;
611 char linebuff[80];
612
613 if (redzone) {
614 *buf_ptr -= 16;
615
616 /* verify prefix redzone */
617 for (n = 0; n < 16; ++n) {
618 if (*buf_ptr[n] != 0xAB) {
619 hex_dump_to_buffer(*buf_ptr, 16,
620 16, 1, linebuff, sizeof(linebuff), 1);
621 i += scnprintf(errmsg + i, errmsg_max - i,
622 "Redzone violation: %s\n", linebuff);
623 break;
624 }
625 }
626
627 /* verify postfix redzone */
628 for (n = 0; n < 16; ++n) {
629 if (*buf_ptr[len + n] != 0xBA) {
630 hex_dump_to_buffer(&(*buf_ptr)[len], 16,
631 16, 1, linebuff, sizeof(linebuff), 1);
632 i += scnprintf(errmsg + i, errmsg_max - i,
633 "Redzone violation: %s\n", linebuff);
634 break;
635 }
636 }
637 }
638 return i;
639}
640
641/**
642 * Write a multiple packets in ascending size and verify packet is received
643 * correctly.
644 *
645 * @buf Buffer for status message
646 * @max Size of buffer
647 * @name Name of the test for error reporting
648 *
649 * @returns Number of bytes written to @buf
650 *
651 * Requires that the port already be opened and loopback mode is
652 * configured correctly (if required).
653 */
654static int smux_ut_loopback_big_pkt(char *buf, int max, const char *name)
655{
656 struct test_vector test_data[] = {
657 {0, 64},
658 {0, 128},
659 {0, 256},
660 {0, 512},
661 {0, 1024},
662 {0, 2048},
663 {0, 4096},
664 {0, 0},
665 };
666 int i = 0;
667 int failed = 0;
668 struct test_vector *tv;
669
670 /* generate test data */
671 for (tv = test_data; tv->len > 0; ++tv) {
672 tv->data = kmalloc(tv->len + 32, GFP_KERNEL);
673 pr_err("%s: allocating %p len %d\n",
674 __func__, tv->data, tv->len);
675 if (!tv->data) {
676 i += scnprintf(buf + i, max - i,
677 "%s: Unable to allocate %d bytes\n",
678 __func__, tv->len);
679 failed = 1;
680 goto out;
681 }
682 test_pattern_fill((uint8_t *)tv->data, tv->len, 1);
683 }
684
685 /* run test */
686 i += scnprintf(buf + i, max - i, "Running %s\n", name);
687 while (!failed) {
688 i += smux_ut_basic_core(buf + i, max - i, test_data, name);
689 break;
690 }
691
692out:
693 if (failed) {
694 pr_err("%s: Failed\n", name);
695 i += scnprintf(buf + i, max - i, "\tFailed\n");
696 }
697
698 for (tv = test_data; tv->len > 0; ++tv) {
699 if (!tv->data) {
700 i += test_pattern_verify((char **)&tv->data,
701 tv->len, 1, buf + i, max - i);
702 pr_err("%s: freeing %p len %d\n", __func__,
703 tv->data, tv->len);
704 kfree(tv->data);
705 }
706 }
707
708 return i;
709}
710
711/**
712 * Verify Large-packet Local Loopback Support.
713 *
714 * @buf Buffer for status message
715 * @max Size of buffer
716 *
717 * @returns Number of bytes written to @buf
718 *
719 * Open port in local loopback mode and write a multiple packets in ascending
720 * size and verify packet is received correctly.
721 */
722static int smux_ut_local_big_pkt(char *buf, int max)
723{
724 int i = 0;
725 int ret;
726
727 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
728 SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
729
730 if (ret == 0) {
731 smux_byte_loopback = SMUX_TEST_LCID;
732 i += smux_ut_loopback_big_pkt(buf, max, __func__);
733 smux_byte_loopback = 0;
734 } else {
735 i += scnprintf(buf + i, max - i,
736 "%s: Unable to set loopback mode\n",
737 __func__);
738 }
739
740 return i;
741}
742
743/**
744 * Verify Large-packet Remote Loopback Support.
745 *
746 * @buf Buffer for status message
747 * @max Size of buffer
748 *
749 * @returns Number of bytes written to @buf
750 *
751 * Open port in remote loopback mode and write a multiple packets in ascending
752 * size and verify packet is received correctly.
753 */
754static int smux_ut_remote_big_pkt(char *buf, int max)
755{
756 int i = 0;
757 int ret;
758
759 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
760 SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
761 if (ret == 0) {
762 i += smux_ut_loopback_big_pkt(buf, max, __func__);
763 } else {
764 i += scnprintf(buf + i, max - i,
765 "%s: Unable to set loopback mode\n",
766 __func__);
767 }
768
769 return i;
770}
771
772/**
773 * Verify set and get operations for each TIOCM bit.
774 *
775 * @buf Buffer for status message
776 * @max Size of buffer
777 * @name Name of the test for error reporting
778 *
779 * @returns Number of bytes written to @buf
780 */
781static int smux_ut_tiocm(char *buf, int max, const char *name)
782{
783 static struct smux_mock_callback cb_data;
784 static int cb_initialized;
785 static const struct tiocm_test_vector tiocm_vectors[] = {
786 /* bit to set, set old, set new, clear old */
787 {TIOCM_DTR, TIOCM_DTR, TIOCM_DTR | TIOCM_DSR, TIOCM_DSR},
788 {TIOCM_RTS, TIOCM_RTS, TIOCM_RTS | TIOCM_CTS, TIOCM_CTS},
789 {TIOCM_RI, 0x0, TIOCM_RI, TIOCM_RI},
790 {TIOCM_CD, 0x0, TIOCM_CD, TIOCM_CD},
791 };
792 int i = 0;
793 int failed = 0;
794 int n;
795 int ret;
796
797 i += scnprintf(buf + i, max - i, "Running %s\n", name);
798
799 if (!cb_initialized)
800 mock_cb_data_init(&cb_data);
801
802 mock_cb_data_reset(&cb_data);
803 while (!failed) {
804 /* open port */
805 ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
806 get_rx_buffer);
807 UT_ASSERT_INT(ret, ==, 0);
808 UT_ASSERT_INT(
809 (int)wait_for_completion_timeout(
810 &cb_data.cb_completion, HZ), >, 0);
811 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
812 UT_ASSERT_INT(cb_data.event_connected, ==, 1);
813 mock_cb_data_reset(&cb_data);
814
815 /* set and clear each TIOCM bit */
816 for (n = 0; n < ARRAY_SIZE(tiocm_vectors) && !failed; ++n) {
817 /* set signal and verify */
818 ret = msm_smux_tiocm_set(SMUX_TEST_LCID,
819 tiocm_vectors[n].input, 0x0);
820 UT_ASSERT_INT(ret, ==, 0);
821 UT_ASSERT_INT(
822 (int)wait_for_completion_timeout(
823 &cb_data.cb_completion, HZ), >, 0);
824 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
825 UT_ASSERT_INT(cb_data.event_tiocm, ==, 1);
826 UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_old, ==,
827 tiocm_vectors[n].set_old);
828 UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_new, ==,
829 tiocm_vectors[n].set_new);
830 mock_cb_data_reset(&cb_data);
831
832 /* clear signal and verify */
833 ret = msm_smux_tiocm_set(SMUX_TEST_LCID, 0x0,
834 tiocm_vectors[n].input);
835 UT_ASSERT_INT(ret, ==, 0);
836 UT_ASSERT_INT(
837 (int)wait_for_completion_timeout(
838 &cb_data.cb_completion, HZ),
839 >, 0);
840 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
841 UT_ASSERT_INT(cb_data.event_tiocm, ==, 1);
842 UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_old, ==,
843 tiocm_vectors[n].clr_old);
844 UT_ASSERT_INT(cb_data.tiocm_meta.tiocm_new, ==, 0x0);
845 mock_cb_data_reset(&cb_data);
846 }
847 if (failed)
848 break;
849
850 /* close port */
851 ret = msm_smux_close(SMUX_TEST_LCID);
852 UT_ASSERT_INT(ret, ==, 0);
853 UT_ASSERT_INT(
854 (int)wait_for_completion_timeout(
855 &cb_data.cb_completion, HZ),
856 >, 0);
857 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
858 UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
859 UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
860 break;
861 }
862
863 if (!failed) {
864 i += scnprintf(buf + i, max - i, "\tOK\n");
865 } else {
866 pr_err("%s: Failed\n", name);
867 i += scnprintf(buf + i, max - i, "\tFailed\n");
868 i += mock_cb_data_print(&cb_data, buf + i, max - i);
869 msm_smux_close(SMUX_TEST_LCID);
870 }
871
872 mock_cb_data_reset(&cb_data);
873 return i;
874}
875
876/**
877 * Verify TIOCM Status Bits for local loopback.
878 *
879 * @buf Buffer for status message
880 * @max Size of buffer
881 *
882 * @returns Number of bytes written to @buf
883 */
884static int smux_ut_local_tiocm(char *buf, int max)
885{
886 int i = 0;
887 int ret;
888
889 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
890 SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
891
892 if (ret == 0) {
893 smux_byte_loopback = SMUX_TEST_LCID;
894 i += smux_ut_tiocm(buf, max, __func__);
895 smux_byte_loopback = 0;
896 } else {
897 i += scnprintf(buf + i, max - i,
898 "%s: Unable to set loopback mode\n",
899 __func__);
900 }
901
902 return i;
903}
904
905/**
906 * Verify TIOCM Status Bits for remote loopback.
907 *
908 * @buf Buffer for status message
909 * @max Size of buffer
910 *
911 * @returns Number of bytes written to @buf
912 */
913static int smux_ut_remote_tiocm(char *buf, int max)
914{
915 int i = 0;
916 int ret;
917
918 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
919 SMUX_CH_OPTION_REMOTE_LOOPBACK, 0);
920 if (ret == 0) {
921 i += smux_ut_tiocm(buf, max, __func__);
922 } else {
923 i += scnprintf(buf + i, max - i,
924 "%s: Unable to set loopback mode\n",
925 __func__);
926 }
927
928 return i;
929}
930
931/**
932 * Verify High/Low Watermark notifications.
933 *
934 * @buf Buffer for status message
935 * @max Size of buffer
936 *
937 * @returns Number of bytes written to @buf
938 */
939static int smux_ut_local_wm(char *buf, int max)
940{
941 static struct smux_mock_callback cb_data;
942 static int cb_initialized;
943 int i = 0;
944 int failed = 0;
945 int ret;
946
947 i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
948 pr_err("%s", buf);
949
950 if (!cb_initialized)
951 mock_cb_data_init(&cb_data);
952
953 mock_cb_data_reset(&cb_data);
954 smux_byte_loopback = SMUX_TEST_LCID;
955 while (!failed) {
956 /* open port for loopback with TX disabled */
957 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
958 SMUX_CH_OPTION_LOCAL_LOOPBACK
959 | SMUX_CH_OPTION_REMOTE_TX_STOP,
960 0);
961 UT_ASSERT_INT(ret, ==, 0);
962
963 ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
964 get_rx_buffer);
965 UT_ASSERT_INT(ret, ==, 0);
966 UT_ASSERT_INT(
967 (int)wait_for_completion_timeout(
968 &cb_data.cb_completion, HZ), >, 0);
969 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
970 UT_ASSERT_INT(cb_data.event_connected, ==, 1);
971 mock_cb_data_reset(&cb_data);
972
973 /* transmit 4 packets and verify high-watermark notification */
974 ret = 0;
975 ret |= msm_smux_write(SMUX_TEST_LCID, (void *)1,
976 test_array, sizeof(test_array));
977 ret |= msm_smux_write(SMUX_TEST_LCID, (void *)2,
978 test_array, sizeof(test_array));
979 ret |= msm_smux_write(SMUX_TEST_LCID, (void *)3,
980 test_array, sizeof(test_array));
981 UT_ASSERT_INT(ret, ==, 0);
982 UT_ASSERT_INT(cb_data.cb_count, ==, 0);
983 UT_ASSERT_INT(cb_data.event_high_wm, ==, 0);
984
985 ret = msm_smux_write(SMUX_TEST_LCID, (void *)4,
986 test_array, sizeof(test_array));
987 UT_ASSERT_INT(ret, ==, 0);
988 UT_ASSERT_INT(
989 (int)wait_for_completion_timeout(
990 &cb_data.cb_completion, HZ),
991 >, 0);
992 UT_ASSERT_INT(cb_data.event_high_wm, ==, 1);
993 UT_ASSERT_INT(cb_data.event_low_wm, ==, 0);
994 mock_cb_data_reset(&cb_data);
995
996 /* exceed watermark and verify failure return value */
997 ret = msm_smux_write(SMUX_TEST_LCID, (void *)5,
998 test_array, sizeof(test_array));
999 UT_ASSERT_INT(ret, ==, -EAGAIN);
1000
1001 /* re-enable TX and verify low-watermark notification */
1002 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
1003 0, SMUX_CH_OPTION_REMOTE_TX_STOP);
1004 UT_ASSERT_INT(ret, ==, 0);
1005 while (cb_data.cb_count < 9) {
1006 UT_ASSERT_INT(
1007 (int)wait_for_completion_timeout(
1008 &cb_data.cb_completion, HZ),
1009 >, 0);
1010 INIT_COMPLETION(cb_data.cb_completion);
1011 }
1012 if (failed)
1013 break;
1014
1015 UT_ASSERT_INT(cb_data.event_high_wm, ==, 0);
1016 UT_ASSERT_INT(cb_data.event_low_wm, ==, 1);
1017 UT_ASSERT_INT(cb_data.event_write_done, ==, 4);
1018 mock_cb_data_reset(&cb_data);
1019
1020 /* close port */
1021 ret = msm_smux_close(SMUX_TEST_LCID);
1022 UT_ASSERT_INT(ret, ==, 0);
1023 UT_ASSERT_INT(
1024 (int)wait_for_completion_timeout(
1025 &cb_data.cb_completion, HZ),
1026 >, 0);
1027 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
1028 UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
1029 UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
1030 break;
1031 }
1032
1033 if (!failed) {
1034 i += scnprintf(buf + i, max - i, "\tOK\n");
1035 } else {
1036 pr_err("%s: Failed\n", __func__);
1037 i += scnprintf(buf + i, max - i, "\tFailed\n");
1038 i += mock_cb_data_print(&cb_data, buf + i, max - i);
1039 msm_smux_close(SMUX_TEST_LCID);
1040 }
1041 smux_byte_loopback = 0;
1042 mock_cb_data_reset(&cb_data);
1043 return i;
1044}
1045
1046/**
1047 * Verify smuxld_receive_buf regular and error processing.
1048 *
1049 * @buf Buffer for status message
1050 * @max Size of buffer
1051 *
1052 * @returns Number of bytes written to @buf
1053 */
1054static int smux_ut_local_smuxld_receive_buf(char *buf, int max)
1055{
1056 static struct smux_mock_callback cb_data;
1057 static int cb_initialized;
1058 struct mock_read_event *meta;
1059 int i = 0;
1060 int failed = 0;
1061 int ret;
1062 char data[] = {SMUX_UT_ECHO_REQ,
1063 SMUX_UT_ECHO_REQ, SMUX_UT_ECHO_REQ,
1064 };
1065 char flags[] = {0x0, 0x1, 0x0,};
1066
1067
1068 i += scnprintf(buf + i, max - i, "Running %s\n", __func__);
1069
1070 if (!cb_initialized)
1071 mock_cb_data_init(&cb_data);
1072
1073 mock_cb_data_reset(&cb_data);
1074 smux_byte_loopback = SMUX_TEST_LCID;
1075 while (!failed) {
1076 /* open port for loopback */
1077 ret = msm_smux_set_ch_option(SMUX_TEST_LCID,
1078 SMUX_CH_OPTION_LOCAL_LOOPBACK, 0);
1079 UT_ASSERT_INT(ret, ==, 0);
1080
1081 ret = msm_smux_open(SMUX_TEST_LCID, &cb_data, smux_mock_cb,
1082 get_rx_buffer);
1083 UT_ASSERT_INT(ret, ==, 0);
1084 UT_ASSERT_INT(
1085 (int)wait_for_completion_timeout(
1086 &cb_data.cb_completion, HZ), >, 0);
1087 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
1088 UT_ASSERT_INT(cb_data.event_connected, ==, 1);
1089 mock_cb_data_reset(&cb_data);
1090
1091 /*
1092 * Verify RX error processing by sending 3 echo requests:
1093 * one OK, one fail, and a final OK
1094 *
1095 * The parsing framework should process the requests
1096 * and send us three BYTE command packets with
1097 * ECHO ACK FAIL and ECHO ACK OK characters.
1098 */
1099 smuxld_receive_buf(0, data, flags, sizeof(data));
1100
1101 /* verify response characters */
1102 do {
1103 UT_ASSERT_INT(
1104 (int)wait_for_completion_timeout(
1105 &cb_data.cb_completion, HZ), >, 0);
1106 INIT_COMPLETION(cb_data.cb_completion);
1107 } while (cb_data.cb_count < 3);
1108 UT_ASSERT_INT(cb_data.cb_count, ==, 3);
1109 UT_ASSERT_INT(cb_data.event_read_done, ==, 3);
1110
1111 meta = list_first_entry(&cb_data.read_events,
1112 struct mock_read_event, list);
1113 UT_ASSERT_INT((int)meta->meta.pkt_priv, ==,
1114 SMUX_UT_ECHO_ACK_OK);
1115 list_del(&meta->list);
1116
1117 meta = list_first_entry(&cb_data.read_events,
1118 struct mock_read_event, list);
1119 UT_ASSERT_INT((int)meta->meta.pkt_priv, ==,
1120 SMUX_UT_ECHO_ACK_FAIL);
1121 list_del(&meta->list);
1122
1123 meta = list_first_entry(&cb_data.read_events,
1124 struct mock_read_event, list);
1125 UT_ASSERT_INT((int)meta->meta.pkt_priv, ==,
1126 SMUX_UT_ECHO_ACK_OK);
1127 list_del(&meta->list);
1128 mock_cb_data_reset(&cb_data);
1129
1130 /* close port */
1131 ret = msm_smux_close(SMUX_TEST_LCID);
1132 UT_ASSERT_INT(ret, ==, 0);
1133 UT_ASSERT_INT(
1134 (int)wait_for_completion_timeout(
1135 &cb_data.cb_completion, HZ),
1136 >, 0);
1137 UT_ASSERT_INT(cb_data.cb_count, ==, 1);
1138 UT_ASSERT_INT(cb_data.event_disconnected, ==, 1);
1139 UT_ASSERT_INT(cb_data.event_disconnected_ssr, ==, 0);
1140 break;
1141 }
1142
1143 if (!failed) {
1144 i += scnprintf(buf + i, max - i, "\tOK\n");
1145 } else {
1146 pr_err("%s: Failed\n", __func__);
1147 i += scnprintf(buf + i, max - i, "\tFailed\n");
1148 i += mock_cb_data_print(&cb_data, buf + i, max - i);
1149 msm_smux_close(SMUX_TEST_LCID);
1150 }
1151 smux_byte_loopback = 0;
1152 mock_cb_data_reset(&cb_data);
1153 return i;
1154}
1155
1156static char debug_buffer[DEBUG_BUFMAX];
1157
1158static ssize_t debug_read(struct file *file, char __user *buf,
1159 size_t count, loff_t *ppos)
1160{
1161 int (*fill)(char *buf, int max) = file->private_data;
1162 int bsize;
1163
1164 if (*ppos != 0)
1165 return 0;
1166
1167 bsize = fill(debug_buffer, DEBUG_BUFMAX);
1168 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
1169}
1170
1171static int debug_open(struct inode *inode, struct file *file)
1172{
1173 file->private_data = inode->i_private;
1174 return 0;
1175}
1176
1177static const struct file_operations debug_ops = {
1178 .read = debug_read,
1179 .open = debug_open,
1180};
1181
1182static void debug_create(const char *name, mode_t mode,
1183 struct dentry *dent,
1184 int (*fill)(char *buf, int max))
1185{
1186 debugfs_create_file(name, mode, dent, fill, &debug_ops);
1187}
1188
1189static int __init smux_debugfs_init(void)
1190{
1191 struct dentry *dent;
1192
1193 dent = debugfs_create_dir("n_smux", 0);
1194 if (IS_ERR(dent))
1195 return PTR_ERR(dent);
1196
1197 /*
1198 * Add Unit Test entries.
1199 *
1200 * The idea with unit tests is that you can run all of them
1201 * from ADB shell by doing:
1202 * adb shell
1203 * cat ut*
1204 *
1205 * And if particular tests fail, you can then repeatedly run the failing
1206 * tests as you debug and resolve the failing test.
1207 */
1208 debug_create("ut_local_basic", 0444, dent, smux_ut_basic);
1209 debug_create("ut_remote_basic", 0444, dent, smux_ut_remote_basic);
1210 debug_create("ut_local_big_pkt", 0444, dent, smux_ut_local_big_pkt);
1211 debug_create("ut_remote_big_pkt", 0444, dent, smux_ut_remote_big_pkt);
1212 debug_create("ut_local_tiocm", 0444, dent, smux_ut_local_tiocm);
1213 debug_create("ut_remote_tiocm", 0444, dent, smux_ut_remote_tiocm);
1214 debug_create("ut_local_wm", 0444, dent, smux_ut_local_wm);
1215 debug_create("ut_local_smuxld_receive_buf", 0444, dent,
1216 smux_ut_local_smuxld_receive_buf);
1217
1218 return 0;
1219}
1220
1221late_initcall(smux_debugfs_init);
1222