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