@@ -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