summaryrefslogtreecommitdiff
path: root/cmd/sbc_harness/usb_keyboard.c
blob: 3500e31e578b19645cf83cfa9595b392b2d12d28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* sbc_harness/usb_keyboard.c - Implementation of a USB keyboard device
 *
 * Copyright (C) 2024  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <tusb.h>

#include <libusb/tusb_helpers.h> /* for TUD_ENDPOINT_IN */
#include <libusb/usb_common.h>

#include "usb_keyboard.h"

#define UNUSED(name) /* name __attribute__ ((unused)) */

/**
 * 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_rpc_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 (usb_keyboard_rpc_can_recv_req(chan)) {
			usb_keyboard_rpc_req_t req = usb_keyboard_rpc_recv_req(chan);
			uint32_t rune = req.req;

			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);

			usb_keyboard_rpc_send_resp(req, 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 UNUSED(instance), uint8_t UNUSED(report_id), hid_report_type_t UNUSED(report_type), uint8_t const *UNUSED(buffer), uint16_t UNUSED(bufsize))
{
	// TODO not implemented
}