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