2004-06-02 07:21:13

by Dmitry Torokhov

[permalink] [raw]
Subject: [RFC/RFT] Raw access to serio ports (1/2)

Hi,

Below is an implementation of rawdev driver. The driver will bind to serio
ports with type SERIO_8042_RAW and provides raw byte-oriented access to the
underlying serio device. I tried to keep behavior as close to 2.4 as possible
so multiple readers/writers are allowed and they will fight with each other
over access.

The driver will attempt to register char device 10,1 (/dev/psaux) and if that
fails will request dynamic minor. So if mousedev psaux support is turned off
and I expect only onle device requiring "special" handling to be present at
a time everything should work pretty much the same as in 2.4

The driver will happily co-exist with psmouse and atkbd loaded as they ignore
SERIO_8042_RAW ports, so it is possible to have one AUX port in raw mode and
other in standard 2.6 mode.

The patch to mark some of i8042 AUX ports as raw is in the next message.

Comments?

--
Dmitry

P.S. The patch is against Vojtech's tree so there is some nice if appllied to
Linus' tree but there should be no rejects.


===================================================================


[email protected], 2004-06-02 01:57:12-05:00, [email protected]
Input: Add rawdev driver that binds to serio ports marked as 'raw'
and provides unobstructed access to the underlying serio port
via a char device. The driver tries to register char device
10,1 (/dev/psaux) first and if it fails goes for dynamically
allocated minor.

Signed-off-by: Dmitry Torokhov <[email protected]>


drivers/input/misc/Kconfig | 12 +
drivers/input/misc/Makefile | 1
drivers/input/misc/rawdev.c | 388 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serio.h | 1
4 files changed, 402 insertions(+)


===================================================================



diff -Nru a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
--- a/drivers/input/misc/Kconfig 2004-06-02 02:15:20 -05:00
+++ b/drivers/input/misc/Kconfig 2004-06-02 02:15:20 -05:00
@@ -54,3 +54,15 @@
To compile this driver as a module, choose M here: the
module will be called uinput.

+config INPUT_RAWDEV
+ tristate "Raw access to serio ports"
+ depends on INPUT && INPUT_MISC
+ help
+ Say Y here if you want to have raw access to serio ports, such as
+ AUX ports on i8042 keyboard controller. Each serio port that is
+ marked as providing raw access will be accessible via a char device
+ with major 10 and dynamically allocated minor (it will first try
+ allocating minor 1 historically corrsponding to /dev/psaux).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rawdev.
diff -Nru a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
--- a/drivers/input/misc/Makefile 2004-06-02 02:15:20 -05:00
+++ b/drivers/input/misc/Makefile 2004-06-02 02:15:20 -05:00
@@ -9,3 +9,4 @@
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_98SPKR) += 98spkr.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+obj-$(CONFIG_INPUT_RAWDEV) += rawdev.o
diff -Nru a/drivers/input/misc/rawdev.c b/drivers/input/misc/rawdev.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/input/misc/rawdev.c 2004-06-02 02:15:20 -05:00
@@ -0,0 +1,388 @@
+/*
+ * Raw serio device providing access to a raw byte stream from underlying
+ * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
+ *
+ * Copyright (c) 2004 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <[email protected]>");
+MODULE_DESCRIPTION("Raw serio driver");
+MODULE_LICENSE("GPL");
+
+#define RAWDEV_QUEUE_LEN 64
+struct rawdev {
+ unsigned char queue[RAWDEV_QUEUE_LEN];
+ unsigned int tail, head;
+ spinlock_t lock;
+
+ char name[16];
+ unsigned int refcnt;
+ struct serio *serio;
+ struct miscdevice dev;
+ wait_queue_head_t wait;
+ struct list_head list;
+ struct list_head node;
+};
+
+struct rawdev_list {
+ struct fasync_struct *fasync;
+ struct rawdev *rawdev;
+ struct list_head node;
+};
+
+static DECLARE_MUTEX(rawdev_sem);
+static LIST_HEAD(rawdev_list);
+static unsigned int rawdev_no;
+
+/*********************************************************************
+ * Interface with userspace (file operations) *
+ *********************************************************************/
+
+static int rawdev_fasync(int fd, struct file *file, int on)
+{
+ struct rawdev_list *list = file->private_data;
+ int retval;
+
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static struct rawdev *rawdev_locate(int minor)
+{
+ struct rawdev *rawdev;
+
+ list_for_each_entry(rawdev, &rawdev_list, node) {
+ if (rawdev->dev.minor == minor)
+ return rawdev;
+ }
+
+ return NULL;
+}
+
+static int rawdev_open(struct inode *inode, struct file *file)
+{
+ struct rawdev *rawdev;
+ struct rawdev_list *list;
+ int retval = 0;
+
+ retval = down_interruptible(&rawdev_sem);
+ if (retval)
+ return retval;
+
+ if (!(rawdev = rawdev_locate(iminor(inode)))) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!rawdev->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!(list = kmalloc(sizeof(struct rawdev_list), GFP_KERNEL))) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ memset(list, 0, sizeof(struct rawdev_list));
+ list->rawdev = rawdev;
+ file->private_data = list;
+
+ rawdev->refcnt++;
+ list_add_tail(&list->node, &rawdev->list);
+
+out:
+ up(&rawdev_sem);
+ return retval;
+}
+
+static int rawdev_cleanup(struct rawdev *rawdev)
+{
+ if (--rawdev->refcnt == 0) {
+ misc_deregister(&rawdev->dev);
+ list_del_init(&rawdev->node);
+ kfree(rawdev);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rawdev_release(struct inode *inode, struct file *file)
+{
+ struct rawdev_list *list = file->private_data;
+ struct rawdev *rawdev = list->rawdev;
+
+ down(&rawdev_sem);
+
+ rawdev_fasync(-1, file, 0);
+ rawdev_cleanup(rawdev);
+
+ up(&rawdev_sem);
+ return 0;
+}
+
+static int rawdev_fetch_byte(struct rawdev *rawdev, char *c)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&rawdev->lock, flags);
+
+ empty = rawdev->head == rawdev->tail;
+ if (!empty) {
+ *c = rawdev->queue[rawdev->tail];
+ rawdev->tail = (rawdev->tail + 1) % RAWDEV_QUEUE_LEN;
+ }
+
+ spin_unlock_irqrestore(&rawdev->lock, flags);
+
+ return !empty;
+}
+
+static ssize_t rawdev_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct rawdev_list *list = file->private_data;
+ struct rawdev *rawdev = list->rawdev;
+ char c;
+ ssize_t retval = 0;
+
+ if (!rawdev->serio)
+ return -ENODEV;
+
+ if (rawdev->head == rawdev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(list->rawdev->wait,
+ rawdev->head != rawdev->tail || !rawdev->serio);
+ if (retval)
+ return retval;
+
+ if (!rawdev->serio)
+ return -ENODEV;
+
+ while (retval < count && rawdev_fetch_byte(rawdev, &c)) {
+ if (put_user(c, buffer++))
+ return -EFAULT;
+ retval++;
+ }
+
+ return retval;
+}
+
+static ssize_t rawdev_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct rawdev_list *list = file->private_data;
+ ssize_t written = 0;
+ int retval;
+ unsigned char c;
+
+ retval = down_interruptible(&rawdev_sem);
+ if (retval)
+ return retval;
+
+ if (!list->rawdev->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (count > 32)
+ count = 32;
+
+ while (count--) {
+ if (get_user(c, buffer++)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ if (serio_write(list->rawdev->serio, c)) {
+ retval = -EIO;
+ goto out;
+ }
+ written++;
+ };
+
+out:
+ up(&rawdev_sem);
+ return written;
+}
+
+static unsigned int rawdev_poll(struct file *file, poll_table *wait)
+{
+ struct rawdev_list *list = file->private_data;
+
+ poll_wait(file, &list->rawdev->wait, wait);
+
+ if (list->rawdev->head != list->rawdev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+struct file_operations rawdev_fops = {
+ .owner = THIS_MODULE,
+ .open = rawdev_open,
+ .release = rawdev_release,
+ .read = rawdev_read,
+ .write = rawdev_write,
+ .poll = rawdev_poll,
+ .fasync = rawdev_fasync,
+};
+
+
+/*********************************************************************
+ * Interface with serio port *
+ *********************************************************************/
+
+static irqreturn_t rawdev_interrupt(struct serio *serio, unsigned char data,
+ unsigned int dfl, struct pt_regs *regs)
+{
+ struct rawdev *rawdev = serio->private;
+ struct rawdev_list *list;
+ unsigned int head = rawdev->head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rawdev->lock, flags);
+
+ rawdev->queue[head] = data;
+ head = (head + 1) % RAWDEV_QUEUE_LEN;
+ if (likely(head != rawdev->tail)) {
+ rawdev->head = head;
+ list_for_each_entry(list, &rawdev->list, node)
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ wake_up_interruptible(&rawdev->wait);
+ }
+
+ spin_unlock_irqrestore(&rawdev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void rawdev_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct rawdev *rawdev;
+ int err;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_8042_RAW)
+ return;
+
+ if (!(rawdev = kmalloc(sizeof(struct rawdev), GFP_KERNEL))) {
+ printk(KERN_ERR "rawdev.c: can't allocate memory for a device\n");
+ return;
+ }
+
+ down(&rawdev_sem);
+
+ memset(rawdev, 0, sizeof(struct rawdev));
+ snprintf(rawdev->name, sizeof(rawdev->name), "serio_raw%d", rawdev_no++);
+ rawdev->refcnt = 1;
+ rawdev->serio = serio;
+ INIT_LIST_HEAD(&rawdev->list);
+ init_waitqueue_head(&rawdev->wait);
+
+ serio->private = rawdev;
+ if (serio_open(serio, dev))
+ goto out_free;
+
+ list_add_tail(&rawdev->node, &rawdev_list);
+
+ rawdev->dev.minor = PSMOUSE_MINOR;
+ rawdev->dev.name = rawdev->name;
+ rawdev->dev.fops = &rawdev_fops;
+
+ err = misc_register(&rawdev->dev);
+ if (err) {
+ rawdev->dev.minor = MISC_DYNAMIC_MINOR;
+ err = misc_register(&rawdev->dev);
+ }
+
+ if (err) {
+ printk(KERN_INFO "rawdev: failed to register raw access device for %s\n",
+ serio->phys);
+ goto out_close;
+ }
+
+ printk(KERN_INFO "rawdev: raw access enabled on %s (%s, minor %d)\n",
+ serio->phys, rawdev->name, rawdev->dev.minor);
+ goto out;
+
+out_close:
+ serio_close(serio);
+ list_del_init(&rawdev->node);
+out_free:
+ serio->private = NULL;
+ kfree(rawdev);
+out:
+ up(&rawdev_sem);
+}
+
+static int rawdev_reconnect(struct serio *serio)
+{
+ struct rawdev *rawdev = serio->private;
+ struct serio_dev *dev = serio->dev;
+
+ if (!dev || !rawdev) {
+ printk(KERN_DEBUG "rawdev: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ /*
+ * Nothing needs to be done here, we just need this method to
+ * keep the same device.
+ */
+ return 0;
+}
+
+static void rawdev_disconnect(struct serio *serio)
+{
+ struct rawdev *rawdev;
+
+ down(&rawdev_sem);
+
+ rawdev = serio->private;
+
+ serio_close(serio);
+ serio->private = NULL;
+
+ rawdev->serio = NULL;
+ if (!rawdev_cleanup(rawdev))
+ wake_up_interruptible(&rawdev->wait);
+
+ up(&rawdev_sem);
+}
+
+static struct serio_dev rawdev_dev = {
+ .interrupt = rawdev_interrupt,
+ .connect = rawdev_connect,
+ .reconnect = rawdev_reconnect,
+ .disconnect = rawdev_disconnect,
+};
+
+int __init rawdev_init(void)
+{
+ serio_register_device(&rawdev_dev);
+ return 0;
+}
+
+void __exit rawdev_exit(void)
+{
+ serio_unregister_device(&rawdev_dev);
+}
+
+module_init(rawdev_init);
+module_exit(rawdev_exit);
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
--- a/include/linux/serio.h 2004-06-02 02:15:20 -05:00
+++ b/include/linux/serio.h 2004-06-02 02:15:20 -05:00
@@ -108,6 +108,7 @@
#define SERIO_PC9800 0x04000000UL
#define SERIO_PS_PSTHRU 0x05000000UL
#define SERIO_8042_XL 0x06000000UL
+#define SERIO_8042_RAW 0x07000000UL

#define SERIO_PROTO 0xFFUL
#define SERIO_MSC 0x01


2004-06-02 07:37:11

by Andrew Morton

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

Dmitry Torokhov <[email protected]> wrote:
>
> Hi,
>
> Below is an implementation of rawdev driver.

Yes, it does appear that we need the feature, thanks.

> Comments?

They're the thingies inside /* and */. Your patch is refreshingly free of
them ;)

> +static ssize_t rawdev_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
> +{
> + struct rawdev_list *list = file->private_data;
> + ssize_t written = 0;
> + int retval;
> + unsigned char c;
> +
> + retval = down_interruptible(&rawdev_sem);
> + if (retval)
> + return retval;
> +
> + if (!list->rawdev->serio) {
> + retval = -ENODEV;
> + goto out;
> + }

The return values here are mucked up - this function returns `written'.

> + if (count > 32)
> + count = 32;

Why? (Don't tell me - add a comment!)

2004-06-02 07:43:16

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

On Wednesday 02 June 2004 02:36 am, Andrew Morton wrote:
> Dmitry Torokhov <[email protected]> wrote:
> >
> > Hi,
> >
> > Below is an implementation of rawdev driver.
>
> Yes, it does appear that we need the feature, thanks.
>
> > Comments?
>
> They're the thingies inside /* and */. Your patch is refreshingly free of
> them ;)
>
> > +static ssize_t rawdev_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
> > +{
> > + struct rawdev_list *list = file->private_data;
> > + ssize_t written = 0;
> > + int retval;
> > + unsigned char c;
> > +
> > + retval = down_interruptible(&rawdev_sem);
> > + if (retval)
> > + return retval;
> > +
> > + if (!list->rawdev->serio) {
> > + retval = -ENODEV;
> > + goto out;
> > + }
>
> The return values here are mucked up - this function returns `written'.

Or an error code in negative notation - it's the standard practice for
read/write. Am I missing something?

>
> > + if (count > 32)
> > + count = 32;
>
> Why? (Don't tell me - add a comment!)

I have no idea - taken from 2.4... My best guess would be it's an attempt not to
hog the device... I'll sprinkle some comments ;)

--
Dmitry

2004-06-02 07:47:36

by Andrew Morton

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

Dmitry Torokhov <[email protected]> wrote:
>
> > The return values here are mucked up - this function returns `written'.
>
> Or an error code in negative notation - it's the standard practice for
> read/write. Am I missing something?

You want the function to return -ENODEV if !list->rawdev->serio

if (!list->rawdev->serio) {
retval = -ENODEV;
goto out;
}
...
out:
up(&rawdev_sem);
return written;
}

But it won't.

2004-06-02 07:49:18

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

On Wednesday 02 June 2004 02:43 am, Dmitry Torokhov wrote:
> On Wednesday 02 June 2004 02:36 am, Andrew Morton wrote:
> > Dmitry Torokhov <[email protected]> wrote:
> > >
> > > Hi,
> > >
> > > Below is an implementation of rawdev driver.
> >
> > Yes, it does appear that we need the feature, thanks.
> >
> > > Comments?
> >
> > They're the thingies inside /* and */. Your patch is refreshingly free of
> > them ;)
> >
> > > +static ssize_t rawdev_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
> > > +{
> > > + struct rawdev_list *list = file->private_data;
> > > + ssize_t written = 0;
> > > + int retval;
> > > + unsigned char c;
> > > +
> > > + retval = down_interruptible(&rawdev_sem);
> > > + if (retval)
> > > + return retval;
> > > +
> > > + if (!list->rawdev->serio) {
> > > + retval = -ENODEV;
> > > + goto out;
> > > + }
> >
> > The return values here are mucked up - this function returns `written'.
>
> Or an error code in negative notation - it's the standard practice for
> read/write. Am I missing something?
>
Oh, yes, I am... Sorry, will fix tomorrow.

--
Dmitry

2004-06-03 05:46:26

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

Ok, new version:

- fixed return value logic in rawdev_write,
- rawdev_read and rawdev_write arguments marked with __user,
- some comments sprinkled around ;)



===================================================================


[email protected], 2004-06-02 22:31:17-05:00, [email protected]
Input: Add rawdev driver that binds to serio ports marked as 'raw'
and provides unobstructed access to the underlying serio port
via a char device. The driver tries to register char device
10,1 (/dev/psaux) first and if it fails goes for dynamically
allocated minor.

Signed-off-by: Dmitry Torokhov <[email protected]>


drivers/input/misc/Kconfig | 12 +
drivers/input/misc/Makefile | 1
drivers/input/misc/rawdev.c | 422 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serio.h | 1
4 files changed, 436 insertions(+)


===================================================================



diff -Nru a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
--- a/drivers/input/misc/Kconfig 2004-06-02 23:08:36 -05:00
+++ b/drivers/input/misc/Kconfig 2004-06-02 23:08:36 -05:00
@@ -54,3 +54,15 @@
To compile this driver as a module, choose M here: the
module will be called uinput.

+config INPUT_RAWDEV
+ tristate "Raw access to serio ports"
+ depends on INPUT && INPUT_MISC
+ help
+ Say Y here if you want to have raw access to serio ports, such as
+ AUX ports on i8042 keyboard controller. Each serio port that is
+ marked as providing raw access will be accessible via a char device
+ with major 10 and dynamically allocated minor (it will first try
+ allocating minor 1 historically corresponding to /dev/psaux).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rawdev.
diff -Nru a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
--- a/drivers/input/misc/Makefile 2004-06-02 23:08:36 -05:00
+++ b/drivers/input/misc/Makefile 2004-06-02 23:08:36 -05:00
@@ -9,3 +9,4 @@
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_98SPKR) += 98spkr.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+obj-$(CONFIG_INPUT_RAWDEV) += rawdev.o
diff -Nru a/drivers/input/misc/rawdev.c b/drivers/input/misc/rawdev.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/input/misc/rawdev.c 2004-06-02 23:08:36 -05:00
@@ -0,0 +1,422 @@
+/*
+ * Raw serio device providing access to a raw byte stream from underlying
+ * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
+ *
+ * Copyright (c) 2004 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <[email protected]>");
+MODULE_DESCRIPTION("Raw serio driver");
+MODULE_LICENSE("GPL");
+
+#define RAWDEV_QUEUE_LEN 64
+struct rawdev {
+ unsigned char queue[RAWDEV_QUEUE_LEN];
+ unsigned int tail, head;
+ spinlock_t lock; /* protects queue, tail and head */
+
+ char name[16];
+ unsigned int refcnt;
+ struct serio *serio;
+ struct miscdevice dev;
+ wait_queue_head_t wait;
+ struct list_head list;
+ struct list_head node;
+};
+
+struct rawdev_list {
+ struct fasync_struct *fasync;
+ struct rawdev *rawdev;
+ struct list_head node;
+};
+
+static DECLARE_MUTEX(rawdev_sem); /* protects rawdev_list, rawdev_no and rawdev->serio */
+static LIST_HEAD(rawdev_list);
+static unsigned int rawdev_no;
+
+
+/*********************************************************************
+ * Interface with userspace (file operations) *
+ *********************************************************************/
+
+static int rawdev_fasync(int fd, struct file *file, int on)
+{
+ struct rawdev_list *list = file->private_data;
+ int retval;
+
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+/*
+ * Try to locate rawdev corresponding to a given minor.
+ * rawdev_sem has to be taken before calling rawdev_locate.
+ */
+static struct rawdev *rawdev_locate(int minor)
+{
+ struct rawdev *rawdev;
+
+ list_for_each_entry(rawdev, &rawdev_list, node) {
+ if (rawdev->dev.minor == minor)
+ return rawdev;
+ }
+
+ return NULL;
+}
+
+static int rawdev_open(struct inode *inode, struct file *file)
+{
+ struct rawdev *rawdev;
+ struct rawdev_list *list;
+ int retval;
+
+ retval = down_interruptible(&rawdev_sem);
+ if (retval)
+ return retval;
+
+ if (!(rawdev = rawdev_locate(iminor(inode)))) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!rawdev->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!(list = kmalloc(sizeof(struct rawdev_list), GFP_KERNEL))) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ memset(list, 0, sizeof(struct rawdev_list));
+ list->rawdev = rawdev;
+ file->private_data = list;
+
+ rawdev->refcnt++;
+ list_add_tail(&list->node, &rawdev->list);
+
+out:
+ up(&rawdev_sem);
+ return retval;
+}
+
+/*
+ * rawdev_cleanup() frees rawdev once last reference has been dropped.
+ * It has to be called with rawdev_sem already taken.
+ */
+static int rawdev_cleanup(struct rawdev *rawdev)
+{
+ if (--rawdev->refcnt == 0) {
+ misc_deregister(&rawdev->dev);
+ list_del_init(&rawdev->node);
+ kfree(rawdev);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rawdev_release(struct inode *inode, struct file *file)
+{
+ struct rawdev_list *list = file->private_data;
+ struct rawdev *rawdev = list->rawdev;
+
+ down(&rawdev_sem);
+
+ rawdev_fasync(-1, file, 0);
+ rawdev_cleanup(rawdev);
+
+ up(&rawdev_sem);
+
+ return 0;
+}
+
+static int rawdev_fetch_byte(struct rawdev *rawdev, unsigned char *c)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&rawdev->lock, flags);
+
+ empty = rawdev->head == rawdev->tail;
+ if (!empty) {
+ *c = rawdev->queue[rawdev->tail];
+ rawdev->tail = (rawdev->tail + 1) % RAWDEV_QUEUE_LEN;
+ }
+
+ spin_unlock_irqrestore(&rawdev->lock, flags);
+
+ return !empty;
+}
+
+static ssize_t rawdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct rawdev_list *list = file->private_data;
+ struct rawdev *rawdev = list->rawdev;
+ unsigned char c;
+ ssize_t retval;
+
+ /*
+ * Unlike rawdev_write we do not have to take rawdev_sem here since
+ * we do not access rawdev->serio itself, we just check if it is
+ * present. And even if it goes away in the middle of copying data
+ * to userspace it's OK because rawdev will stick around anyway.
+ */
+
+ if (!rawdev->serio)
+ return -ENODEV;
+
+ if (rawdev->head == rawdev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(list->rawdev->wait,
+ rawdev->head != rawdev->tail || !rawdev->serio);
+ if (retval)
+ return retval;
+
+ if (!rawdev->serio)
+ return -ENODEV;
+
+ while (retval < count && rawdev_fetch_byte(rawdev, &c)) {
+ if (put_user(c, buffer++))
+ return -EFAULT;
+ retval++;
+ }
+
+ return retval;
+}
+
+static ssize_t rawdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct rawdev_list *list = file->private_data;
+ ssize_t retval;
+ unsigned char c;
+
+ retval = down_interruptible(&rawdev_sem);
+ if (retval)
+ return retval;
+
+ /* Check if underlying serio went away while we were waiting */
+ if (!list->rawdev->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * Do not hog the device for too long.
+ * The same limit is used in 2.4 psaux implementation which we trying
+ * to follow as close as possible
+ */
+ if (count > 32)
+ count = 32;
+
+ while (count--) {
+ if (get_user(c, buffer++)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ if (serio_write(list->rawdev->serio, c)) {
+ retval = -EIO;
+ goto out;
+ }
+ /* Normally write returns number of bytes written */
+ retval++;
+ }
+
+out:
+ up(&rawdev_sem);
+ return retval;
+}
+
+static unsigned int rawdev_poll(struct file *file, poll_table *wait)
+{
+ struct rawdev_list *list = file->private_data;
+
+ poll_wait(file, &list->rawdev->wait, wait);
+
+ if (list->rawdev->head != list->rawdev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+struct file_operations rawdev_fops = {
+ .owner = THIS_MODULE,
+ .open = rawdev_open,
+ .release = rawdev_release,
+ .read = rawdev_read,
+ .write = rawdev_write,
+ .poll = rawdev_poll,
+ .fasync = rawdev_fasync,
+};
+
+
+/*********************************************************************
+ * Interface with serio port *
+ *********************************************************************/
+
+/*
+ * rawdev_interrupt gets a byte from the underlying serio port and stores
+ * it in rawdev's ring buffer for rawdev_fetch_byte to fetch.
+ */
+static irqreturn_t rawdev_interrupt(struct serio *serio, unsigned char data,
+ unsigned int dfl, struct pt_regs *regs)
+{
+ struct rawdev *rawdev = serio->private;
+ struct rawdev_list *list;
+ unsigned int head = rawdev->head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rawdev->lock, flags);
+
+ rawdev->queue[head] = data;
+ head = (head + 1) % RAWDEV_QUEUE_LEN;
+ if (likely(head != rawdev->tail)) {
+ rawdev->head = head;
+ list_for_each_entry(list, &rawdev->list, node)
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ wake_up_interruptible(&rawdev->wait);
+ }
+
+ spin_unlock_irqrestore(&rawdev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void rawdev_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct rawdev *rawdev;
+ int err;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_8042_RAW)
+ return;
+
+ if (!(rawdev = kmalloc(sizeof(struct rawdev), GFP_KERNEL))) {
+ printk(KERN_ERR "rawdev.c: can't allocate memory for a device\n");
+ return;
+ }
+
+ down(&rawdev_sem);
+
+ memset(rawdev, 0, sizeof(struct rawdev));
+ snprintf(rawdev->name, sizeof(rawdev->name), "serio_raw%d", rawdev_no++);
+ rawdev->refcnt = 1;
+ rawdev->serio = serio;
+ INIT_LIST_HEAD(&rawdev->list);
+ init_waitqueue_head(&rawdev->wait);
+
+ serio->private = rawdev;
+ if (serio_open(serio, dev))
+ goto out_free;
+
+ list_add_tail(&rawdev->node, &rawdev_list);
+
+ /*
+ * Try binding to char 10,1 (/dev/psaux) first. Assuming that average
+ * system will have at most one port in raw mode it should keep thigs
+ * pretty much the same as 2.4 and earlier kernels.
+ * If 10,1 has already been taken retry with dynamically allocated
+ * minor.
+ */
+ rawdev->dev.minor = PSMOUSE_MINOR;
+ rawdev->dev.name = rawdev->name;
+ rawdev->dev.fops = &rawdev_fops;
+
+ err = misc_register(&rawdev->dev);
+ if (err) {
+ rawdev->dev.minor = MISC_DYNAMIC_MINOR;
+ err = misc_register(&rawdev->dev);
+ }
+
+ if (err) {
+ printk(KERN_INFO "rawdev: failed to register raw access device for %s\n",
+ serio->phys);
+ goto out_close;
+ }
+
+ printk(KERN_INFO "rawdev: raw access enabled on %s (%s, minor %d)\n",
+ serio->phys, rawdev->name, rawdev->dev.minor);
+ goto out;
+
+out_close:
+ serio_close(serio);
+ list_del_init(&rawdev->node);
+out_free:
+ serio->private = NULL;
+ kfree(rawdev);
+out:
+ up(&rawdev_sem);
+}
+
+static int rawdev_reconnect(struct serio *serio)
+{
+ struct rawdev *rawdev = serio->private;
+ struct serio_dev *dev = serio->dev;
+
+ if (!dev || !rawdev) {
+ printk(KERN_DEBUG "rawdev: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ /*
+ * Nothing needs to be done here, we just need this method to
+ * keep the same device.
+ */
+ return 0;
+}
+
+static void rawdev_disconnect(struct serio *serio)
+{
+ struct rawdev *rawdev;
+
+ down(&rawdev_sem);
+
+ rawdev = serio->private;
+
+ serio_close(serio);
+ serio->private = NULL;
+
+ rawdev->serio = NULL;
+ if (!rawdev_cleanup(rawdev))
+ wake_up_interruptible(&rawdev->wait);
+
+ up(&rawdev_sem);
+}
+
+static struct serio_dev rawdev_dev = {
+ .interrupt = rawdev_interrupt,
+ .connect = rawdev_connect,
+ .reconnect = rawdev_reconnect,
+ .disconnect = rawdev_disconnect,
+};
+
+int __init rawdev_init(void)
+{
+ serio_register_device(&rawdev_dev);
+ return 0;
+}
+
+void __exit rawdev_exit(void)
+{
+ serio_unregister_device(&rawdev_dev);
+}
+
+module_init(rawdev_init);
+module_exit(rawdev_exit);
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
--- a/include/linux/serio.h 2004-06-02 23:08:36 -05:00
+++ b/include/linux/serio.h 2004-06-02 23:08:36 -05:00
@@ -108,6 +108,7 @@
#define SERIO_PC9800 0x04000000UL
#define SERIO_PS_PSTHRU 0x05000000UL
#define SERIO_8042_XL 0x06000000UL
+#define SERIO_8042_RAW 0x07000000UL

#define SERIO_PROTO 0xFFUL
#define SERIO_MSC 0x01

2004-06-03 06:26:41

by Sau Dan Lee

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

>>>>> "Dmitry" == Dmitry Torokhov <[email protected]> writes:

Dmitry> +config INPUT_RAWDEV
Dmitry> + tristate "Raw access to serio ports"
Dmitry> + depends on INPUT && INPUT_MISC
Dmitry> + help

Since this provides raw access to serio ports ONLY, shouldn't it also
depend on INPUT_SERIO? Shall we better call it SERIO_RAW?


--
Sau Dan LEE ???u??(Big5) ~{@nJX6X~}(HZ)

E-mail: [email protected]
Home page: http://www.informatik.uni-freiburg.de/~danlee

2004-06-03 06:38:14

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2)

On Thursday 03 June 2004 01:26 am, Sau Dan Lee wrote:
> >>>>> "Dmitry" == Dmitry Torokhov <[email protected]> writes:
>
> Dmitry> +config INPUT_RAWDEV
> Dmitry> + tristate "Raw access to serio ports"
> Dmitry> + depends on INPUT && INPUT_MISC
> Dmitry> + help
>
> Since this provides raw access to serio ports ONLY, shouldn't it also
> depend on INPUT_SERIO?

Yes, you are right, will change.

> Shall we better call it SERIO_RAW?
>

I could probably rename it to serio_rawdev... It's close to 2AM here so the
next version is due tomorrow night ;)

--
Dmitry

2004-06-05 07:33:41

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [RFC/RFT] Raw access to serio ports (1/2) - take 3

Ok, 3rd attempt... As Sau Dan Lee suggested renamed form rawdev to serio_raw
and adjsuted dependencies. Since it does not really need input subsystem,
just serio, moved to drivers/input/serio.

--
Dmitry


===================================================================


[email protected], 2004-06-05 00:47:44-05:00, [email protected]
Input: Add serio_raw driver that binds to serio ports marked as 'raw'
and provides unobstructed access to the underlying serio port
via a char device. The driver tries to register char device
10,1 (/dev/psaux) first and if it fails goes for dynamically
allocated minor.

Signed-off-by: Dmitry Torokhov <[email protected]>


drivers/input/serio/Kconfig | 14 +
drivers/input/serio/Makefile | 1
drivers/input/serio/serio_raw.c | 388 ++++++++++++++++++++++++++++++++++++++++
include/linux/serio.h | 1
4 files changed, 404 insertions(+)


===================================================================



diff -Nru a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
--- a/drivers/input/serio/Kconfig 2004-06-05 02:05:24 -05:00
+++ b/drivers/input/serio/Kconfig 2004-06-05 02:05:24 -05:00
@@ -140,3 +140,17 @@

To compile this driver as a module, choose M here: the
module will be called maceps2.
+
+config SERIO_RAW
+ tristate "Raw access to serio ports"
+ depends on SERIO
+ help
+ Say Y here if you want to have raw access to serio ports, such as
+ AUX ports on i8042 keyboard controller. Each serio port that is
+ marked as providing raw access will be accessible via a char device
+ with major 10 and dynamically allocated minor. The driver will try
+ allocating minor 1 (that historically corresponds to /dev/psaux)
+ first.
+
+ To compile this driver as a module, choose M here: the
+ module will be called serio_raw.
diff -Nru a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
--- a/drivers/input/serio/Makefile 2004-06-05 02:05:24 -05:00
+++ b/drivers/input/serio/Makefile 2004-06-05 02:05:24 -05:00
@@ -17,3 +17,4 @@
obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
+obj-$(CONFIG_SERIO_RAW) += serio_raw.o
diff -Nru a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/input/serio/serio_raw.c 2004-06-05 02:05:24 -05:00
@@ -0,0 +1,388 @@
+/*
+ * Raw serio device providing access to a raw byte stream from underlying
+ * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
+ *
+ * Copyright (c) 2004 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <[email protected]>");
+MODULE_DESCRIPTION("Raw serio driver");
+MODULE_LICENSE("GPL");
+
+#define SERIO_RAW_QUEUE_LEN 64
+struct serio_raw {
+ unsigned char queue[SERIO_RAW_QUEUE_LEN];
+ unsigned int tail, head;
+ spinlock_t lock;
+
+ char name[16];
+ unsigned int refcnt;
+ struct serio *serio;
+ struct miscdevice dev;
+ wait_queue_head_t wait;
+ struct list_head list;
+ struct list_head node;
+};
+
+struct serio_raw_list {
+ struct fasync_struct *fasync;
+ struct serio_raw *serio_raw;
+ struct list_head node;
+};
+
+static DECLARE_MUTEX(serio_raw_sem);
+static LIST_HEAD(serio_raw_list);
+static unsigned int serio_raw_no;
+
+/*********************************************************************
+ * Interface with userspace (file operations) *
+ *********************************************************************/
+
+static int serio_raw_fasync(int fd, struct file *file, int on)
+{
+ struct serio_raw_list *list = file->private_data;
+ int retval;
+
+ retval = fasync_helper(fd, file, on, &list->fasync);
+ return retval < 0 ? retval : 0;
+}
+
+static struct serio_raw *serio_raw_locate(int minor)
+{
+ struct serio_raw *serio_raw;
+
+ list_for_each_entry(serio_raw, &serio_raw_list, node) {
+ if (serio_raw->dev.minor == minor)
+ return serio_raw;
+ }
+
+ return NULL;
+}
+
+static int serio_raw_open(struct inode *inode, struct file *file)
+{
+ struct serio_raw *serio_raw;
+ struct serio_raw_list *list;
+ int retval = 0;
+
+ retval = down_interruptible(&serio_raw_sem);
+ if (retval)
+ return retval;
+
+ if (!(serio_raw = serio_raw_locate(iminor(inode)))) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!serio_raw->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ memset(list, 0, sizeof(struct serio_raw_list));
+ list->serio_raw = serio_raw;
+ file->private_data = list;
+
+ serio_raw->refcnt++;
+ list_add_tail(&list->node, &serio_raw->list);
+
+out:
+ up(&serio_raw_sem);
+ return retval;
+}
+
+static int serio_raw_cleanup(struct serio_raw *serio_raw)
+{
+ if (--serio_raw->refcnt == 0) {
+ misc_deregister(&serio_raw->dev);
+ list_del_init(&serio_raw->node);
+ kfree(serio_raw);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int serio_raw_release(struct inode *inode, struct file *file)
+{
+ struct serio_raw_list *list = file->private_data;
+ struct serio_raw *serio_raw = list->serio_raw;
+
+ down(&serio_raw_sem);
+
+ serio_raw_fasync(-1, file, 0);
+ serio_raw_cleanup(serio_raw);
+
+ up(&serio_raw_sem);
+ return 0;
+}
+
+static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&serio_raw->lock, flags);
+
+ empty = serio_raw->head == serio_raw->tail;
+ if (!empty) {
+ *c = serio_raw->queue[serio_raw->tail];
+ serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
+ }
+
+ spin_unlock_irqrestore(&serio_raw->lock, flags);
+
+ return !empty;
+}
+
+static ssize_t serio_raw_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct serio_raw_list *list = file->private_data;
+ struct serio_raw *serio_raw = list->serio_raw;
+ char c;
+ ssize_t retval = 0;
+
+ if (!serio_raw->serio)
+ return -ENODEV;
+
+ if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(list->serio_raw->wait,
+ serio_raw->head != serio_raw->tail || !serio_raw->serio);
+ if (retval)
+ return retval;
+
+ if (!serio_raw->serio)
+ return -ENODEV;
+
+ while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
+ if (put_user(c, buffer++))
+ return -EFAULT;
+ retval++;
+ }
+
+ return retval;
+}
+
+static ssize_t serio_raw_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct serio_raw_list *list = file->private_data;
+ ssize_t written = 0;
+ int retval;
+ unsigned char c;
+
+ retval = down_interruptible(&serio_raw_sem);
+ if (retval)
+ return retval;
+
+ if (!list->serio_raw->serio) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (count > 32)
+ count = 32;
+
+ while (count--) {
+ if (get_user(c, buffer++)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ if (serio_write(list->serio_raw->serio, c)) {
+ retval = -EIO;
+ goto out;
+ }
+ written++;
+ };
+
+out:
+ up(&serio_raw_sem);
+ return written;
+}
+
+static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
+{
+ struct serio_raw_list *list = file->private_data;
+
+ poll_wait(file, &list->serio_raw->wait, wait);
+
+ if (list->serio_raw->head != list->serio_raw->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+struct file_operations serio_raw_fops = {
+ .owner = THIS_MODULE,
+ .open = serio_raw_open,
+ .release = serio_raw_release,
+ .read = serio_raw_read,
+ .write = serio_raw_write,
+ .poll = serio_raw_poll,
+ .fasync = serio_raw_fasync,
+};
+
+
+/*********************************************************************
+ * Interface with serio port *
+ *********************************************************************/
+
+static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
+ unsigned int dfl, struct pt_regs *regs)
+{
+ struct serio_raw *serio_raw = serio->private;
+ struct serio_raw_list *list;
+ unsigned int head = serio_raw->head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serio_raw->lock, flags);
+
+ serio_raw->queue[head] = data;
+ head = (head + 1) % SERIO_RAW_QUEUE_LEN;
+ if (likely(head != serio_raw->tail)) {
+ serio_raw->head = head;
+ list_for_each_entry(list, &serio_raw->list, node)
+ kill_fasync(&list->fasync, SIGIO, POLL_IN);
+ wake_up_interruptible(&serio_raw->wait);
+ }
+
+ spin_unlock_irqrestore(&serio_raw->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void serio_raw_connect(struct serio *serio, struct serio_dev *dev)
+{
+ struct serio_raw *serio_raw;
+ int err;
+
+ if ((serio->type & SERIO_TYPE) != SERIO_8042_RAW)
+ return;
+
+ if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
+ printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
+ return;
+ }
+
+ down(&serio_raw_sem);
+
+ memset(serio_raw, 0, sizeof(struct serio_raw));
+ snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
+ serio_raw->refcnt = 1;
+ serio_raw->serio = serio;
+ INIT_LIST_HEAD(&serio_raw->list);
+ init_waitqueue_head(&serio_raw->wait);
+
+ serio->private = serio_raw;
+ if (serio_open(serio, dev))
+ goto out_free;
+
+ list_add_tail(&serio_raw->node, &serio_raw_list);
+
+ serio_raw->dev.minor = PSMOUSE_MINOR;
+ serio_raw->dev.name = serio_raw->name;
+ serio_raw->dev.fops = &serio_raw_fops;
+
+ err = misc_register(&serio_raw->dev);
+ if (err) {
+ serio_raw->dev.minor = MISC_DYNAMIC_MINOR;
+ err = misc_register(&serio_raw->dev);
+ }
+
+ if (err) {
+ printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n",
+ serio->phys);
+ goto out_close;
+ }
+
+ printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n",
+ serio->phys, serio_raw->name, serio_raw->dev.minor);
+ goto out;
+
+out_close:
+ serio_close(serio);
+ list_del_init(&serio_raw->node);
+out_free:
+ serio->private = NULL;
+ kfree(serio_raw);
+out:
+ up(&serio_raw_sem);
+}
+
+static int serio_raw_reconnect(struct serio *serio)
+{
+ struct serio_raw *serio_raw = serio->private;
+ struct serio_dev *dev = serio->dev;
+
+ if (!dev || !serio_raw) {
+ printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ /*
+ * Nothing needs to be done here, we just need this method to
+ * keep the same device.
+ */
+ return 0;
+}
+
+static void serio_raw_disconnect(struct serio *serio)
+{
+ struct serio_raw *serio_raw;
+
+ down(&serio_raw_sem);
+
+ serio_raw = serio->private;
+
+ serio_close(serio);
+ serio->private = NULL;
+
+ serio_raw->serio = NULL;
+ if (!serio_raw_cleanup(serio_raw))
+ wake_up_interruptible(&serio_raw->wait);
+
+ up(&serio_raw_sem);
+}
+
+static struct serio_dev serio_raw_dev = {
+ .interrupt = serio_raw_interrupt,
+ .connect = serio_raw_connect,
+ .reconnect = serio_raw_reconnect,
+ .disconnect = serio_raw_disconnect,
+};
+
+int __init serio_raw_init(void)
+{
+ serio_register_device(&serio_raw_dev);
+ return 0;
+}
+
+void __exit serio_raw_exit(void)
+{
+ serio_unregister_device(&serio_raw_dev);
+}
+
+module_init(serio_raw_init);
+module_exit(serio_raw_exit);
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
--- a/include/linux/serio.h 2004-06-05 02:05:24 -05:00
+++ b/include/linux/serio.h 2004-06-05 02:05:24 -05:00
@@ -111,6 +111,7 @@
#define SERIO_PC9800 0x04000000UL
#define SERIO_PS_PSTHRU 0x05000000UL
#define SERIO_8042_XL 0x06000000UL
+#define SERIO_8042_RAW 0x07000000UL

#define SERIO_PROTO 0xFFUL
#define SERIO_MSC 0x01