source: mainline/uspace/app/devctl/devctl.c

Last change on this file was 8300c72, checked in by Jiri Svoboda <jiri@…>, 4 months ago

Quiesce devices before proceeding with shutdown.

Only implemented for e1k, uhci and xhci.

  • Property mode set to 100644
File size: 9.9 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup devctl
30 * @{
31 */
32/** @file Control device framework (devman server).
33 */
34
35#include <devman.h>
36#include <errno.h>
37#include <io/table.h>
38#include <stdbool.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <str.h>
42#include <str_error.h>
43
44#define NAME "devctl"
45
46#define MAX_NAME_LENGTH 1024
47
48static char name[MAX_NAME_LENGTH];
49static char drv_name[MAX_NAME_LENGTH];
50static bool verbose = false;
51
52static const char *drv_state_str(driver_state_t state)
53{
54 const char *sstate;
55
56 switch (state) {
57 case DRIVER_NOT_STARTED:
58 sstate = "not started";
59 break;
60 case DRIVER_STARTING:
61 sstate = "starting";
62 break;
63 case DRIVER_RUNNING:
64 sstate = "running";
65 break;
66 default:
67 sstate = "unknown";
68 }
69
70 return sstate;
71}
72
73static errno_t fun_subtree_print(devman_handle_t funh, int lvl)
74{
75 devman_handle_t devh;
76 devman_handle_t *cfuns;
77 size_t count, i;
78 unsigned int score;
79 errno_t rc;
80 int j;
81
82 for (j = 0; j < lvl; j++)
83 printf(" ");
84
85 rc = devman_fun_get_name(funh, name, MAX_NAME_LENGTH);
86 if (rc != EOK)
87 return ELIMIT;
88
89 if (name[0] == '\0')
90 str_cpy(name, MAX_NAME_LENGTH, "/");
91
92 rc = devman_fun_get_driver_name(funh, drv_name, MAX_NAME_LENGTH);
93 if (rc != EOK && rc != EINVAL)
94 return ELIMIT;
95
96 if (rc == EINVAL)
97 printf("%s\n", name);
98 else
99 printf("%s : %s\n", name, drv_name);
100
101 if (verbose) {
102 for (i = 0; true; i++) {
103 rc = devman_fun_get_match_id(funh, i, name, MAX_NAME_LENGTH,
104 &score);
105 if (rc != EOK)
106 break;
107
108 for (j = 0; j < lvl; j++)
109 printf(" ");
110
111 printf(" %u %s\n", score, name);
112 }
113 }
114
115 rc = devman_fun_get_child(funh, &devh);
116 if (rc == ENOENT)
117 return EOK;
118
119 if (rc != EOK) {
120 printf(NAME ": Failed getting child device for function "
121 "%s.\n", "xxx");
122 return rc;
123 }
124
125 rc = devman_dev_get_functions(devh, &cfuns, &count);
126 if (rc != EOK) {
127 printf(NAME ": Failed getting list of functions for "
128 "device %s.\n", "xxx");
129 return rc;
130 }
131
132 for (i = 0; i < count; i++)
133 fun_subtree_print(cfuns[i], lvl + 1);
134
135 free(cfuns);
136 return EOK;
137}
138
139static errno_t fun_tree_print(void)
140{
141 devman_handle_t root_fun;
142 errno_t rc;
143
144 rc = devman_fun_get_handle("/", &root_fun, 0);
145 if (rc != EOK) {
146 printf(NAME ": Error resolving root function.\n");
147 return EIO;
148 }
149
150 rc = fun_subtree_print(root_fun, 0);
151 if (rc != EOK)
152 return EIO;
153
154 return EOK;
155}
156
157static errno_t fun_online(const char *path)
158{
159 devman_handle_t funh;
160 errno_t rc;
161
162 rc = devman_fun_get_handle(path, &funh, 0);
163 if (rc != EOK) {
164 printf(NAME ": Error resolving device function '%s' (%s)\n",
165 path, str_error(rc));
166 return rc;
167 }
168
169 rc = devman_fun_online(funh);
170 if (rc != EOK) {
171 printf(NAME ": Failed to online function '%s'.\n", path);
172 return rc;
173 }
174
175 return EOK;
176}
177
178static errno_t fun_offline(const char *path)
179{
180 devman_handle_t funh;
181 errno_t rc;
182
183 rc = devman_fun_get_handle(path, &funh, 0);
184 if (rc != EOK) {
185 printf(NAME ": Error resolving device function '%s' (%s)\n",
186 path, str_error(rc));
187 return rc;
188 }
189
190 rc = devman_fun_offline(funh);
191 if (rc != EOK) {
192 printf(NAME ": Failed to offline function '%s' (%s)\n", path,
193 str_error(rc));
194 return rc;
195 }
196
197 return EOK;
198}
199
200static errno_t fun_quiesce(const char *path)
201{
202 devman_handle_t funh;
203 errno_t rc;
204
205 rc = devman_fun_get_handle(path, &funh, 0);
206 if (rc != EOK) {
207 printf(NAME ": Error resolving device function '%s' (%s)\n",
208 path, str_error(rc));
209 return rc;
210 }
211
212 rc = devman_fun_quiesce(funh);
213 if (rc != EOK) {
214 printf(NAME ": Failed to offline function '%s' (%s)\n", path,
215 str_error(rc));
216 return rc;
217 }
218
219 return EOK;
220}
221
222static errno_t drv_list(void)
223{
224 devman_handle_t *devs;
225 devman_handle_t *drvs;
226 driver_state_t state;
227 const char *sstate;
228 size_t ndrvs;
229 size_t ndevs;
230 size_t i;
231 table_t *table = NULL;
232 errno_t rc;
233
234 rc = devman_get_drivers(&drvs, &ndrvs);
235 if (rc != EOK)
236 return rc;
237
238 rc = table_create(&table);
239 if (rc != EOK) {
240 assert(rc == ENOMEM);
241 goto out;
242 }
243
244 table_header_row(table);
245 table_printf(table, "Driver\t" "Devs\t" "State\n");
246
247 for (i = 0; i < ndrvs; i++) {
248 devs = NULL;
249
250 rc = devman_driver_get_name(drvs[i], drv_name, MAX_NAME_LENGTH);
251 if (rc != EOK)
252 goto skip;
253 rc = devman_driver_get_state(drvs[i], &state);
254 if (rc != EOK)
255 goto skip;
256 rc = devman_driver_get_devices(drvs[i], &devs, &ndevs);
257 if (rc != EOK)
258 goto skip;
259
260 sstate = drv_state_str(state);
261
262 table_printf(table, "%s\t" "%zu\t" "%s\n", drv_name, ndevs, sstate);
263 skip:
264 free(devs);
265 }
266
267 rc = table_print_out(table, stdout);
268 if (rc != EOK)
269 printf("Error printing driver table.\n");
270out:
271 free(drvs);
272 table_destroy(table);
273
274 return rc;
275}
276
277static errno_t drv_show(char *drvname)
278{
279 devman_handle_t *devs;
280 devman_handle_t drvh;
281 devman_handle_t funh;
282 driver_state_t state;
283 const char *sstate;
284 unsigned int score;
285 size_t ndevs;
286 size_t i;
287 errno_t rc;
288
289 rc = devman_driver_get_handle(drvname, &drvh);
290 if (rc != EOK)
291 return rc;
292
293 devs = NULL;
294
295 rc = devman_driver_get_name(drvh, drv_name, MAX_NAME_LENGTH);
296 if (rc != EOK)
297 return rc;
298
299 rc = devman_driver_get_state(drvh, &state);
300 if (rc != EOK)
301 return rc;
302
303 rc = devman_driver_get_devices(drvh, &devs, &ndevs);
304 if (rc != EOK)
305 return rc;
306
307 sstate = drv_state_str(state);
308
309 printf("Driver: %s\n", drv_name);
310 printf("State: %s\n", sstate);
311
312 printf("Attached devices:\n");
313
314 for (i = 0; i < ndevs; i++) {
315 rc = devman_dev_get_parent(devs[i], &funh);
316 if (rc != EOK)
317 goto error;
318
319 rc = devman_fun_get_path(funh, name, MAX_NAME_LENGTH);
320 if (rc != EOK)
321 goto error;
322 printf("\t%s\n", name);
323 }
324
325 printf("Match IDs:\n");
326
327 for (i = 0; true; i++) {
328 rc = devman_driver_get_match_id(drvh, i, name, MAX_NAME_LENGTH,
329 &score);
330 if (rc != EOK)
331 break;
332
333 printf("\t%u %s\n", score, name);
334 }
335
336error:
337 free(devs);
338
339 return EOK;
340}
341
342static errno_t drv_load(const char *drvname)
343{
344 errno_t rc;
345 devman_handle_t drvh;
346
347 rc = devman_driver_get_handle(drvname, &drvh);
348 if (rc != EOK) {
349 printf("Failed resolving driver '%s': %s.\n", drvname, str_error(rc));
350 return rc;
351 }
352
353 rc = devman_driver_load(drvh);
354 if (rc != EOK) {
355 printf("Failed loading driver '%s': %s.\n", drvname, str_error(rc));
356 return rc;
357 }
358
359 return EOK;
360}
361
362static errno_t drv_unload(const char *drvname)
363{
364 errno_t rc;
365 devman_handle_t drvh;
366
367 rc = devman_driver_get_handle(drvname, &drvh);
368 if (rc != EOK) {
369 printf("Failed resolving driver '%s': %s.\n", drvname, str_error(rc));
370 return rc;
371 }
372
373 rc = devman_driver_unload(drvh);
374 if (rc != EOK) {
375 printf("Failed unloading driver '%s': %s.\n", drvname, str_error(rc));
376 return rc;
377 }
378
379 return EOK;
380}
381
382static void print_syntax(void)
383{
384 printf("syntax:\n");
385 printf("\tdevctl\n");
386 printf("\tdevctl online <function>]\n");
387 printf("\tdevctl offline <function>]\n");
388 printf("\tdevctl list-drv\n");
389 printf("\tdevctl show-drv <driver-name>\n");
390 printf("\tdevctl load-drv <driver-name>\n");
391 printf("\tdevctl unload-drv <driver-name>\n");
392}
393
394int main(int argc, char *argv[])
395{
396 errno_t rc;
397
398 if (argc == 1 || argv[1][0] == '-') {
399 if (argc > 1) {
400 if (str_cmp(argv[1], "-v") == 0) {
401 verbose = true;
402 } else {
403 printf(NAME ": Invalid argument '%s'\n", argv[1]);
404 print_syntax();
405 return 1;
406 }
407 }
408 rc = fun_tree_print();
409 if (rc != EOK)
410 return 2;
411 } else if (str_cmp(argv[1], "online") == 0) {
412 if (argc < 3) {
413 printf(NAME ": Argument missing.\n");
414 print_syntax();
415 return 1;
416 }
417
418 rc = fun_online(argv[2]);
419 if (rc != EOK) {
420 return 2;
421 }
422 } else if (str_cmp(argv[1], "offline") == 0) {
423 if (argc < 3) {
424 printf(NAME ": Argument missing.\n");
425 print_syntax();
426 return 1;
427 }
428
429 rc = fun_offline(argv[2]);
430 if (rc != EOK) {
431 return 2;
432 }
433 } else if (str_cmp(argv[1], "quiesce") == 0) {
434 if (argc < 3) {
435 printf(NAME ": Argument missing.\n");
436 print_syntax();
437 return 1;
438 }
439
440 rc = fun_quiesce(argv[2]);
441 if (rc != EOK) {
442 return 2;
443 }
444 } else if (str_cmp(argv[1], "list-drv") == 0) {
445 rc = drv_list();
446 if (rc != EOK)
447 return 2;
448 } else if (str_cmp(argv[1], "show-drv") == 0) {
449 if (argc < 3) {
450 printf(NAME ": Argument missing.\n");
451 print_syntax();
452 return 1;
453 }
454
455 rc = drv_show(argv[2]);
456 if (rc != EOK) {
457 return 2;
458 }
459 } else if (str_cmp(argv[1], "load-drv") == 0) {
460 if (argc < 3) {
461 printf(NAME ": Argument missing.\n");
462 print_syntax();
463 return 1;
464 }
465
466 rc = drv_load(argv[2]);
467 if (rc != EOK)
468 return 2;
469 } else if (str_cmp(argv[1], "unload-drv") == 0) {
470 if (argc < 3) {
471 printf(NAME ": Argument missing.\n");
472 print_syntax();
473 return 1;
474 }
475
476 rc = drv_unload(argv[2]);
477 if (rc != EOK)
478 return 2;
479 } else {
480 printf(NAME ": Invalid argument '%s'.\n", argv[1]);
481 print_syntax();
482 return 1;
483 }
484
485 return 0;
486}
487
488/** @}
489 */
Note: See TracBrowser for help on using the repository browser.