summaryrefslogtreecommitdiff
path: root/usb_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'usb_common.c')
-rw-r--r--usb_common.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/usb_common.c b/usb_common.c
new file mode 100644
index 0000000..0427122
--- /dev/null
+++ b/usb_common.c
@@ -0,0 +1,150 @@
+#include <stdint.h> /* for uint{n}_t types */
+#include <string.h> /* memcpy(newlib) */
+#include <assert.h> /* for assert(newlib) */
+#include <stdlib.h> /* for malloc(pico_malloc) and realloc(pico_malloc) */
+#include "bsp/board_api.h" /* for board_usb_get_serial(TinyUSB) */
+#include "tusb.h" /* for various tusb_*_t types */
+
+#include "tusb_helpers.h" /* for LANGID_*, UTF16(), utf16_strncpy */
+#include "usb_common.h"
+
+/* Strings ********************************************************************/
+
+/**
+ * Return a pointer to the USB "String Descriptor" (see USB 2.0 §9.6.7
+ * "String").
+ */
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ static uint16_t desc[0xFF/2];
+ static const size_t max_u16len = (sizeof desc / sizeof desc[0]) - 1;
+
+ static uint16_t const langids[] = {
+ LANGID_EN_US,
+ };
+
+ size_t bytelen = 0;
+ if (index == 0) {
+ memcpy(&desc[1], langids, sizeof langids);
+ bytelen = sizeof langids;
+ } else {
+ switch (langid) {
+ case LANGID_EN_US:
+ switch (index) {
+ case STRID_MANUF: bytelen = 2 * utf16_strncpy(&desc[1], UTF16("Umorpha Systems"), max_u16len); break;
+ case STRID_PRODUCT: bytelen = 2 * utf16_strncpy(&desc[1], UTF16("SBC-Harness Keyboard"), max_u16len); break;
+ case STRID_SERIAL: bytelen = 2 * board_usb_get_serial(&desc[1], max_u16len); break;
+ case STRID_CFG: bytelen = 2 * utf16_strncpy(&desc[1], UTF16("Standard Configuration"), max_u16len); break;
+ case STRID_KBD_IFC: bytelen = 2 * utf16_strncpy(&desc[1], UTF16("Keyboard Interface"), max_u16len); break;
+ default:
+ return NULL;
+ };
+ default:
+ return NULL;
+ }
+ }
+ assert(bytelen <= 0xFF-2);
+ desc[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2+bytelen));
+ return desc;
+}
+
+/* Globals ********************************************************************/
+
+uint8_t cfgnum_std = 0;
+
+void usb_common_init(void) {
+ if (cfgnum_std)
+ return;
+ cfgnum_std = usb_add_config(
+ STRID_CFG, /* iConfiguration ; Index of string descriptor describing this configuration */
+ TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, /* bmAttributes ; Bitmap of flags (self-powered and remote-wakeup are the only flags defined in USB 2.0) */
+ 100); /* bMaxPower (in mA) ; Maximum power consumption of the device when in this configuration */
+}
+
+/* Main utilities *************************************************************/
+
+static uint8_t configc = 0;
+static uint8_t **configv = NULL;
+
+uint8_t usb_add_config(uint8_t iConfiguration, uint8_t bmAttributes, uint8_t bMaxPower_mA) {
+ uint8_t *desc;
+
+ configv = realloc(configv, (sizeof configv[0]) * ++configc);
+ desc = configv[configc-1] = malloc(TUD_CONFIG_DESC_LEN);
+
+ memcpy(desc, (uint8_t[]){
+ /* USB configuration descriptor header (USB 2.0 §9.6.4 "Configuration") */
+ TUD_CONFIG_DESCRIPTOR(
+ configc, /* bConfigurationValue */
+ 0, /* bNumInterfaces (will be incremented by usb_add_interface() */
+ iConfiguration, /* iConfiguration */
+ TUD_CONFIG_DESC_LEN, /* wTotalLength (will be incremented by usb_add_interface() */
+ bmAttributes, /* bmAttributes */
+ bMaxPower_mA+1), /* bMaxPower (+1 because tusb just does n/2 instead of (n+1)/2) */
+ }, TUD_CONFIG_DESC_LEN);
+
+ return configc;
+}
+
+uint8_t usb_add_interface(uint8_t cfg_num, uint16_t ifc_len, uint8_t *ifc_dat) {
+ assert(cfg_num > 0 && cfg_num <= configc);
+ assert(ifc_len >= 3);
+ assert(ifc_dat);
+
+ uint8_t *desc = configv[cfg_num-1];
+ // wTotalLength
+ uint16_t total_len = TU_U16(desc[3], desc[2]) + ifc_len;
+ desc[3] = TU_U16_HIGH(total_len);
+ desc[2] = TU_U16_LOW(total_len);
+ // bNumInterfaces
+ ifc_dat[3] = desc[4]++;
+
+ desc = configv[cfg_num-1] = realloc(desc, total_len);
+ memcpy(&desc[total_len-ifc_len], ifc_dat, ifc_len);
+
+ return ifc_dat[3];
+}
+
+/**
+ * Return a pointer to the USB "Device Descriptor" (see USB 2.0 §9.6.1
+ * "Device") as a byte-array.
+ */
+uint8_t const *tud_descriptor_device_cb(void) {
+ /* Our device descriptor is static, so just declare it as a
+ * static const. */
+ static tusb_desc_device_t desc = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = 0x200, /* USB 2.0 */
+
+ /* Use the "base device class", which means to use information
+ * from interface descriptors instead of a global device
+ * descriptor. */
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = 0xCAFE, /* Vendor ID (assigned by the USB-IF) */
+ .idProduct = 0x4001, /* Product ID (assigned by the manufacturer) */
+ .bcdDevice = 0x0100, /* Device release number */
+
+ .iManufacturer = STRID_MANUF, /* Index of string descriptor describing manufacturer */
+ .iProduct = STRID_PRODUCT, /* Index of string descriptor describing product */
+ .iSerialNumber = STRID_SERIAL, /* Index of string descriptor describing the device's serial number */
+
+ .bNumConfigurations = 0, /* Number of possible configurations */
+ };
+ desc.bNumConfigurations = configc;
+ return (uint8_t const *) &desc;
+}
+
+/**
+ * Return a pointer to the USB config "Configuration Descriptor" (see
+ * USB 2.0 §9.6.4 "Configuration") for the given index.
+ */
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ if (index >= configc)
+ return NULL;
+ return configv[index];
+}