diff options
Diffstat (limited to 'lib/libdevctl')
-rw-r--r-- | lib/libdevctl/Makefile | 6 | ||||
-rw-r--r-- | lib/libdevctl/Makefile.depend | 15 | ||||
-rw-r--r-- | lib/libdevctl/devctl.3 | 450 | ||||
-rw-r--r-- | lib/libdevctl/devctl.c | 206 | ||||
-rw-r--r-- | lib/libdevctl/devctl.h | 48 |
5 files changed, 725 insertions, 0 deletions
diff --git a/lib/libdevctl/Makefile b/lib/libdevctl/Makefile new file mode 100644 index 000000000000..6e32151aeb12 --- /dev/null +++ b/lib/libdevctl/Makefile @@ -0,0 +1,6 @@ +LIB= devctl +SRCS= devctl.c +INCS= devctl.h +MAN= devctl.3 + +.include <bsd.lib.mk> diff --git a/lib/libdevctl/Makefile.depend b/lib/libdevctl/Makefile.depend new file mode 100644 index 000000000000..6ef78fac5cbf --- /dev/null +++ b/lib/libdevctl/Makefile.depend @@ -0,0 +1,15 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/lib/libdevctl/devctl.3 b/lib/libdevctl/devctl.3 new file mode 100644 index 000000000000..c8a4704825c2 --- /dev/null +++ b/lib/libdevctl/devctl.3 @@ -0,0 +1,450 @@ +.\" +.\" Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org> +.\" +.\" 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. +.\" +.Dd April 4, 2019 +.Dt DEVCTL 3 +.Os +.Sh NAME +.Nm devctl , +.Nm devctl_attach , +.Nm devctl_clear_driver , +.Nm devctl_delete , +.Nm devctl_detach , +.Nm devctl_disable , +.Nm devctl_enable , +.Nm devctl_freeze , +.Nm devctl_getpath , +.Nm devctl_rescan , +.Nm devctl_reset , +.Nm devctl_resume , +.Nm devctl_set_driver , +.Nm devctl_suspend , +.Nm devctl_thaw +.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_clear_driver "const char *device" "bool force" +.Ft int +.Fn devctl_delete "const char *device" "bool force" +.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_freeze "void" +.Ft int +.Fn devctl_getpath "const char *device" "const char *locator" "char **buffer" +.Ft int +.Fn devctl_rescan "const char *device" +.Ft int +.Fn devctl_reset "const char *device" "bool detach" +.Ft int +.Fn devctl_resume "const char *device" +.Ft int +.Fn devctl_set_driver "const char *device" "const char *driver" "bool force" +.Ft int +.Fn devctl_suspend "const char *device" +.Ft int +.Fn devctl_thaw "void" +.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_delete +function deletes a device from the device tree. +No +If +.Fa force +is true, +the device is deleted even if the device is physically present. +.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. +.Pp +The +.Fn devctl_clear_driver +function resets a device so that it can be attached to any valid device +driver rather than only drivers with a previously specified name. +This function is used to undo a previous call to +.Fn devctl_set_driver . +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. +After the device's name is reset, +it is reprobed and attached to a suitable device driver if one is found. +.Pp +The +.Fn devctl_rescan +function rescans a bus device checking for devices that have been added or +removed. +.Pp +The +.Fn devctl_getpath +retrieves the path to the +.Fa device +from the kernel using the +.Fa locator +method to construct the path. +The +.Fa buffer +pointer is updated with an allocated buffer that must be freed with +.Xr free 3 . +.Pp +The +.Fn devctl_freeze +function freezes probe and attach processing initiated in response to +drivers being loaded. +.Pp +The +.Fn devctl_thaw +function resumes (thaws the freeze) probe and attach processing +initiated in response to drivers being loaded. +.Pp +The +.Fn devctl_reset +function resets the specified device using bus-specific reset method. +The +.Fa detach +argument, if true, specifies that the device driver is detached before +the reset, and re-attached afterwards. +If false, the device is suspended before the reset, and resumed after. +.Sh RETURN VALUES +.Rv -std devctl_attach devctl_clear_driver devctl_delete devctl_detach \ +devctl_disable devctl_enable devctl_suspend devctl_rescan 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 +.Pp +The +.Fn devctl_clear_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 EINVAL +The device is not configured for a specific device driver name. +.It Bq Er ENXIO +The device driver chosen after reprobing failed to attach. +.El +.Pp +The +.Fn devctl_rescan +function may fail if: +.Bl -tag -width Er +.It Bq Er ENXIO +The device is not attached to a driver. +.It Bq Er ENXIO +The bus driver does not support rescanning. +.El +.Pp +The +.Fn devctl_delete +function may fail if: +.Bl -tag -width Er +.It Bq Er EBUSY +The device is physically present and +.Fa force +is false. +.It Bq Er EINVAL +.Fa dev +is the root device of the device tree. +.El +.Pp +The +.Fn devctl_reset +function may fail if: +.Bl -tag -width Er +.It Bq Er ENXIO +The bus does not implement the reset method. +.It Bq Er ETIMEDOUT +The device failed to respond after the reset in the time limits +specific to the bus. +.El +The +.Fn devctl_reset +function may also return errors caused by the attach, detach, suspend, +and resume methods of the device driver. +.Sh SEE ALSO +.Xr devinfo 3 , +.Xr devstat 3 , +.Xr devctl 8 +.Sh HISTORY +The +.Nm +library first appeared in +.Fx 10.3 . +.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. +.Pp +Similarly, if the device is suspended, and +.Fn devctl_reset +is called on the device with +.Fa detach +set to +.Va false , +the device is resumed by the +.Fn devctl_reset +call. +Or, if the driver for the device is detached manually, and +.Fn devctl_reset +is called on the device with +.Fa detach +set to +.Va true , +device reset re-attaches the driver. diff --git a/lib/libdevctl/devctl.c b/lib/libdevctl/devctl.c new file mode 100644 index 000000000000..bc82d8b7b470 --- /dev/null +++ b/lib/libdevctl/devctl.c @@ -0,0 +1,206 @@ +/*- + * Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org> + * + * 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 <sys/types.h> +#include <sys/bus.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#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)); +} + +int +devctl_clear_driver(const char *device, bool force) +{ + + return (devctl_simple_request(DEV_CLEAR_DRIVER, device, force ? + DEVF_CLEAR_DRIVER_DETACH : 0)); +} + +int +devctl_rescan(const char *device) +{ + + return (devctl_simple_request(DEV_RESCAN, device, 0)); +} + +int +devctl_delete(const char *device, bool force) +{ + + return (devctl_simple_request(DEV_DELETE, device, force ? + DEVF_FORCE_DELETE : 0)); +} + +int +devctl_freeze(void) +{ + + return (devctl_simple_request(DEV_FREEZE, "", 0)); +} + +int +devctl_thaw(void) +{ + + return (devctl_simple_request(DEV_THAW, "", 0)); +} + +int +devctl_reset(const char *device, bool detach) +{ + + return (devctl_simple_request(DEV_RESET, device, detach ? + DEVF_RESET_DETACH : 0)); +} + +#define BUFLEN 1024 + +int +devctl_getpath(const char *device, const char *locator, char **buffer) +{ + struct devreq req; + int serrno; + + memset(&req, 0, sizeof(req)); + if (strlcpy(req.dr_name, device, sizeof(req.dr_name)) >= + sizeof(req.dr_name)) { + errno = EINVAL; + *buffer = NULL; + return (-1); + } + + /* + * Maybe do the request twice. Once to get the length, and then again to + * get the string if BUFLEN bytes is insufficient. + */ + req.dr_flags = 0; + req.dr_buffer.length = BUFLEN; +again: + req.dr_buffer.buffer = malloc(req.dr_buffer.length); + strlcpy(req.dr_buffer.buffer, locator, req.dr_buffer.length); + if (devctl_request(DEV_GET_PATH, &req) == 0) { + *buffer = req.dr_buffer.buffer; + return (0); + } + if (errno == ENAMETOOLONG && req.dr_buffer.length != BUFLEN) { + free(req.dr_buffer.buffer); + goto again; + } + serrno = errno; + free(req.dr_buffer.buffer); + errno = serrno; + *buffer = NULL; + return (-1); +} diff --git a/lib/libdevctl/devctl.h b/lib/libdevctl/devctl.h new file mode 100644 index 000000000000..97f5af05807a --- /dev/null +++ b/lib/libdevctl/devctl.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org> + * + * 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. + */ + +#ifndef __DEVCTL_H__ +#define __DEVCTL_H__ + +#include <stdbool.h> + +__BEGIN_DECLS +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); +int devctl_clear_driver(const char *device, bool force); +int devctl_rescan(const char *device); +int devctl_delete(const char *device, bool force); +int devctl_freeze(void); +int devctl_thaw(void); +int devctl_reset(const char *device, bool detach); +int devctl_getpath(const char *device, const char *locator, char **buffer); +__END_DECLS + +#endif /* !__DEVCTL_H__ */ |