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, 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,
119 32, 0);
120 static PBUF_MAYBE_CONST struct pbuf_cfg cfg1 = PBUF_CFG_INIT(memory_area, MEM_AREA_SZ,
121 0, 0);
122 static PBUF_MAYBE_CONST struct pbuf_cfg cfg2 = PBUF_CFG_INIT(memory_area, 20, 4, 0);
123
124 static struct pbuf pb0 = {
125 .cfg = &cfg0,
126 };
127
128 static struct pbuf pb1 = {
129 .cfg = &cfg1,
130 };
131
132 static struct pbuf pb2 = {
133 .cfg = &cfg2,
134 };
135
136 /* Initialize buffers. */
137 zassert_equal(pbuf_tx_init(&pb0), 0);
138 zassert_equal(pbuf_tx_init(&pb1), 0);
139 zassert_equal(pbuf_tx_init(&pb2), 0);
140
141 print_pbuf_info(&pb0);
142 print_pbuf_info(&pb1);
143 print_pbuf_info(&pb2);
144
145 uint8_t read_buf[MEM_AREA_SZ];
146 uint8_t write_buf[MEM_AREA_SZ];
147
148 for (size_t i = 0; i < MEM_AREA_SZ; i++) {
149 write_buf[i] = i+1;
150 }
151
152 /* pbuf_write incorrect params tests. */
153 zassert_equal(pbuf_write(NULL, write_buf, 10), -EINVAL);
154 zassert_equal(pbuf_write(&pb2, NULL, 10), -EINVAL);
155 zassert_equal(pbuf_write(&pb2, write_buf, 0), -EINVAL);
156 zassert_equal(pbuf_read(NULL, read_buf, 10), -EINVAL);
157
158 /* Attempt to write more than the buffer can fit. */
159 zassert_equal(pbuf_write(&pb2, write_buf, 5), -ENOMEM);
160
161 /* Write maximal amount, the buffer fit. */
162 zassert_equal(pbuf_write(&pb2, write_buf, 4), 4);
163
164 /* Attempt to write to full buffer. */
165 zassert_equal(pbuf_write(&pb2, write_buf, 1), -ENOMEM);
166
167 /* Get the bytes stored. */
168 zassert_equal(pbuf_read(&pb2, NULL, 1), 4);
169
170 /* Attempt to read with too small read buffer. */
171 zassert_equal(pbuf_read(&pb2, read_buf, 1), -ENOMEM);
172
173 /* Get the bytes stored. */
174 zassert_equal(pbuf_read(&pb2, NULL, 0), 4);
175
176 /* Read the data with correct buffer size. */
177 zassert_equal(pbuf_read(&pb2, read_buf, 4), 4);
178
179 /* Check data correctness. */
180 zassert_mem_equal(read_buf, write_buf, 4);
181
182 /* Read from empty buffer. */
183 zassert_equal(pbuf_read(&pb2, read_buf, 10), 0);
184 zassert_equal(pbuf_read(&pb2, read_buf, 10), 0);
185 zassert_equal(pbuf_read(&pb2, read_buf, 10), 0);
186 }
187
188 #define STRESS_LEN_MOD (44)
189 #define STRESS_LEN_MIN (20)
190 #define STRESS_LEN_MAX (STRESS_LEN_MIN + STRESS_LEN_MOD)
191
192 struct stress_data {
193 struct pbuf *pbuf;
194 uint32_t wr_cnt;
195 uint32_t rd_cnt;
196 uint32_t wr_err;
197 };
198
199 /* Check if buffer of len contains exp values. */
check_buffer(char * buf,uint16_t len,char exp)200 static int check_buffer(char *buf, uint16_t len, char exp)
201 {
202 for (uint16_t i = 0; i < len; i++) {
203 if (buf[i] != exp) {
204 return -EINVAL;
205 }
206 }
207
208 return 0;
209 }
210
stress_read(void * user_data,uint32_t cnt,bool last,int prio)211 bool stress_read(void *user_data, uint32_t cnt, bool last, int prio)
212 {
213 struct stress_data *ctx = (struct stress_data *)user_data;
214 char buf[STRESS_LEN_MAX];
215 int len;
216 int rpt = (sys_rand8_get() & 3) + 1;
217
218 for (int i = 0; i < rpt; i++) {
219 len = pbuf_read(ctx->pbuf, buf, (uint16_t)sizeof(buf));
220 if (len == 0) {
221 return true;
222 }
223
224 if (len < 0) {
225 zassert_true(false, "Unexpected error: %d, cnt:%d", len, ctx->rd_cnt);
226 }
227
228 zassert_ok(check_buffer(buf, len, ctx->rd_cnt));
229 ctx->rd_cnt++;
230 }
231
232 return true;
233 }
234
stress_write(void * user_data,uint32_t cnt,bool last,int prio)235 bool stress_write(void *user_data, uint32_t cnt, bool last, int prio)
236 {
237 struct stress_data *ctx = (struct stress_data *)user_data;
238 char buf[STRESS_LEN_MAX];
239
240 uint16_t len = STRESS_LEN_MIN + (sys_rand8_get() % STRESS_LEN_MOD);
241 int rpt = (sys_rand8_get() & 1) + 1;
242
243 zassert_true(len < sizeof(buf));
244
245 for (int i = 0; i < rpt; i++) {
246 memset(buf, (uint8_t)ctx->wr_cnt, len);
247 int ret = pbuf_write(ctx->pbuf, buf, len);
248
249 if (ret == len) {
250 ctx->wr_cnt++;
251 } else if (ret == -ENOMEM) {
252 ctx->wr_err++;
253 } else {
254 zassert_unreachable();
255 }
256 }
257
258 return true;
259 }
260
ZTEST(test_pbuf,test_stress)261 ZTEST(test_pbuf, test_stress)
262 {
263 static uint8_t buffer[MEM_AREA_SZ] __aligned(32);
264 static struct stress_data ctx = {};
265 uint32_t repeat = 0;
266
267 /* TODO: Use PBUF_DEFINE().
268 * The user should use PBUF_DEFINE() macro to define the buffer,
269 * however for the purpose of this test PBUF_CFG_INIT() is used in
270 * order to avoid clang complains about buffer not being constant
271 * expression.
272 */
273 static PBUF_MAYBE_CONST struct pbuf_cfg cfg = PBUF_CFG_INIT(buffer, MEM_AREA_SZ, 4, 0);
274
275 static struct pbuf pb = {
276 .cfg = &cfg,
277 };
278
279 zassert_equal(pbuf_tx_init(&pb), 0);
280 ctx.pbuf = &pb;
281 ctx.wr_cnt = 0;
282 ctx.rd_cnt = 0;
283
284 ztress_set_timeout(K_MSEC(1500));
285 TC_PRINT("Reading from an interrupt, writing from a thread\n");
286 ZTRESS_EXECUTE(ZTRESS_TIMER(stress_read, &ctx, repeat, Z_TIMEOUT_TICKS(4)),
287 ZTRESS_THREAD(stress_write, &ctx, repeat, 2000, Z_TIMEOUT_TICKS(4)));
288 TC_PRINT("Writes:%d unsuccessful: %d\n", ctx.wr_cnt, ctx.wr_err);
289
290 TC_PRINT("Writing from an interrupt, reading from a thread\n");
291 ZTRESS_EXECUTE(ZTRESS_TIMER(stress_write, &ctx, repeat, Z_TIMEOUT_TICKS(4)),
292 ZTRESS_THREAD(stress_read, &ctx, repeat, 1000, Z_TIMEOUT_TICKS(4)));
293 TC_PRINT("Writes:%d unsuccessful: %d\n", ctx.wr_cnt, ctx.wr_err);
294 }
295
296 ZTEST_SUITE(test_pbuf, NULL, NULL, NULL, NULL, NULL);
297