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. release_aux
  8. open_aux
  9. write_aux
  10. read_aux
  11. aux_select
  12. psaux_init
  13. poll_status

   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  * Modified by Dean Troyer (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
  10  *   to perform (some of) the hardware initialization formerly done in
  11  *   setup.S by the BIOS
  12  *
  13  * Modified by Dean Troyer (troyer@saifr00.cfsat.Honeywell.COM) 09Oct92
  14  *   to perform the hardware initialization formerly done in setup.S by
  15  *   the BIOS.  Mouse characteristic setup is now included.
  16  *
  17  */
  18 
  19 #include <linux/timer.h>
  20 #include <linux/sched.h>
  21 #include <linux/kernel.h>
  22 #include <linux/fcntl.h>
  23 #include <linux/errno.h>
  24 
  25 #include <asm/io.h>
  26 #include <asm/segment.h>
  27 #include <asm/system.h>
  28 
  29 /* aux controller ports */
  30 #define AUX_INPUT_PORT  0x60            /* Aux device output buffer */
  31 #define AUX_OUTPUT_PORT 0x60            /* Aux device input buffer */
  32 #define AUX_COMMAND     0x64            /* Aux device command buffer */
  33 #define AUX_STATUS      0x64            /* Aux device status reg */
  34 
  35 /* aux controller status bits */
  36 #define AUX_OBUF_FULL   0x01            /* output buffer (from device) full */
  37 #define AUX_IBUF_FULL   0x02            /* input buffer (to device) full */
  38 
  39 /* aux controller commands */
  40 #define AUX_CMD_WRITE   0x60            /* value to write to controller */
  41 #define AUX_MAGIC_WRITE 0xd4            /* value to send aux device data */
  42 
  43 #define AUX_INTS_ON     0x47            /* enable controller interrupts */
  44 #define AUX_INTS_OFF    0x65            /* disable controller interrupts */
  45 
  46 #define AUX_DISABLE     0xa7            /* disable aux */
  47 #define AUX_ENABLE      0xa8            /* enable aux */
  48 
  49 /* aux device commands */
  50 #define AUX_SET_RES     0xe8            /* set resolution */
  51 #define AUX_SET_SCALE   0xe9            /* set scaling factor */
  52 #define AUX_SET_STREAM  0xea            /* set stream mode */
  53 #define AUX_SET_SAMPLE  0xf3            /* set sample rate */
  54 #define AUX_ENABLE_DEV  0xf4            /* enable aux device */
  55 #define AUX_DISABLE_DEV 0xf5            /* disable aux device */
  56 #define AUX_RESET       0xff            /* reset aux device */
  57 
  58 #define MAX_RETRIES     3
  59 #define AUX_IRQ         12
  60 #define AUX_BUF_SIZE    2048
  61 
  62 extern unsigned char aux_device_present;
  63 
  64 struct aux_queue {
  65         unsigned long head;
  66         unsigned long tail;
  67         struct wait_queue *proc_list;
  68         unsigned char buf[AUX_BUF_SIZE];
  69 };
  70 
  71 static struct aux_queue *queue;
  72 static int aux_ready = 0;
  73 static int aux_busy = 0;
  74 static int aux_present = 0;
  75 
  76 static int poll_status(void);
  77 
  78 /*
  79  * Write to aux device
  80  */
  81 static void aux_write_dev(int val)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83         poll_status();
  84         outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);    /* write magic cookie */
  85         poll_status();
  86         outb_p(val,AUX_OUTPUT_PORT);            /* write data */
  87         
  88 }
  89 
  90 #if 0
  91 /*
  92  * Write to device & handle returned ack
  93  */
  94  
  95 static int aux_write_ack(int val)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97         aux_write_dev(val);             /* write the value to the device */
  98         while ((inb(AUX_STATUS) & AUX_OBUF_FULL) == 0);  /* wait for ack */
  99         if ((inb(AUX_STATUS) & 0x20) == 0x20)
 100         {
 101                 return (inb(AUX_INPUT_PORT));
 102         }
 103         return 0;
 104 }
 105 #endif
 106 
 107 /*
 108  * Write aux device command
 109  */
 110 
 111 static void aux_write_cmd(int val)
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113         poll_status();
 114         outb_p(AUX_CMD_WRITE,AUX_COMMAND);
 115         poll_status();
 116         outb_p(val,AUX_OUTPUT_PORT);
 117 }
 118 
 119 
 120 static unsigned int get_from_queue()
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122         unsigned int result;
 123         unsigned long flags;
 124 
 125         __asm__ __volatile__ ("pushfl ; popl %0; cli":"=r" (flags));
 126         result = queue->buf[queue->tail];
 127         queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
 128         __asm__ __volatile__ ("pushl %0 ; popfl"::"r" (flags));
 129         return result;
 130 }
 131 
 132 
 133 static inline int queue_empty()
     /* [previous][next][first][last][top][bottom][index][help] */
 134 {
 135         return queue->head == queue->tail;
 136 }
 137 
 138 
 139 /*
 140  * Interrupt from the auxiliary device: a character
 141  * is waiting in the keyboard/aux controller.
 142  */
 143 
 144 static void aux_interrupt(int cpl)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146         int head = queue->head;
 147         int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
 148 
 149         queue->buf[head] = inb(AUX_INPUT_PORT);
 150         if (head != maxhead) {
 151                 head++;
 152                 head &= AUX_BUF_SIZE-1;
 153         }
 154         queue->head = head;
 155         aux_ready = 1;
 156         wake_up_interruptible(&queue->proc_list);
 157 }
 158 
 159 
 160 static void release_aux(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162         poll_status();
 163         aux_write_dev(AUX_DISABLE_DEV);         /* disable aux device */
 164         poll_status();
 165         outb_p(AUX_DISABLE,AUX_COMMAND);        /* Disable Aux device */
 166         aux_write_cmd(AUX_INTS_OFF);            /* disable controller ints */
 167         free_irq(AUX_IRQ);
 168         aux_busy = 0;
 169 }
 170 
 171 
 172 /*
 173  * Install interrupt handler.
 174  * Enable auxiliary device.
 175  */
 176 
 177 static int open_aux(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179         if (!aux_present)
 180                 return -EINVAL;
 181         if (aux_busy)
 182                 return -EBUSY;
 183         if (!poll_status())
 184                 return -EBUSY;
 185         aux_busy = 1;
 186         queue->head = queue->tail = 0;  /* Flush input queue */
 187         if (request_irq(AUX_IRQ, aux_interrupt))
 188                 return -EBUSY;
 189         aux_write_dev(AUX_ENABLE_DEV);          /* enable aux device */
 190         aux_write_cmd(AUX_INTS_ON);             /* enable controller ints */
 191         poll_status();
 192         outb_p(AUX_ENABLE,AUX_COMMAND);         /* Enable Aux */
 193         return 0;
 194 }
 195 
 196 
 197 /*
 198  * Write to the aux device.
 199  */
 200 
 201 static int write_aux(struct inode * inode, struct file * file, char * buffer, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 202 {
 203         int i = count;
 204 
 205         while (i--) {
 206                 if (!poll_status())
 207                         return -EIO;
 208                 outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
 209                 if (!poll_status())
 210                         return -EIO;
 211                 outb_p(get_fs_byte(buffer++),AUX_OUTPUT_PORT);
 212         }
 213         inode->i_mtime = CURRENT_TIME;
 214         return count;
 215 }
 216 
 217 
 218 /*
 219  * Put bytes from input queue to buffer.
 220  */
 221 
 222 static int read_aux(struct inode * inode, struct file * file, char * buffer, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224         struct wait_queue wait = { current, NULL };
 225         int i = count;
 226         unsigned char c;
 227 
 228         if (queue_empty()) {
 229                 if (file->f_flags & O_NONBLOCK)
 230                         return -EAGAIN;
 231                 add_wait_queue(&queue->proc_list, &wait);
 232 repeat:
 233                 current->state = TASK_INTERRUPTIBLE;
 234                 if (queue_empty() && !(current->signal & ~current->blocked)) {
 235                         schedule();
 236                         goto repeat;
 237                 }
 238                 current->state = TASK_RUNNING;
 239                 remove_wait_queue(&queue->proc_list, &wait);                    
 240         }               
 241         while (i > 0 && !queue_empty()) {
 242                 c = get_from_queue();
 243                 put_fs_byte(c, buffer++);
 244                 i--;
 245         }
 246         aux_ready = !queue_empty();
 247         if (count-i) {
 248                 inode->i_atime = CURRENT_TIME;
 249                 return count-i;
 250         }
 251         if (current->signal & ~current->blocked)
 252                 return -ERESTARTSYS;
 253         return 0;
 254 }
 255 
 256 
 257 static int aux_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259         if (sel_type != SEL_IN)
 260                 return 0;
 261         if (aux_ready)
 262                 return 1;
 263         select_wait(&queue->proc_list, wait);
 264         return 0;
 265 }
 266 
 267 
 268 struct file_operations psaux_fops = {
 269         NULL,           /* seek */
 270         read_aux,
 271         write_aux,
 272         NULL,           /* readdir */
 273         aux_select,
 274         NULL,           /* ioctl */
 275         NULL,           /* mmap */
 276         open_aux,
 277         release_aux,
 278 };
 279 
 280 
 281 unsigned long psaux_init(unsigned long kmem_start)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 {
 283         if (aux_device_present != 0xaa) {
 284                 return kmem_start;
 285         }
 286         printk("PS/2 type pointing device detected and installed.\n");
 287         queue = (struct aux_queue *) kmem_start;
 288         kmem_start += sizeof (struct aux_queue);
 289         queue->head = queue->tail = 0;
 290         queue->proc_list = NULL;
 291         aux_present = 1;
 292         poll_status();
 293         outb_p(AUX_DISABLE,AUX_COMMAND);        /* Disable Aux device */
 294         aux_write_cmd(AUX_INTS_OFF);            /* disable controller ints */
 295         return kmem_start;
 296 }
 297 
 298 
 299 static int poll_status(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 300 {
 301         int retries=0;
 302 
 303         while ((inb(AUX_STATUS)&0x03) && retries++ < MAX_RETRIES) {
 304                 if (inb_p(AUX_STATUS)&0x01)
 305                         inb_p(AUX_INPUT_PORT);
 306                 current->state = TASK_INTERRUPTIBLE;
 307                 current->timeout = jiffies + 5;
 308                 schedule();
 309         }
 310         return !(retries==MAX_RETRIES);
 311 }

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