xref: /openbmc/linux/fs/char_dev.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1 b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2 1da177e4SLinus Torvalds /*
3 1da177e4SLinus Torvalds  *  linux/fs/char_dev.c
4 1da177e4SLinus Torvalds  *
5 1da177e4SLinus Torvalds  *  Copyright (C) 1991, 1992  Linus Torvalds
6 1da177e4SLinus Torvalds  */
7 1da177e4SLinus Torvalds 
8 1da177e4SLinus Torvalds #include <linux/init.h>
9 1da177e4SLinus Torvalds #include <linux/fs.h>
10 b446b60eSAndrew Morton #include <linux/kdev_t.h>
11 1da177e4SLinus Torvalds #include <linux/slab.h>
12 1da177e4SLinus Torvalds #include <linux/string.h>
13 1da177e4SLinus Torvalds 
14 1da177e4SLinus Torvalds #include <linux/major.h>
15 1da177e4SLinus Torvalds #include <linux/errno.h>
16 1da177e4SLinus Torvalds #include <linux/module.h>
17 68eef3b4SJoe Korty #include <linux/seq_file.h>
18 1da177e4SLinus Torvalds 
19 1da177e4SLinus Torvalds #include <linux/kobject.h>
20 1da177e4SLinus Torvalds #include <linux/kobj_map.h>
21 1da177e4SLinus Torvalds #include <linux/cdev.h>
22 58383af6SJes Sorensen #include <linux/mutex.h>
23 5da6185bSDavid Howells #include <linux/backing-dev.h>
24 31d1d48eSDavid Howells #include <linux/tty.h>
25 1da177e4SLinus Torvalds 
26 07f3f05cSDavid Howells #include "internal.h"
27 1da177e4SLinus Torvalds 
28 1da177e4SLinus Torvalds static struct kobj_map *cdev_map;
29 1da177e4SLinus Torvalds 
30 58383af6SJes Sorensen static DEFINE_MUTEX(chrdevs_lock);
31 1da177e4SLinus Torvalds 
32 8a932f73SLogan Gunthorpe #define CHRDEV_MAJOR_HASH_SIZE 255
33 8a932f73SLogan Gunthorpe 
34 1da177e4SLinus Torvalds static struct char_device_struct {
35 1da177e4SLinus Torvalds 	struct char_device_struct *next;
36 1da177e4SLinus Torvalds 	unsigned int major;
37 1da177e4SLinus Torvalds 	unsigned int baseminor;
38 1da177e4SLinus Torvalds 	int minorct;
39 7170be5fSNeil Horman 	char name[64];
40 1da177e4SLinus Torvalds 	struct cdev *cdev;		/* will die */
41 68eef3b4SJoe Korty } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
42 1da177e4SLinus Torvalds 
43 1da177e4SLinus Torvalds /* index in the above */
major_to_index(unsigned major)44 e61eb2e9SYang Zhang static inline int major_to_index(unsigned major)
45 1da177e4SLinus Torvalds {
46 68eef3b4SJoe Korty 	return major % CHRDEV_MAJOR_HASH_SIZE;
47 1da177e4SLinus Torvalds }
48 1da177e4SLinus Torvalds 
49 68eef3b4SJoe Korty #ifdef CONFIG_PROC_FS
50 68eef3b4SJoe Korty 
chrdev_show(struct seq_file * f,off_t offset)51 68eef3b4SJoe Korty void chrdev_show(struct seq_file *f, off_t offset)
52 68eef3b4SJoe Korty {
53 7170be5fSNeil Horman 	struct char_device_struct *cd;
54 7170be5fSNeil Horman 
55 58383af6SJes Sorensen 	mutex_lock(&chrdevs_lock);
56 8a932f73SLogan Gunthorpe 	for (cd = chrdevs[major_to_index(offset)]; cd; cd = cd->next) {
57 8a932f73SLogan Gunthorpe 		if (cd->major == offset)
58 68eef3b4SJoe Korty 			seq_printf(f, "%3d %s\n", cd->major, cd->name);
59 68eef3b4SJoe Korty 	}
60 8a932f73SLogan Gunthorpe 	mutex_unlock(&chrdevs_lock);
61 7170be5fSNeil Horman }
62 7170be5fSNeil Horman 
63 68eef3b4SJoe Korty #endif /* CONFIG_PROC_FS */
64 1da177e4SLinus Torvalds 
find_dynamic_major(void)65 a5d31a3fSLogan Gunthorpe static int find_dynamic_major(void)
66 a5d31a3fSLogan Gunthorpe {
67 a5d31a3fSLogan Gunthorpe 	int i;
68 a5d31a3fSLogan Gunthorpe 	struct char_device_struct *cd;
69 a5d31a3fSLogan Gunthorpe 
70 652d703bSSrivatsa S. Bhat 	for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {
71 a5d31a3fSLogan Gunthorpe 		if (chrdevs[i] == NULL)
72 a5d31a3fSLogan Gunthorpe 			return i;
73 a5d31a3fSLogan Gunthorpe 	}
74 a5d31a3fSLogan Gunthorpe 
75 a5d31a3fSLogan Gunthorpe 	for (i = CHRDEV_MAJOR_DYN_EXT_START;
76 652d703bSSrivatsa S. Bhat 	     i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {
77 a5d31a3fSLogan Gunthorpe 		for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)
78 a5d31a3fSLogan Gunthorpe 			if (cd->major == i)
79 a5d31a3fSLogan Gunthorpe 				break;
80 a5d31a3fSLogan Gunthorpe 
81 652d703bSSrivatsa S. Bhat 		if (cd == NULL)
82 a5d31a3fSLogan Gunthorpe 			return i;
83 a5d31a3fSLogan Gunthorpe 	}
84 a5d31a3fSLogan Gunthorpe 
85 a5d31a3fSLogan Gunthorpe 	return -EBUSY;
86 a5d31a3fSLogan Gunthorpe }
87 a5d31a3fSLogan Gunthorpe 
88 1da177e4SLinus Torvalds /*
89 1da177e4SLinus Torvalds  * Register a single major with a specified minor range.
90 1da177e4SLinus Torvalds  *
91 d358b173SChengguang Xu  * If major == 0 this function will dynamically allocate an unused major.
92 d358b173SChengguang Xu  * If major > 0 this function will attempt to reserve the range of minors
93 d358b173SChengguang Xu  * with given major.
94 1da177e4SLinus Torvalds  *
95 1da177e4SLinus Torvalds  */
96 1da177e4SLinus Torvalds static struct char_device_struct *
__register_chrdev_region(unsigned int major,unsigned int baseminor,int minorct,const char * name)97 1da177e4SLinus Torvalds __register_chrdev_region(unsigned int major, unsigned int baseminor,
98 1da177e4SLinus Torvalds 			   int minorct, const char *name)
99 1da177e4SLinus Torvalds {
100 4b0be572SChengguang Xu 	struct char_device_struct *cd, *curr, *prev = NULL;
101 7ef0b152SChengguang Xu 	int ret;
102 1da177e4SLinus Torvalds 	int i;
103 1da177e4SLinus Torvalds 
104 4b0be572SChengguang Xu 	if (major >= CHRDEV_MAJOR_MAX) {
105 4b0be572SChengguang Xu 		pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
106 4b0be572SChengguang Xu 		       name, major, CHRDEV_MAJOR_MAX-1);
107 4b0be572SChengguang Xu 		return ERR_PTR(-EINVAL);
108 4b0be572SChengguang Xu 	}
109 4b0be572SChengguang Xu 
110 4712d379SChengguang Xu 	if (minorct > MINORMASK + 1 - baseminor) {
111 4712d379SChengguang Xu 		pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n",
112 4712d379SChengguang Xu 			name, baseminor, baseminor + minorct - 1, 0, MINORMASK);
113 4712d379SChengguang Xu 		return ERR_PTR(-EINVAL);
114 4712d379SChengguang Xu 	}
115 4712d379SChengguang Xu 
116 11b0b5abSOliver Neukum 	cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
117 1da177e4SLinus Torvalds 	if (cd == NULL)
118 1da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
119 1da177e4SLinus Torvalds 
120 58383af6SJes Sorensen 	mutex_lock(&chrdevs_lock);
121 1da177e4SLinus Torvalds 
122 1da177e4SLinus Torvalds 	if (major == 0) {
123 a5d31a3fSLogan Gunthorpe 		ret = find_dynamic_major();
124 a5d31a3fSLogan Gunthorpe 		if (ret < 0) {
125 a5d31a3fSLogan Gunthorpe 			pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
126 a5d31a3fSLogan Gunthorpe 			       name);
127 1da177e4SLinus Torvalds 			goto out;
128 1da177e4SLinus Torvalds 		}
129 a5d31a3fSLogan Gunthorpe 		major = ret;
130 1da177e4SLinus Torvalds 	}
131 1da177e4SLinus Torvalds 
132 7ef0b152SChengguang Xu 	ret = -EBUSY;
133 4b0be572SChengguang Xu 	i = major_to_index(major);
134 4b0be572SChengguang Xu 	for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) {
135 4b0be572SChengguang Xu 		if (curr->major < major)
136 4b0be572SChengguang Xu 			continue;
137 4b0be572SChengguang Xu 
138 4b0be572SChengguang Xu 		if (curr->major > major)
139 4b0be572SChengguang Xu 			break;
140 4b0be572SChengguang Xu 
141 4b0be572SChengguang Xu 		if (curr->baseminor + curr->minorct <= baseminor)
142 4b0be572SChengguang Xu 			continue;
143 4b0be572SChengguang Xu 
144 4b0be572SChengguang Xu 		if (curr->baseminor >= baseminor + minorct)
145 4b0be572SChengguang Xu 			break;
146 4b0be572SChengguang Xu 
147 8a932f73SLogan Gunthorpe 		goto out;
148 8a932f73SLogan Gunthorpe 	}
149 8a932f73SLogan Gunthorpe 
150 1da177e4SLinus Torvalds 	cd->major = major;
151 1da177e4SLinus Torvalds 	cd->baseminor = baseminor;
152 1da177e4SLinus Torvalds 	cd->minorct = minorct;
153 *c642256bSAzeem Shaikh 	strscpy(cd->name, name, sizeof(cd->name));
154 1da177e4SLinus Torvalds 
155 4b0be572SChengguang Xu 	if (!prev) {
156 4b0be572SChengguang Xu 		cd->next = curr;
157 4b0be572SChengguang Xu 		chrdevs[i] = cd;
158 4b0be572SChengguang Xu 	} else {
159 4b0be572SChengguang Xu 		cd->next = prev->next;
160 4b0be572SChengguang Xu 		prev->next = cd;
161 1da177e4SLinus Torvalds 	}
162 01d553d0SAmos Waterland 
163 58383af6SJes Sorensen 	mutex_unlock(&chrdevs_lock);
164 1da177e4SLinus Torvalds 	return cd;
165 1da177e4SLinus Torvalds out:
166 58383af6SJes Sorensen 	mutex_unlock(&chrdevs_lock);
167 1da177e4SLinus Torvalds 	kfree(cd);
168 1da177e4SLinus Torvalds 	return ERR_PTR(ret);
169 1da177e4SLinus Torvalds }
170 1da177e4SLinus Torvalds 
171 1da177e4SLinus Torvalds static struct char_device_struct *
__unregister_chrdev_region(unsigned major,unsigned baseminor,int minorct)172 1da177e4SLinus Torvalds __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
173 1da177e4SLinus Torvalds {
174 1da177e4SLinus Torvalds 	struct char_device_struct *cd = NULL, **cp;
175 1da177e4SLinus Torvalds 	int i = major_to_index(major);
176 1da177e4SLinus Torvalds 
177 58383af6SJes Sorensen 	mutex_lock(&chrdevs_lock);
178 1da177e4SLinus Torvalds 	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
179 1da177e4SLinus Torvalds 		if ((*cp)->major == major &&
180 1da177e4SLinus Torvalds 		    (*cp)->baseminor == baseminor &&
181 1da177e4SLinus Torvalds 		    (*cp)->minorct == minorct)
182 1da177e4SLinus Torvalds 			break;
183 1da177e4SLinus Torvalds 	if (*cp) {
184 1da177e4SLinus Torvalds 		cd = *cp;
185 1da177e4SLinus Torvalds 		*cp = cd->next;
186 1da177e4SLinus Torvalds 	}
187 58383af6SJes Sorensen 	mutex_unlock(&chrdevs_lock);
188 1da177e4SLinus Torvalds 	return cd;
189 1da177e4SLinus Torvalds }
190 1da177e4SLinus Torvalds 
191 cf3e43dbSJonathan Corbet /**
192 cf3e43dbSJonathan Corbet  * register_chrdev_region() - register a range of device numbers
193 cf3e43dbSJonathan Corbet  * @from: the first in the desired range of device numbers; must include
194 cf3e43dbSJonathan Corbet  *        the major number.
195 cf3e43dbSJonathan Corbet  * @count: the number of consecutive device numbers required
196 cf3e43dbSJonathan Corbet  * @name: the name of the device or driver.
197 cf3e43dbSJonathan Corbet  *
198 cf3e43dbSJonathan Corbet  * Return value is zero on success, a negative error code on failure.
199 cf3e43dbSJonathan Corbet  */
register_chrdev_region(dev_t from,unsigned count,const char * name)200 1da177e4SLinus Torvalds int register_chrdev_region(dev_t from, unsigned count, const char *name)
201 1da177e4SLinus Torvalds {
202 1da177e4SLinus Torvalds 	struct char_device_struct *cd;
203 1da177e4SLinus Torvalds 	dev_t to = from + count;
204 1da177e4SLinus Torvalds 	dev_t n, next;
205 1da177e4SLinus Torvalds 
206 1da177e4SLinus Torvalds 	for (n = from; n < to; n = next) {
207 1da177e4SLinus Torvalds 		next = MKDEV(MAJOR(n)+1, 0);
208 1da177e4SLinus Torvalds 		if (next > to)
209 1da177e4SLinus Torvalds 			next = to;
210 1da177e4SLinus Torvalds 		cd = __register_chrdev_region(MAJOR(n), MINOR(n),
211 1da177e4SLinus Torvalds 			       next - n, name);
212 1da177e4SLinus Torvalds 		if (IS_ERR(cd))
213 1da177e4SLinus Torvalds 			goto fail;
214 1da177e4SLinus Torvalds 	}
215 1da177e4SLinus Torvalds 	return 0;
216 1da177e4SLinus Torvalds fail:
217 1da177e4SLinus Torvalds 	to = n;
218 1da177e4SLinus Torvalds 	for (n = from; n < to; n = next) {
219 1da177e4SLinus Torvalds 		next = MKDEV(MAJOR(n)+1, 0);
220 1da177e4SLinus Torvalds 		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
221 1da177e4SLinus Torvalds 	}
222 1da177e4SLinus Torvalds 	return PTR_ERR(cd);
223 1da177e4SLinus Torvalds }
224 1da177e4SLinus Torvalds 
225 cf3e43dbSJonathan Corbet /**
226 cf3e43dbSJonathan Corbet  * alloc_chrdev_region() - register a range of char device numbers
227 cf3e43dbSJonathan Corbet  * @dev: output parameter for first assigned number
228 cf3e43dbSJonathan Corbet  * @baseminor: first of the requested range of minor numbers
229 cf3e43dbSJonathan Corbet  * @count: the number of minor numbers required
230 cf3e43dbSJonathan Corbet  * @name: the name of the associated device or driver
231 cf3e43dbSJonathan Corbet  *
232 cf3e43dbSJonathan Corbet  * Allocates a range of char device numbers.  The major number will be
233 cf3e43dbSJonathan Corbet  * chosen dynamically, and returned (along with the first minor number)
234 cf3e43dbSJonathan Corbet  * in @dev.  Returns zero or a negative error code.
235 cf3e43dbSJonathan Corbet  */
alloc_chrdev_region(dev_t * dev,unsigned baseminor,unsigned count,const char * name)236 1da177e4SLinus Torvalds int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
237 1da177e4SLinus Torvalds 			const char *name)
238 1da177e4SLinus Torvalds {
239 1da177e4SLinus Torvalds 	struct char_device_struct *cd;
240 1da177e4SLinus Torvalds 	cd = __register_chrdev_region(0, baseminor, count, name);
241 1da177e4SLinus Torvalds 	if (IS_ERR(cd))
242 1da177e4SLinus Torvalds 		return PTR_ERR(cd);
243 1da177e4SLinus Torvalds 	*dev = MKDEV(cd->major, cd->baseminor);
244 1da177e4SLinus Torvalds 	return 0;
245 1da177e4SLinus Torvalds }
246 1da177e4SLinus Torvalds 
247 d247e2c6SRolf Eike Beer /**
248 1905b1bfSTejun Heo  * __register_chrdev() - create and register a cdev occupying a range of minors
249 d247e2c6SRolf Eike Beer  * @major: major device number or 0 for dynamic allocation
250 1905b1bfSTejun Heo  * @baseminor: first of the requested range of minor numbers
251 1905b1bfSTejun Heo  * @count: the number of minor numbers required
252 d247e2c6SRolf Eike Beer  * @name: name of this range of devices
253 d247e2c6SRolf Eike Beer  * @fops: file operations associated with this devices
254 d247e2c6SRolf Eike Beer  *
255 d247e2c6SRolf Eike Beer  * If @major == 0 this functions will dynamically allocate a major and return
256 d247e2c6SRolf Eike Beer  * its number.
257 d247e2c6SRolf Eike Beer  *
258 d247e2c6SRolf Eike Beer  * If @major > 0 this function will attempt to reserve a device with the given
259 d247e2c6SRolf Eike Beer  * major number and will return zero on success.
260 d247e2c6SRolf Eike Beer  *
261 d247e2c6SRolf Eike Beer  * Returns a -ve errno on failure.
262 d247e2c6SRolf Eike Beer  *
263 d247e2c6SRolf Eike Beer  * The name of this device has nothing to do with the name of the device in
264 d247e2c6SRolf Eike Beer  * /dev. It only helps to keep track of the different owners of devices. If
265 d247e2c6SRolf Eike Beer  * your module name has only one type of devices it's ok to use e.g. the name
266 d247e2c6SRolf Eike Beer  * of the module here.
267 d247e2c6SRolf Eike Beer  */
__register_chrdev(unsigned int major,unsigned int baseminor,unsigned int count,const char * name,const struct file_operations * fops)268 1905b1bfSTejun Heo int __register_chrdev(unsigned int major, unsigned int baseminor,
269 1905b1bfSTejun Heo 		      unsigned int count, const char *name,
270 99ac48f5SArjan van de Ven 		      const struct file_operations *fops)
271 1da177e4SLinus Torvalds {
272 1da177e4SLinus Torvalds 	struct char_device_struct *cd;
273 1da177e4SLinus Torvalds 	struct cdev *cdev;
274 1da177e4SLinus Torvalds 	int err = -ENOMEM;
275 1da177e4SLinus Torvalds 
276 1905b1bfSTejun Heo 	cd = __register_chrdev_region(major, baseminor, count, name);
277 1da177e4SLinus Torvalds 	if (IS_ERR(cd))
278 1da177e4SLinus Torvalds 		return PTR_ERR(cd);
279 1da177e4SLinus Torvalds 
280 1da177e4SLinus Torvalds 	cdev = cdev_alloc();
281 1da177e4SLinus Torvalds 	if (!cdev)
282 1da177e4SLinus Torvalds 		goto out2;
283 1da177e4SLinus Torvalds 
284 1da177e4SLinus Torvalds 	cdev->owner = fops->owner;
285 1da177e4SLinus Torvalds 	cdev->ops = fops;
286 1da177e4SLinus Torvalds 	kobject_set_name(&cdev->kobj, "%s", name);
287 1da177e4SLinus Torvalds 
288 1905b1bfSTejun Heo 	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
289 1da177e4SLinus Torvalds 	if (err)
290 1da177e4SLinus Torvalds 		goto out;
291 1da177e4SLinus Torvalds 
292 1da177e4SLinus Torvalds 	cd->cdev = cdev;
293 1da177e4SLinus Torvalds 
294 1da177e4SLinus Torvalds 	return major ? 0 : cd->major;
295 1da177e4SLinus Torvalds out:
296 1da177e4SLinus Torvalds 	kobject_put(&cdev->kobj);
297 1da177e4SLinus Torvalds out2:
298 1905b1bfSTejun Heo 	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
299 1da177e4SLinus Torvalds 	return err;
300 1da177e4SLinus Torvalds }
301 1da177e4SLinus Torvalds 
302 cf3e43dbSJonathan Corbet /**
303 594069bcSPartha Pratim Mukherjee  * unregister_chrdev_region() - unregister a range of device numbers
304 cf3e43dbSJonathan Corbet  * @from: the first in the range of numbers to unregister
305 cf3e43dbSJonathan Corbet  * @count: the number of device numbers to unregister
306 cf3e43dbSJonathan Corbet  *
307 cf3e43dbSJonathan Corbet  * This function will unregister a range of @count device numbers,
308 cf3e43dbSJonathan Corbet  * starting with @from.  The caller should normally be the one who
309 cf3e43dbSJonathan Corbet  * allocated those numbers in the first place...
310 cf3e43dbSJonathan Corbet  */
unregister_chrdev_region(dev_t from,unsigned count)311 1da177e4SLinus Torvalds void unregister_chrdev_region(dev_t from, unsigned count)
312 1da177e4SLinus Torvalds {
313 1da177e4SLinus Torvalds 	dev_t to = from + count;
314 1da177e4SLinus Torvalds 	dev_t n, next;
315 1da177e4SLinus Torvalds 
316 1da177e4SLinus Torvalds 	for (n = from; n < to; n = next) {
317 1da177e4SLinus Torvalds 		next = MKDEV(MAJOR(n)+1, 0);
318 1da177e4SLinus Torvalds 		if (next > to)
319 1da177e4SLinus Torvalds 			next = to;
320 1da177e4SLinus Torvalds 		kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
321 1da177e4SLinus Torvalds 	}
322 1da177e4SLinus Torvalds }
323 1da177e4SLinus Torvalds 
324 1905b1bfSTejun Heo /**
325 1905b1bfSTejun Heo  * __unregister_chrdev - unregister and destroy a cdev
326 1905b1bfSTejun Heo  * @major: major device number
327 1905b1bfSTejun Heo  * @baseminor: first of the range of minor numbers
328 1905b1bfSTejun Heo  * @count: the number of minor numbers this cdev is occupying
329 1905b1bfSTejun Heo  * @name: name of this range of devices
330 1905b1bfSTejun Heo  *
331 1905b1bfSTejun Heo  * Unregister and destroy the cdev occupying the region described by
332 1905b1bfSTejun Heo  * @major, @baseminor and @count.  This function undoes what
333 1905b1bfSTejun Heo  * __register_chrdev() did.
334 1905b1bfSTejun Heo  */
__unregister_chrdev(unsigned int major,unsigned int baseminor,unsigned int count,const char * name)335 1905b1bfSTejun Heo void __unregister_chrdev(unsigned int major, unsigned int baseminor,
336 1905b1bfSTejun Heo 			 unsigned int count, const char *name)
337 1da177e4SLinus Torvalds {
338 1da177e4SLinus Torvalds 	struct char_device_struct *cd;
339 1905b1bfSTejun Heo 
340 1905b1bfSTejun Heo 	cd = __unregister_chrdev_region(major, baseminor, count);
341 1da177e4SLinus Torvalds 	if (cd && cd->cdev)
342 1da177e4SLinus Torvalds 		cdev_del(cd->cdev);
343 1da177e4SLinus Torvalds 	kfree(cd);
344 1da177e4SLinus Torvalds }
345 1da177e4SLinus Torvalds 
346 1da177e4SLinus Torvalds static DEFINE_SPINLOCK(cdev_lock);
347 1da177e4SLinus Torvalds 
cdev_get(struct cdev * p)348 1da177e4SLinus Torvalds static struct kobject *cdev_get(struct cdev *p)
349 1da177e4SLinus Torvalds {
350 1da177e4SLinus Torvalds 	struct module *owner = p->owner;
351 1da177e4SLinus Torvalds 	struct kobject *kobj;
352 1da177e4SLinus Torvalds 
353 1da177e4SLinus Torvalds 	if (owner && !try_module_get(owner))
354 1da177e4SLinus Torvalds 		return NULL;
355 68faa679SWill Deacon 	kobj = kobject_get_unless_zero(&p->kobj);
356 1da177e4SLinus Torvalds 	if (!kobj)
357 1da177e4SLinus Torvalds 		module_put(owner);
358 1da177e4SLinus Torvalds 	return kobj;
359 1da177e4SLinus Torvalds }
360 1da177e4SLinus Torvalds 
cdev_put(struct cdev * p)361 1da177e4SLinus Torvalds void cdev_put(struct cdev *p)
362 1da177e4SLinus Torvalds {
363 1da177e4SLinus Torvalds 	if (p) {
364 7da6844cSBrian King 		struct module *owner = p->owner;
365 1da177e4SLinus Torvalds 		kobject_put(&p->kobj);
366 7da6844cSBrian King 		module_put(owner);
367 1da177e4SLinus Torvalds 	}
368 1da177e4SLinus Torvalds }
369 1da177e4SLinus Torvalds 
370 1da177e4SLinus Torvalds /*
371 1da177e4SLinus Torvalds  * Called every time a character special file is opened
372 1da177e4SLinus Torvalds  */
chrdev_open(struct inode * inode,struct file * filp)373 922f9cfaSDenis Cheng static int chrdev_open(struct inode *inode, struct file *filp)
374 1da177e4SLinus Torvalds {
375 e84f9e57SAl Viro 	const struct file_operations *fops;
376 1da177e4SLinus Torvalds 	struct cdev *p;
377 1da177e4SLinus Torvalds 	struct cdev *new = NULL;
378 1da177e4SLinus Torvalds 	int ret = 0;
379 1da177e4SLinus Torvalds 
380 1da177e4SLinus Torvalds 	spin_lock(&cdev_lock);
381 1da177e4SLinus Torvalds 	p = inode->i_cdev;
382 1da177e4SLinus Torvalds 	if (!p) {
383 1da177e4SLinus Torvalds 		struct kobject *kobj;
384 1da177e4SLinus Torvalds 		int idx;
385 1da177e4SLinus Torvalds 		spin_unlock(&cdev_lock);
386 1da177e4SLinus Torvalds 		kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
387 1da177e4SLinus Torvalds 		if (!kobj)
388 1da177e4SLinus Torvalds 			return -ENXIO;
389 1da177e4SLinus Torvalds 		new = container_of(kobj, struct cdev, kobj);
390 1da177e4SLinus Torvalds 		spin_lock(&cdev_lock);
391 a30427d9SJonathan Corbet 		/* Check i_cdev again in case somebody beat us to it while
392 a30427d9SJonathan Corbet 		   we dropped the lock. */
393 1da177e4SLinus Torvalds 		p = inode->i_cdev;
394 1da177e4SLinus Torvalds 		if (!p) {
395 1da177e4SLinus Torvalds 			inode->i_cdev = p = new;
396 1da177e4SLinus Torvalds 			list_add(&inode->i_devices, &p->list);
397 1da177e4SLinus Torvalds 			new = NULL;
398 1da177e4SLinus Torvalds 		} else if (!cdev_get(p))
399 1da177e4SLinus Torvalds 			ret = -ENXIO;
400 1da177e4SLinus Torvalds 	} else if (!cdev_get(p))
401 1da177e4SLinus Torvalds 		ret = -ENXIO;
402 1da177e4SLinus Torvalds 	spin_unlock(&cdev_lock);
403 1da177e4SLinus Torvalds 	cdev_put(new);
404 1da177e4SLinus Torvalds 	if (ret)
405 1da177e4SLinus Torvalds 		return ret;
406 a518ab93SChristoph Hellwig 
407 a518ab93SChristoph Hellwig 	ret = -ENXIO;
408 e84f9e57SAl Viro 	fops = fops_get(p->ops);
409 e84f9e57SAl Viro 	if (!fops)
410 a518ab93SChristoph Hellwig 		goto out_cdev_put;
411 a518ab93SChristoph Hellwig 
412 e84f9e57SAl Viro 	replace_fops(filp, fops);
413 a518ab93SChristoph Hellwig 	if (filp->f_op->open) {
414 1da177e4SLinus Torvalds 		ret = filp->f_op->open(inode, filp);
415 1da177e4SLinus Torvalds 		if (ret)
416 a518ab93SChristoph Hellwig 			goto out_cdev_put;
417 a518ab93SChristoph Hellwig 	}
418 a518ab93SChristoph Hellwig 
419 a518ab93SChristoph Hellwig 	return 0;
420 a518ab93SChristoph Hellwig 
421 a518ab93SChristoph Hellwig  out_cdev_put:
422 1da177e4SLinus Torvalds 	cdev_put(p);
423 1da177e4SLinus Torvalds 	return ret;
424 1da177e4SLinus Torvalds }
425 1da177e4SLinus Torvalds 
cd_forget(struct inode * inode)426 1da177e4SLinus Torvalds void cd_forget(struct inode *inode)
427 1da177e4SLinus Torvalds {
428 1da177e4SLinus Torvalds 	spin_lock(&cdev_lock);
429 1da177e4SLinus Torvalds 	list_del_init(&inode->i_devices);
430 1da177e4SLinus Torvalds 	inode->i_cdev = NULL;
431 3bc52c45SDan Williams 	inode->i_mapping = &inode->i_data;
432 1da177e4SLinus Torvalds 	spin_unlock(&cdev_lock);
433 1da177e4SLinus Torvalds }
434 1da177e4SLinus Torvalds 
cdev_purge(struct cdev * cdev)435 75c96f85SAdrian Bunk static void cdev_purge(struct cdev *cdev)
436 1da177e4SLinus Torvalds {
437 1da177e4SLinus Torvalds 	spin_lock(&cdev_lock);
438 1da177e4SLinus Torvalds 	while (!list_empty(&cdev->list)) {
439 1da177e4SLinus Torvalds 		struct inode *inode;
440 1da177e4SLinus Torvalds 		inode = container_of(cdev->list.next, struct inode, i_devices);
441 1da177e4SLinus Torvalds 		list_del_init(&inode->i_devices);
442 1da177e4SLinus Torvalds 		inode->i_cdev = NULL;
443 1da177e4SLinus Torvalds 	}
444 1da177e4SLinus Torvalds 	spin_unlock(&cdev_lock);
445 1da177e4SLinus Torvalds }
446 1da177e4SLinus Torvalds 
447 1da177e4SLinus Torvalds /*
448 1da177e4SLinus Torvalds  * Dummy default file-operations: the only thing this does
449 1da177e4SLinus Torvalds  * is contain the open that then fills in the correct operations
450 1da177e4SLinus Torvalds  * depending on the special file...
451 1da177e4SLinus Torvalds  */
452 4b6f5d20SArjan van de Ven const struct file_operations def_chr_fops = {
453 1da177e4SLinus Torvalds 	.open = chrdev_open,
454 6038f373SArnd Bergmann 	.llseek = noop_llseek,
455 1da177e4SLinus Torvalds };
456 1da177e4SLinus Torvalds 
exact_match(dev_t dev,int * part,void * data)457 1da177e4SLinus Torvalds static struct kobject *exact_match(dev_t dev, int *part, void *data)
458 1da177e4SLinus Torvalds {
459 1da177e4SLinus Torvalds 	struct cdev *p = data;
460 1da177e4SLinus Torvalds 	return &p->kobj;
461 1da177e4SLinus Torvalds }
462 1da177e4SLinus Torvalds 
exact_lock(dev_t dev,void * data)463 1da177e4SLinus Torvalds static int exact_lock(dev_t dev, void *data)
464 1da177e4SLinus Torvalds {
465 1da177e4SLinus Torvalds 	struct cdev *p = data;
466 1da177e4SLinus Torvalds 	return cdev_get(p) ? 0 : -1;
467 1da177e4SLinus Torvalds }
468 1da177e4SLinus Torvalds 
469 cf3e43dbSJonathan Corbet /**
470 cf3e43dbSJonathan Corbet  * cdev_add() - add a char device to the system
471 cf3e43dbSJonathan Corbet  * @p: the cdev structure for the device
472 cf3e43dbSJonathan Corbet  * @dev: the first device number for which this device is responsible
473 cf3e43dbSJonathan Corbet  * @count: the number of consecutive minor numbers corresponding to this
474 cf3e43dbSJonathan Corbet  *         device
475 cf3e43dbSJonathan Corbet  *
476 cf3e43dbSJonathan Corbet  * cdev_add() adds the device represented by @p to the system, making it
477 cf3e43dbSJonathan Corbet  * live immediately.  A negative error code is returned on failure.
478 cf3e43dbSJonathan Corbet  */
cdev_add(struct cdev * p,dev_t dev,unsigned count)479 1da177e4SLinus Torvalds int cdev_add(struct cdev *p, dev_t dev, unsigned count)
480 1da177e4SLinus Torvalds {
481 2f0157f1SDmitry Torokhov 	int error;
482 2f0157f1SDmitry Torokhov 
483 1da177e4SLinus Torvalds 	p->dev = dev;
484 1da177e4SLinus Torvalds 	p->count = count;
485 2f0157f1SDmitry Torokhov 
486 4634c973SShang XiaoJing 	if (WARN_ON(dev == WHITEOUT_DEV)) {
487 4634c973SShang XiaoJing 		error = -EBUSY;
488 4634c973SShang XiaoJing 		goto err;
489 4634c973SShang XiaoJing 	}
490 a3c751a5SMiklos Szeredi 
491 2f0157f1SDmitry Torokhov 	error = kobj_map(cdev_map, dev, count, NULL,
492 2f0157f1SDmitry Torokhov 			 exact_match, exact_lock, p);
493 2f0157f1SDmitry Torokhov 	if (error)
494 4634c973SShang XiaoJing 		goto err;
495 2f0157f1SDmitry Torokhov 
496 2f0157f1SDmitry Torokhov 	kobject_get(p->kobj.parent);
497 2f0157f1SDmitry Torokhov 
498 2f0157f1SDmitry Torokhov 	return 0;
499 4634c973SShang XiaoJing 
500 4634c973SShang XiaoJing err:
501 4634c973SShang XiaoJing 	kfree_const(p->kobj.name);
502 4634c973SShang XiaoJing 	p->kobj.name = NULL;
503 4634c973SShang XiaoJing 	return error;
504 1da177e4SLinus Torvalds }
505 1da177e4SLinus Torvalds 
506 233ed09dSLogan Gunthorpe /**
507 233ed09dSLogan Gunthorpe  * cdev_set_parent() - set the parent kobject for a char device
508 233ed09dSLogan Gunthorpe  * @p: the cdev structure
509 233ed09dSLogan Gunthorpe  * @kobj: the kobject to take a reference to
510 233ed09dSLogan Gunthorpe  *
511 233ed09dSLogan Gunthorpe  * cdev_set_parent() sets a parent kobject which will be referenced
512 233ed09dSLogan Gunthorpe  * appropriately so the parent is not freed before the cdev. This
513 233ed09dSLogan Gunthorpe  * should be called before cdev_add.
514 233ed09dSLogan Gunthorpe  */
cdev_set_parent(struct cdev * p,struct kobject * kobj)515 233ed09dSLogan Gunthorpe void cdev_set_parent(struct cdev *p, struct kobject *kobj)
516 233ed09dSLogan Gunthorpe {
517 233ed09dSLogan Gunthorpe 	WARN_ON(!kobj->state_initialized);
518 233ed09dSLogan Gunthorpe 	p->kobj.parent = kobj;
519 233ed09dSLogan Gunthorpe }
520 233ed09dSLogan Gunthorpe 
521 233ed09dSLogan Gunthorpe /**
522 233ed09dSLogan Gunthorpe  * cdev_device_add() - add a char device and it's corresponding
523 233ed09dSLogan Gunthorpe  *	struct device, linkink
524 233ed09dSLogan Gunthorpe  * @dev: the device structure
525 233ed09dSLogan Gunthorpe  * @cdev: the cdev structure
526 233ed09dSLogan Gunthorpe  *
527 233ed09dSLogan Gunthorpe  * cdev_device_add() adds the char device represented by @cdev to the system,
528 233ed09dSLogan Gunthorpe  * just as cdev_add does. It then adds @dev to the system using device_add
529 233ed09dSLogan Gunthorpe  * The dev_t for the char device will be taken from the struct device which
530 233ed09dSLogan Gunthorpe  * needs to be initialized first. This helper function correctly takes a
531 233ed09dSLogan Gunthorpe  * reference to the parent device so the parent will not get released until
532 233ed09dSLogan Gunthorpe  * all references to the cdev are released.
533 233ed09dSLogan Gunthorpe  *
534 233ed09dSLogan Gunthorpe  * This helper uses dev->devt for the device number. If it is not set
535 233ed09dSLogan Gunthorpe  * it will not add the cdev and it will be equivalent to device_add.
536 233ed09dSLogan Gunthorpe  *
537 233ed09dSLogan Gunthorpe  * This function should be used whenever the struct cdev and the
538 233ed09dSLogan Gunthorpe  * struct device are members of the same structure whose lifetime is
539 233ed09dSLogan Gunthorpe  * managed by the struct device.
540 233ed09dSLogan Gunthorpe  *
541 233ed09dSLogan Gunthorpe  * NOTE: Callers must assume that userspace was able to open the cdev and
542 233ed09dSLogan Gunthorpe  * can call cdev fops callbacks at any time, even if this function fails.
543 233ed09dSLogan Gunthorpe  */
cdev_device_add(struct cdev * cdev,struct device * dev)544 233ed09dSLogan Gunthorpe int cdev_device_add(struct cdev *cdev, struct device *dev)
545 233ed09dSLogan Gunthorpe {
546 233ed09dSLogan Gunthorpe 	int rc = 0;
547 233ed09dSLogan Gunthorpe 
548 233ed09dSLogan Gunthorpe 	if (dev->devt) {
549 233ed09dSLogan Gunthorpe 		cdev_set_parent(cdev, &dev->kobj);
550 233ed09dSLogan Gunthorpe 
551 233ed09dSLogan Gunthorpe 		rc = cdev_add(cdev, dev->devt, 1);
552 233ed09dSLogan Gunthorpe 		if (rc)
553 233ed09dSLogan Gunthorpe 			return rc;
554 233ed09dSLogan Gunthorpe 	}
555 233ed09dSLogan Gunthorpe 
556 233ed09dSLogan Gunthorpe 	rc = device_add(dev);
557 11fa7fefSYang Yingliang 	if (rc && dev->devt)
558 233ed09dSLogan Gunthorpe 		cdev_del(cdev);
559 233ed09dSLogan Gunthorpe 
560 233ed09dSLogan Gunthorpe 	return rc;
561 233ed09dSLogan Gunthorpe }
562 233ed09dSLogan Gunthorpe 
563 233ed09dSLogan Gunthorpe /**
564 233ed09dSLogan Gunthorpe  * cdev_device_del() - inverse of cdev_device_add
565 233ed09dSLogan Gunthorpe  * @dev: the device structure
566 233ed09dSLogan Gunthorpe  * @cdev: the cdev structure
567 233ed09dSLogan Gunthorpe  *
568 233ed09dSLogan Gunthorpe  * cdev_device_del() is a helper function to call cdev_del and device_del.
569 233ed09dSLogan Gunthorpe  * It should be used whenever cdev_device_add is used.
570 233ed09dSLogan Gunthorpe  *
571 233ed09dSLogan Gunthorpe  * If dev->devt is not set it will not remove the cdev and will be equivalent
572 233ed09dSLogan Gunthorpe  * to device_del.
573 233ed09dSLogan Gunthorpe  *
574 233ed09dSLogan Gunthorpe  * NOTE: This guarantees that associated sysfs callbacks are not running
575 233ed09dSLogan Gunthorpe  * or runnable, however any cdevs already open will remain and their fops
576 233ed09dSLogan Gunthorpe  * will still be callable even after this function returns.
577 233ed09dSLogan Gunthorpe  */
cdev_device_del(struct cdev * cdev,struct device * dev)578 233ed09dSLogan Gunthorpe void cdev_device_del(struct cdev *cdev, struct device *dev)
579 233ed09dSLogan Gunthorpe {
580 233ed09dSLogan Gunthorpe 	device_del(dev);
581 233ed09dSLogan Gunthorpe 	if (dev->devt)
582 233ed09dSLogan Gunthorpe 		cdev_del(cdev);
583 233ed09dSLogan Gunthorpe }
584 233ed09dSLogan Gunthorpe 
cdev_unmap(dev_t dev,unsigned count)585 1da177e4SLinus Torvalds static void cdev_unmap(dev_t dev, unsigned count)
586 1da177e4SLinus Torvalds {
587 1da177e4SLinus Torvalds 	kobj_unmap(cdev_map, dev, count);
588 1da177e4SLinus Torvalds }
589 1da177e4SLinus Torvalds 
590 cf3e43dbSJonathan Corbet /**
591 cf3e43dbSJonathan Corbet  * cdev_del() - remove a cdev from the system
592 cf3e43dbSJonathan Corbet  * @p: the cdev structure to be removed
593 cf3e43dbSJonathan Corbet  *
594 cf3e43dbSJonathan Corbet  * cdev_del() removes @p from the system, possibly freeing the structure
595 cf3e43dbSJonathan Corbet  * itself.
596 233ed09dSLogan Gunthorpe  *
597 233ed09dSLogan Gunthorpe  * NOTE: This guarantees that cdev device will no longer be able to be
598 233ed09dSLogan Gunthorpe  * opened, however any cdevs already open will remain and their fops will
599 233ed09dSLogan Gunthorpe  * still be callable even after cdev_del returns.
600 cf3e43dbSJonathan Corbet  */
cdev_del(struct cdev * p)601 1da177e4SLinus Torvalds void cdev_del(struct cdev *p)
602 1da177e4SLinus Torvalds {
603 1da177e4SLinus Torvalds 	cdev_unmap(p->dev, p->count);
604 1da177e4SLinus Torvalds 	kobject_put(&p->kobj);
605 1da177e4SLinus Torvalds }
606 1da177e4SLinus Torvalds 
607 1da177e4SLinus Torvalds 
cdev_default_release(struct kobject * kobj)608 1da177e4SLinus Torvalds static void cdev_default_release(struct kobject *kobj)
609 1da177e4SLinus Torvalds {
610 1da177e4SLinus Torvalds 	struct cdev *p = container_of(kobj, struct cdev, kobj);
611 2f0157f1SDmitry Torokhov 	struct kobject *parent = kobj->parent;
612 2f0157f1SDmitry Torokhov 
613 1da177e4SLinus Torvalds 	cdev_purge(p);
614 2f0157f1SDmitry Torokhov 	kobject_put(parent);
615 1da177e4SLinus Torvalds }
616 1da177e4SLinus Torvalds 
cdev_dynamic_release(struct kobject * kobj)617 1da177e4SLinus Torvalds static void cdev_dynamic_release(struct kobject *kobj)
618 1da177e4SLinus Torvalds {
619 1da177e4SLinus Torvalds 	struct cdev *p = container_of(kobj, struct cdev, kobj);
620 2f0157f1SDmitry Torokhov 	struct kobject *parent = kobj->parent;
621 2f0157f1SDmitry Torokhov 
622 1da177e4SLinus Torvalds 	cdev_purge(p);
623 1da177e4SLinus Torvalds 	kfree(p);
624 2f0157f1SDmitry Torokhov 	kobject_put(parent);
625 1da177e4SLinus Torvalds }
626 1da177e4SLinus Torvalds 
627 1da177e4SLinus Torvalds static struct kobj_type ktype_cdev_default = {
628 1da177e4SLinus Torvalds 	.release	= cdev_default_release,
629 1da177e4SLinus Torvalds };
630 1da177e4SLinus Torvalds 
631 1da177e4SLinus Torvalds static struct kobj_type ktype_cdev_dynamic = {
632 1da177e4SLinus Torvalds 	.release	= cdev_dynamic_release,
633 1da177e4SLinus Torvalds };
634 1da177e4SLinus Torvalds 
635 cf3e43dbSJonathan Corbet /**
636 cf3e43dbSJonathan Corbet  * cdev_alloc() - allocate a cdev structure
637 cf3e43dbSJonathan Corbet  *
638 cf3e43dbSJonathan Corbet  * Allocates and returns a cdev structure, or NULL on failure.
639 cf3e43dbSJonathan Corbet  */
cdev_alloc(void)640 1da177e4SLinus Torvalds struct cdev *cdev_alloc(void)
641 1da177e4SLinus Torvalds {
642 11b0b5abSOliver Neukum 	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
643 1da177e4SLinus Torvalds 	if (p) {
644 1da177e4SLinus Torvalds 		INIT_LIST_HEAD(&p->list);
645 f9cb074bSGreg Kroah-Hartman 		kobject_init(&p->kobj, &ktype_cdev_dynamic);
646 1da177e4SLinus Torvalds 	}
647 1da177e4SLinus Torvalds 	return p;
648 1da177e4SLinus Torvalds }
649 1da177e4SLinus Torvalds 
650 cf3e43dbSJonathan Corbet /**
651 cf3e43dbSJonathan Corbet  * cdev_init() - initialize a cdev structure
652 cf3e43dbSJonathan Corbet  * @cdev: the structure to initialize
653 cf3e43dbSJonathan Corbet  * @fops: the file_operations for this device
654 cf3e43dbSJonathan Corbet  *
655 cf3e43dbSJonathan Corbet  * Initializes @cdev, remembering @fops, making it ready to add to the
656 cf3e43dbSJonathan Corbet  * system with cdev_add().
657 cf3e43dbSJonathan Corbet  */
cdev_init(struct cdev * cdev,const struct file_operations * fops)658 99ac48f5SArjan van de Ven void cdev_init(struct cdev *cdev, const struct file_operations *fops)
659 1da177e4SLinus Torvalds {
660 1da177e4SLinus Torvalds 	memset(cdev, 0, sizeof *cdev);
661 1da177e4SLinus Torvalds 	INIT_LIST_HEAD(&cdev->list);
662 f9cb074bSGreg Kroah-Hartman 	kobject_init(&cdev->kobj, &ktype_cdev_default);
663 1da177e4SLinus Torvalds 	cdev->ops = fops;
664 1da177e4SLinus Torvalds }
665 1da177e4SLinus Torvalds 
base_probe(dev_t dev,int * part,void * data)666 1da177e4SLinus Torvalds static struct kobject *base_probe(dev_t dev, int *part, void *data)
667 1da177e4SLinus Torvalds {
668 1da177e4SLinus Torvalds 	if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0)
669 1da177e4SLinus Torvalds 		/* Make old-style 2.4 aliases work */
670 1da177e4SLinus Torvalds 		request_module("char-major-%d", MAJOR(dev));
671 1da177e4SLinus Torvalds 	return NULL;
672 1da177e4SLinus Torvalds }
673 1da177e4SLinus Torvalds 
chrdev_init(void)674 1da177e4SLinus Torvalds void __init chrdev_init(void)
675 1da177e4SLinus Torvalds {
676 1da177e4SLinus Torvalds 	cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
677 1da177e4SLinus Torvalds }
678 1da177e4SLinus Torvalds 
679 1da177e4SLinus Torvalds 
680 1da177e4SLinus Torvalds /* Let modules do char dev stuff */
681 1da177e4SLinus Torvalds EXPORT_SYMBOL(register_chrdev_region);
682 1da177e4SLinus Torvalds EXPORT_SYMBOL(unregister_chrdev_region);
683 1da177e4SLinus Torvalds EXPORT_SYMBOL(alloc_chrdev_region);
684 1da177e4SLinus Torvalds EXPORT_SYMBOL(cdev_init);
685 1da177e4SLinus Torvalds EXPORT_SYMBOL(cdev_alloc);
686 1da177e4SLinus Torvalds EXPORT_SYMBOL(cdev_del);
687 1da177e4SLinus Torvalds EXPORT_SYMBOL(cdev_add);
688 233ed09dSLogan Gunthorpe EXPORT_SYMBOL(cdev_set_parent);
689 233ed09dSLogan Gunthorpe EXPORT_SYMBOL(cdev_device_add);
690 233ed09dSLogan Gunthorpe EXPORT_SYMBOL(cdev_device_del);
691 1905b1bfSTejun Heo EXPORT_SYMBOL(__register_chrdev);
692 1905b1bfSTejun Heo EXPORT_SYMBOL(__unregister_chrdev);
693