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

📄 usb_ohci.c

📁 U-boot源码 ARM7启动代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	/* allocate the TDs */	/* note that td[0] was allocated in ep_add_ed */	for (i = 0; i < size; i++) {		purb_priv->td[i] = td_alloc (dev);		if (!purb_priv->td[i]) {			purb_priv->length = i;			urb_free_priv (purb_priv);			err("sohci_submit_job: ENOMEM");			return -1;		}	}	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {		urb_free_priv (purb_priv);		err("sohci_submit_job: EINVAL");		return -1;	}	/* link the ed into a chain if is not already */	if (ed->state != ED_OPER)		ep_link (ohci, ed);	/* fill the TDs and link it to the ed */	td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval);	return 0;}static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb){	struct ohci_regs *regs = hc->regs;	switch (usb_pipetype (urb->pipe)) {	case PIPE_INTERRUPT:		/* implicitly requeued */		if (urb->dev->irq_handle &&				(urb->dev->irq_act_len = urb->actual_length)) {			writel (OHCI_INTR_WDH, &regs->intrenable);			readl (&regs->intrenable); /* PCI posting flush */			urb->dev->irq_handle(urb->dev);			writel (OHCI_INTR_WDH, &regs->intrdisable);			readl (&regs->intrdisable); /* PCI posting flush */		}		urb->actual_length = 0;		td_submit_job (				urb->dev,				urb->pipe,				urb->transfer_buffer,				urb->transfer_buffer_length,				NULL,				urb,				urb->interval);		break;	case PIPE_CONTROL:	case PIPE_BULK:		break;	default:		return 0;	}	return 1;}/*-------------------------------------------------------------------------*/#ifdef DEBUG/* tell us the current USB frame number */static int sohci_get_current_frame_number (struct usb_device *usb_dev){	ohci_t *ohci = &gohci;	return m16_swap (ohci->hcca->frame_no);}#endif/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*//* search for the right branch to insert an interrupt ed into the int tree * do some load ballancing; * returns the branch and * sets the interval to interval = 2^integer (ld (interval)) */static int ep_int_ballance (ohci_t * ohci, int interval, int load){	int i, branch = 0;	/* search for the least loaded interrupt endpoint	 * branch of all 32 branches	 */	for (i = 0; i < 32; i++)		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])			branch = i;	branch = branch % interval;	for (i = branch; i < 32; i += interval)		ohci->ohci_int_load [i] += load;	return branch;}/*-------------------------------------------------------------------------*//*  2^int( ld (inter)) */static int ep_2_n_interval (int inter){	int i;	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);	return 1 << i;}/*-------------------------------------------------------------------------*//* the int tree is a binary tree * in order to process it sequentially the indexes of the branches have to be mapped * the mapping reverses the bits of a word of num_bits length */static int ep_rev (int num_bits, int word){	int i, wout = 0;	for (i = 0; i < num_bits; i++)		wout |= (((word >> i) & 1) << (num_bits - i - 1));	return wout;}/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*//* link an ed into one of the HC chains */static int ep_link (ohci_t *ohci, ed_t *edi){	volatile ed_t *ed = edi;	int int_branch;	int i;	int inter;	int interval;	int load;	__u32 * ed_p;	ed->state = ED_OPER;	ed->int_interval = 0;	switch (ed->type) {	case PIPE_CONTROL:		ed->hwNextED = 0;		if (ohci->ed_controltail == NULL) {			writel (ed, &ohci->regs->ed_controlhead);		} else {			ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed);		}		ed->ed_prev = ohci->ed_controltail;		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&			!ohci->ed_rm_list[1] && !ohci->sleeping) {			ohci->hc_control |= OHCI_CTRL_CLE;			writel (ohci->hc_control, &ohci->regs->control);		}		ohci->ed_controltail = edi;		break;	case PIPE_BULK:		ed->hwNextED = 0;		if (ohci->ed_bulktail == NULL) {			writel (ed, &ohci->regs->ed_bulkhead);		} else {			ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed);		}		ed->ed_prev = ohci->ed_bulktail;		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&			!ohci->ed_rm_list[1] && !ohci->sleeping) {			ohci->hc_control |= OHCI_CTRL_BLE;			writel (ohci->hc_control, &ohci->regs->control);		}		ohci->ed_bulktail = edi;		break;	case PIPE_INTERRUPT:		load = ed->int_load;		interval = ep_2_n_interval (ed->int_period);		ed->int_interval = interval;		int_branch = ep_int_ballance (ohci, interval, load);		ed->int_branch = int_branch;		for (i = 0; i < ep_rev (6, interval); i += inter) {			inter = 1;			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);				(*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval);				ed_p = &(((ed_t *)ed_p)->hwNextED))					inter = ep_rev (6, ((ed_t *)ed_p)->int_interval);			ed->hwNextED = *ed_p;			*ed_p = m32_swap((unsigned long)ed);		}		break;	}	return 0;}/*-------------------------------------------------------------------------*//* scan the periodic table to find and unlink this ED */static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed,		unsigned index, unsigned period){	for (; index < NUM_INTS; index += period) {		__u32	*ed_p = &ohci->hcca->int_table [index];		/* ED might have been unlinked through another path */		while (*ed_p != 0) {			if (((struct ed *)m32_swap ((unsigned long)ed_p)) == ed) {				*ed_p = ed->hwNextED;				break;			}			ed_p = & (((struct ed *)m32_swap ((unsigned long)ed_p))->hwNextED);		}	}}/* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 * so the HC can eventually finish the processing of the unlinked ed */static int ep_unlink (ohci_t *ohci, ed_t *edi){	volatile ed_t *ed = edi;	int i;	ed->hwINFO |= m32_swap (OHCI_ED_SKIP);	switch (ed->type) {	case PIPE_CONTROL:		if (ed->ed_prev == NULL) {			if (!ed->hwNextED) {				ohci->hc_control &= ~OHCI_CTRL_CLE;				writel (ohci->hc_control, &ohci->regs->control);			}			writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead);		} else {			ed->ed_prev->hwNextED = ed->hwNextED;		}		if (ohci->ed_controltail == ed) {			ohci->ed_controltail = ed->ed_prev;		} else {			((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;		}		break;	case PIPE_BULK:		if (ed->ed_prev == NULL) {			if (!ed->hwNextED) {				ohci->hc_control &= ~OHCI_CTRL_BLE;				writel (ohci->hc_control, &ohci->regs->control);			}			writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead);		} else {			ed->ed_prev->hwNextED = ed->hwNextED;		}		if (ohci->ed_bulktail == ed) {			ohci->ed_bulktail = ed->ed_prev;		} else {			((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;		}		break;	case PIPE_INTERRUPT:		periodic_unlink (ohci, ed, 0, 1);		for (i = ed->int_branch; i < 32; i += ed->int_interval)		    ohci->ohci_int_load[i] -= ed->int_load;		break;	}	ed->state = ED_UNLINK;	return 0;}/*-------------------------------------------------------------------------*//* add/reinit an endpoint; this should be done once at the * usb_set_configuration command, but the USB stack is a little bit * stateless so we do it at every transaction if the state of the ed * is ED_NEW then a dummy td is added and the state is changed to * ED_UNLINK in all other cases the state is left unchanged the ed * info fields are setted anyway even though most of them should not * change */static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe,		int interval, int load){	td_t *td;	ed_t *ed_ret;	volatile ed_t *ed;	ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) |			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))];	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {		err("ep_add_ed: pending delete");		/* pending delete request */		return NULL;	}	if (ed->state == ED_NEW) {		ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */		/* dummy td; end of td list for ed */		td = td_alloc (usb_dev);		ed->hwTailP = m32_swap ((unsigned long)td);		ed->hwHeadP = ed->hwTailP;		ed->state = ED_UNLINK;		ed->type = usb_pipetype (pipe);		ohci_dev.ed_cnt++;	}	ed->hwINFO = m32_swap (usb_pipedevice (pipe)			| usb_pipeendpoint (pipe) << 7			| (usb_pipeisoc (pipe)? 0x8000: 0)			| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))			| usb_pipeslow (pipe) << 13			| usb_maxpacket (usb_dev, pipe) << 16);	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {		ed->int_period = interval;		ed->int_load = load;	}	return ed_ret;}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* enqueue next TD for this URB (OHCI spec 5.2.8.2) */static void td_fill (ohci_t *ohci, unsigned int info,	void *data, int len,	struct usb_device *dev, int index, urb_priv_t *urb_priv){	volatile td_t  *td, *td_pt;#ifdef OHCI_FILL_TRACE	int i;#endif	if (index > urb_priv->length) {		err("index > length");		return;	}	/* use this td as the next dummy */	td_pt = urb_priv->td [index];	td_pt->hwNextTD = 0;	/* fill the old dummy TD */	td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf);	td->ed = urb_priv->ed;	td->next_dl_td = NULL;	td->index = index;	td->data = (__u32)data;#ifdef OHCI_FILL_TRACE	if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) {		for (i = 0; i < len; i++)		printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);		printf("\n");	}#endif	if (!len)		data = 0;	td->hwINFO = m32_swap (info);	td->hwCBP = m32_swap ((unsigned long)data);	if (data)		td->hwBE = m32_swap ((unsigned long)(data + len - 1));	else		td->hwBE = 0;	td->hwNextTD = m32_swap ((unsigned long)td_pt);	/* append to queue */	td->ed->hwTailP = td->hwNextTD;}/*-------------------------------------------------------------------------*//* prepare all TDs of a transfer */static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer,	int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval){	ohci_t *ohci = &gohci;	int data_len = transfer_len;	void *data;	int cnt = 0;	__u32 info = 0;	unsigned int toggle = 0;	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */	if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {		toggle = TD_T_TOGGLE;	} else {		toggle = TD_T_DATA0;		usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);	}	urb->td_cnt = 0;	if (data_len)		data = buffer;	else		data = 0;	switch (usb_pipetype (pipe)) {	case PIPE_BULK:		info = usb_pipeout (pipe)?			TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;		while(data_len > 4096) {			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb);			data += 4096; data_len -= 4096; cnt++;		}		info = usb_pipeout (pipe)?			TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;		td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb);		cnt++;		if (!ohci->sleeping)			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */		break;	case PIPE_CONTROL:		info = TD_CC | TD_DP_SETUP | TD_T_DATA0;		td_fill (ohci, info, setup, 8, dev, cnt++, urb);		if (data_len > 0) {			info = usb_pipeout (pipe)?				TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;			/* NOTE:  mishandles transfers >8K, some >4K */			td_fill (ohci, info, data, data_len, dev, cnt++, urb);		}		info = usb_pipeout (pipe)?			TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;		td_fill (ohci, info, data, 0, dev, cnt++, urb);		if (!ohci->sleeping)			writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */		break;	case PIPE_INTERRUPT:		info = usb_pipeout (urb->pipe)?			TD_CC | TD_DP_OUT | toggle:			TD_CC | TD_R | TD_DP_IN | toggle;		td_fill (ohci, info, data, data_len, dev, cnt++, urb);		break;	}	if (urb->length != cnt)		dbg("TD LENGTH %d != CNT %d", urb->length, cnt);}/*-------------------------------------------------------------------------* * Done List handling functions *-------------------------------------------------------------------------*//* calculate the transfer length and update the urb */static void dl_transfer_length(td_t * td){	__u32 tdINFO, tdBE, tdCBP;	urb_priv_t *lurb_priv = td->ed->purb;	tdINFO = m32_swap (td->hwINFO);	tdBE   = m32_swap (td->hwBE);	tdCBP  = m32_swap (td->hwCBP);	if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL &&	    ((td->index == 0) || (td->index == lurb_priv->length - 1)))) {		if (tdBE != 0) {			if (td->hwCBP == 0)				lurb_priv->actual_length += tdBE - td->data + 1;			else				lurb_priv->actual_length += tdCBP - td->data;		}	}}/*-------------------------------------------------------------------------*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -