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