Blame view

drivers/uio/uio.c 19.8 KB
beafc54c4   Hans J. Koch   UIO: Add the User...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * drivers/uio/uio.c
   *
   * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de>
   * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de>
   * Copyright(C) 2006, Hans J. Koch <hjk@linutronix.de>
   * Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com>
   *
   * Userspace IO
   *
   * Base Functions
   *
   * Licensed under the GPLv2 only.
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/poll.h>
  #include <linux/device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
20
  #include <linux/slab.h>
beafc54c4   Hans J. Koch   UIO: Add the User...
21
22
  #include <linux/mm.h>
  #include <linux/idr.h>
d43c36dc6   Alexey Dobriyan   headers: remove s...
23
  #include <linux/sched.h>
beafc54c4   Hans J. Koch   UIO: Add the User...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  #include <linux/string.h>
  #include <linux/kobject.h>
  #include <linux/uio_driver.h>
  
  #define UIO_MAX_DEVICES 255
  
  struct uio_device {
  	struct module		*owner;
  	struct device		*dev;
  	int			minor;
  	atomic_t		event;
  	struct fasync_struct	*async_queue;
  	wait_queue_head_t	wait;
  	int			vma_count;
  	struct uio_info		*info;
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
39
  	struct kobject		*map_dir;
e70c412ee   Hans J. Koch   UIO: Pass informa...
40
  	struct kobject		*portio_dir;
beafc54c4   Hans J. Koch   UIO: Add the User...
41
42
43
44
  };
  
  static int uio_major;
  static DEFINE_IDR(uio_idr);
4f0146919   Jan Engelhardt   UIO: constify fun...
45
  static const struct file_operations uio_fops;
beafc54c4   Hans J. Koch   UIO: Add the User...
46
47
48
49
50
51
  
  /* UIO class infrastructure */
  static struct uio_class {
  	struct kref kref;
  	struct class *class;
  } *uio_class;
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
52
53
  /* Protect idr accesses */
  static DEFINE_MUTEX(minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
54
55
56
  /*
   * attributes
   */
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
57
58
59
  struct uio_map {
  	struct kobject kobj;
  	struct uio_mem *mem;
beafc54c4   Hans J. Koch   UIO: Add the User...
60
  };
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
61
  #define to_map(map) container_of(map, struct uio_map, kobj)
beafc54c4   Hans J. Koch   UIO: Add the User...
62

820577911   Hans J. Koch   UIO: Add name att...
63
64
65
66
67
68
69
70
  static ssize_t map_name_show(struct uio_mem *mem, char *buf)
  {
  	if (unlikely(!mem->name))
  		mem->name = "";
  
  	return sprintf(buf, "%s
  ", mem->name);
  }
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
71
  static ssize_t map_addr_show(struct uio_mem *mem, char *buf)
beafc54c4   Hans J. Koch   UIO: Add the User...
72
  {
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
73
74
75
  	return sprintf(buf, "0x%lx
  ", mem->addr);
  }
beafc54c4   Hans J. Koch   UIO: Add the User...
76

4f808bcdf   Brandon Philips   UIO: fix Greg's s...
77
78
79
80
  static ssize_t map_size_show(struct uio_mem *mem, char *buf)
  {
  	return sprintf(buf, "0x%lx
  ", mem->size);
beafc54c4   Hans J. Koch   UIO: Add the User...
81
  }
e2b39df11   Hans J. Koch   UIO: Add alignmen...
82
83
84
85
86
  static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
  {
  	return sprintf(buf, "0x%lx
  ", mem->addr & ~PAGE_MASK);
  }
e70c412ee   Hans J. Koch   UIO: Pass informa...
87
  struct map_sysfs_entry {
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
88
89
90
91
  	struct attribute attr;
  	ssize_t (*show)(struct uio_mem *, char *);
  	ssize_t (*store)(struct uio_mem *, const char *, size_t);
  };
820577911   Hans J. Koch   UIO: Add name att...
92
93
  static struct map_sysfs_entry name_attribute =
  	__ATTR(name, S_IRUGO, map_name_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
94
  static struct map_sysfs_entry addr_attribute =
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
95
  	__ATTR(addr, S_IRUGO, map_addr_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
96
  static struct map_sysfs_entry size_attribute =
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
97
  	__ATTR(size, S_IRUGO, map_size_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
98
  static struct map_sysfs_entry offset_attribute =
e2b39df11   Hans J. Koch   UIO: Add alignmen...
99
  	__ATTR(offset, S_IRUGO, map_offset_show, NULL);
beafc54c4   Hans J. Koch   UIO: Add the User...
100

81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
101
  static struct attribute *attrs[] = {
820577911   Hans J. Koch   UIO: Add name att...
102
  	&name_attribute.attr,
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
103
  	&addr_attribute.attr,
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
104
  	&size_attribute.attr,
e2b39df11   Hans J. Koch   UIO: Add alignmen...
105
  	&offset_attribute.attr,
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
106
  	NULL,	/* need to NULL terminate the list of attributes */
beafc54c4   Hans J. Koch   UIO: Add the User...
107
  };
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
108
109
110
111
112
  static void map_release(struct kobject *kobj)
  {
  	struct uio_map *map = to_map(kobj);
  	kfree(map);
  }
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
113
114
115
116
117
  static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr,
  			     char *buf)
  {
  	struct uio_map *map = to_map(kobj);
  	struct uio_mem *mem = map->mem;
e70c412ee   Hans J. Koch   UIO: Pass informa...
118
  	struct map_sysfs_entry *entry;
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
119

e70c412ee   Hans J. Koch   UIO: Pass informa...
120
  	entry = container_of(attr, struct map_sysfs_entry, attr);
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
121
122
123
124
125
126
  
  	if (!entry->show)
  		return -EIO;
  
  	return entry->show(mem, buf);
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
127
  static const struct sysfs_ops map_sysfs_ops = {
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
128
129
  	.show = map_type_show,
  };
beafc54c4   Hans J. Koch   UIO: Add the User...
130
  static struct kobj_type map_attr_type = {
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
131
  	.release	= map_release,
e70c412ee   Hans J. Koch   UIO: Pass informa...
132
  	.sysfs_ops	= &map_sysfs_ops,
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
133
  	.default_attrs	= attrs,
beafc54c4   Hans J. Koch   UIO: Add the User...
134
  };
e70c412ee   Hans J. Koch   UIO: Pass informa...
135
136
137
138
139
  struct uio_portio {
  	struct kobject kobj;
  	struct uio_port *port;
  };
  #define to_portio(portio) container_of(portio, struct uio_portio, kobj)
820577911   Hans J. Koch   UIO: Add name att...
140
141
142
143
144
145
146
147
  static ssize_t portio_name_show(struct uio_port *port, char *buf)
  {
  	if (unlikely(!port->name))
  		port->name = "";
  
  	return sprintf(buf, "%s
  ", port->name);
  }
e70c412ee   Hans J. Koch   UIO: Pass informa...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  static ssize_t portio_start_show(struct uio_port *port, char *buf)
  {
  	return sprintf(buf, "0x%lx
  ", port->start);
  }
  
  static ssize_t portio_size_show(struct uio_port *port, char *buf)
  {
  	return sprintf(buf, "0x%lx
  ", 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
  ", 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);
  };
820577911   Hans J. Koch   UIO: Add name att...
176
177
  static struct portio_sysfs_entry portio_name_attribute =
  	__ATTR(name, S_IRUGO, portio_name_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
178
179
180
181
182
183
184
185
  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[] = {
820577911   Hans J. Koch   UIO: Add name att...
186
  	&portio_name_attribute.attr,
e70c412ee   Hans J. Koch   UIO: Pass informa...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  	&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);
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
213
  static const struct sysfs_ops portio_sysfs_ops = {
e70c412ee   Hans J. Koch   UIO: Pass informa...
214
215
216
217
218
219
220
221
  	.show = portio_type_show,
  };
  
  static struct kobj_type portio_attr_type = {
  	.release	= portio_release,
  	.sysfs_ops	= &portio_sysfs_ops,
  	.default_attrs	= portio_attrs,
  };
beafc54c4   Hans J. Koch   UIO: Add the User...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  static ssize_t show_name(struct device *dev,
  			 struct device_attribute *attr, char *buf)
  {
  	struct uio_device *idev = dev_get_drvdata(dev);
  	if (idev)
  		return sprintf(buf, "%s
  ", idev->info->name);
  	else
  		return -ENODEV;
  }
  static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
  
  static ssize_t show_version(struct device *dev,
  			    struct device_attribute *attr, char *buf)
  {
  	struct uio_device *idev = dev_get_drvdata(dev);
  	if (idev)
  		return sprintf(buf, "%s
  ", idev->info->version);
  	else
  		return -ENODEV;
  }
  static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
  
  static ssize_t show_event(struct device *dev,
  			  struct device_attribute *attr, char *buf)
  {
  	struct uio_device *idev = dev_get_drvdata(dev);
  	if (idev)
  		return sprintf(buf, "%u
  ",
  				(unsigned int)atomic_read(&idev->event));
  	else
  		return -ENODEV;
  }
  static DEVICE_ATTR(event, S_IRUGO, show_event, NULL);
  
  static struct attribute *uio_attrs[] = {
  	&dev_attr_name.attr,
  	&dev_attr_version.attr,
  	&dev_attr_event.attr,
  	NULL,
  };
  
  static struct attribute_group uio_attr_grp = {
  	.attrs = uio_attrs,
  };
  
  /*
   * device functions
   */
  static int uio_dev_add_attributes(struct uio_device *idev)
  {
  	int ret;
e70c412ee   Hans J. Koch   UIO: Pass informa...
276
  	int mi, pi;
beafc54c4   Hans J. Koch   UIO: Add the User...
277
  	int map_found = 0;
e70c412ee   Hans J. Koch   UIO: Pass informa...
278
  	int portio_found = 0;
beafc54c4   Hans J. Koch   UIO: Add the User...
279
  	struct uio_mem *mem;
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
280
  	struct uio_map *map;
e70c412ee   Hans J. Koch   UIO: Pass informa...
281
282
  	struct uio_port *port;
  	struct uio_portio *portio;
beafc54c4   Hans J. Koch   UIO: Add the User...
283
284
285
286
287
288
289
290
291
292
293
  
  	ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
  	if (ret)
  		goto err_group;
  
  	for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
  		mem = &idev->info->mem[mi];
  		if (mem->size == 0)
  			break;
  		if (!map_found) {
  			map_found = 1;
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
294
295
296
  			idev->map_dir = kobject_create_and_add("maps",
  							&idev->dev->kobj);
  			if (!idev->map_dir)
e70c412ee   Hans J. Koch   UIO: Pass informa...
297
  				goto err_map;
beafc54c4   Hans J. Koch   UIO: Add the User...
298
  		}
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
299
300
  		map = kzalloc(sizeof(*map), GFP_KERNEL);
  		if (!map)
e70c412ee   Hans J. Koch   UIO: Pass informa...
301
  			goto err_map;
f9cb074bf   Greg Kroah-Hartman   Kobject: rename k...
302
  		kobject_init(&map->kobj, &map_attr_type);
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
303
304
  		map->mem = mem;
  		mem->map = map;
b2d6db587   Greg Kroah-Hartman   Kobject: rename k...
305
  		ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
306
  		if (ret)
e70c412ee   Hans J. Koch   UIO: Pass informa...
307
  			goto err_map;
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
308
  		ret = kobject_uevent(&map->kobj, KOBJ_ADD);
beafc54c4   Hans J. Koch   UIO: Add the User...
309
  		if (ret)
e70c412ee   Hans J. Koch   UIO: Pass informa...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  			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;
beafc54c4   Hans J. Koch   UIO: Add the User...
337
338
339
  	}
  
  	return 0;
e70c412ee   Hans J. Koch   UIO: Pass informa...
340
341
342
343
344
345
346
347
  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:
beafc54c4   Hans J. Koch   UIO: Add the User...
348
349
  	for (mi--; mi>=0; mi--) {
  		mem = &idev->info->mem[mi];
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
350
  		map = mem->map;
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
351
  		kobject_put(&map->kobj);
beafc54c4   Hans J. Koch   UIO: Add the User...
352
  	}
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
353
  	kobject_put(idev->map_dir);
beafc54c4   Hans J. Koch   UIO: Add the User...
354
355
356
357
358
359
360
361
362
  	sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
  err_group:
  	dev_err(idev->dev, "error creating sysfs files (%d)
  ", ret);
  	return ret;
  }
  
  static void uio_dev_del_attributes(struct uio_device *idev)
  {
e70c412ee   Hans J. Koch   UIO: Pass informa...
363
  	int i;
beafc54c4   Hans J. Koch   UIO: Add the User...
364
  	struct uio_mem *mem;
e70c412ee   Hans J. Koch   UIO: Pass informa...
365
366
367
368
  	struct uio_port *port;
  
  	for (i = 0; i < MAX_UIO_MAPS; i++) {
  		mem = &idev->info->mem[i];
beafc54c4   Hans J. Koch   UIO: Add the User...
369
370
  		if (mem->size == 0)
  			break;
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
371
  		kobject_put(&mem->map->kobj);
beafc54c4   Hans J. Koch   UIO: Add the User...
372
  	}
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
373
  	kobject_put(idev->map_dir);
e70c412ee   Hans J. Koch   UIO: Pass informa...
374
375
376
377
378
379
380
381
  
  	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);
beafc54c4   Hans J. Koch   UIO: Add the User...
382
383
384
385
386
  	sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
  }
  
  static int uio_get_minor(struct uio_device *idev)
  {
beafc54c4   Hans J. Koch   UIO: Add the User...
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  	int retval = -ENOMEM;
  	int id;
  
  	mutex_lock(&minor_lock);
  	if (idr_pre_get(&uio_idr, GFP_KERNEL) == 0)
  		goto exit;
  
  	retval = idr_get_new(&uio_idr, idev, &id);
  	if (retval < 0) {
  		if (retval == -EAGAIN)
  			retval = -ENOMEM;
  		goto exit;
  	}
  	idev->minor = id & MAX_ID_MASK;
  exit:
  	mutex_unlock(&minor_lock);
  	return retval;
  }
  
  static void uio_free_minor(struct uio_device *idev)
  {
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
408
  	mutex_lock(&minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
409
  	idr_remove(&uio_idr, idev->minor);
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
410
  	mutex_unlock(&minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  }
  
  /**
   * uio_event_notify - trigger an interrupt event
   * @info: UIO device capabilities
   */
  void uio_event_notify(struct uio_info *info)
  {
  	struct uio_device *idev = info->uio_dev;
  
  	atomic_inc(&idev->event);
  	wake_up_interruptible(&idev->wait);
  	kill_fasync(&idev->async_queue, SIGIO, POLL_IN);
  }
  EXPORT_SYMBOL_GPL(uio_event_notify);
  
  /**
   * uio_interrupt - hardware interrupt handler
   * @irq: IRQ number, can be UIO_IRQ_CYCLIC for cyclic timer
   * @dev_id: Pointer to the devices uio_device structure
   */
  static irqreturn_t uio_interrupt(int irq, void *dev_id)
  {
  	struct uio_device *idev = (struct uio_device *)dev_id;
  	irqreturn_t ret = idev->info->handler(irq, idev->info);
  
  	if (ret == IRQ_HANDLED)
  		uio_event_notify(idev->info);
  
  	return ret;
  }
  
  struct uio_listener {
  	struct uio_device *dev;
  	s32 event_count;
  };
  
  static int uio_open(struct inode *inode, struct file *filep)
  {
  	struct uio_device *idev;
  	struct uio_listener *listener;
  	int ret = 0;
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
453
  	mutex_lock(&minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
454
  	idev = idr_find(&uio_idr, iminor(inode));
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
455
  	mutex_unlock(&minor_lock);
fbc8a81d6   Jonathan Corbet   UIO: cdev lock_ke...
456
457
458
459
  	if (!idev) {
  		ret = -ENODEV;
  		goto out;
  	}
beafc54c4   Hans J. Koch   UIO: Add the User...
460

fbc8a81d6   Jonathan Corbet   UIO: cdev lock_ke...
461
462
463
464
  	if (!try_module_get(idev->owner)) {
  		ret = -ENODEV;
  		goto out;
  	}
610ad5064   Uwe Kleine-König   UIO: hold a refer...
465

beafc54c4   Hans J. Koch   UIO: Add the User...
466
  	listener = kmalloc(sizeof(*listener), GFP_KERNEL);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
467
468
469
470
  	if (!listener) {
  		ret = -ENOMEM;
  		goto err_alloc_listener;
  	}
beafc54c4   Hans J. Koch   UIO: Add the User...
471
472
473
474
475
476
  
  	listener->dev = idev;
  	listener->event_count = atomic_read(&idev->event);
  	filep->private_data = listener;
  
  	if (idev->info->open) {
beafc54c4   Hans J. Koch   UIO: Add the User...
477
  		ret = idev->info->open(idev->info, inode);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
478
479
  		if (ret)
  			goto err_infoopen;
beafc54c4   Hans J. Koch   UIO: Add the User...
480
  	}
610ad5064   Uwe Kleine-König   UIO: hold a refer...
481
482
483
  	return 0;
  
  err_infoopen:
610ad5064   Uwe Kleine-König   UIO: hold a refer...
484
  	kfree(listener);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
485

0d4a7bc12   Jonathan Corbet   UIO: BKL removal
486
  err_alloc_listener:
610ad5064   Uwe Kleine-König   UIO: hold a refer...
487
  	module_put(idev->owner);
beafc54c4   Hans J. Koch   UIO: Add the User...
488

fbc8a81d6   Jonathan Corbet   UIO: cdev lock_ke...
489
  out:
beafc54c4   Hans J. Koch   UIO: Add the User...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  	return ret;
  }
  
  static int uio_fasync(int fd, struct file *filep, int on)
  {
  	struct uio_listener *listener = filep->private_data;
  	struct uio_device *idev = listener->dev;
  
  	return fasync_helper(fd, filep, on, &idev->async_queue);
  }
  
  static int uio_release(struct inode *inode, struct file *filep)
  {
  	int ret = 0;
  	struct uio_listener *listener = filep->private_data;
  	struct uio_device *idev = listener->dev;
610ad5064   Uwe Kleine-König   UIO: hold a refer...
506
  	if (idev->info->release)
beafc54c4   Hans J. Koch   UIO: Add the User...
507
  		ret = idev->info->release(idev->info, inode);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
508
509
  
  	module_put(idev->owner);
beafc54c4   Hans J. Koch   UIO: Add the User...
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
  	kfree(listener);
  	return ret;
  }
  
  static unsigned int uio_poll(struct file *filep, poll_table *wait)
  {
  	struct uio_listener *listener = filep->private_data;
  	struct uio_device *idev = listener->dev;
  
  	if (idev->info->irq == UIO_IRQ_NONE)
  		return -EIO;
  
  	poll_wait(filep, &idev->wait, wait);
  	if (listener->event_count != atomic_read(&idev->event))
  		return POLLIN | POLLRDNORM;
  	return 0;
  }
  
  static ssize_t uio_read(struct file *filep, char __user *buf,
  			size_t count, loff_t *ppos)
  {
  	struct uio_listener *listener = filep->private_data;
  	struct uio_device *idev = listener->dev;
  	DECLARE_WAITQUEUE(wait, current);
  	ssize_t retval;
  	s32 event_count;
  
  	if (idev->info->irq == UIO_IRQ_NONE)
  		return -EIO;
  
  	if (count != sizeof(s32))
  		return -EINVAL;
  
  	add_wait_queue(&idev->wait, &wait);
  
  	do {
  		set_current_state(TASK_INTERRUPTIBLE);
  
  		event_count = atomic_read(&idev->event);
  		if (event_count != listener->event_count) {
  			if (copy_to_user(buf, &event_count, count))
  				retval = -EFAULT;
  			else {
  				listener->event_count = event_count;
  				retval = count;
  			}
  			break;
  		}
  
  		if (filep->f_flags & O_NONBLOCK) {
  			retval = -EAGAIN;
  			break;
  		}
  
  		if (signal_pending(current)) {
  			retval = -ERESTARTSYS;
  			break;
  		}
  		schedule();
  	} while (1);
  
  	__set_current_state(TASK_RUNNING);
  	remove_wait_queue(&idev->wait, &wait);
  
  	return retval;
  }
328a14e70   Hans J. Koch   UIO: Add write fu...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
  static ssize_t uio_write(struct file *filep, const char __user *buf,
  			size_t count, loff_t *ppos)
  {
  	struct uio_listener *listener = filep->private_data;
  	struct uio_device *idev = listener->dev;
  	ssize_t retval;
  	s32 irq_on;
  
  	if (idev->info->irq == UIO_IRQ_NONE)
  		return -EIO;
  
  	if (count != sizeof(s32))
  		return -EINVAL;
  
  	if (!idev->info->irqcontrol)
  		return -ENOSYS;
  
  	if (copy_from_user(&irq_on, buf, count))
  		return -EFAULT;
  
  	retval = idev->info->irqcontrol(idev->info, irq_on);
  
  	return retval ? retval : sizeof(s32);
  }
beafc54c4   Hans J. Koch   UIO: Add the User...
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
  static int uio_find_mem_index(struct vm_area_struct *vma)
  {
  	int mi;
  	struct uio_device *idev = vma->vm_private_data;
  
  	for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
  		if (idev->info->mem[mi].size == 0)
  			return -1;
  		if (vma->vm_pgoff == mi)
  			return mi;
  	}
  	return -1;
  }
  
  static void uio_vma_open(struct vm_area_struct *vma)
  {
  	struct uio_device *idev = vma->vm_private_data;
  	idev->vma_count++;
  }
  
  static void uio_vma_close(struct vm_area_struct *vma)
  {
  	struct uio_device *idev = vma->vm_private_data;
  	idev->vma_count--;
  }
a18b630d1   Nick Piggin   uio: nopage
625
  static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
beafc54c4   Hans J. Koch   UIO: Add the User...
626
627
  {
  	struct uio_device *idev = vma->vm_private_data;
a18b630d1   Nick Piggin   uio: nopage
628
  	struct page *page;
02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
629
  	unsigned long offset;
beafc54c4   Hans J. Koch   UIO: Add the User...
630
631
632
  
  	int mi = uio_find_mem_index(vma);
  	if (mi < 0)
a18b630d1   Nick Piggin   uio: nopage
633
  		return VM_FAULT_SIGBUS;
beafc54c4   Hans J. Koch   UIO: Add the User...
634

02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
635
636
637
638
639
  	/*
  	 * We need to subtract mi because userspace uses offset = N*PAGE_SIZE
  	 * to use mem[N].
  	 */
  	offset = (vmf->pgoff - mi) << PAGE_SHIFT;
beafc54c4   Hans J. Koch   UIO: Add the User...
640
  	if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
641
  		page = virt_to_page(idev->info->mem[mi].addr + offset);
beafc54c4   Hans J. Koch   UIO: Add the User...
642
  	else
02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
643
644
  		page = vmalloc_to_page((void *)idev->info->mem[mi].addr
  							+ offset);
beafc54c4   Hans J. Koch   UIO: Add the User...
645
  	get_page(page);
a18b630d1   Nick Piggin   uio: nopage
646
647
  	vmf->page = page;
  	return 0;
beafc54c4   Hans J. Koch   UIO: Add the User...
648
  }
f0f37e2f7   Alexey Dobriyan   const: mark struc...
649
  static const struct vm_operations_struct uio_vm_ops = {
beafc54c4   Hans J. Koch   UIO: Add the User...
650
651
  	.open = uio_vma_open,
  	.close = uio_vma_close,
a18b630d1   Nick Piggin   uio: nopage
652
  	.fault = uio_vma_fault,
beafc54c4   Hans J. Koch   UIO: Add the User...
653
654
655
656
657
658
659
660
661
662
  };
  
  static int uio_mmap_physical(struct vm_area_struct *vma)
  {
  	struct uio_device *idev = vma->vm_private_data;
  	int mi = uio_find_mem_index(vma);
  	if (mi < 0)
  		return -EINVAL;
  
  	vma->vm_flags |= VM_IO | VM_RESERVED;
c9698d6b1   Jean-Samuel Chenard   UIO: add pgprot_n...
663
664
  
  	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
beafc54c4   Hans J. Koch   UIO: Add the User...
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
  
  	return remap_pfn_range(vma,
  			       vma->vm_start,
  			       idev->info->mem[mi].addr >> PAGE_SHIFT,
  			       vma->vm_end - vma->vm_start,
  			       vma->vm_page_prot);
  }
  
  static int uio_mmap_logical(struct vm_area_struct *vma)
  {
  	vma->vm_flags |= VM_RESERVED;
  	vma->vm_ops = &uio_vm_ops;
  	uio_vma_open(vma);
  	return 0;
  }
  
  static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
  {
  	struct uio_listener *listener = filep->private_data;
  	struct uio_device *idev = listener->dev;
  	int mi;
  	unsigned long requested_pages, actual_pages;
  	int ret = 0;
  
  	if (vma->vm_end < vma->vm_start)
  		return -EINVAL;
  
  	vma->vm_private_data = idev;
  
  	mi = uio_find_mem_index(vma);
  	if (mi < 0)
  		return -EINVAL;
  
  	requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
6da2d377b   Ian Abbott   UIO: Take offset ...
699
700
  	actual_pages = ((idev->info->mem[mi].addr & ~PAGE_MASK)
  			+ idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT;
beafc54c4   Hans J. Koch   UIO: Add the User...
701
702
703
704
  	if (requested_pages > actual_pages)
  		return -EINVAL;
  
  	if (idev->info->mmap) {
beafc54c4   Hans J. Koch   UIO: Add the User...
705
  		ret = idev->info->mmap(idev->info, vma);
beafc54c4   Hans J. Koch   UIO: Add the User...
706
707
708
709
710
711
712
713
714
715
716
717
718
  		return ret;
  	}
  
  	switch (idev->info->mem[mi].memtype) {
  		case UIO_MEM_PHYS:
  			return uio_mmap_physical(vma);
  		case UIO_MEM_LOGICAL:
  		case UIO_MEM_VIRTUAL:
  			return uio_mmap_logical(vma);
  		default:
  			return -EINVAL;
  	}
  }
4f0146919   Jan Engelhardt   UIO: constify fun...
719
  static const struct file_operations uio_fops = {
beafc54c4   Hans J. Koch   UIO: Add the User...
720
721
722
723
  	.owner		= THIS_MODULE,
  	.open		= uio_open,
  	.release	= uio_release,
  	.read		= uio_read,
328a14e70   Hans J. Koch   UIO: Add write fu...
724
  	.write		= uio_write,
beafc54c4   Hans J. Koch   UIO: Add the User...
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
  	.mmap		= uio_mmap,
  	.poll		= uio_poll,
  	.fasync		= uio_fasync,
  };
  
  static int uio_major_init(void)
  {
  	uio_major = register_chrdev(0, "uio", &uio_fops);
  	if (uio_major < 0)
  		return uio_major;
  	return 0;
  }
  
  static void uio_major_cleanup(void)
  {
  	unregister_chrdev(uio_major, "uio");
  }
  
  static int init_uio_class(void)
  {
  	int ret = 0;
  
  	if (uio_class != NULL) {
  		kref_get(&uio_class->kref);
  		goto exit;
  	}
  
  	/* This is the first time in here, set everything up properly */
  	ret = uio_major_init();
  	if (ret)
  		goto exit;
  
  	uio_class = kzalloc(sizeof(*uio_class), GFP_KERNEL);
  	if (!uio_class) {
  		ret = -ENOMEM;
  		goto err_kzalloc;
  	}
  
  	kref_init(&uio_class->kref);
  	uio_class->class = class_create(THIS_MODULE, "uio");
  	if (IS_ERR(uio_class->class)) {
  		ret = IS_ERR(uio_class->class);
  		printk(KERN_ERR "class_create failed for uio
  ");
  		goto err_class_create;
  	}
  	return 0;
  
  err_class_create:
  	kfree(uio_class);
  	uio_class = NULL;
  err_kzalloc:
  	uio_major_cleanup();
  exit:
  	return ret;
  }
  
  static void release_uio_class(struct kref *kref)
  {
  	/* Ok, we cheat as we know we only have one uio_class */
  	class_destroy(uio_class->class);
  	kfree(uio_class);
  	uio_major_cleanup();
  	uio_class = NULL;
  }
  
  static void uio_class_destroy(void)
  {
  	if (uio_class)
  		kref_put(&uio_class->kref, release_uio_class);
  }
  
  /**
   * uio_register_device - register a new userspace IO device
   * @owner:	module that creates the new device
   * @parent:	parent device
   * @info:	UIO device capabilities
   *
   * returns zero on success or a negative error code.
   */
  int __uio_register_device(struct module *owner,
  			  struct device *parent,
  			  struct uio_info *info)
  {
  	struct uio_device *idev;
  	int ret = 0;
  
  	if (!parent || !info || !info->name || !info->version)
  		return -EINVAL;
  
  	info->uio_dev = NULL;
  
  	ret = init_uio_class();
  	if (ret)
  		return ret;
  
  	idev = kzalloc(sizeof(*idev), GFP_KERNEL);
  	if (!idev) {
  		ret = -ENOMEM;
  		goto err_kzalloc;
  	}
  
  	idev->owner = owner;
  	idev->info = info;
  	init_waitqueue_head(&idev->wait);
  	atomic_set(&idev->event, 0);
  
  	ret = uio_get_minor(idev);
  	if (ret)
  		goto err_get_minor;
a9b12619f   Greg Kroah-Hartman   device create: mi...
835
836
837
  	idev->dev = device_create(uio_class->class, parent,
  				  MKDEV(uio_major, idev->minor), idev,
  				  "uio%d", idev->minor);
beafc54c4   Hans J. Koch   UIO: Add the User...
838
839
840
841
842
843
  	if (IS_ERR(idev->dev)) {
  		printk(KERN_ERR "UIO: device register failed
  ");
  		ret = PTR_ERR(idev->dev);
  		goto err_device_create;
  	}
beafc54c4   Hans J. Koch   UIO: Add the User...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
  
  	ret = uio_dev_add_attributes(idev);
  	if (ret)
  		goto err_uio_dev_add_attributes;
  
  	info->uio_dev = idev;
  
  	if (idev->info->irq >= 0) {
  		ret = request_irq(idev->info->irq, uio_interrupt,
  				  idev->info->irq_flags, idev->info->name, idev);
  		if (ret)
  			goto err_request_irq;
  	}
  
  	return 0;
  
  err_request_irq:
  	uio_dev_del_attributes(idev);
  err_uio_dev_add_attributes:
  	device_destroy(uio_class->class, MKDEV(uio_major, idev->minor));
  err_device_create:
  	uio_free_minor(idev);
  err_get_minor:
  	kfree(idev);
  err_kzalloc:
  	uio_class_destroy();
  	return ret;
  }
  EXPORT_SYMBOL_GPL(__uio_register_device);
  
  /**
   * uio_unregister_device - unregister a industrial IO device
   * @info:	UIO device capabilities
   *
   */
  void uio_unregister_device(struct uio_info *info)
  {
  	struct uio_device *idev;
  
  	if (!info || !info->uio_dev)
  		return;
  
  	idev = info->uio_dev;
  
  	uio_free_minor(idev);
  
  	if (info->irq >= 0)
  		free_irq(info->irq, idev);
  
  	uio_dev_del_attributes(idev);
  
  	dev_set_drvdata(idev->dev, NULL);
  	device_destroy(uio_class->class, MKDEV(uio_major, idev->minor));
  	kfree(idev);
  	uio_class_destroy();
  
  	return;
  }
  EXPORT_SYMBOL_GPL(uio_unregister_device);
  
  static int __init uio_init(void)
  {
  	return 0;
  }
  
  static void __exit uio_exit(void)
  {
  }
  
  module_init(uio_init)
  module_exit(uio_exit)
  MODULE_LICENSE("GPL v2");