| .. | .. |
|---|
| 88 | 88 | /* |
|---|
| 89 | 89 | * Register a single major with a specified minor range. |
|---|
| 90 | 90 | * |
|---|
| 91 | | - * If major == 0 this functions will dynamically allocate a major and return |
|---|
| 92 | | - * its number. |
|---|
| 91 | + * If major == 0 this function will dynamically allocate an unused major. |
|---|
| 92 | + * If major > 0 this function will attempt to reserve the range of minors |
|---|
| 93 | + * with given major. |
|---|
| 93 | 94 | * |
|---|
| 94 | | - * If major > 0 this function will attempt to reserve the passed range of |
|---|
| 95 | | - * minors and will return zero on success. |
|---|
| 96 | | - * |
|---|
| 97 | | - * Returns a -ve errno on failure. |
|---|
| 98 | 95 | */ |
|---|
| 99 | 96 | static struct char_device_struct * |
|---|
| 100 | 97 | __register_chrdev_region(unsigned int major, unsigned int baseminor, |
|---|
| 101 | 98 | int minorct, const char *name) |
|---|
| 102 | 99 | { |
|---|
| 103 | | - struct char_device_struct *cd, **cp; |
|---|
| 104 | | - int ret = 0; |
|---|
| 100 | + struct char_device_struct *cd, *curr, *prev = NULL; |
|---|
| 101 | + int ret; |
|---|
| 105 | 102 | int i; |
|---|
| 103 | + |
|---|
| 104 | + if (major >= CHRDEV_MAJOR_MAX) { |
|---|
| 105 | + pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n", |
|---|
| 106 | + name, major, CHRDEV_MAJOR_MAX-1); |
|---|
| 107 | + return ERR_PTR(-EINVAL); |
|---|
| 108 | + } |
|---|
| 109 | + |
|---|
| 110 | + if (minorct > MINORMASK + 1 - baseminor) { |
|---|
| 111 | + pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n", |
|---|
| 112 | + name, baseminor, baseminor + minorct - 1, 0, MINORMASK); |
|---|
| 113 | + return ERR_PTR(-EINVAL); |
|---|
| 114 | + } |
|---|
| 106 | 115 | |
|---|
| 107 | 116 | cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); |
|---|
| 108 | 117 | if (cd == NULL) |
|---|
| .. | .. |
|---|
| 120 | 129 | major = ret; |
|---|
| 121 | 130 | } |
|---|
| 122 | 131 | |
|---|
| 123 | | - if (major >= CHRDEV_MAJOR_MAX) { |
|---|
| 124 | | - pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n", |
|---|
| 125 | | - name, major, CHRDEV_MAJOR_MAX-1); |
|---|
| 126 | | - ret = -EINVAL; |
|---|
| 132 | + ret = -EBUSY; |
|---|
| 133 | + i = major_to_index(major); |
|---|
| 134 | + for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) { |
|---|
| 135 | + if (curr->major < major) |
|---|
| 136 | + continue; |
|---|
| 137 | + |
|---|
| 138 | + if (curr->major > major) |
|---|
| 139 | + break; |
|---|
| 140 | + |
|---|
| 141 | + if (curr->baseminor + curr->minorct <= baseminor) |
|---|
| 142 | + continue; |
|---|
| 143 | + |
|---|
| 144 | + if (curr->baseminor >= baseminor + minorct) |
|---|
| 145 | + break; |
|---|
| 146 | + |
|---|
| 127 | 147 | goto out; |
|---|
| 128 | 148 | } |
|---|
| 129 | 149 | |
|---|
| .. | .. |
|---|
| 132 | 152 | cd->minorct = minorct; |
|---|
| 133 | 153 | strlcpy(cd->name, name, sizeof(cd->name)); |
|---|
| 134 | 154 | |
|---|
| 135 | | - i = major_to_index(major); |
|---|
| 136 | | - |
|---|
| 137 | | - for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) |
|---|
| 138 | | - if ((*cp)->major > major || |
|---|
| 139 | | - ((*cp)->major == major && |
|---|
| 140 | | - (((*cp)->baseminor >= baseminor) || |
|---|
| 141 | | - ((*cp)->baseminor + (*cp)->minorct > baseminor)))) |
|---|
| 142 | | - break; |
|---|
| 143 | | - |
|---|
| 144 | | - /* Check for overlapping minor ranges. */ |
|---|
| 145 | | - if (*cp && (*cp)->major == major) { |
|---|
| 146 | | - int old_min = (*cp)->baseminor; |
|---|
| 147 | | - int old_max = (*cp)->baseminor + (*cp)->minorct - 1; |
|---|
| 148 | | - int new_min = baseminor; |
|---|
| 149 | | - int new_max = baseminor + minorct - 1; |
|---|
| 150 | | - |
|---|
| 151 | | - /* New driver overlaps from the left. */ |
|---|
| 152 | | - if (new_max >= old_min && new_max <= old_max) { |
|---|
| 153 | | - ret = -EBUSY; |
|---|
| 154 | | - goto out; |
|---|
| 155 | | - } |
|---|
| 156 | | - |
|---|
| 157 | | - /* New driver overlaps from the right. */ |
|---|
| 158 | | - if (new_min <= old_max && new_min >= old_min) { |
|---|
| 159 | | - ret = -EBUSY; |
|---|
| 160 | | - goto out; |
|---|
| 161 | | - } |
|---|
| 162 | | - |
|---|
| 163 | | - if (new_min < old_min && new_max > old_max) { |
|---|
| 164 | | - ret = -EBUSY; |
|---|
| 165 | | - goto out; |
|---|
| 166 | | - } |
|---|
| 167 | | - |
|---|
| 155 | + if (!prev) { |
|---|
| 156 | + cd->next = curr; |
|---|
| 157 | + chrdevs[i] = cd; |
|---|
| 158 | + } else { |
|---|
| 159 | + cd->next = prev->next; |
|---|
| 160 | + prev->next = cd; |
|---|
| 168 | 161 | } |
|---|
| 169 | 162 | |
|---|
| 170 | | - cd->next = *cp; |
|---|
| 171 | | - *cp = cd; |
|---|
| 172 | 163 | mutex_unlock(&chrdevs_lock); |
|---|
| 173 | 164 | return cd; |
|---|
| 174 | 165 | out: |
|---|
| .. | .. |
|---|
| 492 | 483 | p->dev = dev; |
|---|
| 493 | 484 | p->count = count; |
|---|
| 494 | 485 | |
|---|
| 486 | + if (WARN_ON(dev == WHITEOUT_DEV)) |
|---|
| 487 | + return -EBUSY; |
|---|
| 488 | + |
|---|
| 495 | 489 | error = kobj_map(cdev_map, dev, count, NULL, |
|---|
| 496 | 490 | exact_match, exact_lock, p); |
|---|
| 497 | 491 | if (error) |
|---|