/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Floppy Disk driver
*/
/*
* Set CMOS feature:
* CMOS_CONF_MEM: CMOS memory contains configuration info
*/
#define CMOS_CONF_MEM
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/autoconf.h>
#include <sys/vtoc.h>
#include <sys/dkio.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kstat.h>
#include <sys/kmem.h>
#include <sys/ddidmareq.h>
#include <sys/fdio.h>
#include <sys/fdc.h>
#include <sys/fd_debug.h>
#include <sys/fdmedia.h>
#include <sys/debug.h>
#include <sys/modctl.h>
/*
* Local Function Prototypes
*/
static int fd_unit_is_open(struct fdisk *);
static int fdgetlabel(struct fcu_obj *, int);
static void fdstart(struct fcu_obj *);
static int fd_build_label_vtoc(struct fcu_obj *, struct fdisk *,
struct vtoc *, struct dk_label *);
static void fd_build_user_vtoc(struct fcu_obj *, struct fdisk *,
struct vtoc *);
static int fd_rawioctl(struct fcu_obj *, int, caddr_t, int);
static void fd_media_watch(void *);
static int fd_open(dev_t *, int, int, cred_t *);
static int fd_close(dev_t, int, int, cred_t *);
static int fd_strategy(struct buf *);
static int fd_read(dev_t, struct uio *, cred_t *);
static int fd_write(dev_t, struct uio *, cred_t *);
static int fd_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int fd_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
caddr_t, int *);
static int fd_check_media(dev_t dev, enum dkio_state state);
static int fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag);
static struct cb_ops fd_cb_ops = {
fd_open, /* open */
fd_close, /* close */
fd_strategy, /* strategy */
nodev, /* print */
nodev, /* dump */
fd_read, /* read */
fd_write, /* write */
fd_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
fd_prop_op, /* cb_prop_op */
0, /* streamtab */
D_NEW | D_MP /* Driver compatibility flag */
};
static int fd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int fd_probe(dev_info_t *);
static int fd_attach(dev_info_t *, ddi_attach_cmd_t);
static int fd_detach(dev_info_t *, ddi_detach_cmd_t);
static struct dev_ops fd_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
fd_getinfo, /* getinfo */
nulldev, /* identify */
fd_probe, /* probe */
fd_attach, /* attach */
fd_detach, /* detach */
nodev, /* reset */
&fd_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
NULL, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* static data
*/
static void *fd_state_head; /* opaque handle top of state structs */
static int fd_check_media_time = 5000000; /* 5 second state check */
/*
* error handling
*
* for debugging,
* set fderrlevel to 1
* set fderrmask to 224 or 644
*/
#ifdef DEBUG
static uint_t fderrmask = FDEM_ALL;
#endif
static int fderrlevel = 5;
#define KIOSP KSTAT_IO_PTR(fdp->d_iostat)
static struct driver_minor_data {
char *name;
int minor;
int type;
} fd_minor [] = {
{ "a", 0, S_IFBLK},
{ "b", 1, S_IFBLK},
{ "c", 2, S_IFBLK},
{ "a,raw", 0, S_IFCHR},
{ "b,raw", 1, S_IFCHR},
{ "c,raw", 2, S_IFCHR},
{0}
};
static struct modldrv modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
"Floppy Disk driver", /* Name of the module. */
&fd_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
int retval;
if ((retval = ddi_soft_state_init(&fd_state_head,
sizeof (struct fdisk) + sizeof (struct fd_drive) +
sizeof (struct fd_char) + sizeof (struct fdattr), 0)) != 0)
return (retval);
if ((retval = mod_install(&modlinkage)) != 0)
ddi_soft_state_fini(&fd_state_head);
return (retval);
}
int
_fini(void)
{
int retval;
if ((retval = mod_remove(&modlinkage)) != 0)
return (retval);
ddi_soft_state_fini(&fd_state_head);
return (retval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
fd_getdrive(dev_t dev, struct fcu_obj **fjpp, struct fdisk **fdpp)
{
if (fdpp) {
*fdpp = ddi_get_soft_state(fd_state_head, DRIVE(dev));
if (*fdpp && fjpp) {
*fjpp = (*fdpp)->d_obj;
if (*fjpp)
return ((*fjpp)->fj_unit);
}
}
return (-1);
}
/*ARGSUSED*/
static int
fd_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
dev_t dev = (dev_t)arg;
struct fcu_obj *fjp = NULL;
struct fdisk *fdp = NULL;
int rval;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
(void) fd_getdrive(dev, &fjp, &fdp);
/*
* Ignoring return value because success is checked by
* verifying fjp and fdp and returned unit value is not used.
*/
if (fjp && fdp) {
*result = fjp->fj_dip;
rval = DDI_SUCCESS;
} else
rval = DDI_FAILURE;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(uintptr_t)DRIVE(dev);
rval = DDI_SUCCESS;
break;
default:
rval = DDI_FAILURE;
}
return (rval);
}
#ifdef CMOS_CONF_MEM
#define CMOS_ADDR 0x70
#define CMOS_DATA 0x71
#define CMOS_FDRV 0x10
#endif /* CMOS_CONF_MEM */
static int
fd_probe(dev_info_t *dip)
{
#ifdef CMOS_CONF_MEM
int cmos;
int drive_type;
#endif /* CMOS_CONF_MEM */
int debug[2];
int drive_size;
int len;
int unit_num;
char density[8];
len = sizeof (debug);
if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
DDI_PROP_SUCCESS) {
fderrlevel = debug[0];
#ifdef DEBUG
fderrmask = (uint_t)debug[1];
#endif
}
len = sizeof (unit_num);
if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
DDI_PROP_SUCCESS) {
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_probe failed: dip %p", (void *)dip));
return (DDI_PROBE_FAILURE);
}
#ifdef CMOS_CONF_MEM
/* get the cmos memory values quick and dirty */
outb(CMOS_ADDR, CMOS_FDRV);
cmos = drive_type = (int)inb(CMOS_DATA);
#endif /* CMOS_CONF_MEM */
switch (unit_num) {
#ifdef CMOS_CONF_MEM
case 0:
drive_type = drive_type >> 4;
/* FALLTHROUGH */
case 1:
if (cmos && (drive_type & 0x0F)) {
break;
}
/*
* Some enhanced floppy-disk controller adaptor cards
* require NO drives defined in the CMOS configuration
* memory.
* So fall through
*/
#endif /* CMOS_CONF_MEM */
default: /* need to check conf file */
len = sizeof (density);
if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS, "density", (caddr_t)&density, &len) !=
DDI_PROP_SUCCESS) {
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN,
"fd_probe failed density: dip %p unit %d",
(void *)dip, unit_num));
return (DDI_PROBE_FAILURE);
}
len = sizeof (drive_size);
if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS, "size", (caddr_t)&drive_size, &len) !=
DDI_PROP_SUCCESS) {
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_probe failed size: dip %p unit %d",
(void *)dip, unit_num));
return (DDI_PROBE_FAILURE);
}
}
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_probe dip %p unit %d", (void *)dip, unit_num));
return (DDI_PROBE_SUCCESS);
}
/* ARGSUSED */
static int
fd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
struct fcu_obj *fjp;
struct fdisk *fdp;
struct driver_minor_data *dmdp;
int mode_3D;
int drive_num, drive_size, drive_type;
#ifdef CMOS_CONF_MEM
int cmos;
#endif /* CMOS_CONF_MEM */
int len, sig_minor;
int unit_num;
char density[8];
char name[MAXNAMELEN];
switch (cmd) {
case DDI_ATTACH:
len = sizeof (unit_num);
if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS, "unit", (caddr_t)&unit_num, &len) !=
DDI_PROP_SUCCESS) {
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_attach failed: dip %p", (void *)dip));
return (DDI_FAILURE);
}
#ifdef CMOS_CONF_MEM
outb(CMOS_ADDR, CMOS_FDRV);
cmos = drive_type = (int)inb(CMOS_DATA);
#endif /* CMOS_CONF_MEM */
switch (unit_num) {
#ifdef CMOS_CONF_MEM
case 0:
drive_type = drive_type >> 4;
/* FALLTHROUGH */
case 1:
drive_type = drive_type & 0x0F;
if (cmos)
break;
/*
* Some enhanced floppy-disk controller adaptor cards
* require NO drives defined in the CMOS configuration
* memory.
* So fall through
*/
#endif /* CMOS_CONF_MEM */
default: /* need to check .conf file */
drive_type = 0;
len = sizeof (density);
if (ddi_prop_op(DDI_DEV_T_ANY, dip,
PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "density",
(caddr_t)&density, &len) != DDI_PROP_SUCCESS)
density[0] = '\0';
len = sizeof (drive_size);
if (ddi_prop_op(DDI_DEV_T_ANY, dip,
PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "size",
(caddr_t)&drive_size, &len) != DDI_PROP_SUCCESS)
drive_size = 0;
if (strcmp(density, "DSDD") == 0) {
if (drive_size == 5)
drive_type = 1;
else if (drive_size == 3)
drive_type = 3;
} else if (strcmp(density, "DSHD") == 0) {
if (drive_size == 5)
drive_type = 2;
else if (drive_size == 3)
drive_type = 4;
} else if (strcmp(density, "DSED") == 0 &&
drive_size == 3) {
drive_type = 6;
}
break;
}
if (drive_type == 0) {
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_attach failed type: dip %p unit %d",
(void *)dip, unit_num));
return (DDI_FAILURE);
}
drive_num = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(fd_state_head, drive_num) != 0)
return (DDI_FAILURE);
fdp = ddi_get_soft_state(fd_state_head, drive_num);
fjp = fdp->d_obj = ddi_get_driver_private(dip);
mutex_init(&fjp->fj_lock, NULL, MUTEX_DRIVER, *fjp->fj_iblock);
sema_init(&fdp->d_ocsem, 1, NULL, SEMA_DRIVER, NULL);
fjp->fj_drive = (struct fd_drive *)(fdp + 1);
fjp->fj_chars = (struct fd_char *)(fjp->fj_drive + 1);
fjp->fj_attr = (struct fdattr *)(fjp->fj_chars + 1);
/*
* set default floppy drive characteristics & geometry
*/
switch (drive_type) { /* assume doubled sided */
case 2: /* 5.25 high density */
*fjp->fj_drive = dfd_525HD;
fdp->d_media = 1<<FMT_5H | 1<<FMT_5D9 | 1<<FMT_5D8 |
1<<FMT_5D4 | 1<<FMT_5D16;
fdp->d_deffdtype = fdp->d_curfdtype = FMT_5H;
break;
case 4: /* 3.5 high density */
*fjp->fj_drive = dfd_350HD;
fdp->d_media = 1<<FMT_3H | 1<<FMT_3I | 1<<FMT_3D;
len = sizeof (mode_3D);
if (ddi_prop_op(DDI_DEV_T_ANY, dip,
PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "mode_3D",
(caddr_t)&mode_3D, &len) != DDI_PROP_SUCCESS)
mode_3D = 0;
if (mode_3D && (fjp->fj_fdc->c_flags & FCFLG_3DMODE))
/*
* 3D mode should be enabled only if a dual-
* speed 3.5" high-density drive and a
* supported floppy controller are installed.
*/
fdp->d_media |= 1 << FMT_3M;
fdp->d_deffdtype = fdp->d_curfdtype = FMT_3H;
break;
case 1: /* 5.25 double density */
*fjp->fj_drive = dfd_525DD;
fdp->d_media = 1<<FMT_5D9 | 1<<FMT_5D8 | 1<<FMT_5D4 |
1<<FMT_5D16;
fdp->d_deffdtype = fdp->d_curfdtype = FMT_5D9;
break;
case 3: /* 3.5 double density */
*fjp->fj_drive = dfd_350HD;
fdp->d_media = 1<<FMT_3D;
fdp->d_deffdtype = fdp->d_curfdtype = FMT_3D;
break;
case 5: /* 3.5 extended density */
case 6:
case 7:
*fjp->fj_drive = dfd_350ED;
fdp->d_media = 1<<FMT_3E | 1<<FMT_3H | 1<<FMT_3I |
1<<FMT_3D;
fdp->d_deffdtype = fdp->d_curfdtype = FMT_3E;
break;
case 0: /* no drive defined */
default:
goto no_attach;
}
*fjp->fj_chars = *defchar[fdp->d_deffdtype];
*fjp->fj_attr = fdtypes[fdp->d_deffdtype];
bcopy(fdparts[fdp->d_deffdtype], fdp->d_part,
sizeof (struct partition) * NDKMAP);
fjp->fj_rotspd = fdtypes[fdp->d_deffdtype].fda_rotatespd;
sig_minor = drive_num << 3;
for (dmdp = fd_minor; dmdp->name != NULL; dmdp++) {
if (ddi_create_minor_node(dip, dmdp->name, dmdp->type,
sig_minor | dmdp->minor, DDI_NT_FD, NULL)
== DDI_FAILURE) {
ddi_remove_minor_node(dip, NULL);
goto no_attach;
}
}
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_attach: dip %p unit %d",
(void *)dip, unit_num));
(void) sprintf(name, "fd%d", drive_num);
fdp->d_iostat = kstat_create("fd", drive_num, name, "disk",
KSTAT_TYPE_IO, 1, KSTAT_FLAG_PERSISTENT);
if (fdp->d_iostat) {
fdp->d_iostat->ks_lock = &fjp->fj_lock;
kstat_install(fdp->d_iostat);
}
fjp->fj_data = (caddr_t)fdp;
fjp->fj_flags |= FUNIT_DRVATCH;
/*
* Add a zero-length attribute to tell the world we support
* kernel ioctls (for layered drivers)
*/
(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
DDI_KERNEL_IOCTL, NULL, 0);
/*
* We want to get suspend/resume events, so that we can
* refuse to suspend when pcfs is mounted.
*/
(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
"pm-hardware-state", "needs-suspend-resume");
/*
* Ignoring return value because, for passed arguments, only
* DDI_SUCCESS is returned.
*/
ddi_report_dev(dip);
return (DDI_SUCCESS);
case DDI_RESUME:
/* nothing for us to do */
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
no_attach:
fjp->fj_drive = NULL;
fjp->fj_chars = NULL;
fjp->fj_attr = NULL;
mutex_destroy(&fjp->fj_lock);
sema_destroy(&fdp->d_ocsem);
ddi_soft_state_free(fd_state_head, drive_num);
FDERRPRINT(FDEP_L3, FDEM_ATTA,
(CE_WARN, "fd_attach failed: dip %p unit %d",
(void *)dip, unit_num));
return (DDI_FAILURE);
}
/* ARGSUSED */
static int
fd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
struct fcu_obj *fjp;
struct fdisk *fdp;
int drive_num;
int rval = DDI_SUCCESS;
FDERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fd_detach dip %p",
(void *)dip));
drive_num = ddi_get_instance(dip);
if (!(fdp = ddi_get_soft_state(fd_state_head, drive_num)))
return (rval);
switch (cmd) {
case DDI_DETACH:
if (fd_unit_is_open(fdp)) {
rval = DDI_FAILURE;
break;
}
kstat_delete(fdp->d_iostat);
fdp->d_iostat = NULL;
fjp = (struct fcu_obj *)fdp->d_obj;
fjp->fj_flags &= ~FUNIT_DRVATCH;
fjp->fj_data = NULL;
fjp->fj_drive = NULL;
fjp->fj_chars = NULL;
fjp->fj_attr = NULL;
ddi_prop_remove_all(dip);
mutex_destroy(&fjp->fj_lock);
sema_destroy(&fdp->d_ocsem);
ddi_soft_state_free(fd_state_head, drive_num);
break;
case DDI_SUSPEND:
/*
* Bad, bad, bad things will happen if someone
* *changes* the disk in the drive while it is mounted
* and the system is suspended. We have no way to
* detect that. (Undetected filesystem corruption.
* Its akin to changing the boot disk while the system
* is suspended. Don't do it!)
*
* So we refuse to suspend if there is a mounted filesystem.
* (We guess this by looking for a block open. Character
* opens are fine.) This limits some of the usability of
* suspend/resume, but it certainly avoids this
* potential filesytem corruption from pilot error.
* Given the decreasing popularity of floppy media, we
* don't see this as much of a limitation.
*/
if (fdp->d_regopen[OTYP_BLK]) {
cmn_err(CE_NOTE,
"Unable to suspend while floppy is in use.");
rval = DDI_FAILURE;
}
break;
default:
rval = DDI_FAILURE;
break;
}
return (rval);
}
static int
fd_part_is_open(struct fdisk *fdp, int part)
{
int i;
for (i = 0; i < (OTYPCNT - 1); i++)
if (fdp->d_regopen[i] & (1 << part))
return (1);
return (0);
}
static int
fd_unit_is_open(struct fdisk *fdp)
{
int i;
for (i = 0; i < NDKMAP; i++)
if (fdp->d_lyropen[i])
return (1);
for (i = 0; i < (OTYPCNT - 1); i++)
if (fdp->d_regopen[i])
return (1);
return (0);
}
/*ARGSUSED*/
static int
fd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
{
struct fcu_obj *fjp = NULL;
struct fdisk *fdp = NULL;
struct partition *pp;
dev_t dev;
int part, unit;
int part_is_open;
int rval;
uint_t pbit;
dev = *devp;
unit = fd_getdrive(dev, &fjp, &fdp);
if (!fjp || !fdp)
return (ENXIO);
part = PARTITION(dev);
pbit = 1 << part;
pp = &fdp->d_part[part];
/*
* Serialize opens/closes
*/
sema_p(&fdp->d_ocsem);
FDERRPRINT(FDEP_L1, FDEM_OPEN,
(CE_CONT, "fd_open: fd%d part %d flag %x otype %x\n", DRIVE(dev),
part, flag, otyp));
/*
* Check for previous exclusive open, or trying to exclusive open
* An "exclusive open" on any partition is not guaranteed to
* protect against opens on another partition that overlaps it.
*/
if (otyp == OTYP_LYR) {
part_is_open = (fdp->d_lyropen[part] != 0);
} else {
part_is_open = fd_part_is_open(fdp, part);
}
if ((fdp->d_exclmask & pbit) || ((flag & FEXCL) && part_is_open)) {
FDERRPRINT(FDEP_L0, FDEM_OPEN, (CE_CONT,
"fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n",
fdp->d_exclmask, fdp->d_regopen[otyp], fdp->d_lyropen[part],
pbit));
sema_v(&fdp->d_ocsem);
return (EBUSY);
}
/*
* Ensure that drive is recalibrated on first open of new diskette.
*/
fjp->fj_ops->fco_select(fjp, unit, 1);
if (fjp->fj_ops->fco_getchng(fjp, unit) != 0) {
if (fjp->fj_ops->fco_rcseek(fjp, unit, -1, 0)) {
FDERRPRINT(FDEP_L2, FDEM_OPEN,
(CE_NOTE, "fd_open fd%d: not ready", DRIVE(dev)));
fjp->fj_ops->fco_select(fjp, unit, 0);
sema_v(&fdp->d_ocsem);
return (ENXIO);
}
fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
}
if (flag & (FNDELAY | FNONBLOCK)) {
/* don't attempt access, just return successfully */
fjp->fj_ops->fco_select(fjp, unit, 0);
goto out;
}
/*
* auto-sense the density/format of the diskette
*/
rval = fdgetlabel(fjp, unit);
fjp->fj_ops->fco_select(fjp, unit, 0);
if (rval) {
/* didn't find label (couldn't read anything) */
FDERRPRINT(FDEP_L2, FDEM_OPEN,
(CE_NOTE, "fd%d: drive not ready", DRIVE(dev)));
sema_v(&fdp->d_ocsem);
return (EIO);
}
/* check partition */
if (pp->p_size == 0) {
sema_v(&fdp->d_ocsem);
return (ENXIO);
}
/*
* if opening for writing, check write protect on diskette
*/
if ((flag & FWRITE) && (fdp->d_obj->fj_flags & FUNIT_WPROT)) {
sema_v(&fdp->d_ocsem);
return (EROFS);
}
out:
/*
* mark open as having succeeded
*/
if (flag & FEXCL)
fdp->d_exclmask |= pbit;
if (otyp == OTYP_LYR)
fdp->d_lyropen[part]++;
else
fdp->d_regopen[otyp] |= 1 << part;
sema_v(&fdp->d_ocsem);
return (0);
}
/*
* fdgetlabel - read the SunOS label off the diskette
* if it can read a valid label it does so, else it will use a
* default. If it can`t read the diskette - that is an error.
*
* RETURNS: 0 for ok - meaning that it could at least read the device,
* !0 for error XXX TBD NYD error codes
*/
static int
fdgetlabel(struct fcu_obj *fjp, int unit)
{
struct dk_label *label;
struct fdisk *fdp;
char *newlabel;
short *sp;
short count;
short xsum;
int tries, try_this;
uint_t nexttype;
int rval;
short oldlvl;
int i;
FDERRPRINT(FDEP_L0, FDEM_GETL,
(CE_CONT, "fdgetlabel fd unit %d\n", unit));
fdp = (struct fdisk *)fjp->fj_data;
fjp->fj_flags &= ~(FUNIT_UNLABELED);
/*
* get some space to play with the label
*/
label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
FDERRPRINT(FDEP_L0, FDEM_GETL, (CE_CONT,
"fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n",
unit, (void *)label, (size_t)sizeof (struct dk_label)));
/*
* read block 0 (0/0/1) to find the label
* (disk is potentially not present or unformatted)
*/
/* noerrprint since this is a private cmd */
oldlvl = fderrlevel;
fderrlevel = FDEP_LMAX;
/*
* try different characteristics (ie densities)
*
* if fdp->d_curfdtype is -1 then the current characteristics
* were set by ioctl and need to try it as well as everything
* in the table
*/
nexttype = fdp->d_deffdtype;
try_this = 1; /* always try the current characteristics */
for (tries = nfdtypes; tries; tries--) {
if (try_this) {
fjp->fj_flags &= ~FUNIT_CHAROK;
/* try reading last sector of cyl 1, head 0 */
if (!(rval = fjp->fj_ops->fco_rw(fjp, unit,
FDREAD, 1, 0, fjp->fj_chars->fdc_secptrack,
(caddr_t)label,
sizeof (struct dk_label))) &&
/* and last sector plus 1 of cylinder 1 */
fjp->fj_ops->fco_rw(fjp, unit, FDREAD, 1,
0, fjp->fj_chars->fdc_secptrack + 1,
(caddr_t)label,
sizeof (struct dk_label)) &&
/* and label sector on cylinder 0 */
!(rval = fjp->fj_ops->fco_rw(fjp, unit,
FDREAD, 0, 0, 1, (caddr_t)label,
sizeof (struct dk_label))))
break;
if (rval == ENXIO)
break;
}
/*
* try the next entry in the characteristics tbl
*/
fdp->d_curfdtype = (signed char)nexttype;
nexttype = (nexttype + 1) % nfdtypes;
if ((1 << fdp->d_curfdtype) & fdp->d_media) {
*fjp->fj_chars = *defchar[fdp->d_curfdtype];
*fjp->fj_attr = fdtypes[fdp->d_curfdtype];
bcopy(fdparts[fdp->d_curfdtype], fdp->d_part,
sizeof (struct partition) * NDKMAP);
/*
* check for a double_density diskette
* in a high_density 5.25" drive
*/
if (fjp->fj_chars->fdc_transfer_rate == 250 &&
fjp->fj_rotspd > fjp->fj_attr->fda_rotatespd) {
/*
* yes - adjust transfer rate since we don't
* know if we have a 5.25" dual-speed drive
*/
fjp->fj_attr->fda_rotatespd = 360;
fjp->fj_chars->fdc_transfer_rate = 300;
fjp->fj_chars->fdc_medium = 5;
}
if ((2 * fjp->fj_chars->fdc_ncyl) ==
defchar[fdp->d_deffdtype]->fdc_ncyl) {
/* yes - adjust steps per cylinder */
fjp->fj_chars->fdc_steps = 2;
} else
fjp->fj_chars->fdc_steps = 1;
try_this = 1;
} else
try_this = 0;
}
fderrlevel = oldlvl; /* print errors again */
if (rval) {
fdp->d_curfdtype = fdp->d_deffdtype;
goto out; /* couldn't read anything */
}
FDERRPRINT(FDEP_L0, FDEM_GETL,
(CE_CONT,
"fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n",
unit, fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_secptrack,
fjp->fj_chars->fdc_steps, fjp->fj_attr->fda_rotatespd,
fjp->fj_attr->fda_intrlv));
/*
* _something_ was read - look for unixtype label
*/
if (label->dkl_magic != DKL_MAGIC ||
label->dkl_vtoc.v_sanity != VTOC_SANE) {
/* not a label - no magic number */
goto nolabel; /* no errors, but no label */
}
count = sizeof (struct dk_label) / sizeof (short);
sp = (short *)label;
xsum = 0;
while (count--)
xsum ^= *sp++; /* should add up to 0 */
if (xsum) {
/* not a label - checksum didn't compute */
goto nolabel; /* no errors, but no label */
}
/*
* the SunOS label overrides current diskette characteristics
*/
fjp->fj_chars->fdc_ncyl = label->dkl_pcyl;
fjp->fj_chars->fdc_nhead = label->dkl_nhead;
fjp->fj_chars->fdc_secptrack = (label->dkl_nsect * DEV_BSIZE) /
fjp->fj_chars->fdc_sec_size;
if (defchar[fdp->d_deffdtype]->fdc_ncyl == 2 * fjp->fj_chars->fdc_ncyl)
fjp->fj_chars->fdc_steps = 2;
else
fjp->fj_chars->fdc_steps = 1;
fjp->fj_attr->fda_rotatespd = label->dkl_rpm;
fjp->fj_attr->fda_intrlv = label->dkl_intrlv;
fdp->d_vtoc_version = label->dkl_vtoc.v_version;
bcopy(label->dkl_vtoc.v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
bcopy(label->dkl_vtoc.v_asciilabel,
fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
/*
* logical partitions
*/
for (i = 0; i < NDKMAP; i++) {
fdp->d_part[i].p_tag = label->dkl_vtoc.v_part[i].p_tag;
fdp->d_part[i].p_flag = label->dkl_vtoc.v_part[i].p_flag;
fdp->d_part[i].p_start = label->dkl_vtoc.v_part[i].p_start;
fdp->d_part[i].p_size = label->dkl_vtoc.v_part[i].p_size;
fdp->d_vtoc_timestamp[i] = label->dkl_vtoc.timestamp[i];
}
fjp->fj_flags |= FUNIT_LABELOK;
goto out;
nolabel:
/*
* if not found, fill in label info from default (mark default used)
*/
if (fdp->d_media & (1<<FMT_3D))
newlabel = deflabel_35;
else /* if (fdp->d_media & (1<<FMT_5D9)) */
newlabel = deflabel_525;
bzero(fdp->d_vtoc_volume, LEN_DKL_VVOL);
(void) sprintf(fdp->d_vtoc_asciilabel, newlabel,
fjp->fj_chars->fdc_ncyl, fjp->fj_chars->fdc_nhead,
fjp->fj_chars->fdc_secptrack);
fjp->fj_flags |= FUNIT_UNLABELED;
out:
kmem_free(label, sizeof (struct dk_label));
return (rval);
}
/*ARGSUSED*/
static int
fd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
struct fcu_obj *fjp = NULL;
struct fdisk *fdp = NULL;
int part, part_is_closed;
#ifdef DEBUG
int unit;
#define DEBUG_ASSIGN unit=
#else
#define DEBUG_ASSIGN (void)
#endif
DEBUG_ASSIGN fd_getdrive(dev, &fjp, &fdp);
/*
* Ignoring return in non DEBUG mode because success is checked by
* verifying fjp and fdp and returned unit value is not used.
*/
if (!fjp || !fdp)
return (ENXIO);
part = PARTITION(dev);
sema_p(&fdp->d_ocsem);
FDERRPRINT(FDEP_L1, FDEM_CLOS,
(CE_CONT, "fd_close: fd unit %d part %d otype %x\n",
unit, part, otyp));
if (otyp == OTYP_LYR) {
if (fdp->d_lyropen[part])
fdp->d_lyropen[part]--;
part_is_closed = (fdp->d_lyropen[part] == 0);
} else {
fdp->d_regopen[otyp] &= ~(1<<part);
part_is_closed = 1;
}
if (part_is_closed) {
if (part == 2 && fdp->d_exclmask&(1<<part))
fdp->d_exclmask = 0;
else
fdp->d_exclmask &= ~(1<<part);
FDERRPRINT(FDEP_L0, FDEM_CLOS,
(CE_CONT,
"fd_close: exclparts %lx openparts %lx lyrcnt %lx\n",
fdp->d_exclmask, fdp->d_regopen[otyp],
fdp->d_lyropen[part]));
if (fd_unit_is_open(fdp) == 0)
fdp->d_obj->fj_flags &= ~FUNIT_CHANGED;
}
sema_v(&fdp->d_ocsem);
return (0);
}
/* ARGSUSED */
static int
fd_read(dev_t dev, struct uio *uio, cred_t *cred_p)
{
return (physio(fd_strategy, NULL, dev, B_READ, minphys, uio));
}
/* ARGSUSED */
static int
fd_write(dev_t dev, struct uio *uio, cred_t *cred_p)
{
return (physio(fd_strategy, NULL, dev, B_WRITE, minphys, uio));
}
/*
* fd_strategy
* checks operation, hangs buf struct off fdcntlr, calls fdstart
* if not already busy. Note that if we call start, then the operation
* will already be done on return (start sleeps).
*/
static int
fd_strategy(struct buf *bp)
{
struct fcu_obj *fjp;
struct fdisk *fdp;
struct partition *pp;
FDERRPRINT(FDEP_L1, FDEM_STRA,
(CE_CONT, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
(void *)bp, bp->b_edev));
(void) fd_getdrive(bp->b_edev, &fjp, &fdp);
/*
* Ignoring return because device exist.
* Returned unit value is not used.
*/
pp = &fdp->d_part[PARTITION(bp->b_edev)];
if (fjp->fj_chars->fdc_sec_size > NBPSCTR && (bp->b_blkno & 1)) {
FDERRPRINT(FDEP_L3, FDEM_STRA,
(CE_WARN, "fd%d: block %ld is not start of sector!",
DRIVE(bp->b_edev), (long)bp->b_blkno));
bp->b_error = EINVAL;
goto bad;
}
if ((bp->b_blkno > pp->p_size)) {
FDERRPRINT(FDEP_L3, FDEM_STRA,
(CE_WARN, "fd%d: block %ld is past the end! (nblk=%ld)",
DRIVE(bp->b_edev), (long)bp->b_blkno, pp->p_size));
bp->b_error = ENOSPC;
goto bad;
}
/* if at end of file, skip out now */
if (bp->b_blkno == pp->p_size) {
if ((bp->b_flags & B_READ) == 0) {
/* a write needs to get an error! */
bp->b_error = ENOSPC;
goto bad;
}
bp->b_resid = bp->b_bcount;
biodone(bp);
return (0);
}
/* if operation not a multiple of sector size, is error! */
if (bp->b_bcount % fjp->fj_chars->fdc_sec_size) {
FDERRPRINT(FDEP_L3, FDEM_STRA,
(CE_WARN, "fd%d: count %ld must be a multiple of %d",
DRIVE(bp->b_edev), bp->b_bcount,
fjp->fj_chars->fdc_sec_size));
bp->b_error = EINVAL;
goto bad;
}
/*
* Put the buf request in the drive's queue, FIFO.
*/
bp->av_forw = 0;
mutex_enter(&fjp->fj_lock);
if (fdp->d_iostat)
kstat_waitq_enter(KIOSP);
if (fdp->d_actf)
fdp->d_actl->av_forw = bp;
else
fdp->d_actf = bp;
fdp->d_actl = bp;
if (!(fjp->fj_flags & FUNIT_BUSY)) {
fdstart(fjp);
}
mutex_exit(&fjp->fj_lock);
return (0);
bad:
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_ERROR;
biodone(bp);
return (0);
}
/*
* fdstart
* called from fd_strategy() or from fdXXXX() to setup and
* start operations of read or write only (using buf structs).
* Because the chip doesn't handle crossing cylinder boundaries on
* the fly, this takes care of those boundary conditions. Note that
* it sleeps until the operation is done *within fdstart* - so that
* when fdstart returns, the operation is already done.
*/
static void
fdstart(struct fcu_obj *fjp)
{
struct buf *bp;
struct fdisk *fdp = (struct fdisk *)fjp->fj_data;
struct fd_char *chp;
struct partition *pp;
uint_t ptend;
uint_t bincyl; /* (the number of the desired) block in cyl. */
uint_t blk, len, tlen;
uint_t secpcyl; /* number of sectors per cylinder */
int cyl, head, sect;
int sctrshft, unit;
caddr_t addr;
ASSERT(MUTEX_HELD(&fjp->fj_lock));
fjp->fj_flags |= FUNIT_BUSY;
while ((bp = fdp->d_actf) != NULL) {
fdp->d_actf = bp->av_forw;
fdp->d_current = bp;
if (fdp->d_iostat) {
kstat_waitq_to_runq(KIOSP);
}
mutex_exit(&fjp->fj_lock);
FDERRPRINT(FDEP_L0, FDEM_STRT,
(CE_CONT, "fdstart: bp=0x%p blkno=0x%lx bcount=0x%lx\n",
(void *)bp, (long)bp->b_blkno, bp->b_bcount));
bp->b_flags &= ~B_ERROR;
bp->b_error = 0;
bp->b_resid = bp->b_bcount; /* init resid */
ASSERT(DRIVE(bp->b_edev) == ddi_get_instance(fjp->fj_dip));
unit = fjp->fj_unit;
fjp->fj_ops->fco_select(fjp, unit, 1);
bp_mapin(bp); /* map in buffers */
pp = &fdp->d_part[PARTITION(bp->b_edev)];
/* starting blk adjusted for the partition */
blk = bp->b_blkno + pp->p_start;
ptend = pp->p_start + pp->p_size; /* end of the partition */
chp = fjp->fj_chars;
secpcyl = chp->fdc_nhead * chp->fdc_secptrack;
switch (chp->fdc_sec_size) {
/* convert logical block numbers to sector numbers */
case 1024:
sctrshft = SCTRSHFT + 1;
blk >>= 1;
ptend >>= 1;
break;
default:
case NBPSCTR:
sctrshft = SCTRSHFT;
break;
case 256:
sctrshft = SCTRSHFT - 1;
blk <<= 1;
ptend <<= 1;
break;
}
/*
* If off the end, limit to actual amount that
* can be transferred.
*/
if ((blk + (bp->b_bcount >> sctrshft)) > ptend)
/* to end of partition */
len = (ptend - blk) << sctrshft;
else
len = bp->b_bcount;
addr = bp->b_un.b_addr; /* data buffer address */
/*
* now we have the real start blk, addr and len for xfer op
*/
while (len != 0) {
/* start cyl of req */
cyl = blk / secpcyl;
bincyl = blk % secpcyl;
/* start head of req */
head = bincyl / chp->fdc_secptrack;
/* start sector of req */
sect = (bincyl % chp->fdc_secptrack) + 1;
/*
* If the desired block and length will go beyond the
* cylinder end, then limit it to the cylinder end.
*/
if (bp->b_flags & B_READ) {
if (len > ((secpcyl - bincyl) << sctrshft))
tlen = (secpcyl - bincyl) << sctrshft;
else
tlen = len;
} else {
if (len >
((chp->fdc_secptrack - sect + 1) <<
sctrshft))
tlen =
(chp->fdc_secptrack - sect + 1) <<
sctrshft;
else
tlen = len;
}
FDERRPRINT(FDEP_L0, FDEM_STRT, (CE_CONT,
" blk 0x%x addr 0x%p len 0x%x "
"cyl %d head %d sec %d\n resid 0x%lx, tlen %d\n",
blk, (void *)addr, len, cyl, head, sect,
bp->b_resid, tlen));
/*
* (try to) do the operation - failure returns an errno
*/
bp->b_error = fjp->fj_ops->fco_rw(fjp, unit,
bp->b_flags & B_READ, cyl, head, sect, addr, tlen);
if (bp->b_error != 0) {
FDERRPRINT(FDEP_L3, FDEM_STRT, (CE_WARN,
"fdstart: bad exec of bp: 0x%p, err=%d",
(void *)bp, bp->b_error));
bp->b_flags |= B_ERROR;
break;
}
blk += tlen >> sctrshft;
len -= tlen;
addr += tlen;
bp->b_resid -= tlen;
}
FDERRPRINT(FDEP_L0, FDEM_STRT,
(CE_CONT, "fdstart done: b_resid %lu, b_count %lu\n",
bp->b_resid, bp->b_bcount));
if (fdp->d_iostat) {
if (bp->b_flags & B_READ) {
KIOSP->reads++;
KIOSP->nread += (bp->b_bcount - bp->b_resid);
} else {
KIOSP->writes++;
KIOSP->nwritten += (bp->b_bcount - bp->b_resid);
}
kstat_runq_exit(KIOSP);
}
bp_mapout(bp);
biodone(bp);
fjp->fj_ops->fco_select(fjp, unit, 0);
mutex_enter(&fjp->fj_lock);
fdp->d_current = 0;
}
fjp->fj_flags ^= FUNIT_BUSY;
}
/* ARGSUSED */
static int
fd_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
int *rval_p)
{
union {
struct dk_cinfo dki;
struct dk_geom dkg;
struct dk_allmap dka;
struct fd_char fdchar;
struct fd_drive drvchar;
int temp;
} cpy;
struct vtoc vtoc;
struct fcu_obj *fjp = NULL;
struct fdisk *fdp = NULL;
struct dk_map *dmp;
struct dk_label *label;
int nblks, part, unit;
int rval = 0;
enum dkio_state state;
unit = fd_getdrive(dev, &fjp, &fdp);
if (!fjp || !fdp)
return (ENXIO);
FDERRPRINT(FDEP_L1, FDEM_IOCT,
(CE_CONT, "fd_ioctl fd unit %d: cmd %x, arg %lx\n",
unit, cmd, arg));
switch (cmd) {
case DKIOCINFO:
fjp->fj_ops->fco_dkinfo(fjp, &cpy.dki);
cpy.dki.dki_cnum = FDCTLR(fjp->fj_unit);
cpy.dki.dki_unit = FDUNIT(fjp->fj_unit);
cpy.dki.dki_partition = PARTITION(dev);
if (ddi_copyout(&cpy.dki, (void *)arg, sizeof (cpy.dki), flag))
rval = EFAULT;
break;
case DKIOCG_PHYGEOM:
case DKIOCG_VIRTGEOM:
cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
goto get_geom;
case DKIOCGGEOM:
if (fjp->fj_flags & FUNIT_LABELOK)
cpy.dkg.dkg_nsect = (fjp->fj_chars->fdc_secptrack *
fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
else
cpy.dkg.dkg_nsect = fjp->fj_chars->fdc_secptrack;
get_geom:
cpy.dkg.dkg_pcyl = fjp->fj_chars->fdc_ncyl;
cpy.dkg.dkg_ncyl = fjp->fj_chars->fdc_ncyl;
cpy.dkg.dkg_nhead = fjp->fj_chars->fdc_nhead;
cpy.dkg.dkg_intrlv = fjp->fj_attr->fda_intrlv;
cpy.dkg.dkg_rpm = fjp->fj_attr->fda_rotatespd;
cpy.dkg.dkg_read_reinstruct =
(int)(cpy.dkg.dkg_nsect * cpy.dkg.dkg_rpm * 4) / 60000;
cpy.dkg.dkg_write_reinstruct = cpy.dkg.dkg_read_reinstruct;
if (ddi_copyout(&cpy.dkg, (void *)arg, sizeof (cpy.dkg), flag))
rval = EFAULT;
break;
case DKIOCSGEOM:
if (ddi_copyin((void *)arg, &cpy.dkg,
sizeof (struct dk_geom), flag)) {
rval = EFAULT;
break;
}
mutex_enter(&fjp->fj_lock);
fjp->fj_chars->fdc_ncyl = cpy.dkg.dkg_ncyl;
fjp->fj_chars->fdc_nhead = cpy.dkg.dkg_nhead;
fjp->fj_chars->fdc_secptrack = cpy.dkg.dkg_nsect;
fjp->fj_attr->fda_intrlv = cpy.dkg.dkg_intrlv;
fjp->fj_attr->fda_rotatespd = cpy.dkg.dkg_rpm;
fdp->d_curfdtype = -1;
mutex_exit(&fjp->fj_lock);
break;
/*
* return the map of all logical partitions
*/
case DKIOCGAPART:
/*
* Note the conversion from starting sector number
* to starting cylinder number.
* Return error if division results in a remainder.
*/
nblks = fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct dk_allmap32 dka32;
for (part = 0; part < NDKMAP; part++) {
if ((fdp->d_part[part].p_start % nblks) != 0)
return (EINVAL);
dka32.dka_map[part].dkl_cylno =
fdp->d_part[part].p_start / nblks;
dka32.dka_map[part].dkl_nblk =
fdp->d_part[part].p_size;
}
if (ddi_copyout(&dka32, (void *)arg,
sizeof (struct dk_allmap32), flag))
rval = EFAULT;
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
dmp = (struct dk_map *)&cpy.dka;
for (part = 0; part < NDKMAP; part++) {
if ((fdp->d_part[part].p_start % nblks) != 0)
return (EINVAL);
dmp->dkl_cylno =
fdp->d_part[part].p_start / nblks;
dmp->dkl_nblk = fdp->d_part[part].p_size;
dmp++;
}
if (ddi_copyout(&cpy.dka, (void *)arg,
sizeof (struct dk_allmap), flag))
rval = EFAULT;
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
break;
/*
* Set the map of all logical partitions
*/
case DKIOCSAPART:
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct dk_allmap32 dka32;
if (ddi_copyin((void *)arg, &dka32,
sizeof (dka32), flag)) {
rval = EFAULT;
break;
}
for (part = 0; part < NDKMAP; part++) {
cpy.dka.dka_map[part].dkl_cylno =
dka32.dka_map[part].dkl_cylno;
cpy.dka.dka_map[part].dkl_nblk =
dka32.dka_map[part].dkl_nblk;
}
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
if (ddi_copyin((void *)arg, &cpy.dka, sizeof (cpy.dka), flag))
rval = EFAULT;
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
if (rval != 0)
break;
dmp = (struct dk_map *)&cpy.dka;
nblks = fjp->fj_chars->fdc_nhead *
fjp->fj_chars->fdc_secptrack;
mutex_enter(&fjp->fj_lock);
/*
* Note the conversion from starting cylinder number
* to starting sector number.
*/
for (part = 0; part < NDKMAP; part++) {
fdp->d_part[part].p_start = dmp->dkl_cylno *
nblks;
fdp->d_part[part].p_size = dmp->dkl_nblk;
dmp++;
}
mutex_exit(&fjp->fj_lock);
break;
case DKIOCGVTOC:
mutex_enter(&fjp->fj_lock);
/*
* Exit if the diskette has no label.
* Also, get the label to make sure the correct one is
* being used since the diskette may have changed
*/
fjp->fj_ops->fco_select(fjp, unit, 1);
rval = fdgetlabel(fjp, unit);
fjp->fj_ops->fco_select(fjp, unit, 0);
if (rval) {
mutex_exit(&fjp->fj_lock);
rval = EINVAL;
break;
}
fd_build_user_vtoc(fjp, fdp, &vtoc);
mutex_exit(&fjp->fj_lock);
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct vtoc32 vtoc32;
vtoctovtoc32(vtoc, vtoc32);
if (ddi_copyout(&vtoc32, (void *)arg,
sizeof (vtoc32), flag))
rval = EFAULT;
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
if (ddi_copyout(&vtoc, (void *)arg,
sizeof (vtoc), flag))
rval = EFAULT;
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
break;
case DKIOCSVTOC:
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct vtoc32 vtoc32;
if (ddi_copyin((void *)arg, &vtoc32,
sizeof (vtoc32), flag)) {
rval = EFAULT;
break;
}
vtoc32tovtoc(vtoc32, vtoc);
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
if (ddi_copyin((void *)arg, &vtoc, sizeof (vtoc), flag))
rval = EFAULT;
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
if (rval != 0)
break;
label = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
mutex_enter(&fjp->fj_lock);
if ((rval = fd_build_label_vtoc(fjp, fdp, &vtoc, label)) == 0) {
fjp->fj_ops->fco_select(fjp, unit, 1);
rval = fjp->fj_ops->fco_rw(fjp, unit, FDWRITE,
0, 0, 1, (caddr_t)label, sizeof (struct dk_label));
fjp->fj_ops->fco_select(fjp, unit, 0);
}
mutex_exit(&fjp->fj_lock);
kmem_free(label, sizeof (struct dk_label));
break;
case DKIOCSTATE:
FDERRPRINT(FDEP_L1, FDEM_IOCT,
(CE_CONT, "fd_ioctl fd unit %d: DKIOCSTATE\n", unit));
if (ddi_copyin((void *)arg, &state, sizeof (int), flag)) {
rval = EFAULT;
break;
}
rval = fd_check_media(dev, state);
if (ddi_copyout(&fdp->d_media_state, (void *)arg,
sizeof (int), flag))
rval = EFAULT;
break;
case FDIOGCHAR:
if (ddi_copyout(fjp->fj_chars, (void *)arg,
sizeof (struct fd_char), flag))
rval = EFAULT;
break;
case FDIOSCHAR:
if (ddi_copyin((void *)arg, &cpy.fdchar,
sizeof (struct fd_char), flag)) {
rval = EFAULT;
break;
}
switch (cpy.fdchar.fdc_transfer_rate) {
case 417:
if ((fdp->d_media & (1 << FMT_3M)) == 0) {
cmn_err(CE_CONT,
"fdioschar:Medium density not supported\n");
rval = EINVAL;
break;
}
mutex_enter(&fjp->fj_lock);
fjp->fj_attr->fda_rotatespd = 360;
mutex_exit(&fjp->fj_lock);
/* cpy.fdchar.fdc_transfer_rate = 500; */
/* FALLTHROUGH */
case 1000:
case 500:
case 300:
case 250:
mutex_enter(&fjp->fj_lock);
*(fjp->fj_chars) = cpy.fdchar;
fdp->d_curfdtype = -1;
fjp->fj_flags &= ~FUNIT_CHAROK;
mutex_exit(&fjp->fj_lock);
break;
default:
FDERRPRINT(FDEP_L4, FDEM_IOCT,
(CE_WARN, "fd_ioctl fd unit %d: FDIOSCHAR odd "
"xfer rate %dkbs",
unit, cpy.fdchar.fdc_transfer_rate));
rval = EINVAL;
break;
}
break;
/*
* set all characteristics and geometry to the defaults
*/
case FDDEFGEOCHAR:
mutex_enter(&fjp->fj_lock);
fdp->d_curfdtype = fdp->d_deffdtype;
*fjp->fj_chars = *defchar[fdp->d_curfdtype];
*fjp->fj_attr = fdtypes[fdp->d_curfdtype];
bcopy(fdparts[fdp->d_curfdtype],
fdp->d_part, sizeof (struct partition) * NDKMAP);
fjp->fj_flags &= ~FUNIT_CHAROK;
mutex_exit(&fjp->fj_lock);
break;
case FDEJECT: /* eject disk */
case DKIOCEJECT:
fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
rval = ENOSYS;
break;
case FDGETCHANGE: /* disk changed */
if (ddi_copyin((void *)arg, &cpy.temp, sizeof (int), flag)) {
rval = EFAULT;
break;
}
mutex_enter(&fjp->fj_lock);
fjp->fj_ops->fco_select(fjp, unit, 1);
if (fjp->fj_flags & FUNIT_CHANGED)
cpy.temp |= FDGC_HISTORY;
else
cpy.temp &= ~FDGC_HISTORY;
fjp->fj_flags &= ~FUNIT_CHANGED;
if (fjp->fj_ops->fco_getchng(fjp, unit)) {
cpy.temp |= FDGC_DETECTED;
fjp->fj_ops->fco_resetchng(fjp, unit);
/*
* check diskette again only if it was removed
*/
if (fjp->fj_ops->fco_getchng(fjp, unit)) {
/*
* no diskette is present
*/
cpy.temp |= FDGC_CURRENT;
if (fjp->fj_flags & FUNIT_CHGDET)
/*
* again no diskette; not a new change
*/
cpy.temp ^= FDGC_DETECTED;
else
fjp->fj_flags |= FUNIT_CHGDET;
} else {
/*
* a new diskette is present
*/
cpy.temp &= ~FDGC_CURRENT;
fjp->fj_flags &= ~FUNIT_CHGDET;
}
} else {
cpy.temp &= ~(FDGC_DETECTED | FDGC_CURRENT);
fjp->fj_flags &= ~FUNIT_CHGDET;
}
/*
* also get state of write protection
*/
if (fjp->fj_flags & FUNIT_WPROT) {
cpy.temp |= FDGC_CURWPROT;
} else {
cpy.temp &= ~FDGC_CURWPROT;
}
fjp->fj_ops->fco_select(fjp, unit, 0);
mutex_exit(&fjp->fj_lock);
if (ddi_copyout(&cpy.temp, (void *)arg, sizeof (int), flag))
rval = EFAULT;
break;
case FDGETDRIVECHAR:
if (ddi_copyout(fjp->fj_drive, (void *)arg,
sizeof (struct fd_drive), flag))
rval = EFAULT;
break;
case FDSETDRIVECHAR:
if (ddi_copyin((void *)arg, &cpy.drvchar,
sizeof (struct fd_drive), flag)) {
rval = EFAULT;
break;
}
mutex_enter(&fjp->fj_lock);
*(fjp->fj_drive) = cpy.drvchar;
fdp->d_curfdtype = -1;
fjp->fj_flags &= ~FUNIT_CHAROK;
mutex_exit(&fjp->fj_lock);
break;
case DKIOCREMOVABLE: {
int i = 1;
/* no brainer: floppies are always removable */
if (ddi_copyout(&i, (void *)arg, sizeof (int), flag)) {
rval = EFAULT;
}
break;
}
case DKIOCGMEDIAINFO:
rval = fd_get_media_info(fjp, (caddr_t)arg, flag);
break;
case FDIOCMD:
{
struct fd_cmd fc;
int cyl, head, spc, spt;
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(flag & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct fd_cmd32 fc32;
if (ddi_copyin((void *)arg, &fc32,
sizeof (fc32), flag)) {
rval = EFAULT;
break;
}
fc.fdc_cmd = fc32.fdc_cmd;
fc.fdc_flags = fc32.fdc_flags;
fc.fdc_blkno = fc32.fdc_blkno;
fc.fdc_secnt = fc32.fdc_secnt;
fc.fdc_bufaddr = (caddr_t)(uintptr_t)fc32.fdc_bufaddr;
fc.fdc_buflen = fc32.fdc_buflen;
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
if (ddi_copyin((void *)arg, &fc, sizeof (fc), flag)) {
rval = EFAULT;
break;
}
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
if (rval != 0)
break;
if (fc.fdc_cmd == FDCMD_READ || fc.fdc_cmd == FDCMD_WRITE) {
auto struct iovec aiov;
auto struct uio auio;
struct uio *uio = &auio;
spc = (fc.fdc_cmd == FDCMD_READ)? B_READ: B_WRITE;
bzero(&auio, sizeof (struct uio));
bzero(&aiov, sizeof (struct iovec));
aiov.iov_base = fc.fdc_bufaddr;
aiov.iov_len = (uint_t)fc.fdc_secnt *
fjp->fj_chars->fdc_sec_size;
uio->uio_iov = &aiov;
uio->uio_iovcnt = 1;
uio->uio_resid = aiov.iov_len;
uio->uio_segflg = UIO_USERSPACE;
rval = physio(fd_strategy, (struct buf *)0, dev,
spc, minphys, uio);
break;
} else if (fc.fdc_cmd == FDCMD_FORMAT_TRACK) {
spt = fjp->fj_chars->fdc_secptrack; /* sec/trk */
spc = fjp->fj_chars->fdc_nhead * spt; /* sec/cyl */
cyl = fc.fdc_blkno / spc;
head = (fc.fdc_blkno % spc) / spt;
if ((cyl | head) == 0)
fjp->fj_flags &=
~(FUNIT_LABELOK | FUNIT_UNLABELED);
FDERRPRINT(FDEP_L0, FDEM_FORM,
(CE_CONT, "fd_format cyl %d, hd %d\n", cyl, head));
fjp->fj_ops->fco_select(fjp, unit, 1);
rval = fjp->fj_ops->fco_format(fjp, unit, cyl, head,
(int)fc.fdc_flags);
fjp->fj_ops->fco_select(fjp, unit, 0);
break;
}
FDERRPRINT(FDEP_L4, FDEM_IOCT,
(CE_WARN, "fd_ioctl fd unit %d: FDIOCSCMD not yet complete",
unit));
rval = EINVAL;
break;
}
case FDRAW:
rval = fd_rawioctl(fjp, unit, (caddr_t)arg, flag);
break;
default:
FDERRPRINT(FDEP_L4, FDEM_IOCT,
(CE_WARN, "fd_ioctl fd unit %d: invalid ioctl 0x%x",
unit, cmd));
rval = ENOTTY;
break;
}
return (rval);
}
static void
fd_build_user_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp)
{
struct partition *vpart;
int i;
int xblk;
/*
* Return vtoc structure fields in the provided VTOC area, addressed
* by *vtocp.
*
*/
bzero(vtocp, sizeof (struct vtoc));
bcopy(fdp->d_vtoc_bootinfo,
vtocp->v_bootinfo, sizeof (vtocp->v_bootinfo));
vtocp->v_sanity = VTOC_SANE;
vtocp->v_version = fdp->d_vtoc_version;
bcopy(fdp->d_vtoc_volume, vtocp->v_volume, LEN_DKL_VVOL);
if (fjp->fj_flags & FUNIT_LABELOK) {
vtocp->v_sectorsz = DEV_BSIZE;
xblk = 1;
} else {
vtocp->v_sectorsz = fjp->fj_chars->fdc_sec_size;
xblk = vtocp->v_sectorsz / DEV_BSIZE;
}
vtocp->v_nparts = 3; /* <= NDKMAP; */
/*
* Copy partitioning information.
*/
bcopy(fdp->d_part, vtocp->v_part, sizeof (struct partition) * NDKMAP);
for (i = NDKMAP, vpart = vtocp->v_part; i && (xblk > 1); i--, vpart++) {
/* correct partition info if sector size > 512 bytes */
vpart->p_start /= xblk;
vpart->p_size /= xblk;
}
bcopy(fdp->d_vtoc_timestamp,
vtocp->timestamp, sizeof (fdp->d_vtoc_timestamp));
bcopy(fdp->d_vtoc_asciilabel, vtocp->v_asciilabel, LEN_DKL_ASCII);
}
static int
fd_build_label_vtoc(struct fcu_obj *fjp, struct fdisk *fdp, struct vtoc *vtocp,
struct dk_label *labelp)
{
struct partition *vpart;
int i;
int nblks;
int ncyl;
ushort_t sum, *sp;
/*
* Sanity-check the vtoc
*/
if (vtocp->v_sanity != VTOC_SANE ||
vtocp->v_nparts > NDKMAP || vtocp->v_nparts <= 0) {
FDERRPRINT(FDEP_L3, FDEM_IOCT,
(CE_WARN, "fd_build_label: sanity check on vtoc failed"));
return (EINVAL);
}
/*
* before copying the vtoc, the partition information in it should be
* checked against the information the driver already has on the
* diskette.
*/
nblks = (fjp->fj_chars->fdc_nhead * fjp->fj_chars->fdc_secptrack *
fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
if (nblks == 0 || fjp->fj_chars->fdc_ncyl == 0)
return (EFAULT);
vpart = vtocp->v_part;
/*
* Check the partition information in the vtoc. The starting sectors
* must lie along cylinder boundaries. (NDKMAP entries are checked
* to ensure that the unused entries are set to 0 if vtoc->v_nparts
* is less than NDKMAP)
*/
for (i = NDKMAP; i; i--) {
if ((vpart->p_start % nblks) != 0) {
return (EINVAL);
}
ncyl = vpart->p_start / nblks;
ncyl += vpart->p_size / nblks;
if ((vpart->p_size % nblks) != 0)
ncyl++;
if (ncyl > (long)fjp->fj_chars->fdc_ncyl) {
return (EINVAL);
}
vpart++;
}
bcopy(vtocp->v_bootinfo, fdp->d_vtoc_bootinfo,
sizeof (vtocp->v_bootinfo));
fdp->d_vtoc_version = vtocp->v_version;
bcopy(vtocp->v_volume, fdp->d_vtoc_volume, LEN_DKL_VVOL);
/*
* Copy partitioning information.
*/
bcopy(vtocp->v_part, fdp->d_part, sizeof (struct partition) * NDKMAP);
bcopy(vtocp->timestamp, fdp->d_vtoc_timestamp,
sizeof (fdp->d_vtoc_timestamp));
bcopy(vtocp->v_asciilabel, fdp->d_vtoc_asciilabel, LEN_DKL_ASCII);
/*
* construct the diskette label in supplied buffer
*/
/* Put appropriate vtoc structure fields into the disk label */
labelp->dkl_vtoc.v_bootinfo[0] = (uint32_t)vtocp->v_bootinfo[0];
labelp->dkl_vtoc.v_bootinfo[1] = (uint32_t)vtocp->v_bootinfo[1];
labelp->dkl_vtoc.v_bootinfo[2] = (uint32_t)vtocp->v_bootinfo[2];
labelp->dkl_vtoc.v_sanity = vtocp->v_sanity;
labelp->dkl_vtoc.v_version = vtocp->v_version;
bcopy(vtocp->v_volume, labelp->dkl_vtoc.v_volume, LEN_DKL_VVOL);
labelp->dkl_vtoc.v_nparts = vtocp->v_nparts;
bcopy(vtocp->v_reserved, labelp->dkl_vtoc.v_reserved,
sizeof (labelp->dkl_vtoc.v_reserved));
for (i = 0; i < (int)vtocp->v_nparts; i++) {
labelp->dkl_vtoc.v_part[i].p_tag = vtocp->v_part[i].p_tag;
labelp->dkl_vtoc.v_part[i].p_flag = vtocp->v_part[i].p_flag;
labelp->dkl_vtoc.v_part[i].p_start = vtocp->v_part[i].p_start;
labelp->dkl_vtoc.v_part[i].p_size = vtocp->v_part[i].p_size;
}
for (i = 0; i < NDKMAP; i++) {
labelp->dkl_vtoc.v_timestamp[i] = vtocp->timestamp[i];
}
bcopy(vtocp->v_asciilabel, labelp->dkl_asciilabel, LEN_DKL_ASCII);
labelp->dkl_pcyl = fjp->fj_chars->fdc_ncyl;
labelp->dkl_ncyl = fjp->fj_chars->fdc_ncyl;
labelp->dkl_nhead = fjp->fj_chars->fdc_nhead;
/*
* The fdc_secptrack field of the fd_char structure is the number
* of sectors per track where the sectors are fdc_sec_size.
* The dkl_nsect field of the dk_label structure is the number of
* DEV_BSIZE (512) byte sectors per track.
*/
labelp->dkl_nsect = (fjp->fj_chars->fdc_secptrack *
fjp->fj_chars->fdc_sec_size) / DEV_BSIZE;
labelp->dkl_intrlv = fjp->fj_attr->fda_intrlv;
labelp->dkl_rpm = fjp->fj_attr->fda_rotatespd;
labelp->dkl_read_reinstruct =
(int)(labelp->dkl_nsect * labelp->dkl_rpm * 4) / 60000;
labelp->dkl_write_reinstruct = labelp->dkl_read_reinstruct;
labelp->dkl_magic = DKL_MAGIC;
sum = 0;
labelp->dkl_cksum = 0;
sp = (ushort_t *)labelp;
while (sp < &(labelp->dkl_cksum)) {
sum ^= *sp++;
}
labelp->dkl_cksum = sum;
return (0);
}
static int
fd_rawioctl(struct fcu_obj *fjp, int unit, caddr_t arg, int mode)
{
struct fd_raw fdr;
char *arg_result = NULL;
int flag = B_READ;
int rval = 0;
caddr_t uaddr;
uint_t ucount;
FDERRPRINT(FDEP_L1, FDEM_RAWI,
(CE_CONT, "fd_rawioctl: cmd[0]=0x%x\n", fdr.fdr_cmd[0]));
if (fjp->fj_chars->fdc_medium != 3 && fjp->fj_chars->fdc_medium != 5) {
cmn_err(CE_CONT, "fd_rawioctl: Medium density not supported\n");
return (ENXIO);
}
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct fd_raw32 fdr32;
if (ddi_copyin(arg, &fdr32, sizeof (fdr32), mode))
return (EFAULT);
bcopy(fdr32.fdr_cmd, fdr.fdr_cmd, sizeof (fdr.fdr_cmd));
fdr.fdr_cnum = fdr32.fdr_cnum;
fdr.fdr_nbytes = fdr32.fdr_nbytes;
fdr.fdr_addr = (caddr_t)(uintptr_t)fdr32.fdr_addr;
arg_result = ((struct fd_raw32 *)arg)->fdr_result;
break;
}
case DDI_MODEL_NONE:
#endif /* ! _MULTI_DATAMODEL */
if (ddi_copyin(arg, &fdr, sizeof (fdr), mode))
return (EFAULT);
arg_result = ((struct fd_raw *)arg)->fdr_result;
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
/*
* copy user address & nbytes from raw_req so that we can
* put kernel address in req structure
*/
uaddr = fdr.fdr_addr;
ucount = (uint_t)fdr.fdr_nbytes;
unit &= 3;
switch (fdr.fdr_cmd[0] & 0x0f) {
case FDRAW_FORMAT:
ucount += 16;
fdr.fdr_addr = kmem_zalloc(ucount, KM_SLEEP);
if (ddi_copyin(uaddr, fdr.fdr_addr,
(size_t)fdr.fdr_nbytes, mode)) {
kmem_free(fdr.fdr_addr, ucount);
return (EFAULT);
}
if ((*fdr.fdr_addr | fdr.fdr_addr[1]) == 0)
fjp->fj_flags &= ~(FUNIT_LABELOK | FUNIT_UNLABELED);
flag = B_WRITE;
fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
break;
case FDRAW_WRCMD:
case FDRAW_WRITEDEL:
flag = B_WRITE;
/* FALLTHROUGH */
case FDRAW_RDCMD:
case FDRAW_READDEL:
case FDRAW_READTRACK:
if (ucount) {
/*
* In SunOS 4.X, we used to as_fault things in.
* We really cannot do this in 5.0/SVr4. Unless
* someone really believes that speed is of the
* essence here, it is just much simpler to do
* this in kernel space and use copyin/copyout.
*/
fdr.fdr_addr = kmem_alloc((size_t)ucount, KM_SLEEP);
if (flag == B_WRITE) {
if (ddi_copyin(uaddr, fdr.fdr_addr, ucount,
mode)) {
kmem_free(fdr.fdr_addr, ucount);
return (EFAULT);
}
}
} else
return (EINVAL);
fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
break;
case FDRAW_READID:
case FDRAW_REZERO:
case FDRAW_SEEK:
case FDRAW_SENSE_DRV:
ucount = 0;
fdr.fdr_cmd[1] = (fdr.fdr_cmd[1] & ~3) | unit;
break;
case FDRAW_SPECIFY:
fdr.fdr_cmd[2] &= 0xfe; /* keep NoDMA bit clear */
/* FALLTHROUGH */
case FDRAW_SENSE_INT:
ucount = 0;
break;
default:
return (EINVAL);
}
/*
* Note that we ignore any error returns from controller
* This is the way the driver has been, and it may be
* that the raw ioctl senders simply don't want to
* see any errors returned in this fashion.
*/
fjp->fj_ops->fco_select(fjp, unit, 1);
rval = fjp->fj_ops->fco_rwioctl(fjp, unit, (caddr_t)&fdr);
if (ucount && flag == B_READ && rval == 0) {
if (ddi_copyout(fdr.fdr_addr, uaddr, ucount, mode)) {
rval = EFAULT;
}
}
if (ddi_copyout(fdr.fdr_result, arg_result, sizeof (fdr.fdr_cmd), mode))
rval = EFAULT;
fjp->fj_ops->fco_select(fjp, unit, 0);
if (ucount)
kmem_free(fdr.fdr_addr, ucount);
return (rval);
}
/*
* property operation routine. return the number of blocks for the partition
* in question or forward the request to the property facilities.
*/
static int
fd_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
char *name, caddr_t valuep, int *lengthp)
{
struct fcu_obj *fjp = NULL;
struct fdisk *fdp = NULL;
uint64_t nblocks64;
FDERRPRINT(FDEP_L1, FDEM_PROP,
(CE_CONT, "fd_prop_op: dip %p %s\n", (void *)dip, name));
/*
* Our dynamic properties are all device specific and size oriented.
* Requests issued under conditions where size is valid are passed
* to ddi_prop_op_nblocks with the size information, otherwise the
* request is passed to ddi_prop_op.
*/
if (dev == DDI_DEV_T_ANY) {
pass: return (ddi_prop_op(dev, dip, prop_op, mod_flags,
name, valuep, lengthp));
} else {
/*
* Ignoring return value because success is checked by
* verifying fjp and fdp and returned unit value is not used.
*/
(void) fd_getdrive(dev, &fjp, &fdp);
if (!fjp || !fdp)
goto pass;
/* get nblocks value */
nblocks64 = (ulong_t)fdp->d_part[PARTITION(dev)].p_size;
return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
name, valuep, lengthp, nblocks64));
}
}
static void
fd_media_watch(void *arg)
{
struct fcu_obj *fjp;
struct fdisk *fdp;
#ifdef DEBUG
int unit;
#define DEBUG_ASSIGN unit=
#else
#define DEBUG_ASSIGN (void)
#endif
DEBUG_ASSIGN fd_getdrive((dev_t)arg, &fjp, &fdp);
/*
* Ignoring return in non DEBUG mode because device exist.
* Returned unit value is not used.
*/
FDERRPRINT(FDEP_L0, FDEM_IOCT,
(CE_CONT, "fd_media_watch unit %d\n", unit));
/*
* fd_get_media_state() cannot be called from this timeout function
* because the floppy drive has to be selected first, and that could
* force this function to sleep (while waiting for the select
* semaphore).
* Instead, just wakeup up driver.
*/
mutex_enter(&fjp->fj_lock);
cv_broadcast(&fdp->d_statecv);
mutex_exit(&fjp->fj_lock);
}
enum dkio_state
fd_get_media_state(struct fcu_obj *fjp, int unit)
{
enum dkio_state state;
if (fjp->fj_ops->fco_getchng(fjp, unit)) {
/* recheck disk only if DSKCHG "high" */
fjp->fj_ops->fco_resetchng(fjp, unit);
if (fjp->fj_ops->fco_getchng(fjp, unit)) {
if (fjp->fj_flags & FUNIT_CHGDET) {
/*
* again no diskette; not a new change
*/
state = DKIO_NONE;
} else {
/*
* a new change; diskette was ejected
*/
fjp->fj_flags |= FUNIT_CHGDET;
state = DKIO_EJECTED;
}
} else {
fjp->fj_flags &= ~FUNIT_CHGDET;
state = DKIO_INSERTED;
}
} else {
fjp->fj_flags &= ~FUNIT_CHGDET;
state = DKIO_INSERTED;
}
FDERRPRINT(FDEP_L0, FDEM_IOCT,
(CE_CONT, "fd_get_media_state unit %d: state %x\n", unit, state));
return (state);
}
static int
fd_check_media(dev_t dev, enum dkio_state state)
{
struct fcu_obj *fjp;
struct fdisk *fdp;
int unit;
int err;
unit = fd_getdrive(dev, &fjp, &fdp);
mutex_enter(&fjp->fj_lock);
fjp->fj_ops->fco_select(fjp, unit, 1);
fdp->d_media_state = fd_get_media_state(fjp, unit);
fdp->d_media_timeout = drv_usectohz(fd_check_media_time);
while (fdp->d_media_state == state) {
/* release the controller and drive */
fjp->fj_ops->fco_select(fjp, unit, 0);
/* turn on timer */
fdp->d_media_timeout_id = timeout(fd_media_watch,
(void *)dev, fdp->d_media_timeout);
if (cv_wait_sig(&fdp->d_statecv, &fjp->fj_lock) == 0) {
fdp->d_media_timeout = 0;
mutex_exit(&fjp->fj_lock);
return (EINTR);
}
fjp->fj_ops->fco_select(fjp, unit, 1);
fdp->d_media_state = fd_get_media_state(fjp, unit);
}
if (fdp->d_media_state == DKIO_INSERTED) {
err = fdgetlabel(fjp, unit);
if (err) {
fjp->fj_ops->fco_select(fjp, unit, 0);
mutex_exit(&fjp->fj_lock);
return (EIO);
}
}
fjp->fj_ops->fco_select(fjp, unit, 0);
mutex_exit(&fjp->fj_lock);
return (0);
}
/*
* fd_get_media_info :
* Collects medium information for
* DKIOCGMEDIAINFO ioctl.
*/
static int
fd_get_media_info(struct fcu_obj *fjp, caddr_t buf, int flag)
{
struct dk_minfo media_info;
int err = 0;
media_info.dki_media_type = DK_FLOPPY;
media_info.dki_lbsize = fjp->fj_chars->fdc_sec_size;
media_info.dki_capacity = fjp->fj_chars->fdc_ncyl *
fjp->fj_chars->fdc_secptrack * fjp->fj_chars->fdc_nhead;
if (ddi_copyout(&media_info, buf, sizeof (struct dk_minfo), flag))
err = EFAULT;
return (err);
}