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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [mk712.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/* -*- c -*- --------------------------------------------------------- *
2
 *
3
 * linux/drivers/char/mk712.c
4
 *
5
 * Copyright 1999-2002 Transmeta Corporation
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 *
21
 * This driver supports the MK712 touch screen.
22
 * based on busmouse.c, pc_keyb.c, and other mouse drivers
23
 *
24
 * 1999-12-18: original version, Daniel Quinlan
25
 * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
26
 *             to use queue_empty, Nathan Laredo
27
 * 1999-12-20: improved random point rejection, Nathan Laredo
28
 * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
29
 *             queue code, added module options, other fixes, Daniel Quinlan
30
 * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
31
 *             Fixed multi open race, fixed memory checks, fixed resource
32
 *             allocation, fixed close/powerdown bug, switched to new init
33
 *
34
 * ------------------------------------------------------------------------- */
35
 
36
#include <linux/module.h>
37
 
38
#include <linux/kernel.h>
39
#include <linux/sched.h>
40
#include <linux/init.h>
41
#include <linux/signal.h>
42
#include <linux/errno.h>
43
#include <linux/mm.h>
44
#include <linux/poll.h>
45
#include <linux/miscdevice.h>
46
#include <linux/random.h>
47
#include <linux/delay.h>
48
#include <linux/ioport.h>
49
#include <linux/slab.h>
50
#include <linux/interrupt.h>
51
 
52
#include <asm/io.h>
53
#include <asm/uaccess.h>
54
#include <asm/system.h>
55
#include <asm/irq.h>
56
 
57
#define DEBUG(x)        x
58
#define SQUARE(x)       ((x)*(x))
59
 
60
#define MK712_DEFAULT_IO 0x260          /* demo board: 0x200, 0x208, 0x300 */
61
#define MK712_DEFAULT_IRQ 10            /* demo board: 10, 12, 14 or 15  */
62
 
63
/* eight 8-bit registers */
64
#define MK712_STATUS_LOW 0      /* READ */
65
#define MK712_STATUS_HIGH 1     /* READ */
66
#define MK712_X_LOW 2           /* READ */
67
#define MK712_X_HIGH 3          /* READ */
68
#define MK712_Y_LOW 4           /* READ */
69
#define MK712_Y_HIGH 5          /* READ */
70
#define MK712_CONTROL 6         /* R/W */
71
#define MK712_RATE 7            /* R/W */
72
 
73
/* status */
74
#define MK712_STATUS_TOUCH 0x10
75
#define MK712_CONVERSION_COMPLETE 0x80
76
 
77
#define MK712_ENABLE_INT                        0x01 /* enable interrupts */
78
#define MK712_INT_ON_CONVERSION_COMPLETE        0x02 /* if bit 0 = 1 */
79
#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS_A   0x04 /* if bit 0 = 1 */
80
#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS_B   0x08 /* if bit 0 = 1 */
81
#define MK712_ENABLE_PERIODIC_CONVERSIONS       0x10
82
#define MK712_READ_ONE_POINT                    0x20
83
#define MK712_POWERDOWN_A                       0x40
84
#define MK712_POWERDOWN_B                       0x80
85
 
86
#define MK712_BUF_SIZE 256      /* a page */
87
 
88
struct mk712_packet {
89
        unsigned int header;
90
        unsigned int x;
91
        unsigned int y;
92
        unsigned int reserved;
93
};
94
 
95
struct mk712_queue {
96
        unsigned long head;
97
        unsigned long tail;
98
        wait_queue_head_t proc_list;
99
        struct fasync_struct *fasync;
100
        struct mk712_packet buf[256];
101
};
102
 
103
#ifdef MODULE
104
static int io = 0;
105
static int irq = 0;
106
#endif
107
static int mk712_io = MK712_DEFAULT_IO;
108
static int mk712_irq = MK712_DEFAULT_IRQ;
109
static int mk712_users = 0;
110
static spinlock_t mk712_lock = SPIN_LOCK_UNLOCKED;
111
static struct mk712_queue *queue; /* mouse data buffer */
112
 
113
static struct mk712_packet get_from_queue(void)
114
{
115
        struct mk712_packet result;
116
        unsigned long flags;
117
 
118
        spin_lock_irqsave(&mk712_lock, flags);
119
        result = queue->buf[queue->tail];
120
        queue->tail = (queue->tail + 1) & (MK712_BUF_SIZE-1);
121
        spin_unlock_irqrestore(&mk712_lock, flags);
122
        return result;
123
}
124
 
125
static inline int queue_empty(void)
126
{
127
        return queue->head == queue->tail;
128
}
129
 
130
static int mk712_fasync(int fd, struct file *filp, int on)
131
{
132
        int retval;
133
 
134
        retval = fasync_helper(fd, filp, on, &queue->fasync);
135
        if (retval < 0)
136
                return retval;
137
        return 0;
138
}
139
 
140
static void mk712_output_packet(struct mk712_packet data)
141
{
142
        int head = queue->head;
143
 
144
        queue->buf[head] = data;
145
        head = (head + 1) & (MK712_BUF_SIZE-1);
146
        if (head != queue->tail) {
147
                queue->head = head;
148
                kill_fasync(&queue->fasync, SIGIO, POLL_IN);
149
                wake_up_interruptible(&queue->proc_list);
150
        }
151
}
152
 
153
static int points = 0;          /* number of stored points */
154
static int output_point = 0;    /* did I output a point since last release? */
155
 
156
static void mk712_output_point(int x, int y)
157
{
158
        struct mk712_packet t;
159
 
160
        t.header = 0;
161
        t.x = x;
162
        t.y = y;
163
        t.reserved = 0;
164
 
165
        mk712_output_packet(t);
166
        output_point = 1;
167
}
168
 
169
static void mk712_store_point(int x_new, int y_new)
170
{
171
        static int x[3], y[3];
172
        int x_out, y_out;
173
 
174
        x[points] = x_new;
175
        y[points] = y_new;
176
 
177
        if (points == 1 && abs(x[0] - x[1]) < 88 && abs(y[0] - y[1]) < 88)
178
        {
179
                x_out = (x[0] + x[1]) >> 1;
180
                y_out = (y[0] + y[1]) >> 1;
181
                mk712_output_point(x_out, y_out);
182
        }
183
 
184
        if (points == 2) {
185
                if ((abs(x[1] - x[2]) < 88 && abs(y[1] - y[2]) < 88) &&
186
                    (abs(x[0] - x[1]) < 88 && abs(y[0] - y[1]) < 88))
187
                {
188
                        x_out = (x[0] + x[1] + x[2]) / 3;
189
                        y_out = (y[0] + y[1] + y[2]) / 3;
190
                        mk712_output_point(x_out, y_out);
191
                }
192
                else if (abs(x[1] - x[2]) < 88 && abs(y[1] - y[2]) < 88)
193
                {
194
                        x_out = (x[1] + x[2]) >> 1;
195
                        y_out = (y[1] + y[2]) >> 1;
196
                        mk712_output_point(x_out, y_out);
197
                }
198
                else
199
                {
200
                        int x_avg, y_avg, d0, d1, d2;
201
 
202
                        x_avg = (x[0] + x[1] + x[2]) / 3;
203
                        y_avg = (y[0] + y[1] + y[2]) / 3;
204
 
205
                        d0 = SQUARE(x[0] - x_avg) + SQUARE(y[0] - y_avg);
206
                        d1 = SQUARE(x[1] - x_avg) + SQUARE(y[1] - y_avg);
207
                        d2 = SQUARE(x[2] - x_avg) + SQUARE(y[2] - y_avg);
208
 
209
                        if (d2 > d1 && d2 > d0)
210
                        {
211
                                x_out = (x[0] + x[1]) >> 1;
212
                                y_out = (y[0] + y[1]) >> 1;
213
                        }
214
                        if (d1 > d0 && d1 > d2)
215
                        {
216
                                x_out = (x[0] + x[2]) >> 1;
217
                                y_out = (y[0] + y[2]) >> 1;
218
                        }
219
                        else
220
                        {
221
                                x_out = (x[1] + x[2]) >> 1;
222
                                y_out = (y[1] + y[2]) >> 1;
223
                        }
224
 
225
                        mk712_output_point(x_out, y_out);
226
 
227
                        x[0] = x[1];
228
                        x[1] = x[2];
229
                        y[0] = y[1];
230
                        y[1] = y[2];
231
                }
232
        }
233
        else
234
        {
235
                points++;
236
        }
237
}
238
 
239
static void mk712_release_event(void)
240
{
241
        struct mk712_packet t;
242
 
243
        if (!output_point) {
244
                points = 0;
245
                return;
246
        }
247
        output_point = 0;
248
 
249
        t.header = 1;
250
        t.x = t.y = t.reserved = 0;
251
 
252
        mk712_output_packet(t);
253
        points = 0;
254
}
255
 
256
#define MK712_FILTER
257
static void mk712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
258
{
259
        unsigned short x;
260
        unsigned short y;
261
        unsigned char status;
262
        unsigned long flags;
263
#ifdef MK712_FILTER
264
        static int drop_next = 1;
265
#endif
266
 
267
        spin_lock_irqsave(&mk712_lock, flags);
268
 
269
        status = inb(mk712_io + MK712_STATUS_LOW);
270
 
271
        if (!(status & MK712_CONVERSION_COMPLETE)) {
272
#ifdef MK712_FILTER
273
                drop_next = 1;
274
#endif
275
                return;
276
        }
277
        if (!(status & MK712_STATUS_TOUCH))     /* release event */
278
        {
279
#ifdef MK712_FILTER
280
                drop_next = 1;
281
#endif
282
                mk712_release_event();
283
 
284
                spin_unlock_irqrestore(&mk712_lock, flags);
285
                wake_up_interruptible(&queue->proc_list);
286
 
287
                return;
288
        }
289
 
290
        x = inw(mk712_io + MK712_X_LOW) & 0x0fff;
291
        y = inw(mk712_io + MK712_Y_LOW) & 0x0fff;
292
 
293
#ifdef MK712_FILTER
294
        if (drop_next)
295
        {
296
                drop_next = 0;
297
 
298
                spin_unlock_irqrestore(&mk712_lock, flags);
299
                wake_up_interruptible(&queue->proc_list);
300
 
301
                return;
302
        }
303
#endif
304
 
305
        x = inw(mk712_io + MK712_X_LOW) & 0x0fff;
306
        y = inw(mk712_io + MK712_Y_LOW) & 0x0fff;
307
 
308
        mk712_store_point(x, y);
309
 
310
        spin_unlock_irqrestore(&mk712_lock, flags);
311
        wake_up_interruptible(&queue->proc_list);
312
}
313
 
314
static int mk712_open(struct inode *inode, struct file *file)
315
{
316
        unsigned char control;
317
        unsigned long flags;
318
 
319
        control = 0;
320
 
321
        spin_lock_irqsave(&mk712_lock, flags);
322
        if(!mk712_users++)
323
        {
324
                outb(0, mk712_io + MK712_CONTROL);
325
 
326
                control |= (MK712_ENABLE_INT |
327
                            MK712_INT_ON_CONVERSION_COMPLETE |
328
                            MK712_INT_ON_CHANGE_IN_TOUCH_STATUS_B |
329
                            MK712_ENABLE_PERIODIC_CONVERSIONS |
330
                            MK712_POWERDOWN_A);
331
                outb(control, mk712_io + MK712_CONTROL);
332
 
333
                outb(10, mk712_io + MK712_RATE); /* default count = 10 */
334
 
335
                queue->head = queue->tail = 0;          /* Flush input queue */
336
        }
337
        spin_unlock_irqrestore(&mk712_lock, flags);
338
        return 0;
339
}
340
 
341
static int mk712_close(struct inode * inode, struct file * file) {
342
        /* power down controller */
343
        unsigned long flags;
344
        spin_lock_irqsave(&mk712_lock, flags);
345
        if(--mk712_users==0)
346
                outb(0, mk712_io + MK712_CONTROL);
347
        spin_unlock_irqrestore(&mk712_lock, flags);
348
        return 0;
349
}
350
 
351
static unsigned int mk712_poll(struct file *file, poll_table *wait)
352
{
353
        poll_wait(file, &queue->proc_list, wait);
354
        if(!queue_empty())
355
                return POLLIN | POLLRDNORM;
356
        return 0;
357
}
358
 
359
static int mk712_ioctl(struct inode *inode, struct file * file,
360
        unsigned int cmd, unsigned long arg)
361
{
362
        if (!inode)
363
                BUG();
364
        return -ENOTTY;
365
}
366
 
367
 
368
static ssize_t mk712_read(struct file *file, char *buffer,
369
                          size_t count, loff_t *pos)
370
{
371
        DECLARE_WAITQUEUE(wait, current);
372
        ssize_t bytes_read = 0;
373
        struct mk712_packet p;
374
 
375
        /* wait for an event */
376
        if (queue_empty()) {
377
                if (file->f_flags & O_NONBLOCK)
378
                        return -EAGAIN;
379
                add_wait_queue(&queue->proc_list, &wait);
380
repeat:
381
                set_current_state(TASK_INTERRUPTIBLE);
382
                if (queue_empty() && !signal_pending(current)) {
383
                        schedule();
384
                        goto repeat;
385
                }
386
                current->state = TASK_RUNNING;
387
                remove_wait_queue(&queue->proc_list, &wait);
388
        }
389
 
390
        while (bytes_read < count && !queue_empty()) {
391
                p = get_from_queue();
392
                if (copy_to_user (buffer+bytes_read, (void *) &p, sizeof(p)))
393
                {
394
                        bytes_read = -EFAULT;
395
                        break;
396
                }
397
                bytes_read += sizeof(p);
398
        }
399
 
400
        if (bytes_read > 0)
401
        {
402
                file->f_dentry->d_inode->i_atime = CURRENT_TIME;
403
                return bytes_read;
404
        }
405
 
406
        if (signal_pending(current))
407
                return -ERESTARTSYS;
408
 
409
        return bytes_read;
410
}
411
 
412
static ssize_t mk712_write(struct file *file, const char *buffer, size_t count,
413
                           loff_t *ppos)
414
{
415
        return -EINVAL;
416
}
417
 
418
struct file_operations mk712_fops = {
419
        owner: THIS_MODULE,
420
        read: mk712_read,
421
        write: mk712_write,
422
        poll: mk712_poll,
423
        ioctl: mk712_ioctl,
424
        open: mk712_open,
425
        release: mk712_close,
426
        fasync: mk712_fasync,
427
};
428
 
429
static struct miscdevice mk712_touchscreen = {
430
        MK712_MINOR, "mk712_touchscreen", &mk712_fops
431
};
432
 
433
int __init mk712_init(void)
434
{
435
#ifdef MODULE
436
        if (io)
437
                mk712_io = io;
438
        if (irq)
439
                mk712_irq = irq;
440
#endif
441
 
442
        if(!request_region(mk712_io, 8, "mk712_touchscreen"))
443
        {
444
                printk("mk712: unable to get IO region\n");
445
                return -ENODEV;
446
        }
447
 
448
        /* set up wait queue */
449
        queue = (struct mk712_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
450
        if(queue == NULL)
451
        {
452
                release_region(mk712_io, 8);
453
                return -ENOMEM;
454
        }
455
        memset(queue, 0, sizeof(*queue));
456
        queue->head = queue->tail = 0;
457
        init_waitqueue_head(&queue->proc_list);
458
 
459
        /* The MK712 is ISA and hard-coded to a particular IRQ, so the
460
           driver should keep the IRQ as long as it is loaded. */
461
        if(request_irq(mk712_irq, mk712_interrupt, 0, "mk712_touchscreen",
462
                       queue))
463
        {
464
                printk("mk712: unable to get IRQ\n");
465
                release_region(mk712_io, 8);
466
                kfree(queue);
467
                return -EBUSY;
468
        }
469
 
470
        /* register misc device */
471
        if(misc_register(&mk712_touchscreen)<0)
472
        {
473
                release_region(mk712_io, 8);
474
                kfree(queue);
475
                free_irq(mk712_irq, queue);
476
                return -ENODEV;
477
        }
478
        return 0;
479
}
480
 
481
static void __exit mk712_exit(void)
482
{
483
        misc_deregister(&mk712_touchscreen);
484
        release_region(mk712_io, 8);
485
        free_irq(mk712_irq, queue);
486
        kfree(queue);
487
        printk(KERN_INFO "mk712 touchscreen uninstalled\n");
488
}
489
 
490
MODULE_AUTHOR("Daniel Quinlan");
491
MODULE_DESCRIPTION("MK712 touch screen driver");
492
MODULE_PARM(io, "i");
493
MODULE_PARM_DESC(io, "I/O base address of MK712 touch screen controller");
494
MODULE_PARM(irq, "i");
495
MODULE_PARM_DESC(irq, "IRQ of MK712 touch screen controller");
496
MODULE_LICENSE("GPL");
497
 
498
module_init(mk712_init);
499
module_exit(mk712_exit);
500
 
501
/*
502
 * Local variables:
503
 * c-file-style: "linux"
504
 * End:
505
 */

powered by: WebSVN 2.1.0

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