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 + -
显示快捷键?