/* usb_keyboard.c - Implementation of a USB keyboard device * * Copyright (C) 2024 Luke T. Shumaker * SPDX-Licence-Identifier: AGPL-3.0-or-later */ #include "tusb.h" #include "tusb_helpers.h" /* for TUD_ENDPOINT_IN */ #include "usb_common.h" #include "usb_keyboard.h" /** * A USB-HID "Report Descriptor" (see USB-HID 1.11 §6.2.2 "Report * Descriptor") describing a keyboard. */ static uint8_t const hid_report_descriptor_keyboard[] = { TUD_HID_REPORT_DESC_KEYBOARD() }; static uint8_t kbd_ifc = 0; void usb_keyboard_init() { if (kbd_ifc) return; usb_common_earlyinit(); kbd_ifc = usb_add_interface(cfgnum_std, TUD_HID_DESC_LEN, (uint8_t[]){ /* USB-HID input-only descriptor for inclusion in the config descriptor; consisting of 3 parts: * 1. an interface descriptor (USB 2.0 §9.6.5 "Interface"), * 2. a class-specific (class=HID) descriptor of type HID (USB-HID 1.11 §6.2.1 "HID Descriptor"), * 3. an endpoint descriptor for inputs (USB 2.0 §9.6.6 "Endpoint"). * The TUD_HID_DESCRIPTOR() macro takes care of this for us. */ TUD_HID_DESCRIPTOR( 0, /* interface : bInterfaceNumber ; Number of this interface (0-indexed) */ STRID_KBD_IFC, /* interface : iInterface ; Index of string descriptor describing this interface */ HID_ITF_PROTOCOL_KEYBOARD, /* interface : bInterfaceProtocol ; see USB-HID 1.11 §4.3 "Protocols" */ sizeof(hid_report_descriptor_keyboard), /* hid : wDescriptorLength ; Total size of report descriptor */ TUD_ENDPOINT_IN | 1, /* endpoint : bEndpointAddress ; Direction | endpoint number (arbitrary?) */ CFG_TUD_HID_EP_BUFSIZE, /* endpoint : wMaxPacketSize ; Maximum packet size this endpoint is capable of sending or receiving */ 10), /* endpoint : bInterval ; poll interval (in milliseconds?) */ }); } static uint8_t ascii2keycode[128][2] = { HID_ASCII_TO_KEYCODE }; COROUTINE usb_keyboard_cr(void *_chan) { usb_keyboard_chan_t *chan = _chan; cr_begin(); uint8_t report_id = 0; uint8_t modifier = 0; uint8_t keycodes[6] = {0}; for (;;) { while (!tud_hid_n_ready(kbd_ifc)) cr_yield(); if (cr_chan_have_req(chan)) { uint32_t rune; cr_chan_recv_req(chan, &rune); modifier = ascii2keycode[rune][0] ? KEYBOARD_MODIFIER_LEFTSHIFT : 0; keycodes[0] = ascii2keycode[rune][1]; tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes); while (!tud_hid_n_ready(kbd_ifc)) cr_yield(); modifier = 0; keycodes[0] = 0; tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes); cr_chan_send_resp(chan, 1); } else { modifier = 0; keycodes[0] = 0; tud_hid_n_keyboard_report(kbd_ifc, report_id, modifier, keycodes); } } cr_end(); } /** * Return a pointer to the HID "Report Descriptor" (see USB-HID 1.11 * §6.2.2 "Report Descriptor") for the given index. */ uint8_t const *tud_hid_descriptor_report_cb(uint8_t index) { static uint8_t const *reports[] = { hid_report_descriptor_keyboard, }; if (index >= TU_ARRAY_SIZE(reports)) return NULL; return reports[index]; } uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { // TODO not Implemented (void) instance; (void) report_id; (void) report_type; (void) buffer; (void) reqlen; return 0; } // Invoked when received SET_REPORT control request or // received data on OUT endpoint ( Report ID = 0, Type = 0 ) void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { // TODO not implemented (void) report_id; }