hci_t * ehci_init (pcidev_t addr) { int i; hci_t *controller = new_controller (); if (!controller) usb_fatal("Could not create USB controller instance.\n"); controller->instance = malloc (sizeof (ehci_t)); if(!controller->instance) usb_fatal("Not enough memory creating USB controller instance.\n"); #define PCI_COMMAND 4 #define PCI_COMMAND_IO 1 #define PCI_COMMAND_MEMORY 2 #define PCI_COMMAND_MASTER 4 u32 pci_command = pci_read_config32(addr, PCI_COMMAND); pci_command = (pci_command | PCI_COMMAND_MEMORY) & ~PCI_COMMAND_IO ; pci_write_config32(addr, PCI_COMMAND, pci_command); controller->start = ehci_start; controller->stop = ehci_stop; controller->reset = ehci_reset; controller->shutdown = ehci_shutdown; controller->bulk = ehci_bulk; controller->control = ehci_control; controller->create_intr_queue = ehci_create_intr_queue; controller->destroy_intr_queue = ehci_destroy_intr_queue; controller->poll_intr_queue = ehci_poll_intr_queue; for (i = 0; i < 128; i++) { controller->devices[i] = 0; } init_device_entry (controller, 0); EHCI_INST(controller)->capabilities = phys_to_virt(pci_read_config32(addr, USBBASE)); EHCI_INST(controller)->operation = (hc_op_t *)(phys_to_virt(pci_read_config32(addr, USBBASE)) + EHCI_INST(controller)->capabilities->caplength); /* default value for frame length adjust */ pci_write_config8(addr, FLADJ, FLADJ_framelength(60000)); /* Enable operation of controller */ controller->start(controller); /* take over all ports. USB1 should be blind now */ EHCI_INST(controller)->operation->configflag = 1; /* TODO lots of stuff missing */ controller->devices[0]->controller = controller; controller->devices[0]->init = ehci_rh_init; controller->devices[0]->init (controller->devices[0]); return controller; }
void ehci_rh_init (usbdev_t *dev) { int i; dev->destroy = ehci_rh_destroy; dev->poll = ehci_rh_poll; dev->data = malloc(sizeof(rh_inst_t)); RH_INST(dev)->n_ports = EHCI_INST(dev->controller)->capabilities->hcsparams & HCS_NPORTS_MASK; RH_INST(dev)->ports = EHCI_INST(dev->controller)->operation->portsc; usb_debug("root hub has %x ports\n", RH_INST(dev)->n_ports); /* If the host controller has port power control, enable power on * all ports and wait 20ms. */ if (EHCI_INST(dev->controller)->capabilities->hcsparams & HCS_PORT_POWER_CONTROL) { usb_debug("host controller has port power control, " "giving power to all ports.\n"); for (i=0; i < RH_INST(dev)->n_ports; i++) RH_INST(dev)->ports[i] |= P_PP; } mdelay(20); // ehci spec 2.3.9 RH_INST(dev)->devices = malloc(RH_INST(dev)->n_ports * sizeof(int)); for (i=0; i < RH_INST(dev)->n_ports; i++) { RH_INST(dev)->devices[i] = -1; ehci_rh_scanport(dev, i); } dev->address = 0; dev->hub = -1; dev->port = -1; }
static void ehci_stop (hci_t *controller) { EHCI_INST(controller)->operation->rs = 0; }
/* FIXME: Handle control transfers as 3 QHs, so the 2nd stage can be >0x4000 bytes */ static int ehci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, u8 *data) { int endp = 0; // this is control. always 0 (for now) int toggle = 0; int mlen = dev->endpoints[0].maxpacketsize; int result = 0; /* create qTDs */ qtd_t *head = memalign(32, sizeof(qtd_t)); qtd_t *cur = head; memset(cur, 0, sizeof(qtd_t)); cur->active = 1; cur->dt = toggle; cur->pid = EHCI_SETUP; cur->cerr = 3; if (fill_td(cur, devreq, drlen) != drlen) { printf("ERROR: couldn't send the entire device request\n"); } qtd_t *next = memalign(32, sizeof(qtd_t)); cur->next_qtd = virt_to_phys(next); cur->alt_terminate = 1; /* FIXME: We're limited to 16-20K (depending on alignment) for payload for now. * Figure out, how toggle can be set sensibly in this scenario */ if (dalen > 0) { toggle ^= 1; cur = next; memset(cur, 0, sizeof(qtd_t)); cur->active = 1; cur->dt = toggle; cur->pid = (dir == OUT)?EHCI_OUT:EHCI_IN; cur->cerr = 3; if (fill_td(cur, data, dalen) != dalen) { printf("ERROR: couldn't send the entire control payload\n"); } next = memalign(32, sizeof(qtd_t)); cur->next_qtd = virt_to_phys(next); cur->alt_terminate = 1; } toggle = 1; cur = next; memset(cur, 0, sizeof(qtd_t)); cur->active = 1; cur->dt = toggle; cur->pid = (dir == OUT)?EHCI_IN:EHCI_OUT; cur->cerr = 0; fill_td(cur, NULL, 0); cur->next_qtd = virt_to_phys(0); cur->terminate = 1; cur->alt_terminate = 1; /* create QH */ ehci_qh_t *qh = memalign(32, sizeof(ehci_qh_t)); memset(qh, 0, sizeof(ehci_qh_t)); qh->horiz_link_ptr = virt_to_phys(qh); qh->type = 1; // FIXME: proper symbols for type. this is QH qh->addr = dev->address; qh->ep = endp; qh->eps = dev->speed; qh->dtc = 1; /* Take data toggle from TD, as control transfers are special */ qh->reclaim_head = 1; qh->max_packet_len = mlen; qh->non_hs_control_ep = 0; // no support for non-HS devices at this time qh->nak_cnt_reload = 0; qh->pipe_multiplier = 3; qh->td.next_qtd = virt_to_phys(head); /* hook up QH */ EHCI_INST(dev->controller)->operation->asynclistaddr = virt_to_phys(qh); /* start async schedule */ EHCI_INST(dev->controller)->operation->async_sched_enable = 1; while (!EHCI_INST(dev->controller)->operation->async_sched_status) ; /* wait */ result = wait_for_tds(head); /* disable async schedule */ EHCI_INST(dev->controller)->operation->async_sched_enable = 0; while (EHCI_INST(dev->controller)->operation->async_sched_status) ; /* wait */ free_qh_and_tds(qh, head); return result; }
static int ehci_bulk (endpoint_t *ep, int size, u8 *data, int finalize) { int result = 0; int endp = ep->endpoint & 0xf; int pid = (ep->direction==IN)?EHCI_IN:EHCI_OUT; qtd_t *head = memalign(32, sizeof(qtd_t)); qtd_t *cur = head; while (1) { memset(cur, 0, sizeof(qtd_t)); cur->active = 1; cur->pid = pid; cur->cerr = 0; u32 chunk = fill_td(cur, data, size); size -= chunk; data += chunk; cur->alt_terminate = 1; if (size == 0) { cur->next_qtd = virt_to_phys(0); cur->terminate = 1; break; } else { qtd_t *next = memalign(32, sizeof(qtd_t)); cur->next_qtd = virt_to_phys(next); cur = next; } } /* create QH */ ehci_qh_t *qh = memalign(32, sizeof(ehci_qh_t)); memset(qh, 0, sizeof(ehci_qh_t)); qh->horiz_link_ptr = virt_to_phys(qh); qh->type = 1; // FIXME: proper symbols for type. this is QH qh->addr = ep->dev->address; qh->ep = endp; qh->eps = ep->dev->speed; qh->dtc = 0; qh->reclaim_head = 1; qh->max_packet_len = ep->maxpacketsize; qh->nak_cnt_reload = 0; qh->pipe_multiplier = 3; qh->td.next_qtd = virt_to_phys(head); qh->td.dt = ep->toggle; head->dt = ep->toggle; /* hook up QH */ EHCI_INST(ep->dev->controller)->operation->asynclistaddr = virt_to_phys(qh); /* start async schedule */ EHCI_INST(ep->dev->controller)->operation->async_sched_enable = 1; while (!EHCI_INST(ep->dev->controller)->operation->async_sched_status) ; /* wait */ /* wait for result */ result = wait_for_tds(head); /* disable async schedule */ EHCI_INST(ep->dev->controller)->operation->async_sched_enable = 0; while (EHCI_INST(ep->dev->controller)->operation->async_sched_status) ; /* wait */ ep->toggle = cur->dt; free_qh_and_tds(qh, head); return result; }