[usb][omap3] working omap3 usb driver
diff --git a/platform/omap3/rules.mk b/platform/omap3/rules.mk
index c92ba0b..8d80d4b 100644
--- a/platform/omap3/rules.mk
+++ b/platform/omap3/rules.mk
@@ -11,6 +11,9 @@
 	WITH_DEV_USBC=1 \
 	WITH_DEV_UART=1
 
+MODULES += \
+	dev/usb
+
 INCLUDES += \
 	-I$(LOCAL_DIR)/include
 
diff --git a/platform/omap3/usbc.c b/platform/omap3/usbc.c
index 1998ea8..5bb9906 100644
--- a/platform/omap3/usbc.c
+++ b/platform/omap3/usbc.c
@@ -21,15 +21,17 @@
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #include <debug.h>
-#include <reg.h>
-#include <dev/usb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <kernel/thread.h>
 #include <dev/usbc.h>
 #include <dev/twl4030.h>
-#include <hw/usb.h>
-#include <platform/interrupts.h>
+#include <reg.h>
 #include <platform/omap3.h>
+#include <platform/interrupts.h>
+#include <hw/usb.h>
 
-#define LOCAL_TRACE 1
+#define LOCAL_TRACE 0
 
 #define hsusb_reg8(reg) *REG8(USB_HS_BASE + (reg))
 #define hsusb_reg16(reg) *REG16(USB_HS_BASE + (reg))
@@ -77,6 +79,300 @@
 #define RAMINFO	0x79
 #define LINKINFO	0x7a
 
+static void setup_dynamic_fifos(void);
+
+enum usb_state {
+	USB_DEFAULT = 0,
+	USB_ADDRESS,
+	USB_CONFIGURED
+};
+
+struct usbc_ep {
+	bool active;
+	uint width;
+	uint blocksize;
+	
+	/* current data buffer */
+	usbc_transfer *transfer;
+
+	/* callback when tx or rx happens on the endpoint */
+	int (*callback)(ep_t endpoint, usbc_callback_op_t op, usbc_transfer *transfer);
+};
+
+struct usbc_stat {
+	bool active;
+	enum usb_state state;
+	uint8_t active_config;
+
+	// callback for device events
+	usb_callback callback;
+
+	// ep0 pending tx
+	const void *ep0_tx_buf;
+	size_t ep0_tx_len;
+	uint ep0_tx_pos;
+
+	struct usbc_ep inep[16];  // IN endpoints (device to host)
+	struct usbc_ep outep[16]; // OUT endpoint (host to device)
+};
+
+static struct usbc_stat *usbc;
+
+struct usbc_callback {
+	struct list_node node;
+	usb_callback callback;
+};
+
+static struct list_node usbc_callback_list;
+
+static void call_all_callbacks(usbc_callback_op_t op, const union usb_callback_args *arg)
+{
+	struct usbc_callback *cb;
+
+	list_for_every_entry(&usbc_callback_list, cb, struct usbc_callback, node) {
+		LTRACEF("calling %p, op %d, arg %p\n", cb->callback, op, arg);
+		cb->callback(op, arg);
+	}
+}
+
+static void print_usb_setup(const struct usb_setup *setup)
+{
+	printf("usb_setup:\n");
+	printf("\ttype 0x%hhx\n", setup->request_type);
+	printf("\trequest 0x%hhx\n", setup->request);
+	printf("\tvalue 0x%hx\n", setup->value);
+	printf("\tindex 0x%hx\n", setup->index);
+	printf("\tlength 0x%hx\n", setup->length);
+}
+
+static void select_ep(uint ep)
+{
+	DEBUG_ASSERT(ep < 16);
+	hsusb_reg8(INDEX) = ep;
+}
+
+static void dump_ep_regs(uint ep)
+{
+#if 0
+	select_ep(ep);
+
+	LTRACEF("%d txmaxp 0x%hx\n", ep, hsusb_reg16(IDX_TXMAXP));
+	LTRACEF("%d rxmaxp 0x%hx\n", ep, hsusb_reg16(IDX_RXMAXP));
+	LTRACEF("%d txfifosz 0x%hhx\n", ep, hsusb_reg8(TXFIFOSZ));
+	LTRACEF("%d rxfifosz 0x%hhx\n", ep, hsusb_reg8(RXFIFOSZ));
+	LTRACEF("%d txfifoadd 0x%hx\n", ep, hsusb_reg16(TXFIFOADD));
+	LTRACEF("%d rxfifoadd 0x%hx\n", ep, hsusb_reg16(RXFIFOADD));
+#endif
+}
+
+#define MULOF4(val) (((uint32_t)(val) & 0x3) == 0)
+
+static int read_ep_fifo(uint ep, void *_buf, size_t maxlen)
+{
+	char *buf = (void *)_buf;
+
+	select_ep(ep);
+
+	uint8_t fifo_reg = FIFOBASE + ep * 4;
+	size_t rxcount = hsusb_reg16(IDX_RXCOUNT);
+
+	if (rxcount > maxlen)
+		rxcount = maxlen;
+
+	if (MULOF4(buf) && MULOF4(rxcount)) {
+		uint i;
+		uint32_t *buf32 = (uint32_t *)_buf;
+		for (i=0; i < rxcount / 4; i++) {
+			buf32[i] = hsusb_reg32(fifo_reg);
+		}
+	} else {
+		/* slow path */
+		uint i;
+		for (i=0; i < rxcount; i++) {
+			buf[i] = hsusb_reg8(fifo_reg);
+		}
+	}
+
+	return rxcount;
+}
+
+static int write_ep_fifo(uint ep, const void *_buf, size_t len)
+{
+	char *buf = (void *)_buf;
+
+	select_ep(ep);
+
+	uint8_t fifo_reg = FIFOBASE + ep * 4;
+
+	if (MULOF4(buf) && MULOF4(len)) {
+		uint i;
+		uint32_t *buf32 = (uint32_t *)_buf;
+		for (i=0; i < len / 4; i++) {
+			hsusb_reg32(fifo_reg) = buf32[i];
+		}
+	} else {
+		/* slow path */
+		uint i;
+		for (i=0; i < len; i++) {
+			hsusb_reg8(fifo_reg) = buf[i];
+		}
+	}
+
+	return len;
+}
+
+#undef MULOF4
+
+void usbc_ep0_send(const void *buf, size_t len, size_t maxlen)
+{
+	LTRACEF("buf %p, len %zu, maxlen %zu\n", buf, len, maxlen);
+
+	// trim the transfer
+	len = MIN(len, maxlen);
+
+	size_t transfer_len = MIN(64, len);
+
+	// write the first 64 bytes
+	write_ep_fifo(0, buf, transfer_len);
+
+	// set txpktready
+	select_ep(0);
+	if (len > 64) {
+		// we have more data to send, don't mark data end
+		hsusb_reg16(IDX_CSR0) |= (1<<1); // TxPktRdy
+
+		// save our position so we can continue
+		usbc->ep0_tx_buf = buf;
+		usbc->ep0_tx_pos = 64;
+		usbc->ep0_tx_len = len;
+	} else {
+		hsusb_reg16(IDX_CSR0) |= (1<<3) | (1<<1); // DataEnd, TxPktRdy
+		usbc->ep0_tx_buf = NULL;
+	}
+}
+
+static void ep0_control_send_resume(void)
+{
+	DEBUG_ASSERT(usbc->ep0_tx_buf != NULL);
+	DEBUG_ASSERT(usbc->ep0_tx_len > usbc->ep0_tx_pos);
+
+	LTRACEF("buf %p pos %d len %d\n", usbc->ep0_tx_buf, usbc->ep0_tx_pos, usbc->ep0_tx_len);
+
+	size_t transfer_len = MIN(64, usbc->ep0_tx_len - usbc->ep0_tx_pos);
+	
+	write_ep_fifo(0, (const uint8_t *)usbc->ep0_tx_buf + usbc->ep0_tx_pos, transfer_len);
+
+	usbc->ep0_tx_pos += transfer_len;
+
+	if (usbc->ep0_tx_pos >= usbc->ep0_tx_len) {
+		// completes the transfer
+		hsusb_reg16(IDX_CSR0) |= (1<<3) | (1<<1); // DataEnd, TxPktRdy
+		usbc->ep0_tx_buf = NULL;
+	} else {
+		hsusb_reg16(IDX_CSR0) |= (1<<1); // TxPktRdy
+	}
+}
+
+void usbc_ep0_ack(void)
+{
+	hsusb_reg16(IDX_CSR0) |= (1<<6)|(1<<3); // servicedrxpktrdy & dataend
+}
+
+void usbc_ep0_stall(void)
+{
+	printf("USB STALL\n");
+}
+
+static void usb_shutdown_endpoints(void)
+{
+	// iterate through all the endpoints, cancelling any pending io and shut down the endpoint
+	ep_t i;
+	for (i=1; i < 16; i++) {
+		if (usbc->inep[i].active && usbc->inep[i].transfer) {
+			// pool's closed
+			usbc_transfer *t = usbc->inep[i].transfer;
+			usbc->inep[i].transfer = NULL;
+			t->result = USB_TRANSFER_RESULT_CANCELLED;
+			usbc->inep[i].callback(i, CB_EP_TRANSFER_CANCELLED, t);
+		}
+		if (usbc->outep[i].active && usbc->outep[i].transfer) {
+			// pool's closed
+			usbc_transfer *t = usbc->outep[i].transfer;
+			usbc->outep[i].transfer = NULL;
+			t->result = USB_TRANSFER_RESULT_CANCELLED;
+			usbc->outep[i].callback(i, CB_EP_TRANSFER_CANCELLED, t);
+		}
+	}
+
+	// clear pending ep0 data
+	usbc->ep0_tx_buf = 0;
+}
+
+static void usb_enable_endpoints(void)
+{
+	setup_dynamic_fifos();	
+}
+
+static void usb_disconnect(void)
+{
+	// we've been disconnected
+	usbc->state = USB_DEFAULT;
+	usbc->active_config = 0;
+	
+	usb_shutdown_endpoints();
+}
+
+static void usb_reset(void)
+{
+	// this wipes out our endpoint interrupt disables
+	hsusb_reg16(INTRTXE) = (1<<0);
+	hsusb_reg16(INTRRXE) = 0;
+
+	usb_shutdown_endpoints();
+}
+
+static int handle_ep_rx(int ep)
+{
+	struct usbc_ep *e = &usbc->outep[ep];
+
+	DEBUG_ASSERT(e->active);
+
+	DEBUG_ASSERT(e->transfer); // can't rx to no transfer
+	usbc_transfer *t = e->transfer;
+
+	uint rxcount = hsusb_reg16(IDX_RXCOUNT);
+	uint readcount = MIN(rxcount, t->buflen - t->bufpos);
+	readcount = MIN(readcount, e->blocksize);
+
+	int len = read_ep_fifo(ep, (uint8_t *)t->buf + t->bufpos, readcount);
+	LTRACEF("read %d bytes from the fifo\n", len);
+
+	// no more packet ready
+	hsusb_reg16(IDX_RXCSRL) &= ~(1<<0); // clear rxpktrdy
+
+	t->bufpos += len;
+
+	if (rxcount < e->blocksize || t->bufpos >= t->buflen) {
+		// we're done with this transfer, clear it and disable the endpoint
+		e->transfer = NULL;
+		hsusb_reg16(INTRRXE) &= ~(1<<ep);
+
+		t->result = USB_TRANSFER_RESULT_OK;
+
+		DEBUG_ASSERT(e->callback);
+		e->callback(ep, CB_EP_RXCOMPLETE, t);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+bool usbc_is_highspeed(void)
+{
+	return (hsusb_reg8(POWER) & (1<<4)) ? true : false;
+}
+
 static enum handler_return hsusb_interrupt(void *arg)
 {
 	uint16_t intrtx = hsusb_reg16(INTRTX);
@@ -87,26 +383,427 @@
 	LTRACEF("intrtx 0x%hx (0x%x), intrrx 0x%hx (0x%x), intrusb 0x%hhx, intrusbe 0x%hhx\n", 
 			intrtx, hsusb_reg16(INTRTXE), intrrx, hsusb_reg16(INTRRXE), intrusb, hsusb_reg8(INTRUSBE));
 
+	dump_ep_regs(2);
+
+	// look for global usb interrupts
+	intrusb &= hsusb_reg8(INTRUSBE);
+	if (intrusb) {
+		if (intrusb & (1<<0)) {
+			// suspend
+			TRACEF("suspend\n");
+			call_all_callbacks(CB_SUSPEND, 0);
+			ret = INT_RESCHEDULE;
+		}
+		if (intrusb & (1<<1)) {
+			// resume
+			TRACEF("resume\n");
+			call_all_callbacks(CB_RESUME, 0);
+			ret = INT_RESCHEDULE;
+		}
+		if (intrusb & (1<<2)) {
+			// reset
+			TRACEF("reset\n");
+			TRACEF("high speed %d\n", hsusb_reg8(POWER) & (1<<4) ? 1 : 0);
+			call_all_callbacks(CB_RESET, 0);
+			usb_reset();
+			ret = INT_RESCHEDULE;
+		}
+		if (intrusb & (1<<3)) {
+			// SOF
+			TRACEF("sof\n");
+		}
+		if (intrusb & (1<<4)) {
+			// connect (host only)
+			TRACEF("connect\n");
+		}
+		if (intrusb & (1<<5)) {
+			// disconnect
+			TRACEF("disconnect\n");
+			call_all_callbacks(CB_DISCONNECT, 0);
+			usb_disconnect();
+			ret = INT_RESCHEDULE;
+		}
+		if (intrusb & (1<<6)) {
+			// session request (A device only)
+			TRACEF("session request\n");
+		}
+		if (intrusb & (1<<7)) {
+			// vbus error (A device only)
+			TRACEF("vbus error\n");
+		}
+	}
+
+	// look for endpoint 0 interrupt
+	if (intrtx & 1) {
+		select_ep(0);
+		uint16_t csr = hsusb_reg16(IDX_CSR0);
+		LTRACEF("ep0 csr 0x%hhx\n", csr);
+	
+		// clear the stall bit
+		if (csr & (1<<2))
+			hsusb_reg16(IDX_CSR0) &= ~(1<<2);
+
+		// do we have any pending tx data?
+		if (usbc->ep0_tx_buf != NULL) {
+			if (csr & (1<<4)) { // setup end
+				// we got an abort on the data transfer
+				usbc->ep0_tx_buf = NULL;
+			} else {
+				// send more data
+				ep0_control_send_resume();
+			}
+		}
+
+		// clear the setup end bit
+		if (csr & (1<<4)) {
+			hsusb_reg16(IDX_CSR0) |= (1<<7); // servicedsetupend
+		}
+
+		if (csr & 0x1) {
+			// rxpktrdy
+			LTRACEF("ep0: rxpktrdy, count %d\n", hsusb_reg16(IDX_RXCOUNT));
+
+			struct usb_setup setup;
+			read_ep_fifo(0, (void *)&setup, sizeof(setup));
+//			print_usb_setup(&setup);
+
+			hsusb_reg16(IDX_CSR0) |= (1<<6); // servicedrxpktrdy
+
+			union usb_callback_args args;
+			args.setup = &setup;
+			call_all_callbacks(CB_SETUP_MSG, &args);
+
+			switch (setup.request) {
+				case SET_ADDRESS: {
+					LTRACEF("got SET_ADDRESS: value %d\n", setup.value);
+					dprintf(INFO, "usb: got assigned address %d\n", setup.value);
+					usbc_ep0_ack();
+
+					hsusb_reg8(FADDR) = setup.value;
+					if (setup.value == 0)
+						usbc->state = USB_DEFAULT;
+					else
+						usbc->state = USB_ADDRESS;
+
+					break;
+				}
+				case SET_CONFIGURATION:
+					LTRACEF("got SET_CONFIGURATION, config %d\n", setup.value);
+
+					if (setup.value == 0) {
+						if (usbc->state == USB_CONFIGURED)
+							usbc->state = USB_ADDRESS;
+						call_all_callbacks(CB_OFFLINE, 0);
+					} else {
+						usbc->state = USB_CONFIGURED;
+						call_all_callbacks(CB_ONLINE, 0);
+					}
+					usbc->active_config = setup.value;
+					ret = INT_RESCHEDULE;
+
+					// set up all of the endpoints
+					usb_enable_endpoints();
+					break;
+			}
+		}
+	}
+
+	// handle endpoint interrupts
+
+	// mask out ones we don't want to play with
+	intrtx &= hsusb_reg16(INTRTXE);
+	intrrx &= hsusb_reg16(INTRRXE);
+
+	int i;
+	for (i=1; i < 16; i++) {
+		if (intrtx & (1<<i)) {
+			select_ep(i);
+
+			LTRACEF("txcsr %i: 0x%hx\n", i, hsusb_reg16(IDX_TXCSR));
+
+			// data was sent, see if we have more to send
+			struct usbc_ep *e = &usbc->inep[i];
+
+			DEBUG_ASSERT(e->transfer); // interrupts shouldn't be enabled if there isn't a transfer queued
+			usbc_transfer *t = e->transfer;
+
+			if (t->bufpos < t->buflen) {
+				// cram more stuff in the buffer
+				uint queuelen = MIN(e->blocksize, t->buflen - t->bufpos);
+				LTRACEF("writing more tx data into fifo: len %u, remaining %zu\n", queuelen, t->buflen - t->bufpos);
+				write_ep_fifo(i, (uint8_t *)t->buf + t->bufpos, queuelen);
+				t->bufpos += queuelen;
+
+				// start the transfer
+				hsusb_reg16(IDX_TXCSR) |= (1<<0); // txpktrdy
+			} else {
+				// we're done, callback
+				e->transfer = NULL;
+				hsusb_reg16(INTRTXE) &= ~(1<<i);
+
+				t->result = USB_TRANSFER_RESULT_OK;
+
+				DEBUG_ASSERT(e->callback);
+				e->callback(i, CB_EP_TXCOMPLETE, t);
+				ret = INT_RESCHEDULE;
+			}
+		}
+		if (intrrx & (1<<i)) {
+			select_ep(i);
+
+			uint16_t csr = hsusb_reg16(IDX_RXCSR);
+			LTRACEF("rxcsr %i: 0x%hx\n", i, csr);
+
+			if (csr & 0x1) { // rxpktrdy
+				// see if the endpoint is ready
+				struct usbc_ep *e = &usbc->outep[i];
+				if (!e->active) {
+					// stall it
+					hsusb_reg16(IDX_RXCSR) |= (1<<6); // stall
+					hsusb_reg16(IDX_RXCSR) |= (1<<4); // flush fifo
+					panic("rx on inactive endpoint\n");
+					continue;
+				}
+	
+				if (handle_ep_rx(i) > 0)
+					ret = INT_RESCHEDULE;
+			}
+		}
+	}
+
 	return ret;
 }
 
+static enum handler_return hsusb_dma_interrupt(void *arg)
+{
+	LTRACE;
+
+	return INT_NO_RESCHEDULE;
+}
+
+void usbc_setup_endpoint(ep_t ep, ep_dir_t dir, bool active, ep_callback callback, uint width, uint blocksize)
+{
+	DEBUG_ASSERT(ep != 0);
+	DEBUG_ASSERT(ep < 16);
+	DEBUG_ASSERT(dir == IN || dir == OUT);
+
+	struct usbc_ep *e;
+	if (dir == IN)
+		e = &usbc->inep[ep];
+	else
+		e = &usbc->outep[ep];
+
+	// for now we can only make active
+	e->active = active;
+	e->callback = callback;
+	e->width = width;
+	e->blocksize = blocksize;
+}
+
+int usbc_queue_rx(ep_t ep, usbc_transfer *transfer)
+{
+	LTRACE;
+	struct usbc_ep *e = &usbc->outep[ep];
+
+	DEBUG_ASSERT(ep != 0);
+	DEBUG_ASSERT(ep < 16);
+	DEBUG_ASSERT(e->active);
+
+	DEBUG_ASSERT(transfer);
+	DEBUG_ASSERT(transfer->buf);
+
+	DEBUG_ASSERT(e->transfer == NULL);
+
+	// can only queue up multiples of the endpoint blocksize
+	DEBUG_ASSERT(transfer->buflen >= e->blocksize && (transfer->buflen % e->blocksize) == 0);
+
+	enter_critical_section();
+
+	if (usbc->state != USB_CONFIGURED) {
+		// can't transfer now
+		exit_critical_section();
+		return -1;
+	}
+
+	e->transfer = transfer;
+
+	// make sure the ep is set up right
+//	select_ep(ep);
+//	hsusb_reg8(IDX_RXCSRH) = 0;
+	dump_ep_regs(ep);
+
+	select_ep(ep);
+	if (hsusb_reg16(IDX_RXCSR) & (1<<0)) {
+		// packet already ready
+		LTRACEF("****packet already ready (%d)\n", hsusb_reg16(IDX_RXCOUNT));
+
+		int rc = handle_ep_rx(ep);
+		if (rc > 0) {
+			// the transfer was completed
+			goto done;
+		}
+	}
+
+	// unmask irqs for this endpoint
+	hsusb_reg16(INTRRXE) |= (1<<ep);
+
+done:
+	exit_critical_section();
+
+	return 0;
+}
+
+int usbc_queue_tx(ep_t ep, usbc_transfer *transfer)
+{
+	LTRACEF("ep %u, transfer %p (buf %p, len %zu)\n", ep, transfer, transfer->buf, transfer->buflen);
+	struct usbc_ep *e = &usbc->inep[ep];
+
+	DEBUG_ASSERT(ep != 0);
+	DEBUG_ASSERT(ep < 16);
+	DEBUG_ASSERT(e->active);
+
+	DEBUG_ASSERT(e->transfer == NULL);
+
+	enter_critical_section();
+
+	if (usbc->state != USB_CONFIGURED) {
+		// can't transfer now
+		exit_critical_section();
+		return -1;
+	}
+
+e->transfer = transfer;
+
+	select_ep(ep);
+
+	// set this endpoint in tx mode
+//	hsusb_reg8(IDX_TXCSRH) = (1<<7)|(1<<5); // autoset, tx direction
+	dump_ep_regs(ep);
+
+	// unmask irqs for this endpoint
+	hsusb_reg16(INTRTXE) |= (1<<ep);
+	
+	// if the fifo is empty, start the transfer
+	if ((hsusb_reg16(IDX_TXCSR) & (1<<1)) == 0) {
+		// dump the start of the transfer in the fifo
+		uint queuelen = MIN(e->blocksize, transfer->buflen);
+		write_ep_fifo(ep, transfer->buf, queuelen);
+		transfer->bufpos = queuelen;
+
+		// start the transfer
+		hsusb_reg16(IDX_TXCSR) |= (1<<0); // txpktrdy
+	}
+
+	exit_critical_section();
+
+	return 0;
+}
+
+int usbc_set_callback(usb_callback callback)
+{
+	DEBUG_ASSERT(callback != NULL);
+
+	struct usbc_callback *cb = malloc(sizeof(struct usbc_callback));
+
+	enter_critical_section();
+
+	cb->callback = callback;
+	list_add_head(&usbc_callback_list, &cb->node);
+
+	exit_critical_section();
+	return 0;
+}
+
 int usbc_set_active(bool active)
 {
+	LTRACEF("active %d\n", active);
 	if (active) {
-//		DEBUG_ASSERT(!usbc->active);
+		DEBUG_ASSERT(!usbc->active);
 
 		hsusb_reg8(POWER) |= (1<<6); // soft conn
 		twl4030_set_usb_pullup(true);
-//		usbc->active = true;
+		usbc->active = true;
 	} else {
 		hsusb_reg8(POWER) &= ~(1<<6); // soft conn
 		twl4030_set_usb_pullup(false);
-//		usbc->active = false;
+		usbc->active = false;
 	}
 
 	return 0;
 }
 
+static void setup_dynamic_fifos(void)
+{
+//	LTRACE;
+
+#if LOCAL_TRACE
+	uint8_t raminfo = hsusb_reg8(RAMINFO);
+	size_t ramsize = (1 << ((raminfo & 0xf) + 2));
+	LTRACEF("%zd bytes of onboard ram\n", ramsize);
+#endif
+
+	uint32_t offset = 128;
+
+	int highspeed = hsusb_reg8(POWER) & (1<<4);
+
+	int i;
+	for (i=1; i < 16; i++) {
+		select_ep(i);
+		if (usbc->inep[i].active) {
+			hsusb_reg8(TXFIFOSZ) = (1<<4)|(0x6); // 512 byte, double buffered
+			hsusb_reg8(RXFIFOSZ) = 0;
+			hsusb_reg16(TXFIFOADD) = offset / 8;
+			hsusb_reg16(RXFIFOADD) = 0;
+			if (highspeed) {
+				hsusb_reg16(IDX_TXMAXP) = usbc->inep[i].width;
+			} else {
+				hsusb_reg16(IDX_TXMAXP) = (((usbc->inep[i].blocksize / 64) - 1) << 11) | 64;
+//				hsusb_reg16(IDX_TXMAXP) = 64;
+//				usbc->inep[i].blocksize = 64;
+			}
+
+			hsusb_reg16(IDX_RXMAXP) = 0;
+			LTRACEF("%d: txmaxp 0x%hx\n", i, hsusb_reg16(IDX_TXMAXP));
+			hsusb_reg8(IDX_TXCSRH) = (1<<5)|(1<<3);
+			hsusb_reg8(IDX_TXCSRL) = (1<<3);
+			hsusb_reg8(IDX_TXCSRL) = (1<<3);
+			offset += 512*2;
+		} else {
+			hsusb_reg8(TXFIFOSZ) = 0;
+			hsusb_reg16(TXFIFOADD) = 0;
+			hsusb_reg16(IDX_TXMAXP) = 0;
+		}
+		if (usbc->outep[i].active) {
+			hsusb_reg8(TXFIFOSZ) = 0;
+			hsusb_reg8(RXFIFOSZ) = (0<<4)|(0x6); // 512 byte, single buffered
+			hsusb_reg16(TXFIFOADD) = 0;
+			hsusb_reg16(RXFIFOADD) = offset / 8;
+			hsusb_reg16(IDX_TXMAXP) = 0;
+			if (highspeed) {
+				hsusb_reg16(IDX_RXMAXP) = usbc->inep[i].width;
+			} else {
+				hsusb_reg16(IDX_RXMAXP) = (((usbc->outep[i].blocksize / 64) - 1) << 11) | 64;
+//				hsusb_reg16(IDX_RXMAXP) = 64;
+//				usbc->outep[i].blocksize = 64;
+			}
+			LTRACEF("%d: rxmaxp 0x%hx\n", i, hsusb_reg16(IDX_RXMAXP));
+			offset += 512;
+			hsusb_reg8(IDX_RXCSRH) = (1<<7);
+			hsusb_reg8(IDX_RXCSRL) = (1<<7);
+
+//			LTRACEF("rxcsr 0x%hx\n", hsusb_reg16(IDX_RXCSR));
+		} else {
+			hsusb_reg8(RXFIFOSZ) = 0;
+			hsusb_reg16(RXFIFOADD) = 0;
+			hsusb_reg16(IDX_RXMAXP) = 0;
+		}
+//		LTRACEF("%d txfifosz 0x%hhx\n", i, hsusb_reg8(TXFIFOSZ));
+//		LTRACEF("%d rxfifosz 0x%hhx\n", i, hsusb_reg8(RXFIFOSZ));
+//		LTRACEF("%d txfifoadd 0x%hx\n", i, hsusb_reg16(TXFIFOADD));
+//		LTRACEF("%d rxfifoadd 0x%hx\n", i, hsusb_reg16(RXFIFOADD));
+	}
+}
+
 static void otg_reset(void)
 {
 	/* reset the chip */
@@ -129,11 +826,11 @@
 	LTRACE_ENTRY;
 
 	// select endpoint 0
-	dprintf(INFO, "hwvers 0x%hx\n", hsusb_reg16(HWVERS)); 
-	dprintf(INFO, "epinfo 0x%hhx\n", hsusb_reg8(EPINFO)); 
-	dprintf(INFO, "raminfo 0x%hhx\n", hsusb_reg8(RAMINFO)); 
+	dprintf(SPEW, "hwvers 0x%hx\n", hsusb_reg16(HWVERS)); 
+	dprintf(SPEW, "epinfo 0x%hhx\n", hsusb_reg8(EPINFO)); 
+	dprintf(SPEW, "raminfo 0x%hhx\n", hsusb_reg8(RAMINFO)); 
 	hsusb_reg8(INDEX) = 0;
-	dprintf(INFO, "config 0x%hhx\n", hsusb_reg8(IDX_CONFIGDATA));
+	dprintf(SPEW, "config 0x%hhx\n", hsusb_reg8(IDX_CONFIGDATA));
 
 	// assert that we have dynamic fifo sizing
 	DEBUG_ASSERT(hsusb_reg8(IDX_CONFIGDATA) & (1<<2));
@@ -156,9 +853,20 @@
 
 void usbc_init(void)
 {
+	LTRACE_ENTRY;
+
 	// enable the clock
 	RMWREG32(CM_ICLKEN1_CORE, 4, 1, 1);
 
+	// allocate some ram for the usb struct
+	usbc = malloc(sizeof(struct usbc_stat));
+	memset(usbc, 0, sizeof(struct usbc_stat));
+
+	usbc->state = USB_DEFAULT;
+
+	// initialize the callback list
+	list_initialize(&usbc_callback_list);
+
 	// register the interrupt handlers
 	register_int_handler(92, hsusb_interrupt, NULL);
 //	register_int_handler(93, hsusb_dma_interrupt, NULL);
@@ -169,6 +877,6 @@
 	unmask_interrupt(92);
 //	unmask_interrupt(93);
 
-	usbc_set_active(true);
+	LTRACE_EXIT;
 }