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