📄 s3c2410_udc22222222.c
字号:
/*
* linux/drivers/usb/gadget/s3c2410_udc.c
* Samsung on-chip full speed USB device controllers
*
* Copyright (C) 2004 Herbert P鰐zl
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/version.h>
#include <linux/usb.h>
#include <linux/usb_gadget.h>
/* remove this ASAP!!! */
// #include "../core/hub.h"
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/arch/irqs.h>
#include <asm/arch/h1940.h>
#include <asm/arch/hardware.h>
#include <asm/arch/regs-clock.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-usb.h>
#include "s3c2410_udc.h"
#define dprintk(x...) printk("USB: " x)
#define DRIVER_DESC "S3C2410 USB Device Controller Gadget"
#define DRIVER_VERSION "14 Mar 2004"
static const char gadget_name [] = "s3c2410_udc";
/*-------------------------------------------------------------------------*/
struct s3c2410_udc;
struct s3c2410_ep {
struct list_head queue;
unsigned long last_io; /* jiffies timestamp */
struct usb_gadget *gadget;
struct s3c2410_udc *dev;
const struct usb_endpoint_descriptor *desc;
struct usb_ep ep;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned halted : 1;
unsigned already_seen : 1;
unsigned setup_stage : 1;
};
struct s3c2410_request {
struct list_head queue; /* ep's requests */
struct usb_request req;
};
/*-------------------------------------------------------------------------*/
/*
* Every device has ep0 for control requests, plus up to 30 more endpoints.
*
* Gadget drivers are responsible for not setting up conflicting endpoint
* configurations, illegal or unsupported packet lengths, and so on.
*/
static const char ep0name [] = "ep0";
static const char *const ep_name[] = {
ep0name, /* everyone has ep0 */
/* s3c2410 four bidirectional bulk endpoints */
"ep1", "ep2", "ep3", "ep4",
};
#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name)
#define FIFO_SIZE 64
struct s3c2410_udc {
spinlock_t lock;
struct s3c2410_ep ep[S3C2410_ENDPOINTS];
int address;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct s3c2410_request fifo_req;
u8 fifo_buf[FIFO_SIZE];
u16 devstatus;
u32 port_status;
int ep0state;
unsigned got_irq : 1;
unsigned req_std : 1;
unsigned req_config : 1;
unsigned req_pending : 1;
};
static struct s3c2410_udc *the_controller;
/*-------------------------------------------------------------------------*/
/*
* Gadget Helpers
*/
static inline
struct s3c2410_udc *ep_to_udc (struct s3c2410_ep *ep)
{
return container_of (ep->gadget, struct s3c2410_udc, gadget);
}
static inline
struct s3c2410_udc *gadget_dev_to_udc (struct device *dev)
{
return container_of (dev, struct s3c2410_udc, gadget.dev);
}
/* called with spinlock held */
static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep)
{
while (!list_empty (&ep->queue)) {
struct s3c2410_request *req;
req = list_entry (ep->queue.next, struct s3c2410_request, queue);
list_del_init (&req->queue);
req->req.status = -ESHUTDOWN;
spin_unlock (&udc->lock);
req->req.complete (&ep->ep, &req->req);
spin_lock (&udc->lock);
}
}
static void
fifo_complete (struct usb_ep *ep, struct usb_request *req)
{
dprintk( "fifo_complete: %d\n", req->status);
}
/*-------------------------------------------------------------------------*/
/*
* Gadget Operations
*/
static int
s3c2410_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
struct s3c2410_udc *udc;
struct s3c2410_ep *ep;
unsigned max;
int retval;
printk("s3c2410_enable():\n");
ep = container_of (_ep, struct s3c2410_ep, ep);
/*
if (!the_controller->driver || !is_enabled ())
return -ESHUTDOWN;
*/
max = desc->wMaxPacketSize & 0x3ff;
/* drivers must not request bad settings, since lower levels
* (hardware or its drivers) may not check. some endpoints
* can't do iso, many have maxpacket limitations, etc.
*/
udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
retval = -EINVAL;
switch (desc->bmAttributes & 0x03) {
case USB_ENDPOINT_XFER_BULK:
switch (udc->gadget.speed) {
case USB_SPEED_HIGH:
if (max == 512)
break;
/* conserve return statements */
default:
switch (max) {
case 8: case 16: case 32: case 64:
/* we'll fake any legal size */
break;
default:
case USB_SPEED_LOW:
goto done;
}
}
break;
case USB_ENDPOINT_XFER_INT:
switch (udc->gadget.speed) {
case USB_SPEED_HIGH:
if (max <= 1024)
break;
/* save a return statement */
case USB_SPEED_FULL:
if (max <= 64)
break;
/* save a return statement */
default:
if (max <= 8)
break;
goto done;
}
break;
case USB_ENDPOINT_XFER_ISOC:
/* real hardware might not handle all packet sizes */
switch (udc->gadget.speed) {
case USB_SPEED_HIGH:
if (max <= 1024)
break;
/* save a return statement */
case USB_SPEED_FULL:
if (max <= 1023)
break;
/* save a return statement */
default:
goto done;
}
break;
default:
/* few chips support control except on ep0 */
goto done;
}
_ep->maxpacket = max;
ep->desc = desc;
dprintk( "enabled %s (ep%d%s-%s) maxpacket %d\n",
_ep->name,
desc->bEndpointAddress & 0x0f,
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
({ char *val;
switch (desc->bmAttributes & 0x03) {
case USB_ENDPOINT_XFER_BULK: val = "bulk"; break;
case USB_ENDPOINT_XFER_ISOC: val = "iso"; break;
case USB_ENDPOINT_XFER_INT: val = "intr"; break;
default: val = "ctrl"; break;
}; val; }),
max);
/* at this point real hardware should be NAKing transfers
* to that endpoint, until a buffer is queued to it.
*/
retval = 0;
done:
return retval;
}
static int s3c2410_disable (struct usb_ep *_ep)
{
struct s3c2410_ep *ep;
struct s3c2410_udc *udc;
unsigned long flags;
int retval;
printk("s3c2410_disable()\n");
ep = container_of (_ep, struct s3c2410_ep, ep);
if (!_ep || !ep->desc || _ep->name == ep0name)
return -EINVAL;
udc = ep_to_udc (ep);
spin_lock_irqsave (&udc->lock, flags);
ep->desc = 0;
retval = 0;
nuke (udc, ep);
spin_unlock_irqrestore (&udc->lock, flags);
dprintk( "disabled %s\n", _ep->name);
return retval;
}
static struct usb_request *
s3c2410_alloc_request (struct usb_ep *_ep, int mem_flags)
{
struct s3c2410_ep *ep;
struct s3c2410_request *req;
printk("s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);
ep = container_of (_ep, struct s3c2410_ep, ep);
if (!_ep)
return 0;
req = kmalloc (sizeof *req, mem_flags);
if (!req)
return 0;
memset (req, 0, sizeof *req);
INIT_LIST_HEAD (&req->queue);
return &req->req;
}
static void
s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c2410_ep *ep;
struct s3c2410_request *req;
printk("s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);
ep = container_of (_ep, struct s3c2410_ep, ep);
if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
return;
req = container_of (_req, struct s3c2410_request, req);
WARN_ON (!list_empty (&req->queue));
kfree (req);
}
static void *
s3c2410_alloc_buffer (
struct usb_ep *_ep,
unsigned bytes,
dma_addr_t *dma,
int mem_flags)
{
char *retval;
printk("s3c2410_alloc_buffer()\n");
if (!the_controller->driver)
return 0;
retval = kmalloc (bytes, mem_flags);
*dma = (dma_addr_t) retval;
return retval;
}
static void
s3c2410_free_buffer (
struct usb_ep *_ep,
void *buf,
dma_addr_t dma,
unsigned bytes)
{
printk("s3c2410_free_buffer()\n");
if (bytes)
kfree (buf);
}
static int
s3c2410_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
{
struct s3c2410_ep *ep;
struct s3c2410_request *req;
struct s3c2410_udc *udc;
unsigned long flags;
printk("s3c2410_queue(ep=%p,req=%p,mem=%d)\n",
_ep, _req, mem_flags);
req = container_of (_req, struct s3c2410_request, req);
if (!_req || !list_empty (&req->queue) || !_req->complete)
return -EINVAL;
ep = container_of (_ep, struct s3c2410_ep, ep);
if (!_ep || (!ep->desc && _ep->name != ep0name))
return -EINVAL;
/*
if (!the_controller->driver || !is_enabled ())
return -ESHUTDOWN;
*/
udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
dprintk( "ep %p queue req %p to %s, len %d buf %p\n",
ep, _req, _ep->name, _req->length, _req->buf);
_req->status = -EINPROGRESS;
_req->actual = 0;
spin_lock_irqsave (&udc->lock, flags);
/* implement an emulated single-request FIFO */
if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
list_empty (&udc->fifo_req.queue) &&
list_empty (&ep->queue) &&
_req->length <= FIFO_SIZE) {
req = &udc->fifo_req;
req->req = *_req;
req->req.buf = udc->fifo_buf;
memcpy (udc->fifo_buf, _req->buf, _req->length);
req->req.context = udc;
req->req.complete = fifo_complete;
spin_unlock (&udc->lock);
_req->actual = _req->length;
_req->status = 0;
_req->complete (_ep, _req);
spin_lock (&udc->lock);
}
list_add_tail (&req->queue, &ep->queue);
spin_unlock_irqrestore (&udc->lock, flags);
/* real hardware would likely enable transfers here, in case
* it'd been left NAKing.
*/
return 0;
}
static int s3c2410_dequeue (struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c2410_ep *ep;
struct s3c2410_udc *udc;
int retval = -EINVAL;
unsigned long flags;
struct s3c2410_request *req = 0;
printk("s3c2410_dequeue(ep=%p,req=%p)\n", _ep, _req);
if (!the_controller->driver)
return -ESHUTDOWN;
if (!_ep || !_req)
return retval;
ep = container_of (_ep, struct s3c2410_ep, ep);
udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -