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