2008-11-23 12:14:45

by Hans J. Koch

[permalink] [raw]
Subject: [PATCH RFC] UIO: Pass information about ioports to userspace

Devices sometimes have memory where all or parts of it can not be mapped to
userspace. But it might still be possible to access this memory from
userspace by other means. An example are PCI cards that advertise not only
mappable memory but also ioport ranges. On x86 architectures, these can be
accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
reported a similar problem on Blackfin arch where it doesn't seem to be easy
to mmap non-cached memory but it can still be accessed from userspace.

This patch allows kernel drivers to pass information about such ports to
userspace. Similar to the existing mem[] array, it adds a port[] array to
struct uio_info. Each port range is described by start, size, and porttype.

If a driver fills in at least one such port range, the UIO core will simply
pass this information to userspace by creating a new directory "portio"
underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will
contain a subdirectory (portX) for each port range given.

Note that UIO simply passes this information to userspace, it performs no
action whatsoever with this data. It's userspace's responsibility to obtain
access to these ports and to solve arch dependent issues. The "porttype"
attribute tells userspace what kind of port it is dealing with.

This mechanism could also be used to give userspace information about GPIOs
related to a device. You frequently find such hardware in embedded devices,
so I added a UIO_PORT_GPIO definition. I'm not really sure if this is a good
idea since there are other solutions to this problem, but it won't hurt much
anyway.

Signed-off-by: Hans J. Koch <[email protected]>

---
drivers/uio/uio.c | 154 ++++++++++++++++++++++++++++++++++++++++-----
include/linux/uio_driver.h | 25 +++++++
2 files changed, 162 insertions(+), 17 deletions(-)

Index: linux-2.6.28-rc/drivers/uio/uio.c
===================================================================
--- linux-2.6.28-rc.orig/drivers/uio/uio.c 2008-11-23 12:20:00.000000000 +0100
+++ linux-2.6.28-rc/drivers/uio/uio.c 2008-11-23 13:07:55.000000000 +0100
@@ -35,6 +35,7 @@
int vma_count;
struct uio_info *info;
struct kobject *map_dir;
+ struct kobject *portio_dir;
};

static int uio_major;
@@ -75,17 +76,17 @@
return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
}

-struct uio_sysfs_entry {
+struct map_sysfs_entry {
struct attribute attr;
ssize_t (*show)(struct uio_mem *, char *);
ssize_t (*store)(struct uio_mem *, const char *, size_t);
};

-static struct uio_sysfs_entry addr_attribute =
+static struct map_sysfs_entry addr_attribute =
__ATTR(addr, S_IRUGO, map_addr_show, NULL);
-static struct uio_sysfs_entry size_attribute =
+static struct map_sysfs_entry size_attribute =
__ATTR(size, S_IRUGO, map_size_show, NULL);
-static struct uio_sysfs_entry offset_attribute =
+static struct map_sysfs_entry offset_attribute =
__ATTR(offset, S_IRUGO, map_offset_show, NULL);

static struct attribute *attrs[] = {
@@ -106,9 +107,9 @@
{
struct uio_map *map = to_map(kobj);
struct uio_mem *mem = map->mem;
- struct uio_sysfs_entry *entry;
+ struct map_sysfs_entry *entry;

- entry = container_of(attr, struct uio_sysfs_entry, attr);
+ entry = container_of(attr, struct map_sysfs_entry, attr);

if (!entry->show)
return -EIO;
@@ -116,16 +117,88 @@
return entry->show(mem, buf);
}

-static struct sysfs_ops uio_sysfs_ops = {
+static struct sysfs_ops map_sysfs_ops = {
.show = map_type_show,
};

static struct kobj_type map_attr_type = {
.release = map_release,
- .sysfs_ops = &uio_sysfs_ops,
+ .sysfs_ops = &map_sysfs_ops,
.default_attrs = attrs,
};

+struct uio_portio {
+ struct kobject kobj;
+ struct uio_port *port;
+};
+#define to_portio(portio) container_of(portio, struct uio_portio, kobj)
+
+static ssize_t portio_start_show(struct uio_port *port, char *buf)
+{
+ return sprintf(buf, "0x%lx\n", port->start);
+}
+
+static ssize_t portio_size_show(struct uio_port *port, char *buf)
+{
+ return sprintf(buf, "0x%lx\n", port->size);
+}
+
+static ssize_t portio_porttype_show(struct uio_port *port, char *buf)
+{
+ return sprintf(buf, "0x%x\n", port->porttype);
+}
+
+struct portio_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct uio_port *, char *);
+ ssize_t (*store)(struct uio_port *, const char *, size_t);
+};
+
+static struct portio_sysfs_entry portio_start_attribute =
+ __ATTR(start, S_IRUGO, portio_start_show, NULL);
+static struct portio_sysfs_entry portio_size_attribute =
+ __ATTR(size, S_IRUGO, portio_size_show, NULL);
+static struct portio_sysfs_entry portio_porttype_attribute =
+ __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL);
+
+static struct attribute *portio_attrs[] = {
+ &portio_start_attribute.attr,
+ &portio_size_attribute.attr,
+ &portio_porttype_attribute.attr,
+ NULL,
+};
+
+static void portio_release(struct kobject *kobj)
+{
+ struct uio_portio *portio = to_portio(kobj);
+ kfree(portio);
+}
+
+static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct uio_portio *portio = to_portio(kobj);
+ struct uio_port *port = portio->port;
+ struct portio_sysfs_entry *entry;
+
+ entry = container_of(attr, struct portio_sysfs_entry, attr);
+
+ if (!entry->show)
+ return -EIO;
+
+ return entry->show(port, buf);
+}
+
+static struct sysfs_ops portio_sysfs_ops = {
+ .show = portio_type_show,
+};
+
+static struct kobj_type portio_attr_type = {
+ .release = portio_release,
+ .sysfs_ops = &portio_sysfs_ops,
+ .default_attrs = portio_attrs,
+};
+
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -177,10 +250,13 @@
static int uio_dev_add_attributes(struct uio_device *idev)
{
int ret;
- int mi;
+ int mi, pi;
int map_found = 0;
+ int portio_found = 0;
struct uio_mem *mem;
struct uio_map *map;
+ struct uio_port *port;
+ struct uio_portio *portio;

ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
if (ret)
@@ -195,25 +271,58 @@
idev->map_dir = kobject_create_and_add("maps",
&idev->dev->kobj);
if (!idev->map_dir)
- goto err;
+ goto err_map;
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
- goto err;
+ goto err_map;
kobject_init(&map->kobj, &map_attr_type);
map->mem = mem;
mem->map = map;
ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
if (ret)
- goto err;
+ goto err_map;
ret = kobject_uevent(&map->kobj, KOBJ_ADD);
if (ret)
- goto err;
+ goto err_map;
+ }
+
+ for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) {
+ port = &idev->info->port[pi];
+ if (port->size == 0)
+ break;
+ if (!portio_found) {
+ portio_found = 1;
+ idev->portio_dir = kobject_create_and_add("portio",
+ &idev->dev->kobj);
+ if (!idev->portio_dir)
+ goto err_portio;
+ }
+ portio = kzalloc(sizeof(*portio), GFP_KERNEL);
+ if (!portio)
+ goto err_portio;
+ kobject_init(&portio->kobj, &portio_attr_type);
+ portio->port = port;
+ port->portio = portio;
+ ret = kobject_add(&portio->kobj, idev->portio_dir,
+ "port%d", pi);
+ if (ret)
+ goto err_portio;
+ ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
+ if (ret)
+ goto err_portio;
}

return 0;

-err:
+err_portio:
+ for (pi--; pi >= 0; pi--) {
+ port = &idev->info->port[pi];
+ portio = port->portio;
+ kobject_put(&portio->kobj);
+ }
+ kobject_put(idev->portio_dir);
+err_map:
for (mi--; mi>=0; mi--) {
mem = &idev->info->mem[mi];
map = mem->map;
@@ -228,15 +337,26 @@

static void uio_dev_del_attributes(struct uio_device *idev)
{
- int mi;
+ int i;
struct uio_mem *mem;
- for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
- mem = &idev->info->mem[mi];
+ struct uio_port *port;
+
+ for (i = 0; i < MAX_UIO_MAPS; i++) {
+ mem = &idev->info->mem[i];
if (mem->size == 0)
break;
kobject_put(&mem->map->kobj);
}
kobject_put(idev->map_dir);
+
+ for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) {
+ port = &idev->info->port[i];
+ if (port->size == 0)
+ break;
+ kobject_put(&port->portio->kobj);
+ }
+ kobject_put(idev->portio_dir);
+
sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
}

Index: linux-2.6.28-rc/include/linux/uio_driver.h
===================================================================
--- linux-2.6.28-rc.orig/include/linux/uio_driver.h 2008-11-23 12:20:00.000000000 +0100
+++ linux-2.6.28-rc/include/linux/uio_driver.h 2008-11-23 13:07:55.000000000 +0100
@@ -38,6 +38,24 @@

#define MAX_UIO_MAPS 5

+struct uio_portio;
+
+/**
+ * struct uio_port - description of a UIO port region
+ * @start: start of port region
+ * @size: size of port region
+ * @porttype: type of port (see UIO_PORT_* below)
+ * @portio: for use by the UIO core only.
+ */
+struct uio_port {
+ unsigned long start;
+ unsigned long size;
+ int porttype;
+ struct uio_portio *portio;
+};
+
+#define MAX_UIO_PORT_REGIONS 5
+
struct uio_device;

/**
@@ -60,6 +78,7 @@
char *name;
char *version;
struct uio_mem mem[MAX_UIO_MAPS];
+ struct uio_port port[MAX_UIO_PORT_REGIONS];
long irq;
unsigned long irq_flags;
void *priv;
@@ -92,4 +111,10 @@
#define UIO_MEM_LOGICAL 2
#define UIO_MEM_VIRTUAL 3

+/* defines for uio_port->porttype */
+#define UIO_PORT_NONE 0
+#define UIO_PORT_X86 1
+#define UIO_PORT_GPIO 2
+#define UIO_PORT_OTHER 3
+
#endif /* _LINUX_UIO_DRIVER_H_ */


2008-11-24 01:45:24

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH RFC] UIO: Pass information about ioports to userspace

On Sun, Nov 23, 2008 at 01:14:20PM +0100, Hans J. Koch wrote:
> Devices sometimes have memory where all or parts of it can not be mapped to
> userspace. But it might still be possible to access this memory from
> userspace by other means. An example are PCI cards that advertise not only
> mappable memory but also ioport ranges. On x86 architectures, these can be
> accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
> reported a similar problem on Blackfin arch where it doesn't seem to be easy
> to mmap non-cached memory but it can still be accessed from userspace.
>
> This patch allows kernel drivers to pass information about such ports to
> userspace. Similar to the existing mem[] array, it adds a port[] array to
> struct uio_info. Each port range is described by start, size, and porttype.
>
> If a driver fills in at least one such port range, the UIO core will simply
> pass this information to userspace by creating a new directory "portio"
> underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will
> contain a subdirectory (portX) for each port range given.

This is good, but it would really be nice to provide a way for userspace
to access individual ports without having to have access to all ports in
the system. Lots of times we don't want to give root privileges to some
programs that only need to read and write simple data to a single
device.

thanks,

greg k-h

2008-11-24 01:45:42

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH RFC] UIO: Pass information about ioports to userspace

On Sun, Nov 23, 2008 at 01:14:20PM +0100, Hans J. Koch wrote:
> Devices sometimes have memory where all or parts of it can not be mapped to
> userspace. But it might still be possible to access this memory from
> userspace by other means. An example are PCI cards that advertise not only
> mappable memory but also ioport ranges. On x86 architectures, these can be
> accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
> reported a similar problem on Blackfin arch where it doesn't seem to be easy
> to mmap non-cached memory but it can still be accessed from userspace.
>
> This patch allows kernel drivers to pass information about such ports to
> userspace. Similar to the existing mem[] array, it adds a port[] array to
> struct uio_info. Each port range is described by start, size, and porttype.

Looks good, but I think you also forgot the documentation update :)

thanks,

greg k-h

2008-11-24 09:05:06

by Hans J. Koch

[permalink] [raw]
Subject: Re: [PATCH RFC] UIO: Pass information about ioports to userspace

On Sun, Nov 23, 2008 at 05:41:22PM -0800, Greg KH wrote:
> On Sun, Nov 23, 2008 at 01:14:20PM +0100, Hans J. Koch wrote:
> > Devices sometimes have memory where all or parts of it can not be mapped to
> > userspace. But it might still be possible to access this memory from
> > userspace by other means. An example are PCI cards that advertise not only
> > mappable memory but also ioport ranges. On x86 architectures, these can be
> > accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
> > reported a similar problem on Blackfin arch where it doesn't seem to be easy
> > to mmap non-cached memory but it can still be accessed from userspace.
> >
> > This patch allows kernel drivers to pass information about such ports to
> > userspace. Similar to the existing mem[] array, it adds a port[] array to
> > struct uio_info. Each port range is described by start, size, and porttype.
>
> Looks good, but I think you also forgot the documentation update :)

Not forgotten, just delayed :-)
We had other changes lately that need documentation update as well. Big patch
will come Real Soon Now...

thanks,
Hans

2008-11-24 10:19:45

by Hans J. Koch

[permalink] [raw]
Subject: Re: [PATCH RFC] UIO: Pass information about ioports to userspace

On Sun, Nov 23, 2008 at 05:40:54PM -0800, Greg KH wrote:
> On Sun, Nov 23, 2008 at 01:14:20PM +0100, Hans J. Koch wrote:
> > Devices sometimes have memory where all or parts of it can not be mapped to
> > userspace. But it might still be possible to access this memory from
> > userspace by other means. An example are PCI cards that advertise not only
> > mappable memory but also ioport ranges. On x86 architectures, these can be
> > accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
> > reported a similar problem on Blackfin arch where it doesn't seem to be easy
> > to mmap non-cached memory but it can still be accessed from userspace.
> >
> > This patch allows kernel drivers to pass information about such ports to
> > userspace. Similar to the existing mem[] array, it adds a port[] array to
> > struct uio_info. Each port range is described by start, size, and porttype.
> >
> > If a driver fills in at least one such port range, the UIO core will simply
> > pass this information to userspace by creating a new directory "portio"
> > underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will
> > contain a subdirectory (portX) for each port range given.
>
> This is good, but it would really be nice to provide a way for userspace
> to access individual ports without having to have access to all ports in
> the system. Lots of times we don't want to give root privileges to some
> programs that only need to read and write simple data to a single
> device.

Yes, of course, that'd be nice. But it's very much arch dependent. For
example, these x86 ioports need special handling on x86, but you can simply
mmap them on powerpc. Port-like memory ranges on other archs might require
something completely different.
Yes, some generic port access layer would really be good, but I'm not sure
if the UIO core is the right place to implement it. Do you already have a
solution in mind?
Maybe we can look at that in a second step. ATM I just want to avoid these
situations where userspace needs ugly tricks to find out which ioports
belong to a certain card.

Thanks,
Hans

2008-11-25 04:18:51

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH RFC] UIO: Pass information about ioports to userspace

On Mon, Nov 24, 2008 at 11:19:15AM +0100, Hans J. Koch wrote:
> On Sun, Nov 23, 2008 at 05:40:54PM -0800, Greg KH wrote:
> > On Sun, Nov 23, 2008 at 01:14:20PM +0100, Hans J. Koch wrote:
> > > Devices sometimes have memory where all or parts of it can not be mapped to
> > > userspace. But it might still be possible to access this memory from
> > > userspace by other means. An example are PCI cards that advertise not only
> > > mappable memory but also ioport ranges. On x86 architectures, these can be
> > > accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
> > > reported a similar problem on Blackfin arch where it doesn't seem to be easy
> > > to mmap non-cached memory but it can still be accessed from userspace.
> > >
> > > This patch allows kernel drivers to pass information about such ports to
> > > userspace. Similar to the existing mem[] array, it adds a port[] array to
> > > struct uio_info. Each port range is described by start, size, and porttype.
> > >
> > > If a driver fills in at least one such port range, the UIO core will simply
> > > pass this information to userspace by creating a new directory "portio"
> > > underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will
> > > contain a subdirectory (portX) for each port range given.
> >
> > This is good, but it would really be nice to provide a way for userspace
> > to access individual ports without having to have access to all ports in
> > the system. Lots of times we don't want to give root privileges to some
> > programs that only need to read and write simple data to a single
> > device.
>
> Yes, of course, that'd be nice. But it's very much arch dependent. For
> example, these x86 ioports need special handling on x86, but you can simply
> mmap them on powerpc. Port-like memory ranges on other archs might require
> something completely different.
> Yes, some generic port access layer would really be good, but I'm not sure
> if the UIO core is the right place to implement it. Do you already have a
> solution in mind?

No I don't, sorry, it's just come up a few times recently and I was
hoping that you would have something :)

> Maybe we can look at that in a second step. ATM I just want to avoid these
> situations where userspace needs ugly tricks to find out which ioports
> belong to a certain card.

Agreed, I'm not saying that this patch is not ok at all, sorry if you
misinterpreted it that way. I have no objection to this patch, as long
as it includes the needed documentation.

thanks,

greg k-h

2008-11-25 23:57:16

by Hans J. Koch

[permalink] [raw]
Subject: Re: [PATCH RFC] UIO: Pass information about ioports to userspace

On Mon, Nov 24, 2008 at 08:11:16PM -0800, Greg KH wrote:
> On Mon, Nov 24, 2008 at 11:19:15AM +0100, Hans J. Koch wrote:
> > On Sun, Nov 23, 2008 at 05:40:54PM -0800, Greg KH wrote:
> > > On Sun, Nov 23, 2008 at 01:14:20PM +0100, Hans J. Koch wrote:
> > > > Devices sometimes have memory where all or parts of it can not be mapped to
> > > > userspace. But it might still be possible to access this memory from
> > > > userspace by other means. An example are PCI cards that advertise not only
> > > > mappable memory but also ioport ranges. On x86 architectures, these can be
> > > > accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
> > > > reported a similar problem on Blackfin arch where it doesn't seem to be easy
> > > > to mmap non-cached memory but it can still be accessed from userspace.
> > > >
> > > > This patch allows kernel drivers to pass information about such ports to
> > > > userspace. Similar to the existing mem[] array, it adds a port[] array to
> > > > struct uio_info. Each port range is described by start, size, and porttype.
> > > >
> > > > If a driver fills in at least one such port range, the UIO core will simply
> > > > pass this information to userspace by creating a new directory "portio"
> > > > underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will
> > > > contain a subdirectory (portX) for each port range given.
> > >
> > > This is good, but it would really be nice to provide a way for userspace
> > > to access individual ports without having to have access to all ports in
> > > the system. Lots of times we don't want to give root privileges to some
> > > programs that only need to read and write simple data to a single
> > > device.
> >
> > Yes, of course, that'd be nice. But it's very much arch dependent. For
> > example, these x86 ioports need special handling on x86, but you can simply
> > mmap them on powerpc. Port-like memory ranges on other archs might require
> > something completely different.
> > Yes, some generic port access layer would really be good, but I'm not sure
> > if the UIO core is the right place to implement it. Do you already have a
> > solution in mind?
>
> No I don't, sorry, it's just come up a few times recently and I was
> hoping that you would have something :)

No such luck :) But I'll think about it...

>
> > Maybe we can look at that in a second step. ATM I just want to avoid these
> > situations where userspace needs ugly tricks to find out which ioports
> > belong to a certain card.
>
> Agreed, I'm not saying that this patch is not ok at all, sorry if you
> misinterpreted it that way.

I didn't ;-)

> I have no objection to this patch, as long
> as it includes the needed documentation.

Agreed. And I found another flaw. I think that the porttype attribute should
not be an int. A string constant would be much better, what do you think?

I think I'll come up with a second version soon, a new patch and another one
with the documentation.

Thanks,
Hans

2008-12-06 01:23:33

by Hans J. Koch

[permalink] [raw]
Subject: [PATCH 1/2] UIO: Pass information about ioports to userspace (V2)

Changes since version 1:
- The porttype attribute now returns a string instead of an int.
- Added documentation (see second patch)

Devices sometimes have memory where all or parts of it can not be mapped to
userspace. But it might still be possible to access this memory from
userspace by other means. An example are PCI cards that advertise not only
mappable memory but also ioport ranges. On x86 architectures, these can be
accessed with ioperm, iopl, inb, outb, and friends. Mike Frysinger (CCed)
reported a similar problem on Blackfin arch where it doesn't seem to be easy
to mmap non-cached memory but it can still be accessed from userspace.

This patch allows kernel drivers to pass information about such ports to
userspace. Similar to the existing mem[] array, it adds a port[] array to
struct uio_info. Each port range is described by start, size, and porttype.

If a driver fills in at least one such port range, the UIO core will simply
pass this information to userspace by creating a new directory "portio"
underneath /sys/class/uio/uioN/. Similar to the "mem" directory, it will
contain a subdirectory (portX) for each port range given.

Note that UIO simply passes this information to userspace, it performs no
action whatsoever with this data. It's userspace's responsibility to obtain
access to these ports and to solve arch dependent issues. The "porttype"
attribute tells userspace what kind of port it is dealing with.

This mechanism could also be used to give userspace information about GPIOs
related to a device. You frequently find such hardware in embedded devices,
so I added a UIO_PORT_GPIO definition. I'm not really sure if this is a good
idea since there are other solutions to this problem, but it won't hurt much
anyway.

Signed-off-by: Hans J. Koch <[email protected]>

---
drivers/uio/uio.c | 159 ++++++++++++++++++++++++++++++++++++++++-----
include/linux/uio_driver.h | 26 +++++++
2 files changed, 168 insertions(+), 17 deletions(-)

Index: linux-2.6.28-rc/drivers/uio/uio.c
===================================================================
--- linux-2.6.28-rc.orig/drivers/uio/uio.c 2008-11-23 12:20:00.000000000 +0100
+++ linux-2.6.28-rc/drivers/uio/uio.c 2008-12-06 01:26:10.000000000 +0100
@@ -35,6 +35,7 @@
int vma_count;
struct uio_info *info;
struct kobject *map_dir;
+ struct kobject *portio_dir;
};

static int uio_major;
@@ -75,17 +76,17 @@
return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
}

-struct uio_sysfs_entry {
+struct map_sysfs_entry {
struct attribute attr;
ssize_t (*show)(struct uio_mem *, char *);
ssize_t (*store)(struct uio_mem *, const char *, size_t);
};

-static struct uio_sysfs_entry addr_attribute =
+static struct map_sysfs_entry addr_attribute =
__ATTR(addr, S_IRUGO, map_addr_show, NULL);
-static struct uio_sysfs_entry size_attribute =
+static struct map_sysfs_entry size_attribute =
__ATTR(size, S_IRUGO, map_size_show, NULL);
-static struct uio_sysfs_entry offset_attribute =
+static struct map_sysfs_entry offset_attribute =
__ATTR(offset, S_IRUGO, map_offset_show, NULL);

static struct attribute *attrs[] = {
@@ -106,9 +107,9 @@
{
struct uio_map *map = to_map(kobj);
struct uio_mem *mem = map->mem;
- struct uio_sysfs_entry *entry;
+ struct map_sysfs_entry *entry;

- entry = container_of(attr, struct uio_sysfs_entry, attr);
+ entry = container_of(attr, struct map_sysfs_entry, attr);

if (!entry->show)
return -EIO;
@@ -116,16 +117,93 @@
return entry->show(mem, buf);
}

-static struct sysfs_ops uio_sysfs_ops = {
+static struct sysfs_ops map_sysfs_ops = {
.show = map_type_show,
};

static struct kobj_type map_attr_type = {
.release = map_release,
- .sysfs_ops = &uio_sysfs_ops,
+ .sysfs_ops = &map_sysfs_ops,
.default_attrs = attrs,
};

+struct uio_portio {
+ struct kobject kobj;
+ struct uio_port *port;
+};
+#define to_portio(portio) container_of(portio, struct uio_portio, kobj)
+
+static ssize_t portio_start_show(struct uio_port *port, char *buf)
+{
+ return sprintf(buf, "0x%lx\n", port->start);
+}
+
+static ssize_t portio_size_show(struct uio_port *port, char *buf)
+{
+ return sprintf(buf, "0x%lx\n", port->size);
+}
+
+static ssize_t portio_porttype_show(struct uio_port *port, char *buf)
+{
+ const char *porttypes[] = {"none", "x86", "gpio", "other"};
+
+ if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER))
+ return -EINVAL;
+
+ return sprintf(buf, "port_%s\n", porttypes[port->porttype]);
+}
+
+struct portio_sysfs_entry {
+ struct attribute attr;
+ ssize_t (*show)(struct uio_port *, char *);
+ ssize_t (*store)(struct uio_port *, const char *, size_t);
+};
+
+static struct portio_sysfs_entry portio_start_attribute =
+ __ATTR(start, S_IRUGO, portio_start_show, NULL);
+static struct portio_sysfs_entry portio_size_attribute =
+ __ATTR(size, S_IRUGO, portio_size_show, NULL);
+static struct portio_sysfs_entry portio_porttype_attribute =
+ __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL);
+
+static struct attribute *portio_attrs[] = {
+ &portio_start_attribute.attr,
+ &portio_size_attribute.attr,
+ &portio_porttype_attribute.attr,
+ NULL,
+};
+
+static void portio_release(struct kobject *kobj)
+{
+ struct uio_portio *portio = to_portio(kobj);
+ kfree(portio);
+}
+
+static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct uio_portio *portio = to_portio(kobj);
+ struct uio_port *port = portio->port;
+ struct portio_sysfs_entry *entry;
+
+ entry = container_of(attr, struct portio_sysfs_entry, attr);
+
+ if (!entry->show)
+ return -EIO;
+
+ return entry->show(port, buf);
+}
+
+static struct sysfs_ops portio_sysfs_ops = {
+ .show = portio_type_show,
+};
+
+static struct kobj_type portio_attr_type = {
+ .release = portio_release,
+ .sysfs_ops = &portio_sysfs_ops,
+ .default_attrs = portio_attrs,
+};
+
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -177,10 +255,13 @@
static int uio_dev_add_attributes(struct uio_device *idev)
{
int ret;
- int mi;
+ int mi, pi;
int map_found = 0;
+ int portio_found = 0;
struct uio_mem *mem;
struct uio_map *map;
+ struct uio_port *port;
+ struct uio_portio *portio;

ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
if (ret)
@@ -195,25 +276,58 @@
idev->map_dir = kobject_create_and_add("maps",
&idev->dev->kobj);
if (!idev->map_dir)
- goto err;
+ goto err_map;
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
- goto err;
+ goto err_map;
kobject_init(&map->kobj, &map_attr_type);
map->mem = mem;
mem->map = map;
ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
if (ret)
- goto err;
+ goto err_map;
ret = kobject_uevent(&map->kobj, KOBJ_ADD);
if (ret)
- goto err;
+ goto err_map;
+ }
+
+ for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) {
+ port = &idev->info->port[pi];
+ if (port->size == 0)
+ break;
+ if (!portio_found) {
+ portio_found = 1;
+ idev->portio_dir = kobject_create_and_add("portio",
+ &idev->dev->kobj);
+ if (!idev->portio_dir)
+ goto err_portio;
+ }
+ portio = kzalloc(sizeof(*portio), GFP_KERNEL);
+ if (!portio)
+ goto err_portio;
+ kobject_init(&portio->kobj, &portio_attr_type);
+ portio->port = port;
+ port->portio = portio;
+ ret = kobject_add(&portio->kobj, idev->portio_dir,
+ "port%d", pi);
+ if (ret)
+ goto err_portio;
+ ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
+ if (ret)
+ goto err_portio;
}

return 0;

-err:
+err_portio:
+ for (pi--; pi >= 0; pi--) {
+ port = &idev->info->port[pi];
+ portio = port->portio;
+ kobject_put(&portio->kobj);
+ }
+ kobject_put(idev->portio_dir);
+err_map:
for (mi--; mi>=0; mi--) {
mem = &idev->info->mem[mi];
map = mem->map;
@@ -228,15 +342,26 @@

static void uio_dev_del_attributes(struct uio_device *idev)
{
- int mi;
+ int i;
struct uio_mem *mem;
- for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
- mem = &idev->info->mem[mi];
+ struct uio_port *port;
+
+ for (i = 0; i < MAX_UIO_MAPS; i++) {
+ mem = &idev->info->mem[i];
if (mem->size == 0)
break;
kobject_put(&mem->map->kobj);
}
kobject_put(idev->map_dir);
+
+ for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) {
+ port = &idev->info->port[i];
+ if (port->size == 0)
+ break;
+ kobject_put(&port->portio->kobj);
+ }
+ kobject_put(idev->portio_dir);
+
sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
}

Index: linux-2.6.28-rc/include/linux/uio_driver.h
===================================================================
--- linux-2.6.28-rc.orig/include/linux/uio_driver.h 2008-11-23 12:20:00.000000000 +0100
+++ linux-2.6.28-rc/include/linux/uio_driver.h 2008-12-06 02:07:21.000000000 +0100
@@ -38,6 +38,24 @@

#define MAX_UIO_MAPS 5

+struct uio_portio;
+
+/**
+ * struct uio_port - description of a UIO port region
+ * @start: start of port region
+ * @size: size of port region
+ * @porttype: type of port (see UIO_PORT_* below)
+ * @portio: for use by the UIO core only.
+ */
+struct uio_port {
+ unsigned long start;
+ unsigned long size;
+ int porttype;
+ struct uio_portio *portio;
+};
+
+#define MAX_UIO_PORT_REGIONS 5
+
struct uio_device;

/**
@@ -46,6 +64,7 @@
* @name: device name
* @version: device driver version
* @mem: list of mappable memory regions, size==0 for end of list
+ * @port: list of port regions, size==0 for end of list
* @irq: interrupt number or UIO_IRQ_CUSTOM
* @irq_flags: flags for request_irq()
* @priv: optional private data
@@ -60,6 +79,7 @@
char *name;
char *version;
struct uio_mem mem[MAX_UIO_MAPS];
+ struct uio_port port[MAX_UIO_PORT_REGIONS];
long irq;
unsigned long irq_flags;
void *priv;
@@ -92,4 +112,10 @@
#define UIO_MEM_LOGICAL 2
#define UIO_MEM_VIRTUAL 3

+/* defines for uio_port->porttype */
+#define UIO_PORT_NONE 0
+#define UIO_PORT_X86 1
+#define UIO_PORT_GPIO 2
+#define UIO_PORT_OTHER 3
+
#endif /* _LINUX_UIO_DRIVER_H_ */

2008-12-06 01:25:41

by Hans J. Koch

[permalink] [raw]
Subject: [PATCH 2/2] UIO: Documentation for UIO ioport info handling

This patch updates UIO documentation with the changes introduced by
PATCH 1/2.

Signed-off-by: Hans J. Koch <[email protected]>

---
Documentation/DocBook/uio-howto.tmpl | 97 +++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)

Index: linux-2.6.28-rc/Documentation/DocBook/uio-howto.tmpl
===================================================================
--- linux-2.6.28-rc.orig/Documentation/DocBook/uio-howto.tmpl 2008-12-06 01:13:56.000000000 +0100
+++ linux-2.6.28-rc/Documentation/DocBook/uio-howto.tmpl 2008-12-06 01:54:14.000000000 +0100
@@ -42,6 +42,12 @@

<revhistory>
<revision>
+ <revnumber>0.6</revnumber>
+ <date>2008-12-05</date>
+ <authorinitials>hjk</authorinitials>
+ <revremark>Added description of portio sysfs attributes.</revremark>
+ </revision>
+ <revision>
<revnumber>0.5</revnumber>
<date>2008-05-22</date>
<authorinitials>hjk</authorinitials>
@@ -318,6 +324,54 @@
offset = N * getpagesize();
</programlisting>

+<para>
+ Sometimes there is hardware with memory-like regions that can not be
+ mapped with the technique described here, but there are still ways to
+ access them from userspace. The most common example are x86 ioports.
+ On x86 systems, userspace can access these ioports using
+ <function>ioperm()</function>, <function>iopl()</function>,
+ <function>inb()</function>, <function>outb()</function>, and similar
+ functions.
+</para>
+<para>
+ Since these ioport regions can not be mapped, they will not appear under
+ <filename>/sys/class/uio/uioX/maps/</filename> like the normal memory
+ described above. Without information about the port regions a hardware
+ has to offer, it becomes difficult for the userspace part of the
+ driver to find out which ports belong to which UIO device.
+</para>
+<para>
+ To address this situation, the new directory
+ <filename>/sys/class/uio/uioX/portio/</filename> was added. It only
+ exists if the driver wants to pass information about one or more port
+ regions to userspace. If that is the case, subdirectories named
+ <filename>port0</filename>, <filename>port1</filename>, and so on,
+ will appear underneath
+ <filename>/sys/class/uio/uioX/portio/</filename>.
+</para>
+<para>
+ Each <filename>portX/</filename> directory contains three read-only
+ files that show start, size, and type of the port region:
+</para>
+<itemizedlist>
+<listitem>
+ <para>
+ <filename>start</filename>: The first port of this region.
+ </para>
+</listitem>
+<listitem>
+ <para>
+ <filename>size</filename>: The number of ports in this region.
+ </para>
+</listitem>
+<listitem>
+ <para>
+ <filename>porttype</filename>: A string describing the type of port.
+ </para>
+</listitem>
+</itemizedlist>
+
+
</sect1>
</chapter>

@@ -356,6 +410,13 @@
</para></listitem>

<listitem><para>
+<varname>struct uio_port port[ MAX_UIO_PORTS_REGIONS ]</varname>: Required
+if you want to pass information about ioports to userspace. For each port
+region you need to fill one of the <varname>uio_port</varname> structures.
+See the description below for details.
+</para></listitem>
+
+<listitem><para>
<varname>long irq</varname>: Required. If your hardware generates an
interrupt, it's your modules task to determine the irq number during
initialization. If you don't have a hardware generated interrupt but
@@ -448,6 +509,42 @@
<varname>struct uio_mem</varname>! It is used by the UIO framework
to set up sysfs files for this mapping. Simply leave it alone.
</para>
+
+<para>
+Sometimes, your device can have one or more port regions which can not be
+mapped to userspace. But if there are other possibilities for userspace to
+access these ports, it makes sense to make information about the ports
+available in sysfs. For each region, you have to set up a
+<varname>struct uio_port</varname> in the <varname>port[]</varname> array.
+Here's a description of the fields of <varname>struct uio_port</varname>:
+</para>
+
+<itemizedlist>
+<listitem><para>
+<varname>char *porttype</varname>: Required. Set this to one of the predefined
+constants. Use <varname>UIO_PORT_X86</varname> for the ioports found in x86
+architectures.
+</para></listitem>
+
+<listitem><para>
+<varname>unsigned long start</varname>: Required if the port region is used.
+Fill in the number of the first port of this region.
+</para></listitem>
+
+<listitem><para>
+<varname>unsigned long size</varname>: Fill in the number of ports in this
+region. If <varname>size</varname> is zero, the region is considered unused.
+Note that you <emphasis>must</emphasis> initialize <varname>size</varname>
+with zero for all unused regions.
+</para></listitem>
+</itemizedlist>
+
+<para>
+Please do not touch the <varname>portio</varname> element of
+<varname>struct uio_port</varname>! It is used internally by the UIO
+framework to set up sysfs files for this region. Simply leave it alone.
+</para>
+
</sect1>

<sect1 id="adding_irq_handler">

2008-12-22 19:04:16

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/2] UIO: Documentation for UIO ioport info handling

On Sat, 6 Dec 2008 02:25:13 +0100 Hans J. Koch wrote:

> This patch updates UIO documentation with the changes introduced by
> PATCH 1/2.
>
> Signed-off-by: Hans J. Koch <[email protected]>
>
> ---
> Documentation/DocBook/uio-howto.tmpl | 97 +++++++++++++++++++++++++++++++++++
> 1 file changed, 97 insertions(+)
>
> Index: linux-2.6.28-rc/Documentation/DocBook/uio-howto.tmpl
> ===================================================================
> --- linux-2.6.28-rc.orig/Documentation/DocBook/uio-howto.tmpl 2008-12-06 01:13:56.000000000 +0100
> +++ linux-2.6.28-rc/Documentation/DocBook/uio-howto.tmpl 2008-12-06 01:54:14.000000000 +0100
> @@ -42,6 +42,12 @@
>
> <revhistory>
> <revision>
> + <revnumber>0.6</revnumber>
> + <date>2008-12-05</date>
> + <authorinitials>hjk</authorinitials>
> + <revremark>Added description of portio sysfs attributes.</revremark>
> + </revision>
> + <revision>
> <revnumber>0.5</revnumber>
> <date>2008-05-22</date>
> <authorinitials>hjk</authorinitials>
> @@ -318,6 +324,54 @@
> offset = N * getpagesize();
> </programlisting>
>
> +<para>
> + Sometimes there is hardware with memory-like regions that can not be

cannot

> + mapped with the technique described here, but there are still ways to
> + access them from userspace. The most common example are x86 ioports.
> + On x86 systems, userspace can access these ioports using
> + <function>ioperm()</function>, <function>iopl()</function>,
> + <function>inb()</function>, <function>outb()</function>, and similar
> + functions.
> +</para>
> +<para>
> + Since these ioport regions can not be mapped, they will not appear under

ditto

> + <filename>/sys/class/uio/uioX/maps/</filename> like the normal memory
> + described above. Without information about the port regions a hardware
> + has to offer, it becomes difficult for the userspace part of the
> + driver to find out which ports belong to which UIO device.
> +</para>
> +<para>
> + To address this situation, the new directory
> + <filename>/sys/class/uio/uioX/portio/</filename> was added. It only
> + exists if the driver wants to pass information about one or more port
> + regions to userspace. If that is the case, subdirectories named
> + <filename>port0</filename>, <filename>port1</filename>, and so on,
> + will appear underneath
> + <filename>/sys/class/uio/uioX/portio/</filename>.
> +</para>
> +<para>
> + Each <filename>portX/</filename> directory contains three read-only
> + files that show start, size, and type of the port region:
> +</para>
> +<itemizedlist>
> +<listitem>
> + <para>
> + <filename>start</filename>: The first port of this region.
> + </para>
> +</listitem>
> +<listitem>
> + <para>
> + <filename>size</filename>: The number of ports in this region.
> + </para>
> +</listitem>
> +<listitem>
> + <para>
> + <filename>porttype</filename>: A string describing the type of port.
> + </para>
> +</listitem>
> +</itemizedlist>
> +
> +
> </sect1>
> </chapter>
>
> @@ -356,6 +410,13 @@
> </para></listitem>
>
> <listitem><para>
> +<varname>struct uio_port port[ MAX_UIO_PORTS_REGIONS ]</varname>: Required

odd spacing: port[ MAX_UIO_PORTS_REGIONS ]

> +if you want to pass information about ioports to userspace. For each port
> +region you need to fill one of the <varname>uio_port</varname> structures.
> +See the description below for details.
> +</para></listitem>
> +
> +<listitem><para>
> <varname>long irq</varname>: Required. If your hardware generates an
> interrupt, it's your modules task to determine the irq number during
> initialization. If you don't have a hardware generated interrupt but
> @@ -448,6 +509,42 @@
> <varname>struct uio_mem</varname>! It is used by the UIO framework
> to set up sysfs files for this mapping. Simply leave it alone.
> </para>
> +
> +<para>
> +Sometimes, your device can have one or more port regions which can not be

cannot

> +mapped to userspace. But if there are other possibilities for userspace to
> +access these ports, it makes sense to make information about the ports
> +available in sysfs. For each region, you have to set up a
> +<varname>struct uio_port</varname> in the <varname>port[]</varname> array.
> +Here's a description of the fields of <varname>struct uio_port</varname>:
> +</para>
> +
> +<itemizedlist>
> +<listitem><para>
> +<varname>char *porttype</varname>: Required. Set this to one of the predefined
> +constants. Use <varname>UIO_PORT_X86</varname> for the ioports found in x86
> +architectures.
> +</para></listitem>
> +
> +<listitem><para>
> +<varname>unsigned long start</varname>: Required if the port region is used.
> +Fill in the number of the first port of this region.
> +</para></listitem>
> +
> +<listitem><para>
> +<varname>unsigned long size</varname>: Fill in the number of ports in this
> +region. If <varname>size</varname> is zero, the region is considered unused.
> +Note that you <emphasis>must</emphasis> initialize <varname>size</varname>
> +with zero for all unused regions.
> +</para></listitem>
> +</itemizedlist>
> +
> +<para>
> +Please do not touch the <varname>portio</varname> element of
> +<varname>struct uio_port</varname>! It is used internally by the UIO
> +framework to set up sysfs files for this region. Simply leave it alone.
> +</para>
> +
> </sect1>
>
> <sect1 id="adding_irq_handler">


---
~Randy