On Fri, 2004-07-02 at 09:02, Eric BENARD / Free wrote: > from the linux-usb-devel mailing list archive, I found that you are the author > of the n9604_udc Linux USB Gadget COntroller Driver.
Hi Eric Sorry, about the delay. It was a long weekend here, and I was out of town learning to windsurf :) I should have submitted this a while ago, but there are a couple of issues. I'm posting this code right now for anyone else to look at or use, but I'd recommend that it not be placed in a bitkeeper tree until it is fixed up. Most importantly is the fact that there seem to be problems with both the 9603 and 9604 chips. If you look in the data sheet section 7.2.17 RXS0, you can get a quick description of how setup packets work. The chip behaves this way almost always, but on rare occasions, that is sometimes very hard to trigger and other times is triggered in only a few minutes, the 2nd read of this register will show a setup packet with 0 bytes. According to my analyzer the 8 bytes were transmitted and the national chip sent an acknowledgment. If I try and read the data out of the fifo, I get the correct data, but at this point endpoint 0 will no longer transfer or receive data. I'm trying to work with National support, but that isn't getting me very far. I wonder if I'm the only one seeing this problem, or if there are lots of other 9603/4 customers of National that see it. I think it might have to do with the fact that RNDIS heavily uses endpoint 0 for packets in both directions, which isn't that common. If you can see a bug in my code affecting this I'd be very grateful. The other issues with the driver are that I created it to use with RNDIS. Meaning that it works as a CDC Ethernet device and a RNDIS device, but is missing functionality that other gadget drivers use, well at least it is totally untested. I just stripped a whole mess of my debugging comments/statements that would only make sense to me, before sending this e-mail. If I stripped too much, don't hesitate to ask any questions. You will probably notice the voodoo variables. I'd like to get rid of these. They are specifically for the purpose of gadget drivers not setting req_zero. Although I noticed recently that the gadget drivers were fixed up to include this information. I've been using this driver on linux 2.4.25, with many patches applied including gadget updates from upstream kernels. To keep things simple I'm just attaching the n9604 files, and you will have to edit the other gadget directory files required to use this driver. David -- ~~~~~~~~~~~~~~~~~~~~~~~~ David Meggy Engineering Technical Solutions Inc. Unit #1 7157 Honeyman St Delta BC Canada, V4G 1E2 www.techsol.ca eMail: [EMAIL PROTECTED] Tel: 604 946 TECH (8324) Fax: 604 946 6445 ~~~~~~~~~~~~~~~~~~~~~~~~
/* * National 9603/4 USB Device Controller driver * Copyright (C) 2004 Technical Solutions Inc. ([EMAIL PROTECTED]) * ported from : * * Toshiba TC86C001 ("Goku-S") USB Device Controller driver * * Copyright (C) 2000-2002 Lineo * by Stuart Lynne, Tom Rushworth, and Bruce Balden * Copyright (C) 2002 Toshiba Corporation * Copyright (C) 2003 MontaVista Software ([EMAIL PROTECTED]) * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ /* * This device has ep0 and six semi-configurable bulk/interrupt endpoints. * * - Endpoint numbering is fixed: * Endpoint 0: ep0 * Endpoint 1: ep1in (tx) * Endpoint 2: ep2out (rx) * Endpoint 3: ep3in (tx) * Endpoint 4: ep4out (rx) * Endpoint 5: ep5in (tx) * Endpoint 6: ep6out (rx) */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/module.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/proc_fs.h> #include <linux/usb_ch9.h> #include <linux/usb_gadget.h> #include <asm/byteorder.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/unaligned.h> int debug = 0; //inline void dprintk(char * dummy, ...) { } #define dprintk(fmt, args...) \ do { if (debug) printk(fmt, ## args); } while(0) #include "n9604.h" #include "n9604regs.h" #define DRIVER_DESC "N9604 USB Device Controller" #define DRIVER_VERSION "8-Jan 2004" static const char driver_name [] = "n9604_udc"; static const char driver_desc [] = DRIVER_DESC; MODULE_AUTHOR("[EMAIL PROTECTED]"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static void nuke(struct n9604_ep *ep, int status); inline void send_zero_length(int endpoint, struct n9604_udc *dev); u8 * USBN9604_Offset; //device virtual address #define USBD_ENABLE_IRQ {h7201_writel( h7201_readl(ETHER_IRQ_IP_OFFSET) | (1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IP_OFFSET); h7201_writel( h7201_readl(ETHER_IRQ_IM_OFFSET) | (1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IM_OFFSET);} #define USBD_DISABLE_IRQ h7201_writel( h7201_readl(ETHER_IRQ_IM_OFFSET) & ~(1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IM_OFFSET); /*-------------------------------------------------------------------------*/ //enable an end point, of description desc static int n9604_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct n9604_udc *dev; struct n9604_ep *ep; u32 mode; u16 max; ep = container_of(_ep, struct n9604_ep, ep); if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dev = ep->dev; if (ep == &dev->ep[0]) return -EINVAL; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; if (ep->num && !(desc->bEndpointAddress & 0x0f)) return -EINVAL; switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: break; default: return -EINVAL; } write_9604((ep->numActual & EPC_EP_MASK) | EPC_EP_EN | (EPC_ISO * ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)), ep->control); if (ep->is_in) write_9604(TXC_FLUSH, ep->command); else { write_9604(RXC_FLUSH, ep->command); write_9604(RXC_RX_EN, ep->command); } /* enabling the no-toggle interrupt mode would need an api hook */ mode = 0; max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); ep->ep.maxpacket = max; ep->stopped = 0; ep->desc = desc; return 0; } //reset an endpoint static void ep_reset(struct n9604_ep *ep) { //TODO: need to disable the interrupt printk("ep%d reset called\n", ep->num); if (ep->num) write_9604(RXC_FLUSH, ep->command);//flush is the same command for receive and transmit else {//ep == 0 write_9604(RXC_FLUSH, RXC0); write_9604(TXC_FLUSH, TXC0); } ep->ep.maxpacket = MAX_FIFO_SIZE; ep->desc = 0; ep->stopped = 1; ep->irqs = 0; } static int n9604_ep_disable(struct usb_ep *_ep) { struct n9604_ep *ep; struct n9604_udc *dev; unsigned long flags; ep = container_of(_ep, struct n9604_ep, ep); if (!_ep || !ep->desc) return -ENODEV; dev = ep->dev; spin_lock_irqsave(&dev->lock, flags); nuke(ep, -ESHUTDOWN); ep_reset(ep); spin_unlock_irqrestore(&dev->lock, flags); return 0; } /*-------------------------------------------------------------------------*/ static struct usb_request * n9604_alloc_request(struct usb_ep *_ep, int gfp_flags) { struct n9604_request *req; if (!_ep) return 0; 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 n9604_free_request(struct usb_ep *_ep, struct usb_request *_req) { struct n9604_request *req; if (!_ep || !_req) return; req = container_of(_req, struct n9604_request, req); kfree(req); } /*-------------------------------------------------------------------------*/ static void done(struct n9604_ep *ep, struct n9604_request *req, int status); static inline int write_packet(struct n9604_ep *ep, u8 *buf, struct n9604_request *req) { unsigned written_length, desired_length, available_length, maximum_length, flags; u8 fifo = ep->fifo; u8 command = ep->command; u8 status = ep->status; if (!ep->num) { fifo = TXD0; command = TXC0; status = TXS0; } desired_length = req->req.length - req->req.actual; available_length = read_9604(status) & TXS_TCOUNT_MASK;//might be greater written_length = 0; maximum_length = 64; while (available_length && (desired_length--) && (maximum_length--)) { write_9604(*buf++,fifo); available_length = (read_9604(status) & TXS_TCOUNT_MASK);//re-check how much is available//-1 is for room written_length++; } req->req.actual += written_length; flags = TXC_TX_EN; if (ep->num) flags |= TXC_LAST; if (ep->toggle) flags |= TXC_TOGGLE; write_9604(flags, command); ep->toggle = !(ep->toggle); if (!written_length) req->req.zero = 0;//just wrote zero bytes, there is no more need for req.zero return written_length; } // return: 0 = still running, 1 = completed, negative = errno static int write_fifo(struct n9604_ep *ep, struct n9604_request *req) { struct n9604_udc *dev = ep->dev; u8 *buf; unsigned count; int is_last; dprintk("write_fifo ep->%d\n", ep->num); buf = req->req.buf + req->req.actual; prefetch(buf); dev = ep->dev; count = write_packet(ep, buf, req); /* last packet often short (sometimes a zlp, especially on ep0) */ if (unlikely((count != ep->ep.maxpacket) && (!ep->num))) {//David: if this code path is being followed, then else then if below wshould also be followed dev->ep[0].stopped = 1; is_last = 1; } else { if (likely(req->req.length != req->req.actual) || req->req.zero) is_last = 0; else is_last = 1; } /* requests complete when all IN data is in the FIFO, * or sometimes later, if a zlp was needed. */ if (is_last) { done(ep, req, 0); return 1; } return 0; } static inline void pio_irq_enable(struct n9604_ep *ep); static int read_fifo(struct n9604_ep *ep, struct n9604_request *req) { u32 size; u8 *buf; int bufferspace_available, fifospace_left, num_bytes_read; int fifo, status; dprintk("Read_fifo ep->%d\n", ep->num); if (!ep->num) { fifo = RXD0; status = RXS0; } else { fifo = ep->fifo; status = ep->status; } num_bytes_read = 0; buf = req->req.buf + req->req.actual; bufferspace_available = req->req.length - req->req.actual; size = read_9604(status) & (RXS_RCOUNTMASK | RXS_RX_ERR);//number of bytes ready to be read (15 if greater than 15) if (ep->num && (size & RXS_RX_ERR)) { printk("DATA ERROR!!!! on ep%d\nFlushing Fifo", ep->num); write_9604(read_9604(ep->command) | RXC_FLUSH, ep->command); goto leave; } size = size & ~RXS_RX_ERR;//clear the bit if (ep->num) fifospace_left = MAX_FIFO_SIZE; else fifospace_left = MAX_EP0_SIZE; loop: /* read all bytes from this packet */ while (size-- != 0) { u8 byte = read_9604(fifo); if (unlikely(bufferspace_available == 0)) { /* this happens when the driver's buffer * is smaller than what the host sent. * discard the extra data in this packet. */ done(ep, req, -EOVERFLOW); return 1; } else { *buf++ = byte; bufferspace_available--; fifospace_left--; num_bytes_read++; } } if ((size = (read_9604(status) & RXS_RCOUNTMASK))) { goto loop;//since there is more data } /* completion */ req->req.actual = req->req.actual + num_bytes_read; if (fifospace_left || req->req.actual == req->req.length) { if (unlikely(ep->num == 0)) { /* non-control endpoints now usable? */ ep->stopped = 1; } done(ep, req, 0); return 1; } leave: pio_irq_enable(ep);//turn the interrupt back on return 0; } /*-------------------------------------------------------------------------*/ static inline void pio_irq_enable(struct n9604_ep *ep)//epnum != 0 { if (ep->is_in) write_9604(read_9604(TXMSK) | 1 << ep->fifoNum | 16 << ep->fifoNum, TXMSK); else { u8 command = ep->command; if (!ep->num) command = RXC0; write_9604(read_9604(RXMSK) | 1 << ep->fifoNum | 16 << ep->fifoNum, RXMSK); write_9604(RXC_RX_EN | RXC_RFWL0 | RXC_RFWL1, command); } } static inline void pio_irq_disable(struct n9604_ep *ep)//epnum != 0 { if (ep->is_in) write_9604(read_9604(TXMSK) & ~(1 << ep->fifoNum) & ~(16 << ep->fifoNum), TXMSK); else write_9604(read_9604(RXMSK) & ~(1 << ep->fifoNum) & ~(16 << ep->fifoNum), RXMSK); } static int request_voodoo = 0;//number of bytes the host requested static inline void pio_advance(struct n9604_ep *ep) { struct n9604_request *req; dprintk("pio_advance ep%d, entering\n", ep->num); if (unlikely(list_empty (&ep->queue))) { if (ep->num == 0) { ep->is_in = 0;//switch modes write_9604(RXC_FLUSH, RXC0);// write_9604(RXC_RX_EN, RXC0);// } return; } req = list_entry(ep->queue.next, struct n9604_request, queue); (ep->is_in ? write_fifo : read_fifo)(ep, req); dprintk("pio_advance ep%d, exiting\n", ep->num); } /*-------------------------------------------------------------------------*/ static void * n9604_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, int gfp_flags) { return kmalloc(bytes, gfp_flags); } static void n9604_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) { kfree (buf); } /*-------------------------------------------------------------------------*/ static void done(struct n9604_ep *ep, struct n9604_request *req, int status) { struct n9604_udc *dev; unsigned stopped = ep->stopped; list_del_init(&req->queue); if (req->req.status == -EINPROGRESS) req->req.status = status; else status = req->req.status; dev = ep->dev; /* don't modify queue heads during completion callback */ ep->stopped = 1; if (ep->num) pio_irq_disable(ep); else { ep->toggle = 1;//other endpoints stay in their flipping mode between transactions if (!ep->is_in) {//TODO: Can this voodoo stuff be simplified ep->is_in = 1;//switch modes request_voodoo = 1;//prevents n9604_queue from calling us again before doing anything send_zero_length(0, dev); } } req->req.complete(&ep->ep, &req->req); ep->stopped = stopped; } /*-------------------------------------------------------------------------*/ static int n9604_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) { struct n9604_request *req; struct n9604_ep *ep; struct n9604_udc *dev; unsigned long flags; int status; /* always require a cpu-view buffer so pio works */ req = container_of(_req, struct n9604_request, req); if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) return -EINVAL; ep = container_of(_ep, struct n9604_ep, ep); if (unlikely(!_ep || (!ep->desc && ep->num != 0))) return -EINVAL; dev = ep->dev; if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; /* can't touch registers when suspended */ spin_lock_irqsave(&dev->lock, flags); dprintk("n9604_queue, ep%d->is_in=%d request length is %d\n", ep->num, ep->is_in, _req->length); if (!ep->num && (!list_empty(&ep->queue))) { printk("\n\n\n\nMEGA ERROR\n\n\nmultiple requests on ep0\n\n\n, list_empty(&ep->queue) = %d\n\n\n", list_empty(&ep->queue)); //while(1); } _req->status = -EINPROGRESS; _req->actual = 0; /* for ep0 IN without premature status, zlp is required and * writing EOP starts the status stage (OUT). */ if (unlikely(ep->num == 0)) { if ((request_voodoo > _req->length) && !(_req->length % MAX_EP0_SIZE) && (_req->length != 0)) { _req->zero = 1; //Dave } if (!request_voodoo && !ep->is_in) {//this is a zero length request spin_unlock_irqrestore(&dev->lock, flags); done(ep, req, 0); return 0; } } /* kickstart this i/o queue? */ status = 0; if (list_empty(&ep->queue) /*&& ep->stopped*/ && ep->is_in) { status = (ep->is_in ? write_fifo : read_fifo)(ep, req); if (status != 0) { if (status > 0) status = 0; req = 0; } } /* else pio or dma irq handler advances the queue. */ if (likely(req != 0)) list_add_tail(&req->queue, &ep->queue); if (likely(!list_empty(&ep->queue)) && likely(ep->num != 0))//Dave: Should probably only be called if irq not active pio_irq_enable(ep); spin_unlock_irqrestore(&dev->lock, flags); return status; } /* dequeue ALL requests */ static void nuke(struct n9604_ep *ep, int status) { struct n9604_request *req; ep->stopped = 1; if (list_empty(&ep->queue)) return; while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct n9604_request, queue); done(ep, req, status); } } /* dequeue JUST ONE request */ static int n9604_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct n9604_request *req; struct n9604_ep *ep; struct n9604_udc *dev; unsigned long flags; ep = container_of(_ep, struct n9604_ep, ep); if (!_ep || !_req || (!ep->desc && ep->num != 0)) return -EINVAL; dev = ep->dev; if (!dev->driver) return -ESHUTDOWN; spin_lock_irqsave(&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 (&dev->lock, flags); return -EINVAL; } spin_unlock_irqrestore(&dev->lock, flags); return req ? 0 : -EOPNOTSUPP; } static int n9604_clear_halt(struct usb_ep *_ep) { struct n9604_ep *ep; ep = container_of (_ep, struct n9604_ep, ep); printk("n9604_clear_halt\n"); write_9604(read_9604(ep->control) & ~EPC_STALL, ep->control); if (ep->stopped) { ep->stopped = 0; pio_advance(ep); } return 0; } static int n9604_set_halt(struct usb_ep *_ep, int value) { struct n9604_ep *ep; unsigned long flags; int retval = 0; if (!_ep) { retval = -ENODEV; goto exit; } ep = container_of (_ep, struct n9604_ep, ep); if (ep->num == 0) {//is this valid? if (value) { ep->dev->ep[0].stopped = 1; } else { retval = -EINVAL; goto exit; } /* don't change EPxSTATUS_EP_INVALID to READY */ } else if (!ep->desc) { retval = -EINVAL; goto exit; } spin_lock_irqsave(&ep->dev->lock, flags); if (!list_empty(&ep->queue)) retval = -EAGAIN; else if (!value) n9604_clear_halt(_ep); else { ep->stopped = 1; write_9604(read_9604(ep->control) | EPC_STALL, ep->control); } spin_unlock_irqrestore(&ep->dev->lock, flags); exit: return retval; } static int n9604_fifo_status(struct usb_ep *_ep) {//not implemented return -1; } static void n9604_fifo_flush(struct usb_ep *_ep) {//not implemented struct n9604_ep *ep; ep = container_of (_ep, struct n9604_ep, ep); // write_9604(read_9604(ep->command) | RXC_FLUSH, ep->command);//transmit and receive are the same flush bit } /*-------------------------------------------------------------------------*/ static struct usb_ep_ops n9604_ep_ops = { .enable = n9604_ep_enable, .disable = n9604_ep_disable, .alloc_request = n9604_alloc_request,//io request objects called struct usb_request .free_request = n9604_free_request, .alloc_buffer = n9604_alloc_buffer, .free_buffer = n9604_free_buffer, .queue = n9604_queue,//submit a struct usb_request object to an endpoint .dequeue = n9604_dequeue, .set_halt = n9604_set_halt,//halts an endpoint .fifo_status = n9604_fifo_status,//bytes in FIFO + data ready to go in FIFO .fifo_flush = n9604_fifo_flush,//flush all the data, endpoint is probably been reconfigured }; /*-------------------------------------------------------------------------*/ static int n9604_get_frame(struct usb_gadget *_gadget) { return -EOPNOTSUPP; } static const struct usb_gadget_ops n9604_ops = { .get_frame = n9604_get_frame, }; /*-------------------------------------------------------------------------*/ static void udc_reinit (struct n9604_udc *dev) { static char *names [] = { "ep0", "ep1in", "ep2out", "ep3in", "ep4out", "ep5in", "ep6out" }; unsigned i; INIT_LIST_HEAD (&dev->gadget.ep_list); dev->gadget.ep0 = &dev->ep [0].ep; dev->gadget.speed = USB_SPEED_UNKNOWN; dev->irqs = 0; dev->configured = 0; for (i = 0; i < 7; i++) { struct n9604_ep *ep = &dev->ep[i]; ep->num = i; ep->numActual = i; ep->ep.name = names[i]; ep->irqs = 0; if (i) { ep->fifo = (i * 4) + RXD0; //each FIFO address is 4 bytes away. TXD0 is the first ep->control = ep->fifo - 1; ep->status = ep->fifo + 1; ep->command = ep->fifo + 2; } else //were are endpoint 0 ep->fifo = ep->control = ep->status = ep->command = 0xff;//this should force an error //we need to do this since we don't know if //this is tx or rx ep->is_in = i % 2; ep->fifoNum = (i + ep->is_in) / 2;//ignored for endpoint 0 ep->ep.ops = &n9604_ep_ops; list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ep->dev = dev; INIT_LIST_HEAD (&ep->queue); ep_reset(ep); } dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; list_del_init (&dev->ep[0].ep.ep_list); write_9604(~WKUP_PNDUSB & ~WKUP_PNDUC & read_9604(WKUP), WKUP);//clear the bits, we've done a reset write_9604(FAR_AD_EN, FAR);//enable the chip to answer requests//address 0 dev->address = 0; write_9604(RXC_FLUSH, RXC0);//flush the buffer write_9604(TXC_FLUSH, TXC0);//and this one write_9604(RXC_RX_EN, RXC0);//then enable it write_9604(0, EPC0);//clear the control register write_9604(NFSR_NodeOperational, NFSR);//we're going for gold } static void udc_reset(struct n9604_udc *dev) { USBD_DISABLE_IRQ; write_9604(MCNTRL_SRST,MCNTRL);//software reset -- this also prevents pullup write_9604(0x00, MAMSK); //disable interrupts } static void ep0_start(struct n9604_udc *dev) { udc_reset(dev); //this is to prevent a pullup resistor udc_reinit (dev); dev->gadget.speed = USB_SPEED_FULL; // enable ep0 interrupts dev->ep[0].is_in = 0; write_9604(0, MAEV);//shut off the noise write_9604(0xF7, MAMSK);//for now we turn it all on, except frames write_9604(0, ALTEV); write_9604(0, ALTMSK); write_9604(0, TXEV); write_9604(0x11, TXMSK); write_9604(0, RXEV); write_9604(0x11, RXMSK); write_9604(0, NAKEV); write_9604(0x0, NAKMSK);//David:needs to be fixed later write_9604(0, FWEV); write_9604(0x0, FWMSK);//David:needs to be fixed later write_9604(MCNTRL_NAT | MCNTRL_INTOC_ActHigh, MCNTRL);//this activates the pull-up and turns on interrupts USBD_ENABLE_IRQ; } static void udc_enable(struct n9604_udc *dev) { //We start the USB device now, since we don't know when we are plugged ini // we could also wait for a power signal from the v+ if the chip can detect it // but the n9604 can't do this ep0_start(dev); } /*-------------------------------------------------------------------------*/ /* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second */ static struct n9604_udc *the_controller; /* 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 n9604_udc *dev = the_controller; int retval; if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind || !driver->unbind || !driver->disconnect || !driver->setup) return -EINVAL; if (!dev) return -ENODEV; if (dev->driver) return -EBUSY; /* hook up the driver */ dev->driver = driver; retval = driver->bind(&dev->gadget); if (retval) { dev->driver = 0; return retval; } /* then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ udc_enable(dev); return 0; } EXPORT_SYMBOL(usb_gadget_register_driver); static void stop_activity(struct n9604_udc *dev, struct usb_gadget_driver *driver) { unsigned i; if (dev->gadget.speed == USB_SPEED_UNKNOWN) driver = 0; /* disconnect gadget driver after quiesceing hw and the driver */ udc_reset(dev); for (i = 0; i < 7; i++) nuke(&dev->ep [i], -ESHUTDOWN); if (driver) { spin_unlock(&dev->lock); driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } if (dev->driver) udc_enable(dev);//David: this doesn't seem right } int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { struct n9604_udc *dev = the_controller; unsigned long flags; if (!dev) return -ENODEV; if (!driver || driver != dev->driver) return -EINVAL; spin_lock_irqsave(&dev->lock, flags); dev->driver = 0; stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); driver->unbind(&dev->gadget); return 0; } EXPORT_SYMBOL(usb_gadget_unregister_driver); /*-------------------------------------------------------------------------*/ inline void tx_ev_irq(struct n9604_udc *dev) { u8 mask, tmp; mask = read_9604(TXEV) & read_9604(TXMSK); write_9604(0, TXEV); if (mask & TXEV_FIFO0) { write_9604(0, EPC0);//make sure we are not stalled, & not using the default address tmp = read_9604(TXS0);//should really check for error conditions if (!(tmp & TXS_ACK_STAT)) printk("\n\n\n!!!! Major Problem ep0 status register = 0x%x\n\n\n", tmp); dev->ep[0].irqs++; pio_advance(&dev->ep[0]); } if (mask & TXEV_FIFO1) { tmp = read_9604(TXS1); if (!(tmp & TXS_ACK_STAT)) printk("\n\n\n!!!! Major Problem ep1 status register = 0x%x\n\n\n", tmp); dev->ep[1].irqs++; pio_advance(&dev->ep[1]); } if (mask & TXEV_FIFO2) { tmp = read_9604(TXS2); if (!(tmp & TXS_ACK_STAT)) printk("\n\n\n!!!! Major Problem ep3 status register = 0x%x\n\n\n", tmp); dev->ep[3].irqs++; pio_advance(&dev->ep[3]); } if (mask & TXEV_FIFO3) { tmp = read_9604(TXS3); if (!(tmp & TXS_ACK_STAT)) printk("\n\n\n!!!! Major Problem ep5 status register = 0x%x\n\n\n", tmp); dev->ep[5].irqs++; pio_advance(&dev->ep[5]); } } static void my_req_complete(struct usb_ep *_ep, struct usb_request *req) {//this was for the setup packet, but I guess I could use it for anything n9604_free_buffer(_ep, req->buf, req->dma, req->length); n9604_free_request(_ep, req); } void send_dummy_packet(int endpoint, struct n9604_udc *dev, int length) { struct usb_request *my_req; my_req = n9604_alloc_request(&dev->ep[endpoint].ep, GFP_ATOMIC); my_req->length = length; my_req->buf = n9604_alloc_buffer(&dev->ep[endpoint].ep, length, &my_req->dma, GFP_ATOMIC); my_req->complete = my_req_complete; n9604_queue(&dev->ep[endpoint].ep, my_req, GFP_ATOMIC); } inline void send_zero_length(int endpoint, struct n9604_udc *dev) { send_dummy_packet(endpoint, dev, 0); } inline void rx_ev_irq(struct n9604_udc *dev) { u8 mask; struct n9604_ep *ep; mask = read_9604(RXEV) & read_9604(RXMSK); write_9604(0, RXEV); if (mask & RXEV_FIFO0) { static int read_mode = 0; u8 rxs_mask = read_9604(RXS0); ep = &dev->ep[0]; ep->irqs++; if (rxs_mask & RXS_SETUP) { static int expecting_null = 0; int david_tmp = rxs_mask; struct usb_ctrlrequest ctrl; rxs_mask = read_9604(RXS0);//2nd read (1st one is for zero length packet) if ((rxs_mask & RXS_RCOUNTMASK) != 8) { printk("Setup packet isn't 8 bytes, rxs_mask1 = 0x%x, rxs_mask2 = 0x%x\n", david_tmp, rxs_mask); printk("Continuing anyways\n\n\n\n\n\nn\n\n"); //while(1); } ctrl.bRequestType = read_9604(RXD0); ctrl.bRequest = read_9604(RXD0); ctrl.wValue = read_9604(RXD0) + (read_9604(RXD0) << 8); ctrl.wIndex = read_9604(RXD0) + (read_9604(RXD0) << 8); ctrl.wLength = read_9604(RXD0) + (read_9604(RXD0) << 8); dprintk("ctrl.bRequest = 0x%x\n", ctrl.bRequest); ep->toggle = 1; if ((!dev->configured) && (ctrl.wLength > 8)) ctrl.wLength = 8;//fix for buggy windoze request_voodoo = ctrl.wLength; if (ctrl.bRequestType & 0x80) {//This is an IN transaction expecting_null = 1; ep->is_in = 1; read_mode = 0; if (ctrl.wLength) {//should be followed by ZLP out packet } else {//host expects ZLP out packet ep->stopped = 0; write_9604(RXC_FLUSH, RXC0); write_9604(RXC_RX_EN, RXC0); } } else {//This is an out transaction ep->is_in = 0; if (ctrl.wLength) { ep->stopped = 0; write_9604(RXC_FLUSH, RXC0); write_9604(RXC_RX_EN, RXC0); read_mode = 1; } else {//host expects ZLP in packet read_mode = 0; } } switch (ctrl.bRequest) { case USB_REQ_SET_ADDRESS: write_9604(EPC_DEF, EPC0);//we still want to respond to the default address write_9604(((dev->address = (ctrl.wValue & FAR_AD_MASK))) | FAR_AD_EN, FAR); send_zero_length(0, dev); dev->configured = 1;//we can send longer packets now :) read_9604(ALTEV); write_9604(ALTMSK_RESET, ALTMSK);//we also listen to reset requests too break; case USB_REQ_CLEAR_FEATURE: if (ctrl.wValue == 0 && ctrl.bRequestType == 2) {//endpoint halt int i; for (i = 0; i < 7; i++) if ((ctrl.wIndex & 0xF) == dev->ep[i].numActual) n9604_clear_halt(&dev->ep[i].ep); send_zero_length(0, dev); break; } case USB_REQ_SET_DESCRIPTOR://TODO: which cases should this driver take care of, and which should be passed on case USB_REQ_SYNCH_FRAME: case USB_REQ_GET_STATUS: case USB_REQ_SET_FEATURE: case USB_REQ_SET_CONFIGURATION: case USB_REQ_GET_DESCRIPTOR: case USB_REQ_GET_CONFIGURATION: case USB_REQ_SET_INTERFACE: case USB_REQ_GET_INTERFACE: default: david_tmp = dev->driver->setup(&dev->gadget, &ctrl); if (david_tmp < 0) { printk("Unknown request or problem with request, return value = %d\nbRequestType\t= 0x%x\nbRequest\t= 0x%x\nwValue\t= 0x%x\nwIndex\t= 0x%x\nwLength\t= 0x%x\n", david_tmp, ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); if (((ctrl.bRequestType & 0x80) && ctrl.wLength) || (!(ctrl.bRequestType & 0x80) && !ctrl.wLength)) { printk("Sending Null packet\n"); send_zero_length(0, dev); } } }//crtl.bRequest }//setup else if (read_mode) pio_advance(ep); #ifdef accounting else if (!expecting_null) { printk("\n\n\nBad things happen, unknown packet just showed up\n\n\n\n"); // while (1); } else expecting_null = 0; #endif }//fifo 0 if (mask & RXEV_FIFO1) { ep = &dev->ep[2]; pio_advance(ep); ep->irqs++; } if (mask & RXEV_FIFO2) { ep = &dev->ep[4]; pio_advance(ep); ep->irqs++; } if (mask & RXEV_FIFO3) { ep = &dev->ep[6]; pio_advance(ep); ep->irqs++; } } inline void alt_ev_irq(struct n9604_udc *dev) { u8 mask; mask = read_9604(ALTEV) & read_9604(ALTMSK); write_9604(0, ALTEV); if (mask & ALTEV_EOP); if (mask & ALTEV_SD3); if (mask & ALTEV_SD5); if (mask & ALTEV_RESET) { write_9604(NFSR_NodeReset, NFSR); stop_activity(dev, dev->driver); } if (mask & ALTEV_RESUME); //write_9604(NFSR_NodeOperational, NFSR); if (mask & ALTEV_WKUP);//we don't really sleep if (mask & ALTEV_DMA); } static void n9604_irq(int irq, void *_dev, struct pt_regs *r) { struct n9604_udc *dev = _dev; u8 mask; mask = read_9604(MAEV) & read_9604(MAMSK); if (!mask) return; write_9604(0, MAEV);//clear the interrupts if (mask & MAEV_TX_EV) tx_ev_irq(dev); if (mask & MAEV_RX_EV) rx_ev_irq(dev); if (mask & MAEV_ALT) alt_ev_irq(dev); dev->irqs++; return; } /*-------------------------------------------------------------------------*/ static int __init init (void) { struct n9604_udc *dev; int ret; u8 * addr; if (the_controller) return -EBUSY; addr = ioremap(USBN9604_PHYS, 0x2);//ioremap will bump this to 1 page size if (!addr) { printk(KERN_ERR "Unable to remap address\n"); return -EINVAL; } else printk("Dave:gadget: 0x%x remapped to 0x%x\n", USBN9604_PHYS, (unsigned int)addr); USBN9604_Offset = addr; if ((read_9604(RID) & 0xF) == 0x2)//0x2 is the identifier for 9603/4 dprintk("National 9604 chip found at 0x%x, and mapped to 0x%x\n", USBN9604_PHYS, (unsigned int)USBN9604_Offset); else { dprintk("Can't find National 9604 chip, RID location = 0x%x, OFFSET = 0x%x, RID = 0x%x\n", RID, (unsigned int)USBN9604_Offset, read_9604(RID)); iounmap(addr); return -ENODEV; } /* alloc, and start init */ dev = kmalloc(sizeof *dev, SLAB_KERNEL); if (dev == NULL){ iounmap(addr); return -ENOMEM; } memset(dev, 0, sizeof *dev); spin_lock_init(&dev->lock); dev->gadget.ops = &n9604_ops; dev->gadget.is_dualspeed = 0; /* the "gadget" abstracts/virtualizes the controller */ dev->gadget.dev.bus_id = "gadget"; dev->gadget.name = driver_name; /* initialize the hardware */ udc_reset(dev); write_9604(CCONF_CODIS | 11, CCONF); udc_reinit(dev);//this is necessary as it sets up the epx functions the_controller = dev; if ((ret=request_irq(IRQ_GPIOC, n9604_irq, SA_SHIRQ, driver_name,dev))) { iounmap(addr); return ret; } return 0; } module_init (init); static void __exit cleanup (void) { struct n9604_udc *dev = the_controller; //first kill the interrupts udc_reset(dev); free_irq(IRQ_GPIOC, dev); /* start with the driver above us */ if (dev->driver) { /* should have been done already by driver model core */ usb_gadget_unregister_driver(dev->driver); } kfree(dev); the_controller = 0; } module_exit (cleanup);
/* * National 9604 USB device controller driver * * Copyright 2003 Technical Solutions Inc. * * ported from: * * Toshiba TC86C001 ("Goku-S") USB Device Controller driver * * Copyright (C) 2000-2002 Lineo * by Stuart Lynne, Tom Rushworth, and Bruce Balden * Copyright (C) 2002 Toshiba Corporation * Copyright (C) 2003 MontaVista Software ([EMAIL PROTECTED]) * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #define MAX_FIFO_SIZE 64 #define MAX_EP0_SIZE 8 struct n9604_ep { struct usb_ep ep; struct n9604_udc *dev; unsigned long irqs; unsigned num:4, numActual:4, fifoNum:2, is_in:1, stopped:1, toggle:1; /* analogous to a host-side qh */ struct list_head queue; const struct usb_endpoint_descriptor *desc; u8 control; u8 fifo; u8 status; u8 command; }; struct n9604_request { struct usb_request req; struct list_head queue; unsigned mapped:1; }; struct n9604_udc { struct usb_gadget gadget; spinlock_t lock; struct n9604_ep ep[7]; struct usb_gadget_driver *driver; int configured; u8 address; /* statistics... */ unsigned long irqs; }; /*-------------------------------------------------------------------------*/ /* 2.5 stuff that's sometimes missing in 2.4 */ #ifndef container_of #define container_of list_entry #endif #ifndef likely #define likely(x) (x) #define unlikely(x) (x) #endif #ifndef BUG_ON #define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) #endif #ifndef WARN_ON #define WARN_ON(x) do { } while (0) #endif #ifndef IRQ_NONE typedef void irqreturn_t; #define IRQ_NONE #define IRQ_HANDLED #define IRQ_RETVAL(x) #endif
/* National 9604 registers */ #define USBN9604_PHYS 0x08000000 extern u8 * USBN9604_Offset; static u8 last_address = 255;//an invalid address inline u8 read_9604(u8 addr) { u8 tmp; if (addr != last_address) { outb(addr, USBN9604_Offset + 1); last_address = addr; } tmp = inb(USBN9604_Offset); return tmp; } inline void write_9604(u8 value, u8 addr) { if (addr != last_address) { outb(addr, USBN9604_Offset + 1); last_address = addr; } outb(value, USBN9604_Offset); } #define MCNTRL 0x00 #define CCONF 0x01 #define RID 0x03 #define FAR 0x04 #define NFSR 0x05 #define MAEV 0x06 #define MAMSK 0x07 #define ALTEV 0x08 #define ALTMSK 0x09 #define TXEV 0x0A #define TXMSK 0x0B #define RXEV 0x0C #define RXMSK 0x0D #define NAKEV 0x0E #define NAKMSK 0x0F #define FWEV 0x10 #define FWMSK 0x11 #define FNH 0x12 #define FNL 0x13 #define DMACNTRL 0x14 #define DMAEV 0x15 #define DMAMSK 0x16 #define MIR 0x17 #define DMACNT 0x18 #define DMAERR 0x19 #define WKUP 0x1B #define EPC0 0x20 #define TXD0 0x21 #define TXS0 0x22 #define TXC0 0x23 #define RXD0 0x25 #define RXS0 0x26 #define RXC0 0x27 #define EPC1 0x28 #define TXD1 0x29 #define TXS1 0x2A #define TXC1 0x2B #define EPC2 0x2C #define RXD1 0x2D #define RXS1 0x2E #define RXC1 0x2F #define EPC3 0x30 #define TXD2 0x31 #define TXS2 0x32 #define TXC2 0x33 #define EPC4 0x34 #define RXD2 0x35 #define RXS2 0x36 #define RXC2 0x37 #define EPC5 0x38 #define TXD3 0x39 #define TXS3 0x3A #define TXC3 0x3B #define EPC6 0x3C #define RXD3 0x3D #define RXS3 0x3E #define RXC3 0x3F /* MCNTRL values */ #define MCNTRL_SRST (1 << 0) #define MCNTRL_VGE (1 << 2) #define MCNTRL_NAT (1 << 3) #define MCNTRL_INTOC_MASK (3 << 6) #define MCNTRL_INTOC_DISABLE 0 #define MCNTRL_INTOC_ActLowOpen (1 << 6) #define MCNTRL_INTOC_ActHigh (2 << 6) #define MCNTRL_INTOC_ActLowPP (3 << 6) /* CCONF values */ #define CCONF_CLKDIV_MASK 0x0F #define CCONF_CODIS (1 << 7) /* FAR values */ #define FAR_AD_MASK 0x7F #define FAR_AD_EN 0x80 /* NFSR values */ #define NFSR_NodeReset 0x0 #define NFSR_NodeResume 0x1 #define NFSR_NodeOperational 0x2 #define NFSR_NodeSuspend 0x3 /* MAEV values */ #define MAEV_WARN (1 << 0) #define MAEV_ALT (1 << 1) #define MAEV_TX_EV (1 << 2) #define MAEV_FRAME (1 << 3) #define MAEV_NAK (1 << 4) #define MAEV_ULD (1 << 5) #define MAEV_RX_EV (1 << 6) #define MAEV_INTR (1 << 7) /* MAMSK values */ #define MAMSK_WARN (1 << 0) #define MAMSK_ALT (1 << 1) #define MAMSK_TX_EV (1 << 2) #define MAMSK_FRAME (1 << 3) #define MAMSK_NAK (1 << 4) #define MAMSK_ULD (1 << 5) #define MAMSK_RX_EV (1 << 6) #define MAMSK_INTR (1 << 7) /* ALTEV values */ #define ALTEV_WKUP (1 << 1) #define ALTEV_DMA (1 << 2) #define ALTEV_EOP (1 << 3) #define ALTEV_SD3 (1 << 4) #define ALTEV_SD5 (1 << 5) #define ALTEV_RESET (1 << 6) #define ALTEV_RESUME (1 << 7) /* ALTMSK values */ #define ALTMSK_WKUP (1 << 1) #define ALTMSK_DMA (1 << 2) #define ALTMSK_EOP (1 << 3) #define ALTMSK_SD3 (1 << 4) #define ALTMSK_SD5 (1 << 5) #define ALTMSK_RESET (1 << 6) #define ALTMSK_RESUME (1 << 7) /* NAKEV values */ #define NAKEV_TXFIFO0 (1 << 0) #define NAKEV_TXFIFO1 (1 << 1) #define NAKEV_TXFIFO2 (1 << 2) #define NAKEV_TXFIFO3 (1 << 3) #define NAKEV_RXFIFO0 (1 << 4) #define NAKEV_RXFIFO1 (1 << 5) #define NAKEV_RXFIFO2 (1 << 6) #define NAKEV_RXFIFO3 (1 << 7) /* WKUP values */ #define WKUP_PNDUSB (1 << 0) #define WKUP_PNDUC (1 << 1) #define WKUP_ENUSB (1 << 2) #define WKUP_ENUC (1 << 3) #define WKUP_WKMODE (1 << 5) #define WKUP_HOS (1 << 6) #define WKUP_FHT (1 << 7) /* EPC values */ #define EPC_EP_MASK 0x0F //EP0 == 0 #define EPC_EP_EN (1 << 4)//not EP0 #define EPC_ISO (1 << 5)//not EP0 #define EPC_DEF (1 << 6)//EP0 only #define EPC_STALL (1 << 7) /* TXS values */ #define TXS_TCOUNT_MASK 0x1F #define TXS_TX_DONE (1 << 5) #define TXS_ACK_STAT (1 << 6) #define TXS_TX_URUN (1 << 7) /* TXC values */ #define TXC_TX_EN (1 << 0) #define TXC_LAST (1 << 1)//not for endpoint 0 #define TXC_TOGGLE (1 << 2)//sets DATA1 when set #define TXC_FLUSH (1 << 3) #define TXC_IGN_IN (1 << 4)//only endpoint 0 #define TXC_RFF (1 << 4)//not for endpoint 0 #define TXC_TFWL0 (1 << 5)//" #define TXC_TFWL1 (1 << 6)//" #define TXC_IGN_ISOMSK (1 << 7)//" /* TXEV values */ #define TXEV_FIFO0 (1 << 0) #define TXEV_FIFO1 (1 << 1) #define TXEV_FIFO2 (1 << 2) #define TXEV_FIFO3 (1 << 3) #define TXEV_UDRRN0 (1 << 4) #define TXEV_UDRRN1 (1 << 5) #define TXEV_UDRRN2 (1 << 6) #define TXEV_UDRRN3 (1 << 7) /* RXEV values */ #define RXEV_FIFO0 (1 << 0) #define RXEV_FIFO1 (1 << 1) #define RXEV_FIFO2 (1 << 2) #define RXEV_FIFO3 (1 << 3) #define RXEV_OVRRN0 (1 << 4) #define RXEV_OVRRN1 (1 << 5) #define RXEV_OVRRN2 (1 << 6) #define RXEV_OVRRN3 (1 << 7) /* RXC values */ #define RXC_RX_EN (1 << 0) #define RXC_IGN_OUT (1 << 1) #define RXC_IGN_SETUP (1 << 2) #define RXC_FLUSH (1 << 3) #define RXC_RFWL0 (1 << 5) #define RXC_RFWL1 (1 << 6) /* RXS values */ #define RXS_RCOUNTMASK 0xF #define RXS_RX_LAST (1 << 4) #define RXS_TOGGLE (1 << 5) #define RXS_SETUP (1 << 6) #define RXS_RX_ERR (1 << 7)