linux-old/drivers/char/mk712.c
<<
>>
Prefs
   1/* -*- 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
  88struct mk712_packet {
  89        unsigned int header;
  90        unsigned int x;
  91        unsigned int y;
  92        unsigned int reserved;
  93};
  94
  95struct 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
 104static int io = 0;
 105static int irq = 0;
 106#endif
 107static int mk712_io = MK712_DEFAULT_IO;
 108static int mk712_irq = MK712_DEFAULT_IRQ;
 109static int mk712_users = 0;
 110static spinlock_t mk712_lock = SPIN_LOCK_UNLOCKED;
 111static struct mk712_queue *queue; /* mouse data buffer */
 112
 113static 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
 125static inline int queue_empty(void)
 126{
 127        return queue->head == queue->tail;
 128}
 129
 130static 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
 140static 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
 153static int points = 0;          /* number of stored points */
 154static int output_point = 0;    /* did I output a point since last release? */
 155
 156static 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
 169static 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
 239static 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
 257static 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
 314static 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
 341static 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
 351static 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
 359static 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
 368static 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);
 380repeat:
 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
 412static ssize_t mk712_write(struct file *file, const char *buffer, size_t count,
 413                           loff_t *ppos)
 414{
 415        return -EINVAL;
 416}
 417
 418struct 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
 429static struct miscdevice mk712_touchscreen = {
 430        MK712_MINOR, "mk712_touchscreen", &mk712_fops
 431};
 432
 433int __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
 481static 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
 490MODULE_AUTHOR("Daniel Quinlan");
 491MODULE_DESCRIPTION("MK712 touch screen driver");
 492MODULE_PARM(io, "i");
 493MODULE_PARM_DESC(io, "I/O base address of MK712 touch screen controller");
 494MODULE_PARM(irq, "i");
 495MODULE_PARM_DESC(irq, "IRQ of MK712 touch screen controller");
 496MODULE_LICENSE("GPL");
 497
 498module_init(mk712_init);
 499module_exit(mk712_exit);
 500
 501/*
 502 * Local variables:
 503 * c-file-style: "linux"
 504 * End:
 505 */
 506
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.