1 /*
2  * Copyright (c) 2023 Centralp
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/drivers/watchdog.h>
10 
11 #define WDT_SETUP_HELP                                                                             \
12 	"Set up watchdog instance. Syntax:\n"                                                      \
13 	"<device>"
14 
15 #define WDT_DISABLE_HELP                                                                           \
16 	"Disable watchdog instance. Syntax:\n"                                                     \
17 	"<device>"
18 
19 #define WDT_TIMEOUT_HELP                                                                           \
20 	"Install a new timeout. Syntax:\n"                                                         \
21 	"<device> <none|cpu|soc> <min_ms> <max_ms>"
22 
23 #define WDT_FEED_HELP                                                                              \
24 	"Feed specified watchdog timeout. Syntax:\n"                                               \
25 	"<device> <channel_id>"
26 
27 static const char *const wdt_reset_name[] = {
28 	[WDT_FLAG_RESET_NONE] = "none",
29 	[WDT_FLAG_RESET_CPU_CORE] = "cpu",
30 	[WDT_FLAG_RESET_SOC] = "soc",
31 };
32 
33 struct args_index {
34 	uint8_t device;
35 	uint8_t reset;
36 	uint8_t timeout_min;
37 	uint8_t timeout_max;
38 	uint8_t channel_id;
39 };
40 
41 static const struct args_index args_indx = {
42 	.device = 1,
43 	.reset = 2,
44 	.timeout_min = 3,
45 	.timeout_max = 4,
46 	.channel_id = 2,
47 };
48 
parse_named_int(const char * name,const char * const keystack[],size_t count)49 static int parse_named_int(const char *name, const char *const keystack[], size_t count)
50 {
51 	char *endptr;
52 	int i;
53 
54 	/* Attempt to parse name as a number first */
55 	i = strtoul(name, &endptr, 0);
56 
57 	if (*endptr == '\0') {
58 		return i;
59 	}
60 
61 	/* Name is not a number, look it up */
62 	for (i = 0; i < count; i++) {
63 		if (strcmp(name, keystack[i]) == 0) {
64 			return i;
65 		}
66 	}
67 
68 	return -ENOTSUP;
69 }
70 
cmd_setup(const struct shell * sh,size_t argc,char * argv[])71 static int cmd_setup(const struct shell *sh, size_t argc, char *argv[])
72 {
73 	const struct device *dev;
74 
75 	dev = shell_device_get_binding(argv[args_indx.device]);
76 	if (!dev) {
77 		shell_error(sh, "WDT device not found");
78 		return -ENODEV;
79 	}
80 
81 	return wdt_setup(dev, 0);
82 }
83 
cmd_disable(const struct shell * sh,size_t argc,char * argv[])84 static int cmd_disable(const struct shell *sh, size_t argc, char *argv[])
85 {
86 	const struct device *dev;
87 
88 	dev = shell_device_get_binding(argv[args_indx.device]);
89 	if (!dev) {
90 		shell_error(sh, "WDT device not found");
91 		return -ENODEV;
92 	}
93 
94 	return wdt_disable(dev);
95 }
96 
cmd_timeout(const struct shell * sh,size_t argc,char * argv[])97 static int cmd_timeout(const struct shell *sh, size_t argc, char *argv[])
98 {
99 	const struct device *dev;
100 	int flags;
101 	int timeout_min;
102 	int timeout_max;
103 	struct wdt_timeout_cfg cfg;
104 	int rc;
105 
106 	dev = shell_device_get_binding(argv[args_indx.device]);
107 	if (!dev) {
108 		shell_error(sh, "WDT device not found");
109 		return -ENODEV;
110 	}
111 
112 	flags = parse_named_int(argv[args_indx.reset], wdt_reset_name, ARRAY_SIZE(wdt_reset_name));
113 	if (flags < 0) {
114 		shell_error(sh, "Reset mode '%s' unknown", argv[args_indx.reset]);
115 		return -EINVAL;
116 	}
117 
118 	timeout_min = parse_named_int(argv[args_indx.timeout_min], NULL, 0);
119 	if (timeout_min < 0) {
120 		shell_error(sh, "Unable to convert '%s' to integer", argv[args_indx.timeout_min]);
121 		return -EINVAL;
122 	}
123 
124 	timeout_max = parse_named_int(argv[args_indx.timeout_max], NULL, 0);
125 	if (timeout_max < 0) {
126 		shell_error(sh, "Unable to convert '%s' to integer", argv[args_indx.timeout_max]);
127 		return -EINVAL;
128 	}
129 
130 	cfg.window.min = timeout_min;
131 	cfg.window.max = timeout_max;
132 	cfg.callback = NULL;
133 	cfg.flags = flags;
134 
135 	rc = wdt_install_timeout(dev, &cfg);
136 	if (rc >= 0) {
137 		shell_print(sh, "Channel ID = %d", rc);
138 	}
139 
140 	return rc;
141 }
142 
cmd_feed(const struct shell * sh,size_t argc,char * argv[])143 static int cmd_feed(const struct shell *sh, size_t argc, char *argv[])
144 {
145 	const struct device *dev;
146 	int channel_id;
147 
148 	dev = shell_device_get_binding(argv[args_indx.device]);
149 	if (!dev) {
150 		shell_error(sh, "WDT device not found");
151 		return -ENODEV;
152 	}
153 
154 	channel_id = parse_named_int(argv[args_indx.channel_id], NULL, 0);
155 	if (channel_id < 0) {
156 		shell_error(sh, "Unable to convert '%s' to integer", argv[args_indx.channel_id]);
157 		return -EINVAL;
158 	}
159 
160 	return wdt_feed(dev, channel_id);
161 }
162 
163 /* Device name autocompletion support */
device_name_get(size_t idx,struct shell_static_entry * entry)164 static void device_name_get(size_t idx, struct shell_static_entry *entry)
165 {
166 	const struct device *dev = shell_device_lookup(idx, NULL);
167 
168 	entry->syntax = (dev != NULL) ? dev->name : NULL;
169 	entry->handler = NULL;
170 	entry->help = NULL;
171 	entry->subcmd = NULL;
172 }
173 
174 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
175 
176 /* clang-format off */
177 SHELL_STATIC_SUBCMD_SET_CREATE(sub_wdt,
178 	SHELL_CMD_ARG(setup, &dsub_device_name, WDT_SETUP_HELP, cmd_setup,
179 			2, 0),
180 	SHELL_CMD_ARG(disable, &dsub_device_name, WDT_DISABLE_HELP, cmd_disable,
181 			2, 0),
182 	SHELL_CMD_ARG(timeout, &dsub_device_name, WDT_TIMEOUT_HELP, cmd_timeout,
183 			5, 0),
184 	SHELL_CMD_ARG(feed, &dsub_device_name, WDT_FEED_HELP, cmd_feed,
185 			3, 0),
186 	SHELL_SUBCMD_SET_END
187 );
188 /* clang-format on */
189 
190 SHELL_CMD_REGISTER(wdt, &sub_wdt, "Watchdog commands", NULL);
191