Commit e70c412ee45332db2636a8f5a35a0685efb0e4aa

Authored by Hans J. Koch
Committed by Greg Kroah-Hartman
1 parent e543ae8966

UIO: Pass information about ioports to userspace (V2)

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 <hjk@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 2 changed files with 168 additions and 17 deletions Side-by-side Diff

... ... @@ -35,6 +35,7 @@
35 35 int vma_count;
36 36 struct uio_info *info;
37 37 struct kobject *map_dir;
  38 + struct kobject *portio_dir;
38 39 };
39 40  
40 41 static int uio_major;
41 42  
42 43  
43 44  
... ... @@ -75,17 +76,17 @@
75 76 return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
76 77 }
77 78  
78   -struct uio_sysfs_entry {
  79 +struct map_sysfs_entry {
79 80 struct attribute attr;
80 81 ssize_t (*show)(struct uio_mem *, char *);
81 82 ssize_t (*store)(struct uio_mem *, const char *, size_t);
82 83 };
83 84  
84   -static struct uio_sysfs_entry addr_attribute =
  85 +static struct map_sysfs_entry addr_attribute =
85 86 __ATTR(addr, S_IRUGO, map_addr_show, NULL);
86   -static struct uio_sysfs_entry size_attribute =
  87 +static struct map_sysfs_entry size_attribute =
87 88 __ATTR(size, S_IRUGO, map_size_show, NULL);
88   -static struct uio_sysfs_entry offset_attribute =
  89 +static struct map_sysfs_entry offset_attribute =
89 90 __ATTR(offset, S_IRUGO, map_offset_show, NULL);
90 91  
91 92 static struct attribute *attrs[] = {
92 93  
... ... @@ -106,9 +107,9 @@
106 107 {
107 108 struct uio_map *map = to_map(kobj);
108 109 struct uio_mem *mem = map->mem;
109   - struct uio_sysfs_entry *entry;
  110 + struct map_sysfs_entry *entry;
110 111  
111   - entry = container_of(attr, struct uio_sysfs_entry, attr);
  112 + entry = container_of(attr, struct map_sysfs_entry, attr);
112 113  
113 114 if (!entry->show)
114 115 return -EIO;
115 116  
116 117  
... ... @@ -116,16 +117,93 @@
116 117 return entry->show(mem, buf);
117 118 }
118 119  
119   -static struct sysfs_ops uio_sysfs_ops = {
  120 +static struct sysfs_ops map_sysfs_ops = {
120 121 .show = map_type_show,
121 122 };
122 123  
123 124 static struct kobj_type map_attr_type = {
124 125 .release = map_release,
125   - .sysfs_ops = &uio_sysfs_ops,
  126 + .sysfs_ops = &map_sysfs_ops,
126 127 .default_attrs = attrs,
127 128 };
128 129  
  130 +struct uio_portio {
  131 + struct kobject kobj;
  132 + struct uio_port *port;
  133 +};
  134 +#define to_portio(portio) container_of(portio, struct uio_portio, kobj)
  135 +
  136 +static ssize_t portio_start_show(struct uio_port *port, char *buf)
  137 +{
  138 + return sprintf(buf, "0x%lx\n", port->start);
  139 +}
  140 +
  141 +static ssize_t portio_size_show(struct uio_port *port, char *buf)
  142 +{
  143 + return sprintf(buf, "0x%lx\n", port->size);
  144 +}
  145 +
  146 +static ssize_t portio_porttype_show(struct uio_port *port, char *buf)
  147 +{
  148 + const char *porttypes[] = {"none", "x86", "gpio", "other"};
  149 +
  150 + if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER))
  151 + return -EINVAL;
  152 +
  153 + return sprintf(buf, "port_%s\n", porttypes[port->porttype]);
  154 +}
  155 +
  156 +struct portio_sysfs_entry {
  157 + struct attribute attr;
  158 + ssize_t (*show)(struct uio_port *, char *);
  159 + ssize_t (*store)(struct uio_port *, const char *, size_t);
  160 +};
  161 +
  162 +static struct portio_sysfs_entry portio_start_attribute =
  163 + __ATTR(start, S_IRUGO, portio_start_show, NULL);
  164 +static struct portio_sysfs_entry portio_size_attribute =
  165 + __ATTR(size, S_IRUGO, portio_size_show, NULL);
  166 +static struct portio_sysfs_entry portio_porttype_attribute =
  167 + __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL);
  168 +
  169 +static struct attribute *portio_attrs[] = {
  170 + &portio_start_attribute.attr,
  171 + &portio_size_attribute.attr,
  172 + &portio_porttype_attribute.attr,
  173 + NULL,
  174 +};
  175 +
  176 +static void portio_release(struct kobject *kobj)
  177 +{
  178 + struct uio_portio *portio = to_portio(kobj);
  179 + kfree(portio);
  180 +}
  181 +
  182 +static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr,
  183 + char *buf)
  184 +{
  185 + struct uio_portio *portio = to_portio(kobj);
  186 + struct uio_port *port = portio->port;
  187 + struct portio_sysfs_entry *entry;
  188 +
  189 + entry = container_of(attr, struct portio_sysfs_entry, attr);
  190 +
  191 + if (!entry->show)
  192 + return -EIO;
  193 +
  194 + return entry->show(port, buf);
  195 +}
  196 +
  197 +static struct sysfs_ops portio_sysfs_ops = {
  198 + .show = portio_type_show,
  199 +};
  200 +
  201 +static struct kobj_type portio_attr_type = {
  202 + .release = portio_release,
  203 + .sysfs_ops = &portio_sysfs_ops,
  204 + .default_attrs = portio_attrs,
  205 +};
  206 +
129 207 static ssize_t show_name(struct device *dev,
130 208 struct device_attribute *attr, char *buf)
131 209 {
132 210  
133 211  
... ... @@ -177,10 +255,13 @@
177 255 static int uio_dev_add_attributes(struct uio_device *idev)
178 256 {
179 257 int ret;
180   - int mi;
  258 + int mi, pi;
181 259 int map_found = 0;
  260 + int portio_found = 0;
182 261 struct uio_mem *mem;
183 262 struct uio_map *map;
  263 + struct uio_port *port;
  264 + struct uio_portio *portio;
184 265  
185 266 ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
186 267 if (ret)
187 268  
188 269  
189 270  
190 271  
191 272  
... ... @@ -195,25 +276,58 @@
195 276 idev->map_dir = kobject_create_and_add("maps",
196 277 &idev->dev->kobj);
197 278 if (!idev->map_dir)
198   - goto err;
  279 + goto err_map;
199 280 }
200 281 map = kzalloc(sizeof(*map), GFP_KERNEL);
201 282 if (!map)
202   - goto err;
  283 + goto err_map;
203 284 kobject_init(&map->kobj, &map_attr_type);
204 285 map->mem = mem;
205 286 mem->map = map;
206 287 ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
207 288 if (ret)
208   - goto err;
  289 + goto err_map;
209 290 ret = kobject_uevent(&map->kobj, KOBJ_ADD);
210 291 if (ret)
211   - goto err;
  292 + goto err_map;
212 293 }
213 294  
  295 + for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) {
  296 + port = &idev->info->port[pi];
  297 + if (port->size == 0)
  298 + break;
  299 + if (!portio_found) {
  300 + portio_found = 1;
  301 + idev->portio_dir = kobject_create_and_add("portio",
  302 + &idev->dev->kobj);
  303 + if (!idev->portio_dir)
  304 + goto err_portio;
  305 + }
  306 + portio = kzalloc(sizeof(*portio), GFP_KERNEL);
  307 + if (!portio)
  308 + goto err_portio;
  309 + kobject_init(&portio->kobj, &portio_attr_type);
  310 + portio->port = port;
  311 + port->portio = portio;
  312 + ret = kobject_add(&portio->kobj, idev->portio_dir,
  313 + "port%d", pi);
  314 + if (ret)
  315 + goto err_portio;
  316 + ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
  317 + if (ret)
  318 + goto err_portio;
  319 + }
  320 +
214 321 return 0;
215 322  
216   -err:
  323 +err_portio:
  324 + for (pi--; pi >= 0; pi--) {
  325 + port = &idev->info->port[pi];
  326 + portio = port->portio;
  327 + kobject_put(&portio->kobj);
  328 + }
  329 + kobject_put(idev->portio_dir);
  330 +err_map:
217 331 for (mi--; mi>=0; mi--) {
218 332 mem = &idev->info->mem[mi];
219 333 map = mem->map;
220 334  
221 335  
... ... @@ -228,15 +342,26 @@
228 342  
229 343 static void uio_dev_del_attributes(struct uio_device *idev)
230 344 {
231   - int mi;
  345 + int i;
232 346 struct uio_mem *mem;
233   - for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
234   - mem = &idev->info->mem[mi];
  347 + struct uio_port *port;
  348 +
  349 + for (i = 0; i < MAX_UIO_MAPS; i++) {
  350 + mem = &idev->info->mem[i];
235 351 if (mem->size == 0)
236 352 break;
237 353 kobject_put(&mem->map->kobj);
238 354 }
239 355 kobject_put(idev->map_dir);
  356 +
  357 + for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) {
  358 + port = &idev->info->port[i];
  359 + if (port->size == 0)
  360 + break;
  361 + kobject_put(&port->portio->kobj);
  362 + }
  363 + kobject_put(idev->portio_dir);
  364 +
240 365 sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
241 366 }
242 367  
include/linux/uio_driver.h
... ... @@ -38,6 +38,24 @@
38 38  
39 39 #define MAX_UIO_MAPS 5
40 40  
  41 +struct uio_portio;
  42 +
  43 +/**
  44 + * struct uio_port - description of a UIO port region
  45 + * @start: start of port region
  46 + * @size: size of port region
  47 + * @porttype: type of port (see UIO_PORT_* below)
  48 + * @portio: for use by the UIO core only.
  49 + */
  50 +struct uio_port {
  51 + unsigned long start;
  52 + unsigned long size;
  53 + int porttype;
  54 + struct uio_portio *portio;
  55 +};
  56 +
  57 +#define MAX_UIO_PORT_REGIONS 5
  58 +
41 59 struct uio_device;
42 60  
43 61 /**
... ... @@ -46,6 +64,7 @@
46 64 * @name: device name
47 65 * @version: device driver version
48 66 * @mem: list of mappable memory regions, size==0 for end of list
  67 + * @port: list of port regions, size==0 for end of list
49 68 * @irq: interrupt number or UIO_IRQ_CUSTOM
50 69 * @irq_flags: flags for request_irq()
51 70 * @priv: optional private data
... ... @@ -60,6 +79,7 @@
60 79 char *name;
61 80 char *version;
62 81 struct uio_mem mem[MAX_UIO_MAPS];
  82 + struct uio_port port[MAX_UIO_PORT_REGIONS];
63 83 long irq;
64 84 unsigned long irq_flags;
65 85 void *priv;
... ... @@ -91,6 +111,12 @@
91 111 #define UIO_MEM_PHYS 1
92 112 #define UIO_MEM_LOGICAL 2
93 113 #define UIO_MEM_VIRTUAL 3
  114 +
  115 +/* defines for uio_port->porttype */
  116 +#define UIO_PORT_NONE 0
  117 +#define UIO_PORT_X86 1
  118 +#define UIO_PORT_GPIO 2
  119 +#define UIO_PORT_OTHER 3
94 120  
95 121 #endif /* _LINUX_UIO_DRIVER_H_ */