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 <j...@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 <j...@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 <j...@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 ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to