1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 * Copyright (c) 2016 Intel Corporation
4 * Copyright (C) 2025 Bang & Olufsen A/S, Denmark
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/shell/shell.h>
10 #include <zephyr/init.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <zephyr/device.h>
14 #include <zephyr/pm/device.h>
15 #include <zephyr/pm/device_runtime.h>
16 #include <zephyr/arch/arch_interface.h>
17
get_device_name(const struct device * dev,char * buf,size_t len)18 static const char *get_device_name(const struct device *dev,
19 char *buf,
20 size_t len)
21 {
22 const char *name = dev->name;
23
24 if ((name == NULL) || (name[0] == 0)) {
25 snprintf(buf, len, "[%p]", dev);
26 name = buf;
27 }
28
29 return name;
30 }
31
32 #ifdef CONFIG_DEVICE_DEPS
33 struct cmd_device_list_visitor_context {
34 const struct shell *sh;
35 char *buf;
36 size_t buf_size;
37 };
38
cmd_device_list_visitor(const struct device * dev,void * context)39 static int cmd_device_list_visitor(const struct device *dev,
40 void *context)
41 {
42 const struct cmd_device_list_visitor_context *ctx = context;
43
44 shell_fprintf(ctx->sh, SHELL_NORMAL, " requires: %s\n",
45 get_device_name(dev, ctx->buf, ctx->buf_size));
46
47 return 0;
48 }
49 #endif /* CONFIG_DEVICE_DEPS */
50
cmd_device_list(const struct shell * sh,size_t argc,char ** argv)51 static int cmd_device_list(const struct shell *sh,
52 size_t argc, char **argv)
53 {
54 const struct device *devlist;
55 size_t devcnt = z_device_get_all_static(&devlist);
56 const struct device *devlist_end = devlist + devcnt;
57 const struct device *dev;
58
59 shell_fprintf(sh, SHELL_NORMAL, "devices:\n");
60
61 for (dev = devlist; dev < devlist_end; dev++) {
62 char buf[Z_DEVICE_MAX_NAME_LEN];
63 const char *name = get_device_name(dev, buf, sizeof(buf));
64 const char *state = "READY";
65 int usage;
66
67 if (argc == 2
68 && strstr(name, argv[1]) == NULL) {
69 continue;
70 }
71
72 shell_fprintf(sh, SHELL_NORMAL, "- %s", name);
73 if (!device_is_ready(dev)) {
74 state = "DISABLED";
75 } else {
76 #ifdef CONFIG_PM_DEVICE
77 enum pm_device_state st = PM_DEVICE_STATE_ACTIVE;
78 int err = pm_device_state_get(dev, &st);
79
80 if (!err) {
81 state = pm_device_state_str(st);
82 }
83 #endif /* CONFIG_PM_DEVICE */
84 }
85
86 usage = pm_device_runtime_usage(dev);
87 if (usage >= 0) {
88 shell_fprintf(sh, SHELL_NORMAL, " (%s, usage=%d)\n", state, usage);
89 } else {
90 shell_fprintf(sh, SHELL_NORMAL, " (%s)\n", state);
91 }
92
93 #ifdef CONFIG_DEVICE_DEPS
94 if (!k_is_user_context()) {
95 struct cmd_device_list_visitor_context ctx = {
96 .sh = sh,
97 .buf = buf,
98 .buf_size = sizeof(buf),
99 };
100
101 (void)device_required_foreach(dev, cmd_device_list_visitor, &ctx);
102 }
103 #endif /* CONFIG_DEVICE_DEPS */
104
105 #ifdef CONFIG_DEVICE_DT_METADATA
106 const struct device_dt_nodelabels *nl = device_get_dt_nodelabels(dev);
107
108 if (nl != NULL && nl->num_nodelabels > 0) {
109 shell_fprintf(sh, SHELL_NORMAL, " DT node labels:");
110 for (size_t j = 0; j < nl->num_nodelabels; j++) {
111 const char *nodelabel = nl->nodelabels[j];
112
113 shell_fprintf(sh, SHELL_NORMAL, " %s", nodelabel);
114 }
115 shell_fprintf(sh, SHELL_NORMAL, "\n");
116 }
117 #endif /* CONFIG_DEVICE_DT_METADATAa */
118 }
119
120 return 0;
121 }
122
device_name_lookup(size_t idx,struct shell_static_entry * entry)123 static void device_name_lookup(size_t idx,
124 struct shell_static_entry *entry)
125 {
126 const struct device *dev = shell_device_lookup_all(idx, NULL);
127
128 entry->syntax = dev != NULL ? dev->name : NULL;
129 entry->handler = NULL;
130 entry->help = "device";
131 entry->subcmd = NULL;
132 }
133
134 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_lookup, device_name_lookup);
135
136
137 #ifdef CONFIG_DEVICE_SHELL_INIT_CMD
138 #ifdef CONFIG_DEVICE_DEPS
cmd_device_check_deps(const struct device * dev,void * context)139 static int cmd_device_check_deps(const struct device *dev,
140 void *context)
141 {
142 const struct cmd_device_list_visitor_context *ctx = context;
143
144 if (!device_is_ready(dev)) {
145 shell_error(ctx->sh, "Device %s is required, but not ready",
146 get_device_name(dev, ctx->buf, ctx->buf_size));
147 return -EINVAL;
148 }
149
150 return 0;
151 }
152 #endif /* CONFIG_DEVICE_DEPS */
153
cmd_device_init(const struct shell * sh,size_t argc,char ** argv)154 static int cmd_device_init(const struct shell *sh, size_t argc, char **argv)
155 {
156 const struct device *dev;
157 int ret;
158
159 dev = shell_device_get_binding_all(argv[1]);
160 if (dev == NULL) {
161 shell_error(sh, "Device unknown (%s)", argv[1]);
162 return -ENODEV;
163 }
164
165 if (device_is_ready(dev)) {
166 shell_info(sh, "Device %s is already initialized", argv[1]);
167 return 0;
168 }
169
170 #ifdef CONFIG_DEVICE_DEPS
171 if (!k_is_user_context()) {
172 char buf[Z_DEVICE_MAX_NAME_LEN];
173
174 struct cmd_device_list_visitor_context ctx = {
175 .sh = sh,
176 .buf = buf,
177 .buf_size = sizeof(buf),
178 };
179
180 ret = device_required_foreach(dev, cmd_device_check_deps, &ctx);
181 if (ret < 0) {
182 return ret;
183 }
184 }
185 #endif /* CONFIG_DEVICE_DEPS */
186
187 ret = device_init(dev);
188 if (ret != 0) {
189 shell_error(sh, "Device %s initialization failed with err=%d",
190 argv[1], ret);
191 } else {
192 shell_info(sh, "Device %s initialized successfully", argv[1]);
193 }
194
195 return ret;
196 }
197
device_name_get_non_ready(size_t idx,struct shell_static_entry * entry)198 static void device_name_get_non_ready(size_t idx,
199 struct shell_static_entry *entry)
200 {
201 const struct device *dev = shell_device_lookup_non_ready(idx, NULL);
202
203 entry->syntax = dev != NULL ? dev->name : NULL;
204 entry->handler = NULL;
205 entry->help = "device";
206 entry->subcmd = NULL;
207 }
208
209 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_non_ready, device_name_get_non_ready);
210
211 #define DEVICE_INIT_CMD SHELL_CMD_ARG(init, &dsub_device_name_non_ready, \
212 "Manually initialize a device", \
213 cmd_device_init, 2, 0),
214 #else
215 #define DEVICE_INIT_CMD
216 #endif /* CONFIG_DEVICE_SHELL_INIT_CMD */
217
218 #ifdef CONFIG_PM_DEVICE_RUNTIME
cmd_device_pm_toggle(const struct shell * sh,size_t argc,char ** argv)219 static int cmd_device_pm_toggle(const struct shell *sh,
220 size_t argc, char **argv)
221 {
222 const struct device *dev;
223 enum pm_device_state pm_state;
224
225 dev = device_get_binding(argv[1]);
226 if (dev == NULL) {
227 shell_error(sh, "Device unknown (%s)", argv[1]);
228 return -ENODEV;
229 }
230
231 if (!pm_device_runtime_is_enabled(dev)) {
232 shell_error(sh, "Device (%s) does not have runtime power management",
233 argv[1]);
234 return -ENOTSUP;
235 }
236
237 (void)pm_device_state_get(dev, &pm_state);
238
239 if (pm_state == PM_DEVICE_STATE_ACTIVE) {
240 shell_fprintf(sh, SHELL_NORMAL, "pm_device_runtime_put(%s)\n",
241 argv[1]);
242 pm_device_runtime_put(dev);
243 } else {
244 shell_fprintf(sh, SHELL_NORMAL, "pm_device_runtime_get(%s)\n",
245 argv[1]);
246 pm_device_runtime_get(dev);
247 }
248
249 return 0;
250 }
251 #define PM_SHELL_CMD SHELL_CMD(pm_toggle, NULL, "Toggle device power (pm get/put)",\
252 cmd_device_pm_toggle),
253 #else
254 #define PM_SHELL_CMD
255 #endif /* CONFIG_PM_DEVICE_RUNTIME */
256
257
258 #define LIST_CMD_HELP SHELL_HELP("List configured devices, with an optional filter", \
259 "[<device filter>]")
260
261 SHELL_STATIC_SUBCMD_SET_CREATE(sub_device,
262 SHELL_CMD_ARG(list, &dsub_device_name_lookup,
263 LIST_CMD_HELP, cmd_device_list, 1, 1),
264 DEVICE_INIT_CMD
265 PM_SHELL_CMD
266 SHELL_SUBCMD_SET_END /* Array terminated. */
267 );
268
269 SHELL_CMD_REGISTER(device, &sub_device, "Device commands", NULL);
270