tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

use of an intermediate device to handle USB interfaces



Hi -
because of the problems with loadable USB drivers I've been thinking
about cleaning up the USB device attachment process for a while, and
now I've finally implemented some ideas.
This is a device node, called "ucompound" for now, which attaches
to a USB device if no driver wants to take it as a whole, and
tries to attach drivers which only deal with individual interfaces.
This way, a single device node would represent the physical
device even if it provides multiple functions, and some complexity
could be removed from the framework.
There are some drivers which attach to a single interface, but use
others which are related, (ab)using weak interfaces in the USB
framework. the new node is also thought to introduce a stricter
interface to drivers, providing only handles to interfaces the
driver is supposed to deal with. For now, I've implemented the
IAD (interface association descriptor) way of detecting groups
of interfaces -- more are needed.

Am open question is whether such a "ucompound" should be engaged
if a device provides only a single interface. There is a pro
and a con:
pro: A driver for some common subdevice, let's say a "umass"
 disk which can occur standalone or as part of a multifunction
 device, would need to be declared only as a child of "ucompound"
 in kernel config files. Otherwise, it would have to be declared
 as child of both "uhub" or "ucompound", which is error-prone.
con: An intermediate node without appearent purpose is ugly.
 (needs a better name?)

I also doubt that this should deal with multiple configurations.
There seem to be no devices which use it which are not controlled
by a special driver anyway, and the USB framework also doesn't
have any sensible heuristics which configuration to use in the
unlikely case that there was more than one.

Another unsolved problem is how the "ugen" driver fits in --
there is already the problem that ugen can only take over
a whole device but not some interfaces if a multifunction one.
(eg, the scanner part of a printer/scanner/cardreader)
I don't have an idea yet, apart from rewriting it.

The appended patch is still just a sketch. I hope it
is enough to get some feedback.

best regards
Matthias




-------------------------------------------------------------------
-------------------------------------------------------------------
Forschungszentrum Juelich GmbH
52425 Juelich

Sitz der Gesellschaft: Juelich
Eingetragen im Handelsregister des Amtsgerichts Dueren Nr. HR B 3498
Vorsitzende des Aufsichtsrats: MinDir'in Baerbel Brumme-Bothe
Geschaeftsfuehrung: Prof. Dr. Achim Bachem (Vorsitzender),
Dr. Ulrich Krafft (stellv. Vorsitzender), Prof. Dr. Harald Bolt,
Dr. Sebastian M. Schmidt
-------------------------------------------------------------------
-------------------------------------------------------------------
#
#
# add_file "sys/dev/usb/ucompound.c"
#  content [fea4e9d0dfbe7af52102aeaef144b8f5f961172c]
# 
# add_file "sys/dev/usb/usbfunction.h"
#  content [46493d05a44447d40d8dab191ee8a78b8484b4f8]
# 
# add_file "sys/dev/usb/usbiadreg.h"
#  content [9bf158738f9406942171989748462e30ca213ae4]
# 
# patch "sys/dev/usb/files.usb"
#  from [27a8673fba807146c4a3084b06ca2ecb34a34a5b]
#    to [32464009efff62a69139521facfa87cf32eb6e1a]
# 
# patch "sys/dev/usb/uaudio.c"
#  from [75399ad45fa82468807774e7f5d9f32111be1c07]
#    to [836fdca512c4546780ffd1bfdf051f7f49256275]
# 
# patch "sys/dev/usb/usb.h"
#  from [572dc8d893c531cf62045994fcde0d59b80935c7]
#    to [9fc7925ca6786f49e560b52787e6069152d32d59]
# 
# patch "sys/dev/usb/usb_subr.c"
#  from [f832062e7e3cd9a34aa72b7f32df984d6c25bac6]
#    to [3a20585cd2f124bd1f282172488284f10e70b92f]
#
============================================================
--- sys/dev/usb/ucompound.c     fea4e9d0dfbe7af52102aeaef144b8f5f961172c
+++ sys/dev/usb/ucompound.c     fea4e9d0dfbe7af52102aeaef144b8f5f961172c
@@ -0,0 +1,271 @@
+/* $NetBSD$ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <sys/device.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <sys/bus.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbiadreg.h>
+#include <dev/usb/usbfunction.h>
+
+static int ucompound_match(device_t, struct cfdata *, void *);
+static void ucompound_attach(device_t, device_t, void *);
+static int ucompound_detach(device_t, int);
+static void ucompound_childdetached(device_t, device_t);
+static int ucmp_ifprint(void *, const char *);
+static int ucmp_fifprint(void *, const char *);
+
+struct ucompound_softc {
+       device_t sc_dev;
+       usbd_device_handle sc_udev;
+       int sc_port; /* XXX only for historical attach arg */
+       device_t *sc_subdevs;
+       int sc_nsubdevs;
+};
+
+CFATTACH_DECL2_NEW(ucompound, sizeof(struct ucompound_softc),
+                  ucompound_match, ucompound_attach, ucompound_detach,
+                  NULL,
+                  NULL, ucompound_childdetached);
+
+static int
+ucompound_match(device_t parent, struct cfdata *match, void *aux)
+{
+       struct usb_attach_arg *uaa = aux;
+
+       /* XXX don't compete with the generic driver */
+       if (uaa->usegeneric)
+               return 0;
+
+       /* match all at a priority lower than device specific drivers */
+       return 1;
+}
+
+/*
+ * The historical way of attaching drivers which attach to an interface.
+ * This does not handle drivers well which access multiple interfaces.
+ * Needs to be kept until all drivers are converted.
+ */
+static void
+ucompound_attachinterfaces(struct ucompound_softc *sc,
+                          usbd_interface_handle *ifaces, int nifaces)
+{
+       usbd_device_handle dev = sc->sc_udev;
+       usb_device_descriptor_t *dd = &dev->ddesc;
+       struct usbif_attach_arg uiaa;
+       int locs[USBIFIFCF_NLOCS];
+       int i;
+       device_t dv;
+
+       uiaa.device = dev;
+       uiaa.port = sc->sc_port;
+       uiaa.vendor = UGETW(dd->idVendor);
+       uiaa.product = UGETW(dd->idProduct);
+       uiaa.release = UGETW(dd->bcdDevice);
+       uiaa.configno = dev->cdesc->bConfigurationValue;
+       uiaa.nifaces = nifaces;
+       uiaa.ifaces = ifaces;
+       locs[USBIFIFCF_PORT] = uiaa.port;
+       locs[USBIFIFCF_VENDOR] = uiaa.vendor;
+       locs[USBIFIFCF_PRODUCT] = uiaa.product;
+       locs[USBIFIFCF_RELEASE] = uiaa.release;
+       locs[USBIFIFCF_CONFIGURATION] = uiaa.configno;
+
+       for (i = 0; i < nifaces; i++) {
+               if (ifaces[i] == NULL)
+                       continue; /* interface already claimed */
+               uiaa.iface = ifaces[i];
+               uiaa.class = ifaces[i]->idesc->bInterfaceClass;
+               uiaa.subclass = ifaces[i]->idesc->bInterfaceSubClass;
+               uiaa.proto = ifaces[i]->idesc->bInterfaceProtocol;
+               uiaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber;
+               locs[USBIFIFCF_INTERFACE] = uiaa.ifaceno;
+               dv = config_found_sm_loc(sc->sc_dev, "usbifif", locs, &uiaa,
+                                        ucmp_ifprint, config_stdsubmatch);
+               if (dv) {
+                       sc->sc_subdevs[sc->sc_nsubdevs++] = dv;
+                       ifaces[i] = 0; /* consumed */
+               }
+       }
+}
+
+static int
+ucmp_ifprint(void *aux, const char *pnp)
+{
+       struct usbif_attach_arg *uaa = aux;
+
+       if (pnp)
+               return (QUIET);
+       aprint_normal(" configuration %d interface %d",
+                     uaa->configno, uaa->ifaceno);
+       return (UNCONF);
+}
+
+/*
+ * attach by IADs
+ */
+static void
+ucompound_scaniads(struct ucompound_softc *sc,
+                  usbd_interface_handle *ifaces, int nifaces)
+{
+       usbd_device_handle dev = sc->sc_udev;
+       usbd_desc_iter_t di;
+       const usb_descriptor_t *d;
+       const usb_interface_association_descriptor_t *iad;
+       struct usbfunction_attach_args ufaa;
+       usbd_interface_handle *ifs;
+       int i;
+       int locs[USBFIFCF_NLOCS];
+       device_t dv;
+
+       usb_desc_iter_init(dev, &di);
+       while ((d = usb_desc_iter_next(&di))) {
+               if (d->bDescriptorType != UDESC_INTERFACE_ASSOCIATION)
+                       continue;
+               iad = (const usb_interface_association_descriptor_t *)d;
+
+               ifs = malloc(iad->bInterfaceCount
+                            * sizeof(usbd_interface_handle *),
+                            M_USBDEV, M_NOWAIT);
+               for (i = 0; i < iad->bInterfaceCount; i++)
+                       ifs[i] = ifaces[iad->bFirstInterface + i];
+               ufaa.ifaces = ifs;
+               ufaa.nifaces = iad->bInterfaceCount;
+               ufaa.class = iad->bFunctionClass;
+               ufaa.subclass = iad->bFunctionSubClass;
+               ufaa.proto = iad->bFunctionProtocol;
+               ufaa.device = dev;
+               ufaa.ifaceno = iad->bFirstInterface;
+               locs[USBFIFCF_INTERFACE] = ufaa.ifaceno;
+               dv = config_found_sm_loc(sc->sc_dev, "usbfif", locs, &ufaa,
+                                        ucmp_fifprint, config_stdsubmatch);
+               if (dv) {
+                       sc->sc_subdevs[sc->sc_nsubdevs++] = dv;
+                       for (i = 0; i < iad->bInterfaceCount; i++)
+                               ifaces[iad->bFirstInterface + i] = 0;
+               }
+               free(ifs, M_USBDEV);
+       }
+}
+
+static int
+ucmp_fifprint(void *aux, const char *pnp)
+{
+       struct usbfunction_attach_args *uaa = aux;
+
+       if (pnp)
+               aprint_normal("IAD device (class=%d) at %s", uaa->class, pnp);
+       aprint_normal(" interface %d", uaa->ifaceno);
+       return (UNCONF);
+}
+
+static void
+ucompound_attach(device_t parent, device_t self, void *aux)
+{
+       struct ucompound_softc *sc = device_private(self);
+       struct usb_attach_arg *uaa = aux;
+       char *devinfop;
+       int confi, nifaces, i;
+       usbd_status err;
+       device_t dv;
+       usbd_device_handle dev = uaa->device;
+       usb_device_descriptor_t *dd = &dev->ddesc;
+       usbd_interface_handle *ifaces;
+
+       sc->sc_dev = self;
+       sc->sc_udev = uaa->device;
+       sc->sc_port = uaa->port;
+
+       devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
+       printf(": %s\n", devinfop);
+       usbd_devinfo_free(devinfop);
+
+       for (confi = 0; confi < dd->bNumConfigurations; confi++) {
+               err = usbd_set_config_index(dev, confi, 1);
+               if (err) {
+                       printf("%s: port %d, set config failed\n",
+                              device_xname(parent), uaa->port);
+                       continue;
+               }
+
+               /* there are at most as many child drivers as interfaces */
+               nifaces = dev->cdesc->bNumInterface;
+               sc->sc_nsubdevs = 0;
+               sc->sc_subdevs = malloc(nifaces * sizeof dv,
+                                       M_USB, M_NOWAIT);
+               if (sc->sc_subdevs == NULL)
+                       return;
+               ifaces = malloc(nifaces * sizeof(*ifaces), M_USB, M_NOWAIT);
+               if (ifaces == NULL) {
+                       free(sc->sc_subdevs, M_USB);
+                       return;
+               }
+               for (i = 0; i < nifaces; i++)
+                       ifaces[i] = &dev->ifaces[i];
+
+               if ((uaa->class == UDCLASS_MISC) &&
+                   (uaa->subclass == UDSUBCLASS_COMMON) &&
+                   (uaa->proto == UDPROTO_IAD))
+                       ucompound_scaniads(sc, ifaces, nifaces);
+
+               ucompound_attachinterfaces(sc, ifaces, nifaces);
+
+               free(ifaces, M_USB);
+               if (sc->sc_nsubdevs)
+                       return;
+               else
+                       free(sc->sc_subdevs, M_USB);
+       }
+}
+
+static int
+ucompound_detach(device_t self, int flags)
+{
+       struct ucompound_softc *sc = device_private(self);
+       int res;
+#ifdef DIAGNOSTIC
+       int i;
+#endif
+
+       res = config_detach_children(self, flags);
+       if (res)
+               return res;
+
+#ifdef DIAGNOSTIC
+       for (i = 0; i < sc->sc_nsubdevs; i++)
+               if (sc->sc_subdevs[i])
+                       panic("ucompound_detach: childs left");
+#endif
+
+       if (sc->sc_nsubdevs)
+               free(sc->sc_subdevs, M_USB);
+       return 0;
+}
+
+static void
+ucompound_childdetached(device_t self, device_t dev)
+{
+       struct ucompound_softc *sc = device_private(self);
+       int i;
+
+#ifdef DIAGNOSTIC
+       int hit = 0;
+       for (i = 0; i < sc->sc_nsubdevs; i++) {
+               if (sc->sc_subdevs[i] == dev)
+                       hit++;
+       }
+       KASSERT(hit == 1);
+#endif
+
+       for (i = 0; i < sc->sc_nsubdevs; i++) {
+               if (sc->sc_subdevs[i] == dev) {
+                       sc->sc_subdevs[i] = 0;
+                       break;
+               }
+       }
+}
============================================================
--- sys/dev/usb/usbfunction.h   46493d05a44447d40d8dab191ee8a78b8484b4f8
+++ sys/dev/usb/usbfunction.h   46493d05a44447d40d8dab191ee8a78b8484b4f8
@@ -0,0 +1,10 @@
+
+struct usbfunction_attach_args {
+       usbd_device_handle device;
+
+       int class, subclass, proto;
+
+       usbd_interface_handle *ifaces;
+       int nifaces;
+       int ifaceno; /* first one, as locator */
+};
============================================================
--- sys/dev/usb/usbiadreg.h     9bf158738f9406942171989748462e30ca213ae4
+++ sys/dev/usb/usbiadreg.h     9bf158738f9406942171989748462e30ca213ae4
@@ -0,0 +1,14 @@
+/* $NetBSD$ */
+
+typedef struct {
+       uByte           bLength;
+       uByte           bDescriptorType;
+       uByte           bFirstInterface;
+       uByte           bInterfaceCount;
+       uByte           bFunctionClass;
+       uByte           bFunctionSubClass;
+       uByte           bFunctionProtocol;
+       uByte           iFunction;
+} UPACKED usb_interface_association_descriptor_t;
+
+#define UDESC_INTERFACE_ASSOCIATION 11
============================================================
--- sys/dev/usb/files.usb       27a8673fba807146c4a3084b06ca2ecb34a34a5b
+++ sys/dev/usb/files.usb       32464009efff62a69139521facfa87cf32eb6e1a
@@ -10,6 +10,7 @@ define        usbifif { [port = -1], [configura
                   [vendor = -1], [product = -1], [release = -1] }
 define usbifif { [port = -1], [configuration = -1], [interface = -1],
                  [vendor = -1], [product = -1], [release = -1] }
+define usbfif { [interface = -1] }
 
 device usb: usbdevif
 attach usb at usbus
@@ -25,6 +26,10 @@ file dev/usb/uhub.c                  usb
 attach uhub at usbdevif
 file   dev/usb/uhub.c                  usb
 
+device ucompound: usbfif, usbifif
+attach ucompound at usbdevif
+file   dev/usb/ucompound.c             ucompound
+
 # Modem and com serial port "bus"
 define ucombus {[ portno = -1 ]}
 
@@ -34,7 +39,7 @@ device        uaudio: audiobus, auconv, mulaw, 
 
 # Audio devices
 device uaudio: audiobus, auconv, mulaw, aurateconv
-attach uaudio at usbifif
+attach uaudio at usbfif
 file   dev/usb/uaudio.c                uaudio
 
 # MIDI devices
============================================================
--- sys/dev/usb/uaudio.c        75399ad45fa82468807774e7f5d9f32111be1c07
+++ sys/dev/usb/uaudio.c        836fdca512c4546780ffd1bfdf051f7f49256275
@@ -70,6 +70,7 @@ __KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
 #include <dev/usb/usb_quirks.h>
+#include <dev/usb/usbfunction.h>
 
 #include <dev/usb/uaudioreg.h>
 
@@ -162,7 +163,6 @@ struct uaudio_softc {
        USBBASEDEVICE   sc_dev;         /* base device */
        usbd_device_handle sc_udev;     /* USB device */
        int             sc_ac_iface;    /* Audio Control interface */
-       usbd_interface_handle   sc_ac_ifaceh;
        struct chan     sc_playchan;    /* play channel */
        struct chan     sc_recchan;     /* record channel */
        int             sc_nullalt;
@@ -224,9 +224,9 @@ Static usbd_status uaudio_identify_ac
 #endif
 
 Static usbd_status uaudio_identify_ac
-       (struct uaudio_softc *, const usb_config_descriptor_t *);
+       (struct uaudio_softc *, const usb_config_descriptor_t *, int);
 Static usbd_status uaudio_identify_as
-       (struct uaudio_softc *, const usb_config_descriptor_t *);
+       (struct uaudio_softc *, const usb_config_descriptor_t *, int);
 Static usbd_status uaudio_process_as
        (struct uaudio_softc *, const char *, int *, int,
         const usb_interface_descriptor_t *);
@@ -272,7 +272,8 @@ Static usbd_status uaudio_identify
 Static struct terminal_list *uaudio_io_terminaltype
        (int, struct io_terminal *, int);
 Static usbd_status uaudio_identify
-       (struct uaudio_softc *, const usb_config_descriptor_t *);
+       (struct uaudio_softc *, const usb_config_descriptor_t *,
+        usbd_interface_handle *, int);
 
 Static int     uaudio_signext(int, int);
 Static int     uaudio_value2bsd(struct mixerctl *, int);
@@ -366,11 +367,9 @@ USB_MATCH(uaudio)
 
 USB_MATCH(uaudio)
 {
-       USB_IFMATCH_START(uaudio, uaa);
+       struct usbfunction_attach_args *uaa = aux;
 
-       /* Trigger on the control interface. */
        if (uaa->class != UICLASS_AUDIO ||
-           uaa->subclass != UISUBCLASS_AUDIOCONTROL ||
            (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO))
                return UMATCH_NONE;
 
@@ -379,7 +378,8 @@ USB_ATTACH(uaudio)
 
 USB_ATTACH(uaudio)
 {
-       USB_IFATTACH_START(uaudio, sc, uaa);
+       struct uaudio_softc *sc = (struct uaudio_softc *)self;
+       struct usbfunction_attach_args *uaa = aux;
        usb_interface_descriptor_t *id;
        usb_config_descriptor_t *cdesc;
        char *devinfop;
@@ -399,14 +399,13 @@ USB_ATTACH(uaudio)
                USB_ATTACH_ERROR_RETURN;
        }
 
-       err = uaudio_identify(sc, cdesc);
+       err = uaudio_identify(sc, cdesc, uaa->ifaces, uaa->nifaces);
        if (err) {
                printf("%s: audio descriptors make no sense, error=%d\n",
                       USBDEVNAME(sc->sc_dev), err);
                USB_ATTACH_ERROR_RETURN;
        }
 
-       sc->sc_ac_ifaceh = uaa->iface;
        /* Pick up the AS interface. */
        for (i = 0; i < uaa->nifaces; i++) {
                if (uaa->ifaces[i] == NULL)
@@ -525,7 +524,7 @@ Static const usb_interface_descriptor_t 
 }
 
 Static const usb_interface_descriptor_t *
-uaudio_find_iface(const char *tbuf, int size, int *offsp, int subtype)
+uaudio_find_iface(const char *tbuf, int size, int *offsp, int number)
 {
        const usb_interface_descriptor_t *d;
 
@@ -534,7 +533,7 @@ uaudio_find_iface(const char *tbuf, int 
                *offsp += d->bLength;
                if (d->bDescriptorType == UDESC_INTERFACE &&
                    d->bInterfaceClass == UICLASS_AUDIO &&
-                   d->bInterfaceSubClass == subtype)
+                   d->bInterfaceNumber == number)
                        return d;
        }
        return NULL;
@@ -1469,14 +1468,29 @@ Static usbd_status
 }
 
 Static usbd_status
-uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
+uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc,
+               usbd_interface_handle *ifaces, int nifaces)
 {
+       int i;
+       usb_interface_descriptor_t *d;
        usbd_status err;
 
-       err = uaudio_identify_ac(sc, cdesc);
-       if (err)
-               return err;
-       return uaudio_identify_as(sc, cdesc);
+       for (i = 0; i < nifaces; i++) {
+               d = usbd_get_interface_descriptor(ifaces[i]);
+               if (d->bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL) {
+                       err = uaudio_identify_ac(sc, cdesc,
+                                                d->bInterfaceNumber);
+                       if (err)
+                               return err;
+               }
+       }
+       for (i = 0; i < nifaces; i++) {
+               d = usbd_get_interface_descriptor(ifaces[i]);
+               if (d->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)
+                       return uaudio_identify_as(sc, cdesc,
+                                                 d->bInterfaceNumber);
+       }
+       return USBD_INVAL;
 }
 
 Static void
@@ -1719,7 +1733,8 @@ uaudio_identify_as(struct uaudio_softc *
 
 Static usbd_status
 uaudio_identify_as(struct uaudio_softc *sc,
-                  const usb_config_descriptor_t *cdesc)
+                  const usb_config_descriptor_t *cdesc,
+                  int ifaceno)
 {
        const usb_interface_descriptor_t *id;
        const char *tbuf;
@@ -1733,7 +1748,7 @@ uaudio_identify_as(struct uaudio_softc *
 
        /* Locate the AudioStreaming interface descriptor. */
        offs = 0;
-       id = uaudio_find_iface(tbuf, size, &offs, UISUBCLASS_AUDIOSTREAM);
+       id = uaudio_find_iface(tbuf, size, &offs, ifaceno);
        if (id == NULL)
                return USBD_INVAL;
 
@@ -1759,7 +1774,7 @@ uaudio_identify_as(struct uaudio_softc *
                               USBDEVNAME(sc->sc_dev), id->bNumEndpoints);
                        break;
                }
-               id = uaudio_find_iface(tbuf, size, 
&offs,UISUBCLASS_AUDIOSTREAM);
+               id = uaudio_find_iface(tbuf, size, &offs, ifaceno);
                if (id == NULL)
                        break;
        }
@@ -1821,7 +1836,9 @@ Static usbd_status
 }
 
 Static usbd_status
-uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t 
*cdesc)
+uaudio_identify_ac(struct uaudio_softc *sc,
+                  const usb_config_descriptor_t *cdesc,
+                  int ifaceno)
 {
        struct io_terminal* iot;
        const usb_interface_descriptor_t *id;
@@ -1837,7 +1854,7 @@ uaudio_identify_ac(struct uaudio_softc *
 
        /* Locate the AudioControl interface descriptor. */
        offs = 0;
-       id = uaudio_find_iface(tbuf, size, &offs, UISUBCLASS_AUDIOCONTROL);
+       id = uaudio_find_iface(tbuf, size, &offs, ifaceno);
        if (id == NULL)
                return USBD_INVAL;
        if (offs + sizeof *acdp > size)
============================================================
--- sys/dev/usb/usb.h   572dc8d893c531cf62045994fcde0d59b80935c7
+++ sys/dev/usb/usb.h   9fc7925ca6786f49e560b52787e6069152d32d59
@@ -400,6 +400,9 @@ typedef struct {
 #define UDCLASS_WIRELESS       0xe0
 #define  UDSUBCLASS_RF         0x01
 #define   UDPROTO_BLUETOOTH    0x01
+#define UDCLASS_MISC           0xef
+#define  UDSUBCLASS_COMMON     0x02
+#define   UDPROTO_IAD          0x01
 #define UDCLASS_VENDOR         0xff
 
 /* Interface class codes */
============================================================
--- sys/dev/usb/usb_subr.c      f832062e7e3cd9a34aa72b7f32df984d6c25bac6
+++ sys/dev/usb/usb_subr.c      3a20585cd2f124bd1f282172488284f10e70b92f
@@ -828,6 +828,29 @@ usbd_probe_and_attach(device_ptr_t paren
        /* First try with device specific drivers. */
        DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n"));
        dv = USB_DO_ATTACH(dev, bdev, parent, &uaa, usbd_print, usbd_submatch);
+       
+       /*
+        * XXX hack: if the child is ucompound and it didn't succeed
+        * to attach child drivers, roll back and continue the old way.
+        * This allows interface drivers which are not allowed to attach
+        * to ucompound to work, and it keeps the semantics of ugen
+        * (which is suboptimal because it claims the whole device)
+        * for compatibility.
+        */
+       if (dv && !strcmp(device_cfdriver(dv)->cd_name, "ucompound")) {
+               device_t d;
+               int haschild = 0;
+
+               TAILQ_FOREACH(d, &alldevs, dv_list) {
+                       if (device_parent(d) == dv) {
+                               haschild++;
+                               break;
+                       }
+               }
+               if (!haschild && !config_detach(dv, 0))
+                       dv = 0;
+       }
+
        if (dv) {
                dev->subdevs = malloc(2 * sizeof dv, M_USB, M_NOWAIT);
                if (dev->subdevs == NULL)


Home | Main Index | Thread Index | Old Index