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)



Reply via email to