Blame view

fs/char_dev.c 16.6 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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  /*
   * Register a single major with a specified minor range.
   *
   * If major == 0 this functions will dynamically allocate a major and return
   * its number.
   *
   * If major > 0 this function will attempt to reserve the passed range of
   * minors and will return zero on success.
   *
   * Returns a -ve errno on failure.
   */
  static struct char_device_struct *
  __register_chrdev_region(unsigned int major, unsigned int baseminor,
  			   int minorct, const char *name)
  {
  	struct char_device_struct *cd, **cp;
  	int ret = 0;
  	int i;
11b0b5abb   Oliver Neukum   [PATCH] use kzall...
99
  	cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
  	if (cd == NULL)
  		return ERR_PTR(-ENOMEM);
58383af62   Jes Sorensen   [PATCH] kobj_map ...
102
  	mutex_lock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  	if (major == 0) {
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
105
106
107
108
109
  		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
110
111
  			goto out;
  		}
a5d31a3f8   Logan Gunthorpe   char_dev: extend ...
112
  		major = ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  	}
8a932f73e   Logan Gunthorpe   char_dev: order /...
114
  	if (major >= CHRDEV_MAJOR_MAX) {
f33ff110e   Srivatsa S. Bhat   block, char_dev: ...
115
116
117
  		pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)
  ",
  		       name, major, CHRDEV_MAJOR_MAX-1);
8a932f73e   Logan Gunthorpe   char_dev: order /...
118
119
120
  		ret = -EINVAL;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
  	cd->major = major;
  	cd->baseminor = baseminor;
  	cd->minorct = minorct;
8c4018884   Cyrill Gorcunov   fs: fix name over...
124
  	strlcpy(cd->name, name, sizeof(cd->name));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
  
  	i = major_to_index(major);
  
  	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
  		if ((*cp)->major > major ||
01d553d0f   Amos Waterland   [PATCH] Chardev c...
130
131
132
  		    ((*cp)->major == major &&
  		     (((*cp)->baseminor >= baseminor) ||
  		      ((*cp)->baseminor + (*cp)->minorct > baseminor))))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  			break;
01d553d0f   Amos Waterland   [PATCH] Chardev c...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  
  	/* Check for overlapping minor ranges.  */
  	if (*cp && (*cp)->major == major) {
  		int old_min = (*cp)->baseminor;
  		int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
  		int new_min = baseminor;
  		int new_max = baseminor + minorct - 1;
  
  		/* New driver overlaps from the left.  */
  		if (new_max >= old_min && new_max <= old_max) {
  			ret = -EBUSY;
  			goto out;
  		}
  
  		/* New driver overlaps from the right.  */
  		if (new_min <= old_max && new_min >= old_min) {
  			ret = -EBUSY;
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
  	}
01d553d0f   Amos Waterland   [PATCH] Chardev c...
154

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
  	cd->next = *cp;
  	*cp = cd;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
157
  	mutex_unlock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
  	return cd;
  out:
58383af62   Jes Sorensen   [PATCH] kobj_map ...
160
  	mutex_unlock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
163
164
165
166
167
168
169
  	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 ...
170
  	mutex_lock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
178
179
  	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 ...
180
  	mutex_unlock(&chrdevs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
  	return cd;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
183
184
185
186
187
188
189
190
191
  /**
   * 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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  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...
216
217
218
219
220
221
222
223
224
225
226
  /**
   * 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
227
228
229
230
231
232
233
234
235
236
  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...
237
  /**
1905b1bfc   Tejun Heo   chrdev: implement...
238
   * __register_chrdev() - create and register a cdev occupying a range of minors
d247e2c66   Rolf Eike Beer   [PATCH] add funct...
239
   * @major: major device number or 0 for dynamic allocation
1905b1bfc   Tejun Heo   chrdev: implement...
240
241
   * @baseminor: first of the requested range of minor numbers
   * @count: the number of minor numbers required
d247e2c66   Rolf Eike Beer   [PATCH] add funct...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
   * @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...
257
   */
1905b1bfc   Tejun Heo   chrdev: implement...
258
259
260
  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
261
262
263
  {
  	struct char_device_struct *cd;
  	struct cdev *cdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  	int err = -ENOMEM;
1905b1bfc   Tejun Heo   chrdev: implement...
265
  	cd = __register_chrdev_region(major, baseminor, count, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
  	if (IS_ERR(cd))
  		return PTR_ERR(cd);
1ff97647f   Greg Kroah-Hartman   char_dev.c: fix u...
268

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
  	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...
276

1905b1bfc   Tejun Heo   chrdev: implement...
277
  	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
281
282
283
284
285
286
  	if (err)
  		goto out;
  
  	cd->cdev = cdev;
  
  	return major ? 0 : cd->major;
  out:
  	kobject_put(&cdev->kobj);
  out2:
1905b1bfc   Tejun Heo   chrdev: implement...
287
  	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
  	return err;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
290
  /**
594069bc3   Partha Pratim Mukherjee   fs/char_dev.c: fi...
291
   * unregister_chrdev_region() - unregister a range of device numbers
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
292
293
294
295
296
297
298
   * @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
299
300
301
302
303
304
305
306
307
308
309
310
  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...
311
312
313
314
315
316
317
318
319
320
321
322
323
  /**
   * __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
324
325
  {
  	struct char_device_struct *cd;
1905b1bfc   Tejun Heo   chrdev: implement...
326
327
  
  	cd = __unregister_chrdev_region(major, baseminor, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
  	if (cd && cd->cdev)
  		cdev_del(cd->cdev);
  	kfree(cd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  }
  
  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;
  	kobj = kobject_get(&p->kobj);
  	if (!kobj)
  		module_put(owner);
  	return kobj;
  }
  
  void cdev_put(struct cdev *p)
  {
  	if (p) {
7da6844cf   Brian King   [PATCH] cdev: cde...
351
  		struct module *owner = p->owner;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
  		kobject_put(&p->kobj);
7da6844cf   Brian King   [PATCH] cdev: cde...
353
  		module_put(owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
  	}
  }
  
  /*
   * Called every time a character special file is opened
   */
922f9cfa7   Denis Cheng   fs/char_dev.c: ch...
360
  static int chrdev_open(struct inode *inode, struct file *filp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  {
e84f9e57b   Al Viro   consolidate the r...
362
  	const struct file_operations *fops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  	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 ...
378
379
  		/* Check i_cdev again in case somebody beat us to it while
  		   we dropped the lock. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
  		p = inode->i_cdev;
  		if (!p) {
  			inode->i_cdev = p = new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
384
385
386
387
388
389
390
391
392
  			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...
393
394
  
  	ret = -ENXIO;
e84f9e57b   Al Viro   consolidate the r...
395
396
  	fops = fops_get(p->ops);
  	if (!fops)
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
397
  		goto out_cdev_put;
e84f9e57b   Al Viro   consolidate the r...
398
  	replace_fops(filp, fops);
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
399
  	if (filp->f_op->open) {
1ff97647f   Greg Kroah-Hartman   char_dev.c: fix u...
400
  		ret = filp->f_op->open(inode, filp);
a518ab932   Christoph Hellwig   [PATCH] tidy up c...
401
402
403
404
405
406
407
408
  		if (ret)
  			goto out_cdev_put;
  	}
  
  	return 0;
  
   out_cdev_put:
  	cdev_put(p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
414
415
416
  	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...
417
  	inode->i_mapping = &inode->i_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
  	spin_unlock(&cdev_lock);
  }
75c96f858   Adrian Bunk   [PATCH] make some...
420
  static void cdev_purge(struct cdev *cdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  {
  	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...
437
  const struct file_operations def_chr_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
  	.open = chrdev_open,
6038f373a   Arnd Bergmann   llseek: automatic...
439
  	.llseek = noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
446
447
448
449
450
451
452
  };
  
  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...
453
454
455
456
457
458
459
460
461
462
  /**
   * 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
463
464
  int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  {
2f0157f13   Dmitry Torokhov   char_dev: pin par...
465
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
  	p->dev = dev;
  	p->count = count;
2f0157f13   Dmitry Torokhov   char_dev: pin par...
468
469
470
471
472
473
474
475
476
  
  	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
477
  }
233ed09d7   Logan Gunthorpe   chardev: add help...
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
553
554
555
  /**
   * 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
556
557
558
559
  static void cdev_unmap(dev_t dev, unsigned count)
  {
  	kobj_unmap(cdev_map, dev, count);
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
560
561
562
563
564
565
  /**
   * 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...
566
567
568
569
   *
   * 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...
570
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
575
576
577
578
579
580
  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...
581
  	struct kobject *parent = kobj->parent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  	cdev_purge(p);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
583
  	kobject_put(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
  }
  
  static void cdev_dynamic_release(struct kobject *kobj)
  {
  	struct cdev *p = container_of(kobj, struct cdev, kobj);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
589
  	struct kobject *parent = kobj->parent;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
  	cdev_purge(p);
  	kfree(p);
2f0157f13   Dmitry Torokhov   char_dev: pin par...
592
  	kobject_put(parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
597
598
599
600
601
  }
  
  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...
602
603
604
605
606
  /**
   * cdev_alloc() - allocate a cdev structure
   *
   * Allocates and returns a cdev structure, or NULL on failure.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
  struct cdev *cdev_alloc(void)
  {
11b0b5abb   Oliver Neukum   [PATCH] use kzall...
609
  	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
  	if (p) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  		INIT_LIST_HEAD(&p->list);
f9cb074bf   Greg Kroah-Hartman   Kobject: rename k...
612
  		kobject_init(&p->kobj, &ktype_cdev_dynamic);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
615
  	}
  	return p;
  }
cf3e43dbe   Jonathan Corbet   [PATCH] cdev docu...
616
617
618
619
620
621
622
623
  /**
   * 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...
624
  void cdev_init(struct cdev *cdev, const struct file_operations *fops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
627
  {
  	memset(cdev, 0, sizeof *cdev);
  	INIT_LIST_HEAD(&cdev->list);
f9cb074bf   Greg Kroah-Hartman   Kobject: rename k...
628
  	kobject_init(&cdev->kobj, &ktype_cdev_default);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  	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...
654
655
656
  EXPORT_SYMBOL(cdev_set_parent);
  EXPORT_SYMBOL(cdev_device_add);
  EXPORT_SYMBOL(cdev_device_del);
1905b1bfc   Tejun Heo   chrdev: implement...
657
658
  EXPORT_SYMBOL(__register_chrdev);
  EXPORT_SYMBOL(__unregister_chrdev);