⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ohci-sl811-emu.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	} 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 + -