source: rtems/bsps/i386/pc386/console/ps2_mouse.c

Last change on this file was 150dcf5, checked in by Kinsey Moore <kinsey.moore@…>, on 01/02/24 at 20:36:52

libio: Clean up usage of rtems_termios_device_mode

This cleans up outputUsesInterrupts usage with rtems_termios_device_mode
enum values. The outputUsesInterrupts member was typed as an int, named
as if it were a boolean value, and used as if it were a
rtems_termios_device_mode enum. In this patch, values assigned to
outputUsesInterrupts have been converted to the corresponding
rtems_termios_device_mode enum value, conversions from
deviceOutputUsesInterrupts have been made explicit, and uses of
rtems_termios_device_mode enum values with deviceOutputUsesInterrupts
have been converted to booleans.

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