/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:fd.c 12.0$ */
/* $ACIS:fd.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/ca_atr/RCS/fd.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char    *rcsid = "$Header:fd.c 12.0$";
#endif

/* $Header:fd.c 12.0$ */
/* $ACIS:fd.c 12.0$ */

#include "fd.h"
#if NFD > 0
#include	"../h/param.h"
#include	"../h/vm.h"
#include	"../h/buf.h"
#include	"../h/time.h"
#include	"../h/proc.h"
#include	"../h/errno.h"
#include	"../machine/pte.h"
#include	"../machine/io.h"
#include	"../machineio/ioccvar.h"
#include 	"../machineio/fdio.h"
#include	"../h/kernel.h"
#include	"../h/systm.h"
#include	"../h/cmap.h"
#include	"../h/dk.h"
#include	"../h/uio.h"
#include	"../h/ioctl.h"
#include 	"../h/file.h"
#include	"../ca_atr/pcif.h"
#include	"../pc_code/dio.h"
#include	"../machine/mmu.h"
#include	"../machine/debug.h"
#include 	"../machine/dkio.h"



caddr_t         real_buf_addr();

struct pc_dpl   fd_dpl[NFD];

#define	FD_SPL()	_spl3()	/* CPU level 3 */
#define	FDUNIT(dev)	(minor(dev))

#define	BSIZE	DEV_BSIZE
#define FDBPS	DEV_BSIZE

#define PAGESIZE 2048
#define FDTIMEOUT	30	/* number of seconds til interrupt known lost */
#define	FDFORMREQ	0x0b	/* magic flgs for fdformat */
 /* internal command flags */
#define	B_CTRL		0x80000000	/* ctrl request */
#define	B_FMT		0x81000000	/* format request */
#define	B_STATUS	0x82000000	/* get status request */
#define	B_SMT		0x83000000	/* set media type request */
#define	B_RESET		0x84000000	/* reset request */

#define	FD5LO	0x0000
#define	FD5HI	0x0001
#define	FD3LO	0x0002
#define	FD3HI	0x0003
#define FDWP    0x0100

unsigned        fdbusy;

#define b_cylin	b_resid

struct fd_softc {
	int             timer;	/* timer for watchdog */
	u_short         type[NFD];
}               fds;


struct fdst {
	short           nspt;	/* # sectors/track */
	short           ntpc;	/* # surfaces or heads */
	short           nspc;	/* # sectors/cylinder */
	short           ncyl;	/* # cylinders */
	long            nbpd;	/* # bytes per disk */
	char            gpl;	/* Gap length */
	char            fgpl;	/* Format Gap length */
	char            step;	/* Stepping rate and head unload time */
	char            xfer;	/* Data transfer rate */
	int             drive;	/* Drive type */
	char           *name;	/* name of device type */
};

struct buf      rfdbuf[NFD];	/* buffers for raw I/O */
struct buf      fdutab[NFD];	/* per drive buffers */

struct iocc_ctlr *fdminfo[NFDC];
struct iocc_device *fddinfo[NFD];

int 
fdprobe(), fdslave(), fdattach(), fddgo(), fdint();
int             fdwstart, 
fdwatch(), fdstrategy();

int        fdminphys();
int	   fdsuspend();

caddr_t         fdstd[] = {
			   (caddr_t) 0x000003f2, (caddr_t) 0x00000372, 0
};

struct iocc_driver fdcdriver = {
    fdprobe, fdslave, fdattach, fddgo, fdstd, "fd", fddinfo, "fdc", fdminfo,
				fdint, 2, 0, DRIVER_SUSPEND, fdsuspend
};

struct fdst fdst[] = {
/* sec/tr heads sec/cyl #cyl  bytes/disk     gpl   fgpl  step  xfer  drive */
	/* 360K diskette (5.25 in.) */
{   9,     2,     2*9,   40,  2*9*40*BSIZE,  0x23, 0x50, 0xdf, 0x01, FD5LO, "fd360k"  },
	/* 1.2M diskette (5.25 in.) */
{   15,    2,     2*15 , 80,  2*15*80*BSIZE, 0x1b, 0x54, 0xdf, 0x00, FD5HI, "fd1200k"  },
	/* 720K diskette (3.5 in.) */
{   9,     2,     2*9,   80,  2*9*80*BSIZE,  0x2a, 0x50, 0xdf, 0x02, FD3LO, "fd720k"  },
	/* 1.4M diskette (3.5 in.) */
{   18,    2,     2*18,  80,  2*18*80*BSIZE, 0x2a, 0x50, 0xdf, 0x02, FD3HI, "fd1400k"  },
};


char           *bioserrmsg();
int		fd_pc_cb;		/* pc control block */
int		fd_open_flag[NFD];	/* bits to indicate if open */

fdprobe()
{

	fd_pc_cb = get_pc_cb(FDENT);

	set_pcvec_map(FDIRQ, 1);

	fd_dpl[0].op_code = DISKOP_RESET;
	fd_dpl[0].drive = 0;

	if (pc_req(CB_FDREQ, &fd_dpl[0], FDENT) < 0)
		return (PROBE_BAD);

	PROBE_DELAY(20000);

	return (PROBE_OK);

}


fdslave(iod)
	register struct iocc_device *iod;
{
	register short  unit = iod->iod_unit;
	register u_char eq_byte = (u_char) (equip & 0x00ff);

	if (unit == 0)		/* there has to be drive 1 */
		return (1);	/* tell em it's there */

	if ((unit == 1) && (eq_byte & 0x40))
		return (1);	/* if they're looking for */
	/* drive 2, tell em it's there */

	if (unit == 2)		/* special case for external AT */
		return (1);	/* diskette drive */

	return(0);
}


fdattach(iod)
	register struct iocc_device *iod;
{
	register struct buf *dp = &fdutab[iod->iod_unit];

	if (fdwstart == 0) {
		timeout(fdwatch, (caddr_t) 0, hz);
		++fdwstart;
	}
	dp->b_actf = NULL;
	dp->b_actl = NULL;
	dp->b_active = 0;
	fds.timer = 0;
	return (1);
}



fdopen(dev, flags)
	dev_t           dev;
	int             flags;
{
	register int    unit = FDUNIT(dev);
	register struct iocc_device *ui;

	DEBUGF(fddebug > 8, printf("fdopen entered\n");
	);

	if (unit >= NFD || (ui = fddinfo[unit]) == 0 || ui->iod_alive == 0)
		return (ENXIO);

	if (fd_open_flag[minor(dev)] == 0) {
		if (fdautodensity(dev, flags) < 0)
			return (EIO);	/* return error if no disk */
	}

	fd_open_flag[minor(dev)] |= 1 << major(dev);	/* now open */
	DEBUGF(fddebug > 8, printf("fdopen exit\n");
	);
	return (0);
}


fdautodensity(dev, flags)
	dev_t           dev;
	int             flags;
{
	register int    unit = FDUNIT(dev);
	struct pc_dpl   dpl;
	int             return_code;

	DEBUGF(fddebug > 8, printf("fdautodensity entered\n");
	);

	bzero(&dpl, sizeof(struct pc_dpl));

	dpl.op_code = DISKOP_STATUS;	/* reset the drive */
	dpl.drive = (u_short) dev ;

	if (flags == FDFORMREQ)
		return (0);

	if ((return_code = fdop(&dpl)) < 0) {
		printf("fd%d: door open or hardware fault.\n", unit);
		return (-1);
	}
	if ((return_code & FDWP) && (flags & FWRITE)) {
		printf("fd%d: write protected\n", unit);
		return (-1);
	}
	fds.type[unit] = return_code & 0x00ff;
	DEBUGF(fddebug > 8, printf("density set to %d\n", fds.type[unit]);
	);

	return (0);
}



fdclose(dev, flag)
	dev_t           dev;
{

	DEBUGF(fddebug > 8, printf("fdclose: dev=0x%x\n", dev);
	);
	fd_open_flag[minor(dev)] &= ~(1 << major(dev));	/* now closed */
	return (0);
}





fdread(dev, uio)
	dev_t           dev;
	struct uio     *uio;
{
	int             unit = FDUNIT(dev);
	DEBUGF(fddebug > 0x70, printf("fdread entered\n");
	);

	if ((uio->uio_offset < 0) || (unit >= NFD))
		return (ENXIO);

	return (physio(fdstrategy, &rfdbuf[unit], dev, B_READ, fdminphys, uio));
}


fdwrite(dev, uio)
	dev_t           dev;
	struct uio     *uio;
{
	int             unit = FDUNIT(dev);
	DEBUGF(fddebug > 0x70, printf("fdwrite entered\n");
	);

	if ((uio->uio_offset < 0) || (unit >= NFD))
		return (ENXIO);

	return (physio(fdstrategy, &rfdbuf[unit], dev, B_WRITE, fdminphys, uio));
}



fdstrategy(bp)
	register struct buf *bp;
{

	register int    unit = FDUNIT(bp->b_dev);
	register struct iocc_device *iod;
	register struct fdst *st = &fdst[fds.type[unit]];
	register struct buf *dp;
	register int    s;

	iod = fddinfo[unit];

	DEBUGF(fddebug > 9, printf("fdstrategy entered unit=%d\n",unit););
	if ((unit >= NFD) || (iod == 0) || (iod->iod_alive == 0) || 
	    (bp->b_blkno < 0) || (bp->b_blkno >= (st->nbpd / BSIZE))) {
		DEBUGF(fddebug > 9,
			 printf("fdstrategy NON-EX dev unit=%d\n",unit);
		);
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	if (((bp->b_blkno * BSIZE) + bp->b_bcount) > st->nbpd) {
		bp->b_bcount = st->nbpd - (bp->b_blkno * BSIZE);
		if (bp->b_bcount < 0)
			bp->b_bcount = 0;
	}
	bp->b_cylin = (bp->b_blkno / st->nspc);

	s = FD_SPL();
	dp = &fdutab[unit];
	disksort(dp, bp);

	if (dp->b_active == 0) {
		(void) fdustart(iod);
		bp = &iod->iod_mi->ic_tab;
		if (bp->b_actf && bp->b_active == 0)
			(void) fdstart(iod->iod_mi);
	}
	splx(s);
}



fdustart(iod)
	register struct iocc_device *iod;
{
	register struct buf *dp;
	register struct iocc_ctlr *ic;

	DEBUGF(fddebug > 9, printf("fdustart dev unit=%d\n",iod->iod_unit););
	dp = &fdutab[iod->iod_unit];
	ic = iod->iod_mi;

	dp->b_forw = NULL;
	if (ic->ic_tab.b_actf == NULL)
		ic->ic_tab.b_actf = dp;
	else
		ic->ic_tab.b_actl->b_forw = dp;
	ic->ic_tab.b_actl = dp;
	dp->b_active++;

	return (0);
}


/*
 *	Controller Startup Routine:
 *
 *
 */
fdstart(ic)
	register struct iocc_ctlr *ic;
{
	register struct buf *bp, *dp;

	DEBUGF(fddebug > 9, printf("fdstart entered ctrl=%d\n",ic->ic_ctlr););
loop:
	if ((dp = ic->ic_tab.b_actf) == NULL)
		return;
	if ((bp = dp->b_actf) == NULL) {
		ic->ic_tab.b_actf = dp->b_forw;
		goto loop;
	}
	ic->ic_tab.b_active++;	/* Mark controller active */

	fddgo(bp);
	return (0);
}


/* 
 * This function sends the command out via a cbcb call
 */

fddgo(bp)
	register struct buf *bp;
{
	int             i, unit = FDUNIT(bp->b_dev);
	u_short         head, sector, xfer_cnt, cnt;
	char           *vir_addr, *xfer_addr;
	register struct fdst *st = &fdst[fds.type[unit]];

	if ((bp->b_bcount < 0) || (bp->b_bcount >= 0x10000)) {
		printf("fd%d: bad bcount = %x\n", unit, bp->b_bcount);
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	while (fdbusy) {
		delay(10);
		if (i++ > 100)
			panic("fd: Non-sync request attempt.");
	}

	DEBUGF(fddebug > 9,
		 printf("fdgo entered unit=%d flags=%x\n",unit,bp->b_flags);
	);
	vir_addr = bp->b_un.b_addr;
	xfer_addr = real_buf_addr(bp, vir_addr);
	xfer_cnt = (u_short) bp->b_bcount;

	if (bp->b_flags & B_CTRL) {

		switch (bp->b_flags & 0xff000000) {

		case B_RESET:
			fd_dpl[unit].op_code = DISKOP_RESET;
			break;

		case B_STATUS:
			fd_dpl[unit].op_code = DISKOP_STATUS;
			break;

		case B_SMT:
			fd_dpl[unit].op_code = DISKOP_SMT;
			break;

		case B_FMT:
			fd_dpl[unit].op_code = DISKOP_FORMAT;
			break;

		default:
			printf("fd%d: Invalid internal command\n", unit);
			return (-1);

		}

		fd_dpl[unit].atr_addr[0] = (int) xfer_addr;
		fd_dpl[unit].atr_cnt[0] = BSIZE;
		fd_dpl[unit].atr_addr[1] = 0;
		fd_dpl[unit].atr_cnt[1] = 0;
		goto doit;
	} else {
		if (bp->b_flags & B_READ)
			fd_dpl[unit].op_code = DISKOP_READ;
		else
			fd_dpl[unit].op_code = DISKOP_WRITE;
	}

	fd_dpl[unit].atr_addr[0] = (int) xfer_addr;	/* set up 1st tcw */

	if ((cnt = (PAGESIZE - ((int) xfer_addr & 0x000007ff))) > xfer_cnt)
		fd_dpl[unit].atr_cnt[0] = (u_short) xfer_cnt;
	else
		fd_dpl[unit].atr_cnt[0] = (u_short) cnt;

	xfer_cnt -= fd_dpl[unit].atr_cnt[0];	/* adjust the total count */
	vir_addr += fd_dpl[unit].atr_cnt[0];	/* adjust the virtual addr */
	/* we're on a boundry now */
	for (i = 1; xfer_cnt > PAGESIZE; i++) {	/* while there's a count */
		xfer_addr = real_buf_addr(bp, vir_addr);	/* fill out tcw's */
		fd_dpl[unit].atr_addr[i] = (int) xfer_addr;
		fd_dpl[unit].atr_cnt[i] = PAGESIZE;
		vir_addr += PAGESIZE;
		xfer_cnt -= PAGESIZE;
	}

	if (xfer_cnt) {		/* if there is a partial page left, do it */
		xfer_addr = real_buf_addr(bp, vir_addr);
		fd_dpl[unit].atr_addr[i] = (int) xfer_addr;
		fd_dpl[unit].atr_cnt[i] = xfer_cnt;
		i++;
	}
	fd_dpl[unit].atr_addr[i] = 0;	/* mark end of request */
	fd_dpl[unit].atr_cnt[i] = 0;

	/* Then fill in the rest of the dpl and send it off */
doit:
	sector = (u_short) bp->b_blkno;
	sector %= st->nspc;
	head = (sector / st->nspt);
	sector = (sector % st->nspt) + 1;

	fd_dpl[unit].drive = unit;	/* load up the dpl */
	fd_dpl[unit].sector = (u_short) sector;
	fd_dpl[unit].cyl = (u_short) bp->b_cylin;
	fd_dpl[unit].head = (u_short) head;
	fd_dpl[unit].number = (u_short) ((bp->b_bcount + BSIZE - 1) / BSIZE);

	fdbusy++;

	if (pc_req(CB_FDREQ, &fd_dpl[unit], FDENT) < 0) {
		printf("fd%d: pc_req failed\n", unit);
		bp->b_error = EIO;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
}


fdint(ctlr)
	int             ctlr;
{
	register struct buf *bp, *dp;
	register struct iocc_ctlr *ic;
	struct iocc_device *iod;
	struct pc_stat *fd_stat;
	register int    unit, old_window;

	ic = fdminfo[ctlr];

	if (ic == 0 || ic->ic_tab.b_active == 0) {
		return (1);
	}
	dp = ic->ic_tab.b_actf;
	if (dp->b_active == 0) {
		return (1);
	}
	bp = dp->b_actf;
	unit = FDUNIT(bp->b_dev);

	old_window = get_512_window();	/* Save the current window */
	fd_stat = (struct pc_stat *) (set_512_window(fd_pc_cb) + pcif_512_fw);
	fd_stat += unit;


	if (fd_stat->return_code & BIOS_ERROR) {
		printf("fd%d: BIOS error code = %x<%s>\n", unit,
		       (char) (fd_stat->return_code & 0x00ff),
		       bioserrmsg((char) (fd_stat->return_code & 0x00ff)));
		bp->b_flags |= B_ERROR;
	}
	fdbusy = 0;		/* clear the busy flag */

	/*
	 * Flag current request complete 
	 */
	if (ic->ic_tab.b_active) {	/* For when we do error recovery */
		ic->ic_tab.b_active = 0;
		ic->ic_tab.b_errcnt = 0;
		ic->ic_tab.b_actf = dp->b_forw;	/* rotate to next drive */
		dp->b_active = 0;
		dp->b_errcnt = 0;
		dp->b_actf = bp->av_forw;
		bp->b_resid = (bp->b_flags & B_ERROR) ? bp->b_bcount : 0;	/* XXX *//* Error
										 * recovery! */
		iodone(bp);

		/*
		 * If more work to do on this drive, restart it. 
		 */
		iod = fddinfo[unit];
		if (dp->b_actf)
			fdustart(iod);
	}
	fdstart(ic);

	fds.timer = 0;		/* did not timeout */

	set_512_window(old_window);

	return (0);
}


/*
 * watchdog timer - checks to see if we've lost an interrupt
 * by having the timer click 30 times without being cleared
 * (by interrupt routine)
 */

fdwatch(reg)
	caddr_t         reg;
{
	register struct fd_softc *fd = &fds;
	register struct iocc_ctlr *ic;
	register int    s = FD_SPL();
	register int    i;
	register struct buf *dp, *bp;
	struct pc_stat *fd_stat;
	register int    old_window;

	for (i = 0; i < NFDC; ++i) {
		if ((ic = fdminfo[i]) == 0 || ic->ic_alive == 0 || ic->ic_tab.b_active == 0) {
			fd->timer = 0;
			continue;	/* not doing anything */
		}
		if (++fd->timer > FDTIMEOUT) {
			if ((dp = ic->ic_tab.b_actf) == NULL || (bp = dp->b_actf) == NULL)
				continue;

			old_window = get_512_window();
			fd_stat = (struct pc_stat *) (set_512_window(fd_pc_cb) + pcif_512_fw);
			fd_stat += FDUNIT(bp->b_dev);

			if (fd_stat->return_code & (BIOS_ERROR | BIOS_RET_OK)) {
				set_512_window(old_window);
				fd->timer = 0;
				printf("fd%d: fdint called from fdwatch\n", FDUNIT(bp->b_dev));
				fdint(i);
			} else {
				fdbusy = 0;	/* clear the busy flag */
				fd->timer = 0;
				set_512_window(old_window);
				printf("fd%d: lost interrupt\n", FDUNIT(bp->b_dev));
				fdstart(ic);	/* retry it */
			}
		}
	}
	timeout(fdwatch, (caddr_t) 0, hz);
	splx(s);
}




/*
 * Interface for internally generated commands.
 */

fdop(dpl)
	register struct pc_dpl *dpl;
{
	register ushort unit = FDUNIT(dpl->drive);
	struct buf     *bp = geteblk(BSIZE);
	register struct fdst *st = &fdst[fds.type[unit]];
	struct pc_stat *fd_stat;
	register int    old_window, return_code;

	switch (dpl->op_code) {

	case DISKOP_RESET:
		bp->b_flags = B_RESET;
		break;

	case DISKOP_STATUS:
		bp->b_flags = B_STATUS;
		break;

	case DISKOP_SMT:
		bp->b_flags = B_SMT;
		break;

	default:
		printf("fd%d: Invalid fdop command\n", unit);
		return (-1);
	}

	bp->b_dev = (dev_t) dpl->drive;
	bp->b_blkno = dpl->cyl * st->nspc;
	bp->b_bcount = dpl->number * BSIZE;

	fdstrategy(bp);
	iowait(bp);

	old_window = get_512_window();	/* Save the current window */
	fd_stat = (struct pc_stat *) (set_512_window(fd_pc_cb) + pcif_512_fw);
	fd_stat += unit;
	return_code = fd_stat->return_code;
	set_512_window(old_window);

	if (bp->b_flags & B_ERROR)
		return_code = -1;

	brelse(bp);

	return (return_code);

}


/*
 * Control routine:
 * processes two kinds of requests:
 *
 *	(1) Format the diskette according to the specified data parameter.
 *	(2) Report the density of the diskette in the indicated drive
 *	    (since the density it automatically determined by the driver,
 *	     this is the only way to let an application program know the
 *	     density)
 *
 */


fdioctl(dev, cmd, data, flag)
	dev_t           dev;
	caddr_t         data;
{
	int             unit = FDUNIT(dev);
	DEBUGF(fddebug > 8, printf("fdioctl entered\n");
	);

	switch (cmd) {

	case FDIOC_FORMAT:

		if ((flag & FWRITE) == 0)
			return (EBADF);

		return (fdformat(dev, *(int *) data));

	case FDIOC_GDENS:	/* get density */

		*(int *) data = (int) fds.type[unit];
		return (0);

	case FDIOC_RESET:	/* reset specified unit */

		return (fdreset(dev));

	case DKIOCGPART:{
			register struct dkpart *dk = (struct dkpart *) data;
			register struct fdst *st = &fdst[fds.type[unit]];

			dk->dk_size = st->nspc * st->ncyl;	/* size */
			dk->dk_start = 0;	/* start */
			dk->dk_blocksize = FDBPS;	/* device blocksize */
			dk->dk_ntrack = st->ntpc;	/* tracks/cylinder */
			dk->dk_nsector = st->nspt;	/* sectors/track */
			dk->dk_ncyl = st->ncyl;	/* cylinders */
			strcpy(dk->dk_name, st->name);	/* copy name */
			return (0);
		}
	}
	return (ENOTTY);	/* sigh */
}


fdreset(dev)
	dev_t	dev;
{
	struct pc_dpl   dpl;

	bzero(&dpl, sizeof(struct pc_dpl));

	dpl.op_code = DISKOP_RESET;	/* reset the drive */
	dpl.drive = (u_short) dev;

	if (fdop(&dpl) < 0)
		return (-1);

	return (0);
}



/*
 * Initiate a format command.
 */
fdformat(dev, density)
	dev_t           dev;
	int             density;
{
	register struct fdst *st;
	register int    unit = FDUNIT(dev);
	struct pc_dpl   dpl;
	u_short         cyl, head, sect;
	struct buf     *bp = geteblk(BSIZE);
	register char  *idbuff = (char *) bp->b_un.b_addr;

	DEBUGF(fddebug > 8, printf("fd%d: fdformat entered: density = %x\n", unit, density);
	);

	fds.type[unit] = density + FD3LO;	/* set type */
	st = &fdst[fds.type[unit]];

	bzero(&dpl, sizeof(struct pc_dpl));

	dpl.op_code = DISKOP_SMT;	/* set the media type */
	dpl.drive = (u_short) dev;
	dpl.cyl = st->ncyl - 1;
	dpl.number = st->nspt;

	if (fdop(&dpl) < 0)
		return (-1);

	for (cyl = 0; cyl < st->ncyl; cyl++) {

		for (head = 0; head < st->ntpc; head++) {

			DEBUGF(fddebug > 8, printf("Cylinder %d   Head %d\n ", cyl, head);
			);

			for (sect = 0; sect < st->nspt; sect++) {
				idbuff[(sect * 4) + 0] = (char) cyl;
				idbuff[(sect * 4) + 1] = (char) head;
				idbuff[(sect * 4) + 2] = (char) (sect + 1);
				idbuff[(sect * 4) + 3] = 0x02;
			}

			bp->b_dev = dev;
			bp->b_flags = B_FMT;
			bp->b_error = 0;
			bp->b_blkno = (cyl * st->nspc) + (head * st->nspt);
			bp->b_bcount = st->nspt * BSIZE;
			bp->av_forw = 0;


			fdstrategy(bp);
			iowait(bp);

			if (bp->b_flags & B_ERROR) {
				brelse(bp);
				return (-1);
			}
		}		/* end of for head */

	}			/* end of for cyl */

	DEBUGF(fddebug > 8, printf("\n"));

	brelse(bp);
	return (0);
}


/*#define FDMAXPHYS	(1024 * 60)	/* 60K maximum transfer for hd & fd */
#define FDMAXPHYS	(1024 * 16)	/* keep the floppy transfers small */
fdminphys(bp)
	struct buf     *bp;
{

	if (bp->b_bcount > FDMAXPHYS)
		bp->b_bcount = FDMAXPHYS;
}



/*
 * return partition size (for swap partitions)
 */

fdsize(dev)
	dev_t           dev;
{
	register int    unit = FDUNIT(dev);
	register struct iocc_device *iod;
	register struct fdst *st;

	if (unit >= NFD || (iod = fddinfo[unit]) == 0 || iod->iod_alive == 0)
		return (-1);

	if (fd_open_flag[minor(dev)] == 0 && fdautodensity(dev,0) < 0)
		return(-1);

	st = &fdst[fds.type[unit]];
	return (st->nspc * st->ncyl);
}

/*
 * routine called when we suspend and when we return
 * all we have to do is pick up a new fd_pc_cb when we
 * return. At some point we might want to stop the watch
 * routine but this isn't necessary now because timeouts
 * don't run while we are suspended.
 */
fdsuspend(iod, idr, how)
	struct iocc_device *iod;
	struct iocc_driver *idr;
{
	if (how == SUSPEND_DONE) {
		fd_pc_cb = get_pc_cb(FDENT);	/* get pc_cb addr */
	}
}

#endif NFD
