ps2_mouse.c

来自「RTEMS (Real-Time Executive for Multiproc」· C语言 代码 · 共 710 行

C
710
字号
/* * linux/drivers/char/pc_keyb.c * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 * See keyboard.c for the whole history. * Major cleanup by Martin Mares, May 1997 * Combined the keyboard and PS/2 mouse handling into one file, * because they share the same hardware. * Johan Myreen <jem@iki.fi> 1998-10-08. * Code fixes to handle mouse ACKs properly. * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29. * * RTEMS port: by Rosimildo da Silva. * This module was ported from Linux. * */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <bsp.h>#include <irq.h>#include <rtems/libio.h>#include <termios.h>#include <i386_io.h>#include <rtems/mw_uid.h>#define  INITIALIZE_MOUSE/* Some configuration switches are present in the include file... */#include "ps2_mouse.h"#include "mouse_parser.h"static void kbd_write_command_w(int data);static void kbd_write_output_w(int data);static unsigned char handle_kbd_event(void);/* used only by send_data - set by keyboard_interrupt */static volatile unsigned char reply_expected = 0;static volatile unsigned char acknowledge = 0;static volatile unsigned char resend = 0;/* *	PS/2 Auxiliary Device */static int psaux_init(void);static struct aux_queue *queue;	/* Mouse data buffer. */static int aux_count = 0;/* used when we send commands to the mouse that expect an ACK. */static unsigned char mouse_reply_expected = 0;#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)#define AUX_INTS_ON  (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)#define MAX_RETRIES	60		/* some aux operations take long time*/static void ps2_mouse_interrupt();static void ( *driver_input_handler_ps2 )( void *,  char *, int ) = 0;/* * This routine sets the handler to handle the characters received * from the serial port. */void ps2_set_driver_handler( int port, void ( *handler )( void *,  char *, int ) ){   driver_input_handler_ps2 = handler;}static void mdelay( unsigned long t ){  Wait_X_ms( t );}static void*    termios_ttyp_paux = NULL;static voidisr_on(const rtems_irq_connect_data *unused){  return;}						   static voidisr_off(const rtems_irq_connect_data *unused){  return;}static int isr_is_on(const rtems_irq_connect_data *irq){  return BSP_irq_enabled_at_i8259s( irq->name );}static rtems_irq_connect_data ps2_isr_data = { AUX_IRQ,						   ps2_mouse_interrupt, isr_on,  isr_off, isr_is_on };/* * Wait for keyboard controller input buffer to drain. * * Don't use 'jiffies' so that we don't depend on * interrupts.. * * Quote from PS/2 System Reference Manual: * * "Address hex 0060 and address hex 0064 should be written only when * the input-buffer-full bit and output-buffer-full bit in the * Controller Status register are set 0." */static void kb_wait(void){	unsigned long timeout = KBC_TIMEOUT;	do {		/*		 * "handle_kbd_event()" will handle any incoming events		 * while we wait - keypresses or mouse movement.		 */		unsigned char status = handle_kbd_event();		if (! (status & KBD_STAT_IBF))			return;		mdelay(1); 		timeout--;	} while (timeout);#ifdef KBD_REPORT_TIMEOUTS	printk( "Keyboard timed out[1]\n");#endif}static int do_acknowledge(unsigned char scancode){	if (reply_expected) {	  /* Unfortunately, we must recognise these codes only if we know they	   * are known to be valid (i.e., after sending a command), because there	   * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have	   * keys with such codes :(	   */		if (scancode == KBD_REPLY_ACK) {			acknowledge = 1;			reply_expected = 0;			return 0;		} else if (scancode == KBD_REPLY_RESEND) {			resend = 1;			reply_expected = 0;			return 0;		}		/* Should not happen... */#if 0		printk( "keyboard reply expected - got %02x\n",		       scancode);#endif	}	return 1;}static inline void handle_mouse_event(unsigned char scancode){	if (mouse_reply_expected) {		if (scancode == AUX_ACK) {			mouse_reply_expected--;			return;		}		mouse_reply_expected = 0;	}	if (aux_count) {		int head = queue->head;		queue->buf[head] = scancode;		head = (head + 1) & (AUX_BUF_SIZE-1);		if (head != queue->tail) {			queue->head = head;		}      /* if the input queue is active, add to it */      if( driver_input_handler_ps2 )      {          driver_input_handler_ps2( NULL,  &scancode, 1 );      }      else      {         /* post this byte to termios */  	      rtems_termios_enqueue_raw_characters( termios_ttyp_paux, &scancode, 1 );      }	}}/* * This reads the keyboard status port, and does the * appropriate action. * * It requires that we hold the keyboard controller * spinlock. */static unsigned char handle_kbd_event(void){	unsigned char status = kbd_read_status();	unsigned int work = 10000;	while (status & KBD_STAT_OBF) {		unsigned char scancode;		scancode = kbd_read_input();		if (status & KBD_STAT_MOUSE_OBF) {			handle_mouse_event(scancode);		} else {			do_acknowledge(scancode);         printk("pc_keyb: %X ", scancode );		}		status = kbd_read_status();		if(!work--)		{			printk("pc_keyb: controller jammed (0x%02X).\n",				status);			break;		}	}	return status;}static void ps2_mouse_interrupt(){	handle_kbd_event();}/* * send_data sends a character to the keyboard and waits * for an acknowledge, possibly retrying if asked to. Returns * the success status. * * Don't use 'jiffies', so that we don't depend on interrupts */static int send_data(unsigned char data){	int retries = 3;	do {		unsigned long timeout = KBD_TIMEOUT;		acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */		resend = 0;		reply_expected = 1;		kbd_write_output_w(data);		for (;;) {			if (acknowledge)				return 1;			if (resend)				break;			mdelay(1);			if (!--timeout) {#ifdef KBD_REPORT_TIMEOUTS				printk( "Keyboard timeout[2]\n");#endif				return 0;			}		}	} while (retries-- > 0);#ifdef KBD_REPORT_TIMEOUTS	printk( "keyboard: Too many NACKs -- noisy kbd cable?\n");#endif	return 0;}#define KBD_NO_DATA	(-1)	/* No data */#define KBD_BAD_DATA	(-2)	/* Parity or other error */static int kbd_read_data(void){	int retval = KBD_NO_DATA;	unsigned char status;	status = kbd_read_status();	if (status & KBD_STAT_OBF) {		unsigned char data = kbd_read_input();		retval = data;		if (status & (KBD_STAT_GTO | KBD_STAT_PERR))			retval = KBD_BAD_DATA;	}	return retval;}static void kbd_clear_input(void){	int maxread = 100;	/* Random number */	do {		if (kbd_read_data() == KBD_NO_DATA)			break;	} while (--maxread);}static int kbd_wait_for_input(void){	long timeout = KBD_INIT_TIMEOUT;	do {		int retval = kbd_read_data();		if (retval >= 0)			return retval;		mdelay(1);	} while (--timeout);	return -1;}static void kbd_write_command_w(int data){	kb_wait();	kbd_write_command(data);}static void kbd_write_output_w(int data){	kb_wait();	kbd_write_output(data);}static void kbd_write_cmd(int cmd){	kb_wait();	kbd_write_command(KBD_CCMD_WRITE_MODE);	kb_wait();	kbd_write_output(cmd);}/* * Check if this is a dual port controller. */static int detect_auxiliary_port(void){	int loops = 10;	int retval = 0;	/* Put the value 0x5A in the output buffer using the "Write	 * Auxiliary Device Output Buffer" command (0xD3). Poll the	 * Status Register for a while to see if the value really	 * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF	 * bit is also set to 1 in the Status Register, we assume this	 * controller has an Auxiliary Port (a.k.a. Mouse Port).	 */	kb_wait();	kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF);	kb_wait();	kbd_write_output(0x5a); /* 0x5a is a random dummy value. */	do {		unsigned char status = kbd_read_status();		if (status & KBD_STAT_OBF) {			(void) kbd_read_input();			if (status & KBD_STAT_MOUSE_OBF) {				printk( "Detected PS/2 Mouse Port.\n");				retval = 1;			}			break;		}		mdelay(1);	} while (--loops);	return retval;}/* * Send a byte to the mouse. */static void aux_write_dev(int val){	kb_wait();	kbd_write_command(KBD_CCMD_WRITE_MOUSE);	kb_wait();	kbd_write_output(val);}/* * Send a byte to the mouse & handle returned ack */static void aux_write_ack(int val){	kb_wait();	kbd_write_command(KBD_CCMD_WRITE_MOUSE);	kb_wait();	kbd_write_output(val);	/* we expect an ACK in response. */	mouse_reply_expected++;	kb_wait();}static unsigned char get_from_queue(void){	unsigned char result;	result = queue->buf[queue->tail];	queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);	return result;}static int queue_empty(void){	return queue->head == queue->tail;}/* * Random magic cookie for the aux device */#define AUX_DEV ((void *)queue)static int release_aux(){	if (--aux_count)		return 0;	kbd_write_cmd(AUX_INTS_OFF);			    /* Disable controller ints */	kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE);   BSP_remove_rtems_irq_handler( &ps2_isr_data );	return 0;}/* * Install interrupt handler. * Enable auxiliary device. */static int open_aux(){  rtems_status_code status;	if (aux_count++) {		return 0;	}	queue->head = queue->tail = 0;		/* Flush input queue */   status = BSP_install_rtems_irq_handler( &ps2_isr_data );   if( !status )	{	  printk("Error installing ps2-mouse interrupt handler!\n" );	  rtems_fatal_error_occurred( status );	}	kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE);	/* Enable the							   auxiliary port on							   controller. */	aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */	kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */	return 0;}/* * Put bytes from input queue to buffer. */size_t read_aux(char * buffer, size_t count ){	size_t i = count;	unsigned char c;	if (queue_empty()) 	{		return 0;   }	while (i > 0 && !queue_empty()) 	{		c = get_from_queue();		*buffer++ = c;		i--;	}	return count-i;}/* * Write to the aux device. */static int write_aux( int minor, const char * buffer, int count ){	int retval = 0;	if (count) {		int written = 0;		if (count > 32)			count = 32; /* Limit to 32 bytes. */		do {			char c;			c = *buffer++;			aux_write_dev(c);			written++;		} while (--count);		retval = -EIO;		if (written) {			retval = written;		}	}	return retval;}static unsigned int aux_poll(){	if( !queue_empty() )		return 1;	return 0;}static int psaux_init( void ){	if( !detect_auxiliary_port() )   {   	printk( "PS/2 - mouse not found.\n" );		return -EIO;   }	queue = (struct aux_queue *)malloc( sizeof(*queue) );   memset(queue, 0, sizeof(*queue));	queue->head = queue->tail = 0;	queue->proc_list = NULL;#ifdef INITIALIZE_MOUSE	kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */	aux_write_ack(AUX_SET_SAMPLE);	aux_write_ack(100);			      /* 100 samples/sec */	aux_write_ack(AUX_SET_RES);	aux_write_ack(3);			         /* 8 counts per mm */	aux_write_ack(AUX_SET_SCALE21);	/* 2:1 scaling */#endif /* INITIALIZE_MOUSE */	kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */	kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */	return 0;}void paux_reserve_resources(rtems_configuration_table *conf){  rtems_termios_reserve_resources(conf, 1);  return;}/* * paux device driver INITIALIZE entry point. */rtems_device_driverpaux_initialize(  rtems_device_major_number major,                   rtems_device_minor_number minor,                   void                      *arg){   rtems_status_code status;  /*   * Set up TERMIOS   */   rtems_termios_initialize();	printk( "PS/2 mouse probe.\n" );   if( psaux_init() < 0 )   {      printk("Error detecting PS/2 mouse --\n");      /* we might want to finish the application here !!! */   }   open_aux();  /*   * Register the device   */  status = rtems_io_register_name ("/dev/mouse", major, 0);  if (status != RTEMS_SUCCESSFUL)  {      printk("Error registering paux device!\n");      rtems_fatal_error_occurred (status);  }  return RTEMS_SUCCESSFUL;} /* tty_initialize */static int paux_last_close(int major, int minor, void *arg){  release_aux();  return 0;}/* * Write to the aux device. This routine is invoked by the * termios framework whenever the "ECHO" feature is on. * It does nothing write now. */static int write_aux_echo( int minor, const char * buffer, int count ){    return 0;}/* * Some initialization if necessary */static rtems_device_driverpaux_first_open( rtems_device_minor_number major,                 rtems_device_minor_number minor,                 void                      *arg){   return RTEMS_SUCCESSFUL;}/* * paux device driver OPEN entry point */rtems_device_driverpaux_open(rtems_device_major_number major,                rtems_device_minor_number minor,                void                      *arg){  rtems_status_code              status;  static rtems_termios_callbacks cb =   {    NULL,	              /* firstOpen */    paux_last_close,      /* lastClose */    NULL,                 /* poll read  */    write_aux_echo,       /* write */    NULL,	              /* setAttributes */    NULL,	              /* stopRemoteTx */    NULL,	              /* startRemoteTx */    0		                 /* outputUsesInterrupts */  };  status = rtems_termios_open (major, minor, arg, &cb );  termios_ttyp_paux = ( (rtems_libio_open_close_args_t *)arg)->iop->data1;  return status;}/* * paux device driver CLOSE entry point */rtems_device_driverpaux_close(rtems_device_major_number major,              rtems_device_minor_number minor,              void                      *arg){  return (rtems_termios_close (arg));} /* * paux device driver READ entry point. * Read characters from the PS/2 mouse. */rtems_device_driverpaux_read(rtems_device_major_number major,             rtems_device_minor_number minor,             void                      *arg){  return rtems_termios_read (arg);} /* tty_read */ /* * paux device driver WRITE entry point. * Write characters to the PS/2 mouse. */rtems_device_driverpaux_write(rtems_device_major_number major,              rtems_device_minor_number minor,              void                    * arg){  rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;  char                  *buffer  = rw_args->buffer;  int                    maximum  = rw_args->count;  rw_args->bytes_moved = write_aux( minor, buffer, maximum );  return RTEMS_SUCCESSFUL;} /* tty_write */ /* * Handle ioctl request. */rtems_device_driver paux_control(rtems_device_major_number major,		rtems_device_minor_number minor,		void                      * arg){ 	rtems_libio_ioctl_args_t *args = arg;	switch( args->command ) 	{	   default:      return rtems_termios_ioctl (arg);		break;      case MW_UID_REGISTER_DEVICE:      printk( "PS2 Mouse: reg=%s\n", args->buffer );      register_mou_msg_queue( args->buffer, -1 );		break;      case MW_UID_UNREGISTER_DEVICE:      unregister_mou_msg_queue( -1 );		break;   } 	args->ioctl_return = 0;   return RTEMS_SUCCESSFUL;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?