On 2015-12-06 20:10, Ian Darwin wrote:
On 2015-12-06 12:23 PM, 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.

One fairly common (I believe) use is with USB printers that attach as ulpt but CUPS wants as ugen - for these, this is useful as it stands - just run it from rc. Do people want their UPS to sometimes be upd and other times be a ugen? I can see there might be "I want to change this now" cases, but I suspect the majority could be done once at each boot and be quite useful.

Hello,

an updated version that detach/reattach if a device is already connected, when
adding and when removing from the ugen list.
Seems to work well, but I don't have a lot of hardware to test it.
What do you think ?

https://github.com/mickaeltorres/openbsd_ugenctl and inlined.

Cheers,
Mike.

kernel patch below:

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 23:19:48 -⁠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,37 @@ 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;
+
+               switch (udf-⁠>udf_cmd) {
+               case USB_DEVICE_UGEN_FORCE_GET:
+               {
+                       memcpy(udf, &usb_udf, sizeof(usb_udf));
+                       break;
+               }
+               case USB_DEVICE_UGEN_FORCE_SET:
+               {
+                       memcpy(&usb_udf, udf, sizeof(usb_udf));
+                       break;
+               }
+               case USB_DEVICE_UGEN_FORCE_RESET:
+               {
+                       struct usbd_device *dev;
+
+                       if (udf-⁠>udf_addr < 1 ||
+                           udf-⁠>udf_addr >= USB_MAX_DEVICES)
+                               return (EINVAL);
+                       dev = sc-⁠>sc_bus-⁠>devices[udf-⁠>udf_addr];
+ usbd_reset_port(dev->myhub, dev->powersrc->portno);
+                       usb_needs_reattach(dev);
+                       break;
+               }
+               default:
+                       return (EINVAL);
+               }
+               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 23:19:48 -⁠0000
@@ -⁠749,6 +749,17 @@ struct usb_device_stats {
u_long uds_requests[4]; /* indexed by transfer type UE_* */
 };

+#define USB_DEVICE_UGEN_FORCE_MAX 16
+#define USB_DEVICE_UGEN_FORCE_GET 0
+#define USB_DEVICE_UGEN_FORCE_SET 1
+#define USB_DEVICE_UGEN_FORCE_RESET 2
+struct usb_device_ugen_force {
+        int udf_cmd;
+        u_char udf_addr;
+        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 +769,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 23:19:48 -⁠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);


=========== 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_dev_list {
        u_short vid;
        u_short pid;
        u_char addr;
        char *ctrl;
        struct usb_dev_list *next;
};

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_cmd = 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;
                                }
                        }
}

struct usb_dev_list *
usb_dev_get(void)
{
        struct usb_device_info inf;
        struct usb_dev_list *ret;
        struct usb_dev_list **p;
        char buf[11];
        int a, f, i;

        ret = NULL;
        p = &ret;
        for (i = 0; i < 10; i++) {
                snprintf(buf, sizeof(buf), "/⁠dev/⁠usb%d", i);
                f = open(buf, O_RDONLY);
                if (f == -⁠1)
                        continue;
                for (a = 1; a < USB_MAX_DEVICES; a++) {
                        inf.udi_addr = a;
                        if (ioctl(f, USB_DEVICEINFO, &inf))
                                continue;
                        *p = malloc(sizeof(**p));
                        if (!*p)
                                err(EXIT_FAILURE, "malloc()");
                        (*p)-⁠>addr = a;
                        (*p)-⁠>ctrl = strdup(buf);
                        if (!((*p)-⁠>ctrl))
                                err(EXIT_FAILURE, "malloc()");
                        (*p)-⁠>vid = inf.udi_vendorNo;
                        (*p)-⁠>pid = inf.udi_productNo;
                        (*p)-⁠>next = NULL;
                        p = &((*p)-⁠>next);
                }
                close(f);
        }
        return (ret);
}

void
usb_dev_reset(struct usb_change_list *c, struct usb_dev_list *l)
{
        struct usb_device_ugen_force udf;
        struct usb_dev_list *p;
        int f, i;

        for (i = 0; i < c-⁠>end + 1; i++)
                for (p = l; p; p = p-⁠>next)
if ((c->vid[i] == p->vid) && (c->pid[i] == p->pid)) {
                                f = open(p-⁠>ctrl, O_RDONLY);
                                if (f == -⁠1)
                                        err(EXIT_FAILURE,
                                            "can't open usb device %s",
                                            p-⁠>ctrl);
                                udf.udf_addr = p-⁠>addr;
                                usb_list_ioctl(f, &udf,
USB_DEVICE_UGEN_FORCE_RESET);
                                close(f);
                        }
}

int
main(int argc, char **argv)
{
        struct usb_device_ugen_force udf;
        struct usb_change_list usb_change;
        struct usb_dev_list *list;
        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_cmd = USB_DEVICE_UGEN_FORCE_SET;
                        usb_list_mod(&usb_change, optarg, i, 1);
                        i++;
                        break;
                case 's':
                        udf.udf_cmd = USB_DEVICE_UGEN_FORCE_SET;
                        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_cmd == USB_DEVICE_UGEN_FORCE_SET) {
                list = usb_dev_get();
                usb_list_ioctl(f, &udf, USB_DEVICE_UGEN_FORCE_GET);
                usb_list_change(&udf, &usb_change);
                usb_list_ioctl(f, &udf, USB_DEVICE_UGEN_FORCE_SET);
                usb_dev_reset(&usb_change, list);
        }

        usb_list_ioctl(f, &udf, USB_DEVICE_UGEN_FORCE_GET);
        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);
}


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