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 .