📄 ohci-sl811-emu.c
字号:
} else if ((td_flags & TD_T) == TD_T_DATA0) { td_flags = (td_flags & ~TD_T) | TD_T_DATA1; } else { td_flags = (td_flags & ~TD_T) | TD_T_DATA0; } set_hwinfo(td, td_flags);}static int check_data_toggle(struct td *td, struct xfer_buf *done_buf, struct usb_hcd *hcd){ int ret = 0; int pkt_stat = done_buf->pkt_stat; u32 td_flags = get_hwinfo(td) & TD_T; u32 ed_carry = ED_HEADP(td->ed) & ED_C; int data1 = pkt_stat & SL11H_STATMASK_SEQ; int err = (pkt_stat & SL811_PKT_ERR_MASK) ^ data1; if (err) { return -1; } if (td_flags == TD_T_TOGGLE) { ret = ((ed_carry && data1) || (!ed_carry && !data1)); } else { ret = (((td_flags == TD_T_DATA0) && !data1) || ((td_flags == TD_T_DATA1) && data1)); } if (!ret) { ohci_warn(hcd_to_ohci(hcd), "%s: FAIL: pkt_stat=%02x, ret=%d ed_c=%d, td_d=%d\n", __FUNCTION__, pkt_stat, ret, ed_carry, td_flags >> 24); } return ret;}static int update_cbp(struct td *td, int len){ u32 cbp = get_hw_fld(td, CBP); u32 be = get_hw_fld(td, BE); int finished = 0; if (cbp == 0) { return 1; } if (TD_BUF_LEN(td) == len) { cbp = 0; finished = 1; } else { u32 chunk_size = DMA_XFER_SIZE(cbp, len); if (chunk_size == len) { cbp += len; } else { cbp = (be & ~PAGE_MASK) | (len - chunk_size); } WARN_ON(cbp > be); } set_hw_fld(td, CBP, cbp); return finished;}#define TD_EC_GET(info) (((info) & TD_EC) >> 26)#define TD_EC_SET_HW(td, ec) set_hwinfo(td, (get_hwinfo(td) & ~TD_EC) | (((ec) << 26) & TD_EC))static u32 update_td_status(struct usb_hcd *hcd, struct xfer_buf *buf, struct td *td){ u32 ohci_int_status = 0; struct ed *ed = td->ed; struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); u32 td_flags = get_hwinfo(td); u8 pkt_stat = buf->pkt_stat; u32 ed_flags = get_hwinfo(ed); int iso = ed_flags & ED_ISO; int td_finished = 0; int td_halt = 0; u32 cc = TD_CC_NOERROR; BUG_ON(iso); WARN_ON(TD_CC_GET(td_flags) != TD_NOTACCESSED); if (unlikely((pkt_stat & SL811_PKT_ERR_MASK) != 0)) { if (pkt_stat & SL11H_STATMASK_NAK) { ohci_vdbg(hcd_to_ohci(hcd), "%s: Got NAK on TD %08x of ED %08x\n", __FUNCTION__, (u32)td, (u32)ed); dev->current_td = NULL; if (dev->num_bufs > 1) { kill_xfer_bufs(hcd, dev, td); } return 0; } td_halt = 1; if (pkt_stat & SL11H_STATMASK_STALL) { ohci_dbg(hcd_to_ohci(hcd), "%s: Got STALL on TD %08x of ED %08x\n", __FUNCTION__, (u32)td, (u32)ed); cc = TD_CC_STALL; } else if (pkt_stat & (SL11H_STATMASK_TMOUT | SL11H_STATMASK_ERROR | SL11H_STATMASK_SEQ)) { int err_count = TD_EC_GET(td_flags) + 1; ohci_dbg(hcd_to_ohci(hcd), "%s: XMIT ERROR on TD %08x of ED %08x\n", __FUNCTION__, (u32)td, (u32)ed); if (pkt_stat & SL11H_STATMASK_TMOUT) { cc = TD_DEVNOTRESP; } else if (pkt_stat & SL11H_STATMASK_SEQ) { cc = TD_CC_DATATOGGLEM; } else { cc = TD_CC_CRC; } TD_EC_SET_HW(td, err_count); if (err_count < 3) { td_halt = 0; dev->current_td = NULL; if (dev->num_bufs > 1) { kill_xfer_bufs(hcd, dev, td); } return 0; } } else if (pkt_stat & SL11H_STATMASK_OVF) { cc = TD_DATAOVERRUN; } } else if (pkt_stat & SL11H_STATMASK_ACK) { ohci_dbg(hcd_to_ohci(hcd), "%s: Got ACK on TD %08x of ED %08x\n", __FUNCTION__, (u32)td, (u32)ed); if (buf->count > TD_BUF_LEN(td)) { ohci_warn(hcd_to_ohci(hcd), "%s: Data overrun: bufsize: %04x datasize: %04x\n", __FUNCTION__, TD_BUF_LEN(td), buf->count); cc = TD_DATAOVERRUN; goto err; } TD_EC_SET_HW(td, 0); td_finished = update_cbp(td, buf->count); if (!td_finished && (buf->count < ed_mps(ed_flags))) { u32 cbp = get_hw_fld(td, CBP); u32 be = get_hw_fld(td, BE); if (!(td_flags & TD_R) || (buf->count == 0)) { td_halt = 1; ohci_warn(hcd_to_ohci(hcd), "%s: Data underrun: %d byte transferred, %d byte requested\n", __FUNCTION__, buf->count, ed_mps(ed_flags)); cc = TD_DATAUNDERRUN; goto err; } if ((be & PAGE_MASK) == ((cbp + buf->count - 1) & PAGE_MASK)) { be = cbp + buf->count - 1; } else { be = (be & ~PAGE_MASK) + ((cbp + buf->count - 1) & ~PAGE_MASK); } set_hw_fld(td, BE, be); set_hw_fld(td, CBP, 0); td_finished = 1; } } else { cc = TD_CC_CRC; td_halt = 1; } if (pkt_stat & SL11H_STATMASK_ACK) { if (buf->buf_len > FLIP_BUFFERS * buf->count) { buf->buf_len -= FLIP_BUFFERS * buf->count; buf->buf_addr += FLIP_BUFFERS * buf->count; } else { buf->buf_addr = NULL; buf->buf_len = 0; } if (buf->len > buf->buf_len) { buf->len = buf->buf_len; }#if FLIP_BUFFERS == 1 buf->hc ^= SL11H_HCTLMASK_SEQ;#endif update_data_toggle(hcd, td); } err: if (td_finished || (td_halt && !iso)) { ohci_int_status |= retire_td(hcd, td, ed, cc); } return ohci_int_status;}static u32 kill_current_td(struct usb_hcd *hcd, struct hc_sl811_dev *dev){ struct td *td = dev->current_td; u32 ohci_int_status = 0; BUG_ON(td == NULL); kill_xfer_bufs(hcd, dev, td); ohci_warn(hcd_to_ohci(hcd), "%s: killing TD %08x (ED: %08x)\n", __FUNCTION__, (u32)td, (u32)td->ed); ohci_int_status |= retire_td(hcd, td, td->ed, TD_DEVNOTRESP); return ohci_int_status;}static u32 finish_xfer(struct usb_hcd *hcd, struct hc_sl811_dev *dev, int flip){ u32 ohci_int_status = 0; struct xfer_buf *buf = &dev->xfer_buf[flip]; struct td *td = buf->td; int reg_offs = flip * 8; if ((dev->buf_map & (1 << flip)) && td != NULL) { BUG_ON(flip >= FLIP_BUFFERS); __hc_sl811_read_regs(dev, SL11H_PKTSTATREG + reg_offs, &buf->rcv_data, sizeof(buf->rcv_data)); buf->count = buf->len - buf->count; if (!(buf->hc & SL11H_HCTLMASK_WRITE)) { if (check_data_toggle(td, buf, hcd)) { buf->pkt_stat &= ~SL11H_STATMASK_SEQ; } else { buf->pkt_stat |= SL11H_STATMASK_SEQ; } if ((buf->pkt_stat & SL11H_STATMASK_ACK) && !(buf->pkt_stat & SL811_PKT_ERR_MASK)) { int buf_offs = flip * (SL811_BUF_SIZE / FLIP_BUFFERS); int offset = __hc_sl811_read_reg(dev, SL11H_BUFADDRREG + reg_offs); BUG_ON(offset != SL11H_DATA_START + buf_offs); // transfer data from SL811 buffer memory to TD buffer if (buf->count > 0) { ohci_vdbg(hcd_to_ohci(hcd), "%s: Transferring %d of %d byte from %02x to %08x\n", __FUNCTION__, buf->count, buf->buf_len, offset, (u32)buf->buf_addr); BUG_ON(TD_BUF_LEN(td) == 0); sl811_dma_recv_data(dev, buf, SL11H_DATA_START + buf_offs); } } } else { buf->pkt_stat &= ~SL11H_STATMASK_SEQ; } ohci_int_status = update_td_status(hcd, buf, td); } dev->num_bufs--; BUG_ON(dev->num_bufs > FLIP_BUFFERS); dev->buf_map &= ~(1 << flip); buf->td = NULL; return ohci_int_status;}/* * Interrupt routine: * * XFER_DONE interrupt: * - do outstanding data transfer of current TD * - prepare next TD, if any * - postprocess last completed TD * - find next TD to process * * SOF interrupt: * - check current td pointer * - kickstart an ED list processing, if NULL * * */#if FLIP_BUFFERS > 1#define DONE_MASK (SL11H_INTMASK_XFERDONE | SL11H_INTMASK_XFERDONE_B)#else#define DONE_MASK (SL11H_INTMASK_XFERDONE)#endifstatic irqreturn_t ohci_sl811_interrupt(int irq, void *data, struct pt_regs *pt_regs){ int handled = 0; unsigned int oscr = OSCR; struct usb_hcd *hcd = data; struct ohci_regs *regs = hcd->regs; struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct ohci_hcca *hcca = ohci->hcca; struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); u8 int_mask, int_mask_r; //add by hzh u8 int_status, int_status_r; //add by hzh u32 ohci_int_status = 0; int loops = 0; const int max_loops = 1; BUG_ON(!ohci); BUG_ON(!regs); BUG_ON(!hcd); del_timer(&dev->int_timer); if (unlikely(dev->dev_state & DEV_SLEEP)) { ohci_info(ohci, "%s: Waking up SL811 chip\n", __FUNCTION__); writeb(0, hc_sl811_data_reg); dev->dev_state &= ~DEV_SLEEP; } int_mask = int_mask_r = __hc_sl811_read_reg(dev, SL11H_INTENBLREG); //hzh __hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0); do { u8 svc_mask; int_status = int_status_r = __hc_sl811_read_reg(dev, SL11H_INTSTATREG); //hzh BUG_ON((FLIP_BUFFERS == 1) && (int_status & SL11H_INTMASK_XFERDONE_B)); svc_mask = int_status & int_mask; if (handled && (svc_mask == 0)) { break; } if (!handled && (int_status & ~SL11H_INTMASK_DSTATE) == 0) { //ohci_warn(ohci, "%s: UNSOLICITED INTERRUPT! %02x:%02x\n", __FUNCTION__, // int_status, int_mask); //commented by hzh // /* FIX INTERRUPT HANDLER AND REMOVE ME */ handled = 1; handled = 1; //add by hzh break; } if (svc_mask == 0) { WARN_ON(!handled); // /* FIX INTERRUPT HANDLER AND REMOVE ME */ handled = 1; break; } handled = 0x80000000; __hc_sl811_write_reg(dev, SL11H_INTSTATREG, svc_mask & ~(SL11H_INTMASK_RESUME|SL11H_INTMASK_INSRMV)); if (svc_mask & SL11H_INTMASK_INSRMV) { svc_mask &= ~SL11H_INTMASK_INSRMV; int_mask &= ~SL11H_INTMASK_INSRMV; ohci_dbg(ohci, "%s: Device Insert/Remove occured: %02x\n", __FUNCTION__, dev->dev_state); dev->dev_state &= ~(DEV_ACTIVE | DEV_SOF); if (dev->current_td) { ohci_int_status |= kill_current_td(hcd, dev); ohci_int_status |= OHCI_INTR_SF; } if (!(dev->dev_state & DEV_CHECK)) { sl811_trigger_bh(dev, DEV_CONN_CHK); } break; } if (svc_mask & SL11H_INTMASK_RESUME) { u8 ctl1 = hc_sl811_read_reg(dev, SL11H_CTLREG1) & SL11H_CTL1MASK_SUSPEND; svc_mask &= ~SL11H_INTMASK_RESUME; int_mask &= ~SL11H_INTMASK_RESUME; if (ctl1) { ohci_dbg(ohci, "%s: Resume Interrupt occured\n", __FUNCTION__); if (HcRhStatus & RH_HS_DRWE) { ohci_int_status |= OHCI_INTR_RD; } } else { // if no device is present while performing USB reset an interrupt // will be generated. This can safely be ignored and should neither // be acknowledged nor disabled, since that would break device // insertion detection WARN_ON(dev->dev_state & (DEV_ACTIVE | DEV_SOF)); break; } } if (svc_mask & SL11H_INTMASK_SOFINTR) { u16 frame_no = HcFmNumber + 1; int int_no; // index into interrupt table int flip; handled |= SL11H_INTMASK_SOFINTR; svc_mask &= ~SL11H_INTMASK_SOFINTR; BUG_ON(!hcca); if (dev->dev_state & DEV_SOF) { unsigned int elapsed = oscr - dev->last_sof; //if (OSCR_TO_USEC(elapsed) > (1000 + 500)) { if ((elapsed<<10)/3775 > (1000 + 500)) { //3.6864<<10 = 3775 //int missed = (OSCR_TO_USEC(elapsed) / 1000) - 1; int missed = (elapsed / (3686)) - 1; if (missed > 0) { if (((frame_no + missed) & 0x8000) ^ (frame_no & 0x8000)) { ohci_int_status |= OHCI_INTR_FNO; } frame_no += missed; } } } else if (dev->dev_state & DEV_ACTIVE) { dev->dev_state |= DEV_SOF; } dev->last_sof = OSCR; if (HcDoneHead && !(HcIntrStatus & OHCI_INTR_WDH)) { if (dev->intrdelay > 0 && dev->intrdelay < 7) { dev->intrdelay--; } else if (dev->intrdelay == 0) { int ohci_int = HcIntrStatus & HcIntrEnable; HCCADoneHead = HcDoneHead | (ohci_int ? cpu_to_le32(1) : 0); HcDoneHead = 0; ohci_int_status |= OHCI_INTR_WDH; dev->intrdelay = 7; } } HcFmRemaining = HcFmInterval & cpu_to_le32(OHCI_FR | OHCI_FRT); HCCAFrameNumber = HcFmNumber = cpu_to_le32(frame_no); if ((frame_no & 0x7fff) == 0) { ohci_int_status |= OHCI_INTR_FNO; } ohci_int_status |= OHCI_INTR_SF; if (HcPeriodCurrentED != 0) { // increment scheduling overrun counter // (two's complement arithmetic is sometimes very useful ;) int soc = (HcCmdStatus - OHCI_SOC) & OHCI_SOC; if (dev->current_td) { ohci_int_status |= kill_current_td(hcd, dev); } ohci_warn(ohci, "%s: Scheduling overrun at frame %04x\n", __FUNCTION__, frame_no); ohci_int_status |= OHCI_INTR_SO; HcCmdStatus = (HcCmdStatus & ~(OHCI_SOC | OHCI_CLF | OHCI_BLF)) | (soc & OHCI_SOC); HcControlCurrentED = 0; HcBulkCurrentED = 0; HcPeriodCurrentED = 0; } int_no = frame_no & 0x1f; if ((HcControl & OHCI_CTRL_PLE) && (HCCAInterruptTable[int_no] != 0)) { HcPeriodCurrentED = HCCAInterruptTable[int_no]; } if (HcControl & OHCI_CTRL_CLE) { if ((HcControlCurrentED == 0) && (HcCmdStatus & OHCI_CLF)) { HcControlCurrentED = HcControlHeadED; HcCmdStatus &= ~OHCI_CLF; } } if (dev->num_bufs == 0) { WARN_ON((dev->num_bufs == 0) && (dev->buf_map != 0)); dev->buf_map = 0; } if (dev->dev_state & DEV_SOF) { for (flip = 0; flip < FLIP_BUFFERS; flip++) { if (!(dev->buf_map & (1 << flip))) { if (dev->current_td && dev->buf_len > 0) { process_td(ohci, dev->current_td, flip); } else { process_ed_lists(ohci, flip); }#if FLIP_BUFFERS == 1 } else { BUG_ON(!dev->current_td);#endif } } } } if (svc_mask & DONE_MASK) { int loop = 0; int max_loops = dev->num_bufs; u8 im = DONE_MASK; if (dev->num_bufs == 0) { WARN_ON(dev->buf_map); dev->buf_map = 0; svc_mask &= ~DONE_MASK; handled |= DONE_MASK; } BUG_ON((dev->num_bufs == 0) && (svc_mask & DONE_MASK)); while (svc_mask & DONE_MASK) { int flip = (dev->flip & (1 << (dev->num_bufs - 1))) ? 1 : 0; im = flip ? SL11H_INTMASK_XFERDONE_B : SL11H_INTMASK_XFERDONE; if (!(svc_mask & im)) { printk(KERN_ERR "%s: loop=%d(%d), im=%02x, svc_mask=%02x, num_bufs=%d, dev->flip=%08x, flip=%d, buf_map=%x\n", __FUNCTION__, loop, max_loops, im, svc_mask, dev->num_bufs, dev->flip, flip, dev->buf_map); } WARN_ON(!(svc_mask & im)); if (svc_mask & im) { svc_mask &= ~im; handled |= im; ohci_int_status |= finish_xfer(hcd, dev, flip); if (dev->dev_state & DEV_SOF) { if (dev->current_td && dev->buf_len > 0) { process_td(ohci, dev->current_td, flip); } else { process_ed_lists(ohci, flip); } } } else { break; } loop++; BUG_ON(loop > max_loops); } } if (svc_mask) { ohci_warn(ohci, "%s: Unhandled SL811 interrupt svc=%02x, stat=%02x, mask=%02x\n", __FUNCTION__, svc_mask, int_status, int_mask); hc_sl811_dump_regs(dev);#if 0 int_mask &= ~svc_mask;#endif } } while (++loops < max_loops); ohci_int_status &= ~HcIntrStatus; if (ohci_int_status != 0) { HcIntrStatus |= ohci_int_status; if ((HcIntrEnable & OHCI_INTR_MIE) && (ohci_int_status & HcIntrEnable)) { ohci_dbg(ohci, "%s: Calling OHCI interrupt handler: %08x(%08x):%08x\n", __FUNCTION__, HcIntrStatus, ohci_int_status, HcIntrEnable); // call the OHCI IRQ handler if (usb_hcd_irq(hcd->irq, hcd, pt_regs) != IRQ_HANDLED) { ohci_warn(ohci, "%s: Spurious OHCI Interrupt: %08x(%08x):%08x\n", __FUNCTION__, HcIntrStatus, ohci_int_status, HcIntrEnable); } } } mod_timer(&dev->int_timer, jiffies + msecs_to_jiffies(5)); // restore interrupt enable mask __hc_sl811_write_reg(dev, SL11H_INTENBLREG, int_mask); if(!handled) //add by hzh printk(KERN_DEBUG "ohci-sl811:invalid interrupt status mask=0x%x, status=0x%x\n", int_mask_r, int_status_r); return IRQ_HANDLED;//IRQ_RETVAL(handled); //hzh}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -