.. | .. |
---|
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) |
---|