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                 /*
 520                  * We want to get suspend/resume events, so that we can
 521                  * refuse to suspend when pcfs is mounted.
 522                  */
 523                 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
 524                     "pm-hardware-state", "needs-suspend-resume");
 525 
 526                 /*
 527                  * Ignoring return value because, for passed arguments, only
 528                  * DDI_SUCCESS is returned.
 529                  */
 530                 ddi_report_dev(dip);
 531                 return (DDI_SUCCESS);
 532 
 533         case DDI_RESUME:
 534                 /* nothing for us to do */
 535                 return (DDI_SUCCESS);
 536 
 537         default:
 538                 return (DDI_FAILURE);
 539         }
 540 no_attach:
 541         fjp->fj_drive = NULL;
 542         fjp->fj_chars = NULL;
 543         fjp->fj_attr = NULL;
 544         mutex_destroy(&fjp->fj_lock);
 545         sema_destroy(&fdp->d_ocsem);
 546         ddi_soft_state_free(fd_state_head, drive_num);
 547         FDERRPRINT(FDEP_L3, FDEM_ATTA,
 548             (CE_WARN, "fd_attach failed: dip %p unit %d",
 549             (void *)dip, unit_num));
 550         return (DDI_FAILURE);
 551 }
 552 
 553 
 554 /* ARGSUSED */
 555 static int
 556 fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 557 {
 558         struct fcu_obj *fjp;
 559         struct fdisk *fdp;
 560         int drive_num;
 561         int rval = DDI_SUCCESS;
 562 
 563         FDERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fd_detach dip %p",
 564             (void *)dip));
 565 
 566         drive_num = ddi_get_instance(dip);
 567         if (!(fdp = ddi_get_soft_state(fd_state_head, drive_num)))
 568                 return (rval);
 569 
 570         switch (cmd) {
 571         case DDI_DETACH:
 572                 if (fd_unit_is_open(fdp)) {
 573                         rval = DDI_FAILURE;
 574                         break;
 575                 }
 576                 kstat_delete(fdp->d_iostat);
 577                 fdp->d_iostat = NULL;
 578                 fjp = (struct fcu_obj *)fdp->d_obj;
 579                 fjp->fj_flags &= ~FUNIT_DRVATCH;
 580                 fjp->fj_data = NULL;
 581                 fjp->fj_drive = NULL;
 582                 fjp->fj_chars = NULL;
 583                 fjp->fj_attr = NULL;
 584                 ddi_prop_remove_all(dip);
 585                 mutex_destroy(&fjp->fj_lock);
 586                 sema_destroy(&fdp->d_ocsem);
 587                 ddi_soft_state_free(fd_state_head, drive_num);
 588                 break;
 589 
 590         case DDI_SUSPEND:
 591                 /*
 592                  * Bad, bad, bad things will happen if someone
 593                  * *changes* the disk in the drive while it is mounted
 594                  * and the system is suspended.  We have no way to
 595                  * detect that.  (Undetected filesystem corruption.
 596                  * Its akin to changing the boot disk while the system
 597                  * is suspended.  Don't do it!)
 598                  *
 599                  * So we refuse to suspend if there is a mounted filesystem.
 600                  * (We guess this by looking for a block open.  Character
 601                  * opens are fine.)  This limits some of the usability of
 602                  * suspend/resume, but it certainly avoids this
 603                  * potential filesytem corruption from pilot error.
 604                  * Given the decreasing popularity of floppy media, we
 605                  * don't see this as much of a limitation.
 606                  */
 607                 if (fdp->d_regopen[OTYP_BLK]) {
 608                         cmn_err(CE_NOTE,
 609                             "Unable to suspend while floppy is in use.");
 610                         rval = DDI_FAILURE;
 611                 }
 612                 break;
 613 
 614         default:
 615                 rval = DDI_FAILURE;
 616                 break;
 617         }
 618         return (rval);
 619 }
 620 
 621 
 622 static int
 623 fd_part_is_open(struct fdisk *fdp, int part)
 624 {
 625         int i;
 626 
 627         for (i = 0; i < (OTYPCNT - 1); i++)
 628                 if (fdp->d_regopen[i] & (1 << part))
 629                         return (1);
 630         return (0);
 631 }
 632 
 633 static int
 634 fd_unit_is_open(struct fdisk *fdp)
 635 {
 636         int i;
 637 
 638         for (i = 0; i < NDKMAP; i++)
 639                 if (fdp->d_lyropen[i])
 640                         return (1);
 641         for (i = 0; i < (OTYPCNT - 1); i++)
 642                 if (fdp->d_regopen[i])
 643                         return (1);
 644         return (0);
 645 }
 646 
 647 /*ARGSUSED*/
 648 static int
 649 fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
 650 {
 651         struct fcu_obj *fjp = NULL;
 652         struct fdisk *fdp = NULL;
 653         struct partition *pp;
 654         dev_t dev;
 655         int part, unit;
 656         int part_is_open;
 657         int rval;
 658         uint_t pbit;
 659 
 660         dev = *devp;
 661         unit = fd_getdrive(dev, &fjp, &fdp);
 662         if (!fjp || !fdp)
 663                 return (ENXIO);
 664         part = PARTITION(dev);
 665         pbit = 1 << part;
 666         pp = &fdp->d_part[part];
 667 
 668         /*
 669          * Serialize opens/closes
 670          */
 671         sema_p(&fdp->d_ocsem);
 672         FDERRPRINT(FDEP_L1, FDEM_OPEN,
 673             (CE_CONT, "fd_open: fd%d part %d flag %x otype %x\n", DRIVE(dev),
 674             part, flag, otyp));
 675 
 676         /*
 677          * Check for previous exclusive open, or trying to exclusive open
 678          * An "exclusive open" on any partition is not guaranteed to
 679          * protect against opens on another partition that overlaps it.
 680          */
 681         if (otyp == OTYP_LYR) {
 682                 part_is_open = (fdp->d_lyropen[part] != 0);
 683         } else {
 684                 part_is_open = fd_part_is_open(fdp, part);
 685         }
 686         if ((fdp->d_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
 687                 FDERRPRINT(FDEP_L0, FDEM_OPEN, (CE_CONT,
 688                     "fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n",
 689                     fdp->d_exclmask, fdp->d_regopen[otyp], fdp->d_lyropen[part],
 690                     pbit));
 691                 sema_v(&fdp->d_ocsem);
 692                 return (EBUSY);
 693         }
 694 
 695         /*
 696          * Ensure that drive is recalibrated on first open of new diskette.
 697          */
 698         fjp->fj_ops->fco_select(fjp, unit, 1);
 699         if (fjp->fj_ops->fco_getchng(fjp, unit) != 0) {
 700                 if (fjp->fj_ops->fco_rcseek(fjp, unit, -1, 0)) {
 701                         FDERRPRINT(FDEP_L2, FDEM_OPEN,
 702                             (CE_NOTE, "fd_open fd%d: not ready", DRIVE(dev)));
 703                         fjp->fj_ops->fco_select(fjp, unit, 0);
 704                         sema_v(&fdp->d_ocsem);
 705                         return (ENXIO);
 706                 }
 707                 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
 708         }
 709         if (flag & (FNDELAY | FNONBLOCK)) {
 710                 /* don't attempt access, just return successfully */
 711                 fjp->fj_ops->fco_select(fjp, unit, 0);
 712                 goto out;
 713         }
 714 
 715         /*
 716          * auto-sense the density/format of the diskette
 717          */
 718         rval = fdgetlabel(fjp, unit);
 719         fjp->fj_ops->fco_select(fjp, unit, 0);
 720         if (rval) {
 721                 /* didn't find label (couldn't read anything) */
 722                 FDERRPRINT(FDEP_L2, FDEM_OPEN,
 723                     (CE_NOTE, "fd%d: drive not ready", DRIVE(dev)));
 724                 sema_v(&fdp->d_ocsem);
 725                 return (EIO);
 726         }
 727         /* check partition */
 728         if (pp->p_size == 0) {
 729                 sema_v(&fdp->d_ocsem);
 730                 return (ENXIO);
 731         }
 732         /*
 733          * if opening for writing, check write protect on diskette
 734          */
 735         if ((flag & FWRITE) && (fdp->d_obj->fj_flags & FUNIT_WPROT)) {
 736                 sema_v(&fdp->d_ocsem);
 737                 return (EROFS);
 738         }
 739 
 740 out:
 741         /*
 742          * mark open as having succeeded
 743          */
 744         if (flag & FEXCL)
 745                 fdp->d_exclmask |= pbit;
 746         if (otyp == OTYP_LYR)
 747                 fdp->d_lyropen[part]++;
 748         else
 749                 fdp->d_regopen[otyp] |= 1 << part;
 750 
 751         sema_v(&fdp->d_ocsem);
 752         return (0);
 753 }
 754 
 755 /*
 756  * fdgetlabel - read the SunOS label off the diskette
 757  *      if it can read a valid label it does so, else it will use a
 758  *      default.  If it can`t read the diskette - that is an error.
 759  *
 760  * RETURNS: 0 for ok - meaning that it could at least read the device,
 761  *      !0 for error XXX TBD NYD error codes
 762  */
 763 static int
 764 fdgetlabel(struct fcu_obj *fjp, int unit)
 765 {
 766         struct dk_label *label;
 767         struct fdisk *fdp;
 768         char *newlabel;
 769         short *sp;
 770         short count;
 771         short xsum;
 772         int tries, try_this;
 773         uint_t nexttype;
 774         int rval;
 775         short oldlvl;
 776         int i;
 777 
 778         FDERRPRINT(FDEP_L0, FDEM_GETL,
 779             (CE_CONT, "fdgetlabel fd unit %d\n", unit));
 780         fdp = (struct fdisk *)fjp->fj_data;
 781         fjp->fj_flags &= ~(FUNIT_UNLABELED);
 782 
 783         /*
 784          * get some space to play with the label
 785          */
 786         label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
 787         FDERRPRINT(FDEP_L0, FDEM_GETL, (CE_CONT,
 788             "fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n",
 789             unit, (void *)label, (size_t)sizeof (struct dk_label)));
 790 
 791         /*
 792          * read block 0 (0/0/1) to find the label
 793          * (disk is potentially not present or unformatted)
 794          */
 795         /* noerrprint since this is a private cmd */
 796         oldlvl = fderrlevel;
 797         fderrlevel = FDEP_LMAX;
 798         /*
 799          * try different characteristics (ie densities)
 800          *
 801          * if fdp->d_curfdtype is -1 then the current characteristics
 802          * were set by ioctl and need to try it as well as everything
 803          * in the table
 804          */
 805         nexttype = fdp->d_deffdtype;
 806         try_this = 1;           /* always try the current characteristics */
 807 
 808         for (tries = nfdtypes; tries; tries--) {
 809                 if (try_this) {
 810                         fjp->fj_flags &= ~FUNIT_CHAROK;
 811 
 812                         /* try reading last sector of cyl 1, head 0 */
 813                         if (!(rval = fjp->fj_ops->fco_rw(fjp, unit,
 814                             FDREAD, 1, 0, fjp->fj_chars->fdc_secptrack,
 815                             (caddr_t)label,
 816                             sizeof (struct dk_label))) &&
 817                             /* and last sector plus 1 of cylinder 1 */
 818                             fjp->fj_ops->fco_rw(fjp, unit, FDREAD, 1,
 819                             0, fjp->fj_chars->fdc_secptrack + 1,
 820                             (caddr_t)label,
 821                             sizeof (struct dk_label)) &&
 822                             /* and label sector on cylinder 0 */
 823                             !(rval = fjp->fj_ops->fco_rw(fjp, unit,
 824                             FDREAD, 0, 0, 1, (caddr_t)label,
 825                             sizeof (struct dk_label))))
 826                                 break;
 827                         if (rval == ENXIO)
 828                                 break;
 829                 }
 830                 /*
 831                  * try the next entry in the characteristics tbl
 832                  */
 833                 fdp->d_curfdtype = (signed char)nexttype;
 834                 nexttype = (nexttype + 1) % nfdtypes;
 835                 if ((1 << fdp->d_curfdtype) & fdp->d_media) {
 836                         *fjp->fj_chars = *defchar[fdp->d_curfdtype];
 837                         *fjp->fj_attr = fdtypes[fdp->d_curfdtype];
 838                         bcopy(fdparts[fdp->d_curfdtype], fdp->d_part,
 839                             sizeof (struct partition) * NDKMAP);
 840                         /*
 841                          * check for a double_density diskette
 842                          * in a high_density 5.25" drive
 843                          */
 844                         if (fjp->fj_chars->fdc_transfer_rate == 250 &&
 845                             fjp->fj_rotspd > fjp->fj_attr->fda_rotatespd) {
 846                                 /*
 847                                  * yes - adjust transfer rate since we don't
 848                                  * know if we have a 5.25" dual-speed drive
 849                                  */
 850                                 fjp->fj_attr->fda_rotatespd = 360;
 851                                 fjp->fj_chars->fdc_transfer_rate = 300;
 852                                 fjp->fj_chars->fdc_medium = 5;
 853                         }
 854                         if ((2 * fjp->fj_chars->fdc_ncyl) ==
 855                             defchar[fdp->d_deffdtype]->fdc_ncyl) {
 856                                 /* yes - adjust steps per cylinder */
 857                                 fjp->fj_chars->fdc_steps = 2;
 858                         } else
 859                                 fjp->fj_chars->fdc_steps = 1;
 860                         try_this = 1;
 861                 } else
 862                         try_this = 0;
 863         }
 864         fderrlevel = oldlvl;    /* print errors again */
 865 
 866         if (rval) {
 867                 fdp->d_curfdtype = fdp->d_deffdtype;
 868                 goto out;                       /* couldn't read anything */
 869         }
 870 
 871         FDERRPRINT(FDEP_L0, FDEM_GETL,
 872             (CE_CONT,
 873             "fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n",
 874             unit, fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_secptrack,
 875             fjp->fj_chars->fdc_steps, fjp->fj_attr->fda_rotatespd,
 876             fjp->fj_attr->fda_intrlv));
 877 
 878         /*
 879          * _something_ was read  -  look for unixtype label
 880          */
 881         if (label->dkl_magic != DKL_MAGIC ||
 882             label->dkl_vtoc.v_sanity != VTOC_SANE) {
 883                 /* not a label - no magic number */
 884                 goto nolabel;   /* no errors, but no label */
 885         }
 886 
 887         count = sizeof (struct dk_label) / sizeof (short);
 888         sp = (short *)label;
 889         xsum = 0;
 890         while (count--)
 891                 xsum ^= *sp++;  /* should add up to 0 */
 892         if (xsum) {
 893                 /* not a label - checksum didn't compute */
 894                 goto nolabel;   /* no errors, but no label */
 895         }
 896 
 897         /*
 898          * the SunOS label overrides current diskette characteristics
 899          */
 900         fjp->fj_chars->fdc_ncyl = label->dkl_pcyl;
 901         fjp->fj_chars->fdc_nhead = label->dkl_nhead;
 902         fjp->fj_chars->fdc_secptrack = (label->dkl_nsect * DEV_BSIZE) /
 903             fjp->fj_chars->fdc_sec_size;
 904         if (defchar[fdp->d_deffdtype]->fdc_ncyl == 2 * fjp->fj_chars->fdc_ncyl)
 905                 fjp->fj_chars->fdc_steps = 2;
 906         else
 907                 fjp->fj_chars->fdc_steps = 1;
 908 
 909         fjp->fj_attr->fda_rotatespd = label->dkl_rpm;
 910         fjp->fj_attr->fda_intrlv = label->dkl_intrlv;
 911 
 912         fdp->d_vtoc_version = label->dkl_vtoc.v_version;
 913         bcopy(label->dkl_vtoc.v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
 914         bcopy(label->dkl_vtoc.v_asciilabel,
 915             fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
 916         /*
 917          * logical partitions
 918          */
 919         for (i = 0; i < NDKMAP; i++) {
 920                 fdp->d_part[i].p_tag = label->dkl_vtoc.v_part[i].p_tag;
 921                 fdp->d_part[i].p_flag = label->dkl_vtoc.v_part[i].p_flag;
 922                 fdp->d_part[i].p_start = label->dkl_vtoc.v_part[i].p_start;
 923                 fdp->d_part[i].p_size = label->dkl_vtoc.v_part[i].p_size;
 924 
 925                 fdp->d_vtoc_timestamp[i] = label->dkl_vtoc.timestamp[i];
 926         }
 927 
 928         fjp->fj_flags |= FUNIT_LABELOK;
 929         goto out;
 930 
 931 nolabel:
 932         /*
 933          * if not found, fill in label info from default (mark default used)
 934          */
 935         if (fdp->d_media & (1<<FMT_3D))
 936                 newlabel = deflabel_35;
 937         else /* if (fdp->d_media & (1<<FMT_5D9)) */
 938                 newlabel = deflabel_525;
 939         bzero(fdp->d_vtoc_volume, LEN_DKL_VVOL);
 940         (void) sprintf(fdp->d_vtoc_asciilabel, newlabel,
 941             fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_nhead,
 942             fjp->fj_chars->fdc_secptrack);
 943         fjp->fj_flags |= FUNIT_UNLABELED;
 944 
 945 out:
 946         kmem_free(label, sizeof (struct dk_label));
 947         return (rval);
 948 }
 949 
 950 
 951 /*ARGSUSED*/
 952 static int
 953 fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
 954 {
 955         struct fcu_obj *fjp = NULL;
 956         struct fdisk *fdp = NULL;
 957         int part, part_is_closed;
 958 
 959 #ifdef DEBUG
 960         int unit;
 961 #define DEBUG_ASSIGN    unit=
 962 #else
 963 #define DEBUG_ASSIGN    (void)
 964 #endif
 965 
 966         DEBUG_ASSIGN fd_getdrive(dev, &fjp, &fdp);
 967         /*
 968          * Ignoring return in non DEBUG mode because success is checked by
 969          * verifying fjp and fdp and returned unit value is not used.
 970          */
 971         if (!fjp || !fdp)
 972                 return (ENXIO);
 973         part = PARTITION(dev);
 974 
 975         sema_p(&fdp->d_ocsem);
 976         FDERRPRINT(FDEP_L1, FDEM_CLOS,
 977             (CE_CONT, "fd_close: fd unit %d part %d otype %x\n",
 978             unit, part, otyp));
 979 
 980         if (otyp == OTYP_LYR) {
 981                 if (fdp->d_lyropen[part])
 982                         fdp->d_lyropen[part]--;
 983                 part_is_closed = (fdp->d_lyropen[part] == 0);
 984         } else {
 985                 fdp->d_regopen[otyp] &= ~(1<<part);
 986                 part_is_closed = 1;
 987         }
 988         if (part_is_closed) {
 989                 if (part == 2 && fdp->d_exclmask&(1<<part))
 990                         fdp->d_exclmask = 0;
 991                 else
 992                         fdp->d_exclmask &= ~(1<<part);
 993                 FDERRPRINT(FDEP_L0, FDEM_CLOS,
 994                     (CE_CONT,
 995                     "fd_close: exclparts %lx openparts %lx lyrcnt %lx\n",
 996                     fdp->d_exclmask, fdp->d_regopen[otyp],
 997                     fdp->d_lyropen[part]));
 998 
 999                 if (fd_unit_is_open(fdp) == 0)
1000                         fdp->d_obj->fj_flags &= ~FUNIT_CHANGED;
1001         }
1002         sema_v(&fdp->d_ocsem);
1003         return (0);
1004 }
1005 
1006 /* ARGSUSED */
1007 static int
1008 fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
1009 {
1010         return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
1011 }
1012 
1013 /* ARGSUSED */
1014 static int
1015 fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
1016 {
1017         return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
1018 }
1019 
1020 /*
1021  * fd_strategy
1022  *      checks operation, hangs buf struct off fdcntlr, calls fdstart
1023  *      if not already busy.  Note that if we call start, then the operation
1024  *      will already be done on return (start sleeps).
1025  */
1026 static int
1027 fd_strategy(struct buf *bp)
1028 {
1029         struct fcu_obj *fjp;
1030         struct fdisk *fdp;
1031         struct partition *pp;
1032 
1033         FDERRPRINT(FDEP_L1, FDEM_STRA,
1034             (CE_CONT, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
1035             (void *)bp, bp->b_edev));
1036 
1037         (void) fd_getdrive(bp->b_edev, &fjp, &fdp);
1038 
1039         /*
1040          * Ignoring return because device exist.
1041          * Returned unit value is not used.
1042          */
1043         pp = &fdp->d_part[PARTITION(bp->b_edev)];
1044 
1045         if (fjp->fj_chars->fdc_sec_size > NBPSCTR && (bp->b_blkno & 1))  {
1046                 FDERRPRINT(FDEP_L3, FDEM_STRA,
1047                     (CE_WARN, "fd%d: block %ld is not start of sector!",
1048                     DRIVE(bp->b_edev), (long)bp->b_blkno));
1049                 bp->b_error = EINVAL;
1050                 goto bad;
1051         }
1052 
1053         if ((bp->b_blkno > pp->p_size)) {
1054                 FDERRPRINT(FDEP_L3, FDEM_STRA,
1055                     (CE_WARN, "fd%d: block %ld is past the end! (nblk=%ld)",
1056                     DRIVE(bp->b_edev), (long)bp->b_blkno, pp->p_size));
1057                 bp->b_error = ENOSPC;
1058                 goto bad;
1059         }
1060 
1061         /* if at end of file, skip out now */
1062         if (bp->b_blkno == pp->p_size) {
1063                 if ((bp->b_flags & B_READ) == 0) {
1064                         /* a write needs to get an error! */
1065                         bp->b_error = ENOSPC;
1066                         goto bad;
1067                 }
1068                 bp->b_resid = bp->b_bcount;
1069                 biodone(bp);
1070                 return (0);
1071         }
1072 
1073         /* if operation not a multiple of sector size, is error! */
1074         if (bp->b_bcount % fjp->fj_chars->fdc_sec_size)  {
1075                 FDERRPRINT(FDEP_L3, FDEM_STRA,
1076                     (CE_WARN, "fd%d: count %ld must be a multiple of %d",
1077                     DRIVE(bp->b_edev), bp->b_bcount,
1078                     fjp->fj_chars->fdc_sec_size));
1079                 bp->b_error = EINVAL;
1080                 goto bad;
1081         }
1082 
1083         /*
1084          * Put the buf request in the drive's queue, FIFO.
1085          */
1086         bp->av_forw = 0;
1087         mutex_enter(&fjp->fj_lock);
1088         if (fdp->d_iostat)
1089                 kstat_waitq_enter(KIOSP);
1090         if (fdp->d_actf)
1091                 fdp->d_actl->av_forw = bp;
1092         else
1093                 fdp->d_actf = bp;
1094         fdp->d_actl = bp;
1095         if (!(fjp->fj_flags & FUNIT_BUSY)) {
1096                 fdstart(fjp);
1097         }
1098         mutex_exit(&fjp->fj_lock);
1099         return (0);
1100 
1101 bad:
1102         bp->b_resid = bp->b_bcount;
1103         bp->b_flags |= B_ERROR;
1104         biodone(bp);
1105         return (0);
1106 }
1107 
1108 /*
1109  * fdstart
1110  *      called from fd_strategy() or from fdXXXX() to setup and
1111  *      start operations of read or write only (using buf structs).
1112  *      Because the chip doesn't handle crossing cylinder boundaries on
1113  *      the fly, this takes care of those boundary conditions.  Note that
1114  *      it sleeps until the operation is done *within fdstart* - so that
1115  *      when fdstart returns, the operation is already done.
1116  */
1117 static void
1118 fdstart(struct fcu_obj *fjp)
1119 {
1120         struct buf *bp;
1121         struct fdisk *fdp = (struct fdisk *)fjp->fj_data;
1122         struct fd_char *chp;
1123         struct partition *pp;
1124         uint_t ptend;
1125         uint_t bincyl;          /* (the number of the desired) block in cyl. */
1126         uint_t blk, len, tlen;
1127         uint_t secpcyl;         /* number of sectors per cylinder */
1128         int cyl, head, sect;
1129         int sctrshft, unit;
1130         caddr_t addr;
1131 
1132         ASSERT(MUTEX_HELD(&fjp->fj_lock));
1133         fjp->fj_flags |= FUNIT_BUSY;
1134 
1135         while ((bp = fdp->d_actf) != NULL) {
1136                 fdp->d_actf = bp->av_forw;
1137                 fdp->d_current = bp;
1138                 if (fdp->d_iostat) {
1139                         kstat_waitq_to_runq(KIOSP);
1140                 }
1141                 mutex_exit(&fjp->fj_lock);
1142 
1143                 FDERRPRINT(FDEP_L0, FDEM_STRT,
1144                     (CE_CONT, "fdstart: bp=0x%p blkno=0x%lx bcount=0x%lx\n",
1145                     (void *)bp, (long)bp->b_blkno, bp->b_bcount));
1146                 bp->b_flags &= ~B_ERROR;
1147                 bp->b_error = 0;
1148                 bp->b_resid = bp->b_bcount;       /* init resid */
1149 
1150                 ASSERT(DRIVE(bp->b_edev) == ddi_get_instance(fjp->fj_dip));
1151                 unit = fjp->fj_unit;
1152                 fjp->fj_ops->fco_select(fjp, unit, 1);
1153 
1154                 bp_mapin(bp);                   /* map in buffers */
1155 
1156                 pp = &fdp->d_part[PARTITION(bp->b_edev)];
1157                 /* starting blk adjusted for the partition */
1158                 blk = bp->b_blkno + pp->p_start;
1159                 ptend = pp->p_start + pp->p_size;   /* end of the partition */
1160 
1161                 chp = fjp->fj_chars;
1162                 secpcyl = chp->fdc_nhead * chp->fdc_secptrack;
1163                 switch (chp->fdc_sec_size) {
1164                 /* convert logical block numbers to sector numbers */
1165                 case 1024:
1166                         sctrshft = SCTRSHFT + 1;
1167                         blk >>= 1;
1168                         ptend >>= 1;
1169                         break;
1170                 default:
1171                 case NBPSCTR:
1172                         sctrshft = SCTRSHFT;
1173                         break;
1174                 case 256:
1175                         sctrshft = SCTRSHFT - 1;
1176                         blk <<= 1;
1177                         ptend <<= 1;
1178                         break;
1179                 }
1180 
1181                 /*
1182                  * If off the end, limit to actual amount that
1183                  * can be transferred.
1184                  */
1185                 if ((blk + (bp->b_bcount >> sctrshft)) > ptend)
1186                         /* to end of partition */
1187                         len = (ptend - blk) << sctrshft;
1188                 else
1189                         len = bp->b_bcount;
1190                 addr = bp->b_un.b_addr;              /* data buffer address */
1191 
1192                 /*
1193                  * now we have the real start blk, addr and len for xfer op
1194                  */
1195                 while (len != 0) {
1196                         /* start cyl of req */
1197                         cyl = blk / secpcyl;
1198                         bincyl = blk % secpcyl;
1199                         /* start head of req */
1200                         head = bincyl / chp->fdc_secptrack;
1201                         /* start sector of req */
1202                         sect = (bincyl % chp->fdc_secptrack) + 1;
1203                         /*
1204                          * If the desired block and length will go beyond the
1205                          * cylinder end, then limit it to the cylinder end.
1206                          */
1207                         if (bp->b_flags & B_READ) {
1208                                 if (len > ((secpcyl - bincyl) << sctrshft))
1209                                         tlen = (secpcyl - bincyl) << sctrshft;
1210                                 else
1211                                         tlen = len;
1212                         } else {
1213                                 if (len >
1214                                     ((chp->fdc_secptrack - sect + 1) <<
1215                                     sctrshft))
1216                                         tlen =
1217                                             (chp->fdc_secptrack - sect + 1) <<
1218                                             sctrshft;
1219                                 else
1220                                         tlen = len;
1221                         }
1222 
1223                         FDERRPRINT(FDEP_L0, FDEM_STRT, (CE_CONT,
1224                             "  blk 0x%x addr 0x%p len 0x%x "
1225                             "cyl %d head %d sec %d\n  resid 0x%lx, tlen %d\n",
1226                             blk, (void *)addr, len, cyl, head, sect,
1227                             bp->b_resid, tlen));
1228 
1229                         /*
1230                          * (try to) do the operation - failure returns an errno
1231                          */
1232                         bp->b_error = fjp->fj_ops->fco_rw(fjp, unit,
1233                             bp->b_flags & B_READ, cyl, head, sect, addr, tlen);
1234                         if (bp->b_error != 0) {
1235                                 FDERRPRINT(FDEP_L3, FDEM_STRT, (CE_WARN,
1236                                     "fdstart: bad exec of bp: 0x%p, err=%d",
1237                                     (void *)bp, bp->b_error));
1238                                 bp->b_flags |= B_ERROR;
1239                                 break;
1240                         }
1241                         blk += tlen >> sctrshft;
1242                         len -= tlen;
1243                         addr += tlen;
1244                         bp->b_resid -= tlen;
1245                 }
1246                 FDERRPRINT(FDEP_L0, FDEM_STRT,
1247                     (CE_CONT, "fdstart done: b_resid %lu, b_count %lu\n",
1248                     bp->b_resid, bp->b_bcount));
1249                 if (fdp->d_iostat) {
1250                         if (bp->b_flags & B_READ) {
1251                                 KIOSP->reads++;
1252                                 KIOSP->nread += (bp->b_bcount - bp->b_resid);
1253                         } else {
1254                                 KIOSP->writes++;
1255                                 KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
1256                         }
1257                         kstat_runq_exit(KIOSP);
1258                 }
1259                 bp_mapout(bp);
1260                 biodone(bp);
1261 
1262                 fjp->fj_ops->fco_select(fjp, unit, 0);
1263                 mutex_enter(&fjp->fj_lock);
1264                 fdp->d_current = 0;
1265         }
1266         fjp->fj_flags ^= FUNIT_BUSY;
1267 }
1268 
1269 /* ARGSUSED */
1270 static int
1271 fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
1272         int *rval_p)
1273 {
1274         union {
1275                 struct dk_cinfo dki;
1276                 struct dk_geom dkg;
1277                 struct dk_allmap dka;
1278                 struct fd_char fdchar;
1279                 struct fd_drive drvchar;
1280                 int     temp;
1281         } cpy;
1282         struct vtoc vtoc;
1283         struct fcu_obj *fjp = NULL;
1284         struct fdisk *fdp = NULL;
1285         struct dk_map *dmp;
1286         struct dk_label *label;
1287         int nblks, part, unit;
1288         int rval = 0;
1289         enum dkio_state state;
1290 
1291         unit = fd_getdrive(dev, &fjp, &fdp);
1292         if (!fjp || !fdp)
1293                 return (ENXIO);
1294 
1295         FDERRPRINT(FDEP_L1, FDEM_IOCT,
1296             (CE_CONT, "fd_ioctl fd unit %d: cmd %x, arg %lx\n",
1297             unit, cmd, arg));
1298 
1299         switch (cmd) {
1300         case DKIOCINFO:
1301                 fjp->fj_ops->fco_dkinfo(fjp, &cpy.dki);
1302                 cpy.dki.dki_cnum = FDCTLR(fjp->fj_unit);
1303                 cpy.dki.dki_unit = FDUNIT(fjp->fj_unit);
1304                 cpy.dki.dki_partition = PARTITION(dev);
1305                 if (ddi_copyout(&cpy.dki, (void *)arg, sizeof (cpy.dki), flag))
1306                         rval = EFAULT;
1307                 break;
1308 
1309         case DKIOCG_PHYGEOM:
1310         case DKIOCG_VIRTGEOM:
1311                 cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1312                 goto get_geom;
1313         case DKIOCGGEOM:
1314                 if (fjp->fj_flags & FUNIT_LABELOK)
1315                         cpy.dkg.dkg_nsect = (fjp->fj_chars->fdc_secptrack *
1316                             fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1317                 else
1318                         cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1319 get_geom:
1320                 cpy.dkg.dkg_pcyl = fjp->fj_chars->fdc_ncyl;
1321                 cpy.dkg.dkg_ncyl = fjp->fj_chars->fdc_ncyl;
1322                 cpy.dkg.dkg_nhead = fjp->fj_chars->fdc_nhead;
1323                 cpy.dkg.dkg_intrlv = fjp->fj_attr->fda_intrlv;
1324                 cpy.dkg.dkg_rpm = fjp->fj_attr->fda_rotatespd;
1325                 cpy.dkg.dkg_read_reinstruct =
1326                     (int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
1327                 cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
1328                 if (ddi_copyout(&cpy.dkg, (void *)arg, sizeof (cpy.dkg), flag))
1329                         rval = EFAULT;
1330                 break;
1331 
1332         case DKIOCSGEOM:
1333                 if (ddi_copyin((void *)arg, &cpy.dkg,
1334                     sizeof (struct dk_geom), flag)) {
1335                         rval = EFAULT;
1336                         break;
1337                 }
1338                 mutex_enter(&fjp->fj_lock);
1339                 fjp->fj_chars->fdc_ncyl = cpy.dkg.dkg_ncyl;
1340                 fjp->fj_chars->fdc_nhead = cpy.dkg.dkg_nhead;
1341                 fjp->fj_chars->fdc_secptrack = cpy.dkg.dkg_nsect;
1342                 fjp->fj_attr->fda_intrlv = cpy.dkg.dkg_intrlv;
1343                 fjp->fj_attr->fda_rotatespd = cpy.dkg.dkg_rpm;
1344                 fdp->d_curfdtype = -1;
1345                 mutex_exit(&fjp->fj_lock);
1346                 break;
1347 
1348         /*
1349          * return the map of all logical partitions
1350          */
1351         case DKIOCGAPART:
1352                 /*
1353                  * Note the conversion from starting sector number
1354                  * to starting cylinder number.
1355                  * Return error if division results in a remainder.
1356                  */
1357                 nblks = fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack;
1358 
1359 #ifdef _MULTI_DATAMODEL
1360                 switch (ddi_model_convert_from(flag & FMODELS)) {
1361                 case DDI_MODEL_ILP32:
1362                 {
1363                         struct dk_allmap32 dka32;
1364 
1365                         for (part = 0; part < NDKMAP; part++) {
1366                                 if ((fdp->d_part[part].p_start % nblks) != 0)
1367                                         return (EINVAL);
1368                                 dka32.dka_map[part].dkl_cylno =
1369                                     fdp->d_part[part].p_start / nblks;
1370                                 dka32.dka_map[part].dkl_nblk =
1371                                     fdp->d_part[part].p_size;
1372                         }
1373 
1374                         if (ddi_copyout(&dka32, (void *)arg,
1375                             sizeof (struct dk_allmap32), flag))
1376                                 rval = EFAULT;
1377 
1378                         break;
1379                 }
1380                 case DDI_MODEL_NONE:
1381 
1382 #endif /* _MULTI_DATAMODEL */
1383 
1384                         dmp = (struct dk_map *)&cpy.dka;
1385                         for (part = 0; part < NDKMAP; part++) {
1386                                 if ((fdp->d_part[part].p_start % nblks) != 0)
1387                                         return (EINVAL);
1388                                 dmp->dkl_cylno =
1389                                     fdp->d_part[part].p_start / nblks;
1390                                 dmp->dkl_nblk = fdp->d_part[part].p_size;
1391                                 dmp++;
1392                         }
1393 
1394                         if (ddi_copyout(&cpy.dka, (void *)arg,
1395                             sizeof (struct dk_allmap), flag))
1396                                 rval = EFAULT;
1397 #ifdef _MULTI_DATAMODEL
1398                         break;
1399 
1400                 }
1401 #endif /* _MULTI_DATAMODEL */
1402 
1403                 break;
1404 
1405         /*
1406          * Set the map of all logical partitions
1407          */
1408         case DKIOCSAPART:
1409 
1410 #ifdef _MULTI_DATAMODEL
1411                 switch (ddi_model_convert_from(flag & FMODELS)) {
1412                 case DDI_MODEL_ILP32:
1413                 {
1414                         struct dk_allmap32 dka32;
1415 
1416                         if (ddi_copyin((void *)arg, &dka32,
1417                             sizeof (dka32), flag)) {
1418                                 rval = EFAULT;
1419                                 break;
1420                         }
1421                         for (part = 0; part < NDKMAP; part++) {
1422                                 cpy.dka.dka_map[part].dkl_cylno =
1423                                     dka32.dka_map[part].dkl_cylno;
1424                                 cpy.dka.dka_map[part].dkl_nblk =
1425                                     dka32.dka_map[part].dkl_nblk;
1426                         }
1427                         break;
1428                 }
1429                 case DDI_MODEL_NONE:
1430 
1431 #endif /* _MULTI_DATAMODEL */
1432                 if (ddi_copyin((void *)arg, &cpy.dka, sizeof (cpy.dka), flag))
1433                         rval = EFAULT;
1434 #ifdef _MULTI_DATAMODEL
1435 
1436                         break;
1437                 }
1438 #endif /* _MULTI_DATAMODEL */
1439 
1440                 if (rval != 0)
1441                         break;
1442 
1443                 dmp = (struct dk_map *)&cpy.dka;
1444                 nblks = fjp->fj_chars->fdc_nhead *
1445                     fjp->fj_chars->fdc_secptrack;
1446                 mutex_enter(&fjp->fj_lock);
1447                 /*
1448                  * Note the conversion from starting cylinder number
1449                  * to starting sector number.
1450                  */
1451                 for (part = 0; part < NDKMAP; part++) {
1452                         fdp->d_part[part].p_start = dmp->dkl_cylno *
1453                             nblks;
1454                         fdp->d_part[part].p_size = dmp->dkl_nblk;
1455                         dmp++;
1456                 }
1457                 mutex_exit(&fjp->fj_lock);
1458 
1459                 break;
1460 
1461         case DKIOCGVTOC:
1462                 mutex_enter(&fjp->fj_lock);
1463 
1464                 /*
1465                  * Exit if the diskette has no label.
1466                  * Also, get the label to make sure the correct one is
1467                  * being used since the diskette may have changed
1468                  */
1469                 fjp->fj_ops->fco_select(fjp, unit, 1);
1470                 rval = fdgetlabel(fjp, unit);
1471                 fjp->fj_ops->fco_select(fjp, unit, 0);
1472                 if (rval) {
1473                         mutex_exit(&fjp->fj_lock);
1474                         rval = EINVAL;
1475                         break;
1476                 }
1477 
1478                 fd_build_user_vtoc(fjp, fdp, &vtoc);
1479                 mutex_exit(&fjp->fj_lock);
1480 
1481 #ifdef _MULTI_DATAMODEL
1482                 switch (ddi_model_convert_from(flag & FMODELS)) {
1483                 case DDI_MODEL_ILP32:
1484                 {
1485                         struct vtoc32   vtoc32;
1486 
1487                         vtoctovtoc32(vtoc, vtoc32);
1488 
1489                         if (ddi_copyout(&vtoc32, (void *)arg,
1490                             sizeof (vtoc32), flag))
1491                                 rval = EFAULT;
1492 
1493                         break;
1494                 }
1495                 case DDI_MODEL_NONE:
1496 
1497 #endif /* _MULTI_DATAMODEL */
1498                         if (ddi_copyout(&vtoc, (void *)arg,
1499                             sizeof (vtoc), flag))
1500                                 rval = EFAULT;
1501 #ifdef _MULTI_DATAMODEL
1502                         break;
1503                 }
1504 #endif /* _MULTI_DATAMODEL */
1505 
1506                 break;
1507 
1508         case DKIOCSVTOC:
1509 
1510 #ifdef _MULTI_DATAMODEL
1511                 switch (ddi_model_convert_from(flag & FMODELS)) {
1512                 case DDI_MODEL_ILP32:
1513                 {
1514                         struct vtoc32   vtoc32;
1515 
1516                         if (ddi_copyin((void *)arg, &vtoc32,
1517                             sizeof (vtoc32), flag)) {
1518                                 rval = EFAULT;
1519                                 break;
1520                         }
1521 
1522                         vtoc32tovtoc(vtoc32, vtoc);
1523 
1524                         break;
1525                 }
1526                 case DDI_MODEL_NONE:
1527 
1528 #endif /* _MULTI_DATAMODEL */
1529                         if (ddi_copyin((void *)arg, &vtoc, sizeof (vtoc), flag))
1530                                 rval = EFAULT;
1531 #ifdef _MULTI_DATAMODEL
1532                         break;
1533                 }
1534 #endif /* _MULTI_DATAMODEL */
1535 
1536                 if (rval != 0)
1537                         break;
1538 
1539 
1540                 label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
1541 
1542                 mutex_enter(&fjp->fj_lock);
1543 
1544                 if ((rval = fd_build_label_vtoc(fjp, fdp, &vtoc, label)) == 0) {
1545                         fjp->fj_ops->fco_select(fjp, unit, 1);
1546                         rval = fjp->fj_ops->fco_rw(fjp, unit, FDWRITE,
1547                             0, 0, 1, (caddr_t)label, sizeof (struct dk_label));
1548                         fjp->fj_ops->fco_select(fjp, unit, 0);
1549                 }
1550                 mutex_exit(&fjp->fj_lock);
1551                 kmem_free(label, sizeof (struct dk_label));
1552                 break;
1553 
1554         case DKIOCSTATE:
1555                 FDERRPRINT(FDEP_L1, FDEM_IOCT,
1556                     (CE_CONT, "fd_ioctl fd unit %d: DKIOCSTATE\n", unit));
1557 
1558                 if (ddi_copyin((void *)arg, &state, sizeof (int), flag)) {
1559                         rval = EFAULT;
1560                         break;
1561                 }
1562 
1563                 rval = fd_check_media(dev, state);
1564 
1565                 if (ddi_copyout(&fdp->d_media_state, (void *)arg,
1566                     sizeof (int), flag))
1567                         rval = EFAULT;
1568                 break;
1569 
1570         case FDIOGCHAR:
1571                 if (ddi_copyout(fjp->fj_chars, (void *)arg,
1572                     sizeof (struct fd_char), flag))
1573                         rval = EFAULT;
1574                 break;
1575 
1576         case FDIOSCHAR:
1577                 if (ddi_copyin((void *)arg, &cpy.fdchar,
1578                     sizeof (struct fd_char), flag)) {
1579                         rval = EFAULT;
1580                         break;
1581                 }
1582                 switch (cpy.fdchar.fdc_transfer_rate) {
1583                 case 417:
1584                         if ((fdp->d_media & (1 << FMT_3M)) == 0) {
1585                                 cmn_err(CE_CONT,
1586                                     "fdioschar:Medium density not supported\n");
1587                                 rval = EINVAL;
1588                                 break;
1589                         }
1590                         mutex_enter(&fjp->fj_lock);
1591                         fjp->fj_attr->fda_rotatespd = 360;
1592                         mutex_exit(&fjp->fj_lock);
1593                         /* cpy.fdchar.fdc_transfer_rate = 500; */
1594                         /* FALLTHROUGH */
1595                 case 1000:
1596                 case 500:
1597                 case 300:
1598                 case 250:
1599                         mutex_enter(&fjp->fj_lock);
1600                         *(fjp->fj_chars) = cpy.fdchar;
1601                         fdp->d_curfdtype = -1;
1602                         fjp->fj_flags &= ~FUNIT_CHAROK;
1603                         mutex_exit(&fjp->fj_lock);
1604 
1605                         break;
1606 
1607                 default:
1608                         FDERRPRINT(FDEP_L4, FDEM_IOCT,
1609                             (CE_WARN, "fd_ioctl fd unit %d: FDIOSCHAR odd "
1610                             "xfer rate %dkbs",
1611                             unit, cpy.fdchar.fdc_transfer_rate));
1612                         rval = EINVAL;
1613                         break;
1614                 }
1615                 break;
1616 
1617         /*
1618          * set all characteristics and geometry to the defaults
1619          */
1620         case FDDEFGEOCHAR:
1621                 mutex_enter(&fjp->fj_lock);
1622                 fdp->d_curfdtype = fdp->d_deffdtype;
1623                 *fjp->fj_chars = *defchar[fdp->d_curfdtype];
1624                 *fjp->fj_attr = fdtypes[fdp->d_curfdtype];
1625                 bcopy(fdparts[fdp->d_curfdtype],
1626                     fdp->d_part, sizeof (struct partition) * NDKMAP);
1627                 fjp->fj_flags &= ~FUNIT_CHAROK;
1628                 mutex_exit(&fjp->fj_lock);
1629                 break;
1630 
1631         case FDEJECT:  /* eject disk */
1632         case DKIOCEJECT:
1633                 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1634                 rval = ENOSYS;
1635                 break;
1636 
1637         case FDGETCHANGE: /* disk changed */
1638                 if (ddi_copyin((void *)arg, &cpy.temp, sizeof (int), flag)) {
1639                         rval = EFAULT;
1640                         break;
1641                 }
1642                 mutex_enter(&fjp->fj_lock);
1643                 fjp->fj_ops->fco_select(fjp, unit, 1);
1644 
1645                 if (fjp->fj_flags & FUNIT_CHANGED)
1646                         cpy.temp |= FDGC_HISTORY;
1647                 else
1648                         cpy.temp &= ~FDGC_HISTORY;
1649                 fjp->fj_flags &= ~FUNIT_CHANGED;
1650 
1651                 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1652                         cpy.temp |= FDGC_DETECTED;
1653                         fjp->fj_ops->fco_resetchng(fjp, unit);
1654                         /*
1655                          * check diskette again only if it was removed
1656                          */
1657                         if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1658                                 /*
1659                                  * no diskette is present
1660                                  */
1661                                 cpy.temp |= FDGC_CURRENT;
1662                                 if (fjp->fj_flags & FUNIT_CHGDET)
1663                                         /*
1664                                          * again no diskette; not a new change
1665                                          */
1666                                         cpy.temp ^= FDGC_DETECTED;
1667                                 else
1668                                         fjp->fj_flags |= FUNIT_CHGDET;
1669                         } else {
1670                                 /*
1671                                  * a new diskette is present
1672                                  */
1673                                 cpy.temp &= ~FDGC_CURRENT;
1674                                 fjp->fj_flags &= ~FUNIT_CHGDET;
1675                         }
1676                 } else {
1677                         cpy.temp &= ~(FDGC_DETECTED | FDGC_CURRENT);
1678                         fjp->fj_flags &= ~FUNIT_CHGDET;
1679                 }
1680                 /*
1681                  * also get state of write protection
1682                  */
1683                 if (fjp->fj_flags & FUNIT_WPROT) {
1684                         cpy.temp |= FDGC_CURWPROT;
1685                 } else {
1686                         cpy.temp &= ~FDGC_CURWPROT;
1687                 }
1688                 fjp->fj_ops->fco_select(fjp, unit, 0);
1689                 mutex_exit(&fjp->fj_lock);
1690 
1691                 if (ddi_copyout(&cpy.temp, (void *)arg, sizeof (int), flag))
1692                         rval = EFAULT;
1693                 break;
1694 
1695         case FDGETDRIVECHAR:
1696                 if (ddi_copyout(fjp->fj_drive, (void *)arg,
1697                     sizeof (struct fd_drive), flag))
1698                         rval = EFAULT;
1699                 break;
1700 
1701         case FDSETDRIVECHAR:
1702                 if (ddi_copyin((void *)arg, &cpy.drvchar,
1703                     sizeof (struct fd_drive), flag)) {
1704                         rval = EFAULT;
1705                         break;
1706                 }
1707                 mutex_enter(&fjp->fj_lock);
1708                 *(fjp->fj_drive) = cpy.drvchar;
1709                 fdp->d_curfdtype = -1;
1710                 fjp->fj_flags &= ~FUNIT_CHAROK;
1711                 mutex_exit(&fjp->fj_lock);
1712                 break;
1713 
1714         case DKIOCREMOVABLE: {
1715                 int     i = 1;
1716 
1717                 /* no brainer: floppies are always removable */
1718                 if (ddi_copyout(&i, (void *)arg, sizeof (int), flag)) {
1719                         rval = EFAULT;
1720                 }
1721                 break;
1722         }
1723 
1724         case DKIOCGMEDIAINFO:
1725                 rval = fd_get_media_info(fjp, (caddr_t)arg, flag);
1726                 break;
1727 
1728         case FDIOCMD:
1729         {
1730                 struct fd_cmd fc;
1731                 int cyl, head, spc, spt;
1732 
1733 #ifdef _MULTI_DATAMODEL
1734                 switch (ddi_model_convert_from(flag & FMODELS)) {
1735                 case DDI_MODEL_ILP32:
1736                 {
1737                         struct fd_cmd32 fc32;
1738 
1739                         if (ddi_copyin((void *)arg, &fc32,
1740                             sizeof (fc32), flag)) {
1741                                 rval = EFAULT;
1742                                 break;
1743                         }
1744 
1745                         fc.fdc_cmd = fc32.fdc_cmd;
1746                         fc.fdc_flags = fc32.fdc_flags;
1747                         fc.fdc_blkno = fc32.fdc_blkno;
1748                         fc.fdc_secnt = fc32.fdc_secnt;
1749                         fc.fdc_bufaddr = (caddr_t)(uintptr_t)fc32.fdc_bufaddr;
1750                         fc.fdc_buflen = fc32.fdc_buflen;
1751 
1752                         break;
1753                 }
1754                 case DDI_MODEL_NONE:
1755 
1756 #endif /* _MULTI_DATAMODEL */
1757 
1758                         if (ddi_copyin((void *)arg, &fc, sizeof (fc), flag)) {
1759                                 rval = EFAULT;
1760                                 break;
1761                         }
1762 #ifdef _MULTI_DATAMODEL
1763                         break;
1764                 }
1765 #endif /* _MULTI_DATAMODEL */
1766 
1767                 if (rval != 0)
1768                         break;
1769 
1770         if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
1771                         auto struct iovec aiov;
1772                         auto struct uio auio;
1773                         struct uio *uio = &auio;
1774 
1775                         spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
1776 
1777                         bzero(&auio, sizeof (struct uio));
1778                         bzero(&aiov, sizeof (struct iovec));
1779                         aiov.iov_base = fc.fdc_bufaddr;
1780                         aiov.iov_len = (uint_t)fc.fdc_secnt *
1781                             fjp->fj_chars->fdc_sec_size;
1782                         uio->uio_iov = &aiov;
1783 
1784                         uio->uio_iovcnt = 1;
1785                         uio->uio_resid = aiov.iov_len;
1786                         uio->uio_segflg = UIO_USERSPACE;
1787 
1788                         rval = physio(fd_strategy, (struct buf *)0, dev,
1789                             spc, minphys, uio);
1790                         break;
1791                 } else if (fc.fdc_cmd == FDCMD_FORMAT_TRACK) {
1792                         spt = fjp->fj_chars->fdc_secptrack;       /* sec/trk */
1793                         spc = fjp->fj_chars->fdc_nhead * spt;     /* sec/cyl */
1794                         cyl = fc.fdc_blkno / spc;
1795                         head = (fc.fdc_blkno % spc) / spt;
1796                         if ((cyl | head) == 0)
1797                                 fjp->fj_flags &=
1798                                     ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1799 
1800                         FDERRPRINT(FDEP_L0, FDEM_FORM,
1801                             (CE_CONT, "fd_format cyl %d, hd %d\n", cyl, head));
1802                         fjp->fj_ops->fco_select(fjp, unit, 1);
1803                         rval = fjp->fj_ops->fco_format(fjp, unit, cyl, head,
1804                             (int)fc.fdc_flags);
1805                         fjp->fj_ops->fco_select(fjp, unit, 0);
1806 
1807                         break;
1808                 }
1809                 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1810                     (CE_WARN, "fd_ioctl fd unit %d: FDIOCSCMD not yet complete",
1811                     unit));
1812                 rval = EINVAL;
1813                 break;
1814         }
1815 
1816         case FDRAW:
1817                 rval = fd_rawioctl(fjp, unit, (caddr_t)arg, flag);
1818                 break;
1819 
1820         default:
1821                 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1822                     (CE_WARN, "fd_ioctl fd unit %d: invalid ioctl 0x%x",
1823                     unit, cmd));
1824                 rval = ENOTTY;
1825                 break;
1826         }
1827         return (rval);
1828 }
1829 
1830 static void
1831 fd_build_user_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp)
1832 {
1833         struct partition *vpart;
1834         int     i;
1835         int     xblk;
1836 
1837         /*
1838          * Return vtoc structure fields in the provided VTOC area, addressed
1839          * by *vtocp.
1840          *
1841          */
1842         bzero(vtocp, sizeof (struct vtoc));
1843 
1844         bcopy(fdp->d_vtoc_bootinfo,
1845             vtocp->v_bootinfo, sizeof (vtocp->v_bootinfo));
1846 
1847         vtocp->v_sanity = VTOC_SANE;
1848         vtocp->v_version = fdp->d_vtoc_version;
1849         bcopy(fdp->d_vtoc_volume, vtocp->v_volume, LEN_DKL_VVOL);
1850         if (fjp->fj_flags & FUNIT_LABELOK) {
1851                 vtocp->v_sectorsz = DEV_BSIZE;
1852                 xblk = 1;
1853         } else {
1854                 vtocp->v_sectorsz = fjp->fj_chars->fdc_sec_size;
1855                 xblk = vtocp->v_sectorsz / DEV_BSIZE;
1856         }
1857         vtocp->v_nparts = 3; /* <= NDKMAP;        */
1858 
1859         /*
1860          * Copy partitioning information.
1861          */
1862         bcopy(fdp->d_part, vtocp->v_part, sizeof (struct partition) * NDKMAP);
1863         for (i = NDKMAP, vpart = vtocp->v_part; i && (xblk > 1); i--, vpart++) {
1864                 /* correct partition info if sector size > 512 bytes */
1865                 vpart->p_start /= xblk;
1866                 vpart->p_size /= xblk;
1867         }
1868 
1869         bcopy(fdp->d_vtoc_timestamp,
1870             vtocp->timestamp, sizeof (fdp->d_vtoc_timestamp));
1871         bcopy(fdp->d_vtoc_asciilabel, vtocp->v_asciilabel, LEN_DKL_ASCII);
1872 }
1873 
1874 
1875 static int
1876 fd_build_label_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp,
1877     struct dk_label *labelp)
1878 {
1879         struct partition *vpart;
1880         int     i;
1881         int     nblks;
1882         int     ncyl;
1883         ushort_t sum, *sp;
1884 
1885 
1886         /*
1887          * Sanity-check the vtoc
1888          */
1889         if (vtocp->v_sanity != VTOC_SANE ||
1890             vtocp->v_nparts > NDKMAP || vtocp->v_nparts <= 0) {
1891                 FDERRPRINT(FDEP_L3, FDEM_IOCT,
1892                     (CE_WARN, "fd_build_label:  sanity check on vtoc failed"));
1893                 return (EINVAL);
1894         }
1895 
1896         /*
1897          * before copying the vtoc, the partition information in it should be
1898          * checked against the information the driver already has on the
1899          * diskette.
1900          */
1901 
1902         nblks = (fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack *
1903             fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1904         if (nblks == 0 || fjp->fj_chars->fdc_ncyl == 0)
1905                 return (EFAULT);
1906         vpart = vtocp->v_part;
1907 
1908         /*
1909          * Check the partition information in the vtoc.  The starting sectors
1910          * must lie along cylinder boundaries. (NDKMAP entries are checked
1911          * to ensure that the unused entries are set to 0 if vtoc->v_nparts
1912          * is less than NDKMAP)
1913          */
1914         for (i = NDKMAP; i; i--) {
1915                 if ((vpart->p_start % nblks) != 0) {
1916                         return (EINVAL);
1917                 }
1918                 ncyl = vpart->p_start / nblks;
1919                 ncyl += vpart->p_size / nblks;
1920                 if ((vpart->p_size % nblks) != 0)
1921                         ncyl++;
1922                 if (ncyl > (long)fjp->fj_chars->fdc_ncyl) {
1923                         return (EINVAL);
1924                 }
1925                 vpart++;
1926         }
1927 
1928 
1929         bcopy(vtocp->v_bootinfo, fdp->d_vtoc_bootinfo,
1930             sizeof (vtocp->v_bootinfo));
1931         fdp->d_vtoc_version = vtocp->v_version;
1932         bcopy(vtocp->v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
1933 
1934         /*
1935          * Copy partitioning information.
1936          */
1937         bcopy(vtocp->v_part, fdp->d_part, sizeof (struct partition) * NDKMAP);
1938         bcopy(vtocp->timestamp, fdp->d_vtoc_timestamp,
1939             sizeof (fdp->d_vtoc_timestamp));
1940         bcopy(vtocp->v_asciilabel, fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
1941 
1942         /*
1943          * construct the diskette label in supplied buffer
1944          */
1945 
1946         /* Put appropriate vtoc structure fields into the disk label */
1947         labelp->dkl_vtoc.v_bootinfo[0] = (uint32_t)vtocp->v_bootinfo[0];
1948         labelp->dkl_vtoc.v_bootinfo[1] = (uint32_t)vtocp->v_bootinfo[1];
1949         labelp->dkl_vtoc.v_bootinfo[2] = (uint32_t)vtocp->v_bootinfo[2];
1950 
1951         labelp->dkl_vtoc.v_sanity = vtocp->v_sanity;
1952         labelp->dkl_vtoc.v_version = vtocp->v_version;
1953 
1954         bcopy(vtocp->v_volume, labelp->dkl_vtoc.v_volume, LEN_DKL_VVOL);
1955 
1956         labelp->dkl_vtoc.v_nparts = vtocp->v_nparts;
1957 
1958         bcopy(vtocp->v_reserved, labelp->dkl_vtoc.v_reserved,
1959             sizeof (labelp->dkl_vtoc.v_reserved));
1960 
1961         for (i = 0; i < (int)vtocp->v_nparts; i++) {
1962                 labelp->dkl_vtoc.v_part[i].p_tag  = vtocp->v_part[i].p_tag;
1963                 labelp->dkl_vtoc.v_part[i].p_flag  = vtocp->v_part[i].p_flag;
1964                 labelp->dkl_vtoc.v_part[i].p_start  = vtocp->v_part[i].p_start;
1965                 labelp->dkl_vtoc.v_part[i].p_size  = vtocp->v_part[i].p_size;
1966         }
1967 
1968         for (i = 0; i < NDKMAP; i++) {
1969                 labelp->dkl_vtoc.v_timestamp[i] = vtocp->timestamp[i];
1970         }
1971         bcopy(vtocp->v_asciilabel, labelp->dkl_asciilabel, LEN_DKL_ASCII);
1972 
1973 
1974         labelp->dkl_pcyl = fjp->fj_chars->fdc_ncyl;
1975         labelp->dkl_ncyl = fjp->fj_chars->fdc_ncyl;
1976         labelp->dkl_nhead = fjp->fj_chars->fdc_nhead;
1977         /*
1978          * The fdc_secptrack field of the fd_char structure is the number
1979          * of sectors per track where the sectors are fdc_sec_size.
1980          * The dkl_nsect field of the dk_label structure is the number of
1981          * DEV_BSIZE (512) byte sectors per track.
1982          */
1983         labelp->dkl_nsect = (fjp->fj_chars->fdc_secptrack *
1984             fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1985         labelp->dkl_intrlv = fjp->fj_attr->fda_intrlv;
1986         labelp->dkl_rpm = fjp->fj_attr->fda_rotatespd;
1987         labelp->dkl_read_reinstruct =
1988             (int)(labelp->dkl_nsect * labelp->dkl_rpm * 4) / 60000;
1989         labelp->dkl_write_reinstruct = labelp->dkl_read_reinstruct;
1990 
1991         labelp->dkl_magic = DKL_MAGIC;
1992 
1993         sum = 0;
1994         labelp->dkl_cksum = 0;
1995         sp = (ushort_t *)labelp;
1996         while (sp < &(labelp->dkl_cksum)) {
1997                 sum ^= *sp++;
1998         }
1999         labelp->dkl_cksum = sum;
2000 
2001         return (0);
2002 }
2003 
2004 static int
2005 fd_rawioctl(struct fcu_obj *fjp, int unit, caddr_t arg, int mode)
2006 {
2007         struct fd_raw fdr;
2008         char *arg_result = NULL;
2009         int flag = B_READ;
2010         int rval = 0;
2011         caddr_t uaddr;
2012         uint_t ucount;
2013 
2014         FDERRPRINT(FDEP_L1, FDEM_RAWI,
2015             (CE_CONT, "fd_rawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
2016 
2017         if (fjp->fj_chars->fdc_medium != 3 && fjp->fj_chars->fdc_medium != 5) {
2018                 cmn_err(CE_CONT, "fd_rawioctl: Medium density not supported\n");
2019                 return (ENXIO);
2020         }
2021 
2022 #ifdef _MULTI_DATAMODEL
2023         switch (ddi_model_convert_from(mode & FMODELS)) {
2024         case DDI_MODEL_ILP32:
2025         {
2026                 struct fd_raw32 fdr32;
2027 
2028                 if (ddi_copyin(arg, &fdr32, sizeof (fdr32), mode))
2029                         return (EFAULT);
2030 
2031                 bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
2032                 fdr.fdr_cnum = fdr32.fdr_cnum;
2033                 fdr.fdr_nbytes = fdr32.fdr_nbytes;
2034                 fdr.fdr_addr = (caddr_t)(uintptr_t)fdr32.fdr_addr;
2035                 arg_result = ((struct fd_raw32 *)arg)->fdr_result;
2036 
2037                 break;
2038         }
2039         case DDI_MODEL_NONE:
2040 #endif /* ! _MULTI_DATAMODEL */
2041 
2042                 if (ddi_copyin(arg, &fdr, sizeof (fdr), mode))
2043                         return (EFAULT);
2044 
2045                 arg_result = ((struct fd_raw *)arg)->fdr_result;
2046 
2047 #ifdef _MULTI_DATAMODEL
2048                 break;
2049         }
2050 #endif /* _MULTI_DATAMODEL */
2051 
2052 
2053 
2054         /*
2055          * copy user address & nbytes from raw_req so that we can
2056          * put kernel address in req structure
2057          */
2058         uaddr = fdr.fdr_addr;
2059         ucount = (uint_t)fdr.fdr_nbytes;
2060         unit &= 3;
2061 
2062         switch (fdr.fdr_cmd[0] & 0x0f) {
2063 
2064         case FDRAW_FORMAT:
2065                 ucount += 16;
2066                 fdr.fdr_addr = kmem_zalloc(ucount, KM_SLEEP);
2067                 if (ddi_copyin(uaddr, fdr.fdr_addr,
2068                     (size_t)fdr.fdr_nbytes, mode)) {
2069                         kmem_free(fdr.fdr_addr, ucount);
2070                         return (EFAULT);
2071                 }
2072                 if ((*fdr.fdr_addr | fdr.fdr_addr[1]) == 0)
2073                         fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
2074                 flag = B_WRITE;
2075                 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2076                 break;
2077 
2078         case FDRAW_WRCMD:
2079         case FDRAW_WRITEDEL:
2080                 flag = B_WRITE;
2081                 /* FALLTHROUGH */
2082         case FDRAW_RDCMD:
2083         case FDRAW_READDEL:
2084         case FDRAW_READTRACK:
2085                 if (ucount) {
2086                         /*
2087                          * In SunOS 4.X, we used to as_fault things in.
2088                          * We really cannot do this in 5.0/SVr4. Unless
2089                          * someone really believes that speed is of the
2090                          * essence here, it is just much simpler to do
2091                          * this in kernel space and use copyin/copyout.
2092                          */
2093                         fdr.fdr_addr = kmem_alloc((size_t)ucount, KM_SLEEP);
2094                         if (flag == B_WRITE) {
2095                                 if (ddi_copyin(uaddr, fdr.fdr_addr, ucount,
2096                                     mode)) {
2097                                         kmem_free(fdr.fdr_addr, ucount);
2098                                         return (EFAULT);
2099                                 }
2100                         }
2101                 } else
2102                         return (EINVAL);
2103                 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2104                 break;
2105 
2106         case FDRAW_READID:
2107         case FDRAW_REZERO:
2108         case FDRAW_SEEK:
2109         case FDRAW_SENSE_DRV:
2110                 ucount = 0;
2111                 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2112                 break;
2113 
2114         case FDRAW_SPECIFY:
2115                 fdr.fdr_cmd[2] &= 0xfe;     /* keep NoDMA bit clear */
2116                 /* FALLTHROUGH */
2117         case FDRAW_SENSE_INT:
2118                 ucount = 0;
2119                 break;
2120 
2121         default:
2122                 return (EINVAL);
2123         }
2124 
2125         /*
2126          * Note that we ignore any error returns from controller
2127          * This is the way the driver has been, and it may be
2128          * that the raw ioctl senders simply don't want to
2129          * see any errors returned in this fashion.
2130          */
2131 
2132         fjp->fj_ops->fco_select(fjp, unit, 1);
2133         rval = fjp->fj_ops->fco_rwioctl(fjp, unit, (caddr_t)&fdr);
2134 
2135         if (ucount && flag == B_READ && rval == 0) {
2136                 if (ddi_copyout(fdr.fdr_addr, uaddr, ucount, mode)) {
2137                         rval = EFAULT;
2138                 }
2139         }
2140         if (ddi_copyout(fdr.fdr_result, arg_result, sizeof (fdr.fdr_cmd), mode))
2141                 rval = EFAULT;
2142 
2143         fjp->fj_ops->fco_select(fjp, unit, 0);
2144         if (ucount)
2145                 kmem_free(fdr.fdr_addr, ucount);
2146 
2147         return (rval);
2148 }
2149 
2150 /*
2151  * property operation routine.  return the number of blocks for the partition
2152  * in question or forward the request to the property facilities.
2153  */
2154 static int
2155 fd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
2156     char *name, caddr_t valuep, int *lengthp)
2157 {
2158         struct fcu_obj  *fjp = NULL;
2159         struct fdisk    *fdp = NULL;
2160         uint64_t        nblocks64;
2161 
2162         FDERRPRINT(FDEP_L1, FDEM_PROP,
2163             (CE_CONT, "fd_prop_op: dip %p %s\n", (void *)dip, name));
2164 
2165         /*
2166          * Our dynamic properties are all device specific and size oriented.
2167          * Requests issued under conditions where size is valid are passed
2168          * to ddi_prop_op_nblocks with the size information, otherwise the
2169          * request is passed to ddi_prop_op.
2170          */
2171         if (dev == DDI_DEV_T_ANY) {
2172 pass:           return (ddi_prop_op(dev, dip, prop_op, mod_flags,
2173                     name, valuep, lengthp));
2174         } else {
2175                 /*
2176                  * Ignoring return value because success is checked by
2177                  * verifying fjp and fdp and returned unit value is not used.
2178                  */
2179                 (void) fd_getdrive(dev, &fjp, &fdp);
2180                 if (!fjp || !fdp)
2181                         goto pass;
2182 
2183                 /* get nblocks value */
2184                 nblocks64 = (ulong_t)fdp->d_part[PARTITION(dev)].p_size;
2185 
2186                 return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
2187                     name, valuep, lengthp, nblocks64));
2188         }
2189 }
2190 
2191 static void
2192 fd_media_watch(void *arg)
2193 {
2194         struct fcu_obj *fjp;
2195         struct fdisk *fdp;
2196 
2197 #ifdef DEBUG
2198         int     unit;
2199 #define DEBUG_ASSIGN    unit=
2200 #else
2201 #define DEBUG_ASSIGN    (void)
2202 #endif
2203         DEBUG_ASSIGN fd_getdrive((dev_t)arg, &fjp, &fdp);
2204         /*
2205          * Ignoring return in non DEBUG mode because device exist.
2206          * Returned unit value is not used.
2207          */
2208 
2209         FDERRPRINT(FDEP_L0, FDEM_IOCT,
2210             (CE_CONT, "fd_media_watch unit %d\n", unit));
2211 
2212         /*
2213          * fd_get_media_state() cannot be called from this timeout function
2214          * because the  floppy drive has to be selected first, and that could
2215          * force this function to sleep (while waiting for the select
2216          * semaphore).
2217          * Instead, just wakeup up driver.
2218          */
2219         mutex_enter(&fjp->fj_lock);
2220         cv_broadcast(&fdp->d_statecv);
2221         mutex_exit(&fjp->fj_lock);
2222 }
2223 
2224 enum dkio_state
2225 fd_get_media_state(struct fcu_obj *fjp, int unit)
2226 {
2227         enum dkio_state state;
2228 
2229         if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2230                 /* recheck disk only if DSKCHG "high" */
2231                 fjp->fj_ops->fco_resetchng(fjp, unit);
2232                 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2233                         if (fjp->fj_flags & FUNIT_CHGDET) {
2234                                 /*
2235                                  * again no diskette; not a new change
2236                                  */
2237                                 state = DKIO_NONE;
2238                         } else {
2239                                 /*
2240                                  * a new change; diskette was ejected
2241                                  */
2242                                 fjp->fj_flags |= FUNIT_CHGDET;
2243                                 state = DKIO_EJECTED;
2244                         }
2245                 } else {
2246                         fjp->fj_flags &= ~FUNIT_CHGDET;
2247                         state = DKIO_INSERTED;
2248                 }
2249         } else {
2250                 fjp->fj_flags &= ~FUNIT_CHGDET;
2251                 state = DKIO_INSERTED;
2252         }
2253         FDERRPRINT(FDEP_L0, FDEM_IOCT,
2254             (CE_CONT, "fd_get_media_state unit %d: state %x\n", unit, state));
2255         return (state);
2256 }
2257 
2258 static int
2259 fd_check_media(dev_t dev, enum dkio_state state)
2260 {
2261         struct fcu_obj *fjp;
2262         struct fdisk *fdp;
2263         int     unit;
2264         int     err;
2265 
2266         unit = fd_getdrive(dev, &fjp, &fdp);
2267 
2268         mutex_enter(&fjp->fj_lock);
2269 
2270         fjp->fj_ops->fco_select(fjp, unit, 1);
2271         fdp->d_media_state = fd_get_media_state(fjp, unit);
2272         fdp->d_media_timeout = drv_usectohz(fd_check_media_time);
2273 
2274         while (fdp->d_media_state == state) {
2275                 /* release the controller and drive */
2276                 fjp->fj_ops->fco_select(fjp, unit, 0);
2277 
2278                 /* turn on timer */
2279                 fdp->d_media_timeout_id = timeout(fd_media_watch,
2280                     (void *)dev, fdp->d_media_timeout);
2281 
2282                 if (cv_wait_sig(&fdp->d_statecv, &fjp->fj_lock) == 0) {
2283                         fdp->d_media_timeout = 0;
2284                         mutex_exit(&fjp->fj_lock);
2285                         return (EINTR);
2286                 }
2287                 fjp->fj_ops->fco_select(fjp, unit, 1);
2288                 fdp->d_media_state = fd_get_media_state(fjp, unit);
2289         }
2290 
2291         if (fdp->d_media_state == DKIO_INSERTED) {
2292                 err = fdgetlabel(fjp, unit);
2293                 if (err) {
2294                         fjp->fj_ops->fco_select(fjp, unit, 0);
2295                         mutex_exit(&fjp->fj_lock);
2296                         return (EIO);
2297                 }
2298         }
2299         fjp->fj_ops->fco_select(fjp, unit, 0);
2300         mutex_exit(&fjp->fj_lock);
2301         return (0);
2302 }
2303 
2304 /*
2305  * fd_get_media_info :
2306  *      Collects medium information for
2307  *      DKIOCGMEDIAINFO ioctl.
2308  */
2309 
2310 static int
2311 fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag)
2312 {
2313         struct dk_minfo media_info;
2314         int err = 0;
2315 
2316         media_info.dki_media_type = DK_FLOPPY;
2317         media_info.dki_lbsize = fjp->fj_chars->fdc_sec_size;
2318         media_info.dki_capacity = fjp->fj_chars->fdc_ncyl *
2319             fjp->fj_chars->fdc_secptrack * fjp->fj_chars->fdc_nhead;
2320 
2321         if (ddi_copyout(&media_info, buf, sizeof (struct dk_minfo), flag))
2322                 err = EFAULT;
2323         return (err);
2324 }