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__FBSDID("$FreeBSD$"); 28 29#include <sys/linker_set.h> 30#include <devctl.h> 31#include <err.h> 32#include <errno.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <strings.h> 37#include <unistd.h> 38 39struct devctl_command { 40 const char *name; 41 int (*handler)(int ac, char **av); 42}; 43 44#define DEVCTL_DATASET(name) devctl_ ## name ## _table 45 46#define DEVCTL_COMMAND(set, name, function) \ 47 static struct devctl_command function ## _devctl_command = \ 48 { #name, function }; \ 49 DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command) 50 51#define DEVCTL_TABLE(set, name) \ 52 SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command); \ 53 \ 54 static int \ 55 devctl_ ## name ## _table_handler(int ac, char **av) \ 56 { \ 57 return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \ 58 SET_LIMIT(DEVCTL_DATASET(name)), ac, av)); \ 59 } \ 60 DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler) 61 62static int devctl_table_handler(struct devctl_command **start, 63 struct devctl_command **end, int ac, char **av); 64 65SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command); 66 67DEVCTL_TABLE(top, clear); 68DEVCTL_TABLE(top, set); 69 70static void 71usage(void) 72{ 73 fprintf(stderr, 74 "usage: devctl attach device\n" 75 " devctl detach [-f] device\n" 76 " devctl disable [-f] device\n" 77 " devctl enable device\n" 78 " devctl suspend device\n" 79 " devctl resume device\n" 80 " devctl set driver [-f] device driver\n" 81 " devctl clear driver [-f] device\n" 82 " devctl rescan device\n" 83 " devctl delete [-f] device\n" 84 " devctl freeze\n" 85 " devctl thaw\n" 86 " devctl reset [-d] device\n" 87 ); 88 exit(1); 89} 90 91static int 92devctl_table_handler(struct devctl_command **start, 93 struct devctl_command **end, int ac, char **av) 94{ 95 struct devctl_command **cmd; 96 97 if (ac < 2) { 98 warnx("The %s command requires a sub-command.", av[0]); 99 return (EINVAL); 100 } 101 for (cmd = start; cmd < end; cmd++) { 102 if (strcmp((*cmd)->name, av[1]) == 0) 103 return ((*cmd)->handler(ac - 1, av + 1)); 104 } 105 106 warnx("%s is not a valid sub-command of %s.", av[1], av[0]); 107 return (ENOENT); 108} 109 110static int 111help(int ac __unused, char **av __unused) 112{ 113 114 usage(); 115 return (0); 116} 117DEVCTL_COMMAND(top, help, help); 118 119static int 120attach(int ac, char **av) 121{ 122 123 if (ac != 2) 124 usage(); 125 if (devctl_attach(av[1]) < 0) 126 err(1, "Failed to attach %s", av[1]); 127 return (0); 128} 129DEVCTL_COMMAND(top, attach, attach); 130 131static void 132detach_usage(void) 133{ 134 135 fprintf(stderr, "usage: devctl detach [-f] device\n"); 136 exit(1); 137} 138 139static int 140detach(int ac, char **av) 141{ 142 bool force; 143 int ch; 144 145 force = false; 146 while ((ch = getopt(ac, av, "f")) != -1) 147 switch (ch) { 148 case 'f': 149 force = true; 150 break; 151 default: 152 detach_usage(); 153 } 154 ac -= optind; 155 av += optind; 156 157 if (ac != 1) 158 detach_usage(); 159 if (devctl_detach(av[0], force) < 0) 160 err(1, "Failed to detach %s", av[0]); 161 return (0); 162} 163DEVCTL_COMMAND(top, detach, detach); 164 165static void 166disable_usage(void) 167{ 168 169 fprintf(stderr, "usage: devctl disable [-f] device\n"); 170 exit(1); 171} 172 173static int 174disable(int ac, char **av) 175{ 176 bool force; 177 int ch; 178 179 force = false; 180 while ((ch = getopt(ac, av, "f")) != -1) 181 switch (ch) { 182 case 'f': 183 force = true; 184 break; 185 default: 186 disable_usage(); 187 } 188 ac -= optind; 189 av += optind; 190 191 if (ac != 1) 192 disable_usage(); 193 if (devctl_disable(av[0], force) < 0) 194 err(1, "Failed to disable %s", av[0]); 195 return (0); 196} 197DEVCTL_COMMAND(top, disable, disable); 198 199static int 200enable(int ac, char **av) 201{ 202 203 if (ac != 2) 204 usage(); 205 if (devctl_enable(av[1]) < 0) 206 err(1, "Failed to enable %s", av[1]); 207 return (0); 208} 209DEVCTL_COMMAND(top, enable, enable); 210 211static int 212suspend(int ac, char **av) 213{ 214 215 if (ac != 2) 216 usage(); 217 if (devctl_suspend(av[1]) < 0) 218 err(1, "Failed to suspend %s", av[1]); 219 return (0); 220} 221DEVCTL_COMMAND(top, suspend, suspend); 222 223static int 224resume(int ac, char **av) 225{ 226 227 if (ac != 2) 228 usage(); 229 if (devctl_resume(av[1]) < 0) 230 err(1, "Failed to resume %s", av[1]); 231 return (0); 232} 233DEVCTL_COMMAND(top, resume, resume); 234 235static void 236set_driver_usage(void) 237{ 238 239 fprintf(stderr, "usage: devctl set driver [-f] device driver\n"); 240 exit(1); 241} 242 243static int 244set_driver(int ac, char **av) 245{ 246 bool force; 247 int ch; 248 249 force = false; 250 while ((ch = getopt(ac, av, "f")) != -1) 251 switch (ch) { 252 case 'f': 253 force = true; 254 break; 255 default: 256 set_driver_usage(); 257 } 258 ac -= optind; 259 av += optind; 260 261 if (ac != 2) 262 set_driver_usage(); 263 if (devctl_set_driver(av[0], av[1], force) < 0) 264 err(1, "Failed to set %s driver to %s", av[0], av[1]); 265 return (0); 266} 267DEVCTL_COMMAND(set, driver, set_driver); 268 269static void 270clear_driver_usage(void) 271{ 272 273 fprintf(stderr, "usage: devctl clear driver [-f] device\n"); 274 exit(1); 275} 276 277static int 278clear_driver(int ac, char **av) 279{ 280 bool force; 281 int ch; 282 283 force = false; 284 while ((ch = getopt(ac, av, "f")) != -1) 285 switch (ch) { 286 case 'f': 287 force = true; 288 break; 289 default: 290 clear_driver_usage(); 291 } 292 ac -= optind; 293 av += optind; 294 295 if (ac != 1) 296 clear_driver_usage(); 297 if (devctl_clear_driver(av[0], force) < 0) 298 err(1, "Failed to clear %s driver", av[0]); 299 return (0); 300} 301DEVCTL_COMMAND(clear, driver, clear_driver); 302 303static int 304rescan(int ac, char **av) 305{ 306 307 if (ac != 2) 308 usage(); 309 if (devctl_rescan(av[1]) < 0) 310 err(1, "Failed to rescan %s", av[1]); 311 return (0); 312} 313DEVCTL_COMMAND(top, rescan, rescan); 314 315static void 316delete_usage(void) 317{ 318 319 fprintf(stderr, "usage: devctl delete [-f] device\n"); 320 exit(1); 321} 322 323static int 324delete(int ac, char **av) 325{ 326 bool force; 327 int ch; 328 329 force = false; 330 while ((ch = getopt(ac, av, "f")) != -1) 331 switch (ch) { 332 case 'f': 333 force = true; 334 break; 335 default: 336 delete_usage(); 337 } 338 ac -= optind; 339 av += optind; 340 341 if (ac != 1) 342 delete_usage(); 343 if (devctl_delete(av[0], force) < 0) 344 err(1, "Failed to delete %s", av[0]); 345 return (0); 346} 347DEVCTL_COMMAND(top, delete, delete); 348 349static void 350freeze_usage(void) 351{ 352 353 fprintf(stderr, "usage: devctl freeze\n"); 354 exit(1); 355} 356 357static int 358freeze(int ac, char **av __unused) 359{ 360 361 if (ac != 1) 362 freeze_usage(); 363 if (devctl_freeze() < 0) 364 err(1, "Failed to freeze probe/attach"); 365 return (0); 366} 367DEVCTL_COMMAND(top, freeze, freeze); 368 369static void 370thaw_usage(void) 371{ 372 373 fprintf(stderr, "usage: devctl thaw\n"); 374 exit(1); 375} 376 377static int 378thaw(int ac, char **av __unused) 379{ 380 381 if (ac != 1) 382 thaw_usage(); 383 if (devctl_thaw() < 0) 384 err(1, "Failed to thaw probe/attach"); 385 return (0); 386} 387DEVCTL_COMMAND(top, thaw, thaw); 388 389static void 390reset_usage(void) 391{ 392 393 fprintf(stderr, "usage: devctl reset [-d] device\n"); 394 exit(1); 395} 396 397static int 398reset(int ac, char **av) 399{ 400 bool detach_drv; 401 int ch; 402 403 detach_drv = false; 404 while ((ch = getopt(ac, av, "d")) != -1) 405 switch (ch) { 406 case 'd': 407 detach_drv = true; 408 break; 409 default: 410 reset_usage(); 411 } 412 ac -= optind; 413 av += optind; 414 415 if (ac != 1) 416 reset_usage(); 417 if (devctl_reset(av[0], detach_drv) < 0) 418 err(1, "Failed to reset %s", av[0]); 419 return (0); 420} 421DEVCTL_COMMAND(top, reset, reset); 422 423int 424main(int ac, char *av[]) 425{ 426 struct devctl_command **cmd; 427 428 if (ac == 1) 429 usage(); 430 ac--; 431 av++; 432 433 SET_FOREACH(cmd, DEVCTL_DATASET(top)) { 434 if (strcmp((*cmd)->name, av[0]) == 0) { 435 if ((*cmd)->handler(ac, av) != 0) 436 return (1); 437 else 438 return (0); 439 } 440 } 441 warnx("Unknown command %s.", av[0]); 442 return (1); 443} 444