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