1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * Floppy Disk driver 29 */ 30 31 /* 32 * Set CMOS feature: 33 * CMOS_CONF_MEM: CMOS memory contains configuration info 34 */ 35 #define CMOS_CONF_MEM 36 37 #include <sys/types.h> 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/buf.h> 41 #include <sys/file.h> 42 #include <sys/open.h> 43 #include <sys/ioctl.h> 44 #include <sys/uio.h> 45 #include <sys/conf.h> 46 #include <sys/stat.h> 47 #include <sys/autoconf.h> 48 #include <sys/vtoc.h> 49 #include <sys/dkio.h> 50 #include <sys/ddi.h> 51 #include <sys/sunddi.h> 52 #include <sys/kstat.h> 53 #include <sys/kmem.h> 54 #include <sys/ddidmareq.h> 55 #include <sys/fdio.h> 56 #include <sys/fdc.h> 57 #include <sys/fd_debug.h> 58 #include <sys/fdmedia.h> 59 #include <sys/debug.h> 60 #include <sys/modctl.h> 61 62 /* 63 * Local Function Prototypes 64 */ 65 static int fd_unit_is_open(struct fdisk *); 66 static int fdgetlabel(struct fcu_obj *, int); 67 static void fdstart(struct fcu_obj *); 68 static int fd_build_label_vtoc(struct fcu_obj *, struct fdisk *, 69 struct vtoc *, struct dk_label *); 70 static void fd_build_user_vtoc(struct fcu_obj *, struct fdisk *, 71 struct vtoc *); 72 static int fd_rawioctl(struct fcu_obj *, int, caddr_t, int); 73 static void fd_media_watch(void *); 74 75 static int fd_open(dev_t *, int, int, cred_t *); 76 static int fd_close(dev_t, int, int, cred_t *); 77 static int fd_strategy(struct buf *); 78 static int fd_read(dev_t, struct uio *, cred_t *); 79 static int fd_write(dev_t, struct uio *, cred_t *); 80 static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 81 static int fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, 82 caddr_t, int *); 83 static int fd_check_media(dev_t dev, enum dkio_state state); 84 static int fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag); 85 86 static struct cb_ops fd_cb_ops = { 87 fd_open, /* open */ 88 fd_close, /* close */ 89 fd_strategy, /* strategy */ 90 nodev, /* print */ 91 nodev, /* dump */ 92 fd_read, /* read */ 93 fd_write, /* write */ 94 fd_ioctl, /* ioctl */ 95 nodev, /* devmap */ 96 nodev, /* mmap */ 97 nodev, /* segmap */ 98 nochpoll, /* poll */ 99 fd_prop_op, /* cb_prop_op */ 100 0, /* streamtab */ 101 D_NEW | D_MP /* Driver compatibility flag */ 102 }; 103 104 static int fd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 105 static int fd_probe(dev_info_t *); 106 static int fd_attach(dev_info_t *, ddi_attach_cmd_t); 107 static int fd_detach(dev_info_t *, ddi_detach_cmd_t); 108 109 static struct dev_ops fd_ops = { 110 DEVO_REV, /* devo_rev, */ 111 0, /* refcnt */ 112 fd_getinfo, /* getinfo */ 113 nulldev, /* identify */ 114 fd_probe, /* probe */ 115 fd_attach, /* attach */ 116 fd_detach, /* detach */ 117 nodev, /* reset */ 118 &fd_cb_ops, /* driver operations */ 119 (struct bus_ops *)0, /* bus operations */ 120 NULL, /* power */ 121 ddi_quiesce_not_supported, /* devo_quiesce */ 122 }; 123 124 125 /* 126 * static data 127 */ 128 static void *fd_state_head; /* opaque handle top of state structs */ 129 static int fd_check_media_time = 5000000; /* 5 second state check */ 130 131 /* 132 * error handling 133 * 134 * for debugging, 135 * set fderrlevel to 1 136 * set fderrmask to 224 or 644 137 */ 138 #ifdef DEBUG 139 static uint_t fderrmask = FDEM_ALL; 140 #endif 141 static int fderrlevel = 5; 142 143 #define KIOSP KSTAT_IO_PTR(fdp->d_iostat) 144 145 static struct driver_minor_data { 146 char *name; 147 int minor; 148 int type; 149 } fd_minor [] = { 150 { "a", 0, S_IFBLK}, 151 { "b", 1, S_IFBLK}, 152 { "c", 2, S_IFBLK}, 153 { "a,raw", 0, S_IFCHR}, 154 { "b,raw", 1, S_IFCHR}, 155 { "c,raw", 2, S_IFCHR}, 156 {0} 157 }; 158 159 static struct modldrv modldrv = { 160 &mod_driverops, /* Type of module. This one is a driver */ 161 "Floppy Disk driver", /* Name of the module. */ 162 &fd_ops, /* driver ops */ 163 }; 164 165 static struct modlinkage modlinkage = { 166 MODREV_1, (void *)&modldrv, NULL 167 }; 168 169 170 int 171 _init(void) 172 { 173 int retval; 174 175 if ((retval = ddi_soft_state_init(&fd_state_head, 176 sizeof (struct fdisk) + sizeof (struct fd_drive) + 177 sizeof (struct fd_char) + sizeof (struct fdattr), 0)) != 0) 178 return (retval); 179 180 if ((retval = mod_install(&modlinkage)) != 0) 181 ddi_soft_state_fini(&fd_state_head); 182 return (retval); 183 } 184 185 int 186 _fini(void) 187 { 188 int retval; 189 190 if ((retval = mod_remove(&modlinkage)) != 0) 191 return (retval); 192 ddi_soft_state_fini(&fd_state_head); 193 return (retval); 194 } 195 196 int 197 _info(struct modinfo *modinfop) 198 { 199 return (mod_info(&modlinkage, modinfop)); 200 } 201 202 203 static int 204 fd_getdrive(dev_t dev, struct fcu_obj **fjpp, struct fdisk **fdpp) 205 { 206 if (fdpp) { 207 *fdpp = ddi_get_soft_state(fd_state_head, DRIVE(dev)); 208 if (*fdpp && fjpp) { 209 *fjpp = (*fdpp)->d_obj; 210 if (*fjpp) 211 return ((*fjpp)->fj_unit); 212 } 213 } 214 return (-1); 215 } 216 217 /*ARGSUSED*/ 218 static int 219 fd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 220 { 221 dev_t dev = (dev_t)arg; 222 struct fcu_obj *fjp = NULL; 223 struct fdisk *fdp = NULL; 224 int rval; 225 226 switch (cmd) { 227 case DDI_INFO_DEVT2DEVINFO: 228 (void) fd_getdrive(dev, &fjp, &fdp); 229 /* 230 * Ignoring return value because success is checked by 231 * verifying fjp and fdp and returned unit value is not used. 232 */ 233 if (fjp && fdp) { 234 *result = fjp->fj_dip; 235 rval = DDI_SUCCESS; 236 } else 237 rval = DDI_FAILURE; 238 break; 239 case DDI_INFO_DEVT2INSTANCE: 240 *result = (void *)(uintptr_t)DRIVE(dev); 241 rval = DDI_SUCCESS; 242 break; 243 default: 244 rval = DDI_FAILURE; 245 } 246 return (rval); 247 } 248 249 #ifdef CMOS_CONF_MEM 250 #define CMOS_ADDR 0x70 251 #define CMOS_DATA 0x71 252 #define CMOS_FDRV 0x10 253 #endif /* CMOS_CONF_MEM */ 254 255 static int 256 fd_probe(dev_info_t *dip) 257 { 258 #ifdef CMOS_CONF_MEM 259 int cmos; 260 int drive_type; 261 #endif /* CMOS_CONF_MEM */ 262 int debug[2]; 263 int drive_size; 264 int len; 265 int unit_num; 266 char density[8]; 267 268 len = sizeof (debug); 269 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 270 DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) == 271 DDI_PROP_SUCCESS) { 272 fderrlevel = debug[0]; 273 #ifdef DEBUG 274 fderrmask = (uint_t)debug[1]; 275 #endif 276 } 277 len = sizeof (unit_num); 278 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 279 DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) != 280 DDI_PROP_SUCCESS) { 281 FDERRPRINT(FDEP_L3, FDEM_ATTA, 282 (CE_WARN, "fd_probe failed: dip %p", (void *)dip)); 283 return (DDI_PROBE_FAILURE); 284 } 285 286 #ifdef CMOS_CONF_MEM 287 /* get the cmos memory values quick and dirty */ 288 outb(CMOS_ADDR, CMOS_FDRV); 289 cmos = drive_type = (int)inb(CMOS_DATA); 290 #endif /* CMOS_CONF_MEM */ 291 292 switch (unit_num) { 293 #ifdef CMOS_CONF_MEM 294 case 0: 295 drive_type = drive_type >> 4; 296 /* FALLTHROUGH */ 297 case 1: 298 if (cmos && (drive_type & 0x0F)) { 299 break; 300 } 301 /* 302 * Some enhanced floppy-disk controller adaptor cards 303 * require NO drives defined in the CMOS configuration 304 * memory. 305 * So fall through 306 */ 307 #endif /* CMOS_CONF_MEM */ 308 default: /* need to check conf file */ 309 len = sizeof (density); 310 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 311 DDI_PROP_DONTPASS, "density", (caddr_t)&density, &len) != 312 DDI_PROP_SUCCESS) { 313 FDERRPRINT(FDEP_L3, FDEM_ATTA, 314 (CE_WARN, 315 "fd_probe failed density: dip %p unit %d", 316 (void *)dip, unit_num)); 317 return (DDI_PROBE_FAILURE); 318 } 319 len = sizeof (drive_size); 320 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 321 DDI_PROP_DONTPASS, "size", (caddr_t)&drive_size, &len) != 322 DDI_PROP_SUCCESS) { 323 FDERRPRINT(FDEP_L3, FDEM_ATTA, 324 (CE_WARN, "fd_probe failed size: dip %p unit %d", 325 (void *)dip, unit_num)); 326 return (DDI_PROBE_FAILURE); 327 } 328 } 329 FDERRPRINT(FDEP_L3, FDEM_ATTA, 330 (CE_WARN, "fd_probe dip %p unit %d", (void *)dip, unit_num)); 331 return (DDI_PROBE_SUCCESS); 332 } 333 334 335 /* ARGSUSED */ 336 static int 337 fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 338 { 339 struct fcu_obj *fjp; 340 struct fdisk *fdp; 341 struct driver_minor_data *dmdp; 342 int mode_3D; 343 int drive_num, drive_size, drive_type; 344 #ifdef CMOS_CONF_MEM 345 int cmos; 346 #endif /* CMOS_CONF_MEM */ 347 int len, sig_minor; 348 int unit_num; 349 char density[8]; 350 char name[MAXNAMELEN]; 351 352 switch (cmd) { 353 case DDI_ATTACH: 354 len = sizeof (unit_num); 355 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 356 DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) != 357 DDI_PROP_SUCCESS) { 358 FDERRPRINT(FDEP_L3, FDEM_ATTA, 359 (CE_WARN, "fd_attach failed: dip %p", (void *)dip)); 360 return (DDI_FAILURE); 361 } 362 363 #ifdef CMOS_CONF_MEM 364 outb(CMOS_ADDR, CMOS_FDRV); 365 cmos = drive_type = (int)inb(CMOS_DATA); 366 #endif /* CMOS_CONF_MEM */ 367 368 switch (unit_num) { 369 #ifdef CMOS_CONF_MEM 370 case 0: 371 drive_type = drive_type >> 4; 372 /* FALLTHROUGH */ 373 case 1: 374 drive_type = drive_type & 0x0F; 375 if (cmos) 376 break; 377 /* 378 * Some enhanced floppy-disk controller adaptor cards 379 * require NO drives defined in the CMOS configuration 380 * memory. 381 * So fall through 382 */ 383 #endif /* CMOS_CONF_MEM */ 384 default: /* need to check .conf file */ 385 drive_type = 0; 386 len = sizeof (density); 387 if (ddi_prop_op(DDI_DEV_T_ANY, dip, 388 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "density", 389 (caddr_t)&density, &len) != DDI_PROP_SUCCESS) 390 density[0] = '\0'; 391 len = sizeof (drive_size); 392 if (ddi_prop_op(DDI_DEV_T_ANY, dip, 393 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "size", 394 (caddr_t)&drive_size, &len) != DDI_PROP_SUCCESS) 395 drive_size = 0; 396 if (strcmp(density, "DSDD") == 0) { 397 if (drive_size == 5) 398 drive_type = 1; 399 else if (drive_size == 3) 400 drive_type = 3; 401 } else if (strcmp(density, "DSHD") == 0) { 402 if (drive_size == 5) 403 drive_type = 2; 404 else if (drive_size == 3) 405 drive_type = 4; 406 } else if (strcmp(density, "DSED") == 0 && 407 drive_size == 3) { 408 drive_type = 6; 409 } 410 break; 411 } 412 if (drive_type == 0) { 413 FDERRPRINT(FDEP_L3, FDEM_ATTA, 414 (CE_WARN, "fd_attach failed type: dip %p unit %d", 415 (void *)dip, unit_num)); 416 return (DDI_FAILURE); 417 } 418 419 drive_num = ddi_get_instance(dip); 420 if (ddi_soft_state_zalloc(fd_state_head, drive_num) != 0) 421 return (DDI_FAILURE); 422 fdp = ddi_get_soft_state(fd_state_head, drive_num); 423 fjp = fdp->d_obj = ddi_get_driver_private(dip); 424 425 mutex_init(&fjp->fj_lock, NULL, MUTEX_DRIVER, *fjp->fj_iblock); 426 sema_init(&fdp->d_ocsem, 1, NULL, SEMA_DRIVER, NULL); 427 428 fjp->fj_drive = (struct fd_drive *)(fdp + 1); 429 fjp->fj_chars = (struct fd_char *)(fjp->fj_drive + 1); 430 fjp->fj_attr = (struct fdattr *)(fjp->fj_chars + 1); 431 432 /* 433 * set default floppy drive characteristics & geometry 434 */ 435 switch (drive_type) { /* assume doubled sided */ 436 case 2: /* 5.25 high density */ 437 *fjp->fj_drive = dfd_525HD; 438 fdp->d_media = 1<<FMT_5H | 1<<FMT_5D9 | 1<<FMT_5D8 | 439 1<<FMT_5D4 | 1<<FMT_5D16; 440 fdp->d_deffdtype = fdp->d_curfdtype = FMT_5H; 441 break; 442 case 4: /* 3.5 high density */ 443 *fjp->fj_drive = dfd_350HD; 444 fdp->d_media = 1<<FMT_3H | 1<<FMT_3I | 1<<FMT_3D; 445 len = sizeof (mode_3D); 446 if (ddi_prop_op(DDI_DEV_T_ANY, dip, 447 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "mode_3D", 448 (caddr_t)&mode_3D, &len) != DDI_PROP_SUCCESS) 449 mode_3D = 0; 450 if (mode_3D && (fjp->fj_fdc->c_flags & FCFLG_3DMODE)) 451 /* 452 * 3D mode should be enabled only if a dual- 453 * speed 3.5" high-density drive and a 454 * supported floppy controller are installed. 455 */ 456 fdp->d_media |= 1 << FMT_3M; 457 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3H; 458 break; 459 case 1: /* 5.25 double density */ 460 *fjp->fj_drive = dfd_525DD; 461 fdp->d_media = 1<<FMT_5D9 | 1<<FMT_5D8 | 1<<FMT_5D4 | 462 1<<FMT_5D16; 463 fdp->d_deffdtype = fdp->d_curfdtype = FMT_5D9; 464 break; 465 case 3: /* 3.5 double density */ 466 *fjp->fj_drive = dfd_350HD; 467 fdp->d_media = 1<<FMT_3D; 468 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3D; 469 break; 470 case 5: /* 3.5 extended density */ 471 case 6: 472 case 7: 473 *fjp->fj_drive = dfd_350ED; 474 fdp->d_media = 1<<FMT_3E | 1<<FMT_3H | 1<<FMT_3I | 475 1<<FMT_3D; 476 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3E; 477 break; 478 case 0: /* no drive defined */ 479 default: 480 goto no_attach; 481 } 482 *fjp->fj_chars = *defchar[fdp->d_deffdtype]; 483 *fjp->fj_attr = fdtypes[fdp->d_deffdtype]; 484 bcopy(fdparts[fdp->d_deffdtype], fdp->d_part, 485 sizeof (struct partition) * NDKMAP); 486 fjp->fj_rotspd = fdtypes[fdp->d_deffdtype].fda_rotatespd; 487 488 sig_minor = drive_num << 3; 489 for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) { 490 if (ddi_create_minor_node(dip, dmdp->name, dmdp->type, 491 sig_minor | dmdp->minor, DDI_NT_FD, NULL) 492 == DDI_FAILURE) { 493 ddi_remove_minor_node(dip, NULL); 494 goto no_attach; 495 } 496 } 497 498 FDERRPRINT(FDEP_L3, FDEM_ATTA, 499 (CE_WARN, "fd_attach: dip %p unit %d", 500 (void *)dip, unit_num)); 501 (void) sprintf(name, "fd%d", drive_num); 502 fdp->d_iostat = kstat_create("fd", drive_num, name, "disk", 503 KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT); 504 if (fdp->d_iostat) { 505 fdp->d_iostat->ks_lock = &fjp->fj_lock; 506 kstat_install(fdp->d_iostat); 507 } 508 509 fjp->fj_data = (caddr_t)fdp; 510 fjp->fj_flags |= FUNIT_DRVATCH; 511 512 /* 513 * Add a zero-length attribute to tell the world we support 514 * kernel ioctls (for layered drivers) 515 */ 516 (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, 517 DDI_KERNEL_IOCTL, NULL, 0); 518 /* 519 * Ignoring return value because, for passed arguments, only 520 * DDI_SUCCESS is returned. 521 */ 522 ddi_report_dev(dip); 523 return (DDI_SUCCESS); 524 525 #ifdef NOT_YET 526 case DDI_RESUME: 527 drive_num = ddi_get_instance(dip); 528 if (!(fdp = ddi_get_soft_state(fd_state_head, drive_num))) 529 return (DDI_FAILURE); 530 fjp = (struct fcu_obj *)fdp->d_obj; 531 mutex_enter(&fjp->fj_lock); 532 if (!fjp->fj_suspended) { 533 mutex_exit(&fjp->fj_lock); 534 return (DDI_SUCCESS); 535 } 536 fjp->fj_fdc->c_curpcyl[drive_num & 3] = -1; 537 fjp->fj_suspended = 0; 538 mutex_exit(&fjp->fj_lock); 539 return (DDI_SUCCESS); 540 #endif 541 542 default: 543 return (DDI_FAILURE); 544 } 545 no_attach: 546 fjp->fj_drive = NULL; 547 fjp->fj_chars = NULL; 548 fjp->fj_attr = NULL; 549 mutex_destroy(&fjp->fj_lock); 550 sema_destroy(&fdp->d_ocsem); 551 ddi_soft_state_free(fd_state_head, drive_num); 552 FDERRPRINT(FDEP_L3, FDEM_ATTA, 553 (CE_WARN, "fd_attach failed: dip %p unit %d", 554 (void *)dip, unit_num)); 555 return (DDI_FAILURE); 556 } 557 558 559 /* ARGSUSED */ 560 static int 561 fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 562 { 563 struct fcu_obj *fjp; 564 struct fdisk *fdp; 565 int drive_num; 566 int rval = DDI_SUCCESS; 567 568 FDERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fd_detach dip %p", 569 (void *)dip)); 570 571 drive_num = ddi_get_instance(dip); 572 if (!(fdp = ddi_get_soft_state(fd_state_head, drive_num))) 573 return (rval); 574 575 switch (cmd) { 576 case DDI_DETACH: 577 if (fd_unit_is_open(fdp)) { 578 rval = EBUSY; 579 break; 580 } 581 kstat_delete(fdp->d_iostat); 582 fdp->d_iostat = NULL; 583 fjp = (struct fcu_obj *)fdp->d_obj; 584 fjp->fj_data = NULL; 585 fjp->fj_drive = NULL; 586 fjp->fj_chars = NULL; 587 fjp->fj_attr = NULL; 588 ddi_prop_remove_all(dip); 589 mutex_destroy(&fjp->fj_lock); 590 sema_destroy(&fdp->d_ocsem); 591 ddi_soft_state_free(fd_state_head, drive_num); 592 break; 593 594 #ifdef NOT_YET 595 case DDI_SUSPEND: 596 fjp = (struct fcu_obj *)fdp->d_obj; 597 fjp->fj_suspended = 1; /* Must be before mutex */ 598 mutex_enter(&fjp->fj_lock); 599 while (fjp->fj_flags & FUNIT_BUSY) { 600 /* Wait for I/O to finish */ 601 cv_wait(&fjp->fj_flags, &fjp->fj_lock); 602 } 603 mutex_exit(&fjp->fj_lock); 604 break; 605 #endif 606 607 default: 608 rval = EINVAL; 609 break; 610 } 611 return (rval); 612 } 613 614 615 static int 616 fd_part_is_open(struct fdisk *fdp, int part) 617 { 618 int i; 619 620 for (i = 0; i < (OTYPCNT - 1); i++) 621 if (fdp->d_regopen[i] & (1 << part)) 622 return (1); 623 return (0); 624 } 625 626 static int 627 fd_unit_is_open(struct fdisk *fdp) 628 { 629 int i; 630 631 for (i = 0; i < NDKMAP; i++) 632 if (fdp->d_lyropen[i]) 633 return (1); 634 for (i = 0; i < (OTYPCNT - 1); i++) 635 if (fdp->d_regopen[i]) 636 return (1); 637 return (0); 638 } 639 640 /*ARGSUSED*/ 641 static int 642 fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 643 { 644 struct fcu_obj *fjp = NULL; 645 struct fdisk *fdp = NULL; 646 struct partition *pp; 647 dev_t dev; 648 int part, unit; 649 int part_is_open; 650 int rval; 651 uint_t pbit; 652 653 dev = *devp; 654 unit = fd_getdrive(dev, &fjp, &fdp); 655 if (!fjp || !fdp) 656 return (ENXIO); 657 part = PARTITION(dev); 658 pbit = 1 << part; 659 pp = &fdp->d_part[part]; 660 661 /* 662 * Serialize opens/closes 663 */ 664 sema_p(&fdp->d_ocsem); 665 FDERRPRINT(FDEP_L1, FDEM_OPEN, 666 (CE_CONT, "fd_open: fd%d part %d flag %x otype %x\n", DRIVE(dev), 667 part, flag, otyp)); 668 669 /* 670 * Check for previous exclusive open, or trying to exclusive open 671 * An "exclusive open" on any partition is not guaranteed to 672 * protect against opens on another partition that overlaps it. 673 */ 674 if (otyp == OTYP_LYR) { 675 part_is_open = (fdp->d_lyropen[part] != 0); 676 } else { 677 part_is_open = fd_part_is_open(fdp, part); 678 } 679 if ((fdp->d_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) { 680 FDERRPRINT(FDEP_L0, FDEM_OPEN, (CE_CONT, 681 "fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n", 682 fdp->d_exclmask, fdp->d_regopen[otyp], fdp->d_lyropen[part], 683 pbit)); 684 sema_v(&fdp->d_ocsem); 685 return (EBUSY); 686 } 687 688 /* 689 * Ensure that drive is recalibrated on first open of new diskette. 690 */ 691 fjp->fj_ops->fco_select(fjp, unit, 1); 692 if (fjp->fj_ops->fco_getchng(fjp, unit) != 0) { 693 if (fjp->fj_ops->fco_rcseek(fjp, unit, -1, 0)) { 694 FDERRPRINT(FDEP_L2, FDEM_OPEN, 695 (CE_NOTE, "fd_open fd%d: not ready", DRIVE(dev))); 696 fjp->fj_ops->fco_select(fjp, unit, 0); 697 sema_v(&fdp->d_ocsem); 698 return (ENXIO); 699 } 700 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED); 701 } 702 if (flag & (FNDELAY | FNONBLOCK)) { 703 /* don't attempt access, just return successfully */ 704 fjp->fj_ops->fco_select(fjp, unit, 0); 705 goto out; 706 } 707 708 /* 709 * auto-sense the density/format of the diskette 710 */ 711 rval = fdgetlabel(fjp, unit); 712 fjp->fj_ops->fco_select(fjp, unit, 0); 713 if (rval) { 714 /* didn't find label (couldn't read anything) */ 715 FDERRPRINT(FDEP_L2, FDEM_OPEN, 716 (CE_NOTE, "fd%d: drive not ready", DRIVE(dev))); 717 sema_v(&fdp->d_ocsem); 718 return (EIO); 719 } 720 /* check partition */ 721 if (pp->p_size == 0) { 722 sema_v(&fdp->d_ocsem); 723 return (ENXIO); 724 } 725 /* 726 * if opening for writing, check write protect on diskette 727 */ 728 if ((flag & FWRITE) && (fdp->d_obj->fj_flags & FUNIT_WPROT)) { 729 sema_v(&fdp->d_ocsem); 730 return (EROFS); 731 } 732 733 out: 734 /* 735 * mark open as having succeeded 736 */ 737 if (flag & FEXCL) 738 fdp->d_exclmask |= pbit; 739 if (otyp == OTYP_LYR) 740 fdp->d_lyropen[part]++; 741 else 742 fdp->d_regopen[otyp] |= 1 << part; 743 744 sema_v(&fdp->d_ocsem); 745 return (0); 746 } 747 748 /* 749 * fdgetlabel - read the SunOS label off the diskette 750 * if it can read a valid label it does so, else it will use a 751 * default. If it can`t read the diskette - that is an error. 752 * 753 * RETURNS: 0 for ok - meaning that it could at least read the device, 754 * !0 for error XXX TBD NYD error codes 755 */ 756 static int 757 fdgetlabel(struct fcu_obj *fjp, int unit) 758 { 759 struct dk_label *label; 760 struct fdisk *fdp; 761 char *newlabel; 762 short *sp; 763 short count; 764 short xsum; 765 int tries, try_this; 766 uint_t nexttype; 767 int rval; 768 short oldlvl; 769 int i; 770 771 FDERRPRINT(FDEP_L0, FDEM_GETL, 772 (CE_CONT, "fdgetlabel fd unit %d\n", unit)); 773 fdp = (struct fdisk *)fjp->fj_data; 774 fjp->fj_flags &= ~(FUNIT_UNLABELED); 775 776 /* 777 * get some space to play with the label 778 */ 779 label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP); 780 FDERRPRINT(FDEP_L0, FDEM_GETL, (CE_CONT, 781 "fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n", 782 unit, (void *)label, (size_t)sizeof (struct dk_label))); 783 784 /* 785 * read block 0 (0/0/1) to find the label 786 * (disk is potentially not present or unformatted) 787 */ 788 /* noerrprint since this is a private cmd */ 789 oldlvl = fderrlevel; 790 fderrlevel = FDEP_LMAX; 791 /* 792 * try different characteristics (ie densities) 793 * 794 * if fdp->d_curfdtype is -1 then the current characteristics 795 * were set by ioctl and need to try it as well as everything 796 * in the table 797 */ 798 nexttype = fdp->d_deffdtype; 799 try_this = 1; /* always try the current characteristics */ 800 801 for (tries = nfdtypes; tries; tries--) { 802 if (try_this) { 803 fjp->fj_flags &= ~FUNIT_CHAROK; 804 805 /* try reading last sector of cyl 1, head 0 */ 806 if (!(rval = fjp->fj_ops->fco_rw(fjp, unit, 807 FDREAD, 1, 0, fjp->fj_chars->fdc_secptrack, 808 (caddr_t)label, 809 sizeof (struct dk_label))) && 810 /* and last sector plus 1 of cylinder 1 */ 811 fjp->fj_ops->fco_rw(fjp, unit, FDREAD, 1, 812 0, fjp->fj_chars->fdc_secptrack + 1, 813 (caddr_t)label, 814 sizeof (struct dk_label)) && 815 /* and label sector on cylinder 0 */ 816 !(rval = fjp->fj_ops->fco_rw(fjp, unit, 817 FDREAD, 0, 0, 1, (caddr_t)label, 818 sizeof (struct dk_label)))) 819 break; 820 if (rval == ENXIO) 821 break; 822 } 823 /* 824 * try the next entry in the characteristics tbl 825 */ 826 fdp->d_curfdtype = (signed char)nexttype; 827 nexttype = (nexttype + 1) % nfdtypes; 828 if ((1 << fdp->d_curfdtype) & fdp->d_media) { 829 *fjp->fj_chars = *defchar[fdp->d_curfdtype]; 830 *fjp->fj_attr = fdtypes[fdp->d_curfdtype]; 831 bcopy(fdparts[fdp->d_curfdtype], fdp->d_part, 832 sizeof (struct partition) * NDKMAP); 833 /* 834 * check for a double_density diskette 835 * in a high_density 5.25" drive 836 */ 837 if (fjp->fj_chars->fdc_transfer_rate == 250 && 838 fjp->fj_rotspd > fjp->fj_attr->fda_rotatespd) { 839 /* 840 * yes - adjust transfer rate since we don't 841 * know if we have a 5.25" dual-speed drive 842 */ 843 fjp->fj_attr->fda_rotatespd = 360; 844 fjp->fj_chars->fdc_transfer_rate = 300; 845 fjp->fj_chars->fdc_medium = 5; 846 } 847 if ((2 * fjp->fj_chars->fdc_ncyl) == 848 defchar[fdp->d_deffdtype]->fdc_ncyl) { 849 /* yes - adjust steps per cylinder */ 850 fjp->fj_chars->fdc_steps = 2; 851 } else 852 fjp->fj_chars->fdc_steps = 1; 853 try_this = 1; 854 } else 855 try_this = 0; 856 } 857 fderrlevel = oldlvl; /* print errors again */ 858 859 if (rval) { 860 fdp->d_curfdtype = fdp->d_deffdtype; 861 goto out; /* couldn't read anything */ 862 } 863 864 FDERRPRINT(FDEP_L0, FDEM_GETL, 865 (CE_CONT, 866 "fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n", 867 unit, fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_secptrack, 868 fjp->fj_chars->fdc_steps, fjp->fj_attr->fda_rotatespd, 869 fjp->fj_attr->fda_intrlv)); 870 871 /* 872 * _something_ was read - look for unixtype label 873 */ 874 if (label->dkl_magic != DKL_MAGIC || 875 label->dkl_vtoc.v_sanity != VTOC_SANE) { 876 /* not a label - no magic number */ 877 goto nolabel; /* no errors, but no label */ 878 } 879 880 count = sizeof (struct dk_label) / sizeof (short); 881 sp = (short *)label; 882 xsum = 0; 883 while (count--) 884 xsum ^= *sp++; /* should add up to 0 */ 885 if (xsum) { 886 /* not a label - checksum didn't compute */ 887 goto nolabel; /* no errors, but no label */ 888 } 889 890 /* 891 * the SunOS label overrides current diskette characteristics 892 */ 893 fjp->fj_chars->fdc_ncyl = label->dkl_pcyl; 894 fjp->fj_chars->fdc_nhead = label->dkl_nhead; 895 fjp->fj_chars->fdc_secptrack = (label->dkl_nsect * DEV_BSIZE) / 896 fjp->fj_chars->fdc_sec_size; 897 if (defchar[fdp->d_deffdtype]->fdc_ncyl == 2 * fjp->fj_chars->fdc_ncyl) 898 fjp->fj_chars->fdc_steps = 2; 899 else 900 fjp->fj_chars->fdc_steps = 1; 901 902 fjp->fj_attr->fda_rotatespd = label->dkl_rpm; 903 fjp->fj_attr->fda_intrlv = label->dkl_intrlv; 904 905 fdp->d_vtoc_version = label->dkl_vtoc.v_version; 906 bcopy(label->dkl_vtoc.v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL); 907 bcopy(label->dkl_vtoc.v_asciilabel, 908 fdp->d_vtoc_asciilabel, LEN_DKL_ASCII); 909 /* 910 * logical partitions 911 */ 912 for (i = 0; i < NDKMAP; i++) { 913 fdp->d_part[i].p_tag = label->dkl_vtoc.v_part[i].p_tag; 914 fdp->d_part[i].p_flag = label->dkl_vtoc.v_part[i].p_flag; 915 fdp->d_part[i].p_start = label->dkl_vtoc.v_part[i].p_start; 916 fdp->d_part[i].p_size = label->dkl_vtoc.v_part[i].p_size; 917 918 fdp->d_vtoc_timestamp[i] = label->dkl_vtoc.timestamp[i]; 919 } 920 921 fjp->fj_flags |= FUNIT_LABELOK; 922 goto out; 923 924 nolabel: 925 /* 926 * if not found, fill in label info from default (mark default used) 927 */ 928 if (fdp->d_media & (1<<FMT_3D)) 929 newlabel = deflabel_35; 930 else /* if (fdp->d_media & (1<<FMT_5D9)) */ 931 newlabel = deflabel_525; 932 bzero(fdp->d_vtoc_volume, LEN_DKL_VVOL); 933 (void) sprintf(fdp->d_vtoc_asciilabel, newlabel, 934 fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_nhead, 935 fjp->fj_chars->fdc_secptrack); 936 fjp->fj_flags |= FUNIT_UNLABELED; 937 938 out: 939 kmem_free(label, sizeof (struct dk_label)); 940 return (rval); 941 } 942 943 944 /*ARGSUSED*/ 945 static int 946 fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 947 { 948 struct fcu_obj *fjp = NULL; 949 struct fdisk *fdp = NULL; 950 int part, part_is_closed; 951 952 #ifdef DEBUG 953 int unit; 954 #define DEBUG_ASSIGN unit= 955 #else 956 #define DEBUG_ASSIGN (void) 957 #endif 958 959 DEBUG_ASSIGN fd_getdrive(dev, &fjp, &fdp); 960 /* 961 * Ignoring return in non DEBUG mode because success is checked by 962 * verifying fjp and fdp and returned unit value is not used. 963 */ 964 if (!fjp || !fdp) 965 return (ENXIO); 966 part = PARTITION(dev); 967 968 sema_p(&fdp->d_ocsem); 969 FDERRPRINT(FDEP_L1, FDEM_CLOS, 970 (CE_CONT, "fd_close: fd unit %d part %d otype %x\n", 971 unit, part, otyp)); 972 973 if (otyp == OTYP_LYR) { 974 if (fdp->d_lyropen[part]) 975 fdp->d_lyropen[part]--; 976 part_is_closed = (fdp->d_lyropen[part] == 0); 977 } else { 978 fdp->d_regopen[otyp] &= ~(1<<part); 979 part_is_closed = 1; 980 } 981 if (part_is_closed) { 982 if (part == 2 && fdp->d_exclmask&(1<<part)) 983 fdp->d_exclmask = 0; 984 else 985 fdp->d_exclmask &= ~(1<<part); 986 FDERRPRINT(FDEP_L0, FDEM_CLOS, 987 (CE_CONT, 988 "fd_close: exclparts %lx openparts %lx lyrcnt %lx\n", 989 fdp->d_exclmask, fdp->d_regopen[otyp], 990 fdp->d_lyropen[part])); 991 992 if (fd_unit_is_open(fdp) == 0) 993 fdp->d_obj->fj_flags &= ~FUNIT_CHANGED; 994 } 995 sema_v(&fdp->d_ocsem); 996 return (0); 997 } 998 999 /* ARGSUSED */ 1000 static int 1001 fd_read(dev_t dev, struct uio *uio, cred_t *cred_p) 1002 { 1003 return (physio(