static int ps2_recvbyte(int aux, int needack, int timeout) { u64 end = calc_future_tsc(timeout); for (;;) { u8 status = inb(PORT_PS2_STATUS); if (status & I8042_STR_OBF) { u8 data = inb(PORT_PS2_DATA); dprintf(7, "ps2 read %x\n", data); if (!!(status & I8042_STR_AUXDATA) == aux) { if (!needack) return data; if (data == PS2_RET_ACK) return data; if (data == PS2_RET_NAK) { dprintf(1, "Got ps2 nak (status=%x)\n", status); return data; } } // This data not part of command - just discard it. dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); } if (check_tsc(end)) { // Don't warn on second byte of a reset if (timeout > 100) warn_timeout(); return -1; } yield(); } }
static int ps2_recvbyte(int aux, int needack, int timeout) { u64 end = calc_future_tsc(timeout); for (;;) { u8 status = inb(PORT_PS2_STATUS); if (status & I8042_STR_OBF) { u8 data = inb(PORT_PS2_DATA); dprintf(7, "ps2 read %x\n", data); if (!!(status & I8042_STR_AUXDATA) == aux) { if (!needack) return data; if (data == PS2_RET_ACK) return data; if (data == PS2_RET_NAK) { dprintf(1, "Got ps2 nak (status=%x)\n", status); return data; } } // Data not part of this command. process_ps2byte(status, data); } if (check_time(end)) { dprintf(1, "ps2_recvbyte timeout\n"); return -1; } yield(); } }
static int ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) { u64 end = calc_future_tsc(timeout); u32 status; for (;;) { status = td->token; if (!(status & QTD_STS_ACTIVE)) break; if (check_tsc(end)) { u32 cur = GET_LOWFLAT(pipe->qh.current); u32 tok = GET_LOWFLAT(pipe->qh.token); u32 next = GET_LOWFLAT(pipe->qh.qtd_next); warn_timeout(); dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n" , pipe, cur, tok, next, td, status); ehci_reset_pipe(pipe); struct usb_ehci_s *cntl = container_of( GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb); ehci_waittick(cntl); return -1; } yield(); } if (status & QTD_STS_HALT) { dprintf(1, "ehci_wait_td error - status=%x\n", status); ehci_reset_pipe(pipe); return -2; } return 0; }
// Check if device attached to port static int usb_hub_detect(struct usbhub_s *hub, u32 port) { // Turn on power to port. int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER); if (ret) goto fail; // Wait for port power to stabilize. msleep(hub->powerwait); // Check periodically for a device connect. struct usb_port_status sts; u64 end = calc_future_tsc(USB_TIME_SIGATT); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) goto fail; if (sts.wPortStatus & USB_PORT_STAT_CONNECTION) // Device connected. break; if (check_tsc(end)) // No device found. return -1; msleep(5); } // XXX - wait USB_TIME_ATTDB time? return 0; fail: dprintf(1, "Failure on hub port %d detect\n", port); return -1; }
int scsi_is_ready(struct disk_op_s *op) { dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_g); /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is * reported by the device. If the device reports "IN PROGRESS", * 30 seconds is added. */ int in_progress = 0; u64 end = calc_future_tsc(5000); for (;;) { if (check_tsc(end)) { dprintf(1, "test unit ready failed\n"); return -1; } int ret = cdb_test_unit_ready(op); if (!ret) // Success break; struct cdbres_request_sense sense; ret = cdb_get_sense(op, &sense); if (ret) // Error - retry. continue; // Sense succeeded. if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */ dprintf(1, "Device reports MEDIUM NOT PRESENT\n"); return -1; } if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) { /* IN PROGRESS OF BECOMING READY */ printf("Waiting for device to detect medium... "); /* Allow 30 seconds more */ end = calc_future_tsc(30000); in_progress = 1; } } return 0; }
// Reset a drive static void ata_reset(struct atadrive_s *adrive_g) { struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf); u8 slave = GET_GLOBAL(adrive_g->slave); u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); dprintf(6, "ata_reset drive=%p\n", &adrive_g->drive); // Pulse SRST outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC); udelay(5); outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC); msleep(2); // wait for device to become not busy. int status = await_not_bsy(iobase1); if (status < 0) goto done; if (slave) { // Change device. u64 end = calc_future_tsc(IDE_TIMEOUT); for (;;) { outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); status = ndelay_await_not_bsy(iobase1); if (status < 0) goto done; if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) break; // Change drive request failed to take effect - retry. if (check_tsc(end)) { warn_timeout(); goto done; } } } else { // QEMU doesn't reset dh on reset, so set it explicitly. outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH); } // On a user-reset request, wait for RDY if it is an ATA device. u8 type=GET_GLOBAL(adrive_g->drive.type); if (type == DTYPE_ATA) status = await_rdy(iobase1); done: // Enable interrupts outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); dprintf(6, "ata_reset exit status=%x\n", status); }
// Wait for the specified ide state static inline int await_ide(u8 mask, u8 flags, u16 base, u16 timeout) { u64 end = calc_future_tsc(timeout); for (;;) { u8 status = inb(base+ATA_CB_STAT); if ((status & mask) == flags) return status; if (check_tsc(end)) { warn_timeout(); return -1; } yield(); } }
static int wait_ed(struct ohci_ed *ed) { // XXX - 500ms just a guess u64 end = calc_future_tsc(500); for (;;) { if (ed->hwHeadP == ed->hwTailP) return 0; if (check_time(end)) { dprintf(1, "Timeout on wait_ed %p\n", ed); return -1; } yield(); } }
// Wait for next USB async frame to start - for ensuring safe memory release. static void ehci_waittick(struct usb_ehci_s *cntl) { if (MODE16) { msleep(10); return; } // Wait for access to "doorbell" barrier(); u32 cmd, sts; u64 end = calc_future_tsc(100); for (;;) { sts = readl(&cntl->regs->usbsts); if (!(sts & STS_IAA)) { cmd = readl(&cntl->regs->usbcmd); if (!(cmd & CMD_IAAD)) break; } if (check_tsc(end)) { warn_timeout(); return; } yield(); } // Ring "doorbell" writel(&cntl->regs->usbcmd, cmd | CMD_IAAD); // Wait for completion for (;;) { sts = readl(&cntl->regs->usbsts); if (sts & STS_IAA) break; if (check_tsc(end)) { warn_timeout(); return; } yield(); } // Ack completion writel(&cntl->regs->usbsts, STS_IAA); }
// Reset device on port static int usb_hub_reset(struct usbhub_s *hub, u32 port) { int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); if (ret) goto fail; // Wait for reset to complete. struct usb_port_status sts; u64 end = calc_future_tsc(USB_TIME_DRST * 2); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) break; if (check_tsc(end)) { warn_timeout(); goto fail; } msleep(5); } // Reset complete. if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) // Device no longer present return -1; return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) >> USB_PORT_STAT_SPEED_SHIFT); fail: dprintf(1, "Failure on hub port %d reset\n", port); usb_hub_disconnect(hub, port); return -1; }
static void configure_ehci(void *data) { struct usb_ehci_s *cntl = data; // Allocate ram for schedule storage struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh)); struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); if (!fl || !intr_qh || !async_qh) { warn_noalloc(); goto fail; } // XXX - check for halted? // Reset the HC u32 cmd = readl(&cntl->regs->usbcmd); writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); u64 end = calc_future_tsc(250); for (;;) { cmd = readl(&cntl->regs->usbcmd); if (!(cmd & CMD_HCRESET)) break; if (check_tsc(end)) { warn_timeout(); goto fail; } yield(); } // Disable interrupts (just to be safe). writel(&cntl->regs->usbintr, 0); // Set schedule to point to primary intr queue head memset(intr_qh, 0, sizeof(*intr_qh)); intr_qh->next = EHCI_PTR_TERM; intr_qh->info2 = (0x01 << QH_SMASK_SHIFT); intr_qh->token = QTD_STS_HALT; intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM; int i; for (i=0; i<ARRAY_SIZE(fl->links); i++) fl->links[i] = (u32)intr_qh | EHCI_PTR_QH; writel(&cntl->regs->periodiclistbase, (u32)fl); // Set async list to point to primary async queue head memset(async_qh, 0, sizeof(*async_qh)); async_qh->next = (u32)async_qh | EHCI_PTR_QH; async_qh->info1 = QH_HEAD; async_qh->token = QTD_STS_HALT; async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM; cntl->async_qh = async_qh; writel(&cntl->regs->asynclistbase, (u32)async_qh); // Enable queues writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN); // Set default of high speed for root hub. writel(&cntl->regs->configflag, 1); cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK; // Find devices int count = check_ehci_ports(cntl); ehci_free_pipes(cntl); if (count) // Success return; // No devices found - shutdown and free controller. writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN); msleep(4); // 2ms to stop reading memory - XXX fail: free(fl); free(intr_qh); free(async_qh); free(cntl); }