1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/ztress.h>
9 #include <zephyr/ipc/pbuf.h>
10 #include <zephyr/random/random.h>
11
12
13 #define MEM_AREA_SZ 256
14 #define MPS 240
15 #define MSGA_SZ 11
16 #define MSGB_SZ 25
17
18 static char memory_area[MEM_AREA_SZ] __aligned(32);
19
print_pbuf_info(struct pbuf * pb)20 static void print_pbuf_info(struct pbuf *pb)
21 {
22 printk("----------stats start-----------\n");
23 printk("cfg->rd_idx_loc: %p, val: %u\n", pb->cfg->rd_idx_loc, *(pb->cfg->rd_idx_loc));
24 printk("cfg->wr_idx_loc: %p, val: %u\n", pb->cfg->wr_idx_loc, *(pb->cfg->wr_idx_loc));
25 printk("cfg->data_loc: %p\n", pb->cfg->data_loc);
26 printk("cfg->len: %u\n", pb->cfg->len);
27 printk("cfg->dcache_alignment: %u\n", pb->cfg->dcache_alignment);
28
29 printk("data.rd_idx: %u\n", pb->data.rd_idx);
30 printk("data.wr_idx: %u\n", pb->data.wr_idx);
31 printk("-----------stats end------------\n");
32 }
33
34 /* Read/write tests. */
ZTEST(test_pbuf,test_rw)35 ZTEST(test_pbuf, test_rw)
36 {
37 uint8_t read_buf[MEM_AREA_SZ] = {0};
38 uint8_t write_buf[MEM_AREA_SZ];
39 int ret;
40
41 BUILD_ASSERT(MSGA_SZ < MEM_AREA_SZ);
42 BUILD_ASSERT(MSGB_SZ < MEM_AREA_SZ);
43 BUILD_ASSERT(MPS < MEM_AREA_SZ);
44
45 /* TODO: Use PBUF_DEFINE().
46 * The user should use PBUF_DEFINE() macro to define the buffer,
47 * however for the purpose of this test PBUF_CFG_INIT() is used in
48 * order to avoid clang complains about memory_area not being constant
49 * expression.
50 */
51 static PBUF_MAYBE_CONST struct pbuf_cfg cfg = PBUF_CFG_INIT(memory_area, MEM_AREA_SZ, 0);
52
53 static struct pbuf pb = {
54 .cfg = &cfg,
55 };
56
57 for (size_t i = 0; i < MEM_AREA_SZ; i++) {
58 write_buf[i] = i+1;
59 }
60
61 zassert_equal(pbuf_tx_init(&pb), 0);
62
63 /* Write MSGA_SZ bytes packet. */
64 ret = pbuf_write(&pb, write_buf, MSGA_SZ);
65 zassert_equal(ret, MSGA_SZ);
66
67 /* Write MSGB_SZ bytes packet. */
68 ret = pbuf_write(&pb, write_buf+MSGA_SZ, MSGB_SZ);
69 zassert_equal(ret, MSGB_SZ);
70
71 /* Get the number of bytes stored. */
72 ret = pbuf_read(&pb, NULL, 0);
73 zassert_equal(ret, MSGA_SZ);
74 /* Attempt to read with too small read buffer. */
75 ret = pbuf_read(&pb, read_buf, ret-1);
76 zassert_equal(ret, -ENOMEM);
77 /* Read the packet. */
78 ret = pbuf_read(&pb, read_buf, ret);
79 zassert_equal(ret, MSGA_SZ);
80 /* Check data corectness. */
81 zassert_mem_equal(read_buf, write_buf, ret);
82
83 /* Get the number of bytes stored. */
84 ret = pbuf_read(&pb, NULL, 0);
85 zassert_equal(ret, MSGB_SZ);
86 /* Read the packet. */
87 ret = pbuf_read(&pb, read_buf, ret);
88 zassert_equal(ret, MSGB_SZ);
89 /* Check data corectness. */
90 zassert_mem_equal(read_buf, write_buf+MSGA_SZ, ret);
91
92 /* Get the number of bytes stored. */
93 ret = pbuf_read(&pb, NULL, 0);
94 zassert_equal(ret, 0);
95
96 /* Write max packet size with wrapping around. */
97 ret = pbuf_write(&pb, write_buf, MPS);
98 zassert_equal(ret, MPS);
99 /* Get the number of bytes stored. */
100 ret = pbuf_read(&pb, NULL, 0);
101 zassert_equal(ret, MPS);
102 /* Read max packet size with wrapp around. */
103 ret = pbuf_read(&pb, read_buf, ret);
104 zassert_equal(ret, MPS);
105 /* Check data corectness. */
106 zassert_mem_equal(write_buf, read_buf, MPS);
107 }
108
109 /* API ret codes tests. */
ZTEST(test_pbuf,test_retcodes)110 ZTEST(test_pbuf, test_retcodes)
111 {
112 /* TODO: Use PBUF_DEFINE().
113 * The user should use PBUF_DEFINE() macro to define the buffer,
114 * however for the purpose of this test PBUF_CFG_INIT() is used in
115 * order to avoid clang complains about memory_area not being constant
116 * expression.
117 */
118 static PBUF_MAYBE_CONST struct pbuf_cfg cfg0 = PBUF_CFG_INIT(memory_area, MEM_AREA_SZ, 32);
119 static PBUF_MAYBE_CONST struct pbuf_cfg cfg1 = PBUF_CFG_INIT(memory_area, MEM_AREA_SZ, 0);
120 static PBUF_MAYBE_CONST struct pbuf_cfg cfg2 = PBUF_CFG_INIT(memory_area, 20, 4);
121
122 static struct pbuf pb0 = {
123 .cfg = &cfg0,
124 };
125
126 static struct pbuf pb1 = {
127 .cfg = &cfg1,
128 };
129
130 static struct pbuf pb2 = {
131 .cfg = &cfg2,
132 };
133
134 /* Initialize buffers. */
135 zassert_equal(pbuf_tx_init(&pb0), 0);
136 zassert_equal(pbuf_tx_init(&pb1), 0);
137 zassert_equal(pbuf_tx_init(&pb2), 0);
138
139 print_pbuf_info(&pb0);
140 print_pbuf_info(&pb1);
141 print_pbuf_info(&pb2);
142
143 uint8_t read_buf[MEM_AREA_SZ];
144 uint8_t write_buf[MEM_AREA_SZ];
145
146 for (size_t i = 0; i < MEM_AREA_SZ; i++) {
147 write_buf[i] = i+1;
148 }
149
150 /* pbuf_write incorrect params tests. */
151 zassert_equal(pbuf_write(NULL, write_buf, 10), -EINVAL);
152 zassert_equal(pbuf_write(&pb2, NULL, 10), -EINVAL);
153 zassert_equal(pbuf_write(&pb2, write_buf, 0), -EINVAL);
154 zassert_equal(pbuf_read(NULL, read_buf, 10), -EINVAL);
155
156 /* Attempt to write more than the buffer can fit. */
157 zassert_equal(pbuf_write(&pb2, write_buf, 5), -ENOMEM);
158
159 /* Write maximal amount, the buffer fit. */
160 zassert_equal(pbuf_write(&pb2, write_buf, 4), 4);
161
162 /* Attempt to write to full buffer. */
163 zassert_equal(pbuf_write(&pb2, write_buf, 1), -ENOMEM);
164
165 /* Get the bytes stored. */
166 zassert_equal(pbuf_read(&pb2, NULL, 1), 4);
167
168 /* Attempt to read with too small read buffer. */
169 zassert_equal(pbuf_read(&pb2, read_buf, 1), -ENOMEM);
170
171 /* Get the bytes stored. */
172 zassert_equal(pbuf_read(&pb2, NULL, 0), 4);
173
174 /* Read the data with correct buffer size. */
175 zassert_equal(pbuf_read(&pb2, read_buf, 4), 4);
176
177 /* Check data correctness. */
178 zassert_mem_equal(read_buf, write_buf, 4);
179
180 /* Read from empty buffer. */
181 zassert_equal(pbuf_read(&pb2, read_buf, 10), 0);
182 zassert_equal(pbuf_read(&pb2, read_buf, 10), 0);
183 zassert_equal(pbuf_read(&pb2, read_buf, 10), 0);
184 }
185
186 #define STRESS_LEN_MOD (44)
187 #define STRESS_LEN_MIN (20)
188 #define STRESS_LEN_MAX (STRESS_LEN_MIN + STRESS_LEN_MOD)
189
190 struct stress_data {
191 struct pbuf *pbuf;
192 uint32_t wr_cnt;
193 uint32_t rd_cnt;
194 uint32_t wr_err;
195 };
196
197 /* Check if buffer of len contains exp values. */
check_buffer(char * buf,uint16_t len,char exp)198 static int check_buffer(char *buf, uint16_t len, char exp)
199 {
200 for (uint16_t i = 0; i < len; i++) {
201 if (buf[i] != exp) {
202 return -EINVAL;
203 }
204 }
205
206 return 0;
207 }
208
stress_read(void * user_data,uint32_t cnt,bool last,int prio)209 bool stress_read(void *user_data, uint32_t cnt, bool last, int prio)
210 {
211 struct stress_data *ctx = (struct stress_data *)user_data;
212 char buf[STRESS_LEN_MAX];
213 int len;
214 int rpt = (sys_rand8_get() & 3) + 1;
215
216 for (int i = 0; i < rpt; i++) {
217 len = pbuf_read(ctx->pbuf, buf, (uint16_t)sizeof(buf));
218 if (len == 0) {
219 return true;
220 }
221
222 if (len < 0) {
223 zassert_true(false, "Unexpected error: %d, cnt:%d", len, ctx->rd_cnt);
224 }
225
226 zassert_ok(check_buffer(buf, len, ctx->rd_cnt));
227 ctx->rd_cnt++;
228 }
229
230 return true;
231 }
232
stress_write(void * user_data,uint32_t cnt,bool last,int prio)233 bool stress_write(void *user_data, uint32_t cnt, bool last, int prio)
234 {
235 struct stress_data *ctx = (struct stress_data *)user_data;
236 char buf[STRESS_LEN_MAX];
237
238 uint16_t len = STRESS_LEN_MIN + (sys_rand8_get() % STRESS_LEN_MOD);
239 int rpt = (sys_rand8_get() & 1) + 1;
240
241 zassert_true(len < sizeof(buf));
242
243 for (int i = 0; i < rpt; i++) {
244 memset(buf, (uint8_t)ctx->wr_cnt, len);
245 int ret = pbuf_write(ctx->pbuf, buf, len);
246
247 if (ret == len) {
248 ctx->wr_cnt++;
249 } else if (ret == -ENOMEM) {
250 ctx->wr_err++;
251 } else {
252 zassert_unreachable();
253 }
254 }
255
256 return true;
257 }
258
ZTEST(test_pbuf,test_stress)259 ZTEST(test_pbuf, test_stress)
260 {
261 static uint8_t buffer[MEM_AREA_SZ] __aligned(32);
262 static struct stress_data ctx = {};
263 uint32_t repeat = 0;
264
265 /* TODO: Use PBUF_DEFINE().
266 * The user should use PBUF_DEFINE() macro to define the buffer,
267 * however for the purpose of this test PBUF_CFG_INIT() is used in
268 * order to avoid clang complains about buffer not being constant
269 * expression.
270 */
271 static PBUF_MAYBE_CONST struct pbuf_cfg cfg = PBUF_CFG_INIT(buffer, MEM_AREA_SZ, 4);
272
273 static struct pbuf pb = {
274 .cfg = &cfg,
275 };
276
277 zassert_equal(pbuf_tx_init(&pb), 0);
278 ctx.pbuf = &pb;
279 ctx.wr_cnt = 0;
280 ctx.rd_cnt = 0;
281
282 ztress_set_timeout(K_MSEC(1500));
283 TC_PRINT("Reading from an interrupt, writing from a thread\n");
284 ZTRESS_EXECUTE(ZTRESS_TIMER(stress_read, &ctx, repeat, Z_TIMEOUT_TICKS(4)),
285 ZTRESS_THREAD(stress_write, &ctx, repeat, 2000, Z_TIMEOUT_TICKS(4)));
286 TC_PRINT("Writes:%d unsuccessful: %d\n", ctx.wr_cnt, ctx.wr_err);
287
288 TC_PRINT("Writing from an interrupt, reading from a thread\n");
289 ZTRESS_EXECUTE(ZTRESS_TIMER(stress_write, &ctx, repeat, Z_TIMEOUT_TICKS(4)),
290 ZTRESS_THREAD(stress_read, &ctx, repeat, 1000, Z_TIMEOUT_TICKS(4)));
291 TC_PRINT("Writes:%d unsuccessful: %d\n", ctx.wr_cnt, ctx.wr_err);
292 }
293
294 ZTEST_SUITE(test_pbuf, NULL, NULL, NULL, NULL, NULL);
295