diff options
Diffstat (limited to 'qemu/roms/openbios/drivers/usbohci.c')
-rw-r--r-- | qemu/roms/openbios/drivers/usbohci.c | 926 |
1 files changed, 0 insertions, 926 deletions
diff --git a/qemu/roms/openbios/drivers/usbohci.c b/qemu/roms/openbios/drivers/usbohci.c deleted file mode 100644 index 774164b0b..000000000 --- a/qemu/roms/openbios/drivers/usbohci.c +++ /dev/null @@ -1,926 +0,0 @@ -/* - * Driver for USB OHCI ported from CoreBoot - * - * Copyright (C) 2014 BALATON Zoltan - * - * This file was part of the libpayload project. - * - * Copyright (C) 2010 Patrick Georgi - * - * 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. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -//#define USB_DEBUG_ED - -#include "config.h" -#include <asm/io.h> -#include <libopenbios/ofmem.h> -#include "timer.h" -#include "drivers/pci.h" -#include "pci.h" -#include <drivers/usb.h> -#include "usbohci_private.h" -#include "usbohci.h" - -static void ohci_start (hci_t *controller); -static void ohci_stop (hci_t *controller); -static void ohci_reset (hci_t *controller); -static void ohci_shutdown (hci_t *controller); -static int ohci_bulk (endpoint_t *ep, int size, u8 *data, int finalize); -static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, - int dalen, u8 *data); -static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); -static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue); -static u8* ohci_poll_intr_queue (void *queue); -static void ohci_process_done_queue(ohci_t *ohci, int spew_debug); - -#ifdef USB_DEBUG_ED -static void -dump_td (td_t *cur) -{ - usb_debug("+---------------------------------------------------+\n"); - if (((__le32_to_cpu(cur->config) & (3UL << 19)) >> 19) == 0) - usb_debug("|..[SETUP]..........................................|\n"); - else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 2) - usb_debug("|..[IN].............................................|\n"); - else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 1) - usb_debug("|..[OUT]............................................|\n"); - else - usb_debug("|..[]...............................................|\n"); - usb_debug("|:|============ OHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(cur)); - usb_debug("|:| ERRORS = [%ld] | CONFIG = [0x%08x] | |:|\n", - 3 - ((__le32_to_cpu(cur->config) & (3UL << 26)) >> 26), __le32_to_cpu(cur->config)); - usb_debug("|:+-----------------------------------------------+:|\n"); - usb_debug("|:| C | Condition Code | [%02ld] |:|\n", - (__le32_to_cpu(cur->config) & (0xFUL << 28)) >> 28); - usb_debug("|:| O | Direction/PID | [%ld] |:|\n", - (__le32_to_cpu(cur->config) & (3UL << 19)) >> 19); - usb_debug("|:| N | Buffer Rounding | [%ld] |:|\n", - (__le32_to_cpu(cur->config) & (1UL << 18)) >> 18); - usb_debug("|:| F | Delay Intterrupt | [%ld] |:|\n", - (__le32_to_cpu(cur->config) & (7UL << 21)) >> 21); - usb_debug("|:| I | Data Toggle | [%ld] |:|\n", - (__le32_to_cpu(cur->config) & (3UL << 24)) >> 24); - usb_debug("|:| G | Error Count | [%ld] |:|\n", - (__le32_to_cpu(cur->config) & (3UL << 26)) >> 26); - usb_debug("|:+-----------------------------------------------+:|\n"); - usb_debug("|:| Current Buffer Pointer [0x%08x] |:|\n", __le32_to_cpu(cur->current_buffer_pointer)); - usb_debug("|:+-----------------------------------------------+:|\n"); - usb_debug("|:| Next TD [0x%08x] |:|\n", __le32_to_cpu(cur->next_td)); - usb_debug("|:+-----------------------------------------------+:|\n"); - usb_debug("|:| Current Buffer End [0x%08x] |:|\n", __le32_to_cpu(cur->buffer_end)); - usb_debug("|:|-----------------------------------------------|:|\n"); - usb_debug("|...................................................|\n"); - usb_debug("+---------------------------------------------------+\n"); -} - -static void -dump_ed (ed_t *cur) -{ - td_t *tmp_td = NULL; - usb_debug("+===================================================+\n"); - usb_debug("| ############# OHCI ED at [0x%08lx] ########### |\n", virt_to_phys(cur)); - usb_debug("+---------------------------------------------------+\n"); - usb_debug("| Next Endpoint Descriptor [0x%08lx] |\n", __le32_to_cpu(cur->next_ed) & ~0xFUL); - usb_debug("+---------------------------------------------------+\n"); - usb_debug("| | @ 0x%08x : |\n", __le32_to_cpu(cur->config)); - usb_debug("| C | Maximum Packet Length | [%04ld] |\n", - ((__le32_to_cpu(cur->config) & (0x3fffUL << 16)) >> 16)); - usb_debug("| O | Function Address | [%04d] |\n", - __le32_to_cpu(cur->config) & 0x7F); - usb_debug("| N | Endpoint Number | [%02ld] |\n", - (__le32_to_cpu(cur->config) & (0xFUL << 7)) >> 7); - usb_debug("| F | Endpoint Direction | [%ld] |\n", - ((__le32_to_cpu(cur->config) & (3UL << 11)) >> 11)); - usb_debug("| I | Endpoint Speed | [%ld] |\n", - ((__le32_to_cpu(cur->config) & (1UL << 13)) >> 13)); - usb_debug("| G | Skip | [%ld] |\n", - ((__le32_to_cpu(cur->config) & (1UL << 14)) >> 14)); - usb_debug("| | Format | [%ld] |\n", - ((__le32_to_cpu(cur->config) & (1UL << 15)) >> 15)); - usb_debug("+---------------------------------------------------+\n"); - usb_debug("| TD Queue Tail Pointer [0x%08lx] |\n", - __le32_to_cpu(cur->tail_pointer) & ~0xFUL); - usb_debug("+---------------------------------------------------+\n"); - usb_debug("| TD Queue Head Pointer [0x%08lx] |\n", - __le32_to_cpu(cur->head_pointer) & ~0xFUL); - usb_debug("| CarryToggleBit [%d] Halted [%d] |\n", - (u16)(__le32_to_cpu(cur->head_pointer) & 0x2UL)>>1, (u16)(__le32_to_cpu(cur->head_pointer) & 0x1UL)); - - tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(cur->head_pointer) & ~0xFUL)); - if ((__le32_to_cpu(cur->head_pointer) & ~0xFUL) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) { - usb_debug("|:::::::::::::::::: OHCI TD CHAIN ::::::::::::::::::|\n"); - while (virt_to_phys(tmp_td) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) - { - dump_td(tmp_td); - tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(tmp_td->next_td) & ~0xFUL)); - } - usb_debug("|:::::::::::::::: EOF OHCI TD CHAIN ::::::::::::::::|\n"); - usb_debug("+---------------------------------------------------+\n"); - } else { - usb_debug("+---------------------------------------------------+\n"); - } -} -#endif - -static void -ohci_reset (hci_t *controller) -{ - if (controller == NULL) - return; - - OHCI_INST(controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset); - mdelay(2); /* wait 2ms */ - OHCI_INST(controller)->opreg->HcControl = 0; - mdelay(10); /* wait 10ms */ -} - -static void -ohci_reinit (hci_t *controller) -{ -} - -hci_t * -ohci_init (void *bar) -{ - int i; - - hci_t *controller = new_controller (); - - if (!controller) { - printk("Could not create USB controller instance.\n"); - return NULL; - } - - controller->instance = malloc (sizeof (ohci_t)); - if(!controller->instance) { - printk("Not enough memory creating USB controller instance.\n"); - return NULL; - } - - controller->type = OHCI; - - controller->start = ohci_start; - controller->stop = ohci_stop; - controller->reset = ohci_reset; - controller->init = ohci_reinit; - controller->shutdown = ohci_shutdown; - controller->bulk = ohci_bulk; - controller->control = ohci_control; - controller->set_address = generic_set_address; - controller->finish_device_config = NULL; - controller->destroy_device = NULL; - controller->create_intr_queue = ohci_create_intr_queue; - controller->destroy_intr_queue = ohci_destroy_intr_queue; - controller->poll_intr_queue = ohci_poll_intr_queue; - for (i = 0; i < 128; i++) { - controller->devices[i] = 0; - } - init_device_entry (controller, 0); - OHCI_INST (controller)->roothub = controller->devices[0]; - - controller->reg_base = (u32)(unsigned long)bar; - OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base); - usb_debug("OHCI Version %x.%x\n", - (READ_OPREG(OHCI_INST(controller), HcRevision) >> 4) & 0xf, - READ_OPREG(OHCI_INST(controller), HcRevision) & 0xf); - - if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) == USBReset) { - /* cold boot */ - OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~RemoteWakeupConnected); - OHCI_INST (controller)->opreg->HcFmInterval = - __cpu_to_le32((11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket)); - /* TODO: right value for PowerOnToPowerGoodTime ? */ - OHCI_INST (controller)->opreg->HcRhDescriptorA = - __cpu_to_le32(NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime)); - OHCI_INST (controller)->opreg->HcRhDescriptorB = __cpu_to_le32(0 * DeviceRemovable); - udelay(100); /* TODO: reset asserting according to USB spec */ - } else if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) != USBOperational) { - OHCI_INST (controller)->opreg->HcControl = - __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) & ~HostControllerFunctionalStateMask) - | USBResume); - udelay(100); /* TODO: resume time according to USB spec */ - } - int interval = OHCI_INST (controller)->opreg->HcFmInterval; - - OHCI_INST (controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset); - udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */ - OHCI_INST (controller)->opreg->HcFmInterval = interval; - ofmem_posix_memalign((void **)&(OHCI_INST (controller)->hcca), 256, 256); - memset((void*)OHCI_INST (controller)->hcca, 0, 256); - - usb_debug("HCCA addr %p\n", OHCI_INST(controller)->hcca); - /* Initialize interrupt table. */ - u32 *const intr_table = OHCI_INST(controller)->hcca->HccaInterruptTable; - ed_t *const periodic_ed; - ofmem_posix_memalign((void **)&periodic_ed, sizeof(ed_t), sizeof(ed_t)); - memset((void *)periodic_ed, 0, sizeof(*periodic_ed)); - for (i = 0; i < 32; ++i) - intr_table[i] = __cpu_to_le32(virt_to_phys(periodic_ed)); - OHCI_INST (controller)->periodic_ed = periodic_ed; - - OHCI_INST (controller)->opreg->HcHCCA = __cpu_to_le32(virt_to_phys(OHCI_INST(controller)->hcca)); - /* Make sure periodic schedule is enabled. */ - OHCI_INST (controller)->opreg->HcControl |= __cpu_to_le32(PeriodicListEnable); - OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~IsochronousEnable); // unused by this driver - // disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs - OHCI_INST (controller)->opreg->HcInterruptEnable = __cpu_to_le32(1<<31); - OHCI_INST (controller)->opreg->HcInterruptDisable = __cpu_to_le32(~(1<<31)); - OHCI_INST (controller)->opreg->HcInterruptStatus = __cpu_to_le32(~0); - OHCI_INST (controller)->opreg->HcPeriodicStart = - __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcFmInterval) & FrameIntervalMask) / 10 * 9); - OHCI_INST (controller)->opreg->HcControl = __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) - & ~HostControllerFunctionalStateMask) | USBOperational); - - mdelay(100); - - controller->devices[0]->controller = controller; - controller->devices[0]->init = ohci_rh_init; - controller->devices[0]->init (controller->devices[0]); - return controller; -} - -hci_t * -ohci_pci_init (pci_addr addr) -{ - u32 reg_base; - uint16_t cmd; - - cmd = pci_config_read16(addr, PCI_COMMAND); - cmd |= PCI_COMMAND_BUS_MASTER; - pci_config_write16(addr, PCI_COMMAND, cmd); - - /* regarding OHCI spec, Appendix A, BAR_OHCI register description, Table A-4 - * BASE ADDRESS only [31-12] bits. All other usually 0, but not all. - * OHCI mandates MMIO, so bit 0 is clear */ - reg_base = pci_config_read32 (addr, PCI_BASE_ADDR_0) & 0xfffff000; - - return ohci_init((void *)(unsigned long)reg_base); -} - -static void -ohci_shutdown (hci_t *controller) -{ - if (controller == 0) - return; - detach_controller (controller); - ohci_stop(controller); - OHCI_INST (controller)->roothub->destroy (OHCI_INST (controller)-> - roothub); - controller->reset (controller); - free ((void *)OHCI_INST (controller)->periodic_ed); - free (OHCI_INST (controller)); - free (controller); -} - -static void -ohci_start (hci_t *controller) -{ -// TODO: turn on all operation of OHCI, but assume that it's initialized. -} - -static void -ohci_stop (hci_t *controller) -{ -// TODO: turn off all operation of OHCI -} - -static int -wait_for_ed(usbdev_t *dev, ed_t *head, int pages) -{ - usb_debug("Waiting for %d pages on dev %p with head %p\n", pages, dev, head); - /* wait for results */ - /* TOTEST: how long to wait? - * give 2s per TD (2 pages) plus another 2s for now - */ - int timeout = pages*1000 + 2000; - while (((__le32_to_cpu(head->head_pointer) & ~3) != __le32_to_cpu(head->tail_pointer)) && - !(__le32_to_cpu(head->head_pointer) & 1) && - ((__le32_to_cpu((((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3)))->config) - & TD_CC_MASK) >= TD_CC_NOACCESS) && timeout--) { - /* don't log every ms */ - if (!(timeout % 100)) - usb_debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n", - READ_OPREG(OHCI_INST(dev->controller), HcInterruptStatus), - READ_OPREG(OHCI_INST(dev->controller), HcControl), - READ_OPREG(OHCI_INST(dev->controller), HcCommandStatus), - __le32_to_cpu(head->head_pointer), - __le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->next_td), - __le32_to_cpu(head->tail_pointer), - (__le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->config) & TD_CC_MASK) >> TD_CC_SHIFT); - mdelay(1); - } - if (timeout < 0) - usb_debug("Error: ohci: endpoint " - "descriptor processing timed out.\n"); - /* Clear the done queue. */ - ohci_process_done_queue(OHCI_INST(dev->controller), 1); - - if (__le32_to_cpu(head->head_pointer) & 1) { - usb_debug("HALTED!\n"); - return 1; - } - return 0; -} - -static void -ohci_free_ed (ed_t *const head) -{ - /* In case the transfer canceled, we have to free unprocessed TDs. */ - while ((__le32_to_cpu(head->head_pointer) & ~0x3) != __le32_to_cpu(head->tail_pointer)) { - /* Save current TD pointer. */ - td_t *const cur_td = - (td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3); - /* Advance head pointer. */ - head->head_pointer = cur_td->next_td; - /* Free current TD. */ - free((void *)cur_td); - } - - /* Always free the dummy TD */ - if ((__le32_to_cpu(head->head_pointer) & ~0x3) == __le32_to_cpu(head->tail_pointer)) - free(phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3)); - /* and the ED. */ - free((void *)head); -} - -static int -ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, - unsigned char *data) -{ - td_t *cur; - - // pages are specified as 4K in OHCI, so don't use getpagesize() - int first_page = (unsigned long)data / 4096; - int last_page = (unsigned long)(data+dalen-1)/4096; - if (last_page < first_page) last_page = first_page; - int pages = (dalen==0)?0:(last_page - first_page + 1); - - /* First TD. */ - td_t *const first_td; - ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t)); - memset((void *)first_td, 0, sizeof(*first_td)); - cur = first_td; - - cur->config = __cpu_to_le32(TD_DIRECTION_SETUP | - TD_DELAY_INTERRUPT_NOINTR | - TD_TOGGLE_FROM_TD | - TD_TOGGLE_DATA0 | - TD_CC_NOACCESS); - cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(devreq)); - cur->buffer_end = __cpu_to_le32(virt_to_phys((char *)devreq + drlen - 1)); - - while (pages > 0) { - /* One more TD. */ - td_t *const next; - ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t)); - memset((void *)next, 0, sizeof(*next)); - /* Linked to the previous. */ - cur->next_td = __cpu_to_le32(virt_to_phys(next)); - /* Advance to the new TD. */ - cur = next; - - cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | - TD_DELAY_INTERRUPT_NOINTR | - TD_TOGGLE_FROM_ED | - TD_CC_NOACCESS); - cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); - pages--; - int consumed = (4096 - ((unsigned long)data % 4096)); - if (consumed >= dalen) { - // end of data is within same page - cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1)); - dalen = 0; - /* assert(pages == 0); */ - } else { - dalen -= consumed; - data += consumed; - pages--; - int second_page_size = dalen; - if (dalen > 4096) { - second_page_size = 4096; - } - cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1)); - dalen -= second_page_size; - data += second_page_size; - } - } - - /* One more TD. */ - td_t *const next_td; - ofmem_posix_memalign((void **)&next_td, sizeof(td_t), sizeof(td_t)); - memset((void *)next_td, 0, sizeof(*next_td)); - /* Linked to the previous. */ - cur->next_td = __cpu_to_le32(virt_to_phys(next_td)); - /* Advance to the new TD. */ - cur = next_td; - cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_OUT : TD_DIRECTION_IN) | - TD_DELAY_INTERRUPT_ZERO | /* Write done head after this TD. */ - TD_TOGGLE_FROM_TD | - TD_TOGGLE_DATA1 | - TD_CC_NOACCESS); - cur->current_buffer_pointer = 0; - cur->buffer_end = 0; - - /* Final dummy TD. */ - td_t *const final_td; - ofmem_posix_memalign((void **)&final_td, sizeof(td_t), sizeof(td_t)); - memset((void *)final_td, 0, sizeof(*final_td)); - /* Linked to the previous. */ - cur->next_td = __cpu_to_le32(virt_to_phys(final_td)); - - /* Data structures */ - ed_t *head; - ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t)); - memset((void*)head, 0, sizeof(*head)); - head->config = __cpu_to_le32((dev->address << ED_FUNC_SHIFT) | - (0 << ED_EP_SHIFT) | - (OHCI_FROM_TD << ED_DIR_SHIFT) | - (dev->speed?ED_LOWSPEED:0) | - (dev->endpoints[0].maxpacketsize << ED_MPS_SHIFT)); - head->tail_pointer = __cpu_to_le32(virt_to_phys(final_td)); - head->head_pointer = __cpu_to_le32(virt_to_phys(first_td)); - - usb_debug("ohci_control(): doing transfer with %x. first_td at %x\n", - __le32_to_cpu(head->config) & ED_FUNC_MASK, __le32_to_cpu(head->head_pointer)); -#ifdef USB_DEBUG_ED - dump_ed(head); -#endif - - /* activate schedule */ - OHCI_INST(dev->controller)->opreg->HcControlHeadED = __cpu_to_le32(virt_to_phys(head)); - OHCI_INST(dev->controller)->opreg->HcControl |= __cpu_to_le32(ControlListEnable); - OHCI_INST(dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(ControlListFilled); - - int failure = wait_for_ed(dev, head, - (dalen==0)?0:(last_page - first_page + 1)); - /* Wait some frames before and one after disabling list access. */ - mdelay(4); - OHCI_INST(dev->controller)->opreg->HcControl &= __cpu_to_le32(~ControlListEnable); - mdelay(1); - - /* free memory */ - ohci_free_ed(head); - - return failure; -} - -/* finalize == 1: if data is of packet aligned size, add a zero length packet */ -static int -ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) -{ - int i; - usb_debug("bulk: %x bytes from %p, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize); - - td_t *cur, *next; - - // pages are specified as 4K in OHCI, so don't use getpagesize() - int first_page = (unsigned long)data / 4096; - int last_page = (unsigned long)(data+dalen-1)/4096; - if (last_page < first_page) last_page = first_page; - int pages = (dalen==0)?0:(last_page - first_page + 1); - int td_count = (pages+1)/2; - - if (finalize && ((dalen % ep->maxpacketsize) == 0)) { - td_count++; - } - - /* First TD. */ - td_t *const first_td; - ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t)); - memset((void *)first_td, 0, sizeof(*first_td)); - cur = next = first_td; - - for (i = 0; i < td_count; ++i) { - /* Advance to next TD. */ - cur = next; - cur->config = __cpu_to_le32((ep->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | - TD_DELAY_INTERRUPT_NOINTR | - TD_TOGGLE_FROM_ED | - TD_CC_NOACCESS); - cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); - pages--; - if (dalen == 0) { - /* magic TD for empty packet transfer */ - cur->current_buffer_pointer = 0; - cur->buffer_end = 0; - /* assert((pages == 0) && finalize); */ - } - int consumed = (4096 - ((unsigned long)data % 4096)); - if (consumed >= dalen) { - // end of data is within same page - cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1)); - dalen = 0; - /* assert(pages == finalize); */ - } else { - dalen -= consumed; - data += consumed; - pages--; - int second_page_size = dalen; - if (dalen > 4096) { - second_page_size = 4096; - } - cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1)); - dalen -= second_page_size; - data += second_page_size; - } - /* One more TD. */ - ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t)); - memset((void *)next, 0, sizeof(*next)); - /* Linked to the previous. */ - cur->next_td = __cpu_to_le32(virt_to_phys(next)); - } - - /* Write done head after last TD. */ - cur->config &= __cpu_to_le32(~TD_DELAY_INTERRUPT_MASK); - /* Advance to final, dummy TD. */ - cur = next; - - /* Data structures */ - ed_t *head; - ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t)); - memset((void*)head, 0, sizeof(*head)); - head->config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) | - ((ep->endpoint & 0xf) << ED_EP_SHIFT) | - (((ep->direction==IN)?OHCI_IN:OHCI_OUT) << ED_DIR_SHIFT) | - (ep->dev->speed?ED_LOWSPEED:0) | - (ep->maxpacketsize << ED_MPS_SHIFT)); - head->tail_pointer = __cpu_to_le32(virt_to_phys(cur)); - head->head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle?ED_TOGGLE:0)); - - usb_debug("doing bulk transfer with %x(%x). first_td at %lx, last %lx\n", - __le32_to_cpu(head->config) & ED_FUNC_MASK, - (__le32_to_cpu(head->config) & ED_EP_MASK) >> ED_EP_SHIFT, - virt_to_phys(first_td), virt_to_phys(cur)); - - /* activate schedule */ - OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = __cpu_to_le32(virt_to_phys(head)); - OHCI_INST(ep->dev->controller)->opreg->HcControl |= __cpu_to_le32(BulkListEnable); - OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(BulkListFilled); - - int failure = wait_for_ed(ep->dev, head, - (dalen==0)?0:(last_page - first_page + 1)); - /* Wait some frames before and one after disabling list access. */ - mdelay(4); - OHCI_INST(ep->dev->controller)->opreg->HcControl &= __cpu_to_le32(~BulkListEnable); - mdelay(1); - - ep->toggle = __le32_to_cpu(head->head_pointer) & ED_TOGGLE; - - /* free memory */ - ohci_free_ed(head); - - if (failure) { - /* try cleanup */ - clear_stall(ep); - } - - return failure; -} - - -struct _intr_queue; - -struct _intrq_td { - volatile td_t td; - u8 *data; - struct _intrq_td *next; - struct _intr_queue *intrq; -}; - -struct _intr_queue { - volatile ed_t ed; - struct _intrq_td *head; - struct _intrq_td *tail; - u8 *data; - int reqsize; - endpoint_t *endp; - unsigned int remaining_tds; - int destroy; -}; - -typedef struct _intrq_td intrq_td_t; -typedef struct _intr_queue intr_queue_t; - -#define INTRQ_TD_FROM_TD(x) ((intrq_td_t *)x) - -static void -ohci_fill_intrq_td(intrq_td_t *const td, intr_queue_t *const intrq, - u8 *const data) -{ - memset(td, 0, sizeof(*td)); - td->td.config = __cpu_to_le32(TD_QUEUETYPE_INTR | - (intrq->endp->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | - TD_DELAY_INTERRUPT_ZERO | - TD_TOGGLE_FROM_ED | - TD_CC_NOACCESS); - td->td.current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); - td->td.buffer_end = __cpu_to_le32(virt_to_phys(data) + intrq->reqsize - 1); - td->intrq = intrq; - td->data = data; -} - -/* create and hook-up an intr queue into device schedule */ -static void * -ohci_create_intr_queue(endpoint_t *const ep, const int reqsize, - const int reqcount, const int reqtiming) -{ - int i; - intrq_td_t *first_td = NULL, *last_td = NULL; - - if (reqsize > 4096) - return NULL; - - intr_queue_t *const intrq; - ofmem_posix_memalign((void **)&intrq, sizeof(intrq->ed), sizeof(*intrq)); - memset(intrq, 0, sizeof(*intrq)); - intrq->data = (u8 *)malloc(reqcount * reqsize); - intrq->reqsize = reqsize; - intrq->endp = ep; - - /* Create #reqcount TDs. */ - u8 *cur_data = intrq->data; - for (i = 0; i < reqcount; ++i) { - intrq_td_t *const td; - ofmem_posix_memalign((void **)&td, sizeof(td->td), sizeof(*td)); - ++intrq->remaining_tds; - ohci_fill_intrq_td(td, intrq, cur_data); - cur_data += reqsize; - if (!first_td) - first_td = td; - else - last_td->td.next_td = __cpu_to_le32(virt_to_phys(&td->td)); - last_td = td; - } - - /* Create last, dummy TD. */ - intrq_td_t *dummy_td; - ofmem_posix_memalign((void **)&dummy_td, sizeof(dummy_td->td), sizeof(*dummy_td)); - memset(dummy_td, 0, sizeof(*dummy_td)); - dummy_td->intrq = intrq; - if (last_td) - last_td->td.next_td = __cpu_to_le32(virt_to_phys(&dummy_td->td)); - last_td = dummy_td; - - /* Initialize ED. */ - intrq->ed.config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) | - ((ep->endpoint & 0xf) << ED_EP_SHIFT) | - (((ep->direction == IN) ? OHCI_IN : OHCI_OUT) << ED_DIR_SHIFT) | - (ep->dev->speed ? ED_LOWSPEED : 0) | - (ep->maxpacketsize << ED_MPS_SHIFT)); - intrq->ed.tail_pointer = __cpu_to_le32(virt_to_phys(last_td)); - intrq->ed.head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle ? ED_TOGGLE : 0)); - -#ifdef USB_DEBUG_ED - dump_ed(&intrq->ed); -#endif - /* Insert ED into periodic table. */ - int nothing_placed = 1; - ohci_t *const ohci = OHCI_INST(ep->dev->controller); - u32 *const intr_table = ohci->hcca->HccaInterruptTable; - const u32 dummy_ptr = __cpu_to_le32(virt_to_phys(ohci->periodic_ed)); - for (i = 0; i < 32; i += reqtiming) { - /* Advance to the next free position. */ - while ((i < 32) && (intr_table[i] != dummy_ptr)) ++i; - if (i < 32) { - usb_debug("Placed endpoint %lx to %d\n", virt_to_phys(&intrq->ed), i); - intr_table[i] = __cpu_to_le32(virt_to_phys(&intrq->ed)); - nothing_placed = 0; - } - } - if (nothing_placed) { - usb_debug("Error: Failed to place ohci interrupt endpoint " - "descriptor into periodic table: no space left\n"); - ohci_destroy_intr_queue(ep, intrq); - return NULL; - } - - return intrq; -} - -/* remove queue from device schedule, dropping all data that came in */ -static void -ohci_destroy_intr_queue(endpoint_t *const ep, void *const q_) -{ - intr_queue_t *const intrq = (intr_queue_t *)q_; - - int i; - - /* Remove interrupt queue from periodic table. */ - ohci_t *const ohci = OHCI_INST(ep->dev->controller); - u32 *const intr_table = ohci->hcca->HccaInterruptTable; - for (i=0; i < 32; ++i) { - if (intr_table[i] == __cpu_to_le32(virt_to_phys(intrq))) - intr_table[i] = __cpu_to_le32(virt_to_phys(ohci->periodic_ed)); - } - /* Wait for frame to finish. */ - mdelay(1); - - /* Free unprocessed TDs. */ - while ((__le32_to_cpu(intrq->ed.head_pointer) & ~0x3) != __le32_to_cpu(intrq->ed.tail_pointer)) { - td_t *const cur_td = (td_t *)phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3); - intrq->ed.head_pointer = cur_td->next_td; - free(INTRQ_TD_FROM_TD(cur_td)); - --intrq->remaining_tds; - } - /* Free final, dummy TD. */ - free(phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3)); - /* Free data buffer. */ - free(intrq->data); - - /* Free TDs already fetched from the done queue. */ - ohci_process_done_queue(ohci, 1); - while (intrq->head) { - intrq_td_t *const cur_td = (intrq_td_t *const )__le32_to_cpu(intrq->head); - intrq->head = intrq->head->next; - free(cur_td); - --intrq->remaining_tds; - } - - /* Mark interrupt queue to be destroyed. - ohci_process_done_queue() will free the remaining TDs - and finish the interrupt queue off once all TDs are gone. */ - intrq->destroy = 1; - - /* Save data toggle. */ - ep->toggle = __le32_to_cpu(intrq->ed.head_pointer) & ED_TOGGLE; -} - -/* read one intr-packet from queue, if available. extend the queue for new input. - return NULL if nothing new available. - Recommended use: while (data=poll_intr_queue(q)) process(data); - */ -static u8 * -ohci_poll_intr_queue(void *const q_) -{ - intr_queue_t *const intrq = (intr_queue_t *)q_; - - u8 *data = NULL; - - /* Process done queue first, then check if we have work to do. */ - ohci_process_done_queue(OHCI_INST(intrq->endp->dev->controller), 0); - - if (intrq->head) { - /* Save pointer to processed TD and advance. */ - intrq_td_t *const cur_td = intrq->head; - intrq->head = cur_td->next; - - /* Return data buffer of this TD. */ - data = cur_td->data; - - /* Requeue this TD (i.e. copy to dummy and requeue as dummy). */ - intrq_td_t *const dummy_td = - INTRQ_TD_FROM_TD(phys_to_virt(__le32_to_cpu(intrq->ed.tail_pointer))); - ohci_fill_intrq_td(dummy_td, intrq, data); - /* Reset all but intrq pointer (i.e. init as dummy). */ - memset(cur_td, 0, sizeof(*cur_td)); - cur_td->intrq = intrq; - /* Insert into interrupt queue as dummy. */ - dummy_td->td.next_td = __le32_to_cpu(virt_to_phys(&cur_td->td)); - intrq->ed.tail_pointer = __le32_to_cpu(virt_to_phys(&cur_td->td)); - } - - return data; -} - -static void -ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) -{ - int i, j; - - /* Temporary queue of interrupt queue TDs (to reverse order). */ - intrq_td_t *temp_tdq = NULL; - - /* Check if done head has been written. */ - if (!(READ_OPREG(ohci, HcInterruptStatus) & WritebackDoneHead)) - return; - /* Fetch current done head. - Lsb is only interesting for hw interrupts. */ - u32 phys_done_queue = __le32_to_cpu(ohci->hcca->HccaDoneHead) & ~1; - /* Tell host controller, he may overwrite the done head pointer. */ - ohci->opreg->HcInterruptStatus = __cpu_to_le32(WritebackDoneHead); - - i = 0; - /* Process done queue (it's in reversed order). */ - while (phys_done_queue) { - td_t *const done_td = (td_t *)phys_to_virt(phys_done_queue); - - /* Advance pointer to next TD. */ - phys_done_queue = __le32_to_cpu(done_td->next_td); - - switch (__le32_to_cpu(done_td->config) & TD_QUEUETYPE_MASK) { - case TD_QUEUETYPE_ASYNC: - /* Free processed async TDs. */ - free((void *)done_td); - break; - case TD_QUEUETYPE_INTR: { - intrq_td_t *const td = INTRQ_TD_FROM_TD(done_td); - intr_queue_t *const intrq = td->intrq; - /* Check if the corresponding interrupt - queue is still beeing processed. */ - if (intrq->destroy) { - /* Free this TD, and */ - free(td); - --intrq->remaining_tds; - /* the interrupt queue if it has no more TDs. */ - if (!intrq->remaining_tds) - free(intrq); - usb_debug("Freed TD from orphaned interrupt " - "queue, %d TDs remain.\n", - intrq->remaining_tds); - } else { - /* Save done TD to be processed. */ - td->next = temp_tdq; - temp_tdq = td; - } - break; - } - default: - break; - } - ++i; - } - if (spew_debug) - usb_debug("Processed %d done TDs.\n", i); - - j = 0; - /* Process interrupt queue TDs in right order. */ - while (temp_tdq) { - /* Save pointer of current TD and advance. */ - intrq_td_t *const cur_td = temp_tdq; - temp_tdq = temp_tdq->next; - - /* The interrupt queue for the current TD. */ - intr_queue_t *const intrq = cur_td->intrq; - /* Append to interrupt queue. */ - if (!intrq->head) { - /* First element. */ - intrq->head = intrq->tail = cur_td; - } else { - /* Insert at tail. */ - intrq->tail->next = cur_td; - intrq->tail = cur_td; - } - /* It's always the last element. */ - cur_td->next = NULL; - ++j; - } - if (spew_debug) - usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j); -} - -int ob_usb_ohci_init (const char *path, uint32_t addr) -{ - hci_t *ctrl; - int i; - - usb_debug("ohci_init: %s addr = %x\n", path, addr); - ctrl = ohci_pci_init(addr); - if (!ctrl) - return 0; - - /* Init ports */ - usb_poll(); - - /* Look for a keyboard */ - for (i = 0; i < 128; i++) { - if (ctrl->devices[i] && ctrl->devices[i]->configuration) { - configuration_descriptor_t *cd; - interface_descriptor_t *intf; - - cd = (configuration_descriptor_t *)ctrl->devices[i]->configuration; - intf = (interface_descriptor_t *)(ctrl->devices[i]->configuration + cd->bLength); - usb_debug("Device at port %d is class %d\n", i, intf->bInterfaceClass); - if (intf->bInterfaceClass == hid_device && - intf->bInterfaceSubClass == hid_subclass_boot && - intf->bInterfaceProtocol == hid_boot_proto_keyboard ) { - break; - } - } - } - if ( i < 128 ) - ob_usb_hid_add_keyboard(path); - - return 1; -} |