ps2 mosue and keyboard interfacing

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.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

argh.. yeah I was checking some of my physical machines, including some at the office (Dell poweredge servers.. quite new ones.. and they still have ps/2 plugs on them)... damn :)
Ok well I guess it can't be ignored, so now how to actually make it work.. I saw Brendan's post in the other topic regarding how he determines the type of devices
and checks the whole ps/2 controller before attempting to actually set either A/B as a kybd mouse etc. I think this is something lacking from the wiki .. a solid full tut on how to actually use ps/2.. because although the kybd mouse tuts and notes out there explain each in it's own context, the whole ps/2 interfacing seems to be a lot more tricky.. Sofar I've only gotten this code to work on half the real machines i've tried..

Anyone up for some collab getting a fully functional ps/2 base in place? we can use it as a starting point for a new wiki article on how to write a full ps/2 mouse/kybd/base controller driver setup.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

On another note, is there anything in acpi relating to ps2 controller? or is it so legacy that no config would be available for it via acpi? possibly at least the option to disable usb device ps2 emulation via acpi?
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

Ok, so i've started on this base generic ps/2 controller driver.. following Brendan's suggestions and detailed process. Few quick questions on it though, firstly..
the interface test:

m) Perform device A interface test (send command 0xAB to device).
n) If device B is still usable, perform device B interface test (send
command 0xAB to device).

device B, normally being the mouse, one would send a command to it as follows:
call wait_kybd_in_empty
mov al,0d4h
out 64h,al
call wait_kybd_in_empty
mov al,0abh
out 60h,al

However I can't find any 0abh / interface test command that would apply to device B?

secondly the lines p and r,
p) If device A is still usable, send the "identify" device command to
it and store anything returned for later.

I can't find any reference on an "identify" cmd for either the std keyboard encoder/controller or for the mouse interface?
Also to identify the mouse (if it is a mouse) surely you'd need to go through that process of setting sample rates to determine the type?
or would that come after some sort of identify cmd?

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

Re: ps2 mosue and keyboard interfacing

Post by Brendan »

Hi,
johnsa wrote:Ok, so i've started on this base generic ps/2 controller driver.. following Brendan's suggestions and detailed process. Few quick questions on it though, firstly..
the interface test:

m) Perform device A interface test (send command 0xAB to device).
n) If device B is still usable, perform device B interface test (send
command 0xAB to device).
These commands go to the controller (e.g. "out 0x64, 0xAB"), not to the device. For device B, you'd want command 0xA9 and not 0xAB. In both cases the controller should return 0x00 (from "in al,0x60") if the interface is OK.
johnsa wrote:secondly the lines p and r,
p) If device A is still usable, send the "identify" device command to
it and store anything returned for later.

I can't find any reference on an "identify" cmd for either the std keyboard encoder/controller or for the mouse interface?
The "identify" command is 0xF2. For e.g.:

Code: Select all

    call wait_kybd_in_empty
    mov al,0D4h
    out 64h,al
    call wait_kybd_in_empty
    mov al,0F2h
    out 60h,al

    mov edi,addess_to_put_ID
    cld
    mov dword [edi],0     ;Clear the ID
    call wait_for_byte_and_get_it
    jc .disable_device    ;Timeout here means the device isn't present
    stosb                 ;Store the first ID byte
    call wait_for_byte_and_get_it
    jc .got_ID            ;Timeout means there was only one ID byte
    stosb                 ;Store the second ID byte
    call wait_for_byte_and_get_it
    jc .got_ID            ;Timeout means there was only two ID bytes
    stosb                 ;Store the third ID byte
    call wait_for_byte_and_get_it
    jc .got_ID            ;Timeout means there was three ID bytes
    stosb                 ;Store the fourth ID byte (shouldn't happen)
.got_ID:
    sub edi,addess_to_put_ID   ;edi = number of ID bytes received
Once you've got the device ID, I'd use it to find/load a device driver for the device. For example, if the device ID is "0xFA, 0xAB, 0x83" then I'd load the device driver "/sys/drv/ps2/4ABFA.drv", which would be a device driver for an MF2 keyboard without scancode translation.
johnsa wrote:Also to identify the mouse (if it is a mouse) surely you'd need to go through that process of setting sample rates to determine the type?
or would that come after some sort of identify cmd?
That would come after sending the identify command, however in some cases the mouse might send the full ID (e.g. mouse on a KVM that was initialized by a different computer). I'd still do the same as above though - e.g. if the device ID is "0xFA, 0x04" then attempt to load the device driver "/sys/drv/ps2/04FA.drv", which may be a symbolic link to "/sys/drv/ps2/FA.drv" (the generic mouse driver).

Note: My code didn't support hot-plug PS/2 devices. For hot-plug PS/2 devices, after attempting to reset the device (command 0xFF) in steps O and Q, don't send command 0xF5 if the device doesn't respond. Then (later) if an unknown device sends 0xAA you'd send the identify command (and load a driver, etc). You'd also need to detect device removal - use time-outs everywhere, and consider sending something (e.g. 0xEE "echo" for keyboards) every few seconds when the device is idle. After device removal I'd keep the device driver in memory and re-use the same device driver if the same type of device is plugged back in (so the driver can restore the previous device state - e.g. keyboard LEDs, typematic rate, 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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: ps2 mosue and keyboard interfacing

Post by Brendan »

Hi,

I forgot to mention, it's best to test this sort of stuff on computers that aren't using "PS/2 emulation" and USB devices.

The "USB Developers Forum" (who are responsible for some of the USB specifications) provide a utility called KBCTEST.EXE which is a utility designed for BIOS and motherboard manufacturers to test PS/2 emulation. According to their "Universal Serial Bus PC Legacy Compatibility Specification", this utility will do the following tests:
  • KBC Input Buffer Full (IBF) bit clears within 100mS
  • KBC Output Buffer Full (OBF) bit clears after up to 100 reads spaced 1mS apart <how generated?>
  • KBC Gate A20 command causes 0x0/0x100000 address aliasing when set low, but not when set high (tested 100 times)
  • KBC Processor Reset command causes transition to real mode and transfer of control to address stored at BIOS Data Area[067H]
  • KBC OBF going true causes IRQ1 to occur (tested ??? times)
  • KBC echoing “.” causes INT15H/4fH vector to produce “.” value (tested ??? times)
  • KBC echoing “.” causes INT16H to produce “.” value (tested ??? times)
  • Set keyboard LEDs and verify no errors reported by keyboard (tested ??? times)
If your code relies on more than this very short list, then it's likely you'll have problems; which means if you attempt to initialize the PS/2 controller properly it's very likely you'll have problems with PS/2 emulation.

Also, some computers don't have PS/2 at all, and don't have PS/2 emulation either. The worst I've seen so far is a "Compaq Evo T20" thin client, where even the BIOS keyboard services (e.g. "Int 0x16, ah = 0x00") will not work - without your own USB keyboard driver you're entirely screwed.


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.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

Sigh... what a mess (like most PC architecture after years of backwards compatibility pseudo-support) :)

In any event, thanks for sharing some of your deep arcane knowledge! :)

I'm thinking that due to this whole usb/ps2 emulation story it might be wise to init the ps2 controller stuff POST usb init in terms of the kernel's device load sequence.. to ensure that any usb hid's are up and running
and ps/2 emulation is off before attempting to determine the status of any remaining REAL ps2 devices?
I see mentioned that the USB devices should halt ps/2 emulation once they've been initialized (or at least their host hub?)
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

Well here is the update I have so-far to test with (will form the basis of the ps/2 controller driver/init)...

It's working perfectly under bochs, under qemu the ps/2 init seems to be perfect as well but once again the mouse refuses to do anything... on real h/w it goes completely wonky!
Feel free to use it, find bugs and let me know..

the values I get back are:
BOCHS:
ps2_init_failed=0
ps2_ctrl_type=1
ps2_deviceA_type=faab8300
ps2_deviceB_type=fa00
ps2_deviceA_res=0
ps2_deviceB_res=0
ps2_deviceA_use=1
ps2_deviceB_use=1
ps2_deviceA_packet=1
ps2_deviceB_packet=3

QEMU:
ps2_init_failed=0
ps2_ctrl_type=1
ps2_deviceA_type=faab8300
ps2_deviceB_type=fa04
ps2_deviceA_res=0
ps2_deviceB_res=0
ps2_deviceA_use=1
ps2_deviceB_use=1
ps2_deviceA_packet=1
ps2_deviceB_packet=4

REAL H/W: (DELL D830 Latitude with PS/2 trackpad+kybd and USB mouse/kybd plugged in)
ps2_init_failed=0
ps2_ctrl_type=1
ps2_deviceA_type=00000000 ????
ps2_deviceB_type=fafa ?????
ps2_deviceA_res=fa ????????
ps2_deviceB_res=0
ps2_deviceA_use=0
ps2_deviceB_use=1
ps2_deviceA_packet=1
ps2_deviceB_packet=3

It's quite long...

Code: Select all


;======================================================================================
; Call before writing to 60h/64h.
;======================================================================================
kybd_ctrl_in_empty:
	mov ecx,100000
	clc
kybd_wait0:
	in al,64h
	test al,02h
	jz short kybd_wait0_done
	dec ecx
	jnz short kybd_wait0
	stc
kybd_wait0_done:
	ret

;======================================================================================
; Call before reading from 60h.
;======================================================================================
kybd_ctrl_out_full:
	mov ecx,100000
	clc
kybd_wait1:
	in al,64h
	test al,01h
	jnz short kybd_wait1_done
	dec ecx
	jnz short kybd_wait1
	stc
kybd_wait1_done:
	ret

;======================================================================================
; Empty the 8042 output buffer.
;======================================================================================
kybd_wait_8042:
	in al,64h
	test al,01h
	jz short no_8042_data
	in al,60h
	jmp kybd_wait_8042
no_8042_data:
	ret

;======================================================================================
; Wait for keyboard controller or mouse to return ACK.
;======================================================================================
kybd_wait_ack:
	mov ecx,100000
	clc
kybd_wait2:	
	in al,60h
	cmp al,0fah
	je short kybd_wait2_done
	dec ecx
	jnz short kybd_wait2
	stc
kybd_wait2_done:
	ret

;======================================================================================
; Wait for Keyboard/Mouse to send Reset 0aah,00h
;======================================================================================
kybd_wait_reset_ack:
	mov ecx,100000
	clc
kybd_wait3:
	in al,60h							; This should be 0aah BAT passed.
	cmp al,0aah
	je short reset_ackok
	dec ecx
	jnz short kybd_wait3
	stc
	ret
reset_ackok:
	in al,60h							; Read in 00h too.
	ret

;======================================================================================
; Write to PS2 Device B (mouse).
; -> Command Byte in BL.
;======================================================================================
ps2_deviceB_write:
	call kybd_ctrl_in_empty
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,bl
	out 60h,al	
	ret

;======================================================================================
; Return Device B ID
; -> BL is ID.
;======================================================================================
ps2_get_deviceB_id:
	mov bl,0f2h							; Get MouseID command.
	call ps2_deviceB_write
	call kybd_wait_ack
	call kybd_ctrl_out_full				; Wait for more data to be ready.
	in al,60h							; Read MouseID byte.
	ret

;======================================================================================
; Sample Rate in BL
; (10,20,40,60,80,100,200)
;======================================================================================
ps2_deviceB_set_sample:
	push rbx
	mov bl,0f3h							; Set Packet Sample-Rate Command.
	call ps2_deviceB_write
	call kybd_wait_ack
	pop rbx
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Resolution in BL
; (0=1pixel/mm,1=2pixel/mm,2=4pixel/mm,3=8pixel/mm)
;======================================================================================
ps2_deviceB_set_resolution:
	push rbx
	mov bl,0e8h							; Set Packet Sample-Rate Command.
	call ps2_deviceB_write
	call kybd_wait_ack
	pop rbx
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Set PS/2 Device B (Mouse) Scaling 2:1
;======================================================================================
ps2_deviceB_set_scaling21:
	mov bl,0e7h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret
	
;======================================================================================
; Set PS/2 Device B (Mouse) Scaling 1:1
;======================================================================================
ps2_deviceB_set_scaling11:
	mov bl,0e6h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Enable PS/2 Device B (Mouse).
;======================================================================================
ps2_deviceB_enable:
	mov bl,0f4h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Disable PS/2 Device B (Mouse).
;======================================================================================	
ps2_deviceB_disable:
	mov bl,0f5h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret
	
;======================================================================================
; Initialize the PS/2 Controller Interface and Attached Devices.
;======================================================================================
ps2_ctrl_init:

	;--------------------------------------------------------
	; Begin Initialization - Determine PS2 Controller Type.
	;--------------------------------------------------------
	call kybd_wait_8042					; Ensure we empty out the 8042 data port if it still has data in it.
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init00
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init00:
	mov al,020h							; Send command 20h (read configuration byte).
	out 64h,al
	call kybd_ctrl_out_full				; Wait for the data to be ready.
	jnc short ps2_init01

	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret

ps2_init01:	
	in al,60h							; read configuration byte (no ACK).

	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init02
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init02:
	mov al,060h							; Send command 60h (set configuration byte).
	out 64h,al
	
	call kybd_ctrl_in_empty
	jnc short ps2_init03
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init03:
	mov al,00000100b					; New configuration byte (Enable PS/2 device A and B and BAT flag).
	out 60h,al
	
	call kybd_ctrl_in_empty
	jnc short ps2_init04
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init04:
	mov al,0a7h							; Send command a7h (disable device B).
	out 64h,al

	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init05
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init05:
	mov al,020h							; Send command 20h (read configuration byte).
	out 64h,al
	call kybd_ctrl_out_full				; Wait for the data to be ready.
	jnc short ps2_init06
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init06:
	in al,60h							; read configuration byte.
	test al,00100000b					; Is Device B still Enabled?
	jnz short dual_ps2

	;--------------------------------------------------------
	; Single Port PS/2 Controller.
	;--------------------------------------------------------
	mov [ps2_ctrl_type],0
	mov [ps2_deviceB_type],0			; Since single port, device B must be none.
	mov [ps2_deviceB_use],0				; Ensure Device B is marked as unusable.
	jmp short got_ps2_type
	
	;--------------------------------------------------------
	; Dual Port PS/2 Controller.
	;--------------------------------------------------------
dual_ps2:
	mov [ps2_ctrl_type],1
	
	call kybd_wait_8042					; Ensure the output buffer is empty.
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init07
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret

ps2_init07:
	mov al,060h
	out 64h,al
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init08
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init08:
	mov al,00001100b					; Re-Enable both devices but leave IRQs off.
	out 60h,al
	mov [ps2_deviceA_use],1				; Ensure so-far that both devices are flagged usable.
	mov [ps2_deviceB_use],1
	
	;--------------------------------------------------------
	; We now know if the PS2 Controller is single/dual device
	; -> Perform Interface tests on attached devices.
	; -> From here it's safe to ignore the general
	; -> PS2 timeouts as it should be in place.
	; -> We'll ack. device specific timeouts now.
	;--------------------------------------------------------
got_ps2_type:

	call kybd_wait_8042					
	call kybd_ctrl_in_empty
	mov al,0f5h							; Disable scanning for device A (keyboard).
	out 60h,al
	call kybd_wait_ack
	
	cmp [ps2_deviceB_use],0
	je short no_disable_devB			; No Device B so skip.

	call kybd_ctrl_in_empty				; Disable packet sending (mouse) / disable device B.
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0f5h
	out 60h,al
	call kybd_wait_ack
	
	;--------------------------------------------------------
	; Perform Interface Test on Device A.
	;--------------------------------------------------------
no_disable_devB:
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0abh							; Device A Interface Test command.
	out 64h,al
	call kybd_ctrl_out_full
	in al,60h
	mov [ps2_deviceA_res],al
	mov al,[ps2_deviceA_res]
	test al,al
	jz short devA_int_ok
	mov [ps2_deviceA_use],0				; Device A Interface Test failed, mark device unusable.
devA_int_ok:

	cmp [ps2_deviceB_use],0
	je short reset_devA					; No device B so skip.
	
	;--------------------------------------------------------
	; Perform Interface Test on Device B.
	;--------------------------------------------------------
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0a9h							; Test Mouse Port (Device B) command.
	out 64h,al
	call kybd_ctrl_out_full
	in al,60h
	mov [ps2_deviceB_res],al
	mov al,[ps2_deviceB_res]
	test al,al
	jz short reset_devA
	mov [ps2_deviceB_use],0				; Device B Interface Test failed, mark device unsuable. 

	;--------------------------------------------------------
	; Perform Reset of Device A.
	;--------------------------------------------------------
reset_devA:
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0ffh							; Reset device A command.
	out 60h,al
	call kybd_wait_reset_ack			; Keyboard might not ACK reset, so wait for 0aah instead.
	jnc short ps2_init09
	
	mov [ps2_deviceA_type],0			; ACK from Device A failed, mark it as unusable for now.
	mov [ps2_deviceA_use],0
	jmp short reset_devB
		
ps2_init09:
	call kybd_ctrl_out_full
	in al,60h							; This should be 0aah BAT passed.
	cmp al,0aah
	je short reset_devB					; The BAT passed and we had ACK, device is responding ok.
	
	mov [ps2_deviceA_use],0				; Reset of Device A failed so mark it not usable.	

	;--------------------------------------------------------
	; Perform Reset of Device B if present.
	;--------------------------------------------------------
reset_devB:
	cmp [ps2_deviceB_use],0
	je short no_devB_reset				; Only attempt device B reset if present and working.
	
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0ffh
	out 60h,al	
	call kybd_wait_reset_ack			; Reset Command might not ACK, rather wait for 0aah.
	jnc short no_devB_reset
	
	mov [ps2_deviceB_use],0				; Reset of Device A failed, so mark it not usable.
	mov [ps2_deviceB_type],0
	
	;--------------------------------------------------------
	; Disable Scanning on Device A again.
	; -> Only if device responded to reset (use=1).
	;--------------------------------------------------------
no_devB_reset:
	cmp [ps2_deviceA_use],1
	jne short no_descanA			
	call kybd_ctrl_in_empty
	mov al,0f5h							; Disable scanning for device A (keyboard).
	out 60h,al
	call kybd_wait_ack
	jnc short no_descanA
	
	mov [ps2_deviceA_use],0				; Disable Device A scanning failed, mark as unusable.
	
	;--------------------------------------------------------
	; Disable Scanning on Device B again.
	; -> Only if device responded to reset (use=1).
	;--------------------------------------------------------
no_descanA:
	cmp [ps2_deviceB_use],1
	jne short no_descanB
	call kybd_ctrl_in_empty				; Disable packet sending (mouse) / disable device B.
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0f5h
	out 60h,al
	call kybd_wait_ack
	jnc short no_descanB
	
	mov [ps2_deviceB_use],0				; Disable Device B scanning failed, mark as unusable.
	
	;--------------------------------------------------------
	; Identify Device A if it's still usable.
	;--------------------------------------------------------
no_descanB:
	cmp [ps2_deviceA_use],1
	jne short identifyB					; Only identify device A if still present and usable.
	
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0f2h							; Send the get keyboard ID command to encoder.
	out 60h,al
  	
  	mov rdi,offset ps2_deviceA_type
  	mov dword [rdi],0					; Ensure device type is 0.
  	call kybd_wait_ack					; Wait for the ACK byte 0fah.
  	jnc short ps2_idA00
  	
 	mov [ps2_deviceA_type],0			; ACK from Device A failed, mark it as unusable for now.
	mov [ps2_deviceA_use],0
	jmp short identifyB					; Go on to Device B identification.
	
ps2_idA00:
  	mov [rdi+0],al						; byte 0 = 0fah.
  	call kybd_ctrl_out_full
  	jc short identifyB					; If this happens ID was only 1 byte.
  	in al,60h
  	cmp al,0fah
  	je short ps2_idA00					; Sometimes we get a double ACK? so ignore it..
	mov [rdi+1],al						; byte 1 = ?
  	call kybd_ctrl_out_full
  	jc short identifyB					; ID was 2 bytes long.
	in al,60h
  	mov [rdi+2],al						; byte 2 = ?
  	call kybd_ctrl_out_full
  	jc short identifyB					; ID was 3 bytes long.
  	in al,60h
  	mov [rdi+3],al						; byte 3 = ? (shouldn't happen as ids are 3 bytes or less).
  	
	;--------------------------------------------------------
	; Identify Device B if it's still usable.
	;--------------------------------------------------------
identifyB:
	cmp [ps2_deviceB_use],1
	jne short no_devB_identify			; No device B present or responding so skip identify.
	
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0f2h							; Send Get Mouse ID / identify command.
	out 60h,al
	
	mov rdi,offset ps2_deviceB_type
	mov dword [rdi],0
	call kybd_wait_ack					; Wait for the ACK byte 0fah.
	jnc short ps2_idB00
	
 	mov [ps2_deviceB_type],0			; ACK from Device B failed, mark it as unusable for now.
	mov [ps2_deviceB_use],0
	jmp short no_devB_identify			; Identify Device B Failed.

ps2_idB00:
	mov [rdi+0],al						; byte 0 = 0fah.
  	call kybd_ctrl_out_full
  	jc short no_devB_identify			; If this happens ID was only 1 byte.
	in al,60h
	cmp al,0fah
  	je short ps2_idB00					; Sometimes we get a double ACK? so ignore it..
	mov [rdi+1],al						; byte 1 = ?
  	call kybd_ctrl_out_full
  	jc short no_devB_identify			; ID was 2 bytes long.
  	in al,60h
  	mov [rdi+2],al						; byte 2 = ?
  	call kybd_ctrl_out_full
  	jc short no_devB_identify			; ID was 3 bytes long.
  	in al,60h
  	mov [rdi+3],al						; byte 3 = ? (shouldn't happen as ids are 3 bytes or less).

	;--------------------------------------------------------
	; If Device B is a PS2 Mouse and
	; it's ID = 0, perform mouse specific init sequence
	; to determin enhanced type (wheel, 5 button).
	;--------------------------------------------------------
no_devB_identify:
	cmp [ps2_deviceB_use],1				; No Device B so skip.
	jne short ps2_done

	cmp [ps2_deviceB_type],000000fah	; Mouse ID reported seems to be correct, no need to determine enhanced state.
	jne short ps2_done

	mov bl,200
	call ps2_deviceB_set_sample
	mov bl,100
	call ps2_deviceB_set_sample
	mov bl,80
	call ps2_deviceB_set_sample
	call ps2_get_deviceB_id
	cmp al,3
	jne short ps2_done					; ID remained 0 so we've got a std. PS/2 mouse.
	
	mov [ps2_deviceB_packet],4			; Update the packet size as we know we have at least a scroll wheel.
	mov rdi,offset ps2_deviceB_type
	mov [rdi+1],al						; Store the updated ID byte.
	
	mov bl,200
	call ps2_deviceB_set_sample
	mov bl,200
	call ps2_deviceB_set_sample
	mov bl,80
	call ps2_deviceB_set_sample
	call ps2_get_deviceB_id
	cmp al,4
	jne short ps2_done					; ID remained 3 so we've got a PS/2 mouse with scroll wheel.

	mov [ps2_deviceB_packet],4			; Update the packet size as we know we have a scroll wheel + 5 buttons.
	mov rdi,offset ps2_deviceB_type
	mov [rdi+1],al						; Store the updated ID byte.
	
ps2_done:

	; HERE IS TEST CODE as on my setup I know dev.B is ok and a mouse...
	INSTALL_IRQ_HANDLER 12,ps2_mouse_handler
	
	;call kybd_ctrl_in_empty
	;mov al,0a8h
	;out 64h,al
	;call kybd_wait_ack
	
	mov rdi,offset ps2_deviceB_type
	mov al,[rdi+1]
	mov [mouse_id],al
	mov eax,[ps2_deviceB_packet]
	mov [mouse_packet_size],eax
	mov [mouse_cycle],0
	
	call kybd_ctrl_in_empty
	mov al,20h
	out 64h,al
	call kybd_wait_ack
	call kybd_ctrl_out_full
	in al,60h
	or al,00000010b
	mov bl,al
	push rbx
	call kybd_ctrl_in_empty
	mov al,60h
	out 64h,al
	call kybd_wait_ack
	call kybd_ctrl_in_empty
	pop rbx
	mov al,bl
	out 60h,al
	call kybd_wait_ack
	call kybd_ctrl_in_empty
		
	call ps2_deviceB_enable				; Packets can now be delivered to the PS/2 controller.
	
	MONITOR_WRITE_BYTE [ps2_init_failed],00ff0000h
	MONITOR_WRITE_BYTE [ps2_ctrl_type],00ffff00h
	MONITOR_WRITE_DWORD [ps2_deviceA_type],00f0ff00h
	MONITOR_WRITE_DWORD [ps2_deviceB_type],00ff00f0h
	MONITOR_WRITE_BYTE [ps2_deviceA_res],00ffff0fh
	MONITOR_WRITE_BYTE [ps2_deviceB_res],00fffff0h
	MONITOR_WRITE_BYTE [ps2_deviceA_use],00ffff00h
	MONITOR_WRITE_BYTE [ps2_deviceB_use],00ffff00h
	
	ret

;######################################################################################
; PS/2 Controller Driver Data / Variables.
;######################################################################################

	; Possible Identification ID's returned.
	;0xFA               AT keyboard with translation (not possible for device B)
  	;0xFA, 0xAB, 0x41   MF2 keyboard with translation (not possible for device B)
  	;0xFA, 0xAB, 0xC1   MF2 keyboard with translation (not possible for device B)
  	;0xFA, 0xAB, 0x83   MF2 keyboard without translation
  	;0xFA, 0x00         Standard mouse
  	;0xFA, 0x03         Mouse with a scroll wheel
  	;0xFA, 0x04         5 button mouse with a scroll wheel
	;0xFA, 0x08			Typhoon 6byte packet mouse
	
ps2_init_failed    db 0					; [ 0 = ps2 controller is ready, 1 = ps2 controller init failed ]
ps2_ctrl_type      db 0					; [ 0 = single port, 1 = dual port ]
ps2_deviceA_type   dd 0					; [ 0 = none, else use table above ]
ps2_deviceB_type   dd 0					; [ 0 = none, else use table above ]
ps2_deviceA_res    db 0					; [ Result Byte from device A interface test (0=ok) all else = h/w failure ]
ps2_deviceB_res    db 0					; [ Result Byte from device B interface test (0=ok) all else = h/w failure ]
ps2_deviceA_use    db 1					; [ 0 = Not usable, 1 = usable ]
ps2_deviceB_use    db 1					; [ 0 = Not usable, 1 = usable ]
ps2_deviceA_packet dd 1					; [ Size in bytes of Device A packet ]
ps2_deviceB_packet dd 3					; [ Size in bytes of Device B packet ]

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

Re: ps2 mosue and keyboard interfacing

Post by Brendan »

Hi,
johnsa wrote:It's working perfectly under bochs, under qemu the ps/2 init seems to be perfect as well but once again the mouse refuses to do anything... on real h/w it goes completely wonky!
I typed "qemu mouse" into google, and found heaps of people complaining and several bug reports. Symptoms differ, and so do suggestions to fix it. I'm not sure if you're using kqemu, or what your host OS is, or... It might also be a good idea to try an older version of Qemu - someone said that recent changes (to add code for a tablet) made the PS/2 mouse unreliable.
johnsa wrote:REAL H/W: (DELL D830 Latitude with PS/2 trackpad+kybd and USB mouse/kybd plugged in)
What happens if you unplug the USB keyboard and mouse first? There might also be an "enable/disable dodgy PS2 emulation" option in the BIOS... :)

Do you have a boot floppy (and/or something I can boot from network)? I can try it on some of the computers here (with real PS/2 devices, with no emulation)...


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.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

Hi,

It's standard QEMU under winXP32, version: 0.9.1
It seems to be working now all of a sudden by making one small change at the end of the test bit of code (after ps2 init) I resend ea to the mouse to enable stream mode. Bochs is rock solid.
So now both emulators are working. I've checked my BIOS and it doesn't have any ps/2 emulation options.

I've got an image/iso for cd we can try.. this silly forum won't let me attach it (200kb)... PM it?

I'm about to reboot and try with both usb's unplugged and then plugged in.. see what happens.

update code here...

Code: Select all


;======================================================================================
; Call before writing to 60h/64h.
;======================================================================================
kybd_ctrl_in_empty:
	mov ecx,100000
	clc
kybd_wait0:
	in al,64h
	test al,02h
	jz short kybd_wait0_done
	dec ecx
	jnz short kybd_wait0
	stc
kybd_wait0_done:
	ret

;======================================================================================
; Call before reading from 60h.
;======================================================================================
kybd_ctrl_out_full:
	mov ecx,100000
	clc
kybd_wait1:
	in al,64h
	test al,01h
	jnz short kybd_wait1_done
	dec ecx
	jnz short kybd_wait1
	stc
kybd_wait1_done:
	ret

;======================================================================================
; Empty the 8042 output buffer.
;======================================================================================
kybd_wait_8042:
	in al,64h
	test al,01h
	jz short no_8042_data
	in al,60h
	jmp kybd_wait_8042
no_8042_data:
	ret

;======================================================================================
; Wait for keyboard controller or mouse to return ACK.
;======================================================================================
kybd_wait_ack:
	mov ecx,100000
	clc
kybd_wait2:	
	in al,60h
	cmp al,0fah
	je short kybd_wait2_done
	dec ecx
	jnz short kybd_wait2
	stc
kybd_wait2_done:
	ret

;======================================================================================
; Wait for Keyboard/Mouse to send Reset 0aah,00h
;======================================================================================
kybd_wait_reset_ack:
	mov ecx,100000
	clc
kybd_wait3:
	in al,60h							; This should be 0aah BAT passed.
	cmp al,0aah
	je short reset_ackok
	dec ecx
	jnz short kybd_wait3
	stc
	ret
reset_ackok:
	in al,60h							; Read in 00h too.
	ret

;======================================================================================
; Write to PS2 Device B (mouse).
; -> Command Byte in BL.
;======================================================================================
ps2_deviceB_write:
	call kybd_ctrl_in_empty
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,bl
	out 60h,al	
	ret

;======================================================================================
; Return Device B ID
; -> BL is ID.
;======================================================================================
ps2_get_deviceB_id:
	mov bl,0f2h							; Get MouseID command.
	call ps2_deviceB_write
	call kybd_wait_ack
	call kybd_ctrl_out_full				; Wait for more data to be ready.
	in al,60h							; Read MouseID byte.
	ret

;======================================================================================
; Sample Rate in BL
; (10,20,40,60,80,100,200)
;======================================================================================
ps2_deviceB_set_sample:
	push rbx
	mov bl,0f3h							; Set Packet Sample-Rate Command.
	call ps2_deviceB_write
	call kybd_wait_ack
	pop rbx
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Resolution in BL
; (0=1pixel/mm,1=2pixel/mm,2=4pixel/mm,3=8pixel/mm)
;======================================================================================
ps2_deviceB_set_resolution:
	push rbx
	mov bl,0e8h							; Set Packet Sample-Rate Command.
	call ps2_deviceB_write
	call kybd_wait_ack
	pop rbx
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Set PS/2 Device B (Mouse) Scaling 2:1
;======================================================================================
ps2_deviceB_set_scaling21:
	mov bl,0e7h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret
	
;======================================================================================
; Set PS/2 Device B (Mouse) Scaling 1:1
;======================================================================================
ps2_deviceB_set_scaling11:
	mov bl,0e6h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Enable PS/2 Device B (Mouse).
;======================================================================================
ps2_deviceB_enable:
	mov bl,0f4h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret

;======================================================================================
; Disable PS/2 Device B (Mouse).
;======================================================================================	
ps2_deviceB_disable:
	mov bl,0f5h
	call ps2_deviceB_write
	call kybd_wait_ack
	ret
	
;======================================================================================
; Initialize the PS/2 Controller Interface and Attached Devices.
;======================================================================================
ps2_ctrl_init:

	;--------------------------------------------------------
	; Begin Initialization - Determine PS2 Controller Type.
	;--------------------------------------------------------
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	mov al,0a8h							; Enable PS2 Aux Port and Mouse.
	out 64h,al
	call kybd_wait_ack
	
	call kybd_wait_8042					; Ensure we empty out the 8042 data port if it still has data in it.
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init00
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init00:
	mov al,020h							; Send command 20h (read configuration byte).
	out 64h,al
	call kybd_ctrl_out_full				; Wait for the data to be ready.
	jnc short ps2_init01

	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret

ps2_init01:	
	in al,60h							; read configuration byte (no ACK).

	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init02
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init02:
	mov al,060h							; Send command 60h (set configuration byte).
	out 64h,al
	
	call kybd_ctrl_in_empty
	jnc short ps2_init03
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init03:
	mov al,00000100b					; New configuration byte (Enable PS/2 device A and B and BAT flag).
	out 60h,al
	
	call kybd_ctrl_in_empty
	jnc short ps2_init04
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init04:
	mov al,0a7h							; Send command a7h (disable device B).
	out 64h,al

	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init05
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init05:
	mov al,020h							; Send command 20h (read configuration byte).
	out 64h,al
	call kybd_ctrl_out_full				; Wait for the data to be ready.
	jnc short ps2_init06
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init06:
	in al,60h							; read configuration byte.
	test al,00100000b					; Is Device B still Enabled?
	jnz short dual_ps2

	;--------------------------------------------------------
	; Single Port PS/2 Controller.
	;--------------------------------------------------------
	mov [ps2_ctrl_type],0
	mov [ps2_deviceB_type],0			; Since single port, device B must be none.
	mov [ps2_deviceB_use],0				; Ensure Device B is marked as unusable.
	jmp short got_ps2_type
	
	;--------------------------------------------------------
	; Dual Port PS/2 Controller.
	;--------------------------------------------------------
dual_ps2:
	mov [ps2_ctrl_type],1
	
	call kybd_wait_8042					; Ensure the output buffer is empty.
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init07
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret

ps2_init07:
	mov al,060h
	out 64h,al
	call kybd_ctrl_in_empty				; Wait for keyboard controller to be ready to receive a command.
	jnc short ps2_init08
	
	mov [ps2_init_failed],1				; Timeout when trying to communicate with ps2 controller.
	ret
	
ps2_init08:
	mov al,00001100b					; Re-Enable both devices but leave IRQs off.
	out 60h,al
	mov [ps2_deviceA_use],1				; Ensure so-far that both devices are flagged usable.
	mov [ps2_deviceB_use],1
	
	;--------------------------------------------------------
	; We now know if the PS2 Controller is single/dual device
	; -> Perform Interface tests on attached devices.
	; -> From here it's safe to ignore the general
	; -> PS2 timeouts as it should be in place.
	; -> We'll ack. device specific timeouts now.
	;--------------------------------------------------------
got_ps2_type:

	call kybd_wait_8042					
	call kybd_ctrl_in_empty
	mov al,0f5h							; Disable scanning for device A (keyboard).
	out 60h,al
	call kybd_wait_ack
	
	cmp [ps2_deviceB_use],0
	je short no_disable_devB			; No Device B so skip.

	call kybd_ctrl_in_empty				; Disable packet sending (mouse) / disable device B.
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0f5h
	out 60h,al
	call kybd_wait_ack
	
	;--------------------------------------------------------
	; Perform Interface Test on Device A.
	;--------------------------------------------------------
no_disable_devB:
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0abh							; Device A Interface Test command.
	out 64h,al
	call kybd_ctrl_out_full
	in al,60h
	mov [ps2_deviceA_res],al
	mov al,[ps2_deviceA_res]
	test al,al
	jz short devA_int_ok
	mov [ps2_deviceA_use],0				; Device A Interface Test failed, mark device unusable.
devA_int_ok:

	cmp [ps2_deviceB_use],0
	je short reset_devA					; No device B so skip.
	
	;--------------------------------------------------------
	; Perform Interface Test on Device B.
	;--------------------------------------------------------
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0a9h							; Test Mouse Port (Device B) command.
	out 64h,al
	call kybd_ctrl_out_full
	in al,60h
	mov [ps2_deviceB_res],al
	mov al,[ps2_deviceB_res]
	test al,al
	jz short reset_devA
	mov [ps2_deviceB_use],0				; Device B Interface Test failed, mark device unsuable. 

	;--------------------------------------------------------
	; Perform Reset of Device A.
	;--------------------------------------------------------
reset_devA:
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0ffh							; Reset device A command.
	out 60h,al
	call kybd_wait_reset_ack			; Keyboard might not ACK reset, so wait for 0aah instead.
	jnc short ps2_init09
	
	mov [ps2_deviceA_type],0			; ACK from Device A failed, mark it as unusable for now.
	mov [ps2_deviceA_use],0
	jmp short reset_devB
		
ps2_init09:
	call kybd_ctrl_out_full
	in al,60h							; This should be 0aah BAT passed.
	cmp al,0aah
	je short reset_devB					; The BAT passed and we had ACK, device is responding ok.
	
	mov [ps2_deviceA_use],0				; Reset of Device A failed so mark it not usable.	

	;--------------------------------------------------------
	; Perform Reset of Device B if present.
	;--------------------------------------------------------
reset_devB:
	cmp [ps2_deviceB_use],0
	je short no_devB_reset				; Only attempt device B reset if present and working.
	
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0ffh
	out 60h,al	
	call kybd_wait_reset_ack			; Reset Command might not ACK, rather wait for 0aah.
	jnc short no_devB_reset
	
	mov [ps2_deviceB_use],0				; Reset of Device A failed, so mark it not usable.
	mov [ps2_deviceB_type],0
	
	;--------------------------------------------------------
	; Disable Scanning on Device A again.
	; -> Only if device responded to reset (use=1).
	;--------------------------------------------------------
no_devB_reset:
	cmp [ps2_deviceA_use],1
	jne short no_descanA			
	call kybd_ctrl_in_empty
	mov al,0f5h							; Disable scanning for device A (keyboard).
	out 60h,al
	call kybd_wait_ack
	jnc short no_descanA
	
	mov [ps2_deviceA_use],0				; Disable Device A scanning failed, mark as unusable.
	
	;--------------------------------------------------------
	; Disable Scanning on Device B again.
	; -> Only if device responded to reset (use=1).
	;--------------------------------------------------------
no_descanA:
	cmp [ps2_deviceB_use],1
	jne short no_descanB
	call kybd_ctrl_in_empty				; Disable packet sending (mouse) / disable device B.
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0f5h
	out 60h,al
	call kybd_wait_ack
	jnc short no_descanB
	
	mov [ps2_deviceB_use],0				; Disable Device B scanning failed, mark as unusable.
	
	;--------------------------------------------------------
	; Identify Device A if it's still usable.
	;--------------------------------------------------------
no_descanB:
	cmp [ps2_deviceA_use],1
	jne short identifyB					; Only identify device A if still present and usable.
	
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0f2h							; Send the get keyboard ID command to encoder.
	out 60h,al
  	
  	mov rdi,offset ps2_deviceA_type
  	mov dword [rdi],0					; Ensure device type is 0.
  	call kybd_wait_ack					; Wait for the ACK byte 0fah.
  	jnc short ps2_idA00
  	
 	mov [ps2_deviceA_type],0			; ACK from Device A failed, mark it as unusable for now.
	mov [ps2_deviceA_use],0
	jmp short identifyB					; Go on to Device B identification.
	
ps2_idA00:
  	mov [rdi+0],al						; byte 0 = 0fah.
  	call kybd_ctrl_out_full
  	jc short identifyB					; If this happens ID was only 1 byte.
  	in al,60h
  	cmp al,0fah
  	je short ps2_idA00					; Sometimes we get a double ACK? so ignore it..
	mov [rdi+1],al						; byte 1 = ?
  	call kybd_ctrl_out_full
  	jc short identifyB					; ID was 2 bytes long.
	in al,60h
  	mov [rdi+2],al						; byte 2 = ?
  	call kybd_ctrl_out_full
  	jc short identifyB					; ID was 3 bytes long.
  	in al,60h
  	mov [rdi+3],al						; byte 3 = ? (shouldn't happen as ids are 3 bytes or less).
  	
	;--------------------------------------------------------
	; Identify Device B if it's still usable.
	;--------------------------------------------------------
identifyB:
	cmp [ps2_deviceB_use],1
	jne short no_devB_identify			; No device B present or responding so skip identify.
	
	call kybd_wait_8042
	call kybd_ctrl_in_empty
	mov al,0d4h
	out 64h,al
	call kybd_ctrl_in_empty
	mov al,0f2h							; Send Get Mouse ID / identify command.
	out 60h,al
	
	mov rdi,offset ps2_deviceB_type
	mov dword [rdi],0
	call kybd_wait_ack					; Wait for the ACK byte 0fah.
	jnc short ps2_idB00
	
 	mov [ps2_deviceB_type],0			; ACK from Device B failed, mark it as unusable for now.
	mov [ps2_deviceB_use],0
	jmp short no_devB_identify			; Identify Device B Failed.

ps2_idB00:
	mov [rdi+0],al						; byte 0 = 0fah.
  	call kybd_ctrl_out_full
  	jc short no_devB_identify			; If this happens ID was only 1 byte.
	in al,60h
	cmp al,0fah
  	je short ps2_idB00					; Sometimes we get a double ACK? so ignore it..
	mov [rdi+1],al						; byte 1 = ?
  	call kybd_ctrl_out_full
  	jc short no_devB_identify			; ID was 2 bytes long.
  	in al,60h
  	mov [rdi+2],al						; byte 2 = ?
  	call kybd_ctrl_out_full
  	jc short no_devB_identify			; ID was 3 bytes long.
  	in al,60h
  	mov [rdi+3],al						; byte 3 = ? (shouldn't happen as ids are 3 bytes or less).

	;--------------------------------------------------------
	; If Device B is a PS2 Mouse and
	; it's ID = 0, perform mouse specific init sequence
	; to determin enhanced type (wheel, 5 button).
	;--------------------------------------------------------
no_devB_identify:
	cmp [ps2_deviceB_use],1				; No Device B so skip.
	jne short ps2_done

	cmp [ps2_deviceB_type],000000fah	; Mouse ID reported seems to be correct, no need to determine enhanced state.
	jne short ps2_done

	mov bl,200
	call ps2_deviceB_set_sample
	mov bl,100
	call ps2_deviceB_set_sample
	mov bl,80
	call ps2_deviceB_set_sample
	call ps2_get_deviceB_id
	cmp al,3
	jne short ps2_done					; ID remained 0 so we've got a std. PS/2 mouse.
	
	mov [ps2_deviceB_packet],4			; Update the packet size as we know we have at least a scroll wheel.
	mov rdi,offset ps2_deviceB_type
	mov [rdi+1],al						; Store the updated ID byte.
	
	mov bl,200
	call ps2_deviceB_set_sample
	mov bl,200
	call ps2_deviceB_set_sample
	mov bl,80
	call ps2_deviceB_set_sample
	call ps2_get_deviceB_id
	cmp al,4
	jne short ps2_done					; ID remained 3 so we've got a PS/2 mouse with scroll wheel.

	mov [ps2_deviceB_packet],4			; Update the packet size as we know we have a scroll wheel + 5 buttons.
	mov rdi,offset ps2_deviceB_type
	mov [rdi+1],al						; Store the updated ID byte.
	
ps2_done:

	; HERE IS TEST CODE as on my setup I know dev.B is ok and a mouse...
	INSTALL_IRQ_HANDLER 12,ps2_mouse_handler
	
	;call kybd_ctrl_in_empty
	;mov al,0a8h
	;out 64h,al
	;call kybd_wait_ack
	
	mov rdi,offset ps2_deviceB_type
	mov al,[rdi+1]
	mov [mouse_id],al
	mov eax,[ps2_deviceB_packet]
	mov [mouse_packet_size],eax
	mov [mouse_cycle],0
	
	call kybd_ctrl_in_empty
	mov al,20h
	out 64h,al
	call kybd_wait_ack
	call kybd_ctrl_out_full
	in al,60h
	or al,00001110b
	and al,11011111b
	mov bl,al
	push rbx
	call kybd_ctrl_in_empty
	mov al,60h
	out 64h,al
	call kybd_wait_ack
	call kybd_ctrl_in_empty
	pop rbx
	mov al,bl
	out 60h,al
	call kybd_wait_ack
	call kybd_ctrl_in_empty
	
	mov bl,0eah							; Ensure stream mode is enabled.
	call ps2_deviceB_write
	call kybd_wait_ack
	
	call ps2_deviceB_enable				; Packets can now be delivered to the PS/2 controller.
	
	MONITOR_WRITE_BYTE [ps2_init_failed],00ff0000h
	MONITOR_WRITE_BYTE [ps2_ctrl_type],00ffff00h
	MONITOR_WRITE_DWORD [ps2_deviceA_type],00f0ff00h
	MONITOR_WRITE_DWORD [ps2_deviceB_type],00ff00f0h
	MONITOR_WRITE_BYTE [ps2_deviceA_res],00ffff0fh
	MONITOR_WRITE_BYTE [ps2_deviceB_res],00fffff0h
	MONITOR_WRITE_BYTE [ps2_deviceA_use],00ffff00h
	MONITOR_WRITE_BYTE [ps2_deviceB_use],00ffff00h
	
	ret

;######################################################################################
; PS/2 Controller Driver Data / Variables.
;######################################################################################

	; Possible Identification ID's returned.
	;0xFA               AT keyboard with translation (not possible for device B)
  	;0xFA, 0xAB, 0x41   MF2 keyboard with translation (not possible for device B)
  	;0xFA, 0xAB, 0xC1   MF2 keyboard with translation (not possible for device B)
  	;0xFA, 0xAB, 0x83   MF2 keyboard without translation
  	;0xFA, 0x00         Standard mouse
  	;0xFA, 0x03         Mouse with a scroll wheel
  	;0xFA, 0x04         5 button mouse with a scroll wheel
	;0xFA, 0x08			Typhoon 6byte packet mouse
	
ps2_init_failed    db 0					; [ 0 = ps2 controller is ready, 1 = ps2 controller init failed ]
ps2_ctrl_type      db 0					; [ 0 = single port, 1 = dual port ]
ps2_deviceA_type   dd 0					; [ 0 = none, else use table above ]
ps2_deviceB_type   dd 0					; [ 0 = none, else use table above ]
ps2_deviceA_res    db 0					; [ Result Byte from device A interface test (0=ok) all else = h/w failure ]
ps2_deviceB_res    db 0					; [ Result Byte from device B interface test (0=ok) all else = h/w failure ]
ps2_deviceA_use    db 1					; [ 0 = Not usable, 1 = usable ]
ps2_deviceB_use    db 1					; [ 0 = Not usable, 1 = usable ]
ps2_deviceA_packet dd 1					; [ Size in bytes of Device A packet ]
ps2_deviceB_packet dd 3					; [ Size in bytes of Device B packet ]

johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

Ok, tried it on real h/w... something happens.. but it seems as if the packets are all muddled up... mouse position goes all over the show,moving the mouse causes the handler to think the buttons are firing etc..
The result was the same with usb kybd/mouse plugged in and both out using just the laptop trackpad/kybd.

Brendan, what do you think about the idea of doing the ps/2 init post usb init to avoid the emulation confusion?

I really hope to get this ps/2 mouse/kybd nonsense sorted soon! :)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: ps2 mosue and keyboard interfacing

Post by Brendan »

Hi,
johnsa wrote:Ok, tried it on real h/w... something happens.. but it seems as if the packets are all muddled up... mouse position goes all over the show,moving the mouse causes the handler to think the buttons are firing etc..
That's sound like everything works perfectly, except that the mouse driver is out of sync - e.g. it thinks the first byte of a 3-byte packet is the second or third byte. It may be that the PS/2 controller initialization code leaves an ACK or something behind (e.g. mouse driver receives "ACK, byte1, byte2" for it's first 3-byte packet, then receives "byte3, byte1, byte2" for it's next 3-byte packet, etc).

You'll also notice that most mouse protocols are designed to allow auto-sync - for example, for the "standard PS/2 mouse" protocol (and most other PS/2 mouse protocols) bit 3 in the first byte of the packet is always set, so the mouse driver knows that if bit 3 is clear it hasn't received the first byte of the packet. If your driver discards bytes that don't have bit 3 set when it's waiting for the first byte of a movement packet, then eventually it will become synchronized. Your driver can also force the mouse to send packets (mouse command 0xEB) until it receives a packet where bit 3 in the first byte is set and bit 3 in all other bytes are clear (e.g. during startup or when "out of sync" is detected during normal operation).

It's probably best to find out why the mouse is out of sync first though (before you implement something to make it automatically synchronize itself).
johnsa wrote:Brendan, what do you think about the idea of doing the ps/2 init post usb init to avoid the emulation confusion?
My suggested method for hardware detection is to start with scanning the PCI bus and disabling everything you can (including disabling any PS/2 emulation in USB controllers), then (optionally) try to detect PnP ISA devices and disable them too, then manually probe for legacy devices after you know (almost) nothing else can confuse your manual probing attempts, then (after all manual probing stuff is completed) setup PnP ISA devices, then setup PCI devices.


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.
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

so after much testing last-night I stopped trying to use the mouse specifically but rather had the handler just write out the packets it's getting.

Under bochs the handler works perfectly. Under Qemu I landed up having to do the following at the beginning of the handler to get the first byte to come through correctly:
in al,64h
test al,01h ; Wait for the output buffer to be full with data.
jz no_mouse_byte

Sometimes the first byte that the handler picks up is 0fah (looks like a left over ack on port 60h).. some times 2 bytes come through and totally mess up the handler..

I'm not sure it would be possible to check bit 3, as for example 0fah would have bit 3 set too.. so im not too sure how I could force the handler to sync the input.. considering the next delta value could be 0ffh and also
have bit 3 set..
johnsa
Member
Member
Posts: 296
Joined: Mon Oct 15, 2007 3:04 pm

Re: ps2 mosue and keyboard interfacing

Post by johnsa »

There is definitely a problem with extra bytes being left over on port 60h throughout the init process.. for the life of me I can't see how.. it seems as if some commands only sometimes return an ACK when they should.. i keep ensuring that all ports are read and commands ack'ed etc..
Post Reply