source: rtems/c/src/lib/libbsp/i386/pc386/console/ps2_mouse.c @ 2daa19f

4.104.115
Last change on this file since 2daa19f was 2daa19f, checked in by Ralf Corsepius <ralf.corsepius@…>, on 04/12/10 at 16:34:31

Reflect termios API changes.

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