/* disable root hub */ static void uhci_rh_disable_port (usbdev_t *dev, int port) { hci_t *controller = dev->controller; if (port == 1) port = PORTSC1; else if (port == 2) port = PORTSC2; else { usb_debug("Invalid port %d\n", port); return; } uhci_reg_write16(controller, port, uhci_reg_read16(controller, port) & ~4); u16 value; /* wait for controller to disable port */ /* TOTEST: how long to wait? 100ms for now */ int timeout = 200; /* time out after 200 * 500us == 100ms */ do { value = uhci_reg_read16 (controller, port); udelay(500); timeout--; } while (((value & (1 << 2)) != 0) && timeout); if (!timeout) usb_debug("Warning: uhci_rh: port disabling timed out.\n"); }
static int dwc2_rh_reset_port(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); hprt.d32 &= HPRT_W1C_MASK; hprt.prtrst = 1; writel(hprt.d32, dwc2->hprt0); /* Wait a bit while reset is active. */ mdelay(50); /* Deassert reset. */ hprt.prtrst = 0; writel(hprt.d32, dwc2->hprt0); /* * If reset and speed enum success the DWC2 core will set enable bit * after port reset bit is deasserted */ mdelay(1); hprt.d32 = readl(dwc2->hprt0); usb_debug("%s reset port ok, hprt = 0x%08x\n", __func__, hprt.d32); if (!hprt.prtena) { usb_debug("%s enable port fail! hprt = 0x%08x\n", __func__, hprt.d32); return -1; } return 0; }
static int xhci_wait_ready(xhci_t *const xhci) { xhci_debug("Waiting for controller to be ready... "); if (!xhci_handshake(&xhci->opreg->usbsts, USBSTS_CNR, 0, 100000L)) { usb_debug("timeout!\n"); return -1; } usb_debug("ok.\n"); return 0; }
static void xhci_reset(hci_t *const controller) { xhci_t *const xhci = XHCI_INST(controller); xhci_stop(controller); xhci->opreg->usbcmd |= USBCMD_HCRST; xhci_debug("Resetting controller... "); if (!xhci_handshake(&xhci->opreg->usbcmd, USBCMD_HCRST, 0, 1000000L)) usb_debug("timeout!\n"); else usb_debug("ok.\n"); }
static void ehci_rh_scanport (usbdev_t *dev, int port) { if (RH_INST(dev)->devices[port]!=-1) { usb_debug("Unregister device at port %x\n", port+1); usb_detach_device(dev->controller, RH_INST(dev)->devices[port]); RH_INST(dev)->devices[port]=-1; } /* device connected, handle */ if (RH_INST(dev)->ports[port] & P_CURR_CONN_STATUS) { mdelay(100); // usb20 spec 9.1.2 if ((RH_INST(dev)->ports[port] & P_LINE_STATUS) == P_LINE_STATUS_LOWSPEED) { ehci_rh_hand_over_port(dev, port); return; } /* Deassert enable, assert reset. These must change * atomically. */ RH_INST(dev)->ports[port] = (RH_INST(dev)->ports[port] & ~P_PORT_ENABLE) | P_PORT_RESET; /* Wait a bit while reset is active. */ mdelay(50); // usb20 spec 7.1.7.5 (TDRSTR) /* Deassert reset. */ RH_INST(dev)->ports[port] &= ~P_PORT_RESET; /* Wait max. 2ms (ehci spec 2.3.9) for flag change to finish. */ int timeout = 20; /* time out after 20 * 100us == 2ms */ while ((RH_INST(dev)->ports[port] & P_PORT_RESET) && timeout--) udelay(100); if (RH_INST(dev)->ports[port] & P_PORT_RESET) { usb_debug("Error: ehci_rh: port reset timed out.\n"); return; } /* If the host controller enabled the port, it's a high-speed * device, otherwise it's full-speed. */ if (!(RH_INST(dev)->ports[port] & P_PORT_ENABLE)) { ehci_rh_hand_over_port(dev, port); return; } usb_debug("port %x hosts a USB2 device\n", port+1); RH_INST(dev)->devices[port] = usb_attach_device(dev->controller, dev->address, port, 2); } /* RW/C register, so clear it by writing 1 */ RH_INST(dev)->ports[port] |= P_CONN_STATUS_CHANGE; }
/* 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* uhci_poll_intr_queue (void *q_) { intr_q *q = (intr_q*)q_; if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) { int current = q->lastread; int previous; if (q->lastread == 0) { previous = q->total - 1; } else { previous = q->lastread - 1; } q->tds[previous].ctrlsts &= ~TD_STATUS_MASK; q->tds[previous].ptr = 0 | TD_TERMINATE; if (q->last_td != &q->tds[previous]) { q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE; q->last_td = &q->tds[previous]; } q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE; q->lastread = (q->lastread + 1) % q->total; if (!(q->tds[current].ctrlsts & TD_STATUS_MASK)) return &q->data[current*q->reqsize]; } /* reset queue if we fully processed it after underrun */ else if (q->qh->elementlinkptr & FLISTP_TERMINATE) { usb_debug("resetting underrun uhci interrupt queue.\n"); q->qh->elementlinkptr = virt_to_phys(q->tds + q->lastread); } return NULL; }
static void usb_handle_get_desc(struct usb_setup_req *req) { switch (req->wValue) { case USB_DESC_VALUE(USB_DESC_DEVICE, 0): usb_send_desc(_dev00, min(sizeof(_dev00),req->wLength)); break; case USB_DESC_VALUE(USB_DESC_CONFIG, 0): usb_send_desc(_cfg00, min(sizeof(_cfg00),req->wLength)); break; case USB_DESC_VALUE(USB_DESC_STRING, 0): usb_send_desc(lang_id, min(sizeof(lang_id),req->wLength)); break; case USB_DESC_VALUE(USB_DESC_STRING, 1): usb_send_desc(mfg_string, min(sizeof(mfg_string),req->wLength)); break; case USB_DESC_VALUE(USB_DESC_STRING, 2): usb_send_desc(prod_string, min(sizeof(prod_string),req->wLength)); break; default: usb_debug("unknown desc: %h\n", req->wValue); usb_nak(&usb_ep_data[0]); break; } }
static int get_mps(dwc2_ep_t *ep) { dwc2_ep_reg_t *ep_reg = ep->ep_regs; depctl_t depctl; uint16_t mps = 0; depctl.d32 = readl(&ep_reg->depctl); if (ep->ep_num == 0) { switch (depctl.mps) { case D0EPCTL_MPS_64: mps = 64; break; case D0EPCTL_MPS_32: mps = 32; break; case D0EPCTL_MPS_16: mps = 16; break; case D0EPCTL_MPS_8: mps = 8; break; default: usb_debug("get mps error\n"); } } else { mps = depctl.mps; } return mps; }
void dwc2_rh_init(usbdev_t *dev) { dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); /* we can set them here because a root hub _really_ shouldn't appear elsewhere */ dev->address = 0; dev->hub = -1; dev->port = -1; generic_hub_init(dev, 1, &dwc2_rh_ops); usb_debug("dwc2_rh_init HPRT 0x%08x p = %p\n ", readl(dwc2->hprt0), dwc2->hprt0); usb_debug("DWC2: root hub init done\n"); }
static void uhci_rh_scanport (usbdev_t *dev, int port) { int portsc, offset; if (port == 1) { portsc = PORTSC1; offset = 0; } else if (port == 2) { portsc = PORTSC2; offset = 1; } else { usb_debug("Invalid port %d\n", port); return; } int devno = RH_INST (dev)->port[offset]; if ((dev->controller->devices[devno] != 0) && (devno != -1)) { usb_detach_device(dev->controller, devno); RH_INST (dev)->port[offset] = -1; } uhci_reg_write16(dev->controller, portsc, uhci_reg_read16(dev->controller, portsc) | (1 << 3) | (1 << 2)); // clear port state change, enable port mdelay(100); // wait for signal to stabilize if ((uhci_reg_read16 (dev->controller, portsc) & 1) != 0) { // device attached uhci_rh_disable_port (dev, port); uhci_rh_enable_port (dev, port); int speed = ((uhci_reg_read16 (dev->controller, portsc) >> 8) & 1); RH_INST (dev)->port[offset] = usb_attach_device(dev->controller, dev->address, portsc, speed); }
/* finalize == 1: if data is of packet aligned size, add a zero length packet */ static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize) { int maxpsize = ep->maxpacketsize; if (maxpsize == 0) fatal("MaxPacketSize == 0!!!"); int numpackets = (size + maxpsize - 1) / maxpsize; if (finalize && ((size % maxpsize) == 0)) { numpackets++; } if (numpackets == 0) return 0; td_t *tds = create_schedule (numpackets); int i = 0, toggle = ep->toggle; while ((size > 0) || ((size == 0) && (finalize != 0))) { fill_schedule (&tds[i], ep, min (size, maxpsize), data, &toggle); i++; data += maxpsize; size -= maxpsize; } if (run_schedule (ep->dev, tds) == 1) { usb_debug("Stalled. Trying to clean up.\n"); clear_stall (ep); free (tds); return 1; } ep->toggle = toggle; free (tds); return 0; }
void xhci_dump_transfer_trb(const trb_t *const cur) { xhci_debug("Transfer TRB (@%p):\n", cur); usb_debug(" PTR_L\t0x%08"PRIx32"\n", cur->ptr_low); usb_debug(" PTR_H\t0x%08"PRIx32"\n", cur->ptr_high); usb_debug(" STATUS\t0x%08"PRIx32"\n", cur->status); usb_debug(" CNTRL\t0x%08"PRIx32"\n", cur->control); TRB_DUMP(TL, cur); TRB_DUMP(TDS, cur); TRB_DUMP(C, cur); TRB_DUMP(ISP, cur); TRB_DUMP(CH, cur); TRB_DUMP(IOC, cur); TRB_DUMP(IDT, cur); TRB_DUMP(TT, cur); TRB_DUMP(DIR, cur); }
static void usb_send_desc(const void *desc, int len) { if (usb_ep_data[0].in_size < len) { usb_debug("descriptor size (%x) larger than in buffer (%x)\n", len, usb_ep_data[0].in_size); usb_nak(&usb_ep_data[0]); return; } usb_ep_write(&usb_ep_data[0], desc, len); }
void xhci_dump_epctx(const epctx_t *const ec) { xhci_debug("Endpoint Context (@%p):\n", ec); usb_debug(" FIELD1\t0x%08"PRIx32"\n", ec->f1); usb_debug(" FIELD2\t0x%08"PRIx32"\n", ec->f2); usb_debug(" TRDQ_L\t0x%08"PRIx32"\n", ec->tr_dq_low); usb_debug(" TRDQ_H\t0x%08"PRIx32"\n", ec->tr_dq_high); usb_debug(" FIELD5\t0x%08"PRIx32"\n", ec->f5); EC_DUMP(STATE, ec); EC_DUMP(INTVAL, ec); EC_DUMP(CERR, ec); EC_DUMP(TYPE, ec); EC_DUMP(MBS, ec); EC_DUMP(MPS, ec); EC_DUMP(DCS, ec); EC_DUMP(AVRTRB, ec); EC_DUMP(MXESIT, ec); }
/* dump uhci */ static void uhci_dump (hci_t *controller) { usb_debug ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD)); usb_debug ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS)); usb_debug ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR)); usb_debug ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM)); usb_debug ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD)); usb_debug ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD)); usb_debug ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1)); usb_debug ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2)); }
int usb_debug_printf( const char *fmt, ...) { va_list ap; va_start(ap, fmt); char buf[60]; int ret = vsnprintf(buf, sizeof(buf), fmt, ap); usb_debug(buf); return ret; }
void xhci_dump_slotctx(const slotctx_t *const sc) { xhci_debug("Slot Context (@%p):\n", sc); usb_debug(" FIELD1\t0x%08"PRIx32"\n", sc->f1); usb_debug(" FIELD2\t0x%08"PRIx32"\n", sc->f2); usb_debug(" FIELD3\t0x%08"PRIx32"\n", sc->f3); usb_debug(" FIELD4\t0x%08"PRIx32"\n", sc->f4); SC_DUMP(ROUTE, sc); SC_DUMP(SPEED1, sc); SC_DUMP(MTT, sc); SC_DUMP(HUB, sc); SC_DUMP(CTXENT, sc); SC_DUMP(RHPORT, sc); SC_DUMP(NPORTS, sc); SC_DUMP(TTID, sc); SC_DUMP(TTPORT, sc); SC_DUMP(TTT, sc); SC_DUMP(UADDR, sc); SC_DUMP(STATE, sc); }
u32 usb_quirk_check(u16 vendor, u16 device) { int i; for (i = 0; i < ARRAY_SIZE(usb_quirks); i++) { if ((usb_quirks[i].vendor == vendor) && (usb_quirks[i].device == device)) { usb_debug("USB quirks enabled: %08x\n", usb_quirks[i].quirks); return usb_quirks[i].quirks; } } return USB_QUIRK_NONE; }
static void usb_handle_setup_req(struct usb_setup_req *req) { switch (req->bRequest) { case USB_REQ_GET_DESCRIPTOR: usb_handle_get_desc(req); break; case USB_REQ_SET_ADDRESS: clr_set_reg(USB_DEVCMDSTAT, USB_DEVCMDSTAT_DEV_ADDR(~0), USB_DEVCMDSTAT_DEV_ADDR(req->wValue)); usb_send_zlp(&usb_ep_data[0]); break; case USB_REQ_GET_CONFIGURATION: { u8 config = usb_state == USB_STATE_ONLINE; usb_debug("get_config(%d)\n", config); usb_ep_write(&usb_ep_data[0], &config, 1); break; } case USB_REQ_SET_CONFIGURATION: usb_handle_set_config(req->wValue); break; default: usb_debug("unknown setup req:\n"); usb_debug(" bmRequestType: %b\n", req->bmRequestType); usb_debug(" dir: %b\n", req->t.dir); usb_debug(" type: %b\n", req->t.type); usb_debug(" rcpt: %b\n", req->t.rcpt); usb_debug(" bRequest: %b\n", req->bRequest); usb_debug(" wValue: %h\n", req->wValue); usb_debug(" wIndex: %h\n", req->wIndex); usb_debug(" wLength: %h\n", req->wLength); usb_nak(&usb_ep_data[0]); break; } }
static void uhci_rh_enable_port (usbdev_t *dev, int port) { u16 value; hci_t *controller = dev->controller; if (port == 1) port = PORTSC1; else if (port == 2) port = PORTSC2; else { usb_debug("Invalid port %d\n", port); return; } uhci_reg_write16(controller, port, uhci_reg_read16(controller, port) & ~(1 << 12)); /* wakeup */ uhci_reg_write16(controller, port, uhci_reg_read16(controller, port) | 1 << 9); /* reset */ mdelay (30); // >10ms uhci_reg_write16(controller, port, uhci_reg_read16(controller, port) & ~(1 << 9)); mdelay (1); // >5.3us per spec, <3ms because some devices make trouble uhci_reg_write16(controller, port, uhci_reg_read16(controller, port) | 1 << 2); /* enable */ /* wait for controller to enable port */ /* TOTEST: how long to wait? 100ms for now */ int timeout = 200; /* time out after 200 * 500us == 100ms */ do { value = uhci_reg_read16 (controller, port); udelay(500); timeout--; } while (((value & (1 << 2)) == 0) && (value & 0x01) && timeout); if (!timeout) usb_debug("Warning: uhci_rh: port enabling timed out.\n"); }
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_rh_hand_over_port (usbdev_t *dev, int port) { usb_debug("giving up port %x, it's USB1\n", port+1); /* Clear ConnectStatusChange before evaluation */ /* RW/C register, so clear it by writing 1 */ RH_INST(dev)->ports[port] |= P_CONN_STATUS_CHANGE; /* Lowspeed device. Hand over to companion */ RH_INST(dev)->ports[port] |= P_PORT_OWNER; /* TOTEST: how long to wait? trying 100ms for now */ int timeout = 10; /* timeout after 10 * 10ms == 100ms */ while (!(RH_INST(dev)->ports[port] & P_CONN_STATUS_CHANGE) && timeout--) mdelay(10); if (!(RH_INST(dev)->ports[port] & P_CONN_STATUS_CHANGE)) { usb_debug("Warning: Handing port over to companion timed out.\n"); } /* RW/C register, so clear it by writing 1 */ RH_INST(dev)->ports[port] |= P_CONN_STATUS_CHANGE; return; }
static void usb_handle_set_config(unsigned config) { usb_debug("set_config(%d)\n", config); if (config != 1) { usb_nak(&usb_ep_data[0]); return; } usb_config_ep(&usb_ep_data[1]); // clr_set_reg(USB_DEVCMDSTAT, 0, USB_DEVCMDSTAT_INTONNAK_AI); usb_send_zlp(&usb_ep_data[0]); usb_state = USB_STATE_ONLINE; if (usb_online_cb) usb_online_cb(1); printx("state = %d\n", usb_state); }
static void uhci_reset (hci_t *controller) { /* reset */ uhci_reg_write16 (controller, USBCMD, 4); /* Global Reset */ mdelay (50); /* uhci spec 2.1.1: at least 10ms */ uhci_reg_write16 (controller, USBCMD, 0); mdelay (10); uhci_reg_write16 (controller, USBCMD, 2); /* Host Controller Reset */ /* wait for controller to finish reset */ /* TOTEST: how long to wait? 100ms for now */ int timeout = 200; /* time out after 200 * 500us == 100ms */ while (((uhci_reg_read16 (controller, USBCMD) & 2) != 0) && timeout--) udelay (500); if (timeout < 0) usb_debug ("Warning: uhci: host controller reset timed out.\n"); }
static void td_dump(td_t *td) { usb_debug("+---------------------------------------------------+\n"); if ((td->token & TD_PID_MASK) == UHCI_SETUP) usb_debug("|..[SETUP]..........................................|\n"); else if ((td->token & TD_PID_MASK) == UHCI_IN) usb_debug("|..[IN].............................................|\n"); else if ((td->token & TD_PID_MASK) == UHCI_OUT) usb_debug("|..[OUT]............................................|\n"); else usb_debug("|..[]...............................................|\n"); usb_debug("|:|============ UHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(td)); usb_debug("|:+-----------------------------------------------+:|\n"); usb_debug("|:| Next TD/QH [0x%08lx] |:|\n", td->ptr & ~0xFUL); usb_debug("|:+-----------------------------------------------+:|\n"); usb_debug("|:| Depth/Breath [%lx] | QH/TD [%lx] | TERMINATE [%lx] |:|\n", (td->ptr & (1UL << 2)) >> 2, (td->ptr & (1UL << 1)) >> 1, td->ptr & 1UL); usb_debug("|:+-----------------------------------------------+:|\n"); usb_debug("|:| T | Maximum Length | [%04lx] |:|\n", (td->token & (0x7FFUL << 21)) >> 21); usb_debug("|:| O | PID CODE | [%04lx] |:|\n", td->token & 0xFF); usb_debug("|:| K | Endpoint | [%04lx] |:|\n", (td->token & TD_EP_MASK) >> TD_EP_SHIFT); usb_debug("|:| E | Device Address | [%04lx] |:|\n", (td->token & (0x7FUL << 8)) >> 8); usb_debug("|:| N | Data Toggle | [%lx] |:|\n", (td->token & (1UL << 19)) >> 19); usb_debug("|:+-----------------------------------------------+:|\n"); usb_debug("|:| C | Short Packet Detector | [%lx] |:|\n", (td->ctrlsts & (1UL << 29)) >> 29); usb_debug("|:| O | Error Counter | [%lx] |:|\n", (td->ctrlsts & (3UL << TD_COUNTER_SHIFT)) >> TD_COUNTER_SHIFT); usb_debug("|:| N | Low Speed Device | [%lx] |:|\n", (td->ctrlsts & (1UL << 26)) >> 26); usb_debug("|:| T | Isochronous Select | [%lx] |:|\n", (td->ctrlsts & (1UL << 25)) >> 25); usb_debug("|:| R | Interrupt on Complete (IOC) | [%lx] |:|\n", (td->ctrlsts & (1UL << 24)) >> 24); usb_debug("|:+ O ----------------------------------------+:|\n"); usb_debug("|:| L | Active | [%lx] |:|\n", (td->ctrlsts & (1UL << 23)) >> 23); usb_debug("|:| & | Stalled | [%lx] |:|\n", (td->ctrlsts & (1UL << 22)) >> 22); usb_debug("|:| S | Data Buffer Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 21)) >> 21); usb_debug("|:| T | Bubble Detected | [%lx] |:|\n", (td->ctrlsts & (1UL << 20)) >> 20); usb_debug("|:| A | NAK Received | [%lx] |:|\n", (td->ctrlsts & (1UL << 19)) >> 19); usb_debug("|:| T | CRC/Timeout Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 18)) >> 18); usb_debug("|:| U | Bitstuff Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 17)) >> 17); usb_debug("|:| S ----------------------------------------|:|\n"); usb_debug("|:| | Actual Length | [%04lx] |:|\n", td->ctrlsts & 0x7FFUL); usb_debug("|:+-----------------------------------------------+:|\n"); usb_debug("|:| Buffer pointer [0x%08lx] |:|\n", td->bufptr); usb_debug("|:|-----------------------------------------------|:|\n"); usb_debug("|...................................................|\n"); usb_debug("+---------------------------------------------------+\n"); }
/* create and hook-up an intr queue into device schedule */ static void* uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming) { u8 *data = malloc(reqsize*reqcount); td_t *tds = memalign(16, sizeof(td_t) * reqcount); qh_t *qh = memalign(16, sizeof(qh_t)); if (!data || !tds || !qh) fatal("Not enough memory to create USB intr queue prerequisites.\n"); qh->elementlinkptr = virt_to_phys(tds); intr_q *q = malloc(sizeof(intr_q)); if (!q) fatal("Not enough memory to create USB intr queue.\n"); q->qh = qh; q->tds = tds; q->data = data; q->lastread = 0; q->total = reqcount; q->reqsize = reqsize; q->last_td = &tds[reqcount - 1]; memset (tds, 0, sizeof (td_t) * reqcount); int i; for (i = 0; i < reqcount; i++) { tds[i].ptr = virt_to_phys (&tds[i + 1]); switch (ep->direction) { case IN: tds[i].token = UHCI_IN; break; case OUT: tds[i].token = UHCI_OUT; break; case SETUP: tds[i].token = UHCI_SETUP; break; } tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT | (ep->endpoint & 0xf) << TD_EP_SHIFT | maxlen (reqsize) << TD_MAXLEN_SHIFT | (ep->toggle & 1) << TD_TOGGLE_SHIFT; tds[i].bufptr = virt_to_phys (data); tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) | (ep->dev->speed?TD_LOWSPEED:0) | TD_STATUS_ACTIVE; ep->toggle ^= 1; data += reqsize; } tds[reqcount - 1].ptr = 0 | TD_TERMINATE; /* insert QH into framelist */ uhci_t *const uhcic = UHCI_INST(ep->dev->controller); const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH; int nothing_placed = 1; qh->headlinkptr = def_ptr; for (i = 0; i < 1024; i += reqtiming) { /* advance to the next free position */ while ((i < 1024) && (uhcic->framelistptr[i] != def_ptr)) ++i; if (i < 1024) { uhcic->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH; nothing_placed = 0; } } if (nothing_placed) { usb_debug("Error: Failed to place UHCI interrupt queue " "head into framelist: no space left\n"); uhci_destroy_intr_queue(ep, q); return NULL; } return q; }
static int uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, unsigned char *data) { int endp = 0; /* this is control: always 0 */ int mlen = dev->endpoints[0].maxpacketsize; int count = (2 + (dalen + mlen - 1) / mlen); unsigned short req = ((unsigned short *) devreq)[0]; int i; td_t *tds = memalign (16, sizeof (td_t) * count); memset (tds, 0, sizeof (td_t) * count); count--; /* to compensate for 0-indexed array */ for (i = 0; i < count; i++) { tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST; } tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE; tds[0].token = UHCI_SETUP | dev->address << TD_DEVADDR_SHIFT | endp << TD_EP_SHIFT | TD_TOGGLE_DATA0 | maxlen(drlen) << TD_MAXLEN_SHIFT; tds[0].bufptr = virt_to_phys (devreq); tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) | (dev->speed?TD_LOWSPEED:0) | TD_STATUS_ACTIVE; int toggle = 1; for (i = 1; i < count; i++) { switch (dir) { case SETUP: tds[i].token = UHCI_SETUP; break; case IN: tds[i].token = UHCI_IN; break; case OUT: tds[i].token = UHCI_OUT; break; } tds[i].token |= dev->address << TD_DEVADDR_SHIFT | endp << TD_EP_SHIFT | maxlen (min (mlen, dalen)) << TD_MAXLEN_SHIFT | toggle << TD_TOGGLE_SHIFT; tds[i].bufptr = virt_to_phys (data); tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) | (dev->speed?TD_LOWSPEED:0) | TD_STATUS_ACTIVE; toggle ^= 1; dalen -= mlen; data += mlen; } tds[count].token = ((dir == OUT) ? UHCI_IN : UHCI_OUT) | dev->address << TD_DEVADDR_SHIFT | endp << TD_EP_SHIFT | maxlen(0) << TD_MAXLEN_SHIFT | TD_TOGGLE_DATA1; tds[count].bufptr = 0; tds[count].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */ (dev->speed?TD_LOWSPEED:0) | TD_STATUS_ACTIVE; UHCI_INST (dev->controller)->qh_data->elementlinkptr = virt_to_phys (tds) & ~(FLISTP_QH | FLISTP_TERMINATE); td_t *td = wait_for_completed_qh (dev->controller, UHCI_INST (dev->controller)-> qh_data); int result; if (td == 0) { result = 0; } else { usb_debug ("control packet, req %x\n", req); td_dump (td); result = 1; } free (tds); return result; }