subr_devsw.c revision 1.6 1 /* $NetBSD: subr_devsw.c,v 1.6 2003/05/16 14:25:03 itojun Exp $ */
2 /*-
3 * Copyright (c) 2001,2002 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by MAEKAWA Masahide <gehenna (at) NetBSD.org>.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the NetBSD
20 * Foundation, Inc. and its contributors.
21 * 4. Neither the name of The NetBSD Foundation nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 /*
39 * New device switch framework is developing.
40 * So debug options are always turned on.
41 */
42 #ifndef DEVSW_DEBUG
43 #define DEVSW_DEBUG
44 #endif /* DEVSW_DEBUG */
45
46 #include <sys/param.h>
47 #include <sys/conf.h>
48 #include <sys/malloc.h>
49 #include <sys/systm.h>
50
51 #ifdef DEVSW_DEBUG
52 #define DPRINTF(x) printf x
53 #else /* DEVSW_DEBUG */
54 #define DPRINTF(x)
55 #endif /* DEVSW_DEBUG */
56
57 #define MAXDEVSW 4096 /* the maximum of major device number */
58 #define BDEVSW_SIZE (sizeof(struct bdevsw *))
59 #define CDEVSW_SIZE (sizeof(struct cdevsw *))
60 #define DEVSWCONV_SIZE (sizeof(struct devsw_conv))
61
62 extern const struct bdevsw **bdevsw, *bdevsw0[];
63 extern const struct cdevsw **cdevsw, *cdevsw0[];
64 extern struct devsw_conv *devsw_conv, devsw_conv0[];
65 extern const int sys_bdevsws, sys_cdevsws;
66 extern int max_bdevsws, max_cdevsws, max_devsw_convs;
67
68 static int bdevsw_attach(const char *, const struct bdevsw *, int *);
69 static int cdevsw_attach(const char *, const struct cdevsw *, int *);
70
71 int
72 devsw_attach(const char *devname, const struct bdevsw *bdev, int *bmajor,
73 const struct cdevsw *cdev, int *cmajor)
74 {
75 struct devsw_conv *conv;
76 char *name;
77 int error, i;
78
79 if (devname == NULL || cdev == NULL)
80 return (EINVAL);
81
82 for (i = 0 ; i < max_devsw_convs ; i++) {
83 conv = &devsw_conv[i];
84 if (conv->d_name == NULL || strcmp(devname, conv->d_name) != 0)
85 continue;
86
87 if (*bmajor < 0)
88 *bmajor = conv->d_bmajor;
89 if (*cmajor < 0)
90 *cmajor = conv->d_cmajor;
91
92 if (*bmajor != conv->d_bmajor || *cmajor != conv->d_cmajor)
93 return (EINVAL);
94 if ((*bmajor >= 0 && bdev == NULL) || *cmajor < 0)
95 return (EINVAL);
96
97 if ((*bmajor >= 0 && bdevsw[*bmajor] != NULL) ||
98 cdevsw[*cmajor] != NULL)
99 return (EEXIST);
100
101 if (bdev != NULL)
102 bdevsw[*bmajor] = bdev;
103 cdevsw[*cmajor] = cdev;
104
105 return (0);
106 }
107
108 error = bdevsw_attach(devname, bdev, bmajor);
109 if (error != 0)
110 return (error);
111 error = cdevsw_attach(devname, cdev, cmajor);
112 if (error != 0) {
113 devsw_detach(bdev, NULL);
114 return (error);
115 }
116
117 for (i = 0 ; i < max_devsw_convs ; i++) {
118 if (devsw_conv[i].d_name == NULL)
119 break;
120 }
121 if (i == max_devsw_convs) {
122 struct devsw_conv *newptr;
123 int old, new;
124
125 old = max_devsw_convs;
126 new = old + 1;
127
128 newptr = malloc(new * DEVSWCONV_SIZE, M_DEVBUF, M_NOWAIT);
129 if (newptr == NULL) {
130 devsw_detach(bdev, cdev);
131 return (ENOMEM);
132 }
133 newptr[old].d_name = NULL;
134 newptr[old].d_bmajor = -1;
135 newptr[old].d_cmajor = -1;
136 memcpy(newptr, devsw_conv, old * DEVSWCONV_SIZE);
137 if (devsw_conv != devsw_conv0)
138 free(devsw_conv, M_DEVBUF);
139 devsw_conv = newptr;
140 max_devsw_convs = new;
141 }
142
143 i = strlen(devname) + 1;
144 name = malloc(i, M_DEVBUF, M_NOWAIT);
145 if (name == NULL) {
146 devsw_detach(bdev, cdev);
147 return (ENOMEM);
148 }
149 strlcpy(name, devname, i);
150
151 devsw_conv[i].d_name = name;
152 devsw_conv[i].d_bmajor = *bmajor;
153 devsw_conv[i].d_cmajor = *cmajor;
154
155 return (0);
156 }
157
158 static int
159 bdevsw_attach(const char *devname, const struct bdevsw *devsw, int *devmajor)
160 {
161 int bmajor, i;
162
163 if (devsw == NULL)
164 return (0);
165
166 if (*devmajor < 0) {
167 for (bmajor = sys_bdevsws ; bmajor < max_bdevsws ; bmajor++) {
168 if (bdevsw[bmajor] != NULL)
169 continue;
170 for (i = 0 ; i < max_devsw_convs ; i++) {
171 if (devsw_conv[i].d_bmajor == bmajor)
172 break;
173 }
174 if (i != max_devsw_convs)
175 continue;
176 break;
177 }
178 *devmajor = bmajor;
179 }
180 if (*devmajor >= MAXDEVSW) {
181 #ifdef DEVSW_DEBUG
182 panic("bdevsw_attach: block majors exhausted");
183 #endif /* DEVSW_DEBUG */
184 return (ENOMEM);
185 }
186
187 if (*devmajor >= max_bdevsws) {
188 const struct bdevsw **newptr;
189 int old, new;
190
191 old = max_bdevsws;
192 new = *devmajor + 1;
193
194 newptr = malloc(new * BDEVSW_SIZE, M_DEVBUF, M_NOWAIT);
195 if (newptr == NULL)
196 return (ENOMEM);
197 memset(newptr + old, 0, (new - old) * BDEVSW_SIZE);
198 if (old != 0) {
199 memcpy(newptr, bdevsw, old * BDEVSW_SIZE);
200 if (bdevsw != bdevsw0)
201 free(bdevsw, M_DEVBUF);
202 }
203 bdevsw = newptr;
204 max_bdevsws = new;
205 }
206
207 if (bdevsw[*devmajor] != NULL)
208 return (EEXIST);
209
210 bdevsw[*devmajor] = devsw;
211
212 return (0);
213 }
214
215 static int
216 cdevsw_attach(const char *devname, const struct cdevsw *devsw, int *devmajor)
217 {
218 int cmajor, i;
219
220 if (*devmajor < 0) {
221 for (cmajor = sys_cdevsws ; cmajor < max_cdevsws ; cmajor++) {
222 if (cdevsw[cmajor] != NULL)
223 continue;
224 for (i = 0 ; i < max_devsw_convs ; i++) {
225 if (devsw_conv[i].d_cmajor == cmajor)
226 break;
227 }
228 if (i != max_devsw_convs)
229 continue;
230 break;
231 }
232 *devmajor = cmajor;
233 }
234 if (*devmajor >= MAXDEVSW) {
235 #ifdef DEVSW_DEBUG
236 panic("cdevsw_attach: character majors exhausted");
237 #endif /* DEVSW_DEBUG */
238 return (ENOMEM);
239 }
240
241 if (*devmajor >= max_cdevsws) {
242 const struct cdevsw **newptr;
243 int old, new;
244
245 old = max_cdevsws;
246 new = *devmajor + 1;
247
248 newptr = malloc(new * CDEVSW_SIZE, M_DEVBUF, M_NOWAIT);
249 if (newptr == NULL)
250 return (ENOMEM);
251 memset(newptr + old, 0, (new - old) * CDEVSW_SIZE);
252 if (old != 0) {
253 memcpy(newptr, cdevsw, old * CDEVSW_SIZE);
254 if (cdevsw != cdevsw0)
255 free(cdevsw, M_DEVBUF);
256 }
257 cdevsw = newptr;
258 max_cdevsws = new;
259 }
260
261 if (cdevsw[*devmajor] != NULL)
262 return (EEXIST);
263
264 cdevsw[*devmajor] = devsw;
265
266 return (0);
267 }
268
269 void
270 devsw_detach(const struct bdevsw *bdev, const struct cdevsw *cdev)
271 {
272 int i;
273
274 if (bdev != NULL) {
275 for (i = 0 ; i < max_bdevsws ; i++) {
276 if (bdevsw[i] != bdev)
277 continue;
278 bdevsw[i] = NULL;
279 break;
280 }
281 }
282 if (cdev != NULL) {
283 for (i = 0 ; i < max_cdevsws ; i++) {
284 if (cdevsw[i] != cdev)
285 continue;
286 cdevsw[i] = NULL;
287 break;
288 }
289 }
290 }
291
292 const struct bdevsw *
293 bdevsw_lookup(dev_t dev)
294 {
295 int bmajor;
296
297 if (dev == NODEV)
298 return (NULL);
299 bmajor = major(dev);
300 if (bmajor < 0 || bmajor >= max_bdevsws)
301 return (NULL);
302
303 return (bdevsw[bmajor]);
304 }
305
306 const struct cdevsw *
307 cdevsw_lookup(dev_t dev)
308 {
309 int cmajor;
310
311 if (dev == NODEV)
312 return (NULL);
313 cmajor = major(dev);
314 if (cmajor < 0 || cmajor >= max_cdevsws)
315 return (NULL);
316
317 return (cdevsw[cmajor]);
318 }
319
320 int
321 bdevsw_lookup_major(const struct bdevsw *bdev)
322 {
323 int bmajor;
324
325 for (bmajor = 0 ; bmajor < max_bdevsws ; bmajor++) {
326 if (bdevsw[bmajor] == bdev)
327 return (bmajor);
328 }
329
330 return (-1);
331 }
332
333 int
334 cdevsw_lookup_major(const struct cdevsw *cdev)
335 {
336 int cmajor;
337
338 for (cmajor = 0 ; cmajor < max_cdevsws ; cmajor++) {
339 if (cdevsw[cmajor] == cdev)
340 return (cmajor);
341 }
342
343 return (-1);
344 }
345
346 /*
347 * Convert from block major number to name.
348 */
349 const char *
350 devsw_blk2name(int bmajor)
351 {
352 int cmajor, i;
353
354 if (bmajor < 0 || bmajor >= max_bdevsws || bdevsw[bmajor] == NULL)
355 return (NULL);
356
357 for (i = 0 ; i < max_devsw_convs ; i++) {
358 if (devsw_conv[i].d_bmajor != bmajor)
359 continue;
360 cmajor = devsw_conv[i].d_cmajor;
361 if (cmajor < 0 || cmajor >= max_cdevsws ||
362 cdevsw[cmajor] == NULL)
363 return (NULL);
364 return (devsw_conv[i].d_name);
365 }
366
367 return (NULL);
368 }
369
370 /*
371 * Convert from device name to block major number.
372 */
373 int
374 devsw_name2blk(const char *name, char *devname, size_t devnamelen)
375 {
376 struct devsw_conv *conv;
377 int bmajor, i;
378
379 if (name == NULL)
380 return (-1);
381
382 for (i = 0 ; i < max_devsw_convs ; i++) {
383 size_t len;
384
385 conv = &devsw_conv[i];
386 if (conv->d_name == NULL)
387 continue;
388 len = strlen(conv->d_name);
389 if (strncmp(conv->d_name, name, len) != 0)
390 continue;
391 if (*(name +len) && !isdigit(*(name + len)))
392 continue;
393 bmajor = conv->d_bmajor;
394 if (bmajor < 0 || bmajor >= max_bdevsws ||
395 bdevsw[bmajor] == NULL)
396 break;
397 if (devname != NULL) {
398 #ifdef DEVSW_DEBUG
399 if (strlen(conv->d_name) >= devnamelen)
400 printf("devsw_name2blk: too short buffer");
401 #endif /* DEVSW_DEBUG */
402 strncpy(devname, conv->d_name, devnamelen);
403 devname[devnamelen - 1] = '\0';
404 }
405 return (bmajor);
406 }
407
408 return (-1);
409 }
410
411 /*
412 * Convert from character dev_t to block dev_t.
413 */
414 dev_t
415 devsw_chr2blk(dev_t cdev)
416 {
417 int bmajor, cmajor, i;
418
419 if (cdevsw_lookup(cdev) == NULL)
420 return (NODEV);
421
422 cmajor = major(cdev);
423
424 for (i = 0 ; i < max_devsw_convs ; i++) {
425 if (devsw_conv[i].d_cmajor != cmajor)
426 continue;
427 bmajor = devsw_conv[i].d_bmajor;
428 if (bmajor < 0 || bmajor >= max_bdevsws ||
429 bdevsw[bmajor] == NULL)
430 return (NODEV);
431 return (makedev(bmajor, minor(cdev)));
432 }
433
434 return (NODEV);
435 }
436
437 /*
438 * Convert from block dev_t to character dev_t.
439 */
440 dev_t
441 devsw_blk2chr(dev_t bdev)
442 {
443 int bmajor, cmajor, i;
444
445 if (bdevsw_lookup(bdev) == NULL)
446 return (NODEV);
447
448 bmajor = major(bdev);
449
450 for (i = 0 ; i < max_devsw_convs ; i++) {
451 if (devsw_conv[i].d_bmajor != bmajor)
452 continue;
453 cmajor = devsw_conv[i].d_cmajor;
454 if (cmajor < 0 || cmajor >= max_cdevsws ||
455 cdevsw[cmajor] == NULL)
456 return (NODEV);
457 return (makedev(cmajor, minor(bdev)));
458 }
459
460 return (NODEV);
461 }
462