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