/*
* linux/drivers/usb/gadget/s3c2410_udc.c
* Samsung on-chip full speed USB device controllers
*
* Copyright (C) 2004 Herbert P�tzl
*
* 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-arm/byteorder.h>
#include <asm-arm/io.h>
#include <asm-arm/irq.h>
#include <asm-arm/system.h>
#include <asm-arm/unaligned.h>
#include <asm-arm/arch/irqs.h>
#include <asm/arch-s3c2410/hardware.h>
#include <asm/arch-s3c2410/regs-clock.h>
#include <asm/arch-s3c2410/regs-gpio.h>
#include <asm/arch-s3c2410/regs-usb.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 long pio_irqs;
unsigned long dma_irqs;
short dma;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned halted : 1;
unsigned already_seen : 1;
unsigned setup_stage : 1;
// unsigned stopped : 1; /* replaced by halted? */
unsigned dma_fixup : 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;
unsigned got_irq : 1;
};
static struct s3c2410_udc *the_controller;
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);
}
#define is_enabled() \
(the_controller->port_status & USB_PORT_STAT_ENABLE)
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;
ep = container_of (_ep, struct s3c2410_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT)
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;
}
/* 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 int s3c2410_disable (struct usb_ep *_ep)
{
struct s3c2410_ep *ep;
struct s3c2410_udc *udc;
unsigned long flags;
int retval;
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;
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;
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;
if (!the
s3c2410_udc.rar_2410_s3c2410_s3c2410_u_s3c2410_udc_udc
版权申诉
97 浏览量
2022-09-20
14:12:50
上传
评论
收藏 8KB RAR 举报
小贝德罗
- 粉丝: 70
- 资源: 1万+