Skip to main content

lk201/
lib.rs

1//! # LK201/LK401 Keyboard Emulator (<https://en.wikipedia.org/wiki/LK201>).
2//!
3//! The protocol interface is documented in
4//! <https://www.netbsd.org/docs/Hardware/Machines/DEC/lk201.html>, and some
5//! bootup sequences are documented at <https://vt100.net/keyboard.html>.
6//!
7//! This crate also includes an optional, slower, but far more accurate hardware
8//! emulator gated behind the `hardware` feature flag.
9#![allow(unused)]
10
11#[cfg(feature = "hardware")]
12mod hardware;
13#[cfg(feature = "hardware")]
14pub use hardware::lk201::{LK201Hardware, ScanCell};
15#[cfg(feature = "hardware")]
16pub use hardware::lk250::{LK250Hardware, Lk250Mode};
17
18mod software;
19pub use software::{LK201, LK201Sender};
20
21mod keys;
22
23pub use keys::{ALL_KEYS, Key, SpecialKey};
24use std::{collections::VecDeque, fmt, sync::mpsc};
25
26use tracing::trace;
27
28/// LED indicators on the LK201 keyboard
29///
30/// The LED parameter byte is a bitmask: 0x80 | (led_bits)
31/// - Bit 7 (0x80): PARAM bit (always set)
32/// - Bit 3 (0x08): Hold LED
33/// - Bit 2 (0x04): Lock LED  
34/// - Bit 1 (0x02): Compose LED
35/// - Bit 0 (0x01): Wait LED
36///
37/// Any combination of LEDs (0x80-0x8F) is valid. Common combinations are named,
38/// but Custom(byte) accepts any bitmask value.
39#[derive(Clone, Copy, PartialEq, Eq)]
40pub struct Led(u8);
41
42impl Led {
43    pub fn new(byte: u8) -> Self {
44        Led(byte)
45    }
46
47    pub fn is_wait(&self) -> bool {
48        self.0 & 0x01 == 0x01
49    }
50
51    pub fn is_compose(&self) -> bool {
52        self.0 & 0x02 == 0x02
53    }
54
55    pub fn is_lock(&self) -> bool {
56        self.0 & 0x04 == 0x04
57    }
58
59    pub fn is_hold(&self) -> bool {
60        self.0 & 0x08 == 0x08
61    }
62
63    pub fn is_all(&self) -> bool {
64        self.0 & 0x0F == 0x0F
65    }
66}
67
68impl fmt::Debug for Led {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "Led({:02X}=", self.0)?;
71        let mut first = true;
72        for led in [
73            ("Wait", self.is_wait()),
74            ("Compose", self.is_compose()),
75            ("Lock", self.is_lock()),
76            ("Hold", self.is_hold()),
77        ] {
78            if led.1 {
79                if first {
80                    first = false;
81                } else {
82                    write!(f, "+")?;
83                }
84                write!(f, "{}", led.0)?;
85            }
86        }
87        write!(f, ")")?;
88        Ok(())
89    }
90}
91
92/// Key mode for a keyboard division
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum KeyMode {
95    /// Key down generates code once, no repeat
96    Down = 0x80,
97    /// Key down generates code, then repeats with LK_REPEAT (0xB4)
98    AutoDown = 0x82,
99    /// Illegal value, unused.
100    Illegal = 0x84,
101    /// Key down/up both generate codes (for modifier keys)
102    UpDown = 0x86,
103}
104
105/// Keyboard division (1-14, corresponding to groups of keys)
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub struct Division(pub u8);
108
109impl Division {
110    pub fn new(div: u8) -> Option<Self> {
111        if div <= 14 && div > 0 {
112            Some(Division(div))
113        } else {
114            None
115        }
116    }
117}
118
119/// Auto-repeat register (0-3)
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub struct AutoRepeatRegister(pub u8);
122
123impl AutoRepeatRegister {
124    pub fn new(reg: u8) -> Option<Self> {
125        if reg <= 3 {
126            Some(AutoRepeatRegister(reg))
127        } else {
128            None
129        }
130    }
131}
132
133/// Volume level (0 = max, 7 = min)
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub struct Volume(pub u8);
136
137impl Volume {
138    pub fn new(level: u8) -> Option<Self> {
139        if level <= 7 {
140            Some(Volume(level))
141        } else {
142            None
143        }
144    }
145
146    pub fn as_param_byte(self) -> u8 {
147        0x80 | (self.0 & 0x7)
148    }
149}
150
151/// Commands sent from the computer to the LK201 keyboard
152#[derive(Debug, Copy, Clone, PartialEq, Eq)]
153pub enum LK201Command {
154    // LED Control
155    /// Enable specified LED(s)
156    LedEnable(Led),
157    /// Disable specified LED(s)
158    LedDisable(Led),
159
160    // Key Click Control
161    /// Enable key clicks with specified volume
162    KeyClickEnable(Volume),
163    /// Enable key clicks for CTRL key
164    CtrlKeyClickEnable,
165    /// Disable all key clicks
166    KeyClickDisable,
167    /// Disable CTRL key clicks
168    CtrlKeyClickDisable,
169    /// Cause keyboard to generate a single click sound
170    SoundClick,
171
172    // Bell Control
173    /// Enable bell with specified volume
174    BellEnable(Volume),
175    /// Disable bell
176    BellDisable,
177    /// Ring the bell
178    RingBell,
179
180    // Key Mode Control
181    /// Set mode for a keyboard division
182    SetMode { mode: KeyMode, division: Division },
183    /// Set mode and associate with auto-repeat register
184    SetModeWithAutoRepeat {
185        mode: KeyMode,
186        division: Division,
187        register: AutoRepeatRegister,
188    },
189    /// Convert all LK_AUTODOWN divisions to LK_DOWN
190    RepeatToDown,
191    /// Enable auto-repeat on LK_AUTODOWN keys
192    EnableRepeat,
193    /// Disable auto-repeat on all keys
194    DisableRepeat,
195    /// Enable LK401 mode
196    EnableLK401,
197    /// Disable LK401 mode
198    DisableLK401,
199    /// Temporarily disable auto-repeat for currently pressed key
200    TempNoRepeat,
201
202    // Auto-repeat Control
203    /// Set auto-repeat parameters for a register
204    SetAutoRepeat {
205        register: AutoRepeatRegister,
206        /// Timeout before repeat starts (in 5ms increments, 1-126)
207        timeout: u8,
208        /// Repeat rate in Hz (12-125, but not 125)
209        rate: u8,
210    },
211
212    // Power-Up and Self Test
213    /// Initiate keyboard power-up sequence
214    PowerUp,
215    /// Request keyboard ID (2 bytes returned)
216    RequestId,
217    /// Restore keyboard to default state
218    SetDefaults,
219    /// Enter factory test mode
220    TestMode,
221    /// Exit factory test mode (0x80, also used as LK_PARAM bit)
222    TestExit,
223
224    // Keyboard Control
225    /// Suspend keyboard transmission, turn on LOCK LED
226    Inhibit,
227    /// Resume normal keyboard operation
228    Resume,
229
230    /// Unknown initial byte
231    Unknown(u8),
232    /// Unknown 2-byte command
233    Unknown2(u8, u8),
234    /// Unknown 3-byte command (e.g., 0xE9 xx xx - possibly division-specific repeat control)
235    Unknown3(u8, u8, u8),
236}
237
238impl LK201Command {
239    /// Returns the number of bytes this command occupies
240    pub fn len(&self) -> usize {
241        match self {
242            LK201Command::LedEnable(_) => 2,
243            LK201Command::LedDisable(_) => 2,
244            LK201Command::KeyClickEnable(_) => 2,
245            LK201Command::CtrlKeyClickEnable => 1,
246            LK201Command::KeyClickDisable => 1,
247            LK201Command::CtrlKeyClickDisable => 1,
248            LK201Command::SoundClick => 1,
249            LK201Command::BellEnable(_) => 2,
250            LK201Command::BellDisable => 1,
251            LK201Command::RingBell => 1,
252            LK201Command::SetMode { .. } => 1,
253            LK201Command::SetModeWithAutoRepeat { .. } => 2,
254            LK201Command::RepeatToDown => 1,
255            LK201Command::EnableRepeat => 1,
256            LK201Command::DisableRepeat => 1,
257            LK201Command::EnableLK401 => 1,
258            LK201Command::DisableLK401 => 1,
259            LK201Command::TempNoRepeat => 1,
260            LK201Command::SetAutoRepeat { .. } => 3,
261            LK201Command::PowerUp => 1,
262            LK201Command::RequestId => 1,
263            LK201Command::SetDefaults => 1,
264            LK201Command::TestMode => 1,
265            LK201Command::TestExit => 1,
266            LK201Command::Inhibit => 1,
267            LK201Command::Resume => 1,
268            LK201Command::Unknown(_) => 1,
269            LK201Command::Unknown2(_, _) => 2,
270            LK201Command::Unknown3(_, _, _) => 3,
271        }
272    }
273
274    /// Returns the response the keyboard should send for this command, if any.
275    ///
276    /// Based on the LK201 documentation and Linux kernel driver:
277    /// - Power-up commands return multi-byte sequences
278    /// - Mode-related commands return ModeChangeAck (0xBA)
279    /// - Test/Inhibit commands return specific acks
280    /// - Invalid commands return InputError (0xB6)
281    /// - Most other commands (LED, bell, click) have no response
282    pub fn response(&self) -> Option<LK201Response> {
283        Some(match self {
284            // Power-up and ID requests return multi-byte responses
285            LK201Command::PowerUp => LK201Response::PowerUpSelfTest {
286                keyboard_id_firmware: 0x01, // Standard LK201 firmware ID
287                keyboard_id_hardware: 0x00, // Hardware ID from jumpers
288                error: PowerUpError::NoError,
289                keycode: 0,
290            },
291            LK201Command::RequestId => LK201Response::KeyboardId {
292                firmware_id: 0x01, // Firmware version
293                hardware_id: 0x01, // 1 = LK201, 2 = LK401, 3 = LK443, 4 = LK421
294            },
295
296            // Mode change commands return ModeChangeAck (0xBA)
297            // "Upon successful receipt of the command, the LK201 responds with LK_MODECHG_ACK"
298            LK201Command::SetMode { .. } => LK201Response::ModeChangeAck,
299            LK201Command::SetModeWithAutoRepeat { .. } => LK201Response::ModeChangeAck,
300            LK201Command::TempNoRepeat => LK201Response::ModeChangeAck,
301            LK201Command::SetDefaults => LK201Response::ModeChangeAck,
302
303            // Special control commands with specific acks
304            LK201Command::TestMode => LK201Response::TestModeAck,
305            LK201Command::Inhibit => LK201Response::KeyboardLockAck,
306
307            // Invalid commands return InputError
308            LK201Command::Unknown(_) => LK201Response::InputError,
309            LK201Command::Unknown2(_, _) => LK201Response::InputError,
310            LK201Command::Unknown3(_, _, _) => LK201Response::InputError,
311
312            // All other commands (LED, bell, click, autorepeat rate, etc.) have no response
313            _ => return None,
314        })
315    }
316}
317
318impl TryFrom<&VecDeque<u8>> for LK201Command {
319    type Error = ();
320
321    fn try_from(value: &VecDeque<u8>) -> Result<Self, Self::Error> {
322        let Some(&byte0) = value.get(0) else {
323            return Err(());
324        };
325
326        match byte0 {
327            // LED Control
328            0x13 => {
329                let Some(&led_byte) = value.get(1) else {
330                    return Err(());
331                };
332                let led = Led::new(led_byte);
333                Ok(LK201Command::LedEnable(led))
334            }
335            0x11 => {
336                let Some(&led_byte) = value.get(1) else {
337                    return Err(());
338                };
339                let led = Led::new(led_byte);
340                Ok(LK201Command::LedDisable(led))
341            }
342
343            // Key Click Control
344            0x1B => {
345                let Some(&vol_byte) = value.get(1) else {
346                    return Err(());
347                };
348                let Some(volume) = Volume::new((vol_byte & 0x7) as u8) else {
349                    return Ok(LK201Command::Unknown2(0x1B, vol_byte));
350                };
351                Ok(LK201Command::KeyClickEnable(volume))
352            }
353            0xBB => Ok(LK201Command::CtrlKeyClickEnable),
354            0x99 => Ok(LK201Command::KeyClickDisable),
355            0xB9 => Ok(LK201Command::CtrlKeyClickDisable),
356            0x9F => Ok(LK201Command::SoundClick),
357            0x80 => Ok(LK201Command::TestExit),
358
359            // Bell Control
360            0x23 => {
361                let Some(&vol_byte) = value.get(1) else {
362                    return Err(());
363                };
364                let Some(volume) = Volume::new((vol_byte & 0x7) as u8) else {
365                    return Ok(LK201Command::Unknown2(0x23, vol_byte));
366                };
367                Ok(LK201Command::BellEnable(volume))
368            }
369            0xA1 => Ok(LK201Command::BellDisable),
370            0xA7 => Ok(LK201Command::RingBell),
371
372            // Autorepeat rate commands (0x78-0x7F range: bits 6-3 = 1111, bits 2-1 = register)
373            0x78..=0x7F if (byte0 >> 3) & 0xF == 0xF => {
374                let Some(&timeout) = value.get(1) else {
375                    return Err(());
376                };
377                let Some(&rate) = value.get(2) else {
378                    return Err(());
379                };
380                let Some(register) = AutoRepeatRegister::new((byte0 >> 1) & 0x3) else {
381                    return Ok(LK201Command::Unknown2(0x78, byte0));
382                };
383                Ok(LK201Command::SetAutoRepeat {
384                    register,
385                    timeout: timeout & 0x7F,
386                    rate: rate & 0x7F,
387                })
388            }
389
390            // Key Mode Control
391            // Check if this is a mode command by examining the structure
392            // Note: 0x80 (TestExit) has the same bit pattern as SetMode{division:0, mode:Down}
393            // It's only interpreted as TestExit when the keyboard is already in test mode
394            b if (b & 0x01) == 0 => {
395                // Bit 0 is 0, could be a mode command
396                let division_bits = (b >> 3) & 0xF;
397                let mode_bits = (b >> 1) & 0x3;
398                let has_param = (b & 0x80) == 0;
399
400                // Check if division is valid (0-14)
401                if division_bits <= 14 {
402                    let Some(division) = Division::new(division_bits) else {
403                        return Ok(LK201Command::Unknown(b));
404                    };
405                    let mode = match mode_bits {
406                        0b00 => KeyMode::Down,
407                        0b01 => KeyMode::AutoDown,
408                        0b11 => KeyMode::UpDown,
409                        _ => return Ok(LK201Command::Unknown(b)),
410                    };
411
412                    // Check PARAM bit (bit 7)
413                    if has_param {
414                        // PARAM = 0, parameter follows with autorepeat register
415                        let Some(&param_byte) = value.get(1) else {
416                            return Err(());
417                        };
418                        let Some(register) = AutoRepeatRegister::new(param_byte & 0x3) else {
419                            return Ok(LK201Command::Unknown2(b, param_byte));
420                        };
421                        return Ok(LK201Command::SetModeWithAutoRepeat {
422                            mode,
423                            division,
424                            register,
425                        });
426                    } else {
427                        // PARAM = 1, no parameter
428                        return Ok(LK201Command::SetMode { mode, division });
429                    }
430                }
431
432                // Not a valid mode command, fall through
433                Ok(LK201Command::Unknown(byte0))
434            }
435
436            // Other Commands
437            0xC1 => Ok(LK201Command::TempNoRepeat),
438            0xD9 => Ok(LK201Command::RepeatToDown),
439            0xE1 => Ok(LK201Command::DisableRepeat),
440            0xE3 => Ok(LK201Command::EnableRepeat),
441            0xE9 => Ok(LK201Command::EnableLK401),
442            0xEB => Ok(LK201Command::DisableLK401),
443
444            // Power-Up and Self Test
445            0xFD => Ok(LK201Command::PowerUp),
446            0xAB => Ok(LK201Command::RequestId),
447            0xD3 => Ok(LK201Command::SetDefaults),
448            0xCB => Ok(LK201Command::TestMode),
449
450            // Keyboard Control
451            0x8B => Ok(LK201Command::Resume),
452            0x89 => Ok(LK201Command::Inhibit),
453
454            // Unknown command
455            _ => Ok(LK201Command::Unknown(byte0)),
456        }
457    }
458}
459
460impl Into<Vec<u8>> for LK201Command {
461    fn into(self) -> Vec<u8> {
462        match self {
463            LK201Command::LedEnable(led) => vec![0x13, led.0],
464            LK201Command::LedDisable(led) => vec![0x11, led.0],
465            LK201Command::KeyClickEnable(vol) => vec![0x1B, vol.as_param_byte()],
466            LK201Command::CtrlKeyClickEnable => vec![0xBB],
467            LK201Command::KeyClickDisable => vec![0x99],
468            LK201Command::CtrlKeyClickDisable => vec![0xB9],
469            LK201Command::SoundClick => vec![0x9F],
470            LK201Command::BellEnable(vol) => vec![0x23, vol.as_param_byte()],
471            LK201Command::BellDisable => vec![0xA1],
472            LK201Command::RingBell => vec![0xA7],
473            LK201Command::SetMode { mode, division } => {
474                let mode_bits = match mode {
475                    KeyMode::Down => 0b00,
476                    KeyMode::AutoDown => 0b01,
477                    KeyMode::UpDown => 0b11,
478                    KeyMode::Illegal => 0b10,
479                };
480                vec![(division.0 << 3) | (mode_bits << 1) | 0x80]
481            }
482            LK201Command::SetModeWithAutoRepeat {
483                mode,
484                division,
485                register,
486            } => {
487                let mode_bits = match mode {
488                    KeyMode::Down => 0b00,
489                    KeyMode::AutoDown => 0b01,
490                    KeyMode::UpDown => 0b11,
491                    KeyMode::Illegal => 0b10,
492                };
493                vec![
494                    (division.0 << 3) | (mode_bits << 1),
495                    0x80 | (register.0 & 0x3),
496                ]
497            }
498            LK201Command::RepeatToDown => vec![0xD9],
499            LK201Command::EnableRepeat => vec![0xE3],
500            LK201Command::DisableRepeat => vec![0xE1],
501            LK201Command::EnableLK401 => vec![0xE9],
502            LK201Command::DisableLK401 => vec![0xEB],
503            LK201Command::TempNoRepeat => vec![0xC1],
504            LK201Command::SetAutoRepeat {
505                register,
506                timeout,
507                rate,
508            } => {
509                vec![
510                    0b0_1111_000 | (register.0 << 1),
511                    (timeout / 5) & 0x7F,
512                    rate & 0x7F | 0x80,
513                ]
514            }
515            LK201Command::PowerUp => vec![0xFD],
516            LK201Command::RequestId => vec![0xAB],
517            LK201Command::SetDefaults => vec![0xD3],
518            LK201Command::TestMode => vec![0xCB],
519            LK201Command::TestExit => vec![0x80],
520            LK201Command::Inhibit => vec![0x89],
521            LK201Command::Resume => vec![0x8B],
522            LK201Command::Unknown(b) => vec![b],
523            LK201Command::Unknown2(b1, b2) => vec![b1, b2],
524            LK201Command::Unknown3(b1, b2, b3) => vec![b1, b2, b3],
525        }
526    }
527}
528
529/// Special keycodes that have specific meanings
530pub mod keycodes {
531    /// Caps Lock key
532    pub const KEY_LOCK: u8 = 0xB0;
533    /// Shift key
534    pub const KEY_SHIFT: u8 = 0xAE;
535    /// Control key
536    pub const KEY_CTRL: u8 = 0xAF;
537    /// Compose key
538    pub const KEY_COMP: u8 = 0xB1;
539}
540
541/// Responses sent from the LK201 keyboard to the computer
542#[derive(Debug, Clone, PartialEq, Eq)]
543pub enum LK201Response {
544    // Power-up and ID
545    /// Power-up self test result (4 bytes: ID1, ID2, error, keycode)
546    PowerUpSelfTest {
547        keyboard_id_firmware: u8,
548        keyboard_id_hardware: u8,
549        error: PowerUpError,
550        keycode: u8,
551    },
552    /// Keyboard ID response (2 bytes)
553    KeyboardId { firmware_id: u8, hardware_id: u8 },
554
555    // Acknowledgments
556    /// Mode change acknowledged
557    ModeChangeAck,
558    /// Keyboard locked acknowledged
559    KeyboardLockAck,
560    /// Test mode acknowledged
561    TestModeAck,
562
563    // Errors
564    /// Input error (invalid command received)
565    InputError,
566    /// Output error (keystrokes lost during inhibit)
567    OutputError,
568
569    // Key Events
570    /// Key pressed (keycode 0x00-0xFF)
571    KeyDown(u8),
572    /// Key repeat indicator (sent repeatedly for held keys in AutoDown mode)
573    Repeat,
574    /// All keys released (sent when last key in UpDown mode is released)
575    AllUp,
576    /// Prefix indicating next byte is keycode for key already down
577    PrefixKeyDown(u8),
578}
579
580impl LK201Response {
581    /// Serialize this response to bytes for transmission
582    pub fn to_bytes(&self) -> Vec<u8> {
583        match self {
584            LK201Response::PowerUpSelfTest {
585                keyboard_id_firmware,
586                keyboard_id_hardware,
587                error,
588                keycode,
589            } => {
590                vec![
591                    *keyboard_id_firmware,
592                    *keyboard_id_hardware,
593                    (*error).into(),
594                    *keycode,
595                ]
596            }
597            LK201Response::KeyboardId {
598                firmware_id,
599                hardware_id,
600            } => {
601                vec![*firmware_id, *hardware_id]
602            }
603            LK201Response::ModeChangeAck => vec![0xBA],
604            LK201Response::TestModeAck => vec![0xB8],
605            LK201Response::KeyboardLockAck => vec![0xB7],
606            LK201Response::InputError => vec![0xB6],
607            LK201Response::OutputError => vec![0xB5],
608            LK201Response::KeyDown(keycode) => vec![*keycode],
609            LK201Response::Repeat => vec![0xB4],
610            LK201Response::AllUp => vec![0xB3],
611            LK201Response::PrefixKeyDown(keycode) => vec![0xB9, *keycode],
612        }
613    }
614}
615
616/// Keyboard type IDs (returned in byte 1 of KeyboardId response)
617#[derive(Debug, Clone, Copy, PartialEq, Eq)]
618pub enum KeyboardType {
619    /// LK201 keyboard
620    LK201 = 1,
621    /// LK401 keyboard (has ALT keys)
622    LK401 = 2,
623    /// LK443 keyboard
624    LK443 = 3,
625    /// LK421 keyboard
626    LK421 = 4,
627}
628
629impl KeyboardType {
630    pub fn from_id(id: u8) -> Option<Self> {
631        match id {
632            1 => Some(KeyboardType::LK201),
633            2 => Some(KeyboardType::LK401),
634            3 => Some(KeyboardType::LK443),
635            4 => Some(KeyboardType::LK421),
636            _ => None,
637        }
638    }
639}
640
641/// Power-up self test error codes
642#[derive(Debug, Clone, Copy, PartialEq, Eq)]
643pub enum PowerUpError {
644    /// No error detected
645    NoError,
646    /// Key was down during power-up
647    KeyDownError,
648    /// Keyboard failure on power-up test
649    PowerError,
650    /// Unknown error code
651    Unknown(u8),
652}
653
654impl From<u8> for PowerUpError {
655    fn from(byte: u8) -> Self {
656        match byte {
657            0x00 => PowerUpError::NoError,
658            0x3D => PowerUpError::KeyDownError,
659            0x3E => PowerUpError::PowerError,
660            other => PowerUpError::Unknown(other),
661        }
662    }
663}
664
665impl From<PowerUpError> for u8 {
666    fn from(error: PowerUpError) -> u8 {
667        match error {
668            PowerUpError::NoError => 0x00,
669            PowerUpError::KeyDownError => 0x3D,
670            PowerUpError::PowerError => 0x3E,
671            PowerUpError::Unknown(byte) => byte,
672        }
673    }
674}
675
676#[cfg(test)]
677mod tests {
678    use super::*;
679
680    #[track_caller]
681    fn test_parse(input: &[u8], expected: LK201Command) {
682        let queue = VecDeque::from_iter(input.iter().copied());
683        let command = LK201Command::try_from(&queue).unwrap();
684        assert_eq!(
685            command, expected,
686            "input: {:02X?}, expected: {:?}",
687            input, expected
688        );
689        assert_eq!(
690            input.len(),
691            command.len(),
692            "input: {:02X?}, expected: {:?}",
693            input,
694            expected
695        );
696    }
697
698    #[test]
699    fn test_mode_commands() {
700        // 0A 80: division 1, autodown, register 0
701        test_parse(
702            &[0x0A, 0x80],
703            LK201Command::SetModeWithAutoRepeat {
704                division: Division(1),
705                mode: KeyMode::AutoDown,
706                register: AutoRepeatRegister(0),
707            },
708        );
709
710        // 12 81: division 2, autodown, register 1
711        test_parse(
712            &[0x12, 0x81],
713            LK201Command::SetModeWithAutoRepeat {
714                division: Division(2),
715                mode: KeyMode::AutoDown,
716                register: AutoRepeatRegister(1),
717            },
718        );
719
720        // 72 82: division 14, autodown, register 2
721        test_parse(
722            &[0x72, 0x82],
723            LK201Command::SetModeWithAutoRepeat {
724                division: Division(14),
725                mode: KeyMode::AutoDown,
726                register: AutoRepeatRegister(2),
727            },
728        );
729
730        // A2: division 4, autodown, no register parameter
731        test_parse(
732            &[0xA2],
733            LK201Command::SetMode {
734                mode: KeyMode::AutoDown,
735                division: Division(4),
736            },
737        );
738    }
739
740    #[test]
741    fn test_autorepeat_commands() {
742        // 78 64 9E: register 0, timeout 0x64, rate 0x9E
743        test_parse(
744            &[0x78, 0x64, 0x9E],
745            LK201Command::SetAutoRepeat {
746                register: AutoRepeatRegister(0),
747                timeout: 0x64,
748                rate: 0x1E,
749            },
750        );
751
752        // 7A 64 9E: register 1, timeout 0x64, rate 0x9E
753        test_parse(
754            &[0x7A, 0x64, 0x9E],
755            LK201Command::SetAutoRepeat {
756                register: AutoRepeatRegister(1),
757                timeout: 0x64,
758                rate: 0x1E,
759            },
760        );
761
762        // 7C 64 9E: register 2, timeout 0x64, rate 0x9E
763        test_parse(
764            &[0x7C, 0x64, 0x9E],
765            LK201Command::SetAutoRepeat {
766                register: AutoRepeatRegister(2),
767                timeout: 0x64,
768                rate: 0x1E,
769            },
770        );
771    }
772
773    #[test]
774    fn test_led_commands() {
775        // LED enable and disable with named combinations
776        test_parse(&[0x13, 0x84], LK201Command::LedEnable(Led::new(0x84)));
777        test_parse(&[0x11, 0x88], LK201Command::LedDisable(Led::new(0x88)));
778        test_parse(&[0x13, 0x8F], LK201Command::LedEnable(Led::new(0x8F)));
779        test_parse(&[0x13, 0x81], LK201Command::LedEnable(Led::new(0x81)));
780
781        // LED bitmask combinations (any combination of bits 0-3 is valid)
782        test_parse(&[0x13, 0x80], LK201Command::LedEnable(Led::new(0x80))); // No LEDs
783        test_parse(&[0x13, 0x83], LK201Command::LedEnable(Led::new(0x83))); // Wait + Compose
784        test_parse(&[0x13, 0x85], LK201Command::LedEnable(Led::new(0x85))); // Wait + Lock
785        test_parse(&[0x13, 0x86], LK201Command::LedEnable(Led::new(0x86))); // Compose + Lock
786        test_parse(&[0x13, 0x8C], LK201Command::LedEnable(Led::new(0x8C))); // Lock + Hold
787        test_parse(&[0x11, 0x8D], LK201Command::LedDisable(Led::new(0x8D))); // Wait + Lock + Hold
788    }
789
790    #[test]
791    fn test_bell_and_click_commands() {
792        // Bell commands
793        test_parse(&[0x23, 0x80], LK201Command::BellEnable(Volume(0)));
794        test_parse(&[0xA1], LK201Command::BellDisable);
795        test_parse(&[0xA7], LK201Command::RingBell);
796
797        // Click commands
798        test_parse(&[0x1B, 0x80], LK201Command::KeyClickEnable(Volume(0)));
799        test_parse(&[0x99], LK201Command::KeyClickDisable);
800        test_parse(&[0xBB], LK201Command::CtrlKeyClickEnable);
801        test_parse(&[0xB9], LK201Command::CtrlKeyClickDisable);
802    }
803
804    #[test]
805    fn test_control_commands() {
806        test_parse(&[0xFD], LK201Command::PowerUp);
807        test_parse(&[0xAB], LK201Command::RequestId);
808        test_parse(&[0xE3], LK201Command::EnableRepeat);
809        test_parse(&[0xE1], LK201Command::DisableRepeat);
810        test_parse(&[0xD9], LK201Command::RepeatToDown);
811        test_parse(&[0xC1], LK201Command::TempNoRepeat);
812        test_parse(&[0xD3], LK201Command::SetDefaults);
813        test_parse(&[0xCB], LK201Command::TestMode);
814        test_parse(&[0x8B], LK201Command::Resume);
815        test_parse(&[0x89], LK201Command::Inhibit);
816        test_parse(&[0x80], LK201Command::TestExit);
817    }
818
819    #[test]
820    fn test_unknown_commands() {
821        // Test that invalid command bytes are parsed as Unknown
822        test_parse(&[0x0D], LK201Command::Unknown(0x0D));
823        test_parse(&[0xFF], LK201Command::Unknown(0xFF));
824        test_parse(&[0x01], LK201Command::Unknown(0x01));
825        test_parse(&[0x55], LK201Command::Unknown(0x55));
826
827        // Verify Unknown commands return InputError response
828        let cmd = LK201Command::Unknown(0x0D);
829        let resp = cmd.response().unwrap();
830        assert_eq!(resp, LK201Response::InputError);
831        assert_eq!(resp.to_bytes(), vec![0xB6]);
832    }
833
834    #[test]
835    fn test_command_responses() {
836        // Commands that return multi-byte responses
837        let cmd = LK201Command::PowerUp;
838        let resp = cmd.response().unwrap();
839        assert_eq!(resp.to_bytes(), vec![0x01, 0x00, 0x00, 0x00]);
840
841        let cmd = LK201Command::RequestId;
842        let resp = cmd.response().unwrap();
843        assert_eq!(resp.to_bytes(), vec![0x01, 0x01]);
844
845        // Mode commands return ModeChangeAck (0xBA)
846        let cmd = LK201Command::SetMode {
847            mode: KeyMode::AutoDown,
848            division: Division(1),
849        };
850        let resp = cmd.response().unwrap();
851        assert_eq!(resp.to_bytes(), vec![0xBA]);
852
853        let cmd = LK201Command::SetModeWithAutoRepeat {
854            mode: KeyMode::AutoDown,
855            division: Division(1),
856            register: AutoRepeatRegister(0),
857        };
858        let resp = cmd.response().unwrap();
859        assert_eq!(resp.to_bytes(), vec![0xBA]);
860
861        let resp = LK201Command::SetDefaults.response().unwrap();
862        assert_eq!(resp.to_bytes(), vec![0xBA]);
863
864        // Special acks
865        let cmd = LK201Command::TestMode;
866        let resp = cmd.response().unwrap();
867        assert_eq!(resp.to_bytes(), vec![0xB8]);
868
869        let cmd = LK201Command::Inhibit;
870        let resp = cmd.response().unwrap();
871        assert_eq!(resp.to_bytes(), vec![0xB7]);
872
873        // Error response
874        let cmd = LK201Command::Unknown(0xFF);
875        let resp = cmd.response().unwrap();
876        assert_eq!(resp.to_bytes(), vec![0xB6]);
877
878        // Commands with no response
879        assert!(LK201Command::LedEnable(Led::new(0x84)).response().is_none());
880        assert!(LK201Command::BellEnable(Volume(4)).response().is_none());
881        assert!(LK201Command::KeyClickDisable.response().is_none());
882        assert!(LK201Command::Resume.response().is_none());
883    }
884
885    #[test]
886    fn test_response_serialization() {
887        // Test all response types serialize correctly
888        let resp = LK201Response::ModeChangeAck;
889        assert_eq!(resp.to_bytes(), vec![0xBA]);
890
891        let resp = LK201Response::KeyboardLockAck;
892        assert_eq!(resp.to_bytes(), vec![0xB7]);
893
894        let resp = LK201Response::TestModeAck;
895        assert_eq!(resp.to_bytes(), vec![0xB8]);
896
897        let resp = LK201Response::InputError;
898        assert_eq!(resp.to_bytes(), vec![0xB6]);
899
900        let resp = LK201Response::OutputError;
901        assert_eq!(resp.to_bytes(), vec![0xB5]);
902
903        let resp = LK201Response::Repeat;
904        assert_eq!(resp.to_bytes(), vec![0xB4]);
905
906        let resp = LK201Response::AllUp;
907        assert_eq!(resp.to_bytes(), vec![0xB3]);
908
909        let resp = LK201Response::KeyDown(0x42);
910        assert_eq!(resp.to_bytes(), vec![0x42]);
911
912        let resp = LK201Response::PrefixKeyDown(0x42);
913        assert_eq!(resp.to_bytes(), vec![0xB9, 0x42]);
914    }
915
916    #[test]
917    fn test_full_sequence() {
918        // Test parsing a complete initialization sequence
919        // Division 1 (letters), autodown, register 0
920        test_parse(
921            &[0x0A, 0x80],
922            LK201Command::SetModeWithAutoRepeat {
923                division: Division(1),
924                mode: KeyMode::AutoDown,
925                register: AutoRepeatRegister(0),
926            },
927        );
928
929        // Division 3 (Delete key), autodown, register 0
930        test_parse(
931            &[0x1A, 0x80],
932            LK201Command::SetModeWithAutoRepeat {
933                division: Division(3),
934                mode: KeyMode::AutoDown,
935                register: AutoRepeatRegister(0),
936            },
937        );
938
939        // Division 7 (left/right arrows), autodown, register 0
940        test_parse(
941            &[0x3A, 0x80],
942            LK201Command::SetModeWithAutoRepeat {
943                division: Division(7),
944                mode: KeyMode::AutoDown,
945                register: AutoRepeatRegister(0),
946            },
947        );
948
949        // Division 8 (up/down arrows), autodown, register 1
950        test_parse(
951            &[0x42, 0x81],
952            LK201Command::SetModeWithAutoRepeat {
953                division: Division(8),
954                mode: KeyMode::AutoDown,
955                register: AutoRepeatRegister(1),
956            },
957        );
958
959        // Division 9 (E1-E6), autodown, register 2
960        test_parse(
961            &[0x4A, 0x82],
962            LK201Command::SetModeWithAutoRepeat {
963                division: Division(9),
964                mode: KeyMode::AutoDown,
965                register: AutoRepeatRegister(2),
966            },
967        );
968
969        // Division 11 (F6-F10), autodown, register 2
970        test_parse(
971            &[0x5A, 0x82],
972            LK201Command::SetModeWithAutoRepeat {
973                division: Division(11),
974                mode: KeyMode::AutoDown,
975                register: AutoRepeatRegister(2),
976            },
977        );
978
979        // Division 12 (F11-F14), autodown, register 2
980        test_parse(
981            &[0x62, 0x82],
982            LK201Command::SetModeWithAutoRepeat {
983                division: Division(12),
984                mode: KeyMode::AutoDown,
985                register: AutoRepeatRegister(2),
986            },
987        );
988
989        // Division 13 (Help, Do), autodown, register 2
990        test_parse(
991            &[0x6A, 0x82],
992            LK201Command::SetModeWithAutoRepeat {
993                division: Division(13),
994                mode: KeyMode::AutoDown,
995                register: AutoRepeatRegister(2),
996            },
997        );
998    }
999
1000    #[test]
1001    fn test_lk201() {
1002        test_parse(&[0xE9], LK201Command::EnableLK401);
1003        test_parse(&[0xEB], LK201Command::DisableLK401);
1004    }
1005}