From 64de80195bba295c961a4cdf96dbe0e4979bdf2a Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 6 Feb 2015 16:09:01 +0000 Subject: Add a new device control utility for new-bus devices called devctl. This allows the user to request administrative changes to individual devices such as attach or detaching drivers or disabling and re-enabling devices. - Add a new /dev/devctl2 character device which uses ioctls for device requests. The ioctls use a common 'struct devreq' which is somewhat similar to 'struct ifreq'. - The ioctls identify the device to operate on via a string. This string can either by the device's name, or it can be a bus-specific address. (For unattached devices, a bus address is the only way to locate a device.) Bus drivers register an eventhandler to claim unrecognized device names that the driver recognizes as a valid address. Two buses currently support addresses: ACPI recognizes any device in the ACPI namespace via its full path starting with "\" and the PCI bus driver recognizes an address specification of 'pci[:]::' (identical to the PCI selector strings supported by pciconf). - To make it easier to cut and paste, change the PnP location string in the PCI bus driver to output a full PCI selector string rather than 'slot= function='. - Add a devctl(3) interface in libdevctl which provides a wrapper around the ioctls and is the preferred interface for other userland code. - Add a devctl(8) program which is a simple wrapper around the requests supported by devctl(3). - Add a device_is_suspended() function to check DF_SUSPENDED. - Add a resource_unset_value() function that can be used to remove a hint from the kernel environment. This is used to clear a hint...disabled hint when re-enabling a boot-time disabled device. Reviewed by: imp (parts) Requested by: imp (changing PCI location string) Relnotes: yes --- usr.sbin/devctl/Makefile | 9 ++ usr.sbin/devctl/devctl.8 | 137 +++++++++++++++++++++++ usr.sbin/devctl/devctl.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 usr.sbin/devctl/Makefile create mode 100644 usr.sbin/devctl/devctl.8 create mode 100644 usr.sbin/devctl/devctl.c (limited to 'usr.sbin/devctl') diff --git a/usr.sbin/devctl/Makefile b/usr.sbin/devctl/Makefile new file mode 100644 index 000000000000..5a6e19d761a3 --- /dev/null +++ b/usr.sbin/devctl/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +PROG= devctl +MAN= devctl.8 +MAN= + +LIBADD= devctl + +.include diff --git a/usr.sbin/devctl/devctl.8 b/usr.sbin/devctl/devctl.8 new file mode 100644 index 000000000000..77c803a7af29 --- /dev/null +++ b/usr.sbin/devctl/devctl.8 @@ -0,0 +1,137 @@ +.\" +.\" Copyright (c) 2015 John Baldwin +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd February 5, 2015 +.Dt DEVCTL 8 +.Os +.Sh NAME +.Nm devctl +.Nd device control utility +.Sh SYNOPSIS +.Nm +.Cm attach +.Ar device +.Nm +.Cm detach +.Op Fl f +.Ar device +.Nm +.Cm disable +.Op Fl f +.Ar device +.Nm +.Cm enable +.Ar device +.Nm +.Cm suspend +.Ar device +.Nm +.Cm resume +.Ar device +.Nm +.Cm set driver +.Op Fl f +.Ar device driver +.Sh DESCRIPTION +The +.Nm +utility adjusts the state of individual devices in the kernel's +internal device hierarchy. +Each invocation of +.Nm +consists of a single command followed by command-specific arguments. +Each command operates on a single device specified via the +.Ar device +argument. +The +.Ar device +may be specified either as the name of an existing device or as a +bus-specific address. +More details on supported address formats can be found in +.Xr devctl 3 . +.Pp +The following commands are supported: +.Bl -tag -width indent +.It Cm attach Ar device +Force the kernel to re-probe the device. +If a suitable driver is found, +it is attached to the device. +.It Xo Cm detach +.Op Fl f +.Ar device +.Xc +Detach the device from its current device driver. +If the +.Fl f +flag is specified, +the device driver will be detached even if the device is busy. +.It Xo Cm disable +.Op Fl f +.Ar device +.Xc +Disable a device. +If the device is currently attached to a device driver, +the device driver will be detached from the device, +but the device will retain its current name. +If the +.Fl f +flag is specified, +the device driver will be detached even if the device is busy. +.It Cm enable Ar device +Enable a device. +The device will probe and attach if a suitable device driver is found. +Note that this can re-enable a device disabled at boot time via a +loader tunable. +.It Cm suspend Ar device +Suspend a device. +This may include placing the device in a reduced power state. +.It Cm resume device +Resume a suspended device to a fully working state. +.It Xo Cm set driver +.Op Fl f +.Ar device driver +.Xc +Force the device to use a device driver named +.Ar driver . +If the device is already attached to a device driver and the +.Fl f +flag is specified, +the device will be detached from its current device driver before it is +attached to the new device driver. +If the device is already attached to a device driver and the +.Fl f +flag is not specified, +the device will not be changed. +.El +.Sh SEE ALSO +.Xr devctl 3 , +.Xr devinfo 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 11.0 . diff --git a/usr.sbin/devctl/devctl.c b/usr.sbin/devctl/devctl.c new file mode 100644 index 000000000000..076c6503885e --- /dev/null +++ b/usr.sbin/devctl/devctl.c @@ -0,0 +1,282 @@ +/*- + * Copyright (c) 2014 John Baldwin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct devctl_command { + const char *name; + int (*handler)(int ac, char **av); +}; + +#define DEVCTL_DATASET(name) devctl_ ## name ## _table + +#define DEVCTL_COMMAND(set, name, function) \ + static struct devctl_command function ## _devctl_command = \ + { #name, function }; \ + DATA_SET(DEVCTL_DATASET(set), function ## _devctl_command) + +#define DEVCTL_TABLE(set, name) \ + SET_DECLARE(DEVCTL_DATASET(name), struct devctl_command); \ + \ + static int \ + devctl_ ## name ## _table_handler(int ac, char **av) \ + { \ + return (devctl_table_handler(SET_BEGIN(DEVCTL_DATASET(name)), \ + SET_LIMIT(DEVCTL_DATASET(name)), ac, av)); \ + } \ + DEVCTL_COMMAND(set, name, devctl_ ## name ## _table_handler) + +static int devctl_table_handler(struct devctl_command **start, + struct devctl_command **end, int ac, char **av); + +SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command); + +DEVCTL_TABLE(top, set); + +static void +usage(void) +{ + fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + "usage: devctl attach device", + " devctl detach [-f] device", + " devctl disable [-f] device", + " devctl enable device", + " devctl suspend device", + " devctl resume device", + " devctl set driver [-f] device driver"); + exit(1); +} + +static int +devctl_table_handler(struct devctl_command **start, + struct devctl_command **end, int ac, char **av) +{ + struct devctl_command **cmd; + + if (ac < 2) { + warnx("The %s command requires a sub-command.", av[0]); + return (EINVAL); + } + for (cmd = start; cmd < end; cmd++) { + if (strcmp((*cmd)->name, av[1]) == 0) + return ((*cmd)->handler(ac - 1, av + 1)); + } + + warnx("%s is not a valid sub-command of %s.", av[1], av[0]); + return (ENOENT); +} + +static int +help(int ac __unused, char **av __unused) +{ + + usage(); + return (0); +} +DEVCTL_COMMAND(top, help, help); + +static int +attach(int ac, char **av) +{ + + if (ac != 2) + usage(); + if (devctl_attach(av[1]) < 0) + err(1, "Failed to attach %s", av[1]); + return (0); +} +DEVCTL_COMMAND(top, attach, attach); + +static void +detach_usage(void) +{ + + fprintf(stderr, "usage: devctl detach [-f] device\n"); + exit(1); +} + +static int +detach(int ac, char **av) +{ + bool force; + int ch; + + force = false; + while ((ch = getopt(ac, av, "f")) != -1) + switch (ch) { + case 'f': + force = true; + break; + default: + detach_usage(); + } + ac -= optind; + av += optind; + + if (ac != 1) + detach_usage(); + if (devctl_detach(av[0], force) < 0) + err(1, "Failed to detach %s", av[0]); + return (0); +} +DEVCTL_COMMAND(top, detach, detach); + +static void +disable_usage(void) +{ + + fprintf(stderr, "usage: devctl disable [-f] device\n"); + exit(1); +} + +static int +disable(int ac, char **av) +{ + bool force; + int ch; + + force = false; + while ((ch = getopt(ac, av, "f")) != -1) + switch (ch) { + case 'f': + force = true; + break; + default: + disable_usage(); + } + ac -= optind; + av += optind; + + if (ac != 1) + disable_usage(); + if (devctl_disable(av[0], force) < 0) + err(1, "Failed to disable %s", av[0]); + return (0); +} +DEVCTL_COMMAND(top, disable, disable); + +static int +enable(int ac, char **av) +{ + + if (ac != 2) + usage(); + if (devctl_enable(av[1]) < 0) + err(1, "Failed to enable %s", av[1]); + return (0); +} +DEVCTL_COMMAND(top, enable, enable); + +static int +suspend(int ac, char **av) +{ + + if (ac != 2) + usage(); + if (devctl_suspend(av[1]) < 0) + err(1, "Failed to suspend %s", av[1]); + return (0); +} +DEVCTL_COMMAND(top, suspend, suspend); + +static int +resume(int ac, char **av) +{ + + if (ac != 2) + usage(); + if (devctl_resume(av[1]) < 0) + err(1, "Failed to resume %s", av[1]); + return (0); +} +DEVCTL_COMMAND(top, resume, resume); + +static void +set_driver_usage(void) +{ + + fprintf(stderr, "usage: devctl set driver [-f] device driver\n"); + exit(1); +} + +static int +set_driver(int ac, char **av) +{ + bool force; + int ch; + + force = false; + while ((ch = getopt(ac, av, "f")) != -1) + switch (ch) { + case 'f': + force = true; + break; + default: + set_driver_usage(); + } + ac -= optind; + av += optind; + + if (ac != 2) + set_driver_usage(); + if (devctl_set_driver(av[0], av[1], force) < 0) + err(1, "Failed to set %s driver to %s", av[0], av[1]); + return (0); +} +DEVCTL_COMMAND(set, driver, set_driver); + +int +main(int ac, char *av[]) +{ + struct devctl_command **cmd; + + if (ac == 1) + usage(); + ac--; + av++; + + SET_FOREACH(cmd, DEVCTL_DATASET(top)) { + if (strcmp((*cmd)->name, av[0]) == 0) { + if ((*cmd)->handler(ac, av) != 0) + return (1); + else + return (0); + } + } + warnx("Unknown command %s.", av[0]); + return (1); +} -- cgit v1.2.3