1313//! - OSDev: PS/2 Keyboard — https://wiki.osdev.org/PS/2_Keyboard
1414//! - OSDev: Keyboard / Scan Code Set 2 — https://wiki.osdev.org/Keyboard#Scan_Code_Set_2
1515
16+ use crate :: cmd_fifo:: Fifo as CmdFifo ;
1617use crate :: ps2:: Ps2Client ;
1718use crate :: ps2:: Ps2Controller ;
1819use core:: cell:: { Cell , RefCell } ;
@@ -86,6 +87,28 @@ impl CmdEntry {
8687 fn is_done ( & self ) -> bool {
8788 ( self . idx as usize ) >= ( self . len as usize )
8889 }
90+
91+ // helper to avoid duplicating the “turn a byte slice into a queued command” logic
92+ fn try_from_bytes ( seq : & [ u8 ] ) -> Option < Self > {
93+ if seq. is_empty ( ) || seq. len ( ) > CMD_MAX_LEN {
94+ return None ;
95+ }
96+ let mut e = CmdEntry :: empty ( ) ;
97+ e. bytes [ ..seq. len ( ) ] . copy_from_slice ( seq) ;
98+ e. len = seq. len ( ) as u8 ;
99+ Some ( e)
100+ }
101+ }
102+
103+ // this is needed so CmdEntry expects a FifoItem, and not a const default
104+ impl crate :: cmd_fifo:: FifoItem for CmdEntry {
105+ const EMPTY : Self = Self :: empty ( ) ;
106+ }
107+
108+ impl Default for CmdEntry {
109+ fn default ( ) -> Self {
110+ CmdEntry :: empty ( )
111+ }
89112}
90113
91114/// We capture bytes and will later add:
@@ -108,11 +131,8 @@ pub struct Keyboard<'a> {
108131 shift_r : Cell < bool > ,
109132 caps : Cell < bool > ,
110133
111- // here comes the engine
112- cmd_q : RefCell < [ CmdEntry ; CMDQ_LEN ] > ,
113- q_head : Cell < usize > , // write cursor
114- q_tail : Cell < usize > , // read cursor
115- q_count : Cell < usize > , // number of entries enqueued
134+ // command engine queue (ring FIFO)
135+ cmd_q : RefCell < CmdFifo < CmdEntry , CMDQ_LEN > > ,
116136
117137 in_flight : Cell < bool > , // waiting for ACK/RESEND to the last sent byte
118138 retries_left : Cell < u8 > , // remaining entries for the current byte
@@ -138,10 +158,7 @@ impl<'a> Keyboard<'a> {
138158 shift_r : Cell :: new ( false ) ,
139159 caps : Cell :: new ( false ) ,
140160
141- cmd_q : RefCell :: new ( [ CmdEntry :: empty ( ) ; CMDQ_LEN ] ) ,
142- q_head : Cell :: new ( 0 ) ,
143- q_tail : Cell :: new ( 0 ) ,
144- q_count : Cell :: new ( 0 ) ,
161+ cmd_q : RefCell :: new ( CmdFifo :: new ( ) ) ,
145162
146163 in_flight : Cell :: new ( false ) ,
147164 retries_left : Cell :: new ( 0 ) ,
@@ -285,122 +302,81 @@ impl<'a> Keyboard<'a> {
285302 /// No completion callback; failures are tracked internally (telemetry counters).
286303
287304 pub fn enqueue_command ( & self , seq : & [ u8 ] ) -> bool {
288- if seq. is_empty ( ) || seq. len ( ) > CMD_MAX_LEN || self . q_count . get ( ) >= CMDQ_LEN {
305+ let Some ( entry) = CmdEntry :: try_from_bytes ( seq) else {
306+ return false ;
307+ } ;
308+ let mut q = self . cmd_q . borrow_mut ( ) ;
309+ if q. is_full ( ) {
289310 return false ;
290311 }
291-
292- // Copy into the queue at head
293- let head = self . q_head . get ( ) ;
294- {
295- let mut q = self . cmd_q . borrow_mut ( ) ;
296- if let Some ( e) = q. get_mut ( head) {
297- e. bytes = [ 0 ; CMD_MAX_LEN ] ;
298- e. bytes [ ..seq. len ( ) ] . copy_from_slice ( seq) ;
299- e. len = seq. len ( ) as u8 ;
300- e. idx = 0 ;
301- } else {
302- debug_assert ! ( false , "cmd_q head OOB: head={}" , head) ;
303- return false ;
304- }
305- }
306- self . q_head . set ( ( head + 1 ) % CMDQ_LEN ) ;
307- self . q_count . set ( self . q_count . get ( ) + 1 ) ;
312+ let _ = q. push ( entry) ;
313+ drop ( q) ;
308314 self . drive_tx ( ) ;
309-
310315 true
311316 }
312317
313318 /// Try to transmit the next byte of the current command
314319 fn drive_tx ( & self ) {
315- if self . in_flight . get ( ) || self . q_count . get ( ) == 0 {
320+ if self . in_flight . get ( ) {
316321 return ;
317322 }
318323
319- let tail = self . q_tail . get ( ) ;
320- // Peek current entry at tail and the next byte to send
321- let ( byte_opt, done) ;
322- {
324+ // Peek current command
325+ let ( byte_opt, done_opt) = {
323326 let q = self . cmd_q . borrow ( ) ;
324- let e = match q. get ( tail) {
325- Some ( e) => e,
326- None => {
327- debug_assert ! ( false , "cmd_q tail OOB: tail={}" , tail) ;
328- return ;
329- }
330- } ;
331- if e. is_done ( ) {
332- byte_opt = None ;
333- done = true ;
334- } else {
335- byte_opt = Some ( e. bytes [ e. idx as usize ] ) ;
336- done = false ;
327+ match q. peek ( ) {
328+ None => ( None , None ) , // empty
329+ Some ( e) if e. is_done ( ) => ( None , Some ( true ) ) ,
330+ Some ( e) => ( Some ( e. bytes [ e. idx as usize ] ) , Some ( false ) ) ,
337331 }
338- }
332+ } ;
339333
340- if done {
341- // This shouldn't persist-pop and try again
342- self . pop_cmd ( ) ;
343- self . drive_tx ( ) ;
344- return ;
334+ match done_opt {
335+ None => return , // queue empty
336+ Some ( true ) => {
337+ // finished entry => pop and try next
338+ self . cmd_q . borrow_mut ( ) . pop ( ) ;
339+ self . drive_tx ( ) ;
340+ return ;
341+ }
342+ Some ( false ) => { }
345343 }
346344
347345 if let Some ( b) = byte_opt {
348- // Attempt to send. If the controller times out, do not mark inflight,
349- // so a later call may retry. We also don't advance idx here, only on ACK
350346 match self . ps2 . send_port1 ( b) {
351347 Ok ( ( ) ) => {
352348 self . in_flight . set ( true ) ;
353349 if self . retries_left . get ( ) == 0 {
354- // first attempt for this byte
355350 self . retries_left . set ( MAX_RETRIES ) ;
356351 }
357352 self . cmd_sent_bytes
358353 . set ( self . cmd_sent_bytes . get ( ) . wrapping_add ( 1 ) ) ;
359354 }
360-
361355 Err ( _e) => {
362- // Controller busy/timeout; count and let a later tick retry
363356 self . cmd_send_errors
364357 . set ( self . cmd_send_errors . get ( ) . wrapping_add ( 1 ) ) ;
365358 }
366359 }
367360 }
368361 }
369362
370- fn pop_cmd ( & self ) {
371- if self . q_count . get ( ) == 0 {
372- return ;
373- }
374- self . q_tail . set ( ( self . q_tail . get ( ) + 1 ) % CMDQ_LEN ) ;
375- self . q_count . set ( self . q_count . get ( ) - 1 ) ;
376- }
377-
378363 fn advance_idx_after_ack ( & self ) {
379- let tail = self . q_tail . get ( ) ;
380- // Increment idx of the current entry; if complete, pop
381364 let mut finished = false ;
382365 {
383366 let mut q = self . cmd_q . borrow_mut ( ) ;
384- let e = match q. get_mut ( tail) {
385- Some ( e) => e,
386- None => {
387- debug_assert ! ( false , "cmd_q tail OOB on ACK: tail={}" , tail) ;
388- return ;
367+ if let Some ( e) = q. peek_mut ( ) {
368+ if !e. is_done ( ) {
369+ e. idx = e. idx . saturating_add ( 1 ) ;
370+ }
371+ if e. is_done ( ) {
372+ finished = true ;
389373 }
390- } ;
391-
392- if !e. is_done ( ) {
393- e. idx = e. idx . saturating_add ( 1 ) ;
394374 }
395- if e . is_done ( ) {
396- finished = true ;
375+ if finished {
376+ q . pop ( ) ;
397377 }
398378 }
399- if finished {
400- self . pop_cmd ( ) ;
401- }
402-
403- // New byte will get a fresh budget retry on first send
379+ // New byte will get a fresh retry budget on first send
404380 self . retries_left . set ( 0 ) ;
405381 }
406382
@@ -492,7 +468,7 @@ impl Ps2Client for Keyboard<'_> {
492468 // Give up on this command
493469 self . cmd_drops . set ( self . cmd_drops . get ( ) . wrapping_add ( 1 ) ) ;
494470 self . in_flight . set ( false ) ;
495- self . pop_cmd ( ) ;
471+ self . cmd_q . borrow_mut ( ) . pop ( ) ;
496472 self . retries_left . set ( 0 ) ; // clear for the next new byte
497473 self . drive_tx ( ) ;
498474 }
0 commit comments