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

4.104.114.84.95
Last change on this file since f471a45c was f471a45c, checked in by Ralf Corsepius <ralf.corsepius@…>, on Aug 12, 2002 at 5:12:39 AM

2002-08-12 Ralf Corsepius <corsepiu@…>

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