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

4.115
Last change on this file since 441b90e was 1fef02ca, checked in by Joel Sherrill <joel.sherrill@…>, on 03/14/11 at 14:57:00

2011-03-14 Joel Sherrill <joel.sherrill@…>

PR 1762/cpukit

  • Makefile.am, preinstall.am, console/console.c, console/keyboard.c, console/keyboard.h, console/pc_keyb.c, console/ps2_mouse.c, console/vgainit.c: Made mouse parser engine generic. Now use generic serial mouse driver. Moved many externs from C to .h.
  • console/kbd_parser.c, console/serial_mouse_config.c: New files.
  • console/mouse_parser.c, console/mouse_parser.h, console/serial_mouse.c, console/serial_mouse.h: Removed.
  • Property mode set to 100644
File size: 13.3 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 *
14 *  $Id$
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 <bsp/irq.h>
25#include <rtems/libio.h>
26#include <termios.h>
27#include <i386_io.h>
28#include <rtems/mw_uid.h>
29#include <rtems/mouse_parser.h>
30
31#define  INITIALIZE_MOUSE
32/* Some configuration switches are present in the include file... */
33#include "ps2_mouse.h"
34
35static void kbd_write_command_w(int data);
36#if 0
37static void kbd_write_output_w(int data);
38#endif
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
61static void ps2_mouse_interrupt(rtems_irq_hdl_param);
62static mouse_parser_enqueue_handler driver_input_handler_ps2 = NULL;
63
64/*
65 * This routine sets the handler to handle the characters received
66 * from the serial port.
67 */
68void ps2_set_driver_handler(
69  int                          port,
70  mouse_parser_enqueue_handler handler
71)
72{
73  driver_input_handler_ps2 = handler;
74}
75
76static void mdelay( unsigned long t )
77{
78  Wait_X_ms( t );
79}
80
81static void*    termios_ttyp_paux = NULL;
82
83static void
84isr_on(const rtems_irq_connect_data *unused)
85{
86  return;
87}
88
89static void
90isr_off(const rtems_irq_connect_data *unused)
91{
92  return;
93}
94
95static int isr_is_on(const rtems_irq_connect_data *irq)
96{
97  return BSP_irq_enabled_at_i8259s( irq->name );
98}
99
100static rtems_irq_connect_data ps2_isr_data = { AUX_IRQ,
101                                               ps2_mouse_interrupt,
102                                               0,
103                                               isr_on,
104                                               isr_off,
105                                               isr_is_on };
106
107/*
108 * Wait for keyboard controller input buffer to drain.
109 *
110 * Don't use 'jiffies' so that we don't depend on
111 * interrupts..
112 *
113 * Quote from PS/2 System Reference Manual:
114 *
115 * "Address hex 0060 and address hex 0064 should be written only when
116 * the input-buffer-full bit and output-buffer-full bit in the
117 * Controller Status register are set 0."
118 */
119
120static void kb_wait(void)
121{
122  unsigned long timeout = KBC_TIMEOUT;
123
124  do {
125    /*
126     * "handle_kbd_event()" will handle any incoming events
127     * while we wait - keypresses or mouse movement.
128     */
129    unsigned char status = handle_kbd_event();
130
131    if (! (status & KBD_STAT_IBF))
132      return;
133
134    mdelay(1);
135    timeout--;
136  } while (timeout);
137
138  #ifdef KBD_REPORT_TIMEOUTS
139    printk( "Keyboard timed out[1]\n");
140  #endif
141}
142
143static int do_acknowledge(unsigned char scancode)
144{
145  if (reply_expected) {
146
147    /* Unfortunately, we must recognise these codes only if we know they
148     * are known to be valid (i.e., after sending a command), because there
149     * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have
150     * keys with such codes :(
151     */
152    if (scancode == KBD_REPLY_ACK) {
153      acknowledge = 1;
154      reply_expected = 0;
155      return 0;
156    } else if (scancode == KBD_REPLY_RESEND) {
157      resend = 1;
158      reply_expected = 0;
159      return 0;
160    }
161
162    /* Should not happen... */
163    #if 0
164      printk( "keyboard reply expected - got %02x\n", scancode);
165    #endif
166  }
167  return 1;
168}
169
170static inline void handle_mouse_event(unsigned char scancode)
171{
172  if (mouse_reply_expected) {
173    if (scancode == AUX_ACK) {
174      mouse_reply_expected--;
175      return;
176    }
177    mouse_reply_expected = 0;
178  }
179
180  if (aux_count) {
181    int head = queue->head;
182    queue->buf[head] = scancode;
183    head = (head + 1) & (AUX_BUF_SIZE-1);
184    if (head != queue->tail) {
185      queue->head = head;
186    }
187
188    /* if the input queue is active, add to it */
189    if( driver_input_handler_ps2 ) {
190      driver_input_handler_ps2( &scancode, 1 );
191    } else {
192      /* post this byte to termios */
193      rtems_termios_enqueue_raw_characters( termios_ttyp_paux, (char *)&scancode, 1 );
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    scancode = kbd_read_input();
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      printk("pc_keyb: controller jammed (0x%02X).\n", status);
222      break;
223    }
224  }
225  return status;
226}
227
228static void ps2_mouse_interrupt(rtems_irq_hdl_param ignored)
229{
230  handle_kbd_event();
231}
232
233static void kbd_write_command_w(int data)
234{
235  kb_wait();
236  kbd_write_command(data);
237}
238
239static void kbd_write_cmd(int cmd)
240{
241  kb_wait();
242  kbd_write_command(KBD_CCMD_WRITE_MODE);
243  kb_wait();
244  kbd_write_output(cmd);
245}
246
247/*
248 * Check if this is a dual port controller.
249 */
250static int detect_auxiliary_port(void)
251{
252  int loops = 10;
253  int retval = 0;
254
255  /* Put the value 0x5A in the output buffer using the "Write
256   * Auxiliary Device Output Buffer" command (0xD3). Poll the
257   * Status Register for a while to see if the value really
258   * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
259   * bit is also set to 1 in the Status Register, we assume this
260   * controller has an Auxiliary Port (a.k.a. Mouse Port).
261   */
262  kb_wait();
263  kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF);
264
265  kb_wait();
266  kbd_write_output(0x5a); /* 0x5a is a random dummy value. */
267
268  do {
269    unsigned char status = kbd_read_status();
270    if (status & KBD_STAT_OBF) {
271      kbd_read_input();
272      if (status & KBD_STAT_MOUSE_OBF) {
273        printk( "Detected PS/2 Mouse Port.\n");
274        retval = 1;
275      }
276      break;
277    }
278    mdelay(1);
279  } while (--loops);
280  return retval;
281}
282
283/*
284 * Send a byte to the mouse.
285 */
286static void aux_write_dev(int val)
287{
288  kb_wait();
289  kbd_write_command(KBD_CCMD_WRITE_MOUSE);
290  kb_wait();
291  kbd_write_output(val);
292}
293
294/*
295 * Send a byte to the mouse & handle returned ack
296 */
297static void aux_write_ack(int val)
298{
299  kb_wait();
300  kbd_write_command(KBD_CCMD_WRITE_MOUSE);
301  kb_wait();
302  kbd_write_output(val);
303  /* we expect an ACK in response. */
304  mouse_reply_expected++;
305  kb_wait();
306}
307
308static unsigned char get_from_queue(void)
309{
310  unsigned char result;
311  result = queue->buf[queue->tail];
312  queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
313  return result;
314}
315
316static int queue_empty(void)
317{
318  return queue->head == queue->tail;
319}
320
321/*
322 * Random magic cookie for the aux device
323 */
324#define AUX_DEV ((void *)queue)
325
326static int release_aux(void)
327{
328  if (--aux_count)
329    return 0;
330  kbd_write_cmd(AUX_INTS_OFF);                      /* Disable controller ints */
331  kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE);
332  BSP_remove_rtems_irq_handler( &ps2_isr_data );
333  return 0;
334}
335
336/*
337 * Install interrupt handler.
338 * Enable auxiliary device.
339 */
340
341static int open_aux(void)
342{
343  int status;
344
345  if (aux_count++) {
346    return 0;
347  }
348  queue->head = queue->tail = 0;                /* Flush input queue */
349
350  status = BSP_install_rtems_irq_handler( &ps2_isr_data );
351  if( !status ) {
352    printk("Error installing ps2-mouse interrupt handler!\n" );
353    rtems_fatal_error_occurred( status );
354  }
355
356  kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable the auxiliary port on
357                                                 controller. */
358  aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
359  kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
360  return 0;
361}
362
363/*
364 * Put bytes from input queue to buffer.
365 */
366size_t read_aux(char * buffer, size_t count )
367{
368  size_t i = count;
369  unsigned char c;
370
371  if (queue_empty()) {
372    return 0;
373  }
374  while (i > 0 && !queue_empty()) {
375    c = get_from_queue();
376    *buffer++ = c;
377    i--;
378  }
379  return count-i;
380}
381
382/*
383 * Write to the aux device.
384 */
385static int write_aux( int minor, const char * buffer, int count )
386{
387  int retval = 0;
388
389  if (count) {
390    int written = 0;
391    if (count > 32)
392      count = 32; /* Limit to 32 bytes. */
393    do {
394      char c;
395      c = *buffer++;
396      aux_write_dev(c);
397      written++;
398    } while (--count);
399    retval = -EIO;
400    if (written) {
401      retval = written;
402    }
403  }
404  return retval;
405}
406
407static int psaux_init( void )
408{
409  if( !detect_auxiliary_port() ) {
410    printk( "PS/2 - mouse not found.\n" );
411    return -EIO;
412  }
413  queue = (struct aux_queue *)malloc( sizeof(*queue) );
414  memset(queue, 0, sizeof(*queue));
415  queue->head = queue->tail = 0;
416  queue->proc_list = NULL;
417
418  #ifdef INITIALIZE_MOUSE
419    kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */
420    aux_write_ack(AUX_SET_SAMPLE);
421    aux_write_ack(100);                       /* 100 samples/sec */
422    aux_write_ack(AUX_SET_RES);
423    aux_write_ack(3);                            /* 8 counts per mm */
424    aux_write_ack(AUX_SET_SCALE21);     /* 2:1 scaling */
425  #endif /* INITIALIZE_MOUSE */
426  kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */
427  kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */
428  return 0;
429}
430
431/*
432 * paux device driver INITIALIZE entry point.
433 */
434rtems_device_driver paux_initialize( 
435  rtems_device_major_number major,
436  rtems_device_minor_number minor,
437  void                      *arg)
438{
439  rtems_status_code status;
440
441  /*
442   * Set up TERMIOS
443   */
444  rtems_termios_initialize();
445
446  printk( "PS/2 mouse probe.\n" );
447  if( psaux_init() < 0 ) {
448    printk("Error detecting PS/2 mouse --\n");
449    /* we might want to finish the application here !!! */
450  }
451  open_aux();
452
453  /*
454   * Register the device
455   */
456  status = rtems_io_register_name ("/dev/mouse", major, 0);
457  if (status != RTEMS_SUCCESSFUL) {
458    printk("Error registering paux device!\n");
459    rtems_fatal_error_occurred (status);
460  }
461  return RTEMS_SUCCESSFUL;
462} /* tty_initialize */
463
464static int paux_last_close(int major, int minor, void *arg)
465{
466  release_aux();
467  return 0;
468}
469
470/*
471 * Write to the aux device. This routine is invoked by the
472 * termios framework whenever the "ECHO" feature is on.
473 * It does nothing write now.
474 */
475static ssize_t write_aux_echo( int minor, const char * buffer, size_t count )
476{
477  return 0;
478}
479
480/*
481 * paux device driver OPEN entry point
482 */
483rtems_device_driver paux_open(
484  rtems_device_major_number major,
485  rtems_device_minor_number minor,
486  void                      *arg)
487{
488  rtems_status_code              status;
489  static rtems_termios_callbacks cb =
490  {
491    NULL,                 /* firstOpen */
492    paux_last_close,      /* lastClose */
493    NULL,                 /* poll read  */
494    write_aux_echo,       /* write */
495    NULL,                 /* setAttributes */
496    NULL,                 /* stopRemoteTx */
497    NULL,                 /* startRemoteTx */
498    0                     /* outputUsesInterrupts */
499  };
500
501  status = rtems_termios_open (major, minor, arg, &cb );
502  termios_ttyp_paux = ( (rtems_libio_open_close_args_t *)arg)->iop->data1;
503  return status;
504}
505
506/*
507 * paux device driver CLOSE entry point
508 */
509rtems_device_driver paux_close(
510  rtems_device_major_number major,
511  rtems_device_minor_number minor,
512  void                      *arg)
513{
514  return (rtems_termios_close (arg));
515}
516
517/*
518 * paux device driver READ entry point.
519 * Read characters from the PS/2 mouse.
520 */
521rtems_device_driver paux_read(
522  rtems_device_major_number major,
523  rtems_device_minor_number minor,
524  void                      *arg)
525{
526  return rtems_termios_read (arg);
527} /* tty_read */
528
529/*
530 * paux device driver WRITE entry point.
531 * Write characters to the PS/2 mouse.
532 */
533rtems_device_driver  paux_write(
534  rtems_device_major_number major,
535  rtems_device_minor_number minor,
536  void                      *arg)
537{
538  rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg;
539  char                  *buffer  = rw_args->buffer;
540  int                    maximum  = rw_args->count;
541  rw_args->bytes_moved = write_aux( minor, buffer, maximum );
542  return RTEMS_SUCCESSFUL;
543} /* tty_write */
544
545/*
546 * Handle ioctl request.
547 */
548rtems_device_driver paux_control(
549  rtems_device_major_number major,
550  rtems_device_minor_number minor,
551  void                      *arg
552)
553{
554  rtems_libio_ioctl_args_t *args = arg;
555
556  switch( args->command ) {
557    default:
558      return rtems_termios_ioctl (arg);
559      break;
560
561    case MW_UID_REGISTER_DEVICE:
562      printk( "PS2 Mouse: registering\n" );
563      mouse_parser_initialize( "ps2" );
564      ps2_set_driver_handler( minor, mouse_parser_enqueue );
565      break;
566
567    case MW_UID_UNREGISTER_DEVICE:
568/*
569      unregister_mou_msg_queue( -1 );
570*/
571      ps2_set_driver_handler( minor, NULL );
572      break;
573  }
574  args->ioctl_return = 0;
575  return RTEMS_SUCCESSFUL;
576}
Note: See TracBrowser for help on using the repository browser.