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.
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: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.
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?
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?
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.
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).
This was exactly my plan. However:
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.
I feel like this isn't a lost cause from a design perspective.
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.