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[<domain>:]<bus>:<slot>:<func>' (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=<slot> function=<func>'. - 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.<driver>.<unit>.disabled hint when re-enabling a boot-time disabled device. Reviewed by: imp (parts) Requested by: imp (changing PCI location string) Relnotes: yes
This commit is contained in:
parent
b01589c956
commit
64de80195b
@ -41,6 +41,7 @@ LINE("libcrypt", "Crypt Library (libcrypt, \\-lcrypt)")
|
||||
LINE("libcurses", "Curses Library (libcurses, \\-lcurses)")
|
||||
LINE("libcuse", "Userland Character Device Library (libcuse, \\-lcuse)")
|
||||
LINE("libdevattr", "Device attribute and event library (libdevattr, \\-ldevattr)")
|
||||
LINE("libdevctl", "Device Control Library (libdevctl, \\-ldevctl)")
|
||||
LINE("libdevinfo", "Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)")
|
||||
LINE("libdevstat", "Device Statistics Library (libdevstat, \\-ldevstat)")
|
||||
LINE("libdisk", "Interface to Slice and Partition Labels Library (libdisk, \\-ldisk)")
|
||||
|
@ -41,6 +41,7 @@ SUBDIR= ${SUBDIR_ORDERED} \
|
||||
${_libcom_err} \
|
||||
libcompat \
|
||||
libcrypt \
|
||||
libdevctl \
|
||||
libdevinfo \
|
||||
libdevstat \
|
||||
libdpv \
|
||||
|
8
lib/libdevctl/Makefile
Normal file
8
lib/libdevctl/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
# $FreeBSD$
|
||||
|
||||
LIB= devctl
|
||||
SRCS= devctl.c
|
||||
INCS= devctl.h
|
||||
MAN= devctl.3
|
||||
|
||||
.include <bsd.lib.mk>
|
295
lib/libdevctl/devctl.3
Normal file
295
lib/libdevctl/devctl.3
Normal file
@ -0,0 +1,295 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
|
||||
.\" 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.
|
124
lib/libdevctl/devctl.c
Normal file
124
lib/libdevctl/devctl.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/bus.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.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));
|
||||
}
|
42
lib/libdevctl/devctl.h
Normal file
42
lib/libdevctl/devctl.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
|
||||
* 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 <stdbool.h>
|
||||
|
||||
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__ */
|
@ -39,6 +39,7 @@ LIBCRYPT?= ${DESTDIR}${LIBDIR}/libcrypt.a
|
||||
LIBCRYPTO?= ${DESTDIR}${LIBDIR}/libcrypto.a
|
||||
LIBCTF?= ${DESTDIR}${LIBDIR}/libctf.a
|
||||
LIBCURSES?= ${DESTDIR}${LIBDIR}/libcurses.a
|
||||
LIBDEVCTL?= ${DESTDIR}${LIBDIR}/libdevctl.a
|
||||
LIBDEVINFO?= ${DESTDIR}${LIBDIR}/libdevinfo.a
|
||||
LIBDEVSTAT?= ${DESTDIR}${LIBDIR}/libdevstat.a
|
||||
LIBDIALOG?= ${DESTDIR}${LIBDIR}/libdialog.a
|
||||
|
@ -72,6 +72,7 @@ _LIBRARIES= \
|
||||
ctf \
|
||||
cuse \
|
||||
cxxrt \
|
||||
devctl \
|
||||
devinfo \
|
||||
devstat \
|
||||
dialog \
|
||||
|
@ -101,6 +101,7 @@ int acpi_quirks;
|
||||
/* Supported sleep states. */
|
||||
static BOOLEAN acpi_sleep_states[ACPI_S_STATE_COUNT];
|
||||
|
||||
static void acpi_lookup(void *arg, const char *name, device_t *dev);
|
||||
static int acpi_modevent(struct module *mod, int event, void *junk);
|
||||
static int acpi_probe(device_t dev);
|
||||
static int acpi_attach(device_t dev);
|
||||
@ -671,8 +672,10 @@ acpi_attach(device_t dev)
|
||||
/* Register ACPI again to pass the correct argument of pm_func. */
|
||||
power_pm_register(POWER_PM_TYPE_ACPI, acpi_pm_func, sc);
|
||||
|
||||
if (!acpi_disabled("bus"))
|
||||
if (!acpi_disabled("bus")) {
|
||||
EVENTHANDLER_REGISTER(dev_lookup, acpi_lookup, NULL, 1000);
|
||||
acpi_probe_children(dev);
|
||||
}
|
||||
|
||||
/* Update all GPEs and enable runtime GPEs. */
|
||||
status = AcpiUpdateAllGpes();
|
||||
@ -3401,6 +3404,31 @@ acpi_disabled(char *subsys)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_lookup(void *arg, const char *name, device_t *dev)
|
||||
{
|
||||
ACPI_HANDLE handle;
|
||||
|
||||
if (*dev != NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Allow any handle name that is specified as an absolute path and
|
||||
* starts with '\'. We could restrict this to \_SB and friends,
|
||||
* but see acpi_probe_children() for notes on why we scan the entire
|
||||
* namespace for devices.
|
||||
*
|
||||
* XXX: The pathname argument to AcpiGetHandle() should be fixed to
|
||||
* be const.
|
||||
*/
|
||||
if (name[0] != '\\')
|
||||
return;
|
||||
if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, __DECONST(char *, name),
|
||||
&handle)))
|
||||
return;
|
||||
*dev = acpi_get_device(handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Control interface.
|
||||
*
|
||||
|
@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/systm.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/linker.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/conf.h>
|
||||
@ -4824,8 +4825,8 @@ pci_child_location_str_method(device_t dev, device_t child, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
|
||||
snprintf(buf, buflen, "slot=%d function=%d", pci_get_slot(child),
|
||||
pci_get_function(child));
|
||||
snprintf(buf, buflen, "pci%d:%d:%d:%d", pci_get_domain(child),
|
||||
pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -4855,10 +4856,60 @@ pci_assign_interrupt_method(device_t dev, device_t child)
|
||||
cfg->intpin));
|
||||
}
|
||||
|
||||
static void
|
||||
pci_lookup(void *arg, const char *name, device_t *dev)
|
||||
{
|
||||
long val;
|
||||
char *end;
|
||||
int domain, bus, slot, func;
|
||||
|
||||
if (*dev != NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Accept pciconf-style selectors of either pciD:B:S:F or
|
||||
* pciB:S:F. In the latter case, the domain is assumed to
|
||||
* be zero.
|
||||
*/
|
||||
if (strncmp(name, "pci", 3) != 0)
|
||||
return;
|
||||
val = strtol(name + 3, &end, 10);
|
||||
if (val < 0 || val > INT_MAX || *end != ':')
|
||||
return;
|
||||
domain = val;
|
||||
val = strtol(end + 1, &end, 10);
|
||||
if (val < 0 || val > INT_MAX || *end != ':')
|
||||
return;
|
||||
bus = val;
|
||||
val = strtol(end + 1, &end, 10);
|
||||
if (val < 0 || val > INT_MAX)
|
||||
return;
|
||||
slot = val;
|
||||
if (*end == ':') {
|
||||
val = strtol(end + 1, &end, 10);
|
||||
if (val < 0 || val > INT_MAX || *end != '\0')
|
||||
return;
|
||||
func = val;
|
||||
} else if (*end == '\0') {
|
||||
func = slot;
|
||||
slot = bus;
|
||||
bus = domain;
|
||||
domain = 0;
|
||||
} else
|
||||
return;
|
||||
|
||||
if (domain > PCI_DOMAINMAX || bus > PCI_BUSMAX || slot > PCI_SLOTMAX ||
|
||||
func > PCIE_ARI_FUNCMAX || (slot != 0 && func > PCI_FUNCMAX))
|
||||
return;
|
||||
|
||||
*dev = pci_find_dbsf(domain, bus, slot, func);
|
||||
}
|
||||
|
||||
static int
|
||||
pci_modevent(module_t mod, int what, void *arg)
|
||||
{
|
||||
static struct cdev *pci_cdev;
|
||||
static eventhandler_tag tag;
|
||||
|
||||
switch (what) {
|
||||
case MOD_LOAD:
|
||||
@ -4867,9 +4918,13 @@ pci_modevent(module_t mod, int what, void *arg)
|
||||
pci_cdev = make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644,
|
||||
"pci");
|
||||
pci_load_vendor_data();
|
||||
tag = EVENTHANDLER_REGISTER(dev_lookup, pci_lookup, NULL,
|
||||
1000);
|
||||
break;
|
||||
|
||||
case MOD_UNLOAD:
|
||||
if (tag != NULL)
|
||||
EVENTHANDLER_DEREGISTER(dev_lookup, tag);
|
||||
destroy_dev(pci_cdev);
|
||||
break;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/queue.h>
|
||||
@ -139,6 +140,8 @@ struct device {
|
||||
static MALLOC_DEFINE(M_BUS, "bus", "Bus data structures");
|
||||
static MALLOC_DEFINE(M_BUS_SC, "bus-sc", "Bus data structures, softc");
|
||||
|
||||
static void devctl2_init(void);
|
||||
|
||||
#ifdef BUS_DEBUG
|
||||
|
||||
static int bus_debug = 1;
|
||||
@ -423,6 +426,7 @@ devinit(void)
|
||||
cv_init(&devsoftc.cv, "dev cv");
|
||||
TAILQ_INIT(&devsoftc.devq);
|
||||
knlist_init_mtx(&devsoftc.sel.si_note, &devsoftc.mtx);
|
||||
devctl2_init();
|
||||
}
|
||||
|
||||
static int
|
||||
@ -2638,6 +2642,15 @@ device_is_attached(device_t dev)
|
||||
return (dev->state >= DS_ATTACHED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return non-zero if the device is currently suspended.
|
||||
*/
|
||||
int
|
||||
device_is_suspended(device_t dev)
|
||||
{
|
||||
return ((dev->flags & DF_SUSPENDED) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the devclass of a device
|
||||
* @see devclass_add_device().
|
||||
@ -5022,3 +5035,253 @@ bus_free_resource(device_t dev, int type, struct resource *r)
|
||||
return (0);
|
||||
return (bus_release_resource(dev, type, rman_get_rid(r), r));
|
||||
}
|
||||
|
||||
/*
|
||||
* /dev/devctl2 implementation. The existing /dev/devctl device has
|
||||
* implicit semantics on open, so it could not be reused for this.
|
||||
* Another option would be to call this /dev/bus?
|
||||
*/
|
||||
static int
|
||||
find_device(struct devreq *req, device_t *devp)
|
||||
{
|
||||
device_t dev;
|
||||
|
||||
/*
|
||||
* First, ensure that the name is nul terminated.
|
||||
*/
|
||||
if (memchr(req->dr_name, '\0', sizeof(req->dr_name)) == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* Second, try to find an attached device whose name matches
|
||||
* 'name'.
|
||||
*/
|
||||
TAILQ_FOREACH(dev, &bus_data_devices, devlink) {
|
||||
if (dev->nameunit != NULL &&
|
||||
strcmp(dev->nameunit, req->dr_name) == 0) {
|
||||
*devp = dev;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, give device enumerators a chance. */
|
||||
dev = NULL;
|
||||
EVENTHANDLER_INVOKE(dev_lookup, req->dr_name, &dev);
|
||||
if (dev == NULL)
|
||||
return (ENOENT);
|
||||
*devp = dev;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static bool
|
||||
driver_exists(struct device *bus, const char *driver)
|
||||
{
|
||||
devclass_t dc;
|
||||
|
||||
for (dc = bus->devclass; dc != NULL; dc = dc->parent) {
|
||||
if (devclass_find_driver_internal(dc, driver) != NULL)
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
static int
|
||||
devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
struct thread *td)
|
||||
{
|
||||
struct devreq *req;
|
||||
device_t dev;
|
||||
int error, old;
|
||||
|
||||
/* Locate the device to control. */
|
||||
mtx_lock(&Giant);
|
||||
req = (struct devreq *)data;
|
||||
switch (cmd) {
|
||||
case DEV_ATTACH:
|
||||
case DEV_DETACH:
|
||||
case DEV_ENABLE:
|
||||
case DEV_DISABLE:
|
||||
case DEV_SUSPEND:
|
||||
case DEV_RESUME:
|
||||
case DEV_SET_DRIVER:
|
||||
error = priv_check(td, PRIV_DRIVER);
|
||||
if (error == 0)
|
||||
error = find_device(req, &dev);
|
||||
break;
|
||||
default:
|
||||
error = ENOTTY;
|
||||
break;
|
||||
}
|
||||
if (error) {
|
||||
mtx_unlock(&Giant);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Perform the requested operation. */
|
||||
switch (cmd) {
|
||||
case DEV_ATTACH:
|
||||
if (device_is_attached(dev) && (dev->flags & DF_REBID) == 0)
|
||||
error = EBUSY;
|
||||
else if (!device_is_enabled(dev))
|
||||
error = ENXIO;
|
||||
else
|
||||
error = device_probe_and_attach(dev);
|
||||
break;
|
||||
case DEV_DETACH:
|
||||
if (!device_is_attached(dev)) {
|
||||
error = ENXIO;
|
||||
break;
|
||||
}
|
||||
if (!(req->dr_flags & DEVF_FORCE_DETACH)) {
|
||||
error = device_quiesce(dev);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
error = device_detach(dev);
|
||||
break;
|
||||
case DEV_ENABLE:
|
||||
if (device_is_enabled(dev)) {
|
||||
error = EBUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device has been probed but not attached (e.g.
|
||||
* when it has been disabled by a loader hint), just
|
||||
* attach the device rather than doing a full probe.
|
||||
*/
|
||||
device_enable(dev);
|
||||
if (device_is_alive(dev)) {
|
||||
/*
|
||||
* If the device was disabled via a hint, clear
|
||||
* the hint.
|
||||
*/
|
||||
if (resource_disabled(dev->driver->name, dev->unit))
|
||||
resource_unset_value(dev->driver->name,
|
||||
dev->unit, "disabled");
|
||||
error = device_attach(dev);
|
||||
} else
|
||||
error = device_probe_and_attach(dev);
|
||||
break;
|
||||
case DEV_DISABLE:
|
||||
if (!device_is_enabled(dev)) {
|
||||
error = ENXIO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(req->dr_flags & DEVF_FORCE_DETACH)) {
|
||||
error = device_quiesce(dev);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force DF_FIXEDCLASS on around detach to preserve
|
||||
* the existing name.
|
||||
*/
|
||||
old = dev->flags;
|
||||
dev->flags |= DF_FIXEDCLASS;
|
||||
error = device_detach(dev);
|
||||
if (!(old & DF_FIXEDCLASS))
|
||||
dev->flags &= ~DF_FIXEDCLASS;
|
||||
if (error == 0)
|
||||
device_disable(dev);
|
||||
break;
|
||||
case DEV_SUSPEND:
|
||||
if (device_is_suspended(dev)) {
|
||||
error = EBUSY;
|
||||
break;
|
||||
}
|
||||
if (device_get_parent(dev) == NULL) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
error = BUS_SUSPEND_CHILD(device_get_parent(dev), dev);
|
||||
break;
|
||||
case DEV_RESUME:
|
||||
if (!device_is_suspended(dev)) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (device_get_parent(dev) == NULL) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
error = BUS_RESUME_CHILD(device_get_parent(dev), dev);
|
||||
break;
|
||||
case DEV_SET_DRIVER: {
|
||||
devclass_t dc;
|
||||
char driver[128];
|
||||
|
||||
error = copyinstr(req->dr_data, driver, sizeof(driver), NULL);
|
||||
if (error)
|
||||
break;
|
||||
if (driver[0] == '\0') {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (dev->devclass != NULL &&
|
||||
strcmp(driver, dev->devclass->name) == 0)
|
||||
/* XXX: Could possibly force DF_FIXEDCLASS on? */
|
||||
break;
|
||||
|
||||
/*
|
||||
* Scan drivers for this device's bus looking for at
|
||||
* least one matching driver.
|
||||
*/
|
||||
if (dev->parent == NULL) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!driver_exists(dev->parent, driver)) {
|
||||
error = ENOENT;
|
||||
break;
|
||||
}
|
||||
dc = devclass_create(driver);
|
||||
if (dc == NULL) {
|
||||
error = ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Detach device if necessary. */
|
||||
if (device_is_attached(dev)) {
|
||||
if (req->dr_flags & DEVF_SET_DRIVER_DETACH)
|
||||
error = device_detach(dev);
|
||||
else
|
||||
error = EBUSY;
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear any previously-fixed device class and unit. */
|
||||
if (dev->flags & DF_FIXEDCLASS)
|
||||
devclass_delete_device(dev->devclass, dev);
|
||||
dev->flags |= DF_WILDCARD;
|
||||
dev->unit = -1;
|
||||
|
||||
/* Force the new device class. */
|
||||
error = devclass_add_device(dc, dev);
|
||||
if (error)
|
||||
break;
|
||||
dev->flags |= DF_FIXEDCLASS;
|
||||
error = device_probe_and_attach(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&Giant);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static struct cdevsw devctl2_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_ioctl = devctl2_ioctl,
|
||||
.d_name = "devctl2",
|
||||
};
|
||||
|
||||
static void
|
||||
devctl2_init(void)
|
||||
{
|
||||
|
||||
make_dev_credf(MAKEDEV_ETERNAL, &devctl2_cdevsw, 0, NULL,
|
||||
UID_ROOT, GID_WHEEL, 0600, "devctl2");
|
||||
}
|
||||
|
@ -461,3 +461,31 @@ resource_disabled(const char *name, int unit)
|
||||
return (0);
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear a value associated with a device by removing it from
|
||||
* the kernel environment. This only removes a hint for an
|
||||
* exact unit.
|
||||
*/
|
||||
int
|
||||
resource_unset_value(const char *name, int unit, const char *resname)
|
||||
{
|
||||
char varname[128];
|
||||
const char *retname, *retvalue;
|
||||
int error, line;
|
||||
size_t len;
|
||||
|
||||
line = 0;
|
||||
error = resource_find(&line, NULL, name, &unit, resname, NULL,
|
||||
&retname, NULL, NULL, NULL, NULL, &retvalue);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
retname -= strlen("hint.");
|
||||
len = retvalue - retname - 1;
|
||||
if (len > sizeof(varname) - 1)
|
||||
return (ENAMETOOLONG);
|
||||
memcpy(varname, retname, len);
|
||||
varname[len] = '\0';
|
||||
return (kern_unsetenv(varname));
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <machine/_limits.h>
|
||||
#include <sys/_bus_dma.h>
|
||||
#include <sys/ioccom.h>
|
||||
|
||||
/**
|
||||
* @defgroup NEWBUS newbus - a generic framework for managing devices
|
||||
@ -86,9 +87,45 @@ struct u_device {
|
||||
#define DF_REBID 0x80 /* Can rebid after attach */
|
||||
#define DF_SUSPENDED 0x100 /* Device is suspended. */
|
||||
|
||||
/**
|
||||
* @brief Device request structure used for ioctl's.
|
||||
*
|
||||
* Used for ioctl's on /dev/devctl2. All device ioctl's
|
||||
* must have parameter definitions which begin with dr_name.
|
||||
*/
|
||||
struct devreq_buffer {
|
||||
void *buffer;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct devreq {
|
||||
char dr_name[128];
|
||||
int dr_flags; /* request-specific flags */
|
||||
union {
|
||||
struct devreq_buffer dru_buffer;
|
||||
void *dru_data;
|
||||
} dr_dru;
|
||||
#define dr_buffer dr_dru.dru_buffer /* variable-sized buffer */
|
||||
#define dr_data dr_dru.dru_data /* fixed-size buffer */
|
||||
};
|
||||
|
||||
#define DEV_ATTACH _IOW('D', 1, struct devreq)
|
||||
#define DEV_DETACH _IOW('D', 2, struct devreq)
|
||||
#define DEV_ENABLE _IOW('D', 3, struct devreq)
|
||||
#define DEV_DISABLE _IOW('D', 4, struct devreq)
|
||||
#define DEV_SUSPEND _IOW('D', 5, struct devreq)
|
||||
#define DEV_RESUME _IOW('D', 6, struct devreq)
|
||||
#define DEV_SET_DRIVER _IOW('D', 7, struct devreq)
|
||||
|
||||
/* Flags for DEV_DETACH and DEV_DISABLE. */
|
||||
#define DEVF_FORCE_DETACH 0x0000001
|
||||
|
||||
/* Flags for DEV_SET_DRIVER. */
|
||||
#define DEVF_SET_DRIVER_DETACH 0x0000001 /* Detach existing driver. */
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kobj.h>
|
||||
|
||||
/**
|
||||
@ -104,6 +141,14 @@ void devctl_notify(const char *__system, const char *__subsystem,
|
||||
void devctl_queue_data_f(char *__data, int __flags);
|
||||
void devctl_queue_data(char *__data);
|
||||
|
||||
/**
|
||||
* Device name parsers. Hook to allow device enumerators to map
|
||||
* scheme-specific names to a device.
|
||||
*/
|
||||
typedef void (*dev_lookup_fn)(void *arg, const char *name,
|
||||
device_t *result);
|
||||
EVENTHANDLER_DECLARE(dev_lookup, dev_lookup_fn);
|
||||
|
||||
/**
|
||||
* @brief A device driver (included mainly for compatibility with
|
||||
* FreeBSD 4.x).
|
||||
@ -465,6 +510,7 @@ struct sysctl_oid *device_get_sysctl_tree(device_t dev);
|
||||
int device_is_alive(device_t dev); /* did probe succeed? */
|
||||
int device_is_attached(device_t dev); /* did attach succeed? */
|
||||
int device_is_enabled(device_t dev);
|
||||
int device_is_suspended(device_t dev);
|
||||
int device_is_quiet(device_t dev);
|
||||
int device_print_prettyname(device_t dev);
|
||||
int device_printf(device_t dev, const char *, ...) __printflike(2, 3);
|
||||
@ -528,6 +574,8 @@ int resource_set_long(const char *name, int unit, const char *resname,
|
||||
long value);
|
||||
int resource_set_string(const char *name, int unit, const char *resname,
|
||||
const char *value);
|
||||
int resource_unset_value(const char *name, int unit, const char *resname);
|
||||
|
||||
/*
|
||||
* Functions for maintaining and checking consistency of
|
||||
* bus information exported to userspace.
|
||||
|
@ -19,6 +19,7 @@ SUBDIR= adduser \
|
||||
ctld \
|
||||
daemon \
|
||||
dconschat \
|
||||
devctl \
|
||||
devinfo \
|
||||
digictl \
|
||||
diskinfo \
|
||||
|
9
usr.sbin/devctl/Makefile
Normal file
9
usr.sbin/devctl/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= devctl
|
||||
MAN= devctl.8
|
||||
MAN=
|
||||
|
||||
LIBADD= devctl
|
||||
|
||||
.include <bsd.prog.mk>
|
137
usr.sbin/devctl/devctl.8
Normal file
137
usr.sbin/devctl/devctl.8
Normal file
@ -0,0 +1,137 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
|
||||
.\" 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 .
|
282
usr.sbin/devctl/devctl.c
Normal file
282
usr.sbin/devctl/devctl.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 John Baldwin <jhb@FreeBSD.org>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/linker_set.h>
|
||||
#include <devctl.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user