-
-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Read one mouse packet byte per interrupt #953
base: theseus_main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
#![feature(abi_x86_interrupt)] | ||
|
||
use log::{error, warn}; | ||
use spin::Once; | ||
use spin::{Mutex, Once}; | ||
use mpmc::Queue; | ||
use event_types::Event; | ||
use x86_64::structures::idt::InterruptStackFrame; | ||
|
@@ -15,11 +15,51 @@ use ps2::{PS2Mouse, MousePacket}; | |
/// Because we perform the typical PIC remapping, the remapped IRQ vector number is 0x2C. | ||
const PS2_MOUSE_IRQ: u8 = interrupts::IRQ_BASE_OFFSET + 0xC; | ||
|
||
const PS2_MAX_MOUSE_BYTES: usize = 4; | ||
|
||
static MOUSE: Once<MouseInterruptParams> = Once::new(); | ||
|
||
/// Everything we need in [`ps2_mouse_handler`]. | ||
struct MouseInterruptParams { | ||
mouse: PS2Mouse<'static>, | ||
queue: Queue<Event>, | ||
packet_bytes: PacketBytes, | ||
} | ||
|
||
/// Somewhat like an array/vec mixture to allow pushing single bytes | ||
/// of a mouse packet per interrupt into an array. | ||
/// This can handle MouseId 0 (3 bytes) and 3, 4 (4 bytes). | ||
struct PacketBytes { | ||
len: usize, | ||
inner: Mutex<[u8; PS2_MAX_MOUSE_BYTES]>, | ||
// where to push the next element | ||
cursor: Mutex<usize>, | ||
Comment on lines
+34
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's no real benefit here to using two mutexes. I'd condense this into one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want to get even fancier, you can pack all of this data into an atomic u64 since you only need 5 bytes. That would be the fastest option of all. |
||
} | ||
|
||
impl PacketBytes { | ||
const fn new(len: usize) -> Self { | ||
Self { len, inner: Mutex::new([0; PS2_MAX_MOUSE_BYTES]), cursor: Mutex::new(0) } | ||
} | ||
// TODO: we can use a u32 once we switch to the bilge crate | ||
fn push(&self, value: u8) { | ||
let mut cursor = self.cursor.lock(); | ||
if *cursor < PS2_MAX_MOUSE_BYTES { | ||
self.inner.lock()[*cursor] = value; | ||
*cursor += 1; | ||
} | ||
} | ||
|
||
/// Return the packet bytes if they're filled | ||
fn filled_bytes(&self) -> Option<[u8; 4]> { | ||
let mut cursor = self.cursor.lock(); | ||
|
||
if *cursor == self.len { | ||
*cursor = 0; | ||
Some(*self.inner.lock()) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
/// Initialize the PS/2 mouse driver and register its interrupt handler. | ||
|
@@ -41,9 +81,13 @@ pub fn init(mut mouse: PS2Mouse<'static>, mouse_queue_producer: Queue<Event>) -> | |
"PS/2 mouse IRQ was already in use! Sharing IRQs is currently unsupported." | ||
})?; | ||
|
||
// Initialize the mouse packet bytes, which will be filled by 3-4 interrupts, | ||
// depending on the MouseId | ||
let packet_bytes = PacketBytes::new(mouse.packet_size()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here's where you'd specify the const generic parameter, either 3 or 4 depending upon mouse ID. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice idea, didn't consider const generics. |
||
|
||
// Final step: set the producer end of the mouse event queue. | ||
// Also add the mouse struct for access during interrupts. | ||
MOUSE.call_once(|| MouseInterruptParams { mouse, queue: mouse_queue_producer }); | ||
MOUSE.call_once(|| MouseInterruptParams { mouse, queue: mouse_queue_producer, packet_bytes }); | ||
Ok(()) | ||
} | ||
|
||
|
@@ -56,18 +100,20 @@ pub fn init(mut mouse: PS2Mouse<'static>, mouse_queue_producer: Queue<Event>) -> | |
/// | ||
/// In some cases (e.g. on device init), [the PS/2 controller can also send an interrupt](https://wiki.osdev.org/%228042%22_PS/2_Controller#Interrupts). | ||
extern "x86-interrupt" fn ps2_mouse_handler(_stack_frame: InterruptStackFrame) { | ||
if let Some(MouseInterruptParams { mouse, queue }) = MOUSE.get() { | ||
if let Some(MouseInterruptParams { mouse, queue, packet_bytes }) = MOUSE.get() { | ||
// using `while` here didn't interact well with the window manager and increases handler runtime | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried using a loop here and will try again, since some of the code changed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, I recall the previous code there. I phrased my comment as a question because legitimately not sure if the PS/2 mouse device actually allows for multiple bytes to be read per interrupt (i.e., does it do "batching" of multiple bytes into a single interrupt?); once we answer that then we can determine what the best choice is here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it does, I've seen some osdev wiki people use |
||
if mouse.is_output_buffer_full() { | ||
// NOTE: having read some more forum comments now, if this ever breaks on real hardware, | ||
// try to redesign this to only get one byte per interrupt instead of the 3-4 bytes we | ||
// currently get in read_mouse_packet and merge them afterwards | ||
let mouse_packet = mouse.read_mouse_packet(); | ||
|
||
if mouse_packet.always_one() != 1 { | ||
// this could signal a hardware error or a mouse which doesn't conform to the rule | ||
warn!("ps2_mouse_handler(): Discarding mouse data packet since its third bit should always be 1."); | ||
} else if let Err(e) = handle_mouse_input(mouse_packet, queue) { | ||
error!("ps2_mouse_handler(): {e:?}"); | ||
packet_bytes.push(mouse.read_packet_byte()); | ||
|
||
if let Some(bytes) = packet_bytes.filled_bytes() { | ||
Comment on lines
+106
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these two instructions acquire and release a total of 4 locks. You can likely combine their functionality together such that a "push" operation returns an Also, if you adopt a single-Mutex design (or better yet, the AtomicU64 design), that would reduce this overhead from 4 to 1 lock acquisitions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, thanks, exactly what I need to learn. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea np at all, this is just the kind of thing that only becomes apparent once you have another set of eyes on it. 😄 |
||
let mouse_packet = mouse.packet_from_bytes(bytes); | ||
|
||
if mouse_packet.always_one() != 1 { | ||
// this could signal a hardware error or a mouse which doesn't conform to the rule | ||
warn!("ps2_mouse_handler(): Discarding mouse data packet since its third bit should always be 1."); | ||
} else if let Err(e) = handle_mouse_input(mouse_packet, queue) { | ||
error!("ps2_mouse_handler(): {e:?}"); | ||
} | ||
} | ||
} | ||
} else { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if
len
is always a statically-known value, we can use const generics here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also
len
is a bit vague, give it a more precise name (and docs) that conveys it's an "expected byte count" or the "number of bytes in a complete mouse packet"