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

📄 dwc_otg_pcd.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 4 页
字号:
 /* ==========================================================================  *  * 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_HOST_ONLY#if 0/** @file * This file implements the Peripheral Controller Driver. * * The Peripheral Controller Driver (PCD) is responsible for * translating requests from the Function Driver into the appropriate * actions on the DWC_otg controller. It isolates the Function Driver * from the specifics of the controller by providing an API to the * Function Driver. * * The Peripheral Controller Driver for Linux will implement the * Gadget API, so that the existing Gadget drivers can be used. * (Gadget Driver is the Linux terminology for a Function Driver.) * * The Linux Gadget API is defined in the header file * <code><linux/usb_gadget.h></code>.  The USB EP operations API is * defined in the structure <code>usb_ep_ops</code> and the USB * Controller API is defined in the structure * <code>usb_gadget_ops</code>. * * An important function of the PCD is managing interrupts generated * by the DWC_otg controller. The implementation of the DWC_otg device * mode interrupt service routines is in dwc_otg_pcd_intr.c. * * @todo Add Device Mode test modes (Test J mode, Test K mode, etc). * @todo Does it work when the request size is greater than DEPTSIZ * transfer size * */#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 <asm/arch/lm.h>#include <asm/arch/irqs.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)# include <linux/usb/ch9.h>#else# include <linux/usb_ch9.h>#endif#include <linux/usb_gadget.h>#include "dwc_otg_driver.h"#include "dwc_otg_pcd.h"/** * Static PCD pointer for use in usb_gadget_register_driver and * usb_gadget_unregister_driver.  Initialized in dwc_otg_pcd_init. */static dwc_otg_pcd_t *s_pcd = 0;/* Display the contents of the buffer */extern void dump_msg(const u8 * buf, unsigned int length);/** * This function completes a request.  It call's the request call back. */void request_done(dwc_otg_pcd_ep_t * _ep, dwc_otg_pcd_request_t * _req, int _status){	unsigned stopped = _ep->stopped;	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _ep);	list_del_init(&_req->queue);	if (_req->req.status == -EINPROGRESS) {		_req->req.status = _status;	} else {		_status = _req->req.status;	}	/* don't modify queue heads during completion callback */	_ep->stopped = 1;	SPIN_UNLOCK(&_ep->pcd->lock);	_req->req.complete(&_ep->ep, &_req->req);	SPIN_LOCK(&_ep->pcd->lock);	if (_ep->pcd->request_pending > 0) {		--_ep->pcd->request_pending;	}	_ep->stopped = stopped;}/** * This function terminates all the requsts in the EP request queue. */void request_nuke(dwc_otg_pcd_ep_t * _ep){	dwc_otg_pcd_request_t *req;	_ep->stopped = 1;	/* called with irqs blocked?? */	while (!list_empty(&_ep->queue)) {		req = list_entry(_ep->queue.next, dwc_otg_pcd_request_t, queue);		request_done(_ep, req, -ESHUTDOWN);	}}/* USB Endpoint Operations *//* * The following sections briefly describe the behavior of the Gadget * API endpoint operations implemented in the DWC_otg driver * software. Detailed descriptions of the generic behavior of each of * these functions can be found in the Linux header file * include/linux/usb_gadget.h. * * The Gadget API provides wrapper functions for each of the function * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper * function, which then calls the underlying PCD function. The * following sections are named according to the wrapper * functions. Within each section, the corresponding DWC_otg PCD * function name is specified. * *//** * This function assigns periodic Tx FIFO to an periodic EP * in shared Tx FIFO mode */static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if){	uint32_t PerTxMsk = 1;	int i;	for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) {		if ((PerTxMsk & core_if->p_tx_msk) == 0) {			core_if->p_tx_msk |= PerTxMsk;			return i + 1;		}		PerTxMsk <<= 1;	}	return 0;}/** * This function releases periodic Tx FIFO * in shared Tx FIFO mode */static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num){	core_if->p_tx_msk = (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk;}/** * This function assigns periodic Tx FIFO to an periodic EP * in shared Tx FIFO mode */static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if){	uint32_t TxMsk = 1;	int i;	for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) {		if ((TxMsk & core_if->tx_msk) == 0) {			core_if->tx_msk |= TxMsk;			return i + 1;		}		TxMsk <<= 1;	}	return 0;}/** * This function releases periodic Tx FIFO * in shared Tx FIFO mode */static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num){	core_if->tx_msk = (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk;}/** * This function allocates a DMA Descriptor chain for the Endpoint * buffer to be used for a transfer to/from the specified endpoint. * * @param _ep The endpoint for which is allocated Descriptor chain * @param count The desired number of Descriptors in the chain */static dwc_otg_dma_desc_t *ep_alloc_desc(uint32_t * dma_desc_addr){	return dma_alloc_coherent(NULL, sizeof(dwc_otg_dma_desc_t), dma_desc_addr, GFP_KERNEL);}/** * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc. * * @param _ep The endpoint - the owner of the Descriptor chain * @param count The desired number of Descriptors in the chain */static void ep_free_desc(dwc_otg_dma_desc_t * desc_addr, uint32_t dma_desc_addr){	dma_free_coherent(NULL, sizeof(dwc_otg_dma_desc_t), desc_addr, dma_desc_addr);}#ifdef _EN_ISOC_/** * This function allocates a DMA Descriptor chain for the Endpoint * buffer to be used for a transfer to/from the specified endpoint. * * @param _ep The endpoint for which is allocated Descriptor chain * @param count The desired number of Descriptors in the chain */static dwc_otg_iso_dma_desc_t *ep_alloc_iso_desc_chain(uint32_t * dma_desc_addr, uint32_t count){	return dma_alloc_coherent(NULL, count * sizeof(dwc_otg_iso_dma_desc_t), dma_desc_addr,				  GFP_KERNEL);}/** * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc. * * @param _ep The endpoint - the owner of the Descriptor chain * @param count The desired number of Descriptors in the chain */static void ep_free_iso_desc_chain(dwc_otg_iso_dma_desc_t * desc_addr, uint32_t dma_desc_addr,				   uint32_t count){	dma_free_coherent(NULL, count * sizeof(dwc_otg_dma_desc_t), desc_addr, dma_desc_addr);}#endif				//_EN_ISOC_/** * This function is called by the Gadget Driver for each EP to be * configured for the current configuration (SET_CONFIGURATION). * * This function initializes the dwc_otg_ep_t data structure, and then * calls dwc_otg_ep_activate. */static int dwc_otg_pcd_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *_desc){	dwc_otg_pcd_ep_t *ep = 0;	dwc_otg_pcd_t *pcd = 0;	unsigned long flags;	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, _ep, _desc);	ep = container_of(_ep, dwc_otg_pcd_ep_t, ep);	if (!_ep || !_desc || ep->desc || _desc->bDescriptorType != USB_DT_ENDPOINT) {		DWC_WARN("%s, bad ep or descriptor\n", __func__);		return -EINVAL;	}	if (ep == &ep->pcd->ep0) {		DWC_WARN("%s, bad ep(0)\n", __func__);		return -EINVAL;	}	/* Check FIFO size? */	if (!_desc->wMaxPacketSize) {		DWC_WARN("%s, bad %s maxpacket\n", __func__, _ep->name);		return -ERANGE;	}	pcd = ep->pcd;	if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) {		DWC_WARN("%s, bogus device state\n", __func__);		return -ESHUTDOWN;	}	SPIN_LOCK_IRQSAVE(&pcd->lock, flags);	ep->desc = _desc;	ep->ep.maxpacket = le16_to_cpu(_desc->wMaxPacketSize);	/*	 * Activate the EP	 */	ep->stopped = 0;	ep->dwc_ep.is_in = (USB_DIR_IN & _desc->bEndpointAddress) != 0;	ep->dwc_ep.maxpacket = ep->ep.maxpacket;	ep->dwc_ep.type = _desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;	if (ep->dwc_ep.is_in) {		if (!pcd->otg_dev->core_if->en_multiple_tx_fifo) {			ep->dwc_ep.tx_fifo_num = 0;			if (ep->dwc_ep.type == USB_ENDPOINT_XFER_ISOC) {				/*				 * if ISOC EP then assign a Periodic Tx FIFO.				 */				ep->dwc_ep.tx_fifo_num =					assign_perio_tx_fifo(pcd->otg_dev->core_if);			}		} else {			/*			 * if Dedicated FIFOs mode is on then assign a Tx FIFO.			 */			ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if);		}	}	/* Set initial data PID. */	if (ep->dwc_ep.type == USB_ENDPOINT_XFER_BULK) {		ep->dwc_ep.data_pid_start = 0;	}	/* Alloc DMA Descriptors */	if (GET_CORE_IF(pcd)->dma_desc_enable) {		if (ep->dwc_ep.type != USB_ENDPOINT_XFER_ISOC) {			ep->dwc_ep.desc_addr = ep_alloc_desc(&ep->dwc_ep.dma_desc_addr);		}	}	DWC_DEBUGPL(DBG_PCD, "Activate %s-%s: type=%d, mps=%d desc=%p\n",		    ep->ep.name, (ep->dwc_ep.is_in ? "IN" : "OUT"),		    ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc);	dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);	SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags);	return 0;}/** * This function is called when an EP is disabled due to disconnect or * change in configuration. Any pending requests will terminate with a * status of -ESHUTDOWN. * * This function modifies the dwc_otg_ep_t data structure for this EP, * and then calls dwc_otg_ep_deactivate. */static int dwc_otg_pcd_ep_disable(struct usb_ep *_ep){	dwc_otg_pcd_ep_t *ep;	dwc_otg_pcd_t *pcd = 0;	unsigned long flags;	dwc_otg_dma_desc_t *desc_addr;	uint32_t dma_desc_addr;	DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _ep);	ep = container_of(_ep, dwc_otg_pcd_ep_t, ep);	if (!_ep || !ep->desc) {		DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__, _ep ? ep->ep.name : NULL);		return -EINVAL;	}	SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags);	request_nuke(ep);	dwc_otg_ep_deactivate(GET_CORE_IF(ep->pcd), &ep->dwc_ep);	ep->desc = 0;	ep->stopped = 1;	if (ep->dwc_ep.is_in) {		dwc_otg_flush_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num);		release_perio_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num);		release_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num);	}	/* Free DMA Descriptors */	pcd = ep->pcd;	if (GET_CORE_IF(pcd)->dma_desc_enable) {		if (ep->dwc_ep.type != USB_ENDPOINT_XFER_ISOC) {			desc_addr = ep->dwc_ep.desc_addr;			dma_desc_addr = ep->dwc_ep.dma_desc_addr;			/* Cannot call dma_free_coherent() with IRQs disabled */			SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags);			ep_free_desc(desc_addr, dma_desc_addr);			goto out_unlocked;		}	}	SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags);      out_unlocked:	DWC_DEBUGPL(DBG_PCD, "%s disabled\n", _ep->name);	return 0;}/** * This function allocates a request object to use with the specified * endpoint. * * @param _ep The endpoint to be used with with the request * @param _gfp_flags the GFP_* flags to use. */static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *_ep,#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)						     int _gfp_flags#else						     gfp_t _gfp_flags#endif	){	dwc_otg_pcd_request_t *req;	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, _ep, _gfp_flags);	if (0 == _ep) {		DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n");		return 0;	}	req = kmalloc(sizeof(dwc_otg_pcd_request_t), _gfp_flags);	if (0 == req) {		DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n");		return 0;	}	memset(req, 0, sizeof(dwc_otg_pcd_request_t));	req->req.dma = DMA_ADDR_INVALID;	INIT_LIST_HEAD(&req->queue);	return &req->req;}/** * This function frees a request object. * * @param _ep The endpoint associated with the request * @param _req The request being freed */static void dwc_otg_pcd_free_request(struct usb_ep *_ep, struct usb_request *_req){	dwc_otg_pcd_request_t *req;	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, _ep, _req);	if (0 == _ep || 0 == _req) {		DWC_WARN("%s() %s\n", __func__, "Invalid ep or req argument!\n");		return;	}	req = container_of(_req, dwc_otg_pcd_request_t, req);	kfree(req);}/** * This function allocates an I/O buffer to be used for a transfer * to/from the specified endpoint. * * @param _ep The endpoint to be used with with the request * @param _bytes The desired number of bytes for the buffer * @param _dma Pointer to the buffer's DMA address; must be valid * @param _gfp_flags the GFP_* flags to use. * @return address of a new buffer or null is buffer could not be allocated. */static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *_ep, unsigned _bytes, dma_addr_t * _dma,#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)				      int _gfp_flags#else				      gfp_t _gfp_flags#endif	){	void *buf;	dwc_otg_pcd_ep_t *ep;	dwc_otg_pcd_t *pcd = 0;	ep = container_of(_ep, dwc_otg_pcd_ep_t, ep);	pcd = ep->pcd;	DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, _ep, _bytes, _dma, _gfp_flags);	/* Check dword alignment */	if ((_bytes & 0x3UL) != 0) {		DWC_WARN("%s() Buffer size is not a multiple of"			 "DWORD size (%d)", __func__, _bytes);	}	if (GET_CORE_IF(pcd)->dma_enable) {		buf = dma_alloc_coherent(NULL, _bytes, _dma, _gfp_flags);	} else {		buf = kmalloc(_bytes, _gfp_flags);	}	/* Check dword alignment */	if (((int) buf & 0x3UL) != 0) {		DWC_WARN("%s() Buffer is not DWORD aligned (%p)", __func__, buf);	}	return buf;}/** * This function frees an I/O buffer that was allocated by alloc_buffer. * * @param _ep the endpoint associated with the buffer * @param _buf address of the buffer * @param _dma The buffer's DMA address * @param _bytes The number of bytes of the buffer */static void dwc_otg_pcd_free_buffer(struct usb_ep *_ep, void *_buf,				    dma_addr_t _dma, unsigned _bytes){	dwc_otg_pcd_ep_t *ep;	dwc_otg_pcd_t *pcd = 0;

⌨️ 快捷键说明

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