Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Hans J. Koch and committed by
Greg Kroah-Hartman
e70c412e e543ae89

+168 -17
+142 -17
drivers/uio/uio.c
··· 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; ··· 76 75 return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK); 77 76 } 78 77 79 - struct uio_sysfs_entry { 78 + struct map_sysfs_entry { 80 79 struct attribute attr; 81 80 ssize_t (*show)(struct uio_mem *, char *); 82 81 ssize_t (*store)(struct uio_mem *, const char *, size_t); 83 82 }; 84 83 85 - static struct uio_sysfs_entry addr_attribute = 84 + static struct map_sysfs_entry addr_attribute = 86 85 __ATTR(addr, S_IRUGO, map_addr_show, NULL); 87 - static struct uio_sysfs_entry size_attribute = 86 + static struct map_sysfs_entry size_attribute = 88 87 __ATTR(size, S_IRUGO, map_size_show, NULL); 89 - static struct uio_sysfs_entry offset_attribute = 88 + static struct map_sysfs_entry offset_attribute = 90 89 __ATTR(offset, S_IRUGO, map_offset_show, NULL); 91 90 92 91 static struct attribute *attrs[] = { ··· 107 106 { 108 107 struct uio_map *map = to_map(kobj); 109 108 struct uio_mem *mem = map->mem; 110 - struct uio_sysfs_entry *entry; 109 + struct map_sysfs_entry *entry; 111 110 112 - entry = container_of(attr, struct uio_sysfs_entry, attr); 111 + entry = container_of(attr, struct map_sysfs_entry, attr); 113 112 114 113 if (!entry->show) 115 114 return -EIO; ··· 117 116 return entry->show(mem, buf); 118 117 } 119 118 120 - static struct sysfs_ops uio_sysfs_ops = { 119 + static struct sysfs_ops map_sysfs_ops = { 121 120 .show = map_type_show, 122 121 }; 123 122 124 123 static struct kobj_type map_attr_type = { 125 124 .release = map_release, 126 - .sysfs_ops = &uio_sysfs_ops, 125 + .sysfs_ops = &map_sysfs_ops, 127 126 .default_attrs = attrs, 127 + }; 128 + 129 + struct uio_portio { 130 + struct kobject kobj; 131 + struct uio_port *port; 132 + }; 133 + #define to_portio(portio) container_of(portio, struct uio_portio, kobj) 134 + 135 + static ssize_t portio_start_show(struct uio_port *port, char *buf) 136 + { 137 + return sprintf(buf, "0x%lx\n", port->start); 138 + } 139 + 140 + static ssize_t portio_size_show(struct uio_port *port, char *buf) 141 + { 142 + return sprintf(buf, "0x%lx\n", port->size); 143 + } 144 + 145 + static ssize_t portio_porttype_show(struct uio_port *port, char *buf) 146 + { 147 + const char *porttypes[] = {"none", "x86", "gpio", "other"}; 148 + 149 + if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER)) 150 + return -EINVAL; 151 + 152 + return sprintf(buf, "port_%s\n", porttypes[port->porttype]); 153 + } 154 + 155 + struct portio_sysfs_entry { 156 + struct attribute attr; 157 + ssize_t (*show)(struct uio_port *, char *); 158 + ssize_t (*store)(struct uio_port *, const char *, size_t); 159 + }; 160 + 161 + static struct portio_sysfs_entry portio_start_attribute = 162 + __ATTR(start, S_IRUGO, portio_start_show, NULL); 163 + static struct portio_sysfs_entry portio_size_attribute = 164 + __ATTR(size, S_IRUGO, portio_size_show, NULL); 165 + static struct portio_sysfs_entry portio_porttype_attribute = 166 + __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL); 167 + 168 + static struct attribute *portio_attrs[] = { 169 + &portio_start_attribute.attr, 170 + &portio_size_attribute.attr, 171 + &portio_porttype_attribute.attr, 172 + NULL, 173 + }; 174 + 175 + static void portio_release(struct kobject *kobj) 176 + { 177 + struct uio_portio *portio = to_portio(kobj); 178 + kfree(portio); 179 + } 180 + 181 + static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr, 182 + char *buf) 183 + { 184 + struct uio_portio *portio = to_portio(kobj); 185 + struct uio_port *port = portio->port; 186 + struct portio_sysfs_entry *entry; 187 + 188 + entry = container_of(attr, struct portio_sysfs_entry, attr); 189 + 190 + if (!entry->show) 191 + return -EIO; 192 + 193 + return entry->show(port, buf); 194 + } 195 + 196 + static struct sysfs_ops portio_sysfs_ops = { 197 + .show = portio_type_show, 198 + }; 199 + 200 + static struct kobj_type portio_attr_type = { 201 + .release = portio_release, 202 + .sysfs_ops = &portio_sysfs_ops, 203 + .default_attrs = portio_attrs, 128 204 }; 129 205 130 206 static ssize_t show_name(struct device *dev, ··· 255 177 static int uio_dev_add_attributes(struct uio_device *idev) 256 178 { 257 179 int ret; 258 - int mi; 180 + int mi, pi; 259 181 int map_found = 0; 182 + int portio_found = 0; 260 183 struct uio_mem *mem; 261 184 struct uio_map *map; 185 + struct uio_port *port; 186 + struct uio_portio *portio; 262 187 263 188 ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp); 264 189 if (ret) ··· 276 195 idev->map_dir = kobject_create_and_add("maps", 277 196 &idev->dev->kobj); 278 197 if (!idev->map_dir) 279 - goto err; 198 + goto err_map; 280 199 } 281 200 map = kzalloc(sizeof(*map), GFP_KERNEL); 282 201 if (!map) 283 - goto err; 202 + goto err_map; 284 203 kobject_init(&map->kobj, &map_attr_type); 285 204 map->mem = mem; 286 205 mem->map = map; 287 206 ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi); 288 207 if (ret) 289 - goto err; 208 + goto err_map; 290 209 ret = kobject_uevent(&map->kobj, KOBJ_ADD); 291 210 if (ret) 292 - goto err; 211 + goto err_map; 212 + } 213 + 214 + for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) { 215 + port = &idev->info->port[pi]; 216 + if (port->size == 0) 217 + break; 218 + if (!portio_found) { 219 + portio_found = 1; 220 + idev->portio_dir = kobject_create_and_add("portio", 221 + &idev->dev->kobj); 222 + if (!idev->portio_dir) 223 + goto err_portio; 224 + } 225 + portio = kzalloc(sizeof(*portio), GFP_KERNEL); 226 + if (!portio) 227 + goto err_portio; 228 + kobject_init(&portio->kobj, &portio_attr_type); 229 + portio->port = port; 230 + port->portio = portio; 231 + ret = kobject_add(&portio->kobj, idev->portio_dir, 232 + "port%d", pi); 233 + if (ret) 234 + goto err_portio; 235 + ret = kobject_uevent(&portio->kobj, KOBJ_ADD); 236 + if (ret) 237 + goto err_portio; 293 238 } 294 239 295 240 return 0; 296 241 297 - err: 242 + err_portio: 243 + for (pi--; pi >= 0; pi--) { 244 + port = &idev->info->port[pi]; 245 + portio = port->portio; 246 + kobject_put(&portio->kobj); 247 + } 248 + kobject_put(idev->portio_dir); 249 + err_map: 298 250 for (mi--; mi>=0; mi--) { 299 251 mem = &idev->info->mem[mi]; 300 252 map = mem->map; ··· 342 228 343 229 static void uio_dev_del_attributes(struct uio_device *idev) 344 230 { 345 - int mi; 231 + int i; 346 232 struct uio_mem *mem; 347 - for (mi = 0; mi < MAX_UIO_MAPS; mi++) { 348 - mem = &idev->info->mem[mi]; 233 + struct uio_port *port; 234 + 235 + for (i = 0; i < MAX_UIO_MAPS; i++) { 236 + mem = &idev->info->mem[i]; 349 237 if (mem->size == 0) 350 238 break; 351 239 kobject_put(&mem->map->kobj); 352 240 } 353 241 kobject_put(idev->map_dir); 242 + 243 + for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) { 244 + port = &idev->info->port[i]; 245 + if (port->size == 0) 246 + break; 247 + kobject_put(&port->portio->kobj); 248 + } 249 + kobject_put(idev->portio_dir); 250 + 354 251 sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp); 355 252 } 356 253
+26
include/linux/uio_driver.h
··· 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 /** ··· 64 46 * @name: device name 65 47 * @version: device driver version 66 48 * @mem: list of mappable memory regions, size==0 for end of list 49 + * @port: list of port regions, size==0 for end of list 67 50 * @irq: interrupt number or UIO_IRQ_CUSTOM 68 51 * @irq_flags: flags for request_irq() 69 52 * @priv: optional private data ··· 79 60 char *name; 80 61 char *version; 81 62 struct uio_mem mem[MAX_UIO_MAPS]; 63 + struct uio_port port[MAX_UIO_PORT_REGIONS]; 82 64 long irq; 83 65 unsigned long irq_flags; 84 66 void *priv; ··· 111 91 #define UIO_MEM_PHYS 1 112 92 #define UIO_MEM_LOGICAL 2 113 93 #define UIO_MEM_VIRTUAL 3 94 + 95 + /* defines for uio_port->porttype */ 96 + #define UIO_PORT_NONE 0 97 + #define UIO_PORT_X86 1 98 + #define UIO_PORT_GPIO 2 99 + #define UIO_PORT_OTHER 3 114 100 115 101 #endif /* _LINUX_UIO_DRIVER_H_ */