*BSD News Article 20661


Return to BSD News archive

Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!elroy.jpl.nasa.gov!swrinde!cs.utexas.edu!uunet!pipex!sunic!news.funet.fi!funic!news.eunet.fi!KremlSun!kiae!relcom!newsserv
From: vak@zebub.msk.su
Newsgroups: comp.os.386bsd.bugs
Subject: ft: patch to install on 386bsd pk0.2.4
Date: Tue, 07 Sep 93 22:35:03 GMT
Distribution: world
Organization: Vak Software House
Message-ID: <AANoGZiCJ0@zebub.msk.su>
Sender: news-service@kiae.su
Reply-To: vak@zebub.msk.su
X-Return-Path: kiae.UUCP!newcom.kiae.su!vak!zebub!vak
Lines: 703

Floppy tape driver, recently posted to comp.os.386bsd.development,
is based upon old fd.c, and installing it on 386bsd 0.1 pk0.2.4
causes some problems (probably, on NetBSD too).

Apply this patch to ft source tree, and then install it according to README.
After that it will install and work.

This patch also adds yet another utility: ftinfo.
It tests and prints paramaters of tape.

Serge Vakulenko

diff -r -N -C3 ../ft~/driver/fd.c.diff ./driver/fd.c.diff
*** ../ft~/driver/fd.c.diff     Wed Sep  8 19:28:06 1993
--- ./driver/fd.c.diff  Tue Sep  7 22:20:11 1993
***************
*** 1,14 ****
! *** ../fd.c.tape      Wed Feb 10 18:46:22 1993
! --- fd.c      Sat Jun  5 12:36:22 1993
  ***************
! *** 47,67 ****
!   #include "ioctl.h"
!   #include "buf.h"
!   #include "uio.h"
!   #include "i386/isa/isa_device.h"
!   #include "i386/isa/fdreg.h"
!   #include "i386/isa/icu.h"
!   #include "i386/isa/rtc.h"
    #undef NFD
    #define NFD 2

--- 1,7 ----
! *** fd~.c     Tue Sep  7 22:17:38 1993
! --- fd.c      Tue Sep  7 22:19:31 1993
  ***************
! *** 97,103 ****
    #undef NFD
    #define NFD 2

***************
*** 16,36 ****
    #define     FDTYPE(s)       ((s)&7)

    #define b_cylin b_resid
!   #define b_step b_resid
!   #define FDBLK 512
!   #define NUMTYPES 4
!
!   struct fd_type {
!       int     sectrac;                /* sectors per track         */
!       int     secsize;                /* size code for sectors     */
! --- 47,76 ----
!   #include "ioctl.h"
!   #include "buf.h"
!   #include "uio.h"
!   #include "i386/isa/isa_device.h"
!   #include "i386/isa/fdreg.h"
!   #include "i386/isa/icu.h"
!   #include "i386/isa/rtc.h"
    #undef NFD
    #define NFD 2

--- 9,15 ----
    #define     FDTYPE(s)       ((s)&7)

    #define b_cylin b_resid
! --- 97,112 ----
    #undef NFD
    #define NFD 2

***************
*** 43,99 ****
  ! enum { FDC_TAPE_MODE, FDC_DISK_MODE };
  ! #endif
  !
! ! #define     FDUNIT(s)       ((s>>3)&3)
    #define     FDTYPE(s)       ((s)&7)

    #define b_cylin b_resid
-   #define b_step b_resid
-   #define FDBLK 512
-   #define NUMTYPES 4
-
-   struct fd_type {
-       int     sectrac;                /* sectors per track         */
-       int     secsize;                /* size code for sectors     */
  ***************
! *** 184,203 ****
! --- 193,220 ----
!               }
!
!               fdt <<= 4;
!               fd_turnoff(i);
!               hdr = 1;
!       }
!
        /* Set transfer to 500kbps */
!       outb(fdc+fdctl,0);
!       fd_turnoff(0);
  +
  + #if NFT > 0
! +     ftattach(fdc);
  +     if (ft_type == FT_COLORADO)
! +             printf(", %d: Jumbo tape", i);
  +     else if (ft_type == FT_MOUNTAIN)
! +             printf(", %d: Summit tape", i);
  + #endif
    }

    int
-   fdsize(dev)
-   dev_t       dev;
-   {
-       return(0);
-   }
-
-   /****************************************************************************/
  ***************
! *** 315,342 ****
!       while ((inb(fdc+fdsts) & NE7_RQM) == 0 && i-- > 0);
!       if (i <= 0) return (-1);
!       outb(fdc+fddata,x);
!       return (0);
!   }
!
!   static fdopenf;
    /****************************************************************************/
    /*                           fdopen/fdclose                                 */
    /****************************************************************************/
--- 22,50 ----
  ! enum { FDC_TAPE_MODE, FDC_DISK_MODE };
  ! #endif
  !
! ! #define FDUNIT(s)       ((s>>3)&3)
    #define     FDTYPE(s)       ((s)&7)

    #define b_cylin b_resid
  ***************
! *** 332,337 ****
! --- 341,354 ----
!       /* printf(" %s ",rev); */
        /* Set transfer to 500kbps */
!       outb(fdc->baseport+fdctl,0); /*XXX*/
  +
  + #if NFT > 0
! +     ftattach(fdc->baseport);
  +     if (ft_type == FT_COLORADO)
! +             printf(", %d: Jumbo tape", fdu);
  +     else if (ft_type == FT_MOUNTAIN)
! +             printf(", %d: Summit tape", fdu);
  + #endif
    }

    int
  ***************
! *** 501,514 ****
    /****************************************************************************/
    /*                           fdopen/fdclose                                 */
    /****************************************************************************/
***************
*** 101,174 ****
  !     dev_t   dev;
  !     int     flags;
    {
! !     int unit = FDUNIT(minor(dev));
! !     /*int type = FDTYPE(minor(dev));*/
        int s;

-       fdopenf = 1;
        /* check bounds */
!       if (unit >= NFD) return(ENXIO);
        /*if (type >= NUMTYPES) return(ENXIO);*/
!
!       /* Set proper disk type, only allow one type */
!       return 0;
!   }
!
!   fdclose(dev, flags)
! --- 332,360 ----
!       while ((inb(fdc+fdsts) & NE7_RQM) == 0 && i-- > 0);
!       if (i <= 0) return (-1);
!       outb(fdc+fddata,x);
!       return (0);
!   }
!
!   static fdopenf;
    /****************************************************************************/
    /*                           fdopen/fdclose                                 */
    /****************************************************************************/
  ! Fdopen(int dev, int flags, int devtype, struct proc *p)
    {
! !     int unit = FDUNIT(minor(dev));
! !     /*int type = FDTYPE(minor(dev));*/
        int s;

  + #if NFT > 0
! +     if (unit == NFD) return(ftopen(dev, flags, devtype, p));
  + #endif
-       fdopenf = 1;
        /* check bounds */
!       if (unit >= NFD) return(ENXIO);
        /*if (type >= NUMTYPES) return(ENXIO);*/
!
!       /* Set proper disk type, only allow one type */
!       return 0;
    }

!   fdclose(dev, flags)
! ***************
! *** 426,445 ****
! --- 444,467 ----
!   /****************************************************************************/
!   /*                                 fdintr                                   */
!   /****************************************************************************/
!   fdintr(unit)
    {
!       register struct buf *dp,*bp;
!       struct buf *dpother;
!       int read,head,trac,sec,i,s,sectrac,cyl,st0;
!       unsigned long blknum;
!       struct fd_type *ft;
! +
! + #if NFT > 0
! +     if (fdc_mode == FDC_TAPE_MODE) return(ftintr(unit));
! + #endif

-   #ifdef FDTEST
-       printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive);
-   #endif
-
-       if (!fdopenf) return;
-       dp = &fd_unit[fd_drive].head;
-       bp = dp->b_actf;
-       read = bp->b_flags & B_READ;
-       /*ft = &fd_types[FDTYPE(bp->b_dev)];*/
--- 52,98 ----
  !     dev_t   dev;
  !     int     flags;
    {
!       fdu_t fdu = FDUNIT(minor(dev));
!       /*int type = FDTYPE(minor(dev));*/
        int s;

        /* check bounds */
!       if (fdu >= NFD) return(ENXIO);
        /*if (type >= NUMTYPES) return(ENXIO);*/
! --- 518,532 ----
    /****************************************************************************/
    /*                           fdopen/fdclose                                 */
    /****************************************************************************/
  ! Fdopen(int dev, int flags, int devtype, struct proc *p)
    {
!       fdu_t fdu = FDUNIT(minor(dev));
!       /*int type = FDTYPE(minor(dev));*/
        int s;

  + #if NFT > 0
! +     if (fdu == NFD) return(ftopen(dev, flags, devtype, p));
  + #endif
        /* check bounds */
!       if (fdu >= NFD) return(ENXIO);
        /*if (type >= NUMTYPES) return(ENXIO);*/
! ***************
! *** 606,612 ****
!   \***********************************************************************/
!   fdintr(fdcu_t fdcu)
!   {
! !     fdc_p fdc = fdc_data + fdcu;
!       while(fdstate(fdcu, fdc));
    }

! --- 624,634 ----
!   \***********************************************************************/
!   fdintr(fdcu_t fdcu)
    {
! !     fdc_p fdc;
! ! #if NFT > 0
! !     if (fdc_mode == FDC_TAPE_MODE) return(ftintr(fdcu));
! ! #endif
! !     fdc = fdc_data + fdcu;
!       while(fdstate(fdcu, fdc));
!   }

diff -r -N -C3 ../ft~/driver/ft.c ./driver/ft.c
*** ../ft~/driver/ft.c  Wed Sep  8 19:28:14 1993
--- ./driver/ft.c       Tue Sep  7 22:22:31 1993
***************
*** 56,63 ****

  /* The following items are needed from the fd driver. */
  extern int Fdopen();                  /* fd open function */
! extern int in_fdc();                  /* read fdc registers */
! extern int out_fdc();                 /* write fdc registers */
  #undef NFD
  #define NFD   2                       /* Ugh. */

--- 56,63 ----

  /* The following items are needed from the fd driver. */
  extern int Fdopen();                  /* fd open function */
! extern int in_fdc(int fdc);             /* read fdc registers */
! extern int out_fdc(int fdc, int x);     /* write fdc registers */
  #undef NFD
  #define NFD   2                       /* Ugh. */

***************
*** 299,307 ****
                newcn = (cmd <= ft_pcn) ? ft_pcn - cmd : ft_pcn + cmd;
                async_state = 1;
                i = 0;
!               if (out_fdc(NE7CMD_SEEK) < 0) i = 1;
!               if (!i && out_fdc(0x00) < 0) i = 1;
!               if (!i && out_fdc(newcn) < 0) i = 1;
                if (i) {
                        if (++async_retries >= 10) {
                                printf("ft0: async_cmd command seek failed!!\n");
--- 299,307 ----
                newcn = (cmd <= ft_pcn) ? ft_pcn - cmd : ft_pcn + cmd;
                async_state = 1;
                i = 0;
!               if (out_fdc(0, NE7CMD_SEEK) < 0) i = 1;
!               if (!i && out_fdc(0, 0x00) < 0) i = 1;
!               if (!i && out_fdc(0, newcn) < 0) i = 1;
                if (i) {
                        if (++async_retries >= 10) {
                                printf("ft0: async_cmd command seek failed!!\n");
***************
*** 313,321 ****
                }
                break;
            case 1:
!               out_fdc(NE7CMD_SENSEI);
!               st0 = in_fdc();
!               pcn = in_fdc();
                if (st0 < 0 || pcn < 0 || newcn != pcn) {
                        if (++async_retries >= 10) {
                                printf("ft0: async_cmd seek retries exceeded\n");
--- 313,321 ----
                }
                break;
            case 1:
!               out_fdc(0, NE7CMD_SENSEI);
!               st0 = in_fdc(0);
!               pcn = in_fdc(0);
                if (st0 < 0 || pcn < 0 || newcn != pcn) {
                        if (++async_retries >= 10) {
                                printf("ft0: async_cmd seek retries exceeded\n");
***************
*** 357,365 ****
                CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
                /* NOTREACHED */
            case 1:
!               out_fdc(NE7CMD_SENSED);
!               out_fdc(0x00);
!               st3 = in_fdc();
                if (st3 < 0) {
                        printf("ft0: async_status timed out on bit %d r=$%02x\n",bitn,retval);
                        async_ret = -1;
--- 357,365 ----
                CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
                /* NOTREACHED */
            case 1:
!               out_fdc(0, NE7CMD_SENSED);
!               out_fdc(0, 0x00);
!               st3 = in_fdc(0);
                if (st3 < 0) {
                        printf("ft0: async_status timed out on bit %d r=$%02x\n",bitn,retval);
                        async_ret = -1;
***************
*** 467,477 ****
                        /* NOTREACHED */
                }
                async_state = 1;
!               out_fdc(0x4a);          /* READ_ID */
!               out_fdc(0);
                break;
            case 1:
!               for (i = 0; i < 7; i++) rid[i] = in_fdc();
                async_ret = (rid[3]*ftg->g_fdtrk) + (rid[4]*ftg->g_fdside) + rid[5] - 1;
                DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
                        rid[0], rid[1], rid[2], rid[3], rid[4], rid[5], async_ret));
--- 467,477 ----
                        /* NOTREACHED */
                }
                async_state = 1;
!               out_fdc(0, 0x4a);          /* READ_ID */
!               out_fdc(0, 0);
                break;
            case 1:
!               for (i = 0; i < 7; i++) rid[i] = in_fdc(0);
                async_ret = (rid[3]*ftg->g_fdtrk) + (rid[4]*ftg->g_fdside) + rid[5] - 1;
                DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
                        rid[0], rid[1], rid[2], rid[3], rid[4], rid[5], async_ret));
***************
*** 811,831 ****
       case 1:  /* Start DMA */
        /* Tape is now moving and in position-- start DMA now! */
        isa_dmastart(B_READ, ftxptr, QCV_BLKSIZE, 2);
!       out_fdc(0x66);                                  /* read */
!       out_fdc(0x00);                                  /* unit */
!       out_fdc((ftxblk % ftg->g_fdside) / ftg->g_fdtrk);       /* cylinder */
!       out_fdc(ftxblk / ftg->g_fdside);                        /* head */
!       out_fdc((ftxblk % ftg->g_fdtrk) + 1);           /* sector */
!       out_fdc(0x03);                                  /* 1K sectors */
!       out_fdc((ftxblk % ftg->g_fdtrk) + 1);           /* count */
!       out_fdc(0x74);                                  /* gap length */
!       out_fdc(0xff);                                  /* transfer size */
        ard_state = 2;
        break;

       case 2:  /* DMA completed */
        /* Check for positional error. */
!       for (i = 0; i < 7; i++) rddta[i] = in_fdc();
        isa_dmadone(B_READ, ftxptr, QCV_BLKSIZE, 2);

        i = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5] - 1;
--- 811,831 ----
       case 1:  /* Start DMA */
        /* Tape is now moving and in position-- start DMA now! */
        isa_dmastart(B_READ, ftxptr, QCV_BLKSIZE, 2);
!       out_fdc(0, 0x66);                                  /* read */
!       out_fdc(0, 0x00);                                  /* unit */
!       out_fdc(0, (ftxblk % ftg->g_fdside) / ftg->g_fdtrk);       /* cylinder */
!       out_fdc(0, ftxblk / ftg->g_fdside);                        /* head */
!       out_fdc(0, (ftxblk % ftg->g_fdtrk) + 1);           /* sector */
!       out_fdc(0, 0x03);                                  /* 1K sectors */
!       out_fdc(0, (ftxblk % ftg->g_fdtrk) + 1);           /* count */
!       out_fdc(0, 0x74);                                  /* gap length */
!       out_fdc(0, 0xff);                                  /* transfer size */
        ard_state = 2;
        break;

       case 2:  /* DMA completed */
        /* Check for positional error. */
!       for (i = 0; i < 7; i++) rddta[i] = in_fdc(0);
        isa_dmadone(B_READ, ftxptr, QCV_BLKSIZE, 2);

        i = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5] - 1;
***************
*** 923,943 ****
       case 1:  /* Start DMA */
        /* Tape is now moving and in position-- start DMA now! */
        isa_dmastart(B_WRITE, ftxptr, QCV_BLKSIZE, 2);
!       out_fdc(0x45);                                  /* write */
!       out_fdc(0x00);                                  /* unit */
!       out_fdc((ftxblk % ftg->g_fdside) / ftg->g_fdtrk);       /* cylinder */
!       out_fdc(ftxblk / ftg->g_fdside);                        /* head */
!       out_fdc((ftxblk % ftg->g_fdtrk) + 1);           /* sector */
!       out_fdc(0x03);                                  /* 1K sectors */
!       out_fdc((ftxblk % ftg->g_fdtrk) + 1);           /* count */
!       out_fdc(0x74);                                  /* gap length */
!       out_fdc(0xff);                                  /* transfer size */
        awr_state = 2;
        break;

       case 2:  /* DMA completed */
        /* Check for positional error. */
!       for (i = 0; i < 7; i++) rddta[i] = in_fdc();
        isa_dmadone(B_WRITE, ftxptr, QCV_BLKSIZE, 2);

        i = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5] - 1;
--- 923,943 ----
       case 1:  /* Start DMA */
        /* Tape is now moving and in position-- start DMA now! */
        isa_dmastart(B_WRITE, ftxptr, QCV_BLKSIZE, 2);
!       out_fdc(0, 0x45);                                  /* write */
!       out_fdc(0, 0x00);                                  /* unit */
!       out_fdc(0, (ftxblk % ftg->g_fdside) / ftg->g_fdtrk);       /* cylinder */
!       out_fdc(0, ftxblk / ftg->g_fdside);                        /* head */
!       out_fdc(0, (ftxblk % ftg->g_fdtrk) + 1);           /* sector */
!       out_fdc(0, 0x03);                                  /* 1K sectors */
!       out_fdc(0, (ftxblk % ftg->g_fdtrk) + 1);           /* count */
!       out_fdc(0, 0x74);                                  /* gap length */
!       out_fdc(0, 0xff);                                  /* transfer size */
        awr_state = 2;
        break;

       case 2:  /* DMA completed */
        /* Check for positional error. */
!       for (i = 0; i < 7; i++) rddta[i] = in_fdc(0);
        isa_dmadone(B_WRITE, ftxptr, QCV_BLKSIZE, 2);

        i = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5] - 1;
***************
*** 1016,1024 ****

    /* Get interrupt status */
    if (ftcmd_wait != FTCMD_READID) {
!       out_fdc(NE7CMD_SENSEI);
!       st0 = in_fdc();
!       pcn = in_fdc();
    }

    if (ftcmd_wait == FTCMD_NONE || ftsts_wait != FTSTS_SNOOZE) {
--- 1016,1024 ----

    /* Get interrupt status */
    if (ftcmd_wait != FTCMD_READID) {
!       out_fdc(0, NE7CMD_SENSEI);
!       st0 = in_fdc(0);
!       pcn = in_fdc(0);
    }

    if (ftcmd_wait == FTCMD_NONE || ftsts_wait != FTSTS_SNOOZE) {
***************
*** 1042,1048 ****
                printf("ft0: seek error st0 = $%02x pcn = %d\n", st0, pcn);
        break;
       case FTCMD_READID:
!       for (i = 0; i < 7; i++) rid[i] = in_fdc();
        ftsts_wait = FTSTS_INTERRUPT;
        wakeup(&ftsem.wait_chan);
        break;
--- 1042,1048 ----
                printf("ft0: seek error st0 = $%02x pcn = %d\n", st0, pcn);
        break;
       case FTCMD_READID:
!       for (i = 0; i < 7; i++) rid[i] = in_fdc(0);
        ftsts_wait = FTSTS_INTERRUPT;
        wakeup(&ftsem.wait_chan);
        break;
***************
*** 1093,1101 ****
            case FTCMD_RECAL:
            case FTCMD_SEEK:
                for (retries = 0; retries < 10000; retries++) {
!                       out_fdc(NE7CMD_SENSEI);
!                       st0 = in_fdc();
!                       pcn = in_fdc();
                        if (st0 & 0x20) {
                                ftsts_wait = FTSTS_INTERRUPT;
                                ft_pcn = pcn;
--- 1093,1103 ----
            case FTCMD_RECAL:
            case FTCMD_SEEK:
                for (retries = 0; retries < 10000; retries++) {
!                       out_fdc(0, NE7CMD_SENSEI);
!                       st0 = in_fdc(0);
!                       pcn = in_fdc(0);
!                       DPRT(("ft0(0x%x): ftintr_wait: st0=0x%x, pcn=0x%x\n",
!                               fdc, (unsigned char) st0, (unsigned char) pcn));
                        if (st0 & 0x20) {
                                ftsts_wait = FTSTS_INTERRUPT;
                                ft_pcn = pcn;
***************
*** 1138,1150 ****

    DPRT(("tape_recal start\n"));

!   out_fdc(NE7CMD_SPECIFY);
!   out_fdc((totape) ? 0xAD : 0xDF);
!   out_fdc(0x02);

    s = splbio();
!   out_fdc(NE7CMD_RECAL);
!   out_fdc(0x00);
    if (ftintr_wait(FTCMD_RECAL, 100)) {
        splx(s);
        printf("ft0: recalibrate timeout\n");
--- 1140,1152 ----

    DPRT(("tape_recal start\n"));

!   out_fdc(0, NE7CMD_SPECIFY);
!   out_fdc(0, (totape) ? 0xAD : 0xDF);
!   out_fdc(0, 0x02);

    s = splbio();
!   out_fdc(0, NE7CMD_RECAL);
!   out_fdc(0, 0x00);
    if (ftintr_wait(FTCMD_RECAL, 100)) {
        splx(s);
        printf("ft0: recalibrate timeout\n");
***************
*** 1152,1160 ****
    }
    splx(s);

!   out_fdc(NE7CMD_SPECIFY);
!   out_fdc((totape) ? 0xFD : 0xDF);
!   out_fdc(0x02);

    DPRT(("tape_recal end\n"));
    return(0);
--- 1154,1162 ----
    }
    splx(s);

!   out_fdc(0, NE7CMD_SPECIFY);
!   out_fdc(0, (totape) ? 0xFD : 0xDF);
!   out_fdc(0, 0x02);

    DPRT(("tape_recal end\n"));
    return(0);
***************
*** 1205,1213 ****

    /* Perform seek */
    s = splbio();
!   out_fdc(NE7CMD_SEEK);
!   out_fdc(0x00);
!   out_fdc(newcn);

    if (ftintr_wait(FTCMD_SEEK, 250)) {
        DPRT(("ft0: tape_cmd seek timeout\n"));
--- 1207,1215 ----

    /* Perform seek */
    s = splbio();
!   out_fdc(0, NE7CMD_SEEK);
!   out_fdc(0, 0x00);
!   out_fdc(0, newcn);

    if (ftintr_wait(FTCMD_SEEK, 250)) {
        DPRT(("ft0: tape_cmd seek timeout\n"));
***************
*** 1541,1549 ****
    }

    /* Sense drive status */
!   out_fdc(NE7CMD_SENSED);
!   out_fdc(0x00);
!   st3 = in_fdc();

    if ((st3 & 0x10) == 0) {    /* track 0 */
        DPRT(("qic_status has dead drive...  st3 = $%02x\n", st3));
--- 1543,1551 ----
    }

    /* Sense drive status */
!   out_fdc(0, NE7CMD_SENSED);
!   out_fdc(0, 0x00);
!   st3 = in_fdc(0);

    if ((st3 & 0x10) == 0) {    /* track 0 */
        DPRT(("qic_status has dead drive...  st3 = $%02x\n", st3));
***************
*** 1558,1566 ****
                return(-1);
        }

!       out_fdc(NE7CMD_SENSED);
!       out_fdc(0x00);
!       st3 = in_fdc();
        if (st3 < 0) {
                printf("ft0: controller timed out on bit %d r=$%02x\n",i,r);
                return(-1);
--- 1560,1568 ----
                return(-1);
        }

!       out_fdc(0, NE7CMD_SENSED);
!       out_fdc(0, 0x00);
!       st3 = in_fdc(0);
        if (st3 < 0) {
                printf("ft0: controller timed out on bit %d r=$%02x\n",i,r);
                return(-1);
diff -r -N -C3 ../ft~/tools/ftinfo.c ./tools/ftinfo.c
*** ../ft~/tools/ftinfo.c
--- ./tools/ftinfo.c    Wed Sep  8 23:32:46 1993
***************
*** 0 ****
--- 1,39 ----
+ #include <unistd.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <sys/file.h>
+ #include <sys/ftape.h>
+
+ QIC_Segment s;
+
+ void main (int argc, char **argv)
+ {
+       int fd;
+       QIC_Geom g;
+
+       if (argc != 1) {
+               fprintf (stderr, "usage: ftinfo\n");
+               exit (1);
+       }
+       if ((fd = open ("/dev/ft0a", 0)) < 0) {
+               perror ("/dev/ft0a");
+               exit (1);
+       }
+       if (ioctl (fd, QIOGEOM, &g) < 0) {
+               perror ("QIOGEOM");
+               exit (1);
+       }
+       close (fd);
+       printf ("                 Tape format: %.16s (#%d)\n",
+               g.g_fmtdesc, g.g_fmtno);
+       printf ("                 Tape length: %.16s (#%d)\n",
+               g.g_lendesc, g.g_lenno);
+       printf ("   Number of tracks per tape: %d\n", g.g_trktape);
+       printf ("Number of segments per track: %d\n", g.g_segtrk);
+       printf ("  Number of blocks per track: %d\n", g.g_blktrk);
+       printf ("   Floppy disk sectors/track: %d\n", g.g_fdtrk);
+       printf ("       Floppy disk cylinders: %d\n", g.g_fdside/g.g_fdtrk);
+       printf ("           Floppy disk heads: %d\n",
+               g.g_trktape*g.g_blktrk / g.g_fdside);
+       printf ("    Floppy disk sectors/side: %d\n", g.g_fdside);
+ }