uhci.c

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

C
2,637
字号
/*	$NetBSD: uhci.c,v 1.24 1999/02/20 23:26:16 augustss Exp $	*//*	$FreeBSD: src/sys/dev/usb/uhci.c,v 1.7.2.2 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. *//* * USB Universal Host Controller driver. * Handles PIIX3 and PIIX4. * * Data sheets: ftp://download.intel.com/design/intarch/datashts/29055002.pdf *              ftp://download.intel.com/design/intarch/datashts/29056201.pdf * UHCI spec: http://www.intel.com/design/usb/uhci11d.pdf * USB spec: http://www.usb.org/cgi-usb/mailmerge.cgi/home/usb/docs/developers/cgiform.tpl */#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/queue.h>#include <sys/select.h>#include <machine/bus.h>#include <dev/usb/usb.h>#include <dev/usb/usbdi.h>#include <dev/usb/usbdivar.h>#include <dev/usb/usb_mem.h>#include <dev/usb/usb_quirks.h>#include <dev/usb/uhcireg.h>#include <dev/usb/uhcivar.h>#if defined(__FreeBSD__)#include <machine/clock.h>#define delay(d)		DELAY(d)#endif#ifdef UHCI_DEBUG#define DPRINTF(x)	if (uhcidebug) logprintf x#define DPRINTFN(n,x)	if (uhcidebug>(n)) logprintf xint uhcidebug = 1;#else#define DPRINTF(x)#define DPRINTFN(n,x)#endif#define MS_TO_TICKS(ms) ((ms) * hz / 1000)struct uhci_pipe {	struct usbd_pipe pipe;	uhci_intr_info_t *iinfo;	int newtoggle;	/* Info needed for different pipe kinds. */	union {		/* Control pipe */		struct {			uhci_soft_qh_t *sqh;			usb_dma_t reqdma;			usb_dma_t datadma;			uhci_soft_td_t *setup, *stat;			u_int length;		} ctl;		/* Interrupt pipe */		struct {			usb_dma_t datadma;			int npoll;			uhci_soft_qh_t **qhs;		} intr;		/* Bulk pipe */		struct {			uhci_soft_qh_t *sqh;			usb_dma_t datadma;			u_int length;			int isread;		} bulk;		/* Iso pipe */		struct iso {			u_int bufsize;			u_int nbuf;			usb_dma_t *bufs;			uhci_soft_td_t **stds;		} iso;	} u;};/*  * The uhci_intr_info free list can be global since they contain * no dma specific data.  The other free lists do. */LIST_HEAD(, uhci_intr_info) uhci_ii_free;void		uhci_busreset __P((uhci_softc_t *));usbd_status	uhci_run __P((uhci_softc_t *, int run));uhci_soft_td_t *uhci_alloc_std __P((uhci_softc_t *));void		uhci_free_std __P((uhci_softc_t *, uhci_soft_td_t *));uhci_soft_qh_t *uhci_alloc_sqh __P((uhci_softc_t *));void		uhci_free_sqh __P((uhci_softc_t *, uhci_soft_qh_t *));uhci_intr_info_t *uhci_alloc_intr_info __P((uhci_softc_t *));void		uhci_free_intr_info __P((uhci_intr_info_t *ii));#if 0void		uhci_enter_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *,				      uhci_intr_info_t *));void		uhci_exit_ctl_q __P((uhci_softc_t *, uhci_soft_qh_t *));#endifvoid		uhci_free_std_chain __P((uhci_softc_t *, 					 uhci_soft_td_t *, uhci_soft_td_t *));usbd_status	uhci_alloc_std_chain __P((struct uhci_pipe *, uhci_softc_t *,					  int, int, int, usb_dma_t *, 					  uhci_soft_td_t **,					  uhci_soft_td_t **));void		uhci_timo __P((void *));void		uhci_waitintr __P((uhci_softc_t *, usbd_request_handle));void		uhci_check_intr __P((uhci_softc_t *, uhci_intr_info_t *));void		uhci_ii_done __P((uhci_intr_info_t *, int));void		uhci_timeout __P((void *));void		uhci_wakeup_ctrl __P((void *, int, int, void *, int));void		uhci_lock_frames __P((uhci_softc_t *));void		uhci_unlock_frames __P((uhci_softc_t *));void		uhci_add_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *));void		uhci_add_bulk __P((uhci_softc_t *, uhci_soft_qh_t *));void		uhci_remove_ctrl __P((uhci_softc_t *, uhci_soft_qh_t *));void		uhci_remove_bulk __P((uhci_softc_t *, uhci_soft_qh_t *));int		uhci_str __P((usb_string_descriptor_t *, int, char *));void		uhci_wakeup_cb __P((usbd_request_handle reqh));usbd_status	uhci_device_ctrl_transfer __P((usbd_request_handle));usbd_status	uhci_device_ctrl_start __P((usbd_request_handle));void		uhci_device_ctrl_abort __P((usbd_request_handle));void		uhci_device_ctrl_close __P((usbd_pipe_handle));usbd_status	uhci_device_intr_transfer __P((usbd_request_handle));usbd_status	uhci_device_intr_start __P((usbd_request_handle));void		uhci_device_intr_abort __P((usbd_request_handle));void		uhci_device_intr_close __P((usbd_pipe_handle));usbd_status	uhci_device_bulk_transfer __P((usbd_request_handle));usbd_status	uhci_device_bulk_start __P((usbd_request_handle));void		uhci_device_bulk_abort __P((usbd_request_handle));void		uhci_device_bulk_close __P((usbd_pipe_handle));usbd_status	uhci_device_isoc_transfer __P((usbd_request_handle));usbd_status	uhci_device_isoc_start __P((usbd_request_handle));void		uhci_device_isoc_abort __P((usbd_request_handle));void		uhci_device_isoc_close __P((usbd_pipe_handle));usbd_status	uhci_device_isoc_setbuf __P((usbd_pipe_handle, u_int, u_int));usbd_status	uhci_root_ctrl_transfer __P((usbd_request_handle));usbd_status	uhci_root_ctrl_start __P((usbd_request_handle));void		uhci_root_ctrl_abort __P((usbd_request_handle));void		uhci_root_ctrl_close __P((usbd_pipe_handle));usbd_status	uhci_root_intr_transfer __P((usbd_request_handle));usbd_status	uhci_root_intr_start __P((usbd_request_handle));void		uhci_root_intr_abort __P((usbd_request_handle));void		uhci_root_intr_close __P((usbd_pipe_handle));usbd_status	uhci_open __P((usbd_pipe_handle));void		uhci_poll __P((struct usbd_bus *));usbd_status	uhci_device_request __P((usbd_request_handle reqh));void		uhci_ctrl_done __P((uhci_intr_info_t *ii));void		uhci_bulk_done __P((uhci_intr_info_t *ii));void		uhci_add_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *));void		uhci_remove_intr __P((uhci_softc_t *, int, uhci_soft_qh_t *));usbd_status	uhci_device_setintr __P((uhci_softc_t *sc, 					 struct uhci_pipe *pipe, int ival));void		uhci_intr_done __P((uhci_intr_info_t *ii));void		uhci_isoc_done __P((uhci_intr_info_t *ii));#ifdef UHCI_DEBUGstatic void	uhci_dumpregs __P((uhci_softc_t *));void		uhci_dump_tds __P((uhci_soft_td_t *));void		uhci_dump_qh __P((uhci_soft_qh_t *));void		uhci_dump __P((void));void		uhci_dump_td __P((uhci_soft_td_t *));#endif#if defined(__NetBSD__)#define UWRITE2(sc, r, x) bus_space_write_2((sc)->iot, (sc)->ioh, (r), (x))#define UWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x))#define UREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r))#define UREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r))#elif defined(__FreeBSD__)#define UWRITE2(sc,r,x)		outw((sc)->sc_iobase + (r), (x))#define UWRITE4(sc,r,x)		outl((sc)->sc_iobase + (r), (x))#define UREAD2(sc,r)		inw((sc)->sc_iobase + (r))#define UREAD4(sc,r)		inl((sc)->sc_iobase + (r))#endif#define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd)#define UHCISTS(sc) UREAD2(sc, UHCI_STS)#define UHCI_RESET_TIMEOUT 100	/* reset timeout */#define UHCI_CURFRAME(sc) (UREAD2(sc, UHCI_FRNUM) & UHCI_FRNUM_MASK)#define UHCI_INTR_ENDPT 1struct usbd_methods uhci_root_ctrl_methods = {		uhci_root_ctrl_transfer,	uhci_root_ctrl_start,	uhci_root_ctrl_abort,	uhci_root_ctrl_close,	0,};struct usbd_methods uhci_root_intr_methods = {		uhci_root_intr_transfer,	uhci_root_intr_start,	uhci_root_intr_abort,	uhci_root_intr_close,	0,};struct usbd_methods uhci_device_ctrl_methods = {	uhci_device_ctrl_transfer,	uhci_device_ctrl_start,	uhci_device_ctrl_abort,	uhci_device_ctrl_close,	0,};struct usbd_methods uhci_device_intr_methods = {	uhci_device_intr_transfer,	uhci_device_intr_start,	uhci_device_intr_abort,	uhci_device_intr_close,	0,};struct usbd_methods uhci_device_bulk_methods = {	uhci_device_bulk_transfer,	uhci_device_bulk_start,	uhci_device_bulk_abort,	uhci_device_bulk_close,	0,};struct usbd_methods uhci_device_isoc_methods = {	uhci_device_isoc_transfer,	uhci_device_isoc_start,	uhci_device_isoc_abort,	uhci_device_isoc_close,	uhci_device_isoc_setbuf,};voiduhci_busreset(sc)	uhci_softc_t *sc;{	UHCICMD(sc, UHCI_CMD_GRESET);	/* global reset */	usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); /* wait a little */	UHCICMD(sc, 0);			/* do nothing */}usbd_statusuhci_init(sc)	uhci_softc_t *sc;{	usbd_status r;	int i, j;	uhci_soft_qh_t *csqh, *bsqh, *sqh;	uhci_soft_td_t *std;	usb_dma_t dma;	static int uhci_global_init_done = 0;	DPRINTFN(1,("uhci_init: start\n"));	if (!uhci_global_init_done) {		uhci_global_init_done = 1;		LIST_INIT(&uhci_ii_free);	}	uhci_run(sc, 0);			/* stop the controller */	UWRITE2(sc, UHCI_INTR, 0);		/* disable interrupts */	uhci_busreset(sc);		/* Allocate and initialize real frame array. */	r = usb_allocmem(sc->sc_dmatag, 			 UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t),			 UHCI_FRAMELIST_ALIGN, &dma);	if (r != USBD_NORMAL_COMPLETION)		return (r);	sc->sc_pframes = KERNADDR(&dma);	UWRITE2(sc, UHCI_FRNUM, 0);		/* set frame number to 0 */	UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&dma)); /* set frame list */	/* Allocate the dummy QH where bulk traffic will be queued. */	bsqh = uhci_alloc_sqh(sc);	if (!bsqh)		return (USBD_NOMEM);	bsqh->qh->qh_hlink = UHCI_PTR_T;	/* end of QH chain */	bsqh->qh->qh_elink = UHCI_PTR_T;	sc->sc_bulk_start = sc->sc_bulk_end = bsqh;	/* Allocate the dummy QH where control traffic will be queued. */	csqh = uhci_alloc_sqh(sc);	if (!csqh)		return (USBD_NOMEM);	csqh->qh->hlink = bsqh;	csqh->qh->qh_hlink = bsqh->physaddr | UHCI_PTR_Q;	csqh->qh->qh_elink = UHCI_PTR_T;	sc->sc_ctl_start = sc->sc_ctl_end = csqh;	/* 	 * Make all (virtual) frame list pointers point to the interrupt	 * queue heads and the interrupt queue heads at the control	 * queue head and point the physical frame list to the virtual.	 */	for(i = 0; i < UHCI_VFRAMELIST_COUNT; i++) {		std = uhci_alloc_std(sc);		sqh = uhci_alloc_sqh(sc);		if (!std || !sqh)			return (USBD_NOMEM);		std->td->link.sqh = sqh;		std->td->td_link = sqh->physaddr | UHCI_PTR_Q;		std->td->td_status = UHCI_TD_IOS;	/* iso, inactive */		std->td->td_token = 0;		std->td->td_buffer = 0;		sqh->qh->hlink = csqh;		sqh->qh->qh_hlink = csqh->physaddr | UHCI_PTR_Q;		sqh->qh->elink = 0;		sqh->qh->qh_elink = UHCI_PTR_T;		sc->sc_vframes[i].htd = std;		sc->sc_vframes[i].etd = std;		sc->sc_vframes[i].hqh = sqh;		sc->sc_vframes[i].eqh = sqh;		for (j = i; 		     j < UHCI_FRAMELIST_COUNT; 		     j += UHCI_VFRAMELIST_COUNT)			sc->sc_pframes[j] = std->physaddr;	}	LIST_INIT(&sc->sc_intrhead);	/* Set up the bus struct. */	sc->sc_bus.open_pipe = uhci_open;	sc->sc_bus.pipe_size = sizeof(struct uhci_pipe);	sc->sc_bus.do_poll = uhci_poll;	DPRINTFN(1,("uhci_init: enabling\n"));	UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | 		UHCI_INTR_IOCE | UHCI_INTR_SPIE);	/* enable interrupts */	return (uhci_run(sc, 1));		/* and here we go... */}#ifdef UHCI_DEBUGstatic voiduhci_dumpregs(sc)	uhci_softc_t *sc;{	printf("%s: regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, "	       "flbase=%08x, sof=%02x, portsc1=%04x, portsc2=%04x, ",	       USBDEVNAME(sc->sc_bus.bdev),	       UREAD2(sc, UHCI_CMD),	       UREAD2(sc, UHCI_STS),	       UREAD2(sc, UHCI_INTR),	       UREAD2(sc, UHCI_FRNUM),	       UREAD4(sc, UHCI_FLBASEADDR),	       UREAD1(sc, UHCI_SOF),	       UREAD2(sc, UHCI_PORTSC1),	       UREAD2(sc, UHCI_PORTSC2));}int uhci_longtd = 1;voiduhci_dump_td(p)	uhci_soft_td_t *p;{	printf("TD(%p) at %08lx link=0x%08lx st=0x%08lx tok=0x%08lx buf=0x%08lx\n",	       p, (long)p->physaddr,	       (long)p->td->td_link,	       (long)p->td->td_status,	       (long)p->td->td_token,	       (long)p->td->td_buffer);	if (uhci_longtd)		printf("  %b %b,errcnt=%d,actlen=%d pid=%02x,addr=%d,endpt=%d,"		       "D=%d,maxlen=%d\n",		       (int)p->td->td_link,		       "\20\1T\2Q\3VF",		       (int)p->td->td_status,		       "\20\22BITSTUFF\23CRCTO\24NAK\25BABBLE\26DBUFFER\27"		       "STALLED\30ACTIVE\31IOC\32ISO\33LS\36SPD",		       UHCI_TD_GET_ERRCNT(p->td->td_status),		       UHCI_TD_GET_ACTLEN(p->td->td_status),		       UHCI_TD_GET_PID(p->td->td_token),		       UHCI_TD_GET_DEVADDR(p->td->td_token),		       UHCI_TD_GET_ENDPT(p->td->td_token),		       UHCI_TD_GET_DT(p->td->td_token),		       UHCI_TD_GET_MAXLEN(p->td->td_token));}voiduhci_dump_qh(p)	uhci_soft_qh_t *p;{	printf("QH(%p) at %08x: hlink=%08x elink=%08x\n", p, (int)p->physaddr,	       p->qh->qh_hlink, p->qh->qh_elink);}#if 0voiduhci_dump(){	uhci_softc_t *sc = uhci;	uhci_dumpregs(sc);	printf("intrs=%d\n", sc->sc_intrs);	printf("framelist[i].link = %08x\n", sc->sc_framelist[0].link);	uhci_dump_qh(sc->sc_ctl_start->qh->hlink);}#endifvoiduhci_dump_tds(std)	uhci_soft_td_t *std;{	uhci_soft_td_t *p;	for(p = std; p; p = p->td->link.std)		uhci_dump_td(p);}#endif/* * This routine is executed periodically and simulates interrupts * from the root controller interrupt pipe for port status change. */voiduhci_timo(addr)	void *addr;{	usbd_request_handle reqh = addr;	usbd_pipe_handle pipe = reqh->pipe;	uhci_softc_t *sc = (uhci_softc_t *)pipe->device->bus;	struct uhci_pipe *upipe = (struct uhci_pipe *)pipe;	int s;	u_char *p;	DPRINTFN(15, ("uhci_timo\n"));	p = KERNADDR(&upipe->u.intr.datadma);	p[0] = 0;	if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))		p[0] |= 1<<1;	if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))		p[0] |= 1<<2;	s = splusb();	if (p[0] != 0) {		reqh->actlen = 1;		reqh->status = USBD_NORMAL_COMPLETION;		reqh->xfercb(reqh);	}	if (reqh->pipe->intrreqh == reqh) {		usb_timeout(uhci_timo, reqh, sc->sc_ival, reqh->timo_handle);	} else {		usb_freemem(sc->sc_dmatag, &upipe->u.intr.datadma);		usb_start_next(reqh->pipe);	}	splx(s);}voiduhci_lock_frames(sc)	uhci_softc_t *sc;{	int s = splusb();	while (sc->sc_vflock) {		sc->sc_vflock |= UHCI_WANT_LOCK;		tsleep(&sc->sc_vflock, PRIBIO, "uhcqhl", 0);	}	sc->sc_vflock = UHCI_HAS_LOCK;	splx(s);}voiduhci_unlock_frames(sc)	uhci_softc_t *sc;{

⌨️ 快捷键说明

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