OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [char/] [psaux.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.