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 .
> 

Reply via email to