+/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>.
+ * Modifications by Jacob Alexander 2014 <haata@kiibohd.com>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USB_H
+#define __USB_H
+
+// ----- Compiler Includes -----
+
+#include <sys/types.h>
+
+
+
+// ----- Local Includes -----
+
+#include "mchck.h"
+#include "usb-common.h"
+
+
+
+// ----- Defines -----
+
+#define USB_CTRL_REQ_DIR_SHIFT 0
+#define USB_CTRL_REQ_TYPE_SHIFT 1
+#define USB_CTRL_REQ_RECP_SHIFT 3
+#define USB_CTRL_REQ_CODE_SHIFT 8
+#define USB_CTRL_REQ(req_inout, req_type, req_code) \
+ (uint16_t) \
+ ((USB_CTRL_REQ_##req_inout << USB_CTRL_REQ_DIR_SHIFT) \
+ |(USB_CTRL_REQ_##req_type << USB_CTRL_REQ_TYPE_SHIFT) \
+ |(USB_CTRL_REQ_##req_code << USB_CTRL_REQ_CODE_SHIFT))
+
+
+
+// ----- Macros -----
+
+#define USB_DESC_STRING(s) \
+ (const void *)&(const struct { \
+ struct usb_desc_string_t dsc; \
+ char16_t str[sizeof(s) / 2 - 1]; \
+ }) {{ \
+ .bLength = sizeof(struct usb_desc_string_t) + \
+ sizeof(s) - 2, \
+ .bDescriptorType = USB_DESC_STRING, \
+ }, \
+ s \
+ }
+#define USB_DESC_STRING_LANG_ENUS USB_DESC_STRING(u"\x0409")
+#define USB_DESC_STRING_SERIALNO ((const void *)1)
+
+#define USB_FUNCTION_IFACE(iface, iface_off, tx_ep_off, rx_ep_off) \
+ ((iface_off) + (iface))
+#define USB_FUNCTION_TX_EP(ep, iface_off, tx_ep_off, rx_ep_off) \
+ ((tx_ep_off) + (ep))
+#define USB_FUNCTION_RX_EP(ep, iface_off, tx_ep_off, rx_ep_off) \
+ ((rx_ep_off) + (ep))
+
+
+#define USB__INCREMENT(i, _0) (i + 1)
+#define USB__COUNT_IFACE_EP(i, e) \
+ __DEFER(USB__COUNT_IFACE_EP_)(__EXPAND i, e)
+#define USB__COUNT_IFACE_EP_(iface, tx_ep, rx_ep, func) \
+ (iface + USB_FUNCTION_ ## func ## _IFACE_COUNT, \
+ tx_ep + USB_FUNCTION_ ## func ## _TX_EP_COUNT, \
+ rx_ep + USB_FUNCTION_ ## func ## _RX_EP_COUNT)
+#define USB__GET_FUNCTION_IFACE_COUNT(iter, func) \
+ USB_FUNCTION_ ## func ## _IFACE_COUNT +
+
+#define USB__DEFINE_FUNCTION_DESC(iter, func) \
+ USB_FUNCTION_DESC_ ## func ## _DECL __CAT(__usb_func_desc, __COUNTER__);
+#define USB__INIT_FUNCTION_DESC(iter, func) \
+ USB_FUNCTION_DESC_ ## func iter,
+
+#define USB__DEFINE_CONFIG_DESC(confignum, name, ...) \
+ &((const struct name { \
+ struct usb_desc_config_t config; \
+ __REPEAT_INNER(, __EAT, USB__DEFINE_FUNCTION_DESC, __VA_ARGS__) \
+ }){ \
+ .config = { \
+ .bLength = sizeof(struct usb_desc_config_t), \
+ .bDescriptorType = USB_DESC_CONFIG, \
+ .wTotalLength = sizeof(struct name), \
+ .bNumInterfaces = __REPEAT_INNER(, __EAT, USB__GET_FUNCTION_IFACE_COUNT, __VA_ARGS__) 0, \
+ .bConfigurationValue = confignum, \
+ .iConfiguration = 0, \
+ .one = 1, \
+ .bMaxPower = 50 \
+ }, \
+ __REPEAT_INNER((0, 0, 0), USB__COUNT_IFACE_EP, USB__INIT_FUNCTION_DESC, __VA_ARGS__) \
+ }).config
+
+
+#define USB__DEFINE_CONFIG(iter, args) \
+ __DEFER(USB__DEFINE_CONFIG_)(iter, __EXPAND args)
+
+#define USB__DEFINE_CONFIG_(confignum, initfun, ...) \
+ &(const struct usbd_config){ \
+ .init = initfun, \
+ .desc = USB__DEFINE_CONFIG_DESC( \
+ confignum, \
+ __CAT(__usb_desc, __COUNTER__), \
+ __VA_ARGS__) \
+ },
+
+#define USB_INIT_DEVICE(vid, pid, manuf, product, ...) \
+ { \
+ .dev_desc = &(const struct usb_desc_dev_t){ \
+ .bLength = sizeof(struct usb_desc_dev_t), \
+ .bDescriptorType = USB_DESC_DEV, \
+ .bcdUSB = { .maj = 2 }, \
+ .bDeviceClass = USB_DEV_CLASS_SEE_IFACE, \
+ .bDeviceSubClass = USB_DEV_SUBCLASS_SEE_IFACE, \
+ .bDeviceProtocol = USB_DEV_PROTO_SEE_IFACE, \
+ .bMaxPacketSize0 = EP0_BUFSIZE, \
+ .idVendor = vid, \
+ .idProduct = pid, \
+ .bcdDevice = { .raw = 0 }, \
+ .iManufacturer = 1, \
+ .iProduct = 2, \
+ .iSerialNumber = 3, \
+ .bNumConfigurations = __PP_NARG(__VA_ARGS__), \
+ }, \
+ .string_descs = (const struct usb_desc_string_t * const []){ \
+ USB_DESC_STRING_LANG_ENUS, \
+ USB_DESC_STRING(manuf), \
+ USB_DESC_STRING(product), \
+ USB_DESC_STRING_SERIALNO, \
+ NULL \
+ }, \
+ .configs = { \
+ __REPEAT(1, USB__INCREMENT, USB__DEFINE_CONFIG, __VA_ARGS__) \
+ NULL \
+ } \
+ }
+
+
+
+// ----- Structs & Enumerations -----
+
+/**
+ * Note: bitfields ahead.
+ * GCC fills the fields lsb-to-msb on little endian.
+ */
+
+/**
+ * USB descriptors
+ */
+
+enum usb_desc_type {
+ USB_DESC_DEV = 1,
+ USB_DESC_CONFIG = 2,
+ USB_DESC_STRING = 3,
+ USB_DESC_IFACE = 4,
+ USB_DESC_EP = 5,
+ USB_DESC_DEVQUAL = 6,
+ USB_DESC_OTHERSPEED = 7,
+ USB_DESC_POWER = 8
+};
+
+struct usb_desc_type_t {
+ UNION_STRUCT_START(8);
+ enum usb_desc_type id : 5;
+ enum usb_desc_type_type {
+ USB_DESC_TYPE_STD = 0,
+ USB_DESC_TYPE_CLASS = 1,
+ USB_DESC_TYPE_VENDOR = 2
+ } type_type : 2;
+ uint8_t _rsvd0 : 1;
+ UNION_STRUCT_END;
+};
+CTASSERT_SIZE_BYTE(struct usb_desc_type_t, 1);
+
+enum usb_dev_class {
+ USB_DEV_CLASS_SEE_IFACE = 0,
+ USB_DEV_CLASS_APP = 0xfe,
+ USB_DEV_CLASS_VENDOR = 0xff
+};
+
+enum usb_dev_subclass {
+ USB_DEV_SUBCLASS_SEE_IFACE = 0,
+ USB_DEV_SUBCLASS_VENDOR = 0xff
+};
+
+enum usb_dev_proto {
+ USB_DEV_PROTO_SEE_IFACE = 0,
+ USB_DEV_PROTO_VENDOR = 0xff
+};
+
+struct usb_bcd_t {
+ UNION_STRUCT_START(16);
+ struct {
+ uint8_t sub : 4;
+ uint8_t min : 4;
+ uint16_t maj : 8;
+ };
+ UNION_STRUCT_END;
+};
+CTASSERT_SIZE_BYTE(struct usb_bcd_t, 2);
+
+struct usb_desc_generic_t {
+ uint8_t bLength;
+ struct usb_desc_type_t bDescriptorType;
+ uint8_t data[];
+};
+CTASSERT_SIZE_BYTE(struct usb_desc_generic_t, 2);
+
+struct usb_desc_dev_t {
+ uint8_t bLength;
+ enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_DEV */
+ struct usb_bcd_t bcdUSB; /* = 0x0200 */
+ enum usb_dev_class bDeviceClass : 8;
+ enum usb_dev_subclass bDeviceSubClass : 8;
+ enum usb_dev_proto bDeviceProtocol : 8;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ struct usb_bcd_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+};
+CTASSERT_SIZE_BYTE(struct usb_desc_dev_t, 18);
+
+struct usb_desc_ep_t {
+ uint8_t bLength;
+ enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_EP */
+ union {
+ struct {
+ uint8_t ep_num : 4;
+ uint8_t _rsvd0 : 3;
+ uint8_t in : 1;
+ };
+ uint8_t bEndpointAddress;
+ };
+ struct {
+ enum usb_ep_type {
+ USB_EP_CONTROL = 0,
+ USB_EP_ISO = 1,
+ USB_EP_BULK = 2,
+ USB_EP_INTR = 3
+ } type : 2;
+ enum usb_ep_iso_synctype {
+ USB_EP_ISO_NOSYNC = 0,
+ USB_EP_ISO_ASYNC = 1,
+ USB_EP_ISO_ADAPTIVE = 2,
+ USB_EP_ISO_SYNC = 3
+ } sync_type : 2;
+ enum usb_ep_iso_usagetype {
+ USB_EP_ISO_DATA = 0,
+ USB_EP_ISO_FEEDBACK = 1,
+ USB_EP_ISO_IMPLICIT = 2
+ } usage_type : 2;
+ uint8_t _rsvd1 : 2;
+ };
+ struct {
+ uint16_t wMaxPacketSize : 11;
+ uint16_t _rsvd2 : 5;
+ };
+ uint8_t bInterval;
+} __packed;
+CTASSERT_SIZE_BYTE(struct usb_desc_ep_t, 7);
+
+struct usb_desc_iface_t {
+ uint8_t bLength;
+ enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_IFACE */
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ enum usb_dev_class bInterfaceClass : 8;
+ enum usb_dev_subclass bInterfaceSubClass: 8;
+ enum usb_dev_proto bInterfaceProtocol : 8;
+ uint8_t iInterface;
+};
+CTASSERT_SIZE_BYTE(struct usb_desc_iface_t, 9);
+
+struct usb_desc_config_t {
+ uint8_t bLength;
+ enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_CONFIG */
+ uint16_t wTotalLength; /* size of config, iface, ep */
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ struct {
+ uint8_t _rsvd0 : 5;
+ uint8_t remote_wakeup : 1;
+ uint8_t self_powered : 1;
+ uint8_t one : 1; /* = 1 for historical reasons */
+ };
+ uint8_t bMaxPower; /* units of 2mA */
+} __packed;
+CTASSERT_SIZE_BYTE(struct usb_desc_config_t, 9);
+
+struct usb_desc_string_t {
+ uint8_t bLength;
+ enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_STRING */
+ const char16_t bString[];
+};
+CTASSERT_SIZE_BYTE(struct usb_desc_string_t, 2);
+
+struct usb_ctrl_req_t {
+ union /* reqtype and request & u16 */ {
+ struct /* reqtype and request */ {
+ union /* reqtype in bitfield & u8 */ {
+ struct /* reqtype */ {
+ enum usb_ctrl_req_recp {
+ USB_CTRL_REQ_DEV = 0,
+ USB_CTRL_REQ_IFACE = 1,
+ USB_CTRL_REQ_EP = 2,
+ USB_CTRL_REQ_OTHER = 3
+ } recp : 5;
+ enum usb_ctrl_req_type {
+ USB_CTRL_REQ_STD = 0,
+ USB_CTRL_REQ_CLASS = 1,
+ USB_CTRL_REQ_VENDOR = 2
+ } type : 2;
+ enum usb_ctrl_req_dir {
+ USB_CTRL_REQ_OUT = 0,
+ USB_CTRL_REQ_IN = 1
+ } in : 1;
+ };
+ uint8_t bmRequestType;
+ }; /* union */
+ enum usb_ctrl_req_code {
+ USB_CTRL_REQ_GET_STATUS = 0,
+ USB_CTRL_REQ_CLEAR_FEATURE = 1,
+ USB_CTRL_REQ_SET_FEATURE = 3,
+ USB_CTRL_REQ_SET_ADDRESS = 5,
+ USB_CTRL_REQ_GET_DESCRIPTOR = 6,
+ USB_CTRL_REQ_SET_DESCRIPTOR = 7,
+ USB_CTRL_REQ_GET_CONFIGURATION = 8,
+ USB_CTRL_REQ_SET_CONFIGURATION = 9,
+ USB_CTRL_REQ_GET_INTERFACE = 10,
+ USB_CTRL_REQ_SET_INTERFACE = 11,
+ USB_CTRL_REQ_SYNC_FRAME = 12
+ } bRequest : 8;
+ }; /* struct */
+ uint16_t type_and_req;
+ }; /* union */
+ union {
+ uint16_t wValue;
+ struct {
+ uint8_t wValueLow;
+ uint8_t wValueHigh;
+ };
+ };
+ uint16_t wIndex;
+ uint16_t wLength;
+};
+CTASSERT_SIZE_BYTE(struct usb_ctrl_req_t, 8);
+
+/**
+ * status replies for GET_STATUS.
+ */
+
+struct usb_ctrl_req_status_dev_t {
+ uint16_t self_powered : 1;
+ uint16_t remote_wakeup : 1;
+ uint16_t _rsvd : 14;
+};
+CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_dev_t, 16);
+
+struct usb_ctrl_req_status_iface_t {
+ uint16_t _rsvd;
+};
+CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_iface_t, 16);
+
+struct usb_ctrl_req_status_ep_t {
+ uint16_t halt : 1;
+ uint16_t _rsvd : 15;
+};
+CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_ep_t, 16);
+
+/**
+ * Descriptor type (in req->value) for GET_DESCRIPTOR.
+ */
+struct usb_ctrl_req_desc_t {
+ uint8_t idx;
+ enum usb_desc_type type : 8;
+};
+CTASSERT_SIZE_BIT(struct usb_ctrl_req_desc_t, 16);
+
+/**
+ * Feature selector (in req->value) for CLEAR_FEATURE.
+ */
+enum usb_ctrl_req_feature {
+ USB_CTRL_REQ_FEAT_EP_HALT = 0,
+ USB_CTRL_REQ_FEAT_DEV_REMOTE_WKUP = 1,
+ USB_CTRL_REQ_FEAT_TEST_MODE = 2
+};
+
+
+struct usb_xfer_info;
+typedef void (*ep_callback_t)(void *buf, ssize_t len, void *data);
+
+/**
+ * (Artificial) function. Aggregates one or more interfaces.
+ */
+struct usbd_function {
+ int (*configure)(int orig_iface, int iface, int altsetting, void *data);
+ int (*control)(struct usb_ctrl_req_t *, void *);
+ int interface_count;
+ int ep_rx_count;
+ int ep_tx_count;
+};
+
+struct usbd_function_ctx_header {
+ struct usbd_function_ctx_header *next;
+ const struct usbd_function *function;
+ int interface_offset;
+ int ep_rx_offset;
+ int ep_tx_offset;
+};
+
+
+typedef void (usbd_init_fun_t)(int);
+typedef void (usbd_suspend_resume_fun_t)(void);
+
+/**
+ * Configuration. Contains one or more functions which all will be
+ * active concurrently.
+ */
+struct usbd_config {
+ usbd_init_fun_t *init;
+ usbd_suspend_resume_fun_t *suspend;
+ usbd_suspend_resume_fun_t *resume;
+ /**
+ * We will not set a config for now, because there is not much to
+ * configure, except for power
+ *
+ * const struct usb_desc_config_t *config_desc;
+ */
+ const struct usb_desc_config_t *desc;
+ const struct usbd_function *function[];
+};
+
+
+/**
+ * Device. Contains one or more configurations, out of which only one
+ * is active at a time.
+ */
+struct usbd_device {
+ const struct usb_desc_dev_t *dev_desc;
+ const struct usb_desc_string_t * const *string_descs;
+ const struct usbd_config *configs[];
+};
+
+
+
+/* Provided by MD code */
+struct usbd_ep_pipe_state_t;
+
+
+
+// ----- Functions -----
+
+void *usb_get_xfer_data(struct usb_xfer_info *);
+enum usb_tok_pid usb_get_xfer_pid(struct usb_xfer_info *);
+int usb_get_xfer_ep(struct usb_xfer_info *);
+enum usb_ep_dir usb_get_xfer_dir(struct usb_xfer_info *);
+void usb_enable_xfers(void);
+void usb_set_addr(int);
+void usb_ep_stall(int);
+size_t usb_ep_get_transfer_size(struct usbd_ep_pipe_state_t *);
+void usb_queue_next(struct usbd_ep_pipe_state_t *, void *, size_t);
+void usb_pipe_stall(struct usbd_ep_pipe_state_t *);
+void usb_pipe_unstall(struct usbd_ep_pipe_state_t *);
+void usb_pipe_enable(struct usbd_ep_pipe_state_t *s);
+void usb_pipe_disable(struct usbd_ep_pipe_state_t *s);
+#ifdef VUSB
+void vusb_main_loop(void);
+#else
+void usb_poll(void);
+#endif
+int usb_tx_serialno(size_t reqlen);
+
+/* Provided by MI code */
+void usb_init(const struct usbd_device *);
+void usb_attach_function(const struct usbd_function *function, struct usbd_function_ctx_header *ctx);
+void usb_handle_transaction(struct usb_xfer_info *);
+void usb_setup_control(void);
+void usb_handle_control_status_cb(ep_callback_t cb);
+void usb_handle_control_status(int);
+struct usbd_ep_pipe_state_t *usb_init_ep(struct usbd_function_ctx_header *ctx, int ep, enum usb_ep_dir dir, size_t size);
+int usb_rx(struct usbd_ep_pipe_state_t *, void *, size_t, ep_callback_t, void *);
+int usb_tx(struct usbd_ep_pipe_state_t *, const void *, size_t, size_t, ep_callback_t, void *);
+
+int usb_ep0_rx(void *, size_t, ep_callback_t, void *);
+void *usb_ep0_tx_inplace_prepare(size_t len);
+int usb_ep0_tx(void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data);
+int usb_ep0_tx_cp(const void *, size_t, size_t, ep_callback_t, void *);
+
+
+
+// ----- DFU USB Additional Includes -----
+
+#include "dfu.h"
+
+#endif
+