Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:41

0001 /*
0002  * linux/drivers/char/pc_keyb.c
0003  * Separation of the PC low-level part by Geert Uytterhoeven, May 1997
0004  * See keyboard.c for the whole history.
0005  * Major cleanup by Martin Mares, May 1997
0006  * Combined the keyboard and PS/2 mouse handling into one file,
0007  * because they share the same hardware.
0008  * Johan Myreen <jem@iki.fi> 1998-10-08.
0009  * Code fixes to handle mouse ACKs properly.
0010  * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
0011  *
0012  *  RTEMS port: by Rosimildo da Silva.
0013  */
0014 
0015 #include <stdlib.h>
0016 #include <stdio.h>
0017 #include <string.h>
0018 #include <errno.h>
0019 #include <sys/types.h>
0020 #include <assert.h>
0021 
0022 #include <bsp.h>
0023 #include <bsp/irq.h>
0024 #include <rtems/libio.h>
0025 #include <termios.h>
0026 #include <i386_io.h>
0027 #include <rtems/mw_uid.h>
0028 #include <rtems/mouse_parser.h>
0029 
0030 #define  INITIALIZE_MOUSE
0031 /* Some configuration switches are present in the include file... */
0032 #include <rtems/ps2_drv.h>
0033 #include "ps2_mouse.h"
0034 
0035 static void kbd_write_command_w(int data);
0036 #if 0
0037 static void kbd_write_output_w(int data);
0038 #endif
0039 
0040 static unsigned char handle_kbd_event(void);
0041 static void ps2_set_driver_handler(int port, mouse_parser_enqueue_handler handler);
0042 
0043 /* used only by send_data - set by keyboard_interrupt */
0044 static volatile unsigned char reply_expected = 0;
0045 static volatile unsigned char acknowledge = 0;
0046 static volatile unsigned char resend = 0;
0047 
0048 /*
0049  *  PS/2 Auxiliary Device
0050  */
0051 static int psaux_init(void);
0052 
0053 static struct aux_queue *queue; /* Mouse data buffer. */
0054 static int aux_count = 0;
0055 /* used when we send commands to the mouse that expect an ACK. */
0056 static unsigned char mouse_reply_expected = 0;
0057 
0058 #define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
0059 #define AUX_INTS_ON  (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
0060 #define MAX_RETRIES 60      /* some aux operations take long time*/
0061 
0062 static void ps2_mouse_interrupt(void *);
0063 static mouse_parser_enqueue_handler driver_input_handler_ps2 = NULL;
0064 
0065 /*
0066  * This routine sets the handler to handle the characters received
0067  * from the serial port.
0068  */
0069 static void ps2_set_driver_handler(
0070   int                          port,
0071   mouse_parser_enqueue_handler handler
0072 )
0073 {
0074   driver_input_handler_ps2 = handler;
0075 }
0076 
0077 static void mdelay( unsigned long t )
0078 {
0079   Wait_X_ms( t );
0080 }
0081 
0082 static void*    termios_ttyp_paux = NULL;
0083 
0084 /*
0085  * Wait for keyboard controller input buffer to drain.
0086  *
0087  * Don't use 'jiffies' so that we don't depend on
0088  * interrupts..
0089  *
0090  * Quote from PS/2 System Reference Manual:
0091  *
0092  * "Address hex 0060 and address hex 0064 should be written only when
0093  * the input-buffer-full bit and output-buffer-full bit in the
0094  * Controller Status register are set 0."
0095  */
0096 
0097 static void kb_wait(void)
0098 {
0099   unsigned long timeout = KBC_TIMEOUT;
0100 
0101   do {
0102     /*
0103      * "handle_kbd_event()" will handle any incoming events
0104      * while we wait - keypresses or mouse movement.
0105      */
0106     unsigned char status = handle_kbd_event();
0107 
0108     if (! (status & KBD_STAT_IBF))
0109       return;
0110 
0111     mdelay(1);
0112     timeout--;
0113   } while (timeout);
0114 
0115   #ifdef KBD_REPORT_TIMEOUTS
0116     printk( "Keyboard timed out[1]\n");
0117   #endif
0118 }
0119 
0120 static int do_acknowledge(unsigned char scancode)
0121 {
0122   if (reply_expected) {
0123 
0124     /* Unfortunately, we must recognise these codes only if we know they
0125      * are known to be valid (i.e., after sending a command), because there
0126      * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have
0127      * keys with such codes :(
0128      */
0129     if (scancode == KBD_REPLY_ACK) {
0130       acknowledge = 1;
0131       reply_expected = 0;
0132       return 0;
0133     } else if (scancode == KBD_REPLY_RESEND) {
0134       resend = 1;
0135       reply_expected = 0;
0136       return 0;
0137     }
0138 
0139     /* Should not happen... */
0140     #if 0
0141       printk( "keyboard reply expected - got %02x\n", scancode);
0142     #endif
0143   }
0144   return 1;
0145 }
0146 
0147 static inline void handle_mouse_event(unsigned char scancode)
0148 {
0149   if (mouse_reply_expected) {
0150     if (scancode == AUX_ACK) {
0151       mouse_reply_expected--;
0152       return;
0153     }
0154     mouse_reply_expected = 0;
0155   }
0156 
0157   if (aux_count) {
0158     int head = queue->head;
0159     queue->buf[head] = scancode;
0160     head = (head + 1) & (AUX_BUF_SIZE-1);
0161     if (head != queue->tail) {
0162       queue->head = head;
0163     }
0164 
0165     /* if the input queue is active, add to it */
0166     if( driver_input_handler_ps2 ) {
0167       driver_input_handler_ps2( &scancode, 1 );
0168     } else {
0169       /* post this byte to termios */
0170       rtems_termios_enqueue_raw_characters( termios_ttyp_paux, (char *)&scancode, 1 );
0171     }
0172   }
0173 }
0174 
0175 /*
0176  * This reads the keyboard status port, and does the
0177  * appropriate action.
0178  *
0179  * It requires that we hold the keyboard controller
0180  * spinlock.
0181  */
0182 static unsigned char handle_kbd_event(void)
0183 {
0184   unsigned char status = kbd_read_status();
0185   unsigned int work = 10000;
0186 
0187   while (status & KBD_STAT_OBF) {
0188     unsigned char scancode;
0189     scancode = kbd_read_input();
0190     if (status & KBD_STAT_MOUSE_OBF) {
0191       handle_mouse_event(scancode);
0192     } else {
0193       do_acknowledge(scancode);
0194       printk("pc_keyb: %X ", scancode );
0195     }
0196     status = kbd_read_status();
0197     if(!work--) {
0198       printk("pc_keyb: controller jammed (0x%02X).\n", status);
0199       break;
0200     }
0201   }
0202   return status;
0203 }
0204 
0205 static void ps2_mouse_interrupt(void * unused)
0206 {
0207   handle_kbd_event();
0208 }
0209 
0210 static void kbd_write_command_w(int data)
0211 {
0212   kb_wait();
0213   kbd_write_command(data);
0214 }
0215 
0216 static void kbd_write_cmd(int cmd)
0217 {
0218   kb_wait();
0219   kbd_write_command(KBD_CCMD_WRITE_MODE);
0220   kb_wait();
0221   kbd_write_output(cmd);
0222 }
0223 
0224 /*
0225  * Check if this is a dual port controller.
0226  */
0227 static int detect_auxiliary_port(void)
0228 {
0229   int loops = 10;
0230   int retval = 0;
0231 
0232   /* Put the value 0x5A in the output buffer using the "Write
0233    * Auxiliary Device Output Buffer" command (0xD3). Poll the
0234    * Status Register for a while to see if the value really
0235    * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
0236    * bit is also set to 1 in the Status Register, we assume this
0237    * controller has an Auxiliary Port (a.k.a. Mouse Port).
0238    */
0239   kb_wait();
0240   kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF);
0241 
0242   kb_wait();
0243   kbd_write_output(0x5a); /* 0x5a is a random dummy value. */
0244 
0245   do {
0246     unsigned char status = kbd_read_status();
0247     if (status & KBD_STAT_OBF) {
0248       kbd_read_input();
0249       if (status & KBD_STAT_MOUSE_OBF) {
0250         printk( "Detected PS/2 Mouse Port.\n");
0251         retval = 1;
0252       }
0253       break;
0254     }
0255     mdelay(1);
0256   } while (--loops);
0257   return retval;
0258 }
0259 
0260 /*
0261  * Send a byte to the mouse.
0262  */
0263 static void aux_write_dev(int val)
0264 {
0265   kb_wait();
0266   kbd_write_command(KBD_CCMD_WRITE_MOUSE);
0267   kb_wait();
0268   kbd_write_output(val);
0269 }
0270 
0271 /*
0272  * Send a byte to the mouse & handle returned ack
0273  */
0274 static void aux_write_ack(int val)
0275 {
0276   kb_wait();
0277   kbd_write_command(KBD_CCMD_WRITE_MOUSE);
0278   kb_wait();
0279   kbd_write_output(val);
0280   /* we expect an ACK in response. */
0281   mouse_reply_expected++;
0282   kb_wait();
0283 }
0284 
0285 static unsigned char get_from_queue(void)
0286 {
0287   unsigned char result;
0288   result = queue->buf[queue->tail];
0289   queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
0290   return result;
0291 }
0292 
0293 static int queue_empty(void)
0294 {
0295   return queue->head == queue->tail;
0296 }
0297 
0298 /*
0299  * Random magic cookie for the aux device
0300  */
0301 #define AUX_DEV ((void *)queue)
0302 
0303 static int release_aux(void)
0304 {
0305   rtems_status_code status;
0306   if (--aux_count)
0307     return 0;
0308   kbd_write_cmd(AUX_INTS_OFF);              /* Disable controller ints */
0309   kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE);
0310   status = rtems_interrupt_handler_remove(
0311     AUX_IRQ,
0312     ps2_mouse_interrupt,
0313     NULL
0314   );
0315   assert(status == RTEMS_SUCCESSFUL);
0316   return 0;
0317 }
0318 
0319 /*
0320  * Install interrupt handler.
0321  * Enable auxiliary device.
0322  */
0323 
0324 static int open_aux(void)
0325 {
0326   rtems_status_code status;
0327 
0328   if (aux_count++) {
0329     return 0;
0330   }
0331   queue->head = queue->tail = 0;        /* Flush input queue */
0332 
0333   status = rtems_interrupt_handler_install(
0334     AUX_IRQ,
0335     "ps2_mouse",
0336     RTEMS_INTERRUPT_UNIQUE,
0337     ps2_mouse_interrupt,
0338     NULL
0339   );
0340   assert(status == RTEMS_SUCCESSFUL);
0341 
0342   kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable the auxiliary port on
0343                                                  controller. */
0344   aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
0345   kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
0346   return 0;
0347 }
0348 
0349 /*
0350  * Put bytes from input queue to buffer.
0351  */
0352 size_t read_aux(char * buffer, size_t count )
0353 {
0354   size_t i = count;
0355   unsigned char c;
0356 
0357   if (queue_empty()) {
0358     return 0;
0359   }
0360   while (i > 0 && !queue_empty()) {
0361     c = get_from_queue();
0362     *buffer++ = c;
0363     i--;
0364   }
0365   return count-i;
0366 }
0367 
0368 /*
0369  * Write to the aux device.
0370  */
0371 static int write_aux( int minor, const char * buffer, int count )
0372 {
0373   int retval = 0;
0374 
0375   if (count) {
0376     int written = 0;
0377     if (count > 32)
0378       count = 32; /* Limit to 32 bytes. */
0379     do {
0380       char c;
0381       c = *buffer++;
0382       aux_write_dev(c);
0383       written++;
0384     } while (--count);
0385     retval = -EIO;
0386     if (written) {
0387       retval = written;
0388     }
0389   }
0390   return retval;
0391 }
0392 
0393 static int psaux_init( void )
0394 {
0395   if( !detect_auxiliary_port() ) {
0396     printk( "PS/2 - mouse not found.\n" );
0397     return -EIO;
0398   }
0399   queue = (struct aux_queue *)malloc( sizeof(*queue) );
0400   memset(queue, 0, sizeof(*queue));
0401   queue->head = queue->tail = 0;
0402   queue->proc_list = NULL;
0403 
0404   #ifdef INITIALIZE_MOUSE
0405     kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */
0406     aux_write_ack(AUX_SET_SAMPLE);
0407     aux_write_ack(100);               /* 100 samples/sec */
0408     aux_write_ack(AUX_SET_RES);
0409     aux_write_ack(3);                    /* 8 counts per mm */
0410     aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
0411   #endif /* INITIALIZE_MOUSE */
0412   kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
0413   kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
0414   return 0;
0415 }
0416 
0417 /*
0418  * paux device driver INITIALIZE entry point.
0419  */
0420 rtems_device_driver paux_initialize(
0421   rtems_device_major_number major,
0422   rtems_device_minor_number minor,
0423   void                      *arg)
0424 {
0425   rtems_status_code status;
0426 
0427   /*
0428    * Set up TERMIOS
0429    */
0430   rtems_termios_initialize();
0431 
0432   printk( "PS/2 mouse probe.\n" );
0433   if( psaux_init() < 0 ) {
0434     printk("Error detecting PS/2 mouse --\n");
0435     /* we might want to finish the application here !!! */
0436   }
0437   open_aux();
0438 
0439   /*
0440    * Register the device
0441    */
0442   status = rtems_io_register_name ("/dev/mouse", major, 0);
0443   if (status != RTEMS_SUCCESSFUL) {
0444     printk("Error registering paux device!\n");
0445     rtems_fatal_error_occurred (status);
0446   }
0447   return RTEMS_SUCCESSFUL;
0448 } /* tty_initialize */
0449 
0450 static int paux_last_close(int major, int minor, void *arg)
0451 {
0452   release_aux();
0453   return 0;
0454 }
0455 
0456 /*
0457  * Write to the aux device. This routine is invoked by the
0458  * termios framework whenever the "ECHO" feature is on.
0459  * It does nothing write now.
0460  */
0461 static ssize_t write_aux_echo( int minor, const char * buffer, size_t count )
0462 {
0463   return 0;
0464 }
0465 
0466 /*
0467  * paux device driver OPEN entry point
0468  */
0469 rtems_device_driver paux_open(
0470   rtems_device_major_number major,
0471   rtems_device_minor_number minor,
0472   void                      *arg)
0473 {
0474   rtems_status_code              status;
0475   static rtems_termios_callbacks cb =
0476   {
0477     NULL,             /* firstOpen */
0478     paux_last_close,      /* lastClose */
0479     NULL,                 /* poll read  */
0480     write_aux_echo,       /* write */
0481     NULL,             /* setAttributes */
0482     NULL,             /* stopRemoteTx */
0483     NULL,             /* startRemoteTx */
0484     TERMIOS_POLLED    /* outputUsesInterrupts */
0485   };
0486 
0487   status = rtems_termios_open (major, minor, arg, &cb );
0488   termios_ttyp_paux = ( (rtems_libio_open_close_args_t *)arg)->iop->data1;
0489   return status;
0490 }
0491 
0492 /*
0493  * paux device driver CLOSE entry point
0494  */
0495 rtems_device_driver paux_close(
0496   rtems_device_major_number major,
0497   rtems_device_minor_number minor,
0498   void                      *arg)
0499 {
0500   return (rtems_termios_close (arg));
0501 }
0502 
0503 /*
0504  * paux device driver READ entry point.
0505  * Read characters from the PS/2 mouse.
0506  */
0507 rtems_device_driver paux_read(
0508   rtems_device_major_number major,
0509   rtems_device_minor_number minor,
0510   void                      *arg)
0511 {
0512   return rtems_termios_read (arg);
0513 } /* tty_read */
0514 
0515 /*
0516  * paux device driver WRITE entry point.
0517  * Write characters to the PS/2 mouse.
0518  */
0519 rtems_device_driver  paux_write(
0520   rtems_device_major_number major,
0521   rtems_device_minor_number minor,
0522   void                      *arg)
0523 {
0524   rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
0525   char                  *buffer  = rw_args->buffer;
0526   int                    maximum  = rw_args->count;
0527   rw_args->bytes_moved = write_aux( minor, buffer, maximum );
0528   return RTEMS_SUCCESSFUL;
0529 } /* tty_write */
0530 
0531 /*
0532  * Handle ioctl request.
0533  */
0534 rtems_device_driver paux_control(
0535   rtems_device_major_number major,
0536   rtems_device_minor_number minor,
0537   void                      *arg
0538 )
0539 {
0540   rtems_libio_ioctl_args_t *args = arg;
0541 
0542   switch( args->command ) {
0543     default:
0544       return rtems_termios_ioctl (arg);
0545       break;
0546 
0547     case MW_UID_REGISTER_DEVICE:
0548       printk( "PS2 Mouse: registering\n" );
0549       mouse_parser_initialize( "ps2" );
0550       ps2_set_driver_handler( minor, mouse_parser_enqueue );
0551       break;
0552 
0553     case MW_UID_UNREGISTER_DEVICE:
0554 /*
0555       unregister_mou_msg_queue( -1 );
0556 */
0557       ps2_set_driver_handler( minor, NULL );
0558       break;
0559   }
0560   args->ioctl_return = 0;
0561   return RTEMS_SUCCESSFUL;
0562 }