diff options
Diffstat (limited to 'fs/char_dev.c')
-rw-r--r-- | fs/char_dev.c | 88 |
1 files changed, 83 insertions, 5 deletions
diff --git a/fs/char_dev.c b/fs/char_dev.c index 0009346d827f..a885f46ca001 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #ifdef CONFIG_KMOD | 24 | #ifdef CONFIG_KMOD |
25 | #include <linux/kmod.h> | 25 | #include <linux/kmod.h> |
26 | #endif | 26 | #endif |
27 | #include "internal.h" | ||
27 | 28 | ||
28 | /* | 29 | /* |
29 | * capabilities for /dev/mem, /dev/kmem and similar directly mappable character | 30 | * capabilities for /dev/mem, /dev/kmem and similar directly mappable character |
@@ -128,13 +129,31 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, | |||
128 | 129 | ||
129 | for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) | 130 | for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) |
130 | if ((*cp)->major > major || | 131 | if ((*cp)->major > major || |
131 | ((*cp)->major == major && (*cp)->baseminor >= baseminor)) | 132 | ((*cp)->major == major && |
133 | (((*cp)->baseminor >= baseminor) || | ||
134 | ((*cp)->baseminor + (*cp)->minorct > baseminor)))) | ||
132 | break; | 135 | break; |
133 | if (*cp && (*cp)->major == major && | 136 | |
134 | (*cp)->baseminor < baseminor + minorct) { | 137 | /* Check for overlapping minor ranges. */ |
135 | ret = -EBUSY; | 138 | if (*cp && (*cp)->major == major) { |
136 | goto out; | 139 | int old_min = (*cp)->baseminor; |
140 | int old_max = (*cp)->baseminor + (*cp)->minorct - 1; | ||
141 | int new_min = baseminor; | ||
142 | int new_max = baseminor + minorct - 1; | ||
143 | |||
144 | /* New driver overlaps from the left. */ | ||
145 | if (new_max >= old_min && new_max <= old_max) { | ||
146 | ret = -EBUSY; | ||
147 | goto out; | ||
148 | } | ||
149 | |||
150 | /* New driver overlaps from the right. */ | ||
151 | if (new_min <= old_max && new_min >= old_min) { | ||
152 | ret = -EBUSY; | ||
153 | goto out; | ||
154 | } | ||
137 | } | 155 | } |
156 | |||
138 | cd->next = *cp; | 157 | cd->next = *cp; |
139 | *cp = cd; | 158 | *cp = cd; |
140 | mutex_unlock(&chrdevs_lock); | 159 | mutex_unlock(&chrdevs_lock); |
@@ -165,6 +184,15 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) | |||
165 | return cd; | 184 | return cd; |
166 | } | 185 | } |
167 | 186 | ||
187 | /** | ||
188 | * register_chrdev_region() - register a range of device numbers | ||
189 | * @from: the first in the desired range of device numbers; must include | ||
190 | * the major number. | ||
191 | * @count: the number of consecutive device numbers required | ||
192 | * @name: the name of the device or driver. | ||
193 | * | ||
194 | * Return value is zero on success, a negative error code on failure. | ||
195 | */ | ||
168 | int register_chrdev_region(dev_t from, unsigned count, const char *name) | 196 | int register_chrdev_region(dev_t from, unsigned count, const char *name) |
169 | { | 197 | { |
170 | struct char_device_struct *cd; | 198 | struct char_device_struct *cd; |
@@ -190,6 +218,17 @@ fail: | |||
190 | return PTR_ERR(cd); | 218 | return PTR_ERR(cd); |
191 | } | 219 | } |
192 | 220 | ||
221 | /** | ||
222 | * alloc_chrdev_region() - register a range of char device numbers | ||
223 | * @dev: output parameter for first assigned number | ||
224 | * @baseminor: first of the requested range of minor numbers | ||
225 | * @count: the number of minor numbers required | ||
226 | * @name: the name of the associated device or driver | ||
227 | * | ||
228 | * Allocates a range of char device numbers. The major number will be | ||
229 | * chosen dynamically, and returned (along with the first minor number) | ||
230 | * in @dev. Returns zero or a negative error code. | ||
231 | */ | ||
193 | int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, | 232 | int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, |
194 | const char *name) | 233 | const char *name) |
195 | { | 234 | { |
@@ -259,6 +298,15 @@ out2: | |||
259 | return err; | 298 | return err; |
260 | } | 299 | } |
261 | 300 | ||
301 | /** | ||
302 | * unregister_chrdev_region() - return a range of device numbers | ||
303 | * @from: the first in the range of numbers to unregister | ||
304 | * @count: the number of device numbers to unregister | ||
305 | * | ||
306 | * This function will unregister a range of @count device numbers, | ||
307 | * starting with @from. The caller should normally be the one who | ||
308 | * allocated those numbers in the first place... | ||
309 | */ | ||
262 | void unregister_chrdev_region(dev_t from, unsigned count) | 310 | void unregister_chrdev_region(dev_t from, unsigned count) |
263 | { | 311 | { |
264 | dev_t to = from + count; | 312 | dev_t to = from + count; |
@@ -396,6 +444,16 @@ static int exact_lock(dev_t dev, void *data) | |||
396 | return cdev_get(p) ? 0 : -1; | 444 | return cdev_get(p) ? 0 : -1; |
397 | } | 445 | } |
398 | 446 | ||
447 | /** | ||
448 | * cdev_add() - add a char device to the system | ||
449 | * @p: the cdev structure for the device | ||
450 | * @dev: the first device number for which this device is responsible | ||
451 | * @count: the number of consecutive minor numbers corresponding to this | ||
452 | * device | ||
453 | * | ||
454 | * cdev_add() adds the device represented by @p to the system, making it | ||
455 | * live immediately. A negative error code is returned on failure. | ||
456 | */ | ||
399 | int cdev_add(struct cdev *p, dev_t dev, unsigned count) | 457 | int cdev_add(struct cdev *p, dev_t dev, unsigned count) |
400 | { | 458 | { |
401 | p->dev = dev; | 459 | p->dev = dev; |
@@ -408,6 +466,13 @@ static void cdev_unmap(dev_t dev, unsigned count) | |||
408 | kobj_unmap(cdev_map, dev, count); | 466 | kobj_unmap(cdev_map, dev, count); |
409 | } | 467 | } |
410 | 468 | ||
469 | /** | ||
470 | * cdev_del() - remove a cdev from the system | ||
471 | * @p: the cdev structure to be removed | ||
472 | * | ||
473 | * cdev_del() removes @p from the system, possibly freeing the structure | ||
474 | * itself. | ||
475 | */ | ||
411 | void cdev_del(struct cdev *p) | 476 | void cdev_del(struct cdev *p) |
412 | { | 477 | { |
413 | cdev_unmap(p->dev, p->count); | 478 | cdev_unmap(p->dev, p->count); |
@@ -436,6 +501,11 @@ static struct kobj_type ktype_cdev_dynamic = { | |||
436 | .release = cdev_dynamic_release, | 501 | .release = cdev_dynamic_release, |
437 | }; | 502 | }; |
438 | 503 | ||
504 | /** | ||
505 | * cdev_alloc() - allocate a cdev structure | ||
506 | * | ||
507 | * Allocates and returns a cdev structure, or NULL on failure. | ||
508 | */ | ||
439 | struct cdev *cdev_alloc(void) | 509 | struct cdev *cdev_alloc(void) |
440 | { | 510 | { |
441 | struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); | 511 | struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); |
@@ -447,6 +517,14 @@ struct cdev *cdev_alloc(void) | |||
447 | return p; | 517 | return p; |
448 | } | 518 | } |
449 | 519 | ||
520 | /** | ||
521 | * cdev_init() - initialize a cdev structure | ||
522 | * @cdev: the structure to initialize | ||
523 | * @fops: the file_operations for this device | ||
524 | * | ||
525 | * Initializes @cdev, remembering @fops, making it ready to add to the | ||
526 | * system with cdev_add(). | ||
527 | */ | ||
450 | void cdev_init(struct cdev *cdev, const struct file_operations *fops) | 528 | void cdev_init(struct cdev *cdev, const struct file_operations *fops) |
451 | { | 529 | { |
452 | memset(cdev, 0, sizeof *cdev); | 530 | memset(cdev, 0, sizeof *cdev); |