Blame view

fs/char_dev.c 16.5 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
  /*
   *  linux/fs/char_dev.c
   *
   *  Copyright (C) 1991, 1992  Linus Torvalds
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
  #include <linux/init.h>
  #include <linux/fs.h>
b446b60e4   Andrew Morton   [PATCH] rework re...
9
  #include <linux/kdev_t.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
  #include <linux/slab.h>
  #include <linux/string.h>
  
  #include <linux/major.h>
  #include <linux/errno.h>
  #include <linux/module.h>
68eef3b47   Joe Korty   [PATCH] Simplify ...
16
  #include <linux/seq_file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
  
  #include <linux/kobject.h>
  #include <linux/kobj_map.h>
  #include <linux/cdev.h>
58383af62   Jes Sorensen   [PATCH] kobj_map ...
21
  #include <linux/mutex.h>
5da6185bc   David Howells   [PATCH] NOMMU: Se...
22
  #include <linux/backing-dev.h>
31d1d48e1   David Howells   Fix init ordering...
23
  #include <linux/tty.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24

07f3f05c1   David Howells   [PATCH] BLOCK: Mo...
25
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
  
  static struct kobj_map *cdev_map;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
28
  static DEFINE_MUTEX(chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

8a932f73e   Logan Gunthorpe   char_dev: order /...
30
  #define CHRDEV_MAJOR_HASH_SIZE 255
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
  static struct char_device_struct {
  	struct char_device_struct *next;
  	unsigned int major;
  	unsigned int baseminor;
  	int minorct;
7170be5f5   Neil Horman   [PATCH] convert /...
36
  	char name[64];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  	struct cdev *cdev;		/* will die */
68eef3b47   Joe Korty   [PATCH] Simplify ...
38
  } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  
  /* index in the above */
e61eb2e93   Yang Zhang   fs/block: type si...
41
  static inline int major_to_index(unsigned major)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
  {
68eef3b47   Joe Korty   [PATCH] Simplify ...
43
  	return major % CHRDEV_MAJOR_HASH_SIZE;
7170be5f5   Neil Horman   [PATCH] convert /...
44
  }
68eef3b47   Joe Korty   [PATCH] Simplify ...
45
  #ifdef CONFIG_PROC_FS
7170be5f5   Neil Horman   [PATCH] convert /...
46

68eef3b47   Joe Korty   [PATCH] Simplify ...
47
  void chrdev_show(struct seq_file *f, off_t offset)
7170be5f5   Neil Horman   [PATCH] convert /...
48
49
  {
  	struct char_device_struct *cd;
7170be5f5   Neil Horman   [PATCH] convert /...
50

8a932f73e   Logan Gunthorpe   char_dev: order /...
51
52
53
  	mutex_lock(&chrdevs_lock);
  	for (cd = chrdevs[major_to_index(offset)]; cd; cd = cd->next) {
  		if (cd->major == offset)
68eef3b47   Joe Korty   [PATCH] Simplify ...
54
55
  			seq_printf(f, "%3d %s
  ", cd->major, cd->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  	}
8a932f73e   Logan Gunthorpe   char_dev: order /...
57
  	mutex_unlock(&chrdevs_lock);
7170be5f5   Neil Horman   [PATCH] convert /...
58
  }
68eef3b47   Joe Korty   [PATCH] Simplify ...
59
  #endif /* CONFIG_PROC_FS */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
61
62
63
64
  static int find_dynamic_major(void)
  {
  	int i;
  	struct char_device_struct *cd;
652d703b2   Srivatsa S. Bhat   char_dev: Fix off...
65
  	for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
66
67
68
69
70
  		if (chrdevs[i] == NULL)
  			return i;
  	}
  
  	for (i = CHRDEV_MAJOR_DYN_EXT_START;
652d703b2   Srivatsa S. Bhat   char_dev: Fix off...
71
  	     i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
72
73
74
  		for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)
  			if (cd->major == i)
  				break;
652d703b2   Srivatsa S. Bhat   char_dev: Fix off...
75
  		if (cd == NULL)
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
76
77
78
79
80
  			return i;
  	}
  
  	return -EBUSY;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
  /*
   * Register a single major with a specified minor range.
   *
d358b1733   Chengguang Xu   chardev: update c...
84
85
86
   * If major == 0 this function will dynamically allocate an unused major.
   * If major > 0 this function will attempt to reserve the range of minors
   * with given major.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
92
   */
  static struct char_device_struct *
  __register_chrdev_region(unsigned int major, unsigned int baseminor,
  			   int minorct, const char *name)
  {
4b0be5726   Chengguang Xu   chardev: code cle...
93
  	struct char_device_struct *cd, *curr, *prev = NULL;
7ef0b1524   Chengguang Xu   chardev: set vari...
94
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  	int i;
4b0be5726   Chengguang Xu   chardev: code cle...
96
97
98
99
100
101
  	if (major >= CHRDEV_MAJOR_MAX) {
  		pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)
  ",
  		       name, major, CHRDEV_MAJOR_MAX-1);
  		return ERR_PTR(-EINVAL);
  	}
4712d3796   Chengguang Xu   chardev: add a ch...
102
103
104
105
106
107
  	if (minorct > MINORMASK + 1 - baseminor) {
  		pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major
  ",
  			name, baseminor, baseminor + minorct - 1, 0, MINORMASK);
  		return ERR_PTR(-EINVAL);
  	}
11b0b5abb   Oliver Neukum   [PATCH] use kzall...
108
  	cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
  	if (cd == NULL)
  		return ERR_PTR(-ENOMEM);
58383af62   Jes Sorensen   [PATCH] kobj_map ...
111
  	mutex_lock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  	if (major == 0) {
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
114
115
116
117
118
  		ret = find_dynamic_major();
  		if (ret < 0) {
  			pr_err("CHRDEV \"%s\" dynamic allocation region is full
  ",
  			       name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
  			goto out;
  		}
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
121
  		major = ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
  	}
7ef0b1524   Chengguang Xu   chardev: set vari...
123
  	ret = -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  	i = major_to_index(major);
4b0be5726   Chengguang Xu   chardev: code cle...
125
126
127
  	for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) {
  		if (curr->major < major)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128

4b0be5726   Chengguang Xu   chardev: code cle...
129
  		if (curr->major > major)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  			break;
01d553d0f   Amos Waterland   [PATCH] Chardev c...
131

4b0be5726   Chengguang Xu   chardev: code cle...
132
133
  		if (curr->baseminor + curr->minorct <= baseminor)
  			continue;
01d553d0f   Amos Waterland   [PATCH] Chardev c...
134

4b0be5726   Chengguang Xu   chardev: code cle...
135
136
  		if (curr->baseminor >= baseminor + minorct)
  			break;
01d553d0f   Amos Waterland   [PATCH] Chardev c...
137

4b0be5726   Chengguang Xu   chardev: code cle...
138
139
  		goto out;
  	}
de36e16d1   Chengguang Xu   chardev: add addi...
140

4b0be5726   Chengguang Xu   chardev: code cle...
141
142
143
144
  	cd->major = major;
  	cd->baseminor = baseminor;
  	cd->minorct = minorct;
  	strlcpy(cd->name, name, sizeof(cd->name));
de36e16d1   Chengguang Xu   chardev: add addi...
145

4b0be5726   Chengguang Xu   chardev: code cle...
146
147
148
149
150
151
  	if (!prev) {
  		cd->next = curr;
  		chrdevs[i] = cd;
  	} else {
  		cd->next = prev->next;
  		prev->next = cd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  	}
01d553d0f   Amos Waterland   [PATCH] Chardev c...
153

58383af62   Jes Sorensen   [PATCH] kobj_map ...
154
  	mutex_unlock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
  	return cd;
  out:
58383af62   Jes Sorensen   [PATCH] kobj_map ...
157
  	mutex_unlock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
162
163
164
165
166
  	kfree(cd);
  	return ERR_PTR(ret);
  }
  
  static struct char_device_struct *
  __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
  {
  	struct char_device_struct *cd = NULL, **cp;
  	int i = major_to_index(major);
58383af62   Jes Sorensen   [PATCH] kobj_map ...
167
  	mutex_lock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
174
175
176
  	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
  		if ((*cp)->major == major &&
  		    (*cp)->baseminor == baseminor &&
  		    (*cp)->minorct == minorct)
  			break;
  	if (*cp) {
  		cd = *cp;
  		*cp = cd->next;
  	}
58383af62   Jes Sorensen   [PATCH] kobj_map ...
177
  	mutex_unlock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
  	return cd;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
180
181
182
183
184
185
186
187
188
  /**
   * register_chrdev_region() - register a range of device numbers
   * @from: the first in the desired range of device numbers; must include
   *        the major number.
   * @count: the number of consecutive device numbers required
   * @name: the name of the device or driver.
   *
   * Return value is zero on success, a negative error code on failure.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  int register_chrdev_region(dev_t from, unsigned count, const char *name)
  {
  	struct char_device_struct *cd;
  	dev_t to = from + count;
  	dev_t n, next;
  
  	for (n = from; n < to; n = next) {
  		next = MKDEV(MAJOR(n)+1, 0);
  		if (next > to)
  			next = to;
  		cd = __register_chrdev_region(MAJOR(n), MINOR(n),
  			       next - n, name);
  		if (IS_ERR(cd))
  			goto fail;
  	}
  	return 0;
  fail:
  	to = n;
  	for (n = from; n < to; n = next) {
  		next = MKDEV(MAJOR(n)+1, 0);
  		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
  	}
  	return PTR_ERR(cd);
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
213
214
215
216
217
218
219
220
221
222
223
  /**
   * alloc_chrdev_region() - register a range of char device numbers
   * @dev: output parameter for first assigned number
   * @baseminor: first of the requested range of minor numbers
   * @count: the number of minor numbers required
   * @name: the name of the associated device or driver
   *
   * Allocates a range of char device numbers.  The major number will be
   * chosen dynamically, and returned (along with the first minor number)
   * in @dev.  Returns zero or a negative error code.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
229
230
231
232
233
  int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
  			const char *name)
  {
  	struct char_device_struct *cd;
  	cd = __register_chrdev_region(0, baseminor, count, name);
  	if (IS_ERR(cd))
  		return PTR_ERR(cd);
  	*dev = MKDEV(cd->major, cd->baseminor);
  	return 0;
  }
d247e2c66   Rolf Eike Beer   [PATCH] add funct...
234
  /**
1905b1bfc   Tejun Heo   chrdev: implement...
235
   * __register_chrdev() - create and register a cdev occupying a range of minors
d247e2c66   Rolf Eike Beer   [PATCH] add funct...
236
   * @major: major device number or 0 for dynamic allocation
1905b1bfc   Tejun Heo   chrdev: implement...
237
238
   * @baseminor: first of the requested range of minor numbers
   * @count: the number of minor numbers required
d247e2c66   Rolf Eike Beer   [PATCH] add funct...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
   * @name: name of this range of devices
   * @fops: file operations associated with this devices
   *
   * If @major == 0 this functions will dynamically allocate a major and return
   * its number.
   *
   * If @major > 0 this function will attempt to reserve a device with the given
   * major number and will return zero on success.
   *
   * Returns a -ve errno on failure.
   *
   * The name of this device has nothing to do with the name of the device in
   * /dev. It only helps to keep track of the different owners of devices. If
   * your module name has only one type of devices it's ok to use e.g. the name
   * of the module here.
d247e2c66   Rolf Eike Beer   [PATCH] add funct...
254
   */
1905b1bfc   Tejun Heo   chrdev: implement...
255
256
257
  int __register_chrdev(unsigned int major, unsigned int baseminor,
  		      unsigned int count, const char *name,
  		      const struct file_operations *fops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
  {
  	struct char_device_struct *cd;
  	struct cdev *cdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  	int err = -ENOMEM;
1905b1bfc   Tejun Heo   chrdev: implement...
262
  	cd = __register_chrdev_region(major, baseminor, count, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
  	if (IS_ERR(cd))
  		return PTR_ERR(cd);
1ff97647f   Greg Kroah-Hartman   char_dev.c: fix u...
265

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
270
271
272
  	cdev = cdev_alloc();
  	if (!cdev)
  		goto out2;
  
  	cdev->owner = fops->owner;
  	cdev->ops = fops;
  	kobject_set_name(&cdev->kobj, "%s", name);
1ff97647f   Greg Kroah-Hartman   char_dev.c: fix u...
273

1905b1bfc   Tejun Heo   chrdev: implement...
274
  	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
280
281
282
283
  	if (err)
  		goto out;
  
  	cd->cdev = cdev;
  
  	return major ? 0 : cd->major;
  out:
  	kobject_put(&cdev->kobj);
  out2:
1905b1bfc   Tejun Heo   chrdev: implement...
284
  	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
  	return err;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
287
  /**
594069bc3   Partha Pratim Mukherjee   fs/char_dev.c: fi...
288
   * unregister_chrdev_region() - unregister a range of device numbers
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
289
290
291
292
293
294
295
   * @from: the first in the range of numbers to unregister
   * @count: the number of device numbers to unregister
   *
   * This function will unregister a range of @count device numbers,
   * starting with @from.  The caller should normally be the one who
   * allocated those numbers in the first place...
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
300
301
302
303
304
305
306
307
  void unregister_chrdev_region(dev_t from, unsigned count)
  {
  	dev_t to = from + count;
  	dev_t n, next;
  
  	for (n = from; n < to; n = next) {
  		next = MKDEV(MAJOR(n)+1, 0);
  		if (next > to)
  			next = to;
  		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
  	}
  }
1905b1bfc   Tejun Heo   chrdev: implement...
308
309
310
311
312
313
314
315
316
317
318
319
320
  /**
   * __unregister_chrdev - unregister and destroy a cdev
   * @major: major device number
   * @baseminor: first of the range of minor numbers
   * @count: the number of minor numbers this cdev is occupying
   * @name: name of this range of devices
   *
   * Unregister and destroy the cdev occupying the region described by
   * @major, @baseminor and @count.  This function undoes what
   * __register_chrdev() did.
   */
  void __unregister_chrdev(unsigned int major, unsigned int baseminor,
  			 unsigned int count, const char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
  {
  	struct char_device_struct *cd;
1905b1bfc   Tejun Heo   chrdev: implement...
323
324
  
  	cd = __unregister_chrdev_region(major, baseminor, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
  	if (cd && cd->cdev)
  		cdev_del(cd->cdev);
  	kfree(cd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
333
334
335
336
337
338
  }
  
  static DEFINE_SPINLOCK(cdev_lock);
  
  static struct kobject *cdev_get(struct cdev *p)
  {
  	struct module *owner = p->owner;
  	struct kobject *kobj;
  
  	if (owner && !try_module_get(owner))
  		return NULL;
341464390   Will Deacon   chardev: Avoid po...
339
  	kobj = kobject_get_unless_zero(&p->kobj);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
343
344
345
346
347
  	if (!kobj)
  		module_put(owner);
  	return kobj;
  }
  
  void cdev_put(struct cdev *p)
  {
  	if (p) {
7da6844cf   Brian King   [PATCH] cdev: cde...
348
  		struct module *owner = p->owner;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  		kobject_put(&p->kobj);
7da6844cf   Brian King   [PATCH] cdev: cde...
350
  		module_put(owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
  	}
  }
  
  /*
   * Called every time a character special file is opened
   */
922f9cfa7   Denis Cheng   fs/char_dev.c: ch...
357
  static int chrdev_open(struct inode *inode, struct file *filp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  {
e84f9e57b   Al Viro   consolidate the r...
359
  	const struct file_operations *fops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  	struct cdev *p;
  	struct cdev *new = NULL;
  	int ret = 0;
  
  	spin_lock(&cdev_lock);
  	p = inode->i_cdev;
  	if (!p) {
  		struct kobject *kobj;
  		int idx;
  		spin_unlock(&cdev_lock);
  		kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  		if (!kobj)
  			return -ENXIO;
  		new = container_of(kobj, struct cdev, kobj);
  		spin_lock(&cdev_lock);
a30427d92   Jonathan Corbet   Add a comment in ...
375
376
  		/* Check i_cdev again in case somebody beat us to it while
  		   we dropped the lock. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
  		p = inode->i_cdev;
  		if (!p) {
  			inode->i_cdev = p = new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
389
  			list_add(&inode->i_devices, &p->list);
  			new = NULL;
  		} else if (!cdev_get(p))
  			ret = -ENXIO;
  	} else if (!cdev_get(p))
  		ret = -ENXIO;
  	spin_unlock(&cdev_lock);
  	cdev_put(new);
  	if (ret)
  		return ret;
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
390
391
  
  	ret = -ENXIO;
e84f9e57b   Al Viro   consolidate the r...
392
393
  	fops = fops_get(p->ops);
  	if (!fops)
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
394
  		goto out_cdev_put;
e84f9e57b   Al Viro   consolidate the r...
395
  	replace_fops(filp, fops);
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
396
  	if (filp->f_op->open) {
1ff97647f   Greg Kroah-Hartman   char_dev.c: fix u...
397
  		ret = filp->f_op->open(inode, filp);
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
398
399
400
401
402
403
404
405
  		if (ret)
  			goto out_cdev_put;
  	}
  
  	return 0;
  
   out_cdev_put:
  	cdev_put(p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
411
412
413
  	return ret;
  }
  
  void cd_forget(struct inode *inode)
  {
  	spin_lock(&cdev_lock);
  	list_del_init(&inode->i_devices);
  	inode->i_cdev = NULL;
3bc52c45b   Dan Williams   dax: define a uni...
414
  	inode->i_mapping = &inode->i_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
  	spin_unlock(&cdev_lock);
  }
75c96f858   Adrian Bunk   [PATCH] make some...
417
  static void cdev_purge(struct cdev *cdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
  {
  	spin_lock(&cdev_lock);
  	while (!list_empty(&cdev->list)) {
  		struct inode *inode;
  		inode = container_of(cdev->list.next, struct inode, i_devices);
  		list_del_init(&inode->i_devices);
  		inode->i_cdev = NULL;
  	}
  	spin_unlock(&cdev_lock);
  }
  
  /*
   * Dummy default file-operations: the only thing this does
   * is contain the open that then fills in the correct operations
   * depending on the special file...
   */
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
434
  const struct file_operations def_chr_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  	.open = chrdev_open,
6038f373a   Arnd Bergmann   llseek: automatic...
436
  	.llseek = noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
440
441
442
443
444
445
446
447
448
449
  };
  
  static struct kobject *exact_match(dev_t dev, int *part, void *data)
  {
  	struct cdev *p = data;
  	return &p->kobj;
  }
  
  static int exact_lock(dev_t dev, void *data)
  {
  	struct cdev *p = data;
  	return cdev_get(p) ? 0 : -1;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
450
451
452
453
454
455
456
457
458
459
  /**
   * cdev_add() - add a char device to the system
   * @p: the cdev structure for the device
   * @dev: the first device number for which this device is responsible
   * @count: the number of consecutive minor numbers corresponding to this
   *         device
   *
   * cdev_add() adds the device represented by @p to the system, making it
   * live immediately.  A negative error code is returned on failure.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
  int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  {
2f0157f13   Dmitry Torokhov   char_dev: pin par...
462
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
  	p->dev = dev;
  	p->count = count;
2f0157f13   Dmitry Torokhov   char_dev: pin par...
465
466
467
468
469
470
471
472
473
  
  	error = kobj_map(cdev_map, dev, count, NULL,
  			 exact_match, exact_lock, p);
  	if (error)
  		return error;
  
  	kobject_get(p->kobj.parent);
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
  }
233ed09d7   Logan Gunthorpe   chardev: add help...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
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
  /**
   * cdev_set_parent() - set the parent kobject for a char device
   * @p: the cdev structure
   * @kobj: the kobject to take a reference to
   *
   * cdev_set_parent() sets a parent kobject which will be referenced
   * appropriately so the parent is not freed before the cdev. This
   * should be called before cdev_add.
   */
  void cdev_set_parent(struct cdev *p, struct kobject *kobj)
  {
  	WARN_ON(!kobj->state_initialized);
  	p->kobj.parent = kobj;
  }
  
  /**
   * cdev_device_add() - add a char device and it's corresponding
   *	struct device, linkink
   * @dev: the device structure
   * @cdev: the cdev structure
   *
   * cdev_device_add() adds the char device represented by @cdev to the system,
   * just as cdev_add does. It then adds @dev to the system using device_add
   * The dev_t for the char device will be taken from the struct device which
   * needs to be initialized first. This helper function correctly takes a
   * reference to the parent device so the parent will not get released until
   * all references to the cdev are released.
   *
   * This helper uses dev->devt for the device number. If it is not set
   * it will not add the cdev and it will be equivalent to device_add.
   *
   * This function should be used whenever the struct cdev and the
   * struct device are members of the same structure whose lifetime is
   * managed by the struct device.
   *
   * NOTE: Callers must assume that userspace was able to open the cdev and
   * can call cdev fops callbacks at any time, even if this function fails.
   */
  int cdev_device_add(struct cdev *cdev, struct device *dev)
  {
  	int rc = 0;
  
  	if (dev->devt) {
  		cdev_set_parent(cdev, &dev->kobj);
  
  		rc = cdev_add(cdev, dev->devt, 1);
  		if (rc)
  			return rc;
  	}
  
  	rc = device_add(dev);
  	if (rc)
  		cdev_del(cdev);
  
  	return rc;
  }
  
  /**
   * cdev_device_del() - inverse of cdev_device_add
   * @dev: the device structure
   * @cdev: the cdev structure
   *
   * cdev_device_del() is a helper function to call cdev_del and device_del.
   * It should be used whenever cdev_device_add is used.
   *
   * If dev->devt is not set it will not remove the cdev and will be equivalent
   * to device_del.
   *
   * NOTE: This guarantees that associated sysfs callbacks are not running
   * or runnable, however any cdevs already open will remain and their fops
   * will still be callable even after this function returns.
   */
  void cdev_device_del(struct cdev *cdev, struct device *dev)
  {
  	device_del(dev);
  	if (dev->devt)
  		cdev_del(cdev);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
  static void cdev_unmap(dev_t dev, unsigned count)
  {
  	kobj_unmap(cdev_map, dev, count);
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
557
558
559
560
561
562
  /**
   * cdev_del() - remove a cdev from the system
   * @p: the cdev structure to be removed
   *
   * cdev_del() removes @p from the system, possibly freeing the structure
   * itself.
233ed09d7   Logan Gunthorpe   chardev: add help...
563
564
565
566
   *
   * NOTE: This guarantees that cdev device will no longer be able to be
   * opened, however any cdevs already open will remain and their fops will
   * still be callable even after cdev_del returns.
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
567
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
  void cdev_del(struct cdev *p)
  {
  	cdev_unmap(p->dev, p->count);
  	kobject_put(&p->kobj);
  }
  
  
  static void cdev_default_release(struct kobject *kobj)
  {
  	struct cdev *p = container_of(kobj, struct cdev, kobj);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
578
  	struct kobject *parent = kobj->parent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
  	cdev_purge(p);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
580
  	kobject_put(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
583
584
585
  }
  
  static void cdev_dynamic_release(struct kobject *kobj)
  {
  	struct cdev *p = container_of(kobj, struct cdev, kobj);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
586
  	struct kobject *parent = kobj->parent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
  	cdev_purge(p);
  	kfree(p);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
589
  	kobject_put(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
595
596
597
598
  }
  
  static struct kobj_type ktype_cdev_default = {
  	.release	= cdev_default_release,
  };
  
  static struct kobj_type ktype_cdev_dynamic = {
  	.release	= cdev_dynamic_release,
  };
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
599
600
601
602
603
  /**
   * cdev_alloc() - allocate a cdev structure
   *
   * Allocates and returns a cdev structure, or NULL on failure.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
  struct cdev *cdev_alloc(void)
  {
11b0b5abb   Oliver Neukum   [PATCH] use kzall...
606
  	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
  	if (p) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  		INIT_LIST_HEAD(&p->list);
f9cb074bf   Greg Kroah-Hartman   Kobject: rename k...
609
  		kobject_init(&p->kobj, &ktype_cdev_dynamic);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
  	}
  	return p;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
613
614
615
616
617
618
619
620
  /**
   * cdev_init() - initialize a cdev structure
   * @cdev: the structure to initialize
   * @fops: the file_operations for this device
   *
   * Initializes @cdev, remembering @fops, making it ready to add to the
   * system with cdev_add().
   */
99ac48f54   Arjan van de Ven   [PATCH] mark f_op...
621
  void cdev_init(struct cdev *cdev, const struct file_operations *fops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
  {
  	memset(cdev, 0, sizeof *cdev);
  	INIT_LIST_HEAD(&cdev->list);
f9cb074bf   Greg Kroah-Hartman   Kobject: rename k...
625
  	kobject_init(&cdev->kobj, &ktype_cdev_default);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
  	cdev->ops = fops;
  }
  
  static struct kobject *base_probe(dev_t dev, int *part, void *data)
  {
  	if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0)
  		/* Make old-style 2.4 aliases work */
  		request_module("char-major-%d", MAJOR(dev));
  	return NULL;
  }
  
  void __init chrdev_init(void)
  {
  	cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
  }
  
  
  /* Let modules do char dev stuff */
  EXPORT_SYMBOL(register_chrdev_region);
  EXPORT_SYMBOL(unregister_chrdev_region);
  EXPORT_SYMBOL(alloc_chrdev_region);
  EXPORT_SYMBOL(cdev_init);
  EXPORT_SYMBOL(cdev_alloc);
  EXPORT_SYMBOL(cdev_del);
  EXPORT_SYMBOL(cdev_add);
233ed09d7   Logan Gunthorpe   chardev: add help...
651
652
653
  EXPORT_SYMBOL(cdev_set_parent);
  EXPORT_SYMBOL(cdev_device_add);
  EXPORT_SYMBOL(cdev_device_del);
1905b1bfc   Tejun Heo   chrdev: implement...
654
655
  EXPORT_SYMBOL(__register_chrdev);
  EXPORT_SYMBOL(__unregister_chrdev);