Skip to content

Commit 4b33d19

Browse files
committed
usb keyboard added support for media keys using second report id
1 parent da50151 commit 4b33d19

1 file changed

Lines changed: 164 additions & 26 deletions

File tree

klib/usb/device/keyboard.hpp

Lines changed: 164 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,21 @@ namespace klib::usb::device {
191191

192192
static_assert(sizeof(key_t) == sizeof(uint8_t), "invalid keys size");
193193

194+
/**
195+
* @brief Enum for consumer control keys (media keys)
196+
*/
197+
enum class consumer_key_t: uint16_t {
198+
key_none = 0x00,
199+
key_volume_up = 0xe9,
200+
key_volume_down = 0xea,
201+
key_mute = 0xe2,
202+
key_media_play_pause = 0xcd,
203+
key_media_stop = 0xb7,
204+
key_media_previous_track = 0xb6,
205+
key_media_next_track = 0xb5,
206+
key_media_eject = 0xb8,
207+
};
208+
194209
protected:
195210
/**
196211
* @brief Enum with the string descriptor indexes
@@ -208,7 +223,8 @@ namespace klib::usb::device {
208223
*
209224
*/
210225
enum class report_id: uint8_t {
211-
keyboard = 1,
226+
keycode = 1,
227+
consumer = 2,
212228
};
213229

214230
// Push the current pack to the stack and set the pack to 1
@@ -279,6 +295,20 @@ namespace klib::usb::device {
279295
0x95, 0x01, // Report Count (1)
280296
0x81, 0x00, // Input (Data, Array) ; Keycode byte
281297
0xc0, // End Collection
298+
299+
// Consumer Control report (Report ID 2)
300+
0x05, 0x0c, // Usage Page (Consumer)
301+
0x09, 0x01, // Usage (Consumer Control)
302+
0xa1, 0x01, // Collection (Application)
303+
0x85, 0x02, // Report ID (2)
304+
0x15, 0x00, // Logical Minimum (0)
305+
0x26, 0xff, 0x03, // Logical Maximum (0x3FF)
306+
0x19, 0x00, // Usage Minimum (0)
307+
0x2a, 0xff, 0x03, // Usage Maximum (0x3FF)
308+
0x75, 0x10, // Report Size (16)
309+
0x95, 0x01, // Report Count (1)
310+
0x81, 0x00, // Input (Data, Array)
311+
0xc0 // End Collection
282312
};
283313

284314
// configuration descriptor
@@ -340,37 +370,98 @@ namespace klib::usb::device {
340370
// as the following structs have specific sizes
341371
#pragma pack(push, 1)
342372

373+
/**
374+
* @brief Struct that represents the keycode report
375+
* we send to the host. This is defined in the report
376+
* descriptor (report_id::keycode)
377+
*
378+
*/
343379
struct keycode_report_t {
344-
// fixed report id for the keyboard
345-
const report_id id = report_id::keyboard;
380+
// fixed report id for the keycode
381+
const report_id id = report_id::keycode;
346382

347383
// key data
348384
uint8_t modifier;
349385
key_t key;
350386
};
351387

352-
static_assert(sizeof(keycode_report_t) == 3, "invalid keyboard report size");
388+
static_assert(sizeof(keycode_report_t) == 3, "invalid keycode report size");
389+
390+
/**
391+
* @brief Struct that represents the consumer control
392+
* report we send to the host. This is defined in the
393+
* report descriptor (report_id::consumer)
394+
*
395+
*/
396+
struct consumer_report_t {
397+
// fixed report id for the consumer control
398+
const report_id id = report_id::consumer;
399+
400+
consumer_key_t key;
401+
};
402+
403+
static_assert(sizeof(consumer_report_t) == 3, "invalid consumer report size");
353404

354405
// release the old pack so the rest of the structs are not
355406
// affected by the pack(1)
356407
#pragma pack(pop)
357408

358-
// storage for the keyboard hid messages
409+
// storage for the reports when we are sending them
359410
static inline keycode_report_t keycode_report = {};
411+
static inline consumer_report_t consumer_report = {};
360412

361413
// static parameters with data to write to the host
362414
static inline const char *volatile irq_data = nullptr;
363415
static inline volatile uint32_t irq_size = 0;
364416
static inline bool repeated_key = false;
365417

418+
/**
419+
* @brief Callbakc that will clear the consumer report after it has been sent
420+
*
421+
* @tparam Usb
422+
* @param endpoint
423+
* @param mode
424+
* @param error_code
425+
* @param transferred
426+
*/
427+
template <typename Usb>
428+
static void hid_callback_consumer(const uint8_t endpoint, const usb::endpoint_mode mode, const usb::error error_code, const uint32_t transferred) {
429+
// only continue if we do not have any errors
430+
if (error_code != usb::error::no_error) {
431+
// we have a error
432+
return;
433+
}
434+
435+
// check if we are configured
436+
if (!configuration) {
437+
return;
438+
}
439+
440+
// we have send a report. Send a new report that clears the key
441+
consumer_report.key = consumer_key_t::key_none;
442+
443+
// send the no key pressed to the host
444+
Usb::write(
445+
nullptr, usb::get_endpoint(config.endpoint.bEndpointAddress),
446+
mode, {reinterpret_cast<const uint8_t*>(&consumer_report), sizeof(consumer_report)}
447+
);
448+
449+
// we have nothing more to send. Clear the data to notify we are done with the string
450+
irq_data = nullptr;
451+
irq_size = 0;
452+
}
453+
366454
/**
367455
* @brief Callback that can send more keypresses or will send no keys
368-
*
369-
* @tparam Usb
370-
* @param data
456+
*
457+
* @tparam Usb
458+
* @param endpoint
459+
* @param mode
460+
* @param error_code
461+
* @param transferred
371462
*/
372463
template <typename Usb>
373-
static void hid_callback(const uint8_t endpoint, const usb::endpoint_mode mode, const usb::error error_code, const uint32_t transferred) {
464+
static void hid_callback_keycode(const uint8_t endpoint, const usb::endpoint_mode mode, const usb::error error_code, const uint32_t transferred) {
374465
// only continue if we do not have any errors
375466
if (error_code != usb::error::no_error) {
376467
// we have a error
@@ -391,7 +482,7 @@ namespace klib::usb::device {
391482

392483
// send the no key pressed to the host
393484
Usb::write(
394-
hid_callback<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
485+
hid_callback_keycode<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
395486
mode, {reinterpret_cast<const uint8_t*>(&keycode_report), sizeof(keycode_report)}
396487
);
397488

@@ -418,7 +509,7 @@ namespace klib::usb::device {
418509

419510
// send the report to the host
420511
Usb::write(
421-
hid_callback<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
512+
hid_callback_keycode<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
422513
mode, {reinterpret_cast<const uint8_t*>(&keycode_report), sizeof(keycode_report)}
423514
);
424515

@@ -442,7 +533,7 @@ namespace klib::usb::device {
442533

443534
// send the report to the host
444535
Usb::write(
445-
hid_callback<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
536+
hid_callback_keycode<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
446537
mode, {reinterpret_cast<const uint8_t*>(&keycode_report), sizeof(keycode_report)}
447538
);
448539

@@ -551,10 +642,10 @@ namespace klib::usb::device {
551642
return false;
552643
}
553644

554-
template <typename Usb, bool Async = false, typename T = keycode_report_t>
645+
template <typename Usb, bool Async = false, bool Keycode = true, typename T = keycode_report_t>
555646
static bool write_impl(const T& r) {
556647
// write the first report to the endpoint
557-
if (!Usb::write(hid_callback<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
648+
if (!Usb::write(Keycode ? hid_callback_keycode<Usb> : hid_callback_consumer<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
558649
usb::get_endpoint_mode(config.endpoint.bEndpointAddress), {reinterpret_cast<const uint8_t*>(&r), sizeof(r)}))
559650
{
560651
return false;
@@ -618,7 +709,29 @@ namespace klib::usb::device {
618709
irq_data = &dummy;
619710

620711
// write the data
621-
return write_impl<Usb, Async>(keycode_report);
712+
return write_impl<Usb, Async, true>(keycode_report);
713+
}
714+
715+
template <typename Usb, bool Async = false>
716+
static bool write(const consumer_key_t key) {
717+
// static dummy data for the interrupt handler
718+
// to signal if we are still busy
719+
static char dummy;
720+
721+
// check if we are busy or not
722+
if (is_invalid_or_busy<Usb>()) {
723+
return false;
724+
}
725+
726+
// set the report
727+
consumer_report.key = key;
728+
729+
// set the data in the interrupt
730+
irq_size = 0;
731+
irq_data = &dummy;
732+
733+
// write the data
734+
return write_impl<Usb, Async, false>(consumer_report);
622735
}
623736

624737
/**
@@ -852,12 +965,13 @@ namespace klib::usb::device {
852965
static usb::handshake set_config(const klib::usb::setup_packet &packet) {
853966
// check if the set is the same as the configuration we have stored
854967
if (packet.wValue == config.configuration.bConfigurationValue) {
855-
// configure the endpoint for our report data
968+
// configure the endpoint for our report data. We need to give it
969+
// the largest report size we have
856970
Usb::configure(
857971
usb::get_endpoint(config.endpoint.bEndpointAddress),
858972
usb::get_endpoint_mode(config.endpoint.bEndpointAddress),
859973
usb::get_transfer_type(config.endpoint.bmAttributes),
860-
sizeof(keycode_report)
974+
klib::max(sizeof(keycode_report), sizeof(consumer_report))
861975
);
862976

863977
// store the configuration value
@@ -872,7 +986,7 @@ namespace klib::usb::device {
872986

873987
// write the inital report
874988
if (Usb::write(
875-
hid_callback<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
989+
hid_callback_keycode<Usb>, usb::get_endpoint(config.endpoint.bEndpointAddress),
876990
usb::get_endpoint_mode(config.endpoint.bEndpointAddress),
877991
{reinterpret_cast<const uint8_t*>(&keycode_report), sizeof(keycode_report)}))
878992
{
@@ -960,7 +1074,7 @@ namespace klib::usb::device {
9601074
break;
9611075
case hid::class_request::get_idle:
9621076
switch (static_cast<report_id>(packet.wValue & 0xff)) {
963-
case report_id::keyboard:
1077+
case report_id::keycode:
9641078
// write the data to the control endpoint
9651079
if (Usb::write(nullptr, usb::control_endpoint, usb::endpoint_mode::in,
9661080
{reinterpret_cast<const uint8_t*>(&keycode_report), sizeof(keycode_report)}))
@@ -973,6 +1087,19 @@ namespace klib::usb::device {
9731087
return usb::handshake::stall;
9741088
}
9751089
break;
1090+
case report_id::consumer:
1091+
// write the data to the control endpoint
1092+
if (Usb::write(nullptr, usb::control_endpoint, usb::endpoint_mode::in,
1093+
{reinterpret_cast<const uint8_t*>(&consumer_report), sizeof(consumer_report)}))
1094+
{
1095+
// no issue for now ack
1096+
return usb::handshake::ack;
1097+
}
1098+
else {
1099+
// something went wrong stall for now
1100+
return usb::handshake::stall;
1101+
}
1102+
break;
9761103
default:
9771104
// not supported for now
9781105
return usb::handshake::stall;
@@ -990,15 +1117,26 @@ namespace klib::usb::device {
9901117
}
9911118
break;
9921119
case hid::class_request::set_idle:
993-
// TODO: add support for report id != 0
994-
// for now we only support report id == 0 and we do not
995-
// support changing the idle
996-
if ((packet.wValue & 0xff) != 0 || (packet.wValue >> 8) != 0) {
1120+
// for now we do not support changing the idle rate. This means
1121+
// we only send a report when there is a change
1122+
if ((packet.wValue >> 8) != 0) {
1123+
// we do not support idle rates other than 0, stall
9971124
return usb::handshake::stall;
9981125
}
999-
else {
1000-
// ack and ignore
1001-
return usb::handshake::ack;
1126+
1127+
// check what report id we have
1128+
switch (static_cast<report_id>(packet.wValue & 0xff)) {
1129+
case static_cast<report_id>(0):
1130+
// applies to all report ids. We do not support idle rates
1131+
// so simply ack and ignore
1132+
return usb::handshake::ack;
1133+
case report_id::keycode:
1134+
case report_id::consumer:
1135+
// ack and ignore
1136+
return usb::handshake::ack;
1137+
default:
1138+
// unknown report id. Stall
1139+
return usb::handshake::stall;
10021140
}
10031141
break;
10041142
case hid::class_request::get_protocol:

0 commit comments

Comments
 (0)