📄 isp116x-hcd.c
字号:
dump_ptd_data(&ptd[i], (__u8 *) data + done, 0); write_ptddata_to_fifo(isp116x, (__u8 *) data + done, PTD_GET_LEN(&ptd[i])); done += PTD_GET_LEN(&ptd[i]); }}/* Read the processed PTD's and data from fifo ram back to URBs' buffers. * Fifo must be full and done */static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev, unsigned long pipe, struct ptd *ptd, int n, void *data, int len){ int buflen = n * sizeof(struct ptd) + len; int i, done, cc, ret; isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); isp116x_write_reg16(isp116x, HCXFERCTR, buflen); isp116x_write_addr(isp116x, HCATLPORT); ret = TD_CC_NOERROR; done = 0; for (i = 0; i < n; i++) { DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); ptd[i].count = isp116x_read_data16(isp116x); ptd[i].mps = isp116x_read_data16(isp116x); ptd[i].len = isp116x_read_data16(isp116x); ptd[i].faddr = isp116x_read_data16(isp116x); dump_ptd(&ptd[i]); read_ptddata_from_fifo(isp116x, (__u8 *) data + done, PTD_GET_LEN(&ptd[i])); dump_ptd_data(&ptd[i], (__u8 *) data + done, 1); done += PTD_GET_LEN(&ptd[i]); cc = PTD_GET_CC(&ptd[i]); /* Data underrun means basically that we had more buffer space than * the function had data. It is perfectly normal but upper levels have * to know how much we actually transferred. */ if (cc == TD_NOTACCESSED || (cc != TD_CC_NOERROR && (ret == TD_CC_NOERROR || ret == TD_DATAUNDERRUN))) ret = cc; } DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); return ret;}/* Interrupt handling */static int isp116x_interrupt(struct isp116x *isp116x){ u16 irqstat; u32 intstat; int ret = 0; isp116x_write_reg16(isp116x, HCuPINTENB, 0); irqstat = isp116x_read_reg16(isp116x, HCuPINT); isp116x_write_reg16(isp116x, HCuPINT, irqstat); DBG(">>>>>> irqstat %x <<<<<<", irqstat); if (irqstat & HCuPINT_ATL) { DBG(">>>>>> HCuPINT_ATL <<<<<<"); udelay(500); ret = 1; } if (irqstat & HCuPINT_OPR) { intstat = isp116x_read_reg32(isp116x, HCINTSTAT); isp116x_write_reg32(isp116x, HCINTSTAT, intstat); DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat); if (intstat & HCINT_UE) { ERR("unrecoverable error, controller disabled"); /* FIXME: be optimistic, hope that bug won't repeat * often. Make some non-interrupt context restart the * controller. Count and limit the retries though; * either hardware or software errors can go forever... */ isp116x_reset(isp116x); ret = -1; return -1; } if (intstat & HCINT_RHSC) { got_rhsc = 1; ret = 1; /* When root hub or any of its ports is going to come out of suspend, it may take more than 10ms for status bits to stabilize. */ wait_ms(20); } if (intstat & HCINT_SO) { ERR("schedule overrun"); ret = -1; } irqstat &= ~HCuPINT_OPR; } return ret;}/* With one PTD we can transfer almost 1K in one go; * HC does the splitting into endpoint digestible transactions */struct ptd ptd[1];static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe){ unsigned mpck = usb_maxpacket(dev, pipe); /* One PTD can transfer 1023 bytes but try to always * transfer multiples of endpoint buffer size */ return 1023 / mpck * mpck;}/* Do an USB transfer */static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe, int dir, void *buffer, int len){ struct isp116x *isp116x = &isp116x_dev; int type = usb_pipetype(pipe); int epnum = usb_pipeendpoint(pipe); int max = usb_maxpacket(dev, pipe); int dir_out = usb_pipeout(pipe); int speed_low = usb_pipeslow(pipe); int i, done = 0, stat, timeout, cc; /* 500 frames or 0.5s timeout when function is busy and NAKs transactions for a while */ int retries = 500; DBG("------------------------------------------------"); dump_msg(dev, pipe, buffer, len, "SUBMIT"); DBG("------------------------------------------------"); if (len >= 1024) { ERR("Too big job"); dev->status = USB_ST_CRC_ERR; return -1; } if (isp116x->disabled) { ERR("EPIPE"); dev->status = USB_ST_CRC_ERR; return -1; } /* device pulled? Shortcut the action. */ if (devgone == dev) { ERR("ENODEV"); dev->status = USB_ST_CRC_ERR; return USB_ST_CRC_ERR; } if (!max) { ERR("pipesize for pipe %lx is zero", pipe); dev->status = USB_ST_CRC_ERR; return -1; } if (type == PIPE_ISOCHRONOUS) { ERR("isochronous transfers not supported"); dev->status = USB_ST_CRC_ERR; return -1; } /* FIFO not empty? */ if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) { ERR("****** FIFO not empty! ******"); dev->status = USB_ST_BUF_ERR; return -1; } retry: isp116x_write_reg32(isp116x, HCINTSTAT, 0xff); /* Prepare the PTD data */ ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out)); ptd->mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum) | PTD_LAST_MSK; ptd->len = PTD_LEN(len) | PTD_DIR(dir); ptd->faddr = PTD_FA(usb_pipedevice(pipe));retry_same: /* Pack data into FIFO ram */ pack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len);#ifdef EXTRA_DELAY wait_ms(EXTRA_DELAY);#endif /* Start the data transfer */ /* Allow more time for a BULK device to react - some are slow */ if (usb_pipetype(pipe) == PIPE_BULK) timeout = 5000; else timeout = 100; /* Wait for it to complete */ for (;;) { /* Check whether the controller is done */ stat = isp116x_interrupt(isp116x); if (stat < 0) { dev->status = USB_ST_CRC_ERR; break; } if (stat > 0) break; /* Check the timeout */ if (--timeout) udelay(1); else { ERR("CTL:TIMEOUT "); stat = USB_ST_CRC_ERR; break; } } /* We got an Root Hub Status Change interrupt */ if (got_rhsc) { isp116x_show_regs(isp116x); got_rhsc = 0; /* Abuse timeout */ timeout = rh_check_port_status(isp116x); if (timeout >= 0) { /* * FIXME! NOTE! AAAARGH! * This is potentially dangerous because it assumes * that only one device is ever plugged in! */ devgone = dev; } } /* Ok, now we can read transfer status */ /* FIFO not ready? */ if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) { ERR("****** FIFO not ready! ******"); dev->status = USB_ST_BUF_ERR; return -1; } /* Unpack data from FIFO ram */ cc = unpack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); i = PTD_GET_COUNT(ptd); done += i; buffer += i; len -= i; /* There was some kind of real problem; Prepare the PTD again * and retry from the failed transaction on */ if (cc && cc != TD_NOTACCESSED && cc != TD_DATAUNDERRUN) { if (retries >= 100) { retries -= 100; /* The chip will have toggled the toggle bit for the failed * transaction too. We have to toggle it back. */ usb_settoggle(dev, epnum, dir_out, !PTD_GET_TOGGLE(ptd)); goto retry; } } /* "Normal" errors; TD_NOTACCESSED would mean in effect that the function have NAKed * the transactions from the first on for the whole frame. It may be busy and we retry * with the same PTD. PTD_ACTIVE (and not TD_NOTACCESSED) would mean that some of the * PTD didn't make it because the function was busy or the frame ended before the PTD * finished. We prepare the rest of the data and try again. */ else if (cc == TD_NOTACCESSED || PTD_GET_ACTIVE(ptd) || (cc != TD_DATAUNDERRUN && PTD_GET_COUNT(ptd) < PTD_GET_LEN(ptd))) { if (retries) { --retries; if (cc == TD_NOTACCESSED && PTD_GET_ACTIVE(ptd) && !PTD_GET_COUNT(ptd)) goto retry_same; usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); goto retry; } } if (cc != TD_CC_NOERROR && cc != TD_DATAUNDERRUN) { DBG("****** completition code error %x ******", cc); switch (cc) { case TD_CC_BITSTUFFING: dev->status = USB_ST_BIT_ERR; break; case TD_CC_STALL: dev->status = USB_ST_STALLED; break; case TD_BUFFEROVERRUN: case TD_BUFFERUNDERRUN: dev->status = USB_ST_BUF_ERR; break; default: dev->status = USB_ST_CRC_ERR; } return -cc; } else usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)"); dev->status = 0; return done;}/* Adapted from au1x00_usb_ohci.c */static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *cmd){ struct isp116x *isp116x = &isp116x_dev; u32 tmp = 0; int leni = transfer_len; int len = 0; int stat = 0; u32 datab[4]; u8 *data_buf = (u8 *) datab; u16 bmRType_bReq; u16 wValue; u16 wIndex; u16 wLength; if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { INFO("Root-Hub submit IRQ: NOT implemented"); return 0; } bmRType_bReq = cmd->requesttype | (cmd->request << 8); wValue = swap_16(cmd->value); wIndex = swap_16(cmd->index); wLength = swap_16(cmd->length); DBG("--- HUB ----------------------------------------"); DBG("submit rh urb, req=%x val=%#x index=%#x len=%d", bmRType_bReq, wValue, wIndex, wLength); dump_msg(dev, pipe, buffer, transfer_len, "RH"); DBG("------------------------------------------------"); switch (bmRType_bReq) { case RH_GET_STATUS: DBG("RH_GET_STATUS"); *(__u16 *) data_buf = swap_16(1); len = 2; break; case RH_GET_STATUS | RH_INTERFACE: DBG("RH_GET_STATUS | RH_INTERFACE"); *(__u16 *) data_buf = swap_16(0); len = 2; break; case RH_GET_STATUS | RH_ENDPOINT: DBG("RH_GET_STATUS | RH_ENDPOINT"); *(__u16 *) data_buf = swap_16(0); len = 2; break; case RH_GET_STATUS | RH_CLASS: DBG("RH_GET_STATUS | RH_CLASS"); tmp = isp116x_read_reg32(isp116x, HCRHSTATUS); *(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE)); len = 4; break; case RH_GET_STATUS | RH_OTHER | RH_CLASS: DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS"); tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1); *(__u32 *) data_buf = swap_32(tmp); isp116x_show_regs(isp116x); len = 4; break; case RH_CLEAR_FEATURE | RH_ENDPOINT: DBG("RH_CLEAR_FEATURE | RH_ENDPOINT"); switch (wValue) { case RH_ENDPOINT_STALL: DBG("C_HUB_ENDPOINT_STALL"); len = 0; break; } break; case RH_CLEAR_FEATURE | RH_CLASS: DBG("RH_CLEAR_FEATURE | RH_CLASS"); switch (wValue) { case RH_C_HUB_LOCAL_POWER: DBG("C_HUB_LOCAL_POWER"); len = 0; break; case RH_C_HUB_OVER_CURRENT: DBG("C_HUB_OVER_CURRENT"); isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); len = 0; break; } break; case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS"); switch (wValue) { case RH_PORT_ENABLE: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_CCS); len = 0; break; case RH_PORT_SUSPEND: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_POCI); len = 0; break; case RH_PORT_POWER: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_LSDA); len = 0; break; case RH_C_PORT_CONNECTION: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_CSC); len = 0; break; case RH_C_PORT_ENABLE: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_PESC); len = 0; break; case RH_C_PORT_SUSPEND: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_PSSC); len = 0; break; case RH_C_PORT_OVER_CURRENT: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_POCI); len = 0; break; case RH_C_PORT_RESET: isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, RH_PS_PRSC); len = 0; break; default: ERR("invalid wValue"); stat = USB_ST_STALLED; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -