From 71dc079032816c7c3b86485cc674ea1aeb3d5a84 Mon Sep 17 00:00:00 2001 From: Jonathan Struebel Date: Sat, 18 Jun 2016 20:02:22 -0700 Subject: Added USB HID driver to community HAL --- os/hal/hal.mk | 1 + os/hal/include/hal_community.h | 5 + os/hal/include/hal_usb_hid.h | 510 ++++++++++++++++++++++++++++++++++++ os/hal/src/hal_usb_hid.c | 581 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1097 insertions(+) create mode 100644 os/hal/include/hal_usb_hid.h create mode 100644 os/hal/src/hal_usb_hid.c (limited to 'os/hal') diff --git a/os/hal/hal.mk b/os/hal/hal.mk index 751b59a..ce74620 100644 --- a/os/hal/hal.mk +++ b/os/hal/hal.mk @@ -18,5 +18,6 @@ HALSRC += ${CHIBIOS_CONTRIB}/os/hal/src/hal_community.c \ ${CHIBIOS_CONTRIB}/os/hal/src/hal_eeprom.c \ ${CHIBIOS_CONTRIB}/os/hal/src/hal_timcap.c \ ${CHIBIOS_CONTRIB}/os/hal/src/hal_qei.c \ + ${CHIBIOS_CONTRIB}/os/hal/src/hal_usb_hid.c HALINC += ${CHIBIOS_CONTRIB}/os/hal/include diff --git a/os/hal/include/hal_community.h b/os/hal/include/hal_community.h index 75b3916..1518c7e 100644 --- a/os/hal/include/hal_community.h +++ b/os/hal/include/hal_community.h @@ -63,6 +63,10 @@ #define HAL_USE_USBH FALSE #endif +#if !defined(HAL_USE_USB_HID) +#define HAL_USE_USB_HID FALSE +#endif + /* Abstract interfaces.*/ /* Shared headers.*/ @@ -79,6 +83,7 @@ #include "hal_onewire.h" #include "hal_crc.h" #include "hal_eeprom.h" +#include "hal_usb_hid.h" /*===========================================================================*/ /* Driver constants. */ diff --git a/os/hal/include/hal_usb_hid.h b/os/hal/include/hal_usb_hid.h new file mode 100644 index 0000000..2a2d73a --- /dev/null +++ b/os/hal/include/hal_usb_hid.h @@ -0,0 +1,510 @@ +/* + ChibiOS - Copyright (C) 2016 Jonathan Struebel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_usb_hid.h + * @brief USB HID macros and structures. + * + * @addtogroup USB_HID + * @{ + */ + +#ifndef HAL_USB_HID_H +#define HAL_USB_HID_H + +#if (HAL_USE_USB_HID == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name HID specific messages. + * @{ + */ +#define HID_GET_REPORT 0x01U +#define HID_GET_IDLE 0x02U +#define HID_GET_PROTOCOL 0x03U +#define HID_SET_REPORT 0x09U +#define HID_SET_IDLE 0x0AU +#define HID_SET_PROTOCOL 0x0BU +/** @} */ + +/** + * @name HID classes + * @{ + */ +#define HID_INTERFACE_CLASS 0x03U +/** @} */ + +/** + * @name HID subclasses + * @{ + */ +#define HID_BOOT_INTERFACE 0x01U +/** @} */ + +/** + * @name HID descriptors + * @{ + */ +#define USB_DESCRIPTOR_HID 0x21U +#define HID_REPORT 0x22U +#define HID_PHYSICAL 0x23U +/** @} */ + +/** + * @name HID Report items + * @{ + */ +#define HID_REPORT_USAGE_PAGE 0x04 +#define HID_REPORT_USAGE 0x08 +#define HID_REPORT_LOGICAL_MINIMUM 0x14 +#define HID_REPORT_USAGE_MINIMUM 0x18 +#define HID_REPORT_LOGICAL_MAXIMUM 0x24 +#define HID_REPORT_USAGE_MAXIMUM 0x28 +#define HID_REPORT_REPORT_SIZE 0x74 +#define HID_REPORT_INPUT 0x80 +#define HID_REPORT_REPORT_COUNT 0x94 +#define HID_REPORT_COLLECTION 0xA0 +#define HID_REPORT_END_COLLECTION 0xC0 +/** @} */ + +/** + * @name HID Collection item definitions + * @{ + */ +#define HID_COLLECTION_PHYSICAL 0x00 +#define HID_COLLECTION_APPLICATION 0x01 +#define HID_COLLECTION_LOGICAL 0x02 +#define HID_COLLECTION_REPORT 0x03 +#define HID_COLLECTION_NAMED_ARRAY 0x04 +#define HID_COLLECTION_USAGE_SWITCH 0x05 +#define HID_COLLECTION_USAGE_MODIFIER 0x06 +/** @} */ + +/** + * @name HID Usage Page item definitions + * @{ + */ +#define HID_USAGE_PAGE_GENERIC_DESKTOP 0x01 +#define HID_USAGE_PAGE_SIMULATION 0x02 +#define HID_USAGE_PAGE_VR 0x03 +#define HID_USAGE_PAGE_SPORT 0x04 +#define HID_USAGE_PAGE_GAME 0x05 +#define HID_USAGE_PAGE_GENERIC_DEVICE 0x06 +#define HID_USAGE_PAGE_KEYBOARD_KEYPAD 0x07 +#define HID_USAGE_PAGE_LEDS 0x08 +#define HID_USAGE_PAGE_BUTTON 0x09 +#define HID_USAGE_PAGE_ORDINAL 0x0A +#define HID_USAGE_PAGE_TELEPHONY 0x0B +#define HID_USAGE_PAGE_CONSUMER 0x0C +#define HID_USAGE_PAGE_DIGITIZER 0x0D +#define HID_USAGE_PAGE_PID 0x0F +#define HID_USAGE_PAGE_UNICODE 0x10 +/** @} */ + +/** + * @name HID Usage item definitions + * @{ + */ +#define HID_USAGE_ALPHANUMERIC_DISPLAY 0x14 +#define HID_USAGE_MEDICAL_INSTRUMENTS 0x40 +#define HID_USAGE_MONITOR_PAGE1 0x80 +#define HID_USAGE_MONITOR_PAGE2 0x81 +#define HID_USAGE_MONITOR_PAGE3 0x82 +#define HID_USAGE_MONITOR_PAGE4 0x83 +#define HID_USAGE_POWER_PAGE1 0x84 +#define HID_USAGE_POWER_PAGE2 0x85 +#define HID_USAGE_POWER_PAGE3 0x86 +#define HID_USAGE_POWER_PAGE4 0x87 +#define HID_USAGE_BAR_CODE_SCANNER_PAGE 0x8C +#define HID_USAGE_SCALE_PAGE 0x8D +#define HID_USAGE_MSR_PAGE 0x8E +#define HID_USAGE_CAMERA_PAGE 0x90 +#define HID_USAGE_ARCADE_PAGE 0x91 + +#define HID_USAGE_POINTER 0x01 +#define HID_USAGE_MOUSE 0x02 +#define HID_USAGE_JOYSTICK 0x04 +#define HID_USAGE_GAMEPAD 0x05 +#define HID_USAGE_KEYBOARD 0x06 +#define HID_USAGE_KEYPAD 0x07 +#define HID_USAGE_MULTIAXIS_CONTROLLER 0x08 + +#define HID_USAGE_BUTTON1 0x01 +#define HID_USAGE_BUTTON2 0x02 +#define HID_USAGE_BUTTON3 0x03 +#define HID_USAGE_BUTTON4 0x04 +#define HID_USAGE_BUTTON5 0x05 +#define HID_USAGE_BUTTON6 0x06 +#define HID_USAGE_BUTTON7 0x07 +#define HID_USAGE_BUTTON8 0x08 + +#define HID_USAGE_X 0x30 +#define HID_USAGE_Y 0x31 +#define HID_USAGE_Z 0x32 +#define HID_USAGE_RX 0x33 +#define HID_USAGE_RY 0x34 +#define HID_USAGE_RZ 0x35 +#define HID_USAGE_VX 0x40 +#define HID_USAGE_VY 0x41 +#define HID_USAGE_VZ 0x42 +#define HID_USAGE_VBRX 0x43 +#define HID_USAGE_VBRY 0x44 +#define HID_USAGE_VBRZ 0x45 +#define HID_USAGE_VNO 0x46 +/** @} */ + +/** + * @name HID Input item definitions. + * @{ + */ +#define HID_INPUT_DATA_VAR_ABS 0x02 +#define HID_INPUT_CNST_VAR_ABS 0x03 +#define HID_INPUT_DATA_VAR_REL 0x06 +/** @} */ + +/** + * @name Helper macros for USB HID descriptors + * @{ + */ +/* + * @define HID Descriptor size. + */ +#define USB_DESC_HID_SIZE 9U + +/** + * @brief HID Descriptor helper macro. + * @note This macro can only be used with a single HID report descriptor + */ +#define USB_DESC_HID(bcdHID, bCountryCode, bNumDescriptors, \ + bDescriptorType, wDescriptorLength) \ + USB_DESC_BYTE(USB_DESC_HID_SIZE), \ + USB_DESC_BYTE(USB_DESCRIPTOR_HID), \ + USB_DESC_BCD(bcdHID), \ + USB_DESC_BYTE(bCountryCode), \ + USB_DESC_BYTE(bNumDescriptors), \ + USB_DESC_BYTE(bDescriptorType), \ + USB_DESC_WORD(wDescriptorLength) + +/** + * @brief HID Report Usage Page item helper macro (Single byte). + */ +#define HID_USAGE_PAGE_B(up) \ + USB_DESC_BYTE(HID_REPORT_USAGE_PAGE | 0x01), \ + USB_DESC_BYTE(up) + +/** + * @brief HID Report Usage Page item helper macro (Double byte). + */ +#define HID_USAGE_PAGE_W(up) \ + USB_DESC_BYTE(HID_REPORT_USAGE_PAGE | 0x02), \ + USB_DESC_WORD(up) + +/** + * @brief HID Report Usage item helper macro (Single byte). + */ +#define HID_USAGE_B(u) \ + USB_DESC_BYTE(HID_REPORT_USAGE | 0x01), \ + USB_DESC_BYTE(u) + +/** + * @brief HID Report Usage item helper macro (Double byte). + */ +#define HID_USAGE_W(u) \ + USB_DESC_BYTE(HID_REPORT_USAGE | 0x02), \ + USB_DESC_WORD(u) + +/** + * @brief HID Report Collection item helper macro (Single Byte). + */ +#define HID_COLLECTION_B(c) \ + USB_DESC_BYTE(HID_REPORT_COLLECTION | 0x01), \ + USB_DESC_BYTE(c) + +/** + * @brief HID Report Collection item helper macro (Double Byte). + */ +#define HID_COLLECTION_W(c) \ + USB_DESC_BYTE(HID_REPORT_COLLECTION | 0x02), \ + USB_DESC_WORD(c) + +/** + * @brief HID Report End Collection item helper macro. + */ +#define HID_END_COLLECTION \ + USB_DESC_BYTE(HID_REPORT_END_COLLECTION) + +/** + * @brief HID Report Usage Minimum item helper macro (Single byte). + */ +#define HID_USAGE_MINIMUM_B(x) \ + USB_DESC_BYTE(HID_REPORT_USAGE_MINIMUM | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Usage Minimum item helper macro (Double byte). + */ +#define HID_USAGE_MINIMUM_W(x) \ + USB_DESC_BYTE(HID_REPORT_USAGE_MINIMUM | 0x02), \ + USB_DESC_WORD(x) + +/** + * @brief HID Report Usage Maximum item helper macro (Single byte). + */ +#define HID_USAGE_MAXIMUM_B(x) \ + USB_DESC_BYTE(HID_REPORT_USAGE_MAXIMUM | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Usage Maximum item helper macro (Double byte). + */ +#define HID_USAGE_MAXIMUM_W(x) \ + USB_DESC_BYTE(HID_REPORT_USAGE_MAXIMUM | 0x02), \ + USB_DESC_WORD(x) + +/** + * @brief HID Report Logical Minimum item helper macro (Single byte). + */ +#define HID_LOGICAL_MINIMUM_B(x) \ + USB_DESC_BYTE(HID_REPORT_LOGICAL_MINIMUM | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Logical Minimum item helper macro (Double byte). + */ +#define HID_LOGICAL_MINIMUM_W(x) \ + USB_DESC_BYTE(HID_REPORT_LOGICAL_MINIMUM | 0x02), \ + USB_DESC_WORD(x) + +/** + * @brief HID Report Logical Maximum item helper macro (Single byte). + */ +#define HID_LOGICAL_MAXIMUM_B(x) \ + USB_DESC_BYTE(HID_REPORT_LOGICAL_MAXIMUM | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Logical Maximum item helper macro (Double byte). + */ +#define HID_LOGICAL_MAXIMUM_W(x) \ + USB_DESC_BYTE(HID_REPORT_LOGICAL_MAXIMUM | 0x02), \ + USB_DESC_WORD(x) + +/** + * @brief HID Report Count item helper macro (Single byte). + */ +#define HID_REPORT_COUNT_B(x) \ + USB_DESC_BYTE(HID_REPORT_REPORT_COUNT | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Count item helper macro (Double byte). + */ +#define HID_REPORT_COUNT_W(x) \ + USB_DESC_BYTE(HID_REPORT_REPORT_COUNT | 0x02), \ + USB_DESC_WORD(x) + +/** + * @brief HID Report Size item helper macro (Single byte). + */ +#define HID_REPORT_SIZE_B(x) \ + USB_DESC_BYTE(HID_REPORT_REPORT_SIZE | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Size item helper macro (Double byte). + */ +#define HID_REPORT_SIZE_W(x) \ + USB_DESC_BYTE(HID_REPORT_REPORT_SIZE | 0x02), \ + USB_DESC_WORD(x) + +/** + * @brief HID Report Input item helper macro (Single byte). + */ +#define HID_INPUT_B(x) \ + USB_DESC_BYTE(HID_REPORT_INPUT | 0x01), \ + USB_DESC_BYTE(x) + +/** + * @brief HID Report Input item helper macro (Double byte). + */ +#define HID_INPUT_W(x) \ + USB_DESC_BYTE(HID_REPORT_INPUT | 0x02), \ + USB_DESC_WORD(x) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name USB HID configuration options + * @{ + */ +/** + * @brief USB HID buffers size. + * @details Configuration parameter, the buffer size must be a multiple of + * the USB data endpoint maximum packet size. + * @note The default is 256 bytes for both the transmission and receive + * buffers. + */ +#if !defined(USB_HID_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define USB_HID_BUFFERS_SIZE 256 +#endif + +/** + * @brief USB HID number of buffers. + * @note The default is 2 buffers. + */ +#if !defined(USB_HID_BUFFERS_NUMBER) || defined(__DOXYGEN__) +#define USB_HID_BUFFERS_NUMBER 2 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if HAL_USE_USB == FALSE +#error "USB HID Driver requires HAL_USE_USB" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Driver state machine possible states. + */ +typedef enum { + HID_UNINIT = 0, /**< Not initialized. */ + HID_STOP = 1, /**< Stopped. */ + HID_READY = 2 /**< Ready. */ +} hidstate_t; + +/** + * @brief Structure representing a USB HID driver. + */ +typedef struct USBHIDDriver USBHIDDriver; + +/** + * @brief USB HID Driver configuration structure. + * @details An instance of this structure must be passed to @p hidStart() + * in order to configure and start the driver operations. + */ +typedef struct { + /** + * @brief USB driver to use. + */ + USBDriver *usbp; + /** + * @brief Interrupt IN endpoint used for outgoing data transfer. + */ + usbep_t int_in; + /** + * @brief Interrupt OUT endpoint used for incoming data transfer. + */ + usbep_t int_out; +} USBHIDConfig; + +/** + * @brief @p USBHIDDriver specific data. + */ +#define _usb_hid_driver_data \ + _base_asynchronous_channel_data \ + /* Driver state.*/ \ + hidstate_t state; \ + /* Input buffers queue.*/ \ + input_buffers_queue_t ibqueue; \ + /* Output queue.*/ \ + output_buffers_queue_t obqueue; \ + /* Input buffer.*/ \ + uint8_t ib[BQ_BUFFER_SIZE(USB_HID_BUFFERS_NUMBER, \ + USB_HID_BUFFERS_SIZE)]; \ + /* Output buffer.*/ \ + uint8_t ob[BQ_BUFFER_SIZE(USB_HID_BUFFERS_NUMBER, \ + USB_HID_BUFFERS_SIZE)]; \ + /* End of the mandatory fields.*/ \ + /* Current configuration data.*/ \ + const USBHIDConfig *config; + +/** + * @brief @p USBHIDDriver specific methods. + */ +#define _usb_hid_driver_methods \ + _base_asynchronous_channel_methods \ + /* Buffer flush method.*/ \ + void (*flush)(void *instance); + +/** + * @extends BaseAsynchronousChannelVMT + * + * @brief @p USBHIDDriver virtual methods table. + */ +struct USBHIDDriverVMT { + _usb_hid_driver_methods +}; + +/** + * @extends BaseAsynchronousChannel + * + * @brief Full duplex USB HID driver class. + * @details This class extends @p BaseAsynchronousChannel by adding physical + * I/O queues. + */ +struct USBHIDDriver { + /** @brief Virtual Methods Table.*/ + const struct USBHIDDriverVMT *vmt; + _usb_hid_driver_data +}; + +#define USB_DRIVER_EXT_FIELDS \ + USBHIDDriver hid + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ +#ifdef __cplusplus +extern "C" { +#endif + void hidInit(void); + void hidObjectInit(USBHIDDriver *uhdp); + void hidStart(USBHIDDriver *uhdp, const USBHIDConfig *config); + void hidStop(USBHIDDriver *uhdp); + void hidDisconnectI(USBHIDDriver *uhdp); + void hidConfigureHookI(USBHIDDriver *uhdp); + bool hidRequestsHook(USBDriver *usbp); + void hidDataTransmitted(USBDriver *usbp, usbep_t ep); + void hidDataReceived(USBDriver *usbp, usbep_t ep); + size_t hidWriteReport(USBHIDDriver *uhdp, uint8_t *bp, size_t n); + size_t hidWriteReportt(USBHIDDriver *uhdp, uint8_t *bp, size_t n, systime_t timeout); + size_t hidReadReport(USBHIDDriver *uhdp, uint8_t *bp, size_t n); + size_t hidReadReportt(USBHIDDriver *uhdp, uint8_t *bp, size_t n, systime_t timeout); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB_HID */ + +#endif /* HAL_USB_HID_H */ + +/** @} */ diff --git a/os/hal/src/hal_usb_hid.c b/os/hal/src/hal_usb_hid.c new file mode 100644 index 0000000..56be9b7 --- /dev/null +++ b/os/hal/src/hal_usb_hid.c @@ -0,0 +1,581 @@ +/* + ChibiOS - Copyright (C) 2016 Jonathan Struebel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_usb_hid.c + * @brief USB HID Driver code. + * + * @addtogroup USB_HID + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_USB_HID == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static uint16_t get_hword(uint8_t *p) { + uint16_t hw; + + hw = (uint16_t)*p++; + hw |= (uint16_t)*p << 8U; + return hw; +} + +/* + * Interface implementation. + */ + +static size_t write(void *ip, const uint8_t *bp, size_t n) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return 0; + } + + return obqWriteTimeout(&((USBHIDDriver *)ip)->obqueue, bp, + n, TIME_INFINITE); +} + +static size_t read(void *ip, uint8_t *bp, size_t n) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return 0; + } + + return ibqReadTimeout(&((USBHIDDriver *)ip)->ibqueue, bp, + n, TIME_INFINITE); +} + +static msg_t put(void *ip, uint8_t b) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return MSG_RESET; + } + + return obqPutTimeout(&((USBHIDDriver *)ip)->obqueue, b, TIME_INFINITE); +} + +static msg_t get(void *ip) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return MSG_RESET; + } + + return ibqGetTimeout(&((USBHIDDriver *)ip)->ibqueue, TIME_INFINITE); +} + +static msg_t putt(void *ip, uint8_t b, systime_t timeout) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return MSG_RESET; + } + + return obqPutTimeout(&((USBHIDDriver *)ip)->obqueue, b, timeout); +} + +static msg_t gett(void *ip, systime_t timeout) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return MSG_RESET; + } + + return ibqGetTimeout(&((USBHIDDriver *)ip)->ibqueue, timeout); +} + +static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t timeout) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return 0; + } + + return obqWriteTimeout(&((USBHIDDriver *)ip)->obqueue, bp, n, timeout); +} + +static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t timeout) { + + if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { + return 0; + } + + return ibqReadTimeout(&((USBHIDDriver *)ip)->ibqueue, bp, n, timeout); +} + +static void flush(void *ip) { + + obqFlush(&((USBHIDDriver *)ip)->obqueue); +} + +static const struct USBHIDDriverVMT vmt = { + write, read, put, get, + putt, gett, writet, readt, + flush +}; + +/** + * @brief Notification of empty buffer released into the input buffers queue. + * + * @param[in] bqp the buffers queue pointer. + */ +static void ibnotify(io_buffers_queue_t *bqp) { + USBHIDDriver *uhdp = bqGetLinkX(bqp); + + /* If the USB driver is not in the appropriate state then transactions + must not be started.*/ + if ((usbGetDriverStateI(uhdp->config->usbp) != USB_ACTIVE) || + (uhdp->state != HID_READY)) { + return; + } + + /* Checking if there is already a transaction ongoing on the endpoint.*/ + if (!usbGetReceiveStatusI(uhdp->config->usbp, uhdp->config->int_out)) { + /* Trying to get a free buffer.*/ + uint8_t *buf = ibqGetEmptyBufferI(&uhdp->ibqueue); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + usbStartReceiveI(uhdp->config->usbp, uhdp->config->int_out, + buf, SERIAL_USB_BUFFERS_SIZE); + } + } +} + +/** + * @brief Notification of filled buffer inserted into the output buffers queue. + * + * @param[in] bqp the buffers queue pointer. + */ +static void obnotify(io_buffers_queue_t *bqp) { + size_t n; + USBHIDDriver *uhdp = bqGetLinkX(bqp); + + /* If the USB driver is not in the appropriate state then transactions + must not be started.*/ + if ((usbGetDriverStateI(uhdp->config->usbp) != USB_ACTIVE) || + (uhdp->state != HID_READY)) { + return; + } + + /* Checking if there is already a transaction ongoing on the endpoint.*/ + if (!usbGetTransmitStatusI(uhdp->config->usbp, uhdp->config->int_in)) { + /* Trying to get a full buffer.*/ + uint8_t *buf = obqGetFullBufferI(&uhdp->obqueue, &n); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + usbStartTransmitI(uhdp->config->usbp, uhdp->config->int_in, buf, n); + } + } +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief USB HID Driver initialization. + * @note This function is implicitly invoked by @p halInit(), there is + * no need to explicitly initialize the driver. + * + * @init + */ +void hidInit(void) { +} + +/** + * @brief Initializes a generic full duplex USB HID driver object. + * @details The HW dependent part of the initialization has to be performed + * outside, usually in the hardware initialization code. + * + * @param[out] uhdp pointer to a @p USBHIDDriver structure + * + * @init + */ +void hidObjectInit(USBHIDDriver *uhdp) { + + uhdp->vmt = &vmt; + osalEventObjectInit(&uhdp->event); + uhdp->state = HID_STOP; + ibqObjectInit(&uhdp->ibqueue, uhdp->ib, + USB_HID_BUFFERS_SIZE, USB_HID_BUFFERS_NUMBER, + ibnotify, uhdp); + obqObjectInit(&uhdp->obqueue, uhdp->ob, + USB_HID_BUFFERS_SIZE, USB_HID_BUFFERS_NUMBER, + obnotify, uhdp); +} + +/** + * @brief Configures and starts the driver. + * + * @param[in] uhdp pointer to a @p USBHIDDriver object + * @param[in] config the USB HID driver configuration + * + * @api + */ +void hidStart(USBHIDDriver *uhdp, const USBHIDConfig *config) { + USBDriver *usbp = config->usbp; + + osalDbgCheck(uhdp != NULL); + + osalSysLock(); + osalDbgAssert((uhdp->state == HID_STOP) || (uhdp->state == HID_READY), + "invalid state"); + usbp->in_params[config->int_in - 1U] = uhdp; + usbp->out_params[config->int_out - 1U] = uhdp; + uhdp->config = config; + uhdp->state = HID_READY; + osalSysUnlock(); +} + +/** + * @brief Stops the driver. + * @details Any thread waiting on the driver's queues will be awakened with + * the message @p MSG_RESET. + * + * @param[in] uhdp pointer to a @p USBHIDDriver object + * + * @api + */ +void hidStop(USBHIDDriver *uhdp) { + USBDriver *usbp = uhdp->config->usbp; + + osalDbgCheck(uhdp != NULL); + + osalSysLock(); + osalDbgAssert((uhdp->state == HID_STOP) || (uhdp->state == HID_READY), + "invalid state"); + + /* Driver in stopped state.*/ + usbp->in_params[uhdp->config->int_in - 1U] = NULL; + usbp->out_params[uhdp->config->int_out - 1U] = NULL; + uhdp->state = HID_STOP; + + /* Enforces a disconnection.*/ + hidDisconnectI(uhdp); + osalOsRescheduleS(); + osalSysUnlock(); +} + +/** + * @brief USB device disconnection handler. + * @note If this function is not called from an ISR then an explicit call + * to @p osalOsRescheduleS() in necessary afterward. + * + * @param[in] uhdp pointer to a @p USBHIDDriver object + * + * @iclass + */ +void hidDisconnectI(USBHIDDriver *uhdp) { + + /* Queues reset in order to signal the driver stop to the application.*/ + chnAddFlagsI(uhdp, CHN_DISCONNECTED); + ibqResetI(&uhdp->ibqueue); + obqResetI(&uhdp->obqueue); +} + +/** + * @brief USB device configured handler. + * + * @param[in] uhdp pointer to a @p USBHIDDriver object + * + * @iclass + */ +void hidConfigureHookI(USBHIDDriver *uhdp) { + uint8_t *buf; + + ibqResetI(&uhdp->ibqueue); + obqResetI(&uhdp->obqueue); + chnAddFlagsI(uhdp, CHN_CONNECTED); + + /* Starts the first OUT transaction immediately.*/ + buf = ibqGetEmptyBufferI(&uhdp->ibqueue); + + osalDbgAssert(buf != NULL, "no free buffer"); + + usbStartReceiveI(uhdp->config->usbp, uhdp->config->int_out, + buf, USB_HID_BUFFERS_SIZE); +} + +/** + * @brief Default requests hook. + * @details Applications wanting to use the USB HID driver can use + * this function at the end of the application specific + * requests hook. The HID_* requests handled here do not + * transfer any data to the application. + * The following requests are handled: + * - HID_GET_IDLE. + * - HID_GET_PROTOCOL. + * - HID_SET_REPORT. + * - HID_SET_IDLE. + * - HID_SET_PROTOCOL. + * - USB_REQ_GET_DESCRIPTOR. + * . + * + * @param[in] usbp pointer to the @p USBDriver object + * @return The hook status. + * @retval true Message handled internally. + * @retval false Message not handled. + */ +bool hidRequestsHook(USBDriver *usbp) { + const USBDescriptor *dp; + + if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) { + switch (usbp->setup[1]) { + case HID_GET_IDLE: + usbSetupTransfer(usbp, NULL, 0, NULL); + return true; + case HID_GET_PROTOCOL: + return true; + case HID_SET_REPORT: + usbSetupTransfer(usbp, NULL, 0, NULL); + return true; + case HID_SET_IDLE: + usbSetupTransfer(usbp, NULL, 0, NULL); + return true; + case HID_SET_PROTOCOL: + return true; + default: + return false; + } + } + + /* GET_DESCRIPTOR from interface not handled by default so handle it here */ + if (((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) && + ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + switch (usbp->setup[1]) { + case USB_REQ_GET_DESCRIPTOR: + dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], + get_hword(&usbp->setup[4])); + if (dp == NULL) + return false; + + usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); + return true; + default: + return false; + } + } + return false; +} + +/** + * @brief Default data transmitted callback. + * @details The application must use this function as callback for the IN + * data endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep IN endpoint number + */ +void hidDataTransmitted(USBDriver *usbp, usbep_t ep) { + uint8_t *buf; + size_t n; + USBHIDDriver *uhdp = usbp->in_params[ep - 1U]; + + if (uhdp == NULL) { + return; + } + + osalSysLockFromISR(); + + /* Signaling that space is available in the output queue.*/ + chnAddFlagsI(uhdp, CHN_OUTPUT_EMPTY); + + /* Freeing the buffer just transmitted, if it was not a zero size packet.*/ + if (usbp->epc[ep]->in_state->txsize > 0U) { + obqReleaseEmptyBufferI(&uhdp->obqueue); + } + + /* Checking if there is a buffer ready for transmission.*/ + buf = obqGetFullBufferI(&uhdp->obqueue, &n); + + if (buf != NULL) { + /* The endpoint cannot be busy, we are in the context of the callback, + so it is safe to transmit without a check.*/ + usbStartTransmitI(usbp, ep, buf, n); + } + else if ((usbp->epc[ep]->in_state->txsize > 0U) && + ((usbp->epc[ep]->in_state->txsize & + ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) { + /* Transmit zero sized packet in case the last one has maximum allowed + size. Otherwise the recipient may expect more data coming soon and + not return buffered data to app. See section 5.8.3 Bulk Transfer + Packet Size Constraints of the USB Specification document.*/ + usbStartTransmitI(usbp, ep, usbp->setup, 0); + + } + else { + /* Nothing to transmit.*/ + } + + osalSysUnlockFromISR(); +} + +/** + * @brief Default data received callback. + * @details The application must use this function as callback for the OUT + * data endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep OUT endpoint number + */ +void hidDataReceived(USBDriver *usbp, usbep_t ep) { + uint8_t *buf; + USBHIDDriver *uhdp = usbp->out_params[ep - 1U]; + + if (uhdp == NULL) { + return; + } + + osalSysLockFromISR(); + + /* Signaling that data is available in the input queue.*/ + chnAddFlagsI(uhdp, CHN_INPUT_AVAILABLE); + + /* Posting the filled buffer in the queue.*/ + ibqPostFullBufferI(&uhdp->ibqueue, + usbGetReceiveTransactionSizeX(uhdp->config->usbp, ep)); + + /* The endpoint cannot be busy, we are in the context of the callback, + so a packet is in the buffer for sure. Trying to get a free buffer + for the next transaction.*/ + buf = ibqGetEmptyBufferI(&uhdp->ibqueue); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + usbStartReceiveI(uhdp->config->usbp, ep, buf, USB_HID_BUFFERS_SIZE); + } + + osalSysUnlockFromISR(); +} + +/** + * @brief Write HID Report + * @details The function writes data from a buffer to an output queue. The + * operation completes when the specified amount of data has been + * transferred or if the queue has been reset. + * + * @param[in] uhdp pointer to the @p USBHIDDriver object + * @param[in] bp pointer to the report data buffer + * @param[in] n the maximum amount of data to be transferred, the + * value 0 is reserved + * @return The number of bytes effectively transferred. + * @retval 0 if a timeout occurred. + * + * @api + */ +size_t hidWriteReport(USBHIDDriver *uhdp, uint8_t *bp, size_t n) { + size_t val; + + val = uhdp->vmt->write(uhdp, bp, n); + + if (val > 0) + uhdp->vmt->flush(uhdp); + + return val; +} + +/** + * @brief Write HID report with timeout + * @details The function writes data from a buffer to an output queue. The + * operation completes when the specified amount of data has been + * transferred or after the specified timeout or if the queue has + * been reset. + * + * @param[in] uhdp pointer to the @p USBHIDDriver object + * @param[in] bp pointer to the report data buffer + * @param[in] n the maximum amount of data to be transferred, the + * value 0 is reserved + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The number of bytes effectively transferred. + * @retval 0 if a timeout occurred. + * + * @api + */ +size_t hidWriteReportt(USBHIDDriver *uhdp, uint8_t *bp, size_t n, systime_t timeout) { + size_t val; + + val = uhdp->vmt->writet(uhdp, bp, n, timeout); + + if (val > 0) + uhdp->vmt->flush(uhdp); + + return val; +} + +/** + * @brief Read HID report + * @details The function reads data from an input queue into a buffer. + * The operation completes when the specified amount of data has been + * transferred or if the queue has been reset. + * + * @param[in] uhdp pointer to the @p input_buffers_queue_t object + * @param[out] bp pointer to the data buffer + * @param[in] n the maximum amount of data to be transferred, the + * value 0 is reserved + * @return The number of bytes effectively transferred. + * @retval 0 if a timeout occurred. + * + * @api + */ +size_t hidReadReport(USBHIDDriver *uhdp, uint8_t *bp, size_t n) { + + return uhdp->vmt->read(uhdp, bp, n); +} + +/** + * @brief Read HID report with timeout + * @details The function reads data from an input queue into a buffer. + * The operation completes when the specified amount of data has been + * transferred or after the specified timeout or if the queue has + * been reset. + * + * @param[in] uhdp pointer to the @p input_buffers_queue_t object + * @param[out] bp pointer to the data buffer + * @param[in] n the maximum amount of data to be transferred, the + * value 0 is reserved + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_IMMEDIATE immediate timeout. + * - @a TIME_INFINITE no timeout. + * . + * @return The number of bytes effectively transferred. + * @retval 0 if a timeout occurred. + * + * @api + */ +size_t hidReadReportt(USBHIDDriver *uhdp, uint8_t *bp, size_t n, systime_t timeout) { + + return uhdp->vmt->readt(uhdp, bp, n, timeout); +} + +#endif /* HAL_USE_USB_HID == TRUE */ + +/** @} */ -- cgit v1.2.3