+/* flp.c 3.1 %H% */
+
+#include "../h/flp.h"
+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/conf.h"
+#include "../h/dir.h"
+#include "../h/user.h"
+#include "../h/mtpr.h"
+#include "../h/buf.h"
+#include "../h/cons.h"
+
+struct {
+ short fl_state; /* open and busy flags */
+ short fl_active; /* driver state flag */
+ struct buf *fl_buf; /* buffer we're using */
+ unsigned char *fl_xaddr; /* transfer address */
+ short fl_errcnt;
+} fltab;
+
+/*ARGSUSED*/
+flopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ struct buf *geteblk();
+
+ if (fltab.fl_state != 0) {
+ u.u_error = ENXIO;
+ return;
+ }
+ fltab.fl_state = FL_OPEN;
+ fltab.fl_buf = geteblk();
+ fltab.fl_active = FL_IDLE;
+}
+
+/*ARGSUSED*/
+flclose(dev, flag)
+ dev_t dev;
+ int flag;
+{
+
+ brelse(fltab.fl_buf);
+ fltab.fl_state = 0;
+}
+
+flstrategy(rw)
+ int rw;
+{
+ register struct buf *bp;
+ register unsigned i;
+
+ /*
+ * Assume one block read/written for each call -
+ * and enforce this by checking for block size of 128.
+ * Use the b_blkno field to address
+ * physical, 128-byte blocks (u.u_offset/128).
+ * This is checked for validity, and is further interpreted as:
+ *
+ * track# * (sectors/track) + sector #
+ */
+ if (u.u_count == 0)
+ return;
+ VOID spl4();
+ while (fltab.fl_state & FL_BUSY)
+ sleep((caddr_t)&fltab, PRIBIO);
+ fltab.fl_state |= FL_BUSY;
+ VOID spl0();
+
+ bp = fltab.fl_buf;
+ while ((i = min(RXBYSEC, u.u_count)) != 0) {
+ bp->b_blkno = u.u_offset>>7;
+ if (bp->b_blkno >= MAXSEC || (u.u_offset & 0177) != 0) {
+ /* block number out of range */
+ /* or offset in middle of block */
+ u.u_error = ENXIO;
+ break;
+ }
+ if (rw == B_WRITE) {
+ iomove(bp->b_un.b_addr, i, B_WRITE);
+ if (u.u_error != 0)
+ break;
+ }
+ bp->b_flags = rw;
+ VOID spl4();
+ flstart();
+ while ((bp->b_flags & B_DONE) == 0)
+ sleep((caddr_t)bp, PRIBIO);
+ VOID spl0();
+ if (bp->b_flags & B_ERROR) {
+ u.u_error = EIO;
+ break;
+ }
+ if (rw == B_READ) {
+ iomove(bp->b_un.b_addr, i, B_READ);
+ if (u.u_error != 0)
+ break;
+ }
+ }
+ u.u_count = bp->b_resid;
+ fltab.fl_state &= ~FL_BUSY;
+ wakeup((caddr_t)&fltab);
+}
+
+/*ARGSUSED*/
+flread(dev)
+ dev_t dev;
+{
+
+ flstrategy(B_READ);
+}
+
+/*ARGSUSED*/
+flwrite(dev)
+ dev_t dev;
+{
+
+ flstrategy(B_WRITE);
+}
+
+flstart()
+{
+ register struct buf *bp;
+
+ bp = fltab.fl_buf;
+ fltab.fl_active = FL_MAND;
+ fltab.fl_errcnt = 0;
+ fltab.fl_xaddr = (unsigned char *) bp->b_un.b_addr;
+ bp->b_resid = 0;
+ bp->b_bcount = RXBYSEC; /* always transfer a full sector */
+
+ if ((mfpr(TXCS) & TXCS_RDY) == 0)
+ /* not ready to receive order */
+ return;
+ /*
+ * Wake up floppy LSI software with command
+ */
+ fltab.fl_active = FL_SEC;
+ if ((bp->b_flags&B_READ) == B_READ)
+ mtpr(TXDB, FL_RS);
+ else
+ mtpr(TXDB, FL_WS);
+}
+
+/*
+ * See if we want to transmit something
+ * to the floppy - and do it
+ */
+conxfl()
+{
+ register int databyte;
+ register struct buf *bp;
+
+ bp = fltab.fl_buf;
+ switch (fltab.fl_active) {
+
+ case FL_MAND: /* send command */
+ if ((bp->b_flags&B_READ) == B_READ)
+ mtpr(TXDB,FL_RS);
+ else
+ mtpr(TXDB, FL_WS);
+ fltab.fl_active = FL_SEC;
+ break;
+
+ case FL_SEC: /* send sector address */
+ databyte = (int)bp->b_blkno % RXSTRK + 1;
+ mtpr(TXDB, FL_DATA | databyte);
+ fltab.fl_active = FL_TRACK;
+ break;
+
+ case FL_TRACK: /* send track address */
+ databyte = (int)bp->b_blkno / RXSTRK;
+ mtpr(TXDB , FL_DATA | databyte);
+ if ((bp->b_flags&B_READ) == B_READ)
+ /* prepare to receive complete */
+ fltab.fl_active = FL_COM;
+ else
+ /* prepare to send data */
+ fltab.fl_active = FL_DAX;
+ break;
+
+ case FL_DAX:
+ databyte = *(fltab.fl_xaddr++);
+ mtpr(TXDB, FL_DATA | databyte);
+ if (--bp->b_bcount == 0)
+ fltab.fl_active = FL_COM;
+ break;
+
+ case FL_CAN: /* give cancel order */
+ mtpr(TXDB, FL_CANCEL);
+ if (++fltab.fl_errcnt <= FLERRS) {
+ /* If error count permits, retry order */
+ fltab.fl_active = FL_MAND;
+ bp->b_bcount = RXBYSEC;
+ fltab.fl_xaddr = (unsigned char *) bp->b_un.b_addr;
+ } else {
+ /*
+ * We're really stupid today - call it an
+ * error and give up
+ */
+ bp->b_flags |= B_ERROR | B_DONE;
+ bp->b_resid = -RXBYSEC;
+ fltab.fl_active = FL_IDLE;
+ wakeup((caddr_t)bp);
+ }
+ }
+}
+
+cnrfl(c)
+ int c;
+{
+ register int datum;
+ register struct buf *bp;
+
+ datum = c;
+ bp = fltab.fl_buf;
+ if (datum == FL_PERR) {
+ /*
+ * Got a protocol error - cancel the
+ * current function and try again if error count isn't
+ * too great. First, though, make sure that an actual
+ * transaction is in progress (so a spurious error from
+ * the LSI won't screw us up too much!
+ */
+ if (fltab.fl_active != FL_IDLE)
+ fltab.fl_active = FL_CAN;
+ } else switch(fltab.fl_active ) {
+
+ case FL_DAR: /* expecting a datum */
+ if ((c&RXDB_ID) != FL_DATA)
+ goto error;
+ *(fltab.fl_xaddr++) = (c & RXDB_DATA);
+ if (--bp->b_bcount==0) {
+ fltab.fl_active = FL_IDLE;
+ bp->b_flags |= B_DONE;
+ wakeup((caddr_t)bp);
+ }
+ break;
+
+ case FL_COM: /* expecting a "function complete" */
+ if ((c&RXDB_ID)!= FL_FFC || (c&FL_ERR) == FL_ERR){
+error:
+ bp->b_flags |= B_ERROR | B_DONE;
+ bp->b_resid = -bp->b_bcount;
+ fltab.fl_active = FL_IDLE;
+ wakeup((caddr_t)bp);
+ } else if ((bp->b_flags&B_READ) == B_READ)
+ /* got function complete, now get data */
+ fltab.fl_active = FL_DAR;
+ else {
+ /* got function complete on write - finish up */
+ fltab.fl_active = FL_IDLE;
+ bp->b_flags |= B_DONE;
+ wakeup((caddr_t)bp);
+ }
+ break;
+ }
+}