Hello Stuart,

On 2015-12-06 18:23, Stuart Henderson wrote:
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.


Actually another utility (pic32prog, but I can't seem to make it work right now) wants a uhid device, so it was easier to have a dynamic way to switch. I'll try to modify this to have the device doing a detach/reattach automatically, thanks for the idea.

Cheers,
Mike.

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