1beafc54cSHans J. Koch /* 2beafc54cSHans J. Koch * drivers/uio/uio.c 3beafc54cSHans J. Koch * 4beafc54cSHans J. Koch * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de> 5beafc54cSHans J. Koch * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de> 6318af55dSHans J. Koch * Copyright(C) 2006, Hans J. Koch <hjk@hansjkoch.de> 7beafc54cSHans J. Koch * Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com> 8beafc54cSHans J. Koch * 9beafc54cSHans J. Koch * Userspace IO 10beafc54cSHans J. Koch * 11beafc54cSHans J. Koch * Base Functions 12beafc54cSHans J. Koch * 13beafc54cSHans J. Koch * Licensed under the GPLv2 only. 14beafc54cSHans J. Koch */ 15beafc54cSHans J. Koch 16beafc54cSHans J. Koch #include <linux/module.h> 17beafc54cSHans J. Koch #include <linux/init.h> 18beafc54cSHans J. Koch #include <linux/poll.h> 19beafc54cSHans J. Koch #include <linux/device.h> 205a0e3ad6STejun Heo #include <linux/slab.h> 21beafc54cSHans J. Koch #include <linux/mm.h> 22beafc54cSHans J. Koch #include <linux/idr.h> 23d43c36dcSAlexey Dobriyan #include <linux/sched.h> 24beafc54cSHans J. Koch #include <linux/string.h> 25beafc54cSHans J. Koch #include <linux/kobject.h> 2691960a46SEric W. Biederman #include <linux/cdev.h> 27beafc54cSHans J. Koch #include <linux/uio_driver.h> 28beafc54cSHans J. Koch 2991960a46SEric W. Biederman #define UIO_MAX_DEVICES (1U << MINORBITS) 30beafc54cSHans J. Koch 31beafc54cSHans J. Koch struct uio_device { 32beafc54cSHans J. Koch struct module *owner; 33beafc54cSHans J. Koch struct device *dev; 34beafc54cSHans J. Koch int minor; 35beafc54cSHans J. Koch atomic_t event; 36beafc54cSHans J. Koch struct fasync_struct *async_queue; 37beafc54cSHans J. Koch wait_queue_head_t wait; 38beafc54cSHans J. Koch int vma_count; 39beafc54cSHans J. Koch struct uio_info *info; 4081e7c6a6SGreg Kroah-Hartman struct kobject *map_dir; 41e70c412eSHans J. Koch struct kobject *portio_dir; 42beafc54cSHans J. Koch }; 43beafc54cSHans J. Koch 44beafc54cSHans J. Koch static int uio_major; 4591960a46SEric W. Biederman static struct cdev *uio_cdev; 46beafc54cSHans J. Koch static DEFINE_IDR(uio_idr); 474f014691SJan Engelhardt static const struct file_operations uio_fops; 48beafc54cSHans J. Koch 490d4a7bc1SJonathan Corbet /* Protect idr accesses */ 500d4a7bc1SJonathan Corbet static DEFINE_MUTEX(minor_lock); 510d4a7bc1SJonathan Corbet 52beafc54cSHans J. Koch /* 53beafc54cSHans J. Koch * attributes 54beafc54cSHans J. Koch */ 55beafc54cSHans J. Koch 5681e7c6a6SGreg Kroah-Hartman struct uio_map { 5781e7c6a6SGreg Kroah-Hartman struct kobject kobj; 5881e7c6a6SGreg Kroah-Hartman struct uio_mem *mem; 59beafc54cSHans J. Koch }; 6081e7c6a6SGreg Kroah-Hartman #define to_map(map) container_of(map, struct uio_map, kobj) 61beafc54cSHans J. Koch 6282057791SHans J. Koch static ssize_t map_name_show(struct uio_mem *mem, char *buf) 6382057791SHans J. Koch { 6482057791SHans J. Koch if (unlikely(!mem->name)) 6582057791SHans J. Koch mem->name = ""; 6682057791SHans J. Koch 6782057791SHans J. Koch return sprintf(buf, "%s\n", mem->name); 6882057791SHans J. Koch } 6982057791SHans J. Koch 704f808bcdSBrandon Philips static ssize_t map_addr_show(struct uio_mem *mem, char *buf) 71beafc54cSHans J. Koch { 7227a90700SKai Jiang return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr); 73beafc54cSHans J. Koch } 74beafc54cSHans J. Koch 754f808bcdSBrandon Philips static ssize_t map_size_show(struct uio_mem *mem, char *buf) 764f808bcdSBrandon Philips { 774f808bcdSBrandon Philips return sprintf(buf, "0x%lx\n", mem->size); 784f808bcdSBrandon Philips } 794f808bcdSBrandon Philips 80e2b39df1SHans J. Koch static ssize_t map_offset_show(struct uio_mem *mem, char *buf) 81e2b39df1SHans J. Koch { 8227a90700SKai Jiang return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr & ~PAGE_MASK); 83e2b39df1SHans J. Koch } 84e2b39df1SHans J. Koch 85e70c412eSHans J. Koch struct map_sysfs_entry { 864f808bcdSBrandon Philips struct attribute attr; 874f808bcdSBrandon Philips ssize_t (*show)(struct uio_mem *, char *); 884f808bcdSBrandon Philips ssize_t (*store)(struct uio_mem *, const char *, size_t); 894f808bcdSBrandon Philips }; 904f808bcdSBrandon Philips 9182057791SHans J. Koch static struct map_sysfs_entry name_attribute = 9282057791SHans J. Koch __ATTR(name, S_IRUGO, map_name_show, NULL); 93e70c412eSHans J. Koch static struct map_sysfs_entry addr_attribute = 944f808bcdSBrandon Philips __ATTR(addr, S_IRUGO, map_addr_show, NULL); 95e70c412eSHans J. Koch static struct map_sysfs_entry size_attribute = 964f808bcdSBrandon Philips __ATTR(size, S_IRUGO, map_size_show, NULL); 97e70c412eSHans J. Koch static struct map_sysfs_entry offset_attribute = 98e2b39df1SHans J. Koch __ATTR(offset, S_IRUGO, map_offset_show, NULL); 99beafc54cSHans J. Koch 10081e7c6a6SGreg Kroah-Hartman static struct attribute *attrs[] = { 10182057791SHans J. Koch &name_attribute.attr, 1024f808bcdSBrandon Philips &addr_attribute.attr, 10381e7c6a6SGreg Kroah-Hartman &size_attribute.attr, 104e2b39df1SHans J. Koch &offset_attribute.attr, 10581e7c6a6SGreg Kroah-Hartman NULL, /* need to NULL terminate the list of attributes */ 106beafc54cSHans J. Koch }; 107beafc54cSHans J. Koch 10881e7c6a6SGreg Kroah-Hartman static void map_release(struct kobject *kobj) 10981e7c6a6SGreg Kroah-Hartman { 11081e7c6a6SGreg Kroah-Hartman struct uio_map *map = to_map(kobj); 11181e7c6a6SGreg Kroah-Hartman kfree(map); 11281e7c6a6SGreg Kroah-Hartman } 11381e7c6a6SGreg Kroah-Hartman 1144f808bcdSBrandon Philips static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr, 1154f808bcdSBrandon Philips char *buf) 1164f808bcdSBrandon Philips { 1174f808bcdSBrandon Philips struct uio_map *map = to_map(kobj); 1184f808bcdSBrandon Philips struct uio_mem *mem = map->mem; 119e70c412eSHans J. Koch struct map_sysfs_entry *entry; 1204f808bcdSBrandon Philips 121e70c412eSHans J. Koch entry = container_of(attr, struct map_sysfs_entry, attr); 1224f808bcdSBrandon Philips 1234f808bcdSBrandon Philips if (!entry->show) 1244f808bcdSBrandon Philips return -EIO; 1254f808bcdSBrandon Philips 1264f808bcdSBrandon Philips return entry->show(mem, buf); 1274f808bcdSBrandon Philips } 1284f808bcdSBrandon Philips 12952cf25d0SEmese Revfy static const struct sysfs_ops map_sysfs_ops = { 1304f808bcdSBrandon Philips .show = map_type_show, 1314f808bcdSBrandon Philips }; 1324f808bcdSBrandon Philips 133beafc54cSHans J. Koch static struct kobj_type map_attr_type = { 13481e7c6a6SGreg Kroah-Hartman .release = map_release, 135e70c412eSHans J. Koch .sysfs_ops = &map_sysfs_ops, 13681e7c6a6SGreg Kroah-Hartman .default_attrs = attrs, 137beafc54cSHans J. Koch }; 138beafc54cSHans J. Koch 139e70c412eSHans J. Koch struct uio_portio { 140e70c412eSHans J. Koch struct kobject kobj; 141e70c412eSHans J. Koch struct uio_port *port; 142e70c412eSHans J. Koch }; 143e70c412eSHans J. Koch #define to_portio(portio) container_of(portio, struct uio_portio, kobj) 144e70c412eSHans J. Koch 14582057791SHans J. Koch static ssize_t portio_name_show(struct uio_port *port, char *buf) 14682057791SHans J. Koch { 14782057791SHans J. Koch if (unlikely(!port->name)) 14882057791SHans J. Koch port->name = ""; 14982057791SHans J. Koch 15082057791SHans J. Koch return sprintf(buf, "%s\n", port->name); 15182057791SHans J. Koch } 15282057791SHans J. Koch 153e70c412eSHans J. Koch static ssize_t portio_start_show(struct uio_port *port, char *buf) 154e70c412eSHans J. Koch { 155e70c412eSHans J. Koch return sprintf(buf, "0x%lx\n", port->start); 156e70c412eSHans J. Koch } 157e70c412eSHans J. Koch 158e70c412eSHans J. Koch static ssize_t portio_size_show(struct uio_port *port, char *buf) 159e70c412eSHans J. Koch { 160e70c412eSHans J. Koch return sprintf(buf, "0x%lx\n", port->size); 161e70c412eSHans J. Koch } 162e70c412eSHans J. Koch 163e70c412eSHans J. Koch static ssize_t portio_porttype_show(struct uio_port *port, char *buf) 164e70c412eSHans J. Koch { 165e70c412eSHans J. Koch const char *porttypes[] = {"none", "x86", "gpio", "other"}; 166e70c412eSHans J. Koch 167e70c412eSHans J. Koch if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER)) 168e70c412eSHans J. Koch return -EINVAL; 169e70c412eSHans J. Koch 170e70c412eSHans J. Koch return sprintf(buf, "port_%s\n", porttypes[port->porttype]); 171e70c412eSHans J. Koch } 172e70c412eSHans J. Koch 173e70c412eSHans J. Koch struct portio_sysfs_entry { 174e70c412eSHans J. Koch struct attribute attr; 175e70c412eSHans J. Koch ssize_t (*show)(struct uio_port *, char *); 176e70c412eSHans J. Koch ssize_t (*store)(struct uio_port *, const char *, size_t); 177e70c412eSHans J. Koch }; 178e70c412eSHans J. Koch 17982057791SHans J. Koch static struct portio_sysfs_entry portio_name_attribute = 18082057791SHans J. Koch __ATTR(name, S_IRUGO, portio_name_show, NULL); 181e70c412eSHans J. Koch static struct portio_sysfs_entry portio_start_attribute = 182e70c412eSHans J. Koch __ATTR(start, S_IRUGO, portio_start_show, NULL); 183e70c412eSHans J. Koch static struct portio_sysfs_entry portio_size_attribute = 184e70c412eSHans J. Koch __ATTR(size, S_IRUGO, portio_size_show, NULL); 185e70c412eSHans J. Koch static struct portio_sysfs_entry portio_porttype_attribute = 186e70c412eSHans J. Koch __ATTR(porttype, S_IRUGO, portio_porttype_show, NULL); 187e70c412eSHans J. Koch 188e70c412eSHans J. Koch static struct attribute *portio_attrs[] = { 18982057791SHans J. Koch &portio_name_attribute.attr, 190e70c412eSHans J. Koch &portio_start_attribute.attr, 191e70c412eSHans J. Koch &portio_size_attribute.attr, 192e70c412eSHans J. Koch &portio_porttype_attribute.attr, 193e70c412eSHans J. Koch NULL, 194e70c412eSHans J. Koch }; 195e70c412eSHans J. Koch 196e70c412eSHans J. Koch static void portio_release(struct kobject *kobj) 197e70c412eSHans J. Koch { 198e70c412eSHans J. Koch struct uio_portio *portio = to_portio(kobj); 199e70c412eSHans J. Koch kfree(portio); 200e70c412eSHans J. Koch } 201e70c412eSHans J. Koch 202e70c412eSHans J. Koch static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr, 203e70c412eSHans J. Koch char *buf) 204e70c412eSHans J. Koch { 205e70c412eSHans J. Koch struct uio_portio *portio = to_portio(kobj); 206e70c412eSHans J. Koch struct uio_port *port = portio->port; 207e70c412eSHans J. Koch struct portio_sysfs_entry *entry; 208e70c412eSHans J. Koch 209e70c412eSHans J. Koch entry = container_of(attr, struct portio_sysfs_entry, attr); 210e70c412eSHans J. Koch 211e70c412eSHans J. Koch if (!entry->show) 212e70c412eSHans J. Koch return -EIO; 213e70c412eSHans J. Koch 214e70c412eSHans J. Koch return entry->show(port, buf); 215e70c412eSHans J. Koch } 216e70c412eSHans J. Koch 21752cf25d0SEmese Revfy static const struct sysfs_ops portio_sysfs_ops = { 218e70c412eSHans J. Koch .show = portio_type_show, 219e70c412eSHans J. Koch }; 220e70c412eSHans J. Koch 221e70c412eSHans J. Koch static struct kobj_type portio_attr_type = { 222e70c412eSHans J. Koch .release = portio_release, 223e70c412eSHans J. Koch .sysfs_ops = &portio_sysfs_ops, 224e70c412eSHans J. Koch .default_attrs = portio_attrs, 225e70c412eSHans J. Koch }; 226e70c412eSHans J. Koch 227beafc54cSHans J. Koch static ssize_t show_name(struct device *dev, 228beafc54cSHans J. Koch struct device_attribute *attr, char *buf) 229beafc54cSHans J. Koch { 230beafc54cSHans J. Koch struct uio_device *idev = dev_get_drvdata(dev); 231beafc54cSHans J. Koch return sprintf(buf, "%s\n", idev->info->name); 232beafc54cSHans J. Koch } 233beafc54cSHans J. Koch 234beafc54cSHans J. Koch static ssize_t show_version(struct device *dev, 235beafc54cSHans J. Koch struct device_attribute *attr, char *buf) 236beafc54cSHans J. Koch { 237beafc54cSHans J. Koch struct uio_device *idev = dev_get_drvdata(dev); 238beafc54cSHans J. Koch return sprintf(buf, "%s\n", idev->info->version); 239beafc54cSHans J. Koch } 240beafc54cSHans J. Koch 241beafc54cSHans J. Koch static ssize_t show_event(struct device *dev, 242beafc54cSHans J. Koch struct device_attribute *attr, char *buf) 243beafc54cSHans J. Koch { 244beafc54cSHans J. Koch struct uio_device *idev = dev_get_drvdata(dev); 24570a9156bSEric W. Biederman return sprintf(buf, "%u\n", (unsigned int)atomic_read(&idev->event)); 246beafc54cSHans J. Koch } 247beafc54cSHans J. Koch 248c66fdab6SEric W. Biederman static struct device_attribute uio_class_attributes[] = { 249c66fdab6SEric W. Biederman __ATTR(name, S_IRUGO, show_name, NULL), 250c66fdab6SEric W. Biederman __ATTR(version, S_IRUGO, show_version, NULL), 251c66fdab6SEric W. Biederman __ATTR(event, S_IRUGO, show_event, NULL), 252c66fdab6SEric W. Biederman {} 253beafc54cSHans J. Koch }; 254beafc54cSHans J. Koch 255c66fdab6SEric W. Biederman /* UIO class infrastructure */ 256c66fdab6SEric W. Biederman static struct class uio_class = { 257c66fdab6SEric W. Biederman .name = "uio", 258c66fdab6SEric W. Biederman .dev_attrs = uio_class_attributes, 259beafc54cSHans J. Koch }; 260beafc54cSHans J. Koch 261beafc54cSHans J. Koch /* 262beafc54cSHans J. Koch * device functions 263beafc54cSHans J. Koch */ 264beafc54cSHans J. Koch static int uio_dev_add_attributes(struct uio_device *idev) 265beafc54cSHans J. Koch { 266beafc54cSHans J. Koch int ret; 267e70c412eSHans J. Koch int mi, pi; 268beafc54cSHans J. Koch int map_found = 0; 269e70c412eSHans J. Koch int portio_found = 0; 270beafc54cSHans J. Koch struct uio_mem *mem; 27181e7c6a6SGreg Kroah-Hartman struct uio_map *map; 272e70c412eSHans J. Koch struct uio_port *port; 273e70c412eSHans J. Koch struct uio_portio *portio; 274beafc54cSHans J. Koch 275beafc54cSHans J. Koch for (mi = 0; mi < MAX_UIO_MAPS; mi++) { 276beafc54cSHans J. Koch mem = &idev->info->mem[mi]; 277beafc54cSHans J. Koch if (mem->size == 0) 278beafc54cSHans J. Koch break; 279beafc54cSHans J. Koch if (!map_found) { 280beafc54cSHans J. Koch map_found = 1; 28181e7c6a6SGreg Kroah-Hartman idev->map_dir = kobject_create_and_add("maps", 28281e7c6a6SGreg Kroah-Hartman &idev->dev->kobj); 28381e7c6a6SGreg Kroah-Hartman if (!idev->map_dir) 284e70c412eSHans J. Koch goto err_map; 285beafc54cSHans J. Koch } 28681e7c6a6SGreg Kroah-Hartman map = kzalloc(sizeof(*map), GFP_KERNEL); 28781e7c6a6SGreg Kroah-Hartman if (!map) 288e70c412eSHans J. Koch goto err_map; 289f9cb074bSGreg Kroah-Hartman kobject_init(&map->kobj, &map_attr_type); 29081e7c6a6SGreg Kroah-Hartman map->mem = mem; 29181e7c6a6SGreg Kroah-Hartman mem->map = map; 292b2d6db58SGreg Kroah-Hartman ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi); 293beafc54cSHans J. Koch if (ret) 294e70c412eSHans J. Koch goto err_map; 29581e7c6a6SGreg Kroah-Hartman ret = kobject_uevent(&map->kobj, KOBJ_ADD); 29681e7c6a6SGreg Kroah-Hartman if (ret) 297e70c412eSHans J. Koch goto err_map; 298e70c412eSHans J. Koch } 299e70c412eSHans J. Koch 300e70c412eSHans J. Koch for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) { 301e70c412eSHans J. Koch port = &idev->info->port[pi]; 302e70c412eSHans J. Koch if (port->size == 0) 303e70c412eSHans J. Koch break; 304e70c412eSHans J. Koch if (!portio_found) { 305e70c412eSHans J. Koch portio_found = 1; 306e70c412eSHans J. Koch idev->portio_dir = kobject_create_and_add("portio", 307e70c412eSHans J. Koch &idev->dev->kobj); 308e70c412eSHans J. Koch if (!idev->portio_dir) 309e70c412eSHans J. Koch goto err_portio; 310e70c412eSHans J. Koch } 311e70c412eSHans J. Koch portio = kzalloc(sizeof(*portio), GFP_KERNEL); 312e70c412eSHans J. Koch if (!portio) 313e70c412eSHans J. Koch goto err_portio; 314e70c412eSHans J. Koch kobject_init(&portio->kobj, &portio_attr_type); 315e70c412eSHans J. Koch portio->port = port; 316e70c412eSHans J. Koch port->portio = portio; 317e70c412eSHans J. Koch ret = kobject_add(&portio->kobj, idev->portio_dir, 318e70c412eSHans J. Koch "port%d", pi); 319e70c412eSHans J. Koch if (ret) 320e70c412eSHans J. Koch goto err_portio; 321e70c412eSHans J. Koch ret = kobject_uevent(&portio->kobj, KOBJ_ADD); 322e70c412eSHans J. Koch if (ret) 323e70c412eSHans J. Koch goto err_portio; 324beafc54cSHans J. Koch } 325beafc54cSHans J. Koch 326beafc54cSHans J. Koch return 0; 327beafc54cSHans J. Koch 328e70c412eSHans J. Koch err_portio: 329e70c412eSHans J. Koch for (pi--; pi >= 0; pi--) { 330e70c412eSHans J. Koch port = &idev->info->port[pi]; 331e70c412eSHans J. Koch portio = port->portio; 332e70c412eSHans J. Koch kobject_put(&portio->kobj); 333e70c412eSHans J. Koch } 334e70c412eSHans J. Koch kobject_put(idev->portio_dir); 335e70c412eSHans J. Koch err_map: 336beafc54cSHans J. Koch for (mi--; mi>=0; mi--) { 337beafc54cSHans J. Koch mem = &idev->info->mem[mi]; 33881e7c6a6SGreg Kroah-Hartman map = mem->map; 339c10997f6SGreg Kroah-Hartman kobject_put(&map->kobj); 340beafc54cSHans J. Koch } 341c10997f6SGreg Kroah-Hartman kobject_put(idev->map_dir); 342beafc54cSHans J. Koch dev_err(idev->dev, "error creating sysfs files (%d)\n", ret); 343beafc54cSHans J. Koch return ret; 344beafc54cSHans J. Koch } 345beafc54cSHans J. Koch 346beafc54cSHans J. Koch static void uio_dev_del_attributes(struct uio_device *idev) 347beafc54cSHans J. Koch { 348e70c412eSHans J. Koch int i; 349beafc54cSHans J. Koch struct uio_mem *mem; 350e70c412eSHans J. Koch struct uio_port *port; 351e70c412eSHans J. Koch 352e70c412eSHans J. Koch for (i = 0; i < MAX_UIO_MAPS; i++) { 353e70c412eSHans J. Koch mem = &idev->info->mem[i]; 354beafc54cSHans J. Koch if (mem->size == 0) 355beafc54cSHans J. Koch break; 356c10997f6SGreg Kroah-Hartman kobject_put(&mem->map->kobj); 357beafc54cSHans J. Koch } 358c10997f6SGreg Kroah-Hartman kobject_put(idev->map_dir); 359e70c412eSHans J. Koch 360e70c412eSHans J. Koch for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) { 361e70c412eSHans J. Koch port = &idev->info->port[i]; 362e70c412eSHans J. Koch if (port->size == 0) 363e70c412eSHans J. Koch break; 364e70c412eSHans J. Koch kobject_put(&port->portio->kobj); 365e70c412eSHans J. Koch } 366e70c412eSHans J. Koch kobject_put(idev->portio_dir); 367beafc54cSHans J. Koch } 368beafc54cSHans J. Koch 369beafc54cSHans J. Koch static int uio_get_minor(struct uio_device *idev) 370beafc54cSHans J. Koch { 371beafc54cSHans J. Koch int retval = -ENOMEM; 372beafc54cSHans J. Koch 373beafc54cSHans J. Koch mutex_lock(&minor_lock); 3746d770931STejun Heo retval = idr_alloc(&uio_idr, idev, 0, UIO_MAX_DEVICES, GFP_KERNEL); 3756d770931STejun Heo if (retval >= 0) { 3766d770931STejun Heo idev->minor = retval; 3775ed0505cSDamian Hobson-Garcia retval = 0; 3786d770931STejun Heo } else if (retval == -ENOSPC) { 379c6edc42fSHillf Danton dev_err(idev->dev, "too many uio devices\n"); 380c6edc42fSHillf Danton retval = -EINVAL; 381c6edc42fSHillf Danton } 382beafc54cSHans J. Koch mutex_unlock(&minor_lock); 383beafc54cSHans J. Koch return retval; 384beafc54cSHans J. Koch } 385beafc54cSHans J. Koch 386beafc54cSHans J. Koch static void uio_free_minor(struct uio_device *idev) 387beafc54cSHans J. Koch { 3880d4a7bc1SJonathan Corbet mutex_lock(&minor_lock); 389beafc54cSHans J. Koch idr_remove(&uio_idr, idev->minor); 3900d4a7bc1SJonathan Corbet mutex_unlock(&minor_lock); 391beafc54cSHans J. Koch } 392beafc54cSHans J. Koch 393beafc54cSHans J. Koch /** 394beafc54cSHans J. Koch * uio_event_notify - trigger an interrupt event 395beafc54cSHans J. Koch * @info: UIO device capabilities 396beafc54cSHans J. Koch */ 397beafc54cSHans J. Koch void uio_event_notify(struct uio_info *info) 398beafc54cSHans J. Koch { 399beafc54cSHans J. Koch struct uio_device *idev = info->uio_dev; 400beafc54cSHans J. Koch 401beafc54cSHans J. Koch atomic_inc(&idev->event); 402beafc54cSHans J. Koch wake_up_interruptible(&idev->wait); 403beafc54cSHans J. Koch kill_fasync(&idev->async_queue, SIGIO, POLL_IN); 404beafc54cSHans J. Koch } 405beafc54cSHans J. Koch EXPORT_SYMBOL_GPL(uio_event_notify); 406beafc54cSHans J. Koch 407beafc54cSHans J. Koch /** 408beafc54cSHans J. Koch * uio_interrupt - hardware interrupt handler 409beafc54cSHans J. Koch * @irq: IRQ number, can be UIO_IRQ_CYCLIC for cyclic timer 410beafc54cSHans J. Koch * @dev_id: Pointer to the devices uio_device structure 411beafc54cSHans J. Koch */ 412beafc54cSHans J. Koch static irqreturn_t uio_interrupt(int irq, void *dev_id) 413beafc54cSHans J. Koch { 414beafc54cSHans J. Koch struct uio_device *idev = (struct uio_device *)dev_id; 415beafc54cSHans J. Koch irqreturn_t ret = idev->info->handler(irq, idev->info); 416beafc54cSHans J. Koch 417beafc54cSHans J. Koch if (ret == IRQ_HANDLED) 418beafc54cSHans J. Koch uio_event_notify(idev->info); 419beafc54cSHans J. Koch 420beafc54cSHans J. Koch return ret; 421beafc54cSHans J. Koch } 422beafc54cSHans J. Koch 423beafc54cSHans J. Koch struct uio_listener { 424beafc54cSHans J. Koch struct uio_device *dev; 425beafc54cSHans J. Koch s32 event_count; 426beafc54cSHans J. Koch }; 427beafc54cSHans J. Koch 428beafc54cSHans J. Koch static int uio_open(struct inode *inode, struct file *filep) 429beafc54cSHans J. Koch { 430beafc54cSHans J. Koch struct uio_device *idev; 431beafc54cSHans J. Koch struct uio_listener *listener; 432beafc54cSHans J. Koch int ret = 0; 433beafc54cSHans J. Koch 4340d4a7bc1SJonathan Corbet mutex_lock(&minor_lock); 435beafc54cSHans J. Koch idev = idr_find(&uio_idr, iminor(inode)); 4360d4a7bc1SJonathan Corbet mutex_unlock(&minor_lock); 437fbc8a81dSJonathan Corbet if (!idev) { 438fbc8a81dSJonathan Corbet ret = -ENODEV; 439fbc8a81dSJonathan Corbet goto out; 440fbc8a81dSJonathan Corbet } 441beafc54cSHans J. Koch 442fbc8a81dSJonathan Corbet if (!try_module_get(idev->owner)) { 443fbc8a81dSJonathan Corbet ret = -ENODEV; 444fbc8a81dSJonathan Corbet goto out; 445fbc8a81dSJonathan Corbet } 446610ad506SUwe Kleine-König 447beafc54cSHans J. Koch listener = kmalloc(sizeof(*listener), GFP_KERNEL); 448610ad506SUwe Kleine-König if (!listener) { 449610ad506SUwe Kleine-König ret = -ENOMEM; 450610ad506SUwe Kleine-König goto err_alloc_listener; 451610ad506SUwe Kleine-König } 452beafc54cSHans J. Koch 453beafc54cSHans J. Koch listener->dev = idev; 454beafc54cSHans J. Koch listener->event_count = atomic_read(&idev->event); 455beafc54cSHans J. Koch filep->private_data = listener; 456beafc54cSHans J. Koch 457beafc54cSHans J. Koch if (idev->info->open) { 458beafc54cSHans J. Koch ret = idev->info->open(idev->info, inode); 459610ad506SUwe Kleine-König if (ret) 460610ad506SUwe Kleine-König goto err_infoopen; 461beafc54cSHans J. Koch } 462610ad506SUwe Kleine-König return 0; 463610ad506SUwe Kleine-König 464610ad506SUwe Kleine-König err_infoopen: 465beafc54cSHans J. Koch kfree(listener); 466610ad506SUwe Kleine-König 4670d4a7bc1SJonathan Corbet err_alloc_listener: 468610ad506SUwe Kleine-König module_put(idev->owner); 469beafc54cSHans J. Koch 470fbc8a81dSJonathan Corbet out: 471beafc54cSHans J. Koch return ret; 472beafc54cSHans J. Koch } 473beafc54cSHans J. Koch 474beafc54cSHans J. Koch static int uio_fasync(int fd, struct file *filep, int on) 475beafc54cSHans J. Koch { 476beafc54cSHans J. Koch struct uio_listener *listener = filep->private_data; 477beafc54cSHans J. Koch struct uio_device *idev = listener->dev; 478beafc54cSHans J. Koch 479beafc54cSHans J. Koch return fasync_helper(fd, filep, on, &idev->async_queue); 480beafc54cSHans J. Koch } 481beafc54cSHans J. Koch 482beafc54cSHans J. Koch static int uio_release(struct inode *inode, struct file *filep) 483beafc54cSHans J. Koch { 484beafc54cSHans J. Koch int ret = 0; 485beafc54cSHans J. Koch struct uio_listener *listener = filep->private_data; 486beafc54cSHans J. Koch struct uio_device *idev = listener->dev; 487beafc54cSHans J. Koch 488610ad506SUwe Kleine-König if (idev->info->release) 489beafc54cSHans J. Koch ret = idev->info->release(idev->info, inode); 490610ad506SUwe Kleine-König 491beafc54cSHans J. Koch module_put(idev->owner); 492beafc54cSHans J. Koch kfree(listener); 493beafc54cSHans J. Koch return ret; 494beafc54cSHans J. Koch } 495beafc54cSHans J. Koch 496beafc54cSHans J. Koch static unsigned int uio_poll(struct file *filep, poll_table *wait) 497beafc54cSHans J. Koch { 498beafc54cSHans J. Koch struct uio_listener *listener = filep->private_data; 499beafc54cSHans J. Koch struct uio_device *idev = listener->dev; 500beafc54cSHans J. Koch 5016427a765SEric W. Biederman if (!idev->info->irq) 502beafc54cSHans J. Koch return -EIO; 503beafc54cSHans J. Koch 504beafc54cSHans J. Koch poll_wait(filep, &idev->wait, wait); 505beafc54cSHans J. Koch if (listener->event_count != atomic_read(&idev->event)) 506beafc54cSHans J. Koch return POLLIN | POLLRDNORM; 507beafc54cSHans J. Koch return 0; 508beafc54cSHans J. Koch } 509beafc54cSHans J. Koch 510beafc54cSHans J. Koch static ssize_t uio_read(struct file *filep, char __user *buf, 511beafc54cSHans J. Koch size_t count, loff_t *ppos) 512beafc54cSHans J. Koch { 513beafc54cSHans J. Koch struct uio_listener *listener = filep->private_data; 514beafc54cSHans J. Koch struct uio_device *idev = listener->dev; 515beafc54cSHans J. Koch DECLARE_WAITQUEUE(wait, current); 516beafc54cSHans J. Koch ssize_t retval; 517beafc54cSHans J. Koch s32 event_count; 518beafc54cSHans J. Koch 5196427a765SEric W. Biederman if (!idev->info->irq) 520beafc54cSHans J. Koch return -EIO; 521beafc54cSHans J. Koch 522beafc54cSHans J. Koch if (count != sizeof(s32)) 523beafc54cSHans J. Koch return -EINVAL; 524beafc54cSHans J. Koch 525beafc54cSHans J. Koch add_wait_queue(&idev->wait, &wait); 526beafc54cSHans J. Koch 527beafc54cSHans J. Koch do { 528beafc54cSHans J. Koch set_current_state(TASK_INTERRUPTIBLE); 529beafc54cSHans J. Koch 530beafc54cSHans J. Koch event_count = atomic_read(&idev->event); 531beafc54cSHans J. Koch if (event_count != listener->event_count) { 532beafc54cSHans J. Koch if (copy_to_user(buf, &event_count, count)) 533beafc54cSHans J. Koch retval = -EFAULT; 534beafc54cSHans J. Koch else { 535beafc54cSHans J. Koch listener->event_count = event_count; 536beafc54cSHans J. Koch retval = count; 537beafc54cSHans J. Koch } 538beafc54cSHans J. Koch break; 539beafc54cSHans J. Koch } 540beafc54cSHans J. Koch 541beafc54cSHans J. Koch if (filep->f_flags & O_NONBLOCK) { 542beafc54cSHans J. Koch retval = -EAGAIN; 543beafc54cSHans J. Koch break; 544beafc54cSHans J. Koch } 545beafc54cSHans J. Koch 546beafc54cSHans J. Koch if (signal_pending(current)) { 547beafc54cSHans J. Koch retval = -ERESTARTSYS; 548beafc54cSHans J. Koch break; 549beafc54cSHans J. Koch } 550beafc54cSHans J. Koch schedule(); 551beafc54cSHans J. Koch } while (1); 552beafc54cSHans J. Koch 553beafc54cSHans J. Koch __set_current_state(TASK_RUNNING); 554beafc54cSHans J. Koch remove_wait_queue(&idev->wait, &wait); 555beafc54cSHans J. Koch 556beafc54cSHans J. Koch return retval; 557beafc54cSHans J. Koch } 558beafc54cSHans J. Koch 559328a14e7SHans J. Koch static ssize_t uio_write(struct file *filep, const char __user *buf, 560328a14e7SHans J. Koch size_t count, loff_t *ppos) 561328a14e7SHans J. Koch { 562328a14e7SHans J. Koch struct uio_listener *listener = filep->private_data; 563328a14e7SHans J. Koch struct uio_device *idev = listener->dev; 564328a14e7SHans J. Koch ssize_t retval; 565328a14e7SHans J. Koch s32 irq_on; 566328a14e7SHans J. Koch 5676427a765SEric W. Biederman if (!idev->info->irq) 568328a14e7SHans J. Koch return -EIO; 569328a14e7SHans J. Koch 570328a14e7SHans J. Koch if (count != sizeof(s32)) 571328a14e7SHans J. Koch return -EINVAL; 572328a14e7SHans J. Koch 573328a14e7SHans J. Koch if (!idev->info->irqcontrol) 574328a14e7SHans J. Koch return -ENOSYS; 575328a14e7SHans J. Koch 576328a14e7SHans J. Koch if (copy_from_user(&irq_on, buf, count)) 577328a14e7SHans J. Koch return -EFAULT; 578328a14e7SHans J. Koch 579328a14e7SHans J. Koch retval = idev->info->irqcontrol(idev->info, irq_on); 580328a14e7SHans J. Koch 581328a14e7SHans J. Koch return retval ? retval : sizeof(s32); 582328a14e7SHans J. Koch } 583328a14e7SHans J. Koch 584beafc54cSHans J. Koch static int uio_find_mem_index(struct vm_area_struct *vma) 585beafc54cSHans J. Koch { 586beafc54cSHans J. Koch struct uio_device *idev = vma->vm_private_data; 587beafc54cSHans J. Koch 588f0c554fdSHillf Danton if (vma->vm_pgoff < MAX_UIO_MAPS) { 589f0c554fdSHillf Danton if (idev->info->mem[vma->vm_pgoff].size == 0) 590beafc54cSHans J. Koch return -1; 591f0c554fdSHillf Danton return (int)vma->vm_pgoff; 592beafc54cSHans J. Koch } 593beafc54cSHans J. Koch return -1; 594beafc54cSHans J. Koch } 595beafc54cSHans J. Koch 596beafc54cSHans J. Koch static void uio_vma_open(struct vm_area_struct *vma) 597beafc54cSHans J. Koch { 598beafc54cSHans J. Koch struct uio_device *idev = vma->vm_private_data; 599beafc54cSHans J. Koch idev->vma_count++; 600beafc54cSHans J. Koch } 601beafc54cSHans J. Koch 602beafc54cSHans J. Koch static void uio_vma_close(struct vm_area_struct *vma) 603beafc54cSHans J. Koch { 604beafc54cSHans J. Koch struct uio_device *idev = vma->vm_private_data; 605beafc54cSHans J. Koch idev->vma_count--; 606beafc54cSHans J. Koch } 607beafc54cSHans J. Koch 608a18b630dSNick Piggin static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 609beafc54cSHans J. Koch { 610beafc54cSHans J. Koch struct uio_device *idev = vma->vm_private_data; 611a18b630dSNick Piggin struct page *page; 61202683ffdSAndrew G. Harvey unsigned long offset; 613beafc54cSHans J. Koch 614beafc54cSHans J. Koch int mi = uio_find_mem_index(vma); 615beafc54cSHans J. Koch if (mi < 0) 616a18b630dSNick Piggin return VM_FAULT_SIGBUS; 617beafc54cSHans J. Koch 61802683ffdSAndrew G. Harvey /* 61902683ffdSAndrew G. Harvey * We need to subtract mi because userspace uses offset = N*PAGE_SIZE 62002683ffdSAndrew G. Harvey * to use mem[N]. 62102683ffdSAndrew G. Harvey */ 62202683ffdSAndrew G. Harvey offset = (vmf->pgoff - mi) << PAGE_SHIFT; 62302683ffdSAndrew G. Harvey 624beafc54cSHans J. Koch if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) 62502683ffdSAndrew G. Harvey page = virt_to_page(idev->info->mem[mi].addr + offset); 626beafc54cSHans J. Koch else 62727a90700SKai Jiang page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset); 628beafc54cSHans J. Koch get_page(page); 629a18b630dSNick Piggin vmf->page = page; 630a18b630dSNick Piggin return 0; 631beafc54cSHans J. Koch } 632beafc54cSHans J. Koch 6337294151dSUwe Kleine-König static const struct vm_operations_struct uio_logical_vm_ops = { 634beafc54cSHans J. Koch .open = uio_vma_open, 635beafc54cSHans J. Koch .close = uio_vma_close, 636a18b630dSNick Piggin .fault = uio_vma_fault, 637beafc54cSHans J. Koch }; 638beafc54cSHans J. Koch 6397294151dSUwe Kleine-König static int uio_mmap_logical(struct vm_area_struct *vma) 6407294151dSUwe Kleine-König { 6417294151dSUwe Kleine-König vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 6427294151dSUwe Kleine-König vma->vm_ops = &uio_logical_vm_ops; 6437294151dSUwe Kleine-König uio_vma_open(vma); 6447294151dSUwe Kleine-König return 0; 6457294151dSUwe Kleine-König } 6467294151dSUwe Kleine-König 6477294151dSUwe Kleine-König static const struct vm_operations_struct uio_physical_vm_ops = { 6487294151dSUwe Kleine-König #ifdef CONFIG_HAVE_IOREMAP_PROT 6497294151dSUwe Kleine-König .access = generic_access_phys, 6507294151dSUwe Kleine-König #endif 6517294151dSUwe Kleine-König }; 6527294151dSUwe Kleine-König 653beafc54cSHans J. Koch static int uio_mmap_physical(struct vm_area_struct *vma) 654beafc54cSHans J. Koch { 655beafc54cSHans J. Koch struct uio_device *idev = vma->vm_private_data; 656beafc54cSHans J. Koch int mi = uio_find_mem_index(vma); 657beafc54cSHans J. Koch if (mi < 0) 658beafc54cSHans J. Koch return -EINVAL; 659beafc54cSHans J. Koch 6607294151dSUwe Kleine-König vma->vm_ops = &uio_physical_vm_ops; 6617294151dSUwe Kleine-König 662c9698d6bSJean-Samuel Chenard vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 663c9698d6bSJean-Samuel Chenard 664beafc54cSHans J. Koch return remap_pfn_range(vma, 665beafc54cSHans J. Koch vma->vm_start, 666beafc54cSHans J. Koch idev->info->mem[mi].addr >> PAGE_SHIFT, 667beafc54cSHans J. Koch vma->vm_end - vma->vm_start, 668beafc54cSHans J. Koch vma->vm_page_prot); 669beafc54cSHans J. Koch } 670beafc54cSHans J. Koch 671beafc54cSHans J. Koch static int uio_mmap(struct file *filep, struct vm_area_struct *vma) 672beafc54cSHans J. Koch { 673beafc54cSHans J. Koch struct uio_listener *listener = filep->private_data; 674beafc54cSHans J. Koch struct uio_device *idev = listener->dev; 675beafc54cSHans J. Koch int mi; 676beafc54cSHans J. Koch unsigned long requested_pages, actual_pages; 677beafc54cSHans J. Koch int ret = 0; 678beafc54cSHans J. Koch 679beafc54cSHans J. Koch if (vma->vm_end < vma->vm_start) 680beafc54cSHans J. Koch return -EINVAL; 681beafc54cSHans J. Koch 682beafc54cSHans J. Koch vma->vm_private_data = idev; 683beafc54cSHans J. Koch 684beafc54cSHans J. Koch mi = uio_find_mem_index(vma); 685beafc54cSHans J. Koch if (mi < 0) 686beafc54cSHans J. Koch return -EINVAL; 687beafc54cSHans J. Koch 68852c2dad9SLibin requested_pages = vma_pages(vma); 6896da2d377SIan Abbott actual_pages = ((idev->info->mem[mi].addr & ~PAGE_MASK) 6906da2d377SIan Abbott + idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT; 691beafc54cSHans J. Koch if (requested_pages > actual_pages) 692beafc54cSHans J. Koch return -EINVAL; 693beafc54cSHans J. Koch 694beafc54cSHans J. Koch if (idev->info->mmap) { 695beafc54cSHans J. Koch ret = idev->info->mmap(idev->info, vma); 696beafc54cSHans J. Koch return ret; 697beafc54cSHans J. Koch } 698beafc54cSHans J. Koch 699beafc54cSHans J. Koch switch (idev->info->mem[mi].memtype) { 700beafc54cSHans J. Koch case UIO_MEM_PHYS: 701beafc54cSHans J. Koch return uio_mmap_physical(vma); 702beafc54cSHans J. Koch case UIO_MEM_LOGICAL: 703beafc54cSHans J. Koch case UIO_MEM_VIRTUAL: 704beafc54cSHans J. Koch return uio_mmap_logical(vma); 705beafc54cSHans J. Koch default: 706beafc54cSHans J. Koch return -EINVAL; 707beafc54cSHans J. Koch } 708beafc54cSHans J. Koch } 709beafc54cSHans J. Koch 7104f014691SJan Engelhardt static const struct file_operations uio_fops = { 711beafc54cSHans J. Koch .owner = THIS_MODULE, 712beafc54cSHans J. Koch .open = uio_open, 713beafc54cSHans J. Koch .release = uio_release, 714beafc54cSHans J. Koch .read = uio_read, 715328a14e7SHans J. Koch .write = uio_write, 716beafc54cSHans J. Koch .mmap = uio_mmap, 717beafc54cSHans J. Koch .poll = uio_poll, 718beafc54cSHans J. Koch .fasync = uio_fasync, 7196038f373SArnd Bergmann .llseek = noop_llseek, 720beafc54cSHans J. Koch }; 721beafc54cSHans J. Koch 722beafc54cSHans J. Koch static int uio_major_init(void) 723beafc54cSHans J. Koch { 72491960a46SEric W. Biederman static const char name[] = "uio"; 72591960a46SEric W. Biederman struct cdev *cdev = NULL; 72691960a46SEric W. Biederman dev_t uio_dev = 0; 72791960a46SEric W. Biederman int result; 72891960a46SEric W. Biederman 72991960a46SEric W. Biederman result = alloc_chrdev_region(&uio_dev, 0, UIO_MAX_DEVICES, name); 73091960a46SEric W. Biederman if (result) 73191960a46SEric W. Biederman goto out; 73291960a46SEric W. Biederman 73391960a46SEric W. Biederman result = -ENOMEM; 73491960a46SEric W. Biederman cdev = cdev_alloc(); 73591960a46SEric W. Biederman if (!cdev) 73691960a46SEric W. Biederman goto out_unregister; 73791960a46SEric W. Biederman 73891960a46SEric W. Biederman cdev->owner = THIS_MODULE; 73991960a46SEric W. Biederman cdev->ops = &uio_fops; 74091960a46SEric W. Biederman kobject_set_name(&cdev->kobj, "%s", name); 74191960a46SEric W. Biederman 74291960a46SEric W. Biederman result = cdev_add(cdev, uio_dev, UIO_MAX_DEVICES); 74391960a46SEric W. Biederman if (result) 74491960a46SEric W. Biederman goto out_put; 74591960a46SEric W. Biederman 74691960a46SEric W. Biederman uio_major = MAJOR(uio_dev); 74791960a46SEric W. Biederman uio_cdev = cdev; 748d80df1ceSWanlong Gao return 0; 74991960a46SEric W. Biederman out_put: 75091960a46SEric W. Biederman kobject_put(&cdev->kobj); 75191960a46SEric W. Biederman out_unregister: 75291960a46SEric W. Biederman unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES); 753d80df1ceSWanlong Gao out: 754d80df1ceSWanlong Gao return result; 755beafc54cSHans J. Koch } 756beafc54cSHans J. Koch 757beafc54cSHans J. Koch static void uio_major_cleanup(void) 758beafc54cSHans J. Koch { 75991960a46SEric W. Biederman unregister_chrdev_region(MKDEV(uio_major, 0), UIO_MAX_DEVICES); 76091960a46SEric W. Biederman cdev_del(uio_cdev); 761beafc54cSHans J. Koch } 762beafc54cSHans J. Koch 763beafc54cSHans J. Koch static int init_uio_class(void) 764beafc54cSHans J. Koch { 7653d4f9d76SEric W. Biederman int ret; 766beafc54cSHans J. Koch 767beafc54cSHans J. Koch /* This is the first time in here, set everything up properly */ 768beafc54cSHans J. Koch ret = uio_major_init(); 769beafc54cSHans J. Koch if (ret) 770beafc54cSHans J. Koch goto exit; 771beafc54cSHans J. Koch 772c66fdab6SEric W. Biederman ret = class_register(&uio_class); 773c66fdab6SEric W. Biederman if (ret) { 774c66fdab6SEric W. Biederman printk(KERN_ERR "class_register failed for uio\n"); 775c66fdab6SEric W. Biederman goto err_class_register; 776beafc54cSHans J. Koch } 777beafc54cSHans J. Koch return 0; 778beafc54cSHans J. Koch 779c66fdab6SEric W. Biederman err_class_register: 780beafc54cSHans J. Koch uio_major_cleanup(); 781beafc54cSHans J. Koch exit: 782beafc54cSHans J. Koch return ret; 783beafc54cSHans J. Koch } 784beafc54cSHans J. Koch 7853d4f9d76SEric W. Biederman static void release_uio_class(void) 786beafc54cSHans J. Koch { 787c66fdab6SEric W. Biederman class_unregister(&uio_class); 788beafc54cSHans J. Koch uio_major_cleanup(); 789beafc54cSHans J. Koch } 790beafc54cSHans J. Koch 791beafc54cSHans J. Koch /** 792beafc54cSHans J. Koch * uio_register_device - register a new userspace IO device 793beafc54cSHans J. Koch * @owner: module that creates the new device 794beafc54cSHans J. Koch * @parent: parent device 795beafc54cSHans J. Koch * @info: UIO device capabilities 796beafc54cSHans J. Koch * 797beafc54cSHans J. Koch * returns zero on success or a negative error code. 798beafc54cSHans J. Koch */ 799beafc54cSHans J. Koch int __uio_register_device(struct module *owner, 800beafc54cSHans J. Koch struct device *parent, 801beafc54cSHans J. Koch struct uio_info *info) 802beafc54cSHans J. Koch { 803beafc54cSHans J. Koch struct uio_device *idev; 804beafc54cSHans J. Koch int ret = 0; 805beafc54cSHans J. Koch 806beafc54cSHans J. Koch if (!parent || !info || !info->name || !info->version) 807beafc54cSHans J. Koch return -EINVAL; 808beafc54cSHans J. Koch 809beafc54cSHans J. Koch info->uio_dev = NULL; 810beafc54cSHans J. Koch 811beafc54cSHans J. Koch idev = kzalloc(sizeof(*idev), GFP_KERNEL); 812beafc54cSHans J. Koch if (!idev) { 813beafc54cSHans J. Koch ret = -ENOMEM; 814beafc54cSHans J. Koch goto err_kzalloc; 815beafc54cSHans J. Koch } 816beafc54cSHans J. Koch 817beafc54cSHans J. Koch idev->owner = owner; 818beafc54cSHans J. Koch idev->info = info; 819beafc54cSHans J. Koch init_waitqueue_head(&idev->wait); 820beafc54cSHans J. Koch atomic_set(&idev->event, 0); 821beafc54cSHans J. Koch 822beafc54cSHans J. Koch ret = uio_get_minor(idev); 823beafc54cSHans J. Koch if (ret) 824beafc54cSHans J. Koch goto err_get_minor; 825beafc54cSHans J. Koch 826c66fdab6SEric W. Biederman idev->dev = device_create(&uio_class, parent, 82743691da4SGreg Kroah-Hartman MKDEV(uio_major, idev->minor), idev, 828beafc54cSHans J. Koch "uio%d", idev->minor); 829beafc54cSHans J. Koch if (IS_ERR(idev->dev)) { 830beafc54cSHans J. Koch printk(KERN_ERR "UIO: device register failed\n"); 831beafc54cSHans J. Koch ret = PTR_ERR(idev->dev); 832beafc54cSHans J. Koch goto err_device_create; 833beafc54cSHans J. Koch } 834beafc54cSHans J. Koch 835beafc54cSHans J. Koch ret = uio_dev_add_attributes(idev); 836beafc54cSHans J. Koch if (ret) 837beafc54cSHans J. Koch goto err_uio_dev_add_attributes; 838beafc54cSHans J. Koch 839beafc54cSHans J. Koch info->uio_dev = idev; 840beafc54cSHans J. Koch 8416427a765SEric W. Biederman if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) { 8426427a765SEric W. Biederman ret = request_irq(info->irq, uio_interrupt, 8436427a765SEric W. Biederman info->irq_flags, info->name, idev); 844beafc54cSHans J. Koch if (ret) 845beafc54cSHans J. Koch goto err_request_irq; 846beafc54cSHans J. Koch } 847beafc54cSHans J. Koch 848beafc54cSHans J. Koch return 0; 849beafc54cSHans J. Koch 850beafc54cSHans J. Koch err_request_irq: 851beafc54cSHans J. Koch uio_dev_del_attributes(idev); 852beafc54cSHans J. Koch err_uio_dev_add_attributes: 853c66fdab6SEric W. Biederman device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); 854beafc54cSHans J. Koch err_device_create: 855beafc54cSHans J. Koch uio_free_minor(idev); 856beafc54cSHans J. Koch err_get_minor: 857beafc54cSHans J. Koch kfree(idev); 858beafc54cSHans J. Koch err_kzalloc: 859beafc54cSHans J. Koch return ret; 860beafc54cSHans J. Koch } 861beafc54cSHans J. Koch EXPORT_SYMBOL_GPL(__uio_register_device); 862beafc54cSHans J. Koch 863beafc54cSHans J. Koch /** 864beafc54cSHans J. Koch * uio_unregister_device - unregister a industrial IO device 865beafc54cSHans J. Koch * @info: UIO device capabilities 866beafc54cSHans J. Koch * 867beafc54cSHans J. Koch */ 868beafc54cSHans J. Koch void uio_unregister_device(struct uio_info *info) 869beafc54cSHans J. Koch { 870beafc54cSHans J. Koch struct uio_device *idev; 871beafc54cSHans J. Koch 872beafc54cSHans J. Koch if (!info || !info->uio_dev) 873beafc54cSHans J. Koch return; 874beafc54cSHans J. Koch 875beafc54cSHans J. Koch idev = info->uio_dev; 876beafc54cSHans J. Koch 877beafc54cSHans J. Koch uio_free_minor(idev); 878beafc54cSHans J. Koch 8796427a765SEric W. Biederman if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) 880beafc54cSHans J. Koch free_irq(info->irq, idev); 881beafc54cSHans J. Koch 882beafc54cSHans J. Koch uio_dev_del_attributes(idev); 883beafc54cSHans J. Koch 884c66fdab6SEric W. Biederman device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); 885beafc54cSHans J. Koch kfree(idev); 886beafc54cSHans J. Koch 887beafc54cSHans J. Koch return; 888beafc54cSHans J. Koch } 889beafc54cSHans J. Koch EXPORT_SYMBOL_GPL(uio_unregister_device); 890beafc54cSHans J. Koch 891beafc54cSHans J. Koch static int __init uio_init(void) 892beafc54cSHans J. Koch { 8933d4f9d76SEric W. Biederman return init_uio_class(); 894beafc54cSHans J. Koch } 895beafc54cSHans J. Koch 896beafc54cSHans J. Koch static void __exit uio_exit(void) 897beafc54cSHans J. Koch { 8983d4f9d76SEric W. Biederman release_uio_class(); 899beafc54cSHans J. Koch } 900beafc54cSHans J. Koch 901beafc54cSHans J. Koch module_init(uio_init) 902beafc54cSHans J. Koch module_exit(uio_exit) 903beafc54cSHans J. Koch MODULE_LICENSE("GPL v2"); 904