/dev/port only supports reading and writing 8-bit ports; multi-byte
operations on /dev/port will just operate on multiple successive 8-bit
ports.

Add a new device, /dev/ioports, which supports reading and writing
16-bit and 32-bit ports.  This makes it possible to perform arbitrary
I/O port operations cleanly from privileged userspace processes, without
using iopl or ioperm.

Signed-off-by: Josh Triplett <[email protected]>
---

Written after encountering yet another out-of-tree omnibus "do arbitrary
I/O for test purposes" driver; this one's main reason for existence was
the inability to operate on 16-bit and 32-bit I/O ports.  Let's get a
proper interface into the kernel, to make such drivers obsolete.

I've also written a corresponding manpage patch, which I'll submit as a
reply to this one.

 drivers/char/mem.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 77 insertions(+), 2 deletions(-)

diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 917403f..84e0526 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -35,6 +35,7 @@
 #endif
 
 #define DEVPORT_MINOR  4
+#define DEVIOPORTS_MINOR       12
 
 static inline unsigned long size_inside_page(unsigned long start,
                                             unsigned long size)
@@ -584,6 +585,69 @@ static ssize_t write_port(struct file *file, const char 
__user *buf,
        *ppos = i;
        return tmp-buf;
 }
+
+static ssize_t read_ioports(struct file *file, char __user *buf,
+                           size_t count, loff_t *ppos)
+{
+       unsigned long port = *ppos;
+
+       if (!access_ok(VERIFY_WRITE, buf, count))
+               return -EFAULT;
+       if (port > 65535)
+               return 0;
+       switch (count) {
+       case 1:
+               if (__put_user(inb(port), buf) < 0)
+                       return -EFAULT;
+               return 1;
+       case 2:
+               if (__put_user(inw(port), buf) < 0)
+                       return -EFAULT;
+               return 2;
+       case 4:
+               if (__put_user(inl(port), buf) < 0)
+                       return -EFAULT;
+               return 4;
+       default:
+               return -EINVAL;
+       }
+}
+
+static ssize_t write_ioports(struct file *file, const char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       unsigned long port = *ppos;
+
+       if (!access_ok(VERIFY_READ, buf, count))
+               return -EFAULT;
+       if (port > 65535)
+               return 0;
+       switch (count) {
+       case 1: {
+               u8 b;
+               if (__get_user(b, buf))
+                       return -EFAULT;
+               outb(b, port);
+               return 1;
+       }
+       case 2: {
+               u16 w;
+               if (__get_user(w, buf))
+                       return -EFAULT;
+               outw(w, port);
+               return 2;
+       }
+       case 4: {
+               u32 l;
+               if (__get_user(l, buf))
+                       return -EFAULT;
+               outl(l, port);
+               return 4;
+       }
+       default:
+               return -EINVAL;
+       }
+}
 #endif
 
 static ssize_t read_null(struct file *file, char __user *buf,
@@ -779,6 +843,13 @@ static const struct file_operations port_fops = {
        .write          = write_port,
        .open           = open_port,
 };
+
+static const struct file_operations ioports_fops = {
+       .llseek         = memory_lseek,
+       .read           = read_ioports,
+       .write          = write_ioports,
+       .open           = open_port,
+};
 #endif
 
 static const struct file_operations zero_fops = {
@@ -827,6 +898,9 @@ static const struct memdev {
 #ifdef CONFIG_PRINTK
        [11] = { "kmsg", 0644, &kmsg_fops, NULL },
 #endif
+#ifdef CONFIG_DEVPORT
+        [12] = { "ioports", 0, &ioports_fops, NULL },
+#endif
 };
 
 static int memory_open(struct inode *inode, struct file *filp)
@@ -892,9 +966,10 @@ static int __init chr_dev_init(void)
                        continue;
 
                /*
-                * Create /dev/port?
+                * Create /dev/port and /dev/ioports?
                 */
-               if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
+               if ((minor == DEVPORT_MINOR || minor == DEVIOPORTS_MINOR)
+                   && !arch_has_dev_port())
                        continue;
 
                device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
-- 
2.0.0.rc2

--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to