Add firmware for the ATUSB IEEE 802.15.4 USB Adapter
[linux-libre-firmware.git] / atusb / usb / usb.c
diff --git a/atusb/usb/usb.c b/atusb/usb/usb.c
new file mode 100644 (file)
index 0000000..543d8c2
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * fw/usb/usb.c - USB hardware setup and standard device requests
+ *
+ * Written 2008-2011, 2013, 2015 by Werner Almesberger
+ * Copyright 2008-2011, 2013, 2015 Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * Known issues:
+ * - no suspend/resume
+ * - should support EP clearing and stalling
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb.h"
+#include "board.h"
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if 1
+extern void panic(void);
+#define BUG_ON(cond)   do { if (cond) panic(); } while (0)
+#else
+#define BUG_ON(cond)
+#endif
+
+bool (*user_setup)(const struct setup_request *setup);
+void (*user_set_interface)(int nth);
+bool (*user_get_descriptor)(uint8_t type, uint8_t index,
+    const uint8_t **reply, uint8_t *size);
+void (*user_reset)(void);
+
+
+void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
+    uint8_t size, void (*callback)(void *user), void *user)
+{
+       BUG_ON(ep->state);
+       ep->state = state;
+       ep->buf = buf;
+       ep->end = buf+size;
+       ep->callback = callback;
+       ep->user = user;
+       usb_ep_change(ep);
+}
+
+
+static bool get_descriptor(uint8_t type, uint8_t index, uint16_t length)
+{
+       const uint8_t *reply;
+       uint8_t size;
+
+       switch (type) {
+       case USB_DT_DEVICE:
+               reply = device_descriptor;
+               size = reply[0];
+               break;
+       case USB_DT_CONFIG:
+               if (index)
+                       return 0;
+               reply = config_descriptor;
+               size = reply[2];
+               break;
+       default:
+               if (!user_get_descriptor)
+                       return 0;
+               if (!user_get_descriptor(type, index, &reply, &size))
+                       return 0;
+       }
+       if (length < size)
+               size = length;
+       usb_send(&eps[0], reply, size, NULL, NULL);
+       return 1;
+}
+
+
+bool handle_setup(const struct setup_request *setup)
+{
+       switch (setup->bmRequestType | setup->bRequest << 8) {
+
+       /*
+        * Device request
+        *
+        * See http://www.beyondlogic.org/usbnutshell/usb6.htm
+        */
+
+       case FROM_DEVICE(GET_STATUS):
+               if (setup->wLength != 2)
+                       return 0;
+               usb_send(&eps[0], "\000", 2, NULL, NULL);
+               break;
+       case TO_DEVICE(CLEAR_FEATURE):
+               break;
+       case TO_DEVICE(SET_FEATURE):
+               return 0;
+       case TO_DEVICE(SET_ADDRESS):
+               set_addr(setup->wValue);
+               break;
+       case FROM_DEVICE(GET_DESCRIPTOR):
+       case FROM_INTERFACE(GET_DESCRIPTOR):
+               if (!get_descriptor(setup->wValue >> 8, setup->wValue,
+                   setup->wLength))
+                       return 0;
+               break;
+       case TO_DEVICE(SET_DESCRIPTOR):
+               return 0;
+       case FROM_DEVICE(GET_CONFIGURATION):
+               usb_send(&eps[0], "", 1, NULL, NULL);
+               break;
+       case TO_DEVICE(SET_CONFIGURATION):
+               if (setup->wValue != config_descriptor[5])
+                       return 0;
+               break;
+
+       /*
+        * Interface request
+        */
+
+       case FROM_INTERFACE(GET_STATUS):
+               return 0;
+       case TO_INTERFACE(CLEAR_FEATURE):
+               return 0;
+       case TO_INTERFACE(SET_FEATURE):
+               return 0;
+       case FROM_INTERFACE(GET_INTERFACE):
+               return 0;
+       case TO_INTERFACE(SET_INTERFACE):
+               {
+                       const uint8_t *interface_descriptor =
+                           config_descriptor+9;
+                       const uint8_t *p;
+                       int i;
+
+                       i = 0;
+                       for (p = interface_descriptor;
+                           p != config_descriptor+config_descriptor[2];
+                           p += p[0]) {
+                               if (p[1] != USB_DT_INTERFACE)
+                                       continue;
+                               if (p[2] == setup->wIndex &&
+                                   p[3] == setup->wValue) {
+                                       if (user_set_interface)
+                                               user_set_interface(i);
+                                       return 1;
+                               }
+                               i++;
+                       }
+                       return 0;
+               }
+               break;
+
+       /*
+        * Endpoint request
+        */
+
+       case FROM_ENDPOINT(GET_STATUS):
+               return 0;
+       case TO_ENDPOINT(CLEAR_FEATURE):
+               return 0;
+       case TO_ENDPOINT(SET_FEATURE):
+               return 0;
+       case FROM_ENDPOINT(SYNCH_FRAME):
+               return 0;
+
+       default:
+               if (user_setup)
+                       return user_setup(setup);
+               return 0;
+       }
+
+       return 1;
+}