📄 s3c2410_udc.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-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 64struct 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 inlinestruct s3c2410_udc *ep_to_udc (struct s3c2410_ep *ep){ return container_of (ep->gadget, struct s3c2410_udc, gadget);}static inlinestruct 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 ints3c2410_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 voids3c2410_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_controller->driver) return 0; retval = kmalloc (bytes, mem_flags); *dma = (dma_addr_t) retval; return retval;}static voids3c2410_free_buffer ( struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes){ if (bytes) kfree (buf);}static voidfifo_complete (struct usb_ep *ep, struct usb_request *req){#if 0 dprintk( "fifo_complete: %d\n", req->status);#endif}static ints3c2410_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; 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);#if 0 dprintk( "ep %p queue req %p to %s, len %d buf %p\n", ep, _req, _ep->name, _req->length, _req->buf);#endif _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; 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); spin_lock_irqsave (&udc->lock, flags); list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) { list_del_init (&req->queue); _req->status = -ECONNRESET; retval = 0; break; } } spin_unlock_irqrestore (&udc->lock, flags); if (retval == 0) { dprintk( "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); _req->complete (_ep, _req); } return retval;}static ints3c2410_set_halt (struct usb_ep *_ep, int value){ struct s3c2410_ep *ep; if (!_ep) return -EINVAL; if (!the_controller->driver) return -ESHUTDOWN; ep = container_of (_ep, struct s3c2410_ep, ep); if (!value) ep->halted = 0; else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && !list_empty (&ep->queue)) return -EAGAIN; else ep->halted = 1; /* FIXME clear emulated data toggle too */ return 0;}static const struct usb_ep_ops s3c2410_ep_ops = { .enable = s3c2410_enable, .disable = s3c2410_disable, .alloc_request = s3c2410_alloc_request, .free_request = s3c2410_free_request, .alloc_buffer = s3c2410_alloc_buffer, .free_buffer = s3c2410_free_buffer, /* map, unmap, ... eventually hook the "generic" dma calls */ .queue = s3c2410_queue, .dequeue = s3c2410_dequeue, .set_halt = s3c2410_set_halt,};/*-------------------------------------------------------------------------*//* there are both host and device side versions of this call ... */static int s3c2410_g_get_frame (struct usb_gadget *_gadget){ struct timeval tv; do_gettimeofday (&tv); return tv.tv_usec / 1000;}static int s3c2410_wakeup (struct usb_gadget *_gadget){ struct s3c2410_udc *udc; udc = container_of (_gadget, struct s3c2410_udc, gadget); if ((udc->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0 || !(udc->port_status & (1 << USB_PORT_FEAT_SUSPEND))) return -EINVAL; udc->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); return 0;}static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value){ struct s3c2410_udc *udc; udc = container_of (_gadget, struct s3c2410_udc, gadget); if (value) udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -