aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/uio/uio.c
diff options
context:
space:
mode:
authorHans J. Koch <hjk@linutronix.de>2008-12-05 20:23:13 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-06 13:44:44 -0500
commite70c412ee45332db2636a8f5a35a0685efb0e4aa (patch)
tree98340f89baa09af04f67f4be5184363c54f2ef2f /drivers/uio/uio.c
parente543ae896626a54c0c05e3c434312d6d033d450c (diff)
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>
Diffstat (limited to 'drivers/uio/uio.c')
-rw-r--r--drivers/uio/uio.c159
1 files changed, 142 insertions, 17 deletions
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 2d2440cd57a9..4ca85a113aa2 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -35,6 +35,7 @@ struct uio_device {
35 int vma_count; 35 int vma_count;
36 struct uio_info *info; 36 struct uio_info *info;
37 struct kobject *map_dir; 37 struct kobject *map_dir;
38 struct kobject *portio_dir;
38}; 39};
39 40
40static int uio_major; 41static int uio_major;
@@ -75,17 +76,17 @@ static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
75 return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK); 76 return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
76} 77}
77 78
78struct uio_sysfs_entry { 79struct map_sysfs_entry {
79 struct attribute attr; 80 struct attribute attr;
80 ssize_t (*show)(struct uio_mem *, char *); 81 ssize_t (*show)(struct uio_mem *, char *);
81 ssize_t (*store)(struct uio_mem *, const char *, size_t); 82 ssize_t (*store)(struct uio_mem *, const char *, size_t);
82}; 83};
83 84
84static struct uio_sysfs_entry addr_attribute = 85static struct map_sysfs_entry addr_attribute =
85 __ATTR(addr, S_IRUGO, map_addr_show, NULL); 86 __ATTR(addr, S_IRUGO, map_addr_show, NULL);
86static struct uio_sysfs_entry size_attribute = 87static struct map_sysfs_entry size_attribute =
87 __ATTR(size, S_IRUGO, map_size_show, NULL); 88 __ATTR(size, S_IRUGO, map_size_show, NULL);
88static struct uio_sysfs_entry offset_attribute = 89static struct map_sysfs_entry offset_attribute =
89 __ATTR(offset, S_IRUGO, map_offset_show, NULL); 90 __ATTR(offset, S_IRUGO, map_offset_show, NULL);
90 91
91static struct attribute *attrs[] = { 92static struct attribute *attrs[] = {
@@ -106,9 +107,9 @@ static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr,
106{ 107{
107 struct uio_map *map = to_map(kobj); 108 struct uio_map *map = to_map(kobj);
108 struct uio_mem *mem = map->mem; 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 if (!entry->show) 114 if (!entry->show)
114 return -EIO; 115 return -EIO;
@@ -116,16 +117,93 @@ static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr,
116 return entry->show(mem, buf); 117 return entry->show(mem, buf);
117} 118}
118 119
119static struct sysfs_ops uio_sysfs_ops = { 120static struct sysfs_ops map_sysfs_ops = {
120 .show = map_type_show, 121 .show = map_type_show,
121}; 122};
122 123
123static struct kobj_type map_attr_type = { 124static struct kobj_type map_attr_type = {
124 .release = map_release, 125 .release = map_release,
125 .sysfs_ops = &uio_sysfs_ops, 126 .sysfs_ops = &map_sysfs_ops,
126 .default_attrs = attrs, 127 .default_attrs = attrs,
127}; 128};
128 129
130struct 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
136static ssize_t portio_start_show(struct uio_port *port, char *buf)
137{
138 return sprintf(buf, "0x%lx\n", port->start);
139}
140
141static ssize_t portio_size_show(struct uio_port *port, char *buf)
142{
143 return sprintf(buf, "0x%lx\n", port->size);
144}
145
146static 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
156struct 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
162static struct portio_sysfs_entry portio_start_attribute =
163 __ATTR(start, S_IRUGO, portio_start_show, NULL);
164static struct portio_sysfs_entry portio_size_attribute =
165 __ATTR(size, S_IRUGO, portio_size_show, NULL);
166static struct portio_sysfs_entry portio_porttype_attribute =
167 __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL);
168
169static struct attribute *portio_attrs[] = {
170 &portio_start_attribute.attr,
171 &portio_size_attribute.attr,
172 &portio_porttype_attribute.attr,
173 NULL,
174};
175
176static void portio_release(struct kobject *kobj)
177{
178 struct uio_portio *portio = to_portio(kobj);
179 kfree(portio);
180}
181
182static 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
197static struct sysfs_ops portio_sysfs_ops = {
198 .show = portio_type_show,
199};
200
201static struct kobj_type portio_attr_type = {
202 .release = portio_release,
203 .sysfs_ops = &portio_sysfs_ops,
204 .default_attrs = portio_attrs,
205};
206
129static ssize_t show_name(struct device *dev, 207static ssize_t show_name(struct device *dev,
130 struct device_attribute *attr, char *buf) 208 struct device_attribute *attr, char *buf)
131{ 209{
@@ -177,10 +255,13 @@ static struct attribute_group uio_attr_grp = {
177static int uio_dev_add_attributes(struct uio_device *idev) 255static int uio_dev_add_attributes(struct uio_device *idev)
178{ 256{
179 int ret; 257 int ret;
180 int mi; 258 int mi, pi;
181 int map_found = 0; 259 int map_found = 0;
260 int portio_found = 0;
182 struct uio_mem *mem; 261 struct uio_mem *mem;
183 struct uio_map *map; 262 struct uio_map *map;
263 struct uio_port *port;
264 struct uio_portio *portio;
184 265
185 ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp); 266 ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
186 if (ret) 267 if (ret)
@@ -195,25 +276,58 @@ static int uio_dev_add_attributes(struct uio_device *idev)
195 idev->map_dir = kobject_create_and_add("maps", 276 idev->map_dir = kobject_create_and_add("maps",
196 &idev->dev->kobj); 277 &idev->dev->kobj);
197 if (!idev->map_dir) 278 if (!idev->map_dir)
198 goto err; 279 goto err_map;
199 } 280 }
200 map = kzalloc(sizeof(*map), GFP_KERNEL); 281 map = kzalloc(sizeof(*map), GFP_KERNEL);
201 if (!map) 282 if (!map)
202 goto err; 283 goto err_map;
203 kobject_init(&map->kobj, &map_attr_type); 284 kobject_init(&map->kobj, &map_attr_type);
204 map->mem = mem; 285 map->mem = mem;
205 mem->map = map; 286 mem->map = map;
206 ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi); 287 ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
207 if (ret) 288 if (ret)
208 goto err; 289 goto err_map;
209 ret = kobject_uevent(&map->kobj, KOBJ_ADD); 290 ret = kobject_uevent(&map->kobj, KOBJ_ADD);
210 if (ret) 291 if (ret)
211 goto err; 292 goto err_map;
293 }
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;
212 } 319 }
213 320
214 return 0; 321 return 0;
215 322
216err: 323err_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);
330err_map:
217 for (mi--; mi>=0; mi--) { 331 for (mi--; mi>=0; mi--) {
218 mem = &idev->info->mem[mi]; 332 mem = &idev->info->mem[mi];
219 map = mem->map; 333 map = mem->map;
@@ -228,15 +342,26 @@ err_group:
228 342
229static void uio_dev_del_attributes(struct uio_device *idev) 343static void uio_dev_del_attributes(struct uio_device *idev)
230{ 344{
231 int mi; 345 int i;
232 struct uio_mem *mem; 346 struct uio_mem *mem;
233 for (mi = 0; mi < MAX_UIO_MAPS; mi++) { 347 struct uio_port *port;
234 mem = &idev->info->mem[mi]; 348
349 for (i = 0; i < MAX_UIO_MAPS; i++) {
350 mem = &idev->info->mem[i];
235 if (mem->size == 0) 351 if (mem->size == 0)
236 break; 352 break;
237 kobject_put(&mem->map->kobj); 353 kobject_put(&mem->map->kobj);
238 } 354 }
239 kobject_put(idev->map_dir); 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 sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp); 365 sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
241} 366}
242 367