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