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

📄 dwc_otg_hcd.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 5 页
字号:
/* ========================================================================== * * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. You are permitted to use and * redistribute this Software in source and binary forms, with or without * modification, provided that redistributions of source code must retain this * notice. You may not view, use, disclose, copy or distribute this file or * any information contained herein except pursuant to this license grant from * Synopsys. If you do not agree with this notice, including the disclaimer * below, then you are not authorized to use the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================== */#ifndef DWC_DEVICE_ONLY/** * @file * * This file contains the implementation of the HCD. In Linux, the HCD * implements the hc_driver API. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/device.h>#include <linux/errno.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/string.h>#include <linux/dma-mapping.h>#include <linux/version.h>#include <linux/platform_device.h>#include <asm/arch/irqs.h>#include "dwc_otg_driver.h"#include "dwc_otg_hcd.h"#include "dwc_otg_regs.h"static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";static const struct hc_driver dwc_otg_hc_driver = {	.description = dwc_otg_hcd_name,	.product_desc = "DWC OTG Controller",	.hcd_priv_size = sizeof(dwc_otg_hcd_t),	.irq = dwc_otg_hcd_irq,	.flags = HCD_MEMORY | HCD_USB2,	//.reset =	.start = dwc_otg_hcd_start,	//.suspend =	//.resume =	.stop = dwc_otg_hcd_stop,	.urb_enqueue = dwc_otg_hcd_urb_enqueue,	.urb_dequeue = dwc_otg_hcd_urb_dequeue,	.endpoint_disable = dwc_otg_hcd_endpoint_disable,	.get_frame_number = dwc_otg_hcd_get_frame_number,	.hub_status_data = dwc_otg_hcd_hub_status_data,	.hub_control = dwc_otg_hcd_hub_control,	//.hub_suspend =	//.hub_resume =};/** * Work queue function for starting the HCD when A-Cable is connected. * The dwc_otg_hcd_start() must be called in a process context. */static void hcd_start_func(#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)				  void *_vp#else				  struct work_struct *_work#endif	){#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)	struct usb_hcd *usb_hcd = (struct usb_hcd *) _vp;#else	struct dwc_otg_hcd *otg_hcd = container_of(_work, struct dwc_otg_hcd, start_work);	struct usb_hcd *usb_hcd = container_of((void *) otg_hcd, struct usb_hcd, hcd_priv);#endif	DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, usb_hcd);	if (usb_hcd) {		dwc_otg_hcd_start(usb_hcd);	}}/** * HCD Callback function for starting the HCD when A-Cable is * connected. * * @param _p void pointer to the <code>struct usb_hcd</code> */static int32_t dwc_otg_hcd_start_cb(void *_p){	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_p);	dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;	hprt0_data_t hprt0;	if (core_if->op_state == B_HOST) {		/*		 * Reset the port.  During a HNP mode switch the reset		 * needs to occur within 1ms and have a duration of at		 * least 50ms.		 */		hprt0.d32 = dwc_otg_read_hprt0(core_if);		hprt0.b.prtrst = 1;		dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);		((struct usb_hcd *) _p)->self.is_b_host = 1;	} else {		((struct usb_hcd *) _p)->self.is_b_host = 0;	}	/* Need to start the HCD in a non-interrupt context. */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)	INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func, _p);#else	INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func);#endif	schedule_work(&dwc_otg_hcd->start_work);	return 1;}/** * HCD Callback function for stopping the HCD. * * @param _p void pointer to the <code>struct usb_hcd</code> */static int32_t dwc_otg_hcd_stop_cb(void *_p){	struct usb_hcd *usb_hcd = (struct usb_hcd *) _p;	DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p);	dwc_otg_hcd_stop(usb_hcd);	return 1;}static void del_xfer_timers(dwc_otg_hcd_t * _hcd){#ifdef DEBUG	int i;	int num_channels = _hcd->core_if->core_params->host_channels;	for (i = 0; i < num_channels; i++) {		del_timer(&_hcd->core_if->hc_xfer_timer[i]);	}#endif}static void del_timers(dwc_otg_hcd_t * _hcd){	del_xfer_timers(_hcd);	del_timer(&_hcd->conn_timer);}/** * Processes all the URBs in a single list of QHs. Completes them with * -ETIMEDOUT and frees the QTD. */static void kill_urbs_in_qh_list(dwc_otg_hcd_t * _hcd, struct list_head *_qh_list){	struct list_head *qh_item;	dwc_otg_qh_t *qh;	struct list_head *qtd_item;	dwc_otg_qtd_t *qtd;	list_for_each(qh_item, _qh_list) {		qh = list_entry(qh_item, dwc_otg_qh_t, qh_list_entry);		for (qtd_item = qh->qtd_list.next;		     qtd_item != &qh->qtd_list; qtd_item = qh->qtd_list.next) {			qtd = list_entry(qtd_item, dwc_otg_qtd_t, qtd_list_entry);			if (qtd->urb != NULL) {				dwc_otg_hcd_complete_urb(_hcd, qtd->urb, -ETIMEDOUT);			}			dwc_otg_hcd_qtd_remove_and_free(qtd);		}	}}/** * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic * and periodic schedules. The QTD associated with each URB is removed from * the schedule and freed. This function may be called when a disconnect is * detected or when the HCD is being stopped. */static void kill_all_urbs(dwc_otg_hcd_t * _hcd){	kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_inactive);	kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_active);	kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_inactive);	kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_ready);	kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_assigned);	kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_queued);}/** * HCD Callback function for disconnect of the HCD. * * @param _p void pointer to the <code>struct usb_hcd</code> */static int32_t dwc_otg_hcd_disconnect_cb(void *_p){	gintsts_data_t intr;	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_p);	//DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p);	/*	 * Set status flags for the hub driver.	 */	dwc_otg_hcd->flags.b.port_connect_status_change = 1;	dwc_otg_hcd->flags.b.port_connect_status = 0;	/*	 * Shutdown any transfers in process by clearing the Tx FIFO Empty	 * interrupt mask and status bits and disabling subsequent host	 * channel interrupts.	 */	intr.d32 = 0;	intr.b.nptxfempty = 1;	intr.b.ptxfempty = 1;	intr.b.hcintr = 1;	dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, intr.d32, 0);	dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintsts, intr.d32, 0);	del_timers(dwc_otg_hcd);	/*	 * Turn off the vbus power only if the core has transitioned to device	 * mode. If still in host mode, need to keep power on to detect a	 * reconnection.	 */	if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) {		if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) {			hprt0_data_t hprt0 = {.d32 = 0 };			DWC_PRINT("Disconnect: PortPower off\n");			hprt0.b.prtpwr = 0;			dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32);		}		dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if);	}	/* Respond with an error status to all URBs in the schedule. */	kill_all_urbs(dwc_otg_hcd);	if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) {		/* Clean up any host channels that were in use. */		int num_channels;		int i;		dwc_hc_t *channel;		dwc_otg_hc_regs_t *hc_regs;		hcchar_data_t hcchar;		num_channels = dwc_otg_hcd->core_if->core_params->host_channels;		if (!dwc_otg_hcd->core_if->dma_enable) {			/* Flush out any channel requests in slave mode. */			for (i = 0; i < num_channels; i++) {				channel = dwc_otg_hcd->hc_ptr_array[i];				if (list_empty(&channel->hc_list_entry)) {					hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i];					hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);					if (hcchar.b.chen) {						hcchar.b.chen = 0;						hcchar.b.chdis = 1;						hcchar.b.epdir = 0;						dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);					}				}			}		}		for (i = 0; i < num_channels; i++) {			channel = dwc_otg_hcd->hc_ptr_array[i];			if (list_empty(&channel->hc_list_entry)) {				hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i];				hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);				if (hcchar.b.chen) {					/* Halt the channel. */					hcchar.b.chdis = 1;					dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);				}				dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, channel);				list_add_tail(&channel->hc_list_entry, &dwc_otg_hcd->free_hc_list);			}		}	}	/* A disconnect will end the session so the B-Device is no	 * longer a B-host. */	((struct usb_hcd *) _p)->self.is_b_host = 0;	return 1;}/** * Connection timeout function.  An OTG host is required to display a * message if the device does not connect within 10 seconds. */void dwc_otg_hcd_connect_timeout(unsigned long _ptr){	DWC_DEBUGPL(DBG_HCDV, "%s(%x)\n", __func__, (int) _ptr);	DWC_PRINT("Connect Timeout\n");	DWC_ERROR("Device Not Connected/Responding\n");}/** * Start the connection timer.  An OTG host is required to display a * message if the device does not connect within 10 seconds.  The * timer is deleted if a port connect interrupt occurs before the * timer expires. */static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t * _hcd){	init_timer(&_hcd->conn_timer);	_hcd->conn_timer.function = dwc_otg_hcd_connect_timeout;	_hcd->conn_timer.data = (unsigned long) 0;	_hcd->conn_timer.expires = jiffies + (HZ * 10);	add_timer(&_hcd->conn_timer);}/** * HCD Callback function for disconnect of the HCD. * * @param _p void pointer to the <code>struct usb_hcd</code> */static int32_t dwc_otg_hcd_session_start_cb(void *_p){	dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_p);	DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p);	dwc_otg_hcd_start_connect_timer(dwc_otg_hcd);	return 1;}/** * HCD Callback structure for handling mode switching. */static dwc_otg_cil_callbacks_t hcd_cil_callbacks = {	.start = dwc_otg_hcd_start_cb,	.stop = dwc_otg_hcd_stop_cb,	.disconnect = dwc_otg_hcd_disconnect_cb,	.session_start = dwc_otg_hcd_session_start_cb,	.p = 0,};/** * Reset tasklet function */static void reset_tasklet_func(unsigned long data){	dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *) data;	dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;	hprt0_data_t hprt0;	dbg_otg("USB RESET tasklet called\n");	hprt0.d32 = dwc_otg_read_hprt0(core_if);	hprt0.b.prtrst = 1;	dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);	mdelay(60);	hprt0.b.prtrst = 0;	dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);	dwc_otg_hcd->flags.b.port_reset_change = 1;	return;}static struct tasklet_struct reset_tasklet = {	.next = NULL,	.state = 0,	.count = ATOMIC_INIT(0),	.func = reset_tasklet_func,	.data = 0,};/** * Initializes the HCD. This function allocates memory for and initializes the * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the * USB bus with the core and calls the hc_driver->start() function. It returns * a negative error on failure. */int dwc_otg_hcd_init (struct platform_device *pdev){	struct usb_hcd *hcd = NULL;	dwc_otg_hcd_t *dwc_otg_hcd = NULL;	dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev);	int num_channels;	int i;	dwc_hc_t *channel;	int retval = 0;	dbg_otg("%s\n", __FUNCTION__);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)	/* 2.6.20+ requires pdev.dma_mask to be set prior to calling usb_create_hcd() */	/* Set device flags indicating whether the HCD supports DMA. */	if (otg_dev->core_if->dma_enable) {		printk("Using DMA mode\n");		pdev->dev.dma_mask = (void *) ~0;		pdev->dev.coherent_dma_mask = ~0;		if (otg_dev->core_if->dma_desc_enable) {			dbg_otg("Device using Descriptor DMA mode\n");		} else {			dbg_otg("Device using Buffer DMA mode\n");		}	} else {		printk("Using Slave mode\n");		pdev->dev.dma_mask = (void *) 0;		pdev->dev.coherent_dma_mask = 0;	}#endif	/*	 * Allocate memory for the base HCD plus the DWC OTG HCD.	 * Initialize the base HCD.	 * 1st contact point of USB Core. by scsuh.	 */	hcd = usb_create_hcd(&dwc_otg_hc_driver, &pdev->dev, pdev->dev.bus_id);

⌨️ 快捷键说明

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