usb_subr.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,109 行 · 第 1/2 页

C
1,109
字号
/*	$NetBSD: usb_subr.c,v 1.29 1999/03/18 12:08:43 augustss Exp $	*//*	$FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.7.2.1 1999/05/08 23:04:55 n_hibma 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 (augustss@carlstedt.se) 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. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *        This product includes software developed by the NetBSD *        Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its *    contributors may be used to endorse or promote products derived *    from this software without specific prior written permission. * * 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/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/malloc.h>#if defined(__NetBSD__)#include <sys/device.h>#elif defined(__FreeBSD__)#include <sys/module.h>#include <sys/bus.h>#endif#include <sys/proc.h>#include <sys/select.h>#include <dev/usb/usb.h>#include <dev/usb/usbdi.h>#include <dev/usb/usbdi_util.h>#include <dev/usb/usbdivar.h>#include <dev/usb/usbdevs.h>#include <dev/usb/usb_quirks.h>#if defined(__FreeBSD__)#include <machine/clock.h>#define delay(d)         DELAY(d)#endif#ifdef USB_DEBUG#define DPRINTF(x)	if (usbdebug) logprintf x#define DPRINTFN(n,x)	if (usbdebug>(n)) logprintf xextern int usbdebug;#else#define DPRINTF(x)#define DPRINTFN(n,x)#endiftypedef u_int16_t usb_vendor_id_t;typedef u_int16_t usb_product_id_t;/* * Descriptions of of known vendors and devices ("products"). */struct usb_knowndev {        usb_vendor_id_t         vendor;        usb_product_id_t        product;        int                     flags;         char                    *vendorname, *productname;};#define USB_KNOWNDEV_NOPROD     0x01            /* match on vendor only */#include <dev/usb/usbdevs_data.h>static usbd_status	usbd_set_config __P((usbd_device_handle, int));char *usbd_get_string __P((usbd_device_handle, int, char *));int usbd_getnewaddr __P((usbd_bus_handle bus));#if defined(__NetBSD__)int usbd_print __P((void *aux, const char *pnp));int usbd_submatch __P((bdevice *, struct cfdata *cf, void *));#endifvoid usbd_free_iface_data __P((usbd_device_handle dev, int ifcno));void usbd_kill_pipe __P((usbd_pipe_handle));usbd_status usbd_probe_and_attach 	__P((bdevice *parent, usbd_device_handle dev, int port, int addr));usbd_statususbd_get_string_desc(dev, sindex, langid, sdesc)	usbd_device_handle dev;	int sindex;	int langid;	usb_string_descriptor_t *sdesc;{	usb_device_request_t req;	usbd_status r;	req.bmRequestType = UT_READ_DEVICE;	req.bRequest = UR_GET_DESCRIPTOR;	USETW2(req.wValue, UDESC_STRING, sindex);	USETW(req.wIndex, langid);	USETW(req.wLength, 1);	/* only size byte first */	r = usbd_do_request(dev, &req, sdesc);	if (r != USBD_NORMAL_COMPLETION)		return (r);	USETW(req.wLength, sdesc->bLength);	/* the whole string */	return (usbd_do_request(dev, &req, sdesc));}char *usbd_get_string(dev, si, buf)	usbd_device_handle dev;	int si;	char *buf;{	int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;	usb_string_descriptor_t us;	char *s;	int i, n;	u_int16_t c;	usbd_status r;	if (si == 0)		return (0);	if (dev->quirks->uq_flags & UQ_NO_STRINGS)		return (0);	if (dev->langid == USBD_NOLANG) {		/* Set up default language */		r = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us);		if (r != USBD_NORMAL_COMPLETION || us.bLength < 4) {			dev->langid = 0; /* Well, just pick English then */		} else {			/* Pick the first language as the default. */			dev->langid = UGETW(us.bString[0]);		}	}	r = usbd_get_string_desc(dev, si, dev->langid, &us);	if (r != USBD_NORMAL_COMPLETION)		return (0);	s = buf;	n = us.bLength / 2 - 1;	for (i = 0; i < n; i++) {		c = UGETW(us.bString[i]);		/* Convert from Unicode, handle buggy strings. */		if ((c & 0xff00) == 0)			*s++ = c;		else if ((c & 0x00ff) == 0 && swap)			*s++ = c >> 8;		else 			*s++ = '?';	}	*s++ = 0;	return buf;}voidusbd_devinfo_vp(dev, v, p)	usbd_device_handle dev;	char *v, *p;{	usb_device_descriptor_t *udd = &dev->ddesc;	char *vendor, *product;	struct usb_knowndev *kdp;	vendor = usbd_get_string(dev, udd->iManufacturer, v);	product = usbd_get_string(dev, udd->iProduct, p);	if (!vendor) {		for(kdp = usb_knowndevs;		    kdp->vendorname != NULL;		    kdp++) {			if (kdp->vendor == UGETW(udd->idVendor) && 			    (kdp->product == UGETW(udd->idProduct) ||			    (kdp->flags & USB_KNOWNDEV_NOPROD) != 0))				break;		}		if (kdp->vendorname == NULL) {			vendor = product = NULL;		} else {			vendor = kdp->vendorname;			product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ?			kdp->productname : NULL;		}	}	if (vendor)		strcpy(v, vendor);	else		sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor));	if (product)		strcpy(p, product);	else		sprintf(p, "product 0x%04x", UGETW(udd->idProduct));}intusbd_printBCD(cp, bcd)	char *cp;	int bcd;{	return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff));}voidusbd_devinfo(dev, showclass, cp)	usbd_device_handle dev;	int showclass;	char *cp;{	usb_device_descriptor_t *udd = &dev->ddesc;	char vendor[USB_MAX_STRING_LEN];	char product[USB_MAX_STRING_LEN];	int bcdDevice, bcdUSB;	usbd_devinfo_vp(dev, vendor, product);	cp += sprintf(cp, "%s %s", vendor, product);	if (showclass)		cp += sprintf(cp, ", class %d/%d",			      udd->bDeviceClass, udd->bDeviceSubClass);	bcdUSB = UGETW(udd->bcdUSB);	bcdDevice = UGETW(udd->bcdDevice);	cp += sprintf(cp, ", rev ");	cp += usbd_printBCD(cp, bcdUSB);	*cp++ = '/';	cp += usbd_printBCD(cp, bcdDevice);	cp += sprintf(cp, ", addr %d", dev->address);	*cp = 0;}/* Delay for a certain number of ms */voidusb_delay_ms(bus, ms)	usbd_bus_handle bus;	u_int ms;{	/* Wait at least two clock ticks so we know the time has passed. */	if (bus->use_polling)		delay((ms+1) * 1000);	else		tsleep(&ms, PRIBIO, "usbdly", (ms*hz+999)/1000 + 1);}/* Delay given a device handle. */voidusbd_delay_ms(dev, ms)	usbd_device_handle dev;	u_int ms;{	usb_delay_ms(dev->bus, ms);}usbd_statususbd_reset_port(dev, port, ps)	usbd_device_handle dev;	int port;	usb_port_status_t *ps;{	usb_device_request_t req;	usbd_status r;	int n;		req.bmRequestType = UT_WRITE_CLASS_OTHER;	req.bRequest = UR_SET_FEATURE;	USETW(req.wValue, UHF_PORT_RESET);	USETW(req.wIndex, port);	USETW(req.wLength, 0);	r = usbd_do_request(dev, &req, 0);	DPRINTFN(1,("usbd_reset_port: port %d reset done, %s\n",		port, usbd_errstr(r)));	if (r != USBD_NORMAL_COMPLETION)		return(r);	n = 10;	do {		/* Wait for device to recover from reset. */		usbd_delay_ms(dev, USB_PORT_RESET_DELAY);		r = usbd_get_port_status(dev, port, ps);		if (r != USBD_NORMAL_COMPLETION) {			DPRINTF(("usbd_reset_port: get port %d status failed %s\n",				port, usbd_errstr(r)));			return (r);		}	} while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0);	if (n == 0) {		printf("usbd_reset_port: timeout\n");		return (USBD_IOERROR);	}	r = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);#ifdef USB_DEBUG	if (r != USBD_NORMAL_COMPLETION)		DPRINTF(("usbd_reset_port: clear port %d feature failed %d\n",			port, r));#endif	/* Wait for the device to recover from reset. */	usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY);	return (r);}usb_interface_descriptor_t *usbd_find_idesc(cd, ifaceidx, altidx)	usb_config_descriptor_t *cd;	int ifaceidx;	int altidx;{	char *p = (char *)cd;	char *end = p + UGETW(cd->wTotalLength);	usb_interface_descriptor_t *d;	int curidx, lastidx, curaidx = 0;	for (curidx = lastidx = -1; p < end; ) {		d = (usb_interface_descriptor_t *)p;		DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d "			    "type=%d\n", 			    ifaceidx, curidx, altidx, curaidx,			    d->bLength, d->bDescriptorType));		if (d->bLength == 0) /* bad descriptor */			break;		p += d->bLength;		if (p <= end && d->bDescriptorType == UDESC_INTERFACE) {			if (d->bInterfaceNumber != lastidx) {				lastidx = d->bInterfaceNumber;				curidx++;				curaidx = 0;			} else				curaidx++;			if (ifaceidx == curidx && altidx == curaidx)				return (d);		}	}	return (0);}usb_endpoint_descriptor_t *usbd_find_edesc(cd, ifaceidx, altidx, endptidx)	usb_config_descriptor_t *cd;	int ifaceidx;	int altidx;	int endptidx;{	char *p = (char *)cd;	char *end = p + UGETW(cd->wTotalLength);	usb_interface_descriptor_t *d;	usb_endpoint_descriptor_t *e;	int curidx;	d = usbd_find_idesc(cd, ifaceidx, altidx);	if (!d)		return (0);	if (endptidx >= d->bNumEndpoints) /* quick exit */		return (0);	curidx = -1;	for (p = (char *)d + d->bLength; p < end; ) {		e = (usb_endpoint_descriptor_t *)p;		if (e->bLength == 0) /* bad descriptor */			break;		p += e->bLength;		if (p <= end && e->bDescriptorType == UDESC_INTERFACE)			return (0);		if (p <= end && e->bDescriptorType == UDESC_ENDPOINT) {			curidx++;			if (curidx == endptidx)				return (e);		}	}	return (0);}usbd_statususbd_fill_iface_data(dev, ifaceidx, altidx)	usbd_device_handle dev;	int ifaceidx;	int altidx;{	usbd_interface_handle ifc = &dev->ifaces[ifaceidx];	char *p, *end;	int endpt, nendpt;	DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n",		    ifaceidx, altidx));	ifc->device = dev;	ifc->idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx);	if (ifc->idesc == 0)		return (USBD_INVAL);	ifc->index = ifaceidx;	ifc->altindex = altidx;	nendpt = ifc->idesc->bNumEndpoints;	DPRINTFN(10,("usbd_fill_iface_data: found idesc n=%d\n", nendpt));	if (nendpt != 0) {		ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint),					M_USB, M_NOWAIT);		if (ifc->endpoints == 0)			return (USBD_NOMEM);	} else		ifc->endpoints = 0;	ifc->priv = 0;	p = (char *)ifc->idesc + ifc->idesc->bLength;	end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength);#define ed ((usb_endpoint_descriptor_t *)p)	for (endpt = 0; endpt < nendpt; endpt++) {		DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt));		for (; p < end; p += ed->bLength) {			ed = (usb_endpoint_descriptor_t *)p;			DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p "				     "len=%d type=%d\n",				 p, end, ed->bLength, ed->bDescriptorType));			if (p + ed->bLength <= end && ed->bLength != 0 &&			    ed->bDescriptorType == UDESC_ENDPOINT)				goto found;			if (ed->bDescriptorType == UDESC_INTERFACE ||			    ed->bLength == 0)				break;		}		/* passed end, or bad desc */		goto bad;	found:		ifc->endpoints[endpt].edesc = ed;		ifc->endpoints[endpt].state = USBD_ENDPOINT_ACTIVE;		ifc->endpoints[endpt].refcnt = 0;		ifc->endpoints[endpt].toggle = 0;		p += ed->bLength;	}#undef ed	LIST_INIT(&ifc->pipes);	ifc->state = USBD_INTERFACE_ACTIVE;	return (USBD_NORMAL_COMPLETION); bad:	free(ifc->endpoints, M_USB);	return (USBD_INVAL);}voidusbd_free_iface_data(dev, ifcno)	usbd_device_handle dev;	int ifcno;{	usbd_interface_handle ifc = &dev->ifaces[ifcno];	if (ifc->endpoints)		free(ifc->endpoints, M_USB);}static usbd_statususbd_set_config(dev, conf)	usbd_device_handle dev;	int conf;{	usb_device_request_t req;	req.bmRequestType = UT_WRITE_DEVICE;	req.bRequest = UR_SET_CONFIG;	USETW(req.wValue, conf);	USETW(req.wIndex, 0);	USETW(req.wLength, 0);	return (usbd_do_request(dev, &req, 0));}usbd_statususbd_set_config_no(dev, no, msg)	usbd_device_handle dev;	int no;	int msg;{	int index;	usb_config_descriptor_t cd;	usbd_status r;	DPRINTFN(5,("usbd_set_config_no: %d\n", no));	/* Figure out what config index to use. */	for (index = 0; index < dev->ddesc.bNumConfigurations; index++) {		r = usbd_get_config_desc(dev, index, &cd);		if (r != USBD_NORMAL_COMPLETION)			return (r);		if (cd.bConfigurationValue == no)			return (usbd_set_config_index(dev, index, msg));	}	return (USBD_INVAL);}usbd_statususbd_set_config_index(dev, index, msg)	usbd_device_handle dev;	int index;	int msg;{	usb_status_t ds;	usb_hub_status_t hs;	usb_config_descriptor_t cd, *cdp;	usbd_status r;	int ifcidx, nifc, len, selfpowered, power;	DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index));	/* XXX check that all interfaces are idle */	if (dev->config != 0) {		DPRINTF(("usbd_set_config_index: free old config\n"));		/* Free all configuration data structures. */		nifc = dev->cdesc->bNumInterface;		for (ifcidx = 0; ifcidx < nifc; ifcidx++)			usbd_free_iface_data(dev, ifcidx);		free(dev->ifaces, M_USB);		free(dev->cdesc, M_USB);		dev->ifaces = 0;		dev->cdesc = 0;		dev->config = 0;		dev->state = USBD_DEVICE_ADDRESSED;	}	/* Figure out what config number to use. */	r = usbd_get_config_desc(dev, index, &cd);	if (r != USBD_NORMAL_COMPLETION)		return (r);	len = UGETW(cd.wTotalLength);	cdp = malloc(len, M_USB, M_NOWAIT);	if (cdp == 0)		return (USBD_NOMEM);	r = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp);	if (r != USBD_NORMAL_COMPLETION)		goto bad;	if (cdp->bDescriptorType != UDESC_CONFIG) {		DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n",			     cdp->bDescriptorType));		r = USBD_INVAL;		goto bad;	}	selfpowered = 0;	if (cdp->bmAttributes & UC_SELF_POWERED) {		/* May be self powered. */		if (cdp->bmAttributes & UC_BUS_POWERED) {			/* Must ask device. */			if (dev->quirks->uq_flags & UQ_HUB_POWER) {				/* Buggy hub, use hub descriptor. */				r = usbd_get_hub_status(dev, &hs);				if (r == USBD_NORMAL_COMPLETION && 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?