/* Service an endpoint list. Returns nonzero if active TD were found. */ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) { struct ohci_ed ed; uint32_t next_ed; uint32_t cur; int active; active = 0; if (head == 0) return 0; for (cur = head; cur; cur = next_ed) { if (!ohci_read_ed(ohci, cur, &ed)) { fprintf(stderr, "usb-ohci: ED read error at %x\n", cur); return 0; } next_ed = ed.next & OHCI_DPTR_MASK; if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; /* Cancel pending packets for ED that have been paused. */ addr = ed.head & OHCI_DPTR_MASK; if (ohci->async_td && addr == ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } continue; } while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { #ifdef DEBUG_PACKET DPRINTF("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur, OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0, (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK, ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); #endif active = 1; if ((ed.flags & OHCI_ED_F) == 0) { if (ohci_service_td(ohci, &ed)) break; } else { /* Handle isochronous endpoints */ if (ohci_service_iso_td(ohci, &ed, completion)) break; } } ohci_put_ed(ohci, cur, &ed); } return active; }
/* Do frame processing on frame boundary */ static void ohci_frame_boundary(void *opaque) { OHCIState *ohci = opaque; struct ohci_hcca hcca; ohci_read_hcca(ohci, ohci->hcca, &hcca); /* Process all the lists at the end of the frame */ if (ohci->ctl & OHCI_CTL_PLE) { int n; n = ohci->frame_number & 0x1f; ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0); } /* Cancel all pending packets if either of the lists has been disabled. */ if (ohci->async_td && ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } ohci->old_ctl = ohci->ctl; ohci_process_lists(ohci, 0); /* Frame boundary, so do EOF stuf here */ ohci->frt = ohci->fit; /* Increment frame number and take care of endianness. */ ohci->frame_number = (ohci->frame_number + 1) & 0xffff; hcca.frame = cpu_to_le16(ohci->frame_number); if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { if (!ohci->done) abort(); if (ohci->intr & ohci->intr_status) ohci->done |= 1; hcca.done = cpu_to_le32(ohci->done); ohci->done = 0; ohci->done_count = 7; ohci_set_interrupt(ohci, OHCI_INTR_WD); } if (ohci->done_count != 7 && ohci->done_count != 0) ohci->done_count--; /* Do SOF stuff here */ ohci_sof(ohci); /* Writeback HCCA */ ohci_put_hcca(ohci, ohci->hcca, &hcca); }
/* Reset the controller */ static void ohci_reset(void *opaque) { OHCIState *ohci = opaque; OHCIPort *port; int i; ohci_bus_stop(ohci); ohci->ctl = 0; ohci->old_ctl = 0; ohci->status = 0; ohci->intr_status = 0; ohci->intr = OHCI_INTR_MIE; ohci->hcca = 0; ohci->ctrl_head = ohci->ctrl_cur = 0; ohci->bulk_head = ohci->bulk_cur = 0; ohci->per_cur = 0; ohci->done = 0; ohci->done_count = 7; /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? * I took the value linux sets ... */ ohci->fsmps = 0x2778; ohci->fi = 0x2edf; ohci->fit = 0; ohci->frt = 0; ohci->frame_number = 0; ohci->pstart = 0; ohci->lst = OHCI_LS_THRESH; ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; ohci->rhdesc_b = 0x0; /* Impl. specific */ ohci->rhstatus = 0; for (i = 0; i < ohci->num_ports; i++) { port = &ohci->rhport[i]; port->ctrl = 0; if (port->port.dev) { usb_attach(&port->port, port->port.dev); } } if (ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } DPRINTF("usb-ohci: Reset %s\n", ohci->name); }
static void usb_ohci_exit(PCIDevice *dev) { OHCIPCIState *ohci = PCI_OHCI(dev); OHCIState *s = &ohci->state; trace_usb_ohci_exit(s->name); ohci_bus_stop(s); if (s->async_td) { usb_cancel_packet(&s->usb_packet); s->async_td = 0; } ohci_stop_endpoints(s); if (!ohci->masterbus) { usb_bus_release(&s->bus); } timer_del(s->eof_timer); timer_free(s->eof_timer); }