svn commit: r278320 - in head: contrib/mdocml lib lib/libdevctl share/mk sys/dev/acpica sys/dev/pci sys/kern sys/sys usr.sbin usr.sbin/devctl

John Baldwin jhb at FreeBSD.org
Fri Feb 6 16:09:05 UTC 2015


Author: jhb
Date: Fri Feb  6 16:09:01 2015
New Revision: 278320
URL: https://svnweb.freebsd.org/changeset/base/278320

Log:
  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

Added:
  head/lib/libdevctl/
  head/lib/libdevctl/Makefile   (contents, props changed)
  head/lib/libdevctl/devctl.3   (contents, props changed)
  head/lib/libdevctl/devctl.c   (contents, props changed)
  head/lib/libdevctl/devctl.h   (contents, props changed)
  head/usr.sbin/devctl/
  head/usr.sbin/devctl/Makefile   (contents, props changed)
  head/usr.sbin/devctl/devctl.8   (contents, props changed)
  head/usr.sbin/devctl/devctl.c   (contents, props changed)
Modified:
  head/contrib/mdocml/lib.in
  head/lib/Makefile
  head/share/mk/bsd.libnames.mk
  head/share/mk/src.libnames.mk
  head/sys/dev/acpica/acpi.c
  head/sys/dev/pci/pci.c
  head/sys/kern/subr_bus.c
  head/sys/kern/subr_hints.c
  head/sys/sys/bus.h
  head/usr.sbin/Makefile

Modified: head/contrib/mdocml/lib.in
==============================================================================
--- head/contrib/mdocml/lib.in	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/contrib/mdocml/lib.in	Fri Feb  6 16:09:01 2015	(r278320)
@@ -41,6 +41,7 @@ LINE("libcrypt",	"Crypt Library (libcryp
 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)")

Modified: head/lib/Makefile
==============================================================================
--- head/lib/Makefile	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/lib/Makefile	Fri Feb  6 16:09:01 2015	(r278320)
@@ -41,6 +41,7 @@ SUBDIR=	${SUBDIR_ORDERED} \
 	${_libcom_err} \
 	libcompat \
 	libcrypt \
+	libdevctl \
 	libdevinfo \
 	libdevstat \
 	libdpv \

Added: head/lib/libdevctl/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libdevctl/Makefile	Fri Feb  6 16:09:01 2015	(r278320)
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+LIB=	devctl
+SRCS=	devctl.c
+INCS=	devctl.h
+MAN=	devctl.3
+
+.include <bsd.lib.mk>

Added: head/lib/libdevctl/devctl.3
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libdevctl/devctl.3	Fri Feb  6 16:09:01 2015	(r278320)
@@ -0,0 +1,295 @@
+.\"
+.\" Copyright (c) 2014 John Baldwin <jhb at 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.

Added: head/lib/libdevctl/devctl.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libdevctl/devctl.c	Fri Feb  6 16:09:01 2015	(r278320)
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2014 John Baldwin <jhb at 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));
+}

Added: head/lib/libdevctl/devctl.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libdevctl/devctl.h	Fri Feb  6 16:09:01 2015	(r278320)
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2014 John Baldwin <jhb at 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__ */

Modified: head/share/mk/bsd.libnames.mk
==============================================================================
--- head/share/mk/bsd.libnames.mk	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/share/mk/bsd.libnames.mk	Fri Feb  6 16:09:01 2015	(r278320)
@@ -39,6 +39,7 @@ LIBCRYPT?=	${DESTDIR}${LIBDIR}/libcrypt.
 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

Modified: head/share/mk/src.libnames.mk
==============================================================================
--- head/share/mk/src.libnames.mk	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/share/mk/src.libnames.mk	Fri Feb  6 16:09:01 2015	(r278320)
@@ -72,6 +72,7 @@ _LIBRARIES=	\
 		ctf \
 		cuse \
 		cxxrt \
+		devctl \
 		devinfo \
 		devstat \
 		dialog \

Modified: head/sys/dev/acpica/acpi.c
==============================================================================
--- head/sys/dev/acpica/acpi.c	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/sys/dev/acpica/acpi.c	Fri Feb  6 16:09:01 2015	(r278320)
@@ -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.
  *

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/sys/dev/pci/pci.c	Fri Feb  6 16:09:01 2015	(r278320)
@@ -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 d
     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
 	    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, voi
 		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;
 	}

Modified: head/sys/kern/subr_bus.c
==============================================================================
--- head/sys/kern/subr_bus.c	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/sys/kern/subr_bus.c	Fri Feb  6 16:09:01 2015	(r278320)
@@ -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
@@ -2639,6 +2643,15 @@ device_is_attached(device_t dev)
 }
 
 /**
+ * @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
 		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");
+}

Modified: head/sys/kern/subr_hints.c
==============================================================================
--- head/sys/kern/subr_hints.c	Fri Feb  6 15:53:13 2015	(r278319)
+++ head/sys/kern/subr_hints.c	Fri Feb  6 16:09:01 2015	(r278320)
@@ -461,3 +461,31 @@ resource_disabled(const char *name, int 
 	       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';

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list