| From: |
| Thomas Brinker <thomas.brinker@gmx.de> |
| To: |
| linux-usb-devel@lists.sourceforge.net |
| Subject: |
| [PATCH] USB-Gadget support for fhg_usb32 |
| Date: |
| Mon, 3 Oct 2005 19:24:35 +0200 |
| Archive-link: |
| Article,
Thread
|
Hi!
This adds support for the FHG_USB32 IP-Core Modul from emsys GmbH and Fraumhofer Gesellschaft.
This is a USB 1.1 Function/Client Module. It can be attached to the adress space.
The two base adresses of the modul can be configured by modul parameters.
This might also be a good example for future gadget development as this driver
does not have any confusing PCI or DMA handling, because the hardware does
not support this.
Patch is against 2.6.13
Some time ago this has already been discussed on kernel-mentors@selenic.com
Please consider applying.
Regards
Thomas
diff -bBurNp linux-2.6.13/drivers/usb/gadget_ori/fhg_usb32.c
linux-2.6.13/drivers/usb/gadget/fhg_usb32.c
--- linux-2.6.13/drivers/usb/gadget_ori/fhg_usb32.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/usb/gadget/fhg_usb32.c 2005-10-03 17:58:31.000000000 +0200
@@ -0,0 +1,1928 @@
+/*
+ * linux/drivers/usb/gadget/fhg_usb32.c
+ *
+ * based on pxa2xx_udc.c and net2290.c
+ *
+ * Copyright (C) 2005 Thomas Brinker thomas.brinker@gmx.de
+ * Copyright (C) 2003-2005 David Brownell
+ *
+ * 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
+ *
+ *
+ * Configure I/O-Space with modul parameters 'ctrl_base' and 'fifo_base'.
+ * Set 'ctrl_base' to base address of control registers.
+ * Set 'fifo_base' to base address of FIFOs.
+ * Defaults:
+ * ctrl_base = 0x00190000
+ * fifo_base = 0x001a0000
+ *-----------------------------------------------------------------------------
+ */
+
+/*
+#define DEBUG
+#define VERBOSE DBG_VERBOSE
+#define UDC_DEBUG DBG_VERY_NOISY
+*/
+
+
+#define UDC_DEBUG 0
+#undef DEBUG
+#undef VERBOSE
+
+/* Show or hide any USB-request, for debugging */
+/*#define SHOW_REQ*/
+#undef SHOW_REQ
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/hardirq.h>
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "fhg_usb32.h"
+
+#define DRIVER_VERSION "xx-Jul-2005"
+#define DRIVER_DESC "FHG_USB32 USB Device Controller"
+
+
+static struct timer_list isr_lost_timer;
+
+
+static const char driver_name [] = "fhg_usb32";
+
+static const char ep0name [] = "ep0";
+
+
+module_param(ctrl_base, uint, S_IRUGO);
+MODULE_PARM_DESC(ctrl_base,"Base-address of Control-registers");
+module_param(fifo_base, uint, S_IRUGO);
+MODULE_PARM_DESC(fifo_base,"Base-address of FIFO-Space");
+
+
+/* ---------------------------------------------------------------------------
+ * endpoint related parts of the api to the usb controller hardware,
+ * used by gadget driver; and the inner talker-to-hardware core.
+ * ---------------------------------------------------------------------------
+ */
+
+static void fhg_usb32_ep_fifo_flush(struct usb_ep *ep);
+static void nuke(struct fhg_usb32_ep *, int status);
+
+
+
+/*
+ * endpoint enable
+ */
+static int fhg_usb32_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct fhg_usb32_ep *ep;
+ struct fhg_usb32_udc *dev;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (!_ep
+ || !desc
+ || ep->desc /* ep already initalized */
+ || _ep->name == ep0name /* initalizing ep0 */
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+ DMSG("%s, bad ep or descriptor\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (le16_to_cpu(desc->wMaxPacketSize) > FIFO_SIZE) {
+ DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
+ return -ERANGE;
+ }
+
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ DMSG("%s, bogus device state\n", __FUNCTION__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ ep->desc = desc;
+ ep->stopped = 0;
+ ep->irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* flush fifo (mostly for OUT buffers) */
+ fhg_usb32_ep_fifo_flush(_ep);
+
+ if (ep->dev->stall_state & (0x1 << ep->number)) {
+ FHG_USB32_IEP_STALL |= 0x1 << ep->number;
+ FHG_USB32_OEP_STALL |= 0x1 << ep->number;
+ } else {
+ FHG_USB32_IEP_STALL &= ~(0x1 << ep->number);
+ FHG_USB32_OEP_STALL &= ~(0x1 << ep->number);
+ }
+
+ if (desc->bEndpointAddress & USB_DIR_IN) {
+ if (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ FHG_USB32_IN_ISO_CONF |= 0x1 << (ep->number);
+ ep->is_iso = 1;
+ } else {
+ FHG_USB32_IN_ISO_CONF &= ~(0x1 << ep->number);
+ ep->is_iso = 0;
+ }
+
+ FHG_USB32_IEP_ENA |= 0x1 << ep->number;
+ FHG_USB32_IN_EVENT_MSK |= 0x1 << ep->number;
+
+ ep->is_in = 1;
+ DBG(DBG_VERY_NOISY,"%s enabled as IN\n",_ep->name);
+ } else {
+ if (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ FHG_USB32_OUT_ISO_CONF |= 0x1 << ep->number;
+ ep->is_iso = 1;
+ } else {
+ FHG_USB32_OUT_ISO_CONF &= ~(0x1 << ep->number);
+ ep->is_iso = 0;
+ }
+
+ FHG_USB32_OEP_ENA |= 0x1 << ep->number;
+ FHG_USB32_OUT_EVENT_MSK |= 0x1 << ep->number;
+ ep->is_in = 0;
+ DBG(DBG_VERY_NOISY,"%s enabled as OUT\n",_ep->name);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
+ return 0;
+}
+
+/*
+ * Disable endpoint and nuke all enqueued requests
+ */
+static int fhg_usb32_ep_disable(struct usb_ep *_ep)
+{
+ struct fhg_usb32_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (!_ep || !ep->desc) {
+ DBG(DBG_VERBOSE,"%s, %s was not enabled\n", __FUNCTION__,
+ _ep ? ep->ep.name : "NULL");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+
+ nuke(ep, -ESHUTDOWN);
+
+ if (ep->is_in) {
+ FHG_USB32_IN_EVENT_MSK &= ~(0x1 << ep->number);
+ FHG_USB32_IEP_ENA &= ~(0x1 << ep->number);
+ FHG_USB32_IN_ISO_CONF &= ~(0x1 << ep->number);
+ } else {
+ FHG_USB32_OUT_EVENT_MSK &= ~(0x1 << ep->number);
+ FHG_USB32_OEP_ENA &= ~(0x1 << ep->number);
+ FHG_USB32_OUT_ISO_CONF &= ~(0x1 << ep->number);
+ }
+
+ /* flush fifo (mostly for IN buffers) */
+ fhg_usb32_ep_fifo_flush(_ep);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * for the fhg_usb32, these can just wrap kmalloc/kfree.
+ * Because there is no DMA.
+ */
+
+static struct usb_request *fhg_usb32_ep_alloc_request(struct usb_ep *_ep,
+ int gfp_flags)
+{
+ struct fhg_usb32_request *req;
+
+ DBG(DBG_VERY_NOISY,"Requesting request: %s\n",_ep->name);
+
+ req = kmalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return 0;
+
+ memset(req, 0, sizeof *req);
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+
+static void fhg_usb32_ep_free_request(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct fhg_usb32_request *req;
+
+ req = container_of(_req, struct fhg_usb32_request, req);
+ if (!list_empty(&req->queue))
+ DBG(DBG_VERY_NOISY,"Freeing request %p from list: %s\n",
+ req,_ep->name);
+
+ DBG(DBG_VERY_NOISY,"Freeing request %p: %s\n",req,_ep->name);
+ kfree(req);
+}
+
+static void *fhg_usb32_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t *dma, int gfp_flags)
+{
+ char *retval;
+
+ retval = kmalloc(bytes, gfp_flags);
+ DBG(DBG_VERY_NOISY,"Requesting buffer: ep: %s, size: %d, "
+ "place: 0x%p\n", _ep->name,bytes,retval);
+ return retval;
+}
+
+static void fhg_usb32_ep_free_buffer(struct usb_ep *_ep, void *buf,
+ dma_addr_t dma, unsigned bytes)
+{
+ DBG(DBG_VERY_NOISY,"Freeing buffer %s\n",_ep->name);
+ kfree(buf);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct fhg_usb32_ep *ep, struct fhg_usb32_request *req,
+ int status)
+{
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN)
+ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ spin_unlock(&ep->dev->lock);
+ DBG(DBG_VERY_NOISY, "Completion Callback Start Function: 0x%p\n",
+ req->req.complete);
+ req->req.complete(&ep->ep, &req->req);
+ DBG(DBG_VERY_NOISY, "Completion Callback End\n");
+ spin_lock(&ep->dev->lock);
+ ep->stopped = stopped;
+}
+
+static int write_packet(struct fhg_usb32_ep *ep,
+ struct fhg_usb32_request *req)
+{
+ u8 *buf;
+ int length,count;
+
+ buf = req->req.buf + req->req.actual;
+
+ FHG_USB32_IN_EP_SEL = ep->number;
+
+ /* how big will this packet be? */
+ length = min(req->req.length - req->req.actual, FIFO_SIZE);
+ req->req.actual += length;
+
+ /*
+ * Ohhh yes! The +4 is needed. Because of some really nasty hardware
+ * bugs. But never write more than 64 Bytes because next fifo starts
+ * right away. The Hardwarebug does not occur at end of the fifo
+ */
+ count = length + 4;
+ if (count > 64)
+ count = 64;
+
+ memcpy((char*)in_fifo[(int)ep->number],buf,count);
+
+ return length;
+}
+
+/*
+ * write to an IN endpoint fifo, as many packets as possible.
+ * irqs will use this to write the rest later.
+ * caller guarantees at least one packet buffer is ready (or a zlp).
+ * returns number of written bytes
+ */
+static int write_fifo(struct fhg_usb32_ep *ep, struct fhg_usb32_request *req)
+{
+ unsigned count;
+ int is_last;
+
+ count = write_packet(ep, req);
+
+ /* last packet is usually short (or a zlp) */
+ if (unlikely(count != FIFO_SIZE)) {
+ is_last = 1;
+ } else {
+ if (likely(req->req.length != req->req.actual)
+ || req->req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+ }
+
+#ifdef SHOW_REQ
+ if (is_last)
+ printk("%s H <-- C IN %d bytes\n",
+ ep->ep.name, req->req.actual);
+#endif
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ if (ep->number == 0) {
+ if (ep->dev->ep0state == EP0_DATA_QUEUED) {
+ DBG_STATE_MACH("EP0_DATA_QUEUED",
+ "EP0_ZLP_TO_QUEUE");
+ ep->dev->ep0state = EP0_ZLP_TO_QUEUE;
+ /* the following ACK'ing ZLP is going
+ * to be a OUT one.
+ */
+ ep->is_in = 0;
+ }
+ }
+
+ done(ep, req, 0);
+
+ if (list_empty(&ep->queue) && ep->number != 0) {
+ in_irq_disable (ep->number);
+ }
+ }
+ if (!in_irq())
+ udelay(1000);
+
+ FHG_USB32_IN_CMD = ((count & USB_IN_COUNT_MSK)|USB_IENA_MSK);
+
+ return count;
+}
+
+
+
+
+/*
+ * read_fifo - unload packet(s) from the fifo we use for usb OUT
+ * transfers and put them into the request. caller should have made
+ * sure there's at least one packet ready.
+ *
+ * returns number of bytes written
+ */
+static int read_fifo(struct fhg_usb32_ep *ep, struct fhg_usb32_request *req)
+{
+ u8 byte_number;
+ int length, n;
+ u8 *buf;
+ unsigned int bufferspace, count, is_short;
+
+ byte_number = 0;
+ n = 0;
+
+ buf = req->req.buf + req->req.actual;
+
+ /* remaining space in requestbuffer */
+ bufferspace = req->req.length - req->req.actual;
+
+ /* read all bytes from this packet */
+ FHG_USB32_OUT_EP_SEL = ep->number;
+
+ count = FHG_USB32_OUT_STAT & USB_OUT_COUNT_MSK;
+ length = count;
+ req->req.actual += min(count, bufferspace);
+
+ is_short = (count < FIFO_SIZE);
+
+ /*DBG(DBG_VERY_NOISY, "read %s , %d bytes%s req %p %d/%d\n",
+ ep->ep.name, count,
+ is_short ? "/S" : "",
+ req, req->req.actual, req->req.length);*/
+
+ while (likely(count-- != 0)) {
+ if (unlikely(bufferspace == 0)) {
+ /* this happens when the driver's buffer
+ * is smaller than what the host sent.
+ * discard the extra data.
+ */
+ if (req->req.status != -EOVERFLOW)
+ DMSG("%s overflow %d\n",
+ ep->ep.name, count);
+ req->req.status = -EOVERFLOW;
+ } else {
+ *buf++ = out_fifo[(int)ep->number][n];
+ n++;
+ bufferspace--;
+ }
+ }
+
+
+ /* completion */
+ if (is_short || req->req.actual == req->req.length) {
+#ifdef SHOW_REQ
+ printk("%s H --> C OUT %d bytes\n",
+ ep->ep.name, req->req.actual);
+#endif
+ if (ep->number == 0) {
+ if (ep->dev->ep0state == EP0_WAIT4DATA) {
+ /*
+ * the callback which will be done in done()
+ * hopefully queues a zlp so that the received
+ * DATA is ACK'ed
+ */
+ DBG_STATE_MACH("EP0_WAIT4DATA",
+ "EP0_ZLP_TO_QUEUE");
+ ep->dev->ep0state = EP0_ZLP_TO_QUEUE;
+ ep->is_in = 1;
+ if (length > 0) {
+ done(ep, req, 0);
+ if (ep->dev->ep0state ==
+ EP0_ZLP_TO_QUEUE) {
+ /* no zlp was queued in
+ * callback made from done()
+ * so we have to ACK the
+ * received DATA with a zlp
+ */
+ FHG_USB32_IN_CMD =
+ (0 & USB_IN_COUNT_MSK)
+ |USB_IENA_MSK;
+ ep->dev->ep0state =
+ EP0_ZLP_QUEUED;
+ }
+ }
+ else
+ printk("Strange %s:%d\n",__FILE__,
+ __LINE__);
+ } else if (ep->dev->ep0state == EP0_WAIT4ZLP) {
+ if (length == 0) {
+ DBG_STATE_MACH("EP0_WAIT4ZLP",
+ "EP0_IDLE");
+ ep->dev->ep0state = EP0_IDLE;
+ done(ep, req, 0);
+ } else {
+ done(ep, req, -EPROTO);
+ }
+ }
+ else
+ printk("Strange state:%d %s:%d\n",
+ ep->dev->ep0state,__FILE__,__LINE__);
+ } else
+ done(ep, req, 0);
+
+ if (list_empty(&ep->queue) && ep->number!=0)
+ out_irq_disable(ep->number); /* switch off endpoint */
+ }
+ if (!in_irq())
+ udelay(1000);
+ /*Flush Fifo*/
+ FHG_USB32_OUT_CMD |= USB_FLUSH_MSK;
+
+ return length;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fhg_usb32_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ int gfp_flags)
+{
+ struct fhg_usb32_request *req;
+ struct fhg_usb32_ep *ep;
+ struct fhg_usb32_udc *dev;
+ unsigned long flags;
+
+ req = container_of(_req, struct fhg_usb32_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf
+ || !list_empty(&req->queue))) {
+
+ printk("_req=0x%p;_req->complete=0x%p; "
+ "_req->buf=0x%p;list_empty=%d\n",
+ _req, _req->complete, _req->buf,
+ list_empty(&req->queue));
+
+ DMSG("%s, bad params\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ dev = ep->dev;
+ if (unlikely(!dev->driver
+ || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+ DMSG("%s, bogus device state\n", __FUNCTION__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ if (ep->number==0) {
+ if (ep->dev->ep0state == EP0_ZLP_TO_QUEUE) {
+ if (_req->length == 0) {
+ if (ep->is_in) {
+ DBG_STATE_MACH("EP0_ZLP_TO_QUEUE",
+ "EP0_ZLP_QUEUED");
+ ep->dev->ep0state = EP0_ZLP_QUEUED;
+ } else {
+ DBG_STATE_MACH("EP0_ZLP_TO_QUEUE",
+ "EP0_WAIT4ZLP");
+ ep->dev->ep0state = EP0_WAIT4ZLP;
+ }
+ } else {
+ printk("strange: %s:%d\n",__FILE__,__LINE__);
+ }
+ } else if (ep->dev->ep0state == EP0_DATA_TO_QUEUE) {
+ if (_req->length > 0) {
+ DBG_STATE_MACH("EP0_DATA_TO_QUEUE",
+ "EP0_DATA_QUEUED");
+ ep->dev->ep0state = EP0_DATA_QUEUED;
+ } else {
+ printk("strange: %s:%d\n",__FILE__,__LINE__);
+ }
+ } else if (ep->dev->ep0state == EP0_EMPTY_TO_QUEUE) {
+ if (_req->length > 0) {
+ DBG_STATE_MACH("EP0_EMPTY_TO_QUEUE",
+ "EP0_WAIT4DATA");
+ ep->dev->ep0state = EP0_WAIT4DATA;
+ } else {
+ printk("strange: %s:%d\n",__FILE__,__LINE__);
+ }
+ } else
+ printk("Strange state:%d %s:%d\n",ep->dev->ep0state,
+ __FILE__,__LINE__);
+ }
+
+ /* kickstart this i/o queue? */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ if (ep->is_in) {
+ /* write_fifo can have sideeffects on _req->length */
+ int x,y;
+ y = _req->length;
+ x = write_fifo(ep, req);
+
+ if (x == y) { /* all bytes written, no need to queue */
+ req = 0;
+ }
+ } else {
+
+ if (FHG_USB32_OUT_EVENT & (0x1 << ep->number)) {
+ /* read_fifo can have sideeffects
+ * on _req->length
+ */
+ int x,y;
+ y = _req->length;
+ x = read_fifo(ep, req);
+
+ if (x == y)
+ /* all bytes read, no need to queue */
+ req = 0;
+ }
+ }
+
+ if (likely (req && ep->desc)) {
+ if (ep->is_in)
+ in_irq_enable(ep->number);
+ else
+ out_irq_enable(ep->number);
+ }
+ }
+
+ if (req != 0) {
+ list_add_tail(&req->queue, &ep->queue);
+ DBG(DBG_VERY_NOISY,"Added req %p to list\n",req);
+ }
+
+ if (ep->number==0) {
+ dev->stats.write.bytes += _req->length;
+ dev->stats.write.ops++;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+
+/*
+ * nuke - dequeue ALL requests
+ * called with irqs blocked
+ */
+static void nuke(struct fhg_usb32_ep *ep, int status)
+{
+ struct fhg_usb32_request *req;
+
+ DBG(DBG_VERY_NOISY, "nuke ep%d, status: %d\n",ep->number,status);
+
+ list_for_each_entry(req, &ep->queue, queue) {
+ DBG(DBG_VERY_NOISY, "Content of list: %p\n",req);
+ }
+
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct fhg_usb32_request,
+ queue);
+ done(ep, req, status);
+ }
+ if (ep->desc) {
+ out_irq_disable(ep->number);
+ in_irq_disable(ep->number);
+ }
+
+}
+
+
+/* dequeue JUST ONE request */
+static int fhg_usb32_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fhg_usb32_ep *ep;
+ struct fhg_usb32_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int fhg_usb32_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct fhg_usb32_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (unlikely(!_ep|| (!ep->desc && ep->ep.name != ep0name))
+ || ep->is_iso) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ if (!value) {
+ if (ep->is_in)
+ FHG_USB32_IEP_STALL &= ~(0x1 << ep->number);
+ else
+ FHG_USB32_OEP_STALL &= ~(0x1 << ep->number);
+ ep->dev->stall_state &= ~(0x1 << ep->number);
+ } else if (ep->desc && (ep->is_in) && !list_empty (&ep->queue)) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EAGAIN;
+ } else {
+ if (ep->is_in)
+ FHG_USB32_IEP_STALL |= 0x1 << ep->number;
+ else
+ FHG_USB32_OEP_STALL |= 0x1 << ep->number;
+ ep->dev->stall_state |= 0x1 << ep->number;
+ }
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
+ return 0;
+}
+
+static int fhg_usb32_ep_fifo_status(struct usb_ep *_ep)
+{
+ struct fhg_usb32_ep *ep;
+ int tmp;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (!_ep) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return 0;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ if (ep->is_in) {
+ FHG_USB32_IN_EP_SEL = ep->number;
+ tmp = FHG_USB32_IN_CMD & USB_IN_COUNT_MSK;
+ } else {
+ FHG_USB32_OUT_EP_SEL = ep->number;
+ tmp = FHG_USB32_OUT_STAT & USB_OUT_COUNT_MSK;
+ }
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return tmp;
+}
+
+static void fhg_usb32_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct fhg_usb32_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct fhg_usb32_ep, ep);
+ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
+ DMSG("%s, bad ep, ep%d\n", __FUNCTION__,ep->number);
+ return;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ if(!(ep->is_in)) {
+ FHG_USB32_OUT_EP_SEL = ep->number;
+ FHG_USB32_OUT_CMD |= USB_FLUSH_MSK;
+ }
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+}
+
+
+static struct usb_ep_ops fhg_usb32_ep_ops = {
+ .enable = fhg_usb32_ep_enable,
+ .disable = fhg_usb32_ep_disable,
+
+ .alloc_request = fhg_usb32_ep_alloc_request,
+ .free_request = fhg_usb32_ep_free_request,
+
+ .alloc_buffer = fhg_usb32_ep_alloc_buffer,
+ .free_buffer = fhg_usb32_ep_free_buffer,
+
+ .queue = fhg_usb32_ep_queue,
+ .dequeue = fhg_usb32_ep_dequeue,
+
+ .set_halt = fhg_usb32_ep_set_halt,
+ .fifo_status = fhg_usb32_ep_fifo_status,
+ .fifo_flush = fhg_usb32_ep_fifo_flush,
+};
+
+
+/* ---------------------------------------------------------------------------
+ * device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static int fhg_usb32_udc_get_frame(struct usb_gadget *_gadget)
+{
+ int tmp;
+ struct fhg_usb32_udc *dev;
+ unsigned long flags;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct fhg_usb32_udc, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = FHG_USB32_FRM_TIMER & USB_FRAME_NR_MSK;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return tmp;
+}
+
+static int fhg_usb32_udc_wakeup(struct usb_gadget *_gadget)
+{
+ struct fhg_usb32_udc *dev;
+ unsigned long flags;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct fhg_usb32_udc, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ FHG_USB32_CTRL |= USB_RESU_MSK;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+
+static const struct usb_gadget_ops fhg_usb32_udc_ops = {
+ .get_frame = fhg_usb32_udc_get_frame,
+ .wakeup = fhg_usb32_udc_wakeup,
+
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_DEBUG_FS
+#define DEBUGBUFFERSIZE 200
+static struct dentry *debug_dentry;
+static struct dentry *debug_dir;
+
+static ssize_t debug_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[DEBUGBUFFERSIZE];
+ struct fhg_usb32_udc *dev = file->private_data;
+ char *next = buf;
+ unsigned size = DEBUGBUFFERSIZE;
+ unsigned long flags;
+ int t,i;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* basic device status */
+ t = scnprintf(next, size, DRIVER_DESC "\n"
+ "%s version: %s\n ctrl base address: 0x%x fifo base address: 0x%x\n"
+ "Gadget driver: %s\nFunctionadress: %d\n",
+ driver_name, DRIVER_VERSION, ctrl_base, fifo_base,
+ dev->driver ? dev->driver->driver.name : "(none)",
+ FHG_USB32_FUNC_ADDR);
+ size -= t;
+ next += t;
+
+ if (!is_usb_connected() || !dev->driver)
+ goto done;
+
+ t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+ dev->stats.write.bytes, dev->stats.write.ops,
+ dev->stats.read.bytes, dev->stats.read.ops,
+ dev->stats.irqs);
+ size -= t;
+ next += t;
+
+
+ /* dump endpoint queues */
+ for (i = 0; i <= FHG_USB32_NUM_ENDPOINTS; i++) {
+ struct fhg_usb32_ep *ep = &dev->ep[i];
+ struct fhg_usb32_request *req;
+ int t;
+ const struct usb_endpoint_descriptor *d;
+
+ d = ep->desc;
+ if (!d)
+ continue;
+ t = scnprintf(next, size, "%s max %d irqs %lu %s\n",
+ ep->ep.name, le16_to_cpu(d->wMaxPacketSize), ep->irqs,
+ ep->is_in?"[IN]":"[OUT]");
+
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "\t(nothing queued)\n");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ continue;
+ }
+
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ }
+ }
+
+done:
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, DEBUGBUFFERSIZE);
+}
+
+static int debug_open_file(struct inode *inode, struct file *file)
+{
+ /* What a hack... */
+ file->private_data = inode->u.generic_ip;
+ return 0;
+}
+
+static struct file_operations debug_fops = {
+ .read = debug_read_file,
+ .open = debug_open_file,
+};
+
+#endif /* CONFIG_DEBUG_FS */
+
+/* "function" sysfs attribute */
+static ssize_t show_function(struct device *_dev, char *buf)
+{
+ struct fhg_usb32_udc *dev = dev_get_drvdata(_dev);
+
+ if (!dev->driver
+ || !dev->driver->function
+ || strlen(dev->driver->function) > PAGE_SIZE)
+ return 0;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct fhg_usb32_udc *dev)
+{
+ /* block all irqs */
+ disable_irq(IRQ_USB);
+
+ /* clear all Pending Interrupts */
+ FHG_USB32_MAIN_EVENT = 0;
+ FHG_USB32_STATIC_EVENT = 0;
+ FHG_USB32_OUT_EVENT = 0;
+ FHG_USB32_IN_EVENT = 0;
+
+ FHG_USB32_MAIN_EVENT_MSK = 0;
+ FHG_USB32_STATIC_EVENT_MSK = 0;
+ FHG_USB32_OUT_EVENT_MSK = 0;
+ FHG_USB32_IN_EVENT_MSK = 0;
+
+ /* if hardware supports it, disconnect from usb */
+ make_usb_disappear();
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+}
+
+
+/*
+ * initialize software state
+ */
+static void udc_reinit(struct fhg_usb32_udc *dev)
+{
+ u32 i;
+
+ /* device/ep0 records init */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* basic endpoint records init */
+ for (i = 0; i <= FHG_USB32_NUM_ENDPOINTS; i++) {
+ struct fhg_usb32_ep *ep = &dev->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+ ep->desc = 0;
+ ep->stopped = 0;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->irqs = 0;
+ }
+
+ dev->stall_state = 0;
+ dev->ep0state = EP0_IDLE;
+
+ /* the rest was statically initialized, and is read-only */
+}
+
+static void init_p2001_usb_pll(void)
+{
+ u8 M,P,S,PWRDN,SEL_PLL;
+
+ M = (u8)(117 & 0xff);
+ P = (u8)(30 & 0x3f);
+ S = (u8)(0 & 0x3 );
+ PWRDN = (u8)(0 & 0x1 );
+ SEL_PLL = (u8)(1 & 0x1);
+
+ P2001_PLL_12000_CONFIG = (SEL_PLL<<28)|(PWRDN<<26)|(S<<14)|(P<<8)|(M<<0);
+}
+
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ * This function brings us into powered state.
+ */
+static void udc_enable(struct fhg_usb32_udc *dev)
+{
+ /* stop interrupts */
+ FHG_USB32_MODE_CRTL &= ~(USB_INTE_MSK);
+ FHG_USB32_CTRL_REG = 0x281;
+
+ init_p2001_usb_pll();
+
+ /* TODO: change with clockscale */
+#ifdef CONFIG_ARCH_P2001
+ P2001_SYS->WaitState_Asic &= ~((0xf)<<8);
+ P2001_SYS->WaitState_Asic |= (7&0xf)<<8;
+#endif
+ /* nCSmask_value reset */
+ FHG_USB32_CTRL_REG &= ~(0x3<<14);
+ /* nCSmask_value set */
+ FHG_USB32_CTRL_REG |= (3&0x3)<<14;
+
+
+ /* let Lowsspeed be possible also with 48MHz Clock */
+ FHG_USB32_CTRL_REG |= (0x1<<8);
+
+ /* no suspend */
+ FHG_USB32_CTRL_REG &= ~(0x1<<7);
+
+ /* Interrupt polarity */
+ FHG_USB32_MODE_CRTL |= USB_INTE_MSK;
+
+ /* I/O Interrupts enable*/
+ FHG_USB32_MAIN_EVENT_MSK = (USB_OUTE_MSK | USB_INE_MSK | USB_ZOE_MSK);
+
+ /* static Interrupts enable */
+ FHG_USB32_MAIN_EVENT_MSK |= USB_STE_MSK;
+ FHG_USB32_STATIC_EVENT_MSK = (USB_URESE_MSK | USB_SUSPE_MSK | USB_RSUE_MSK);
+
+
+ /* clear all Pending Interrupts */
+ FHG_USB32_MAIN_EVENT = 0;
+ FHG_USB32_STATIC_EVENT = 0;
+ FHG_USB32_OUT_EVENT = 0;
+ FHG_USB32_IN_EVENT = 0;
+
+
+ /* let interrupts start firing */
+ FHG_USB32_MODE_CRTL |= USB_INTE_MSK;
+
+ /* connect to usb and wait for host */
+
+ enable_irq(IRQ_USB);
+ let_usb_appear();
+
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests. then usb traffic follows until a
+ * disconnect is reported. then a host may connect again, or
+ * the driver might get unbound.
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct fhg_usb32_udc *dev = the_controller;
+ int retval;
+ unsigned long flags;
+
+ if (!driver
+ || !driver->bind
+ || !driver->unbind
+ || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->driver) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+
+ /* first hook up the driver ... */
+ dev->driver = driver;
+
+ dev->gadget.dev.driver = &driver->driver;
+ retval = device_add(&dev->gadget.dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ retval = driver->bind(&dev->gadget);
+ if (retval) {
+ DMSG("bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ dev->driver = 0;
+ return retval;
+ }
+ device_create_file(dev->dev, &dev_attr_function);
+
+ DMSG("registered gadget driver '%s'\n", driver->driver.name);
+ udc_enable(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void stop_activity(struct fhg_usb32_udc *dev,
+ struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = 0;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i <= FHG_USB32_NUM_ENDPOINTS; i++) {
+ struct fhg_usb32_ep *ep = &dev->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ /* TODO: flush Hardwarefifo ?? */
+ }
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver)
+ driver->disconnect(&dev->gadget);
+
+ /* re-init driver-visible data structures */
+ udc_reinit(dev);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct fhg_usb32_udc *dev = the_controller;
+ unsigned long flags;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ udc_disable(dev);
+ stop_activity(dev, driver);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ driver->unbind(&dev->gadget);
+ dev->driver = 0;
+ device_del(&dev->gadget.dev);
+ device_remove_file(dev->dev, &dev_attr_function);
+ del_timer_sync(&isr_lost_timer);
+
+ DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+
+/*-------------------------------------------------------------------------*/
+
+static void send2bytes(u16 status, struct fhg_usb32_ep *ep)
+{
+
+ /* really +4 see write_packet()
+ * we always write only 2bytes here
+ * so there is no need to care about
+ * the end of the fifo
+ */
+ memcpy((char*)in_fifo[0],&status,sizeof(status)+4);
+
+ FHG_USB32_IN_CMD = ((sizeof(status) & USB_IN_COUNT_MSK)|USB_IENA_MSK);
+ ep->dev->stats.write.bytes += sizeof(status);
+ ep->dev->stats.write.ops++;
+ DBG_STATE_MACH("..","EP0_DATA_QUEUED");
+ ep->dev->ep0state = EP0_DATA_QUEUED;
+
+}
+
+static void stall_ep0(struct fhg_usb32_ep *ep)
+{
+ FHG_USB32_OEP_STALL |= 0x1;
+ FHG_USB32_IEP_STALL |= 0x1;
+ ep->dev->stall_state|= 0x1;
+ DBG_STATE_MACH("(all)","EP0_STALLED");
+ ep->dev->ep0state = EP0_STALLED;
+}
+
+/*
+ * handle status request to ep0
+ * some requests needs to behandled here
+ * but most are delegated to upper layer
+ *
+ * We had SETUP transfer from Host to ep0
+ * Next can be Data OUT followed by a ZLP
+ * from function
+ * Or there can be a Data IN followed by
+ * a ZLP from Host
+ *
+ */
+
+static void handle_ep0_out(struct fhg_usb32_ep *ep0)
+{
+ union {
+ u8 raw [8];
+ struct usb_ctrlrequest r;
+ } u;
+
+ int tmp;
+ int n;
+
+ if(ep0->number != 0) {
+ printk("Bogus ep to handle for this!\n");
+ return;
+ }
+
+ FHG_USB32_OUT_EP_SEL = ep0->number;
+ FHG_USB32_IN_EP_SEL = ep0->number;
+
+
+ for(n=0;n<8;n++)
+ u.raw[n] = FHG_USB32_EP0OUT[n];
+
+ ep0->dev->stats.read.bytes += 8;
+ ep0->dev->stats.read.ops++;
+
+ le16_to_cpus(&u.r.wValue);
+ le16_to_cpus(&u.r.wIndex);
+ le16_to_cpus(&u.r.wLength);
+
+ DBG(DBG_VERY_NOISY,"bRequestType: 0x%x; bRequest: 0x%x; "
+ "wValue: 0x%x; wIndex: 0x%x; wLength: 0x%x\n",
+ u.r.bRequestType, u.r.bRequest, u.r.wValue, u.r.wIndex,
+ u.r.wLength);
+
+ nuke(ep0,-EPROTO);
+
+ if ( (u.r.bRequestType & USB_DIR_IN) || (u.r.wLength==0) )
+ /* Data has to be send to Host or a ZLP has to be send to
+ * Host. So the direction for the next transfer is IN
+ */
+ ep0->is_in = 1;
+ else
+ /* The Host is giving out some Data so the direction is OUT */
+ ep0->is_in = 0;
+
+#ifdef DBG_STATE
+ printk("ep0->is_in=%d, len=%d\n",ep0->is_in,u.r.wLength);
+#endif
+
+ switch (u.r.bRequest) {
+ case USB_REQ_GET_STATUS:
+ switch (u.r.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE: {
+ u16 status;
+ /* Tell host that this device is
+ * selfpowered and cannot wake host up
+ *
+ * 2 Bytes are going to be send to HOST
+ * (IN-Transfer)
+ * The Host will ack this with a ZLP
+ *
+ */
+ status = __constant_cpu_to_le16(0x1);
+ send2bytes(status,ep0);
+ DBG(DBG_VERY_NOISY,"%s stat %02x\n", ep0->ep.name,
+ status);
+ break;
+ }
+ case USB_RECIP_INTERFACE: {
+ u16 status;
+ status = __constant_cpu_to_le16(0x0);
+
+ send2bytes(status,ep0);
+ break;
+ }
+ case USB_RECIP_ENDPOINT: {
+ u16 status;
+ u32 to_stall;
+ to_stall = 0x1<<(u.r.wIndex&0xf);
+ if ((FHG_USB32_OEP_STALL & to_stall)
+ ||(FHG_USB32_IEP_STALL & to_stall)
+ ||(ep0->dev->stall_state & to_stall))
+ status = __constant_cpu_to_le16(1);
+ else
+ status = __constant_cpu_to_le16(0);
+
+ send2bytes(status,ep0);
+ DBG(DBG_VERY_NOISY,"%s status 0x%02x\n", ep0->ep.name,
+ status);
+
+ break;
+ }
+ default:
+ printk("Strange things happend in: %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+ }
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ switch (u.r.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ printk("Not Implemented. %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+ break;
+ case USB_RECIP_INTERFACE:
+ printk("Not Specified in USB2.0. %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+ break;
+ case USB_RECIP_ENDPOINT: {
+ u32 to_stall;
+ to_stall = 0x1<<(u.r.wIndex&0xf);
+ FHG_USB32_OEP_STALL &= ~(to_stall);
+ FHG_USB32_IEP_STALL &= ~(to_stall);
+ ep0->dev->stall_state &= ~(to_stall);
+ /* send a zlp */
+ FHG_USB32_IN_CMD = (0 & USB_IN_COUNT_MSK)
+ |USB_IENA_MSK;
+ ep0->dev->stats.write.ops++;
+ DBG(DBG_VERY_NOISY,
+ "%s clear halt for ep: 0x%x\n",
+ ep0->ep.name, u.r.wIndex);
+ DBG_STATE_MACH("..","EP0_ZLP_QUEUED");
+ ep0->dev->ep0state = EP0_ZLP_QUEUED;
+ break;
+ }
+ default:
+ printk("Strange things happend in: %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+ }
+ break;
+ case USB_REQ_SET_FEATURE:
+ switch (u.r.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ printk("Not Implemented. %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+ break;
+ case USB_RECIP_INTERFACE:
+ printk("Not Specified in USB2.0. %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+ break;
+ case USB_RECIP_ENDPOINT: {
+ u32 to_stall;
+ to_stall = 0x1<<(u.r.wIndex&0xf);
+ FHG_USB32_OEP_STALL |= to_stall;
+ FHG_USB32_IEP_STALL |= to_stall;
+ ep0->dev->stall_state |= to_stall;
+ /* send a zlp */
+ FHG_USB32_IN_CMD = (0 & USB_IN_COUNT_MSK)
+ |USB_IENA_MSK;
+ ep0->dev->stats.write.ops++;
+ DBG(DBG_VERY_NOISY,
+ "%s set halt for ep: 0x%x\n",
+ ep0->ep.name, u.r.wIndex);
+ DBG_STATE_MACH("..","EP0_ZLP_QUEUED");
+ ep0->dev->ep0state = EP0_ZLP_QUEUED;
+ break;
+ }
+ default:
+ printk("Strange things happend in: %s:%d\n",
+ __FILE__,__LINE__);
+ stall_ep0(ep0);
+
+ }
+ break;
+ case USB_REQ_SET_ADDRESS:
+ if(u.r.bRequestType != USB_RECIP_DEVICE)
+ goto delegate;
+
+ /* send a zlp, but request is not yet finished */
+ FHG_USB32_IN_CMD = (0 & USB_IN_COUNT_MSK)|USB_IENA_MSK;
+ ep0->dev->stats.write.ops++;
+
+ ep0->dev->function_address = u.r.wValue;
+ ep0->dev->set_function_address = 1;
+
+ DBG_STATE_MACH("..","EP0_ZLP_QUEUED");
+ ep0->dev->ep0state = EP0_ZLP_QUEUED;
+ break;
+ default:
+ delegate:
+ if (u.r.wLength > 0 && ep0->is_in) {
+ DBG_STATE_MACH("..","EP0_DATA_TO_QUEUE");
+ ep0->dev->ep0state = EP0_DATA_TO_QUEUE;
+ }
+ if (u.r.wLength == 0) {
+ DBG_STATE_MACH("..","EP0_ZLP_TO_QUEUE");
+ ep0->dev->ep0state = EP0_ZLP_TO_QUEUE;
+ }
+ if (u.r.wLength > 0 && !(ep0->is_in)) {
+ DBG_STATE_MACH("..","EP0_EMPTY_TO_QUEUE");
+ ep0->dev->ep0state = EP0_EMPTY_TO_QUEUE;
+ }
+
+ spin_unlock(ep0->dev->lock);
+ tmp = ep0->dev->driver->setup(&ep0->dev->gadget, &u.r);
+ spin_lock(ep0->dev->lock);
+ if (tmp<0) {
+ DBG(DBG_NORMAL,"Error from setup call: %d\n",
+ tmp);
+ /* error so stall ep0 */
+ FHG_USB32_OEP_STALL |= 0x1;
+ FHG_USB32_IEP_STALL |= 0x1;
+ ep0->dev->stall_state|= 0x1;
+ DBG_STATE_MACH("..","EP0_STALLED");
+ ep0->dev->ep0state = EP0_STALLED;
+ }
+ }
+ /*Flush Fifo*/
+ FHG_USB32_OUT_CMD |= USB_FLUSH_MSK;
+}
+
+/*
+ *IN transfer on ep has completed
+ */
+
+static void handle_ep_in(struct fhg_usb32_ep *ep)
+{
+ struct fhg_usb32_request *req;
+
+ if (ep->number == 0) {
+ if (ep->dev->ep0state == EP0_ZLP_QUEUED) {
+ if (ep->dev->set_function_address) {
+ FHG_USB32_FUNC_ADDR =
+ ep->dev->function_address;
+ printk("USB-Functionaddress was set to %d\n",
+ ep->dev->function_address);
+ ep->dev->set_function_address = 0;
+ }
+ DBG_STATE_MACH("EP0_ZLP_QUEUED","EP0_IDLE");
+ ep->dev->ep0state = EP0_IDLE;
+ } else if (ep->dev->ep0state == EP0_ZLP_TO_QUEUE) {
+ /* all of previously queued IN-DATA have finished
+ * now we should wait for the ACK'ing ZLP from HOST
+ * fhg_usb32 needs no preparation to receive ZLP on ep0
+ */
+
+ ep->dev->ep0state = EP0_WAIT4ZLP;
+ DBG_STATE_MACH("EP0_ZLP_TO_QUEUE","EP0_WAIT4ZLP");
+ ep->is_in = 0;
+ } else if (ep->dev->ep0state == EP0_DATA_QUEUED) {
+ /* Some IN-DATA are in queue write fifo will proceeded them */
+ } else {
+ printk("Strange state:%d %s:%d\n",ep->dev->ep0state,
+ __FILE__,__LINE__);
+ }
+ }
+
+ if (likely (!list_empty(&ep->queue)))
+ req = list_entry(ep->queue.next, struct fhg_usb32_request, queue);
+ else
+ req = 0;
+
+ if (req)
+ write_fifo(ep, req);
+ else {
+ if (ep->number == 0) {
+ if (ep->dev->ep0state == EP0_DATA_QUEUED) {
+ DBG_STATE_MACH("EP0_DATA_QUEUED","EP0_WAIT4ZLP");
+ ep->dev->ep0state = EP0_WAIT4ZLP;
+ }
+ }
+ }
+ ep->irqs++;
+}
+
+static void handle_ep_out(struct fhg_usb32_ep *ep)
+{
+ struct fhg_usb32_request *req;
+ int completed;
+
+ {
+ completed = 0;
+ if (likely(!list_empty(&ep->queue)))
+ req = list_entry(ep->queue.next, struct fhg_usb32_request,
+ queue);
+ else
+ req = 0;
+
+ if (ep->number == 0) {
+ FHG_USB32_OUT_EP_SEL = 0;
+ if (FHG_USB32_OUT_STAT & USB_SETUP_MSK) {
+ /*
+ * Even if ep0 was stalled after receiving a
+ * STATUS it is at least IDLE (and most
+ * probably will change next)
+ */
+ ep->dev->ep0state = EP0_IDLE;
+ handle_ep0_out(ep);
+ goto done;
+ }
+
+ }
+ if (req) {
+ completed = read_fifo(ep, req);
+ } else {
+ if (ep->number != 0)
+ out_irq_disable(ep->number);
+ else { /* ep0 */
+ if (ep->dev->ep0state == EP0_WAIT4ZLP) {
+ ep->dev->ep0state = EP0_IDLE;
+ DBG_STATE_MACH("EP0_WAIT4ZLP",
+ "EP0_IDLE");
+ }
+ }
+ }
+
+done:
+ ep->irqs++;
+ }
+}
+
+/*
+ * Entering the Default state Fig. 9-1
+ */
+static void goto_default_state(void)
+{
+ FHG_USB32_MAIN_EVENT_MSK |= (USB_OUTE_MSK | USB_INE_MSK);
+ FHG_USB32_IN_EVENT_MSK = 0x1;
+ FHG_USB32_OUT_EVENT_MSK = 0x1;
+}
+
+/*
+ * fhg_usb32_udc_irq - interrupt handler
+ *
+ */
+static irqreturn_t fhg_usb32_udc_irq(int irq, void *_dev, struct pt_regs *r)
+{
+ struct fhg_usb32_udc *dev = _dev;
+ int handled, out_ep_sel, in_ep_sel;
+ unsigned long flags;
+
+ /* parts of these registers are clear on read */
+ u32 main_event, static_event, in_event, out_event;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ out_ep_sel = FHG_USB32_OUT_EP_SEL;
+ in_ep_sel = FHG_USB32_IN_EP_SEL;
+
+ handled = 0;
+
+ main_event = FHG_USB32_MAIN_EVENT;
+ FHG_USB32_MAIN_EVENT = 0;
+
+ while(main_event) {
+
+ dev->stats.irqs++;
+
+ out_event = FHG_USB32_OUT_EVENT;
+ FHG_USB32_OUT_EVENT = 0;
+ in_event = FHG_USB32_IN_EVENT;
+ FHG_USB32_IN_EVENT = 0;
+
+ while (out_event | in_event) {
+ /* In/Out event occured */
+ int i;
+ u16 event_msk;
+
+ event_msk = 0x1;
+ for (i=0;i<=FHG_USB32_NUM_ENDPOINTS;i++) {
+ if (out_event & event_msk) {
+ DBG(DBG_VERY_NOISY,
+ "USB OUT-Event event, ep: %d\n",i);
+ handle_ep_out(&dev->ep[i]);
+ handled = 1;
+ }
+ if (in_event & event_msk) {
+ DBG(DBG_VERY_NOISY,
+ "USB IN-Event event, ep: %d\n",i);
+ handle_ep_in(&dev->ep[i]);
+ handled = 1;
+ }
+ event_msk = event_msk << 1;
+ }
+ if (!handled)
+ printk("Bogus IN event Interrupt; line: %d\n",
+ __LINE__);
+
+ out_event = FHG_USB32_OUT_EVENT;
+ FHG_USB32_OUT_EVENT = 0;
+ in_event = FHG_USB32_IN_EVENT;
+ FHG_USB32_IN_EVENT = 0;
+ }
+
+ static_event = FHG_USB32_STATIC_EVENT;
+ FHG_USB32_STATIC_EVENT = 0;
+
+ if (static_event) {
+ /* Static event */
+ if (static_event & USB_URESE_MSK) {
+ /* USB Reset */
+ DBG(DBG_VERY_NOISY,"rst\n");
+
+ stop_activity(dev, dev->driver);
+ goto_default_state();
+ dev->gadget.speed = USB_SPEED_FULL;
+ dev->set_function_address = 0;
+ dev->function_address = 0;
+
+ handled = 1;
+ }
+ if (static_event & USB_SUSPE_MSK) {
+ /* Suspend event */
+ printk("susp\n");
+ handled = 1;
+ }
+ if (static_event & USB_RSUE_MSK) {
+ /* Resume Event */
+ printk("USB Resume event\n");
+ FHG_USB32_CTRL &= ~(USB_RESU_MSK);
+ handled = 1;
+ }
+ if (static_event & USB_WAKE_MSK) {
+ /* External wake event */
+ printk("USB External wake event\n");
+ handled = 1;
+ }
+ if (static_event & USB_RSUCE_MSK) {
+ /* Resume Complete */
+ printk("USB Resume complete event\n");
+ handled = 1;
+ }
+ if (!handled) {
+ printk("Bogus Static event Interrupt; "
+ "line: %d\n",__LINE__);
+ }
+ }
+ if (main_event & USB_FRME_MSK) {
+ /* Frame event */
+ handled = 1;
+ }
+ if (main_event & USB_ZOE_MSK) {
+ /* Zero Out EP0 Event */
+ if (dev->ep0state == EP0_WAIT4ZLP) {
+ dev->ep0state = EP0_IDLE;
+ DBG_STATE_MACH("EP0_WAIT4ZLP","EP0_IDLE");
+ }
+ else
+ printk("Strange state=%d; %s:%d\n",
+ dev->ep0state,__FILE__,__LINE__);
+
+ handled = 1;
+ }
+ if (!handled) {
+ printk("Bogus USB Interrupt; line: %d\n",__LINE__);
+ }
+
+ main_event = FHG_USB32_MAIN_EVENT;
+ main_event &= 0x30; /* killall except i/o events */
+ FHG_USB32_MAIN_EVENT = 0;
+
+ } /* while */
+
+ FHG_USB32_OUT_EP_SEL = out_ep_sel;
+ FHG_USB32_IN_EP_SEL = in_ep_sel;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void nop_release(struct device *dev)
+{
+ DMSG("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+/* supports only one fhg_usb32 */
+static struct fhg_usb32_udc memory;
+
+/*
+ * init datastructures
+ * this ugly because it should compile with gcc 2.95
+ */
+static void init_static(void)
+{
+
+ int i;
+
+ memory.gadget.ops = &fhg_usb32_udc_ops;
+ memory.gadget.ep0 = &memory.ep[0].ep;
+ memory.gadget.name = driver_name;
+ strcpy(memory.gadget.dev.bus_id, "gadget");
+ memory.gadget.dev.release = nop_release;
+
+ /* control endpoint */
+ memory.ep[0].ep.name = ep0name;
+ memory.ep[0].ep.ops = &fhg_usb32_ep_ops;
+ memory.ep[0].ep.maxpacket = FIFO_SIZE;
+ memory.ep[0].dev = &memory;
+ memory.ep[0].fifo_size = FIFO_SIZE;
+ memory.ep[0].number = 0;
+ memory.ep[0].is_in = 0;
+ memory.ep[0].is_iso = 0;
+
+ memory.ep[1].ep.name = "ep1";
+ memory.ep[1].ep.ops = &fhg_usb32_ep_ops;
+ memory.ep[1].ep.maxpacket = FIFO_SIZE;
+ memory.ep[1].dev = &memory;
+ memory.ep[1].fifo_size = FIFO_SIZE;
+ memory.ep[1].number = 1;
+ memory.ep[1].is_in = 0;
+ memory.ep[1].is_iso = 0;
+
+ memory.ep[2].ep.name = "ep2";
+ memory.ep[2].ep.ops = &fhg_usb32_ep_ops;
+ memory.ep[2].ep.maxpacket = FIFO_SIZE;
+ memory.ep[2].dev = &memory;
+ memory.ep[2].fifo_size = FIFO_SIZE;
+ memory.ep[2].is_in = 0;
+ memory.ep[2].is_iso = 0;
+ memory.ep[2].number = 2;
+
+ memory.ep[3].ep.name = "ep3";
+ memory.ep[3].ep.ops = &fhg_usb32_ep_ops;
+ memory.ep[3].ep.maxpacket = FIFO_SIZE;
+ memory.ep[3].dev = &memory;
+ memory.ep[3].fifo_size = FIFO_SIZE;
+ memory.ep[3].is_in = 0;
+ memory.ep[3].is_iso = 0;
+ memory.ep[3].number = 3;
+
+ memory.ep[4].ep.name = "ep4";
+ memory.ep[4].ep.ops = &fhg_usb32_ep_ops;
+ memory.ep[4].ep.maxpacket = FIFO_SIZE;
+ memory.ep[4].dev = &memory;
+ memory.ep[4].fifo_size = FIFO_SIZE;
+ memory.ep[4].is_in = 0;
+ memory.ep[4].is_iso = 0;
+ memory.ep[4].number = 4;
+
+ memory.ep[5].ep.name = "ep5";
+ memory.ep[5].ep.ops = &fhg_usb32_ep_ops;
+ memory.ep[5].ep.maxpacket = FIFO_SIZE;
+ memory.ep[5].dev = &memory;
+ memory.ep[5].fifo_size = FIFO_SIZE;
+ memory.ep[5].is_in = 0;
+ memory.ep[5].is_iso = 0;
+ memory.ep[5].number = 5;
+
+ for(i=0;i<=FHG_USB32_NUM_ENDPOINTS;i++) {
+ in_fifo[i] = (FHG_USB32_FIFO_ptr)fifo_base + i*FIFO_SIZE;
+ }
+ for(i=0;i<=FHG_USB32_NUM_ENDPOINTS;i++) {
+ out_fifo[i] = (FHG_USB32_FIFO_ptr)fifo_base +
+ (i+FHG_USB32_NUM_ENDPOINTS+1)*FIFO_SIZE;
+ }
+
+}
+
+static int __init fhg_usb32_udc_probe(struct device *_dev)
+{
+ struct fhg_usb32_udc *dev = &memory;
+ int retval;
+
+ init_static();
+
+ spin_lock_init(&dev->lock);
+ dev->dev = _dev;
+
+ device_initialize(&dev->gadget.dev);
+ dev->gadget.dev.parent = _dev;
+
+ the_controller = dev;
+ dev_set_drvdata(_dev, dev);
+
+ udc_disable(dev);
+ udc_reinit(dev);
+
+ retval = (int)request_mem_region(ctrl_base,ADR_FHG_USB32_CTRL_LENGTH,
+ "fhg_usb32 Control");
+ if (retval == 0) {
+ printk(KERN_ERR "%s: can't get mem region 0x%x for Control, "
+ "err %d\n", driver_name, ctrl_base, retval);
+ goto no_mem_ctrl;
+ }
+
+ retval = (int)request_mem_region(fifo_base,ADR_FHG_USB32_FIFO_LENGTH,
+ "fhg_usb32 Fifos");
+ if (retval == 0) {
+ printk(KERN_ERR "%s: can't get mem region 0x%x for Fifos, err "
+ "%d\n", driver_name, fifo_base, retval);
+ goto no_mem_fifo;
+ }
+
+ if(((FHG_USB32_MODE_CRTL&0x3c)>>2)!=0xf) {
+ printk(KERN_ERR "%s: no device available at 0x%x\n",
+ driver_name, ctrl_base);
+ goto no_dev;
+ }
+
+ /* irq setup after old hardware state is cleaned up */
+ retval = request_irq(IRQ_USB, fhg_usb32_udc_irq, SA_INTERRUPT,
+ driver_name, dev);
+ if (retval != 0) {
+ printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
+ IRQ_USB, retval);
+ goto no_irq;
+ }
+ dev->got_irq = 1;
+ disable_irq(IRQ_USB);
+
+#ifdef CONFIG_DEBUG_FS
+ debug_dir = debugfs_create_dir(driver_name, NULL);
+ debug_dentry = debugfs_create_file("info", 0444, debug_dir,
+ dev, &debug_fops);
+#endif
+ return 0;
+
+no_irq:
+no_dev:
+ release_mem_region(fifo_base,ADR_FHG_USB32_FIFO_LENGTH);
+
+no_mem_fifo:
+ release_mem_region(ctrl_base,ADR_FHG_USB32_CTRL_LENGTH);
+
+no_mem_ctrl:
+ return -EBUSY;
+
+}
+static int __exit fhg_usb32_udc_remove(struct device *_dev)
+{
+ struct fhg_usb32_udc *dev = the_controller;
+
+ udc_disable(dev);
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debug_dentry);
+ debugfs_remove(debug_dir);
+#endif
+ usb_gadget_unregister_driver(dev->driver);
+
+ if (dev->got_irq) {
+ free_irq(IRQ_USB, dev);
+ dev->got_irq = 0;
+ }
+
+ release_mem_region(fifo_base,ADR_FHG_USB32_FIFO_LENGTH);
+ release_mem_region(ctrl_base,ADR_FHG_USB32_CTRL_LENGTH);
+
+ dev_set_drvdata(_dev, 0);
+ the_controller = 0;
+
+ printk("FHG_USB32 disabled\n");
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct device_driver udc_driver = {
+ .name = "fhg_usb32-udc",
+ .bus = &platform_bus_type,
+ .probe = fhg_usb32_udc_probe,
+ .remove = __exit_p(fhg_usb32_udc_remove),
+
+};
+
+
+static int __init udc_init(void)
+{
+ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
+ return driver_register(&udc_driver);
+}
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+ driver_unregister(&udc_driver);
+}
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Thomas Brinker, David Brownell");
+MODULE_LICENSE("GPL");
diff -bBurNp linux-2.6.13/drivers/usb/gadget_ori/fhg_usb32.h
linux-2.6.13/drivers/usb/gadget/fhg_usb32.h
--- linux-2.6.13/drivers/usb/gadget_ori/fhg_usb32.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13/drivers/usb/gadget/fhg_usb32.h 2005-10-03 17:58:31.000000000 +0200
@@ -0,0 +1,334 @@
+/*
+ * linux/drivers/usb/gadget/fhg_usb32.h
+ *
+ * Copyright (C) 2004 Thomas Brinker
+ *
+ *
+ * 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
+ */
+
+#ifndef __LINUX_USB_GADGET_FHG_USB32_H
+#define __LINUX_USB_GADGET_FHG_USB32_H
+
+#include <linux/types.h>
+
+#define DMSG(stuff...) printk("udc: " stuff)
+
+#define DBG_NORMAL 1 /* error paths, device state transitions */
+#define DBG_VERBOSE 2 /* add some success path trace info */
+#define DBG_NOISY 3 /* ... even more: request level */
+#define DBG_VERY_NOISY 4 /* ... even more: packet level */
+
+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
+
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+/* Debugging the state machine*/
+#ifdef DBG_STATE
+#define DBG_STATE_MACH(x,y) printk("udc state: %s -> %s\n",x,y)
+#else
+#define DBG_STATE_MACH(x,y)
+#endif
+
+#ifndef IRQ_USB
+#define IRQ_USB 0
+#endif
+
+
+#define FHG_USB32_NUM_ENDPOINTS 5
+#define FIFO_SIZE ((unsigned)64)
+
+/* ------------------IO-Ports--------------*/
+#define ADR_LOW_LEV 0x00110000
+#define ADR_PLL_12000_CONFIG (( volatile u32 *)(ADR_LOW_LEV + 0x00000030))
+#define P2001_PLL_12000_CONFIG (*(ADR_PLL_12000_CONFIG))
+
+
+#define ADR_FHG_USB32_STD_BASE 0x00190000
+
+#define ADR_FHG_USB32 ctrl_base
+
+#define ADR_FHG_USB32_FUNC_ADDR (( volatile u32 *)(ADR_FHG_USB32 + 0x00000000))
+#define FHG_USB32_FUNC_ADDR (*(ADR_FHG_USB32_FUNC_ADDR))
+
+#define ADR_FHG_USB32_MODE_CRTL (( volatile u32 *)(ADR_FHG_USB32 + 0x00000004))
+#define FHG_USB32_MODE_CRTL (*(ADR_FHG_USB32_MODE_CRTL))
+#define USB_SCRATCH_MSK (0x3ff << 6)
+#define USB_REVISION_MSK (0xf << 2)
+#define USB_INTE_MSK (0x1 << 1)
+#define USB_IPOL_MSK (0x1 << 0)
+
+#define ADR_FHG_USB32_CTRL (( volatile u32 *)(ADR_FHG_USB32 + 0x00000008))
+#define FHG_USB32_CTRL (*(ADR_FHG_USB32_CTRL))
+#define USB_SUSP_MSK (0x1 << 2)
+#define USB_NAT_MSK (0x1 << 1)
+#define USB_RESU_MSK (0x1 << 0)
+
+#define ADR_FHG_USB32_MAIN_EVENT (( volatile u32 *)(ADR_FHG_USB32 + 0x0000000c))
+#define FHG_USB32_MAIN_EVENT (*(ADR_FHG_USB32_MAIN_EVENT))
+#define USB_OUTE_MSK (0x1 << 5)
+#define USB_INE_MSK (0x1 << 4)
+/* 3 is missing */
+#define USB_ZOE_MSK (0x1 << 2)
+#define USB_FRME_MSK (0x1 << 1)
+#define USB_STE_MSK (0x1 << 0)
+
+#define ADR_FHG_USB32_MAIN_EVENT_MSK (( volatile u32 *)(ADR_FHG_USB32 + 0x00000010))
+#define FHG_USB32_MAIN_EVENT_MSK (*(ADR_FHG_USB32_MAIN_EVENT_MSK))
+
+#define ADR_FHG_USB32_STATIC_EVENT (( volatile u32 *)(ADR_FHG_USB32 + 0x00000014))
+#define FHG_USB32_STATIC_EVENT (*(ADR_FHG_USB32_STATIC_EVENT))
+#define USB_RSUCE_MSK (0x1 << 4)
+#define USB_WAKE_MSK (0x1 << 3)
+#define USB_RSUE_MSK (0x1 << 2)
+#define USB_SUSPE_MSK (0x1 << 1)
+#define USB_URESE_MSK (0x1 << 0)
+
+#define ADR_FHG_USB32_STATIC_EVENT_MSK (( volatile u32 *)(ADR_FHG_USB32 + 0x00000018))
+#define FHG_USB32_STATIC_EVENT_MSK (*(ADR_FHG_USB32_STATIC_EVENT_MSK))
+
+#define ADR_FHG_USB32_FRM_TIMER (( volatile u32 *)(ADR_FHG_USB32 + 0x0000001c))
+#define FHG_USB32_FRM_TIMER (*(ADR_FHG_USB32_FRM_TIMER))
+#define USB_FTLOCK_MSK (0x1 << 15)
+#define USB_AGSOF_MSK (0x1 << 14)
+#define USB_FRAME_NR_MSK (0x3ff << 0)
+
+#define ADR_FHG_USB32_OUT_EP_SEL (( volatile u32 *)(ADR_FHG_USB32 + 0x00000020))
+#define FHG_USB32_OUT_EP_SEL (*(ADR_FHG_USB32_OUT_EP_SEL))
+
+#define ADR_FHG_USB32_OUT_DATA (( volatile u32 *)(ADR_FHG_USB32 + 0x00000024))
+#define FHG_USB32_OUT_DATA (*(ADR_FHG_USB32_OUT_DATA))
+
+#define ADR_FHG_USB32_OUT_CMD (( volatile u32 *)(ADR_FHG_USB32 + 0x00000028))
+#define FHG_USB32_OUT_CMD (*(ADR_FHG_USB32_OUT_CMD))
+#define USB_ACD_MSK (0x1 << 1)
+#define USB_FLUSH_MSK (0x1 << 0)
+
+#define ADR_FHG_USB32_OUT_STAT (( volatile u32 *)(ADR_FHG_USB32 + 0x0000002c))
+#define FHG_USB32_OUT_STAT (*(ADR_FHG_USB32_OUT_STAT))
+#define USB_SETUP_MSK (0x1 << 15)
+#define USB_CRCE_MSK (0x1 << 14)
+#define USB_OUT_COUNT_MSK (0x3ff << 0)
+
+#define ADR_FHG_USB32_IN_EP_SEL (( volatile u32 *)(ADR_FHG_USB32 + 0x00000030))
+#define FHG_USB32_IN_EP_SEL (*(ADR_FHG_USB32_IN_EP_SEL))
+
+#define ADR_FHG_USB32_IN_DATA (( volatile u32 *)(ADR_FHG_USB32 + 0x00000034))
+#define FHG_USB32_IN_DATA (*(ADR_FHG_USB32_IN_DATA))
+
+#define ADR_FHG_USB32_IN_CMD (( volatile u32 *)(ADR_FHG_USB32 + 0x00000038))
+#define FHG_USB32_IN_CMD (*(ADR_FHG_USB32_IN_CMD))
+#define USB_ITOG_MSK (0x1 << 15)
+#define USB_IENA_MSK (0x1 << 10)
+#define USB_IN_COUNT_MSK (0x3ff << 0)
+
+#define ADR_FHG_USB32_OEP_ENA (( volatile u32 *)(ADR_FHG_USB32 + 0x00000040))
+#define FHG_USB32_OEP_ENA (*(ADR_FHG_USB32_OEP_ENA))
+
+#define ADR_FHG_USB32_IEP_ENA (( volatile u32 *)(ADR_FHG_USB32 + 0x00000044))
+#define FHG_USB32_IEP_ENA (*(ADR_FHG_USB32_IEP_ENA))
+
+#define ADR_FHG_USB32_OEP_STALL (( volatile u32 *)(ADR_FHG_USB32 + 0x00000048))
+#define FHG_USB32_OEP_STALL (*(ADR_FHG_USB32_OEP_STALL))
+
+#define ADR_FHG_USB32_IEP_STALL (( volatile u32 *)(ADR_FHG_USB32 + 0x0000004c))
+#define FHG_USB32_IEP_STALL (*(ADR_FHG_USB32_IEP_STALL))
+
+#define ADR_FHG_USB32_OUT_EVENT (( volatile u32 *)(ADR_FHG_USB32 + 0x00000050))
+#define FHG_USB32_OUT_EVENT (*(ADR_FHG_USB32_OUT_EVENT))
+
+#define ADR_FHG_USB32_OUT_EVENT_MSK (( volatile u32 *)(ADR_FHG_USB32 + 0x00000054))
+#define FHG_USB32_OUT_EVENT_MSK (*(ADR_FHG_USB32_OUT_EVENT_MSK))
+
+#define ADR_FHG_USB32_IN_EVENT (( volatile u32 *)(ADR_FHG_USB32 + 0x00000058))
+#define FHG_USB32_IN_EVENT (*(ADR_FHG_USB32_IN_EVENT))
+
+#define ADR_FHG_USB32_IN_EVENT_MSK (( volatile u32 *)(ADR_FHG_USB32 + 0x0000005c))
+#define FHG_USB32_IN_EVENT_MSK (*(ADR_FHG_USB32_IN_EVENT_MSK))
+
+#define ADR_FHG_USB32_IN_ISO_CONF (( volatile u32 *)(ADR_FHG_USB32 + 0x00000060))
+#define FHG_USB32_IN_ISO_CONF (*(ADR_FHG_USB32_IN_ISO_CONF))
+
+#define ADR_FHG_USB32_OUT_ISO_CONF (( volatile u32 *)(ADR_FHG_USB32 + 0x00000064))
+#define FHG_USB32_OUT_ISO_CONF (*(ADR_FHG_USB32_OUT_ISO_CONF))
+
+#define ADR_FHG_USB32_IN_PTR (( volatile u32 *)(ADR_FHG_USB32 + 0x00000068))
+#define FHG_USB32_IN_PTR (*(ADR_FHG_USB32_IN_PTR_REG))
+
+#define ADR_FHG_USB32_OUT_PTR (( volatile u32 *)(ADR_FHG_USB32 + 0x0000006c))
+#define FHG_USB32_OUT_PTR (*(ADR_FHG_USB32_OUT_PTR_REG))
+
+#define ADR_FHG_USB32_CTRL_REG (( volatile u32 *)(ADR_FHG_USB32 + 0x0000007c))
+#define FHG_USB32_CTRL_REG (*(ADR_FHG_USB32_CTRL_REG))
+
+#define ADR_FHG_USB32_CTRL_LENGTH 0x80
+
+/* ---fifos--- */
+
+#define ADR_FHG_USB32_FIFO_STD_BASE 0x001A0000
+
+typedef volatile u8 *FHG_USB32_FIFO_ptr;
+#define FHG_USB32_EP0IN ((volatile FHG_USB32_FIFO_ptr) (0x00000000+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP1IN ((volatile FHG_USB32_FIFO_ptr) (0x00000040+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP2IN ((volatile FHG_USB32_FIFO_ptr) (0x00000080+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP3IN ((volatile FHG_USB32_FIFO_ptr) (0x000000C0+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP4IN ((volatile FHG_USB32_FIFO_ptr) (0x00000100+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP5IN ((volatile FHG_USB32_FIFO_ptr) (0x00000140+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP0OUT ((volatile FHG_USB32_FIFO_ptr) (0x00000180+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP1OUT ((volatile FHG_USB32_FIFO_ptr) (0x000001C0+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP2OUT ((volatile FHG_USB32_FIFO_ptr) (0x00000200+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP3OUT ((volatile FHG_USB32_FIFO_ptr) (0x00000240+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP4OUT ((volatile FHG_USB32_FIFO_ptr) (0x00000280+ADR_FHG_USB32_FIFO_STD_BASE))
+#define FHG_USB32_EP5OUT ((volatile FHG_USB32_FIFO_ptr) (0x000002C0+ADR_FHG_USB32_FIFO_STD_BASE))
+#define ADR_FHG_USB32_FIFO_LENGTH 0x300
+
+
+volatile FHG_USB32_FIFO_ptr in_fifo[6] = {
+ FHG_USB32_EP0IN,
+ FHG_USB32_EP1IN,
+ FHG_USB32_EP2IN,
+ FHG_USB32_EP3IN,
+ FHG_USB32_EP4IN,
+ FHG_USB32_EP5IN};
+
+volatile FHG_USB32_FIFO_ptr out_fifo[6] = {
+ FHG_USB32_EP0OUT,
+ FHG_USB32_EP1OUT,
+ FHG_USB32_EP2OUT,
+ FHG_USB32_EP3OUT,
+ FHG_USB32_EP4OUT,
+ FHG_USB32_EP5OUT};
+
+/*-------------------------------------------------------------------------*/
+
+static uint ctrl_base = ADR_FHG_USB32_STD_BASE;
+static uint fifo_base = ADR_FHG_USB32_FIFO_STD_BASE;
+
+struct fhg_usb32_udc;
+
+struct fhg_usb32_ep {
+ struct usb_ep ep;
+ struct fhg_usb32_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long irqs;
+
+ unsigned short fifo_size;
+ char number;
+
+ unsigned stopped : 1,
+ is_iso : 1,
+ is_in : 1 ;
+};
+
+struct fhg_usb32_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+enum ep0_state {
+ EP0_IDLE = 0,
+ EP0_WAIT4ZLP = 1,
+ EP0_ZLP_QUEUED = 2,
+ EP0_WAIT4DATA = 3,
+ EP0_STALLED = 4,
+ EP0_ZLP_TO_QUEUE = 5,
+ EP0_DATA_TO_QUEUE = 6,
+ EP0_DATA_QUEUED = 7,
+ EP0_EMPTY_TO_QUEUE = 8,
+};
+
+
+
+struct udc_stats {
+ struct ep0stats {
+ unsigned long ops;
+ unsigned long bytes;
+ } read, write;
+ unsigned long irqs;
+};
+
+
+struct fhg_usb32_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ enum ep0_state ep0state;
+ struct udc_stats stats;
+ unsigned got_irq : 1,
+ got_disc : 1,
+ has_cfr : 1,
+ req_pending : 1,
+ req_std : 1,
+ req_config : 1;
+
+ struct device *dev;
+ spinlock_t lock;
+ struct fhg_usb32_ep ep[FHG_USB32_NUM_ENDPOINTS+1];
+ int set_function_address;
+ int function_address;
+ u32 stall_state;
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct fhg_usb32_udc *the_controller;
+
+/* one GPIO should be used to detect host disconnect */
+static inline int is_usb_connected(void)
+{
+ return !(FHG_USB32_CTRL_REG&0x1<<18);
+}
+
+/* one GPIO should force the host to see this device (or not) */
+static inline void make_usb_disappear(void)
+{
+ /* Hardwarespeed select via Pullup */
+ FHG_USB32_CTRL_REG &= ~(0x1<<16);
+ FHG_USB32_CTRL &= ~(USB_NAT_MSK);
+}
+
+static inline void let_usb_appear(void)
+{
+ FHG_USB32_CTRL |= USB_NAT_MSK;
+
+ /* Hardwarespeed select via Pullup */
+ FHG_USB32_CTRL_REG |= (0x3<<16);
+}
+
+static inline void in_irq_enable(int ep_number)
+{
+ FHG_USB32_IN_EVENT_MSK |= 0x1 << ep_number;
+}
+
+static inline void in_irq_disable(int ep_number)
+{
+ FHG_USB32_IN_EVENT_MSK &= ~(0x1 << ep_number);
+}
+
+static inline void out_irq_enable(int ep_number)
+{
+ FHG_USB32_OUT_EVENT_MSK |= 0x1 << ep_number;
+}
+
+static inline void out_irq_disable(int ep_number)
+{
+ FHG_USB32_OUT_EVENT_MSK &= ~(0x1 << ep_number);
+}
+
+
+#endif /* __LINUX_USB_GADGET_FHG_USB32_H */
diff -bBurNp linux-2.6.13/drivers/usb/gadget_ori/Kconfig linux-2.6.13/drivers/usb/gadget/Kconfig
--- linux-2.6.13/drivers/usb/gadget_ori/Kconfig 2005-10-03 18:45:59.000000000 +0200
+++ linux-2.6.13/drivers/usb/gadget/Kconfig 2005-10-03 18:51:17.000000000 +0200
@@ -187,6 +187,18 @@ config USB_OTG
Select this only if your OMAP board has a Mini-AB connector.
+config USB_GADGET_FHG_USB32
+ boolean "FHG_USB32"
+ help
+ Adds support for USB 1.1 Client-only IP-Modul of
+ Fraunhofer Gesellschaft and emsys GmbH. Used e.g. in
+ P2001 of MAZ Brandenburg and many others.
+
+config USB_FHG_USB32
+ tristate
+ depends on USB_GADGET_FHG_USB32
+ default USB_GADGET
+ select USB_GADGET_SELECTED
config USB_GADGET_DUMMY_HCD
boolean "Dummy HCD (DEVELOPMENT)"
diff -bBurNp linux-2.6.13/drivers/usb/gadget_ori/Makefile linux-2.6.13/drivers/usb/gadget/Makefile
--- linux-2.6.13/drivers/usb/gadget_ori/Makefile 2005-10-03 18:45:59.000000000 +0200
+++ linux-2.6.13/drivers/usb/gadget/Makefile 2005-10-03 17:58:31.000000000 +0200
@@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
+obj-$(CONFIG_USB_FHG_USB32) += fhg_usb32.o
#
# USB gadget drivers
-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel