Improved USB keyboard report handling.

A USB keyboard may return key codes in any of the 6 key code array
positions (issue #48). So we have to compare the latest report to
the previous one to detect new key presses and handle rollover. As
the same change is needed for all HCI types, factor out the common
code into the base driver.
This commit is contained in:
Martin Whitaker 2022-05-01 16:44:46 +01:00
parent 3aebe091cb
commit 97d6c7140d
6 changed files with 102 additions and 119 deletions

View File

@ -138,7 +138,6 @@
#define WS_QHD_SIZE (1 + MAX_KEYBOARDS) // Queue Head Descriptors
#define WS_QTD_SIZE (3 + MAX_KEYBOARDS) // Queue Transfer Descriptors
#define WS_KC_BUFFER_SIZE 8 // keycodes
#define MILLISEC 1000 // in microseconds
@ -205,22 +204,20 @@ typedef struct {
hcd_workspace_t base_ws;
// System memory data structures used by the host controller.
ehci_qhd_t qhd[WS_QHD_SIZE];
ehci_qtd_t qtd[WS_QTD_SIZE];
ehci_qhd_t qhd[WS_QHD_SIZE] __attribute__ ((aligned (32)));
ehci_qtd_t qtd[WS_QTD_SIZE] __attribute__ ((aligned (32)));
// Keyboard data transfer buffers.
hid_kbd_rpt_t kbd_rpt[MAX_KEYBOARDS];
// Saved keyboard reports.
hid_kbd_rpt_t prev_kbd_rpt[MAX_KEYBOARDS];
// Pointer to the host controller registers.
ehci_op_regs_t *op_regs;
// Number of keyboards detected.
int num_keyboards;
// Circular buffer for received keycodes.
uint8_t kc_buffer[WS_KC_BUFFER_SIZE];
int kc_index_i;
int kc_index_o;
} workspace_t __attribute__ ((aligned (256)));
//------------------------------------------------------------------------------
@ -444,7 +441,7 @@ static bool get_data_request(const usb_hcd_t *hcd, const usb_ep_t *ep, const usb
return do_async_transfer(ws, 3);
}
static uint8_t get_keycode(const usb_hcd_t *hcd)
static void poll_keyboards(const usb_hcd_t *hcd)
{
workspace_t *ws = (workspace_t *)hcd->ws;
@ -456,32 +453,18 @@ static uint8_t get_keycode(const usb_hcd_t *hcd)
hid_kbd_rpt_t *kbd_rpt = &ws->kbd_rpt[kbd_idx];
uint32_t error_mask = EHCI_QTD_HALTED | EHCI_QTD_DB_ERR | EHCI_QTD_BABBLE | EHCI_QTD_TR_ERR | EHCI_QTD_MMF | EHCI_QTD_PS;
uint8_t error_mask = EHCI_QTD_HALTED | EHCI_QTD_DB_ERR | EHCI_QTD_BABBLE | EHCI_QTD_TR_ERR | EHCI_QTD_MMF | EHCI_QTD_PS;
if (~status & error_mask) {
uint8_t keycode = kbd_rpt->key_code[0];
if (keycode != 0) {
int kc_index_i = ws->kc_index_i;
int kc_index_n = (kc_index_i + 1) % WS_KC_BUFFER_SIZE;
if (kc_index_n != ws->kc_index_o) {
ws->kc_buffer[kc_index_i] = keycode;
ws->kc_index_i = kc_index_n;
}
}
hid_kbd_rpt_t *prev_kbd_rpt = &ws->prev_kbd_rpt[kbd_idx];
process_usb_keyboard_report(hcd, kbd_rpt, prev_kbd_rpt);
*prev_kbd_rpt = *kbd_rpt;
}
build_ehci_qtd(kbd_qtd, kbd_qtd, EHCI_QTD_PID_IN, EHCI_QTD_DT(0), kbd_rpt, sizeof(hid_kbd_rpt_t));
build_ehci_qtd(kbd_qtd, kbd_qtd, EHCI_QTD_PID_IN, EHCI_QTD_DT(1), kbd_rpt, sizeof(hid_kbd_rpt_t));
ehci_qhd_t *kbd_qhd = &ws->qhd[1 + kbd_idx];
kbd_qhd->next_qtd_ptr = (uintptr_t)kbd_qtd;
}
int kc_index_o = ws->kc_index_o;
if (kc_index_o != ws->kc_index_i) {
ws->kc_index_o = (kc_index_o + 1) % WS_KC_BUFFER_SIZE;
return ws->kc_buffer[kc_index_o];
}
return 0;
}
//------------------------------------------------------------------------------
@ -497,7 +480,7 @@ static const hcd_methods_t methods = {
.configure_kbd_ep = NULL,
.setup_request = setup_request,
.get_data_request = get_data_request,
.get_keycode = get_keycode
.poll_keyboards = poll_keyboards
};
//------------------------------------------------------------------------------

View File

@ -161,7 +161,6 @@
#define WS_ED_SIZE (1 + MAX_KEYBOARDS) // Endpoint Descriptors
#define WS_TD_SIZE (3 + MAX_KEYBOARDS) // Transfer Descriptors
#define WS_KC_BUFFER_SIZE 8 // keycodes
#define MILLISEC 1000 // in microseconds
@ -226,20 +225,18 @@ typedef struct {
hcd_workspace_t base_ws;
// System memory data structures used by the host controller.
ohci_hcca_t hcca;
ohci_ed_t ed[WS_ED_SIZE];
ohci_td_t td[WS_TD_SIZE];
ohci_hcca_t hcca __attribute__ ((aligned (256)));
ohci_ed_t ed[WS_ED_SIZE] __attribute__ ((aligned (16)));
ohci_td_t td[WS_TD_SIZE] __attribute__ ((aligned (16)));
// Keyboad data transfer buffers.
// Keyboard data transfer buffers.
hid_kbd_rpt_t kbd_rpt[MAX_KEYBOARDS];
// Saved keyboard reports.
hid_kbd_rpt_t prev_kbd_rpt[MAX_KEYBOARDS];
// Pointer to the host controller registers.
ohci_op_regs_t *op_regs;
// Circular buffer for received keycodes.
uint8_t kc_buffer[WS_KC_BUFFER_SIZE];
int kc_index_i;
int kc_index_o;
} workspace_t __attribute__ ((aligned (256)));
//------------------------------------------------------------------------------
@ -367,7 +364,7 @@ static bool get_data_request(const usb_hcd_t *hcd, const usb_ep_t *ep, const usb
return wait_for_ohci_done(ws, 3);
}
static uint8_t get_keycode(const usb_hcd_t *hcd)
static void poll_keyboards(const usb_hcd_t *hcd)
{
workspace_t *ws = (workspace_t *)hcd->ws;
@ -378,15 +375,9 @@ static uint8_t get_keycode(const usb_hcd_t *hcd)
hid_kbd_rpt_t *kbd_rpt = &ws->kbd_rpt[kbd_idx];
if ((td->control & OHCI_TD_CC) == OHCI_TD_CC_NO_ERR) {
uint8_t keycode = kbd_rpt->key_code[0];
if (keycode != 0) {
int kc_index_i = ws->kc_index_i;
int kc_index_n = (kc_index_i + 1) % WS_KC_BUFFER_SIZE;
if (kc_index_n != ws->kc_index_o) {
ws->kc_buffer[kc_index_i] = keycode;
ws->kc_index_i = kc_index_n;
}
}
hid_kbd_rpt_t *prev_kbd_rpt = &ws->prev_kbd_rpt[kbd_idx];
process_usb_keyboard_report(hcd, kbd_rpt, prev_kbd_rpt);
*prev_kbd_rpt = *kbd_rpt;
}
ohci_td_t *next_td = (ohci_td_t *)((uintptr_t)td->next_td);
@ -397,14 +388,6 @@ static uint8_t get_keycode(const usb_hcd_t *hcd)
td = next_td;
}
int kc_index_o = ws->kc_index_o;
if (kc_index_o != ws->kc_index_i) {
ws->kc_index_o = (kc_index_o + 1) % WS_KC_BUFFER_SIZE;
return ws->kc_buffer[kc_index_o];
}
return 0;
}
//------------------------------------------------------------------------------
@ -420,7 +403,7 @@ static const hcd_methods_t methods = {
.configure_kbd_ep = NULL,
.setup_request = setup_request,
.get_data_request = get_data_request,
.get_keycode = get_keycode
.poll_keyboards = poll_keyboards
};
//------------------------------------------------------------------------------

View File

@ -146,7 +146,6 @@
#define WS_QH_SIZE (1 + MAX_KEYBOARDS) // Queue Head Descriptors
#define WS_TD_SIZE (2 + MAX_PACKETS) // Queue Transfer Descriptors
#define WS_KC_BUFFER_SIZE 8 // keycodes
#define MILLISEC 1000 // in microseconds
@ -176,22 +175,20 @@ typedef struct {
hcd_workspace_t base_ws;
// System memory data structures used by the host controller.
uhci_qh_t qh[WS_QH_SIZE];
uhci_td_t td[WS_TD_SIZE];
uhci_qh_t qh[WS_QH_SIZE] __attribute__ ((aligned (16)));
uhci_td_t td[WS_TD_SIZE] __attribute__ ((aligned (16)));
// Keyboard data transfer buffers.
hid_kbd_rpt_t kbd_rpt[MAX_KEYBOARDS];
// Saved keyboard reports.
hid_kbd_rpt_t prev_kbd_rpt[MAX_KEYBOARDS];
// I/O base address of the host controller registers.
uint16_t io_base;
// Number of keyboards detected.
int num_keyboards;
// Circular buffer for received keycodes.
uint8_t kc_buffer[WS_KC_BUFFER_SIZE];
int kc_index_i;
int kc_index_o;
} workspace_t __attribute__ ((aligned (256)));
//------------------------------------------------------------------------------
@ -373,7 +370,7 @@ static bool get_data_request(const usb_hcd_t *hcd, const usb_ep_t *ep, const usb
return wait_for_uhci_done(ws);
}
static uint8_t get_keycode(const usb_hcd_t *hcd)
static void poll_keyboards(const usb_hcd_t *hcd)
{
workspace_t *ws = (workspace_t *)hcd->ws;
uint16_t io_base = ws->io_base;
@ -393,15 +390,9 @@ static uint8_t get_keycode(const usb_hcd_t *hcd)
uint32_t error_mask = UHCI_TD_STALLED | UHCI_TD_DB_ERR | UHCI_TD_BABBLE | UHCI_TD_NAK_RX | UHCI_TD_CRC_TO | UHCI_TD_BS_ERR;
if (~status & error_mask) {
uint8_t keycode = kbd_rpt->key_code[0];
if (keycode != 0) {
int kc_index_i = ws->kc_index_i;
int kc_index_n = (kc_index_i + 1) % WS_KC_BUFFER_SIZE;
if (kc_index_n != ws->kc_index_o) {
ws->kc_buffer[kc_index_i] = keycode;
ws->kc_index_i = kc_index_n;
}
}
hid_kbd_rpt_t *prev_kbd_rpt = &ws->prev_kbd_rpt[kbd_idx];
process_usb_keyboard_report(hcd, kbd_rpt, prev_kbd_rpt);
*prev_kbd_rpt = *kbd_rpt;
}
// Reenable the TD.
@ -409,14 +400,6 @@ static uint8_t get_keycode(const usb_hcd_t *hcd)
write32(&kbd_qh->qe_link_ptr, (uintptr_t)kbd_td | UHCI_LP_TYPE_TD);
}
}
int kc_index_o = ws->kc_index_o;
if (kc_index_o != ws->kc_index_i) {
ws->kc_index_o = (kc_index_o + 1) % WS_KC_BUFFER_SIZE;
return ws->kc_buffer[kc_index_o];
}
return 0;
}
//------------------------------------------------------------------------------
@ -432,7 +415,7 @@ static const hcd_methods_t methods = {
.configure_kbd_ep = NULL,
.setup_request = setup_request,
.get_data_request = get_data_request,
.get_keycode = get_keycode
.poll_keyboards = poll_keyboards
};
//------------------------------------------------------------------------------

View File

@ -56,7 +56,7 @@ static const hcd_methods_t methods = {
.configure_kbd_ep = NULL,
.setup_request = NULL,
.get_data_request = NULL,
.get_keycode = NULL
.poll_keyboards = NULL
};
// All entries in this array must be initialised in order to generate the necessary relocation records.
@ -587,6 +587,33 @@ bool find_attached_usb_keyboards(const usb_hcd_t *hcd, const usb_hub_t *hub, int
return keyboard_found;
}
void process_usb_keyboard_report(const usb_hcd_t *hcd, hid_kbd_rpt_t *report, hid_kbd_rpt_t *prev_report)
{
hcd_workspace_t *ws = hcd->ws;
for (int i = 0; i < 6; i++) {
uint8_t key_code = report->key_code[i];
if (key_code != 0) {
// Check if we've already seen this key press.
for (int j = 0; j < 6; j++) {
if (prev_report->key_code[j] == key_code) {
key_code = 0;
break;
}
}
// If not, put it in the key code buffer.
if (key_code != 0) {
int kc_index_i = ws->kc_index_i;
int kc_index_n = (kc_index_i + 1) % HCD_KC_BUFFER_SIZE;
if (kc_index_n != ws->kc_index_o) {
ws->kc_buffer[kc_index_i] = key_code;
ws->kc_index_i = kc_index_n;
}
}
}
}
}
static void probe_usb_controller(int bus, int dev, int func, hci_type_t controller_type)
{
uint16_t vendor_id = pci_config_read16(bus, dev, func, 0x00);
@ -755,8 +782,15 @@ void find_usb_keyboards(bool pause_if_none)
uint8_t get_usb_keycode(void)
{
for (int i = 0; i < num_usb_controllers; i++) {
uint8_t keycode = usb_controllers[i].methods->get_keycode(&usb_controllers[i]);
if (keycode != 0) return keycode;
const usb_hcd_t *hcd = &usb_controllers[i];
usb_controllers[i].methods->poll_keyboards(hcd);
int kc_index_o = hcd->ws->kc_index_o;
if (kc_index_o != hcd->ws->kc_index_i) {
hcd->ws->kc_index_o = (kc_index_o + 1) % HCD_KC_BUFFER_SIZE;
return hcd->ws->kc_buffer[kc_index_o];
}
}
return 0;
}

View File

@ -30,9 +30,13 @@
/**
* The size of the data transfer buffer in a host controller driver workspace.
* This aligns the start of the driver private data to a 1024 byte boundary.
*/
#define HCD_DATA_BUFFER_SIZE (1024 - sizeof(size_t))
#define HCD_DATA_BUFFER_SIZE 512 // bytes
/**
* The size of the key code buffer in a host controller driver workspace.
*/
#define HCD_KC_BUFFER_SIZE 8 // keycodes
/**
* A USB device speed (used internally by the various HCI drivers).
@ -87,7 +91,7 @@ typedef struct {
bool (*configure_kbd_ep) (usb_hcd_r, const usb_ep_t *, int);
bool (*setup_request) (usb_hcd_r, const usb_ep_t *, const usb_setup_pkt_t *);
bool (*get_data_request) (usb_hcd_r, const usb_ep_t *, const usb_setup_pkt_t *, const void *, size_t);
uint8_t (*get_keycode) (usb_hcd_r);
void (*poll_keyboards) (usb_hcd_r);
} hcd_methods_t;
/**
@ -97,6 +101,9 @@ typedef struct {
typedef struct __attribute__((packed)) {
uint8_t data_buffer[HCD_DATA_BUFFER_SIZE];
size_t data_length;
uint8_t kc_buffer[HCD_KC_BUFFER_SIZE];
int8_t kc_index_i;
int8_t kc_index_o;
} hcd_workspace_t;
/**
@ -259,6 +266,15 @@ bool find_attached_usb_keyboards(const usb_hcd_t *hcd, const usb_hub_t *hub, int
usb_speed_t device_speed, int device_id, int *num_devices,
usb_ep_t keyboards[], int max_keyboards, int *num_keyboards);
/**
* Scans the latest keyboard report from a HID keyboard for key presses that
* weren't present in the previous report from that keyboard. Appends the HID
* key code for each new key press to the driver's key code buffer.
*
* Used internally by the various HCI drivers.
*/
void process_usb_keyboard_report(const usb_hcd_t *hcd, hid_kbd_rpt_t *report, hid_kbd_rpt_t *prev_report);
/**
* Scans the attached USB devices and initialises all HID keyboard devices
* it finds (subject to implementation limits on the number of devices).

View File

@ -138,7 +138,6 @@
#define WS_CR_SIZE 8 // TRBs (multiple of 4 to maintain 64 byte alignment)
#define WS_ER_SIZE 16 // TRBs (multiple of 4 to maintain 64 byte alignment)
#define WS_ERST_SIZE 4 // entries (multiple of 4 to maintain 64 byte alignment)
#define WS_KC_BUFFER_SIZE 8 // keycodes
#define EP_TR_SIZE 8 // TRBs (multiple of 4 to maintain 64 byte alignment)
@ -304,9 +303,9 @@ typedef struct {
hcd_workspace_t base_ws;
// System memory data structures used by the host controller.
xhci_trb_t cr [WS_CR_SIZE]; // command ring
xhci_trb_t er [WS_ER_SIZE]; // event ring
xhci_erst_entry_t erst [WS_ERST_SIZE]; // event ring segment table
xhci_trb_t cr [WS_CR_SIZE] __attribute__ ((aligned (64))); // command ring
xhci_trb_t er [WS_ER_SIZE] __attribute__ ((aligned (64))); // event ring
xhci_erst_entry_t erst [WS_ERST_SIZE] __attribute__ ((aligned (64))); // event ring segment table
// Keyboard data transfer rings
ep_tr_t kbd_tr [MAX_KEYBOARDS];
@ -314,6 +313,9 @@ typedef struct {
// Keyboard data transfer buffers.
hid_kbd_rpt_t kbd_rpt [MAX_KEYBOARDS];
// Saved keyboard reports.
hid_kbd_rpt_t prev_kbd_rpt[MAX_KEYBOARDS];
// Pointers to the host controller registers.
xhci_op_regs_t *op_regs;
xhci_rt_regs_t *rt_regs;
@ -338,11 +340,6 @@ typedef struct {
// Keyboard endpoint ID lookup table
uint8_t kbd_ep_id [MAX_KEYBOARDS];
// Circular buffer for received keycodes.
uint8_t kc_buffer[WS_KC_BUFFER_SIZE];
int32_t kc_index_i;
int32_t kc_index_o;
} workspace_t __attribute__ ((aligned (64)));
//------------------------------------------------------------------------------
@ -881,7 +878,7 @@ static int identify_keyboard(workspace_t *ws, int slot_id, int ep_id)
return -1;
}
static uint8_t get_keycode(const usb_hcd_t *hcd)
static void poll_keyboards(const usb_hcd_t *hcd)
{
workspace_t *ws = (workspace_t *)hcd->ws;
@ -894,28 +891,15 @@ static uint8_t get_keycode(const usb_hcd_t *hcd)
if (kbd_idx < 0) continue;
hid_kbd_rpt_t *kbd_rpt = &ws->kbd_rpt[kbd_idx];
uint8_t keycode = kbd_rpt->key_code[0];
if (keycode != 0) {
int kc_index_i = ws->kc_index_i;
int kc_index_n = (kc_index_i + 1) % WS_KC_BUFFER_SIZE;
if (kc_index_n != ws->kc_index_o) {
ws->kc_buffer[kc_index_i] = keycode;
ws->kc_index_i = kc_index_n;
}
}
hid_kbd_rpt_t *prev_kbd_rpt = &ws->prev_kbd_rpt[kbd_idx];
process_usb_keyboard_report(hcd, kbd_rpt, prev_kbd_rpt);
*prev_kbd_rpt = *kbd_rpt;
ep_tr_t *kbd_tr = &ws->kbd_tr[kbd_idx];
issue_normal_trb(kbd_tr, kbd_rpt, XHCI_TRB_DIR_IN, sizeof(hid_kbd_rpt_t));
ring_device_doorbell(ws->db_regs, ws->kbd_slot_id[kbd_idx], ws->kbd_ep_id[kbd_idx]);
}
int kc_index_o = ws->kc_index_o;
if (kc_index_o != ws->kc_index_i) {
ws->kc_index_o = (kc_index_o + 1) % WS_KC_BUFFER_SIZE;
return ws->kc_buffer[kc_index_o];
}
return 0;
}
static bool set_heap_segment(void)
@ -945,7 +929,7 @@ static const hcd_methods_t methods = {
.configure_kbd_ep = configure_kbd_ep,
.setup_request = setup_request,
.get_data_request = get_data_request,
.get_keycode = get_keycode
.poll_keyboards = poll_keyboards
};
//------------------------------------------------------------------------------