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