diff --git a/include/usb/class/usb_hid.h b/include/usb/class/usb_hid.h new file mode 100644 index 00000000000..410b5a58a6f --- /dev/null +++ b/include/usb/class/usb_hid.h @@ -0,0 +1,71 @@ +/* + * Human Interface Device (HID) USB class core header + * + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief USB Human Interface Device (HID) Class public header + * + * Header follows Device Class Definition for Human Interface Devices (HID) + * Version 1.11 document (HID1_11-1.pdf). + */ + +#ifndef __USB_HID_H__ +#define __USB_HID_H__ + +struct usb_hid_class_subdescriptor { + u8_t bDescriptorType; + u16_t wDescriptorLength; +} __packed; + +struct usb_hid_descriptor { + u8_t bLength; + u8_t bDescriptorType; + u16_t bcdHID; + u8_t bCountryCode; + u8_t bNumDescriptors; + + /* + * Specification says at least one Class Descriptor needs to + * be present (Report Descriptor). + */ + struct usb_hid_class_subdescriptor subdesc[1]; +} __packed; + +/* HID Class Specific Requests */ + +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* Public headers */ + +struct hid_ops { + int (*get_report)(struct usb_setup_packet *setup, s32_t *len, + u8_t **data); + int (*get_idle)(struct usb_setup_packet *setup, s32_t *len, + u8_t **data); + int (*get_protocol)(struct usb_setup_packet *setup, s32_t *len, + u8_t **data); + int (*set_report)(struct usb_setup_packet *setup, s32_t *len, + u8_t **data); + int (*set_idle)(struct usb_setup_packet *setup, s32_t *len, + u8_t **data); + int (*set_protocol)(struct usb_setup_packet *setup, s32_t *len, + u8_t **data); +}; + +/* Register HID device */ +void usb_hid_register_device(const u8_t *desc, size_t size, struct hid_ops *op); + +/* Initialize USB HID */ +int usb_hid_init(void); + +#endif /* __USB_HID_H__ */ diff --git a/subsys/usb/class/CMakeLists.txt b/subsys/usb/class/CMakeLists.txt index 9060918365c..0e60fc0481c 100644 --- a/subsys/usb/class/CMakeLists.txt +++ b/subsys/usb/class/CMakeLists.txt @@ -2,3 +2,4 @@ zephyr_sources_ifdef(CONFIG_USB_CDC_ACM cdc_acm.c) zephyr_sources_ifdef(CONFIG_USB_MASS_STORAGE mass_storage.c) add_subdirectory_ifdef(CONFIG_USB_DEVICE_NETWORK netusb) +add_subdirectory_ifdef(CONFIG_USB_DEVICE_HID hid) diff --git a/subsys/usb/class/Kconfig b/subsys/usb/class/Kconfig index 2765f3c65a0..99064b4db5a 100644 --- a/subsys/usb/class/Kconfig +++ b/subsys/usb/class/Kconfig @@ -134,4 +134,6 @@ config SYS_LOG_USB_MASS_STORAGE_LEVEL source "subsys/usb/class/netusb/Kconfig" +source "subsys/usb/class/hid/Kconfig" + endif # CONFIG_USB_DEVICE_STACK diff --git a/subsys/usb/class/hid/CMakeLists.txt b/subsys/usb/class/hid/CMakeLists.txt new file mode 100644 index 00000000000..c7d5adabc6c --- /dev/null +++ b/subsys/usb/class/hid/CMakeLists.txt @@ -0,0 +1,12 @@ +zephyr_library() + +zephyr_library_include_directories( + # USB headers + #${ZEPHYR_BASE}/include/drivers/usb + ${ZEPHYR_BASE}/include/usb + ${ZEPHYR_BASE}/subsys/usb + ) + +zephyr_library_sources( + core.c + ) diff --git a/subsys/usb/class/hid/Kconfig b/subsys/usb/class/hid/Kconfig new file mode 100644 index 00000000000..298b99258ec --- /dev/null +++ b/subsys/usb/class/hid/Kconfig @@ -0,0 +1,32 @@ +# Kconfig - USB HID configuration options + +# +# Copyright (c) 2018 Intel Corp. +# +# SPDX-License-Identifier: Apache-2.0 +# + +config USB_DEVICE_HID + bool + prompt "USB Human Interface Device support" + default n + help + Enables USB Human Interface Device support. + +if USB_DEVICE_HID + +config HID_INT_EP_ADDR + hex + prompt "USB HID Device Interrupt Endpoint address" + default 0x81 + range 0x81 0x8F + help + USB HID Device interrupt endpoint address + +config HID_INTERRUPT_EP_MPS + int + default 16 + help + USB HID Device interrupt endpoint size + +endif # USB_DEVICE_HID diff --git a/subsys/usb/class/hid/core.c b/subsys/usb/class/hid/core.c new file mode 100644 index 00000000000..5cc5ecd3464 --- /dev/null +++ b/subsys/usb/class/hid/core.c @@ -0,0 +1,201 @@ +/* + * Human Interface Device (HID) USB class core + * + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_USB_DEVICE_LEVEL +#define SYS_LOG_DOMAIN "usb/hid" +#include + +#include +#include + +#include +#include + +static struct hid_device_info { + const u8_t *report_desc; + size_t report_size; + + struct hid_ops *ops; +} hid_device; + +static void hid_status_cb(enum usb_dc_status_code status, u8_t *param) +{ + /* Check the USB status and do needed action if required */ + switch (status) { + case USB_DC_ERROR: + SYS_LOG_DBG("USB device error"); + break; + case USB_DC_RESET: + SYS_LOG_DBG("USB device reset detected"); + break; + case USB_DC_CONNECTED: + SYS_LOG_DBG("USB device connected"); + break; + case USB_DC_CONFIGURED: + SYS_LOG_DBG("USB device configured"); + break; + case USB_DC_DISCONNECTED: + SYS_LOG_DBG("USB device disconnected"); + break; + case USB_DC_SUSPEND: + SYS_LOG_DBG("USB device suspended"); + break; + case USB_DC_RESUME: + SYS_LOG_DBG("USB device resumed"); + break; + case USB_DC_UNKNOWN: + default: + SYS_LOG_DBG("USB unknown state"); + break; + } +} + +static int hid_class_handle_req(struct usb_setup_packet *setup, + s32_t *len, u8_t **data) +{ + SYS_LOG_DBG("Class request: bRequest 0x%x bmRequestType 0x%x len %d", + setup->bRequest, setup->bmRequestType, *len); + + if (REQTYPE_GET_DIR(setup->bmRequestType) == REQTYPE_DIR_TO_HOST) { + switch (setup->bRequest) { + case HID_GET_REPORT: + SYS_LOG_DBG("Get Report"); + if (hid_device.ops->get_report) { + return hid_device.ops->get_report(setup, len, + data); + } else { + SYS_LOG_ERR("Mandatory request not supported"); + return -EINVAL; + } + break; + default: + SYS_LOG_ERR("Unhandled request 0x%x", setup->bRequest); + break; + } + } else { + switch (setup->bRequest) { + case HID_SET_IDLE: + SYS_LOG_DBG("Set Idle"); + if (hid_device.ops->set_idle) { + return hid_device.ops->set_idle(setup, len, + data); + } + break; + default: + SYS_LOG_ERR("Unhandled request 0x%x", setup->bRequest); + break; + } + } + + return -ENOTSUP; +} + +static int hid_custom_handle_req(struct usb_setup_packet *setup, + s32_t *len, u8_t **data) +{ + SYS_LOG_DBG("Standard request: bRequest 0x%x bmRequestType 0x%x len %d", + setup->bRequest, setup->bmRequestType, *len); + + if (REQTYPE_GET_DIR(setup->bmRequestType) == REQTYPE_DIR_TO_HOST && + REQTYPE_GET_RECIP(setup->bmRequestType) == + REQTYPE_RECIP_INTERFACE && + setup->bRequest == REQ_GET_DESCRIPTOR) { + switch (setup->wValue) { + case 0x2200: + SYS_LOG_DBG("Return Report Descriptor"); + if (*len > hid_device.report_size) { + SYS_LOG_ERR("Trying to read too much"); + return -ENODATA; + } + *data = (u8_t *)hid_device.report_desc; + break; + default: + return -ENOTSUP; + } + + return 0; + } + + return -ENOTSUP; +} + +static void hid_int_in(u8_t ep, enum usb_dc_ep_cb_status_code ep_status) +{ + SYS_LOG_DBG("ep %x status %d", ep, ep_status); +} + +/* Describe Endpoints configuration */ +static struct usb_ep_cfg_data hid_ep_data[] = { + { + .ep_cb = hid_int_in, + .ep_addr = CONFIG_HID_INT_EP_ADDR + } +}; + +static struct usb_cfg_data hid_config = { + .usb_device_description = NULL, + .cb_usb_status = hid_status_cb, + .interface = { + .class_handler = hid_class_handle_req, + .custom_handler = hid_custom_handle_req, + .payload_data = NULL, + }, + .num_endpoints = NUMOF_ENDPOINTS_HID, + .endpoint = hid_ep_data, +}; + +#if !defined(CONFIG_USB_COMPOSITE_DEVICE) +static u8_t interface_data[64]; +#endif + +int usb_hid_init(void) +{ + int ret; + + SYS_LOG_DBG("Iinitializing HID Device"); + +#ifdef CONFIG_USB_COMPOSITE_DEVICE + ret = composite_add_function(&hid_config, FIRST_IFACE_HID); + if (ret < 0) { + SYS_LOG_ERR("Failed to add HID function"); + return ret; + } +#else + hid_config.interface.payload_data = interface_data; + hid_config.usb_device_description = usb_get_device_descriptor(); + + /* + * Modify Report Descriptor Size + */ + usb_set_hid_report_size(hid_device.report_size); + + /* Initialize the USB driver with the right configuration */ + ret = usb_set_config(&hid_config); + if (ret < 0) { + SYS_LOG_ERR("Failed to config USB"); + return ret; + } + + /* Enable USB driver */ + ret = usb_enable(&hid_config); + if (ret < 0) { + SYS_LOG_ERR("Failed to enable USB"); + return ret; + } +#endif + + return 0; +} + +void usb_hid_register_device(const u8_t *desc, size_t size, struct hid_ops *ops) +{ + hid_device.report_desc = desc; + hid_device.report_size = size; + + hid_device.ops = ops; +} diff --git a/subsys/usb/usb_descriptor.c b/subsys/usb/usb_descriptor.c index 6c46e93d3d9..7b7dc3c5aa7 100644 --- a/subsys/usb/usb_descriptor.c +++ b/subsys/usb/usb_descriptor.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2017 PHYTEC Messtechnik GmbH + * Copyright (c) 2017, 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,6 +13,7 @@ #include #include #include +#include #include "usb_descriptor.h" #define SYS_LOG_LEVEL CONFIG_SYS_LOG_USB_DEVICE_LEVEL @@ -100,6 +102,13 @@ struct dev_common_descriptor { struct usb_ep_descriptor if0_in_ep; struct usb_ep_descriptor if0_out_ep; } __packed mass_cfg; +#endif +#ifdef CONFIG_USB_DEVICE_HID + struct usb_hid_config { + struct usb_if_descriptor if0; + struct usb_hid_descriptor if0_hid; + struct usb_ep_descriptor if0_int_ep; + } __packed hid_cfg; #endif struct usb_string_desription { struct usb_string_descriptor lang_descr; @@ -544,7 +553,48 @@ static struct dev_common_descriptor common_desc = { .bInterval = 0x00, }, }, -#endif +#endif /* CONFIG_USB_MASS_STORAGE */ +#ifdef CONFIG_USB_DEVICE_HID + .hid_cfg = { + /* Interface descriptor */ + .if0 = { + .bLength = sizeof(struct usb_if_descriptor), + .bDescriptorType = USB_INTERFACE_DESC, + .bInterfaceNumber = FIRST_IFACE_HID, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = HID_CLASS, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + .if0_hid = { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = USB_HID_DESC, + .bcdHID = sys_cpu_to_le16(USB_1_1), + .bCountryCode = 0, + .bNumDescriptors = 1, + .subdesc[0] = { + .bDescriptorType = USB_HID_REPORT_DESC, + /* + * descriptor length needs to be set + * after initialization + */ + .wDescriptorLength = 0, + }, + }, + .if0_int_ep = { + .bLength = sizeof(struct usb_ep_descriptor), + .bDescriptorType = USB_ENDPOINT_DESC, + .bEndpointAddress = CONFIG_HID_INT_EP_ADDR, + .bmAttributes = USB_DC_EP_INTERRUPT, + .wMaxPacketSize = + sys_cpu_to_le16( + CONFIG_HID_INTERRUPT_EP_MPS), + .bInterval = 0x09, + }, + }, +#endif /* CONFIG_USB_DEVICE_HID */ .string_descr = { .lang_descr = { .bLength = sizeof(struct usb_string_descriptor), @@ -614,3 +664,11 @@ u8_t *usb_get_device_descriptor(void) return (u8_t *) &common_desc; } + +#ifdef CONFIG_USB_DEVICE_HID +void usb_set_hid_report_size(u16_t report_desc_size) +{ + common_desc.hid_cfg.if0_hid.subdesc[0].wDescriptorLength = + sys_cpu_to_le16(report_desc_size); +} +#endif diff --git a/subsys/usb/usb_descriptor.h b/subsys/usb/usb_descriptor.h index f9a4307c717..2858793b517 100644 --- a/subsys/usb/usb_descriptor.h +++ b/subsys/usb/usb_descriptor.h @@ -41,10 +41,25 @@ #define NUMOF_ENDPOINTS_CDC_ECM 0 #endif +#ifdef CONFIG_USB_DEVICE_HID +#ifdef CONFIG_USB_DEVICE_HID_BOOTP +#define NUMOF_IFACES_HID 2 +#define NUMOF_ENDPOINTS_HID 2 +#else /* CONFIG_USB_DEVICE_HID_BOOTP */ +#define NUMOF_IFACES_HID 1 +#define NUMOF_ENDPOINTS_HID 1 +#endif /* CONFIG_USB_DEVICE_HID_BOOTP */ +#else /* CONFIG_USB_DEVICE_HID */ +#define NUMOF_IFACES_HID 0 +#define NUMOF_ENDPOINTS_HID 0 +#endif /* CONFIG_USB_DEVICE_HID */ + #define NUMOF_IFACES (NUMOF_IFACES_CDC_ACM + NUMOF_IFACES_MASS + \ - NUMOF_IFACES_RNDIS + NUMOF_IFACES_CDC_ECM) + NUMOF_IFACES_RNDIS + NUMOF_IFACES_CDC_ECM + \ + NUMOF_IFACES_HID) #define NUMOF_ENDPOINTS (NUMOF_ENDPOINTS_CDC_ACM + NUMOF_ENDPOINTS_MASS + \ - NUMOF_ENDPOINTS_RNDIS + NUMOF_ENDPOINTS_CDC_ECM) + NUMOF_ENDPOINTS_RNDIS + NUMOF_ENDPOINTS_CDC_ECM + \ + NUMOF_ENDPOINTS_HID) #define FIRST_IFACE_CDC_ACM 0 #define FIRST_IFACE_MASS_STORAGE NUMOF_IFACES_CDC_ACM @@ -53,6 +68,8 @@ #define FIRST_IFACE_CDC_ECM (NUMOF_IFACES_CDC_ACM + \ NUMOF_IFACES_MASS + \ NUMOF_IFACES_RNDIS) +#define FIRST_IFACE_HID (FIRST_IFACE_CDC_ECM + \ + NUMOF_IFACES_CDC_ECM) #define MFR_DESC_LENGTH (sizeof(CONFIG_USB_DEVICE_MANUFACTURER) * 2) #define MFR_UC_IDX_MAX (MFR_DESC_LENGTH - 3) @@ -73,4 +90,8 @@ void usb_fix_unicode_string(int idx_max, int asci_idx_max, u8_t *buf); u8_t *usb_get_device_descriptor(void); +#ifdef CONFIG_USB_DEVICE_HID +void usb_set_hid_report_size(u16_t report_desc_size); +#endif + #endif /* __USB_DESCRIPTOR_H__ */