ohci.c

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

C
2,245
字号
/*	$NetBSD: ohci.c,v 1.27 1999/01/13 10:33:53 augustss Exp $	*//*	$FreeBSD: src/sys/dev/usb/ohci.c,v 1.10.2.1 1999/05/08 23:04:44 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 Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/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 <machine/endian.h>#include <dev/usb/usb.h>#include <dev/usb/usbdi.h>#include <dev/usb/usbdivar.h>#include <dev/usb/usb_quirks.h>#include <dev/usb/usb_mem.h>#include <dev/usb/ohcireg.h>#include <dev/usb/ohcivar.h>#if defined(__FreeBSD__)#include <machine/clock.h>#define delay(d)                DELAY(d)#endif#ifdef OHCI_DEBUG#define DPRINTF(x)	if (ohcidebug) logprintf x#define DPRINTFN(n,x)	if (ohcidebug>(n)) logprintf xint ohcidebug = 1;#else#define DPRINTF(x)#define DPRINTFN(n,x)#endif/* * The OHCI controller is little endian, so on big endian machines * the data strored in memory needs to be swapped. */#if BYTE_ORDER == BIG_ENDIAN#define LE(x) (bswap32(x))#else#define LE(x) (x)#endifstruct ohci_pipe;ohci_soft_ed_t *ohci_alloc_sed __P((ohci_softc_t *));void		ohci_free_sed __P((ohci_softc_t *, ohci_soft_ed_t *));ohci_soft_td_t *ohci_alloc_std __P((ohci_softc_t *));void		ohci_free_std __P((ohci_softc_t *, ohci_soft_td_t *));usbd_status	ohci_open __P((usbd_pipe_handle));void		ohci_poll __P((struct usbd_bus *));void		ohci_waitintr __P((ohci_softc_t *, usbd_request_handle));void		ohci_rhsc __P((ohci_softc_t *, usbd_request_handle));void		ohci_process_done __P((ohci_softc_t *, ohci_physaddr_t));void		ohci_ii_done __P((ohci_softc_t *, usbd_request_handle));void		ohci_ctrl_done __P((ohci_softc_t *, usbd_request_handle));void		ohci_intr_done __P((ohci_softc_t *, usbd_request_handle));void		ohci_bulk_done __P((ohci_softc_t *, usbd_request_handle));usbd_status	ohci_device_request __P((usbd_request_handle reqh));void		ohci_add_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *));void		ohci_rem_ed __P((ohci_soft_ed_t *, ohci_soft_ed_t *));void		ohci_hash_add_td __P((ohci_softc_t *, ohci_soft_td_t *));void		ohci_hash_rem_td __P((ohci_softc_t *, ohci_soft_td_t *));ohci_soft_td_t *ohci_hash_find_td __P((ohci_softc_t *, ohci_physaddr_t));usbd_status	ohci_root_ctrl_transfer __P((usbd_request_handle));usbd_status	ohci_root_ctrl_start __P((usbd_request_handle));void		ohci_root_ctrl_abort __P((usbd_request_handle));void		ohci_root_ctrl_close __P((usbd_pipe_handle));usbd_status	ohci_root_intr_transfer __P((usbd_request_handle));usbd_status	ohci_root_intr_start __P((usbd_request_handle));void		ohci_root_intr_abort __P((usbd_request_handle));void		ohci_root_intr_close __P((usbd_pipe_handle));usbd_status	ohci_device_ctrl_transfer __P((usbd_request_handle));usbd_status	ohci_device_ctrl_start __P((usbd_request_handle));void		ohci_device_ctrl_abort __P((usbd_request_handle));void		ohci_device_ctrl_close __P((usbd_pipe_handle));usbd_status	ohci_device_bulk_transfer __P((usbd_request_handle));usbd_status	ohci_device_bulk_start __P((usbd_request_handle));void		ohci_device_bulk_abort __P((usbd_request_handle));void		ohci_device_bulk_close __P((usbd_pipe_handle));usbd_status	ohci_device_intr_transfer __P((usbd_request_handle));usbd_status	ohci_device_intr_start __P((usbd_request_handle));void		ohci_device_intr_abort __P((usbd_request_handle));void		ohci_device_intr_close __P((usbd_pipe_handle));usbd_status	ohci_device_setintr __P((ohci_softc_t *sc, 					 struct ohci_pipe *pipe, int ival));int		ohci_str __P((usb_string_descriptor_t *, int, char *));void		ohci_timeout __P((void *));void		ohci_rhsc_able __P((ohci_softc_t *, int));#ifdef OHCI_DEBUGohci_softc_t   *thesc;void		ohci_dumpregs __P((ohci_softc_t *));void		ohci_dump_tds __P((ohci_soft_td_t *));void		ohci_dump_td __P((ohci_soft_td_t *));void		ohci_dump_ed __P((ohci_soft_ed_t *));#endif#if defined(__NetBSD__)#define OWRITE4(sc, r, x) bus_space_write_4((sc)->iot, (sc)->ioh, (r), (x))#define OREAD4(sc, r) bus_space_read_4((sc)->iot, (sc)->ioh, (r))#define OREAD2(sc, r) bus_space_read_2((sc)->iot, (sc)->ioh, (r))#elif defined(__FreeBSD__)#define OWRITE4(sc, r, x) *(u_int32_t *) ((sc)->sc_iobase + (r)) = x#define OREAD4(sc, r) (*(u_int32_t *) ((sc)->sc_iobase + (r)))#define OREAD2(sc, r) (*(u_int16_t *) ((sc)->sc_iobase + (r)))#endif/* Reverse the bits in a value 0 .. 31 */static u_int8_t revbits[OHCI_NO_INTRS] =   { 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c,    0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,    0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d,    0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f };struct ohci_pipe {	struct usbd_pipe pipe;	ohci_soft_ed_t *sed;	ohci_soft_td_t *tail;	/* Info needed for different pipe kinds. */	union {		/* Control pipe */		struct {			usb_dma_t datadma;			usb_dma_t reqdma;			u_int length;			ohci_soft_td_t *setup, *xfer, *stat;		} ctl;		/* Interrupt pipe */		struct {			usb_dma_t datadma;			int nslots;			int pos;		} intr;		/* Bulk pipe */		struct {			usb_dma_t datadma;			u_int length;		} bulk;	} u;};#define OHCI_INTR_ENDPT 1struct usbd_methods ohci_root_ctrl_methods = {		ohci_root_ctrl_transfer,	ohci_root_ctrl_start,	ohci_root_ctrl_abort,	ohci_root_ctrl_close,	0,};struct usbd_methods ohci_root_intr_methods = {		ohci_root_intr_transfer,	ohci_root_intr_start,	ohci_root_intr_abort,	ohci_root_intr_close,	0,};struct usbd_methods ohci_device_ctrl_methods = {		ohci_device_ctrl_transfer,	ohci_device_ctrl_start,	ohci_device_ctrl_abort,	ohci_device_ctrl_close,	0,};struct usbd_methods ohci_device_intr_methods = {		ohci_device_intr_transfer,	ohci_device_intr_start,	ohci_device_intr_abort,	ohci_device_intr_close,};struct usbd_methods ohci_device_bulk_methods = {		ohci_device_bulk_transfer,	ohci_device_bulk_start,	ohci_device_bulk_abort,	ohci_device_bulk_close,	0,};ohci_soft_ed_t *ohci_alloc_sed(sc)	ohci_softc_t *sc;{	ohci_soft_ed_t *sed;	usbd_status r;	int i, offs;	usb_dma_t dma;	if (!sc->sc_freeeds) {		DPRINTFN(2, ("ohci_alloc_sed: allocating chunk\n"));		sed = malloc(sizeof(ohci_soft_ed_t) * OHCI_ED_CHUNK, 			     M_USBDEV, M_NOWAIT);		if (!sed)			return 0;		r = usb_allocmem(sc->sc_dmatag, OHCI_ED_SIZE * OHCI_ED_CHUNK,				 OHCI_ED_ALIGN, &dma);		if (r != USBD_NORMAL_COMPLETION) {			free(sed, M_USBDEV);			return 0;		}		for(i = 0; i < OHCI_ED_CHUNK; i++, sed++) {			offs = i * OHCI_ED_SIZE;			sed->physaddr = DMAADDR(&dma) + offs;			sed->ed = (ohci_ed_t *)					((char *)KERNADDR(&dma) + offs);			sed->next = sc->sc_freeeds;			sc->sc_freeeds = sed;		}	}	sed = sc->sc_freeeds;	sc->sc_freeeds = sed->next;	memset(sed->ed, 0, OHCI_ED_SIZE);	sed->next = 0;	return sed;}voidohci_free_sed(sc, sed)	ohci_softc_t *sc;	ohci_soft_ed_t *sed;{	sed->next = sc->sc_freeeds;	sc->sc_freeeds = sed;}ohci_soft_td_t *ohci_alloc_std(sc)	ohci_softc_t *sc;{	ohci_soft_td_t *std;	usbd_status r;	int i, offs;	usb_dma_t dma;	if (!sc->sc_freetds) {		DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n"));		std = malloc(sizeof(ohci_soft_td_t) * OHCI_TD_CHUNK, 			     M_USBDEV, M_NOWAIT);		if (!std)			return 0;		r = usb_allocmem(sc->sc_dmatag, OHCI_TD_SIZE * OHCI_TD_CHUNK,				 OHCI_TD_ALIGN, &dma);		if (r != USBD_NORMAL_COMPLETION) {			free(std, M_USBDEV);			return 0;		}		for(i = 0; i < OHCI_TD_CHUNK; i++, std++) {			offs = i * OHCI_TD_SIZE;			std->physaddr = DMAADDR(&dma) + offs;			std->td = (ohci_td_t *)					((char *)KERNADDR(&dma) + offs);			std->nexttd = sc->sc_freetds;			sc->sc_freetds = std;		}	}	std = sc->sc_freetds;	sc->sc_freetds = std->nexttd;	memset(std->td, 0, OHCI_TD_SIZE);	std->nexttd = 0;	return (std);}voidohci_free_std(sc, std)	ohci_softc_t *sc;	ohci_soft_td_t *std;{	std->nexttd = sc->sc_freetds;	sc->sc_freetds = std;}usbd_statusohci_init(sc)	ohci_softc_t *sc;{	ohci_soft_ed_t *sed, *psed;	usbd_status r;	int rev;	int i;	u_int32_t s, ctl, ival, hcr, fm, per;	DPRINTF(("ohci_init: start\n"));	rev = OREAD4(sc, OHCI_REVISION);	printf("%s: OHCI version %d.%d%s\n", USBDEVNAME(sc->sc_bus.bdev),	       OHCI_REV_HI(rev), OHCI_REV_LO(rev),	       OHCI_REV_LEGACY(rev) ? ", legacy support" : "");	if (OHCI_REV_HI(rev) != 1 || OHCI_REV_LO(rev) != 0) {		printf("%s: unsupported OHCI revision\n", 		       USBDEVNAME(sc->sc_bus.bdev));		return (USBD_INVAL);	}	for (i = 0; i < OHCI_HASH_SIZE; i++)		LIST_INIT(&sc->sc_hash_tds[i]);	/* Allocate the HCCA area. */	r = usb_allocmem(sc->sc_dmatag, OHCI_HCCA_SIZE, 			 OHCI_HCCA_ALIGN, &sc->sc_hccadma);	if (r != USBD_NORMAL_COMPLETION)		return (r);	sc->sc_hcca = (struct ohci_hcca *)KERNADDR(&sc->sc_hccadma);	memset(sc->sc_hcca, 0, OHCI_HCCA_SIZE);	sc->sc_eintrs = OHCI_NORMAL_INTRS;	sc->sc_ctrl_head = ohci_alloc_sed(sc);	if (!sc->sc_ctrl_head) {		r = USBD_NOMEM;		goto bad1;	}	sc->sc_ctrl_head->ed->ed_flags |= LE(OHCI_ED_SKIP);	sc->sc_bulk_head = ohci_alloc_sed(sc);	if (!sc->sc_bulk_head) {		r = USBD_NOMEM;		goto bad2;	}	sc->sc_bulk_head->ed->ed_flags |= LE(OHCI_ED_SKIP);	/* Allocate all the dummy EDs that make up the interrupt tree. */	for (i = 0; i < OHCI_NO_EDS; i++) {		sed = ohci_alloc_sed(sc);		if (!sed) {			while (--i >= 0)				ohci_free_sed(sc, sc->sc_eds[i]);			r = USBD_NOMEM;			goto bad3;		}		/* All ED fields are set to 0. */		sc->sc_eds[i] = sed;		sed->ed->ed_flags |= LE(OHCI_ED_SKIP);		if (i != 0) {			psed = sc->sc_eds[(i-1) / 2];			sed->next = psed;			sed->ed->ed_nexted = LE(psed->physaddr);		}	}	/* 	 * Fill HCCA interrupt table.  The bit reversal is to get	 * the tree set up properly to spread the interrupts.	 */	for (i = 0; i < OHCI_NO_INTRS; i++)		sc->sc_hcca->hcca_interrupt_table[revbits[i]] = 			LE(sc->sc_eds[OHCI_NO_EDS-OHCI_NO_INTRS+i]->physaddr);	/* Determine in what context we are running. */	ctl = OREAD4(sc, OHCI_CONTROL);	if (ctl & OHCI_IR) {		/* SMM active, request change */		DPRINTF(("ohci_init: SMM active, request owner change\n"));		s = OREAD4(sc, OHCI_COMMAND_STATUS);		OWRITE4(sc, OHCI_COMMAND_STATUS, s | OHCI_OCR);		for (i = 0; i < 100 && (ctl & OHCI_IR); i++) {			delay(1000);			ctl = OREAD4(sc, OHCI_CONTROL);		}		if ((ctl & OHCI_IR) == 0) {			printf("%s: SMM does not respond, resetting\n",			       USBDEVNAME(sc->sc_bus.bdev));			OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);			goto reset;		}	} else if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_RESET) {		/* BIOS started controller. */		DPRINTF(("ohci_init: BIOS active\n"));		if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_OPERATIONAL) {			OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_OPERATIONAL);			delay(USB_RESUME_DELAY * 1000);		}	} else {		DPRINTF(("ohci_init: cold started\n"));	reset:		/* Controller was cold started. */		delay(USB_BUS_RESET_DELAY * 1000);	}	/*	 * This reset should not be necessary according to the OHCI spec, but	 * without it some controllers do not start.	 */	DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev)));	OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);	delay(USB_BUS_RESET_DELAY * 1000);	/* We now own the host controller and the bus has been reset. */	ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL));	OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */	/* Nominal time for a reset is 10 us. */	for (i = 0; i < 10; i++) {		delay(10);		hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR;		if (!hcr)			break;	}	if (hcr) {		printf("%s: reset timeout\n", USBDEVNAME(sc->sc_bus.bdev));		r = USBD_IOERROR;		goto bad3;	}#ifdef OHCI_DEBUG	thesc = sc;	if (ohcidebug > 15)		ohci_dumpregs(sc);#endif	/* The controller is now in suspend state, we have 2ms to finish. */	/* Set up HC registers. */	OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma));	OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr);	OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr);	OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);	OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE);	ctl = OREAD4(sc, OHCI_CONTROL);	ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR);	ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE |		OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL;	/* And finally start it! */	OWRITE4(sc, OHCI_CONTROL, ctl);	/*	 * The controller is now OPERATIONAL.  Set a some final	 * registers that should be set earlier, but that the	 * controller ignores when in the SUSPEND state.	 */	fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT;	fm |= OHCI_FSMPS(ival) | ival;	OWRITE4(sc, OHCI_FM_INTERVAL, fm);	per = OHCI_PERIODIC(ival); /* 90% periodic */	OWRITE4(sc, OHCI_PERIODIC_START, per);	OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC);	/* Enable port power */	sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A));#ifdef OHCI_DEBUG	if (ohcidebug > 5)		ohci_dumpregs(sc);#endif		/* Set up the bus struct. */	sc->sc_bus.open_pipe = ohci_open;	sc->sc_bus.pipe_size = sizeof(struct ohci_pipe);	sc->sc_bus.do_poll = ohci_poll;	return (USBD_NORMAL_COMPLETION); bad3:	ohci_free_sed(sc, sc->sc_ctrl_head); bad2:	ohci_free_sed(sc, sc->sc_bulk_head); bad1:	usb_freemem(sc->sc_dmatag, &sc->sc_hccadma);	return (r);}#ifdef OHCI_DEBUGvoid ohcidump(void);void ohcidump(void) { ohci_dumpregs(thesc); }voidohci_dumpregs(sc)	ohci_softc_t *sc;{	printf("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n",	       OREAD4(sc, OHCI_REVISION),	       OREAD4(sc, OHCI_CONTROL),	       OREAD4(sc, OHCI_COMMAND_STATUS));	printf("               intrstat=0x%08x intre=0x%08x intrd=0x%08x\n",	       OREAD4(sc, OHCI_INTERRUPT_STATUS),	       OREAD4(sc, OHCI_INTERRUPT_ENABLE),	       OREAD4(sc, OHCI_INTERRUPT_DISABLE));	printf("               hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n",	       OREAD4(sc, OHCI_HCCA),	       OREAD4(sc, OHCI_PERIOD_CURRENT_ED),	       OREAD4(sc, OHCI_CONTROL_HEAD_ED));	printf("               ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n",	       OREAD4(sc, OHCI_CONTROL_CURRENT_ED),	       OREAD4(sc, OHCI_BULK_HEAD_ED),	       OREAD4(sc, OHCI_BULK_CURRENT_ED));	printf("               done=0x%08x fmival=0x%08x fmrem=0x%08x\n",	       OREAD4(sc, OHCI_DONE_HEAD),	       OREAD4(sc, OHCI_FM_INTERVAL),	       OREAD4(sc, OHCI_FM_REMAINING));	printf("               fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n",	       OREAD4(sc, OHCI_FM_NUMBER),	       OREAD4(sc, OHCI_PERIODIC_START),	       OREAD4(sc, OHCI_LS_THRESHOLD));	printf("               desca=0x%08x descb=0x%08x stat=0x%08x\n",	       OREAD4(sc, OHCI_RH_DESCRIPTOR_A),	       OREAD4(sc, OHCI_RH_DESCRIPTOR_B),	       OREAD4(sc, OHCI_RH_STATUS));

⌨️ 快捷键说明

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