From 057de4f017af9e108bc22c394aa1769b658f08f9 Mon Sep 17 00:00:00 2001 From: Radoslav Gerganov Date: Thu, 4 Jun 2020 13:21:23 +0300 Subject: [PATCH 1/3] Add keyboard HID interface to Solo This patch adds a keyboard HID interface to the existing FIDO2 HID interface. It allows injecting keystokes into the host when the Solo button is pressed. For now it injects the hardcoded sequence "1234". The new interface is enabled with the ENABLE_KBD macro. Right now it doesn't work with the other additional interfaces (CCID and CDC). Advertising a random combination of USB interfaces (FIDO2, kbd, CCID, CDC) requires more work and could be addressed in the future. Issue: #354 --- fido2/device.h | 1 + targets/stm32l432/build/common.mk | 4 +- targets/stm32l432/lib/usbd/usbd_composite.c | 65 +++- targets/stm32l432/lib/usbd/usbd_composite.h | 2 +- targets/stm32l432/lib/usbd/usbd_conf.c | 2 + targets/stm32l432/lib/usbd/usbd_hid.h | 7 + targets/stm32l432/lib/usbd/usbd_kbd.c | 312 ++++++++++++++++++++ targets/stm32l432/src/app.h | 3 + targets/stm32l432/src/device.c | 24 +- targets/stm32l432/src/init.c | 4 +- 10 files changed, 408 insertions(+), 16 deletions(-) create mode 100644 targets/stm32l432/lib/usbd/usbd_kbd.c diff --git a/fido2/device.h b/fido2/device.h index 0c96c732..6c7881f5 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -24,6 +24,7 @@ uint32_t millis(); */ void usbhid_send(uint8_t * msg); +void usbkbd_send(uint8_t * msg); /** Reboot / power reset the device. * **Optional** this is not used for FIDO2, and simply won't do anything if not implemented. diff --git a/targets/stm32l432/build/common.mk b/targets/stm32l432/build/common.mk index cf3fe966..c682a118 100644 --- a/targets/stm32l432/build/common.mk +++ b/targets/stm32l432/build/common.mk @@ -14,7 +14,7 @@ DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \ lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \ lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c \ - lib/usbd/usbd_ccid.c + lib/usbd/usbd_ccid.c lib/usbd/usbd_kbd.c VERSION_FULL?=$(SOLO_VERSION_FULL) VERSION:=$(SOLO_VERSION) @@ -32,4 +32,4 @@ _all: echo $(SOLO_VERSION_PAT) %.o: %.s - $(AS) -o $@ $^ \ No newline at end of file + $(AS) -o $@ $^ diff --git a/targets/stm32l432/lib/usbd/usbd_composite.c b/targets/stm32l432/lib/usbd/usbd_composite.c index b1de7d27..ca61adc1 100644 --- a/targets/stm32l432/lib/usbd/usbd_composite.c +++ b/targets/stm32l432/lib/usbd/usbd_composite.c @@ -36,6 +36,14 @@ static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length); #define CCID_SIZE 0 #endif +#ifdef ENABLE_KBD +#define KBD_SIZE 25 +#define KBD_NUM_INTERFACE 1 +#else +#define KBD_NUM_INTERFACE 0 +#define KBD_SIZE 0 +#endif + #if DEBUG_LEVEL > 0 #define CDC_SIZE (49 + 8 + 9 + 4) #define CDC_NUM_INTERFACE 2 @@ -46,15 +54,16 @@ static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length); #define HID_SIZE 41 -#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (HID_SIZE + CDC_SIZE + CCID_SIZE) -#define NUM_INTERFACES (1 + CDC_NUM_INTERFACE + CCID_NUM_INTERFACE) -#define NUM_CLASSES 3 +#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (HID_SIZE + CDC_SIZE + CCID_SIZE + KBD_SIZE) +#define NUM_INTERFACES (1 + CDC_NUM_INTERFACE + CCID_NUM_INTERFACE + KBD_NUM_INTERFACE) +#define NUM_CLASSES 4 #define HID_INTF_NUM 0 #define CDC_MASTER_INTF_NUM 1 #define CDC_SLAVE_INTF_NUM 2 #define CCID_INTF_NUM 3 +#define KBD_INTF_NUM 1 __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END = { /*Configuration Descriptor*/ @@ -284,7 +293,40 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_ #endif +#ifdef ENABLE_KBD + /* */ + /* KBD */ + /* */ + /************** Descriptor of Keyboard interface ****************/ + 0x09, /*bLength: Interface Descriptor size*/ + USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/ + KBD_INTF_NUM, /*bInterfaceNumber: Number of Interface*/ + 0x00, /*bAlternateSetting: Alternate setting*/ + 0x01, /*bNumEndpoints*/ + 0x03, /*bInterfaceClass: HID*/ + 0x01, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/ + 0x01, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/ + 2, /*iInterface: Index of string descriptor*/ + /******************** Descriptor of Keyboard HID ********************/ + 0x09, /*bLength: HID Descriptor size*/ + HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/ + 0x11, /*bcdHID: HID Class Spec release number*/ + 0x01, + 0x00, /*bCountryCode: Hardware target country*/ + 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ + 0x22, /*bDescriptorType*/ + HID_KBD_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/ + 0, + /******************** Descriptor of Keyboard endpoint ********************/ + 0x07, /*bLength: Endpoint Descriptor size*/ + USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/ + KBD_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/ + 0x03, /*bmAttributes: Interrupt endpoint*/ + KBD_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */ + 0x00, + KBD_BINTERVAL, /*bInterval: Polling Interval */ +#endif }; USBD_ClassTypeDef USBD_Composite = @@ -311,7 +353,7 @@ int in_endpoint_to_class[MAX_ENDPOINTS]; int out_endpoint_to_class[MAX_ENDPOINTS]; -void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *ccid_class, USBD_ClassTypeDef *cdc_class) { +void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *ccid_class, USBD_ClassTypeDef *cdc_class, USBD_ClassTypeDef *kbd_class) { memset(USBD_Classes, 0 , sizeof(USBD_Classes)); USBD_Classes[0] = hid_class; #ifdef ENABLE_CCID @@ -320,6 +362,9 @@ void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef #if DEBUG_LEVEL > 0 USBD_Classes[2] = cdc_class; #endif +#ifdef ENABLE_KBD + USBD_Classes[1] = kbd_class; +#endif } static USBD_ClassTypeDef * getClass(uint8_t index) @@ -336,6 +381,10 @@ static USBD_ClassTypeDef * getClass(uint8_t index) case CDC_MASTER_INTF_NUM: case CDC_SLAVE_INTF_NUM: return USBD_Classes[2]; +#endif +#ifdef ENABLE_KBD + case KBD_INTF_NUM: + return USBD_Classes[1]; #endif } return NULL; @@ -364,7 +413,6 @@ static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) } static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { - int i; USBD_ClassTypeDef * device_class; device_class = getClass(req->wIndex); @@ -380,12 +428,7 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType switch (req->bRequest) { case USB_REQ_GET_DESCRIPTOR : - for(i = 0; i < NUM_CLASSES; i++) { - if (USBD_Classes[i] != NULL && USBD_Classes[i]->Setup(pdev, req) != USBD_OK) { - return USBD_FAIL; - } - } - + return device_class->Setup(pdev, req); break; case USB_REQ_GET_INTERFACE : diff --git a/targets/stm32l432/lib/usbd/usbd_composite.h b/targets/stm32l432/lib/usbd/usbd_composite.h index 0fdc513d..c54955bd 100644 --- a/targets/stm32l432/lib/usbd/usbd_composite.h +++ b/targets/stm32l432/lib/usbd/usbd_composite.h @@ -17,7 +17,7 @@ extern int in_endpoint_to_class[MAX_ENDPOINTS]; extern int out_endpoint_to_class[MAX_ENDPOINTS]; -void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1, USBD_ClassTypeDef *class2); +void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1, USBD_ClassTypeDef *class2, USBD_ClassTypeDef *class3); #ifdef __cplusplus } diff --git a/targets/stm32l432/lib/usbd/usbd_conf.c b/targets/stm32l432/lib/usbd/usbd_conf.c index bd3c442b..a8f71c42 100644 --- a/targets/stm32l432/lib/usbd/usbd_conf.c +++ b/targets/stm32l432/lib/usbd/usbd_conf.c @@ -272,6 +272,8 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xd8 + 64*5); // data OUT HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0xd8 + 64*6); // data IN + // KBD + HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , KBD_EPIN_ADDR , PCD_SNG_BUF, 0xd8 + 64*7); // data IN // dump_pma_header("usbd_conf"); return USBD_OK; diff --git a/targets/stm32l432/lib/usbd/usbd_hid.h b/targets/stm32l432/lib/usbd/usbd_hid.h index 03dcb893..4d2ee8d0 100644 --- a/targets/stm32l432/lib/usbd/usbd_hid.h +++ b/targets/stm32l432/lib/usbd/usbd_hid.h @@ -60,13 +60,19 @@ #define HID_EPOUT_ADDR 0x01U #define HID_EPOUT_SIZE HID_PACKET_SIZE +#define KBD_PACKET_SIZE 8 +#define KBD_EPIN_ADDR 0x82U +#define KBD_EPIN_SIZE KBD_PACKET_SIZE + #define USB_HID_DESC_SIZ 9U #define HID_FIDO_REPORT_DESC_SIZE 34U +#define HID_KBD_REPORT_DESC_SIZE 63U #define HID_DESCRIPTOR_TYPE 0x21U #define HID_REPORT_DESC 0x22U #define HID_BINTERVAL 5 +#define KBD_BINTERVAL 10 #define HID_REQ_SET_PROTOCOL 0x0BU #define HID_REQ_GET_PROTOCOL 0x03U @@ -94,6 +100,7 @@ typedef struct USBD_HID_HandleTypeDef; extern USBD_ClassTypeDef USBD_HID; +extern USBD_ClassTypeDef USBD_KBD; void usb_hid_recieve_callback(uint8_t ep); diff --git a/targets/stm32l432/lib/usbd/usbd_kbd.c b/targets/stm32l432/lib/usbd/usbd_kbd.c new file mode 100644 index 00000000..6e84798f --- /dev/null +++ b/targets/stm32l432/lib/usbd/usbd_kbd.c @@ -0,0 +1,312 @@ +/** + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics International N.V. + * All rights reserved.

+ * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific written permission. + * 4. This software, including modifications and/or derivative works of this + * software, must execute solely and exclusively on microcontroller or + * microprocessor devices manufactured by or for STMicroelectronics. + * 5. Redistribution and use of this software other than as permitted under + * this license is void and will automatically terminate your rights under + * this license. + * + * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY + * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT + * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + + /* BSPDependencies + - "stm32xxxxx_{eval}{discovery}{nucleo_144}.c" + - "stm32xxxxx_{eval}{discovery}_io.c" + EndBSPDependencies */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_hid.h" +#include "usbd_ctlreq.h" +#include "usbd_conf.h" +#include "usbd_core.h" + +#include "log.h" + +static uint8_t USBD_KBD_Init (USBD_HandleTypeDef *pdev, + uint8_t cfgidx); + +static uint8_t USBD_KBD_DeInit (USBD_HandleTypeDef *pdev, + uint8_t cfgidx); + +static uint8_t USBD_KBD_Setup (USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req); + +static uint8_t USBD_KBD_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum); + + +USBD_ClassTypeDef USBD_KBD = +{ + USBD_KBD_Init, + USBD_KBD_DeInit, + USBD_KBD_Setup, + NULL, /*EP0_TxSent*/ + NULL, /*EP0_RxReady*/ + USBD_KBD_DataIn, /*DataIn*/ + NULL, /*DataOut*/ + NULL, /*SOF */ + NULL, + NULL, + + + NULL, + NULL, + NULL, + NULL, +}; + +/* USB HID device Configuration Descriptor */ +__ALIGN_BEGIN static uint8_t USBD_HID_Desc[USB_HID_DESC_SIZ] __ALIGN_END = +{ + /* 18 */ + 0x09, /*bLength: HID Descriptor size*/ + HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/ + 0x11, /*bcdHID: HID Class Spec release number*/ + 0x01, + 0x00, /*bCountryCode: Hardware target country*/ + 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ + 0x22, /*bDescriptorType*/ + HID_KBD_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/ + 0x00, +}; + +__ALIGN_BEGIN static uint8_t HID_KBD_ReportDesc[HID_KBD_REPORT_DESC_SIZE] __ALIGN_END = +{ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ + 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ + 0x95, 0x05, /* REPORT_COUNT (5) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x05, 0x08, /* USAGE_PAGE (LEDs) */ + 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ + 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */ + 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x03, /* REPORT_SIZE (3) */ + 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */ + 0x95, 0x06, /* REPORT_COUNT (6) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0x00, /* USAGE_MINIMUM (Reserved) */ + 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ + 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0xc0 /* END_COLLECTION */ +}; + +/** + * @brief USBD_KBD_Init + * Initialize the HID interface + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ + +static uint8_t USBD_KBD_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + /* Open EP IN */ + USBD_LL_OpenEP(pdev, KBD_EPIN_ADDR, USBD_EP_TYPE_INTR, KBD_EPIN_SIZE); + static uint8_t mem[sizeof (USBD_HID_HandleTypeDef)]; + pdev->ep_in[KBD_EPIN_ADDR & 0xFU].is_used = 1U; + + pdev->pClassData = mem; + + ((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE; + + return USBD_OK; +} + +/** + * @brief USBD_KBD_Init + * DeInitialize the HID layer + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_KBD_DeInit (USBD_HandleTypeDef *pdev, + uint8_t cfgidx) +{ + /* Close HID EPs */ + USBD_LL_CloseEP(pdev, KBD_EPIN_ADDR); + pdev->ep_in[KBD_EPIN_ADDR & 0xFU].is_used = 0U; + + return USBD_OK; +} + +/** + * @brief USBD_KBD_Setup + * Handle the HID specific requests + * @param pdev: instance + * @param req: usb requests + * @retval status + */ +static uint8_t USBD_KBD_Setup (USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) +{ + USBD_HID_HandleTypeDef *hhid = (USBD_HID_HandleTypeDef*) pdev->pClassData; + uint16_t len = 0U; + uint8_t *pbuf = NULL; + uint16_t status_info = 0U; + USBD_StatusTypeDef ret = USBD_OK; + req->wLength = req->wLength & 0x7f; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS : + switch (req->bRequest) + { + case HID_REQ_SET_PROTOCOL: + hhid->Protocol = (uint8_t)(req->wValue); + break; + + case HID_REQ_GET_PROTOCOL: + USBD_CtlSendData (pdev, (uint8_t *)(void *)&hhid->Protocol, 1U); + break; + + case HID_REQ_SET_IDLE: + hhid->IdleState = (uint8_t)(req->wValue >> 8); + break; + + case HID_REQ_GET_IDLE: + USBD_CtlSendData (pdev, (uint8_t *)(void *)&hhid->IdleState, 1U); + break; + + default: + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + break; + } + break; + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) + { + case USB_REQ_GET_STATUS: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + USBD_CtlSendData (pdev, (uint8_t *)(void *)&status_info, 2U); + } + else + { + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_GET_DESCRIPTOR: + if(req->wValue >> 8 == HID_REPORT_DESC) + { + len = MIN(HID_KBD_REPORT_DESC_SIZE , req->wLength); + pbuf = HID_KBD_ReportDesc; + printf1(TAG_GREEN,"get report desc\r\n"); + } + else if(req->wValue >> 8 == HID_DESCRIPTOR_TYPE) + { + pbuf = USBD_HID_Desc; + len = MIN(USB_HID_DESC_SIZ, req->wLength); + } + else + { + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + break; + } + USBD_CtlSendData (pdev, pbuf, len); + break; + + case USB_REQ_GET_INTERFACE : + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + USBD_CtlSendData (pdev, (uint8_t *)(void *)&hhid->AltSetting, 1U); + } + else + { + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_SET_INTERFACE : + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + hhid->AltSetting = (uint8_t)(req->wValue); + } + else + { + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + } + break; + + default: + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + break; + } + break; + + default: + USBD_CtlError (pdev, req); + ret = USBD_FAIL; + break; + } + + return ret; +} + +/** + * @brief USBD_KBD_DataIn + * handle data IN Stage + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t USBD_KBD_DataIn (USBD_HandleTypeDef *pdev, + uint8_t epnum) +{ + /* Ensure that the FIFO is empty before a new transfer, this condition could + be caused by a new transfer before the end of the previous transfer */ + ((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE; + return USBD_OK; +} + diff --git a/targets/stm32l432/src/app.h b/targets/stm32l432/src/app.h index fa3d3806..c045c529 100644 --- a/targets/stm32l432/src/app.h +++ b/targets/stm32l432/src/app.h @@ -22,6 +22,9 @@ // Enable the CCID USB interface // #define ENABLE_CCID +// Enable the KBD USB interface +// #define ENABLE_KBD + #define NON_BLOCK_PRINTING 0 diff --git a/targets/stm32l432/src/device.c b/targets/stm32l432/src/device.c index c8f487bd..692a04f4 100644 --- a/targets/stm32l432/src/device.c +++ b/targets/stm32l432/src/device.c @@ -48,6 +48,7 @@ extern PCD_HandleTypeDef hpcd; static int _NFC_status = 0; static bool isLowFreq = 0; static bool _up_disabled = false; +static bool _up_wait = false; // #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN)) static int is_physical_button_pressed(void) @@ -127,6 +128,7 @@ void TIM6_DAC_IRQHandler(void) // Interrupt on rising edge of button (button released) void EXTI0_IRQHandler(void) { + uint8_t report[8] = {0, 0, 0x1e, 0x1f, 0x20, 0x21, 0, 0}; EXTI->PR1 = EXTI->PR1; if (is_physical_button_pressed == IS_BUTTON_PRESSED) { @@ -134,6 +136,12 @@ void EXTI0_IRQHandler(void) if ((millis() - __last_button_bounce_time) > 25) { __last_button_press_time = millis(); + if (!_up_wait) + { + usbkbd_send(report); + memset(report, 0, sizeof(report)); + usbkbd_send(report); + } } __last_button_bounce_time = millis(); } @@ -398,6 +406,18 @@ void usbhid_send(uint8_t * msg) } +void usbkbd_send(uint8_t * msg) +{ + printf1(TAG_DUMP2,"<< "); + dump_hex1(TAG_DUMP2, msg, KBD_PACKET_SIZE); + + Solo_USBD_Device.ep_in[KBD_EPIN_ADDR & 0xFU].total_length = KBD_PACKET_SIZE; + + while (PCD_GET_EP_TX_STATUS(USB, KBD_EPIN_ADDR & 0x0f) == USB_EP_TX_VALID) + ; + USBD_LL_Transmit(&Solo_USBD_Device, KBD_EPIN_ADDR, msg, KBD_PACKET_SIZE); +} + void ctaphid_write_block(uint8_t * data) { usbhid_send(data); @@ -742,6 +762,7 @@ int ctap_user_presence_test(uint32_t up_delay) // Set LED status and wait. led_rgb(0xff3520); + _up_wait = true; // Block and wait for some time. ret = wait_for_button_activate(up_delay); @@ -755,13 +776,14 @@ int ctap_user_presence_test(uint32_t up_delay) goto done; } - + _up_wait = false; return 0; done: ret = wait_for_button_release(up_delay); __last_button_press_time = 0; + _up_wait = false; return 1; } diff --git a/targets/stm32l432/src/init.c b/targets/stm32l432/src/init.c index def698d2..1a762be0 100644 --- a/targets/stm32l432/src/init.c +++ b/targets/stm32l432/src/init.c @@ -709,10 +709,12 @@ void init_usb(void) // Enable USB Clock SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_USBFSEN); #ifndef IS_BOOTLOADER - USBD_Composite_Set_Classes(&USBD_HID, &USBD_CCID, &USBD_CDC); + USBD_Composite_Set_Classes(&USBD_HID, &USBD_CCID, &USBD_CDC, &USBD_KBD); in_endpoint_to_class[HID_EPIN_ADDR & 0x7F] = 0; out_endpoint_to_class[HID_EPOUT_ADDR & 0x7F] = 0; + in_endpoint_to_class[KBD_EPIN_ADDR & 0x7F] = 1; + in_endpoint_to_class[CCID_IN_EP & 0x7F] = 1; out_endpoint_to_class[CCID_OUT_EP & 0x7F] = 1; From ec8a2ebbde34090a230d8b71556822970d8a6d28 Mon Sep 17 00:00:00 2001 From: Radoslav Gerganov Date: Fri, 12 Jun 2020 17:06:39 +0300 Subject: [PATCH 2/3] usbkbd: support for all printable characters --- fido2/device.h | 2 - targets/stm32l432/lib/usbd/usbd_hid.h | 1 + targets/stm32l432/lib/usbd/usbd_kbd.c | 91 +++++++++++++++++++++++++++ targets/stm32l432/src/device.c | 20 ++---- 4 files changed, 96 insertions(+), 18 deletions(-) diff --git a/fido2/device.h b/fido2/device.h index 6c7881f5..e002ee5c 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -24,8 +24,6 @@ uint32_t millis(); */ void usbhid_send(uint8_t * msg); -void usbkbd_send(uint8_t * msg); - /** Reboot / power reset the device. * **Optional** this is not used for FIDO2, and simply won't do anything if not implemented. */ diff --git a/targets/stm32l432/lib/usbd/usbd_hid.h b/targets/stm32l432/lib/usbd/usbd_hid.h index 4d2ee8d0..31902132 100644 --- a/targets/stm32l432/lib/usbd/usbd_hid.h +++ b/targets/stm32l432/lib/usbd/usbd_hid.h @@ -104,6 +104,7 @@ extern USBD_ClassTypeDef USBD_KBD; void usb_hid_recieve_callback(uint8_t ep); +void usb_kbd_send(uint8_t *msg, int len); #ifdef __cplusplus diff --git a/targets/stm32l432/lib/usbd/usbd_kbd.c b/targets/stm32l432/lib/usbd/usbd_kbd.c index 6e84798f..9a328c63 100644 --- a/targets/stm32l432/lib/usbd/usbd_kbd.c +++ b/targets/stm32l432/lib/usbd/usbd_kbd.c @@ -63,6 +63,46 @@ static uint8_t USBD_KBD_Setup (USBD_HandleTypeDef *pdev, static uint8_t USBD_KBD_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t key_map[][3] = +{ + {'0', 0, 0x27}, + {'-', 0, 0x2d}, + {'=', 0, 0x2e}, + {'[', 0, 0x2f}, + {']', 0, 0x30}, + {'\\', 0, 0x31}, + {';', 0, 0x33}, + {'\'', 0, 0x34}, + {'`', 0, 0x35}, + {',', 0, 0x36}, + {'.', 0, 0x37}, + {'/', 0, 0x38}, + {' ', 0, 0x2c}, + {'\n', 0, 0x28}, + + {'~', 2, 0x35}, + {'!', 2, 0x1e}, + {'@', 2, 0x1f}, + {'#', 2, 0x20}, + {'$', 2, 0x21}, + {'%', 2, 0x22}, + {'^', 2, 0x23}, + {'&', 2, 0x24}, + {'*', 2, 0x25}, + {'(', 2, 0x26}, + {')', 2, 0x27}, + {'_', 2, 0x2d}, + {'+', 2, 0x2e}, + {'{', 2, 0x2f}, + {'}', 2, 0x30}, + {':', 2, 0x33}, + {'"', 2, 0x34}, + {'|', 2, 0x31}, + {'<', 2, 0x36}, + {'>', 2, 0x37}, + {'?', 2, 0x38}, + {0, 0, 0}, +}; USBD_ClassTypeDef USBD_KBD = { @@ -310,3 +350,54 @@ static uint8_t USBD_KBD_DataIn (USBD_HandleTypeDef *pdev, return USBD_OK; } +static void USBD_KBD_Transmit(uint8_t * msg) +{ + printf1(TAG_DUMP2,"<< "); + dump_hex1(TAG_DUMP2, msg, KBD_PACKET_SIZE); + + Solo_USBD_Device.ep_in[KBD_EPIN_ADDR & 0xFU].total_length = KBD_PACKET_SIZE; + + while (PCD_GET_EP_TX_STATUS(USB, KBD_EPIN_ADDR & 0x0f) == USB_EP_TX_VALID) + ; + USBD_LL_Transmit(&Solo_USBD_Device, KBD_EPIN_ADDR, msg, KBD_PACKET_SIZE); +} + +void usb_kbd_send(uint8_t *msg, int len) +{ + uint8_t report[8]; + for (int i = 0; i < len; i++) + { + memset(report, 0, sizeof(report)); + uint8_t ch = msg[i]; + if (ch >= 'a' && ch <= 'z') + { + report[2] = ch - 'a' + 4; + USBD_KBD_Transmit(report); + } + else if (ch >= 'A' && ch <= 'Z') + { + report[0] = 2; //left shift + report[2] = ch - 'A' + 4; + USBD_KBD_Transmit(report); + } + else if (ch >= '1' && ch <= '9') + { + report[2] = ch - '1' + 0x1e; + USBD_KBD_Transmit(report); + } + else + { + for (int j=0; key_map[j][0] != 0; j++) + { + if (ch == key_map[j][0]) { + report[0] = key_map[j][1]; + report[2] = key_map[j][2]; + USBD_KBD_Transmit(report); + break; + } + } + } + memset(report, 0, sizeof(report)); + USBD_KBD_Transmit(report); + } +} diff --git a/targets/stm32l432/src/device.c b/targets/stm32l432/src/device.c index 692a04f4..e841f1e5 100644 --- a/targets/stm32l432/src/device.c +++ b/targets/stm32l432/src/device.c @@ -128,7 +128,6 @@ void TIM6_DAC_IRQHandler(void) // Interrupt on rising edge of button (button released) void EXTI0_IRQHandler(void) { - uint8_t report[8] = {0, 0, 0x1e, 0x1f, 0x20, 0x21, 0, 0}; EXTI->PR1 = EXTI->PR1; if (is_physical_button_pressed == IS_BUTTON_PRESSED) { @@ -136,12 +135,13 @@ void EXTI0_IRQHandler(void) if ((millis() - __last_button_bounce_time) > 25) { __last_button_press_time = millis(); +#ifndef IS_BOOTLOADER + uint8_t msg[8] = {'F', 'i', '0', '`', ']', '$', '}', '<'}; if (!_up_wait) { - usbkbd_send(report); - memset(report, 0, sizeof(report)); - usbkbd_send(report); + usb_kbd_send(msg, 8); } +#endif } __last_button_bounce_time = millis(); } @@ -406,18 +406,6 @@ void usbhid_send(uint8_t * msg) } -void usbkbd_send(uint8_t * msg) -{ - printf1(TAG_DUMP2,"<< "); - dump_hex1(TAG_DUMP2, msg, KBD_PACKET_SIZE); - - Solo_USBD_Device.ep_in[KBD_EPIN_ADDR & 0xFU].total_length = KBD_PACKET_SIZE; - - while (PCD_GET_EP_TX_STATUS(USB, KBD_EPIN_ADDR & 0x0f) == USB_EP_TX_VALID) - ; - USBD_LL_Transmit(&Solo_USBD_Device, KBD_EPIN_ADDR, msg, KBD_PACKET_SIZE); -} - void ctaphid_write_block(uint8_t * data) { usbhid_send(data); From d444309db443727ec4301ac68b7a6ea9c3a15bd6 Mon Sep 17 00:00:00 2001 From: Radoslav Gerganov Date: Mon, 15 Jun 2020 16:36:31 +0300 Subject: [PATCH 3/3] usbkbd: add vendor command for programming the keyboard The command value is 0x51 and it accepts a single argument which is a byte string. --- fido2/ctap.c | 18 ++++++++++++++++++ fido2/ctap.h | 6 ++++++ fido2/ctap_parse.c | 22 ++++++++++++++++++++++ fido2/ctap_parse.h | 1 + fido2/device.h | 2 ++ pc/device.c | 5 +++++ targets/stm32l432/src/device.c | 22 ++++++++++++++++++++-- targets/stm32l432/src/memory_layout.h | 4 ++++ 8 files changed, 78 insertions(+), 2 deletions(-) diff --git a/fido2/ctap.c b/fido2/ctap.c index 3ccb9cbf..7730e563 100644 --- a/fido2/ctap.c +++ b/fido2/ctap.c @@ -1738,6 +1738,20 @@ uint8_t ctap_cred_mgmt(CborEncoder * encoder, uint8_t * request, int length) return 0; } +uint8_t ctap_solo_kbd(CborEncoder * encoder, uint8_t * request, int length) +{ + CTAP_soloKbd KBD; + int ret = ctap_parse_solo_kbd(&KBD, request, length); + if (ret != 0) + { + printf2(TAG_ERR,"error, ctap_parse_solo_kbd failed\n"); + return ret; + } + dump_hex1(TAG_CTAP, KBD.sequence, KBD.length); + ctap_store_kbd(&KBD); + return 0; +} + uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length) { CTAP_getAssertion GA; @@ -2318,6 +2332,10 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp) dump_hex1(TAG_DUMP,buf, resp->length); break; + case CTAP_SOLO_KBD: + printf1(TAG_CTAP,"CTAP_SOLO_KBD\n"); + status = ctap_solo_kbd(&encoder, pkt_raw, length); + break; default: status = CTAP1_ERR_INVALID_COMMAND; printf2(TAG_ERR,"error, invalid cmd: 0x%02x\n", cmd); diff --git a/fido2/ctap.h b/fido2/ctap.h index db98c27b..79f1190e 100644 --- a/fido2/ctap.h +++ b/fido2/ctap.h @@ -19,6 +19,7 @@ #define CTAP_CBOR_CRED_MGMT 0x0A #define CTAP_VENDOR_FIRST 0x40 #define CTAP_CBOR_CRED_MGMT_PRE 0x41 +#define CTAP_SOLO_KBD 0x51 #define CTAP_VENDOR_LAST 0xBF #define MC_clientDataHash 0x01 @@ -338,6 +339,11 @@ typedef struct int pinProtocol; } CTAP_credMgmt; +typedef struct +{ + uint8_t sequence[64]; + uint8_t length; +} CTAP_soloKbd; typedef struct { diff --git a/fido2/ctap_parse.c b/fido2/ctap_parse.c index a6e53f24..4c15b4e3 100644 --- a/fido2/ctap_parse.c +++ b/fido2/ctap_parse.c @@ -713,6 +713,28 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext) return 0; } +uint8_t ctap_parse_solo_kbd(CTAP_soloKbd * KBD, uint8_t * request, int length) +{ + int ret; + CborParser parser; + CborValue it; + + memset(KBD, 0, sizeof(CTAP_soloKbd)); + ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it); + check_retr(ret); + CborType type = cbor_value_get_type(&it); + if (type != CborByteStringType) + { + printf2(TAG_ERR,"Error, expecting cbor byte string\n"); + return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; + } + size_t len = sizeof(KBD->sequence); + ret = cbor_value_copy_byte_string(&it, KBD->sequence, &len, NULL); + check_ret(ret); + KBD->length = len; + return ret; +} + uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length) { int ret; diff --git a/fido2/ctap_parse.h b/fido2/ctap_parse.h index e0be8071..ac29bac5 100644 --- a/fido2/ctap_parse.h +++ b/fido2/ctap_parse.h @@ -38,6 +38,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length); uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length); uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred); +uint8_t ctap_parse_solo_kbd(CTAP_soloKbd * KBD, uint8_t * request, int length); #endif diff --git a/fido2/device.h b/fido2/device.h index e002ee5c..cce54e23 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -162,6 +162,8 @@ void ctap_load_rk(int index,CTAP_residentKey * rk); */ void ctap_overwrite_rk(int index,CTAP_residentKey * rk); +void ctap_load_kbd(CTAP_soloKbd * kbd); +void ctap_store_kbd(CTAP_soloKbd * kbd); /** Called by HID layer to indicate that a wink behavior should be performed. * Should not block, and the wink behavior should occur in parallel to FIDO operations. diff --git a/pc/device.c b/pc/device.c index 0536bb2d..bbf21160 100644 --- a/pc/device.c +++ b/pc/device.c @@ -449,6 +449,11 @@ void ctap_store_rk(int index, CTAP_residentKey * rk) } +void ctap_store_kbd(CTAP_soloKbd * kbd) +{ + // nop +} + void ctap_delete_rk(int index) { CTAP_residentKey rk; diff --git a/targets/stm32l432/src/device.c b/targets/stm32l432/src/device.c index e841f1e5..913779cb 100644 --- a/targets/stm32l432/src/device.c +++ b/targets/stm32l432/src/device.c @@ -136,10 +136,14 @@ void EXTI0_IRQHandler(void) { __last_button_press_time = millis(); #ifndef IS_BOOTLOADER - uint8_t msg[8] = {'F', 'i', '0', '`', ']', '$', '}', '<'}; if (!_up_wait) { - usb_kbd_send(msg, 8); + CTAP_soloKbd kbd; + ctap_load_kbd(&kbd); + if (kbd.length > 0) + { + usb_kbd_send(kbd.sequence, kbd.length); + } } #endif } @@ -856,6 +860,20 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk) printf1(TAG_GREEN, "4\r\n"); } +void ctap_store_kbd(CTAP_soloKbd * kbd) +{ + uint8_t buf[PAGE_SIZE]; + memmove(buf, kbd, sizeof(CTAP_soloKbd)); + flash_erase_page(KEYBOARD_PAGE); + flash_write(KEYBOARD_PAGE_ADDR, buf, 2048); +} + +void ctap_load_kbd(CTAP_soloKbd * kbd) +{ + uint32_t * ptr = (uint32_t *) flash_addr(KEYBOARD_PAGE); + memmove(kbd, ptr, sizeof(CTAP_soloKbd)); +} + void boot_st_bootloader(void) { __disable_irq(); diff --git a/targets/stm32l432/src/memory_layout.h b/targets/stm32l432/src/memory_layout.h index d9682c86..4ef8295e 100644 --- a/targets/stm32l432/src/memory_layout.h +++ b/targets/stm32l432/src/memory_layout.h @@ -38,6 +38,10 @@ #define ATTESTATION_PAGE (PAGES - 15) #define ATTESTATION_PAGE_ADDR (0x08000000 + ATTESTATION_PAGE*PAGE_SIZE) +// keyboard stuff +#define KEYBOARD_PAGE (PAGES - 16) +#define KEYBOARD_PAGE_ADDR (0x08000000 + KEYBOARD_PAGE*PAGE_SIZE) + // End of application code. Leave some extra room for future data storage. // NOT included in application #define APPLICATION_END_PAGE ((PAGES - 20))