1/*- 2 * Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27#include <sys/linker_set.h> 28#include <devctl.h> 29#include <err.h> 30#include <errno.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <strings.h> 35#include <unistd.h> 36 37struct devctl_command { 38 const char *name; 39 int (*handler)(int ac, char **av); 40}; 41 42#define DEVCTL_DATASET(name) devctl_ ## name ## _table 43 44#define DEVCTL_COMMAND(set, name, function) \ 45 static struct devctl_command function ## _devctl_command = \ 46 { #name, function }; \ 47 DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command) 48 49#define DEVCTL_TABLE(set, name) \ 50 SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command); \ 51 \ 52 static int \ 53 devctl_ ## name ## _table_handler(int ac, char **av) \ 54 { \ 55 return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \ 56 SET_LIMIT(DEVCTL_DATASET(name)), ac, av)); \ 57 } \ 58 DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler) 59 60static int devctl_table_handler(struct devctl_command **start, 61 struct devctl_command **end, int ac, char **av); 62 63SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command); 64 65DEVCTL_TABLE(top, clear); 66DEVCTL_TABLE(top, set); 67 68static void 69usage(void) 70{ 71 fprintf(stderr, 72 "usage: devctl attach device\n" 73 " devctl detach [-f] device\n" 74 " devctl disable [-f] device\n" 75 " devctl enable device\n" 76 " devctl suspend device\n" 77 " devctl resume device\n" 78 " devctl set driver [-f] device driver\n" 79 " devctl clear driver [-f] device\n" 80 " devctl rescan device\n" 81 " devctl delete [-f] device\n" 82 " devctl freeze\n" 83 " devctl thaw\n" 84 " devctl reset [-d] device\n" 85 " devctl getpath locator device\n" 86 ); 87 exit(1); 88} 89 90static int 91devctl_table_handler(struct devctl_command **start, 92 struct devctl_command **end, int ac, char **av) 93{ 94 struct devctl_command **cmd; 95 96 if (ac < 2) { 97 warnx("The %s command requires a sub-command.", av[0]); 98 return (EINVAL); 99 } 100 for (cmd = start; cmd < end; cmd++) { 101 if (strcmp((*cmd)->name, av[1]) == 0) 102 return ((*cmd)->handler(ac - 1, av + 1)); 103 } 104 105 warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 106 return (ENOENT); 107} 108 109static int 110help(int ac __unused, char **av __unused) 111{ 112 113 usage(); 114 return (0); 115} 116DEVCTL_COMMAND(top, help, help); 117 118static int 119attach(int ac, char **av) 120{ 121 122 if (ac != 2) 123 usage(); 124 if (devctl_attach(av[1]) < 0) 125 err(1, "Failed to attach %s", av[1]); 126 return (0); 127} 128DEVCTL_COMMAND(top, attach, attach); 129 130static void 131detach_usage(void) 132{ 133 134 fprintf(stderr, "usage: devctl detach [-f] device\n"); 135 exit(1); 136} 137 138static int 139detach(int ac, char **av) 140{ 141 bool force; 142 int ch; 143 144 force = false; 145 while ((ch = getopt(ac, av, "f")) != -1) 146 switch (ch) { 147 case 'f': 148 force = true; 149 break; 150 default: 151 detach_usage(); 152 } 153 ac -= optind; 154 av += optind; 155 156 if (ac != 1) 157 detach_usage(); 158 if (devctl_detach(av[0], force) < 0) 159 err(1, "Failed to detach %s", av[0]); 160 return (0); 161} 162DEVCTL_COMMAND(top, detach, detach); 163 164static void 165disable_usage(void) 166{ 167 168 fprintf(stderr, "usage: devctl disable [-f] device\n"); 169 exit(1); 170} 171 172static int 173disable(int ac, char **av) 174{ 175 bool force; 176 int ch; 177 178 force = false; 179 while ((ch = getopt(ac, av, "f")) != -1) 180 switch (ch) { 181 case 'f': 182 force = true; 183 break; 184 default: 185 disable_usage(); 186 } 187 ac -= optind; 188 av += optind; 189 190 if (ac != 1) 191 disable_usage(); 192 if (devctl_disable(av[0], force) < 0) 193 err(1, "Failed to disable %s", av[0]); 194 return (0); 195} 196DEVCTL_COMMAND(top, disable, disable); 197 198static int 199enable(int ac, char **av) 200{ 201 202 if (ac != 2) 203 usage(); 204 if (devctl_enable(av[1]) < 0) 205 err(1, "Failed to enable %s", av[1]); 206 return (0); 207} 208DEVCTL_COMMAND(top, enable, enable); 209 210static int 211suspend(int ac, char **av) 212{ 213 214 if (ac != 2) 215 usage(); 216 if (devctl_suspend(av[1]) < 0) 217 err(1, "Failed to suspend %s", av[1]); 218 return (0); 219} 220DEVCTL_COMMAND(top, suspend, suspend); 221 222static int 223resume(int ac, char **av) 224{ 225 226 if (ac != 2) 227 usage(); 228 if (devctl_resume(av[1]) < 0) 229 err(1, "Failed to resume %s", av[1]); 230 return (0); 231} 232DEVCTL_COMMAND(top, resume, resume); 233 234static void 235set_driver_usage(void) 236{ 237 238 fprintf(stderr, "usage: devctl set driver [-f] device driver\n"); 239 exit(1); 240} 241 242static int 243set_driver(int ac, char **av) 244{ 245 bool force; 246 int ch; 247 248 force = false; 249 while ((ch = getopt(ac, av, "f")) != -1) 250 switch (ch) { 251 case 'f': 252 force = true; 253 break; 254 default: 255 set_driver_usage(); 256 } 257 ac -= optind; 258 av += optind; 259 260 if (ac != 2) 261 set_driver_usage(); 262 if (devctl_set_driver(av[0], av[1], force) < 0) 263 err(1, "Failed to set %s driver to %s", av[0], av[1]); 264 return (0); 265} 266DEVCTL_COMMAND(set, driver, set_driver); 267 268static void 269clear_driver_usage(void) 270{ 271 272 fprintf(stderr, "usage: devctl clear driver [-f] device\n"); 273 exit(1); 274} 275 276static int 277clear_driver(int ac, char **av) 278{ 279 bool force; 280 int ch; 281 282 force = false; 283 while ((ch = getopt(ac, av, "f")) != -1) 284 switch (ch) { 285 case 'f': 286 force = true; 287 break; 288 default: 289 clear_driver_usage(); 290 } 291 ac -= optind; 292 av += optind; 293 294 if (ac != 1) 295 clear_driver_usage(); 296 if (devctl_clear_driver(av[0], force) < 0) 297 err(1, "Failed to clear %s driver", av[0]); 298 return (0); 299} 300DEVCTL_COMMAND(clear, driver, clear_driver); 301 302static int 303rescan(int ac, char **av) 304{ 305 306 if (ac != 2) 307 usage(); 308 if (devctl_rescan(av[1]) < 0) 309 err(1, "Failed to rescan %s", av[1]); 310 return (0); 311} 312DEVCTL_COMMAND(top, rescan, rescan); 313 314static void 315delete_usage(void) 316{ 317 318 fprintf(stderr, "usage: devctl delete [-f] device\n"); 319 exit(1); 320} 321 322static int 323delete(int ac, char **av) 324{ 325 bool force; 326 int ch; 327 328 force = false; 329 while ((ch = getopt(ac, av, "f")) != -1) 330 switch (ch) { 331 case 'f': 332 force = true; 333 break; 334 default: 335 delete_usage(); 336 } 337 ac -= optind; 338 av += optind; 339 340 if (ac != 1) 341 delete_usage(); 342 if (devctl_delete(av[0], force) < 0) 343 err(1, "Failed to delete %s", av[0]); 344 return (0); 345} 346DEVCTL_COMMAND(top, delete, delete); 347 348static void 349freeze_usage(void) 350{ 351 352 fprintf(stderr, "usage: devctl freeze\n"); 353 exit(1); 354} 355 356static int 357freeze(int ac, char **av __unused) 358{ 359 360 if (ac != 1) 361 freeze_usage(); 362 if (devctl_freeze() < 0) 363 err(1, "Failed to freeze probe/attach"); 364 return (0); 365} 366DEVCTL_COMMAND(top, freeze, freeze); 367 368static void 369thaw_usage(void) 370{ 371 372 fprintf(stderr, "usage: devctl thaw\n"); 373 exit(1); 374} 375 376static int 377thaw(int ac, char **av __unused) 378{ 379 380 if (ac != 1) 381 thaw_usage(); 382 if (devctl_thaw() < 0) 383 err(1, "Failed to thaw probe/attach"); 384 return (0); 385} 386DEVCTL_COMMAND(top, thaw, thaw); 387 388static void 389reset_usage(void) 390{ 391 392 fprintf(stderr, "usage: devctl reset [-d] device\n"); 393 exit(1); 394} 395 396static int 397reset(int ac, char **av) 398{ 399 bool detach_drv; 400 int ch; 401 402 detach_drv = false; 403 while ((ch = getopt(ac, av, "d")) != -1) 404 switch (ch) { 405 case 'd': 406 detach_drv = true; 407 break; 408 default: 409 reset_usage(); 410 } 411 ac -= optind; 412 av += optind; 413 414 if (ac != 1) 415 reset_usage(); 416 if (devctl_reset(av[0], detach_drv) < 0) 417 err(1, "Failed to reset %s", av[0]); 418 return (0); 419} 420DEVCTL_COMMAND(top, reset, reset); 421 422static int 423getpath(int ac, char **av) 424{ 425 char *buffer = NULL; 426 427 if (ac != 3) 428 usage(); 429 if (devctl_getpath(av[2], av[1], &buffer) < 0) 430 err(1, "Failed to get path via %s to %s", av[1], av[2]); 431 printf("%s\n", buffer); 432 free(buffer); 433 return (0); 434} 435DEVCTL_COMMAND(top, getpath, getpath); 436 437int 438main(int ac, char *av[]) 439{ 440 struct devctl_command **cmd; 441 442 if (ac == 1) 443 usage(); 444 ac--; 445 av++; 446 447 SET_FOREACH(cmd, DEVCTL_DATASET(top)) { 448 if (strcmp((*cmd)->name, av[0]) == 0) { 449 if ((*cmd)->handler(ac, av) != 0) 450 return (1); 451 else 452 return (0); 453 } 454 } 455 warnx("Unknown command %s.", av[0]); 456 return (1); 457} 458