Implement 'devctl clear driver' to undo a previous 'devctl set driver'.

Add a new 'clear driver' command for devctl along with the accompanying
ioctl and devctl_clear_driver() library routine to reset a device to
use a wildcard devclass instead of a fixed devclass.  This can be used
to undo a previous 'set driver' command.  After the device's name has
been reset to permit wildcard names, it is reprobed so that it can
attach to newly-available (to it) device drivers.

MFC after:	1 month
Sponsored by:	Chelsio Communications
This commit is contained in:
John Baldwin 2016-08-29 22:48:36 +00:00
parent a3d5eb2481
commit e05ec081fe
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=305034
7 changed files with 132 additions and 5 deletions

View file

@ -25,12 +25,13 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 27, 2016
.Dd August 29, 2016
.Dt DEVCTL 3
.Os
.Sh NAME
.Nm devctl ,
.Nm devctl_attach ,
.Nm devctl_clear_driver ,
.Nm devctl_delete ,
.Nm devctl_detach ,
.Nm devctl_disable ,
@ -47,6 +48,8 @@
.Ft int
.Fn devctl_attach "const char *device"
.Ft int
.Fn devctl_clear_driver "const char *device" "bool force"
.Ft int
.Fn devctl_delete "const char *device" "bool force"
.Ft int
.Fn devctl_detach "const char *device" "bool force"
@ -166,12 +169,30 @@ the device will be detached from its current device driver before it is
attached to the new device driver.
.Pp
The
.Fn devctl_clear_driver
function resets a device so that it can be attached to any valid device
driver rather than only drivers with a previously specified name.
This function is used to undo a previous call to
.Fn devctl_set_driver .
If the device is already attached and
.Fa force
is false,
the request will fail.
If the device is already attached and
.Fa force
is true,
the device will be detached from its current device driver.
After the device's name is reset,
it is reprobed and attached to a suitable device driver if one is found.
.Pp
The
.Fn devctl_rescan
function rescans a bus device checking for devices that have been added or
removed.
.Sh RETURN VALUES
.Rv -std devctl_attach devctl_delete devctl_detach devctl_disable \
devctl_enable devctl_suspend devctl_rescan devctl_resume devctl_set_driver
.Rv -std devctl_attach devctl_clear_driver devctl_delete devctl_detach \
devctl_disable devctl_enable devctl_suspend devctl_rescan devctl_resume \
devctl_set_driver
.Sh ERRORS
In addition to specific errors noted below,
all of the
@ -302,6 +323,24 @@ The new device driver failed to attach.
.El
.Pp
The
.Fn devctl_clear_driver
function may fail if:
.Bl -tag -width Er
.It Bq Er EBUSY
The device is currently attached to a device driver and
.Fa force
is false.
.It Bq Er EBUSY
The current device driver for
.Fa device
is busy and cannot detach at this time.
.It Bq Er EINVAL
The device is not configured for a specific device driver name.
.It Bq Er ENXIO
The device driver chosen after reprobing failed to attach.
.El
.Pp
The
.Fn devctl_rescan
function may fail if:
.Bl -tag -width Er

View file

@ -123,6 +123,14 @@ devctl_set_driver(const char *device, const char *driver, bool force)
return (devctl_request(DEV_SET_DRIVER, &req));
}
int
devctl_clear_driver(const char *device, bool force)
{
return (devctl_simple_request(DEV_CLEAR_DRIVER, device, force ?
DEVF_CLEAR_DRIVER_DETACH : 0));
}
int
devctl_rescan(const char *device)
{

View file

@ -38,6 +38,7 @@ int devctl_disable(const char *device, bool force_detach);
int devctl_suspend(const char *device);
int devctl_resume(const char *device);
int devctl_set_driver(const char *device, const char *driver, bool force);
int devctl_clear_driver(const char *device, bool force);
int devctl_rescan(const char *device);
int devctl_delete(const char *device, bool force);

View file

@ -5349,6 +5349,7 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
case DEV_SUSPEND:
case DEV_RESUME:
case DEV_SET_DRIVER:
case DEV_CLEAR_DRIVER:
case DEV_RESCAN:
case DEV_DELETE:
error = priv_check(td, PRIV_DRIVER);
@ -5514,6 +5515,25 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
error = device_probe_and_attach(dev);
break;
}
case DEV_CLEAR_DRIVER:
if (!(dev->flags & DF_FIXEDCLASS)) {
error = 0;
break;
}
if (device_is_attached(dev)) {
if (req->dr_flags & DEVF_CLEAR_DRIVER_DETACH)
error = device_detach(dev);
else
error = EBUSY;
if (error)
break;
}
dev->flags &= ~DF_FIXEDCLASS;
dev->flags |= DF_WILDCARD;
devclass_delete_device(dev->devclass, dev);
error = device_probe_and_attach(dev);
break;
case DEV_RESCAN:
if (!device_is_attached(dev)) {
error = ENXIO;

View file

@ -117,6 +117,7 @@ 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)
#define DEV_CLEAR_DRIVER _IOW('D', 8, struct devreq)
#define DEV_RESCAN _IOW('D', 9, struct devreq)
#define DEV_DELETE _IOW('D', 10, struct devreq)
@ -126,6 +127,9 @@ struct devreq {
/* Flags for DEV_SET_DRIVER. */
#define DEVF_SET_DRIVER_DETACH 0x0000001 /* Detach existing driver. */
/* Flags for DEV_CLEAR_DRIVER. */
#define DEVF_CLEAR_DRIVER_DETACH 0x0000001 /* Detach existing driver. */
/* Flags for DEV_DELETE. */
#define DEVF_FORCE_DELETE 0x0000001

View file

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 27, 2016
.Dd August 29, 2016
.Dt DEVCTL 8
.Os
.Sh NAME
@ -36,6 +36,10 @@
.Cm attach
.Ar device
.Nm
.Cm clear driver
.Op Fl f
.Ar device
.Nm
.Cm detach
.Op Fl f
.Ar device
@ -133,6 +137,21 @@ If the device is already attached to a device driver and the
.Fl f
flag is not specified,
the device will not be changed.
.It Xo Cm clear driver
.Op Fl f
.Ar device
.Xc
Clear a previously-forced driver name so that the device is able to use any
valid device driver.
After the previous name has been cleared,
the device is reprobed so that other device drivers may attach to it.
This can be used to undo an earlier
.Cm set driver
command.
If the device is currently attached to a device driver and the
.Fl f
flag is not specified,
the device will not be changed.
.It Cm rescan Ar device
Rescan a bus device checking for devices that have been added or
removed.

View file

@ -65,12 +65,13 @@ static int devctl_table_handler(struct devctl_command **start,
SET_DECLARE(DEVCTL_DATASET(top), struct devctl_command);
DEVCTL_TABLE(top, clear);
DEVCTL_TABLE(top, set);
static void
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
fprintf(stderr, "%s\n%s\n%s\n%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",
@ -78,6 +79,7 @@ usage(void)
" devctl suspend device",
" devctl resume device",
" devctl set driver [-f] device driver",
" devctl clear driver [-f] device",
" devctl rescan device",
" devctl delete [-f] device");
exit(1);
@ -261,6 +263,40 @@ set_driver(int ac, char **av)
}
DEVCTL_COMMAND(set, driver, set_driver);
static void
clear_driver_usage(void)
{
fprintf(stderr, "usage: devctl clear driver [-f] device\n");
exit(1);
}
static int
clear_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:
clear_driver_usage();
}
ac -= optind;
av += optind;
if (ac != 1)
clear_driver_usage();
if (devctl_clear_driver(av[0], force) < 0)
err(1, "Failed to clear %s driver", av[0]);
return (0);
}
DEVCTL_COMMAND(clear, driver, clear_driver);
static int
rescan(int ac, char **av)
{