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