home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / drivers / char / psaux.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-16  |  13.1 KB  |  552 lines

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