0a99a7950b
Add support for the Zoom 3095 USB Fax Modem. There are some problems with this modem: 1) The CS descriptors were placed after all other descriptors rather than in the Control Interface. This is true in both configurations and is the issue this patch deals with. 2) Modem audio is not done on the modem. There is a second configuration with an extra interface (3 interfaces) which presumably is for handling audio. This patch does not support that. 3) The interrupts are raised a bit slowly. This means that pppd(8) needs to wait a bit before trying to read from the link (i.e., just before the get_input() loop in src/dist/pppd/pppd/main.c line 547). If you don't wait, then DCD will not be detected high before that first read and get_input() will drop the line. This is not a problem if you use the "local" option to pppd(8) and ignore DCD, but that is less than desirable. 4) You apparently have to toggle "RSDL (DCD) option"[1] when you initialize the modem or you will never see a second DCD high interrupt when redialing after a disconnect. Without this the pppd(8) "persist" mode will not work. Presumably, some extra initialization is missing from the driver for this chipset. [1] Send the AT commands: AT&C0 followed by AT&C1 - doing it in one command doesn't work. The AT commands for this modem are at: http://www.zoom.com/documentation/dial_up/3095F_ATcommands.pdf
563 lines
15 KiB
C
563 lines
15 KiB
C
/* $NetBSD: usbdi_util.c,v 1.54 2010/10/01 20:56:10 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Lennart Augustsson (lennart@augustsson.net) at
|
|
* Carlstedt Research & Technology.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: usbdi_util.c,v 1.54 2010/10/01 20:56:10 christos Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <dev/usb/usb.h>
|
|
#include <dev/usb/usbhid.h>
|
|
|
|
#include <dev/usb/usbdi.h>
|
|
#include <dev/usb/usbdi_util.h>
|
|
|
|
#ifdef USB_DEBUG
|
|
#define DPRINTF(x) if (usbdebug) logprintf x
|
|
#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
|
|
extern int usbdebug;
|
|
#else
|
|
#define DPRINTF(x)
|
|
#define DPRINTFN(n,x)
|
|
#endif
|
|
|
|
usbd_status
|
|
usbd_get_desc(usbd_device_handle dev, int type, int index, int len, void *desc)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n",
|
|
type, index, len));
|
|
|
|
req.bmRequestType = UT_READ_DEVICE;
|
|
req.bRequest = UR_GET_DESCRIPTOR;
|
|
USETW2(req.wValue, type, index);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, len);
|
|
return (usbd_do_request(dev, &req, desc));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_config_desc(usbd_device_handle dev, int confidx,
|
|
usb_config_descriptor_t *d)
|
|
{
|
|
usbd_status err;
|
|
|
|
DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx));
|
|
err = usbd_get_desc(dev, UDESC_CONFIG, confidx,
|
|
USB_CONFIG_DESCRIPTOR_SIZE, d);
|
|
if (err)
|
|
return (err);
|
|
if (d->bDescriptorType != UDESC_CONFIG) {
|
|
DPRINTFN(-1,("usbd_get_config_desc: confidx=%d, bad desc "
|
|
"len=%d type=%d\n",
|
|
confidx, d->bLength, d->bDescriptorType));
|
|
return (USBD_INVAL);
|
|
}
|
|
return (USBD_NORMAL_COMPLETION);
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_config_desc_full(usbd_device_handle dev, int conf, void *d, int size)
|
|
{
|
|
DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
|
|
return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_device_desc(usbd_device_handle dev, usb_device_descriptor_t *d)
|
|
{
|
|
DPRINTFN(3,("usbd_get_device_desc:\n"));
|
|
return (usbd_get_desc(dev, UDESC_DEVICE,
|
|
0, USB_DEVICE_DESCRIPTOR_SIZE, d));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_device_status(usbd_device_handle dev, usb_status_t *st)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_READ_DEVICE;
|
|
req.bRequest = UR_GET_STATUS;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, sizeof(usb_status_t));
|
|
return (usbd_do_request(dev, &req, st));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_hub_status(usbd_device_handle dev, usb_hub_status_t *st)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_READ_CLASS_DEVICE;
|
|
req.bRequest = UR_GET_STATUS;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, sizeof(usb_hub_status_t));
|
|
return (usbd_do_request(dev, &req, st));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_address(usbd_device_handle dev, int addr)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_WRITE_DEVICE;
|
|
req.bRequest = UR_SET_ADDRESS;
|
|
USETW(req.wValue, addr);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, 0);
|
|
return usbd_do_request(dev, &req, 0);
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_port_status(usbd_device_handle dev, int port, usb_port_status_t *ps)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_READ_CLASS_OTHER;
|
|
req.bRequest = UR_GET_STATUS;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, port);
|
|
USETW(req.wLength, sizeof *ps);
|
|
return (usbd_do_request(dev, &req, ps));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_clear_hub_feature(usbd_device_handle dev, int sel)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_WRITE_CLASS_DEVICE;
|
|
req.bRequest = UR_CLEAR_FEATURE;
|
|
USETW(req.wValue, sel);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, 0);
|
|
return (usbd_do_request(dev, &req, 0));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_hub_feature(usbd_device_handle dev, int sel)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_WRITE_CLASS_DEVICE;
|
|
req.bRequest = UR_SET_FEATURE;
|
|
USETW(req.wValue, sel);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, 0);
|
|
return (usbd_do_request(dev, &req, 0));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_clear_port_feature(usbd_device_handle dev, int port, int sel)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_WRITE_CLASS_OTHER;
|
|
req.bRequest = UR_CLEAR_FEATURE;
|
|
USETW(req.wValue, sel);
|
|
USETW(req.wIndex, port);
|
|
USETW(req.wLength, 0);
|
|
return (usbd_do_request(dev, &req, 0));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_port_feature(usbd_device_handle dev, int port, int sel)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_WRITE_CLASS_OTHER;
|
|
req.bRequest = UR_SET_FEATURE;
|
|
USETW(req.wValue, sel);
|
|
USETW(req.wIndex, port);
|
|
USETW(req.wLength, 0);
|
|
return (usbd_do_request(dev, &req, 0));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report)
|
|
{
|
|
usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
|
|
usbd_device_handle dev;
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n",
|
|
iface, id->bInterfaceNumber));
|
|
if (id == NULL)
|
|
return (USBD_IOERROR);
|
|
usbd_interface2device_handle(iface, &dev);
|
|
req.bmRequestType = UT_READ_CLASS_INTERFACE;
|
|
req.bRequest = UR_GET_PROTOCOL;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, id->bInterfaceNumber);
|
|
USETW(req.wLength, 1);
|
|
return (usbd_do_request(dev, &req, report));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_protocol(usbd_interface_handle iface, int report)
|
|
{
|
|
usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
|
|
usbd_device_handle dev;
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
|
|
iface, report, id->bInterfaceNumber));
|
|
if (id == NULL)
|
|
return (USBD_IOERROR);
|
|
usbd_interface2device_handle(iface, &dev);
|
|
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
|
|
req.bRequest = UR_SET_PROTOCOL;
|
|
USETW(req.wValue, report);
|
|
USETW(req.wIndex, id->bInterfaceNumber);
|
|
USETW(req.wLength, 0);
|
|
return (usbd_do_request(dev, &req, 0));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_report(usbd_interface_handle iface, int type, int id, void *data,
|
|
int len)
|
|
{
|
|
usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
|
|
usbd_device_handle dev;
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
|
|
if (ifd == NULL)
|
|
return (USBD_IOERROR);
|
|
usbd_interface2device_handle(iface, &dev);
|
|
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
|
|
req.bRequest = UR_SET_REPORT;
|
|
USETW2(req.wValue, type, id);
|
|
USETW(req.wIndex, ifd->bInterfaceNumber);
|
|
USETW(req.wLength, len);
|
|
return (usbd_do_request(dev, &req, data));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_report_async(usbd_interface_handle iface, int type, int id, void *data,
|
|
int len)
|
|
{
|
|
usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
|
|
usbd_device_handle dev;
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
|
|
if (ifd == NULL)
|
|
return (USBD_IOERROR);
|
|
usbd_interface2device_handle(iface, &dev);
|
|
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
|
|
req.bRequest = UR_SET_REPORT;
|
|
USETW2(req.wValue, type, id);
|
|
USETW(req.wIndex, ifd->bInterfaceNumber);
|
|
USETW(req.wLength, len);
|
|
return (usbd_do_request_async(dev, &req, data));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_report(usbd_interface_handle iface, int type, int id, void *data,
|
|
int len)
|
|
{
|
|
usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
|
|
usbd_device_handle dev;
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(4, ("usbd_get_report: len=%d\n", len));
|
|
if (ifd == NULL)
|
|
return (USBD_IOERROR);
|
|
usbd_interface2device_handle(iface, &dev);
|
|
req.bmRequestType = UT_READ_CLASS_INTERFACE;
|
|
req.bRequest = UR_GET_REPORT;
|
|
USETW2(req.wValue, type, id);
|
|
USETW(req.wIndex, ifd->bInterfaceNumber);
|
|
USETW(req.wLength, len);
|
|
return (usbd_do_request(dev, &req, data));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_set_idle(usbd_interface_handle iface, int duration, int id)
|
|
{
|
|
usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
|
|
usbd_device_handle dev;
|
|
usb_device_request_t req;
|
|
|
|
DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
|
|
if (ifd == NULL)
|
|
return (USBD_IOERROR);
|
|
usbd_interface2device_handle(iface, &dev);
|
|
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
|
|
req.bRequest = UR_SET_IDLE;
|
|
USETW2(req.wValue, duration, id);
|
|
USETW(req.wIndex, ifd->bInterfaceNumber);
|
|
USETW(req.wLength, 0);
|
|
return (usbd_do_request(dev, &req, 0));
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
|
|
int size, void *d)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_READ_INTERFACE;
|
|
req.bRequest = UR_GET_DESCRIPTOR;
|
|
USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
|
|
USETW(req.wIndex, ifcno);
|
|
USETW(req.wLength, size);
|
|
return (usbd_do_request(dev, &req, d));
|
|
}
|
|
|
|
usb_hid_descriptor_t *
|
|
usbd_get_hid_descriptor(usbd_interface_handle ifc)
|
|
{
|
|
usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
|
|
usbd_device_handle dev;
|
|
usb_config_descriptor_t *cdesc;
|
|
usb_hid_descriptor_t *hd;
|
|
char *p, *end;
|
|
|
|
if (idesc == NULL)
|
|
return (NULL);
|
|
usbd_interface2device_handle(ifc, &dev);
|
|
cdesc = usbd_get_config_descriptor(dev);
|
|
|
|
p = (char *)idesc + idesc->bLength;
|
|
end = (char *)cdesc + UGETW(cdesc->wTotalLength);
|
|
|
|
for (; p < end; p += hd->bLength) {
|
|
hd = (usb_hid_descriptor_t *)p;
|
|
if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
|
|
return (hd);
|
|
if (hd->bDescriptorType == UDESC_INTERFACE)
|
|
break;
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
usbd_status
|
|
usbd_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep,
|
|
usb_malloc_type mem)
|
|
{
|
|
usb_interface_descriptor_t *id;
|
|
usb_hid_descriptor_t *hid;
|
|
usbd_device_handle dev;
|
|
usbd_status err;
|
|
|
|
usbd_interface2device_handle(ifc, &dev);
|
|
id = usbd_get_interface_descriptor(ifc);
|
|
if (id == NULL)
|
|
return (USBD_INVAL);
|
|
hid = usbd_get_hid_descriptor(ifc);
|
|
if (hid == NULL)
|
|
return (USBD_IOERROR);
|
|
*sizep = UGETW(hid->descrs[0].wDescriptorLength);
|
|
*descp = malloc(*sizep, mem, M_NOWAIT);
|
|
if (*descp == NULL)
|
|
return (USBD_NOMEM);
|
|
err = usbd_get_report_descriptor(dev, id->bInterfaceNumber,
|
|
*sizep, *descp);
|
|
if (err) {
|
|
free(*descp, mem);
|
|
*descp = NULL;
|
|
return (err);
|
|
}
|
|
return (USBD_NORMAL_COMPLETION);
|
|
}
|
|
|
|
usbd_status
|
|
usbd_get_config(usbd_device_handle dev, u_int8_t *conf)
|
|
{
|
|
usb_device_request_t req;
|
|
|
|
req.bmRequestType = UT_READ_DEVICE;
|
|
req.bRequest = UR_GET_CONFIG;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, 1);
|
|
return (usbd_do_request(dev, &req, conf));
|
|
}
|
|
|
|
Static void usbd_bulk_transfer_cb(usbd_xfer_handle xfer,
|
|
usbd_private_handle priv, usbd_status status);
|
|
Static void
|
|
usbd_bulk_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
|
|
usbd_status status)
|
|
{
|
|
wakeup(xfer);
|
|
}
|
|
|
|
usbd_status
|
|
usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
|
|
u_int16_t flags, u_int32_t timeout, void *buf,
|
|
u_int32_t *size, const char *lbl)
|
|
{
|
|
usbd_status err;
|
|
int s, error;
|
|
|
|
usbd_setup_xfer(xfer, pipe, 0, buf, *size,
|
|
flags, timeout, usbd_bulk_transfer_cb);
|
|
DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
|
|
s = splusb(); /* don't want callback until tsleep() */
|
|
err = usbd_transfer(xfer);
|
|
if (err != USBD_IN_PROGRESS) {
|
|
splx(s);
|
|
return (err);
|
|
}
|
|
error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
|
|
splx(s);
|
|
if (error) {
|
|
DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error));
|
|
usbd_abort_pipe(pipe);
|
|
return (USBD_INTERRUPTED);
|
|
}
|
|
usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
|
|
DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
|
|
if (err) {
|
|
DPRINTF(("usbd_bulk_transfer: error=%d\n", err));
|
|
usbd_clear_endpoint_stall(pipe);
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
Static void usbd_intr_transfer_cb(usbd_xfer_handle xfer,
|
|
usbd_private_handle priv, usbd_status status);
|
|
Static void
|
|
usbd_intr_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
|
|
usbd_status status)
|
|
{
|
|
wakeup(xfer);
|
|
}
|
|
|
|
usbd_status
|
|
usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
|
|
u_int16_t flags, u_int32_t timeout, void *buf,
|
|
u_int32_t *size, const char *lbl)
|
|
{
|
|
usbd_status err;
|
|
int s, error;
|
|
|
|
usbd_setup_xfer(xfer, pipe, 0, buf, *size,
|
|
flags, timeout, usbd_intr_transfer_cb);
|
|
DPRINTFN(1, ("usbd_intr_transfer: start transfer %d bytes\n", *size));
|
|
s = splusb(); /* don't want callback until tsleep() */
|
|
err = usbd_transfer(xfer);
|
|
if (err != USBD_IN_PROGRESS) {
|
|
splx(s);
|
|
return (err);
|
|
}
|
|
error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
|
|
splx(s);
|
|
if (error) {
|
|
DPRINTF(("usbd_intr_transfer: tsleep=%d\n", error));
|
|
usbd_abort_pipe(pipe);
|
|
return (USBD_INTERRUPTED);
|
|
}
|
|
usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
|
|
DPRINTFN(1,("usbd_intr_transfer: transferred %d\n", *size));
|
|
if (err) {
|
|
DPRINTF(("usbd_intr_transfer: error=%d\n", err));
|
|
usbd_clear_endpoint_stall(pipe);
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
void
|
|
usb_detach_wait(device_ptr_t dv)
|
|
{
|
|
DPRINTF(("usb_detach_wait: waiting for %s\n", USBDEVPTRNAME(dv)));
|
|
if (tsleep(dv, PZERO, "usbdet", hz * 60))
|
|
printf("usb_detach_wait: %s didn't detach\n",
|
|
USBDEVPTRNAME(dv));
|
|
DPRINTF(("usb_detach_wait: %s done\n", USBDEVPTRNAME(dv)));
|
|
}
|
|
|
|
void
|
|
usb_detach_wakeup(device_ptr_t dv)
|
|
{
|
|
DPRINTF(("usb_detach_wakeup: for %s\n", USBDEVPTRNAME(dv)));
|
|
wakeup(dv);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|