From 571edab7e40427c587347cfdc33ac92bd143836b Mon Sep 17 00:00:00 2001 From: jhb 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 --- lib/Makefile | 1 + lib/libdevctl/Makefile | 8 ++ lib/libdevctl/devctl.3 | 295 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/libdevctl/devctl.c | 124 +++++++++++++++++++++ lib/libdevctl/devctl.h | 42 +++++++ 5 files changed, 470 insertions(+) create mode 100644 lib/libdevctl/Makefile create mode 100644 lib/libdevctl/devctl.3 create mode 100644 lib/libdevctl/devctl.c create mode 100644 lib/libdevctl/devctl.h (limited to 'lib') diff --git a/lib/Makefile b/lib/Makefile index c00af75..fdab453 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -41,6 +41,7 @@ SUBDIR= ${SUBDIR_ORDERED} \ ${_libcom_err} \ libcompat \ libcrypt \ + libdevctl \ libdevinfo \ libdevstat \ libdpv \ diff --git a/lib/libdevctl/Makefile b/lib/libdevctl/Makefile new file mode 100644 index 0000000..74687ec --- /dev/null +++ b/lib/libdevctl/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +LIB= devctl +SRCS= devctl.c +INCS= devctl.h +MAN= devctl.3 + +.include diff --git a/lib/libdevctl/devctl.3 b/lib/libdevctl/devctl.3 new file mode 100644 index 0000000..be869f9 --- /dev/null +++ b/lib/libdevctl/devctl.3 @@ -0,0 +1,295 @@ +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd December 26, 2014 +.Dt DEVCTL 3 +.Os +.Sh NAME +.Nm devctl , +.Nm devctl_attach , +.Nm devctl_detach , +.Nm devctl_disable , +.Nm devctl_enable , +.Nm devctl_resume , +.Nm devctl_suspend +.Nd device control library +.Sh LIBRARY +.Lb libdevctl +.Sh SYNOPSIS +.In devctl.h +.Ft int +.Fn devctl_attach "const char *device" +.Ft int +.Fn devctl_detach "const char *device" "bool force" +.Ft int +.Fn devctl_disable "const char *device" "bool force_detach" +.Ft int +.Fn devctl_enable "const char *device" +.Ft int +.Fn devctl_resume "const char *device" +.Ft int +.Fn devctl_suspend "const char *device" +.Ft int +.Fn devctl_set_driver "const char *device" "const char *driver" "bool force" +.Sh DESCRIPTION +The +.Nm +library adjusts the state of devices in the kernel's internal device +hierarchy. +Each control operation accepts a +.Fa device +argument that identifies the device to adjust. +The +.Fa device +may be specified as either the name of an existing device or as a +bus-specific address. +The following bus-specific address formats are currently supported: +.Bl -tag -offset indent +.It Sy pci Ns Fa domain Ns : Ns Fa bus Ns : Ns Fa slot Ns : Ns Fa function +A PCI device with the specified +.Fa domain , +.Fa bus , +.Fa slot , +and +.Fa function . +.It Sy pci Ns Fa bus Ns : Ns Fa slot Ns : Ns Fa function +A PCI device in domain zero with the specified +.Fa bus , +.Fa slot , +and +.Fa function . +.It Fa handle +A device with an ACPI handle of +.Fa handle . +The handle must be specified as an absolute path and must begin with a +.Dq \e . +.El +.Pp +The +.Fn devctl_attach +function probes a device and attaches a suitable device driver if one is +found. +.Pp +The +.Fn devctl_detach +function detaches a device from its current device driver. +The device is left detached until either a new driver for its parent +bus is loaded or the device is explicitly probed via +.Fn devctl_attach . +If +.Fa force +is true, +the current device driver will be detached even if the device is busy. +.Pp +The +.Fn devctl_disable +function disables 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 +.Fa force_detach +is true, +the current device driver will be detached even if the device is busy. +The device will remain disabled and detached until it is explicitly enabled +via +.Fn devctl_enable . +.Pp +The +.Fn devctl_enable +function re-enables a disabled device. +The device will probe and attach if a suitable device driver is found. +.Pp +The +.Fn devctl_suspend +function suspends a device. +This may include placing the device in a reduced power state, +but any device driver currently attached to the device will remain attached. +.Pp +The +.Fn devctl_resume +function resumes a suspended device to a fully working state. +.Pp +The +.Fn devctl_set_driver +function attaches a device driver named +.Fa driver +to a device. +If the device is already attached and +.Fa force +is false, +the request will fail. +If the device is already attached and +.Fa force +is true, +the device will be detached from its current device driver before it is +attached to the new device driver. +.Sh RETURN VALUES +.Rv -std devctl_attach devctl_detach devctl_disable devctl_enable \ +devctl_suspend devctl_resume devctl_set_driver +.Sh ERRORS +In addition to specific errors noted below, +all of the +.Nm +functions may fail for any of the errors described in +.Xr open 2 +as well as: +.Bl -tag -width Er +.It Bq Er EINVAL +The device name is too long. +.It Bq Er ENOENT +No existing device matches the specified name or location. +.It Bq Er EPERM +The current process is not permitted to adjust the state of +.Fa device . +.El +.Pp +The +.Fn devctl_attach +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The device is already attached. +.It Bq Er ENOMEM +An internal memory allocation request failed. +.It Bq Er ENXIO +The device is disabled. +.It Bq Er ENXIO +No suitable driver for the device could be found, +or the driver failed to attach. +.El +.Pp +The +.Fn devctl_detach +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The current device driver for +.Fa device +is busy and cannot detach at this time. +Note that some drivers may return this even if +.Fa force +is true. +.It Bq Er ENXIO +The device is not attached to a driver. +.It Bq Er ENXIO +The current device driver for +.Fa device +does not support detaching. +.El +.Pp +The +.Fn devctl_enable +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The device is already enabled. +.It Bq Er ENOMEM +An internal memory allocation request failed. +.It Bq Er ENXIO +No suitable driver for the device could be found, +or the driver failed to attach. +.El +.Pp +The +.Fn devctl_disable +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The current device driver for +.Fa device +is busy and cannot detach at this time. +Note that some drivers may return this even if +.Fa force_detach +is true. +.It Bq Er ENXIO +The device is already disabled. +.It Bq Er ENXIO +The current device driver for +.Fa device +does not support detaching. +.El +.Pp +The +.Fn devctl_suspend +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The device is already suspended. +.It Bq Er EINVAL +The device to be suspended is the root bus device. +.El +.Pp +The +.Fn devctl_resume +function may fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The device is not suspended. +.It Bq Er EINVAL +The device to be resumed is the root bus device. +.El +.Pp +The +.Fn devctl_set_driver +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The device is currently attached to a device driver and +.Fa force +is false. +.It Bq Er EBUSY +The current device driver for +.Fa device +is busy and cannot detach at this time. +.It Bq Er EFAULT +The +.Fa driver +argument points outside the process' allocated address space. +.It Bq Er ENOENT +No device driver with the requested name exists. +.It Bq Er ENOMEM +An internal memory allocation request failed. +.It Bq Er ENXIO +The device is disabled. +.It Bq Er ENXIO +The new device driver failed to attach. +.El +.Sh SEE ALSO +.Xr devinfo 3 , +.Xr devstat 3 , +.Xr devctl 8 +.Sh HISTORY +The +.Nm +library first appeared in +.Fx 11.0 . +.Sh BUGS +If a device is suspended individually via +.Fn devctl_suspend +and the entire machine is subsequently suspended, +the device will be resumed when the machine resumes. diff --git a/lib/libdevctl/devctl.c b/lib/libdevctl/devctl.c new file mode 100644 index 0000000..7be431e --- /dev/null +++ b/lib/libdevctl/devctl.c @@ -0,0 +1,124 @@ +/*- + * 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 "devctl.h" + +static int +devctl_request(u_long cmd, struct devreq *req) +{ + static int devctl2_fd = -1; + + if (devctl2_fd == -1) { + devctl2_fd = open("/dev/devctl2", O_RDONLY); + if (devctl2_fd == -1) + return (-1); + } + return (ioctl(devctl2_fd, cmd, req)); +} + +static int +devctl_simple_request(u_long cmd, const char *name, int flags) +{ + struct devreq req; + + memset(&req, 0, sizeof(req)); + if (strlcpy(req.dr_name, name, sizeof(req.dr_name)) >= + sizeof(req.dr_name)) { + errno = EINVAL; + return (-1); + } + req.dr_flags = flags; + return (devctl_request(cmd, &req)); +} + +int +devctl_attach(const char *device) +{ + + return (devctl_simple_request(DEV_ATTACH, device, 0)); +} + +int +devctl_detach(const char *device, bool force) +{ + + return (devctl_simple_request(DEV_DETACH, device, force ? + DEVF_FORCE_DETACH : 0)); +} + +int +devctl_enable(const char *device) +{ + + return (devctl_simple_request(DEV_ENABLE, device, 0)); +} + +int +devctl_disable(const char *device, bool force_detach) +{ + + return (devctl_simple_request(DEV_DISABLE, device, force_detach ? + DEVF_FORCE_DETACH : 0)); +} + +int +devctl_suspend(const char *device) +{ + + return (devctl_simple_request(DEV_SUSPEND, device, 0)); +} + +int +devctl_resume(const char *device) +{ + + return (devctl_simple_request(DEV_RESUME, device, 0)); +} + +int +devctl_set_driver(const char *device, const char *driver, bool force) +{ + struct devreq req; + + memset(&req, 0, sizeof(req)); + if (strlcpy(req.dr_name, device, sizeof(req.dr_name)) >= + sizeof(req.dr_name)) { + errno = EINVAL; + return (-1); + } + req.dr_data = __DECONST(char *, driver); + if (force) + req.dr_flags |= DEVF_SET_DRIVER_DETACH; + return (devctl_request(DEV_SET_DRIVER, &req)); +} diff --git a/lib/libdevctl/devctl.h b/lib/libdevctl/devctl.h new file mode 100644 index 0000000..f773b11 --- /dev/null +++ b/lib/libdevctl/devctl.h @@ -0,0 +1,42 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef __DEVCTL_H__ +#define __DEVCTL_H__ + +#include + +int devctl_attach(const char *device); +int devctl_detach(const char *device, bool force); +int devctl_enable(const char *device); +int devctl_disable(const char *device, bool force_detach); +int devctl_suspend(const char *device); +int devctl_resume(const char *device); +int devctl_set_driver(const char *device, const char *driver, bool force); + +#endif /* !__DEVCTL_H__ */ -- cgit v1.1