xref: /illumos-gate/usr/src/uts/common/io/fd.c (revision 63f91fbc3c024870d86dc3332a4a0080fb29bc40)
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  * Copyright (c) 2018, Joyent, Inc.
28  */
29 
30 /*
31  * Floppy Disk driver
32  */
33 
34 /*
35  * Set CMOS feature:
36  *	CMOS_CONF_MEM:	CMOS memory contains configuration info
37  */
38 #define	CMOS_CONF_MEM
39 
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/buf.h>
44 #include <sys/file.h>
45 #include <sys/open.h>
46 #include <sys/ioctl.h>
47 #include <sys/uio.h>
48 #include <sys/conf.h>
49 #include <sys/stat.h>
50 #include <sys/autoconf.h>
51 #include <sys/vtoc.h>
52 #include <sys/dkio.h>
53 #include <sys/ddi.h>
54 #include <sys/sunddi.h>
55 #include <sys/kstat.h>
56 #include <sys/kmem.h>
57 #include <sys/ddidmareq.h>
58 #include <sys/fdio.h>
59 #include <sys/fdc.h>
60 #include <sys/fd_debug.h>
61 #include <sys/fdmedia.h>
62 #include <sys/debug.h>
63 #include <sys/modctl.h>
64 
65 /*
66  * Local Function Prototypes
67  */
68 static int fd_unit_is_open(struct fdisk *);
69 static int fdgetlabel(struct fcu_obj *, int);
70 static void fdstart(struct fcu_obj *);
71 static int fd_build_label_vtoc(struct fcu_obj *, struct fdisk *,
72     struct vtoc *, struct dk_label *);
73 static void fd_build_user_vtoc(struct fcu_obj *, struct fdisk *,
74     struct vtoc *);
75 static int fd_rawioctl(struct fcu_obj *, int, caddr_t, int);
76 static void fd_media_watch(void *);
77 
78 static int fd_open(dev_t *, int, int, cred_t *);
79 static int fd_close(dev_t, int, int, cred_t *);
80 static int fd_strategy(struct buf *);
81 static int fd_read(dev_t, struct uio *, cred_t *);
82 static int fd_write(dev_t, struct uio *, cred_t *);
83 static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
84 static int fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
85     caddr_t, int *);
86 static int fd_check_media(dev_t dev, enum dkio_state state);
87 static int fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag);
88 
89 static struct cb_ops fd_cb_ops = {
90 	fd_open,		/* open */
91 	fd_close,		/* close */
92 	fd_strategy,		/* strategy */
93 	nodev,			/* print */
94 	nodev,			/* dump */
95 	fd_read,		/* read */
96 	fd_write,		/* write */
97 	fd_ioctl,		/* ioctl */
98 	nodev,			/* devmap */
99 	nodev,			/* mmap */
100 	nodev,			/* segmap */
101 	nochpoll,		/* poll */
102 	fd_prop_op,		/* cb_prop_op */
103 	0,			/* streamtab  */
104 	D_NEW | D_MP		/* Driver compatibility flag */
105 };
106 
107 static int fd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
108 static int fd_probe(dev_info_t *);
109 static int fd_attach(dev_info_t *, ddi_attach_cmd_t);
110 static int fd_detach(dev_info_t *, ddi_detach_cmd_t);
111 
112 static struct dev_ops fd_ops = {
113 	DEVO_REV,		/* devo_rev, */
114 	0,			/* refcnt  */
115 	fd_getinfo,		/* getinfo */
116 	nulldev,		/* identify */
117 	fd_probe,		/* probe */
118 	fd_attach,		/* attach */
119 	fd_detach,		/* detach */
120 	nodev,			/* reset */
121 	&fd_cb_ops,		/* driver operations */
122 	(struct bus_ops *)0,	/* bus operations */
123 	NULL,			/* power */
124 	ddi_quiesce_not_supported,	/* devo_quiesce */
125 };
126 
127 
128 /*
129  * static data
130  */
131 static void *fd_state_head;		/* opaque handle top of state structs */
132 static int fd_check_media_time = 5000000;	/* 5 second state check */
133 
134 /*
135  * error handling
136  *
137  * for debugging,
138  *		set fderrlevel to 1
139  *		set fderrmask  to 224  or 644
140  */
141 #ifdef DEBUG
142 static uint_t fderrmask = FDEM_ALL;
143 #endif
144 static int fderrlevel = 5;
145 
146 #define	KIOSP	KSTAT_IO_PTR(fdp->d_iostat)
147 
148 static struct driver_minor_data {
149 	char	*name;
150 	int	minor;
151 	int	type;
152 } fd_minor [] = {
153 	{ "a", 0, S_IFBLK},
154 	{ "b", 1, S_IFBLK},
155 	{ "c", 2, S_IFBLK},
156 	{ "a,raw", 0, S_IFCHR},
157 	{ "b,raw", 1, S_IFCHR},
158 	{ "c,raw", 2, S_IFCHR},
159 	{0}
160 };
161 
162 static struct modldrv modldrv = {
163 	&mod_driverops,		/* Type of module. This one is a driver */
164 	"Floppy Disk driver",	/* Name of the module. */
165 	&fd_ops,		/* driver ops */
166 };
167 
168 static struct modlinkage modlinkage = {
169 	MODREV_1, (void *)&modldrv, NULL
170 };
171 
172 
173 int
174 _init(void)
175 {
176 	int retval;
177 
178 	if ((retval = ddi_soft_state_init(&fd_state_head,
179 	    sizeof (struct fdisk) + sizeof (struct fd_drive) +
180 	    sizeof (struct fd_char) + sizeof (struct fdattr), 0)) != 0)
181 		return (retval);
182 
183 	if ((retval = mod_install(&modlinkage)) != 0)
184 		ddi_soft_state_fini(&fd_state_head);
185 	return (retval);
186 }
187 
188 int
189 _fini(void)
190 {
191 	int retval;
192 
193 	if ((retval = mod_remove(&modlinkage)) != 0)
194 		return (retval);
195 	ddi_soft_state_fini(&fd_state_head);
196 	return (retval);
197 }
198 
199 int
200 _info(struct modinfo *modinfop)
201 {
202 	return (mod_info(&modlinkage, modinfop));
203 }
204 
205 
206 static int
207 fd_getdrive(dev_t dev, struct fcu_obj **fjpp, struct fdisk **fdpp)
208 {
209 	if (fdpp) {
210 		*fdpp = ddi_get_soft_state(fd_state_head, DRIVE(dev));
211 		if (*fdpp && fjpp) {
212 			*fjpp = (*fdpp)->d_obj;
213 			if (*fjpp)
214 				return ((*fjpp)->fj_unit);
215 		}
216 	}
217 	return (-1);
218 }
219 
220 /*ARGSUSED*/
221 static int
222 fd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
223 {
224 	dev_t dev = (dev_t)arg;
225 	struct fcu_obj *fjp = NULL;
226 	struct fdisk *fdp = NULL;
227 	int rval;
228 
229 	switch (cmd) {
230 	case DDI_INFO_DEVT2DEVINFO:
231 		(void) fd_getdrive(dev, &fjp, &fdp);
232 		/*
233 		 * Ignoring return value because success is checked by
234 		 * verifying fjp and fdp and returned unit value is not used.
235 		 */
236 		if (fjp && fdp) {
237 			*result = fjp->fj_dip;
238 			rval = DDI_SUCCESS;
239 		} else
240 			rval = DDI_FAILURE;
241 		break;
242 	case DDI_INFO_DEVT2INSTANCE:
243 		*result = (void *)(uintptr_t)DRIVE(dev);
244 		rval = DDI_SUCCESS;
245 		break;
246 	default:
247 		rval = DDI_FAILURE;
248 	}
249 	return (rval);
250 }
251 
252 #ifdef CMOS_CONF_MEM
253 #define	CMOS_ADDR	0x70
254 #define	CMOS_DATA	0x71
255 #define	CMOS_FDRV	0x10
256 #endif	/* CMOS_CONF_MEM */
257 
258 static int
259 fd_probe(dev_info_t *dip)
260 {
261 #ifdef CMOS_CONF_MEM
262 	int cmos;
263 	int drive_type;
264 #endif	/* CMOS_CONF_MEM */
265 	int debug[2];
266 	int drive_size;
267 	int len;
268 	int unit_num;
269 	char density[8];
270 
271 	len = sizeof (debug);
272 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
273 	    DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
274 	    DDI_PROP_SUCCESS) {
275 		fderrlevel = debug[0];
276 #ifdef DEBUG
277 		fderrmask = (uint_t)debug[1];
278 #endif
279 	}
280 	len = sizeof (unit_num);
281 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
282 	    DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
283 	    DDI_PROP_SUCCESS) {
284 		FDERRPRINT(FDEP_L3, FDEM_ATTA,
285 		    (CE_WARN, "fd_probe failed: dip %p", (void *)dip));
286 		return (DDI_PROBE_FAILURE);
287 	}
288 
289 #ifdef CMOS_CONF_MEM
290 	/* get the cmos memory values quick and dirty */
291 	outb(CMOS_ADDR, CMOS_FDRV);
292 	cmos = drive_type = (int)inb(CMOS_DATA);
293 #endif	/* CMOS_CONF_MEM */
294 
295 	switch (unit_num) {
296 #ifdef CMOS_CONF_MEM
297 	case 0:
298 		drive_type = drive_type >> 4;
299 		/* FALLTHROUGH */
300 	case 1:
301 		if (cmos && (drive_type & 0x0F)) {
302 			break;
303 		}
304 		/*
305 		 * Some enhanced floppy-disk controller adaptor cards
306 		 * require NO drives defined in the CMOS configuration
307 		 * memory.
308 		 * So fall through
309 		 */
310 #endif	/* CMOS_CONF_MEM */
311 		/* FALLTHROUGH */
312 	default:		/* need to check conf file */
313 		len = sizeof (density);
314 		if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
315 		    DDI_PROP_DONTPASS, "density", (caddr_t)&density, &len) !=
316 		    DDI_PROP_SUCCESS) {
317 			FDERRPRINT(FDEP_L3, FDEM_ATTA,
318 			    (CE_WARN,
319 			    "fd_probe failed density: dip %p unit %d",
320 			    (void *)dip, unit_num));
321 			return (DDI_PROBE_FAILURE);
322 		}
323 		len = sizeof (drive_size);
324 		if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
325 		    DDI_PROP_DONTPASS, "size", (caddr_t)&drive_size, &len) !=
326 		    DDI_PROP_SUCCESS) {
327 			FDERRPRINT(FDEP_L3, FDEM_ATTA,
328 			    (CE_WARN, "fd_probe failed size: dip %p unit %d",
329 			    (void *)dip, unit_num));
330 			return (DDI_PROBE_FAILURE);
331 		}
332 	}
333 	FDERRPRINT(FDEP_L3, FDEM_ATTA,
334 	    (CE_WARN, "fd_probe dip %p unit %d", (void *)dip, unit_num));
335 	return (DDI_PROBE_SUCCESS);
336 }
337 
338 
339 /* ARGSUSED */
340 static int
341 fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
342 {
343 	struct fcu_obj *fjp;
344 	struct fdisk *fdp;
345 	struct driver_minor_data *dmdp;
346 	int mode_3D;
347 	int drive_num, drive_size, drive_type;
348 #ifdef CMOS_CONF_MEM
349 	int cmos;
350 #endif	/* CMOS_CONF_MEM */
351 	int len, sig_minor;
352 	int unit_num;
353 	char density[8];
354 	char name[MAXNAMELEN];
355 
356 	switch (cmd) {
357 	case DDI_ATTACH:
358 		len = sizeof (unit_num);
359 		if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
360 		    DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
361 		    DDI_PROP_SUCCESS) {
362 			FDERRPRINT(FDEP_L3, FDEM_ATTA,
363 			    (CE_WARN, "fd_attach failed: dip %p", (void *)dip));
364 			return (DDI_FAILURE);
365 		}
366 
367 #ifdef CMOS_CONF_MEM
368 		outb(CMOS_ADDR, CMOS_FDRV);
369 		cmos = drive_type = (int)inb(CMOS_DATA);
370 #endif	/* CMOS_CONF_MEM */
371 
372 		switch (unit_num) {
373 #ifdef CMOS_CONF_MEM
374 		case 0:
375 			drive_type = drive_type >> 4;
376 			/* FALLTHROUGH */
377 		case 1:
378 			drive_type = drive_type & 0x0F;
379 			if (cmos)
380 				break;
381 			/*
382 			 * Some enhanced floppy-disk controller adaptor cards
383 			 * require NO drives defined in the CMOS configuration
384 			 * memory.
385 			 * So fall through
386 			 */
387 #endif	/* CMOS_CONF_MEM */
388 			/* FALLTHROUGH */
389 		default:		/* need to check .conf file */
390 			drive_type = 0;
391 			len = sizeof (density);
392 			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
393 			    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "density",
394 			    (caddr_t)&density, &len) != DDI_PROP_SUCCESS)
395 				density[0] = '\0';
396 			len = sizeof (drive_size);
397 			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
398 			    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "size",
399 			    (caddr_t)&drive_size, &len) != DDI_PROP_SUCCESS)
400 				drive_size = 0;
401 			if (strcmp(density, "DSDD") == 0) {
402 				if (drive_size == 5)
403 					drive_type = 1;
404 				else if (drive_size == 3)
405 					drive_type = 3;
406 			} else if (strcmp(density, "DSHD") == 0) {
407 				if (drive_size == 5)
408 					drive_type = 2;
409 				else if (drive_size == 3)
410 					drive_type = 4;
411 			} else if (strcmp(density, "DSED") == 0 &&
412 			    drive_size == 3) {
413 				drive_type = 6;
414 			}
415 			break;
416 		}
417 		if (drive_type == 0) {
418 			FDERRPRINT(FDEP_L3, FDEM_ATTA,
419 			    (CE_WARN, "fd_attach failed type: dip %p unit %d",
420 			    (void *)dip, unit_num));
421 			return (DDI_FAILURE);
422 		}
423 
424 		drive_num = ddi_get_instance(dip);
425 		if (ddi_soft_state_zalloc(fd_state_head, drive_num) != 0)
426 			return (DDI_FAILURE);
427 		fdp = ddi_get_soft_state(fd_state_head, drive_num);
428 		fjp = fdp->d_obj = ddi_get_driver_private(dip);
429 
430 		mutex_init(&fjp->fj_lock, NULL, MUTEX_DRIVER, *fjp->fj_iblock);
431 		sema_init(&fdp->d_ocsem, 1, NULL, SEMA_DRIVER, NULL);
432 
433 		fjp->fj_drive = (struct fd_drive *)(fdp + 1);
434 		fjp->fj_chars = (struct fd_char *)(fjp->fj_drive + 1);
435 		fjp->fj_attr = (struct fdattr *)(fjp->fj_chars + 1);
436 
437 		/*
438 		 * set default floppy drive characteristics & geometry
439 		 */
440 		switch (drive_type) {	/* assume doubled sided */
441 		case 2:			/* 5.25 high density */
442 			*fjp->fj_drive = dfd_525HD;
443 			fdp->d_media = 1<<FMT_5H | 1<<FMT_5D9 | 1<<FMT_5D8 |
444 			    1<<FMT_5D4 | 1<<FMT_5D16;
445 			fdp->d_deffdtype = fdp->d_curfdtype = FMT_5H;
446 			break;
447 		case 4:			/* 3.5 high density */
448 			*fjp->fj_drive = dfd_350HD;
449 			fdp->d_media = 1<<FMT_3H | 1<<FMT_3I | 1<<FMT_3D;
450 			len = sizeof (mode_3D);
451 			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
452 			    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "mode_3D",
453 			    (caddr_t)&mode_3D, &len) != DDI_PROP_SUCCESS)
454 				mode_3D = 0;
455 			if (mode_3D && (fjp->fj_fdc->c_flags & FCFLG_3DMODE))
456 				/*
457 				 * 3D mode should be enabled only if a dual-
458 				 * speed 3.5" high-density drive and a
459 				 * supported floppy controller are installed.
460 				 */
461 				fdp->d_media |= 1 << FMT_3M;
462 			fdp->d_deffdtype = fdp->d_curfdtype = FMT_3H;
463 			break;
464 		case 1:			/* 5.25 double density */
465 			*fjp->fj_drive = dfd_525DD;
466 			fdp->d_media = 1<<FMT_5D9 | 1<<FMT_5D8 | 1<<FMT_5D4 |
467 			    1<<FMT_5D16;
468 			fdp->d_deffdtype = fdp->d_curfdtype = FMT_5D9;
469 			break;
470 		case 3:			/* 3.5 double density */
471 			*fjp->fj_drive = dfd_350HD;
472 			fdp->d_media = 1<<FMT_3D;
473 			fdp->d_deffdtype = fdp->d_curfdtype = FMT_3D;
474 			break;
475 		case 5:			/* 3.5 extended density */
476 		case 6:
477 		case 7:
478 			*fjp->fj_drive = dfd_350ED;
479 			fdp->d_media = 1<<FMT_3E | 1<<FMT_3H | 1<<FMT_3I |
480 			    1<<FMT_3D;
481 			fdp->d_deffdtype = fdp->d_curfdtype = FMT_3E;
482 			break;
483 		case 0:			/* no drive defined */
484 		default:
485 			goto no_attach;
486 		}
487 		*fjp->fj_chars = *defchar[fdp->d_deffdtype];
488 		*fjp->fj_attr = fdtypes[fdp->d_deffdtype];
489 		bcopy(fdparts[fdp->d_deffdtype], fdp->d_part,
490 		    sizeof (struct partition) * NDKMAP);
491 		fjp->fj_rotspd = fdtypes[fdp->d_deffdtype].fda_rotatespd;
492 
493 		sig_minor = drive_num << 3;
494 		for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) {
495 			if (ddi_create_minor_node(dip, dmdp->name, dmdp->type,
496 			    sig_minor | dmdp->minor, DDI_NT_FD, 0)
497 			    == DDI_FAILURE) {
498 				ddi_remove_minor_node(dip, NULL);
499 				goto no_attach;
500 			}
501 		}
502 
503 		FDERRPRINT(FDEP_L3, FDEM_ATTA,
504 		    (CE_WARN, "fd_attach: dip %p unit %d",
505 		    (void *)dip, unit_num));
506 		(void) sprintf(name, "fd%d", drive_num);
507 		fdp->d_iostat = kstat_create("fd", drive_num, name, "disk",
508 		    KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
509 		if (fdp->d_iostat) {
510 			fdp->d_iostat->ks_lock = &fjp->fj_lock;
511 			kstat_install(fdp->d_iostat);
512 		}
513 
514 		fjp->fj_data = (caddr_t)fdp;
515 		fjp->fj_flags |= FUNIT_DRVATCH;
516 
517 		/*
518 		 * Add a zero-length attribute to tell the world we support
519 		 * kernel ioctls (for layered drivers)
520 		 */
521 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
522 		    DDI_KERNEL_IOCTL, NULL, 0);
523 
524 		/*
525 		 * We want to get suspend/resume events, so that we can
526 		 * refuse to suspend when pcfs is mounted.
527 		 */
528 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
529 		    "pm-hardware-state", "needs-suspend-resume");
530 
531 		/*
532 		 * Ignoring return value because, for passed arguments, only
533 		 * DDI_SUCCESS is returned.
534 		 */
535 		ddi_report_dev(dip);
536 		return (DDI_SUCCESS);
537 
538 	case DDI_RESUME:
539 		/* nothing for us to do */
540 		return (DDI_SUCCESS);
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 = DDI_FAILURE;
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_flags &= ~FUNIT_DRVATCH;
585 		fjp->fj_data = NULL;
586 		fjp->fj_drive = NULL;
587 		fjp->fj_chars = NULL;
588 		fjp->fj_attr = NULL;
589 		ddi_prop_remove_all(dip);
590 		mutex_destroy(&fjp->fj_lock);
591 		sema_destroy(&fdp->d_ocsem);
592 		ddi_soft_state_free(fd_state_head, drive_num);
593 		break;
594 
595 	case DDI_SUSPEND:
596 		/*
597 		 * Bad, bad, bad things will happen if someone
598 		 * *changes* the disk in the drive while it is mounted
599 		 * and the system is suspended.  We have no way to
600 		 * detect that.  (Undetected filesystem corruption.
601 		 * Its akin to changing the boot disk while the system
602 		 * is suspended.  Don't do it!)
603 		 *
604 		 * So we refuse to suspend if there is a mounted filesystem.
605 		 * (We guess this by looking for a block open.  Character
606 		 * opens are fine.)  This limits some of the usability of
607 		 * suspend/resume, but it certainly avoids this
608 		 * potential filesystem corruption from pilot error.
609 		 * Given the decreasing popularity of floppy media, we
610 		 * don't see this as much of a limitation.
611 		 */
612 		if (fdp->d_regopen[OTYP_BLK]) {
613 			cmn_err(CE_NOTE,
614 			    "Unable to suspend while floppy is in use.");
615 			rval = DDI_FAILURE;
616 		}
617 		break;
618 
619 	default:
620 		rval = DDI_FAILURE;
621 		break;
622 	}
623 	return (rval);
624 }
625 
626 
627 static int
628 fd_part_is_open(struct fdisk *fdp, int part)
629 {
630 	int i;
631 
632 	for (i = 0; i < (OTYPCNT - 1); i++)
633 		if (fdp->d_regopen[i] & (1 << part))
634 			return (1);
635 	return (0);
636 }
637 
638 static int
639 fd_unit_is_open(struct fdisk *fdp)
640 {
641 	int i;
642 
643 	for (i = 0; i < NDKMAP; i++)
644 		if (fdp->d_lyropen[i])
645 			return (1);
646 	for (i = 0; i < (OTYPCNT - 1); i++)
647 		if (fdp->d_regopen[i])
648 			return (1);
649 	return (0);
650 }
651 
652 /*ARGSUSED*/
653 static int
654 fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
655 {
656 	struct fcu_obj *fjp = NULL;
657 	struct fdisk *fdp = NULL;
658 	struct partition *pp;
659 	dev_t dev;
660 	int part, unit;
661 	int part_is_open;
662 	int rval;
663 	uint_t pbit;
664 
665 	dev = *devp;
666 	unit = fd_getdrive(dev, &fjp, &fdp);
667 	if (!fjp || !fdp)
668 		return (ENXIO);
669 	part = PARTITION(dev);
670 	pbit = 1 << part;
671 	pp = &fdp->d_part[part];
672 
673 	/*
674 	 * Serialize opens/closes
675 	 */
676 	sema_p(&fdp->d_ocsem);
677 	FDERRPRINT(FDEP_L1, FDEM_OPEN,
678 	    (CE_CONT, "fd_open: fd%d part %d flag %x otype %x\n", DRIVE(dev),
679 	    part, flag, otyp));
680 
681 	/*
682 	 * Check for previous exclusive open, or trying to exclusive open
683 	 * An "exclusive open" on any partition is not guaranteed to
684 	 * protect against opens on another partition that overlaps it.
685 	 */
686 	if (otyp == OTYP_LYR) {
687 		part_is_open = (fdp->d_lyropen[part] != 0);
688 	} else {
689 		part_is_open = fd_part_is_open(fdp, part);
690 	}
691 	if ((fdp->d_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
692 		FDERRPRINT(FDEP_L0, FDEM_OPEN, (CE_CONT,
693 		    "fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n",
694 		    fdp->d_exclmask, fdp->d_regopen[otyp], fdp->d_lyropen[part],
695 		    pbit));
696 		sema_v(&fdp->d_ocsem);
697 		return (EBUSY);
698 	}
699 
700 	/*
701 	 * Ensure that drive is recalibrated on first open of new diskette.
702 	 */
703 	fjp->fj_ops->fco_select(fjp, unit, 1);
704 	if (fjp->fj_ops->fco_getchng(fjp, unit) != 0) {
705 		if (fjp->fj_ops->fco_rcseek(fjp, unit, -1, 0)) {
706 			FDERRPRINT(FDEP_L2, FDEM_OPEN,
707 			    (CE_NOTE, "fd_open fd%d: not ready", DRIVE(dev)));
708 			fjp->fj_ops->fco_select(fjp, unit, 0);
709 			sema_v(&fdp->d_ocsem);
710 			return (ENXIO);
711 		}
712 		fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
713 	}
714 	if (flag & (FNDELAY | FNONBLOCK)) {
715 		/* don't attempt access, just return successfully */
716 		fjp->fj_ops->fco_select(fjp, unit, 0);
717 		goto out;
718 	}
719 
720 	/*
721 	 * auto-sense the density/format of the diskette
722 	 */
723 	rval = fdgetlabel(fjp, unit);
724 	fjp->fj_ops->fco_select(fjp, unit, 0);
725 	if (rval) {
726 		/* didn't find label (couldn't read anything) */
727 		FDERRPRINT(FDEP_L2, FDEM_OPEN,
728 		    (CE_NOTE, "fd%d: drive not ready", DRIVE(dev)));
729 		sema_v(&fdp->d_ocsem);
730 		return (EIO);
731 	}
732 	/* check partition */
733 	if (pp->p_size == 0) {
734 		sema_v(&fdp->d_ocsem);
735 		return (ENXIO);
736 	}
737 	/*
738 	 * if opening for writing, check write protect on diskette
739 	 */
740 	if ((flag & FWRITE) && (fdp->d_obj->fj_flags & FUNIT_WPROT)) {
741 		sema_v(&fdp->d_ocsem);
742 		return (EROFS);
743 	}
744 
745 out:
746 	/*
747 	 * mark open as having succeeded
748 	 */
749 	if (flag & FEXCL)
750 		fdp->d_exclmask |= pbit;
751 	if (otyp == OTYP_LYR)
752 		fdp->d_lyropen[part]++;
753 	else
754 		fdp->d_regopen[otyp] |= 1 << part;
755 
756 	sema_v(&fdp->d_ocsem);
757 	return (0);
758 }
759 
760 /*
761  * fdgetlabel - read the SunOS label off the diskette
762  *	if it can read a valid label it does so, else it will use a
763  *	default.  If it can`t read the diskette - that is an error.
764  *
765  * RETURNS: 0 for ok - meaning that it could at least read the device,
766  *	!0 for error XXX TBD NYD error codes
767  */
768 static int
769 fdgetlabel(struct fcu_obj *fjp, int unit)
770 {
771 	struct dk_label *label;
772 	struct fdisk *fdp;
773 	char *newlabel;
774 	short *sp;
775 	short count;
776 	short xsum;
777 	int tries, try_this;
778 	uint_t nexttype;
779 	int rval;
780 	short oldlvl;
781 	int i;
782 
783 	FDERRPRINT(FDEP_L0, FDEM_GETL,
784 	    (CE_CONT, "fdgetlabel fd unit %d\n", unit));
785 	fdp = (struct fdisk *)fjp->fj_data;
786 	fjp->fj_flags &= ~(FUNIT_UNLABELED);
787 
788 	/*
789 	 * get some space to play with the label
790 	 */
791 	label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
792 	FDERRPRINT(FDEP_L0, FDEM_GETL, (CE_CONT,
793 	    "fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n",
794 	    unit, (void *)label, (size_t)sizeof (struct dk_label)));
795 
796 	/*
797 	 * read block 0 (0/0/1) to find the label
798 	 * (disk is potentially not present or unformatted)
799 	 */
800 	/* noerrprint since this is a private cmd */
801 	oldlvl = fderrlevel;
802 	fderrlevel = FDEP_LMAX;
803 	/*
804 	 * try different characteristics (ie densities)
805 	 *
806 	 * if fdp->d_curfdtype is -1 then the current characteristics
807 	 * were set by ioctl and need to try it as well as everything
808 	 * in the table
809 	 */
810 	nexttype = fdp->d_deffdtype;
811 	try_this = 1;		/* always try the current characteristics */
812 
813 	rval = ENXIO;
814 	for (tries = nfdtypes; tries; tries--) {
815 		if (try_this) {
816 			fjp->fj_flags &= ~FUNIT_CHAROK;
817 
818 			/* try reading last sector of cyl 1, head 0 */
819 			if (!(rval = fjp->fj_ops->fco_rw(fjp, unit,
820 			    FDREAD, 1, 0, fjp->fj_chars->fdc_secptrack,
821 			    (caddr_t)label,
822 			    sizeof (struct dk_label))) &&
823 			    /* and last sector plus 1 of cylinder 1 */
824 			    fjp->fj_ops->fco_rw(fjp, unit, FDREAD, 1,
825 			    0, fjp->fj_chars->fdc_secptrack + 1,
826 			    (caddr_t)label,
827 			    sizeof (struct dk_label)) &&
828 			    /* and label sector on cylinder 0 */
829 			    !(rval = fjp->fj_ops->fco_rw(fjp, unit,
830 			    FDREAD, 0, 0, 1, (caddr_t)label,
831 			    sizeof (struct dk_label))))
832 				break;
833 			if (rval == ENXIO)
834 				break;
835 		}
836 		/*
837 		 * try the next entry in the characteristics tbl
838 		 */
839 		fdp->d_curfdtype = (signed char)nexttype;
840 		nexttype = (nexttype + 1) % nfdtypes;
841 		if ((1 << fdp->d_curfdtype) & fdp->d_media) {
842 			*fjp->fj_chars = *defchar[fdp->d_curfdtype];
843 			*fjp->fj_attr = fdtypes[fdp->d_curfdtype];
844 			bcopy(fdparts[fdp->d_curfdtype], fdp->d_part,
845 			    sizeof (struct partition) * NDKMAP);
846 			/*
847 			 * check for a double_density diskette
848 			 * in a high_density 5.25" drive
849 			 */
850 			if (fjp->fj_chars->fdc_transfer_rate == 250 &&
851 			    fjp->fj_rotspd > fjp->fj_attr->fda_rotatespd) {
852 				/*
853 				 * yes - adjust transfer rate since we don't
854 				 * know if we have a 5.25" dual-speed drive
855 				 */
856 				fjp->fj_attr->fda_rotatespd = 360;
857 				fjp->fj_chars->fdc_transfer_rate = 300;
858 				fjp->fj_chars->fdc_medium = 5;
859 			}
860 			if ((2 * fjp->fj_chars->fdc_ncyl) ==
861 			    defchar[fdp->d_deffdtype]->fdc_ncyl) {
862 				/* yes - adjust steps per cylinder */
863 				fjp->fj_chars->fdc_steps = 2;
864 			} else
865 				fjp->fj_chars->fdc_steps = 1;
866 			try_this = 1;
867 		} else
868 			try_this = 0;
869 	}
870 	fderrlevel = oldlvl;	/* print errors again */
871 
872 	if (rval) {
873 		fdp->d_curfdtype = fdp->d_deffdtype;
874 		goto out;			/* couldn't read anything */
875 	}
876 
877 	FDERRPRINT(FDEP_L0, FDEM_GETL,
878 	    (CE_CONT,
879 	    "fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n",
880 	    unit, fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_secptrack,
881 	    fjp->fj_chars->fdc_steps, fjp->fj_attr->fda_rotatespd,
882 	    fjp->fj_attr->fda_intrlv));
883 
884 	/*
885 	 * _something_ was read  -  look for unixtype label
886 	 */
887 	if (label->dkl_magic != DKL_MAGIC ||
888 	    label->dkl_vtoc.v_sanity != VTOC_SANE) {
889 		/* not a label - no magic number */
890 		goto nolabel;	/* no errors, but no label */
891 	}
892 
893 	count = sizeof (struct dk_label) / sizeof (short);
894 	sp = (short *)label;
895 	xsum = 0;
896 	while (count--)
897 		xsum ^= *sp++;	/* should add up to 0 */
898 	if (xsum) {
899 		/* not a label - checksum didn't compute */
900 		goto nolabel;	/* no errors, but no label */
901 	}
902 
903 	/*
904 	 * the SunOS label overrides current diskette characteristics
905 	 */
906 	fjp->fj_chars->fdc_ncyl = label->dkl_pcyl;
907 	fjp->fj_chars->fdc_nhead = label->dkl_nhead;
908 	fjp->fj_chars->fdc_secptrack = (label->dkl_nsect * DEV_BSIZE) /
909 	    fjp->fj_chars->fdc_sec_size;
910 	if (defchar[fdp->d_deffdtype]->fdc_ncyl == 2 * fjp->fj_chars->fdc_ncyl)
911 		fjp->fj_chars->fdc_steps = 2;
912 	else
913 		fjp->fj_chars->fdc_steps = 1;
914 
915 	fjp->fj_attr->fda_rotatespd = label->dkl_rpm;
916 	fjp->fj_attr->fda_intrlv = label->dkl_intrlv;
917 
918 	fdp->d_vtoc_version = label->dkl_vtoc.v_version;
919 	bcopy(label->dkl_vtoc.v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
920 	bcopy(label->dkl_vtoc.v_asciilabel,
921 	    fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
922 	/*
923 	 * logical partitions
924 	 */
925 	for (i = 0; i < NDKMAP; i++) {
926 		fdp->d_part[i].p_tag = label->dkl_vtoc.v_part[i].p_tag;
927 		fdp->d_part[i].p_flag = label->dkl_vtoc.v_part[i].p_flag;
928 		fdp->d_part[i].p_start = label->dkl_vtoc.v_part[i].p_start;
929 		fdp->d_part[i].p_size = label->dkl_vtoc.v_part[i].p_size;
930 
931 		fdp->d_vtoc_timestamp[i] = label->dkl_vtoc.timestamp[i];
932 	}
933 
934 	fjp->fj_flags |= FUNIT_LABELOK;
935 	goto out;
936 
937 nolabel:
938 	/*
939 	 * if not found, fill in label info from default (mark default used)
940 	 */
941 	if (fdp->d_media & (1<<FMT_3D))
942 		newlabel = deflabel_35;
943 	else /* if (fdp->d_media & (1<<FMT_5D9)) */
944 		newlabel = deflabel_525;
945 	bzero(fdp->d_vtoc_volume, LEN_DKL_VVOL);
946 	(void) sprintf(fdp->d_vtoc_asciilabel, newlabel,
947 	    fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_nhead,
948 	    fjp->fj_chars->fdc_secptrack);
949 	fjp->fj_flags |= FUNIT_UNLABELED;
950 
951 out:
952 	kmem_free(label, sizeof (struct dk_label));
953 	return (rval);
954 }
955 
956 
957 /*ARGSUSED*/
958 static int
959 fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
960 {
961 	struct fcu_obj *fjp = NULL;
962 	struct fdisk *fdp = NULL;
963 	int part, part_is_closed;
964 
965 #ifdef DEBUG
966 	int unit;
967 #define	DEBUG_ASSIGN	unit=
968 #else
969 #define	DEBUG_ASSIGN	(void)
970 #endif
971 
972 	DEBUG_ASSIGN fd_getdrive(dev, &fjp, &fdp);
973 	/*
974 	 * Ignoring return in non DEBUG mode because success is checked by
975 	 * verifying fjp and fdp and returned unit value is not used.
976 	 */
977 	if (!fjp || !fdp)
978 		return (ENXIO);
979 	part = PARTITION(dev);
980 
981 	sema_p(&fdp->d_ocsem);
982 	FDERRPRINT(FDEP_L1, FDEM_CLOS,
983 	    (CE_CONT, "fd_close: fd unit %d part %d otype %x\n",
984 	    unit, part, otyp));
985 
986 	if (otyp == OTYP_LYR) {
987 		if (fdp->d_lyropen[part])
988 			fdp->d_lyropen[part]--;
989 		part_is_closed = (fdp->d_lyropen[part] == 0);
990 	} else {
991 		fdp->d_regopen[otyp] &= ~(1<<part);
992 		part_is_closed = 1;
993 	}
994 	if (part_is_closed) {
995 		if (part == 2 && fdp->d_exclmask&(1<<part))
996 			fdp->d_exclmask = 0;
997 		else
998 			fdp->d_exclmask &= ~(1<<part);
999 		FDERRPRINT(FDEP_L0, FDEM_CLOS,
1000 		    (CE_CONT,
1001 		    "fd_close: exclparts %lx openparts %lx lyrcnt %lx\n",
1002 		    fdp->d_exclmask, fdp->d_regopen[otyp],
1003 		    fdp->d_lyropen[part]));
1004 
1005 		if (fd_unit_is_open(fdp) == 0)
1006 			fdp->d_obj->fj_flags &= ~FUNIT_CHANGED;
1007 	}
1008 	sema_v(&fdp->d_ocsem);
1009 	return (0);
1010 }
1011 
1012 /* ARGSUSED */
1013 static int
1014 fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
1015 {
1016 	return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
1017 }
1018 
1019 /* ARGSUSED */
1020 static int
1021 fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
1022 {
1023 	return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
1024 }
1025 
1026 /*
1027  * fd_strategy
1028  *	checks operation, hangs buf struct off fdcntlr, calls fdstart
1029  *	if not already busy.  Note that if we call start, then the operation
1030  *	will already be done on return (start sleeps).
1031  */
1032 static int
1033 fd_strategy(struct buf *bp)
1034 {
1035 	struct fcu_obj *fjp;
1036 	struct fdisk *fdp;
1037 	struct partition *pp;
1038 
1039 	FDERRPRINT(FDEP_L1, FDEM_STRA,
1040 	    (CE_CONT, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
1041 	    (void *)bp, bp->b_edev));
1042 
1043 	(void) fd_getdrive(bp->b_edev, &fjp, &fdp);
1044 
1045 	/*
1046 	 * Ignoring return because device exist.
1047 	 * Returned unit value is not used.
1048 	 */
1049 	pp = &fdp->d_part[PARTITION(bp->b_edev)];
1050 
1051 	if (fjp->fj_chars->fdc_sec_size > NBPSCTR && (bp->b_blkno & 1))  {
1052 		FDERRPRINT(FDEP_L3, FDEM_STRA,
1053 		    (CE_WARN, "fd%d: block %ld is not start of sector!",
1054 		    DRIVE(bp->b_edev), (long)bp->b_blkno));
1055 		bp->b_error = EINVAL;
1056 		goto bad;
1057 	}
1058 
1059 	if ((bp->b_blkno > pp->p_size)) {
1060 		FDERRPRINT(FDEP_L3, FDEM_STRA,
1061 		    (CE_WARN, "fd%d: block %ld is past the end! (nblk=%ld)",
1062 		    DRIVE(bp->b_edev), (long)bp->b_blkno, pp->p_size));
1063 		bp->b_error = ENOSPC;
1064 		goto bad;
1065 	}
1066 
1067 	/* if at end of file, skip out now */
1068 	if (bp->b_blkno == pp->p_size) {
1069 		if ((bp->b_flags & B_READ) == 0) {
1070 			/* a write needs to get an error! */
1071 			bp->b_error = ENOSPC;
1072 			goto bad;
1073 		}
1074 		bp->b_resid = bp->b_bcount;
1075 		biodone(bp);
1076 		return (0);
1077 	}
1078 
1079 	/* if operation not a multiple of sector size, is error! */
1080 	if (bp->b_bcount % fjp->fj_chars->fdc_sec_size)  {
1081 		FDERRPRINT(FDEP_L3, FDEM_STRA,
1082 		    (CE_WARN, "fd%d: count %ld must be a multiple of %d",
1083 		    DRIVE(bp->b_edev), bp->b_bcount,
1084 		    fjp->fj_chars->fdc_sec_size));
1085 		bp->b_error = EINVAL;
1086 		goto bad;
1087 	}
1088 
1089 	/*
1090 	 * Put the buf request in the drive's queue, FIFO.
1091 	 */
1092 	bp->av_forw = 0;
1093 	mutex_enter(&fjp->fj_lock);
1094 	if (fdp->d_iostat)
1095 		kstat_waitq_enter(KIOSP);
1096 	if (fdp->d_actf)
1097 		fdp->d_actl->av_forw = bp;
1098 	else
1099 		fdp->d_actf = bp;
1100 	fdp->d_actl = bp;
1101 	if (!(fjp->fj_flags & FUNIT_BUSY)) {
1102 		fdstart(fjp);
1103 	}
1104 	mutex_exit(&fjp->fj_lock);
1105 	return (0);
1106 
1107 bad:
1108 	bp->b_resid = bp->b_bcount;
1109 	bp->b_flags |= B_ERROR;
1110 	biodone(bp);
1111 	return (0);
1112 }
1113 
1114 /*
1115  * fdstart
1116  *	called from fd_strategy() or from fdXXXX() to setup and
1117  *	start operations of read or write only (using buf structs).
1118  *	Because the chip doesn't handle crossing cylinder boundaries on
1119  *	the fly, this takes care of those boundary conditions.  Note that
1120  *	it sleeps until the operation is done *within fdstart* - so that
1121  *	when fdstart returns, the operation is already done.
1122  */
1123 static void
1124 fdstart(struct fcu_obj *fjp)
1125 {
1126 	struct buf *bp;
1127 	struct fdisk *fdp = (struct fdisk *)fjp->fj_data;
1128 	struct fd_char *chp;
1129 	struct partition *pp;
1130 	uint_t ptend;
1131 	uint_t bincyl;		/* (the number of the desired) block in cyl. */
1132 	uint_t blk, len, tlen;
1133 	uint_t secpcyl;		/* number of sectors per cylinder */
1134 	int cyl, head, sect;
1135 	int sctrshft, unit;
1136 	caddr_t	addr;
1137 
1138 	ASSERT(MUTEX_HELD(&fjp->fj_lock));
1139 	fjp->fj_flags |= FUNIT_BUSY;
1140 
1141 	while ((bp = fdp->d_actf) != NULL) {
1142 		fdp->d_actf = bp->av_forw;
1143 		fdp->d_current = bp;
1144 		if (fdp->d_iostat) {
1145 			kstat_waitq_to_runq(KIOSP);
1146 		}
1147 		mutex_exit(&fjp->fj_lock);
1148 
1149 		FDERRPRINT(FDEP_L0, FDEM_STRT,
1150 		    (CE_CONT, "fdstart: bp=0x%p blkno=0x%lx bcount=0x%lx\n",
1151 		    (void *)bp, (long)bp->b_blkno, bp->b_bcount));
1152 		bp->b_flags &= ~B_ERROR;
1153 		bp->b_error = 0;
1154 		bp->b_resid = bp->b_bcount;	/* init resid */
1155 
1156 		ASSERT(DRIVE(bp->b_edev) == ddi_get_instance(fjp->fj_dip));
1157 		unit = fjp->fj_unit;
1158 		fjp->fj_ops->fco_select(fjp, unit, 1);
1159 
1160 		bp_mapin(bp);			/* map in buffers */
1161 
1162 		pp = &fdp->d_part[PARTITION(bp->b_edev)];
1163 		/* starting blk adjusted for the partition */
1164 		blk = bp->b_blkno + pp->p_start;
1165 		ptend = pp->p_start + pp->p_size;   /* end of the partition */
1166 
1167 		chp = fjp->fj_chars;
1168 		secpcyl = chp->fdc_nhead * chp->fdc_secptrack;
1169 		switch (chp->fdc_sec_size) {
1170 		/* convert logical block numbers to sector numbers */
1171 		case 1024:
1172 			sctrshft = SCTRSHFT + 1;
1173 			blk >>= 1;
1174 			ptend >>= 1;
1175 			break;
1176 		default:
1177 		case NBPSCTR:
1178 			sctrshft = SCTRSHFT;
1179 			break;
1180 		case 256:
1181 			sctrshft = SCTRSHFT - 1;
1182 			blk <<= 1;
1183 			ptend <<= 1;
1184 			break;
1185 		}
1186 
1187 		/*
1188 		 * If off the end, limit to actual amount that
1189 		 * can be transferred.
1190 		 */
1191 		if ((blk + (bp->b_bcount >> sctrshft)) > ptend)
1192 			/* to end of partition */
1193 			len = (ptend - blk) << sctrshft;
1194 		else
1195 			len = bp->b_bcount;
1196 		addr = bp->b_un.b_addr;		/* data buffer address */
1197 
1198 		/*
1199 		 * now we have the real start blk, addr and len for xfer op
1200 		 */
1201 		while (len != 0) {
1202 			/* start cyl of req */
1203 			cyl = blk / secpcyl;
1204 			bincyl = blk % secpcyl;
1205 			/* start head of req */
1206 			head = bincyl / chp->fdc_secptrack;
1207 			/* start sector of req */
1208 			sect = (bincyl % chp->fdc_secptrack) + 1;
1209 			/*
1210 			 * If the desired block and length will go beyond the
1211 			 * cylinder end, then limit it to the cylinder end.
1212 			 */
1213 			if (bp->b_flags & B_READ) {
1214 				if (len > ((secpcyl - bincyl) << sctrshft))
1215 					tlen = (secpcyl - bincyl) << sctrshft;
1216 				else
1217 					tlen = len;
1218 			} else {
1219 				if (len >
1220 				    ((chp->fdc_secptrack - sect + 1) <<
1221 				    sctrshft))
1222 					tlen =
1223 					    (chp->fdc_secptrack - sect + 1) <<
1224 					    sctrshft;
1225 				else
1226 					tlen = len;
1227 			}
1228 
1229 			FDERRPRINT(FDEP_L0, FDEM_STRT, (CE_CONT,
1230 			    "  blk 0x%x addr 0x%p len 0x%x "
1231 			    "cyl %d head %d sec %d\n  resid 0x%lx, tlen %d\n",
1232 			    blk, (void *)addr, len, cyl, head, sect,
1233 			    bp->b_resid, tlen));
1234 
1235 			/*
1236 			 * (try to) do the operation - failure returns an errno
1237 			 */
1238 			bp->b_error = fjp->fj_ops->fco_rw(fjp, unit,
1239 			    bp->b_flags & B_READ, cyl, head, sect, addr, tlen);
1240 			if (bp->b_error != 0) {
1241 				FDERRPRINT(FDEP_L3, FDEM_STRT, (CE_WARN,
1242 				    "fdstart: bad exec of bp: 0x%p, err=%d",
1243 				    (void *)bp, bp->b_error));
1244 				bp->b_flags |= B_ERROR;
1245 				break;
1246 			}
1247 			blk += tlen >> sctrshft;
1248 			len -= tlen;
1249 			addr += tlen;
1250 			bp->b_resid -= tlen;
1251 		}
1252 		FDERRPRINT(FDEP_L0, FDEM_STRT,
1253 		    (CE_CONT, "fdstart done: b_resid %lu, b_count %lu\n",
1254 		    bp->b_resid, bp->b_bcount));
1255 		if (fdp->d_iostat) {
1256 			if (bp->b_flags & B_READ) {
1257 				KIOSP->reads++;
1258 				KIOSP->nread += (bp->b_bcount - bp->b_resid);
1259 			} else {
1260 				KIOSP->writes++;
1261 				KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
1262 			}
1263 			kstat_runq_exit(KIOSP);
1264 		}
1265 		bp_mapout(bp);
1266 		biodone(bp);
1267 
1268 		fjp->fj_ops->fco_select(fjp, unit, 0);
1269 		mutex_enter(&fjp->fj_lock);
1270 		fdp->d_current = 0;
1271 	}
1272 	fjp->fj_flags ^= FUNIT_BUSY;
1273 }
1274 
1275 /* ARGSUSED */
1276 static int
1277 fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
1278     int *rval_p)
1279 {
1280 	union {
1281 		struct dk_cinfo dki;
1282 		struct dk_geom dkg;
1283 		struct dk_allmap dka;
1284 		struct fd_char fdchar;
1285 		struct fd_drive drvchar;
1286 		int	temp;
1287 	} cpy;
1288 	struct vtoc vtoc;
1289 	struct fcu_obj *fjp = NULL;
1290 	struct fdisk *fdp = NULL;
1291 	struct dk_map *dmp;
1292 	struct dk_label *label;
1293 	int nblks, part, unit;
1294 	int rval = 0;
1295 	enum dkio_state state;
1296 
1297 	unit = fd_getdrive(dev, &fjp, &fdp);
1298 	if (!fjp || !fdp)
1299 		return (ENXIO);
1300 
1301 	FDERRPRINT(FDEP_L1, FDEM_IOCT,
1302 	    (CE_CONT, "fd_ioctl fd unit %d: cmd %x, arg %lx\n",
1303 	    unit, cmd, arg));
1304 
1305 	switch (cmd) {
1306 	case DKIOCINFO:
1307 		fjp->fj_ops->fco_dkinfo(fjp, &cpy.dki);
1308 		cpy.dki.dki_cnum = FDCTLR(fjp->fj_unit);
1309 		cpy.dki.dki_unit = FDUNIT(fjp->fj_unit);
1310 		cpy.dki.dki_partition = PARTITION(dev);
1311 		if (ddi_copyout(&cpy.dki, (void *)arg, sizeof (cpy.dki), flag))
1312 			rval = EFAULT;
1313 		break;
1314 
1315 	case DKIOCG_PHYGEOM:
1316 	case DKIOCG_VIRTGEOM:
1317 		cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1318 		goto get_geom;
1319 	case DKIOCGGEOM:
1320 		if (fjp->fj_flags & FUNIT_LABELOK)
1321 			cpy.dkg.dkg_nsect = (fjp->fj_chars->fdc_secptrack *
1322 			    fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1323 		else
1324 			cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1325 get_geom:
1326 		cpy.dkg.dkg_pcyl = fjp->fj_chars->fdc_ncyl;
1327 		cpy.dkg.dkg_ncyl = fjp->fj_chars->fdc_ncyl;
1328 		cpy.dkg.dkg_nhead = fjp->fj_chars->fdc_nhead;
1329 		cpy.dkg.dkg_intrlv = fjp->fj_attr->fda_intrlv;
1330 		cpy.dkg.dkg_rpm = fjp->fj_attr->fda_rotatespd;
1331 		cpy.dkg.dkg_read_reinstruct =
1332 		    (int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
1333 		cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
1334 		if (ddi_copyout(&cpy.dkg, (void *)arg, sizeof (cpy.dkg), flag))
1335 			rval = EFAULT;
1336 		break;
1337 
1338 	case DKIOCSGEOM:
1339 		if (ddi_copyin((void *)arg, &cpy.dkg,
1340 		    sizeof (struct dk_geom), flag)) {
1341 			rval = EFAULT;
1342 			break;
1343 		}
1344 		mutex_enter(&fjp->fj_lock);
1345 		fjp->fj_chars->fdc_ncyl = cpy.dkg.dkg_ncyl;
1346 		fjp->fj_chars->fdc_nhead = cpy.dkg.dkg_nhead;
1347 		fjp->fj_chars->fdc_secptrack = cpy.dkg.dkg_nsect;
1348 		fjp->fj_attr->fda_intrlv = cpy.dkg.dkg_intrlv;
1349 		fjp->fj_attr->fda_rotatespd = cpy.dkg.dkg_rpm;
1350 		fdp->d_curfdtype = -1;
1351 		mutex_exit(&fjp->fj_lock);
1352 		break;
1353 
1354 	/*
1355 	 * return the map of all logical partitions
1356 	 */
1357 	case DKIOCGAPART:
1358 		/*
1359 		 * Note the conversion from starting sector number
1360 		 * to starting cylinder number.
1361 		 * Return error if division results in a remainder.
1362 		 */
1363 		nblks = fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack;
1364 
1365 #ifdef _MULTI_DATAMODEL
1366 		switch (ddi_model_convert_from(flag & FMODELS)) {
1367 		case DDI_MODEL_ILP32:
1368 		{
1369 			struct dk_allmap32 dka32;
1370 
1371 			for (part = 0; part < NDKMAP; part++) {
1372 				if ((fdp->d_part[part].p_start % nblks) != 0)
1373 					return (EINVAL);
1374 				dka32.dka_map[part].dkl_cylno =
1375 				    fdp->d_part[part].p_start / nblks;
1376 				dka32.dka_map[part].dkl_nblk =
1377 				    fdp->d_part[part].p_size;
1378 			}
1379 
1380 			if (ddi_copyout(&dka32, (void *)arg,
1381 			    sizeof (struct dk_allmap32), flag))
1382 				rval = EFAULT;
1383 
1384 			break;
1385 		}
1386 		case DDI_MODEL_NONE:
1387 
1388 #endif /* _MULTI_DATAMODEL */
1389 
1390 			dmp = (struct dk_map *)&cpy.dka;
1391 			for (part = 0; part < NDKMAP; part++) {
1392 				if ((fdp->d_part[part].p_start % nblks) != 0)
1393 					return (EINVAL);
1394 				dmp->dkl_cylno =
1395 				    fdp->d_part[part].p_start / nblks;
1396 				dmp->dkl_nblk = fdp->d_part[part].p_size;
1397 				dmp++;
1398 			}
1399 
1400 			if (ddi_copyout(&cpy.dka, (void *)arg,
1401 			    sizeof (struct dk_allmap), flag))
1402 				rval = EFAULT;
1403 #ifdef _MULTI_DATAMODEL
1404 			break;
1405 
1406 		}
1407 #endif /* _MULTI_DATAMODEL */
1408 
1409 		break;
1410 
1411 	/*
1412 	 * Set the map of all logical partitions
1413 	 */
1414 	case DKIOCSAPART:
1415 
1416 #ifdef _MULTI_DATAMODEL
1417 		switch (ddi_model_convert_from(flag & FMODELS)) {
1418 		case DDI_MODEL_ILP32:
1419 		{
1420 			struct dk_allmap32 dka32;
1421 
1422 			if (ddi_copyin((void *)arg, &dka32,
1423 			    sizeof (dka32), flag)) {
1424 				rval = EFAULT;
1425 				break;
1426 			}
1427 			for (part = 0; part < NDKMAP; part++) {
1428 				cpy.dka.dka_map[part].dkl_cylno =
1429 				    dka32.dka_map[part].dkl_cylno;
1430 				cpy.dka.dka_map[part].dkl_nblk =
1431 				    dka32.dka_map[part].dkl_nblk;
1432 			}
1433 			break;
1434 		}
1435 		case DDI_MODEL_NONE:
1436 
1437 #endif /* _MULTI_DATAMODEL */
1438 		if (ddi_copyin((void *)arg, &cpy.dka, sizeof (cpy.dka), flag))
1439 			rval = EFAULT;
1440 #ifdef _MULTI_DATAMODEL
1441 
1442 			break;
1443 		}
1444 #endif /* _MULTI_DATAMODEL */
1445 
1446 		if (rval != 0)
1447 			break;
1448 
1449 		dmp = (struct dk_map *)&cpy.dka;
1450 		nblks = fjp->fj_chars->fdc_nhead *
1451 		    fjp->fj_chars->fdc_secptrack;
1452 		mutex_enter(&fjp->fj_lock);
1453 		/*
1454 		 * Note the conversion from starting cylinder number
1455 		 * to starting sector number.
1456 		 */
1457 		for (part = 0; part < NDKMAP; part++) {
1458 			fdp->d_part[part].p_start = dmp->dkl_cylno *
1459 			    nblks;
1460 			fdp->d_part[part].p_size = dmp->dkl_nblk;
1461 			dmp++;
1462 		}
1463 		mutex_exit(&fjp->fj_lock);
1464 
1465 		break;
1466 
1467 	case DKIOCGVTOC:
1468 		mutex_enter(&fjp->fj_lock);
1469 
1470 		/*
1471 		 * Exit if the diskette has no label.
1472 		 * Also, get the label to make sure the correct one is
1473 		 * being used since the diskette may have changed
1474 		 */
1475 		fjp->fj_ops->fco_select(fjp, unit, 1);
1476 		rval = fdgetlabel(fjp, unit);
1477 		fjp->fj_ops->fco_select(fjp, unit, 0);
1478 		if (rval) {
1479 			mutex_exit(&fjp->fj_lock);
1480 			rval = EINVAL;
1481 			break;
1482 		}
1483 
1484 		fd_build_user_vtoc(fjp, fdp, &vtoc);
1485 		mutex_exit(&fjp->fj_lock);
1486 
1487 #ifdef _MULTI_DATAMODEL
1488 		switch (ddi_model_convert_from(flag & FMODELS)) {
1489 		case DDI_MODEL_ILP32:
1490 		{
1491 			struct vtoc32	vtoc32;
1492 
1493 			vtoctovtoc32(vtoc, vtoc32);
1494 
1495 			if (ddi_copyout(&vtoc32, (void *)arg,
1496 			    sizeof (vtoc32), flag))
1497 				rval = EFAULT;
1498 
1499 			break;
1500 		}
1501 		case DDI_MODEL_NONE:
1502 
1503 #endif /* _MULTI_DATAMODEL */
1504 			if (ddi_copyout(&vtoc, (void *)arg,
1505 			    sizeof (vtoc), flag))
1506 				rval = EFAULT;
1507 #ifdef _MULTI_DATAMODEL
1508 			break;
1509 		}
1510 #endif /* _MULTI_DATAMODEL */
1511 
1512 		break;
1513 
1514 	case DKIOCSVTOC:
1515 
1516 #ifdef _MULTI_DATAMODEL
1517 		switch (ddi_model_convert_from(flag & FMODELS)) {
1518 		case DDI_MODEL_ILP32:
1519 		{
1520 			struct vtoc32	vtoc32;
1521 
1522 			if (ddi_copyin((void *)arg, &vtoc32,
1523 			    sizeof (vtoc32), flag)) {
1524 				rval = EFAULT;
1525 				break;
1526 			}
1527 
1528 			vtoc32tovtoc(vtoc32, vtoc);
1529 
1530 			break;
1531 		}
1532 		case DDI_MODEL_NONE:
1533 
1534 #endif /* _MULTI_DATAMODEL */
1535 			if (ddi_copyin((void *)arg, &vtoc, sizeof (vtoc), flag))
1536 				rval = EFAULT;
1537 #ifdef _MULTI_DATAMODEL
1538 			break;
1539 		}
1540 #endif /* _MULTI_DATAMODEL */
1541 
1542 		if (rval != 0)
1543 			break;
1544 
1545 
1546 		label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
1547 
1548 		mutex_enter(&fjp->fj_lock);
1549 
1550 		if ((rval = fd_build_label_vtoc(fjp, fdp, &vtoc, label)) == 0) {
1551 			fjp->fj_ops->fco_select(fjp, unit, 1);
1552 			rval = fjp->fj_ops->fco_rw(fjp, unit, FDWRITE,
1553 			    0, 0, 1, (caddr_t)label, sizeof (struct dk_label));
1554 			fjp->fj_ops->fco_select(fjp, unit, 0);
1555 		}
1556 		mutex_exit(&fjp->fj_lock);
1557 		kmem_free(label, sizeof (struct dk_label));
1558 		break;
1559 
1560 	case DKIOCSTATE:
1561 		FDERRPRINT(FDEP_L1, FDEM_IOCT,
1562 		    (CE_CONT, "fd_ioctl fd unit %d: DKIOCSTATE\n", unit));
1563 
1564 		if (ddi_copyin((void *)arg, &state, sizeof (int), flag)) {
1565 			rval = EFAULT;
1566 			break;
1567 		}
1568 
1569 		rval = fd_check_media(dev, state);
1570 
1571 		if (ddi_copyout(&fdp->d_media_state, (void *)arg,
1572 		    sizeof (int), flag))
1573 			rval = EFAULT;
1574 		break;
1575 
1576 	case FDIOGCHAR:
1577 		if (ddi_copyout(fjp->fj_chars, (void *)arg,
1578 		    sizeof (struct fd_char), flag))
1579 			rval = EFAULT;
1580 		break;
1581 
1582 	case FDIOSCHAR:
1583 		if (ddi_copyin((void *)arg, &cpy.fdchar,
1584 		    sizeof (struct fd_char), flag)) {
1585 			rval = EFAULT;
1586 			break;
1587 		}
1588 		switch (cpy.fdchar.fdc_transfer_rate) {
1589 		case 417:
1590 			if ((fdp->d_media & (1 << FMT_3M)) == 0) {
1591 				cmn_err(CE_CONT,
1592 				    "fdioschar:Medium density not supported\n");
1593 				rval = EINVAL;
1594 				break;
1595 			}
1596 			mutex_enter(&fjp->fj_lock);
1597 			fjp->fj_attr->fda_rotatespd = 360;
1598 			mutex_exit(&fjp->fj_lock);
1599 			/* cpy.fdchar.fdc_transfer_rate = 500; */
1600 			/* FALLTHROUGH */
1601 		case 1000:
1602 		case 500:
1603 		case 300:
1604 		case 250:
1605 			mutex_enter(&fjp->fj_lock);
1606 			*(fjp->fj_chars) = cpy.fdchar;
1607 			fdp->d_curfdtype = -1;
1608 			fjp->fj_flags &= ~FUNIT_CHAROK;
1609 			mutex_exit(&fjp->fj_lock);
1610 
1611 			break;
1612 
1613 		default:
1614 			FDERRPRINT(FDEP_L4, FDEM_IOCT,
1615 			    (CE_WARN, "fd_ioctl fd unit %d: FDIOSCHAR odd "
1616 			    "xfer rate %dkbs",
1617 			    unit, cpy.fdchar.fdc_transfer_rate));
1618 			rval = EINVAL;
1619 			break;
1620 		}
1621 		break;
1622 
1623 	/*
1624 	 * set all characteristics and geometry to the defaults
1625 	 */
1626 	case FDDEFGEOCHAR:
1627 		mutex_enter(&fjp->fj_lock);
1628 		fdp->d_curfdtype = fdp->d_deffdtype;
1629 		*fjp->fj_chars = *defchar[fdp->d_curfdtype];
1630 		*fjp->fj_attr = fdtypes[fdp->d_curfdtype];
1631 		bcopy(fdparts[fdp->d_curfdtype],
1632 		    fdp->d_part, sizeof (struct partition) * NDKMAP);
1633 		fjp->fj_flags &= ~FUNIT_CHAROK;
1634 		mutex_exit(&fjp->fj_lock);
1635 		break;
1636 
1637 	case FDEJECT:  /* eject disk */
1638 	case DKIOCEJECT:
1639 		fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1640 		rval = ENOSYS;
1641 		break;
1642 
1643 	case FDGETCHANGE: /* disk changed */
1644 		if (ddi_copyin((void *)arg, &cpy.temp, sizeof (int), flag)) {
1645 			rval = EFAULT;
1646 			break;
1647 		}
1648 		mutex_enter(&fjp->fj_lock);
1649 		fjp->fj_ops->fco_select(fjp, unit, 1);
1650 
1651 		if (fjp->fj_flags & FUNIT_CHANGED)
1652 			cpy.temp |= FDGC_HISTORY;
1653 		else
1654 			cpy.temp &= ~FDGC_HISTORY;
1655 		fjp->fj_flags &= ~FUNIT_CHANGED;
1656 
1657 		if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1658 			cpy.temp |= FDGC_DETECTED;
1659 			fjp->fj_ops->fco_resetchng(fjp, unit);
1660 			/*
1661 			 * check diskette again only if it was removed
1662 			 */
1663 			if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1664 				/*
1665 				 * no diskette is present
1666 				 */
1667 				cpy.temp |= FDGC_CURRENT;
1668 				if (fjp->fj_flags & FUNIT_CHGDET)
1669 					/*
1670 					 * again no diskette; not a new change
1671 					 */
1672 					cpy.temp ^= FDGC_DETECTED;
1673 				else
1674 					fjp->fj_flags |= FUNIT_CHGDET;
1675 			} else {
1676 				/*
1677 				 * a new diskette is present
1678 				 */
1679 				cpy.temp &= ~FDGC_CURRENT;
1680 				fjp->fj_flags &= ~FUNIT_CHGDET;
1681 			}
1682 		} else {
1683 			cpy.temp &= ~(FDGC_DETECTED | FDGC_CURRENT);
1684 			fjp->fj_flags &= ~FUNIT_CHGDET;
1685 		}
1686 		/*
1687 		 * also get state of write protection
1688 		 */
1689 		if (fjp->fj_flags & FUNIT_WPROT) {
1690 			cpy.temp |= FDGC_CURWPROT;
1691 		} else {
1692 			cpy.temp &= ~FDGC_CURWPROT;
1693 		}
1694 		fjp->fj_ops->fco_select(fjp, unit, 0);
1695 		mutex_exit(&fjp->fj_lock);
1696 
1697 		if (ddi_copyout(&cpy.temp, (void *)arg, sizeof (int), flag))
1698 			rval = EFAULT;
1699 		break;
1700 
1701 	case FDGETDRIVECHAR:
1702 		if (ddi_copyout(fjp->fj_drive, (void *)arg,
1703 		    sizeof (struct fd_drive), flag))
1704 			rval = EFAULT;
1705 		break;
1706 
1707 	case FDSETDRIVECHAR:
1708 		if (ddi_copyin((void *)arg, &cpy.drvchar,
1709 		    sizeof (struct fd_drive), flag)) {
1710 			rval = EFAULT;
1711 			break;
1712 		}
1713 		mutex_enter(&fjp->fj_lock);
1714 		*(fjp->fj_drive) = cpy.drvchar;
1715 		fdp->d_curfdtype = -1;
1716 		fjp->fj_flags &= ~FUNIT_CHAROK;
1717 		mutex_exit(&fjp->fj_lock);
1718 		break;
1719 
1720 	case DKIOCREMOVABLE: {
1721 		int	i = 1;
1722 
1723 		/* no brainer: floppies are always removable */
1724 		if (ddi_copyout(&i, (void *)arg, sizeof (int), flag)) {
1725 			rval = EFAULT;
1726 		}
1727 		break;
1728 	}
1729 
1730 	case DKIOCGMEDIAINFO:
1731 		rval = fd_get_media_info(fjp, (caddr_t)arg, flag);
1732 		break;
1733 
1734 	case FDIOCMD:
1735 	{
1736 		struct fd_cmd fc;
1737 		int cyl, head, spc, spt;
1738 
1739 #ifdef _MULTI_DATAMODEL
1740 		switch (ddi_model_convert_from(flag & FMODELS)) {
1741 		case DDI_MODEL_ILP32:
1742 		{
1743 			struct fd_cmd32 fc32;
1744 
1745 			if (ddi_copyin((void *)arg, &fc32,
1746 			    sizeof (fc32), flag)) {
1747 				rval = EFAULT;
1748 				break;
1749 			}
1750 
1751 			fc.fdc_cmd = fc32.fdc_cmd;
1752 			fc.fdc_flags = fc32.fdc_flags;
1753 			fc.fdc_blkno = fc32.fdc_blkno;
1754 			fc.fdc_secnt = fc32.fdc_secnt;
1755 			fc.fdc_bufaddr = (caddr_t)(uintptr_t)fc32.fdc_bufaddr;
1756 			fc.fdc_buflen = fc32.fdc_buflen;
1757 
1758 			break;
1759 		}
1760 		case DDI_MODEL_NONE:
1761 
1762 #endif /* _MULTI_DATAMODEL */
1763 
1764 			if (ddi_copyin((void *)arg, &fc, sizeof (fc), flag)) {
1765 				rval = EFAULT;
1766 				break;
1767 			}
1768 #ifdef _MULTI_DATAMODEL
1769 			break;
1770 		}
1771 #endif /* _MULTI_DATAMODEL */
1772 
1773 		if (rval != 0)
1774 			break;
1775 
1776 		if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
1777 			auto struct iovec aiov;
1778 			auto struct uio auio;
1779 			struct uio *uio = &auio;
1780 
1781 			spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
1782 
1783 			bzero(&auio, sizeof (struct uio));
1784 			bzero(&aiov, sizeof (struct iovec));
1785 			aiov.iov_base = fc.fdc_bufaddr;
1786 			aiov.iov_len = (uint_t)fc.fdc_secnt *
1787 			    fjp->fj_chars->fdc_sec_size;
1788 			uio->uio_iov = &aiov;
1789 
1790 			uio->uio_iovcnt = 1;
1791 			uio->uio_resid = aiov.iov_len;
1792 			uio->uio_segflg = UIO_USERSPACE;
1793 
1794 			rval = physio(fd_strategy, (struct buf *)0, dev,
1795 			    spc, minphys, uio);
1796 			break;
1797 		} else if (fc.fdc_cmd == FDCMD_FORMAT_TRACK) {
1798 			spt = fjp->fj_chars->fdc_secptrack;	/* sec/trk */
1799 			spc = fjp->fj_chars->fdc_nhead * spt;	/* sec/cyl */
1800 			cyl = fc.fdc_blkno / spc;
1801 			head = (fc.fdc_blkno % spc) / spt;
1802 			if ((cyl | head) == 0)
1803 				fjp->fj_flags &=
1804 				    ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1805 
1806 			FDERRPRINT(FDEP_L0, FDEM_FORM,
1807 			    (CE_CONT, "fd_format cyl %d, hd %d\n", cyl, head));
1808 			fjp->fj_ops->fco_select(fjp, unit, 1);
1809 			rval = fjp->fj_ops->fco_format(fjp, unit, cyl, head,
1810 			    (int)fc.fdc_flags);
1811 			fjp->fj_ops->fco_select(fjp, unit, 0);
1812 
1813 			break;
1814 		}
1815 		FDERRPRINT(FDEP_L4, FDEM_IOCT,
1816 		    (CE_WARN, "fd_ioctl fd unit %d: FDIOCSCMD not yet complete",
1817 		    unit));
1818 		rval = EINVAL;
1819 		break;
1820 	}
1821 
1822 	case FDRAW:
1823 		rval = fd_rawioctl(fjp, unit, (caddr_t)arg, flag);
1824 		break;
1825 
1826 	default:
1827 		FDERRPRINT(FDEP_L4, FDEM_IOCT,
1828 		    (CE_WARN, "fd_ioctl fd unit %d: invalid ioctl 0x%x",
1829 		    unit, cmd));
1830 		rval = ENOTTY;
1831 		break;
1832 	}
1833 	return (rval);
1834 }
1835 
1836 static void
1837 fd_build_user_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp)
1838 {
1839 	struct partition *vpart;
1840 	int	i;
1841 	int	xblk;
1842 
1843 	/*
1844 	 * Return vtoc structure fields in the provided VTOC area, addressed
1845 	 * by *vtocp.
1846 	 *
1847 	 */
1848 	bzero(vtocp, sizeof (struct vtoc));
1849 
1850 	bcopy(fdp->d_vtoc_bootinfo,
1851 	    vtocp->v_bootinfo, sizeof (vtocp->v_bootinfo));
1852 
1853 	vtocp->v_sanity = VTOC_SANE;
1854 	vtocp->v_version = fdp->d_vtoc_version;
1855 	bcopy(fdp->d_vtoc_volume, vtocp->v_volume, LEN_DKL_VVOL);
1856 	if (fjp->fj_flags & FUNIT_LABELOK) {
1857 		vtocp->v_sectorsz = DEV_BSIZE;
1858 		xblk = 1;
1859 	} else {
1860 		vtocp->v_sectorsz = fjp->fj_chars->fdc_sec_size;
1861 		xblk = vtocp->v_sectorsz / DEV_BSIZE;
1862 	}
1863 	vtocp->v_nparts = 3;	/* <= NDKMAP;	*/
1864 
1865 	/*
1866 	 * Copy partitioning information.
1867 	 */
1868 	bcopy(fdp->d_part, vtocp->v_part, sizeof (struct partition) * NDKMAP);
1869 	for (i = NDKMAP, vpart = vtocp->v_part; i && (xblk > 1); i--, vpart++) {
1870 		/* correct partition info if sector size > 512 bytes */
1871 		vpart->p_start /= xblk;
1872 		vpart->p_size /= xblk;
1873 	}
1874 
1875 	bcopy(fdp->d_vtoc_timestamp,
1876 	    vtocp->timestamp, sizeof (fdp->d_vtoc_timestamp));
1877 	bcopy(fdp->d_vtoc_asciilabel, vtocp->v_asciilabel, LEN_DKL_ASCII);
1878 }
1879 
1880 
1881 static int
1882 fd_build_label_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp,
1883     struct dk_label *labelp)
1884 {
1885 	struct partition *vpart;
1886 	int	i;
1887 	int	nblks;
1888 	int	ncyl;
1889 	ushort_t sum, *sp;
1890 
1891 
1892 	/*
1893 	 * Sanity-check the vtoc
1894 	 */
1895 	if (vtocp->v_sanity != VTOC_SANE ||
1896 	    vtocp->v_nparts > NDKMAP || vtocp->v_nparts <= 0) {
1897 		FDERRPRINT(FDEP_L3, FDEM_IOCT,
1898 		    (CE_WARN, "fd_build_label:  sanity check on vtoc failed"));
1899 		return (EINVAL);
1900 	}
1901 
1902 	/*
1903 	 * before copying the vtoc, the partition information in it should be
1904 	 * checked against the information the driver already has on the
1905 	 * diskette.
1906 	 */
1907 
1908 	nblks = (fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack *
1909 	    fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1910 	if (nblks == 0 || fjp->fj_chars->fdc_ncyl == 0)
1911 		return (EFAULT);
1912 	vpart = vtocp->v_part;
1913 
1914 	/*
1915 	 * Check the partition information in the vtoc.  The starting sectors
1916 	 * must lie along cylinder boundaries. (NDKMAP entries are checked
1917 	 * to ensure that the unused entries are set to 0 if vtoc->v_nparts
1918 	 * is less than NDKMAP)
1919 	 */
1920 	for (i = NDKMAP; i; i--) {
1921 		if ((vpart->p_start % nblks) != 0) {
1922 			return (EINVAL);
1923 		}
1924 		ncyl = vpart->p_start / nblks;
1925 		ncyl += vpart->p_size / nblks;
1926 		if ((vpart->p_size % nblks) != 0)
1927 			ncyl++;
1928 		if (ncyl > (long)fjp->fj_chars->fdc_ncyl) {
1929 			return (EINVAL);
1930 		}
1931 		vpart++;
1932 	}
1933 
1934 
1935 	bcopy(vtocp->v_bootinfo, fdp->d_vtoc_bootinfo,
1936 	    sizeof (vtocp->v_bootinfo));
1937 	fdp->d_vtoc_version = vtocp->v_version;
1938 	bcopy(vtocp->v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
1939 
1940 	/*
1941 	 * Copy partitioning information.
1942 	 */
1943 	bcopy(vtocp->v_part, fdp->d_part, sizeof (struct partition) * NDKMAP);
1944 	bcopy(vtocp->timestamp, fdp->d_vtoc_timestamp,
1945 	    sizeof (fdp->d_vtoc_timestamp));
1946 	bcopy(vtocp->v_asciilabel, fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
1947 
1948 	/*
1949 	 * construct the diskette label in supplied buffer
1950 	 */
1951 
1952 	/* Put appropriate vtoc structure fields into the disk label */
1953 	labelp->dkl_vtoc.v_bootinfo[0] = (uint32_t)vtocp->v_bootinfo[0];
1954 	labelp->dkl_vtoc.v_bootinfo[1] = (uint32_t)vtocp->v_bootinfo[1];
1955 	labelp->dkl_vtoc.v_bootinfo[2] = (uint32_t)vtocp->v_bootinfo[2];
1956 
1957 	labelp->dkl_vtoc.v_sanity = vtocp->v_sanity;
1958 	labelp->dkl_vtoc.v_version = vtocp->v_version;
1959 
1960 	bcopy(vtocp->v_volume, labelp->dkl_vtoc.v_volume, LEN_DKL_VVOL);
1961 
1962 	labelp->dkl_vtoc.v_nparts = vtocp->v_nparts;
1963 
1964 	bcopy(vtocp->v_reserved, labelp->dkl_vtoc.v_reserved,
1965 	    sizeof (labelp->dkl_vtoc.v_reserved));
1966 
1967 	for (i = 0; i < (int)vtocp->v_nparts; i++) {
1968 		labelp->dkl_vtoc.v_part[i].p_tag  = vtocp->v_part[i].p_tag;
1969 		labelp->dkl_vtoc.v_part[i].p_flag  = vtocp->v_part[i].p_flag;
1970 		labelp->dkl_vtoc.v_part[i].p_start  = vtocp->v_part[i].p_start;
1971 		labelp->dkl_vtoc.v_part[i].p_size  = vtocp->v_part[i].p_size;
1972 	}
1973 
1974 	for (i = 0; i < NDKMAP; i++) {
1975 		labelp->dkl_vtoc.v_timestamp[i] = vtocp->timestamp[i];
1976 	}
1977 	bcopy(vtocp->v_asciilabel, labelp->dkl_asciilabel, LEN_DKL_ASCII);
1978 
1979 
1980 	labelp->dkl_pcyl = fjp->fj_chars->fdc_ncyl;
1981 	labelp->dkl_ncyl = fjp->fj_chars->fdc_ncyl;
1982 	labelp->dkl_nhead = fjp->fj_chars->fdc_nhead;
1983 	/*
1984 	 * The fdc_secptrack field of the fd_char structure is the number
1985 	 * of sectors per track where the sectors are fdc_sec_size.
1986 	 * The dkl_nsect field of the dk_label structure is the number of
1987 	 * DEV_BSIZE (512) byte sectors per track.
1988 	 */
1989 	labelp->dkl_nsect = (fjp->fj_chars->fdc_secptrack *
1990 	    fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1991 	labelp->dkl_intrlv = fjp->fj_attr->fda_intrlv;
1992 	labelp->dkl_rpm = fjp->fj_attr->fda_rotatespd;
1993 	labelp->dkl_read_reinstruct =
1994 	    (int)(labelp->dkl_nsect * labelp->dkl_rpm * 4) / 60000;
1995 	labelp->dkl_write_reinstruct = labelp->dkl_read_reinstruct;
1996 
1997 	labelp->dkl_magic = DKL_MAGIC;
1998 
1999 	sum = 0;
2000 	labelp->dkl_cksum = 0;
2001 	sp = (ushort_t *)labelp;
2002 	while (sp < &(labelp->dkl_cksum)) {
2003 		sum ^= *sp++;
2004 	}
2005 	labelp->dkl_cksum = sum;
2006 
2007 	return (0);
2008 }
2009 
2010 static int
2011 fd_rawioctl(struct fcu_obj *fjp, int unit, caddr_t arg, int mode)
2012 {
2013 	struct fd_raw fdr;
2014 	char *arg_result = NULL;
2015 	int flag = B_READ;
2016 	int rval = 0;
2017 	caddr_t	uaddr;
2018 	uint_t ucount;
2019 
2020 	FDERRPRINT(FDEP_L1, FDEM_RAWI,
2021 	    (CE_CONT, "fd_rawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
2022 
2023 	if (fjp->fj_chars->fdc_medium != 3 && fjp->fj_chars->fdc_medium != 5) {
2024 		cmn_err(CE_CONT, "fd_rawioctl: Medium density not supported\n");
2025 		return (ENXIO);
2026 	}
2027 
2028 #ifdef _MULTI_DATAMODEL
2029 	switch (ddi_model_convert_from(mode & FMODELS)) {
2030 	case DDI_MODEL_ILP32:
2031 	{
2032 		struct fd_raw32 fdr32;
2033 
2034 		if (ddi_copyin(arg, &fdr32, sizeof (fdr32), mode))
2035 			return (EFAULT);
2036 
2037 		bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
2038 		fdr.fdr_cnum = fdr32.fdr_cnum;
2039 		fdr.fdr_nbytes = fdr32.fdr_nbytes;
2040 		fdr.fdr_addr = (caddr_t)(uintptr_t)fdr32.fdr_addr;
2041 		arg_result = ((struct fd_raw32 *)arg)->fdr_result;
2042 
2043 		break;
2044 	}
2045 	case DDI_MODEL_NONE:
2046 #endif /* ! _MULTI_DATAMODEL */
2047 
2048 		if (ddi_copyin(arg, &fdr, sizeof (fdr), mode))
2049 			return (EFAULT);
2050 
2051 		arg_result = ((struct fd_raw *)arg)->fdr_result;
2052 
2053 #ifdef _MULTI_DATAMODEL
2054 		break;
2055 	}
2056 #endif /* _MULTI_DATAMODEL */
2057 
2058 
2059 
2060 	/*
2061 	 * copy user address & nbytes from raw_req so that we can
2062 	 * put kernel address in req structure
2063 	 */
2064 	uaddr = fdr.fdr_addr;
2065 	ucount = (uint_t)fdr.fdr_nbytes;
2066 	unit &= 3;
2067 
2068 	switch (fdr.fdr_cmd[0] & 0x0f) {
2069 
2070 	case FDRAW_FORMAT:
2071 		ucount += 16;
2072 		fdr.fdr_addr = kmem_zalloc(ucount, KM_SLEEP);
2073 		if (ddi_copyin(uaddr, fdr.fdr_addr,
2074 		    (size_t)fdr.fdr_nbytes, mode)) {
2075 			kmem_free(fdr.fdr_addr, ucount);
2076 			return (EFAULT);
2077 		}
2078 		if ((*fdr.fdr_addr | fdr.fdr_addr[1]) == 0)
2079 			fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
2080 		flag = B_WRITE;
2081 		fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2082 		break;
2083 
2084 	case FDRAW_WRCMD:
2085 	case FDRAW_WRITEDEL:
2086 		flag = B_WRITE;
2087 		/* FALLTHROUGH */
2088 	case FDRAW_RDCMD:
2089 	case FDRAW_READDEL:
2090 	case FDRAW_READTRACK:
2091 		if (ucount) {
2092 			/*
2093 			 * In SunOS 4.X, we used to as_fault things in.
2094 			 * We really cannot do this in 5.0/SVr4. Unless
2095 			 * someone really believes that speed is of the
2096 			 * essence here, it is just much simpler to do
2097 			 * this in kernel space and use copyin/copyout.
2098 			 */
2099 			fdr.fdr_addr = kmem_alloc((size_t)ucount, KM_SLEEP);
2100 			if (flag == B_WRITE) {
2101 				if (ddi_copyin(uaddr, fdr.fdr_addr, ucount,
2102 				    mode)) {
2103 					kmem_free(fdr.fdr_addr, ucount);
2104 					return (EFAULT);
2105 				}
2106 			}
2107 		} else
2108 			return (EINVAL);
2109 		fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2110 		break;
2111 
2112 	case FDRAW_READID:
2113 	case FDRAW_REZERO:
2114 	case FDRAW_SEEK:
2115 	case FDRAW_SENSE_DRV:
2116 		ucount = 0;
2117 		fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2118 		break;
2119 
2120 	case FDRAW_SPECIFY:
2121 		fdr.fdr_cmd[2] &= 0xfe;	/* keep NoDMA bit clear */
2122 		/* FALLTHROUGH */
2123 	case FDRAW_SENSE_INT:
2124 		ucount = 0;
2125 		break;
2126 
2127 	default:
2128 		return (EINVAL);
2129 	}
2130 
2131 	/*
2132 	 * Note that we ignore any error returns from controller
2133 	 * This is the way the driver has been, and it may be
2134 	 * that the raw ioctl senders simply don't want to
2135 	 * see any errors returned in this fashion.
2136 	 */
2137 
2138 	fjp->fj_ops->fco_select(fjp, unit, 1);
2139 	rval = fjp->fj_ops->fco_rwioctl(fjp, unit, (caddr_t)&fdr);
2140 
2141 	if (ucount && flag == B_READ && rval == 0) {
2142 		if (ddi_copyout(fdr.fdr_addr, uaddr, ucount, mode)) {
2143 			rval = EFAULT;
2144 		}
2145 	}
2146 	if (ddi_copyout(fdr.fdr_result, arg_result, sizeof (fdr.fdr_cmd), mode))
2147 		rval = EFAULT;
2148 
2149 	fjp->fj_ops->fco_select(fjp, unit, 0);
2150 	if (ucount)
2151 		kmem_free(fdr.fdr_addr, ucount);
2152 
2153 	return (rval);
2154 }
2155 
2156 /*
2157  * property operation routine.  return the number of blocks for the partition
2158  * in question or forward the request to the property facilities.
2159  */
2160 static int
2161 fd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
2162     char *name, caddr_t valuep, int *lengthp)
2163 {
2164 	struct fcu_obj	*fjp = NULL;
2165 	struct fdisk	*fdp = NULL;
2166 	uint64_t	nblocks64;
2167 
2168 	FDERRPRINT(FDEP_L1, FDEM_PROP,
2169 	    (CE_CONT, "fd_prop_op: dip %p %s\n", (void *)dip, name));
2170 
2171 	/*
2172 	 * Our dynamic properties are all device specific and size oriented.
2173 	 * Requests issued under conditions where size is valid are passed
2174 	 * to ddi_prop_op_nblocks with the size information, otherwise the
2175 	 * request is passed to ddi_prop_op.
2176 	 */
2177 	if (dev == DDI_DEV_T_ANY) {
2178 pass:		return (ddi_prop_op(dev, dip, prop_op, mod_flags,
2179 		    name, valuep, lengthp));
2180 	} else {
2181 		/*
2182 		 * Ignoring return value because success is checked by
2183 		 * verifying fjp and fdp and returned unit value is not used.
2184 		 */
2185 		(void) fd_getdrive(dev, &fjp, &fdp);
2186 		if (!fjp || !fdp)
2187 			goto pass;
2188 
2189 		/* get nblocks value */
2190 		nblocks64 = (ulong_t)fdp->d_part[PARTITION(dev)].p_size;
2191 
2192 		return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
2193 		    name, valuep, lengthp, nblocks64));
2194 	}
2195 }
2196 
2197 static void
2198 fd_media_watch(void *arg)
2199 {
2200 	struct fcu_obj *fjp;
2201 	struct fdisk *fdp;
2202 
2203 #ifdef DEBUG
2204 	int	unit;
2205 #define	DEBUG_ASSIGN	unit=
2206 #else
2207 #define	DEBUG_ASSIGN	(void)
2208 #endif
2209 	DEBUG_ASSIGN fd_getdrive((dev_t)arg, &fjp, &fdp);
2210 	/*
2211 	 * Ignoring return in non DEBUG mode because device exist.
2212 	 * Returned unit value is not used.
2213 	 */
2214 
2215 	FDERRPRINT(FDEP_L0, FDEM_IOCT,
2216 	    (CE_CONT, "fd_media_watch unit %d\n", unit));
2217 
2218 	/*
2219 	 * fd_get_media_state() cannot be called from this timeout function
2220 	 * because the  floppy drive has to be selected first, and that could
2221 	 * force this function to sleep (while waiting for the select
2222 	 * semaphore).
2223 	 * Instead, just wakeup up driver.
2224 	 */
2225 	mutex_enter(&fjp->fj_lock);
2226 	cv_broadcast(&fdp->d_statecv);
2227 	mutex_exit(&fjp->fj_lock);
2228 }
2229 
2230 enum dkio_state
2231 fd_get_media_state(struct fcu_obj *fjp, int unit)
2232 {
2233 	enum dkio_state state;
2234 
2235 	if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2236 		/* recheck disk only if DSKCHG "high" */
2237 		fjp->fj_ops->fco_resetchng(fjp, unit);
2238 		if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2239 			if (fjp->fj_flags & FUNIT_CHGDET) {
2240 				/*
2241 				 * again no diskette; not a new change
2242 				 */
2243 				state = DKIO_NONE;
2244 			} else {
2245 				/*
2246 				 * a new change; diskette was ejected
2247 				 */
2248 				fjp->fj_flags |= FUNIT_CHGDET;
2249 				state = DKIO_EJECTED;
2250 			}
2251 		} else {
2252 			fjp->fj_flags &= ~FUNIT_CHGDET;
2253 			state = DKIO_INSERTED;
2254 		}
2255 	} else {
2256 		fjp->fj_flags &= ~FUNIT_CHGDET;
2257 		state = DKIO_INSERTED;
2258 	}
2259 	FDERRPRINT(FDEP_L0, FDEM_IOCT,
2260 	    (CE_CONT, "fd_get_media_state unit %d: state %x\n", unit, state));
2261 	return (state);
2262 }
2263 
2264 static int
2265 fd_check_media(dev_t dev, enum dkio_state state)
2266 {
2267 	struct fcu_obj *fjp;
2268 	struct fdisk *fdp;
2269 	int	unit;
2270 	int	err;
2271 
2272 	unit = fd_getdrive(dev, &fjp, &fdp);
2273 
2274 	mutex_enter(&fjp->fj_lock);
2275 
2276 	fjp->fj_ops->fco_select(fjp, unit, 1);
2277 	fdp->d_media_state = fd_get_media_state(fjp, unit);
2278 	fdp->d_media_timeout = drv_usectohz(fd_check_media_time);
2279 
2280 	while (fdp->d_media_state == state) {
2281 		/* release the controller and drive */
2282 		fjp->fj_ops->fco_select(fjp, unit, 0);
2283 
2284 		/* turn on timer */
2285 		fdp->d_media_timeout_id = timeout(fd_media_watch,
2286 		    (void *)dev, fdp->d_media_timeout);
2287 
2288 		if (cv_wait_sig(&fdp->d_statecv, &fjp->fj_lock) == 0) {
2289 			fdp->d_media_timeout = 0;
2290 			mutex_exit(&fjp->fj_lock);
2291 			return (EINTR);
2292 		}
2293 		fjp->fj_ops->fco_select(fjp, unit, 1);
2294 		fdp->d_media_state = fd_get_media_state(fjp, unit);
2295 	}
2296 
2297 	if (fdp->d_media_state == DKIO_INSERTED) {
2298 		err = fdgetlabel(fjp, unit);
2299 		if (err) {
2300 			fjp->fj_ops->fco_select(fjp, unit, 0);
2301 			mutex_exit(&fjp->fj_lock);
2302 			return (EIO);
2303 		}
2304 	}
2305 	fjp->fj_ops->fco_select(fjp, unit, 0);
2306 	mutex_exit(&fjp->fj_lock);
2307 	return (0);
2308 }
2309 
2310 /*
2311  * fd_get_media_info :
2312  *	Collects medium information for
2313  *	DKIOCGMEDIAINFO ioctl.
2314  */
2315 
2316 static int
2317 fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag)
2318 {
2319 	struct dk_minfo media_info;
2320 	int err = 0;
2321 
2322 	media_info.dki_media_type = DK_FLOPPY;
2323 	media_info.dki_lbsize = fjp->fj_chars->fdc_sec_size;
2324 	media_info.dki_capacity = fjp->fj_chars->fdc_ncyl *
2325 	    fjp->fj_chars->fdc_secptrack * fjp->fj_chars->fdc_nhead;
2326 
2327 	if (ddi_copyout(&media_info, buf, sizeof (struct dk_minfo), flag))
2328 		err = EFAULT;
2329 	return (err);
2330 }
2331