1 /*
2 * Copyright (c) 2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/rtio/rtio.h>
9 #include <zephyr/sys/mpsc_lockfree.h>
10 #include <zephyr/kernel.h>
11
12 #ifndef RTIO_IODEV_TEST_H_
13 #define RTIO_IODEV_TEST_H_
14
15 struct rtio_iodev_test_data {
16 /* k_timer for an asynchronous task */
17 struct k_timer timer;
18
19 /* Queue of requests */
20 struct mpsc io_q;
21
22 /* Currently executing transaction */
23 struct rtio_iodev_sqe *txn_head;
24 struct rtio_iodev_sqe *txn_curr;
25
26 /* Count of submit calls */
27 atomic_t submit_count;
28
29 /* Lock around kicking off next timer */
30 struct k_spinlock lock;
31
32 /* Mocked result to receive by the IODEV */
33 int result;
34 };
35
rtio_iodev_test_next(struct rtio_iodev_test_data * data,bool completion)36 static void rtio_iodev_test_next(struct rtio_iodev_test_data *data, bool completion)
37 {
38 /* The next section must be serialized to ensure single consumer semantics */
39 k_spinlock_key_t key = k_spin_lock(&data->lock);
40
41 /* Already working on something, bail early */
42 if (!completion && data->txn_head != NULL) {
43 goto out;
44 }
45
46 struct mpsc_node *next = mpsc_pop(&data->io_q);
47
48 /* Nothing left to do, cleanup */
49 if (next == NULL) {
50 data->txn_head = NULL;
51 data->txn_curr = NULL;
52 goto out;
53 }
54
55 struct rtio_iodev_sqe *next_sqe = CONTAINER_OF(next, struct rtio_iodev_sqe, q);
56
57 data->txn_head = next_sqe;
58 data->txn_curr = next_sqe;
59 k_timer_start(&data->timer, K_MSEC(10), K_NO_WAIT);
60
61 out:
62 k_spin_unlock(&data->lock, key);
63 }
64
rtio_iodev_test_complete(struct rtio_iodev_test_data * data,int status)65 static void rtio_iodev_test_complete(struct rtio_iodev_test_data *data, int status)
66 {
67 if (status < 0) {
68 rtio_iodev_sqe_err(data->txn_head, status);
69 rtio_iodev_test_next(data, true);
70 return;
71 }
72
73 data->txn_curr = rtio_txn_next(data->txn_curr);
74 if (data->txn_curr) {
75 k_timer_start(&data->timer, K_MSEC(10), K_NO_WAIT);
76 return;
77 }
78
79 rtio_iodev_sqe_ok(data->txn_head, status);
80 rtio_iodev_test_next(data, true);
81 }
82
rtio_iodev_await_signaled(struct rtio_iodev_sqe * iodev_sqe,void * userdata)83 static void rtio_iodev_await_signaled(struct rtio_iodev_sqe *iodev_sqe, void *userdata)
84 {
85 struct rtio_iodev_test_data *data = userdata;
86
87 rtio_iodev_test_complete(data, data->result);
88 }
89
rtio_iodev_timer_fn(struct k_timer * tm)90 static void rtio_iodev_timer_fn(struct k_timer *tm)
91 {
92 struct rtio_iodev_test_data *data = CONTAINER_OF(tm, struct rtio_iodev_test_data, timer);
93 struct rtio_iodev_sqe *iodev_sqe = data->txn_curr;
94 uint8_t *buf;
95 uint32_t buf_len;
96 int rc;
97
98 switch (iodev_sqe->sqe.op) {
99 case RTIO_OP_NOP:
100 rtio_iodev_test_complete(data, data->result);
101 break;
102 case RTIO_OP_RX:
103 rc = rtio_sqe_rx_buf(iodev_sqe, 16, 16, &buf, &buf_len);
104 if (rc != 0) {
105 rtio_iodev_test_complete(data, rc);
106 return;
107 }
108 /* For reads the test device copies from the given userdata */
109 memcpy(buf, ((uint8_t *)iodev_sqe->sqe.userdata), 16);
110 rtio_iodev_test_complete(data, data->result);
111 break;
112 case RTIO_OP_AWAIT:
113 rtio_iodev_sqe_await_signal(iodev_sqe, rtio_iodev_await_signaled, data);
114 break;
115 default:
116 rtio_iodev_test_complete(data, -ENOTSUP);
117 }
118 }
119
rtio_iodev_test_submit(struct rtio_iodev_sqe * iodev_sqe)120 static void rtio_iodev_test_submit(struct rtio_iodev_sqe *iodev_sqe)
121 {
122 struct rtio_iodev *iodev = (struct rtio_iodev *)iodev_sqe->sqe.iodev;
123 struct rtio_iodev_test_data *data = iodev->data;
124
125 atomic_inc(&data->submit_count);
126
127 /* The only safe operation is enqueuing */
128 mpsc_push(&data->io_q, &iodev_sqe->q);
129
130 rtio_iodev_test_next(data, false);
131 }
132
133 const struct rtio_iodev_api rtio_iodev_test_api = {
134 .submit = rtio_iodev_test_submit,
135 };
136
rtio_iodev_test_init(struct rtio_iodev * test)137 void rtio_iodev_test_init(struct rtio_iodev *test)
138 {
139 struct rtio_iodev_test_data *data = test->data;
140
141 mpsc_init(&data->io_q);
142 data->txn_head = NULL;
143 data->txn_curr = NULL;
144 k_timer_init(&data->timer, rtio_iodev_timer_fn, NULL);
145 data->result = 0;
146 }
147
rtio_iodev_test_set_result(struct rtio_iodev * test,int result)148 void rtio_iodev_test_set_result(struct rtio_iodev *test, int result)
149 {
150 struct rtio_iodev_test_data *data = test->data;
151
152 data->result = result;
153 }
154
155 #define RTIO_IODEV_TEST_DEFINE(name) \
156 static struct rtio_iodev_test_data _iodev_data_##name; \
157 RTIO_IODEV_DEFINE(name, &rtio_iodev_test_api, &_iodev_data_##name)
158
159
160
161 #endif /* RTIO_IODEV_TEST_H_ */
162