Page 1 of 2

Mouse movement data packets... [SOLVED]

Posted: Wed Mar 07, 2007 12:31 am
by pcmattman
I'm trying to get the mouse working in my OS, but it's not reading correctly. In a typcial run, the movement counters get mixed with the status byte... so instead of the status being the status, it might be the y movement byte. I have no idea how to check whether or not the bytes are coming in the right order, or how to order them properly. Any ideas?

Posted: Wed Mar 07, 2007 9:51 am
by grunge_asm
http://www.programmersheaven.com/downlo ... nload.aspx

I find it in my developement/research of mouse driver.... this source reads the bytes (separated)... I have suspect that it is my same problem... do you remember???

grunge

Posted: Wed Mar 07, 2007 11:18 am
by bubach
have you looked at this:
http://bos.asmhackers.net/docs/mouse/sn ... /mouse.inc

sanik's donated source. i might have other info in those folders too.

Posted: Thu Mar 08, 2007 1:54 am
by pcmattman
No, it's not helping. The problem is that the bytes are being read in the wrong order sometimes... Is there any way to avoid this, because it's this mixing that causes the problems.

Posted: Thu Mar 08, 2007 2:19 am
by ~
What are the sources, or at least the algorithm you are using to gather them? The best approach would be to read 1 at a time and return from the ISR and then the next, and the next. You should store them in a byte array for when you are done.

Remember that for standard PS/2 mouse you will gather 3 bytes per single movement packet, and IF YOU HAVE MOUSE WHEEL ENABLED, you will gather 4 bytes per single movement packet.


------------------------------
NOTE: Read this:

http://www.osdev.org/phpBB2/viewtopic.p ... ght=#90326

--------------------------------
It might help you since it directly relates to both mouse and keyboard since they communicate using the same ports and the same circuitry.

Posted: Thu Mar 08, 2007 2:32 am
by pcmattman
I do that, when the mouse data needs to be read, the bytes are read one at a time. The problem is that if the mouse is moved too fast, often the y-movement information gets mixed into the status code, and after this the mouse never recovers and always gives mixed information.

Mouse driver:

Code: Select all

#include "sys/mattise.h"
#include "sys/kernel.h"
#include "sys/ps2.h"
#include "sys/kstdio.h"

// commands for PS2
#define ENABLE_PS2	0xA8
#define KBD_STAT	0x64
#define MOUSE		0xD4
#define MOUSE_STREAM	0xF4
#define MOUSE_DISABLE	0xF5
#define KBD_CMD		0x60
#define DISABLE_KBD	0xAD
#define ENABLE_KBD	0xAE

// checks the port
void CheckPort()
{
	unsigned char temp;
	while( true )
	{
		temp = inportb( KBD_STAT );
		if( (  temp & 2 ) == 0 )
			break;
	}
}

// set the ps2 bytes
void PS2Set()
{
	outportb( KBD_STAT, ENABLE_PS2 );
	CheckPort();
}

// waits for the mouse
void WaitMouse()
{
	outportb( KBD_STAT, MOUSE );
	CheckPort();
}

// waits for the ouse buffer to be full
void MouseBufferFull()
{
	unsigned char temp;
	while( true )
	{
		temp = inportb( KBD_STAT );
		if( (  temp & 0x20 ) == 0 )
			break;
	}
}

// checks the mouse
char CheckMouse()
{
	unsigned char temp;
	temp = inportb( KBD_STAT );

	if( temp & 1 )
		return 0;
	else
		return 1;
}

// sets the streaming mode
void StartStream()
{
	WaitMouse();
	outportb( KBD_CMD, MOUSE_STREAM );
	CheckPort();
	CheckMouse();
}

// disables the keyboard
void DisableKeyboard()
{
	outportb( KBD_STAT, DISABLE_KBD );
	CheckPort();
}

// enables the keyboard
void EnableKeyboard()
{
	outportb( KBD_STAT, ENABLE_KBD );
	CheckPort();
}

void MouseWait( char thetype )
{
	long timeout = 100000;
	if( thetype == 0 )
	{
		while( timeout-- )
		{
			if( inportb( 0x64 ) & 1 )
			{
				return;
			}
		}
		return;
	}
	if( thetype == 1 )
	{
		while( timeout-- )
		{
			if( ( inportb( 0x64 ) & 2 ) == 0 )
			{
				return;
			}
		}
		return;
	}
}

// get a byte from the port
char GetByte()
{
	DisableKeyboard();

	char ret = 1;

	while( ret )
		ret = CheckMouse();

	MouseWait( 0 );
	ret = inportb( 0x60 );
	EnableKeyboard();

	return ret;
}

// get data from the mouse
void GetMouseData( MOUSEDATA* data )
{
	// wait for the buffer to be full
	// MouseBufferFull();

	// get the data
	data->status = GetByte();
	data->xcoord = GetByte();
	data->ycoord = GetByte();
}

// init the mouse
void InitMouse()
{
	kputs( "Initializing PS2 mouse..." );
	PS2Set();
	StartStream();
}
Header:

Code: Select all

#include "mattise.h"

#ifndef PS2_H
#define PS2_H

/** PS2 **/

typedef struct tagMOUSEDATA
{ 
	char status; 
	char xcoord; 
	char ycoord; 
} MOUSEDATA;

// init the mouse
void InitMouse();

// get data from the mouse
void GetMouseData( MOUSEDATA* data );

/** END PS2 **/

#endif

Posted: Thu Mar 08, 2007 2:49 am
by ~
Yes, the "GetMouseData( MOUSEDATA* data )" routine is supposed to be called by the IRQ as an interrupt (hope it's accurate), which in turn calls the "GetByte()" routine.

Note that this "GetByte()" routine has many things that make it TERRYBLY and DEADLY slow from the point of view of mouse speed:

Code: Select all

// get a byte from the port 
char GetByte() 
{ 
   DisableKeyboard();   //This is BAD (slow)

   char ret = 1;

   while( ret )                        //BAD (obsolete)
      ret = CheckMouse();       //BAD (obsolete)

   MouseWait( 0 );               //BAD (slow)
   ret = inportb( 0x60 );       //The only RELIABLE thing around here
   EnableKeyboard();          //This is BAD (slow)

   return ret;         //Here we return 1 byte
} 


Note that when I refer to 1 byte at a time I refer to something like this:

Code: Select all

char off=0;

void GetMouseData( MOUSEDATA* data ) 
{ 
   // get the data 
   (off==0)?(data->status = GetByte();off++;return;):(0);
   (off==1)?(data->xcoord = GetByte();off++;return;):(0); 
   (off==2)?(data->ycoord = GetByte();off++;return;):(0);

   if(off==3)
   {
    //Reset buffer:
      off=0;

    //Do some processing:
    ///////////
    ///////////
    ///////////
    ///////////
   }

 //Acknowledge IRQ by PIC:
 //
} 
As you can see, with this code we will only read ONE BYTE BY INTERRUPT TRIGGER. So, our mouse will trigger 3 times for each of the bytes of 1 standard packet and only after that we will make our final interpretation. The other things, if you do them, will give you big trouble as you have already seen by yourself, even when they look appropiate in practice. Things like disabling keyboard is not necessary since it will have its chance to automatically report its data bytes through its own IRQ handler, and it's better not to delay that not a bit because it will eventually cause the buffers, specially the mouse one, being the fastest one, and probably the keyboard's, to start becoming full and losing bytes which will cause everything to start going crazy.

By the way, i am not expecting the rapid pseudocode above to be bug-free, but to give the ideas.

Posted: Thu Mar 08, 2007 3:15 am
by pcmattman
Thanks for that, I didn't realize I could use an ISR to handle the bytes. This should make my life much easier now, means I can spend more time actually implementing the graphical stuff instead of trying to figure out why the mouse is moving erratically.

Posted: Thu Mar 08, 2007 5:04 am
by pcmattman
The mouse IRQ is not firing... Any ideas?

Code for new driver:

Code: Select all

#include "sys/mattise.h"
#include "sys/kernel.h"
#include "sys/ps2.h"
#include "sys/kstdio.h"

// commands for PS2
#define ENABLE_PS2	0xA8
#define KBD_STAT	0x64
#define MOUSE		0xD4
#define MOUSE_STREAM	0xF4
#define MOUSE_DISABLE	0xF5
#define KBD_CMD		0x60
#define DISABLE_KBD	0xAD
#define ENABLE_KBD	0xAE

// checks the port
void CheckPort()
{
	unsigned char temp;
	while( true )
	{
		temp = inportb( KBD_STAT );
		if( (  temp & 2 ) == 0 )
			break;
	}
}

// waits for the ouse buffer to be full
void MouseBufferFull()
{
	unsigned char temp;
	while( true )
	{
		temp = inportb( KBD_STAT );
		if( (  temp & 0x20 ) == 0 )
			break;
	}
}

// checks the mouse
char CheckMouse()
{
	unsigned char temp;
	temp = inportb( KBD_STAT );

	if( temp & 1 )
		return 0;
	else
		return 1;
}

// writes to the keyboard
void WriteKeyboard( unsigned char val )
{
	unsigned int n;

	n = 0xFFFF;
	while( n-- )
	{
		if( inportb( 0x64 ) & 2 == 0 )
		{
			n = 1;
			break;
		}
	}

	if( !n ) return;

	outportb( 0x60, val );

	n = 0xFFFF;
	while( n-- )
	{
		if( inportb( 0x64 ) & 2 == 0 )
		{
			n = 1;
			break;
		}
	}
}

unsigned char ReadKeyboard()
{
	unsigned char res;
	unsigned int n;

	n = 0xFFFF;
	while( n-- )
	{
		if( inportb( 0x64 ) & 1 )
		{
			n = 1;
			break;
		}
	}

	if( !n ) return;

	res = inportb( 0x60 );

	return res;
}

// sends a command to the keyboard
void KeyboardCmd( unsigned char cmd )
{
	outportb( 0x64, cmd );
}

// set the ps2 bytes
void PS2Set()
{
	outportb( KBD_STAT, ENABLE_PS2 );
	CheckPort();

	// enable the mouse irq
	unsigned char c;
	KeyboardCmd( 0x20 );
	c = ReadKeyboard() | 2;
	KeyboardCmd( 0x60 );
	WriteKeyboard( c );
}

// waits for the mouse
void WaitMouse()
{
	outportb( KBD_STAT, MOUSE );
	CheckPort();
}

// sets the streaming mode
void StartStream()
{
	WaitMouse();
	outportb( KBD_CMD, MOUSE_STREAM );
	CheckPort();
	CheckMouse();
}

// disables the keyboard
void DisableKeyboard()
{
	outportb( KBD_STAT, DISABLE_KBD );
	CheckPort();
}

// enables the keyboard
void EnableKeyboard()
{
	outportb( KBD_STAT, ENABLE_KBD );
	CheckPort();
}

void MouseWait( char thetype )
{
	long timeout = 100000;
	if( thetype == 0 )
	{
		while( timeout-- )
		{
			if( inportb( 0x64 ) & 1 )
			{
				return;
			}
		}
		return;
	}
	if( thetype == 1 )
	{
		while( timeout-- )
		{
			if( ( inportb( 0x64 ) & 2 ) == 0 )
			{
				return;
			}
		}
		return;
	}
}

// get a byte from the port
char GetByte()
{
//	DisableKeyboard();

	char ret = 1;

/*	while( ret )
		ret = CheckMouse();

	MouseWait( 0 );*/
	ret = inportb( 0x60 );
/*	EnableKeyboard();*/

	return ret;
}

// info
int getstat = 0;
int readable = 0;
MOUSEDATA LocalData;

// data pointer

// irq handler for the mouse
void MouseIRQ( struct regs* r )
{
	kputs( "MouseIRQ " );

	// check the counter
	if( getstat == 0 )
	{
		LocalData.status = GetByte();
		getstat = 1;
		return;
	}
	if( getstat == 1 )
	{
		LocalData.xcoord = GetByte();
		getstat = 2;
		return;
	}
	if( getstat == 2 )
	{
		LocalData.ycoord = GetByte();
		getstat = 3;
	}

	if( getstat == 3 )
	{
		readable = 1;
	}
}

// get data from the mouse
void GetMouseData( MOUSEDATA* data )
{
	kputs( "reading..." );

	// is there data?
	while( !readable )
	{
		pause();
	}

	// readable is false now
	readable = 0;

	// return the data
	*data = LocalData;

	// set sc to 0, so we can get more data
	getstat = 0;
}

// init the mouse
void InitMouse()
{
	kputs( "Initializing PS2 mouse... " );
	PS2Set();
	StartStream();

	kputs( "Installing irq handler..." );

	// install the irq handler
	irq_install_handler( 12, MouseIRQ );
}
"MouseIRQ " never gets printed...

Posted: Thu Mar 08, 2007 8:56 am
by Combuster
Routine question: Have you enabled the Mouse IRQ at the PIC (both pics, actually)?

Posted: Thu Mar 08, 2007 3:36 pm
by pcmattman
I think so... I used Bran's development tutorial so that's where my irq installer is from, and it works for things like the keyboard and floppy. I think it might have something to do with enabling the mouse interrupt on the keyboard controller?

Posted: Thu Mar 08, 2007 10:04 pm
by ~
You probably need to use an IRET instruction instead of the C/C++ "return" in the "MouseIRQ( struct regs* r )". And make sure that you acknowledge the PIC before executing IRET.

You should poke 1 or more bytes to screen to find out how many times the mouse/keyboard IRQ gets called if any. If it's called only once an then it gets stuck, then definitely you need to acknowledge PICs now that you are using an ISR instead of polling, since I don't see any code that performs such acknowledgement.

Code: Select all

//For mouse:
  outportb(0xA0,0x20);
  outportb(0x20,0x20);



//For keyboard:
  outportb(0x20,0x20);

Remember that it should be the last thing to do (acknowledging) for each byte, so it should also be done when you finish receiving the third byte and using them.

Posted: Thu Mar 08, 2007 10:45 pm
by pcmattman
Well, actually, the way IRQ/ISRs work in my OS is that they point to an assembly stub, which then handles pushing/popping etc. and calls the C handler, and also has the IRET. Inside the C handler it chooses a handler function to run, and if the interrupt came from the second PIC, it sends the correct values.

At the moment, the mouse interrupt just isn't firing, and I think it might have something to do with the keyboard controller not having the mouse interrupt enabled.

Edit: I've come up with this to enable the mouse device interrupt (thanks to the Art of Assembly chapter 22)

Code: Select all

outportb( 0x64, 0x20 );
// wait for that to be sent
unsgined char c = inportb( 0x60 ) | 2;
outportb( 0x64, 0x60 );
// wait for that to be sent
outportb( 0x60, c );
Can anyone tell me if this is correct?

Posted: Thu Mar 08, 2007 11:05 pm
by ~
Something like this should be done:

Code: Select all

asm{
             mov al,0xD1 ;WRITE OUTPUT PORT.
             out 0x64,al

            call KeybController_waitUntilReady

             mov al,11111111b
               ; 0-Normal
               ; 1-Enable A20
               ; 2-Data to Mouse
               ; 3-Mouse Clock
               ; 4-IRQ1 Active
               ; 5-IRQ12 Active
               ; 6-Keyboard Clock
               ; 7-Data to Keyboard
               out 0x60,al
}

asm{
  KeybController_waitUntilReady:
   in al,0x64
   test al,00000010b
   jnz KeybController_waitUntilReady
  ret
}

Posted: Thu Mar 08, 2007 11:46 pm
by ~
pcmattman wrote:Well, actually, the way IRQ/ISRs work in my OS is that they point to an assembly stub, which then handles pushing/popping etc. and calls the C handler, and also has the IRET. Inside the C handler it chooses a handler function to run, and if the interrupt came from the second PIC, it sends the correct values.

At the moment, the mouse interrupt just isn't firing, and I think it might have something to do with the keyboard controller not having the mouse interrupt enabled.

Edit: I've come up with this to enable the mouse device interrupt (thanks to the Art of Assembly chapter 22)

Code: Select all

outportb( 0x64, 0x20 );
// wait for that to be sent
unsgined char c = inportb( 0x60 ) | 2;
outportb( 0x64, 0x60 );
// wait for that to be sent
outportb( 0x60, c );
Can anyone tell me if this is correct?
It looks like it should work. I remember I do these two things, the one of the snippet above and the assembly code I posted, and a third one that consists of initializing mouse itself, for it to work and finally enabling the IRQ in the PIC. All those are required.