/*
 * National 9603/4 USB Device Controller driver
 * Copyright (C) 2004 Technical Solutions Inc. (support@techsol.ca)
 * ported from : The Goku-S driver
 * Copyright (C) 2003 MontaVista Software (source@mvista.com)
 *
 * 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)
 */

/*
 * The ep->stage information refers to the state of a setup transaction
 *
 * state 0: no setup packet has been received
 * state 1: setup packet has been received
 * state 2: data has been sent/received
 * state 3: ZLP has been received/sent
 */

#include <common.h>
#include <linux/list.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <malloc.h>
#include <usb/lin_gadget_compat.h>

#include <asm/byteorder.h>
#include <asm/io.h>

#include "n9604_udc.h"
#include "n9604regs.h"

inline void Flush_and_enable(u8 control_reg) {
	write_9604(RXC_FLUSH, control_reg);
	while (read_9604(control_reg) & RXC_FLUSH);
	write_9604(RXC_RX_EN, control_reg);
}
inline void Flush(u8 control_reg) {
	write_9604(RXC_FLUSH, control_reg);
	while (read_9604(control_reg) & RXC_FLUSH);
}

#define	DRIVER_DESC		"N9604 USB Device Controller"
#define	DRIVER_VERSION		"29-Oct 2004"

static const char driver_name [] = "n9604_udc";
static const char driver_desc [] = DRIVER_DESC;

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

/*-------------------------------------------------------------------------*/

//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;
	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->num) 
		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:
		case USB_ENDPOINT_XFER_ISOC:
			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)
		Flush(ep->command);
	else
		Flush_and_enable(ep->command);
	
	max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
	ep->ep.maxpacket = min_t(u16, max, MAX_FIFO_SIZE);
	ep->desc = desc;
	
	return 0;
}

static int n9604_ep_disable(struct usb_ep *_ep)//ep > 0
{
	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);
	write_9604(0, ep->command);
	ep->desc = NULL;
	spin_unlock_irqrestore(&dev->lock, flags);

	return 0;
}

/*-------------------------------------------------------------------------*/

static struct usb_request *
n9604_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
	struct n9604_request	*req;

	if (!_ep)
		return 0;
	req = malloc(sizeof *req);
	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);
	WARN_ON(!list_empty(&req->queue));
	free(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, loop_length;

	u8 fifo = ep->fifo;
	u8 command = ep->command;
	u8 status = ep->status;
	if (!ep->num) {
		fifo = TXD0;
		command = TXC0;
		status = TXS0;
	}
	
	if (read_9604(command) & TXC_TX_EN)
		return -EBUSY;
	ep->packets++;
	
	desired_length = req->req.length - req->req.actual;
	available_length = read_9604(status) & TXS_TCOUNT_MASK;//might be greater
	written_length = 0;
	if (ep->num)
		maximum_length = MAX_FIFO_SIZE;
	else
		maximum_length = MAX_EP0_SIZE;

	while ((loop_length = min(min(available_length, desired_length), maximum_length))) {
		int i = loop_length;
		while (i) { write_9604(*buf++,fifo); i--; }
		written_length += loop_length;
		desired_length -= loop_length;
		maximum_length -= loop_length;
		if (desired_length && maximum_length)//check if really need to read the chip again
			available_length = (read_9604(status) & TXS_TCOUNT_MASK);
	}
	
	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)
{
	u8              *buf;
	unsigned        count;
	int             is_last;

	buf = req->req.buf + req->req.actual;
	prefetch(buf);
	
	count = write_packet(ep, buf, req);
	if (count < 0)
		return count;
	
	/* last packet often short (sometimes a zlp, especially on ep0) */
	if ((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;
	ep->packets++;
	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)) {
		ERROR(ep->dev, "DATA ERROR!!!! on ep%d\nFlushing Fifo", ep->num);
		Flush_and_enable(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) {
		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)
{
	if (ep->is_in) 
		write_9604(read_9604(TXMSK) | 1 << ep->fifoNum | 0x10 << ep->fifoNum, TXMSK);
	else {
		u8 command = ep->command;
		if (!ep->num) command = RXC0;
		write_9604(read_9604(RXMSK) | 1 << ep->fifoNum | 0x10 << 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) & ~(0x10 << ep->fifoNum), TXMSK);
	else
		write_9604(read_9604(RXMSK) & ~(1 << ep->fifoNum) & ~(0x10 << 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;

	if (list_empty (&ep->queue)) {
		if (!ep->num) {
			if (ep->is_in && (ep->stage == 2)) {
				ep->is_in = 0;//switch modes
				Flush_and_enable(RXC0);//needed to receive a ZLP after tx
				ep->stage++;//and bump the stage number 
			} else if (ep->stage == 3) {
				ep->stage = 0;
			}
		}
		return;
	}
	req = list_entry(ep->queue.next, struct n9604_request, queue);
	(ep->is_in ? write_fifo : read_fifo)(ep, req);
}

/*-------------------------------------------------------------------------*/

static void * n9604_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, gfp_t gfp_flags)
{
	return malloc(bytes);
}

static void n9604_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes)
{
	free (buf);
}



/*-------------------------------------------------------------------------*/

static void
done(struct n9604_ep *ep, struct n9604_request *req, int status)
{
	struct n9604_udc         *dev;

	list_del_init(&req->queue);
	ep->queue_active--;
	
	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 */
	if (ep->num)
		pio_irq_disable(ep);
	else if (!ep->nuking) {
		ep->stage++;
		ep->toggle = 1;//other endpoints stay in their flipping mode between transactions
		if (ep->stage == 2) {//we are in stage 2 now
			if (!ep->is_in) {
				ep->is_in = 1;//switch modes
				request_voodoo = 1;//prevents n9604_queue from calling us again before doing anything
				send_zero_length(0, dev);
			} else {//we have to receive a ZLP
				//this will happen when the tx is complete, the pio_advance fcn will activate it for us
			}
		}
	}
		
	req->req.complete(&ep->ep, &req->req);
}


/*-------------------------------------------------------------------------*/

static int
n9604_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
	struct n9604_request	*req;
	struct n9604_ep		*ep;
	struct n9604_udc	*dev;
	unsigned long		flags;
	int			status;

	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 (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
		return -ESHUTDOWN;
	}
	if (ep->nuking)
		return -ESHUTDOWN;

	spin_lock_irqsave(&dev->lock, flags);

	ep->queue_reqs++;
	ep->queue_active++;

	_req->status = -EINPROGRESS;
	_req->actual = 0;

	/* for ep0 IN without premature status, zlp is required and
	 * writing EOP starts the status stage (OUT).
	 */
	if (ep->num == 0) {
		if ((request_voodoo > _req->length) && !(_req->length % MAX_EP0_SIZE) && (_req->length != 0)) {
			_req->zero = 1;
		}
		if (!request_voodoo && !ep->is_in) {//this is a zero length request
			spin_unlock_irqrestore(&dev->lock, flags);//David
			done(ep, req, 0);//this doesn't check if the list is empty (probably not an issue)
			return 0;	//shouldn't this be handled by the rx irq fcn, and passed to pio_advance
		}//that may conflict with the voodoo stuff, maybe best to leave it
	}
	
	/* kickstart this i/o queue? */
	status = 0;
	if (list_empty(&ep->queue) && ep->is_in) {
		status = write_fifo(ep, req);
		if (status == -EBUSY)
			;//we should queue up the request then
		else {
			if (status != 0) {
				if (status > 0)
					status = 0;
				req = 0;
			}
		}
	} /* else pio or dma irq handler advances the queue. */

	if (req != 0) {
		list_add_tail(&req->queue, &ep->queue);
		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;
	
	if (list_empty(&ep->queue))
		return;
	ep->nuking = 1;
	while (!list_empty(&ep->queue)) {
		req = list_entry(ep->queue.next, struct n9604_request, queue);
		done(ep, req, status);
	}
	ep->nuking = 0;
}

/* 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);

	write_9604(read_9604(ep->control) & ~EPC_STALL, ep->control);
	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) {
			 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 {
		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
}

/*-------------------------------------------------------------------------*/

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,

	.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++) {
	for (i = 0; i < ARRAY_SIZE(names); 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;
			Flush(ep->command);//flush any data in the fifo//we don't care about the previous state
			read_9604(ep->status);
			ep->ep.maxpacket = MAX_FIFO_SIZE;
		} 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
			read_9604(TXS0);
			Flush(TXC0);
			Flush(RXC0);//we could potentially (probably) overwriting a pending setup packet
			if (ep->stage)//if we get a setup packet before we have a chance to finish the reset we have a problem
				read_9604(RXS0);//fix this by sending stalls or something
			ep->stage = 0;
			ep->ep.maxpacket = MAX_EP0_SIZE;
		}
		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->nuking=0;
		ep->queue_reqs = 0;
		ep->queue_active = 0;
		ep->packets = 0;
		ep->desc = 0;
		ep->irqs = 0;
	}

	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(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; This disables all interrupts sharing that line
	write_9604(MCNTRL_SRST,MCNTRL);//software reset -- this also prevents pullup
	write_9604(0x00, MAMSK); //disable interrupts
}



static void udc_enable(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(MAMSK_WARN | MAMSK_ALT | MAMSK_TX_EV | MAMSK_RX_EV | MAMSK_INTR, MAMSK);//for now we turn it all on, except frames & ULD & NAK
	write_9604(ALTMSK_RESET, ALTMSK);//just turn on reset
	write_9604(0x11, TXMSK);
	write_9604(0x11, RXMSK);
	write_9604(0x0, NAKMSK);
	write_9604(0x0, FWMSK);
	write_9604(MCNTRL_VGE | MCNTRL_NAT | MCNTRL_INTOC_ActHigh, MCNTRL);//this activates the pull-up and turns on interrupts
}

/*-------------------------------------------------------------------------*/

/* 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;
}

int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
	struct n9604_udc	*dev = the_controller;
	unsigned long	flags;
	int i;

	if (!dev)
		return -ENODEV;
	if (!driver || driver != dev->driver)
		return -EINVAL;

	spin_lock_irqsave(&dev->lock, flags);
	dev->driver = 0;
	
	udc_reset(dev);//reset & diable irqs
	for (i = 0; i < ARRAY_SIZE(dev->ep); i++)
		nuke(&dev->ep [i], -ESHUTDOWN);
	spin_unlock_irqrestore(&dev->lock, flags);

	if (dev->gadget.speed != USB_SPEED_UNKNOWN)
		driver->disconnect(&dev->gadget);
	driver->unbind(&dev->gadget);

	return 0;
}


/*-------------------------------------------------------------------------*/

inline u8 tx_ev_irq(struct n9604_udc *dev) {
	u8      mask;

	mask = read_9604(TXEV) & read_9604(TXMSK);

	if (mask & TXEV_FIFO0) {
		write_9604(0, EPC0);//make sure we are not stalled, & not using the default address
		read_9604(TXS0);//should really check for error conditions
		dev->ep[0].irqs++;
		pio_advance(&dev->ep[0]);
	}
	if (mask & TXEV_FIFO1) {
		read_9604(TXS1);
		dev->ep[1].irqs++;
		pio_advance(&dev->ep[1]);
	}
	if (mask & TXEV_FIFO2) {
		read_9604(TXS2);
		dev->ep[3].irqs++;
		pio_advance(&dev->ep[3]);
	}
	if (mask & TXEV_FIFO3) {
		read_9604(TXS3);
		dev->ep[5].irqs++;
		pio_advance(&dev->ep[5]);
	}
	return mask;
}

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);
}

inline 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, 0);
	my_req->length = length;
	my_req->buf = n9604_alloc_buffer(&dev->ep[endpoint].ep, length, &my_req->dma, 0);
	my_req->complete = my_req_complete;
	n9604_queue(&dev->ep[endpoint].ep, my_req, 0);
}

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);

	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) {
			struct usb_ctrlrequest ctrl;
			ep->packets++;
			write_9604(0x40, ALTMSK);//someone is talking to us.  Make sure we can be reset if we lose this communication
			ep->stage = 1;
			rxs_mask = read_9604(RXS0);//2nd read (1st one is for zero length packet)
			ctrl.bRequestType = read_9604(RXD0);
			ctrl.bRequest = read_9604(RXD0);
			ctrl.wValue = cpu_to_le16(read_9604(RXD0) + (read_9604(RXD0) << 8));
			ctrl.wIndex = cpu_to_le16(read_9604(RXD0) + (read_9604(RXD0) << 8));
			ctrl.wLength = cpu_to_le16(read_9604(RXD0) + (read_9604(RXD0) << 8));
			ep->toggle = 1;
			request_voodoo = le16_to_cpu(ctrl.wLength);
			if (ctrl.bRequestType & 0x80) {//This is an IN transaction
				ep->is_in = 1;//David: is this correct for both cases//check with n9604_queue
				read_mode = 0;
				if (ctrl.wLength) {//should be followed by ZLP out packet
				} else {//host expects ZLP out packet
					ep->stage = 2;
				}
			} else {//This is an out transaction
				if (ctrl.wLength) {
					ep->is_in = 0;
					read_mode = 1;
				} else {//host expects ZLP in packet
					read_mode = 0;
					ep->stage = 2;
					ep->is_in = 1;
				}
			}
			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 = (le16_to_cpu(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 < ARRAY_SIZE(dev->ep); i++) 
							if ((le16_to_cpu(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:
				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:
					if (dev->driver->setup(&dev->gadget, &ctrl) < 0)//there was an error
						if (((ctrl.bRequestType & 0x80) && ctrl.wLength) || (!(ctrl.bRequestType & 0x80) && !ctrl.wLength))
							send_zero_length(0, dev);
			}//crtl.bRequest
		}//setup
		else if (read_mode)
			pio_advance(ep);
		else {
			ep->stage = 0;
			ep->packets++;
		}
	}//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);

	if (mask & ALTEV_EOP);
	if (mask & ALTEV_SD3);
	if (mask & ALTEV_SD5);
	if (mask & ALTEV_RESET) {
		int i;
		udelay(1200);//no idea why this is needed, but it makes things work
		write_9604(0x0, FAR);//lets not respond to any packets until we are ready
		write_9604(NFSR_NodeReset, NFSR);
		dev->driver->disconnect(&dev->gadget);
		for (i = 0; i < ARRAY_SIZE(dev->ep); i++)
			nuke(&dev->ep [i], -ESHUTDOWN);//this should be handled above by disconnect
		write_9604(0x00, ALTMSK);//make sure reset is turned off, or we will constantly be interrupted
		write_9604(0x11, TXMSK);
		write_9604(0x11, RXMSK);
		udc_reinit(dev);
		dev->gadget.speed = USB_SPEED_FULL;
		dev->ep[0].is_in = 0;
	}
	if (mask & ALTEV_RESUME); //write_9604(NFSR_NodeOperational, NFSR);
	if (mask & ALTEV_WKUP);//we don't really sleep
	if (mask & ALTEV_DMA);
}

int usb_gadget_handle_interrupts(void) {
	struct n9604_udc         *dev = the_controller;
	u8			mask;

	mask = read_9604(MAEV) & read_9604(MAMSK);
	if (!mask)
		return 0;

	if (mask & MAEV_ALT) {
		alt_ev_irq(dev);
		mask = read_9604(MAEV) & read_9604(MAMSK);//force a re-read of the current pending interrupts
	}
	if (mask & MAEV_TX_EV)
		tx_ev_irq(dev);
	if (mask & MAEV_RX_EV)
		rx_ev_irq(dev);
	dev->irqs++;
	return 0;
}

/*-------------------------------------------------------------------------*/
int n9604_udc_probe (void)
{
	struct n9604_udc	*dev;

	if (the_controller) 
		return -EBUSY;

	USBN9604_Offset = (u8 *)CONFIG_USBN9604_BASE;
	
	if ((read_9604(RID) & 0xF) != 0x2) { //0x2 is the identifier for 9603/4
		return -ENODEV;
	}
	
	/* alloc, and start init */
	dev = malloc(sizeof *dev);
	if (dev == NULL){
		WARN(dev, "No memory");
		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.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;

	return 0;
}
