ugen.c

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

C
1,003
字号
/*	$NetBSD: ugen.c,v 1.11 1999/01/08 11:58:25 augustss Exp $	*//*	$FreeBSD: src/sys/dev/usb/ugen.c,v 1.8.2.1 1999/05/08 23:04:46 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>#include <sys/ioctl.h>#elif defined(__FreeBSD__)#include <sys/module.h>#include <sys/bus.h>#include <sys/ioccom.h>#include <sys/conf.h>#include <sys/fcntl.h>#include <sys/filio.h>#endif#include <sys/tty.h>#include <sys/file.h>#include <sys/select.h>#include <sys/proc.h>#include <sys/vnode.h>#include <sys/poll.h>#include <dev/usb/usb.h>#include <dev/usb/usbdi.h>#include <dev/usb/usbdi_util.h>#ifdef UGEN_DEBUG#define DPRINTF(x)	if (ugendebug) logprintf x#define DPRINTFN(n,x)	if (ugendebug>(n)) logprintf xint	ugendebug = 1;#else#define DPRINTF(x)#define DPRINTFN(n,x)#endifstruct ugen_endpoint {	struct ugen_softc *sc;	usb_endpoint_descriptor_t *edesc;	usbd_interface_handle iface;	int state;#define UGEN_OPEN	0x01	/* device is open */#define	UGEN_ASLP	0x02	/* waiting for data */#define UGEN_SHORT_OK	0x04	/* short xfers are OK */	usbd_pipe_handle pipeh;	struct clist q;	struct selinfo rsel;	void *ibuf;};#define	UGEN_CHUNK	128	/* chunk size for read */#define	UGEN_IBSIZE	1020	/* buffer size */#define	UGEN_BBSIZE	1024struct ugen_softc {	bdevice sc_dev;		/* base device */	struct usbd_device *sc_udev;	struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];#define OUT 0			/* index order is important, from UE_OUT */#define IN  1			/* from UE_IN */	int sc_disconnected;		/* device is gone */};#if defined(__NetBSD__)int ugenopen __P((dev_t, int, int, struct proc *));int ugenclose __P((dev_t, int, int, struct proc *p));int ugenread __P((dev_t, struct uio *uio, int));int ugenwrite __P((dev_t, struct uio *uio, int));int ugenioctl __P((dev_t, u_long, caddr_t, int, struct proc *));int ugenpoll __P((dev_t, int, struct proc *));#elif defined(__FreeBSD__)d_open_t  ugenopen;d_close_t ugenclose;d_read_t  ugenread;d_write_t ugenwrite;d_ioctl_t ugenioctl;d_poll_t  ugenpoll;#define UGEN_CDEV_MAJOR	114static struct cdevsw ugen_cdevsw = {	ugenopen,	ugenclose,	ugenread,	ugenwrite,	ugenioctl,	nostop,		nullreset,	nodevtotty,	ugenpoll,	nommap,		nostrat,	"ugen",		NULL,		-1};#endifvoid ugenintr __P((usbd_request_handle reqh, usbd_private_handle addr, 		   usbd_status status));void ugen_disco __P((void *));int ugen_set_config __P((struct ugen_softc *sc, int configno));usb_config_descriptor_t *ugen_get_cdesc __P((struct ugen_softc *sc, int index,					     int *lenp));usbd_status ugen_set_interface __P((struct ugen_softc *, int, int));int ugen_get_alt_index __P((struct ugen_softc *sc, int ifaceidx));#define UGENUNIT(n) (((n) >> 4) & 0xf)#define UGENENDPOINT(n) ((n) & 0xf)USB_DECLARE_DRIVER(ugen);USB_MATCH(ugen){	USB_MATCH_START(ugen, uaa);	if (uaa->usegeneric)		return (UMATCH_GENERIC);	else		return (UMATCH_NONE);}USB_ATTACH(ugen){	USB_ATTACH_START(ugen, sc, uaa);	char devinfo[1024];	usbd_status r;	int conf;		usbd_devinfo(uaa->device, 0, devinfo);	USB_ATTACH_SETUP;	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);	sc->sc_udev = uaa->device;	conf = 1;		/* XXX should not hard code 1 */	r = ugen_set_config(sc, conf);	if (r != USBD_NORMAL_COMPLETION) {		printf("%s: setting configuration %d failed\n", 		       USBDEVNAME(sc->sc_dev), conf);		sc->sc_disconnected = 1;		USB_ATTACH_ERROR_RETURN;	}	USB_ATTACH_SUCCESS_RETURN;}intugen_set_config(sc, configno)	struct ugen_softc *sc;	int configno;{	usbd_device_handle dev = sc->sc_udev;	usbd_interface_handle iface;	usb_endpoint_descriptor_t *ed;	struct ugen_endpoint *sce;	u_int8_t niface, nendpt;	int ifaceno, endptno, endpt;	usbd_status r;	DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",		    USBDEVNAME(sc->sc_dev), configno, sc));	if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) {		/* Avoid setting the current value. */		r = usbd_set_config_no(dev, configno, 0);		if (r != USBD_NORMAL_COMPLETION)			return (r);	}	r = usbd_interface_count(dev, &niface);	if (r != USBD_NORMAL_COMPLETION)		return (r);	memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);	for (ifaceno = 0; ifaceno < niface; ifaceno++) {		DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));		r = usbd_device2interface_handle(dev, ifaceno, &iface);		if (r != USBD_NORMAL_COMPLETION)			return (r);		r = usbd_endpoint_count(iface, &nendpt);		if (r != USBD_NORMAL_COMPLETION)			return (r);		for (endptno = 0; endptno < nendpt; endptno++) {			ed = usbd_interface2endpoint_descriptor(iface,endptno);			endpt = ed->bEndpointAddress;			sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)]				               [UE_GET_IN(endpt)];			DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"				    "(%d,%d), sce=%p\n", 				    endptno, endpt, UE_GET_ADDR(endpt),				    UE_GET_IN(endpt), sce));			sce->sc = sc;			sce->edesc = ed;			sce->iface = iface;		}	}	return (USBD_NORMAL_COMPLETION);}voidugen_disco(p)	void *p;{	struct ugen_softc *sc = p;	sc->sc_disconnected = 1;}intugenopen(dev, flag, mode, p)	dev_t dev;	int flag;	int mode;	struct proc *p;{	int unit = UGENUNIT(dev);	int endpt = UGENENDPOINT(dev);	usb_endpoint_descriptor_t *edesc;	struct ugen_endpoint *sce;	int dir, isize;	usbd_status r;	USB_GET_SC_OPEN(ugen, unit, sc);	DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", 		     flag, mode, unit, endpt));	if (sc->sc_disconnected)		return (EIO);	if (endpt == USB_CONTROL_ENDPOINT) {		/*if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))		  return (EACCES);*/		sce = &sc->sc_endpoints[USB_CONTROL_ENDPOINT][OUT];		if (sce->state & UGEN_OPEN)			return (EBUSY);	} else {		switch (flag & (FWRITE|FREAD)) {		case FWRITE:			dir = OUT;			break;		case FREAD:			dir = IN;			break;		default:			return (EACCES);		}		sce = &sc->sc_endpoints[endpt][dir];		DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", 			     sc, endpt, dir, sce));		if (sce->state & UGEN_OPEN)			return (EBUSY);		edesc = sce->edesc;		if (!edesc)			return (ENXIO);		switch (edesc->bmAttributes & UE_XFERTYPE) {		case UE_INTERRUPT:			isize = UGETW(edesc->wMaxPacketSize);			if (isize == 0)	/* shouldn't happen */				return (EINVAL);			sce->ibuf = malloc(isize, M_USB, M_WAITOK);			DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", 				     endpt, isize));#if defined(__NetBSD__)			if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1)				return (ENOMEM);#elif defined(__FreeBSD__)			clist_alloc_cblocks(&sce->q, UGEN_IBSIZE, 0);#endif			r = usbd_open_pipe_intr(sce->iface, 				edesc->bEndpointAddress, 				USBD_SHORT_XFER_OK, &sce->pipeh, sce, 				sce->ibuf, isize, ugenintr);			if (r != USBD_NORMAL_COMPLETION) {				free(sce->ibuf, M_USB);#if defined(__NetBSD__)				clfree(&sce->q);#elif defined(__FreeBSD__)				clist_free_cblocks(&sce->q);#endif				return (EIO);			}			usbd_set_disco(sce->pipeh, ugen_disco, sc);			DPRINTFN(5, ("ugenopen: interrupt open done\n"));			break;		case UE_BULK:			r = usbd_open_pipe(sce->iface, 					   edesc->bEndpointAddress, 0, 					   &sce->pipeh);			if (r != USBD_NORMAL_COMPLETION)				return (EIO);			break;		case UE_CONTROL:		case UE_ISOCHRONOUS:			return (EINVAL);		}	}	sce->state |= UGEN_OPEN;	return (0);}intugenclose(dev, flag, mode, p)	dev_t dev;	int flag;	int mode;	struct proc *p;{	USB_GET_SC(ugen, UGENUNIT(dev), sc);	int endpt = UGENENDPOINT(dev);	struct ugen_endpoint *sce;	int dir;	DPRINTFN(5, ("ugenclose: flag=%d, mode=%d\n", flag, mode));	if (sc->sc_disconnected)		return (EIO);	if (endpt == USB_CONTROL_ENDPOINT) {		DPRINTFN(5, ("ugenclose: close control\n"));		sc->sc_endpoints[endpt][OUT].state = 0;		return (0);	}	flag = FWRITE | FREAD;	/* XXX bug if generic open/close */	/* The open modes have been joined, so check for both modes. */	for (dir = OUT; dir <= IN; dir++) {		if (flag & (dir == OUT ? FWRITE : FREAD)) {			sce = &sc->sc_endpoints[endpt][dir];			if (!sce || !sce->pipeh) /* XXX */				continue; /* XXX */			DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", 				     endpt, dir, sce));			sce->state = 0;			usbd_abort_pipe(sce->pipeh);			usbd_close_pipe(sce->pipeh);			sce->pipeh = 0;			if (sce->ibuf) {				free(sce->ibuf, M_USB);				sce->ibuf = 0;			}		}	}	return (0);}intugenread(dev, uio, flag)	dev_t dev;	struct uio *uio;	int flag;{	USB_GET_SC(ugen, UGENUNIT(dev), sc);	int endpt = UGENENDPOINT(dev);	struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN];	u_int32_t n, tn;	char buf[UGEN_BBSIZE];	usbd_request_handle reqh;	usbd_status r;	int s;	int error = 0;	u_char buffer[UGEN_CHUNK];	DPRINTFN(5, ("ugenread: %d:%d\n", UGENUNIT(dev), UGENENDPOINT(dev)));	if (sc->sc_disconnected)		return (EIO);#ifdef DIAGNOSTIC	if (!sce->edesc) {		printf("ugenread: no edesc\n");		return (EIO);	}	if (!sce->pipeh) {		printf("ugenread: no pipe\n");		return (EIO);	}#endif	switch (sce->edesc->bmAttributes & UE_XFERTYPE) {	case UE_INTERRUPT:		/* Block until activity occured. */		s = splusb();		while (sce->q.c_cc == 0) {			if (flag & IO_NDELAY) {				splx(s);				return (EWOULDBLOCK);			}			sce->state |= UGEN_ASLP;			DPRINTFN(5, ("ugenread: sleep on %p\n", sc));			error = tsleep((caddr_t)sce, PZERO | PCATCH, 				       "ugenri", 0);			DPRINTFN(5, ("ugenread: woke, error=%d\n", error));			if (error) {				sce->state &= ~UGEN_ASLP;				splx(s);				return (error);			}		}		splx(s);		/* Transfer as many chunks as possible. */		while (sce->q.c_cc > 0 && uio->uio_resid > 0) {			n = min(sce->q.c_cc, uio->uio_resid);			if (n > sizeof(buffer))				n = sizeof(buffer);			/* Remove a small chunk from the input queue. */			q_to_b(&sce->q, buffer, n);			DPRINTFN(5, ("ugenread: got %d chars\n", n));			/* Copy the data to the user process. */			error = uiomove(buffer, n, uio);			if (error)				break;		}		break;	case UE_BULK:		reqh = usbd_alloc_request();		if (reqh == 0)			return (ENOMEM);		while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) {			DPRINTFN(1, ("ugenread: start transfer %d bytes\n", n));			tn = n;			r = usbd_bulk_transfer(reqh, sce->pipeh, 0, buf, 					       &tn, "ugenrb");			if (r != USBD_NORMAL_COMPLETION) {				if (r == USBD_INTERRUPTED)					error = EINTR;				else					error = EIO;				break;			}			DPRINTFN(1, ("ugenread: got %d bytes\n", tn));			error = uiomove(buf, tn, uio);			if (error || tn < n)				break;		}		usbd_free_request(reqh);		break;	default:		return (ENXIO);	}	return (error);}intugenwrite(dev, uio, flag)	dev_t dev;	struct uio *uio;	int flag;{	USB_GET_SC(ugen, UGENUNIT(dev), sc);	int endpt = UGENENDPOINT(dev);	struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT];	size_t n;	int error = 0;	char buf[UGEN_BBSIZE];	usbd_request_handle reqh;	usbd_status r;	if (sc->sc_disconnected)		return (EIO);#ifdef DIAGNOSTIC	if (!sce->edesc) {		printf("ugenwrite: no edesc\n");		return (EIO);

⌨️ 快捷键说明

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