[Maintenance] USB HID control packet as struct (#21688)

* ChibiOS: USB HID control request as dedicated struct

Instead of accessing the raw USB setup packet and documenting the values
as the corresponding USB HID control request fields we introduce a
struct that allows direct access to the fields. This is safer and self
documenting.

* Rename usb_request.h to usb_types.h

In the future all shared USB data types can live in this file.
This commit is contained in:
Stefan Kerkmann 2023-10-27 18:44:58 +02:00 committed by GitHub
parent 64d1ce751f
commit 5856d5e13b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 92 deletions

View file

@ -43,6 +43,7 @@
#include "usb_device_state.h" #include "usb_device_state.h"
#include "usb_descriptor.h" #include "usb_descriptor.h"
#include "usb_driver.h" #include "usb_driver.h"
#include "usb_types.h"
#ifdef NKRO_ENABLE #ifdef NKRO_ENABLE
# include "keycode_config.h" # include "keycode_config.h"
@ -103,30 +104,18 @@ union {
NULL, /* SETUP buffer (not a SETUP endpoint) */ NULL, /* SETUP buffer (not a SETUP endpoint) */
#endif #endif
/* HID specific constants */
#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
/*
* Handles the GET_DESCRIPTOR callback
*
* Returns the proper descriptor
*/
static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) { static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
(void)usbp; usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
static USBDescriptor desc;
uint16_t wValue = ((uint16_t)dtype << 8) | dindex; static USBDescriptor descriptor;
uint16_t wLength = ((uint16_t)usbp->setup[7] << 8) | usbp->setup[6]; descriptor.ud_string = NULL;
desc.ud_string = NULL; descriptor.ud_size = get_usb_descriptor(setup->wValue.word, setup->wIndex, setup->wLength, (const void **const) & descriptor.ud_string);
desc.ud_size = get_usb_descriptor(wValue, wIndex, wLength, (const void **const) & desc.ud_string);
if (desc.ud_string == NULL) if (descriptor.ud_string == NULL) {
return NULL; return NULL;
else }
return &desc;
return &descriptor;
} }
/* /*
@ -497,8 +486,7 @@ void usb_event_queue_task(void) {
} }
} }
/* Handles the USB driver global events /* Handles the USB driver global events. */
* TODO: maybe disable some things when connection is lost? */
static void usb_event_cb(USBDriver *usbp, usbevent_t event) { static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
switch (event) { switch (event) {
case USB_EVENT_ADDRESS: case USB_EVENT_ADDRESS:
@ -570,16 +558,6 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
} }
} }
/* Function used locally in os/hal/src/usb.c for getting descriptors
* need it here for HID descriptor */
static uint16_t get_hword(uint8_t *p) {
uint16_t hw;
hw = (uint16_t)*p++;
hw |= (uint16_t)*p << 8U;
return hw;
}
/* /*
* Appendix G: HID Request Support Requirements * Appendix G: HID Request Support Requirements
* *
@ -596,7 +574,9 @@ static uint16_t get_hword(uint8_t *p) {
static uint8_t set_report_buf[2] __attribute__((aligned(4))); static uint8_t set_report_buf[2] __attribute__((aligned(4)));
static void set_led_transfer_cb(USBDriver *usbp) { static void set_led_transfer_cb(USBDriver *usbp) {
if (usbp->setup[6] == 2) { /* LSB(wLength) */ usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
if (setup->wLength == 2) {
uint8_t report_id = set_report_buf[0]; uint8_t report_id = set_report_buf[0];
if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) { if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) {
keyboard_led_state = set_report_buf[1]; keyboard_led_state = set_report_buf[1];
@ -606,24 +586,16 @@ static void set_led_transfer_cb(USBDriver *usbp) {
} }
} }
/* Callback for SETUP request on the endpoint 0 (control) */ static bool usb_requests_hook_cb(USBDriver *usbp) {
static bool usb_request_hook_cb(USBDriver *usbp) { usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;
const USBDescriptor *dp;
/* usbp->setup fields:
* 0: bmRequestType (bitmask)
* 1: bRequest
* 2,3: (LSB,MSB) wValue
* 4,5: (LSB,MSB) wIndex
* 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */
/* Handle HID class specific requests */ /* Handle HID class specific requests */
if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { if ((setup->bmRequestType & (USB_RTYPE_TYPE_MASK | USB_RTYPE_RECIPIENT_MASK)) == (USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE)) {
switch (usbp->setup[0] & USB_RTYPE_DIR_MASK) { switch (setup->bmRequestType & USB_RTYPE_DIR_MASK) {
case USB_RTYPE_DIR_DEV2HOST: case USB_RTYPE_DIR_DEV2HOST:
switch (usbp->setup[1]) { /* bRequest */ switch (setup->bRequest) {
case HID_GET_REPORT: case HID_REQ_GetReport:
switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ switch (setup->wIndex) {
#ifndef KEYBOARD_SHARED_EP #ifndef KEYBOARD_SHARED_EP
case KEYBOARD_INTERFACE: case KEYBOARD_INTERFACE:
usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL); usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL);
@ -639,59 +611,54 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
#ifdef SHARED_EP_ENABLE #ifdef SHARED_EP_ENABLE
case SHARED_INTERFACE: case SHARED_INTERFACE:
# ifdef KEYBOARD_SHARED_EP # ifdef KEYBOARD_SHARED_EP
if (usbp->setup[2] == REPORT_ID_KEYBOARD) { if (setup->wValue.lbyte == REPORT_ID_KEYBOARD) {
usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL); usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, KEYBOARD_REPORT_SIZE, NULL);
return TRUE; return true;
break;
} }
# endif # endif
# ifdef MOUSE_SHARED_EP # ifdef MOUSE_SHARED_EP
if (usbp->setup[2] == REPORT_ID_MOUSE) { if (setup->wValue.lbyte == REPORT_ID_MOUSE) {
usbSetupTransfer(usbp, (uint8_t *)&mouse_report_sent, sizeof(mouse_report_sent), NULL); usbSetupTransfer(usbp, (uint8_t *)&mouse_report_sent, sizeof(mouse_report_sent), NULL);
return TRUE; return true;
break;
} }
# endif # endif
#endif /* SHARED_EP_ENABLE */ #endif /* SHARED_EP_ENABLE */
default: default:
universal_report_blank.report_id = usbp->setup[2]; universal_report_blank.report_id = setup->wValue.lbyte;
usbSetupTransfer(usbp, (uint8_t *)&universal_report_blank, usbp->setup[6], NULL); usbSetupTransfer(usbp, (uint8_t *)&universal_report_blank, setup->wLength, NULL);
return TRUE; return true;
break;
} }
break; break;
case HID_GET_PROTOCOL: case HID_REQ_GetProtocol:
if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ if (setup->wIndex == KEYBOARD_INTERFACE) {
usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); usbSetupTransfer(usbp, &keyboard_protocol, sizeof(uint8_t), NULL);
return TRUE; return true;
} }
break; break;
case HID_GET_IDLE: case HID_REQ_GetIdle:
usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); usbSetupTransfer(usbp, &keyboard_idle, sizeof(uint8_t), NULL);
return TRUE; return true;
break;
} }
break; break;
case USB_RTYPE_DIR_HOST2DEV: case USB_RTYPE_DIR_HOST2DEV:
switch (usbp->setup[1]) { /* bRequest */ switch (setup->bRequest) {
case HID_SET_REPORT: case HID_REQ_SetReport:
switch (usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ switch (setup->wIndex) {
case KEYBOARD_INTERFACE: case KEYBOARD_INTERFACE:
#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) #if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
case SHARED_INTERFACE: case SHARED_INTERFACE:
#endif #endif
usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);
return TRUE; return true;
break;
} }
break; break;
case HID_SET_PROTOCOL: case HID_REQ_SetProtocol:
if ((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ if (setup->wIndex == KEYBOARD_INTERFACE) {
keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ keyboard_protocol = setup->wValue.word;
#ifdef NKRO_ENABLE #ifdef NKRO_ENABLE
if (!keyboard_protocol && keyboard_idle) { if (!keyboard_protocol && keyboard_idle) {
#else /* NKRO_ENABLE */ #else /* NKRO_ENABLE */
@ -704,11 +671,10 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
} }
} }
usbSetupTransfer(usbp, NULL, 0, NULL); usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE; return true;
break;
case HID_SET_IDLE: case HID_REQ_SetIdle:
keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ keyboard_idle = setup->wValue.hbyte;
/* arm the timer */ /* arm the timer */
#ifdef NKRO_ENABLE #ifdef NKRO_ENABLE
if (!keymap_config.nkro && keyboard_idle) { if (!keymap_config.nkro && keyboard_idle) {
@ -720,19 +686,21 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
osalSysUnlockFromISR(); osalSysUnlockFromISR();
} }
usbSetupTransfer(usbp, NULL, 0, NULL); usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE; return true;
break;
} }
break; break;
} }
} }
/* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ /* Handle the Get_Descriptor Request for HID class, which is not handled by
if ((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { * the ChibiOS USB driver */
dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); if (((setup->bmRequestType & (USB_RTYPE_DIR_MASK | USB_RTYPE_RECIPIENT_MASK)) == (USB_RTYPE_DIR_DEV2HOST | USB_RTYPE_RECIPIENT_INTERFACE)) && (setup->bRequest == USB_REQ_GET_DESCRIPTOR)) {
if (dp == NULL) return FALSE; const USBDescriptor *descriptor = usbp->config->get_descriptor_cb(usbp, setup->wValue.lbyte, setup->wValue.hbyte, setup->wIndex);
usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); if (descriptor == NULL) {
return TRUE; return false;
}
usbSetupTransfer(usbp, (uint8_t *)descriptor->ud_string, descriptor->ud_size, NULL);
return true;
} }
for (int i = 0; i < NUM_USB_DRIVERS; i++) { for (int i = 0; i < NUM_USB_DRIVERS; i++) {
@ -742,10 +710,9 @@ static bool usb_request_hook_cb(USBDriver *usbp) {
} }
} }
return FALSE; return false;
} }
/* Start-of-frame callback */
static void usb_sof_cb(USBDriver *usbp) { static void usb_sof_cb(USBDriver *usbp) {
osalSysLockFromISR(); osalSysLockFromISR();
for (int i = 0; i < NUM_USB_DRIVERS; i++) { for (int i = 0; i < NUM_USB_DRIVERS; i++) {
@ -758,7 +725,7 @@ static void usb_sof_cb(USBDriver *usbp) {
static const USBConfig usbcfg = { static const USBConfig usbcfg = {
usb_event_cb, /* USB events callback */ usb_event_cb, /* USB events callback */
usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */
usb_request_hook_cb, /* Requests hook callback */ usb_requests_hook_cb, /* Requests hook callback */
usb_sof_cb /* Start Of Frame callback */ usb_sof_cb /* Start Of Frame callback */
}; };

View file

@ -0,0 +1,23 @@
// Copyright 2023 Stefan Kerkmann
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "util.h"
/**
* @brief Common USB 2.0 control request structure
*/
typedef struct {
uint8_t bmRequestType; // [0] (Bitmask)
uint8_t bRequest; // [1]
union {
struct {
uint8_t lbyte; // [2] (LSB)
uint8_t hbyte; // [3] (MSB)
};
uint16_t word; // [2,3] (LSB,MSB)
} wValue;
uint16_t wIndex; // [4,5] (LSB,MSB)
uint16_t wLength; // [6,7] (LSB,MSB)
} PACKED usb_control_request_t;