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