/* same as usb_find_desc(), but searches only in the specified interface. */ const usb_cdc_descriptor_t * usb_find_desc_if(usbd_device_handle dev, int type, int subtype, usb_interface_descriptor_t *id) { usbd_desc_iter_t iter; const usb_cdc_descriptor_t *desc; if (id == NULL) return usb_find_desc(dev, type, subtype); usb_desc_iter_init(dev, &iter); iter.cur = (void *)id; /* start from the interface desc */ usb_desc_iter_next(&iter); /* and skip it */ while ((desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter)) != NULL) { if (desc->bDescriptorType == UDESC_INTERFACE) { /* we ran into the next interface --- not found */ return NULL; } if (desc->bDescriptorType == type && (subtype == USBD_CDCSUBTYPE_ANY || subtype == desc->bDescriptorSubtype)) break; } return desc; }
const usb_cdc_descriptor_t * usb_find_desc(usbd_device_handle dev, int type, int subtype) { usbd_desc_iter_t iter; const usb_cdc_descriptor_t *desc; usb_desc_iter_init(dev, &iter); for (;;) { desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter); if (!desc || (desc->bDescriptorType == type && (subtype == USBD_CDCSUBTYPE_ANY || subtype == desc->bDescriptorSubtype))) break; } return desc; }
void umodem_attach(struct device *parent, struct device *self, void *aux) { struct umodem_softc *sc = (struct umodem_softc *)self; struct usb_attach_arg *uaa = aux; usbd_device_handle dev = uaa->device; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usb_cdc_cm_descriptor_t *cmd; usb_interface_descriptor_t *idesc; const usb_cdc_acm_descriptor_t *acmd; const usb_cdc_union_descriptor_t *uniond; const usb_descriptor_t *desc; usbd_desc_iter_t iter; usbd_status err; int current_iface_no = -1; int i; struct ucom_attach_args uca; sc->sc_udev = dev; sc->sc_ctl_iface = uaa->iface; id = usbd_get_interface_descriptor(sc->sc_ctl_iface); //printf("%s: iclass %d/%d\n", sc->sc_dev.dv_xname, // id->bInterfaceClass, id->bInterfaceSubClass); sc->sc_ctl_iface_no = id->bInterfaceNumber; /* Get the data interface no. and capabilities */ sc->sc_cm_cap = 0; sc->sc_data_iface_no = 0; sc->sc_acm_cap = 0; usb_desc_iter_init(dev, &iter); desc = usb_desc_iter_next(&iter); while (desc) { if (desc->bDescriptorType == UDESC_INTERFACE) { idesc = (usb_interface_descriptor_t *)desc; current_iface_no = idesc->bInterfaceNumber; } if (current_iface_no == sc->sc_ctl_iface_no && desc->bDescriptorType == UDESC_CS_INTERFACE) { switch(desc->bDescriptorSubtype) { case UDESCSUB_CDC_CM: cmd = (usb_cdc_cm_descriptor_t *)desc; sc->sc_cm_cap = cmd->bmCapabilities; sc->sc_data_iface_no = cmd->bDataInterface; break; case UDESCSUB_CDC_ACM: acmd = (usb_cdc_acm_descriptor_t *)desc; sc->sc_acm_cap = acmd->bmCapabilities; break; case UDESCSUB_CDC_UNION: uniond = (usb_cdc_union_descriptor_t *)desc; sc->sc_data_iface_no = uniond->bSlaveInterface[0]; break; } } desc = usb_desc_iter_next(&iter); } if (sc->sc_data_iface_no == 0) { printf("%s: no pointer to data interface\n", sc->sc_dev.dv_xname); goto bad; } printf("%s: data interface %d, has %sCM over data, has %sbreak\n", sc->sc_dev.dv_xname, sc->sc_data_iface_no, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); /* Get the data interface too. */ for (i = 0; i < uaa->nifaces; i++) { if (uaa->ifaces[i] != NULL) { id = usbd_get_interface_descriptor(uaa->ifaces[i]); if (id != NULL && id->bInterfaceNumber == sc->sc_data_iface_no) { sc->sc_data_iface = uaa->ifaces[i]; uaa->ifaces[i] = NULL; } } } if (sc->sc_data_iface == NULL) { printf("%s: no data interface\n", sc->sc_dev.dv_xname); goto bad; } /* * Find the bulk endpoints. * Iterate over all endpoints in the data interface and take note. */ uca.bulkin = uca.bulkout = -1; id = usbd_get_interface_descriptor(sc->sc_data_iface); for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i); if (ed == NULL) { printf("%s: no endpoint descriptor for %d\n", sc->sc_dev.dv_xname, i); goto bad; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { uca.bulkin = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { uca.bulkout = ed->bEndpointAddress; } } if (uca.bulkin == -1) { printf("%s: Could not find data bulk in\n", sc->sc_dev.dv_xname); goto bad; } if (uca.bulkout == -1) { printf("%s: Could not find data bulk out\n", sc->sc_dev.dv_xname); goto bad; } if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_ASSUME_CM_OVER_DATA) { sc->sc_cm_over_data = 1; } else { if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) err = umodem_set_comm_feature(sc, UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); else err = 0; if (err) { printf("%s: could not set data multiplex mode\n", sc->sc_dev.dv_xname); goto bad; } sc->sc_cm_over_data = 1; } } /* * The standard allows for notification messages (to indicate things * like a modem hangup) to come in via an interrupt endpoint * off of the control interface. Iterate over the endpoints on * the control interface and see if there are any interrupt * endpoints; if there are, then register it. */ sc->sc_ctl_notify = -1; sc->sc_notify_pipe = NULL; id = usbd_get_interface_descriptor(sc->sc_ctl_iface); for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_ctl_iface, i); if (ed == NULL) continue; if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { printf("%s: status change notification available\n", sc->sc_dev.dv_xname); sc->sc_ctl_notify = ed->bEndpointAddress; } } sc->sc_dtr = -1; uca.portno = UCOM_UNK_PORTNO; /* bulkin, bulkout set above */ uca.ibufsize = UMODEMIBUFSIZE; uca.obufsize = UMODEMOBUFSIZE; uca.ibufsizepad = UMODEMIBUFSIZE; uca.opkthdrlen = 0; uca.device = sc->sc_udev; uca.iface = sc->sc_data_iface; uca.methods = &umodem_methods; uca.arg = sc; uca.info = NULL; usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, &sc->sc_dev); DPRINTF(("umodem_attach: sc=%p\n", sc)); sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); return; bad: sc->sc_dying = 1; }