On 2015/12/06 06:02, Mickael Torres wrote: > Hello, > > This is a kernel patch plus a utility called ugenctl I use to allow > selected USB devices to attach as ugen(4) instead of their more specific > driver. My use case is a Microchip "PICkit 2 Microcontroller Programmer" > that attaches as uhid(4), but the command line utility pk2cmd wants a > udev(4) device. There maybe are some other use cases.
If a device is generally pointless with uhid, we can just knock it out completely in the kernel, see the UQ_BAD_HID mechanism. I think this applies to your device. The bigger problem is "dual use" devices; e.g. some want UPS to attach to upd(4), others want ugen(4) for use with NUT/apcupsd. Your code is partially useful for these, but because it just changes things at attach, won't survive a reboot - if it could instead force a device to detach and reattach to ugen it would help a lot more cases. > It is mostly quick and dirty, but does the job. > > It can be found there: https://github.com/mickaeltorres/openbsd_ugenctl or > inlined at the end of this mail. > > Cheers, > Mike. > > Here is the kernel patch: > > Index: usb.c > =================================================================== > RCS file: /cvs/src/sys/dev/usb/usb.c,v > retrieving revision 1.107 > diff -u -p -r1.107 usb.c > --- usb.c 14 Mar 2015 03:38:50 -0000 1.107 > +++ usb.c 6 Dec 2015 04:29:22 -0000 > @@ -137,6 +137,8 @@ const struct cfattach usb_ca = { > usb_activate, > }; > > +struct usb_device_ugen_force usb_udf; > + > int > usb_match(struct device *parent, void *match, void *aux) > { > @@ -156,6 +158,7 @@ usb_attach(struct device *parent, struct > TAILQ_INIT(&usb_generic_tasks); > usb_run_tasks = usb_run_abort_tasks = 1; > kthread_create_deferred(usb_create_task_threads, NULL); > + bzero(&usb_udf, sizeof(usb_udf)); > } > usb_nbuses++; > > @@ -792,6 +795,23 @@ usbioctl(dev_t devt, u_long cmd, caddr_t > return (error); > } > > + case USB_DEVICE_FORCE_UGEN: > + { > + struct usb_device_ugen_force *udf = > + (struct usb_device_ugen_force *)data; > + > + if (udf->udf_set) // replace our list > + { > + memcpy(&usb_udf, udf, sizeof(usb_udf)); > + return (0); > + } > + else // return the list > + { > + memcpy(udf, &usb_udf, sizeof(usb_udf)); > + return (0); > + } > + } > + > default: > return (EINVAL); > } > Index: usb.h > =================================================================== > RCS file: /cvs/src/sys/dev/usb/usb.h,v > retrieving revision 1.53 > diff -u -p -r1.53 usb.h > --- usb.h 9 Jul 2015 05:40:44 -0000 1.53 > +++ usb.h 6 Dec 2015 04:29:23 -0000 > @@ -749,6 +749,13 @@ struct usb_device_stats { > u_long uds_requests[4]; /* indexed by transfer type UE_* */ > }; > > +#define USB_DEVICE_UGEN_FORCE_MAX 16 > +struct usb_device_ugen_force { > + int udf_set; > + u_short udf_ven_id[USB_DEVICE_UGEN_FORCE_MAX]; > + u_short udf_dev_id[USB_DEVICE_UGEN_FORCE_MAX]; > +}; > + > /* USB controller */ > #define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request) > #define USB_SETDEBUG _IOW ('U', 2, unsigned int) > @@ -758,6 +765,7 @@ struct usb_device_stats { > #define USB_DEVICE_GET_CDESC _IOWR('U', 6, struct usb_device_cdesc) > #define USB_DEVICE_GET_FDESC _IOWR('U', 7, struct usb_device_fdesc) > #define USB_DEVICE_GET_DDESC _IOWR('U', 8, struct usb_device_ddesc) > +#define USB_DEVICE_FORCE_UGEN _IOWR('U', 9, struct > usb_device_ugen_force) > > /* Generic HID device */ > #define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb_ctl_report_desc) > Index: usb_subr.c > =================================================================== > RCS file: /cvs/src/sys/dev/usb/usb_subr.c,v > retrieving revision 1.117 > diff -u -p -r1.117 usb_subr.c > --- usb_subr.c 23 Mar 2015 22:26:01 -0000 1.117 > +++ usb_subr.c 6 Dec 2015 04:29:23 -0000 > @@ -840,6 +840,7 @@ usbd_probe_and_attach(struct device *par > struct device *dv; > struct usbd_interface **ifaces; > extern struct rwlock usbpalock; > + extern struct usb_device_ugen_force usb_udf; > > rw_enter_write(&usbpalock); > > @@ -855,6 +856,14 @@ usbd_probe_and_attach(struct device *par > uaa.product = UGETW(dd->idProduct); > uaa.release = UGETW(dd->bcdDevice); > > + /* Check if this device is in the ugen force list */ > + for (i = 0; i < USB_DEVICE_UGEN_FORCE_MAX; i++) > + if ((usb_udf.udf_ven_id[i] == uaa.vendor) && > + (usb_udf.udf_dev_id[i] == uaa.product)) > + break; > + if (i != USB_DEVICE_UGEN_FORCE_MAX) > + goto generic; > + > /* First try with device specific drivers. */ > DPRINTF(("usbd_probe_and_attach trying device specific > drivers\n")); > dv = config_found(parent, &uaa, usbd_print); > > > And here is the ugenctl utility: > > Makefile: > > # $OpenBSD: Makefile,v 1.1 2000/02/03 21:52:15 jakob Exp $ > # $NetBSD: Makefile,v 1.2 1998/07/12 20:40:45 augustss Exp $ > > PROG= ugenctl > MAN= ugenctl.8 > > .include <bsd.prog.mk> > > ugenctl.c: > > #include <stdio.h> > #include <stdlib.h> > #include <unistd.h> > #include <fcntl.h> > #include <string.h> > #include <err.h> > #include <dev/usb/usb.h> > > struct usb_change_list { > u_short vid[USB_DEVICE_UGEN_FORCE_MAX]; > u_short pid[USB_DEVICE_UGEN_FORCE_MAX]; > int rem[USB_DEVICE_UGEN_FORCE_MAX]; > int end; > }; > > #define USB_PATH "/dev/usb0" > > extern char *__progname; > > void > usage(void) > { > fprintf(stderr, "usage: %s [-f dev] [-r vid:pid] [-s vid:pid]\n", > __progname); > exit(EXIT_FAILURE); > } > > void > usb_list_ioctl(int f, struct usb_device_ugen_force *udf, int set) > { > int e; > > udf->udf_set = set; > e = ioctl(f, USB_DEVICE_FORCE_UGEN, udf); > if (e) > err(EXIT_FAILURE, "ioctl()"); > } > > unsigned short > usb_parse_id(char *usb_id, int dev) > { > unsigned short id; > char *pid; > > id = 0; > > if (!dev) > id = strtol(usb_id, NULL, 0); > else { > pid = strchr(usb_id, ':'); > if (!pid) > errx(EXIT_FAILURE, "can't parse usb vendor_id:device_id > '%s'", > usb_id); > id = strtol(pid + 1, NULL, 0); > } > > if (!id) > errx(EXIT_FAILURE, "vid/pid can't be zero '%s'", usb_id); > > return id; > } > > void > usb_list_mod(struct usb_change_list *u, char *usb_id, int i, int rem) > { > unsigned short vid, pid; > > if (i >= USB_DEVICE_UGEN_FORCE_MAX) > errx(EXIT_FAILURE, "too many changes, max changes is %d", > USB_DEVICE_UGEN_FORCE_MAX); > > vid = usb_parse_id(usb_id, 0); > pid = usb_parse_id(usb_id, 1); > > u->vid[i] = vid; > u->pid[i] = pid; > u->rem[i] = rem; > u->end = i; > } > > void > usb_list_change(struct usb_device_ugen_force *udf, struct usb_change_list > *u) > { > int i, j; > > for (i = 0; i < u->end + 1; i++) > for (j = 0; j < USB_DEVICE_UGEN_FORCE_MAX; j++) > if (u->rem[i]) { > if ((u->vid[i] == udf->udf_ven_id[j]) && > (u->pid[i] == udf->udf_dev_id[j])) > { > udf->udf_ven_id[j] = 0; > udf->udf_dev_id[j] = 0; > } > } else { > if ((!udf->udf_ven_id[j]) && > (!udf->udf_dev_id[j])) { > udf->udf_ven_id[j] = u->vid[i]; > udf->udf_dev_id[j] = u->pid[i]; > break; > } > } > } > > int > main(int argc, char **argv) > { > struct usb_device_ugen_force udf; > struct usb_change_list usb_change; > int ch, f, i; > char *dev; > > bzero(&udf, sizeof(udf)); > bzero(&usb_change, sizeof(usb_change)); > > dev = strdup(USB_PATH); > if (!dev) > err(EXIT_FAILURE, "strdup()"); > > i = 0; > while ((ch = getopt(argc, argv, "f:r:s:")) != -1) { > switch (ch) { > case 'f': > free(dev); > dev = optarg; > break; > case 'r': > udf.udf_set = 1; > usb_list_mod(&usb_change, optarg, i, 1); > i++; > break; > case 's': > udf.udf_set = 1; > usb_list_mod(&usb_change, optarg, i, 0); > i++; > break; > default: > usage(); > } > } > argc -= optind; > argv += optind; > > if (argc) > usage(); > > f = open(dev, O_RDONLY); > if (f == -1) > err(EXIT_FAILURE, "can't open usb device %s", dev); > > if (udf.udf_set) { > usb_list_ioctl(f, &udf, 0); > usb_list_change(&udf, &usb_change); > usb_list_ioctl(f, &udf, 1); > } > > usb_list_ioctl(f, &udf, 0); > printf("currently ugen forced devices:\n"); > for (i = 0; i < USB_DEVICE_UGEN_FORCE_MAX; i++) > if (udf.udf_ven_id[i] && udf.udf_dev_id[i]) > printf("VID:0x%04x PID:0x%04x\n", > udf.udf_ven_id[i], > udf.udf_dev_id[i]); > > return (EXIT_SUCCESS); > } > > And ugenctl.8: > > .Dd $Mdocdate: December 6 2015 $ > .Dt UGENCTL 8 > .Os > .Sh NAME > .Nm ugenctl > .Nd select which usb devices are forced to attach to ugen > .Sh SYNOPSIS > .Nm > .Op Fl f Ar dev > .Op Fl r Ar vid:pid > .Op Fl s Ar vid:pid > .Sh DESCRIPTION > .Nm > permits to change the in-kernel list of devices that are forced to attach > as > ugen(4), even if they have a more specific driver. The actual list is > printed > at the end of any operation. If no operation are specified, it only prints > the list. > .Pp > The options are as follows: > .Bl -tag -width Fl > .It Fl f Ar dev > Select the usb controller device to use, instead of the default > (/dev/usb0). > It has no incidence on the operation, devices connected to ALL controllers > will > be affected. > .It Fl r Ar vid:pid > Remove the device identified by the vid:pid pair from the list, if it is on > it. > .It Fl s Ar vid:pid > Add the device identified by the vid:pid pair on the list. > .El > .Sh FILES > .Bl -tag -width Pa > .It Pa /dev/usb[0-9] > Default USB controllers. > .El > .Sh SEE ALSO > .Xr usb 4 > .Xr ugen 4 > .Sh HISTORY > The > .Nm > command appeared in > .Ox 5.9 . > .Sh BUGS > Actually the maximum number of forced devices is constant and defined in > macro > USB_DEVICE_UGEN_FORCE_MAX in usb.h . >
