root/drivers/char/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. fasync_aux
  11. open_aux
  12. open_qp
  13. write_aux
  14. write_qp
  15. read_aux
  16. aux_select
  17. psaux_init
  18. poll_aux_status
  19. poll_aux_status_nosleep
  20. poll_qp_status
  21. read_710
  22. probe_qp

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

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