1 /*
2  * Copyright (c) 2018 Diego Sueiro
3  * Copyright (c) 2022 Thomas Stranger
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdlib.h>
9 
10 #include <zephyr/drivers/w1.h>
11 #include <zephyr/shell/shell.h>
12 #include <zephyr/kernel.h>
13 
14 #define BUF_SIZE CONFIG_W1_SHELL_BUFFER_SIZE
15 static uint8_t msg_buf[BUF_SIZE];
16 
17 #define W1DEV_X_NOT_FOUND "1-Wire device not found: \"%s\""
18 
19 #define OPTION_HELP_RESET "-r Perform bus reset before executing cmd."
20 
21 static const char *w1_settings_name[W1_SETINGS_TYPE_COUNT] = {
22 	[W1_SETTING_SPEED] = "speed",
23 	[W1_SETTING_STRONG_PULLUP] = "spu",
24 };
25 
read_io_options(const struct shell * sh,int pos,char ** argv,bool * reset)26 static int read_io_options(const struct shell *sh, int pos, char **argv,
27 			   bool *reset)
28 {
29 	char *arg = argv[pos];
30 
31 	if (arg[0] != '-') {
32 		return pos;
33 	}
34 
35 	for (arg = &arg[1]; *arg; arg++) {
36 		switch (*arg) {
37 		case 'r':
38 			*reset = true;
39 			break;
40 		default:
41 			shell_error(sh, "Unknown option %c", *arg);
42 			return -EINVAL;
43 		}
44 	}
45 
46 	return ++pos;
47 }
48 
49 /* 1-Wire reset bus <device> */
cmd_w1_reset_bus(const struct shell * sh,size_t argc,char ** argv)50 static int cmd_w1_reset_bus(const struct shell *sh, size_t argc, char **argv)
51 {
52 	const struct device *dev;
53 	int ret;
54 
55 	dev = device_get_binding(argv[1]);
56 	if (!dev) {
57 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
58 		return -EINVAL;
59 	}
60 
61 	(void)w1_lock_bus(dev);
62 	ret = w1_reset_bus(dev);
63 	if (ret < 0) {
64 		shell_error(sh, "Failed to reset bus [%d]", ret);
65 	}
66 
67 	(void)w1_unlock_bus(dev);
68 	return ret;
69 }
70 
71 /* 1-Wire read_bit <device> */
cmd_w1_read_bit(const struct shell * sh,size_t argc,char ** argv)72 static int cmd_w1_read_bit(const struct shell *sh, size_t argc, char **argv)
73 {
74 	const struct device *dev;
75 	int ret;
76 
77 	dev = device_get_binding(argv[1]);
78 	if (!dev) {
79 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
80 		return -EINVAL;
81 	}
82 
83 	(void)w1_lock_bus(dev);
84 	ret = w1_read_bit(dev);
85 	if (ret < 0) {
86 		shell_error(sh, "Failed to read bit [%d]", ret);
87 	} else {
88 		shell_print(sh, "Output: 0b%x", ret);
89 	}
90 
91 	(void)w1_unlock_bus(dev);
92 	return ret;
93 }
94 
95 /* 1-Wire read_byte <device> */
cmd_w1_read_byte(const struct shell * sh,size_t argc,char ** argv)96 static int cmd_w1_read_byte(const struct shell *sh, size_t argc, char **argv)
97 {
98 	const struct device *dev;
99 	int ret;
100 
101 	dev = device_get_binding(argv[1]);
102 	if (!dev) {
103 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
104 		return -EINVAL;
105 	}
106 
107 	(void)w1_lock_bus(dev);
108 	ret = w1_read_byte(dev);
109 	if (ret < 0) {
110 		shell_error(sh, "Failed to read byte [%d]", ret);
111 	} else {
112 		shell_print(sh, "Output: 0x%x", ret);
113 	}
114 
115 	(void)w1_unlock_bus(dev);
116 	return ret;
117 }
118 
119 /* 1-Wire read_block <device> [num_bytes] */
cmd_w1_read_block(const struct shell * sh,size_t argc,char ** argv)120 static int cmd_w1_read_block(const struct shell *sh, size_t argc, char **argv)
121 {
122 	const struct device *dev;
123 	char *end_ptr;
124 	size_t read_len;
125 	int ret;
126 
127 	dev = device_get_binding(argv[1]);
128 	if (!dev) {
129 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
130 		return -EINVAL;
131 	}
132 
133 	read_len = strtoul(argv[2], &end_ptr, 0);
134 	if (*end_ptr != '\0') {
135 		shell_error(sh, "num_bytes is not a number");
136 		return -EINVAL;
137 	}
138 	if (read_len > BUF_SIZE) {
139 		shell_error(sh, "num_bytes limited to: %u", BUF_SIZE);
140 		return -EINVAL;
141 	}
142 
143 	(void)w1_lock_bus(dev);
144 	ret = w1_read_block(dev, msg_buf, read_len);
145 	if (ret < 0) {
146 		shell_error(sh, "Failed to read byte [%d]", ret);
147 		goto out;
148 	}
149 
150 	shell_fprintf(sh, SHELL_NORMAL, "Output:");
151 	for (int i = 0; i < read_len; i++) {
152 		shell_fprintf(sh, SHELL_NORMAL, " 0x%02x", msg_buf[i]);
153 	}
154 	shell_fprintf(sh, SHELL_NORMAL, "\n");
155 
156 out:
157 	(void)w1_unlock_bus(dev);
158 	return ret;
159 }
160 
161 /* 1-Wire write_bit <device> <bit_value> */
cmd_w1_write_bit(const struct shell * sh,size_t argc,char ** argv)162 static int cmd_w1_write_bit(const struct shell *sh, size_t argc, char **argv)
163 {
164 	const struct device *dev;
165 	unsigned long input = strtoul(argv[2], NULL, 0);
166 	int ret;
167 
168 	dev = device_get_binding(argv[1]);
169 	if (!dev) {
170 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
171 		return -EINVAL;
172 	}
173 	if (input > 1UL) {
174 		shell_error(sh, "input must not be > 0b1");
175 		return -EINVAL;
176 	}
177 
178 	(void)w1_lock_bus(dev);
179 	ret = w1_write_byte(dev, (bool)input);
180 	if (ret < 0) {
181 		shell_error(sh, "Failed to write bit [%d]", ret);
182 	}
183 
184 	(void)w1_unlock_bus(dev);
185 	return ret;
186 }
187 
188 /* 1-Wire write_byte <device> <byte_value> */
cmd_w1_write_byte(const struct shell * sh,size_t argc,char ** argv)189 static int cmd_w1_write_byte(const struct shell *sh, size_t argc, char **argv)
190 {
191 	const struct device *dev;
192 	unsigned long input;
193 	int pos = 1;
194 	bool reset = false;
195 	int ret;
196 
197 	dev = device_get_binding(argv[pos]);
198 	if (!dev) {
199 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[pos]);
200 		return -EINVAL;
201 	}
202 	pos++;
203 
204 	pos = read_io_options(sh, pos, argv, &reset);
205 	if (pos < 0) {
206 		return -EINVAL;
207 	}
208 	if (argc <= pos) {
209 		shell_error(sh, "Missing data to be written.");
210 		return -EINVAL;
211 	}
212 
213 	input = strtoul(argv[pos], NULL, 0);
214 	if (input > 0xFFUL) {
215 		shell_error(sh, "input must not be > 0xFF");
216 		return -EINVAL;
217 	}
218 
219 	(void)w1_lock_bus(dev);
220 	if (reset) {
221 		ret = w1_reset_bus(dev);
222 		if (ret <= 0) {
223 			shell_error(sh, "Failed to reset bus [%d]", ret);
224 			goto out;
225 		}
226 	}
227 
228 	ret = w1_write_byte(dev, (uint8_t)input);
229 	if (ret < 0) {
230 		shell_error(sh, "Failed to write byte [%d]", ret);
231 	}
232 out:
233 	(void)w1_unlock_bus(dev);
234 	return ret;
235 }
236 
237 /* 1-Wire write_block <device> <byt1> [byte2, ...] */
cmd_w1_write_block(const struct shell * sh,size_t argc,char ** argv)238 static int cmd_w1_write_block(const struct shell *sh, size_t argc, char **argv)
239 {
240 	const struct device *dev;
241 	int i;
242 	int pos = 1;
243 	bool reset = false;
244 	int ret;
245 
246 	dev = device_get_binding(argv[pos]);
247 	if (!dev) {
248 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
249 		return -EINVAL;
250 	}
251 	pos++;
252 
253 	pos = read_io_options(sh, pos, argv, &reset);
254 	if (pos < 0) {
255 		return -EINVAL;
256 	}
257 	if (argc <= pos) {
258 		shell_error(sh, "Missing data to be written.");
259 		return -EINVAL;
260 	}
261 	if ((argc - pos) > BUF_SIZE) {
262 		shell_error(sh, "Too much data to be written.");
263 		return -EINVAL;
264 	}
265 
266 	(void)w1_lock_bus(dev);
267 	i = 0;
268 	do {
269 		msg_buf[i] = (uint8_t)strtoul(argv[i + pos], NULL, 16);
270 		i++;
271 	} while ((i + pos) < argc);
272 
273 	if (reset) {
274 		ret = w1_reset_bus(dev);
275 		if (ret <= 0) {
276 			shell_error(sh, "Failed to reset bus [%d]", ret);
277 			goto out;
278 		}
279 	}
280 
281 	ret = w1_write_block(dev, msg_buf, i);
282 	if (ret < 0) {
283 		shell_error(sh, "Failed to write block [%d]", ret);
284 	}
285 out:
286 	(void)w1_unlock_bus(dev);
287 	return ret;
288 }
289 
290 /* 1-Wire config <device> <type> <value> */
cmd_w1_configure(const struct shell * sh,size_t argc,char ** argv)291 static int cmd_w1_configure(const struct shell *sh, size_t argc, char **argv)
292 {
293 	const struct device *dev;
294 	char *type_endptr;
295 	char *type_name = argv[2];
296 	int ret;
297 	uint32_t type = strtoul(type_name, &type_endptr, 0);
298 	uint32_t value = strtoul(argv[3], NULL, 0);
299 
300 	dev = device_get_binding(argv[1]);
301 	if (!dev) {
302 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
303 		return -EINVAL;
304 	}
305 
306 	/* if type is not given as number, search it via the name */
307 	if (*type_endptr != '\0') {
308 		for (type = 0; type < ARRAY_SIZE(w1_settings_name); type++) {
309 			if (strcmp(type_name, w1_settings_name[type]) == 0) {
310 				break;
311 			}
312 		}
313 
314 		if (type == ARRAY_SIZE(w1_settings_name)) {
315 			shell_error(sh, "Unknown config name (%s)", type_name);
316 			return -ENOTSUP;
317 		}
318 	}
319 
320 	if (type > W1_SETINGS_TYPE_COUNT) {
321 		shell_error(sh, "invalid type %u", type);
322 		return -EINVAL;
323 	}
324 
325 	(void)w1_lock_bus(dev);
326 	ret = w1_configure(dev, type, value);
327 	if (ret < 0) {
328 		shell_error(sh, "Failed to configure [%d]", ret);
329 		goto out;
330 	}
331 
332 	shell_info(sh, "Applied config: %s = %u (0x%08x)",
333 		   w1_settings_name[type], value, value);
334 
335 out:
336 	(void)w1_unlock_bus(dev);
337 	return ret;
338 }
339 
search_callback(struct w1_rom rom,void * user_data)340 static void search_callback(struct w1_rom rom, void *user_data)
341 {
342 	const struct shell *sh = (const struct shell *)user_data;
343 
344 	shell_print(sh, "ROM found: %016llx", w1_rom_to_uint64(&rom));
345 }
346 
347 /* 1-Wire search <device> */
cmd_w1_search(const struct shell * sh,size_t argc,char ** argv)348 static int cmd_w1_search(const struct shell *sh, size_t argc, char **argv)
349 {
350 	const struct device *dev;
351 	int ret;
352 
353 	dev = device_get_binding(argv[1]);
354 	if (!dev) {
355 		shell_error(sh, W1DEV_X_NOT_FOUND, argv[1]);
356 		return -EINVAL;
357 	}
358 
359 	(void)w1_lock_bus(dev);
360 	ret = w1_search_rom(dev, search_callback, (void *)sh);
361 	if (ret < 0) {
362 		shell_error(sh, "Failed to initiate search [%d]", ret);
363 	} else {
364 		shell_print(sh, "Found %d device(s)", ret);
365 	}
366 
367 	(void)w1_unlock_bus(dev);
368 	return ret;
369 }
370 
371 SHELL_STATIC_SUBCMD_SET_CREATE(sub_w1,
372 	SHELL_CMD_ARG(reset, NULL,
373 		      "Reset 1-Wire bus.\n"
374 		      "Usage: <device>",
375 		      cmd_w1_reset_bus, 2, 0),
376 	SHELL_CMD_ARG(read_bit, NULL,
377 		      "Read 1-Wire bit.\n"
378 		      "Usage: <device>",
379 		      cmd_w1_read_bit, 2, 0),
380 	SHELL_CMD_ARG(read_byte, NULL,
381 		      "Read 1-Wire byte.\n"
382 		      "Usage: <device>",
383 		      cmd_w1_read_byte, 2, 0),
384 	SHELL_CMD_ARG(read_block, NULL,
385 		      "Read 1-Wire block.\n"
386 		      "Usage: <device> <num_bytes>",
387 		      cmd_w1_read_block, 3, 0),
388 	SHELL_CMD_ARG(write_bit, NULL,
389 		      "Write 1-Wire bit.\n"
390 		      "Usage: <device> <bit>",
391 		      cmd_w1_write_bit, 3, 0),
392 	SHELL_CMD_ARG(write_byte, NULL,
393 		      "Write 1-Wire byte.\n"
394 		      "Usage: <device> [-r] <byte>\n"
395 		      OPTION_HELP_RESET,
396 		      cmd_w1_write_byte, 3, 1),
397 	SHELL_CMD_ARG(write_block, NULL,
398 		      "Write 1-Wire block.\n"
399 		      "Usage: <device> [-r] <byte1> [<byte2>, ...]\n"
400 		      OPTION_HELP_RESET,
401 		      cmd_w1_write_block, 3, BUF_SIZE),
402 	SHELL_CMD_ARG(config, NULL,
403 		      "Configure 1-Wire host.\n"
404 		      "Usage: <device> <type> <value>\n"
405 		      "<type> is either a name or an id.",
406 		      cmd_w1_configure, 4, 0),
407 	SHELL_CMD_ARG(search, NULL,
408 		      "1-Wire devices.\n"
409 		      "Usage: <device>",
410 		      cmd_w1_search, 2, 0),
411 	SHELL_SUBCMD_SET_END /* Array terminated. */
412 );
413 
414 SHELL_CMD_ARG_REGISTER(w1, &sub_w1, "1-Wire commands", NULL, 2, 0);
415