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

📄 dwc_otg_hcd_intr.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 4 页
字号:
/** * Gets the actual length of a transfer after the transfer halts. _halt_status * holds the reason for the halt. * * For IN transfers where _halt_status is DWC_OTG_HC_XFER_COMPLETE, * *_short_read is set to 1 upon return if less than the requested * number of bytes were transferred. Otherwise, *_short_read is set to 0 upon * return. _short_read may also be NULL on entry, in which case it remains * unchanged. */static uint32_t get_actual_xfer_length(dwc_hc_t * _hc,				       dwc_otg_hc_regs_t * _hc_regs,				       dwc_otg_qtd_t * _qtd,				       dwc_otg_halt_status_e _halt_status,				       int *_short_read){	hctsiz_data_t hctsiz;	uint32_t length;	if (_short_read != NULL) {		*_short_read = 0;	}	hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz);	if (_halt_status == DWC_OTG_HC_XFER_COMPLETE) {		if (_hc->ep_is_in) {			length = _hc->xfer_len - hctsiz.b.xfersize;			if (_short_read != NULL) {				*_short_read = (hctsiz.b.xfersize != 0);			}		} else if (_hc->qh->do_split) {			length = _qtd->ssplit_out_xfer_count;		} else {			length = _hc->xfer_len;		}	} else {		/*		 * Must use the hctsiz.pktcnt field to determine how much data		 * has been transferred. This field reflects the number of		 * packets that have been transferred via the USB. This is		 * always an integral number of packets if the transfer was		 * halted before its normal completion. (Can't use the		 * hctsiz.xfersize field because that reflects the number of		 * bytes transferred via the AHB, not the USB).		 */		length = (_hc->start_pkt_count - hctsiz.b.pktcnt) * _hc->max_packet;	}	return length;}/** * Updates the state of the URB after a Transfer Complete interrupt on the * host channel. Updates the actual_length field of the URB based on the * number of bytes transferred via the host channel. Sets the URB status * if the data transfer is finished. * * @return 1 if the data transfer specified by the URB is completely finished, * 0 otherwise. */static int update_urb_state_xfer_comp(dwc_hc_t * _hc,				      dwc_otg_hc_regs_t * _hc_regs,				      struct urb *_urb, dwc_otg_qtd_t * _qtd){	int xfer_done = 0;	int short_read = 0;	_urb->actual_length += get_actual_xfer_length(_hc, _hc_regs, _qtd,						      DWC_OTG_HC_XFER_COMPLETE,						      &short_read);#if 1 //orig	if (short_read || (_urb->actual_length == _urb->transfer_buffer_length)) {		xfer_done = 1;		if (short_read && (_urb->transfer_flags & URB_SHORT_NOT_OK)) {			_urb->status = -EREMOTEIO;		} else {			_urb->status = 0;		}	}#else	if (short_read) {		xfer_done = 1;		if (_urb->transfer_flags & URB_SHORT_NOT_OK) {			_urb->status = -EREMOTEIO;		} else {			_urb->status = 0;		}	}	if (_urb->actual_length == _urb->transfer_buffer_length) {		xfer_done = 1;	}#endif	{		hctsiz_data_t hctsiz;		hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz);		dbg_otg("DWC_otg: %s: %s, channel %d\n",			    __FUNCTION__, (_hc->ep_is_in ? "IN" : "OUT"), _hc->hc_num);		dbg_otg("  hc->xfer_len %d\n", _hc->xfer_len);		dbg_otg("  hctsiz.xfersize %d\n", hctsiz.b.xfersize);		dbg_otg("  urb->transfer_buffer_length %d\n",			    _urb->transfer_buffer_length);		dbg_otg("  urb->actual_length %d\n", _urb->actual_length);		dbg_otg("  short_read %d, xfer_done %d\n", short_read, xfer_done);	}	return xfer_done;}/* * Save the starting data toggle for the next transfer. The data toggle is * saved in the QH for non-control transfers and it's saved in the QTD for * control transfers. */static void save_data_toggle(dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){	hctsiz_data_t hctsiz;	hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz);	if (_hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) {		dwc_otg_qh_t *qh = _hc->qh;		if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {			qh->data_toggle = DWC_OTG_HC_PID_DATA0;		} else {			qh->data_toggle = DWC_OTG_HC_PID_DATA1;		}	} else {		if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {			_qtd->data_toggle = DWC_OTG_HC_PID_DATA0;		} else {			_qtd->data_toggle = DWC_OTG_HC_PID_DATA1;		}	}}/** * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic * QHs, removes the QH from the active non-periodic schedule. If any QTDs are * still linked to the QH, the QH is added to the end of the inactive * non-periodic schedule. For periodic QHs, removes the QH from the periodic * schedule if no more QTDs are linked to the QH. */static void deactivate_qh(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh, int free_qtd){	int continue_split = 0;	dwc_otg_qtd_t *qtd;	DWC_DEBUGPL(DBG_HCDV, "  %s(%p,%p,%d)\n", __func__, _hcd, _qh, free_qtd);	qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry);	if (qtd->complete_split) {		continue_split = 1;	} else if ((qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID) ||		   (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END)) {		continue_split = 1;	}	if (free_qtd) {		dwc_otg_hcd_qtd_remove_and_free(qtd);		continue_split = 0;	}	_qh->channel = NULL;	_qh->qtd_in_process = NULL;	dwc_otg_hcd_qh_deactivate(_hcd, _qh, continue_split);}/** * Updates the state of an Isochronous URB when the transfer is stopped for * any reason. The fields of the current entry in the frame descriptor array * are set based on the transfer state and the input _halt_status. Completes * the Isochronous URB if all the URB frames have been completed. * * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE. */static dwc_otg_halt_status_eupdate_isoc_urb_state(dwc_otg_hcd_t * _hcd,		      dwc_hc_t * _hc,		      dwc_otg_hc_regs_t * _hc_regs,		      dwc_otg_qtd_t * _qtd, dwc_otg_halt_status_e _halt_status){	struct urb *urb = _qtd->urb;	dwc_otg_halt_status_e ret_val = _halt_status;	struct usb_iso_packet_descriptor *frame_desc;	frame_desc = &urb->iso_frame_desc[_qtd->isoc_frame_index];	switch (_halt_status) {	case DWC_OTG_HC_XFER_COMPLETE:		frame_desc->status = 0;		frame_desc->actual_length =			get_actual_xfer_length(_hc, _hc_regs, _qtd, _halt_status, NULL);		break;	case DWC_OTG_HC_XFER_FRAME_OVERRUN:		urb->error_count++;		if (_hc->ep_is_in) {			frame_desc->status = -ENOSR;		} else {			frame_desc->status = -ECOMM;		}		frame_desc->actual_length = 0;		break;	case DWC_OTG_HC_XFER_BABBLE_ERR:		urb->error_count++;		frame_desc->status = -EOVERFLOW;		/* Don't need to update actual_length in this case. */		break;	case DWC_OTG_HC_XFER_XACT_ERR:		urb->error_count++;		frame_desc->status = -EPROTO;		frame_desc->actual_length =			get_actual_xfer_length(_hc, _hc_regs, _qtd, _halt_status, NULL);	default:		DWC_ERROR("%s: Unhandled _halt_status (%d)\n", __func__, _halt_status);		BUG();		break;	}	if (++_qtd->isoc_frame_index == urb->number_of_packets) {		/*		 * urb->status is not used for isoc transfers.		 * The individual frame_desc statuses are used instead.		 */		dwc_otg_hcd_complete_urb(_hcd, urb, 0);		ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;	} else {		ret_val = DWC_OTG_HC_XFER_COMPLETE;	}	return ret_val;}/** * Releases a host channel for use by other transfers. Attempts to select and * queue more transactions since at least one host channel is available. * * @param _hcd The HCD state structure. * @param _hc The host channel to release. * @param _qtd The QTD associated with the host channel. This QTD may be freed * if the transfer is complete or an error has occurred. * @param _halt_status Reason the channel is being released. This status * determines the actions taken by this function. */static void release_channel(dwc_otg_hcd_t * _hcd,			    dwc_hc_t * _hc,			    dwc_otg_qtd_t * _qtd, dwc_otg_halt_status_e _halt_status){	dwc_otg_transaction_type_e tr_type;	int free_qtd;	DWC_DEBUGPL(DBG_HCDV, "  %s: channel %d, halt_status %d\n",		    __func__, _hc->hc_num, _halt_status);	switch (_halt_status) {	case DWC_OTG_HC_XFER_URB_COMPLETE:		free_qtd = 1;		break;	case DWC_OTG_HC_XFER_AHB_ERR:	case DWC_OTG_HC_XFER_STALL:	case DWC_OTG_HC_XFER_BABBLE_ERR:		free_qtd = 1;		break;	case DWC_OTG_HC_XFER_XACT_ERR:		if (_qtd->error_count >= 3) {			DWC_DEBUGPL(DBG_HCDV, "  Complete URB with transaction error\n");			free_qtd = 1;			_qtd->urb->status = -EPROTO;			dwc_otg_hcd_complete_urb(_hcd, _qtd->urb, -EPROTO);		} else {			free_qtd = 0;		}		break;	case DWC_OTG_HC_XFER_URB_DEQUEUE:		/*		 * The QTD has already been removed and the QH has been		 * deactivated. Don't want to do anything except release the		 * host channel and try to queue more transfers.		 */		goto cleanup;	case DWC_OTG_HC_XFER_NO_HALT_STATUS:		DWC_ERROR("%s: No halt_status, channel %d\n", __func__, _hc->hc_num);		free_qtd = 0;		break;	default:		free_qtd = 0;		break;	}	deactivate_qh(_hcd, _hc->qh, free_qtd);      cleanup:	/*	 * Release the host channel for use by other transfers. The cleanup	 * function clears the channel interrupt enables and conditions, so	 * there's no need to clear the Channel Halted interrupt separately.	 */	dwc_otg_hc_cleanup(_hcd->core_if, _hc);	list_add_tail(&_hc->hc_list_entry, &_hcd->free_hc_list);	switch (_hc->ep_type) {	case DWC_OTG_EP_TYPE_CONTROL:	case DWC_OTG_EP_TYPE_BULK:		_hcd->non_periodic_channels--;		break;	default:		/*		 * Don't release reservations for periodic channels here.		 * That's done when a periodic transfer is descheduled (i.e.		 * when the QH is removed from the periodic schedule).		 */		break;	}	/* Try to queue more transfers now that there's a free channel. */	tr_type = dwc_otg_hcd_select_transactions(_hcd);	if (tr_type != DWC_OTG_TRANSACTION_NONE) {		dwc_otg_hcd_queue_transactions(_hcd, tr_type);	}}/** * Halts a host channel. If the channel cannot be halted immediately because * the request queue is full, this function ensures that the FIFO empty * interrupt for the appropriate queue is enabled so that the halt request can * be queued when there is space in the request queue. * * This function may also be called in DMA mode. In that case, the channel is * simply released since the core always halts the channel automatically in * DMA mode. */static void halt_channel(dwc_otg_hcd_t * _hcd,			 dwc_hc_t * _hc, dwc_otg_qtd_t * _qtd, dwc_otg_halt_status_e _halt_status){	if (_hcd->core_if->dma_enable) {		release_channel(_hcd, _hc, _qtd, _halt_status);		return;	}	/* Slave mode processing... */	dwc_otg_hc_halt(_hcd->core_if, _hc, _halt_status);	if (_hc->halt_on_queue) {		gintmsk_data_t gintmsk = {.d32 = 0 };		dwc_otg_core_global_regs_t *global_regs;		global_regs = _hcd->core_if->core_global_regs;		if (_hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || _hc->ep_type == DWC_OTG_EP_TYPE_BULK) {			/*			 * Make sure the Non-periodic Tx FIFO empty interrupt			 * is enabled so that the non-periodic schedule will			 * be processed.			 */			gintmsk.b.nptxfempty = 1;			dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32);		} else {			/*			 * Move the QH from the periodic queued schedule to			 * the periodic assigned schedule. This allows the			 * halt to be queued when the periodic schedule is			 * processed.			 */			list_move(&_hc->qh->qh_list_entry, &_hcd->periodic_sched_assigned);			/*			 * Make sure the Periodic Tx FIFO Empty interrupt is			 * enabled so that the periodic schedule will be			 * processed.			 */			gintmsk.b.ptxfempty = 1;			dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32);		}	}}/** * Performs common cleanup for non-periodic transfers after a Transfer * Complete interrupt. This function should be called after any endpoint type * specific handling is finished to release the host channel. */static void complete_non_periodic_xfer(dwc_otg_hcd_t * _hcd,				       dwc_hc_t * _hc,				       dwc_otg_hc_regs_t * _hc_regs,				       dwc_otg_qtd_t * _qtd,				       dwc_otg_halt_status_e _halt_status){	hcint_data_t hcint;	_qtd->error_count = 0;	hcint.d32 = dwc_read_reg32(&_hc_regs->hcint);	if (hcint.b.nyet) {		/*		 * Got a NYET on the last transaction of the transfer. This		 * means that the endpoint should be in the PING state at the		 * beginning of the next transfer.		 */		_hc->qh->ping_state = 1;		clear_hc_int(_hc_regs, nyet);	}	/*	 * Always halt and release the host channel to make it available for	 * more transfers. There may still be more phases for a control	 * transfer or more data packets for a bulk transfer at this point,	 * but the host channel is still halted. A channel will be reassigned	 * to the transfer when the non-periodic schedule is processed after	 * the channel is released. This allows transactions to be queued	 * properly via dwc_otg_hcd_queue_transactions, which also enables the	 * Tx FIFO Empty interrupt if necessary.	 */	if (_hc->ep_is_in) {		/*		 * IN transfers in Slave mode require an explicit disable to		 * halt the channel. (In DMA mode, this call simply releases		 * the channel.)		 */		halt_channel(_hcd, _hc, _qtd, _halt_status);	} else {		/*		 * The channel is automatically disabled by the core for OUT		 * transfers in Slave mode.		 */		release_channel(_hcd, _hc, _qtd, _halt_status);	}}/** * Performs common cleanup for periodic transfers after a Transfer Complete * interrupt. This function should be called after any endpoint type specific * handling is finished to release the host channel. */static void complete_periodic_xfer(dwc_otg_hcd_t * _hcd,				   dwc_hc_t * _hc,				   dwc_otg_hc_regs_t * _hc_regs,				   dwc_otg_qtd_t * _qtd, dwc_otg_halt_status_e _halt_status){	hctsiz_data_t hctsiz;	_qtd->error_count = 0;

⌨️ 快捷键说明

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