Again PS/2 Keyboard problem

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
ivannz

Again PS/2 Keyboard problem

Post by ivannz »

Hello everyone!
i am trying to develop my own OS, as you might guess...
And its crucial part is a proper initialisation and detection of Human Interface Devices. particularily PS/2 keyboards and mice.
here http://www.mega-tokyo.com/forum/index.php?board=1;action=display;threadid=7794;start=msg65842#msg65842 i found an interesting description of an initialisation sequences.

so the problem is the following.
When i try my OS on a real PC (PII, quite an old one with PS/2 keyboard), i get the following:

Code: Select all

#    port     cmd/data

;;apply default parameters to, disable scanning of device A
00) p60 <- F5
01) p60 -> FA  ;;ACK

;;initiate device A interface test
02) p64 <- AB
03) p60 -> 00  ;;no errors

;;reset device A
04) p60 <- FF
05) p60 -> FA  ;;ACK
and that is it. the device A status buffer says that there is no incoming data. no BAT result...

The ideal outcome should be the following (under BOCHS 2.2.1):

Code: Select all

#    port     cmd/data
;;apply default parameters to, disable scanning of device A
00) p60 <- F5
01) p60 -> FA  ;;ACK

;;initiate device A interface test
02) p64 <- AB
03) p60 -> 00  ;;no errors

;;reset device A
04) p60 <- FF
05) p60 -> FA  ;;ACK
06) p60 -> AA  ;;BAT successful

;;set default parameters, disable scanning
07) p60 <- F5
08) p60 -> FA  ;;ACK

;;perform an id request
09) p60 <- F2
10) p60 -> FA  ;;ACK
11) p60 -> AB  ;;ID byte #1
12) p60 -> 41  ;;ID byte #2
Great, everything is OK and we have a working device A and its ID code.

Can You tell me what might the problem here be?
Don't hesitate to ask me to provide any further comments.

P.S.: of course all inputs and outputs are done with proper checks if the input/output buffers are ready.
ivannz

Re:Again PS/2 Keyboard problem

Post by ivannz »

The next problem:
on a real hardware (see the first post) i get the following:

Code: Select all

#    port    cmd/data
;;disable device B
00) p64 <- A7

;;read ctrl8042 command byte
01) p64 <- 20

;;receive ctrl8042 command byte
02) p60 -> 60

;;write ctrl8042 command byte
03) p64 <- 60

;;set ctrl8042 command byte
04) p60 <- 60

;;enable device B
05) p64 <- A8

;;write data to device B input register
06) p64 <- D4

;;apply default parameters to and disable scanning of device B
07) p60 <- F5

;;recieve ACK response from device B
08) p60 -> FA

;;initiate device B interface test
09) p64 <- A9

;;get the results of a test
10) p60 -> 00

;;write data to device B input register
11) p64 <- D4

;;reset device B (out60: 0xFF; in60: 0xFA)
12) p60 <- FF

!13.1) p60 -> 00 (?? this is not what i expected)
!13.2) p60 -> FA (i added this extra read to figure out what was happenning)
;instead of ACK only i receive first 00h and then ACK

;;wait for the BAT result (0xAA or 0xFC) reply
14) p60 -> FA (?? this is not what i expected)
And again, here there is no proper BAT result.

Under BOCHS 2.2.1:

Code: Select all

;;disable device B
00) p64 <- A7

;;read ctrl8042 command byte
01) p64 <- 20

;;receive ctrl8042 command byte
02) p60 -> 60
;;  01100000b
;;        ^IRQ12 disabled
;; device B present (i disabled its IRQ, and here it is disabled)
;; according to the link i gave in the post above

;;write ctrl8042 command byte
03) p64 <- 60

;;set ctrl8042 command byte
04) p60 <- 60

;;enable device B
05) p64 <- A8

;;write data to device B input register
06) p64 <- D4

;;apply default parameters to and disable scanning of device B
07) p60 <- F5

;;recieve ACK response from device B
08) p60 -> FA

;;initiate device B interface test
09) p64 <- A9

;;get the results of a test
10) p60 -> 00

;;write data to device B input register
11) p64 <- D4

;;reset device B (out60: 0xFF; in60: 0xFA)
12) p60 <- FF

;;recieve ACK response
13) p60 -> FA

;;wait for the BAT result (0xAA or 0xFC) reply
14) p60 -> AA

;; discard incoming data if there is any (as device B returns 00h next as its device ID (if it is a standard PS/2 mouse))

;;write data to device B input register
15) p64 <- D4

;;apply default parameters to and disable scanning of device B
16) p60 <- F5

;;recieve ACK response from device B
17) p60 -> FA

;;write data to device B input register
18) p64 <- D4

;;preform an id request
19) p60 <- F2

;;recieve ACK response
20) p60 -> FA

;;recieve 1st id byte
21) p60 -> 00

;;recieve 2nd id byte (something non-standard like two keyboards) PS/2 mice do not return it.
22) p60 -> 00
I suppose these two probles are of the same nature.
Am i right?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Again PS/2 Keyboard problem

Post by Brendan »

Hi,

To me it sounds like the status port flags aren't being used correctly, which is likely enough considering that most of the documentation is written from the perspective of the device rather than from our perspective. For e.g. the "input buffer" is often data going from the computer out to the keyboard (or the keyboard's input buffer, rather than our output buffer).

Could you post the code to get and send data? For example, you should have something like:

Code: Select all

;Keyboard definitions
;_______________________________________________________________________________

%define STATUSPORT   0x64
%define COMMANDPORT   0x64
%define DATAPORT   0x60


;Status Port Bits

%define PS2controllerOutputAfull   0x01
%define PS2controllerOutputBfull   0x20
%define PS2controllerInputFull      0x02


;Get a byte from the controller
;_______________________________________________________________________________

PS2getByte:
   pushad

   mov ebx,1000000         ;ebx = 1 mS
   APIINT 0x04D         ;Wait for ebx nano-seconds

   APIINT 0x024         ;esi:ebx = current system timer tick
   mov ecx,100
   clr edi            ;edi:ecx = 100 mS time out
   add ecx,ebx
   adc edi,esi         ;edi:ecx = system timer tick for time out

.gb1:   in al,STATUSPORT
   test al,PS2controllerOutputAfull
   jne .gb2

   APIINT 0x024         ;esi:ebx = current system timer tick
   cmp esi,edi         ;Did the time out expire?
   jb .gb1            ; no
   cmp ebx,ecx         ;Did the time out expire?
   jbe .gb1         ; no
   
   popad
   stc
   ret

.gb2:   popad
   in al,DATAPORT
   clc
   ret
Also note that after sending command 0xFF (reset) you may need to wait for a long time before you get a response, while the keyboard resets and does it's little LED flash. I know that a timeout of 400 mS is too short for some computers - I'm using 600 mS which seems to work ok. For everything else a smaller timeout is fine (100 mS is heaps).


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.
ivannz

Re:Again PS/2 Keyboard problem

Post by ivannz »

Hello Brendan,

Indeed, i think you are right about inserting some delays in reading and writind to the i8042, and its devices.
that is what my code lacks.

So below i post my .inc assm file.
Just to let you know:
1)This part of the code is executed in protected mode (A20 enabled)
2)gs register is pointing at my own special region in memory where all global values are stored (like timer value, or or keyboard scan2ASCII tables)
3)IDT table is properly set up, so interrupts and exceptions are working.
4)The code attached below was heavily diluted with "a lot of conditional short jumps and unconditional long jumps", because otherwise i would have been impossible for me to get the debug information. (BUT i removed all the debug information printing calls, to increase its readability)
5)Actually i have no special ps/2get and ps/2put functions (the code i present you is just a sketch of how it will be done. of course in the nearest future i am going to improve and optimize the code, and make it look ''neat and tidy")

I will be really glad if you tell me how to improve on my code.
Information in some comments was copied from the link http://www.mega-tokyo.com/forum/index.php?board=1;action=display;threadid=7794;start=msg65842#msg65842

Please ask me for any necessary comments.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Again PS/2 Keyboard problem

Post by Brendan »

Hi,

I'd recommend using a reliable time base for your time-outs - the "mov ecx,0xFFFF; loop" will depend on the speed of the computer and isn't anywhere near long enough (unless you've got a 3 MHz CPU, or Bochs).

After sending 0xFF (reset device) you'll get an ACK fairly quickly, but then need to wait ages for the BAT results - for this I've got 2 seperate "get byte" routines, one for normal use with a 100 mS timeout and one for the BAT results with a 600 mS timeout. Your code (on a real computer) would give up waiting for the BAT results well before they're likely to arrive.

At the start you've got code to flush the output buffer (lines 9 and 61), which calls "__kbd8042obw" (wait for output buffer to become full). This will cause at least one timeout - it'd be better to use something like:

Code: Select all

;;*-------------------------------------------------------;;
;;void __kbd8042_force_output_empty()
;; CF = 0 - output buffer empty
;; CF = 1 - timeout
__kbd8042obw
push ecx
  mov ecx, 0xffff      ;;timeout

@kbd2p1:
  in al, 0x64      ;;check status
  test al, 00000001b   ;;output buffer
  je @kbd2p2
  in al, 0x60
  loop kbd2p1
  stc
pop ecx
ret

@kbd2p1:
  clc
pop ecx
ret
For the changes you make to the command byte (line 41) you do "and ah, 10111100b". On most controllers this would leave the clock signals to each device how they were previously, where it's probably best to either deliberately enable them or deliberately disable them. On older controllers there is no second device, and bit 5 doesn't control a clock signal. Instead it's used for communcations parameters and should be clear. Anyway, for these reasons I do "and ah, 10001100b" so I know any devices do have their clock signals enabled, and that it's not going to mess up older single-channel controllers.

AFAIK the default setting for the command byte on modern computers is for the clock to be enabled for the first channel, but disabled for the second channel. In this case your "i8042DeviceBdet__" code will be disabling an already disabled clock signal :).

I think the test in "i8042DeviceBdet__" is backwards. Sending command 0xA7 should disable the second device's clock signal (but it's ignored on older single-channel controllers). This causes the second device's clock bit in the controller command byte to become set (if the second channel is supported).

For this you'd want the following:

Code: Select all

;;extract the 2nd bit indicating if the IRQ (clock signal) for device B is enabled
;; "If clock signal to device B is still enabled, then assume the PS/2
;;  controller is an older "single device" controller rather than
;;  a "dual device" controller, and mark device B as unusable."
  mov ah, al
  and al, 00000010b
  jnz @CONT23          ;Second channel present if bit is set
  jmp @IDBI99            ;Else second channel not present
@CONT23:
If there is a second channel, then you'd want to re-enable it's clock signal again. You seem to be setting the controller command byte with the same value read previously, and then sending the 0xA8 command. You could enable the clock when you set the command byte (do an "and al, 10001100b" at line 320) and not send the 0xA8 at all, or you could send the 0xA8 and not worry about setting the controller command byte. You don't need both (they do the same thing).

Before I go further than this I normally disable scanning of any devices. The reason for this is that if someone presses a key or moves the mouse while you're expecting data from the other device then the unexpected data can block the data you are expecting from being received. Your code "i8042DeviceBdet__" doesn't disable device A scanning, so if someone presses a key at the wrong time it'll cause the results of the interface test, reset and/or identify to timeout.

For the identify command you should get data from a PS/2 mouse. On Bochs you don't get anything after the ACK, but on every real computer I tried and QEMU you do. I keep trying to receive ID bytes until I get a timeout, and use "shl edx,8; mov dl,al" for each byte. In this case bochs returns edx=0xFA (nothing after the ACK), a standard 2 button mouse returns edx=0xFA00 (1 byte after the ACK) and a standard keyboard returns "0xFAAB83" (2 bytes after the ACK).

Also, you don't need to do the "jmp short $+2" stuff unless you support 80386 CPUs..


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.
ivannz

Re:Again PS/2 Keyboard problem

Post by ivannz »

Thank you Brendan,

I've read the ports.a file more carefuly and thoroughly and came up with a working code,
which, with delays, now detects both PS/2 devices properly.

But now the following problem emerged:
when i try to setup the A20 address gate the real hardware either resets after some time or it hangs.

i use either of the following sequences:

Code: Select all

1)p64h <- D0h   ;; read the i8042 output register
2)p60h -> al   ;; get the value

or al, 000000010b

3)p64h <- D1h   ;; write the i8042 output register
4)p60h <- al   ;; set the value
or

Code: Select all

1)p64h <- D0h   ;; read the i8042 output register
2)p60h -> al   ;; get the value

or al, 000000011b

3)p64h <- D1h   ;; write the i8042 output register
4)p60h <- al   ;; set the value
every read or write is done with delays:
"write" to 64/60 is done with a delay of 100ms
"read" from 60 is done with a delay of 300ms

What might be the problem here?
Maybe it is that i try to enable A20 in Protected Mode? not in real? but then the next question arises: The AT method (in the above sequneces) which is extremely popular (it is described everywhere) fails to work, what makes me use the PS/2 method which is about setting the 1st bit of the port 0x92. Vectra fails also.
in real mode to check if the A20 is on i used:

Code: Select all

probeA20__
mov al, byte [0]
mov ah, al
not al
xchg al, byte [0x100000]
cmp byte [0], ah
mov byte [0x100000], al
ret
Kemp

Re:Again PS/2 Keyboard problem

Post by Kemp »

All the code I've seen for enabling the A20 (including the OSFAQ one I think) uses checks to see if the input/output buffers are empty/full rather than a fixed delay. This means that if the buffers are already ok then there is less delay and if they take longer than you think then your code still waits long enough.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Again PS/2 Keyboard problem

Post by Brendan »

Hi,

IIRC there's 2 different methods of enableing A20 that are supported by most motherboards, and some motherboards support neither. The best way to enable A20 is to use the BIOS function, which should work because it knows what the motherboard expects.

It's also good to test if A20 was enabled or not, rather than just assuming that it did.

For an example, see:
http://bcos.hopto.org/src/boot/boot144/gatea20-asm.html


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.
ivannz

Not a Keyboard problem but still...

Post by ivannz »

Hello ance again.

I am either stupid or i just can't understand anything...

when i'm writing 0xAA (initiate i8042 controller self-test) to 0x64 port i get a General Protection Fault (on a real PC)...
But when i detect PS/2 devices ... EVERYTHING is allrgiht.

Please tell me, what is going ON?

P.S.: the same thing was with A20 line when i sent 0xD1 to the command port...
ivannz

Re:Again PS/2 Keyboard problem

Post by ivannz »

Brendan,

When i pulg in either two mice, or two keyboards or a mouse and (or) a keyboard my code detects them well. (it gives proper ID bytes)

if i plug keyboard and mouse into their rightful places
(Main -- Keyboard, Aux -- Mouse) i get the following:

Code: Select all

PS/2 Device A: FOUND=1, ID=AB83  ;; <- ID
PS/2 Device B: FOUND=1, ID=0000  ;; <- ID
well that is fine.
but when i swap keyboard with mouse and vice versa, i get the same result (Aux -- Keyboard, Main -- Mouse).

Code: Select all

PS/2 Device A: FOUND=1, ID=AB83  ;; <- ID
PS/2 Device B: FOUND=1, ID=0000  ;; <- ID
When i try the following:
1) Main -- Mouse, Aux -- NULL or Aux -- Mouse, Main -- NULL
2) Main -- Keyboard, Aux -- NULL or Aux -- Keyboard, Main -- NULL
Mouse is always a Device B and keyboard is always a Device A.

it seems like 8042 works according to the following:
1) find 1st keyboard
1.1) KBD found --> mark it as Device A.
2) find 1st mouse
2.1) MOUSE found --> mark it as Device B.

3) if there are any spare device slots (either B or A)
3.1) Search for other devices
3.1.1) KBD found
3.1.1.1) if device A is used mark it as Device B
3.1.1.2) if device A is unused mark it as Device A
3.1.2) MOUSE found
3.1.2.1) if device B is used mark it as Device A
3.1.2.2) if device B is unused mark it as Device B

My question is:
is such behaviour of 8042 normal? or i've made errors in my code?

thank you in advance.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Again PS/2 Keyboard problem

Post by Brendan »

Hi,
ivannz wrote:My question is:
is such behaviour of 8042 normal? or i've made errors in my code?
Sorry for delays...

AFAIK it's not normal 8042 behaviour - it seems like your BIOS/chipset is doing some extra work so that the end user doesn't need to have the "correct" devices plugged into the normal ports. I don't think it'd matter much either - normally (if a PS/2 keyboard is plugged into a computer that doesn't automatically shuffle PS/2 devices) the user will plug the keyboard into the first PS/2 port so they can use the keyboard with the BIOS, etc.


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.
ivannz

Re:Again PS/2 Keyboard problem

Post by ivannz »

Hello Brendan,

Indeed when i plug a keyboard into either port, be it main or auxiliary, my BIOS detects it and properly works with it (i can even access BIOS SETUP) the PC i am testing upon is an old one (PI 120 MHz 64Mb sdram without any harddrive) so i can't test if windows or anything else works fine.
But can this ports shuffling affect IRQ settings?

Thank you
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Again PS/2 Keyboard problem

Post by Brendan »

Hi,
ivannz wrote:Indeed when i plug a keyboard into either port, be it main or auxiliary, my BIOS detects it and properly works with it (i can even access BIOS SETUP) the PC i am testing upon is an old one (PI 120 MHz 64Mb sdram without any harddrive) so i can't test if windows or anything else works fine.
But can this ports shuffling affect IRQ settings?
I'd expect the IRQs to be shuffled too, so that it makes no difference at all which port the keyboard is plugged into. Otherwise it'd mess up most OS software, and break backward compatibility.

I'm only guessing though - none of my computers do this port shuffling. Instead you get the highly intelligent "Keyboard not detected, press F1 to continue" error message (if you don't disable the "Halt on keyboard error" option in the BIOS).


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.
Post Reply