root/kernel/chr_drv/psaux.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. aux_write_dev
  2. aux_write_ack
  3. aux_write_cmd
  4. get_from_queue
  5. queue_empty
  6. aux_interrupt
  7. qp_interrupt
  8. release_aux
  9. release_qp
  10. open_aux
  11. open_qp
  12. write_aux
  13. write_qp
  14. read_aux
  15. aux_select
  16. psaux_init
  17. poll_aux_status
  18. poll_qp_status
  19. read_710
  20. probe_qp

   1 /*
   2  * linux/kernel/chr_drv/psaux.c
   3  *
   4  * Driver for PS/2 type mouse by Johan Myreen.
   5  *
   6  * Supports pointing devices attached to a PS/2 type
   7  * Keyboard and Auxiliary Device Controller.
   8  *
   9  * Corrections in device setup for some laptop mice & trackballs.
  10  * 02Feb93  (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
  11  *
  12  * Changed to prevent keyboard lockups on AST Power Exec.
  13  * 28Jul93  Brad Bosch - brad@lachman.com
  14  *
  15  * Modified by Johan Myreen (jem@cs.hut.fi) 04Aug93
  16  *   to include support for QuickPort mouse.
  17  */
  18 
  19 /* Uncomment the following line if your mouse needs initialization. */
  20 
  21 /* #define INITIALIZE_DEVICE */
  22 
  23 #include <linux/timer.h>
  24 #include <linux/sched.h>
  25 #include <linux/kernel.h>
  26 #include <linux/fcntl.h>
  27 #include <linux/errno.h>
  28 
  29 #include <asm/io.h>
  30 #include <asm/segment.h>
  31 #include <asm/system.h>
  32 
  33 #include <linux/config.h>
  34 
  35 /* aux controller ports */
  36 #define AUX_INPUT_PORT  0x60            /* Aux device output buffer */
  37 #define AUX_OUTPUT_PORT 0x60            /* Aux device input buffer */
  38 #define AUX_COMMAND     0x64            /* Aux device command buffer */
  39 #define AUX_STATUS      0x64            /* Aux device status reg */
  40 
  41 /* aux controller status bits */
  42 #define AUX_OBUF_FULL   0x21            /* output buffer (from device) full */
  43 #define AUX_IBUF_FULL   0x02            /* input buffer (to device) full */
  44 
  45 /* aux controller commands */
  46 #define AUX_CMD_WRITE   0x60            /* value to write to controller */
  47 #define AUX_MAGIC_WRITE 0xd4            /* value to send aux device data */
  48 
  49 #define AUX_INTS_ON     0x47            /* enable controller interrupts */
  50 #define AUX_INTS_OFF    0x65            /* disable controller interrupts */
  51 
  52 #define AUX_DISABLE     0xa7            /* disable aux */
  53 #define AUX_ENABLE      0xa8            /* enable aux */
  54 
  55 /* aux device commands */
  56 #define AUX_SET_RES     0xe8            /* set resolution */
  57 #define AUX_SET_SCALE11 0xe6            /* set 1:1 scaling */
  58 #define AUX_SET_SCALE21 0xe7            /* set 2:1 scaling */
  59 #define AUX_GET_SCALE   0xe9            /* get scaling factor */
  60 #define AUX_SET_STREAM  0xea            /* set stream mode */
  61 #define AUX_SET_SAMPLE  0xf3            /* set sample rate */
  62 #define AUX_ENABLE_DEV  0xf4            /* enable aux device */
  63 #define AUX_DISABLE_DEV 0xf5            /* disable aux device */
  64 #define AUX_RESET       0xff            /* reset aux device */
  65 
  66 #define MAX_RETRIES     30              /* some aux operations take long time*/
  67 #define AUX_IRQ         12
  68 #define AUX_BUF_SIZE    2048
  69 
  70 /* QuickPort definitions */
  71 
  72 #define QP_DATA         0x310           /* Data Port I/O Address */
  73 #define QP_STATUS       0x311           /* Status Port I/O Address */
  74 
  75 #define QP_DEV_IDLE     0x01            /* Device Idle */
  76 #define QP_RX_FULL      0x02            /* Device Char received */
  77 #define QP_TX_IDLE      0x04            /* Device XMIT Idle */
  78 #define QP_RESET        0x08            /* Device Reset */
  79 #define QP_INTS_ON      0x10            /* Device Interrupt On */
  80 #define QP_ERROR_FLAG   0x20            /* Device Error */
  81 #define QP_CLEAR        0x40            /* Device Clear */
  82 #define QP_ENABLE       0x80            /* Device Enable */
  83 
  84 #define QP_IRQ          12
  85 
  86 extern unsigned char aux_device_present;
  87 extern unsigned char kbd_read_mask;     /* from keyboard.c */
  88 
  89 struct aux_queue {
  90         unsigned long head;
  91         unsigned long tail;
  92         struct wait_queue *proc_list;
  93         unsigned char buf[AUX_BUF_SIZE];
  94 };
  95 
  96 static struct aux_queue *queue;
  97 static int aux_ready = 0;
  98 static int aux_busy = 0;
  99 static int aux_present = 0;
 100 static int poll_aux_status(void);
 101 
 102 #ifdef CONFIG_QUICKPORT_MOUSE
 103 static int qp_present = 0;
 104 static int qp_busy = 0;
 105 static int qp_data = QP_DATA;
 106 static int qp_status = QP_STATUS;
 107 
 108 static int poll_qp_status(void);
 109 static int probe_qp(void);
 110 #endif
 111 
 112 
 113 /*
 114  * Write to aux device
 115  */
 116 
 117 static void aux_write_dev(int val)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119         poll_aux_status();
 120         outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);    /* write magic cookie */
 121         poll_aux_status();
 122         outb_p(val,AUX_OUTPUT_PORT);            /* write data */
 123 }
 124 
 125 /*
 126  * Write to device & handle returned ack
 127  */
 128  
 129 #if defined INITIALIZE_DEVICE
 130 static int aux_write_ack(int val)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132         int retries = 0;
 133 
 134         aux_write_dev(val);             /* write the value to the device */
 135         while ((inb(AUX_STATUS) & AUX_OBUF_FULL) != AUX_OBUF_FULL
 136                     && retries < MAX_RETRIES) {          /* wait for ack */
 137                 current->state = TASK_INTERRUPTIBLE;
 138                 current->timeout = jiffies + 5;
 139                 schedule();
 140                 retries++;
 141         }
 142 
 143         if ((inb(AUX_STATUS) & AUX_OBUF_FULL) == AUX_OBUF_FULL)
 144         {
 145                 return (inb(AUX_INPUT_PORT));
 146         }
 147         return 0;
 148 }
 149 #endif /* INITIALIZE_DEVICE */
 150 
 151 /*
 152  * Write aux device command
 153  */
 154 
 155 static void aux_write_cmd(int val)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157         poll_aux_status();
 158         outb_p(AUX_CMD_WRITE,AUX_COMMAND);
 159         poll_aux_status();
 160         outb_p(val,AUX_OUTPUT_PORT);
 161 }
 162 
 163 
 164 static unsigned int get_from_queue(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166         unsigned int result;
 167         unsigned long flags;
 168 
 169         save_flags(flags);
 170         cli();
 171         result = queue->buf[queue->tail];
 172         queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
 173         restore_flags(flags);
 174         return result;
 175 }
 176 
 177 
 178 static inline int queue_empty(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180         return queue->head == queue->tail;
 181 }
 182 
 183 
 184 
 185 /*
 186  * Interrupt from the auxiliary device: a character
 187  * is waiting in the keyboard/aux controller.
 188  */
 189 
 190 static void aux_interrupt(int cpl)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192         int head = queue->head;
 193         int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
 194 
 195         queue->buf[head] = inb(AUX_INPUT_PORT);
 196         if (head != maxhead) {
 197                 head++;
 198                 head &= AUX_BUF_SIZE-1;
 199         }
 200         queue->head = head;
 201         aux_ready = 1;
 202         wake_up_interruptible(&queue->proc_list);
 203 }
 204 
 205 /*
 206  * Interrupt handler for the QuickPort. A character
 207  * is waiting in the 82C710.
 208  */
 209 
 210 #ifdef CONFIG_QUICKPORT_MOUSE
 211 static void qp_interrupt(int cpl)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213         int head = queue->head;
 214         int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
 215 
 216         queue->buf[head] = inb(qp_data);
 217         if (head != maxhead) {
 218                 head++;
 219                 head &= AUX_BUF_SIZE-1;
 220         }
 221         queue->head = head;
 222         aux_ready = 1;
 223         wake_up_interruptible(&queue->proc_list);
 224 }
 225 #endif
 226 
 227 
 228 static void release_aux(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230         aux_write_dev(AUX_DISABLE_DEV);         /* disable aux device */
 231         aux_write_cmd(AUX_INTS_OFF);            /* disable controller ints */
 232         poll_aux_status();
 233         outb_p(AUX_DISABLE,AUX_COMMAND);        /* Disable Aux device */
 234         poll_aux_status();
 235         free_irq(AUX_IRQ);
 236         aux_busy = 0;
 237 }
 238 
 239 #ifdef CONFIG_QUICKPORT_MOUSE
 240 static void release_qp(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242         unsigned char status;
 243 
 244         if (!poll_qp_status())
 245                 printk("Warning: QuickPort device busy in release_qp()\n");
 246         status = inb_p(qp_status);
 247         outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
 248         if (!poll_qp_status())
 249                 printk("Warning: QuickPort device busy in release_qp()\n");
 250         free_irq(QP_IRQ);
 251         qp_busy = 0;
 252 }
 253 #endif
 254 
 255 /*
 256  * Install interrupt handler.
 257  * Enable auxiliary device.
 258  */
 259 
 260 static int open_aux(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 261 {
 262         if (!aux_present)
 263                 return -EINVAL;
 264         if (aux_busy)
 265                 return -EBUSY;
 266         if (!poll_aux_status())
 267                 return -EBUSY;
 268         aux_busy = 1;
 269         queue->head = queue->tail = 0;          /* Flush input queue */
 270         if (request_irq(AUX_IRQ, aux_interrupt)) {
 271                 aux_busy = 0;
 272                 return -EBUSY;
 273         }
 274         poll_aux_status();
 275         outb_p(AUX_ENABLE,AUX_COMMAND);         /* Enable Aux */
 276         aux_write_dev(AUX_ENABLE_DEV);          /* enable aux device */
 277         aux_write_cmd(AUX_INTS_ON);             /* enable controller ints */
 278         poll_aux_status();
 279         aux_ready = 0;
 280         return 0;
 281 }
 282 
 283 #ifdef CONFIG_QUICKPORT_MOUSE
 284 /*
 285  * Install interrupt handler.
 286  * Enable the device, enable interrupts. Set qp_busy
 287  * (allow only one opener at a time.)
 288  */
 289 
 290 static int open_qp(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 291 {
 292         unsigned char status;
 293 
 294         if (!qp_present)
 295                 return -EINVAL;
 296 
 297         if (qp_busy)
 298                 return -EBUSY;
 299 
 300         if (request_irq(QP_IRQ, qp_interrupt))
 301                 return -EBUSY;
 302 
 303         qp_busy = 1;
 304 
 305         status = inb_p(qp_status);
 306         status |= (QP_ENABLE|QP_RESET);
 307         outb_p(status, qp_status);
 308         status &= ~(QP_RESET);
 309         outb_p(status, qp_status);
 310 
 311         queue->head = queue->tail = 0;          /* Flush input queue */
 312         status |= QP_INTS_ON;
 313         outb_p(status, qp_status);              /* Enable interrupts */
 314 
 315         while (!poll_qp_status()) {
 316                 printk("Error: QuickPort device busy in open_qp()\n");
 317                 return -EBUSY;
 318         }
 319 
 320         outb_p(AUX_ENABLE_DEV, qp_data);        /* Wake up mouse */
 321 
 322         return 0;
 323 }
 324 #endif
 325 
 326 /*
 327  * Write to the aux device.
 328  */
 329 
 330 static int write_aux(struct inode * inode, struct file * file, char * buffer, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 331 {
 332         int i = count;
 333 
 334         while (i--) {
 335                 if (!poll_aux_status())
 336                         return -EIO;
 337                 outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
 338                 if (!poll_aux_status())
 339                         return -EIO;
 340                 outb_p(get_fs_byte(buffer++),AUX_OUTPUT_PORT);
 341         }
 342         inode->i_mtime = CURRENT_TIME;
 343         return count;
 344 }
 345 
 346 
 347 #ifdef CONFIG_QUICKPORT_MOUSE
 348 /*
 349  * Write to the QuickPort device.
 350  */
 351 
 352 static int write_qp(struct inode * inode, struct file * file, char * buffer, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354         int i = count;
 355 
 356         while (i--) {
 357                 if (!poll_qp_status())
 358                         return -EIO;
 359                 outb_p(get_fs_byte(buffer++), qp_data);
 360         }
 361         inode->i_mtime = CURRENT_TIME;
 362         return count;
 363 }
 364 #endif
 365 
 366 
 367 /*
 368  * Put bytes from input queue to buffer.
 369  */
 370 
 371 static int read_aux(struct inode * inode, struct file * file, char * buffer, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 372 {
 373         struct wait_queue wait = { current, NULL };
 374         int i = count;
 375         unsigned char c;
 376 
 377         if (queue_empty()) {
 378                 if (file->f_flags & O_NONBLOCK)
 379                         return -EAGAIN;
 380                 add_wait_queue(&queue->proc_list, &wait);
 381 repeat:
 382                 current->state = TASK_INTERRUPTIBLE;
 383                 if (queue_empty() && !(current->signal & ~current->blocked)) {
 384                         schedule();
 385                         goto repeat;
 386                 }
 387                 current->state = TASK_RUNNING;
 388                 remove_wait_queue(&queue->proc_list, &wait);                    
 389         }               
 390         while (i > 0 && !queue_empty()) {
 391                 c = get_from_queue();
 392                 put_fs_byte(c, buffer++);
 393                 i--;
 394         }
 395         aux_ready = !queue_empty();
 396         if (count-i) {
 397                 inode->i_atime = CURRENT_TIME;
 398                 return count-i;
 399         }
 400         if (current->signal & ~current->blocked)
 401                 return -ERESTARTSYS;
 402         return 0;
 403 }
 404 
 405 
 406 static int aux_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
     /* [previous][next][first][last][top][bottom][index][help] */
 407 {
 408         if (sel_type != SEL_IN)
 409                 return 0;
 410         if (aux_ready)
 411                 return 1;
 412         select_wait(&queue->proc_list, wait);
 413         return 0;
 414 }
 415 
 416 
 417 struct file_operations psaux_fops = {
 418         NULL,           /* seek */
 419         read_aux,
 420         write_aux,
 421         NULL,           /* readdir */
 422         aux_select,
 423         NULL,           /* ioctl */
 424         NULL,           /* mmap */
 425         open_aux,
 426         release_aux,
 427 };
 428 
 429 
 430 /*
 431  * Initialize driver. First check for QuickPort device; if found
 432  * forget about the Aux port and use the QuickPort functions.
 433  */
 434 
 435 unsigned long psaux_init(unsigned long kmem_start)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437         int qp_found = 0;
 438 
 439 #ifdef CONFIG_QUICKPORT_MOUSE
 440         printk("Probing QuickPort device.\n");
 441         if (qp_found = probe_qp()) {
 442                 printk("QuickPort pointing device detected -- driver installed.\n");
 443 /*              printk("QuickPort address = %x (should be 0x310)\n", qp_data); */
 444                 qp_present = 1;
 445                 psaux_fops.write = write_qp;
 446                 psaux_fops.open = open_qp;
 447                 psaux_fops.release = release_qp;
 448                 poll_qp_status();
 449         } else
 450 #endif
 451         if (aux_device_present == 0xaa) {
 452                 printk("PS/2 auxiliary pointing device detected -- driver installed.\n");
 453                 aux_present = 1;
 454                 kbd_read_mask = AUX_OBUF_FULL;
 455                 poll_aux_status();
 456         } else {
 457                 return kmem_start;              /* No mouse at all */
 458         }
 459         queue = (struct aux_queue *) kmem_start;
 460         kmem_start += sizeof (struct aux_queue);
 461         queue->head = queue->tail = 0;
 462         queue->proc_list = NULL;
 463         if (!qp_found) {
 464 #if defined INITIALIZE_DEVICE
 465                 outb_p(AUX_ENABLE,AUX_COMMAND);         /* Enable Aux */
 466                 aux_write_ack(AUX_SET_SAMPLE);
 467                 aux_write_ack(100);                     /* 100 samples/sec */
 468                 aux_write_ack(AUX_SET_RES);
 469                 aux_write_ack(3);                       /* 8 counts per mm */
 470                 aux_write_ack(AUX_SET_SCALE21);         /* 2:1 scaling */
 471                 poll_aux_status();
 472 #endif /* INITIALIZE_DEVICE */
 473                 outb_p(AUX_DISABLE,AUX_COMMAND);   /* Disable Aux device */
 474                 aux_write_cmd(AUX_INTS_OFF);    /* disable controller ints */
 475                 poll_aux_status();
 476         }
 477         return kmem_start;
 478 }
 479 
 480 static int poll_aux_status(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 481 {
 482         int retries=0;
 483 
 484         while ((inb(AUX_STATUS)&0x03) && retries < MAX_RETRIES) {
 485                 if (inb_p(AUX_STATUS) & AUX_OBUF_FULL == AUX_OBUF_FULL)
 486                         inb_p(AUX_INPUT_PORT);
 487                 current->state = TASK_INTERRUPTIBLE;
 488                 current->timeout = jiffies + 5;
 489                 schedule();
 490                 retries++;
 491         }
 492         return !(retries==MAX_RETRIES);
 493 }
 494 
 495 #ifdef CONFIG_QUICKPORT_MOUSE
 496 /*
 497  * Wait for device to send output char and flush any input char.
 498  */
 499 
 500 static int poll_qp_status(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 501 {
 502         int retries=0;
 503 
 504         while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
 505                        != (QP_DEV_IDLE|QP_TX_IDLE)
 506                        && retries < MAX_RETRIES) {
 507 
 508                 if (inb_p(qp_status)&(QP_RX_FULL))
 509                         inb_p(qp_data);
 510                 current->state = TASK_INTERRUPTIBLE;
 511                 current->timeout = jiffies + 5;
 512                 schedule();
 513                 retries++;
 514         }
 515         return !(retries==MAX_RETRIES);
 516 }
 517 
 518 /*
 519  * Function to read register in 82C710.
 520  */
 521 
 522 static inline unsigned char read_710(unsigned char index)
     /* [previous][next][first][last][top][bottom][index][help] */
 523 {
 524         outb_p(index, 0x390);                   /* Write index */
 525         return inb_p(0x391);                    /* Read the data */
 526 }
 527 
 528 /*
 529  * See if we can find a QuickPort device. Read mouse address.
 530  */
 531 
 532 static int probe_qp(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 533 {
 534         outb_p(0x55, 0x2fa);                    /* Any value except 9, ff or 36 */
 535         outb_p(0xaa, 0x3fa);                    /* Inverse of 55 */
 536         outb_p(0x36, 0x3fa);                    /* Address the chip */
 537         outb_p(0xe4, 0x3fa);                    /* 390/4; 390 = config address */
 538         outb_p(0x1b, 0x2fa);                    /* Inverse of e4 */
 539         if (read_710(0x0f) != 0xe4)             /* Config address found? */
 540           return 0;                             /* No: no 82C710 here */
 541         qp_data = read_710(0x0d)*4;             /* Get mouse I/O address */
 542         qp_status = qp_data+1;
 543         outb_p(0x0f, 0x390);
 544         outb_p(0x0f, 0x391);                    /* Close config mode */
 545         return 1;
 546 }
 547 #endif

/* [previous][next][first][last][top][bottom][index][help] */