Page 1 of 1
How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:19 am
by Pype.Clicker
sorry. the PS/2 mouse thread has been accidentally deleted. reconstructing it from the cached copy i have.
purevoid:
Hi,
I'm having some trouble with the PS/2 Mouse. It works fine in VirtualPC, VMWare 5.5, and qemu, but not on my real hardware (a Dell P3-500, using a Belkin KVM Switch, and MS IntelliMouse Explorer).
I can't get any mouse movement (not even erratic movement) on the Dell. And when it starts up, it draws two cursors .. the initial (programmed at center of screen), then a second a little to the left, and near the bottom edge of the screen.
My init code is @
http://devnulled.ath.cx/svn/dst/trunk/kernel/mouse.ml (I know it's not your normal systems programming language, but should be understandable).
Would love to figure this out. BTW: the commented code in the init phase doesn't help either.
Kindest Regards,
Jonathan
durand:
My first thought was that you're not checking to see if your mouse is a 2 button, 3 button or wheeled moused and I don't think I can see that you're doing that after looking through your code.
It looks like you're expecting a 2 button mouse. In the case of 3 buttons and wheels, the mouse will send extra data in it's stream. So, in the case of your code, your movement across the screen will be jumpy and random.
I don't really understand your read bits code but it also looks like you're reading multiple bytes per interrupt. So you could be leaving some data on the keyboard/mouse line (third button or wheel data) and stopping the IRQ from ever firing again...
Just thinking out loud.
purevoid:
It reads one byte per interrupt. Shouldn't it be configured as a plain PS/2 mouse? I know there's some protocol to set it up to work with all buttons, but from what I understand, this is not the default.
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:20 am
by Pype.Clicker
Quote from: purevoid on 15-Jan-06, 10:05PM
It reads one byte per interrupt. Shouldn't it be configured as a plain PS/2 mouse?
yes, it should.
Quote:
I know there's some protocol to set it up to work with all buttons, but from what I understand, this is not the default.
it is indeed not the default.
What you may want to check is the 'order' in which your receive the stream. For some (yet unknown) reasons, it may happen that your mouse doesn't send you what you expect first ... first. There's a "sticky bit" in the first byte of the protocol (iirc). Check it has the value expected and discard the byte until you get a first byte that *do* look to be the first.
That should get you in sync in a few mouse packets.
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:21 am
by _purevoid
This is the mouse from hell...
Here's my code now:
Code: Select all
let rec read bits dx dy bytes_read =
let fb = get_framebuffer () in
let status = read_status () in
if test_mouse_output_full status &&
test_keyboard_output_full status
then
match bytes_read, read_data () with
| 0, bits -> read bits dx dy 1
| 1, dx -> read bits dx dy 2
| 2, dy ->
fb.{!i / 800,!i mod 800} <- 0xFFFFFF; incr i;
read 0 0 0 0
| _ -> Bigarray.Array2.fill fb 0xFF0000; read 0 0 0 0
else
read bits dx dy bytes_read
So no waiting for interrupts. Yet I don't get any white pixels drawn to the screen. The last match case should never happen. And the flow is correct (when bytes_read = 0, it calls read with bytes_read set to 1, and so forth till 2, then sets it to 0). Note this is tail recursive (compiles to a loop), so stack overflow is not the problem.
Maybe my test is wrong?
Code: Select all
(* (w >> b) & 1 == 1 in C parlance *)
let test_bit w b = (w asr b) land 1 = 1
let test_mouse_output_full w = test_bit w 5
let test_keyboard_output_full w = test_bit w 0
And getting the status byte:
Jonathan
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:22 am
by _pype
i'd say better check the assembler output ... i fear some sort of operator precedence issues ...
Code: Select all
let test_bit w b = (w asr b) land 1 = 1
is "land" for "logical and" ? do you have something for "bitwise and" instead ? do <var> = <expr2> = <expr3> be correctly compiled into "assign '<expr2 equals to expr3>' to <var>" ?
...
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:25 am
by _phugoid
Those are not really assignments. The first one begins the definition of "test_bit w b", it "assigns" statically the body to the symbol "test_bit" (with some additional stuff for currying). The second equals sign is an equality test.
I am not sure about land though (I use SML often which is similar enough, but not for systems work). I would imagine in ocaml land on integers would be bitwise. The precedence is something to think about, though.
--
I've converted ocaml to C, if that may help. I did not eliminate the recursion in read, but I hope it is still understandable to C-heads
Code: Select all
bool test_bit( int w, int b ) {
return ((w >> b) & 1) == 1;
}
int get_bit( int w, int b ) {
return (w >> b) & 1;
}
bool test_input_full( int w ) {
return test_bit( w, 1 );
}
bool test_mouse_output_full( int w ) {
return test_bit( w, 5 );
}
bool test_keyboard_output_full( int w ) {
return test_bit( w, 0 );
}
bool test_output_full( int status ) {
return test_keyboard_output_full( status ) &&
!test_mouse_output_full( status );
}
char read_status( ) {
char result;
asm volatile( "inb $0x64, %0;" : "=d" (result) );
}
char read_data( ) {
char result;
asm volatile( "inb $0x64, %0;" : "=d" (result) );
}
void wait_write( ) {
while( test_input_full( read_status( ) ) );
}
void write_controller( char w ) {
wait_write( );
asm volatile( "outb %0, $0x64;" : : "d" (w) );
}
void write_data( char w ) {
wait_write( );
asm volatile( "outb %0, $0x60;" : : "d" (w) );
}
void delay( int n ) {
int i;
for( i = 0; i < n; i++ )
asm volatile( "outb $0, $0x80;" );
}
char wait_read( ) {
char status = read_status( );
while( !test_keyboard_output_full( status ) ) {
delay( 50 );
status = read_status( );
}
return status;
}
char get_data( ) {
return wait_read( );
}
void write_mouse( char w )
{
write_controller( 0xD4 );
write_data( w );
get_data( );
}
void enable_mouse( ) {
write_controller( 0xA8 );
}
void enable_keyboard( ) {
write_controller( 0xAE );
}
void flush_data( )
{
while( test_keyboard_output_full( read_status( ) ) )
read_data( );
}
void set_mode( char mode ) {
write_controller( 0x60 );
write_data( mode );
}
// I am guessing that letting unit equal something at global scope causes it
// to be executed at initialization. This routine has no name, so I will name
// it blah.
// I will omit all code that is commented out in the ocaml source.
void blah( )
{
flush_data( );
set_mode( 0x47 );
enable_mouse( );
write_mouse( 0xFF );
get_data( );
get_data( );
write_mouse( 0xF4 );
enable_keyboard( );
flush_data( );
// The following code it taken from below the definition of read.
Interrupts::unmask_irq( 12 );
// thread_handle really becomes a global symbol. Create calls read with
// the parameters that follow its name (in the original code, a thunk is
// created and applied, but I did not want to pollute this code with a
// thunk). read obviously runs in the context of a new thread.
Thread::thread thread_handle = Thread::create( read, 0, 0, 0, 0 );
}
// Is this used?
typedef struct
{
int dx;
int dy;
bool buttons[];
} mouse_event;
// This creates a new "object" of type channel, at global scope (in C++ terms).
Channel channel;
void read( int bits, int dx, int dy, int bytes_read )
{
FrameBuffer fb = get_framebuffer( );
char status = read_status( );
if( test_mouse_output_full( status ) && test_keyboard_output_full( status ) )
{
switch( bytes_read )
{
case 0:
read( read_data( ), dx, dy, 1 );
break;
case 1:
read( bits, read_data( ), dy, 2 );
break;
case 2:
// Do some magic with the frame buffer. i is a reference to an int,
// but I do not see where it is bound.
read( 0, 0, 0, 0 );
break;
default:
// Fill the frame buffer with red or blue?
read( 0, 0, 0, 0 );
break;
}
}
read( bits, dx, dy, bytes_read );
}
// The code that comes below the definition of read has been placed in the
// definition of blah. I hope this is not wrong.
--
Ah! There should be a return statement after the switch inside the if statement block in read (or replace each break with return).
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:26 am
by _purevoid
read isn't quite right. It's turned into a loop.
Code: Select all
void read()
{
FrameBuffer fb = get_framebuffer( );
char status;
int bits = 0, dx = 0, dy = 0, bytes_read = 0;
while (1) {
status = read_status( );
if( test_mouse_output_full( status ) && test_keyboard_output_full( status ) )
{
switch( bytes_read )
{
case 0:
bits = read_data( ); bytes_read = 1;
break;
case 1:
dx = read_data( ); bytes_read = 2;
break;
case 2:
dy = read_data( ); bytes_read = 0;
// Do some magic with the frame buffer to show we've read a full packet
break;
default:
assert(false);
}
// no data, so just loop again
}
}
}
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:27 am
by _phugoid
Right. It's tail-recursive. Even in C tail-recursion is understood to be looping and vice versa for the purposes of static analysis, even when the compiler might not turn it into a loop.
What isn't quite right is the return statements I left out in read_status and read_data.
Re:How To Initialise PS/2 Mouse? (reconstructed)
Posted: Tue Jan 17, 2006 3:28 am
by _purevoid
I think it has to be my interrupts model. I can't think what else would be causing it.
Goes something like...
Code: Select all
open Sys;;
let recv = Array.init 16 (fun _ -> false);;
let waiting = Array.init 16 (fun _ -> []);;
let mask_irq irq = set_signal irq Signal_default;;
let irq_handler irq =
mask_irq irq;
if waiting.(irq) = [] then (
recv.(irq) <- true
) else (
List.iter (fun t -> Thread.wakeup t) waiting.(irq);
);
waiting.(irq) <- [];;
let unmask_irq irq = set_signal irq (Signal_handle irq_handler);;
(* blocks the calling thread *)
let wait irq =
unmask_irq irq;
if recv.(irq) then begin
recv.(irq) <- false;
end else begin
waiting.(irq) <- Thread.self () :: waiting.(irq);
Thread.sleep ()
end;;
IE: The requested IRQ is unmasked when we want to wait for it to be triggered, and masked again when it's been processed. After it's been processed, is an EOI sent to the PIC (not after the interrupt has fired).
Here are some snippets from my signal handling functions...
Code: Select all
void irq_handler(unsigned int irq) {
if ( sigactions[irq] ) {
sigactions[irq](irq);
} else {
/* send EOI for unhandled interrupt */
if(irq < 16) {
if(irq > 7)
out8(0xa0,0x20);
out8(0x20,0x20);
}
}
}
CAMLprim value caml_install_signal_handler(value signal_number, value action)
{
CAMLparam2 (signal_number, action);
void (*act)(int signo), (*oldact)(int signo);
interrupt_handler handler;
CAMLlocal1 (res);
switch(action) {
case Val_int(0): /* Signal_default */
act = 0;
break;
default: /* Signal_handle */
act = handle_signal;
break;
}
oldact = sigactions[Int_val(signal_number)];
sigactions[Int_val(signal_number)] = act;
if (oldact == handle_signal) {
res = caml_alloc_small (1, 0); /* Signal_handle */
Field(res, 0) = Field(caml_signal_handlers, Int_val(signal_number));
}
else
res = Val_int(0); /* Signal_default */
if (Is_block(action)) {
if (caml_signal_handlers == 0) {
caml_signal_handlers = caml_alloc(16, 0);
caml_register_global_root(&caml_signal_handlers);
}
caml_modify(&Field(caml_signal_handlers, Int_val(signal_number)), Field(action, 0));
/* tell PIC that we want to handle signal_number */
switch (Int_val(signal_number)) {
case 0: handler = irq_0; break;
case 1: handler = irq_1; break;
case 2: handler = irq_2; break;
case 3: handler = irq_3; break;
case 4: handler = irq_4; break;
case 5: handler = irq_5; break;
case 6: handler = irq_6; break;
case 7: handler = irq_7; break;
case 8: handler = irq_8; break;
case 9: handler = irq_9; break;
case 10: handler = irq_10; break;
case 11: handler = irq_11; break;
case 12: handler = irq_12; break;
case 13: handler = irq_13; break;
case 14: handler = irq_14; break;
case 15: handler = irq_15; break;
}
set_irq(Int_val(signal_number), handler, INT_GATE|BITS_32|PRESENT|RING_0); /* unmasks automatically */
} else {
/* tell PIC we're not handling this anymore */
unset_irq(Int_val(signal_number)); /* only masks it, handler is still present */
}
caml_process_pending_signals();
CAMLreturn (res);
}
static void handle_signal(int signal_number)
{
if (caml_try_leave_blocking_section_hook()) {
caml_execute_signal(signal_number, 1);
caml_enter_blocking_section_hook();
}else{
caml_record_signal(signal_number);
}
}
void caml_execute_signal(int signal_number, int in_signal_handler)
{
value res;
res = caml_callback_exn(
Field(caml_signal_handlers, signal_number),
Val_int(signal_number));
/* do I need to add this? I think so... */
caml_pending_signals[signal_number] = 0;
/* send EOI .. ocaml function has handled it */
if(signal_number < 16) {
if(signal_number > 7)
out8(0xa0,0x20);
out8(0x20,0x20);
}
if (Is_exception_result(res)) caml_raise(Extract_exception(res));
}
The last function is either called immediately, or for each pending signal at a check for signals in the VM.
Is my approach to handling interrupts wrong/bad? It's worked on real hardware for a network driver, and also keyboard input. And it's worked for everything on virtual machines... though they're not really that representative of actual hardware.
Jonathan
--
On second thought, it can't be interrupts. I turned off mouse interrupt mode, and still I get no packet data from the mouse. It's like it's never ready to send any data to the host...