/**

	Small test programm to play with the
	touchpad on a Fujitsu Point 510.

	changes:
	2002-04-20  Norbert Wagner <nw@softwarekombinat.de>
	    created

	2002-04-20 (later) Norbert Wagner <nw@softwarekombinat.de>
	    changed coordinates to signed values
		

	compile with: gcc -O2 -o pad pad.c

	The touchpad protocol (as far as i understand it) should be
	quite self explaining if you look at sources...
	Have fun!
*/

/*
 *
 * This product is distributed under the terms of
 * the GNU General Public License
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */



#include <sys/io.h>
#include <stdio.h>


#define DEFAULT_IOBASE 0x003e
#define DEFAULT_IRQ    0x0f

#define OFFS_FLAG    0x01
#define OFFS_DATA    0x00


#define byte_t unsigned char

struct pen_packet_t {
    byte_t          flags;
    short  x;
    short  y;
};


int pen_iobase = DEFAULT_IOBASE;
int pen_irq    = DEFAULT_IRQ;

/*
    inb_p / outb_p don't exist in user mode?? 
*/
#define inb_p inb
#define outb_p outb

inline byte_t pen_flags_in() {
	return inb_p( pen_iobase + OFFS_FLAG );
}

inline void pen_flags_out( byte_t b ) {
	outb_p( b, pen_iobase + OFFS_FLAG );
}

inline byte_t pen_data_in() {
	return inb_p( pen_iobase + OFFS_DATA );
}

/*
	returns the passed byte as a printable bitfield
*/
char* bitsb( byte_t b )
{
	int i;
	static char str[9];

	str[8] = 0;

	for (i = 0; i < 8; ++i)
	{
		str[7-i] = ((b >> i) & 1) ? '1' : '0';
	}

	return str;
}


int initialize()
{
	if ( ioperm( pen_iobase, 0x2, 1 ) )
	{
		fprintf(stderr, "Failed to get access to ioport - are you root? \n");
		return -1;
	}
	return 0;
}


void pen_enable( )
{

	int in  = 0;
	int out = 0;

	in  = pen_flags_in();
	out = (in & 0x7f) | 4;

	pen_flags_out( out );

}

inline int has_bit6_set()
{
    return (pen_flags_in() & 0x40) != 0;
}

inline int wait_for_bit6_set()
{
	int in = 0;

	do {
		in = pen_flags_in();
	} while ( (in & 0x40) == 0);

	return in;
}

inline int wait_for_bit6_clear()
{
	int in = 0;

	do {
		in = pen_flags_in();
	} while (in & 0x40) ;

	return in;
}



byte_t read_byte() {

	byte_t b = 0;
	byte_t x = 0;

	wait_for_bit6_set();

	b = pen_data_in();

	// clear bit 7 and 1
	pen_flags_out( pen_flags_in() & 0x7d );

	x = wait_for_bit6_clear();

	// clear bit 7, set bit 1
	x = (x & 0x7f) | 2;
	pen_flags_out( x );

	return b;
}

short read_coordinate()
{
	short l = read_byte() & 0x7f;
	short h = read_byte() & 0x7f;

	short coor = (h << 7) | l ;

	if (h & 0x40) {
	    /* negative value! */
	    coor |= 0xc000;
	}

	return coor;
}

/*
	if ( (buf[0] & 0x10) && ! (buf[0] & 0x02) )
	{
		outb( 0x80, ioport + OFFS_FLAG );
		printf("-\n");
	}
*/


int pen_has_packet()
{
    return has_bit6_set();
}

void pen_read_packet( struct pen_packet_t *packet )
{
	byte_t b = 0, l = 0, h = 0;
	do {
		b = read_byte();
	} while (b < 0x80);

	packet->flags = b ;
	packet->x = read_coordinate();
	packet->y = read_coordinate();
}

int main( int argc, char** argv)
{

	struct pen_packet_t packet;
	int i;

	char buffer[128];

	printf(" inializing... \n");
	if ( initialize( ) ) return 1;

	pen_enable ( );


	for ( ;; ) {

		pen_read_packet( &packet);


		printf( "flags: %x %s    ",
		(packet.flags & 0xff), bitsb( packet.flags ));
		printf( "x: %+05d  ",   packet.x );
		printf( "y: %+05d  \n", packet.y );

	}


	return 0;
}



