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

📄 atmel_usba_udc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Driver for the Atmel USBA high speed USB device controller * * Copyright (C) 2005-2007 Atmel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/clk.h>#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <linux/list.h>#include <linux/platform_device.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include <linux/delay.h>#include <asm/gpio.h>#include <asm/arch/board.h>#include "atmel_usba_udc.h"static struct usba_udc the_udc;#ifdef CONFIG_USB_GADGET_DEBUG_FS#include <linux/debugfs.h>#include <linux/uaccess.h>static int queue_dbg_open(struct inode *inode, struct file *file){	struct usba_ep *ep = inode->i_private;	struct usba_request *req, *req_copy;	struct list_head *queue_data;	queue_data = kmalloc(sizeof(*queue_data), GFP_KERNEL);	if (!queue_data)		return -ENOMEM;	INIT_LIST_HEAD(queue_data);	spin_lock_irq(&ep->udc->lock);	list_for_each_entry(req, &ep->queue, queue) {		req_copy = kmalloc(sizeof(*req_copy), GFP_ATOMIC);		if (!req_copy)			goto fail;		memcpy(req_copy, req, sizeof(*req_copy));		list_add_tail(&req_copy->queue, queue_data);	}	spin_unlock_irq(&ep->udc->lock);	file->private_data = queue_data;	return 0;fail:	spin_unlock_irq(&ep->udc->lock);	list_for_each_entry_safe(req, req_copy, queue_data, queue) {		list_del(&req->queue);		kfree(req);	}	kfree(queue_data);	return -ENOMEM;}/* * bbbbbbbb llllllll IZS sssss nnnn FDL\n\0 * * b: buffer address * l: buffer length * I/i: interrupt/no interrupt * Z/z: zero/no zero * S/s: short ok/short not ok * s: status * n: nr_packets * F/f: submitted/not submitted to FIFO * D/d: using/not using DMA * L/l: last transaction/not last transaction */static ssize_t queue_dbg_read(struct file *file, char __user *buf,		size_t nbytes, loff_t *ppos){	struct list_head *queue = file->private_data;	struct usba_request *req, *tmp_req;	size_t len, remaining, actual = 0;	char tmpbuf[38];	if (!access_ok(VERIFY_WRITE, buf, nbytes))		return -EFAULT;	mutex_lock(&file->f_dentry->d_inode->i_mutex);	list_for_each_entry_safe(req, tmp_req, queue, queue) {		len = snprintf(tmpbuf, sizeof(tmpbuf),				"%8p %08x %c%c%c %5d %c%c%c\n",				req->req.buf, req->req.length,				req->req.no_interrupt ? 'i' : 'I',				req->req.zero ? 'Z' : 'z',				req->req.short_not_ok ? 's' : 'S',				req->req.status,				req->submitted ? 'F' : 'f',				req->using_dma ? 'D' : 'd',				req->last_transaction ? 'L' : 'l');		len = min(len, sizeof(tmpbuf));		if (len > nbytes)			break;		list_del(&req->queue);		kfree(req);		remaining = __copy_to_user(buf, tmpbuf, len);		actual += len - remaining;		if (remaining)			break;		nbytes -= len;		buf += len;	}	mutex_unlock(&file->f_dentry->d_inode->i_mutex);	return actual;}static int queue_dbg_release(struct inode *inode, struct file *file){	struct list_head *queue_data = file->private_data;	struct usba_request *req, *tmp_req;	list_for_each_entry_safe(req, tmp_req, queue_data, queue) {		list_del(&req->queue);		kfree(req);	}	kfree(queue_data);	return 0;}static int regs_dbg_open(struct inode *inode, struct file *file){	struct usba_udc *udc;	unsigned int i;	u32 *data;	int ret = -ENOMEM;	mutex_lock(&inode->i_mutex);	udc = inode->i_private;	data = kmalloc(inode->i_size, GFP_KERNEL);	if (!data)		goto out;	spin_lock_irq(&udc->lock);	for (i = 0; i < inode->i_size / 4; i++)		data[i] = __raw_readl(udc->regs + i * 4);	spin_unlock_irq(&udc->lock);	file->private_data = data;	ret = 0;out:	mutex_unlock(&inode->i_mutex);	return ret;}static ssize_t regs_dbg_read(struct file *file, char __user *buf,		size_t nbytes, loff_t *ppos){	struct inode *inode = file->f_dentry->d_inode;	int ret;	mutex_lock(&inode->i_mutex);	ret = simple_read_from_buffer(buf, nbytes, ppos,			file->private_data,			file->f_dentry->d_inode->i_size);	mutex_unlock(&inode->i_mutex);	return ret;}static int regs_dbg_release(struct inode *inode, struct file *file){	kfree(file->private_data);	return 0;}const struct file_operations queue_dbg_fops = {	.owner		= THIS_MODULE,	.open		= queue_dbg_open,	.llseek		= no_llseek,	.read		= queue_dbg_read,	.release	= queue_dbg_release,};const struct file_operations regs_dbg_fops = {	.owner		= THIS_MODULE,	.open		= regs_dbg_open,	.llseek		= generic_file_llseek,	.read		= regs_dbg_read,	.release	= regs_dbg_release,};static void usba_ep_init_debugfs(struct usba_udc *udc,		struct usba_ep *ep){	struct dentry *ep_root;	ep_root = debugfs_create_dir(ep->ep.name, udc->debugfs_root);	if (!ep_root)		goto err_root;	ep->debugfs_dir = ep_root;	ep->debugfs_queue = debugfs_create_file("queue", 0400, ep_root,						ep, &queue_dbg_fops);	if (!ep->debugfs_queue)		goto err_queue;	if (ep->can_dma) {		ep->debugfs_dma_status			= debugfs_create_u32("dma_status", 0400, ep_root,					&ep->last_dma_status);		if (!ep->debugfs_dma_status)			goto err_dma_status;	}	if (ep_is_control(ep)) {		ep->debugfs_state			= debugfs_create_u32("state", 0400, ep_root,					&ep->state);		if (!ep->debugfs_state)			goto err_state;	}	return;err_state:	if (ep->can_dma)		debugfs_remove(ep->debugfs_dma_status);err_dma_status:	debugfs_remove(ep->debugfs_queue);err_queue:	debugfs_remove(ep_root);err_root:	dev_err(&ep->udc->pdev->dev,		"failed to create debugfs directory for %s\n", ep->ep.name);}static void usba_ep_cleanup_debugfs(struct usba_ep *ep){	debugfs_remove(ep->debugfs_queue);	debugfs_remove(ep->debugfs_dma_status);	debugfs_remove(ep->debugfs_state);	debugfs_remove(ep->debugfs_dir);	ep->debugfs_dma_status = NULL;	ep->debugfs_dir = NULL;}static void usba_init_debugfs(struct usba_udc *udc){	struct dentry *root, *regs;	struct resource *regs_resource;	root = debugfs_create_dir(udc->gadget.name, NULL);	if (IS_ERR(root) || !root)		goto err_root;	udc->debugfs_root = root;	regs = debugfs_create_file("regs", 0400, root, udc, &regs_dbg_fops);	if (!regs)		goto err_regs;	regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM,				CTRL_IOMEM_ID);	regs->d_inode->i_size = regs_resource->end - regs_resource->start + 1;	udc->debugfs_regs = regs;	usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0));	return;err_regs:	debugfs_remove(root);err_root:	udc->debugfs_root = NULL;	dev_err(&udc->pdev->dev, "debugfs is not available\n");}static void usba_cleanup_debugfs(struct usba_udc *udc){	usba_ep_cleanup_debugfs(to_usba_ep(udc->gadget.ep0));	debugfs_remove(udc->debugfs_regs);	debugfs_remove(udc->debugfs_root);	udc->debugfs_regs = NULL;	udc->debugfs_root = NULL;}#elsestatic inline void usba_ep_init_debugfs(struct usba_udc *udc,					 struct usba_ep *ep){}static inline void usba_ep_cleanup_debugfs(struct usba_ep *ep){}static inline void usba_init_debugfs(struct usba_udc *udc){}static inline void usba_cleanup_debugfs(struct usba_udc *udc){}#endifstatic int vbus_is_present(struct usba_udc *udc){	if (udc->vbus_pin != -1)		return gpio_get_value(udc->vbus_pin);	/* No Vbus detection: Assume always present */	return 1;}static void copy_to_fifo(void __iomem *fifo, const void *buf, int len){	unsigned long tmp;	DBG(DBG_FIFO, "copy to FIFO (len %d):\n", len);	for (; len > 0; len -= 4, buf += 4, fifo += 4) {		tmp = *(unsigned long *)buf;		if (len >= 4) {			DBG(DBG_FIFO, "  -> %08lx\n", tmp);			__raw_writel(tmp, fifo);		} else {			do {				DBG(DBG_FIFO, "  -> %02lx\n", tmp >> 24);				__raw_writeb(tmp >> 24, fifo);				fifo++;				tmp <<= 8;			} while (--len);			break;		}	}}static void copy_from_fifo(void *buf, void __iomem *fifo, int len){	union {		unsigned long *w;		unsigned char *b;	} p;	unsigned long tmp;	DBG(DBG_FIFO, "copy from FIFO (len %d):\n", len);	for (p.w = buf; len > 0; len -= 4, p.w++, fifo += 4) {		if (len >= 4) {			tmp = __raw_readl(fifo);			*p.w = tmp;			DBG(DBG_FIFO, "  -> %08lx\n", tmp);		} else {			do {				tmp = __raw_readb(fifo);				*p.b = tmp;				DBG(DBG_FIFO, " -> %02lx\n", tmp);				fifo++, p.b++;			} while (--len);		}	}}static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req){	unsigned int transaction_len;	transaction_len = req->req.length - req->req.actual;	req->last_transaction = 1;	if (transaction_len > ep->ep.maxpacket) {		transaction_len = ep->ep.maxpacket;		req->last_transaction = 0;	} else if (transaction_len == ep->ep.maxpacket && req->req.zero)		req->last_transaction = 0;	DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n",		ep->ep.name, req, transaction_len,		req->last_transaction ? ", done" : "");	copy_to_fifo(ep->fifo, req->req.buf + req->req.actual, transaction_len);	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);	req->req.actual += transaction_len;}static void submit_request(struct usba_ep *ep, struct usba_request *req){	DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d)\n",		ep->ep.name, req, req->req.length);	req->req.actual = 0;	req->submitted = 1;	if (req->using_dma) {		if (req->req.length == 0) {			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);			return;		}		if (req->req.zero)			usba_ep_writel(ep, CTL_ENB, USBA_SHORT_PACKET);		else			usba_ep_writel(ep, CTL_DIS, USBA_SHORT_PACKET);		usba_dma_writel(ep, ADDRESS, req->req.dma);		usba_dma_writel(ep, CONTROL, req->ctrl);	} else {		next_fifo_transaction(ep, req);		if (req->last_transaction) {			usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY);			usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);		} else {			usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE);			usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY);		}	}}static void submit_next_request(struct usba_ep *ep){	struct usba_request *req;	if (list_empty(&ep->queue)) {		usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY | USBA_RX_BK_RDY);		return;	}	req = list_entry(ep->queue.next, struct usba_request, queue);	if (!req->submitted)		submit_request(ep, req);}static void send_status(struct usba_udc *udc, struct usba_ep *ep){	ep->state = STATUS_STAGE_IN;	usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);	usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE);}static void receive_data(struct usba_ep *ep){	struct usba_udc *udc = ep->udc;	struct usba_request *req;	unsigned long status;	unsigned int bytecount, nr_busy;	int is_complete = 0;	status = usba_ep_readl(ep, STA);	nr_busy = USBA_BFEXT(BUSY_BANKS, status);	DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy);	while (nr_busy > 0) {		if (list_empty(&ep->queue)) {			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);			break;		}		req = list_entry(ep->queue.next,				 struct usba_request, queue);		bytecount = USBA_BFEXT(BYTE_COUNT, status);		if (status & (1 << 31))			is_complete = 1;		if (req->req.actual + bytecount >= req->req.length) {			is_complete = 1;			bytecount = req->req.length - req->req.actual;		}		copy_from_fifo(req->req.buf + req->req.actual,				ep->fifo, bytecount);		req->req.actual += bytecount;		usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY);		if (is_complete) {			DBG(DBG_QUEUE, "%s: request done\n", ep->ep.name);			req->req.status = 0;			list_del_init(&req->queue);			usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);			spin_unlock(&udc->lock);			req->req.complete(&ep->ep, &req->req);			spin_lock(&udc->lock);		}		status = usba_ep_readl(ep, STA);		nr_busy = USBA_BFEXT(BUSY_BANKS, status);		if (is_complete && ep_is_control(ep)) {			send_status(udc, ep);			break;		}	}}static voidrequest_complete(struct usba_ep *ep, struct usba_request *req, int status){	struct usba_udc *udc = ep->udc;	WARN_ON(!list_empty(&req->queue));	if (req->req.status == -EINPROGRESS)		req->req.status = status;	if (req->mapped) {		dma_unmap_single(			&udc->pdev->dev, req->req.dma, req->req.length,			ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);		req->req.dma = DMA_ADDR_INVALID;		req->mapped = 0;

⌨️ 快捷键说明

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