9242 st: this statement may fall through
[unleashed.git] / usr / src / uts / common / io / fd.c
blobfadd776414384363ea0b63ac0d0b55c021622f4d
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.
28 * Floppy Disk driver
32 * Set CMOS feature:
33 * CMOS_CONF_MEM: CMOS memory contains configuration info
35 #define CMOS_CONF_MEM
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>
63 * Local Function Prototypes
65 static int fd_unit_is_open(struct fdisk *);
66 static int fdgetlabel(struct fcu_obj *, int);
67 static void fdstart(struct fcu_obj *);
68 static int fd_build_label_vtoc(struct fcu_obj *, struct fdisk *,
69 struct vtoc *, struct dk_label *);
70 static void fd_build_user_vtoc(struct fcu_obj *, struct fdisk *,
71 struct vtoc *);
72 static int fd_rawioctl(struct fcu_obj *, int, caddr_t, int);
73 static void fd_media_watch(void *);
75 static int fd_open(dev_t *, int, int, cred_t *);
76 static int fd_close(dev_t, int, int, cred_t *);
77 static int fd_strategy(struct buf *);
78 static int fd_read(dev_t, struct uio *, cred_t *);
79 static int fd_write(dev_t, struct uio *, cred_t *);
80 static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
81 static int fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
82 caddr_t, int *);
83 static int fd_check_media(dev_t dev, enum dkio_state state);
84 static int fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag);
86 static struct cb_ops fd_cb_ops = {
87 fd_open, /* open */
88 fd_close, /* close */
89 fd_strategy, /* strategy */
90 nodev, /* print */
91 nodev, /* dump */
92 fd_read, /* read */
93 fd_write, /* write */
94 fd_ioctl, /* ioctl */
95 nodev, /* devmap */
96 nodev, /* mmap */
97 nodev, /* segmap */
98 nochpoll, /* poll */
99 fd_prop_op, /* cb_prop_op */
100 0, /* streamtab */
101 D_NEW | D_MP /* Driver compatibility flag */
104 static int fd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
105 static int fd_probe(dev_info_t *);
106 static int fd_attach(dev_info_t *, ddi_attach_cmd_t);
107 static int fd_detach(dev_info_t *, ddi_detach_cmd_t);
109 static struct dev_ops fd_ops = {
110 DEVO_REV, /* devo_rev, */
111 0, /* refcnt */
112 fd_getinfo, /* getinfo */
113 nulldev, /* identify */
114 fd_probe, /* probe */
115 fd_attach, /* attach */
116 fd_detach, /* detach */
117 nodev, /* reset */
118 &fd_cb_ops, /* driver operations */
119 (struct bus_ops *)0, /* bus operations */
120 NULL, /* power */
121 ddi_quiesce_not_supported, /* devo_quiesce */
126 * static data
128 static void *fd_state_head; /* opaque handle top of state structs */
129 static int fd_check_media_time = 5000000; /* 5 second state check */
132 * error handling
134 * for debugging,
135 * set fderrlevel to 1
136 * set fderrmask to 224 or 644
138 #ifdef DEBUG
139 static uint_t fderrmask = FDEM_ALL;
140 #endif
141 static int fderrlevel = 5;
143 #define KIOSP KSTAT_IO_PTR(fdp->d_iostat)
145 static struct driver_minor_data {
146 char *name;
147 int minor;
148 int type;
149 } fd_minor [] = {
150 { "a", 0, S_IFBLK},
151 { "b", 1, S_IFBLK},
152 { "c", 2, S_IFBLK},
153 { "a,raw", 0, S_IFCHR},
154 { "b,raw", 1, S_IFCHR},
155 { "c,raw", 2, S_IFCHR},
159 static struct modldrv modldrv = {
160 &mod_driverops, /* Type of module. This one is a driver */
161 "Floppy Disk driver", /* Name of the module. */
162 &fd_ops, /* driver ops */
165 static struct modlinkage modlinkage = {
166 MODREV_1, (void *)&modldrv, NULL
171 _init(void)
173 int retval;
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);
180 if ((retval = mod_install(&modlinkage)) != 0)
181 ddi_soft_state_fini(&fd_state_head);
182 return (retval);
186 _fini(void)
188 int retval;
190 if ((retval = mod_remove(&modlinkage)) != 0)
191 return (retval);
192 ddi_soft_state_fini(&fd_state_head);
193 return (retval);
197 _info(struct modinfo *modinfop)
199 return (mod_info(&modlinkage, modinfop));
203 static int
204 fd_getdrive(dev_t dev, struct fcu_obj **fjpp, struct fdisk **fdpp)
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);
214 return (-1);
217 /*ARGSUSED*/
218 static int
219 fd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
221 dev_t dev = (dev_t)arg;
222 struct fcu_obj *fjp = NULL;
223 struct fdisk *fdp = NULL;
224 int rval;
226 switch (cmd) {
227 case DDI_INFO_DEVT2DEVINFO:
228 (void) fd_getdrive(dev, &fjp, &fdp);
230 * Ignoring return value because success is checked by
231 * verifying fjp and fdp and returned unit value is not used.
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;
246 return (rval);
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 */
255 static int
256 fd_probe(dev_info_t *dip)
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];
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
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);
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 */
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;
302 * Some enhanced floppy-disk controller adaptor cards
303 * require NO drives defined in the CMOS configuration
304 * memory.
305 * So fall through
307 #endif /* CMOS_CONF_MEM */
308 /* FALLTHROUGH */
309 default: /* need to check conf file */
310 len = sizeof (density);
311 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
312 DDI_PROP_DONTPASS, "density", (caddr_t)&density, &len) !=
313 DDI_PROP_SUCCESS) {
314 FDERRPRINT(FDEP_L3, FDEM_ATTA,
315 (CE_WARN,
316 "fd_probe failed density: dip %p unit %d",
317 (void *)dip, unit_num));
318 return (DDI_PROBE_FAILURE);
320 len = sizeof (drive_size);
321 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
322 DDI_PROP_DONTPASS, "size", (caddr_t)&drive_size, &len) !=
323 DDI_PROP_SUCCESS) {
324 FDERRPRINT(FDEP_L3, FDEM_ATTA,
325 (CE_WARN, "fd_probe failed size: dip %p unit %d",
326 (void *)dip, unit_num));
327 return (DDI_PROBE_FAILURE);
330 FDERRPRINT(FDEP_L3, FDEM_ATTA,
331 (CE_WARN, "fd_probe dip %p unit %d", (void *)dip, unit_num));
332 return (DDI_PROBE_SUCCESS);
336 /* ARGSUSED */
337 static int
338 fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
340 struct fcu_obj *fjp;
341 struct fdisk *fdp;
342 struct driver_minor_data *dmdp;
343 int mode_3D;
344 int drive_num, drive_size, drive_type;
345 #ifdef CMOS_CONF_MEM
346 int cmos;
347 #endif /* CMOS_CONF_MEM */
348 int len, sig_minor;
349 int unit_num;
350 char density[8];
351 char name[MAXNAMELEN];
353 switch (cmd) {
354 case DDI_ATTACH:
355 len = sizeof (unit_num);
356 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
357 DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
358 DDI_PROP_SUCCESS) {
359 FDERRPRINT(FDEP_L3, FDEM_ATTA,
360 (CE_WARN, "fd_attach failed: dip %p", (void *)dip));
361 return (DDI_FAILURE);
364 #ifdef CMOS_CONF_MEM
365 outb(CMOS_ADDR, CMOS_FDRV);
366 cmos = drive_type = (int)inb(CMOS_DATA);
367 #endif /* CMOS_CONF_MEM */
369 switch (unit_num) {
370 #ifdef CMOS_CONF_MEM
371 case 0:
372 drive_type = drive_type >> 4;
373 /* FALLTHROUGH */
374 case 1:
375 drive_type = drive_type & 0x0F;
376 if (cmos)
377 break;
379 * Some enhanced floppy-disk controller adaptor cards
380 * require NO drives defined in the CMOS configuration
381 * memory.
382 * So fall through
384 #endif /* CMOS_CONF_MEM */
385 /* FALLTHROUGH */
386 default: /* need to check .conf file */
387 drive_type = 0;
388 len = sizeof (density);
389 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
390 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "density",
391 (caddr_t)&density, &len) != DDI_PROP_SUCCESS)
392 density[0] = '\0';
393 len = sizeof (drive_size);
394 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
395 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "size",
396 (caddr_t)&drive_size, &len) != DDI_PROP_SUCCESS)
397 drive_size = 0;
398 if (strcmp(density, "DSDD") == 0) {
399 if (drive_size == 5)
400 drive_type = 1;
401 else if (drive_size == 3)
402 drive_type = 3;
403 } else if (strcmp(density, "DSHD") == 0) {
404 if (drive_size == 5)
405 drive_type = 2;
406 else if (drive_size == 3)
407 drive_type = 4;
408 } else if (strcmp(density, "DSED") == 0 &&
409 drive_size == 3) {
410 drive_type = 6;
412 break;
414 if (drive_type == 0) {
415 FDERRPRINT(FDEP_L3, FDEM_ATTA,
416 (CE_WARN, "fd_attach failed type: dip %p unit %d",
417 (void *)dip, unit_num));
418 return (DDI_FAILURE);
421 drive_num = ddi_get_instance(dip);
422 if (ddi_soft_state_zalloc(fd_state_head, drive_num) != 0)
423 return (DDI_FAILURE);
424 fdp = ddi_get_soft_state(fd_state_head, drive_num);
425 fjp = fdp->d_obj = ddi_get_driver_private(dip);
427 mutex_init(&fjp->fj_lock, NULL, MUTEX_DRIVER, *fjp->fj_iblock);
428 sema_init(&fdp->d_ocsem, 1, NULL, SEMA_DRIVER, NULL);
430 fjp->fj_drive = (struct fd_drive *)(fdp + 1);
431 fjp->fj_chars = (struct fd_char *)(fjp->fj_drive + 1);
432 fjp->fj_attr = (struct fdattr *)(fjp->fj_chars + 1);
435 * set default floppy drive characteristics & geometry
437 switch (drive_type) { /* assume doubled sided */
438 case 2: /* 5.25 high density */
439 *fjp->fj_drive = dfd_525HD;
440 fdp->d_media = 1<<FMT_5H | 1<<FMT_5D9 | 1<<FMT_5D8 |
441 1<<FMT_5D4 | 1<<FMT_5D16;
442 fdp->d_deffdtype = fdp->d_curfdtype = FMT_5H;
443 break;
444 case 4: /* 3.5 high density */
445 *fjp->fj_drive = dfd_350HD;
446 fdp->d_media = 1<<FMT_3H | 1<<FMT_3I | 1<<FMT_3D;
447 len = sizeof (mode_3D);
448 if (ddi_prop_op(DDI_DEV_T_ANY, dip,
449 PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "mode_3D",
450 (caddr_t)&mode_3D, &len) != DDI_PROP_SUCCESS)
451 mode_3D = 0;
452 if (mode_3D && (fjp->fj_fdc->c_flags & FCFLG_3DMODE))
454 * 3D mode should be enabled only if a dual-
455 * speed 3.5" high-density drive and a
456 * supported floppy controller are installed.
458 fdp->d_media |= 1 << FMT_3M;
459 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3H;
460 break;
461 case 1: /* 5.25 double density */
462 *fjp->fj_drive = dfd_525DD;
463 fdp->d_media = 1<<FMT_5D9 | 1<<FMT_5D8 | 1<<FMT_5D4 |
464 1<<FMT_5D16;
465 fdp->d_deffdtype = fdp->d_curfdtype = FMT_5D9;
466 break;
467 case 3: /* 3.5 double density */
468 *fjp->fj_drive = dfd_350HD;
469 fdp->d_media = 1<<FMT_3D;
470 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3D;
471 break;
472 case 5: /* 3.5 extended density */
473 case 6:
474 case 7:
475 *fjp->fj_drive = dfd_350ED;
476 fdp->d_media = 1<<FMT_3E | 1<<FMT_3H | 1<<FMT_3I |
477 1<<FMT_3D;
478 fdp->d_deffdtype = fdp->d_curfdtype = FMT_3E;
479 break;
480 case 0: /* no drive defined */
481 default:
482 goto no_attach;
484 *fjp->fj_chars = *defchar[fdp->d_deffdtype];
485 *fjp->fj_attr = fdtypes[fdp->d_deffdtype];
486 bcopy(fdparts[fdp->d_deffdtype], fdp->d_part,
487 sizeof (struct partition) * NDKMAP);
488 fjp->fj_rotspd = fdtypes[fdp->d_deffdtype].fda_rotatespd;
490 sig_minor = drive_num << 3;
491 for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) {
492 if (ddi_create_minor_node(dip, dmdp->name, dmdp->type,
493 sig_minor | dmdp->minor, DDI_NT_FD, NULL)
494 == DDI_FAILURE) {
495 ddi_remove_minor_node(dip, NULL);
496 goto no_attach;
500 FDERRPRINT(FDEP_L3, FDEM_ATTA,
501 (CE_WARN, "fd_attach: dip %p unit %d",
502 (void *)dip, unit_num));
503 (void) sprintf(name, "fd%d", drive_num);
504 fdp->d_iostat = kstat_create("fd", drive_num, name, "disk",
505 KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
506 if (fdp->d_iostat) {
507 fdp->d_iostat->ks_lock = &fjp->fj_lock;
508 kstat_install(fdp->d_iostat);
511 fjp->fj_data = (caddr_t)fdp;
512 fjp->fj_flags |= FUNIT_DRVATCH;
515 * Add a zero-length attribute to tell the world we support
516 * kernel ioctls (for layered drivers)
518 (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
519 DDI_KERNEL_IOCTL, NULL, 0);
522 * We want to get suspend/resume events, so that we can
523 * refuse to suspend when pcfs is mounted.
525 (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
526 "pm-hardware-state", "needs-suspend-resume");
529 * Ignoring return value because, for passed arguments, only
530 * DDI_SUCCESS is returned.
532 ddi_report_dev(dip);
533 return (DDI_SUCCESS);
535 case DDI_RESUME:
536 /* nothing for us to do */
537 return (DDI_SUCCESS);
539 default:
540 return (DDI_FAILURE);
542 no_attach:
543 fjp->fj_drive = NULL;
544 fjp->fj_chars = NULL;
545 fjp->fj_attr = NULL;
546 mutex_destroy(&fjp->fj_lock);
547 sema_destroy(&fdp->d_ocsem);
548 ddi_soft_state_free(fd_state_head, drive_num);
549 FDERRPRINT(FDEP_L3, FDEM_ATTA,
550 (CE_WARN, "fd_attach failed: dip %p unit %d",
551 (void *)dip, unit_num));
552 return (DDI_FAILURE);
556 /* ARGSUSED */
557 static int
558 fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
560 struct fcu_obj *fjp;
561 struct fdisk *fdp;
562 int drive_num;
563 int rval = DDI_SUCCESS;
565 FDERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fd_detach dip %p",
566 (void *)dip));
568 drive_num = ddi_get_instance(dip);
569 if (!(fdp = ddi_get_soft_state(fd_state_head, drive_num)))
570 return (rval);
572 switch (cmd) {
573 case DDI_DETACH:
574 if (fd_unit_is_open(fdp)) {
575 rval = DDI_FAILURE;
576 break;
578 kstat_delete(fdp->d_iostat);
579 fdp->d_iostat = NULL;
580 fjp = (struct fcu_obj *)fdp->d_obj;
581 fjp->fj_flags &= ~FUNIT_DRVATCH;
582 fjp->fj_data = NULL;
583 fjp->fj_drive = NULL;
584 fjp->fj_chars = NULL;
585 fjp->fj_attr = NULL;
586 ddi_prop_remove_all(dip);
587 mutex_destroy(&fjp->fj_lock);
588 sema_destroy(&fdp->d_ocsem);
589 ddi_soft_state_free(fd_state_head, drive_num);
590 break;
592 case DDI_SUSPEND:
594 * Bad, bad, bad things will happen if someone
595 * *changes* the disk in the drive while it is mounted
596 * and the system is suspended. We have no way to
597 * detect that. (Undetected filesystem corruption.
598 * Its akin to changing the boot disk while the system
599 * is suspended. Don't do it!)
601 * So we refuse to suspend if there is a mounted filesystem.
602 * (We guess this by looking for a block open. Character
603 * opens are fine.) This limits some of the usability of
604 * suspend/resume, but it certainly avoids this
605 * potential filesystem corruption from pilot error.
606 * Given the decreasing popularity of floppy media, we
607 * don't see this as much of a limitation.
609 if (fdp->d_regopen[OTYP_BLK]) {
610 cmn_err(CE_NOTE,
611 "Unable to suspend while floppy is in use.");
612 rval = DDI_FAILURE;
614 break;
616 default:
617 rval = DDI_FAILURE;
618 break;
620 return (rval);
624 static int
625 fd_part_is_open(struct fdisk *fdp, int part)
627 int i;
629 for (i = 0; i < (OTYPCNT - 1); i++)
630 if (fdp->d_regopen[i] & (1 << part))
631 return (1);
632 return (0);
635 static int
636 fd_unit_is_open(struct fdisk *fdp)
638 int i;
640 for (i = 0; i < NDKMAP; i++)
641 if (fdp->d_lyropen[i])
642 return (1);
643 for (i = 0; i < (OTYPCNT - 1); i++)
644 if (fdp->d_regopen[i])
645 return (1);
646 return (0);
649 /*ARGSUSED*/
650 static int
651 fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
653 struct fcu_obj *fjp = NULL;
654 struct fdisk *fdp = NULL;
655 struct partition *pp;
656 dev_t dev;
657 int part, unit;
658 int part_is_open;
659 int rval;
660 uint_t pbit;
662 dev = *devp;
663 unit = fd_getdrive(dev, &fjp, &fdp);
664 if (!fjp || !fdp)
665 return (ENXIO);
666 part = PARTITION(dev);
667 pbit = 1 << part;
668 pp = &fdp->d_part[part];
671 * Serialize opens/closes
673 sema_p(&fdp->d_ocsem);
674 FDERRPRINT(FDEP_L1, FDEM_OPEN,
675 (CE_CONT, "fd_open: fd%d part %d flag %x otype %x\n", DRIVE(dev),
676 part, flag, otyp));
679 * Check for previous exclusive open, or trying to exclusive open
680 * An "exclusive open" on any partition is not guaranteed to
681 * protect against opens on another partition that overlaps it.
683 if (otyp == OTYP_LYR) {
684 part_is_open = (fdp->d_lyropen[part] != 0);
685 } else {
686 part_is_open = fd_part_is_open(fdp, part);
688 if ((fdp->d_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
689 FDERRPRINT(FDEP_L0, FDEM_OPEN, (CE_CONT,
690 "fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n",
691 fdp->d_exclmask, fdp->d_regopen[otyp], fdp->d_lyropen[part],
692 pbit));
693 sema_v(&fdp->d_ocsem);
694 return (EBUSY);
698 * Ensure that drive is recalibrated on first open of new diskette.
700 fjp->fj_ops->fco_select(fjp, unit, 1);
701 if (fjp->fj_ops->fco_getchng(fjp, unit) != 0) {
702 if (fjp->fj_ops->fco_rcseek(fjp, unit, -1, 0)) {
703 FDERRPRINT(FDEP_L2, FDEM_OPEN,
704 (CE_NOTE, "fd_open fd%d: not ready", DRIVE(dev)));
705 fjp->fj_ops->fco_select(fjp, unit, 0);
706 sema_v(&fdp->d_ocsem);
707 return (ENXIO);
709 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
711 if (flag & (FNDELAY | FNONBLOCK)) {
712 /* don't attempt access, just return successfully */
713 fjp->fj_ops->fco_select(fjp, unit, 0);
714 goto out;
718 * auto-sense the density/format of the diskette
720 rval = fdgetlabel(fjp, unit);
721 fjp->fj_ops->fco_select(fjp, unit, 0);
722 if (rval) {
723 /* didn't find label (couldn't read anything) */
724 FDERRPRINT(FDEP_L2, FDEM_OPEN,
725 (CE_NOTE, "fd%d: drive not ready", DRIVE(dev)));
726 sema_v(&fdp->d_ocsem);
727 return (EIO);
729 /* check partition */
730 if (pp->p_size == 0) {
731 sema_v(&fdp->d_ocsem);
732 return (ENXIO);
735 * if opening for writing, check write protect on diskette
737 if ((flag & FWRITE) && (fdp->d_obj->fj_flags & FUNIT_WPROT)) {
738 sema_v(&fdp->d_ocsem);
739 return (EROFS);
742 out:
744 * mark open as having succeeded
746 if (flag & FEXCL)
747 fdp->d_exclmask |= pbit;
748 if (otyp == OTYP_LYR)
749 fdp->d_lyropen[part]++;
750 else
751 fdp->d_regopen[otyp] |= 1 << part;
753 sema_v(&fdp->d_ocsem);
754 return (0);
758 * fdgetlabel - read the SunOS label off the diskette
759 * if it can read a valid label it does so, else it will use a
760 * default. If it can`t read the diskette - that is an error.
762 * RETURNS: 0 for ok - meaning that it could at least read the device,
763 * !0 for error XXX TBD NYD error codes
765 static int
766 fdgetlabel(struct fcu_obj *fjp, int unit)
768 struct dk_label *label;
769 struct fdisk *fdp;
770 char *newlabel;
771 short *sp;
772 short count;
773 short xsum;
774 int tries, try_this;
775 uint_t nexttype;
776 int rval;
777 short oldlvl;
778 int i;
780 FDERRPRINT(FDEP_L0, FDEM_GETL,
781 (CE_CONT, "fdgetlabel fd unit %d\n", unit));
782 fdp = (struct fdisk *)fjp->fj_data;
783 fjp->fj_flags &= ~(FUNIT_UNLABELED);
786 * get some space to play with the label
788 label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
789 FDERRPRINT(FDEP_L0, FDEM_GETL, (CE_CONT,
790 "fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n",
791 unit, (void *)label, (size_t)sizeof (struct dk_label)));
794 * read block 0 (0/0/1) to find the label
795 * (disk is potentially not present or unformatted)
797 /* noerrprint since this is a private cmd */
798 oldlvl = fderrlevel;
799 fderrlevel = FDEP_LMAX;
801 * try different characteristics (ie densities)
803 * if fdp->d_curfdtype is -1 then the current characteristics
804 * were set by ioctl and need to try it as well as everything
805 * in the table
807 nexttype = fdp->d_deffdtype;
808 try_this = 1; /* always try the current characteristics */
810 for (tries = nfdtypes; tries; tries--) {
811 if (try_this) {
812 fjp->fj_flags &= ~FUNIT_CHAROK;
814 /* try reading last sector of cyl 1, head 0 */
815 if (!(rval = fjp->fj_ops->fco_rw(fjp, unit,
816 FDREAD, 1, 0, fjp->fj_chars->fdc_secptrack,
817 (caddr_t)label,
818 sizeof (struct dk_label))) &&
819 /* and last sector plus 1 of cylinder 1 */
820 fjp->fj_ops->fco_rw(fjp, unit, FDREAD, 1,
821 0, fjp->fj_chars->fdc_secptrack + 1,
822 (caddr_t)label,
823 sizeof (struct dk_label)) &&
824 /* and label sector on cylinder 0 */
825 !(rval = fjp->fj_ops->fco_rw(fjp, unit,
826 FDREAD, 0, 0, 1, (caddr_t)label,
827 sizeof (struct dk_label))))
828 break;
829 if (rval == ENXIO)
830 break;
833 * try the next entry in the characteristics tbl
835 fdp->d_curfdtype = (signed char)nexttype;
836 nexttype = (nexttype + 1) % nfdtypes;
837 if ((1 << fdp->d_curfdtype) & fdp->d_media) {
838 *fjp->fj_chars = *defchar[fdp->d_curfdtype];
839 *fjp->fj_attr = fdtypes[fdp->d_curfdtype];
840 bcopy(fdparts[fdp->d_curfdtype], fdp->d_part,
841 sizeof (struct partition) * NDKMAP);
843 * check for a double_density diskette
844 * in a high_density 5.25" drive
846 if (fjp->fj_chars->fdc_transfer_rate == 250 &&
847 fjp->fj_rotspd > fjp->fj_attr->fda_rotatespd) {
849 * yes - adjust transfer rate since we don't
850 * know if we have a 5.25" dual-speed drive
852 fjp->fj_attr->fda_rotatespd = 360;
853 fjp->fj_chars->fdc_transfer_rate = 300;
854 fjp->fj_chars->fdc_medium = 5;
856 if ((2 * fjp->fj_chars->fdc_ncyl) ==
857 defchar[fdp->d_deffdtype]->fdc_ncyl) {
858 /* yes - adjust steps per cylinder */
859 fjp->fj_chars->fdc_steps = 2;
860 } else
861 fjp->fj_chars->fdc_steps = 1;
862 try_this = 1;
863 } else
864 try_this = 0;
866 fderrlevel = oldlvl; /* print errors again */
868 if (rval) {
869 fdp->d_curfdtype = fdp->d_deffdtype;
870 goto out; /* couldn't read anything */
873 FDERRPRINT(FDEP_L0, FDEM_GETL,
874 (CE_CONT,
875 "fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n",
876 unit, fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_secptrack,
877 fjp->fj_chars->fdc_steps, fjp->fj_attr->fda_rotatespd,
878 fjp->fj_attr->fda_intrlv));
881 * _something_ was read - look for unixtype label
883 if (label->dkl_magic != DKL_MAGIC ||
884 label->dkl_vtoc.v_sanity != VTOC_SANE) {
885 /* not a label - no magic number */
886 goto nolabel; /* no errors, but no label */
889 count = sizeof (struct dk_label) / sizeof (short);
890 sp = (short *)label;
891 xsum = 0;
892 while (count--)
893 xsum ^= *sp++; /* should add up to 0 */
894 if (xsum) {
895 /* not a label - checksum didn't compute */
896 goto nolabel; /* no errors, but no label */
900 * the SunOS label overrides current diskette characteristics
902 fjp->fj_chars->fdc_ncyl = label->dkl_pcyl;
903 fjp->fj_chars->fdc_nhead = label->dkl_nhead;
904 fjp->fj_chars->fdc_secptrack = (label->dkl_nsect * DEV_BSIZE) /
905 fjp->fj_chars->fdc_sec_size;
906 if (defchar[fdp->d_deffdtype]->fdc_ncyl == 2 * fjp->fj_chars->fdc_ncyl)
907 fjp->fj_chars->fdc_steps = 2;
908 else
909 fjp->fj_chars->fdc_steps = 1;
911 fjp->fj_attr->fda_rotatespd = label->dkl_rpm;
912 fjp->fj_attr->fda_intrlv = label->dkl_intrlv;
914 fdp->d_vtoc_version = label->dkl_vtoc.v_version;
915 bcopy(label->dkl_vtoc.v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
916 bcopy(label->dkl_vtoc.v_asciilabel,
917 fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
919 * logical partitions
921 for (i = 0; i < NDKMAP; i++) {
922 fdp->d_part[i].p_tag = label->dkl_vtoc.v_part[i].p_tag;
923 fdp->d_part[i].p_flag = label->dkl_vtoc.v_part[i].p_flag;
924 fdp->d_part[i].p_start = label->dkl_vtoc.v_part[i].p_start;
925 fdp->d_part[i].p_size = label->dkl_vtoc.v_part[i].p_size;
927 fdp->d_vtoc_timestamp[i] = label->dkl_vtoc.timestamp[i];
930 fjp->fj_flags |= FUNIT_LABELOK;
931 goto out;
933 nolabel:
935 * if not found, fill in label info from default (mark default used)
937 if (fdp->d_media & (1<<FMT_3D))
938 newlabel = deflabel_35;
939 else /* if (fdp->d_media & (1<<FMT_5D9)) */
940 newlabel = deflabel_525;
941 bzero(fdp->d_vtoc_volume, LEN_DKL_VVOL);
942 (void) sprintf(fdp->d_vtoc_asciilabel, newlabel,
943 fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_nhead,
944 fjp->fj_chars->fdc_secptrack);
945 fjp->fj_flags |= FUNIT_UNLABELED;
947 out:
948 kmem_free(label, sizeof (struct dk_label));
949 return (rval);
953 /*ARGSUSED*/
954 static int
955 fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
957 struct fcu_obj *fjp = NULL;
958 struct fdisk *fdp = NULL;
959 int part, part_is_closed;
961 #ifdef DEBUG
962 int unit;
963 #define DEBUG_ASSIGN unit=
964 #else
965 #define DEBUG_ASSIGN (void)
966 #endif
968 DEBUG_ASSIGN fd_getdrive(dev, &fjp, &fdp);
970 * Ignoring return in non DEBUG mode because success is checked by
971 * verifying fjp and fdp and returned unit value is not used.
973 if (!fjp || !fdp)
974 return (ENXIO);
975 part = PARTITION(dev);
977 sema_p(&fdp->d_ocsem);
978 FDERRPRINT(FDEP_L1, FDEM_CLOS,
979 (CE_CONT, "fd_close: fd unit %d part %d otype %x\n",
980 unit, part, otyp));
982 if (otyp == OTYP_LYR) {
983 if (fdp->d_lyropen[part])
984 fdp->d_lyropen[part]--;
985 part_is_closed = (fdp->d_lyropen[part] == 0);
986 } else {
987 fdp->d_regopen[otyp] &= ~(1<<part);
988 part_is_closed = 1;
990 if (part_is_closed) {
991 if (part == 2 && fdp->d_exclmask&(1<<part))
992 fdp->d_exclmask = 0;
993 else
994 fdp->d_exclmask &= ~(1<<part);
995 FDERRPRINT(FDEP_L0, FDEM_CLOS,
996 (CE_CONT,
997 "fd_close: exclparts %lx openparts %lx lyrcnt %lx\n",
998 fdp->d_exclmask, fdp->d_regopen[otyp],
999 fdp->d_lyropen[part]));
1001 if (fd_unit_is_open(fdp) == 0)
1002 fdp->d_obj->fj_flags &= ~FUNIT_CHANGED;
1004 sema_v(&fdp->d_ocsem);
1005 return (0);
1008 /* ARGSUSED */
1009 static int
1010 fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
1012 return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
1015 /* ARGSUSED */
1016 static int
1017 fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
1019 return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
1023 * fd_strategy
1024 * checks operation, hangs buf struct off fdcntlr, calls fdstart
1025 * if not already busy. Note that if we call start, then the operation
1026 * will already be done on return (start sleeps).
1028 static int
1029 fd_strategy(struct buf *bp)
1031 struct fcu_obj *fjp;
1032 struct fdisk *fdp;
1033 struct partition *pp;
1035 FDERRPRINT(FDEP_L1, FDEM_STRA,
1036 (CE_CONT, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
1037 (void *)bp, bp->b_edev));
1039 (void) fd_getdrive(bp->b_edev, &fjp, &fdp);
1042 * Ignoring return because device exist.
1043 * Returned unit value is not used.
1045 pp = &fdp->d_part[PARTITION(bp->b_edev)];
1047 if (fjp->fj_chars->fdc_sec_size > NBPSCTR && (bp->b_blkno & 1)) {
1048 FDERRPRINT(FDEP_L3, FDEM_STRA,
1049 (CE_WARN, "fd%d: block %ld is not start of sector!",
1050 DRIVE(bp->b_edev), (long)bp->b_blkno));
1051 bp->b_error = EINVAL;
1052 goto bad;
1055 if ((bp->b_blkno > pp->p_size)) {
1056 FDERRPRINT(FDEP_L3, FDEM_STRA,
1057 (CE_WARN, "fd%d: block %ld is past the end! (nblk=%ld)",
1058 DRIVE(bp->b_edev), (long)bp->b_blkno, pp->p_size));
1059 bp->b_error = ENOSPC;
1060 goto bad;
1063 /* if at end of file, skip out now */
1064 if (bp->b_blkno == pp->p_size) {
1065 if ((bp->b_flags & B_READ) == 0) {
1066 /* a write needs to get an error! */
1067 bp->b_error = ENOSPC;
1068 goto bad;
1070 bp->b_resid = bp->b_bcount;
1071 biodone(bp);
1072 return (0);
1075 /* if operation not a multiple of sector size, is error! */
1076 if (bp->b_bcount % fjp->fj_chars->fdc_sec_size) {
1077 FDERRPRINT(FDEP_L3, FDEM_STRA,
1078 (CE_WARN, "fd%d: count %ld must be a multiple of %d",
1079 DRIVE(bp->b_edev), bp->b_bcount,
1080 fjp->fj_chars->fdc_sec_size));
1081 bp->b_error = EINVAL;
1082 goto bad;
1086 * Put the buf request in the drive's queue, FIFO.
1088 bp->av_forw = 0;
1089 mutex_enter(&fjp->fj_lock);
1090 if (fdp->d_iostat)
1091 kstat_waitq_enter(KIOSP);
1092 if (fdp->d_actf)
1093 fdp->d_actl->av_forw = bp;
1094 else
1095 fdp->d_actf = bp;
1096 fdp->d_actl = bp;
1097 if (!(fjp->fj_flags & FUNIT_BUSY)) {
1098 fdstart(fjp);
1100 mutex_exit(&fjp->fj_lock);
1101 return (0);
1103 bad:
1104 bp->b_resid = bp->b_bcount;
1105 bp->b_flags |= B_ERROR;
1106 biodone(bp);
1107 return (0);
1111 * fdstart
1112 * called from fd_strategy() or from fdXXXX() to setup and
1113 * start operations of read or write only (using buf structs).
1114 * Because the chip doesn't handle crossing cylinder boundaries on
1115 * the fly, this takes care of those boundary conditions. Note that
1116 * it sleeps until the operation is done *within fdstart* - so that
1117 * when fdstart returns, the operation is already done.
1119 static void
1120 fdstart(struct fcu_obj *fjp)
1122 struct buf *bp;
1123 struct fdisk *fdp = (struct fdisk *)fjp->fj_data;
1124 struct fd_char *chp;
1125 struct partition *pp;
1126 uint_t ptend;
1127 uint_t bincyl; /* (the number of the desired) block in cyl. */
1128 uint_t blk, len, tlen;
1129 uint_t secpcyl; /* number of sectors per cylinder */
1130 int cyl, head, sect;
1131 int sctrshft, unit;
1132 caddr_t addr;
1134 ASSERT(MUTEX_HELD(&fjp->fj_lock));
1135 fjp->fj_flags |= FUNIT_BUSY;
1137 while ((bp = fdp->d_actf) != NULL) {
1138 fdp->d_actf = bp->av_forw;
1139 fdp->d_current = bp;
1140 if (fdp->d_iostat) {
1141 kstat_waitq_to_runq(KIOSP);
1143 mutex_exit(&fjp->fj_lock);
1145 FDERRPRINT(FDEP_L0, FDEM_STRT,
1146 (CE_CONT, "fdstart: bp=0x%p blkno=0x%lx bcount=0x%lx\n",
1147 (void *)bp, (long)bp->b_blkno, bp->b_bcount));
1148 bp->b_flags &= ~B_ERROR;
1149 bp->b_error = 0;
1150 bp->b_resid = bp->b_bcount; /* init resid */
1152 ASSERT(DRIVE(bp->b_edev) == ddi_get_instance(fjp->fj_dip));
1153 unit = fjp->fj_unit;
1154 fjp->fj_ops->fco_select(fjp, unit, 1);
1156 bp_mapin(bp); /* map in buffers */
1158 pp = &fdp->d_part[PARTITION(bp->b_edev)];
1159 /* starting blk adjusted for the partition */
1160 blk = bp->b_blkno + pp->p_start;
1161 ptend = pp->p_start + pp->p_size; /* end of the partition */
1163 chp = fjp->fj_chars;
1164 secpcyl = chp->fdc_nhead * chp->fdc_secptrack;
1165 switch (chp->fdc_sec_size) {
1166 /* convert logical block numbers to sector numbers */
1167 case 1024:
1168 sctrshft = SCTRSHFT + 1;
1169 blk >>= 1;
1170 ptend >>= 1;
1171 break;
1172 default:
1173 case NBPSCTR:
1174 sctrshft = SCTRSHFT;
1175 break;
1176 case 256:
1177 sctrshft = SCTRSHFT - 1;
1178 blk <<= 1;
1179 ptend <<= 1;
1180 break;
1184 * If off the end, limit to actual amount that
1185 * can be transferred.
1187 if ((blk + (bp->b_bcount >> sctrshft)) > ptend)
1188 /* to end of partition */
1189 len = (ptend - blk) << sctrshft;
1190 else
1191 len = bp->b_bcount;
1192 addr = bp->b_un.b_addr; /* data buffer address */
1195 * now we have the real start blk, addr and len for xfer op
1197 while (len != 0) {
1198 /* start cyl of req */
1199 cyl = blk / secpcyl;
1200 bincyl = blk % secpcyl;
1201 /* start head of req */
1202 head = bincyl / chp->fdc_secptrack;
1203 /* start sector of req */
1204 sect = (bincyl % chp->fdc_secptrack) + 1;
1206 * If the desired block and length will go beyond the
1207 * cylinder end, then limit it to the cylinder end.
1209 if (bp->b_flags & B_READ) {
1210 if (len > ((secpcyl - bincyl) << sctrshft))
1211 tlen = (secpcyl - bincyl) << sctrshft;
1212 else
1213 tlen = len;
1214 } else {
1215 if (len >
1216 ((chp->fdc_secptrack - sect + 1) <<
1217 sctrshft))
1218 tlen =
1219 (chp->fdc_secptrack - sect + 1) <<
1220 sctrshft;
1221 else
1222 tlen = len;
1225 FDERRPRINT(FDEP_L0, FDEM_STRT, (CE_CONT,
1226 " blk 0x%x addr 0x%p len 0x%x "
1227 "cyl %d head %d sec %d\n resid 0x%lx, tlen %d\n",
1228 blk, (void *)addr, len, cyl, head, sect,
1229 bp->b_resid, tlen));
1232 * (try to) do the operation - failure returns an errno
1234 bp->b_error = fjp->fj_ops->fco_rw(fjp, unit,
1235 bp->b_flags & B_READ, cyl, head, sect, addr, tlen);
1236 if (bp->b_error != 0) {
1237 FDERRPRINT(FDEP_L3, FDEM_STRT, (CE_WARN,
1238 "fdstart: bad exec of bp: 0x%p, err=%d",
1239 (void *)bp, bp->b_error));
1240 bp->b_flags |= B_ERROR;
1241 break;
1243 blk += tlen >> sctrshft;
1244 len -= tlen;
1245 addr += tlen;
1246 bp->b_resid -= tlen;
1248 FDERRPRINT(FDEP_L0, FDEM_STRT,
1249 (CE_CONT, "fdstart done: b_resid %lu, b_count %lu\n",
1250 bp->b_resid, bp->b_bcount));
1251 if (fdp->d_iostat) {
1252 if (bp->b_flags & B_READ) {
1253 KIOSP->reads++;
1254 KIOSP->nread += (bp->b_bcount - bp->b_resid);
1255 } else {
1256 KIOSP->writes++;
1257 KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
1259 kstat_runq_exit(KIOSP);
1261 bp_mapout(bp);
1262 biodone(bp);
1264 fjp->fj_ops->fco_select(fjp, unit, 0);
1265 mutex_enter(&fjp->fj_lock);
1266 fdp->d_current = 0;
1268 fjp->fj_flags ^= FUNIT_BUSY;
1271 /* ARGSUSED */
1272 static int
1273 fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
1274 int *rval_p)
1276 union {
1277 struct dk_cinfo dki;
1278 struct dk_geom dkg;
1279 struct dk_allmap dka;
1280 struct fd_char fdchar;
1281 struct fd_drive drvchar;
1282 int temp;
1283 } cpy;
1284 struct vtoc vtoc;
1285 struct fcu_obj *fjp = NULL;
1286 struct fdisk *fdp = NULL;
1287 struct dk_map *dmp;
1288 struct dk_label *label;
1289 int nblks, part, unit;
1290 int rval = 0;
1291 enum dkio_state state;
1293 unit = fd_getdrive(dev, &fjp, &fdp);
1294 if (!fjp || !fdp)
1295 return (ENXIO);
1297 FDERRPRINT(FDEP_L1, FDEM_IOCT,
1298 (CE_CONT, "fd_ioctl fd unit %d: cmd %x, arg %lx\n",
1299 unit, cmd, arg));
1301 switch (cmd) {
1302 case DKIOCINFO:
1303 fjp->fj_ops->fco_dkinfo(fjp, &cpy.dki);
1304 cpy.dki.dki_cnum = FDCTLR(fjp->fj_unit);
1305 cpy.dki.dki_unit = FDUNIT(fjp->fj_unit);
1306 cpy.dki.dki_partition = PARTITION(dev);
1307 if (ddi_copyout(&cpy.dki, (void *)arg, sizeof (cpy.dki), flag))
1308 rval = EFAULT;
1309 break;
1311 case DKIOCG_PHYGEOM:
1312 case DKIOCG_VIRTGEOM:
1313 cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1314 goto get_geom;
1315 case DKIOCGGEOM:
1316 if (fjp->fj_flags & FUNIT_LABELOK)
1317 cpy.dkg.dkg_nsect = (fjp->fj_chars->fdc_secptrack *
1318 fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1319 else
1320 cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
1321 get_geom:
1322 cpy.dkg.dkg_pcyl = fjp->fj_chars->fdc_ncyl;
1323 cpy.dkg.dkg_ncyl = fjp->fj_chars->fdc_ncyl;
1324 cpy.dkg.dkg_nhead = fjp->fj_chars->fdc_nhead;
1325 cpy.dkg.dkg_intrlv = fjp->fj_attr->fda_intrlv;
1326 cpy.dkg.dkg_rpm = fjp->fj_attr->fda_rotatespd;
1327 cpy.dkg.dkg_read_reinstruct =
1328 (int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
1329 cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
1330 if (ddi_copyout(&cpy.dkg, (void *)arg, sizeof (cpy.dkg), flag))
1331 rval = EFAULT;
1332 break;
1334 case DKIOCSGEOM:
1335 if (ddi_copyin((void *)arg, &cpy.dkg,
1336 sizeof (struct dk_geom), flag)) {
1337 rval = EFAULT;
1338 break;
1340 mutex_enter(&fjp->fj_lock);
1341 fjp->fj_chars->fdc_ncyl = cpy.dkg.dkg_ncyl;
1342 fjp->fj_chars->fdc_nhead = cpy.dkg.dkg_nhead;
1343 fjp->fj_chars->fdc_secptrack = cpy.dkg.dkg_nsect;
1344 fjp->fj_attr->fda_intrlv = cpy.dkg.dkg_intrlv;
1345 fjp->fj_attr->fda_rotatespd = cpy.dkg.dkg_rpm;
1346 fdp->d_curfdtype = -1;
1347 mutex_exit(&fjp->fj_lock);
1348 break;
1351 * return the map of all logical partitions
1353 case DKIOCGAPART:
1355 * Note the conversion from starting sector number
1356 * to starting cylinder number.
1357 * Return error if division results in a remainder.
1359 nblks = fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack;
1361 #ifdef _MULTI_DATAMODEL
1362 switch (ddi_model_convert_from(flag & FMODELS)) {
1363 case DDI_MODEL_ILP32:
1365 struct dk_allmap32 dka32;
1367 for (part = 0; part < NDKMAP; part++) {
1368 if ((fdp->d_part[part].p_start % nblks) != 0)
1369 return (EINVAL);
1370 dka32.dka_map[part].dkl_cylno =
1371 fdp->d_part[part].p_start / nblks;
1372 dka32.dka_map[part].dkl_nblk =
1373 fdp->d_part[part].p_size;
1376 if (ddi_copyout(&dka32, (void *)arg,
1377 sizeof (struct dk_allmap32), flag))
1378 rval = EFAULT;
1380 break;
1382 case DDI_MODEL_NONE:
1384 #endif /* _MULTI_DATAMODEL */
1386 dmp = (struct dk_map *)&cpy.dka;
1387 for (part = 0; part < NDKMAP; part++) {
1388 if ((fdp->d_part[part].p_start % nblks) != 0)
1389 return (EINVAL);
1390 dmp->dkl_cylno =
1391 fdp->d_part[part].p_start / nblks;
1392 dmp->dkl_nblk = fdp->d_part[part].p_size;
1393 dmp++;
1396 if (ddi_copyout(&cpy.dka, (void *)arg,
1397 sizeof (struct dk_allmap), flag))
1398 rval = EFAULT;
1399 #ifdef _MULTI_DATAMODEL
1400 break;
1403 #endif /* _MULTI_DATAMODEL */
1405 break;
1408 * Set the map of all logical partitions
1410 case DKIOCSAPART:
1412 #ifdef _MULTI_DATAMODEL
1413 switch (ddi_model_convert_from(flag & FMODELS)) {
1414 case DDI_MODEL_ILP32:
1416 struct dk_allmap32 dka32;
1418 if (ddi_copyin((void *)arg, &dka32,
1419 sizeof (dka32), flag)) {
1420 rval = EFAULT;
1421 break;
1423 for (part = 0; part < NDKMAP; part++) {
1424 cpy.dka.dka_map[part].dkl_cylno =
1425 dka32.dka_map[part].dkl_cylno;
1426 cpy.dka.dka_map[part].dkl_nblk =
1427 dka32.dka_map[part].dkl_nblk;
1429 break;
1431 case DDI_MODEL_NONE:
1433 #endif /* _MULTI_DATAMODEL */
1434 if (ddi_copyin((void *)arg, &cpy.dka, sizeof (cpy.dka), flag))
1435 rval = EFAULT;
1436 #ifdef _MULTI_DATAMODEL
1438 break;
1440 #endif /* _MULTI_DATAMODEL */
1442 if (rval != 0)
1443 break;
1445 dmp = (struct dk_map *)&cpy.dka;
1446 nblks = fjp->fj_chars->fdc_nhead *
1447 fjp->fj_chars->fdc_secptrack;
1448 mutex_enter(&fjp->fj_lock);
1450 * Note the conversion from starting cylinder number
1451 * to starting sector number.
1453 for (part = 0; part < NDKMAP; part++) {
1454 fdp->d_part[part].p_start = dmp->dkl_cylno *
1455 nblks;
1456 fdp->d_part[part].p_size = dmp->dkl_nblk;
1457 dmp++;
1459 mutex_exit(&fjp->fj_lock);
1461 break;
1463 case DKIOCGVTOC:
1464 mutex_enter(&fjp->fj_lock);
1467 * Exit if the diskette has no label.
1468 * Also, get the label to make sure the correct one is
1469 * being used since the diskette may have changed
1471 fjp->fj_ops->fco_select(fjp, unit, 1);
1472 rval = fdgetlabel(fjp, unit);
1473 fjp->fj_ops->fco_select(fjp, unit, 0);
1474 if (rval) {
1475 mutex_exit(&fjp->fj_lock);
1476 rval = EINVAL;
1477 break;
1480 fd_build_user_vtoc(fjp, fdp, &vtoc);
1481 mutex_exit(&fjp->fj_lock);
1483 #ifdef _MULTI_DATAMODEL
1484 switch (ddi_model_convert_from(flag & FMODELS)) {
1485 case DDI_MODEL_ILP32:
1487 struct vtoc32 vtoc32;
1489 vtoctovtoc32(vtoc, vtoc32);
1491 if (ddi_copyout(&vtoc32, (void *)arg,
1492 sizeof (vtoc32), flag))
1493 rval = EFAULT;
1495 break;
1497 case DDI_MODEL_NONE:
1499 #endif /* _MULTI_DATAMODEL */
1500 if (ddi_copyout(&vtoc, (void *)arg,
1501 sizeof (vtoc), flag))
1502 rval = EFAULT;
1503 #ifdef _MULTI_DATAMODEL
1504 break;
1506 #endif /* _MULTI_DATAMODEL */
1508 break;
1510 case DKIOCSVTOC:
1512 #ifdef _MULTI_DATAMODEL
1513 switch (ddi_model_convert_from(flag & FMODELS)) {
1514 case DDI_MODEL_ILP32:
1516 struct vtoc32 vtoc32;
1518 if (ddi_copyin((void *)arg, &vtoc32,
1519 sizeof (vtoc32), flag)) {
1520 rval = EFAULT;
1521 break;
1524 vtoc32tovtoc(vtoc32, vtoc);
1526 break;
1528 case DDI_MODEL_NONE:
1530 #endif /* _MULTI_DATAMODEL */
1531 if (ddi_copyin((void *)arg, &vtoc, sizeof (vtoc), flag))
1532 rval = EFAULT;
1533 #ifdef _MULTI_DATAMODEL
1534 break;
1536 #endif /* _MULTI_DATAMODEL */
1538 if (rval != 0)
1539 break;
1542 label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
1544 mutex_enter(&fjp->fj_lock);
1546 if ((rval = fd_build_label_vtoc(fjp, fdp, &vtoc, label)) == 0) {
1547 fjp->fj_ops->fco_select(fjp, unit, 1);
1548 rval = fjp->fj_ops->fco_rw(fjp, unit, FDWRITE,
1549 0, 0, 1, (caddr_t)label, sizeof (struct dk_label));
1550 fjp->fj_ops->fco_select(fjp, unit, 0);
1552 mutex_exit(&fjp->fj_lock);
1553 kmem_free(label, sizeof (struct dk_label));
1554 break;
1556 case DKIOCSTATE:
1557 FDERRPRINT(FDEP_L1, FDEM_IOCT,
1558 (CE_CONT, "fd_ioctl fd unit %d: DKIOCSTATE\n", unit));
1560 if (ddi_copyin((void *)arg, &state, sizeof (int), flag)) {
1561 rval = EFAULT;
1562 break;
1565 rval = fd_check_media(dev, state);
1567 if (ddi_copyout(&fdp->d_media_state, (void *)arg,
1568 sizeof (int), flag))
1569 rval = EFAULT;
1570 break;
1572 case FDIOGCHAR:
1573 if (ddi_copyout(fjp->fj_chars, (void *)arg,
1574 sizeof (struct fd_char), flag))
1575 rval = EFAULT;
1576 break;
1578 case FDIOSCHAR:
1579 if (ddi_copyin((void *)arg, &cpy.fdchar,
1580 sizeof (struct fd_char), flag)) {
1581 rval = EFAULT;
1582 break;
1584 switch (cpy.fdchar.fdc_transfer_rate) {
1585 case 417:
1586 if ((fdp->d_media & (1 << FMT_3M)) == 0) {
1587 cmn_err(CE_CONT,
1588 "fdioschar:Medium density not supported\n");
1589 rval = EINVAL;
1590 break;
1592 mutex_enter(&fjp->fj_lock);
1593 fjp->fj_attr->fda_rotatespd = 360;
1594 mutex_exit(&fjp->fj_lock);
1595 /* cpy.fdchar.fdc_transfer_rate = 500; */
1596 /* FALLTHROUGH */
1597 case 1000:
1598 case 500:
1599 case 300:
1600 case 250:
1601 mutex_enter(&fjp->fj_lock);
1602 *(fjp->fj_chars) = cpy.fdchar;
1603 fdp->d_curfdtype = -1;
1604 fjp->fj_flags &= ~FUNIT_CHAROK;
1605 mutex_exit(&fjp->fj_lock);
1607 break;
1609 default:
1610 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1611 (CE_WARN, "fd_ioctl fd unit %d: FDIOSCHAR odd "
1612 "xfer rate %dkbs",
1613 unit, cpy.fdchar.fdc_transfer_rate));
1614 rval = EINVAL;
1615 break;
1617 break;
1620 * set all characteristics and geometry to the defaults
1622 case FDDEFGEOCHAR:
1623 mutex_enter(&fjp->fj_lock);
1624 fdp->d_curfdtype = fdp->d_deffdtype;
1625 *fjp->fj_chars = *defchar[fdp->d_curfdtype];
1626 *fjp->fj_attr = fdtypes[fdp->d_curfdtype];
1627 bcopy(fdparts[fdp->d_curfdtype],
1628 fdp->d_part, sizeof (struct partition) * NDKMAP);
1629 fjp->fj_flags &= ~FUNIT_CHAROK;
1630 mutex_exit(&fjp->fj_lock);
1631 break;
1633 case FDEJECT: /* eject disk */
1634 case DKIOCEJECT:
1635 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1636 rval = ENOSYS;
1637 break;
1639 case FDGETCHANGE: /* disk changed */
1640 if (ddi_copyin((void *)arg, &cpy.temp, sizeof (int), flag)) {
1641 rval = EFAULT;
1642 break;
1644 mutex_enter(&fjp->fj_lock);
1645 fjp->fj_ops->fco_select(fjp, unit, 1);
1647 if (fjp->fj_flags & FUNIT_CHANGED)
1648 cpy.temp |= FDGC_HISTORY;
1649 else
1650 cpy.temp &= ~FDGC_HISTORY;
1651 fjp->fj_flags &= ~FUNIT_CHANGED;
1653 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1654 cpy.temp |= FDGC_DETECTED;
1655 fjp->fj_ops->fco_resetchng(fjp, unit);
1657 * check diskette again only if it was removed
1659 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
1661 * no diskette is present
1663 cpy.temp |= FDGC_CURRENT;
1664 if (fjp->fj_flags & FUNIT_CHGDET)
1666 * again no diskette; not a new change
1668 cpy.temp ^= FDGC_DETECTED;
1669 else
1670 fjp->fj_flags |= FUNIT_CHGDET;
1671 } else {
1673 * a new diskette is present
1675 cpy.temp &= ~FDGC_CURRENT;
1676 fjp->fj_flags &= ~FUNIT_CHGDET;
1678 } else {
1679 cpy.temp &= ~(FDGC_DETECTED | FDGC_CURRENT);
1680 fjp->fj_flags &= ~FUNIT_CHGDET;
1683 * also get state of write protection
1685 if (fjp->fj_flags & FUNIT_WPROT) {
1686 cpy.temp |= FDGC_CURWPROT;
1687 } else {
1688 cpy.temp &= ~FDGC_CURWPROT;
1690 fjp->fj_ops->fco_select(fjp, unit, 0);
1691 mutex_exit(&fjp->fj_lock);
1693 if (ddi_copyout(&cpy.temp, (void *)arg, sizeof (int), flag))
1694 rval = EFAULT;
1695 break;
1697 case FDGETDRIVECHAR:
1698 if (ddi_copyout(fjp->fj_drive, (void *)arg,
1699 sizeof (struct fd_drive), flag))
1700 rval = EFAULT;
1701 break;
1703 case FDSETDRIVECHAR:
1704 if (ddi_copyin((void *)arg, &cpy.drvchar,
1705 sizeof (struct fd_drive), flag)) {
1706 rval = EFAULT;
1707 break;
1709 mutex_enter(&fjp->fj_lock);
1710 *(fjp->fj_drive) = cpy.drvchar;
1711 fdp->d_curfdtype = -1;
1712 fjp->fj_flags &= ~FUNIT_CHAROK;
1713 mutex_exit(&fjp->fj_lock);
1714 break;
1716 case DKIOCREMOVABLE: {
1717 int i = 1;
1719 /* no brainer: floppies are always removable */
1720 if (ddi_copyout(&i, (void *)arg, sizeof (int), flag)) {
1721 rval = EFAULT;
1723 break;
1726 case DKIOCGMEDIAINFO:
1727 rval = fd_get_media_info(fjp, (caddr_t)arg, flag);
1728 break;
1730 case FDIOCMD:
1732 struct fd_cmd fc;
1733 int cyl, head, spc, spt;
1735 #ifdef _MULTI_DATAMODEL
1736 switch (ddi_model_convert_from(flag & FMODELS)) {
1737 case DDI_MODEL_ILP32:
1739 struct fd_cmd32 fc32;
1741 if (ddi_copyin((void *)arg, &fc32,
1742 sizeof (fc32), flag)) {
1743 rval = EFAULT;
1744 break;
1747 fc.fdc_cmd = fc32.fdc_cmd;
1748 fc.fdc_flags = fc32.fdc_flags;
1749 fc.fdc_blkno = fc32.fdc_blkno;
1750 fc.fdc_secnt = fc32.fdc_secnt;
1751 fc.fdc_bufaddr = (caddr_t)(uintptr_t)fc32.fdc_bufaddr;
1752 fc.fdc_buflen = fc32.fdc_buflen;
1754 break;
1756 case DDI_MODEL_NONE:
1758 #endif /* _MULTI_DATAMODEL */
1760 if (ddi_copyin((void *)arg, &fc, sizeof (fc), flag)) {
1761 rval = EFAULT;
1762 break;
1764 #ifdef _MULTI_DATAMODEL
1765 break;
1767 #endif /* _MULTI_DATAMODEL */
1769 if (rval != 0)
1770 break;
1772 if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
1773 auto struct iovec aiov;
1774 auto struct uio auio;
1775 struct uio *uio = &auio;
1777 spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
1779 bzero(&auio, sizeof (struct uio));
1780 bzero(&aiov, sizeof (struct iovec));
1781 aiov.iov_base = fc.fdc_bufaddr;
1782 aiov.iov_len = (uint_t)fc.fdc_secnt *
1783 fjp->fj_chars->fdc_sec_size;
1784 uio->uio_iov = &aiov;
1786 uio->uio_iovcnt = 1;
1787 uio->uio_resid = aiov.iov_len;
1788 uio->uio_segflg = UIO_USERSPACE;
1790 rval = physio(fd_strategy, (struct buf *)0, dev,
1791 spc, minphys, uio);
1792 break;
1793 } else if (fc.fdc_cmd == FDCMD_FORMAT_TRACK) {
1794 spt = fjp->fj_chars->fdc_secptrack; /* sec/trk */
1795 spc = fjp->fj_chars->fdc_nhead * spt; /* sec/cyl */
1796 cyl = fc.fdc_blkno / spc;
1797 head = (fc.fdc_blkno % spc) / spt;
1798 if ((cyl | head) == 0)
1799 fjp->fj_flags &=
1800 ~(FUNIT_LABELOK | FUNIT_UNLABELED);
1802 FDERRPRINT(FDEP_L0, FDEM_FORM,
1803 (CE_CONT, "fd_format cyl %d, hd %d\n", cyl, head));
1804 fjp->fj_ops->fco_select(fjp, unit, 1);
1805 rval = fjp->fj_ops->fco_format(fjp, unit, cyl, head,
1806 (int)fc.fdc_flags);
1807 fjp->fj_ops->fco_select(fjp, unit, 0);
1809 break;
1811 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1812 (CE_WARN, "fd_ioctl fd unit %d: FDIOCSCMD not yet complete",
1813 unit));
1814 rval = EINVAL;
1815 break;
1818 case FDRAW:
1819 rval = fd_rawioctl(fjp, unit, (caddr_t)arg, flag);
1820 break;
1822 default:
1823 FDERRPRINT(FDEP_L4, FDEM_IOCT,
1824 (CE_WARN, "fd_ioctl fd unit %d: invalid ioctl 0x%x",
1825 unit, cmd));
1826 rval = ENOTTY;
1827 break;
1829 return (rval);
1832 static void
1833 fd_build_user_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp)
1835 struct partition *vpart;
1836 int i;
1837 int xblk;
1840 * Return vtoc structure fields in the provided VTOC area, addressed
1841 * by *vtocp.
1844 bzero(vtocp, sizeof (struct vtoc));
1846 bcopy(fdp->d_vtoc_bootinfo,
1847 vtocp->v_bootinfo, sizeof (vtocp->v_bootinfo));
1849 vtocp->v_sanity = VTOC_SANE;
1850 vtocp->v_version = fdp->d_vtoc_version;
1851 bcopy(fdp->d_vtoc_volume, vtocp->v_volume, LEN_DKL_VVOL);
1852 if (fjp->fj_flags & FUNIT_LABELOK) {
1853 vtocp->v_sectorsz = DEV_BSIZE;
1854 xblk = 1;
1855 } else {
1856 vtocp->v_sectorsz = fjp->fj_chars->fdc_sec_size;
1857 xblk = vtocp->v_sectorsz / DEV_BSIZE;
1859 vtocp->v_nparts = 3; /* <= NDKMAP; */
1862 * Copy partitioning information.
1864 bcopy(fdp->d_part, vtocp->v_part, sizeof (struct partition) * NDKMAP);
1865 for (i = NDKMAP, vpart = vtocp->v_part; i && (xblk > 1); i--, vpart++) {
1866 /* correct partition info if sector size > 512 bytes */
1867 vpart->p_start /= xblk;
1868 vpart->p_size /= xblk;
1871 bcopy(fdp->d_vtoc_timestamp,
1872 vtocp->timestamp, sizeof (fdp->d_vtoc_timestamp));
1873 bcopy(fdp->d_vtoc_asciilabel, vtocp->v_asciilabel, LEN_DKL_ASCII);
1877 static int
1878 fd_build_label_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp,
1879 struct dk_label *labelp)
1881 struct partition *vpart;
1882 int i;
1883 int nblks;
1884 int ncyl;
1885 ushort_t sum, *sp;
1889 * Sanity-check the vtoc
1891 if (vtocp->v_sanity != VTOC_SANE ||
1892 vtocp->v_nparts > NDKMAP || vtocp->v_nparts <= 0) {
1893 FDERRPRINT(FDEP_L3, FDEM_IOCT,
1894 (CE_WARN, "fd_build_label: sanity check on vtoc failed"));
1895 return (EINVAL);
1899 * before copying the vtoc, the partition information in it should be
1900 * checked against the information the driver already has on the
1901 * diskette.
1904 nblks = (fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack *
1905 fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1906 if (nblks == 0 || fjp->fj_chars->fdc_ncyl == 0)
1907 return (EFAULT);
1908 vpart = vtocp->v_part;
1911 * Check the partition information in the vtoc. The starting sectors
1912 * must lie along cylinder boundaries. (NDKMAP entries are checked
1913 * to ensure that the unused entries are set to 0 if vtoc->v_nparts
1914 * is less than NDKMAP)
1916 for (i = NDKMAP; i; i--) {
1917 if ((vpart->p_start % nblks) != 0) {
1918 return (EINVAL);
1920 ncyl = vpart->p_start / nblks;
1921 ncyl += vpart->p_size / nblks;
1922 if ((vpart->p_size % nblks) != 0)
1923 ncyl++;
1924 if (ncyl > (long)fjp->fj_chars->fdc_ncyl) {
1925 return (EINVAL);
1927 vpart++;
1931 bcopy(vtocp->v_bootinfo, fdp->d_vtoc_bootinfo,
1932 sizeof (vtocp->v_bootinfo));
1933 fdp->d_vtoc_version = vtocp->v_version;
1934 bcopy(vtocp->v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
1937 * Copy partitioning information.
1939 bcopy(vtocp->v_part, fdp->d_part, sizeof (struct partition) * NDKMAP);
1940 bcopy(vtocp->timestamp, fdp->d_vtoc_timestamp,
1941 sizeof (fdp->d_vtoc_timestamp));
1942 bcopy(vtocp->v_asciilabel, fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
1945 * construct the diskette label in supplied buffer
1948 /* Put appropriate vtoc structure fields into the disk label */
1949 labelp->dkl_vtoc.v_bootinfo[0] = (uint32_t)vtocp->v_bootinfo[0];
1950 labelp->dkl_vtoc.v_bootinfo[1] = (uint32_t)vtocp->v_bootinfo[1];
1951 labelp->dkl_vtoc.v_bootinfo[2] = (uint32_t)vtocp->v_bootinfo[2];
1953 labelp->dkl_vtoc.v_sanity = vtocp->v_sanity;
1954 labelp->dkl_vtoc.v_version = vtocp->v_version;
1956 bcopy(vtocp->v_volume, labelp->dkl_vtoc.v_volume, LEN_DKL_VVOL);
1958 labelp->dkl_vtoc.v_nparts = vtocp->v_nparts;
1960 bcopy(vtocp->v_reserved, labelp->dkl_vtoc.v_reserved,
1961 sizeof (labelp->dkl_vtoc.v_reserved));
1963 for (i = 0; i < (int)vtocp->v_nparts; i++) {
1964 labelp->dkl_vtoc.v_part[i].p_tag = vtocp->v_part[i].p_tag;
1965 labelp->dkl_vtoc.v_part[i].p_flag = vtocp->v_part[i].p_flag;
1966 labelp->dkl_vtoc.v_part[i].p_start = vtocp->v_part[i].p_start;
1967 labelp->dkl_vtoc.v_part[i].p_size = vtocp->v_part[i].p_size;
1970 for (i = 0; i < NDKMAP; i++) {
1971 labelp->dkl_vtoc.v_timestamp[i] = vtocp->timestamp[i];
1973 bcopy(vtocp->v_asciilabel, labelp->dkl_asciilabel, LEN_DKL_ASCII);
1976 labelp->dkl_pcyl = fjp->fj_chars->fdc_ncyl;
1977 labelp->dkl_ncyl = fjp->fj_chars->fdc_ncyl;
1978 labelp->dkl_nhead = fjp->fj_chars->fdc_nhead;
1980 * The fdc_secptrack field of the fd_char structure is the number
1981 * of sectors per track where the sectors are fdc_sec_size.
1982 * The dkl_nsect field of the dk_label structure is the number of
1983 * DEV_BSIZE (512) byte sectors per track.
1985 labelp->dkl_nsect = (fjp->fj_chars->fdc_secptrack *
1986 fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
1987 labelp->dkl_intrlv = fjp->fj_attr->fda_intrlv;
1988 labelp->dkl_rpm = fjp->fj_attr->fda_rotatespd;
1989 labelp->dkl_read_reinstruct =
1990 (int)(labelp->dkl_nsect * labelp->dkl_rpm * 4) / 60000;
1991 labelp->dkl_write_reinstruct = labelp->dkl_read_reinstruct;
1993 labelp->dkl_magic = DKL_MAGIC;
1995 sum = 0;
1996 labelp->dkl_cksum = 0;
1997 sp = (ushort_t *)labelp;
1998 while (sp < &(labelp->dkl_cksum)) {
1999 sum ^= *sp++;
2001 labelp->dkl_cksum = sum;
2003 return (0);
2006 static int
2007 fd_rawioctl(struct fcu_obj *fjp, int unit, caddr_t arg, int mode)
2009 struct fd_raw fdr;
2010 char *arg_result = NULL;
2011 int flag = B_READ;
2012 int rval = 0;
2013 caddr_t uaddr;
2014 uint_t ucount;
2016 FDERRPRINT(FDEP_L1, FDEM_RAWI,
2017 (CE_CONT, "fd_rawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
2019 if (fjp->fj_chars->fdc_medium != 3 && fjp->fj_chars->fdc_medium != 5) {
2020 cmn_err(CE_CONT, "fd_rawioctl: Medium density not supported\n");
2021 return (ENXIO);
2024 #ifdef _MULTI_DATAMODEL
2025 switch (ddi_model_convert_from(mode & FMODELS)) {
2026 case DDI_MODEL_ILP32:
2028 struct fd_raw32 fdr32;
2030 if (ddi_copyin(arg, &fdr32, sizeof (fdr32), mode))
2031 return (EFAULT);
2033 bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
2034 fdr.fdr_cnum = fdr32.fdr_cnum;
2035 fdr.fdr_nbytes = fdr32.fdr_nbytes;
2036 fdr.fdr_addr = (caddr_t)(uintptr_t)fdr32.fdr_addr;
2037 arg_result = ((struct fd_raw32 *)arg)->fdr_result;
2039 break;
2041 case DDI_MODEL_NONE:
2042 #endif /* ! _MULTI_DATAMODEL */
2044 if (ddi_copyin(arg, &fdr, sizeof (fdr), mode))
2045 return (EFAULT);
2047 arg_result = ((struct fd_raw *)arg)->fdr_result;
2049 #ifdef _MULTI_DATAMODEL
2050 break;
2052 #endif /* _MULTI_DATAMODEL */
2057 * copy user address & nbytes from raw_req so that we can
2058 * put kernel address in req structure
2060 uaddr = fdr.fdr_addr;
2061 ucount = (uint_t)fdr.fdr_nbytes;
2062 unit &= 3;
2064 switch (fdr.fdr_cmd[0] & 0x0f) {
2066 case FDRAW_FORMAT:
2067 ucount += 16;
2068 fdr.fdr_addr = kmem_zalloc(ucount, KM_SLEEP);
2069 if (ddi_copyin(uaddr, fdr.fdr_addr,
2070 (size_t)fdr.fdr_nbytes, mode)) {
2071 kmem_free(fdr.fdr_addr, ucount);
2072 return (EFAULT);
2074 if ((*fdr.fdr_addr | fdr.fdr_addr[1]) == 0)
2075 fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
2076 flag = B_WRITE;
2077 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2078 break;
2080 case FDRAW_WRCMD:
2081 case FDRAW_WRITEDEL:
2082 flag = B_WRITE;
2083 /* FALLTHROUGH */
2084 case FDRAW_RDCMD:
2085 case FDRAW_READDEL:
2086 case FDRAW_READTRACK:
2087 if (ucount) {
2089 * In SunOS 4.X, we used to as_fault things in.
2090 * We really cannot do this in 5.0/SVr4. Unless
2091 * someone really believes that speed is of the
2092 * essence here, it is just much simpler to do
2093 * this in kernel space and use copyin/copyout.
2095 fdr.fdr_addr = kmem_alloc((size_t)ucount, KM_SLEEP);
2096 if (flag == B_WRITE) {
2097 if (ddi_copyin(uaddr, fdr.fdr_addr, ucount,
2098 mode)) {
2099 kmem_free(fdr.fdr_addr, ucount);
2100 return (EFAULT);
2103 } else
2104 return (EINVAL);
2105 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2106 break;
2108 case FDRAW_READID:
2109 case FDRAW_REZERO:
2110 case FDRAW_SEEK:
2111 case FDRAW_SENSE_DRV:
2112 ucount = 0;
2113 fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
2114 break;
2116 case FDRAW_SPECIFY:
2117 fdr.fdr_cmd[2] &= 0xfe; /* keep NoDMA bit clear */
2118 /* FALLTHROUGH */
2119 case FDRAW_SENSE_INT:
2120 ucount = 0;
2121 break;
2123 default:
2124 return (EINVAL);
2128 * Note that we ignore any error returns from controller
2129 * This is the way the driver has been, and it may be
2130 * that the raw ioctl senders simply don't want to
2131 * see any errors returned in this fashion.
2134 fjp->fj_ops->fco_select(fjp, unit, 1);
2135 rval = fjp->fj_ops->fco_rwioctl(fjp, unit, (caddr_t)&fdr);
2137 if (ucount && flag == B_READ && rval == 0) {
2138 if (ddi_copyout(fdr.fdr_addr, uaddr, ucount, mode)) {
2139 rval = EFAULT;
2142 if (ddi_copyout(fdr.fdr_result, arg_result, sizeof (fdr.fdr_cmd), mode))
2143 rval = EFAULT;
2145 fjp->fj_ops->fco_select(fjp, unit, 0);
2146 if (ucount)
2147 kmem_free(fdr.fdr_addr, ucount);
2149 return (rval);
2153 * property operation routine. return the number of blocks for the partition
2154 * in question or forward the request to the property facilities.
2156 static int
2157 fd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
2158 char *name, caddr_t valuep, int *lengthp)
2160 struct fcu_obj *fjp = NULL;
2161 struct fdisk *fdp = NULL;
2162 uint64_t nblocks64;
2164 FDERRPRINT(FDEP_L1, FDEM_PROP,
2165 (CE_CONT, "fd_prop_op: dip %p %s\n", (void *)dip, name));
2168 * Our dynamic properties are all device specific and size oriented.
2169 * Requests issued under conditions where size is valid are passed
2170 * to ddi_prop_op_nblocks with the size information, otherwise the
2171 * request is passed to ddi_prop_op.
2173 if (dev == DDI_DEV_T_ANY) {
2174 pass: return (ddi_prop_op(dev, dip, prop_op, mod_flags,
2175 name, valuep, lengthp));
2176 } else {
2178 * Ignoring return value because success is checked by
2179 * verifying fjp and fdp and returned unit value is not used.
2181 (void) fd_getdrive(dev, &fjp, &fdp);
2182 if (!fjp || !fdp)
2183 goto pass;
2185 /* get nblocks value */
2186 nblocks64 = (ulong_t)fdp->d_part[PARTITION(dev)].p_size;
2188 return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
2189 name, valuep, lengthp, nblocks64));
2193 static void
2194 fd_media_watch(void *arg)
2196 struct fcu_obj *fjp;
2197 struct fdisk *fdp;
2199 #ifdef DEBUG
2200 int unit;
2201 #define DEBUG_ASSIGN unit=
2202 #else
2203 #define DEBUG_ASSIGN (void)
2204 #endif
2205 DEBUG_ASSIGN fd_getdrive((dev_t)arg, &fjp, &fdp);
2207 * Ignoring return in non DEBUG mode because device exist.
2208 * Returned unit value is not used.
2211 FDERRPRINT(FDEP_L0, FDEM_IOCT,
2212 (CE_CONT, "fd_media_watch unit %d\n", unit));
2215 * fd_get_media_state() cannot be called from this timeout function
2216 * because the floppy drive has to be selected first, and that could
2217 * force this function to sleep (while waiting for the select
2218 * semaphore).
2219 * Instead, just wakeup up driver.
2221 mutex_enter(&fjp->fj_lock);
2222 cv_broadcast(&fdp->d_statecv);
2223 mutex_exit(&fjp->fj_lock);
2226 enum dkio_state
2227 fd_get_media_state(struct fcu_obj *fjp, int unit)
2229 enum dkio_state state;
2231 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2232 /* recheck disk only if DSKCHG "high" */
2233 fjp->fj_ops->fco_resetchng(fjp, unit);
2234 if (fjp->fj_ops->fco_getchng(fjp, unit)) {
2235 if (fjp->fj_flags & FUNIT_CHGDET) {
2237 * again no diskette; not a new change
2239 state = DKIO_NONE;
2240 } else {
2242 * a new change; diskette was ejected
2244 fjp->fj_flags |= FUNIT_CHGDET;
2245 state = DKIO_EJECTED;
2247 } else {
2248 fjp->fj_flags &= ~FUNIT_CHGDET;
2249 state = DKIO_INSERTED;
2251 } else {
2252 fjp->fj_flags &= ~FUNIT_CHGDET;
2253 state = DKIO_INSERTED;
2255 FDERRPRINT(FDEP_L0, FDEM_IOCT,
2256 (CE_CONT, "fd_get_media_state unit %d: state %x\n", unit, state));
2257 return (state);
2260 static int
2261 fd_check_media(dev_t dev, enum dkio_state state)
2263 struct fcu_obj *fjp;
2264 struct fdisk *fdp;
2265 int unit;
2266 int err;
2268 unit = fd_getdrive(dev, &fjp, &fdp);
2270 mutex_enter(&fjp->fj_lock);
2272 fjp->fj_ops->fco_select(fjp, unit, 1);
2273 fdp->d_media_state = fd_get_media_state(fjp, unit);
2274 fdp->d_media_timeout = drv_usectohz(fd_check_media_time);
2276 while (fdp->d_media_state == state) {
2277 /* release the controller and drive */
2278 fjp->fj_ops->fco_select(fjp, unit, 0);
2280 /* turn on timer */
2281 fdp->d_media_timeout_id = timeout(fd_media_watch,
2282 (void *)dev, fdp->d_media_timeout);
2284 if (cv_wait_sig(&fdp->d_statecv, &fjp->fj_lock) == 0) {
2285 fdp->d_media_timeout = 0;
2286 mutex_exit(&fjp->fj_lock);
2287 return (EINTR);
2289 fjp->fj_ops->fco_select(fjp, unit, 1);
2290 fdp->d_media_state = fd_get_media_state(fjp, unit);
2293 if (fdp->d_media_state == DKIO_INSERTED) {
2294 err = fdgetlabel(fjp, unit);
2295 if (err) {
2296 fjp->fj_ops->fco_select(fjp, unit, 0);
2297 mutex_exit(&fjp->fj_lock);
2298 return (EIO);
2301 fjp->fj_ops->fco_select(fjp, unit, 0);
2302 mutex_exit(&fjp->fj_lock);
2303 return (0);
2307 * fd_get_media_info :
2308 * Collects medium information for
2309 * DKIOCGMEDIAINFO ioctl.
2312 static int
2313 fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag)
2315 struct dk_minfo media_info;
2316 int err = 0;
2318 media_info.dki_media_type = DK_FLOPPY;
2319 media_info.dki_lbsize = fjp->fj_chars->fdc_sec_size;
2320 media_info.dki_capacity = fjp->fj_chars->fdc_ncyl *
2321 fjp->fj_chars->fdc_secptrack * fjp->fj_chars->fdc_nhead;
2323 if (ddi_copyout(&media_info, buf, sizeof (struct dk_minfo), flag))
2324 err = EFAULT;
2325 return (err);