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