keebrs 0.3.0

Keyboard firmware building blocks
//! Utilities for report generation

use core::ops::RangeInclusive;

use crate::{
    key::LogiKey,
    keycode::KeyCode,
    translate::Translated,
};

// Note: not actually used. Soarer's NRKO descriptor is *actually* sent.
#[allow(dead_code)]
#[rustfmt::skip]
const BOOT_DESC: &[u8] = &[
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x68,                    //   LOGICAL_MAXIMUM (104)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x68,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION
];

const USB_FIRST_KEY_BIT: u8 = 1;
const USB_LAST_KEY_BIT: u8 = 0xA4;
const USB_NUM_KEY_BITS: u8 = USB_LAST_KEY_BIT - USB_FIRST_KEY_BIT + 1;
const USB_NUM_KEY_BIT_BYTES: u8 = (USB_NUM_KEY_BITS + 7) / 8;
const USB_NUM_PADDING_KEY_BITS: u8 = 8 * USB_NUM_KEY_BIT_BYTES - USB_NUM_KEY_BITS;

// Main key bitfield + 1 byte for modifiers + 1 media byte + 6 boot desc bytes
const USB_REPORT_SIZE: u8 = USB_NUM_KEY_BIT_BYTES + 8;

// Report descriptor by Soarer on geekhack
#[rustfmt::skip]
const SOARER_DESC: &[u8] = &[
    0x05, 0x01,          // Usage Page (Generic Desktop),
    0x09, 0x06,          // Usage (Keyboard),
    0xA1, 0x01,          // Collection (Application),

    // modifier byte
    0x75, 0x01,          //   Report Size (1),
    0x95, 0x08,          //   Report Count (8),
    0x05, 0x07,          //   Usage Page (Key Codes),
    0x19, 0xE0,          //   Usage Minimum (224),
    0x29, 0xE7,          //   Usage Maximum (231),
    0x15, 0x00,          //   Logical Minimum (0),
    0x25, 0x01,          //   Logical Maximum (1),
    0x81, 0x02,          //   Input (Data, Variable, Absolute), ;Modifier byte
    0xC0,                 // End Collection

    // Media controls (constant in boot desc)
    0x05, 0x0C,        // Usage Page (Consumer)
    0x09, 0x01,        // Usage (Consumer Control)
    0xA1, 0x01,        // Collection (Application)
    0x05, 0x0C,        //   Usage Page (Consumer)
    0x15, 0x00,        //   Logical Minimum (0)
    0x25, 0x01,        //   Logical Maximum (1)
    0x75, 0x01,        //   Report Size (1)
    0x95, 0x08,        //   Report Count (8)
    0x09, 0xB5,        //   Usage (Scan Next Track)
    0x09, 0xB6,        //   Usage (Scan Previous Track)
    0x09, 0xB7,        //   Usage (Stop)
    0x09, 0xB8,        //   Usage (Eject)
    0x09, 0xCD,        //   Usage (Play/Pause)
    0x09, 0xE2,        //   Usage (Mute)
    0x09, 0xE9,        //   Usage (Volume Increment)
    0x09, 0xEA,        //   Usage (Volume Decrement)
    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,                 // End Collection

    0x05, 0x01,          // Usage Page (Generic Desktop),
    0x09, 0x06,          // Usage (Keyboard),
    0xA1, 0x01,          // Collection (Application),

    // LEDs
    0x95, 0x05,          //   Report Count (5),
    0x75, 0x01,          //   Report Size (1),
    0x05, 0x08,          //   Usage Page (LEDs),
    0x19, 0x01,          //   Usage Minimum (1),
    0x29, 0x05,          //   Usage Maximum (5),
    0x91, 0x02,          //   Output (Data, Variable, Absolute), ;LED report

    // LED padding
    0x95, 0x01,          //   Report Count (1),
    0x75, 0x03,          //   Report Size (3),
    0x91, 0x03,          //   Output (Constant),                 ;LED report padding

    // Boot Desc bytes
    0x95, 0x06,                     //   Report Count (6),
    0x75, 0x08,						//   Report Size (8),
    0x81, 0x03,						//   Input (Constant),                 ;Padding

    // Keys
    0x75, 0x01,					//   Report Size (1),
    0x95, USB_NUM_KEY_BITS,		//   Report Count (),
    0x05, 0x07,					//   Usage Page (Key Codes),
    0x19, USB_FIRST_KEY_BIT,    //   Usage Minimum (),
    0x29, USB_LAST_KEY_BIT,     //   Usage Maximum (),
    0x15, 0x00,					//   Logical Minimum (0),
    0x25, 0x01,					//   Logical Maximum (1),
    0x81, 0x02,					//   Input (Data, Variable, Absolute), ;keys bit array

    // Might not be needed if USB_NUM_PADDING_KEY_BITS is 0, check the math
    0x95, USB_NUM_PADDING_KEY_BITS, //   Report Count (4),
    0x75, 0x01,						//   Report Size (1),
    0x81, 0x03,						//   Input (Constant),                 ;Padding

    0xC0,                 // End Collection
];

/// Report generator
pub struct Reporter {
    report: [u8; USB_REPORT_SIZE as usize],
}

impl Reporter {
    /// Create a new report generator
    pub const fn new() -> Self {
        Reporter {
            report: [0; USB_REPORT_SIZE as usize],
        }
    }

    /// Accept a key up/down event and update the internal report representation.
    pub fn update_report(&mut self, event: Translated) {
        match event {
            Translated::Single(event) => {
                self.update_report_single(event);
            }
            Translated::Multi(event) => {
                for code in event.codes {
                    self.update_report_single(LogiKey {
                        code: *code,
                        state: event.state,
                    });
                }
            }
            _ => {}
        }
    }

    fn update_report_single(&mut self, event: LogiKey) {
        if self.update_modifier(event) {
            return;
        }
        if self.update_media(event) {
            return;
        }
        self.update_boot_report(event);
        self.update_full_report(event);
    }

    fn update_full_report(&mut self, event: LogiKey) {
        use KeyCode::*;
        self.update_bitfield(8, KbErrRollover..=KbExSel, event);
    }

    fn update_boot_report(&mut self, event: LogiKey) {
        let state = *event.state;
        let key = event.code;
        let (find, replace) = if state {
            (0u8, key as u8)
        } else {
            (key as u8, 0u8)
        };
        if let Some(b) = self.report[2..8].iter_mut().find(|b| **b == find) {
            *b = replace;
        }
    }

    fn update_bitfield(
        &mut self,
        offset: usize,
        range: RangeInclusive<KeyCode>,
        event: LogiKey,
    ) -> bool {
        if range.contains(&event.code) {
            let bit = (event.code as u8) - (*range.start() as u8);

            let byte = offset + bit as usize / 8;
            if byte >= self.report.len() {
                return false;
            }
            let byte = &mut self.report[byte];

            let sub_bit = bit % 8;
            let mask = 0x01 << sub_bit;

            if *event.state {
                *byte |= mask;
            } else {
                *byte &= !mask;
            }

            true
        } else {
            false
        }
    }

    fn update_modifier(&mut self, event: LogiKey) -> bool {
        use KeyCode::*;
        self.update_bitfield(0, KbLCtrl..=KbRGui, event)
    }

    fn update_media(&mut self, event: LogiKey) -> bool {
        use KeyCode::*;
        self.update_bitfield(1, MediaNext..=MediaVolDown, event)
    }

    /// Get the full report descriptor
    pub fn get_desc(&self) -> &'static [u8] {
        SOARER_DESC
    }

    /// Get the boot descriptor
    pub fn get_boot_desc(&self) -> &'static [u8] {
        BOOT_DESC
    }

    /// Get a full report
    pub fn get_report(&self) -> &[u8] {
        &self.report[..]
    }

    /// Get the boot report
    pub fn get_boot_report(&self) -> &[u8] {
        &self.report[..8]
    }

    /// Get the length of the report
    pub fn report_len(&self) -> usize {
        self.get_report().len()
    }
}