Blame view

drivers/uio/uio.c 19.5 KB
beafc54c4   Hans J. Koch   UIO: Add the User...
1
2
3
4
5
  /*
   * drivers/uio/uio.c
   *
   * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de>
   * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de>
318af55dd   Hans J. Koch   uio: Change mail ...
6
   * Copyright(C) 2006, Hans J. Koch <hjk@hansjkoch.de>
beafc54c4   Hans J. Koch   UIO: Add the User...
7
8
9
10
11
12
13
14
15
16
17
18
19
   * 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
  #include <linux/string.h>
  #include <linux/kobject.h>
91960a46c   Eric W. Biederman   uio: Support 2^MI...
26
  #include <linux/cdev.h>
beafc54c4   Hans J. Koch   UIO: Add the User...
27
  #include <linux/uio_driver.h>
91960a46c   Eric W. Biederman   uio: Support 2^MI...
28
  #define UIO_MAX_DEVICES		(1U << MINORBITS)
beafc54c4   Hans J. Koch   UIO: Add the User...
29
30
31
32
33
34
35
36
37
38
  
  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
  };
  
  static int uio_major;
91960a46c   Eric W. Biederman   uio: Support 2^MI...
44
  static struct cdev *uio_cdev;
beafc54c4   Hans J. Koch   UIO: Add the User...
45
  static DEFINE_IDR(uio_idr);
4f0146919   Jan Engelhardt   UIO: constify fun...
46
  static const struct file_operations uio_fops;
beafc54c4   Hans J. Koch   UIO: Add the User...
47

0d4a7bc12   Jonathan Corbet   UIO: BKL removal
48
49
  /* Protect idr accesses */
  static DEFINE_MUTEX(minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
50
51
52
  /*
   * attributes
   */
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
53
54
55
  struct uio_map {
  	struct kobject kobj;
  	struct uio_mem *mem;
beafc54c4   Hans J. Koch   UIO: Add the User...
56
  };
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
57
  #define to_map(map) container_of(map, struct uio_map, kobj)
beafc54c4   Hans J. Koch   UIO: Add the User...
58

820577911   Hans J. Koch   UIO: Add name att...
59
60
61
62
63
64
65
66
  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...
67
  static ssize_t map_addr_show(struct uio_mem *mem, char *buf)
beafc54c4   Hans J. Koch   UIO: Add the User...
68
  {
27a90700a   Kai Jiang   uio: Support phys...
69
70
  	return sprintf(buf, "0x%llx
  ", (unsigned long long)mem->addr);
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
71
  }
beafc54c4   Hans J. Koch   UIO: Add the User...
72

4f808bcdf   Brandon Philips   UIO: fix Greg's s...
73
74
75
76
  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...
77
  }
e2b39df11   Hans J. Koch   UIO: Add alignmen...
78
79
  static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
  {
27a90700a   Kai Jiang   uio: Support phys...
80
81
  	return sprintf(buf, "0x%llx
  ", (unsigned long long)mem->addr & ~PAGE_MASK);
e2b39df11   Hans J. Koch   UIO: Add alignmen...
82
  }
e70c412ee   Hans J. Koch   UIO: Pass informa...
83
  struct map_sysfs_entry {
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
84
85
86
87
  	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...
88
89
  static struct map_sysfs_entry name_attribute =
  	__ATTR(name, S_IRUGO, map_name_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
90
  static struct map_sysfs_entry addr_attribute =
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
91
  	__ATTR(addr, S_IRUGO, map_addr_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
92
  static struct map_sysfs_entry size_attribute =
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
93
  	__ATTR(size, S_IRUGO, map_size_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
94
  static struct map_sysfs_entry offset_attribute =
e2b39df11   Hans J. Koch   UIO: Add alignmen...
95
  	__ATTR(offset, S_IRUGO, map_offset_show, NULL);
beafc54c4   Hans J. Koch   UIO: Add the User...
96

81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
97
  static struct attribute *attrs[] = {
820577911   Hans J. Koch   UIO: Add name att...
98
  	&name_attribute.attr,
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
99
  	&addr_attribute.attr,
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
100
  	&size_attribute.attr,
e2b39df11   Hans J. Koch   UIO: Add alignmen...
101
  	&offset_attribute.attr,
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
102
  	NULL,	/* need to NULL terminate the list of attributes */
beafc54c4   Hans J. Koch   UIO: Add the User...
103
  };
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
104
105
106
107
108
  static void map_release(struct kobject *kobj)
  {
  	struct uio_map *map = to_map(kobj);
  	kfree(map);
  }
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
109
110
111
112
113
  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...
114
  	struct map_sysfs_entry *entry;
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
115

e70c412ee   Hans J. Koch   UIO: Pass informa...
116
  	entry = container_of(attr, struct map_sysfs_entry, attr);
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
117
118
119
120
121
122
  
  	if (!entry->show)
  		return -EIO;
  
  	return entry->show(mem, buf);
  }
52cf25d0a   Emese Revfy   Driver core: Cons...
123
  static const struct sysfs_ops map_sysfs_ops = {
4f808bcdf   Brandon Philips   UIO: fix Greg's s...
124
125
  	.show = map_type_show,
  };
beafc54c4   Hans J. Koch   UIO: Add the User...
126
  static struct kobj_type map_attr_type = {
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
127
  	.release	= map_release,
e70c412ee   Hans J. Koch   UIO: Pass informa...
128
  	.sysfs_ops	= &map_sysfs_ops,
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
129
  	.default_attrs	= attrs,
beafc54c4   Hans J. Koch   UIO: Add the User...
130
  };
e70c412ee   Hans J. Koch   UIO: Pass informa...
131
132
133
134
135
  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...
136
137
138
139
140
141
142
143
  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...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  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...
172
173
  static struct portio_sysfs_entry portio_name_attribute =
  	__ATTR(name, S_IRUGO, portio_name_show, NULL);
e70c412ee   Hans J. Koch   UIO: Pass informa...
174
175
176
177
178
179
180
181
  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...
182
  	&portio_name_attribute.attr,
e70c412ee   Hans J. Koch   UIO: Pass informa...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  	&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...
209
  static const struct sysfs_ops portio_sysfs_ops = {
e70c412ee   Hans J. Koch   UIO: Pass informa...
210
211
212
213
214
215
216
217
  	.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...
218
219
220
221
  static ssize_t show_name(struct device *dev,
  			 struct device_attribute *attr, char *buf)
  {
  	struct uio_device *idev = dev_get_drvdata(dev);
70a9156ba   Eric W. Biederman   uio: Don't clear ...
222
223
  	return sprintf(buf, "%s
  ", idev->info->name);
beafc54c4   Hans J. Koch   UIO: Add the User...
224
  }
beafc54c4   Hans J. Koch   UIO: Add the User...
225
226
227
228
229
  
  static ssize_t show_version(struct device *dev,
  			    struct device_attribute *attr, char *buf)
  {
  	struct uio_device *idev = dev_get_drvdata(dev);
70a9156ba   Eric W. Biederman   uio: Don't clear ...
230
231
  	return sprintf(buf, "%s
  ", idev->info->version);
beafc54c4   Hans J. Koch   UIO: Add the User...
232
  }
beafc54c4   Hans J. Koch   UIO: Add the User...
233
234
235
236
237
  
  static ssize_t show_event(struct device *dev,
  			  struct device_attribute *attr, char *buf)
  {
  	struct uio_device *idev = dev_get_drvdata(dev);
70a9156ba   Eric W. Biederman   uio: Don't clear ...
238
239
  	return sprintf(buf, "%u
  ", (unsigned int)atomic_read(&idev->event));
beafc54c4   Hans J. Koch   UIO: Add the User...
240
  }
beafc54c4   Hans J. Koch   UIO: Add the User...
241

c66fdab64   Eric W. Biederman   uio: Statically a...
242
243
244
245
246
  static struct device_attribute uio_class_attributes[] = {
  	__ATTR(name, S_IRUGO, show_name, NULL),
  	__ATTR(version, S_IRUGO, show_version, NULL),
  	__ATTR(event, S_IRUGO, show_event, NULL),
  	{}
beafc54c4   Hans J. Koch   UIO: Add the User...
247
  };
c66fdab64   Eric W. Biederman   uio: Statically a...
248
249
250
251
  /* UIO class infrastructure */
  static struct class uio_class = {
  	.name = "uio",
  	.dev_attrs = uio_class_attributes,
beafc54c4   Hans J. Koch   UIO: Add the User...
252
253
254
255
256
257
258
259
  };
  
  /*
   * device functions
   */
  static int uio_dev_add_attributes(struct uio_device *idev)
  {
  	int ret;
e70c412ee   Hans J. Koch   UIO: Pass informa...
260
  	int mi, pi;
beafc54c4   Hans J. Koch   UIO: Add the User...
261
  	int map_found = 0;
e70c412ee   Hans J. Koch   UIO: Pass informa...
262
  	int portio_found = 0;
beafc54c4   Hans J. Koch   UIO: Add the User...
263
  	struct uio_mem *mem;
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
264
  	struct uio_map *map;
e70c412ee   Hans J. Koch   UIO: Pass informa...
265
266
  	struct uio_port *port;
  	struct uio_portio *portio;
beafc54c4   Hans J. Koch   UIO: Add the User...
267

beafc54c4   Hans J. Koch   UIO: Add the User...
268
269
270
271
272
273
  	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 ...
274
275
276
  			idev->map_dir = kobject_create_and_add("maps",
  							&idev->dev->kobj);
  			if (!idev->map_dir)
e70c412ee   Hans J. Koch   UIO: Pass informa...
277
  				goto err_map;
beafc54c4   Hans J. Koch   UIO: Add the User...
278
  		}
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
279
280
  		map = kzalloc(sizeof(*map), GFP_KERNEL);
  		if (!map)
e70c412ee   Hans J. Koch   UIO: Pass informa...
281
  			goto err_map;
f9cb074bf   Greg Kroah-Hartman   Kobject: rename k...
282
  		kobject_init(&map->kobj, &map_attr_type);
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
283
284
  		map->mem = mem;
  		mem->map = map;
b2d6db587   Greg Kroah-Hartman   Kobject: rename k...
285
  		ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
286
  		if (ret)
e70c412ee   Hans J. Koch   UIO: Pass informa...
287
  			goto err_map;
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
288
  		ret = kobject_uevent(&map->kobj, KOBJ_ADD);
beafc54c4   Hans J. Koch   UIO: Add the User...
289
  		if (ret)
e70c412ee   Hans J. Koch   UIO: Pass informa...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  			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...
317
318
319
  	}
  
  	return 0;
e70c412ee   Hans J. Koch   UIO: Pass informa...
320
321
322
323
324
325
326
327
  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...
328
329
  	for (mi--; mi>=0; mi--) {
  		mem = &idev->info->mem[mi];
81e7c6a63   Greg Kroah-Hartman   UIO: fix kobject ...
330
  		map = mem->map;
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
331
  		kobject_put(&map->kobj);
beafc54c4   Hans J. Koch   UIO: Add the User...
332
  	}
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
333
  	kobject_put(idev->map_dir);
beafc54c4   Hans J. Koch   UIO: Add the User...
334
335
336
337
338
339
340
  	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...
341
  	int i;
beafc54c4   Hans J. Koch   UIO: Add the User...
342
  	struct uio_mem *mem;
e70c412ee   Hans J. Koch   UIO: Pass informa...
343
344
345
346
  	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...
347
348
  		if (mem->size == 0)
  			break;
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
349
  		kobject_put(&mem->map->kobj);
beafc54c4   Hans J. Koch   UIO: Add the User...
350
  	}
c10997f65   Greg Kroah-Hartman   Kobject: convert ...
351
  	kobject_put(idev->map_dir);
e70c412ee   Hans J. Koch   UIO: Pass informa...
352
353
354
355
356
357
358
359
  
  	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...
360
361
362
363
  }
  
  static int uio_get_minor(struct uio_device *idev)
  {
beafc54c4   Hans J. Koch   UIO: Add the User...
364
365
366
367
368
369
370
371
372
373
374
375
376
  	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;
  	}
c6edc42fe   Hillf Danton   uio: fix allocati...
377
378
379
380
381
382
383
384
  	if (id < UIO_MAX_DEVICES) {
  		idev->minor = id;
  	} else {
  		dev_err(idev->dev, "too many uio devices
  ");
  		retval = -EINVAL;
  		idr_remove(&uio_idr, id);
  	}
beafc54c4   Hans J. Koch   UIO: Add the User...
385
386
387
388
389
390
391
  exit:
  	mutex_unlock(&minor_lock);
  	return retval;
  }
  
  static void uio_free_minor(struct uio_device *idev)
  {
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
392
  	mutex_lock(&minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
393
  	idr_remove(&uio_idr, idev->minor);
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
394
  	mutex_unlock(&minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
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
  }
  
  /**
   * 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
437
  	mutex_lock(&minor_lock);
beafc54c4   Hans J. Koch   UIO: Add the User...
438
  	idev = idr_find(&uio_idr, iminor(inode));
0d4a7bc12   Jonathan Corbet   UIO: BKL removal
439
  	mutex_unlock(&minor_lock);
fbc8a81d6   Jonathan Corbet   UIO: cdev lock_ke...
440
441
442
443
  	if (!idev) {
  		ret = -ENODEV;
  		goto out;
  	}
beafc54c4   Hans J. Koch   UIO: Add the User...
444

fbc8a81d6   Jonathan Corbet   UIO: cdev lock_ke...
445
446
447
448
  	if (!try_module_get(idev->owner)) {
  		ret = -ENODEV;
  		goto out;
  	}
610ad5064   Uwe Kleine-König   UIO: hold a refer...
449

beafc54c4   Hans J. Koch   UIO: Add the User...
450
  	listener = kmalloc(sizeof(*listener), GFP_KERNEL);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
451
452
453
454
  	if (!listener) {
  		ret = -ENOMEM;
  		goto err_alloc_listener;
  	}
beafc54c4   Hans J. Koch   UIO: Add the User...
455
456
457
458
459
460
  
  	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...
461
  		ret = idev->info->open(idev->info, inode);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
462
463
  		if (ret)
  			goto err_infoopen;
beafc54c4   Hans J. Koch   UIO: Add the User...
464
  	}
610ad5064   Uwe Kleine-König   UIO: hold a refer...
465
466
467
  	return 0;
  
  err_infoopen:
610ad5064   Uwe Kleine-König   UIO: hold a refer...
468
  	kfree(listener);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
469

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

fbc8a81d6   Jonathan Corbet   UIO: cdev lock_ke...
473
  out:
beafc54c4   Hans J. Koch   UIO: Add the User...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  	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...
490
  	if (idev->info->release)
beafc54c4   Hans J. Koch   UIO: Add the User...
491
  		ret = idev->info->release(idev->info, inode);
610ad5064   Uwe Kleine-König   UIO: hold a refer...
492
493
  
  	module_put(idev->owner);
beafc54c4   Hans J. Koch   UIO: Add the User...
494
495
496
497
498
499
500
501
  	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;
6427a7655   Eric W. Biederman   uio: Cleanup irq ...
502
  	if (!idev->info->irq)
beafc54c4   Hans J. Koch   UIO: Add the User...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
  		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;
6427a7655   Eric W. Biederman   uio: Cleanup irq ...
519
  	if (!idev->info->irq)
beafc54c4   Hans J. Koch   UIO: Add the User...
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
  		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...
558
559
560
561
562
563
564
  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;
6427a7655   Eric W. Biederman   uio: Cleanup irq ...
565
  	if (!idev->info->irq)
328a14e70   Hans J. Koch   UIO: Add write fu...
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
  		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...
581
582
  static int uio_find_mem_index(struct vm_area_struct *vma)
  {
beafc54c4   Hans J. Koch   UIO: Add the User...
583
  	struct uio_device *idev = vma->vm_private_data;
f0c554fdd   Hillf Danton   uio: fix finding ...
584
585
  	if (vma->vm_pgoff < MAX_UIO_MAPS) {
  		if (idev->info->mem[vma->vm_pgoff].size == 0)
beafc54c4   Hans J. Koch   UIO: Add the User...
586
  			return -1;
f0c554fdd   Hillf Danton   uio: fix finding ...
587
  		return (int)vma->vm_pgoff;
beafc54c4   Hans J. Koch   UIO: Add the User...
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  	}
  	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
603
  static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
beafc54c4   Hans J. Koch   UIO: Add the User...
604
605
  {
  	struct uio_device *idev = vma->vm_private_data;
a18b630d1   Nick Piggin   uio: nopage
606
  	struct page *page;
02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
607
  	unsigned long offset;
beafc54c4   Hans J. Koch   UIO: Add the User...
608
609
610
  
  	int mi = uio_find_mem_index(vma);
  	if (mi < 0)
a18b630d1   Nick Piggin   uio: nopage
611
  		return VM_FAULT_SIGBUS;
beafc54c4   Hans J. Koch   UIO: Add the User...
612

02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
613
614
615
616
617
  	/*
  	 * 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...
618
  	if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
02683ffdf   Andrew G. Harvey   UIO: Fix mapping ...
619
  		page = virt_to_page(idev->info->mem[mi].addr + offset);
beafc54c4   Hans J. Koch   UIO: Add the User...
620
  	else
27a90700a   Kai Jiang   uio: Support phys...
621
  		page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset);
beafc54c4   Hans J. Koch   UIO: Add the User...
622
  	get_page(page);
a18b630d1   Nick Piggin   uio: nopage
623
624
  	vmf->page = page;
  	return 0;
beafc54c4   Hans J. Koch   UIO: Add the User...
625
  }
f0f37e2f7   Alexey Dobriyan   const: mark struc...
626
  static const struct vm_operations_struct uio_vm_ops = {
beafc54c4   Hans J. Koch   UIO: Add the User...
627
628
  	.open = uio_vma_open,
  	.close = uio_vma_close,
a18b630d1   Nick Piggin   uio: nopage
629
  	.fault = uio_vma_fault,
beafc54c4   Hans J. Koch   UIO: Add the User...
630
631
632
633
634
635
636
637
638
639
  };
  
  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...
640
641
  
  	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
beafc54c4   Hans J. Koch   UIO: Add the User...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  
  	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 ...
676
677
  	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...
678
679
680
681
  	if (requested_pages > actual_pages)
  		return -EINVAL;
  
  	if (idev->info->mmap) {
beafc54c4   Hans J. Koch   UIO: Add the User...
682
  		ret = idev->info->mmap(idev->info, vma);
beafc54c4   Hans J. Koch   UIO: Add the User...
683
684
685
686
687
688
689
690
691
692
693
694
695
  		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...
696
  static const struct file_operations uio_fops = {
beafc54c4   Hans J. Koch   UIO: Add the User...
697
698
699
700
  	.owner		= THIS_MODULE,
  	.open		= uio_open,
  	.release	= uio_release,
  	.read		= uio_read,
328a14e70   Hans J. Koch   UIO: Add write fu...
701
  	.write		= uio_write,
beafc54c4   Hans J. Koch   UIO: Add the User...
702
703
704
  	.mmap		= uio_mmap,
  	.poll		= uio_poll,
  	.fasync		= uio_fasync,
6038f373a   Arnd Bergmann   llseek: automatic...
705
  	.llseek		= noop_llseek,
beafc54c4   Hans J. Koch   UIO: Add the User...
706
707
708
709
  };
  
  static int uio_major_init(void)
  {
91960a46c   Eric W. Biederman   uio: Support 2^MI...
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
  	static const char name[] = "uio";
  	struct cdev *cdev = NULL;
  	dev_t uio_dev = 0;
  	int result;
  
  	result = alloc_chrdev_region(&uio_dev, 0, UIO_MAX_DEVICES, name);
  	if (result)
  		goto out;
  
  	result = -ENOMEM;
  	cdev = cdev_alloc();
  	if (!cdev)
  		goto out_unregister;
  
  	cdev->owner = THIS_MODULE;
  	cdev->ops = &uio_fops;
  	kobject_set_name(&cdev->kobj, "%s", name);
  
  	result = cdev_add(cdev, uio_dev, UIO_MAX_DEVICES);
  	if (result)
  		goto out_put;
  
  	uio_major = MAJOR(uio_dev);
  	uio_cdev = cdev;
d80df1cea   Wanlong Gao   drivers:uio:chang...
734
  	return 0;
91960a46c   Eric W. Biederman   uio: Support 2^MI...
735
736
737
738
  out_put:
  	kobject_put(&cdev->kobj);
  out_unregister:
  	unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES);
d80df1cea   Wanlong Gao   drivers:uio:chang...
739
740
  out:
  	return result;
beafc54c4   Hans J. Koch   UIO: Add the User...
741
742
743
744
  }
  
  static void uio_major_cleanup(void)
  {
91960a46c   Eric W. Biederman   uio: Support 2^MI...
745
746
  	unregister_chrdev_region(MKDEV(uio_major, 0), UIO_MAX_DEVICES);
  	cdev_del(uio_cdev);
beafc54c4   Hans J. Koch   UIO: Add the User...
747
748
749
750
  }
  
  static int init_uio_class(void)
  {
3d4f9d76b   Eric W. Biederman   uio: Fix lack of ...
751
  	int ret;
beafc54c4   Hans J. Koch   UIO: Add the User...
752
753
754
755
756
  
  	/* This is the first time in here, set everything up properly */
  	ret = uio_major_init();
  	if (ret)
  		goto exit;
c66fdab64   Eric W. Biederman   uio: Statically a...
757
758
759
760
761
  	ret = class_register(&uio_class);
  	if (ret) {
  		printk(KERN_ERR "class_register failed for uio
  ");
  		goto err_class_register;
beafc54c4   Hans J. Koch   UIO: Add the User...
762
763
  	}
  	return 0;
c66fdab64   Eric W. Biederman   uio: Statically a...
764
  err_class_register:
beafc54c4   Hans J. Koch   UIO: Add the User...
765
766
767
768
  	uio_major_cleanup();
  exit:
  	return ret;
  }
3d4f9d76b   Eric W. Biederman   uio: Fix lack of ...
769
  static void release_uio_class(void)
beafc54c4   Hans J. Koch   UIO: Add the User...
770
  {
c66fdab64   Eric W. Biederman   uio: Statically a...
771
  	class_unregister(&uio_class);
beafc54c4   Hans J. Koch   UIO: Add the User...
772
  	uio_major_cleanup();
beafc54c4   Hans J. Koch   UIO: Add the User...
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
  }
  
  /**
   * 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;
beafc54c4   Hans J. Koch   UIO: Add the User...
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  	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;
c66fdab64   Eric W. Biederman   uio: Statically a...
808
  	idev->dev = device_create(&uio_class, parent,
a9b12619f   Greg Kroah-Hartman   device create: mi...
809
810
  				  MKDEV(uio_major, idev->minor), idev,
  				  "uio%d", idev->minor);
beafc54c4   Hans J. Koch   UIO: Add the User...
811
812
813
814
815
816
  	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...
817
818
819
820
821
822
  
  	ret = uio_dev_add_attributes(idev);
  	if (ret)
  		goto err_uio_dev_add_attributes;
  
  	info->uio_dev = idev;
6427a7655   Eric W. Biederman   uio: Cleanup irq ...
823
824
825
  	if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
  		ret = request_irq(info->irq, uio_interrupt,
  				  info->irq_flags, info->name, idev);
beafc54c4   Hans J. Koch   UIO: Add the User...
826
827
828
829
830
831
832
833
834
  		if (ret)
  			goto err_request_irq;
  	}
  
  	return 0;
  
  err_request_irq:
  	uio_dev_del_attributes(idev);
  err_uio_dev_add_attributes:
c66fdab64   Eric W. Biederman   uio: Statically a...
835
  	device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
beafc54c4   Hans J. Koch   UIO: Add the User...
836
837
838
839
840
  err_device_create:
  	uio_free_minor(idev);
  err_get_minor:
  	kfree(idev);
  err_kzalloc:
beafc54c4   Hans J. Koch   UIO: Add the User...
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
  	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);
6427a7655   Eric W. Biederman   uio: Cleanup irq ...
860
  	if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
beafc54c4   Hans J. Koch   UIO: Add the User...
861
862
863
  		free_irq(info->irq, idev);
  
  	uio_dev_del_attributes(idev);
c66fdab64   Eric W. Biederman   uio: Statically a...
864
  	device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
beafc54c4   Hans J. Koch   UIO: Add the User...
865
  	kfree(idev);
beafc54c4   Hans J. Koch   UIO: Add the User...
866
867
868
869
870
871
872
  
  	return;
  }
  EXPORT_SYMBOL_GPL(uio_unregister_device);
  
  static int __init uio_init(void)
  {
3d4f9d76b   Eric W. Biederman   uio: Fix lack of ...
873
  	return init_uio_class();
beafc54c4   Hans J. Koch   UIO: Add the User...
874
875
876
877
  }
  
  static void __exit uio_exit(void)
  {
3d4f9d76b   Eric W. Biederman   uio: Fix lack of ...
878
  	release_uio_class();
beafc54c4   Hans J. Koch   UIO: Add the User...
879
880
881
882
883
  }
  
  module_init(uio_init)
  module_exit(uio_exit)
  MODULE_LICENSE("GPL v2");