PS/2 Mouse Packet Alignment
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
PS/2 Mouse Packet Alignment
Hi,
I have written a PS/2 mouse driver, essentially this code translated to C++. The mouse is set up on IRQ 12 and streams packets. It works for a little bit, and then the cursor starts jumping to the edges of the screen.
My assumption is that this is a packet alignment issue.
It is my understanding that interrupts are not disabled by IRQ 12. This means that while the interrupt handler is processing, another byte from the mouse may come in and interrupt it, causing another interrupt handler, and so on. The accumulated information of the interrupt handlers within interrupt handlers would pile up on the stack. In the meantime, the mouse goes nuts (because the input IO and the counter are not atomic).
I could try using "sti" and "cli" as soon as the interrupt handler begins. It seems like that should help a lot, but it's still a race condition before "cli". What other solutions are there?
Thanks,
I have written a PS/2 mouse driver, essentially this code translated to C++. The mouse is set up on IRQ 12 and streams packets. It works for a little bit, and then the cursor starts jumping to the edges of the screen.
My assumption is that this is a packet alignment issue.
It is my understanding that interrupts are not disabled by IRQ 12. This means that while the interrupt handler is processing, another byte from the mouse may come in and interrupt it, causing another interrupt handler, and so on. The accumulated information of the interrupt handlers within interrupt handlers would pile up on the stack. In the meantime, the mouse goes nuts (because the input IO and the counter are not atomic).
I could try using "sti" and "cli" as soon as the interrupt handler begins. It seems like that should help a lot, but it's still a race condition before "cli". What other solutions are there?
Thanks,
Last edited by Geometrian on Wed Jan 31, 2024 5:39 pm, edited 1 time in total.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: PS/2 Mouse Packet Alignment
Then you should fix your IDT.It is my understanding that interrupts are not disabled by IRQ 12.
The same interrupt can only be fired again if you have sent an EOI.This means that while the interrupt handler is processing, another byte from the mouse may come in and interrupt it
In other words, fix your own code first.
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
Re: PS/2 Mouse Packet Alignment
. . . Or my understanding was wrong. I'm pretty certain that my IDT maps IRQs to ISRs well.Combuster wrote:Then you should fix your IDT.It is my understanding that interrupts are not disabled by IRQ 12.
What else could be causing a desynchronization?
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: PS/2 Mouse Packet Alignment
You sound like you didn't bother to check.
Hint: how many different types of IDT entries exist? what are the differences?
Hint: how many different types of IDT entries exist? what are the differences?
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
Re: PS/2 Mouse Packet Alignment
Three: interrupt gates, task gates, and trap gates.Combuster wrote:Hint: how many different types of IDT entries exist? what are the differences?
I don't know what the task gates are very well, though they're related to task switching. The difference between interrupt gates and trap gates is that interrupt gates do disable interrupts.
Therefore, to keep things simple, my IDT was loaded with 256, 32-bit, interrupt gates.
So this is bogus. I wrote the IDT code a while ago, so I didn't remember the intricacies, but I did pay attention when I wrote it.It is my understanding that interrupts are not disabled by IRQ 12.
Still, now I don't have a hypothesis as to what's happening . . .
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
Re: PS/2 Mouse Packet Alignment
Checking with the debugger, I was able to see that the IF of EFLAGS is properly cleared (automatically) and restored (with iret) by the ISR for interrupt 44 (mapped by IRQ 12), at least for the first time it happens.
Re: PS/2 Mouse Packet Alignment
Hi,
That way you might accept a few "out of sync" packets, but you'll get back in sync relatively quickly. Alternatively, if you do detect that the "must be set" bit is not set then you could reset the mouse, do the self-tests, etc again and re-initialise.
For the first problem (why it gets out of sync in the first place); I don't know specifically what the problem might be. However:
Cheers,
Brendan
There's at least 2 problems here:Geometrian wrote:Still, now I don't have a hypothesis as to what's happening . . .
- the mouse gets out of sync
- the mouse driver fails to detect or handle "out of sync"
Code: Select all
case 0:
mouse_byte[0]=inportb(0x60);
if( (mouse_byte[0] & 0x08) != 0) {
mouse_cycle++; // Only accept this as the first byte if the "must be 1" bit is set
}
break;
For the first problem (why it gets out of sync in the first place); I don't know specifically what the problem might be. However:
- There's no "separation of concerns". Instead of having one driver for the PS/2 controller and separate drivers for mouse, keyboard, bar-code scanner, etc; you have an "all in one hackfest".
- Initialisation (for both the PS/2 controller and for the mouse) is considerably inadequate - no self tests, no "get device ID", no "reset", etc (you don't even know if the second PS/2 port exists or works or if a mouse is actually plugged into it).
- I have no idea what you're doing with the keyboard; and because it's an "all in one hackfest" with inadequate initialisation, I can't rule out the possibility of your mouse code being effected by interactions with the keyboard.
- Because the PS/2 controller driver doesn't exist; it doesn't handle things like "BAT response" (e.g. if the mouse is unplugged then plugged back in) and "resend". This means that your mouse driver has to do these things, but it doesn't.
- You don't disable data reporting when you send commands to the mouse. This means that if timing is unlucky you might send a command to the mouse and read the first byte of a movement packet instead of reading the "ACK".
- You ignore all possible errors; including failing to check if any "ACK" is actually an "ACK", and ignoring any time-out that occurs.
- You've assumed the "set defaults" command resets the device. It doesn't (that's what the "reset" command is for). This may mean that if any extensions are enabled before your driver starts, you may end up setting the defaults for the previously enabled extension and not disabling the extension. Because you never bother with "get device ID" (and therefore don't know what protocol the mouse is using), this would go undetected and you may end up (e.g.) receiving three 4-byte packets and thinking you've received four 3-byte packets (where one looks right and the next 3 look dodgy).
- Because the overall quality of the code is lacking; I'd be very tempted to assume that you've taken a "quick and dirty; let's fail to do anything right and ignore USB" approach, and that the mouse may be a USB mouse and that the firmware's "PS/2 emulation" is a pile of broken puke and causing problems
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
Re: PS/2 Mouse Packet Alignment
Just as a quick test, I tried the modification you suggested and it didn't magic the problem away.Brendan wrote:There's at least 2 problems here:I'd fix the second problem first. . . .
- the mouse gets out of sync
- the mouse driver fails to detect or handle "out of sync"
Thanks. The driver was based off of the original link, which didn't match very well with the recommendations on the Wiki. I reread through the PS/2 page, the PS/2 Controller page, the Mouse Input page, and the PS/2 Mouse page. From these, I rewrote from scratch the driver for the PS/2 controller, which for now is only linked with the mouse (no keyboard). For initialization, it follows all but the first two steps of the PS/2 Controller page's recommendation. I don't know what to do about USB initialization (though you did mention that as a possible problem?), and for now I'm assuming that the PS/2 controller actually does exist, as I'm not writing for ancient hardware.However:
- <list of possible reasons, presumably drawing from the information that my code was based on the original linked>
The driver for the PS/2 controller works perfectly, as nearly as I can tell, without any devices. That is, it can set itself up, run self-tests, check both ports, and so on.
However, when adding a mouse, the mouse fails on resetting (which for my driver comprises sending 0xFF and then 0xF6 to restore defaults). I get the proper response of 0xAA after sending 0xFF, but it fails on sending 0xF6, since it returns a response of 0x00 exstead of the expected 0xFA (ACK). I checked the code repeatedly, but found no sign of error. In particular, all commands sent to the device on IO port 0x60 are prefixed with a 0xD4 command to the controller on IO port 0x64, as I understand they should, and all IO whatsoever is guarded by appropriate checks on the input and output flags of the controller's status byte.
I tried figuring out the problem, but I couldn't find reference to any PS/2 command ever returning zero in this way on the Wiki. I wasn't able to perturb the problem out of existence. When initializing the PS/2 controller, I also tried moving the enabling of interrupts from step 9 to a new step 11, which seemed more logical, especially since one of the pages points out that interrupts ought to be disabled while "reprogramming" a PS/2 device (as one is doing in step 10).
My source for the driver comes in four files, from which I have removed extraneous bits for clarity (e.g. extra #includes, most commented code). I would appreciate if someone could look them over? Pastebin:
controller_ps2.h, controller_ps2.cpp
mouse_ps2.h, mouse_ps2.cpp
The files are well-commented C++, and should be easy reading. I release them into the public domain.
Thanks,
Last edited by Geometrian on Wed Jan 31, 2024 5:39 pm, edited 1 time in total.
Re: PS/2 Mouse Packet Alignment
Hi,
The problem with USB is that when there's a USB keyboard, most computers will be setup for "PS/2 emulation". In this case the USB controller generates a special SMI (System Management Interrupt) and any access to the PS/2 controller will also generate an SMI, The SMI triggers the firmware's special/hidden SMM code (System Management Mode code) that emulates the PS/2 controller (by talking to the USB controller, etc). This is intended for backward compatibility (so that old software designed for PS/2 keyboard still works for a USB keyboard). However; this is a complex (error prone) arrangement to begin with and there's a very large number of "corner cases" that the firmware should worry about; and firmware developers don't care much about old software (and it's not meant to be used by new software). The end result is that "PS/2 emulation" is usually good enough to work for extremely simple things (e.g. getting scan-codes) but almost always fails as soon as you attempt anything more complicated (e.g. thorough PS/2 controller initialisation).
I suspect that you're expecting an ACK (from your "set defaults" command) and getting the ID byte 0x00 left over from the reset instead of the ACK you expect; and that you'd get the ACK after the 0x00.
Cheers,
Brendan
Hrm - in theory it should've helped (unless the mouse is using a different protocol than what you're expecting or something else is going wrong). There's a 50% chance that the bit 3 is set in the second byte and a 50% chance that bit 3 is set in the third byte, so it wouldn't always immediately become synchronised; but you would expect that the more packets that are sent the higher the probability of it getting back in sync would be.Geometrian wrote:Just as a quick test, I tried the modification you suggested and it didn't magic the problem away.Brendan wrote:There's at least 2 problems here:I'd fix the second problem first. . . .
- the mouse gets out of sync
- the mouse driver fails to detect or handle "out of sync"
The PS/2 controller existed from the very beginning and will exist in all old "PC compatible" computers. It's new computers (especially Apple's 80x86 machines) that might not have a PS/2 controller.Geometrian wrote:Thanks. The driver was based off of the original link, which didn't match very well with the recommendations on the Wiki. I reread through the PS/2 page, the PS/2 Controller page, the Mouse Input page, and the PS/2 Mouse page. From these, I rewrote from scratch the driver for the PS/2 controller, which for now is only linked with the mouse (no keyboard). For initialization, it follows all but the first two steps of the PS/2 Controller page's recommendation. I don't know what to do about USB initialization (though you did mention that as a possible problem?), and for now I'm assuming that the PS/2 controller actually does exist, as I'm not writing for ancient hardware.However:
- <list of possible reasons, presumably drawing from the information that my code was based on the original linked>
The problem with USB is that when there's a USB keyboard, most computers will be setup for "PS/2 emulation". In this case the USB controller generates a special SMI (System Management Interrupt) and any access to the PS/2 controller will also generate an SMI, The SMI triggers the firmware's special/hidden SMM code (System Management Mode code) that emulates the PS/2 controller (by talking to the USB controller, etc). This is intended for backward compatibility (so that old software designed for PS/2 keyboard still works for a USB keyboard). However; this is a complex (error prone) arrangement to begin with and there's a very large number of "corner cases" that the firmware should worry about; and firmware developers don't care much about old software (and it's not meant to be used by new software). The end result is that "PS/2 emulation" is usually good enough to work for extremely simple things (e.g. getting scan-codes) but almost always fails as soon as you attempt anything more complicated (e.g. thorough PS/2 controller initialisation).
When you send "reset" (0xFF) some devices (including "generic mouse") do their BAT and send 0xAA (test passed) and then send their "device ID". For a generic mouse the ID is 0x00 (but for other types of devices it can be multiple bytes and there's no real way to determine how many the device might send).Geometrian wrote:The driver for the PS/2 controller works perfectly, as nearly as I can tell, without any devices. That is, it can set itself up, run self-tests, check both ports, and so on.
However, when adding a mouse, the mouse fails on resetting (which for my driver comprises sending 0xFF and then 0xF6 to restore defaults). I get the proper response of 0xAA after sending 0xFF, but it fails on sending 0xF6, since it returns a response of 0x00 exstead of the expected 0xFA (ACK).
I suspect that you're expecting an ACK (from your "set defaults" command) and getting the ID byte 0x00 left over from the reset instead of the ACK you expect; and that you'd get the ACK after the 0x00.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
Re: PS/2 Mouse Packet Alignment
As soon as the problem occurred, I wasn't really trying to move the mouse, which should have gotten it back in sync by the next packet. Meh. That code was suboptimal anyway.Brendan wrote:Hrm - in theory it should've helped (unless the mouse is using a different protocol than what you're expecting or something else is going wrong). There's a 50% chance that the bit 3 is set in the second byte and a 50% chance that bit 3 is set in the third byte, so it wouldn't always immediately become synchronised; but you would expect that the more packets that are sent the higher the probability of it getting back in sync would be.
Oh, interesting! I suppose that makes more sense--gotta get rid of older cruft eventually.Brendan wrote:The PS/2 controller existed from the very beginning and will exist in all old "PC compatible" computers. It's new computers (especially Apple's 80x86 machines) that might not have a PS/2 controller.
Noted. Still, I have next to nothing implemented, and I don't feel like I'm really close to getting a USB driver working soon. It would be best right now if I could rely on the emulation.The problem with USB is that when there's a USB keyboard, most computers will be setup for "PS/2 emulation". In this case the USB controller generates a special SMI (System Management Interrupt) and any access to the PS/2 controller will also generate an SMI, The SMI triggers the firmware's special/hidden SMM code (System Management Mode code) that emulates the PS/2 controller (by talking to the USB controller, etc). This is intended for backward compatibility (so that old software designed for PS/2 keyboard still works for a USB keyboard). However; this is a complex (error prone) arrangement to begin with and there's a very large number of "corner cases" that the firmware should worry about; and firmware developers don't care much about old software (and it's not meant to be used by new software). The end result is that "PS/2 emulation" is usually good enough to work for extremely simple things (e.g. getting scan-codes) but almost always fails as soon as you attempt anything more complicated (e.g. thorough PS/2 controller initialisation).
A quick experiment confirms that an ACK does indeed follow the 0x00. If this is indeed acceptable behavior for a mouse, then the wording on the Mouse page here is unclear:When you send "reset" (0xFF) some devices (including "generic mouse") do their BAT and send 0xAA (test passed) and then send their "device ID". For a generic mouse the ID is 0x00 (but for other types of devices it can be multiple bytes and there's no real way to determine how many the device might send).
I suspect that you're expecting an ACK (from your "set defaults" command) and getting the ID byte 0x00 left over from the reset instead of the ACK you expect; and that you'd get the ACK after the 0x00.
I took this to mean that some sequence of bytes is terminated by 0xAA, not that 0xAA must be contained in those bytes, not necessarily last.Wiki wrote:The mouse probably sends ACK (0xFA) plus several more bytes, then resets itself, and always sends 0xAA.
My algorithm was to catch the 0xAA (lines 23-27), but that doesn't work. Since there's no way to know what the last byte is, I guess just wait with a timeout until it's done?
If all that's true, give me an ACK and I'll update the Wiki
Re: PS/2 Mouse Packet Alignment
Hi,
For later "MF2" keyboards (as far as I can tell) it's the same; except that they're meant to send 0xFD instead of 0xFC for "some tests failed". In both of these cases there is no "keyboard ID" afterwards, and you do have to issue a "read ID" (0xF2) command to determine the type of device.
For "generic PS/2 mouse" I'd expect that all of the above should be the same; except that the mouse does send "device ID" (0x00) after sending "all tests passed" (0xAA). Note: I'd expect that if the mouse returns "some tests failed" (0xFC) it won't send its device ID. For all other types of PS/2 mouse (e.g. 3-button with scroll wheel, and 5-button "Intellimouse") the reset puts them into "generic PS/2 mouse emulation mode" (all extension/s disabled) and therefore they should respond the same as "generic PS/2 mouse" would.
For remaining PS/2 device types (bar code scanners, touch-pads, touch-screens, etc) I'm not too sure. However; I'd expect that some devices (e.g. bar code scanner) behave like a keyboard and others (e.g. touch-pad, touch-screen) behave like a mouse. Note: I have looked at a "Synaptics TouchPad Interfacing Guide" which suggests that this device pretends to be a "generic PS/2 mouse" (and does send "device ID" (0x00) after sending "all tests passed" (0xAA) ) until after you enable its "special" extensions.
Now; originally "PC compatible" hardware was bad and had no sane way to identify common devices. It wasn't until later that hardware manufacturers realised how important it is for software to be able to determine the type/s of hardware installed; and there was a major push towards allowing this to happen. This started with EISA and "Plug and Play" (which tried to retro-fit device identification to existing things like ISA, serial port devices and parallel port devices), and has continued since (e.g. with PCI and USB specifications). This means that today, for almost all hardware, you can find out what the hardware is and then start suitable drivers for that hardware (which is obviously a lot better than expecting the user to figure out the mess and use things like "config.sys" to tell software what hardware to expect). The problem is trying to apply this "find out what it is then start a suitable driver" model to ancient hardware that wasn't designed for it; like PS/2 and ISA.
For PS/2 devices; the "least worst" approach may be to do the reset and BAT stuff; then ignore any number of "device ID" bytes the device might send; then send the "read ID" command (as this removes differences between "keyboard like" and "mouse like" during the device identification phase). Once you've got the "device ID" you'd still have to do further qualification to determine (e.g.) if it's a generic mouse, a mouse with scroll wheel, a 5-button with scroll wheel mouse, a touch-pad, a touch-screen, etc (e.g. by attempting to enable special extensions/modes and determining if they exist or not). On one hand you'd want to avoid having this "further qualification" mess cluttering up your nice clean PS/2 controller driver. On the other hand; to fully support a modern "find out what device it is then start suitable driver" model you'd have to have the "further qualification" mess cluttering up your PS/2 controller driver.
Cheers,
Brendan
Let's start with AT keyboards. For these, the host sends "reset" (0xFF) and the device responds with either "resend" (0xFE) and does nothing else; or disables itself and responds with "ACK" (0xFA) and begins waiting for the host to either accept its "ACK" or send another command (which I assume would cause the reset to be cancelled). The host accepts the "ACK" by raising the clock and data lines for a minimum of 500 ms; and the device begins its self tests when it sees this happen. The self tests take between 300 ms and 500 ms (and all keyboard LEDs should be lit for this time). When the device has finished its self tests it sends a "BAT status", which is either "all tests passed" (0xAA) or "some tests failed" (0xFC), but any other undefined values are meant to be treated as "some tests failed" too.Geometrian wrote:My algorithm was to catch the 0xAA (lines 23-27), but that doesn't work. Since there's no way to know what the last byte is, I guess just wait with a timeout until it's done?
If all that's true, give me an ACK and I'll update the Wiki
For later "MF2" keyboards (as far as I can tell) it's the same; except that they're meant to send 0xFD instead of 0xFC for "some tests failed". In both of these cases there is no "keyboard ID" afterwards, and you do have to issue a "read ID" (0xF2) command to determine the type of device.
For "generic PS/2 mouse" I'd expect that all of the above should be the same; except that the mouse does send "device ID" (0x00) after sending "all tests passed" (0xAA). Note: I'd expect that if the mouse returns "some tests failed" (0xFC) it won't send its device ID. For all other types of PS/2 mouse (e.g. 3-button with scroll wheel, and 5-button "Intellimouse") the reset puts them into "generic PS/2 mouse emulation mode" (all extension/s disabled) and therefore they should respond the same as "generic PS/2 mouse" would.
For remaining PS/2 device types (bar code scanners, touch-pads, touch-screens, etc) I'm not too sure. However; I'd expect that some devices (e.g. bar code scanner) behave like a keyboard and others (e.g. touch-pad, touch-screen) behave like a mouse. Note: I have looked at a "Synaptics TouchPad Interfacing Guide" which suggests that this device pretends to be a "generic PS/2 mouse" (and does send "device ID" (0x00) after sending "all tests passed" (0xAA) ) until after you enable its "special" extensions.
Now; originally "PC compatible" hardware was bad and had no sane way to identify common devices. It wasn't until later that hardware manufacturers realised how important it is for software to be able to determine the type/s of hardware installed; and there was a major push towards allowing this to happen. This started with EISA and "Plug and Play" (which tried to retro-fit device identification to existing things like ISA, serial port devices and parallel port devices), and has continued since (e.g. with PCI and USB specifications). This means that today, for almost all hardware, you can find out what the hardware is and then start suitable drivers for that hardware (which is obviously a lot better than expecting the user to figure out the mess and use things like "config.sys" to tell software what hardware to expect). The problem is trying to apply this "find out what it is then start a suitable driver" model to ancient hardware that wasn't designed for it; like PS/2 and ISA.
For PS/2 devices; the "least worst" approach may be to do the reset and BAT stuff; then ignore any number of "device ID" bytes the device might send; then send the "read ID" command (as this removes differences between "keyboard like" and "mouse like" during the device identification phase). Once you've got the "device ID" you'd still have to do further qualification to determine (e.g.) if it's a generic mouse, a mouse with scroll wheel, a 5-button with scroll wheel mouse, a touch-pad, a touch-screen, etc (e.g. by attempting to enable special extensions/modes and determining if they exist or not). On one hand you'd want to avoid having this "further qualification" mess cluttering up your nice clean PS/2 controller driver. On the other hand; to fully support a modern "find out what device it is then start suitable driver" model you'd have to have the "further qualification" mess cluttering up your PS/2 controller driver.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 77
- Joined: Tue Nov 20, 2012 4:45 pm
- Contact:
Re: PS/2 Mouse Packet Alignment
Again, a lot of this isn't listed on the Wiki page. In particular, the ACK response is not listed as a possibility. I am under the impression that AT keyboards are obsolete. Is this the reason?Brendan wrote:Let's start with AT keyboards. For these, the host sends "reset" (0xFF) and the device responds with either "resend" (0xFE) and does nothing else; or disables itself and responds with "ACK" (0xFA) and begins waiting for the host to either accept its "ACK" or send another command (which I assume would cause the reset to be cancelled). The host accepts the "ACK" by raising the clock and data lines for a minimum of 500 ms; and the device begins its self tests when it sees this happen. The self tests take between 300 ms and 500 ms (and all keyboard LEDs should be lit for this time). When the device has finished its self tests it sends a "BAT status", which is either "all tests passed" (0xAA) or "some tests failed" (0xFC), but any other undefined values are meant to be treated as "some tests failed" too.
Brendan wrote:For "generic PS/2 mouse" I'd expect that all of the above should be the same; except that the mouse does send "device ID" (0x00) after sending "all tests passed" (0xAA). Note: I'd expect that if the mouse returns "some tests failed" (0xFC) it won't send its device ID. For all other types of PS/2 mouse (e.g. 3-button with scroll wheel, and 5-button "Intellimouse") the reset puts them into "generic PS/2 mouse emulation mode" (all extension/s disabled) and therefore they should respond the same as "generic PS/2 mouse" would.
So, all PS/2 / PS/2-emulating mice return a single device ID byte after 0xAA (the byte stream concludes with 0xAA <id byte>)? If this is the case, one could simply wait for 0xAA and then get the device id right afterward. You say later "any number of \"device ID\" bytes the device might send"--I'm assuming that applies to devices in general, not mice specifically?Geometrian wrote:My[ original] algorithm was to catch the 0xAA (lines 23-27), but that doesn't work. Since there's no way to know what the last byte is, I guess just wait with a timeout until it's done?
Brendan wrote:For later "MF2" keyboards (as far as I can tell) it's the same; except that they're meant to send 0xFD instead of 0xFC for "some tests failed". In both of these cases there is no "keyboard ID" afterwards, and you do have to issue a "read ID" (0xF2) command to determine the type of device.
This was exactly my plan. However:Brendan wrote:For PS/2 devices; the "least worst" approach may be to do the reset and BAT stuff; then ignore any number of "device ID" bytes the device might send; then send the "read ID" command (as this removes differences between "keyboard like" and "mouse like" during the device identification phase).
I feel like this isn't a lost cause from a design perspective.Brendan wrote:Once you've got the "device ID" you'd still have to do further qualification to determine (e.g.) if it's a generic mouse, a mouse with scroll wheel, a 5-button with scroll wheel mouse, a touch-pad, a touch-screen, etc (e.g. by attempting to enable special extensions/modes and determining if they exist or not). On one hand you'd want to avoid having this "further qualification" mess cluttering up your nice clean PS/2 controller driver. On the other hand; to fully support a modern "find out what device it is then start suitable driver" model you'd have to have the "further qualification" mess cluttering up your PS/2 controller driver.
My impression is that the PS/2 controller ("keyboard controller") is intertwined with up to two devices, using two different IO ports for reading and writing from/to all three. It's needlessly complicated, and like much of the x86 architecture it relates to, it hasn't aged well. Control can still be naturally separated, though. I encapsulate the PS/2 controller proper in its own class, and have classes for keyboard and mouse that share a common base class.
I currently have the controller assume that the device on the first port is a keyboard and that the device on the second port is a mouse, but a better approach would be to have the device base class support a static .get_new_device(unsigned short port) method, which resets the device on the given port, gets the device's device id in the manner you describe above, allocates an instance of the proper device subclass based on that id, and finally returns the new device as a base class pointer to the controller.
All this assumes a language like C++, but the same semantic separation could be achieved in any language.
This way, the PS/2 controller doesn't need to care what kind of device it has on either port. Since it has a pointer to a base representation of both devices, it would still be able to interact with them as generic devices, which should be all it needs to ever do (in my kernel, each device handles setting itself up with the kernel's API, so the PS/2 controller doesn't need to bother with routing commands (e.g. ISRs) at all). Additionally, the PS/2 controller doesn't need to figure out which kind of device it has on each port--the device base class does it. It makes sense that code for figuring out what specific type of device to change the generic device into should go in the generic device base class common to all devices.
Last edited by Geometrian on Wed Jan 31, 2024 5:39 pm, edited 1 time in total.
Re: PS/2 Mouse Packet Alignment
Hi,
A PS/2 device is a device that uses "PS/2 serial communication" that plugs into a PS/2 port; in the same way that a USB device is a device that uses "USB serial communication" that plugs into a USB port; or an RS232 device is a device that uses "RS232 serial communication" that plugs into an RS232 port.
There can be an infinite number of different types of PS/2 device that plug into a PS/2 port. For example; if someone wanted to invent a "weather station" PS/2 device (that reports temperature, wind speed and wind direction), or a "GPS" PS/2 device (that reports longitude and latitude), or any other kind of device that plugs into a PS/2 port; they can. In the same way, there can be an infinite number of different types of USB device that plug into a USB port; and there can be an infinite number of different types of RS232 device that plug into an RS232 port. For example; if someone wanted to invent a "weather station" USB device, a "GPS" USB device, any other type of USB device, a "weather station" RS232 device, a "GPS" RS232 device, or any other type of RS232 device; they can.
Also note that for PS/2 devices it's possible for the user to (e.g.) boot the computer with 2 keyboards plugged in; then unplug both the keyboards and plug in a mouse and a touch-screen; then (later on) unplug them and plug in a "PS/2 weather station" and a "PS/2 GPS". This is less common for PS/2; but it's probably a good idea to get used to the idea of "hot-plug" (and design your kernel to support "hot-plug devices") as it's extremely common for other types of devices (especially USB).
Cheers,
Brendan
AT keyboards are obsolete (replaced by MF2 keyboards, then replaced by USB) but it still should be mentioned in the wiki as MF2 behaves the same (and other PS/2 devices are very similar).Geometrian wrote:Again, a lot of this isn't listed on the Wiki page. In particular, the ACK response is not listed as a possibility. I am under the impression that AT keyboards are obsolete. Is this the reason?Brendan wrote:Let's start with AT keyboards. For these, the host sends "reset" (0xFF) and the device responds with either "resend" (0xFE) and does nothing else; or disables itself and responds with "ACK" (0xFA) and begins waiting for the host to either accept its "ACK" or send another command (which I assume would cause the reset to be cancelled). The host accepts the "ACK" by raising the clock and data lines for a minimum of 500 ms; and the device begins its self tests when it sees this happen. The self tests take between 300 ms and 500 ms (and all keyboard LEDs should be lit for this time). When the device has finished its self tests it sends a "BAT status", which is either "all tests passed" (0xAA) or "some tests failed" (0xFC), but any other undefined values are meant to be treated as "some tests failed" too.
For the "read ID" command, the device ID can be none, one or 2 bytes (just for standard types of keyboard and mouse), and there's no guarantee that less common devices couldn't send 3 or more bytes. If some devices send their device ID after "BAT status", and if some devices have 2 (or possibly more) bytes of device ID; then it'd be logical to assume that some devices may send 2 (or possibly more) bytes of device ID after "BAT status" (or at least it would make sense to write code that can handle it correctly just in case).Geometrian wrote:Brendan wrote:For "generic PS/2 mouse" I'd expect that all of the above should be the same; except that the mouse does send "device ID" (0x00) after sending "all tests passed" (0xAA). Note: I'd expect that if the mouse returns "some tests failed" (0xFC) it won't send its device ID. For all other types of PS/2 mouse (e.g. 3-button with scroll wheel, and 5-button "Intellimouse") the reset puts them into "generic PS/2 mouse emulation mode" (all extension/s disabled) and therefore they should respond the same as "generic PS/2 mouse" would.So, all PS/2 / PS/2-emulating mice return a single device ID byte after 0xAA (the byte stream concludes with 0xAA <id byte>)? If this is the case, one could simply wait for 0xAA and then get the device id right afterward. You say later "any number of \"device ID\" bytes the device might send"--I'm assuming that applies to devices in general, not mice specifically?Geometrian wrote:My[ original] algorithm was to catch the 0xAA (lines 23-27), but that doesn't work. Since there's no way to know what the last byte is, I guess just wait with a timeout until it's done?
For an example; (as far as I know) all synaptics touch-pads start in "generic mouse mode" and report a device ID of 0x00; but they all support the same additional "get model ID" command (that no mouse supports). With the "further qualification mess" in the PS/2 controller driver, you could try the synaptics "get model ID" command and determine if it's a synaptics touch-pad; list the device as "synaptics touch-pad model 1234-5678" in your device manager (even when there's no driver at all); and then try to start a synaptics touch-pad driver for the specific model of touch-pad (and if there is no driver for the specific model, try to start a generic synaptics touch-pad driver, then fall back to a generic mouse driver). Alternatively; you could have a single driver that handles generic 3-button mouse plus all the other types of PS/2 mouse, plus all types of touch-pad, plus all types of touch-screen; that has the same "further qualification mess" but also has to handle everything from mouse scaling to touch-pad gestures to touch-screen calibration.Geometrian wrote:Brendan wrote:For later "MF2" keyboards (as far as I can tell) it's the same; except that they're meant to send 0xFD instead of 0xFC for "some tests failed". In both of these cases there is no "keyboard ID" afterwards, and you do have to issue a "read ID" (0xF2) command to determine the type of device.This was exactly my plan. However:Brendan wrote:For PS/2 devices; the "least worst" approach may be to do the reset and BAT stuff; then ignore any number of "device ID" bytes the device might send; then send the "read ID" command (as this removes differences between "keyboard like" and "mouse like" during the device identification phase).I feel like this isn't a lost cause from a design perspective.Brendan wrote:Once you've got the "device ID" you'd still have to do further qualification to determine (e.g.) if it's a generic mouse, a mouse with scroll wheel, a 5-button with scroll wheel mouse, a touch-pad, a touch-screen, etc (e.g. by attempting to enable special extensions/modes and determining if they exist or not). On one hand you'd want to avoid having this "further qualification" mess cluttering up your nice clean PS/2 controller driver. On the other hand; to fully support a modern "find out what device it is then start suitable driver" model you'd have to have the "further qualification" mess cluttering up your PS/2 controller driver.
The PS/2 controller is a piece of hardware that controls one or 2 "PS/2 ports"; in the same way that a USB controller is a piece of hardware that controls one or more "USB ports"; or an RS232 serial port controller is a piece of hardware that controls an RS232 serial port.Geometrian wrote:My impression is that the PS/2 controller ("keyboard controller") is intertwined with up to two devices, using two different IO ports for reading and writing from/to all three. It's needlessly complicated, and like much of the x86 architecture it relates to, it hasn't aged well.
A PS/2 device is a device that uses "PS/2 serial communication" that plugs into a PS/2 port; in the same way that a USB device is a device that uses "USB serial communication" that plugs into a USB port; or an RS232 device is a device that uses "RS232 serial communication" that plugs into an RS232 port.
There can be an infinite number of different types of PS/2 device that plug into a PS/2 port. For example; if someone wanted to invent a "weather station" PS/2 device (that reports temperature, wind speed and wind direction), or a "GPS" PS/2 device (that reports longitude and latitude), or any other kind of device that plugs into a PS/2 port; they can. In the same way, there can be an infinite number of different types of USB device that plug into a USB port; and there can be an infinite number of different types of RS232 device that plug into an RS232 port. For example; if someone wanted to invent a "weather station" USB device, a "GPS" USB device, any other type of USB device, a "weather station" RS232 device, a "GPS" RS232 device, or any other type of RS232 device; they can.
For a basic monolithic kernel (where everything you can imagine is built into a single binary) using classes would be enough. Modern monolithic kernels (and micro-kernels too, in a different way) allow device drivers to be implemented as separate modules, as this increases flexibility/extensibility. For example; you can have generic CD containing a relatively small kernel that dynamically loads the drivers it actually needs during boot (with no RAM wasted for unneeded drivers, even though you can't know what will be needed when the kernel was compiled). For another example; when your OS is booted there might not be a driver for "ACME PS/2 touch screen" and you might end up using a "generic PS/2 mouse driver" instead; but 2 weeks later the user might download and install a suitable driver for "ACME PS/2 touch screen" and tell the OS to unload the "generic PS/2 mouse driver" and start the "ACME PS/2 touch screen driver"; all without rebooting or interrupting service. Using classes isn't enough for this - you'd have to have some well defined interface/s that modules use; and these interfaces should be stable (e.g. so that everything doesn't break each time you release a different version of the kernel) and therefore should be defined in some sort of standard/specification (where the modules and your kernel comply with your specification/s).Geometrian wrote:Control can still be naturally separated, though. I encapsulate the PS/2 controller proper in its own class, and have classes for keyboard and mouse that share a common base class.
Also note that for PS/2 devices it's possible for the user to (e.g.) boot the computer with 2 keyboards plugged in; then unplug both the keyboards and plug in a mouse and a touch-screen; then (later on) unplug them and plug in a "PS/2 weather station" and a "PS/2 GPS". This is less common for PS/2; but it's probably a good idea to get used to the idea of "hot-plug" (and design your kernel to support "hot-plug devices") as it's extremely common for other types of devices (especially USB).
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: PS/2 Mouse Packet Alignment
Hi,
Just something I forgot to mention...
For hardware; devices are connected to each other. For example, you might have a PS/2 keyboard connected to a USB to PS/2 adapter, connected to a USB hub, connected to a USB controller, connected to a PCI bus. These connections describe a hierarchical tree; where each device (except the root of the tree, e.g. "Computer") has a parent, and where each device has none or more children. For example, the USB hub's parent is the USB controller, and the USB hub may have 3 children (e.g. the USB to PS/2 adapter, and 2 other USB devices).
For software; device drivers talk to each other and form a hierarchical tree that mirrors the hardware's hierarchical tree; where each device driver (except the root of the tree) has a parent and none or more children. For example, the USB hub's device driver would talk to the USB controller's device driver, and the USB hub's driver may have 3 children (including the device driver for the USB to PS/2 adapter, and 2 other drivers).
Device drivers rely on the API/protocol/interface provided by their parent; and provide a API/protocol/interface to their children. They should not care about any of their parent's details. For example; a PS/2 keyboard driver should use a "PS/2 interface" to talk to it's parent; and should not care if the parent is a USB to PS/2 adapter driver or if the parent is a PS/2 controller driver.
Hardware:
Software:
Cheers,
Brendan
Just something I forgot to mention...
For hardware; devices are connected to each other. For example, you might have a PS/2 keyboard connected to a USB to PS/2 adapter, connected to a USB hub, connected to a USB controller, connected to a PCI bus. These connections describe a hierarchical tree; where each device (except the root of the tree, e.g. "Computer") has a parent, and where each device has none or more children. For example, the USB hub's parent is the USB controller, and the USB hub may have 3 children (e.g. the USB to PS/2 adapter, and 2 other USB devices).
For software; device drivers talk to each other and form a hierarchical tree that mirrors the hardware's hierarchical tree; where each device driver (except the root of the tree) has a parent and none or more children. For example, the USB hub's device driver would talk to the USB controller's device driver, and the USB hub's driver may have 3 children (including the device driver for the USB to PS/2 adapter, and 2 other drivers).
Device drivers rely on the API/protocol/interface provided by their parent; and provide a API/protocol/interface to their children. They should not care about any of their parent's details. For example; a PS/2 keyboard driver should use a "PS/2 interface" to talk to it's parent; and should not care if the parent is a USB to PS/2 adapter driver or if the parent is a PS/2 controller driver.
Hardware:
Code: Select all
Computer
|__PCI_host_controller
|__PCI_to_LPC_bridge
| |__PS/2 controller
| |__PS/2 keyboard
| |__PS/2 mouse
|__PCI_to_PCI_bridge
|__USB_controller
|__USB_hub
|__USB to PS/2 adapter
|__PS/2 keyboard
|__PS/2 mouse
Code: Select all
Computer
|__PCI_host_controller driver
|__PCI_to_LPC_bridge driver
| |__PS/2 controller driver
| |__PS/2 keyboard driver (same as "PS/2 keyboard driver" below)
| |__PS/2 mouse driver (same as "PS/2 mouse driver" below)
|__PCI_to_PCI_bridge driver
|__USB_controller driver
|__USB_hub driver
|__USB to PS/2 adapter driver
|__PS/2 keyboard driver (same as "PS/2 keyboard driver" above)
|__PS/2 mouse driver (same as "PS/2 mouse driver" above)
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.