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

📄 usb-ohci.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	}	 		return 0;}/*-------------------------------------------------------------------------*//* 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 * ed) {	int int_branch;	int i;	int inter;	int interval;	__u32 * ed_p;	ed->hwINFO |= cpu_to_le32 (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 (le32_to_cpup (&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 *) bus_to_virt (le32_to_cpup (&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 (le32_to_cpup (&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 *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;		}		break;      	case PIPE_INTERRUPT:		int_branch = ed->int_branch;		interval = ed->int_interval;		for (i = 0; i < ep_rev (6, interval); i += inter) {			for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1; 				(*ed_p != 0) && (*ed_p != ed->hwNextED); 				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED), 				inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) {									if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {			  			*ed_p = ed->hwNextED;					  			break;			  		}			  }		}		for (i = int_branch; i < 32; i += interval)		    ohci->ohci_int_load[i] -= ed->int_load;#ifdef DEBUG		ep_print_int_eds (ohci, "UNLINK_INT");#endif		break;			case PIPE_ISOCHRONOUS:		if (ohci->ed_isotail == ed)			ohci->ed_isotail = ed->ed_prev;		if (ed->hwNextED != 0) 		    ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;				    		if (ed->ed_prev != NULL) {			ed->ed_prev->hwNextED = ed->hwNextED;		} else {			for (i = 0; i < 32; i++) {				for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); 						*ed_p != 0; 						ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) {					// inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);					if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) {						*ed_p = ed->hwNextED;								break;					}				}			}			}	#ifdef DEBUG		ep_print_int_eds (ohci, "UNLINK_ISO");#endif		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 int pipe, int interval, int load){   	ohci_t * ohci = usb_dev->bus->hcpriv;	td_t * td;	ed_t * ed_ret;	volatile ed_t * ed; 	unsigned long flags; 	 		spin_lock_irqsave (&usb_ed_lock, flags);	ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | 			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {		/* pending delete request */		spin_unlock_irqrestore (&usb_ed_lock, flags);		return NULL;	}		if (ed->state == ED_NEW) {		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */  		/* dummy td; end of td list for ed */		td = td_alloc (ohci);  		if (!td) {			/* out of memory */			spin_unlock_irqrestore (&usb_ed_lock, flags);			return NULL;		}		ed->hwTailP = cpu_to_le32 (virt_to_bus (td));		ed->hwHeadP = ed->hwTailP;			ed->state = ED_UNLINK;		ed->type = usb_pipetype (pipe);		usb_to_ohci (usb_dev)->ed_cnt++;	}	ohci->dev[usb_pipedevice (pipe)] = usb_dev;		ed->hwINFO = cpu_to_le32 (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, usb_pipeout (pipe)) << 16);    	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {  		ed->int_period = interval;  		ed->int_load = load;  	}  		spin_unlock_irqrestore (&usb_ed_lock, flags);	return ed_ret; }/*-------------------------------------------------------------------------*/ /* request the removal of an endpoint * put the ep on the rm_list and request a stop of the bulk or ctrl list  * real removal is done at the next start frame (SF) hardware interrupt */ static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed){    	unsigned int frame;	ohci_t * ohci = usb_dev->bus->hcpriv;	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))		return;		ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);	if (!ohci->disabled) {		switch (ed->type) {			case PIPE_CONTROL: /* stop control list */				ohci->hc_control &= ~OHCI_CTRL_CLE;				writel (ohci->hc_control, &ohci->regs->control);   				break;			case PIPE_BULK: /* stop bulk list */				ohci->hc_control &= ~OHCI_CTRL_BLE;				writel (ohci->hc_control, &ohci->regs->control); 				break;		}	}	frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;	ed->ed_rm_list = ohci->ed_rm_list[frame];	ohci->ed_rm_list[frame] = ed;	if (!ohci->disabled) {		/* enable SOF interrupt */		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);		writel (OHCI_INTR_SF, &ohci->regs->intrenable);	}}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* prepare a TD */static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index){	volatile td_t  * td, * td_pt;	urb_priv_t * urb_priv = urb->hcpriv;	if (index >= urb_priv->length) {		err("internal OHCI error: TD index > length");		return;	}		td_pt = urb_priv->td [index];	/* fill the old dummy TD */	td = urb_priv->td [index] = (td_t *)		bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);	td->ed = urb_priv->ed;	td->next_dl_td = NULL;	td->index = index;	td->urb = urb; 	td->hwINFO = cpu_to_le32 (info);	if ((td->ed->type) == PIPE_ISOCHRONOUS) {		td->hwCBP = cpu_to_le32 (((!data || !len)			? 0			: virt_to_bus (data)) & 0xFFFFF000);		td->ed->last_iso = info & 0xffff;	} else {		td->hwCBP = cpu_to_le32 (((!data || !len)			? 0			: virt_to_bus (data))); 	}				td->hwBE = cpu_to_le32 ((!data || !len )		? 0		: virt_to_bus (data + len - 1));	td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));	td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);	td_pt->hwNextTD = 0;	td->ed->hwTailP = td->hwNextTD;}/*-------------------------------------------------------------------------*/ /* prepare all TDs of a transfer */static void td_submit_urb (urb_t * urb){ 	urb_priv_t * urb_priv = urb->hcpriv;	ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;	void * ctrl = urb->setup_packet;	void * data = urb->transfer_buffer;	int data_len = urb->transfer_buffer_length;	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(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {  		toggle = TD_T_TOGGLE;	} else {  		toggle = TD_T_DATA0;		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);	}		urb_priv->td_cnt = 0;		switch (usb_pipetype (urb->pipe)) {		case PIPE_BULK:			info = usb_pipeout (urb->pipe)? 				TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;			while(data_len > 4096) {						td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);				data += 4096; data_len -= 4096; cnt++;			}			info = usb_pipeout (urb->pipe)?				TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;			td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);			cnt++;			writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk 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 (info, data, data_len, urb, cnt++);			break;		case PIPE_CONTROL:			info = TD_CC | TD_DP_SETUP | TD_T_DATA0;			td_fill (info, ctrl, 8, urb, cnt++); 			if (data_len > 0) {  				info = usb_pipeout (urb->pipe)? 					TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;				td_fill (info, data, data_len, urb, cnt++);  			} 			info = usb_pipeout (urb->pipe)?  				TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;			td_fill (info, NULL, 0, urb, cnt++);			writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */			break;		case PIPE_ISOCHRONOUS:			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {				td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff),  					(__u8 *) data + urb->iso_frame_desc[cnt].offset, 					urb->iso_frame_desc[cnt].length, urb, cnt); 			}			break;	} 	if (urb_priv->length != cnt) 		dbg("TD LENGTH %d != CNT %d", urb_priv->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; 	__u16 tdPSW; 	urb_t * urb = td->urb; 	urb_priv_t * urb_priv = urb->hcpriv;	int dlen = 0;	int cc = 0;		tdINFO = le32_to_cpup (&td->hwINFO);  	tdBE   = le32_to_cpup (&td->hwBE);  	tdCBP  = le32_to_cpup (&td->hwCBP);  	if (tdINFO & TD_ISO) { 		tdPSW = le16_to_cpu (td->hwPSW[0]); 		cc = (tdPSW >> 12) & 0xF;		if (cc < 0xE)  {			if (usb_pipeout(urb->pipe)) {				dlen = urb->iso_frame_desc[td->index].length;			} else {				dlen = tdPSW & 0x3ff;			}			urb->actual_length += dlen;			urb->iso_frame_desc[td->index].actual_length = dlen;			if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))				cc = TD_CC_NOERROR;					 			urb->iso_frame_desc[td->index].status = cc_to_error[cc];		}	} else { /* BULK, INT, CONTROL DATA */		if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && 				((td->index == 0) || (td->index == urb_priv->length - 1)))) { 			if (tdBE != 0) { 				if (td->hwCBP == 0)					urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1;  				else					urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer;			}  		}  	}}/* handle an urb that is being unlinked */static void dl_del_urb (urb_t * urb){	wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;	urb_rm_priv_locked (urb);	if (urb->transfer_flags & USB_ASYNC_UNLINK) {		urb->status = -ECONNRESET;		if (urb->complete)			urb->complete (urb);	} else {		urb->status = -ENOENT;		/* unblock sohci_unlink_urb */		if (wait_head)			wake_up (wait_head);	}}/*-------------------------------------------------------------------------*//* replies to the request have to be on a FIFO basis so * we reverse the reversed done-list */ static td_t * dl_reverse_done_list (ohci_t * ohci){	__u32 td_list_hc;	td_t * td_rev = NULL;	td_t * td_list = NULL;  	urb_priv_t * urb_priv = NULL;  	unsigned long flags;  	  	spin_lock_irqsave (&usb_ed_lock, flags);  		td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0;	ohci->hcca.done_head = 0;		while (td_list_hc) {				td_list = (td_t *) bus_to_virt (td_list_hc);		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;			dbg(" USB-error/status: %x : %p", 					TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);			if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {				if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {					td_list->ed->hwHeadP = 						(urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |									(td_list->ed->hwHeadP & cpu_to_le32 (0x2));					urb_priv->td_cnt += urb_priv->length - td_list->index - 1;				} else 					td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);			}		}		td_list->next_dl_td = td_rev;			td_rev = td_list;		td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;		}		spin_unlock_irqrestore (&usb_ed_lock, flags);	return td_list;}/*-------------------------------------------------------------------------*//* there are some pending requests to remove  * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev) * - some URBs/TDs if urb_priv->state == URB_DEL */ static void dl_del_list (ohci_t  * ohci, unsigned int frame){	unsigned long flags;	ed_t * ed;	__u32 edINFO;	__u32 tdINFO;	td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;	__u32 * td_p;	int ctrl = 0, bulk = 0;	spin_lock_irqsave (&usb_ed_lock, flags);	for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {   	 	tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);		tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);		edINFO = le32_to_cpup (&ed->hwINFO);		td_p = &ed->hwHeadP;		for (td = tdHeadP; td != tdTailP; td = td_next) { 			urb_t * urb = td->urb;			urb_priv_t * urb_priv = td->urb->hcpriv;						td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0);			if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {				tdINFO = le32_to_cpup (&td->hwINFO);				if (TD_CC_GET (tdINFO) < 0xE)

⌨️ 快捷键说明

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