xref: /netbsd-src/sys/kern/subr_devsw.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: subr_devsw.c,v 1.7 2003/07/14 14:59:02 lukem 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.7 2003/07/14 14:59:02 lukem 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, const struct bdevsw *devsw, int *devmajor)
163 {
164 	int bmajor, i;
165 
166 	if (devsw == NULL)
167 		return (0);
168 
169 	if (*devmajor < 0) {
170 		for (bmajor = sys_bdevsws ; bmajor < max_bdevsws ; bmajor++) {
171 			if (bdevsw[bmajor] != NULL)
172 				continue;
173 			for (i = 0 ; i < max_devsw_convs ; i++) {
174 				if (devsw_conv[i].d_bmajor == bmajor)
175 					break;
176 			}
177 			if (i != max_devsw_convs)
178 				continue;
179 			break;
180 		}
181 		*devmajor = bmajor;
182 	}
183 	if (*devmajor >= MAXDEVSW) {
184 #ifdef DEVSW_DEBUG
185 		panic("bdevsw_attach: block majors exhausted");
186 #endif /* DEVSW_DEBUG */
187 		return (ENOMEM);
188 	}
189 
190 	if (*devmajor >= max_bdevsws) {
191 		const struct bdevsw **newptr;
192 		int old, new;
193 
194 		old = max_bdevsws;
195 		new = *devmajor + 1;
196 
197 		newptr = malloc(new * BDEVSW_SIZE, M_DEVBUF, M_NOWAIT);
198 		if (newptr == NULL)
199 			return (ENOMEM);
200 		memset(newptr + old, 0, (new - old) * BDEVSW_SIZE);
201 		if (old != 0) {
202 			memcpy(newptr, bdevsw, old * BDEVSW_SIZE);
203 			if (bdevsw != bdevsw0)
204 				free(bdevsw, M_DEVBUF);
205 		}
206 		bdevsw = newptr;
207 		max_bdevsws = new;
208 	}
209 
210 	if (bdevsw[*devmajor] != NULL)
211 		return (EEXIST);
212 
213 	bdevsw[*devmajor] = devsw;
214 
215 	return (0);
216 }
217 
218 static int
219 cdevsw_attach(const char *devname, const struct cdevsw *devsw, int *devmajor)
220 {
221 	int cmajor, i;
222 
223 	if (*devmajor < 0) {
224 		for (cmajor = sys_cdevsws ; cmajor < max_cdevsws ; cmajor++) {
225 			if (cdevsw[cmajor] != NULL)
226 				continue;
227 			for (i = 0 ; i < max_devsw_convs ; i++) {
228 				if (devsw_conv[i].d_cmajor == cmajor)
229 					break;
230 			}
231 			if (i != max_devsw_convs)
232 				continue;
233 			break;
234 		}
235 		*devmajor = cmajor;
236 	}
237 	if (*devmajor >= MAXDEVSW) {
238 #ifdef DEVSW_DEBUG
239 		panic("cdevsw_attach: character majors exhausted");
240 #endif /* DEVSW_DEBUG */
241 		return (ENOMEM);
242 	}
243 
244 	if (*devmajor >= max_cdevsws) {
245 		const struct cdevsw **newptr;
246 		int old, new;
247 
248 		old = max_cdevsws;
249 		new = *devmajor + 1;
250 
251 		newptr = malloc(new * CDEVSW_SIZE, M_DEVBUF, M_NOWAIT);
252 		if (newptr == NULL)
253 			return (ENOMEM);
254 		memset(newptr + old, 0, (new - old) * CDEVSW_SIZE);
255 		if (old != 0) {
256 			memcpy(newptr, cdevsw, old * CDEVSW_SIZE);
257 			if (cdevsw != cdevsw0)
258 				free(cdevsw, M_DEVBUF);
259 		}
260 		cdevsw = newptr;
261 		max_cdevsws = new;
262 	}
263 
264 	if (cdevsw[*devmajor] != NULL)
265 		return (EEXIST);
266 
267 	cdevsw[*devmajor] = devsw;
268 
269 	return (0);
270 }
271 
272 void
273 devsw_detach(const struct bdevsw *bdev, const struct cdevsw *cdev)
274 {
275 	int i;
276 
277 	if (bdev != NULL) {
278 		for (i = 0 ; i < max_bdevsws ; i++) {
279 			if (bdevsw[i] != bdev)
280 				continue;
281 			bdevsw[i] = NULL;
282 			break;
283 		}
284 	}
285 	if (cdev != NULL) {
286 		for (i = 0 ; i < max_cdevsws ; i++) {
287 			if (cdevsw[i] != cdev)
288 				continue;
289 			cdevsw[i] = NULL;
290 			break;
291 		}
292 	}
293 }
294 
295 const struct bdevsw *
296 bdevsw_lookup(dev_t dev)
297 {
298 	int bmajor;
299 
300 	if (dev == NODEV)
301 		return (NULL);
302 	bmajor = major(dev);
303 	if (bmajor < 0 || bmajor >= max_bdevsws)
304 		return (NULL);
305 
306 	return (bdevsw[bmajor]);
307 }
308 
309 const struct cdevsw *
310 cdevsw_lookup(dev_t dev)
311 {
312 	int cmajor;
313 
314 	if (dev == NODEV)
315 		return (NULL);
316 	cmajor = major(dev);
317 	if (cmajor < 0 || cmajor >= max_cdevsws)
318 		return (NULL);
319 
320 	return (cdevsw[cmajor]);
321 }
322 
323 int
324 bdevsw_lookup_major(const struct bdevsw *bdev)
325 {
326 	int bmajor;
327 
328 	for (bmajor = 0 ; bmajor < max_bdevsws ; bmajor++) {
329 		if (bdevsw[bmajor] == bdev)
330 			return (bmajor);
331 	}
332 
333 	return (-1);
334 }
335 
336 int
337 cdevsw_lookup_major(const struct cdevsw *cdev)
338 {
339 	int cmajor;
340 
341 	for (cmajor = 0 ; cmajor < max_cdevsws ; cmajor++) {
342 		if (cdevsw[cmajor] == cdev)
343 			return (cmajor);
344 	}
345 
346 	return (-1);
347 }
348 
349 /*
350  * Convert from block major number to name.
351  */
352 const char *
353 devsw_blk2name(int bmajor)
354 {
355 	int cmajor, i;
356 
357 	if (bmajor < 0 || bmajor >= max_bdevsws || bdevsw[bmajor] == NULL)
358 		return (NULL);
359 
360 	for (i = 0 ; i < max_devsw_convs ; i++) {
361 		if (devsw_conv[i].d_bmajor != bmajor)
362 			continue;
363 		cmajor = devsw_conv[i].d_cmajor;
364 		if (cmajor < 0 || cmajor >= max_cdevsws ||
365 		    cdevsw[cmajor] == NULL)
366 			return (NULL);
367 		return (devsw_conv[i].d_name);
368 	}
369 
370 	return (NULL);
371 }
372 
373 /*
374  * Convert from device name to block major number.
375  */
376 int
377 devsw_name2blk(const char *name, char *devname, size_t devnamelen)
378 {
379 	struct devsw_conv *conv;
380 	int bmajor, i;
381 
382 	if (name == NULL)
383 		return (-1);
384 
385 	for (i = 0 ; i < max_devsw_convs ; i++) {
386 		size_t len;
387 
388 		conv = &devsw_conv[i];
389 		if (conv->d_name == NULL)
390 			continue;
391 		len = strlen(conv->d_name);
392 		if (strncmp(conv->d_name, name, len) != 0)
393 			continue;
394 		if (*(name +len) && !isdigit(*(name + len)))
395 			continue;
396 		bmajor = conv->d_bmajor;
397 		if (bmajor < 0 || bmajor >= max_bdevsws ||
398 		    bdevsw[bmajor] == NULL)
399 			break;
400 		if (devname != NULL) {
401 #ifdef DEVSW_DEBUG
402 			if (strlen(conv->d_name) >= devnamelen)
403 				printf("devsw_name2blk: too short buffer");
404 #endif /* DEVSW_DEBUG */
405 			strncpy(devname, conv->d_name, devnamelen);
406 			devname[devnamelen - 1] = '\0';
407 		}
408 		return (bmajor);
409 	}
410 
411 	return (-1);
412 }
413 
414 /*
415  * Convert from character dev_t to block dev_t.
416  */
417 dev_t
418 devsw_chr2blk(dev_t cdev)
419 {
420 	int bmajor, cmajor, i;
421 
422 	if (cdevsw_lookup(cdev) == NULL)
423 		return (NODEV);
424 
425 	cmajor = major(cdev);
426 
427 	for (i = 0 ; i < max_devsw_convs ; i++) {
428 		if (devsw_conv[i].d_cmajor != cmajor)
429 			continue;
430 		bmajor = devsw_conv[i].d_bmajor;
431 		if (bmajor < 0 || bmajor >= max_bdevsws ||
432 		    bdevsw[bmajor] == NULL)
433 			return (NODEV);
434 		return (makedev(bmajor, minor(cdev)));
435 	}
436 
437 	return (NODEV);
438 }
439 
440 /*
441  * Convert from block dev_t to character dev_t.
442  */
443 dev_t
444 devsw_blk2chr(dev_t bdev)
445 {
446 	int bmajor, cmajor, i;
447 
448 	if (bdevsw_lookup(bdev) == NULL)
449 		return (NODEV);
450 
451 	bmajor = major(bdev);
452 
453 	for (i = 0 ; i < max_devsw_convs ; i++) {
454 		if (devsw_conv[i].d_bmajor != bmajor)
455 			continue;
456 		cmajor = devsw_conv[i].d_cmajor;
457 		if (cmajor < 0 || cmajor >= max_cdevsws ||
458 		    cdevsw[cmajor] == NULL)
459 			return (NODEV);
460 		return (makedev(cmajor, minor(bdev)));
461 	}
462 
463 	return (NODEV);
464 }
465