[usb] first stab at a usb device stack
diff --git a/dev/usb/usb.c b/dev/usb/usb.c
index 6042391..b3ddabc 100644
--- a/dev/usb/usb.c
+++ b/dev/usb/usb.c
@@ -21,9 +21,267 @@
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #include <debug.h>
-#include <dev/usb.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
 #include <dev/usbc.h>
-#include <hw/usb.h>
+#include <dev/usb.h>
+
+//#include <lib/pmux.h>
+//#include <lib/usbmass.h>
+//#include <lib/dfu.h>
+//#include <target/usbconfig.h>
+
+#define LOCAL_TRACE 1
+
+static usb_config *config;
+
+static uint8_t active_config;
+static bool usb_active = false;
+
+static void append_desc_data(usb_descriptor *desc, const void *dat, size_t len)
+{
+	uint8_t *ptr = malloc(desc->len + len);
+
+	memcpy(ptr, desc->desc, desc->len);
+	memcpy(ptr + desc->len, dat, len);
+	free(desc->desc);
+	desc->desc = ptr;
+	desc->len += len;
+}
+
+/* returns the interface number assigned */
+static int usb_append_interface(usb_descriptor *desc, const uint8_t *int_descr, size_t len)
+{
+	uint8_t *ptr = malloc(len);
+	int interface_num;
+
+	// create a temporary copy of the interface
+	memcpy(ptr, int_descr, len);
+
+	// find the last interface used
+	interface_num = ((uint8_t *)desc->desc)[4]; // current interface
+
+	// patch our interface descriptor with the new id
+	ptr[2] = interface_num;
+
+	// append it to our config desriptor
+	append_desc_data(desc, ptr, len);
+	free(ptr);
+
+	// patch the total length of the config descriptor and set the number of interfaces
+	((uint16_t *)desc->desc)[1] += len;
+	interface_num++;
+	((uint8_t *)desc->desc)[4] = interface_num;
+
+	return interface_num - 1;
+}
+
+int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len)
+{
+	return usb_append_interface(&config->highspeed.config, int_descr, len);
+}
+
+int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len)
+{
+	return usb_append_interface(&config->lowspeed.config, int_descr, len);
+}
+
+void usb_set_string_descriptor(usb_descriptor *desc, const char *string)
+{
+	int len = strlen(string);
+	ushort *data;
+	int datalen = len * 2 + 2;
+
+	data = malloc(datalen);
+	
+	/* write length field */
+	data[0] = 0x0300 + datalen;
+
+	/* copy the string into the uint16_t based usb string */
+	int i;
+	for (i = 0; i < len; i++) {
+		data[i + 1] = string[i];
+	}
+
+	desc->desc = (void *)data;
+	desc->len = datalen;
+}
+
+static void set_usb_id(uint16_t vendor, uint16_t product)
+{
+	// patch the current configuration to with the vendor/product id
+	((uint16_t *)config->lowspeed.device.desc)[4] = vendor;
+	((uint16_t *)config->lowspeed.device.desc)[5] = product;
+
+	((uint16_t *)config->highspeed.device.desc)[4] = vendor;
+	((uint16_t *)config->highspeed.device.desc)[5] = product;
+}
+
+static int default_usb_callback(usbc_callback_op_t op, const union usb_callback_args *args)
+{
+	LTRACEF("op %d, args %p\n", op, args);
+
+	/* start looking for specific things to handle */
+	if (op == CB_SETUP_MSG) {
+		const struct usb_setup *setup = args->setup;
+		DEBUG_ASSERT(setup);
+		LTRACEF("SETUP: req_type=%#x req=%#x value=%#x index=%#x len=%#x\n", setup->request_type, setup->request, setup->value, setup->index, setup->length);
+
+		if ((setup->request_type & TYPE_MASK) == TYPE_STANDARD) {
+			switch (setup->request) {
+				case SET_ADDRESS:
+					LTRACEF("SET_ADDRESS 0x%x\n", setup->value);
+					usbc_ep0_ack();
+					break;
+				case SET_FEATURE:
+				case CLEAR_FEATURE:
+					// OTAY
+					LTRACEF("SET/CLEAR_FEATURE, feature 0x%x\n", setup->value);
+					usbc_ep0_ack();
+					break;
+				case SET_DESCRIPTOR:
+					LTRACEF("SET_DESCRIPTOR\n");
+					usbc_ep0_stall();
+					break;
+				case GET_DESCRIPTOR: {
+					/* Get the right descriptors based on current speed */
+					const struct usb_descriptor_speed *speed;
+					if (usbc_is_highspeed()) {
+						speed = &config->highspeed;
+					} else {
+						speed = &config->lowspeed;
+					}
+
+					if ((setup->request_type & RECIP_MASK) == RECIP_DEVICE)	{
+						switch (setup->value) {
+							case 0x100: /* device */
+								LTRACEF("got GET_DESCRIPTOR, device descriptor\n");
+								usbc_ep0_send(speed->device.desc, speed->device.len, 
+										setup->length);
+								break;
+							case 0x200:    /* CONFIGURATION */
+								LTRACEF("got GET_DESCRIPTOR, config descriptor\n");
+								usbc_ep0_send(speed->config.desc, speed->config.len,
+										setup->length);
+								break;
+							case 0x300:    /* Language ID */
+								LTRACEF("got GET_DESCRIPTOR, language id\n");
+								usbc_ep0_send(config->langid.desc,
+										config->langid.len, setup->length);
+								break;
+							case 0x301:    /* Manufacturer string */
+								LTRACEF("got GET_DESCRIPTOR, mfg string\n");
+								usbc_ep0_send(config->mfg_string.desc,
+										config->mfg_string.len, 
+										setup->length);
+								break;
+							case 0x302:    /* Device string */
+								LTRACEF("got GET_DESCRIPTOR, device string\n");
+								usbc_ep0_send(config->device_string.desc,
+										config->device_string.len,
+										setup->length);
+								break;
+							case 0x303:    /* Serial number string */
+								LTRACEF("got GET_DESCRIPTOR, serial number string\n");
+								if (config->serial_string.desc) {
+									usbc_ep0_send(config->serial_string.desc,
+											config->serial_string.len,
+											setup->length);
+								} else {
+									/* stall */
+									usbc_ep0_stall();
+								}
+								break;
+							case 0x600:    /* DEVICE QUALIFIER */
+								LTRACEF("got GET_DESCRIPTOR, device qualifier\n");
+								usbc_ep0_send(speed->device_qual.desc, 
+										speed->device_qual.len, setup->length);
+								break;
+							case 0xa00:
+								/* we aint got one of these */
+								LTRACEF("got GET_DESCRIPTOR, debug descriptor\n");
+								usbc_ep0_stall();
+								break;
+							default:
+								LTRACEF("unhandled descriptor %#x\n", setup->value);
+								// stall
+								break;
+						}
+					} else {
+						// interface/endpoint descriptors? let someone else handle it
+						// STALL
+					}
+					break;
+				}
+
+				case SET_CONFIGURATION:
+					LTRACEF("SET_CONFIGURATION %d\n", setup->value);
+					active_config = setup->value;
+					usbc_ep0_ack();
+					break;
+
+				case GET_CONFIGURATION:
+					LTRACEF("GET_CONFIGURATION\n");
+					usbc_ep0_send(&active_config, 1, setup->length);
+					break;
+
+				case SET_INTERFACE:
+					LTRACEF("SET_INTERFACE %d\n", setup->value);
+					usbc_ep0_ack();
+					break;
+
+				case GET_INTERFACE: {
+					static uint8_t i = 1;
+					LTRACEF("GET_INTERFACE\n");
+					usbc_ep0_send(&i, 1, setup->length);
+					break;
+				}
+
+				case GET_STATUS: {
+					static uint16_t i = 1; // self powered
+					LTRACEF("GET_STATUS\n");
+					usbc_ep0_send(&i, 2, setup->length);
+					break;
+				}
+				default:
+					LTRACEF("unhandled standard request 0x%x\n", setup->request);
+			}
+		}
+	}
+
+	return 0;
+}
+
+void usb_setup(usb_config *_config)
+{
+	ASSERT(_config);
+
+	config = _config;
+
+	ASSERT(usb_active == false);
+
+	// set the default usb control callback handler	
+	usbc_set_callback(&default_usb_callback);
+}
+
+void usb_start(void)
+{
+	ASSERT(config);
+	ASSERT(usb_active == false);
+
+	// go online
+	usbc_set_active(true);
+	usb_active = true;
+}
+
+void usb_stop(void)
+{
+	ASSERT(usb_active == true);
+
+	usb_active = false;
+	usbc_set_active(false);
+}
 
 void usb_init(void)
 {
diff --git a/include/dev/usb.h b/include/dev/usb.h
index 556a4ed..a35a819 100644
--- a/include/dev/usb.h
+++ b/include/dev/usb.h
@@ -23,9 +23,39 @@
 #ifndef __DEV_USB_H
 #define __DEV_USB_H
 
-/* device side usb stack api */
+#include <sys/types.h>
+#include <compiler.h>
+
+/* top level initialization for usb client, abstracts away the interfaces */
+typedef struct {
+	void *desc;
+	size_t len;
+} usb_descriptor __ALIGNED(2);
+
+/* complete usb config struct, passed in to usb_setup() */
+typedef struct {
+	struct usb_descriptor_speed {
+		usb_descriptor device;
+		usb_descriptor device_qual;
+		usb_descriptor config;
+	} lowspeed, highspeed;
+	usb_descriptor device_string;
+	usb_descriptor mfg_string;
+	usb_descriptor serial_string;
+	usb_descriptor langid;
+} usb_config;
 
 void usb_init(void);
 
+/* external code needs to set up the usb stack via the following calls */
+void usb_setup(usb_config *config);
+
+/* apped new interface descriptors to the existing config if desired */
+int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len);
+int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len);
+
+void usb_start(void);
+void usb_stop(void);
+
 #endif
 
diff --git a/include/dev/usbc.h b/include/dev/usbc.h
index 26c1945..f566190 100644
--- a/include/dev/usbc.h
+++ b/include/dev/usbc.h
@@ -23,8 +23,76 @@
 #ifndef __DEV_USBC_H
 #define __DEV_USBC_H
 
-/* device side usb controller api (used by the usb stack) */
+#include <sys/types.h>
+#include <debug.h>
+#include <hw/usb.h>
+
 void usbc_init(void);
 
+typedef uint ep_t;
+
+typedef enum {
+	IN = 0,
+	OUT
+} ep_dir_t;
+
+typedef enum {
+	CB_RESET,
+	CB_SUSPEND,
+	CB_RESUME,
+	CB_DISCONNECT,
+	CB_ONLINE,
+	CB_OFFLINE,
+	CB_SETUP_MSG,
+	
+	/* endpoint transfer stuff */
+	CB_EP_RXCOMPLETE,
+	CB_EP_TXCOMPLETE,
+	CB_EP_TRANSFER_CANCELLED,
+} usbc_callback_op_t;
+
+typedef struct {
+	void *buf;
+	size_t buflen;
+	uint bufpos;
+	int result;
+	void *extra; // extra pointer to store whatever you want
+} usbc_transfer;
+
+enum {
+	USB_TRANSFER_RESULT_OK = 0,
+	USB_TRANSFER_RESULT_ERR = -1,
+	USB_TRANSFER_RESULT_CANCELLED = -2,
+};
+
+typedef int (*ep_callback)(ep_t endpoint, usbc_callback_op_t op, usbc_transfer *transfer);
+
+void usbc_setup_endpoint(ep_t ep, ep_dir_t dir, bool active, ep_callback callback, uint width, uint blocksize);
+int usbc_queue_rx(ep_t ep, usbc_transfer *transfer);
+int usbc_queue_tx(ep_t ep, usbc_transfer *transfer);
+
+/* setup arg is valid during CB_SETUP_MSG */
+union usb_callback_args {
+	const struct usb_setup *setup;
+};
+
+typedef int (*usb_callback)(usbc_callback_op_t op, const union usb_callback_args *args);
+
+int usbc_set_callback(usb_callback);
+int usbc_set_active(bool active);
+
+/* called back from within a callback to handle setup responses */
+void usbc_ep0_ack(void);
+void usbc_ep0_stall(void);
+void usbc_ep0_send(const void *buf, size_t len, size_t maxlen);
+void usbc_ep0_recv(void *buf, size_t len, ep_callback);
+
+bool usbc_is_highspeed(void);
+
+static inline void usbc_dump_transfer(const usbc_transfer *t)
+{
+	printf("usb transfer %p: buf %p, buflen %zd, bufpos %u, result %d\n", t, t->buf, t->buflen, t->bufpos, t->result);
+}
+
 #endif