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 0c96c732..cce54e23 100644 --- a/fido2/device.h +++ b/fido2/device.h @@ -24,7 +24,6 @@ uint32_t millis(); */ void usbhid_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. */ @@ -163,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/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..31902132 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,9 +100,11 @@ typedef struct USBD_HID_HandleTypeDef; extern USBD_ClassTypeDef USBD_HID; +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 new file mode 100644 index 00000000..9a328c63 --- /dev/null +++ b/targets/stm32l432/lib/usbd/usbd_kbd.c @@ -0,0 +1,403 @@ +/** + ****************************************************************************** + * @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); + +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 = +{ + 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; +} + +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/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..913779cb 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) @@ -134,6 +135,17 @@ void EXTI0_IRQHandler(void) if ((millis() - __last_button_bounce_time) > 25) { __last_button_press_time = millis(); +#ifndef IS_BOOTLOADER + if (!_up_wait) + { + CTAP_soloKbd kbd; + ctap_load_kbd(&kbd); + if (kbd.length > 0) + { + usb_kbd_send(kbd.sequence, kbd.length); + } + } +#endif } __last_button_bounce_time = millis(); } @@ -742,6 +754,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 +768,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; } @@ -846,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/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; 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))