1 /*
2  * Copyright (c) 2018 Nordic Semiconductor ASA
3  * Copyright (c) 2016 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/init.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <zephyr/device.h>
13 #include <zephyr/pm/device.h>
14 #include <zephyr/pm/device_runtime.h>
15 #include <zephyr/arch/arch_interface.h>
16 
get_device_name(const struct device * dev,char * buf,size_t len)17 static const char *get_device_name(const struct device *dev,
18 				   char *buf,
19 				   size_t len)
20 {
21 	const char *name = dev->name;
22 
23 	if ((name == NULL) || (name[0] == 0)) {
24 		snprintf(buf, len, "[%p]", dev);
25 		name = buf;
26 	}
27 
28 	return name;
29 }
30 
31 #ifdef CONFIG_DEVICE_DEPS
32 struct cmd_device_list_visitor_context {
33 	const struct shell *sh;
34 	char *buf;
35 	size_t buf_size;
36 };
37 
cmd_device_list_visitor(const struct device * dev,void * context)38 static int cmd_device_list_visitor(const struct device *dev,
39 				   void *context)
40 {
41 	const struct cmd_device_list_visitor_context *ctx = context;
42 
43 	shell_fprintf(ctx->sh, SHELL_NORMAL, "  requires: %s\n",
44 		      get_device_name(dev, ctx->buf, ctx->buf_size));
45 
46 	return 0;
47 }
48 #endif /* CONFIG_DEVICE_DEPS */
49 
cmd_device_list(const struct shell * sh,size_t argc,char ** argv)50 static int cmd_device_list(const struct shell *sh,
51 			   size_t argc, char **argv)
52 {
53 	const struct device *devlist;
54 	size_t devcnt = z_device_get_all_static(&devlist);
55 	const struct device *devlist_end = devlist + devcnt;
56 	const struct device *dev;
57 
58 	ARG_UNUSED(argc);
59 	ARG_UNUSED(argv);
60 
61 	shell_fprintf(sh, SHELL_NORMAL, "devices:\n");
62 
63 	for (dev = devlist; dev < devlist_end; dev++) {
64 		char buf[20];
65 		const char *name = get_device_name(dev, buf, sizeof(buf));
66 		const char *state = "READY";
67 		int usage;
68 
69 		shell_fprintf(sh, SHELL_NORMAL, "- %s", name);
70 		if (!device_is_ready(dev)) {
71 			state = "DISABLED";
72 		} else {
73 #ifdef CONFIG_PM_DEVICE
74 			enum pm_device_state st = PM_DEVICE_STATE_ACTIVE;
75 			int err = pm_device_state_get(dev, &st);
76 
77 			if (!err) {
78 				state = pm_device_state_str(st);
79 			}
80 #endif /* CONFIG_PM_DEVICE */
81 		}
82 
83 		usage = pm_device_runtime_usage(dev);
84 		if (usage >= 0) {
85 			shell_fprintf(sh, SHELL_NORMAL, " (%s, usage=%d)\n", state, usage);
86 		} else {
87 			shell_fprintf(sh, SHELL_NORMAL, " (%s)\n", state);
88 		}
89 
90 #ifdef CONFIG_DEVICE_DEPS
91 		if (!k_is_user_context()) {
92 			struct cmd_device_list_visitor_context ctx = {
93 				.sh = sh,
94 				.buf = buf,
95 				.buf_size = sizeof(buf),
96 			};
97 
98 			(void)device_required_foreach(dev, cmd_device_list_visitor, &ctx);
99 		}
100 #endif /* CONFIG_DEVICE_DEPS */
101 
102 #ifdef CONFIG_DEVICE_DT_METADATA
103 		const struct device_dt_nodelabels *nl = device_get_dt_nodelabels(dev);
104 
105 		if (nl != NULL && nl->num_nodelabels > 0) {
106 			shell_fprintf(sh, SHELL_NORMAL, "  DT node labels:");
107 			for (size_t j = 0; j < nl->num_nodelabels; j++) {
108 				const char *nodelabel = nl->nodelabels[j];
109 
110 				shell_fprintf(sh, SHELL_NORMAL, " %s", nodelabel);
111 			}
112 			shell_fprintf(sh, SHELL_NORMAL, "\n");
113 		}
114 #endif /* CONFIG_DEVICE_DT_METADATAa */
115 	}
116 
117 	return 0;
118 }
119 
120 #ifdef CONFIG_PM_DEVICE_RUNTIME
cmd_device_pm_toggle(const struct shell * sh,size_t argc,char ** argv)121 static int cmd_device_pm_toggle(const struct shell *sh,
122 			 size_t argc, char **argv)
123 {
124 	const struct device *dev;
125 	enum pm_device_state pm_state;
126 
127 	dev = device_get_binding(argv[1]);
128 	if (dev == NULL) {
129 		shell_error(sh, "Device unknown (%s)", argv[1]);
130 		return -ENODEV;
131 	}
132 
133 	if (!pm_device_runtime_is_enabled(dev)) {
134 		shell_error(sh, "Device (%s) does not have runtime power management",
135 			    argv[1]);
136 		return -ENOTSUP;
137 	}
138 
139 	(void)pm_device_state_get(dev, &pm_state);
140 
141 	if (pm_state == PM_DEVICE_STATE_ACTIVE) {
142 		shell_fprintf(sh, SHELL_NORMAL, "pm_device_runtime_put(%s)\n",
143 			      argv[1]);
144 		pm_device_runtime_put(dev);
145 	} else {
146 		shell_fprintf(sh, SHELL_NORMAL, "pm_device_runtime_get(%s)\n",
147 			      argv[1]);
148 		pm_device_runtime_get(dev);
149 	}
150 
151 	return 0;
152 }
153 #define PM_SHELL_CMD SHELL_CMD(pm_toggle, NULL, "Toggle device power (pm get/put)",\
154 			       cmd_device_pm_toggle),
155 #else
156 #define PM_SHELL_CMD
157 #endif /* CONFIG_PM_DEVICE_RUNTIME  */
158 
159 
160 
161 SHELL_STATIC_SUBCMD_SET_CREATE(sub_device,
162 	SHELL_CMD(list, NULL, "List configured devices", cmd_device_list),
163 	PM_SHELL_CMD
164 	SHELL_SUBCMD_SET_END /* Array terminated. */
165 );
166 
167 SHELL_CMD_REGISTER(device, &sub_device, "Device commands", NULL);
168