xref: /netbsd-src/sys/kern/subr_devsw.c (revision ce2c90c7c172d95d2402a5b3d96d8f8e6d138a21)
1 /*	$NetBSD: subr_devsw.c,v 1.9 2006/10/12 01:32:18 christos 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@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 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: subr_devsw.c,v 1.9 2006/10/12 01:32:18 christos Exp $");
40 
41 /*
42  * New device switch framework is developing.
43  * So debug options are always turned on.
44  */
45 #ifndef DEVSW_DEBUG
46 #define	DEVSW_DEBUG
47 #endif /* DEVSW_DEBUG */
48 
49 #include <sys/param.h>
50 #include <sys/conf.h>
51 #include <sys/malloc.h>
52 #include <sys/systm.h>
53 
54 #ifdef DEVSW_DEBUG
55 #define	DPRINTF(x)	printf x
56 #else /* DEVSW_DEBUG */
57 #define	DPRINTF(x)
58 #endif /* DEVSW_DEBUG */
59 
60 #define	MAXDEVSW	4096	/* the maximum of major device number */
61 #define	BDEVSW_SIZE	(sizeof(struct bdevsw *))
62 #define	CDEVSW_SIZE	(sizeof(struct cdevsw *))
63 #define	DEVSWCONV_SIZE	(sizeof(struct devsw_conv))
64 
65 extern const struct bdevsw **bdevsw, *bdevsw0[];
66 extern const struct cdevsw **cdevsw, *cdevsw0[];
67 extern struct devsw_conv *devsw_conv, devsw_conv0[];
68 extern const int sys_bdevsws, sys_cdevsws;
69 extern int max_bdevsws, max_cdevsws, max_devsw_convs;
70 
71 static int bdevsw_attach(const char *, const struct bdevsw *, int *);
72 static int cdevsw_attach(const char *, const struct cdevsw *, int *);
73 
74 int
75 devsw_attach(const char *devname, const struct bdevsw *bdev, int *bmajor,
76 	     const struct cdevsw *cdev, int *cmajor)
77 {
78 	struct devsw_conv *conv;
79 	char *name;
80 	int error, i;
81 
82 	if (devname == NULL || cdev == NULL)
83 		return (EINVAL);
84 
85 	for (i = 0 ; i < max_devsw_convs ; i++) {
86 		conv = &devsw_conv[i];
87 		if (conv->d_name == NULL || strcmp(devname, conv->d_name) != 0)
88 			continue;
89 
90 		if (*bmajor < 0)
91 			*bmajor = conv->d_bmajor;
92 		if (*cmajor < 0)
93 			*cmajor = conv->d_cmajor;
94 
95 		if (*bmajor != conv->d_bmajor || *cmajor != conv->d_cmajor)
96 			return (EINVAL);
97 		if ((*bmajor >= 0 && bdev == NULL) || *cmajor < 0)
98 			return (EINVAL);
99 
100 		if ((*bmajor >= 0 && bdevsw[*bmajor] != NULL) ||
101 		    cdevsw[*cmajor] != NULL)
102 			return (EEXIST);
103 
104 		if (bdev != NULL)
105 			bdevsw[*bmajor] = bdev;
106 		cdevsw[*cmajor] = cdev;
107 
108 		return (0);
109 	}
110 
111 	error = bdevsw_attach(devname, bdev, bmajor);
112 	if (error != 0)
113 		return (error);
114 	error = cdevsw_attach(devname, cdev, cmajor);
115 	if (error != 0) {
116 		devsw_detach(bdev, NULL);
117 		return (error);
118 	}
119 
120 	for (i = 0 ; i < max_devsw_convs ; i++) {
121 		if (devsw_conv[i].d_name == NULL)
122 			break;
123 	}
124 	if (i == max_devsw_convs) {
125 		struct devsw_conv *newptr;
126 		int old, new;
127 
128 		old = max_devsw_convs;
129 		new = old + 1;
130 
131 		newptr = malloc(new * DEVSWCONV_SIZE, M_DEVBUF, M_NOWAIT);
132 		if (newptr == NULL) {
133 			devsw_detach(bdev, cdev);
134 			return (ENOMEM);
135 		}
136 		newptr[old].d_name = NULL;
137 		newptr[old].d_bmajor = -1;
138 		newptr[old].d_cmajor = -1;
139 		memcpy(newptr, devsw_conv, old * DEVSWCONV_SIZE);
140 		if (devsw_conv != devsw_conv0)
141 			free(devsw_conv, M_DEVBUF);
142 		devsw_conv = newptr;
143 		max_devsw_convs = new;
144 	}
145 
146 	i = strlen(devname) + 1;
147 	name = malloc(i, M_DEVBUF, M_NOWAIT);
148 	if (name == NULL) {
149 		devsw_detach(bdev, cdev);
150 		return (ENOMEM);
151 	}
152 	strlcpy(name, devname, i);
153 
154 	devsw_conv[i].d_name = name;
155 	devsw_conv[i].d_bmajor = *bmajor;
156 	devsw_conv[i].d_cmajor = *cmajor;
157 
158 	return (0);
159 }
160 
161 static int
162 bdevsw_attach(const char *devname __unused, const struct bdevsw *devsw,
163     int *devmajor)
164 {
165 	int bmajor, i;
166 
167 	if (devsw == NULL)
168 		return (0);
169 
170 	if (*devmajor < 0) {
171 		for (bmajor = sys_bdevsws ; bmajor < max_bdevsws ; bmajor++) {
172 			if (bdevsw[bmajor] != NULL)
173 				continue;
174 			for (i = 0 ; i < max_devsw_convs ; i++) {
175 				if (devsw_conv[i].d_bmajor == bmajor)
176 					break;
177 			}
178 			if (i != max_devsw_convs)
179 				continue;
180 			break;
181 		}
182 		*devmajor = bmajor;
183 	}
184 	if (*devmajor >= MAXDEVSW) {
185 #ifdef DEVSW_DEBUG
186 		panic("bdevsw_attach: block majors exhausted");
187 #endif /* DEVSW_DEBUG */
188 		return (ENOMEM);
189 	}
190 
191 	if (*devmajor >= max_bdevsws) {
192 		const struct bdevsw **newptr;
193 		int old, new;
194 
195 		old = max_bdevsws;
196 		new = *devmajor + 1;
197 
198 		newptr = malloc(new * BDEVSW_SIZE, M_DEVBUF, M_NOWAIT);
199 		if (newptr == NULL)
200 			return (ENOMEM);
201 		memset(newptr + old, 0, (new - old) * BDEVSW_SIZE);
202 		if (old != 0) {
203 			memcpy(newptr, bdevsw, old * BDEVSW_SIZE);
204 			if (bdevsw != bdevsw0)
205 				free(bdevsw, M_DEVBUF);
206 		}
207 		bdevsw = newptr;
208 		max_bdevsws = new;
209 	}
210 
211 	if (bdevsw[*devmajor] != NULL)
212 		return (EEXIST);
213 
214 	bdevsw[*devmajor] = devsw;
215 
216 	return (0);
217 }
218 
219 static int
220 cdevsw_attach(const char *devname __unused, const struct cdevsw *devsw,
221     int *devmajor)
222 {
223 	int cmajor, i;
224 
225 	if (*devmajor < 0) {
226 		for (cmajor = sys_cdevsws ; cmajor < max_cdevsws ; cmajor++) {
227 			if (cdevsw[cmajor] != NULL)
228 				continue;
229 			for (i = 0 ; i < max_devsw_convs ; i++) {
230 				if (devsw_conv[i].d_cmajor == cmajor)
231 					break;
232 			}
233 			if (i != max_devsw_convs)
234 				continue;
235 			break;
236 		}
237 		*devmajor = cmajor;
238 	}
239 	if (*devmajor >= MAXDEVSW) {
240 #ifdef DEVSW_DEBUG
241 		panic("cdevsw_attach: character majors exhausted");
242 #endif /* DEVSW_DEBUG */
243 		return (ENOMEM);
244 	}
245 
246 	if (*devmajor >= max_cdevsws) {
247 		const struct cdevsw **newptr;
248 		int old, new;
249 
250 		old = max_cdevsws;
251 		new = *devmajor + 1;
252 
253 		newptr = malloc(new * CDEVSW_SIZE, M_DEVBUF, M_NOWAIT);
254 		if (newptr == NULL)
255 			return (ENOMEM);
256 		memset(newptr + old, 0, (new - old) * CDEVSW_SIZE);
257 		if (old != 0) {
258 			memcpy(newptr, cdevsw, old * CDEVSW_SIZE);
259 			if (cdevsw != cdevsw0)
260 				free(cdevsw, M_DEVBUF);
261 		}
262 		cdevsw = newptr;
263 		max_cdevsws = new;
264 	}
265 
266 	if (cdevsw[*devmajor] != NULL)
267 		return (EEXIST);
268 
269 	cdevsw[*devmajor] = devsw;
270 
271 	return (0);
272 }
273 
274 void
275 devsw_detach(const struct bdevsw *bdev, const struct cdevsw *cdev)
276 {
277 	int i;
278 
279 	if (bdev != NULL) {
280 		for (i = 0 ; i < max_bdevsws ; i++) {
281 			if (bdevsw[i] != bdev)
282 				continue;
283 			bdevsw[i] = NULL;
284 			break;
285 		}
286 	}
287 	if (cdev != NULL) {
288 		for (i = 0 ; i < max_cdevsws ; i++) {
289 			if (cdevsw[i] != cdev)
290 				continue;
291 			cdevsw[i] = NULL;
292 			break;
293 		}
294 	}
295 }
296 
297 const struct bdevsw *
298 bdevsw_lookup(dev_t dev)
299 {
300 	int bmajor;
301 
302 	if (dev == NODEV)
303 		return (NULL);
304 	bmajor = major(dev);
305 	if (bmajor < 0 || bmajor >= max_bdevsws)
306 		return (NULL);
307 
308 	return (bdevsw[bmajor]);
309 }
310 
311 const struct cdevsw *
312 cdevsw_lookup(dev_t dev)
313 {
314 	int cmajor;
315 
316 	if (dev == NODEV)
317 		return (NULL);
318 	cmajor = major(dev);
319 	if (cmajor < 0 || cmajor >= max_cdevsws)
320 		return (NULL);
321 
322 	return (cdevsw[cmajor]);
323 }
324 
325 int
326 bdevsw_lookup_major(const struct bdevsw *bdev)
327 {
328 	int bmajor;
329 
330 	for (bmajor = 0 ; bmajor < max_bdevsws ; bmajor++) {
331 		if (bdevsw[bmajor] == bdev)
332 			return (bmajor);
333 	}
334 
335 	return (-1);
336 }
337 
338 int
339 cdevsw_lookup_major(const struct cdevsw *cdev)
340 {
341 	int cmajor;
342 
343 	for (cmajor = 0 ; cmajor < max_cdevsws ; cmajor++) {
344 		if (cdevsw[cmajor] == cdev)
345 			return (cmajor);
346 	}
347 
348 	return (-1);
349 }
350 
351 /*
352  * Convert from block major number to name.
353  */
354 const char *
355 devsw_blk2name(int bmajor)
356 {
357 	int cmajor, i;
358 
359 	if (bmajor < 0 || bmajor >= max_bdevsws || bdevsw[bmajor] == NULL)
360 		return (NULL);
361 
362 	for (i = 0 ; i < max_devsw_convs ; i++) {
363 		if (devsw_conv[i].d_bmajor != bmajor)
364 			continue;
365 		cmajor = devsw_conv[i].d_cmajor;
366 		if (cmajor < 0 || cmajor >= max_cdevsws ||
367 		    cdevsw[cmajor] == NULL)
368 			return (NULL);
369 		return (devsw_conv[i].d_name);
370 	}
371 
372 	return (NULL);
373 }
374 
375 /*
376  * Convert from device name to block major number.
377  */
378 int
379 devsw_name2blk(const char *name, char *devname, size_t devnamelen)
380 {
381 	struct devsw_conv *conv;
382 	int bmajor, i;
383 
384 	if (name == NULL)
385 		return (-1);
386 
387 	for (i = 0 ; i < max_devsw_convs ; i++) {
388 		size_t len;
389 
390 		conv = &devsw_conv[i];
391 		if (conv->d_name == NULL)
392 			continue;
393 		len = strlen(conv->d_name);
394 		if (strncmp(conv->d_name, name, len) != 0)
395 			continue;
396 		if (*(name +len) && !isdigit(*(name + len)))
397 			continue;
398 		bmajor = conv->d_bmajor;
399 		if (bmajor < 0 || bmajor >= max_bdevsws ||
400 		    bdevsw[bmajor] == NULL)
401 			break;
402 		if (devname != NULL) {
403 #ifdef DEVSW_DEBUG
404 			if (strlen(conv->d_name) >= devnamelen)
405 				printf("devsw_name2blk: too short buffer");
406 #endif /* DEVSW_DEBUG */
407 			strncpy(devname, conv->d_name, devnamelen);
408 			devname[devnamelen - 1] = '\0';
409 		}
410 		return (bmajor);
411 	}
412 
413 	return (-1);
414 }
415 
416 /*
417  * Convert from character dev_t to block dev_t.
418  */
419 dev_t
420 devsw_chr2blk(dev_t cdev)
421 {
422 	int bmajor, cmajor, i;
423 
424 	if (cdevsw_lookup(cdev) == NULL)
425 		return (NODEV);
426 
427 	cmajor = major(cdev);
428 
429 	for (i = 0 ; i < max_devsw_convs ; i++) {
430 		if (devsw_conv[i].d_cmajor != cmajor)
431 			continue;
432 		bmajor = devsw_conv[i].d_bmajor;
433 		if (bmajor < 0 || bmajor >= max_bdevsws ||
434 		    bdevsw[bmajor] == NULL)
435 			return (NODEV);
436 		return (makedev(bmajor, minor(cdev)));
437 	}
438 
439 	return (NODEV);
440 }
441 
442 /*
443  * Convert from block dev_t to character dev_t.
444  */
445 dev_t
446 devsw_blk2chr(dev_t bdev)
447 {
448 	int bmajor, cmajor, i;
449 
450 	if (bdevsw_lookup(bdev) == NULL)
451 		return (NODEV);
452 
453 	bmajor = major(bdev);
454 
455 	for (i = 0 ; i < max_devsw_convs ; i++) {
456 		if (devsw_conv[i].d_bmajor != bmajor)
457 			continue;
458 		cmajor = devsw_conv[i].d_cmajor;
459 		if (cmajor < 0 || cmajor >= max_cdevsws ||
460 		    cdevsw[cmajor] == NULL)
461 			return (NODEV);
462 		return (makedev(cmajor, minor(bdev)));
463 	}
464 
465 	return (NODEV);
466 }
467