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