source: rtems/c/src/lib/libbsp/i386/pc386/console/ps2_mouse.c @ 3cbb63a

4.104.114.84.95
Last change on this file since 3cbb63a was 3cbb63a, checked in by Joel Sherrill <joel.sherrill@…>, on Aug 30, 2000 at 8:15:30 AM

2000-08-26 Rosimildo da Silva <rdasilva@…>

  • Major rework of the "/dev/console" driver.
  • Added termios support for stdin ( keyboard ).
  • Added ioctls() to support modes similar to Linux( XLATE, RAW, MEDIUMRAW ).
  • Added Keyboard mapping and handling of the keyboard's leds.
  • Added Micro FrameBuffer? driver ( "/dev/fb0" ) for bare VGA controller ( 16 colors ).
  • Added PS/2 and Serial mouse support for PC386 BSP.
  • console/defkeymap.c: New file.
  • console/fb_vga.c: New file.
  • console/fb_vga.h: New file.
  • console/i386kbd.h: New file.
  • console/kd.h: New file.
  • console/keyboard.c: New file.
  • console/keyboard.h: New file.
  • console/mouse_parser.c: New file.
  • console/mouse_parser.h: New file.
  • console/pc_keyb.c: New file.
  • console/ps2_drv.h: New file.
  • console/ps2_mouse.c: New file.
  • console/ps2_mouse.h: New file.
  • console/serial_mouse.c: New file.
  • console/serial_mouse.h: New file.
  • console/vgainit.c: New file.
  • console/vt.c: New file.
  • console/Makefile.am: Reflect new files.
  • console/console.c, console/inch.c, console/outch.c: Console functionality modifications.
  • startup/Makefile.am: Pick up tty_drv.c and gdb_glue.c
  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 * linux/drivers/char/pc_keyb.c
3 * Separation of the PC low-level part by Geert Uytterhoeven, May 1997
4 * See keyboard.c for the whole history.
5 * Major cleanup by Martin Mares, May 1997
6 * Combined the keyboard and PS/2 mouse handling into one file,
7 * because they share the same hardware.
8 * Johan Myreen <jem@iki.fi> 1998-10-08.
9 * Code fixes to handle mouse ACKs properly.
10 * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
11 *
12 * RTEMS port: by Rosimildo da Silva.
13 * This module was ported from Linux.
14 *
15 */
16
17#include <stdlib.h>
18#include <stdio.h>
19#include <errno.h>
20#include <sys/types.h>
21
22#include <bsp.h>
23#include <irq.h>
24#include <rtems/libio.h>
25#include <termios.h>
26#include <i386_io.h>
27#include <rtems/mw_uid.h>
28
29#define  INITIALIZE_MOUSE
30/* Some configuration switches are present in the include file... */
31#include "ps2_mouse.h"
32#include "mouse_parser.h"
33
34static void kbd_write_command_w(int data);
35static void kbd_write_output_w(int data);
36
37static unsigned char handle_kbd_event(void);
38
39/* used only by send_data - set by keyboard_interrupt */
40static volatile unsigned char reply_expected = 0;
41static volatile unsigned char acknowledge = 0;
42static volatile unsigned char resend = 0;
43
44/*
45 *      PS/2 Auxiliary Device
46 */
47static int psaux_init(void);
48
49static struct aux_queue *queue; /* Mouse data buffer. */
50static int aux_count = 0;
51/* used when we send commands to the mouse that expect an ACK. */
52static unsigned char mouse_reply_expected = 0;
53
54#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
55#define AUX_INTS_ON  (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
56#define MAX_RETRIES     60              /* some aux operations take long time*/
57
58static void ps2_mouse_interrupt();
59
60static void ( *driver_input_handler_ps2 )( void *,  char *, int ) = 0;
61
62/*
63 * This routine sets the handler to handle the characters received
64 * from the serial port.
65 */
66void ps2_set_driver_handler( int port, void ( *handler )( void *,  char *, int ) )
67{
68   driver_input_handler_ps2 = handler;
69}
70
71static void mdelay( unsigned long t )
72{
73  Wait_X_ms( t );
74}
75
76
77static void*    termios_ttyp_paux = NULL;
78
79static void
80isr_on(const rtems_irq_connect_data *unused)
81{
82  return;
83}
84                                                   
85static void
86isr_off(const rtems_irq_connect_data *unused)
87{
88  return;
89}
90
91static int isr_is_on(const rtems_irq_connect_data *irq)
92{
93  return BSP_irq_enabled_at_i8259s( irq->name );
94}
95
96
97static rtems_irq_connect_data ps2_isr_data = { AUX_IRQ,
98                                                   ps2_mouse_interrupt, isr_on,  isr_off, isr_is_on };
99
100
101/*
102 * Wait for keyboard controller input buffer to drain.
103 *
104 * Don't use 'jiffies' so that we don't depend on
105 * interrupts..
106 *
107 * Quote from PS/2 System Reference Manual:
108 *
109 * "Address hex 0060 and address hex 0064 should be written only when
110 * the input-buffer-full bit and output-buffer-full bit in the
111 * Controller Status register are set 0."
112 */
113
114static void kb_wait(void)
115{
116        unsigned long timeout = KBC_TIMEOUT;
117
118        do {
119                /*
120                 * "handle_kbd_event()" will handle any incoming events
121                 * while we wait - keypresses or mouse movement.
122                 */
123                unsigned char status = handle_kbd_event();
124
125                if (! (status & KBD_STAT_IBF))
126                        return;
127
128                mdelay(1); 
129
130                timeout--;
131        } while (timeout);
132#ifdef KBD_REPORT_TIMEOUTS
133        printk( "Keyboard timed out[1]\n");
134#endif
135}
136
137static int do_acknowledge(unsigned char scancode)
138{
139        if (reply_expected) {
140          /* Unfortunately, we must recognise these codes only if we know they
141           * are known to be valid (i.e., after sending a command), because there
142           * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have
143           * keys with such codes :(
144           */
145                if (scancode == KBD_REPLY_ACK) {
146                        acknowledge = 1;
147                        reply_expected = 0;
148                        return 0;
149                } else if (scancode == KBD_REPLY_RESEND) {
150                        resend = 1;
151                        reply_expected = 0;
152                        return 0;
153                }
154                /* Should not happen... */
155#if 0
156                printk( "keyboard reply expected - got %02x\n",
157                       scancode);
158#endif
159        }
160        return 1;
161}
162
163
164static inline void handle_mouse_event(unsigned char scancode)
165{
166        if (mouse_reply_expected) {
167                if (scancode == AUX_ACK) {
168                        mouse_reply_expected--;
169                        return;
170                }
171                mouse_reply_expected = 0;
172        }
173
174        if (aux_count) {
175                int head = queue->head;
176
177                queue->buf[head] = scancode;
178                head = (head + 1) & (AUX_BUF_SIZE-1);
179                if (head != queue->tail) {
180                        queue->head = head;
181                }
182      /* if the input queue is active, add to it */
183      if( driver_input_handler_ps2 )
184      {
185          driver_input_handler_ps2( NULL,  &scancode, 1 );
186      }
187      else
188      {
189         /* post this byte to termios */
190              rtems_termios_enqueue_raw_characters( termios_ttyp_paux, &scancode, 1 );
191      }
192        }
193}
194
195/*
196 * This reads the keyboard status port, and does the
197 * appropriate action.
198 *
199 * It requires that we hold the keyboard controller
200 * spinlock.
201 */
202static unsigned char handle_kbd_event(void)
203{
204        unsigned char status = kbd_read_status();
205        unsigned int work = 10000;
206
207        while (status & KBD_STAT_OBF) {
208                unsigned char scancode;
209
210                scancode = kbd_read_input();
211
212                if (status & KBD_STAT_MOUSE_OBF) {
213                        handle_mouse_event(scancode);
214                } else {
215                        do_acknowledge(scancode);
216         printk("pc_keyb: %X ", scancode );
217                }
218                status = kbd_read_status();
219                if(!work--)
220                {
221                        printk("pc_keyb: controller jammed (0x%02X).\n",
222                                status);
223                        break;
224                }
225        }
226
227        return status;
228}
229
230
231static void ps2_mouse_interrupt()
232{
233        handle_kbd_event();
234}
235
236/*
237 * send_data sends a character to the keyboard and waits
238 * for an acknowledge, possibly retrying if asked to. Returns
239 * the success status.
240 *
241 * Don't use 'jiffies', so that we don't depend on interrupts
242 */
243static int send_data(unsigned char data)
244{
245        int retries = 3;
246
247        do {
248                unsigned long timeout = KBD_TIMEOUT;
249
250                acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */
251                resend = 0;
252                reply_expected = 1;
253                kbd_write_output_w(data);
254                for (;;) {
255                        if (acknowledge)
256                                return 1;
257                        if (resend)
258                                break;
259                        mdelay(1);
260                        if (!--timeout) {
261#ifdef KBD_REPORT_TIMEOUTS
262                                printk( "Keyboard timeout[2]\n");
263#endif
264                                return 0;
265                        }
266                }
267        } while (retries-- > 0);
268#ifdef KBD_REPORT_TIMEOUTS
269        printk( "keyboard: Too many NACKs -- noisy kbd cable?\n");
270#endif
271        return 0;
272}
273
274#define KBD_NO_DATA     (-1)    /* No data */
275#define KBD_BAD_DATA    (-2)    /* Parity or other error */
276
277static int kbd_read_data(void)
278{
279        int retval = KBD_NO_DATA;
280        unsigned char status;
281
282        status = kbd_read_status();
283        if (status & KBD_STAT_OBF) {
284                unsigned char data = kbd_read_input();
285
286                retval = data;
287                if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
288                        retval = KBD_BAD_DATA;
289        }
290        return retval;
291}
292
293static void kbd_clear_input(void)
294{
295        int maxread = 100;      /* Random number */
296
297        do {
298                if (kbd_read_data() == KBD_NO_DATA)
299                        break;
300        } while (--maxread);
301}
302
303static int kbd_wait_for_input(void)
304{
305        long timeout = KBD_INIT_TIMEOUT;
306
307        do {
308                int retval = kbd_read_data();
309                if (retval >= 0)
310                        return retval;
311                mdelay(1);
312        } while (--timeout);
313        return -1;
314}
315
316static void kbd_write_command_w(int data)
317{
318        kb_wait();
319        kbd_write_command(data);
320}
321
322static void kbd_write_output_w(int data)
323{
324        kb_wait();
325        kbd_write_output(data);
326}
327
328static void kbd_write_cmd(int cmd)
329{
330        kb_wait();
331        kbd_write_command(KBD_CCMD_WRITE_MODE);
332        kb_wait();
333        kbd_write_output(cmd);
334}
335
336/*
337 * Check if this is a dual port controller.
338 */
339static int detect_auxiliary_port(void)
340{
341        int loops = 10;
342        int retval = 0;
343
344        /* Put the value 0x5A in the output buffer using the "Write
345         * Auxiliary Device Output Buffer" command (0xD3). Poll the
346         * Status Register for a while to see if the value really
347         * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
348         * bit is also set to 1 in the Status Register, we assume this
349         * controller has an Auxiliary Port (a.k.a. Mouse Port).
350         */
351        kb_wait();
352        kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF);
353
354        kb_wait();
355        kbd_write_output(0x5a); /* 0x5a is a random dummy value. */
356
357        do {
358                unsigned char status = kbd_read_status();
359
360                if (status & KBD_STAT_OBF) {
361                        (void) kbd_read_input();
362                        if (status & KBD_STAT_MOUSE_OBF) {
363                                printk( "Detected PS/2 Mouse Port.\n");
364                                retval = 1;
365                        }
366                        break;
367                }
368                mdelay(1);
369        } while (--loops);
370        return retval;
371}
372
373/*
374 * Send a byte to the mouse.
375 */
376static void aux_write_dev(int val)
377{
378        kb_wait();
379        kbd_write_command(KBD_CCMD_WRITE_MOUSE);
380        kb_wait();
381        kbd_write_output(val);
382}
383
384/*
385 * Send a byte to the mouse & handle returned ack
386 */
387static void aux_write_ack(int val)
388{
389        kb_wait();
390        kbd_write_command(KBD_CCMD_WRITE_MOUSE);
391        kb_wait();
392        kbd_write_output(val);
393        /* we expect an ACK in response. */
394        mouse_reply_expected++;
395        kb_wait();
396}
397
398static unsigned char get_from_queue(void)
399{
400        unsigned char result;
401        result = queue->buf[queue->tail];
402        queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
403        return result;
404}
405
406
407static int queue_empty(void)
408{
409        return queue->head == queue->tail;
410}
411
412/*
413 * Random magic cookie for the aux device
414 */
415#define AUX_DEV ((void *)queue)
416
417static int release_aux()
418{
419        if (--aux_count)
420                return 0;
421        kbd_write_cmd(AUX_INTS_OFF);                        /* Disable controller ints */
422        kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE);
423
424   BSP_remove_rtems_irq_handler( &ps2_isr_data );
425        return 0;
426}
427/*
428 * Install interrupt handler.
429 * Enable auxiliary device.
430 */
431
432static int open_aux()
433{
434  rtems_status_code status;
435
436        if (aux_count++) {
437                return 0;
438        }
439        queue->head = queue->tail = 0;          /* Flush input queue */
440
441
442   status = BSP_install_rtems_irq_handler( &ps2_isr_data );
443   if( !status )
444        {
445          printk("Error installing ps2-mouse interrupt handler!\n" );
446          rtems_fatal_error_occurred( status );
447        }
448
449        kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE);     /* Enable the
450                                                           auxiliary port on
451                                                           controller. */
452        aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
453        kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
454        return 0;
455}
456
457/*
458 * Put bytes from input queue to buffer.
459 */
460size_t read_aux(char * buffer, size_t count )
461{
462        size_t i = count;
463        unsigned char c;
464
465        if (queue_empty()) 
466        {
467                return 0;
468   }
469        while (i > 0 && !queue_empty()) 
470        {
471                c = get_from_queue();
472                *buffer++ = c;
473                i--;
474        }
475        return count-i;
476}
477
478/*
479 * Write to the aux device.
480 */
481
482static int write_aux( int minor, const char * buffer, int count )
483{
484        int retval = 0;
485
486        if (count) {
487                int written = 0;
488
489                if (count > 32)
490                        count = 32; /* Limit to 32 bytes. */
491                do {
492                        char c;
493                        c = *buffer++;
494                        aux_write_dev(c);
495                        written++;
496                } while (--count);
497                retval = -EIO;
498                if (written) {
499                        retval = written;
500                }
501        }
502        return retval;
503}
504
505static unsigned int aux_poll()
506{
507        if( !queue_empty() )
508                return 1;
509        return 0;
510}
511
512static int psaux_init( void )
513{
514        if( !detect_auxiliary_port() )
515   {
516        printk( "PS/2 - mouse not found.\n" );
517                return -EIO;
518   }
519
520        queue = (struct aux_queue *)malloc( sizeof(*queue) );
521   memset(queue, 0, sizeof(*queue));
522        queue->head = queue->tail = 0;
523        queue->proc_list = NULL;
524
525#ifdef INITIALIZE_MOUSE
526        kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */
527        aux_write_ack(AUX_SET_SAMPLE);
528        aux_write_ack(100);                           /* 100 samples/sec */
529        aux_write_ack(AUX_SET_RES);
530        aux_write_ack(3);                                /* 8 counts per mm */
531        aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
532#endif /* INITIALIZE_MOUSE */
533        kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
534        kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
535        return 0;
536}
537
538void paux_reserve_resources(rtems_configuration_table *conf)
539{
540  rtems_termios_reserve_resources(conf, 1);
541  return;
542}
543
544/*
545 * paux device driver INITIALIZE entry point.
546 */
547rtems_device_driver
548paux_initialize(  rtems_device_major_number major,
549                   rtems_device_minor_number minor,
550                   void                      *arg)
551{
552   rtems_status_code status;
553
554  /*
555   * Set up TERMIOS
556   */
557   rtems_termios_initialize();
558
559        printk( "PS/2 mouse probe.\n" );
560   if( psaux_init() < 0 )
561   {
562      printk("Error detecting PS/2 mouse --\n");
563
564      /* we might want to finish the application here !!! */
565   }
566   open_aux();
567
568  /*
569   * Register the device
570   */
571  status = rtems_io_register_name ("/dev/mouse", major, 0);
572  if (status != RTEMS_SUCCESSFUL)
573  {
574      printk("Error registering paux device!\n");
575      rtems_fatal_error_occurred (status);
576  }
577  return RTEMS_SUCCESSFUL;
578} /* tty_initialize */
579
580
581static int paux_last_close(int major, int minor, void *arg)
582{
583  release_aux();
584  return 0;
585}
586
587/*
588 * Write to the aux device. This routine is invoked by the
589 * termios framework whenever the "ECHO" feature is on.
590 * It does nothing write now.
591 */
592static int write_aux_echo( int minor, const char * buffer, int count )
593{ 
594   return 0;
595}
596
597
598/*
599 * Some initialization if necessary
600 */
601static rtems_device_driver
602paux_first_open( rtems_device_minor_number major,
603                 rtems_device_minor_number minor,
604                 void                      *arg)
605{
606   return RTEMS_SUCCESSFUL;
607}
608
609
610/*
611 * paux device driver OPEN entry point
612 */
613rtems_device_driver
614paux_open(rtems_device_major_number major,
615                rtems_device_minor_number minor,
616                void                      *arg)
617{
618  rtems_status_code              status;
619  static rtems_termios_callbacks cb = 
620  {
621    NULL,                     /* firstOpen */
622    paux_last_close,      /* lastClose */
623    NULL,                 /* poll read  */
624    write_aux_echo,       /* write */
625    NULL,                     /* setAttributes */
626    NULL,                     /* stopRemoteTx */
627    NULL,                     /* startRemoteTx */
628    0                            /* outputUsesInterrupts */
629  };
630
631  status = rtems_termios_open (major, minor, arg, &cb );
632  termios_ttyp_paux = ( (rtems_libio_open_close_args_t *)arg)->iop->data1;
633  return status;
634}
635
636/*
637 * paux device driver CLOSE entry point
638 */
639rtems_device_driver
640paux_close(rtems_device_major_number major,
641              rtems_device_minor_number minor,
642              void                      *arg)
643{
644  return (rtems_termios_close (arg));
645}
646
647 
648/*
649 * paux device driver READ entry point.
650 * Read characters from the PS/2 mouse.
651 */
652rtems_device_driver
653paux_read(rtems_device_major_number major,
654             rtems_device_minor_number minor,
655             void                      *arg)
656{
657  return rtems_termios_read (arg);
658} /* tty_read */
659 
660
661/*
662 * paux device driver WRITE entry point.
663 * Write characters to the PS/2 mouse.
664 */
665rtems_device_driver
666paux_write(rtems_device_major_number major,
667              rtems_device_minor_number minor,
668              void                    * arg)
669{
670  rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
671  char                  *buffer  = rw_args->buffer;
672  int                    maximum  = rw_args->count;
673  rw_args->bytes_moved = write_aux( minor, buffer, maximum );
674  return RTEMS_SUCCESSFUL;
675} /* tty_write */
676
677 
678/*
679 * Handle ioctl request.
680 */
681rtems_device_driver
682paux_control(rtems_device_major_number major,
683                rtems_device_minor_number minor,
684                void                      * arg
685)
686{ 
687        rtems_libio_ioctl_args_t *args = arg;
688        switch( args->command ) 
689        {
690           default:
691      return rtems_termios_ioctl (arg);
692                break;
693
694      case MW_UID_REGISTER_DEVICE:
695      printk( "PS2 Mouse: reg=%s\n", args->buffer );
696      register_mou_msg_queue( args->buffer, -1 );
697                break;
698
699      case MW_UID_UNREGISTER_DEVICE:
700      unregister_mou_msg_queue( -1 );
701                break;
702   }
703        args->ioctl_return = 0;
704   return RTEMS_SUCCESSFUL;
705}
706
707
708
Note: See TracBrowser for help on using the repository browser.