Subject: list/suspend/resume w/ drvctl, plus bug fixes
To: None <current-users@netbsd.org>
From: David Young <dyoung@pobox.com>
List: current-users
Date: 12/21/2007 19:11:54
--bCsyhTFzCvuiizWE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Here are patches for drvctl(8) / drvctl(4) that let us list the children
of a device (drvctl -l device), suspend a device and its children (drvctl
-S device), resume a device (drvctl -R device), and resume a device and
its children (drvctl -Q device).
I have also made bus re-scanning/re-attachment work a bit better for
the case of cbb0 at pci0.
You may find this useful, as I have, for testing individual device
suspend/resume routines, for suspending/resuming device trees on embedded
systems, and for testing device detachment routines.
drvctl(8) will help test individual suspended drivers for their reaction
to ioctls, sysctls, and such. It is a no-no for a driver to access
hardware that is suspended!
Dave
--
David Young OJC Technologies
dyoung@ojctech.com Urbana, IL * (217) 278-3933 ext 24
--bCsyhTFzCvuiizWE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="drvctl.latest"
Index: sys/sys/drvctlio.h
===================================================================
RCS file: /cvsroot/src/sys/sys/drvctlio.h,v
retrieving revision 1.3
diff -p -u -u -p -r1.3 drvctlio.h
--- sys/sys/drvctlio.h 22 Sep 2006 04:37:37 -0000 1.3
+++ sys/sys/drvctlio.h 22 Dec 2007 00:19:13 -0000
@@ -50,6 +50,21 @@ struct devdetachargs {
char devname[16];
};
+struct devlistargs {
+ char l_devname[16];
+ char (*l_childname)[16];
+ size_t l_children;
+};
+
+enum devpmflags {
+ DEVPM_F_SUBTREE = 0x1
+};
+
+struct devpmargs {
+ char devname[16];
+ uint32_t flags;
+};
+
struct devrescanargs {
char busname[16];
char ifattr[16];
@@ -59,6 +74,9 @@ struct devrescanargs {
#define DRVDETACHDEV _IOW('D', 123, struct devdetachargs)
#define DRVRESCANBUS _IOW('D', 124, struct devrescanargs)
+#define DRVSUSPENDDEV _IOW('D', 125, struct devpmargs)
+#define DRVRESUMEDEV _IOW('D', 126, struct devpmargs)
+#define DRVLISTDEV _IOWR('D', 127, struct devlistargs)
/*
* Generic ioctl that takes a dictionary as an argument (specifies the
Index: sys/kern/kern_drvctl.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_drvctl.c,v
retrieving revision 1.11
diff -p -u -u -p -r1.11 kern_drvctl.c
--- sys/kern/kern_drvctl.c 3 Apr 2007 23:02:39 -0000 1.11
+++ sys/kern/kern_drvctl.c 22 Dec 2007 00:19:10 -0000
@@ -48,41 +48,113 @@ const struct cdevsw drvctl_cdevsw = {
};
void drvctlattach(int);
+static struct device *devbyname(const char *);
#define MAXLOCATORS 100
static int drvctl_command(struct lwp *, struct plistref *, u_long, int flag);
+static struct device *
+devbyname(const char *devname)
+{
+ struct device *d;
+
+ TAILQ_FOREACH(d, &alldevs, dv_list) {
+ if (strcmp(devname, device_xname(d)) == 0)
+ break;
+ }
+ return d;
+}
+
+static int
+pmdevbyname(int cmd, struct devpmargs *a)
+{
+ struct device *d;
+
+ if ((d = devbyname(a->devname)) == NULL)
+ return ENXIO;
+
+ switch (cmd) {
+ case DRVSUSPENDDEV:
+ return pmf_device_recursive_suspend(d) ? 0 : EBUSY;
+ case DRVRESUMEDEV:
+ if (a->flags & DEVPM_F_SUBTREE)
+ return pmf_device_resume_subtree(d) ? 0 : EBUSY;
+ else
+ return pmf_device_recursive_resume(d) ? 0 : EBUSY;
+ default:
+ return EPASSTHROUGH;
+ }
+}
+
+struct listdevchild_arg {
+ int la_error;
+ int la_idx;
+ struct devlistargs *la_l;
+};
+
+static bool
+listdevchild(device_t child, void *arg)
+{
+ struct listdevchild_arg *la = arg;
+ struct devlistargs *l = la->la_l;
+ int idx;
+
+ idx = la->la_idx++;
+
+ aprint_debug_dev(child, "idx %d\n", idx);
+
+ if (l->l_childname == NULL || idx >= l->l_children)
+ return true;
+
+ la->la_error = copyoutstr(device_xname(child), l->l_childname[idx],
+ sizeof(l->l_childname[idx]), NULL);
+
+ return la->la_error == 0;
+}
+
+static int
+listdevbyname(struct devlistargs *l)
+{
+ struct device *d;
+ struct listdevchild_arg la = {.la_idx = 0, .la_l = l, .la_error = 0};
+
+ if ((d = devbyname(l->l_devname)) == NULL)
+ return ENXIO;
+
+ if (!device_foreach_child(d, listdevchild, &la))
+ return la.la_error;
+
+ l->l_children = la.la_idx;
+ return 0;
+}
+
static int
detachdevbyname(const char *devname)
{
struct device *d;
- TAILQ_FOREACH(d, &alldevs, dv_list) {
- if (!strcmp(devname, d->dv_xname)) {
+ if ((d = devbyname(devname)) == NULL)
+ return ENXIO;
+
#ifndef XXXFULLRISK
- /*
- * If the parent cannot be notified, it might keep
- * pointers to the detached device.
- * There might be a private notification mechanism,
- * but better play save here.
- */
- if (d->dv_parent &&
- !d->dv_parent->dv_cfattach->ca_childdetached)
- return (ENOTSUP);
+ /*
+ * If the parent cannot be notified, it might keep
+ * pointers to the detached device.
+ * There might be a private notification mechanism,
+ * but better play save here.
+ */
+ if (d->dv_parent && !d->dv_parent->dv_cfattach->ca_childdetached)
+ return (ENOTSUP);
#endif
- return (config_detach(d, 0));
- }
- }
-
- return (ENXIO);
+ return (config_detach(d, 0));
}
static int
rescanbus(const char *busname, const char *ifattr,
int numlocators, const int *locators)
{
- int i;
+ int i, rc;
struct device *d;
const struct cfiattrdata * const *ap;
@@ -95,35 +167,34 @@ rescanbus(const char *busname, const cha
for (i = 0; i < numlocators;i++)
locs[i] = locators[i];
- TAILQ_FOREACH(d, &alldevs, dv_list) {
- if (!strcmp(busname, d->dv_xname)) {
- /*
- * must support rescan, and must have something
- * to attach to
- */
- if (!d->dv_cfattach->ca_rescan ||
- !d->dv_cfdriver->cd_attrs)
- return (ENODEV);
-
- /* allow to omit attribute if there is exactly one */
- if (!ifattr) {
- if (d->dv_cfdriver->cd_attrs[1])
- return (EINVAL);
- ifattr = d->dv_cfdriver->cd_attrs[0]->ci_name;
- } else {
- /* check for valid attribute passed */
- for (ap = d->dv_cfdriver->cd_attrs; *ap; ap++)
- if (!strcmp((*ap)->ci_name, ifattr))
- break;
- if (!*ap)
- return (EINVAL);
- }
+ if ((d = devbyname(busname)) == NULL)
+ return ENXIO;
- return (*d->dv_cfattach->ca_rescan)(d, ifattr, locs);
- }
+ /*
+ * must support rescan, and must have something
+ * to attach to
+ */
+ if (!d->dv_cfattach->ca_rescan ||
+ !d->dv_cfdriver->cd_attrs)
+ return (ENODEV);
+
+ /* allow to omit attribute if there is exactly one */
+ if (!ifattr) {
+ if (d->dv_cfdriver->cd_attrs[1])
+ return (EINVAL);
+ ifattr = d->dv_cfdriver->cd_attrs[0]->ci_name;
+ } else {
+ /* check for valid attribute passed */
+ for (ap = d->dv_cfdriver->cd_attrs; *ap; ap++)
+ if (!strcmp((*ap)->ci_name, ifattr))
+ break;
+ if (!*ap)
+ return (EINVAL);
}
- return (ENXIO);
+ rc = (*d->dv_cfattach->ca_rescan)(d, ifattr, locs);
+ config_deferred(NULL);
+ return rc;
}
int
@@ -134,6 +205,15 @@ drvctlioctl(dev_t dev, u_long cmd, void
int *locs;
switch (cmd) {
+ case DRVSUSPENDDEV:
+ case DRVRESUMEDEV:
+#define d ((struct devpmargs *)data)
+ res = pmdevbyname(cmd, d);
+#undef d
+ break;
+ case DRVLISTDEV:
+ res = listdevbyname((struct devlistargs *)data);
+ break;
case DRVDETACHDEV:
#define d ((struct devdetachargs *)data)
res = detachdevbyname(d->devname);
Index: sbin/drvctl/drvctl.c
===================================================================
RCS file: /cvsroot/src/sbin/drvctl/drvctl.c,v
retrieving revision 1.5
diff -p -u -u -p -r1.5 drvctl.c
--- sbin/drvctl/drvctl.c 22 Sep 2006 04:37:36 -0000 1.5
+++ sbin/drvctl/drvctl.c 22 Dec 2007 00:18:56 -0000
@@ -35,7 +35,7 @@
#include <sys/ioctl.h>
#include <sys/drvctlio.h>
-#define OPTS "dra:p"
+#define OPTS "QRSa:dlpr"
#define OPEN_MODE(mode) \
(((mode) == 'd' || (mode) == 'r') ? O_RDWR \
@@ -49,7 +49,12 @@ usage(void)
fprintf(stderr, "Usage: %s -r [-a attribute] busdevice [locator ...]\n"
" %s -d device\n"
- " %s -p device\n",
+ " %s -l device\n"
+ " %s -p device\n"
+ " %s -Q device\n"
+ " %s -R device\n"
+ " %s -S device\n",
+ getprogname(), getprogname(), getprogname(), getprogname(),
getprogname(), getprogname(), getprogname());
exit(1);
}
@@ -62,16 +67,29 @@ main(int argc, char **argv)
extern char *optarg;
extern int optind;
int fd, res;
+ size_t children;
+ struct devpmargs paa = {.devname = "", .flags = 0};
+ struct devlistargs laa = {.l_devname = "", .l_childname = NULL,
+ .l_children = 0};
struct devdetachargs daa;
struct devrescanargs raa;
int *locs, i;
+ prop_dictionary_t command_dict, args_dict, results_dict,
+ data_dict;
+ prop_string_t string;
+ prop_number_t number;
+ char *xml;
mode = 0;
while ((c = getopt(argc, argv, OPTS)) != -1) {
switch (c) {
+ case 'Q':
+ case 'R':
+ case 'S':
case 'd':
- case 'r':
+ case 'l':
case 'p':
+ case 'r':
mode = c;
break;
case 'a':
@@ -93,13 +111,48 @@ main(int argc, char **argv)
if (fd < 0)
err(2, "open %s", DRVCTLDEV);
- if (mode == 'd') {
+ switch (mode) {
+ case 'Q':
+ paa.flags = DEVPM_F_SUBTREE;
+ /*FALLTHROUGH*/
+ case 'R':
+ strlcpy(paa.devname, argv[0], sizeof(paa.devname));
+
+ if (ioctl(fd, DRVRESUMEDEV, &paa) == -1)
+ err(3, "DRVRESUMEDEV");
+ break;
+ case 'S':
+ strlcpy(paa.devname, argv[0], sizeof(paa.devname));
+
+ if (ioctl(fd, DRVSUSPENDDEV, &paa) == -1)
+ err(3, "DRVSUSPENDDEV");
+ break;
+ case 'd':
strlcpy(daa.devname, argv[0], sizeof(daa.devname));
- res = ioctl(fd, DRVDETACHDEV, &daa);
- if (res)
+ if (ioctl(fd, DRVDETACHDEV, &daa) == -1)
err(3, "DRVDETACHDEV");
- } else if (mode == 'r') {
+ break;
+ case 'l':
+ strlcpy(laa.l_devname, argv[0], sizeof(laa.l_devname));
+
+ if (ioctl(fd, DRVLISTDEV, &laa) == -1)
+ err(3, "DRVLISTDEV");
+
+ children = laa.l_children;
+
+ laa.l_childname = malloc(children * sizeof(laa.l_childname[0]));
+ if (laa.l_childname == NULL)
+ err(5, "DRVLISTDEV");
+ if (ioctl(fd, DRVLISTDEV, &laa) == -1)
+ err(3, "DRVLISTDEV");
+ if (laa.l_children > children)
+ err(6, "DRVLISTDEV: number of children grew");
+
+ for (i = 0; i < laa.l_children; i++)
+ printf("%s %s\n", laa.l_devname, laa.l_childname[i]);
+ break;
+ case 'r':
memset(&raa, 0, sizeof(raa));
strlcpy(raa.busname, argv[0], sizeof(raa.busname));
if (attr)
@@ -114,15 +167,10 @@ main(int argc, char **argv)
raa.locators = locs;
}
- res = ioctl(fd, DRVRESCANBUS, &raa);
- if (res)
+ if (ioctl(fd, DRVRESCANBUS, &raa) == -1)
err(3, "DRVRESCANBUS");
- } else if (mode == 'p') {
- prop_dictionary_t command_dict, args_dict, results_dict,
- data_dict;
- prop_string_t string;
- prop_number_t number;
- char *xml;
+ break;
+ case 'p':
command_dict = prop_dictionary_create();
args_dict = prop_dictionary_create();
@@ -164,8 +212,10 @@ main(int argc, char **argv)
printf("Properties for device `%s':\n%s",
argv[0], xml);
free(xml);
- } else
+ break;
+ default:
errx(4, "unknown command");
+ }
return (0);
}
--bCsyhTFzCvuiizWE--