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