linux/drivers/input/serio/serio_raw.c
<<
>>
Prefs
   1/*
   2 * Raw serio device providing access to a raw byte stream from underlying
   3 * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
   4 *
   5 * Copyright (c) 2004 Dmitry Torokhov
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 */
  11
  12#include <linux/sched.h>
  13#include <linux/slab.h>
  14#include <linux/smp_lock.h>
  15#include <linux/poll.h>
  16#include <linux/module.h>
  17#include <linux/serio.h>
  18#include <linux/init.h>
  19#include <linux/major.h>
  20#include <linux/device.h>
  21#include <linux/miscdevice.h>
  22#include <linux/wait.h>
  23#include <linux/mutex.h>
  24
  25#define DRIVER_DESC     "Raw serio driver"
  26
  27MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
  28MODULE_DESCRIPTION(DRIVER_DESC);
  29MODULE_LICENSE("GPL");
  30
  31#define SERIO_RAW_QUEUE_LEN     64
  32struct serio_raw {
  33        unsigned char queue[SERIO_RAW_QUEUE_LEN];
  34        unsigned int tail, head;
  35
  36        char name[16];
  37        unsigned int refcnt;
  38        struct serio *serio;
  39        struct miscdevice dev;
  40        wait_queue_head_t wait;
  41        struct list_head list;
  42        struct list_head node;
  43};
  44
  45struct serio_raw_list {
  46        struct fasync_struct *fasync;
  47        struct serio_raw *serio_raw;
  48        struct list_head node;
  49};
  50
  51static DEFINE_MUTEX(serio_raw_mutex);
  52static LIST_HEAD(serio_raw_list);
  53static unsigned int serio_raw_no;
  54
  55/*********************************************************************
  56 *             Interface with userspace (file operations)            *
  57 *********************************************************************/
  58
  59static int serio_raw_fasync(int fd, struct file *file, int on)
  60{
  61        struct serio_raw_list *list = file->private_data;
  62
  63        return fasync_helper(fd, file, on, &list->fasync);
  64}
  65
  66static struct serio_raw *serio_raw_locate(int minor)
  67{
  68        struct serio_raw *serio_raw;
  69
  70        list_for_each_entry(serio_raw, &serio_raw_list, node) {
  71                if (serio_raw->dev.minor == minor)
  72                        return serio_raw;
  73        }
  74
  75        return NULL;
  76}
  77
  78static int serio_raw_open(struct inode *inode, struct file *file)
  79{
  80        struct serio_raw *serio_raw;
  81        struct serio_raw_list *list;
  82        int retval = 0;
  83
  84        lock_kernel();
  85        retval = mutex_lock_interruptible(&serio_raw_mutex);
  86        if (retval)
  87                goto out_bkl;
  88
  89        if (!(serio_raw = serio_raw_locate(iminor(inode)))) {
  90                retval = -ENODEV;
  91                goto out;
  92        }
  93
  94        if (!serio_raw->serio) {
  95                retval = -ENODEV;
  96                goto out;
  97        }
  98
  99        if (!(list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
 100                retval = -ENOMEM;
 101                goto out;
 102        }
 103
 104        list->serio_raw = serio_raw;
 105        file->private_data = list;
 106
 107        serio_raw->refcnt++;
 108        list_add_tail(&list->node, &serio_raw->list);
 109
 110out:
 111        mutex_unlock(&serio_raw_mutex);
 112out_bkl:
 113        unlock_kernel();
 114        return retval;
 115}
 116
 117static int serio_raw_cleanup(struct serio_raw *serio_raw)
 118{
 119        if (--serio_raw->refcnt == 0) {
 120                misc_deregister(&serio_raw->dev);
 121                list_del_init(&serio_raw->node);
 122                kfree(serio_raw);
 123
 124                return 1;
 125        }
 126
 127        return 0;
 128}
 129
 130static int serio_raw_release(struct inode *inode, struct file *file)
 131{
 132        struct serio_raw_list *list = file->private_data;
 133        struct serio_raw *serio_raw = list->serio_raw;
 134
 135        mutex_lock(&serio_raw_mutex);
 136
 137        serio_raw_cleanup(serio_raw);
 138
 139        mutex_unlock(&serio_raw_mutex);
 140        return 0;
 141}
 142
 143static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
 144{
 145        unsigned long flags;
 146        int empty;
 147
 148        spin_lock_irqsave(&serio_raw->serio->lock, flags);
 149
 150        empty = serio_raw->head == serio_raw->tail;
 151        if (!empty) {
 152                *c = serio_raw->queue[serio_raw->tail];
 153                serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
 154        }
 155
 156        spin_unlock_irqrestore(&serio_raw->serio->lock, flags);
 157
 158        return !empty;
 159}
 160
 161static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
 162{
 163        struct serio_raw_list *list = file->private_data;
 164        struct serio_raw *serio_raw = list->serio_raw;
 165        char uninitialized_var(c);
 166        ssize_t retval = 0;
 167
 168        if (!serio_raw->serio)
 169                return -ENODEV;
 170
 171        if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
 172                return -EAGAIN;
 173
 174        retval = wait_event_interruptible(list->serio_raw->wait,
 175                                          serio_raw->head != serio_raw->tail || !serio_raw->serio);
 176        if (retval)
 177                return retval;
 178
 179        if (!serio_raw->serio)
 180                return -ENODEV;
 181
 182        while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
 183                if (put_user(c, buffer++))
 184                        return -EFAULT;
 185                retval++;
 186        }
 187
 188        return retval;
 189}
 190
 191static ssize_t serio_raw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
 192{
 193        struct serio_raw_list *list = file->private_data;
 194        ssize_t written = 0;
 195        int retval;
 196        unsigned char c;
 197
 198        retval = mutex_lock_interruptible(&serio_raw_mutex);
 199        if (retval)
 200                return retval;
 201
 202        if (!list->serio_raw->serio) {
 203                retval = -ENODEV;
 204                goto out;
 205        }
 206
 207        if (count > 32)
 208                count = 32;
 209
 210        while (count--) {
 211                if (get_user(c, buffer++)) {
 212                        retval = -EFAULT;
 213                        goto out;
 214                }
 215                if (serio_write(list->serio_raw->serio, c)) {
 216                        retval = -EIO;
 217                        goto out;
 218                }
 219                written++;
 220        };
 221
 222out:
 223        mutex_unlock(&serio_raw_mutex);
 224        return written;
 225}
 226
 227static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
 228{
 229        struct serio_raw_list *list = file->private_data;
 230
 231        poll_wait(file, &list->serio_raw->wait, wait);
 232
 233        if (list->serio_raw->head != list->serio_raw->tail)
 234                return POLLIN | POLLRDNORM;
 235
 236        return 0;
 237}
 238
 239static const struct file_operations serio_raw_fops = {
 240        .owner =        THIS_MODULE,
 241        .open =         serio_raw_open,
 242        .release =      serio_raw_release,
 243        .read =         serio_raw_read,
 244        .write =        serio_raw_write,
 245        .poll =         serio_raw_poll,
 246        .fasync =       serio_raw_fasync,
 247};
 248
 249
 250/*********************************************************************
 251 *                   Interface with serio port                       *
 252 *********************************************************************/
 253
 254static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
 255                                        unsigned int dfl)
 256{
 257        struct serio_raw *serio_raw = serio_get_drvdata(serio);
 258        struct serio_raw_list *list;
 259        unsigned int head = serio_raw->head;
 260
 261        /* we are holding serio->lock here so we are prootected */
 262        serio_raw->queue[head] = data;
 263        head = (head + 1) % SERIO_RAW_QUEUE_LEN;
 264        if (likely(head != serio_raw->tail)) {
 265                serio_raw->head = head;
 266                list_for_each_entry(list, &serio_raw->list, node)
 267                        kill_fasync(&list->fasync, SIGIO, POLL_IN);
 268                wake_up_interruptible(&serio_raw->wait);
 269        }
 270
 271        return IRQ_HANDLED;
 272}
 273
 274static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
 275{
 276        struct serio_raw *serio_raw;
 277        int err;
 278
 279        if (!(serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
 280                printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
 281                return -ENOMEM;
 282        }
 283
 284        mutex_lock(&serio_raw_mutex);
 285
 286        snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
 287        serio_raw->refcnt = 1;
 288        serio_raw->serio = serio;
 289        INIT_LIST_HEAD(&serio_raw->list);
 290        init_waitqueue_head(&serio_raw->wait);
 291
 292        serio_set_drvdata(serio, serio_raw);
 293
 294        err = serio_open(serio, drv);
 295        if (err)
 296                goto out_free;
 297
 298        list_add_tail(&serio_raw->node, &serio_raw_list);
 299
 300        serio_raw->dev.minor = PSMOUSE_MINOR;
 301        serio_raw->dev.name = serio_raw->name;
 302        serio_raw->dev.parent = &serio->dev;
 303        serio_raw->dev.fops = &serio_raw_fops;
 304
 305        err = misc_register(&serio_raw->dev);
 306        if (err) {
 307                serio_raw->dev.minor = MISC_DYNAMIC_MINOR;
 308                err = misc_register(&serio_raw->dev);
 309        }
 310
 311        if (err) {
 312                printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n",
 313                        serio->phys);
 314                goto out_close;
 315        }
 316
 317        printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n",
 318                serio->phys, serio_raw->name, serio_raw->dev.minor);
 319        goto out;
 320
 321out_close:
 322        serio_close(serio);
 323        list_del_init(&serio_raw->node);
 324out_free:
 325        serio_set_drvdata(serio, NULL);
 326        kfree(serio_raw);
 327out:
 328        mutex_unlock(&serio_raw_mutex);
 329        return err;
 330}
 331
 332static int serio_raw_reconnect(struct serio *serio)
 333{
 334        struct serio_raw *serio_raw = serio_get_drvdata(serio);
 335        struct serio_driver *drv = serio->drv;
 336
 337        if (!drv || !serio_raw) {
 338                printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n");
 339                return -1;
 340        }
 341
 342        /*
 343         * Nothing needs to be done here, we just need this method to
 344         * keep the same device.
 345         */
 346        return 0;
 347}
 348
 349static void serio_raw_disconnect(struct serio *serio)
 350{
 351        struct serio_raw *serio_raw;
 352
 353        mutex_lock(&serio_raw_mutex);
 354
 355        serio_raw = serio_get_drvdata(serio);
 356
 357        serio_close(serio);
 358        serio_set_drvdata(serio, NULL);
 359
 360        serio_raw->serio = NULL;
 361        if (!serio_raw_cleanup(serio_raw))
 362                wake_up_interruptible(&serio_raw->wait);
 363
 364        mutex_unlock(&serio_raw_mutex);
 365}
 366
 367static struct serio_device_id serio_raw_serio_ids[] = {
 368        {
 369                .type   = SERIO_8042,
 370                .proto  = SERIO_ANY,
 371                .id     = SERIO_ANY,
 372                .extra  = SERIO_ANY,
 373        },
 374        {
 375                .type   = SERIO_8042_XL,
 376                .proto  = SERIO_ANY,
 377                .id     = SERIO_ANY,
 378                .extra  = SERIO_ANY,
 379        },
 380        { 0 }
 381};
 382
 383MODULE_DEVICE_TABLE(serio, serio_raw_serio_ids);
 384
 385static struct serio_driver serio_raw_drv = {
 386        .driver         = {
 387                .name   = "serio_raw",
 388        },
 389        .description    = DRIVER_DESC,
 390        .id_table       = serio_raw_serio_ids,
 391        .interrupt      = serio_raw_interrupt,
 392        .connect        = serio_raw_connect,
 393        .reconnect      = serio_raw_reconnect,
 394        .disconnect     = serio_raw_disconnect,
 395        .manual_bind    = 1,
 396};
 397
 398static int __init serio_raw_init(void)
 399{
 400        return serio_register_driver(&serio_raw_drv);
 401}
 402
 403static void __exit serio_raw_exit(void)
 404{
 405        serio_unregister_driver(&serio_raw_drv);
 406}
 407
 408module_init(serio_raw_init);
 409module_exit(serio_raw_exit);
 410
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.