/*
* 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
* 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/autoconf.h>
#include <sys/ddidmareq.h>
#include <sys/fd_debug.h>
/*
* Local Function Prototypes
*/
static int fd_unit_is_open(struct fdisk *);
static int fdgetlabel(struct fcu_obj *, int);
struct vtoc *);
static void fd_media_watch(void *);
static int fd_strategy(struct buf *);
caddr_t, int *);
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 */
};
static int fd_probe(dev_info_t *);
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
*/
/*
* error handling
*
* for debugging,
* set fderrlevel to 1
* set fderrmask to 224 or 644
*/
#ifdef DEBUG
#endif
static struct driver_minor_data {
char *name;
int minor;
int type;
} fd_minor [] = {
{ "a", 0, S_IFBLK},
{ "a,raw", 0, S_IFCHR},
{0}
};
&mod_driverops, /* Type of module. This one is a driver */
"Floppy Disk driver", /* Name of the module. */
&fd_ops, /* driver ops */
};
};
int
_init(void)
{
int retval;
return (retval);
return (retval);
}
int
_fini(void)
{
int retval;
return (retval);
return (retval);
}
int
{
}
static int
{
if (fdpp) {
if (*fjpp)
}
}
return (-1);
}
/*ARGSUSED*/
static int
{
int rval;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
/*
* Ignoring return value because success is checked by
* verifying fjp and fdp and returned unit value is not used.
*/
rval = DDI_SUCCESS;
} else
rval = DDI_FAILURE;
break;
case DDI_INFO_DEVT2INSTANCE:
rval = DDI_SUCCESS;
break;
default:
rval = DDI_FAILURE;
}
return (rval);
}
#ifdef CMOS_CONF_MEM
#endif /* CMOS_CONF_MEM */
static int
{
#ifdef CMOS_CONF_MEM
int cmos;
int drive_type;
#endif /* CMOS_CONF_MEM */
int drive_size;
int len;
int unit_num;
fderrlevel = debug[0];
#ifdef DEBUG
#endif
}
return (DDI_PROBE_FAILURE);
}
#ifdef CMOS_CONF_MEM
/* get the cmos memory values quick and dirty */
#endif /* CMOS_CONF_MEM */
switch (unit_num) {
#ifdef CMOS_CONF_MEM
case 0:
/* FALLTHROUGH */
case 1:
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 */
(CE_WARN,
"fd_probe failed density: dip %p unit %d",
return (DDI_PROBE_FAILURE);
}
len = sizeof (drive_size);
(CE_WARN, "fd_probe failed size: dip %p unit %d",
return (DDI_PROBE_FAILURE);
}
}
return (DDI_PROBE_SUCCESS);
}
/* ARGSUSED */
static int
{
int mode_3D;
#ifdef CMOS_CONF_MEM
int cmos;
#endif /* CMOS_CONF_MEM */
int unit_num;
switch (cmd) {
case DDI_ATTACH:
return (DDI_FAILURE);
}
#ifdef CMOS_CONF_MEM
#endif /* CMOS_CONF_MEM */
switch (unit_num) {
#ifdef CMOS_CONF_MEM
case 0:
/* FALLTHROUGH */
case 1:
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;
density[0] = '\0';
len = sizeof (drive_size);
drive_size = 0;
if (drive_size == 5)
drive_type = 1;
else if (drive_size == 3)
drive_type = 3;
if (drive_size == 5)
drive_type = 2;
else if (drive_size == 3)
drive_type = 4;
drive_size == 3) {
drive_type = 6;
}
break;
}
if (drive_type == 0) {
(CE_WARN, "fd_attach failed type: dip %p unit %d",
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/*
* set default floppy drive characteristics & geometry
*/
switch (drive_type) { /* assume doubled sided */
case 2: /* 5.25 high density */
break;
case 4: /* 3.5 high density */
mode_3D = 0;
/*
* 3D mode should be enabled only if a dual-
* speed 3.5" high-density drive and a
* supported floppy controller are installed.
*/
break;
case 1: /* 5.25 double density */
1<<FMT_5D16;
break;
case 3: /* 3.5 double density */
break;
case 5: /* 3.5 extended density */
case 6:
case 7:
1<<FMT_3D;
break;
case 0: /* no drive defined */
default:
goto no_attach;
}
== DDI_FAILURE) {
goto no_attach;
}
}
(CE_WARN, "fd_attach: dip %p unit %d",
}
/*
* Add a zero-length attribute to tell the world we support
* kernel ioctls (for layered drivers)
*/
DDI_KERNEL_IOCTL, NULL, 0);
/*
* refuse to suspend when pcfs is mounted.
*/
"pm-hardware-state", "needs-suspend-resume");
/*
* Ignoring return value because, for passed arguments, only
* DDI_SUCCESS is returned.
*/
return (DDI_SUCCESS);
case DDI_RESUME:
/* nothing for us to do */
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
(CE_WARN, "fd_attach failed: dip %p unit %d",
return (DDI_FAILURE);
}
/* ARGSUSED */
static int
{
int drive_num;
(void *)dip));
return (rval);
switch (cmd) {
case DDI_DETACH:
if (fd_unit_is_open(fdp)) {
rval = DDI_FAILURE;
break;
}
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
* potential filesytem corruption from pilot error.
* Given the decreasing popularity of floppy media, we
* don't see this as much of a limitation.
*/
"Unable to suspend while floppy is in use.");
rval = DDI_FAILURE;
}
break;
default:
rval = DDI_FAILURE;
break;
}
return (rval);
}
static int
{
int i;
for (i = 0; i < (OTYPCNT - 1); i++)
return (1);
return (0);
}
static int
{
int i;
for (i = 0; i < NDKMAP; i++)
return (1);
for (i = 0; i < (OTYPCNT - 1); i++)
return (1);
return (0);
}
/*ARGSUSED*/
static int
{
int part_is_open;
int rval;
return (ENXIO);
/*
*/
/*
* 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.
*/
} else {
}
"fd_open: exclparts %lx openparts %lx lyrcnt %lx pbit %x\n",
pbit));
return (EBUSY);
}
/*
* Ensure that drive is recalibrated on first open of new diskette.
*/
return (ENXIO);
}
}
/* don't attempt access, just return successfully */
goto out;
}
/*
*/
if (rval) {
/* didn't find label (couldn't read anything) */
return (EIO);
}
/* check partition */
return (ENXIO);
}
/*
* if opening for writing, check write protect on diskette
*/
return (EROFS);
}
out:
/*
* mark open as having succeeded
*/
else
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
{
char *newlabel;
short *sp;
short count;
short xsum;
int rval;
short oldlvl;
int i;
/*
* get some space to play with the label
*/
"fdgetlabel fd unit %d kmem_zalloc: ptr = %p, size = %lx\n",
/*
* 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;
/*
* 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
*/
if (try_this) {
/* try reading last sector of cyl 1, head 0 */
sizeof (struct dk_label))) &&
/* and last sector plus 1 of cylinder 1 */
sizeof (struct dk_label)) &&
/* and label sector on cylinder 0 */
sizeof (struct dk_label))))
break;
break;
}
/*
* try the next entry in the characteristics tbl
*/
/*
* check for a double_density diskette
* in a high_density 5.25" drive
*/
/*
* yes - adjust transfer rate since we don't
* know if we have a 5.25" dual-speed drive
*/
}
/* yes - adjust steps per cylinder */
} else
try_this = 1;
} else
try_this = 0;
}
if (rval) {
goto out; /* couldn't read anything */
}
(CE_CONT,
"fdgetlabel fd unit=%d ncyl=%d nsct=%d step=%d rpm=%d intlv=%d\n",
/*
* _something_ was read - look for unixtype label
*/
/* not a label - no magic number */
goto nolabel; /* no errors, but no label */
}
xsum = 0;
while (count--)
if (xsum) {
/* not a label - checksum didn't compute */
goto nolabel; /* no errors, but no label */
}
/*
* the SunOS label overrides current diskette characteristics
*/
else
/*
* logical partitions
*/
for (i = 0; i < NDKMAP; i++) {
}
goto out;
/*
* if not found, fill in label info from default (mark default used)
*/
else /* if (fdp->d_media & (1<<FMT_5D9)) */
out:
return (rval);
}
/*ARGSUSED*/
static int
{
#ifdef DEBUG
int unit;
#else
#define DEBUG_ASSIGN (void)
#endif
/*
* Ignoring return in non DEBUG mode because success is checked by
* verifying fjp and fdp and returned unit value is not used.
*/
return (ENXIO);
(CE_CONT, "fd_close: fd unit %d part %d otype %x\n",
} else {
part_is_closed = 1;
}
if (part_is_closed) {
fdp->d_exclmask = 0;
else
(CE_CONT,
"fd_close: exclparts %lx openparts %lx lyrcnt %lx\n",
if (fd_unit_is_open(fdp) == 0)
}
return (0);
}
/* ARGSUSED */
static int
{
}
/* ARGSUSED */
static int
{
}
/*
* 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
{
(CE_CONT, "fd_strategy: bp = 0x%p, dev = 0x%lx\n",
/*
* Ignoring return because device exist.
* Returned unit value is not used.
*/
(CE_WARN, "fd%d: block %ld is not start of sector!",
goto bad;
}
(CE_WARN, "fd%d: block %ld is past the end! (nblk=%ld)",
goto bad;
}
/* if at end of file, skip out now */
/* a write needs to get an error! */
goto bad;
}
return (0);
}
/* if operation not a multiple of sector size, is error! */
(CE_WARN, "fd%d: count %ld must be a multiple of %d",
goto bad;
}
/*
* Put the buf request in the drive's queue, FIFO.
*/
else
}
return (0);
bad:
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
{
}
(CE_CONT, "fdstart: bp=0x%p blkno=0x%lx bcount=0x%lx\n",
/* starting blk adjusted for the partition */
switch (chp->fdc_sec_size) {
/* convert logical block numbers to sector numbers */
case 1024:
blk >>= 1;
ptend >>= 1;
break;
default:
case NBPSCTR:
break;
case 256:
blk <<= 1;
ptend <<= 1;
break;
}
/*
* If off the end, limit to actual amount that
* can be transferred.
*/
/* to end of partition */
else
/*
* now we have the real start blk, addr and len for xfer op
*/
while (len != 0) {
/* start cyl of req */
/* start head of req */
/* start sector of req */
/*
* If the desired block and length will go beyond the
* cylinder end, then limit it to the cylinder end.
*/
else
} else {
if (len >
sctrshft))
tlen =
else
}
" blk 0x%x addr 0x%p len 0x%x "
"cyl %d head %d sec %d\n resid 0x%lx, tlen %d\n",
/*
* (try to) do the operation - failure returns an errno
*/
"fdstart: bad exec of bp: 0x%p, err=%d",
break;
}
}
(CE_CONT, "fdstart done: b_resid %lu, b_count %lu\n",
} else {
}
}
}
}
/* ARGSUSED */
static int
int *rval_p)
{
union {
int temp;
} cpy;
int rval = 0;
return (ENXIO);
(CE_CONT, "fd_ioctl fd unit %d: cmd %x, arg %lx\n",
switch (cmd) {
case DKIOCINFO:
break;
case DKIOCG_PHYGEOM:
case DKIOCG_VIRTGEOM:
goto get_geom;
case DKIOCGGEOM:
else
break;
case DKIOCSGEOM:
break;
}
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.
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
return (EINVAL);
}
sizeof (struct dk_allmap32), flag))
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
return (EINVAL);
dmp++;
}
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
break;
/*
* Set the map of all logical partitions
*/
case DKIOCSAPART:
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
break;
}
}
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
if (rval != 0)
break;
/*
* Note the conversion from starting cylinder number
* to starting sector number.
*/
dmp++;
}
break;
case DKIOCGVTOC:
/*
* 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
*/
if (rval) {
break;
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
break;
case DKIOCSVTOC:
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
break;
}
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
if (rval != 0)
break;
}
break;
case DKIOCSTATE:
break;
}
sizeof (int), flag))
break;
case FDIOGCHAR:
break;
case FDIOSCHAR:
break;
}
case 417:
"fdioschar:Medium density not supported\n");
break;
}
/* cpy.fdchar.fdc_transfer_rate = 500; */
/* FALLTHROUGH */
case 1000:
case 500:
case 300:
case 250:
break;
default:
(CE_WARN, "fd_ioctl fd unit %d: FDIOSCHAR odd "
"xfer rate %dkbs",
break;
}
break;
/*
* set all characteristics and geometry to the defaults
*/
case FDDEFGEOCHAR:
break;
case FDEJECT: /* eject disk */
case DKIOCEJECT:
break;
case FDGETCHANGE: /* disk changed */
break;
}
else
/*
* check diskette again only if it was removed
*/
/*
* no diskette is present
*/
/*
* again no diskette; not a new change
*/
else
} else {
/*
* a new diskette is present
*/
}
} else {
}
/*
* also get state of write protection
*/
} else {
}
break;
case FDGETDRIVECHAR:
break;
case FDSETDRIVECHAR:
break;
}
break;
case DKIOCREMOVABLE: {
int i = 1;
/* no brainer: floppies are always removable */
}
break;
}
case DKIOCGMEDIAINFO:
break;
case FDIOCMD:
{
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
break;
}
break;
}
case DDI_MODEL_NONE:
#endif /* _MULTI_DATAMODEL */
break;
}
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
if (rval != 0)
break;
break;
~(FUNIT_LABELOK | FUNIT_UNLABELED);
break;
}
(CE_WARN, "fd_ioctl fd unit %d: FDIOCSCMD not yet complete",
unit));
break;
}
case FDRAW:
break;
default:
(CE_WARN, "fd_ioctl fd unit %d: invalid ioctl 0x%x",
break;
}
return (rval);
}
static void
{
int i;
int xblk;
/*
* Return vtoc structure fields in the provided VTOC area, addressed
* by *vtocp.
*
*/
xblk = 1;
} else {
}
/*
* Copy partitioning information.
*/
/* correct partition info if sector size > 512 bytes */
}
}
static int
{
int i;
int nblks;
int ncyl;
/*
* Sanity-check the vtoc
*/
(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.
*/
return (EFAULT);
/*
* 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--) {
return (EINVAL);
}
ncyl++;
return (EINVAL);
}
vpart++;
}
sizeof (vtocp->v_bootinfo));
/*
* Copy partitioning information.
*/
sizeof (fdp->d_vtoc_timestamp));
/*
* construct the diskette label in supplied buffer
*/
/* Put appropriate vtoc structure fields into the disk label */
}
for (i = 0; i < NDKMAP; i++) {
}
/*
* 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.
*/
sum = 0;
}
return (0);
}
static int
{
int rval = 0;
return (ENXIO);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
return (EFAULT);
break;
}
case DDI_MODEL_NONE:
#endif /* ! _MULTI_DATAMODEL */
return (EFAULT);
#ifdef _MULTI_DATAMODEL
break;
}
#endif /* _MULTI_DATAMODEL */
/*
* copy user address & nbytes from raw_req so that we can
* put kernel address in req structure
*/
unit &= 3;
case FDRAW_FORMAT:
ucount += 16;
return (EFAULT);
}
break;
case FDRAW_WRCMD:
case FDRAW_WRITEDEL:
/* 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
*/
mode)) {
return (EFAULT);
}
}
} else
return (EINVAL);
break;
case FDRAW_READID:
case FDRAW_REZERO:
case FDRAW_SEEK:
case FDRAW_SENSE_DRV:
ucount = 0;
break;
case FDRAW_SPECIFY:
/* 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.
*/
}
}
if (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
{
/*
* 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) {
} else {
/*
* Ignoring return value because success is checked by
* verifying fjp and fdp and returned unit value is not used.
*/
goto pass;
/* get nblocks value */
}
}
static void
{
#ifdef DEBUG
int unit;
#else
#define DEBUG_ASSIGN (void)
#endif
/*
* Ignoring return in non DEBUG mode because device exist.
* Returned unit value is not used.
*/
/*
* 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.
*/
}
enum dkio_state
{
/* recheck disk only if DSKCHG "high" */
/*
* again no diskette; not a new change
*/
} else {
/*
* a new change; diskette was ejected
*/
}
} else {
}
} else {
}
return (state);
}
static int
{
int unit;
int err;
/* release the controller and drive */
/* turn on timer */
fdp->d_media_timeout = 0;
return (EINTR);
}
}
if (err) {
return (EIO);
}
}
return (0);
}
/*
* fd_get_media_info :
* Collects medium information for
* DKIOCGMEDIAINFO ioctl.
*/
static int
{
int err = 0;
return (err);
}