home *** CD-ROM | disk | FTP | other *** search
/ Computer Select (Limited Edition) / Computer Select.iso / dobbs / v17n02 / uxdriver.exe / WD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-19  |  31.3 KB  |  1,114 lines

  1. /* wd.c
  2.  * Copyright (c) 1990 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * William Jolitz.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  27. PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
  33. STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  *
  38.  *    @(#)wd.c    7.2 (Berkeley) 5/9/91
  39.  */
  40.  
  41. #include "wd.h"
  42. #if    NWD > 0
  43.  
  44. #include "param.h"
  45. #include "dkbad.h"
  46. #include "systm.h"
  47. #include "conf.h"
  48. #include "file.h"
  49. #include "stat.h"
  50. #include "ioctl.h"
  51. #include "disklabel.h"
  52. #include "buf.h"
  53. #include "uio.h"
  54. #include "i386/isa/isa_device.h"
  55. #include "i386/isa/icu.h"
  56. #include "i386/isa/wdreg.h"
  57. #include "syslog.h"
  58. #include "vm/vm.h"
  59.  
  60. #define    RETRIES        5    /* number of retries before giving up */
  61. #define    MAXTRANSFER    32    /* max size of transfer in page clusters */
  62.  
  63. #define wdctlr(dev)    ((minor(dev) & 0x80) >> 7)
  64. #define wdunit(dev)    ((minor(dev) & 0x60) >> 5)
  65. #define wdpart(dev)    ((minor(dev) & 0x1f))
  66.  
  67. #define b_cylin    b_resid        /* cylinder number for doing IO to */
  68.                 /* shares an entry in the buf struct */
  69.  
  70. /*
  71.  * Drive states.  Used for open and format operations.
  72.  * States < OPEN (> 0) are transient, during an open operation.
  73.  * OPENRAW is used for unlabeled disks, and for floppies, to inhibit
  74.  * bad-sector forwarding.
  75.  */
  76. #define RAWDISK        8        /* raw disk operation, no translation*/
  77. #define ISRAWSTATE(s)    (RAWDISK&(s))    /* are we in a raw state? */
  78. #define DISKSTATE(s)    (~RAWDISK&(s))    /* are we in a given state regardless
  79.                        of raw or cooked mode? */
  80.  
  81. #define    CLOSED        0        /* disk is closed. */
  82.                     /* "cooked" disk states */
  83. #define    WANTOPEN    1        /* open requested, not started */
  84. #define    RECAL        2        /* doing restore */
  85. #define    RDLABEL        3        /* reading pack label */
  86. #define    RDBADTBL    4        /* reading bad-sector table */
  87. #define    OPEN        5        /* done with open */
  88.  
  89. #define    WANTOPENRAW    (WANTOPEN|RAWDISK)    /* raw WANTOPEN */
  90. #define    RECALRAW    (RECAL|RAWDISK)    /* raw open, doing restore */
  91. #define    OPENRAW        (OPEN|RAWDISK)    /* open, but unlabeled disk or floppy */
  92.  
  93.  
  94. /*
  95.  * The structure of a disk drive.
  96.  */
  97. struct    disk {
  98.     struct disklabel dk_dd;    /* device configuration data */
  99.     long    dk_bc;        /* byte count left */
  100.     short    dk_skip;    /* blocks already transferred */
  101.     char    dk_unit;    /* physical unit number */
  102.     char    dk_state;    /* control state */
  103.     u_char    dk_status;    /* copy of status reg. */
  104.     u_char    dk_error;    /* copy of error reg. */
  105.     short    dk_open;    /* open/closed refcnt */
  106.         u_long  dk_copenpart;   /* character units open on this drive */
  107.         u_long  dk_bopenpart;   /* block units open on this drive */
  108.         u_long  dk_openpart;    /* all units open on this drive */
  109.     short    dk_wlabel;    /* label writable? */
  110. };
  111.  
  112. /*
  113.  * This label is used as a default when initializing a new or raw disk.
  114.  * It really only lets us access the first track until we know more.
  115.  */
  116. struct disklabel dflt_sizes = {
  117.     DISKMAGIC, DTYPE_ST506, 0, "default", "",
  118.         512,        /* sector size */
  119.         17,        /* # of sectors per track */
  120.         8,        /* # of tracks per cylinder */
  121.         766,        /* # of cylinders per unit */
  122.         17*8,        /* # of sectors per cylinder */
  123.         766*8*17,    /* # of sectors per unit */
  124.         0,        /* # of spare sectors per track */
  125.         0,        /* # of spare sectors per cylinder */
  126.         0,        /* # of alt. cylinders per unit */
  127.         3600,        /* rotational speed */
  128.         1,        /* hardware sector interleave */
  129.         0,        /* sector 0 skew, per track */
  130.         0,        /* sector 0 skew, per cylinder */
  131.         0,        /* head switch time, usec */
  132.         0,        /* track-to-track seek, usec */
  133.         0,        /* generic flags */
  134.         0,0,0,0,0,
  135.         0,0,0,0,0,
  136.         DISKMAGIC,
  137.         0,
  138.         8,
  139.         8192,
  140.         8192,
  141.     
  142.     {{21600,    0, 0,0,0,0},    /* A=root filesystem */
  143.     {21600,    40, 0,0,0,0},
  144.     {660890, 0, 0,0,0,0},    /* C=whole disk */
  145.     {216000,    80, 0,0,0,0},
  146.     {0,    0, 0,0,0,0},
  147.     {0,    0, 0,0,0,0},
  148.     {0,    0, 0,0,0,0},
  149.     {399600,    480, 0,0,0,0}}
  150. };
  151.  
  152. static    struct    dkbad    dkbad[NWD];
  153. struct    disk    wddrives[NWD] = {0};    /* table of units */
  154. struct    buf    wdtab = {0};
  155. struct    buf    wdutab[NWD] = {0};    /* head of queue per drive */
  156. struct    buf    rwdbuf[NWD] = {0};    /* buffers for raw IO */
  157. long    wdxfer[NWD] = {0};        /* count of transfers */
  158. int    writeprotected[NWD] = { 0 };
  159. int    wdprobe(), wdattach(), wdintr();
  160. struct    isa_driver wddriver = {
  161.     wdprobe, wdattach, "wd",
  162. };
  163.  
  164.  
  165.  
  166.  
  167. static wdc;    /* port address of disk controller */
  168.  
  169. /*
  170.  * Probe routine
  171.  */
  172. wdprobe(dvp)
  173.     struct isa_device *dvp;
  174. {
  175.  
  176. #ifdef lint
  177.     wdintr(0);
  178. #endif
  179.     /* XXX sorry, needs to be better */
  180.     wdc = dvp->id_iobase;
  181.     outb(wdc+wd_error, 0x5a) ;    /* error register not writable */
  182.     outb(wdc+wd_cyl_lo, 0xa5) ;    /* but all of cyllo are implemented */
  183.     if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5)
  184.         return(1) ;
  185.     return (0);
  186. }
  187.  
  188. /*
  189.  * attach each drive if possible.
  190.  */
  191. wdattach(dvp)
  192.     struct isa_device *dvp;
  193. {
  194.     int unit = dvp->id_unit;
  195.  
  196.     outb(wdc+wd_ctlr,12);
  197.     DELAY(1000);
  198.     outb(wdc+wd_ctlr,8);
  199. }
  200.  
  201. /* Read/write routine for a buffer.  Finds the proper unit, range checks
  202.  * arguments, and schedules the transfer.  Does not wait for the transfer
  203.  * to complete.  Multi-page transfers are supported.  All I/O requests must
  204.  * be a multiple of a sector in length.
  205.  */
  206. wdstrategy(bp)
  207.     register struct buf *bp;    /* IO operation to perform */
  208. {
  209.     register struct buf *dp;
  210.     register struct disk *du;    /* Disk unit to do the IO.    */
  211.     register struct partition *p;
  212.     long maxsz, sz;
  213.     int    unit = wdunit(bp->b_dev);
  214.     int    s;
  215.  
  216.     /* check for a sane request */
  217.     if ((unit >= NWD) || (bp->b_blkno < 0)) {
  218.         printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n",
  219.             unit, bp->b_blkno, bp->b_bcount);
  220.         bp->b_flags |= B_ERROR;
  221.         goto bad;
  222.     }
  223.     if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) {
  224.         printf("wd%d: write protected\n", unit);
  225.         goto bad;
  226.     }
  227.  
  228.     /* have we a OPEN unit yet? */
  229.     du = &wddrives[unit];
  230.     if (DISKSTATE(du->dk_state) != OPEN)
  231.         goto q;
  232.  
  233.         /*
  234.          * Determine the size of the transfer, and make sure it is
  235.          * within the boundaries of the partition.
  236.          */
  237.         p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)];
  238.         maxsz = p->p_size;
  239.         sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
  240.         if (bp->b_blkno + p->p_offset <= LABELSECTOR &&
  241. #if LABELSECTOR != 0
  242.             bp->b_blkno + p->p_offset + sz > LABELSECTOR &&
  243. #endif
  244.             (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) {
  245.                 bp->b_error = EROFS;
  246.                 goto bad;
  247.         }
  248.         if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
  249.                 /* if exactly at end of disk, return an EOF */
  250.                 if (bp->b_blkno == maxsz) {
  251.                         bp->b_resid = bp->b_bcount;
  252.                         biodone(bp);
  253.                         return;
  254.                 }
  255.                 /* or truncate if part of it fits */
  256.                 sz = maxsz - bp->b_blkno;
  257.                 if (sz <= 0)
  258.                         goto bad;
  259.                 bp->b_bcount = sz << DEV_BSHIFT;
  260.         }
  261.         bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl;
  262.  
  263. q:    /* queue transfer */
  264.     dp = &wdutab[unit];
  265.     s = splhigh();
  266.     disksort(dp, bp);
  267.     if (dp->b_active == 0)
  268.         wdustart(du);        /* start drive if idle */
  269.     if (wdtab.b_active == 0)
  270.         wdstart(s);        /* start IO if controller idle */
  271.     splx(s);
  272.     return;
  273.  
  274. bad:
  275.     bp->b_error = EINVAL;
  276.     biodone(bp);
  277. }
  278.  
  279. /* Routine to queue a read or write command to the controller.  The request is
  280.  * linked into the active list for the controller.  If the controller is idle,
  281.  * the transfer is started.
  282.  */
  283. wdustart(du)
  284.     register struct disk *du;
  285. {
  286.     register struct buf *bp, *dp;
  287.  
  288.     dp = &wdutab[du->dk_unit];
  289.     if (dp->b_active)
  290.         return;
  291.     bp = dp->b_actf;
  292.     if (bp == NULL)
  293.         return;    
  294.     dp->b_forw = NULL;
  295.     if (wdtab.b_actf  == NULL)        /* link unit into active list */
  296.         wdtab.b_actf = dp;
  297.     else
  298.         wdtab.b_actl->b_forw = dp;
  299.     wdtab.b_actl = dp;
  300.     dp->b_active = 1;        /* mark the drive as busy */
  301. }
  302.  
  303. /*
  304.  * Controller startup routine.  This does the calculation, and starts
  305.  * a single-sector read or write operation.  Called to start a transfer,
  306.  * or from the interrupt routine to continue a multi-sector transfer.
  307.  * RESTRICTIONS:
  308.  * 1.    The transfer length must be an exact multiple of the sector size.
  309.  */
  310.  
  311. static wd_sebyse;    /* must we work sector by sector because of error */
  312.  
  313. wdstart()
  314. {
  315.     register struct disk *du;    /* disk unit for IO */
  316.     register struct buf *bp;
  317.     struct buf *dp;
  318.     register struct bt_bad *bt_ptr;
  319.     long    blknum, pagcnt, cylin, head, sector;
  320.     long    secpertrk, secpercyl, addr, i;
  321.     int    unit, s;
  322.  
  323. loop:
  324.     /* extract from queue */
  325.     dp = wdtab.b_actf;
  326.     if (dp == NULL)
  327.         return;
  328.     bp = dp->b_actf;
  329.     if (bp == NULL) {
  330.         wdtab.b_actf = dp->b_forw;
  331.         goto loop;
  332.     }
  333.  
  334.     /* are we a control request or a real transfer */
  335.     unit = wdunit(bp->b_dev);
  336.     du = &wddrives[unit];
  337.     if (DISKSTATE(du->dk_state) <= RDLABEL) {
  338.         if (wdcontrol(bp)) {
  339.             dp->b_actf = bp->av_forw;
  340.             goto loop;    /* done */
  341.         }
  342.         return;
  343.     }
  344.  
  345.     secpertrk = du->dk_dd.d_nsectors;
  346.     secpercyl = du->dk_dd.d_secpercyl;
  347.     /*
  348.      * Convert DEV_BSIZE "blocks" to sectors.
  349.      */
  350.     blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize
  351.         + du->dk_skip;
  352. #ifdef    WDDEBUG
  353.     if (du->dk_skip == 0) {
  354.         dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit,
  355.             (bp->b_flags & B_READ) ? "read" : "write",
  356.             bp->b_bcount, blknum);
  357.     } else {
  358.         dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts));
  359.     }
  360. #endif
  361.  
  362.     /* where to and how much */
  363.     addr = (int) bp->b_un.b_addr;
  364.     if (du->dk_skip==0)
  365.         du->dk_bc = bp->b_bcount;
  366.  
  367.     /* physical location on the disk drive */
  368.     cylin = blknum / secpercyl;
  369.     head = (blknum % secpercyl) / secpertrk;
  370.     sector = blknum % secpertrk;
  371.     if (DISKSTATE(du->dk_state) == OPEN)
  372.         cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset
  373.                 / secpercyl;
  374.  
  375.     /* 
  376.      * See if the current block is in the bad block list.
  377.      * (If we have one, and not formatting.)
  378.      */
  379.     if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse)
  380.         for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
  381.         if (bt_ptr->bt_cyl > cylin)
  382.             /* Sorted list, and we passed our cylinder. quit. */
  383.             break;
  384.         if (bt_ptr->bt_cyl == cylin &&
  385.                 bt_ptr->bt_trksec == (head << 8) + sector) {
  386.             /*
  387.              * Found bad block.  Calculate new block addr.
  388.              * This starts at the end of the disk (skip the
  389.              * last track which is used for the bad block list),
  390.              * and works backwards to the front of the disk.
  391.              */
  392. #ifdef    WDDEBUG
  393.                 dprintf(DDSK,"--- badblock code -> Old = %d; ",
  394.                 blknum);
  395. #endif
  396.             blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
  397.                 - (bt_ptr - dkbad[unit].bt_bad) - 1;
  398.             cylin = blknum / secpercyl;
  399.             head = (blknum % secpercyl) / secpertrk;
  400.             sector = blknum % secpertrk;
  401. #ifdef    WDDEBUG
  402.                 dprintf(DDSK, "new = %d\n", blknum);
  403. #endif
  404.             break;
  405.         }
  406.     }
  407.  
  408.     sector += 1;    /* sectors begin with 1, not 0 */
  409.  
  410.     wdtab.b_active = 1;        /* mark controller active */
  411.  
  412.     /* do we need to supply parameters to controller and a command? */
  413.     if(du->dk_skip==0 || wd_sebyse) {
  414.         /* bump counter if write or error */
  415.         if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0)
  416.             du->dk_bc += DEV_BSIZE;
  417.  
  418.         /* "Excuse me worker, I'll only be a nanosecond" -- FST */
  419.         while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ;
  420.  
  421.         /* many or one */
  422.         if(wd_sebyse)
  423.             outb(wdc+wd_seccnt, 1);
  424.         else
  425.             outb(wdc+wd_seccnt, ((du->dk_bc + DEV_BSIZE - 1) / DEV_BSIZE));
  426.  
  427.         /* update controller with where we are transferring from */
  428.         outb(wdc+wd_sector, sector);
  429.         outb(wdc+wd_cyl_lo, cylin);
  430.         outb(wdc+wd_cyl_hi, cylin >> 8);
  431.  
  432.         /* Set up the SDH register (select drive).     */
  433.         outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
  434.         while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
  435.  
  436.         /* Fire off command */
  437.         outb(wdc+wd_command,
  438.             (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
  439. #ifdef    WDDEBUG
  440.         dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n",
  441.                 sector, cylin, head, addr, inb(wdc+wd_altsts));
  442. #endif
  443.     }    /* otherwise, values have already been stuffed */
  444.         
  445.     /* If this is a read operation, just go away until it's done.    */
  446.     if (bp->b_flags & B_READ) return;
  447.  
  448.     /* Ready to send data?    */
  449.     while ((inb(wdc+wd_status) & WDCS_DRQ) == 0);
  450.  
  451.     /* ASSUMES CONTIGUOUS MEMORY -- e.g. MMU is mapping the address */
  452.     outsw (wdc+wd_data, addr+du->dk_skip*DEV_BSIZE, DEV_BSIZE/2);
  453.     du->dk_bc -= DEV_BSIZE;
  454. }
  455.  
  456. /*
  457.  * these are globally defined so they can be found
  458.  * by the debugger easily in the case of a system crash
  459.  */
  460. daddr_t wd_errsector;
  461. daddr_t wd_errbn;
  462. unsigned char wd_errstat;
  463.  
  464. /* Interrupt routine for the controller.  Acknowledge the interrupt, check for
  465.  * errors on the current operation, mark it done if necessary, and start
  466.  * the next request.  Also check for a partially done transfer, and
  467.  * continue with the next chunk if so.
  468.  */
  469. wdintr(unit)
  470. {
  471.     register struct    disk *du;
  472.     register struct buf *bp, *dp;
  473.     int status;
  474.     char partch ;
  475.     static wd_haderror;
  476.  
  477.     /* Shouldn't need this, but it may be a slow controller.    */
  478.     while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
  479.     if (!wdtab.b_active) {
  480.         printf("wd: extra interrupt\n");
  481.         return;
  482.     }
  483.  
  484. #ifdef    WDDEBUG
  485.     dprintf(DDSK,"I ");
  486. #endif
  487.  
  488.     /* commonly used during this transfer */
  489.     dp = wdtab.b_actf;
  490.     bp = dp->b_actf;
  491.     du = &wddrives[wdunit(bp->b_dev)];
  492.     partch = wdpart(bp->b_dev) + 'a';
  493.  
  494.     /* control or transfer operation */
  495.     if (DISKSTATE(du->dk_state) <= RDLABEL) {
  496.         if (wdcontrol(bp))
  497.             goto done;
  498.         return;
  499.     }
  500.  
  501.     /* failed transfer? */
  502.     if (status & (WDCS_ERR | WDCS_ECCCOR)) {
  503.         wd_errstat = inb(wdc+wd_error);        /* save error status */
  504. #ifdef    WDDEBUG
  505.         printf("status %x error %x\n", status, wd_errstat);
  506. #endif
  507.         if(wd_sebyse == 0) {
  508.             wd_haderror = 1;
  509.             goto outt;
  510.         }
  511.         
  512.         wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) +
  513.             (((unsigned long) bp->b_blkno * DEV_BSIZE /
  514.                 du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) +
  515.             du->dk_skip;
  516.         wd_errbn = bp->b_blkno
  517.             + du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ;
  518.         if (status & WDCS_ERR) {
  519.             if (++wdtab.b_errcnt < RETRIES) {
  520.                 wdtab.b_active = 0;
  521.             } else {
  522.                 printf("wd%d%c: ", du->dk_unit, partch);
  523.                 printf(
  524.                 "hard %s error, sn %d bn %d status %b error %b\n",
  525.                     (bp->b_flags & B_READ)? "read":"write",
  526.                     wd_errsector, wd_errbn, status, WDCS_BITS,
  527.                     wd_errstat, WDERR_BITS);
  528.                 bp->b_flags |= B_ERROR;    /* flag the error */
  529.             }
  530.         } else
  531.             log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
  532.                 du->dk_unit, partch, wd_errsector,
  533.                 wd_errbn);
  534.     }
  535. outt:
  536.  
  537.     /*
  538.      * If this was a successful read operation, fetch the data.
  539.      */
  540.     if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
  541.         int chk, dummy;
  542.  
  543.         chk = min(DEV_BSIZE/2,du->dk_bc/2);
  544.         /* Ready to receive data?    */
  545.         while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
  546.  
  547.         du->dk_bc -= 2*chk;
  548.         while (chk++ < DEV_BSIZE/2) insw (wdc+wd_data,&dummy,1);
  549.     }
  550.  
  551.     /* done transfer, thank you may I have another */
  552.     wdxfer[du->dk_unit]++;
  553.     if (wdtab.b_active) {
  554.         if ((bp->b_flags & B_ERROR) == 0) {
  555.             du->dk_skip++;        /* Add to successful sectors. */
  556.             if (wdtab.b_errcnt) {
  557.                 log(LOG_WARNING, "wd%d%c: ",
  558.                         du->dk_unit, partch);
  559.                 log(LOG_WARNING,
  560.             "soft %s error, sn %d bn %d error %b retries %d\n",
  561.                     (bp->b_flags & B_READ) ? "read" : "write",
  562.                     wd_errsector, wd_errbn, wd_errstat,
  563.                     WDERR_BITS, wdtab.b_errcnt);
  564.             }
  565.             wdtab.b_errcnt = 0;
  566.  
  567.             /* see if more to transfer */
  568.             if (du->dk_bc > 0 && wd_haderror == 0) {
  569.                 wdstart();
  570.                 return;        /* next chunk is started */
  571.             } else if (wd_haderror && wd_sebyse == 0) {
  572.                 du->dk_skip = 0;
  573.                 wd_haderror = 0;
  574.                 wd_sebyse = 1;
  575.                 wdstart();
  576.                 return;        /* redo xfer sector by sector */
  577.             }
  578.         }
  579.  
  580. done:
  581.         /* done with this transfer, with or without error */
  582.         wdtab.b_actf = dp->b_forw;
  583.         wdtab.b_errcnt = 0;
  584.         du->dk_skip = 0;
  585.         dp->b_active = 0;
  586.         dp->b_actf = bp->av_forw;
  587.         dp->b_errcnt = 0;
  588.         bp->b_resid = 0;
  589.         wd_sebyse = 0;
  590.         biodone(bp);
  591.     }
  592.     wdtab.b_active = 0;
  593.     if (dp->b_actf)
  594.         wdustart(du);        /* requeue disk if more io to do */
  595.     if (wdtab.b_actf)
  596.         wdstart();        /* start IO on next drive */
  597. }
  598.  
  599. /*
  600.  * Initialize a drive.
  601.  */
  602. wdopen(dev, flags, fmt)
  603.         dev_t dev;
  604.         int flags, fmt;
  605. {
  606.     register unsigned int unit;
  607.     register struct buf *bp;
  608.     register struct disk *du;
  609.         int part = wdpart(dev), mask = 1 << part;
  610.         struct partition *pp;
  611.     struct dkbad *db;
  612.     int i, error = 0;
  613.  
  614.     unit = wdunit(dev);
  615.     if (unit >= NWD) return (ENXIO) ;
  616.     du = &wddrives[unit];
  617.     du->dk_unit = unit;
  618.     wdutab[unit].b_actf = NULL;
  619.  
  620.     if (flags & O_NDELAY)
  621.         du->dk_state = WANTOPENRAW;
  622.     else
  623.         du->dk_state = WANTOPEN;
  624.     /*
  625.      * Use the default sizes until we've read the label,
  626.      * or longer if there isn't one there.
  627.      */
  628.     du->dk_dd = dflt_sizes;
  629.  
  630.     /*
  631.      * Recal, read of disk label will be done in wdcontrol
  632.      * during first read operation.
  633.      */
  634.     bp = geteblk(DEV_BSIZE);
  635.     bp->b_dev = dev & 0xff00;
  636.     bp->b_bcount = 0;
  637.     bp->b_blkno = LABELSECTOR;
  638.     bp->b_flags = B_READ;
  639.     wdstrategy(bp);
  640.     biowait(bp);
  641.     if (bp->b_flags & B_ERROR) {
  642.         error = ENXIO;
  643.         du->dk_state = CLOSED;
  644.         goto done;
  645.     }
  646.     if (du->dk_state == OPENRAW) {
  647.         du->dk_state = OPENRAW;
  648.         goto done;
  649.     }
  650.     /*
  651.      * Read bad sector table into memory.
  652.      */
  653.     i = 0;
  654.     do {
  655.         bp->b_flags = B_BUSY | B_READ;
  656.         bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
  657.             + i;
  658.         if (du->dk_dd.d_secsize > DEV_BSIZE)
  659.             bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE;
  660.         else
  661.             bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize;
  662.         bp->b_bcount = du->dk_dd.d_secsize;
  663.         bp->b_cylin = du->dk_dd.d_ncylinders - 1;
  664.         wdstrategy(bp);
  665.         biowait(bp);
  666.     } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
  667.         i < du->dk_dd.d_nsectors);
  668.     db = (struct dkbad *)(bp->b_un.b_addr);
  669. #define DKBAD_MAGIC 0x4321
  670.     if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
  671.         db->bt_flag == DKBAD_MAGIC) {
  672.         dkbad[unit] = *db;
  673.         du->dk_state = OPEN;
  674.     } else {
  675.         printf("wd%d: %s bad-sector file\n", unit,
  676.             (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
  677.         error = ENXIO ;
  678.         du->dk_state = OPENRAW;
  679.     }
  680. done:
  681.     bp->b_flags = B_INVAL | B_AGE;
  682.     brelse(bp);
  683.     if (error == 0)
  684.         du->dk_open = 1;
  685.  
  686.         /*
  687.          * Warn if a partion is opened
  688.          * that overlaps another partition which is open
  689.          * unless one is the "raw" partition (whole disk).
  690.          */
  691. #define RAWPART         8               /* 'x' partition */     /* XXX */
  692.         if ((du->dk_openpart & mask) == 0 && part != RAWPART) {
  693.         int    start, end;
  694.  
  695.                 pp = &du->dk_dd.d_partitions[part];
  696.                 start = pp->p_offset;
  697.                 end = pp->p_offset + pp->p_size;
  698.                 for (pp = du->dk_dd.d_partitions;
  699.                      pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
  700.             pp++) {
  701.                         if (pp->p_offset + pp->p_size <= start ||
  702.                             pp->p_offset >= end)
  703.                                 continue;
  704.                         if (pp - du->dk_dd.d_partitions == RAWPART)
  705.                                 continue;
  706.                         if (du->dk_openpart & (1 << (pp -
  707.                     du->dk_dd.d_partitions)))
  708.                                 log(LOG_WARNING,
  709.                                     "wd%d%c: overlaps open partition (%c)\n",
  710.                                     unit, part + 'a',
  711.                                     pp - du->dk_dd.d_partitions + 'a');
  712.                 }
  713.         }
  714.         if (part >= du->dk_dd.d_npartitions)
  715.                 return (ENXIO);
  716.         du->dk_openpart |= mask;
  717.         switch (fmt) {
  718.         case S_IFCHR:
  719.                 du->dk_copenpart |= mask;
  720.                 break;
  721.         case S_IFBLK:
  722.                 du->dk_bopenpart |= mask;
  723.                 break;
  724.         }
  725.     return (error);
  726. }
  727.  
  728. /*
  729.  * Implement operations other than read/write.
  730.  * Called from wdstart or wdintr during opens and formats.
  731.  * Uses finite-state-machine to track progress of operation in progress.
  732.  * Returns 0 if operation still in progress, 1 if completed.
  733.  */
  734. wdcontrol(bp)
  735.     register struct buf *bp;
  736. {
  737.     register struct disk *du;
  738.     register unit;
  739.     unsigned char  stat;
  740.     int s, cnt;
  741.     extern int bootdev, cyloffset;
  742.  
  743.     du = &wddrives[wdunit(bp->b_dev)];
  744.     unit = du->dk_unit;
  745.     switch (DISKSTATE(du->dk_state)) {
  746.  
  747.     tryagainrecal:
  748.     case WANTOPEN:            /* set SDH, step rate, do restore */
  749.         s = splbio();        /* not called from intr level ... */
  750.         outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
  751.         wdtab.b_active = 1;
  752.         outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
  753.         du->dk_state++;
  754.         splx(s);
  755.         return(0);
  756.  
  757.     case RECAL:
  758.         if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
  759.             printf("wd%d: recal", du->dk_unit);
  760.             if (unit == 0) {
  761.                 printf(": status %b error %b\n",
  762.                     stat, WDCS_BITS,
  763.                     inb(wdc+wd_error), WDERR_BITS);
  764.                 if (++wdtab.b_errcnt < RETRIES)
  765.                     goto tryagainrecal;
  766.             }
  767.             goto badopen;
  768.         }
  769.  
  770.         /* some compaq controllers require this ... */
  771.         wdsetctlr(bp->b_dev, du);
  772.  
  773.         wdtab.b_errcnt = 0;
  774.         if (ISRAWSTATE(du->dk_state)) {
  775.             du->dk_state = OPENRAW;
  776.             return(1);
  777.         }
  778. retry:
  779.         /*
  780.          * Read in sector LABELSECTOR to get the pack label
  781.          * and geometry.
  782.          */
  783.         outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
  784.         outb(wdc+wd_seccnt, 1);
  785.         outb(wdc+wd_sector, LABELSECTOR+1);
  786.         outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
  787.         outb(wdc+wd_cyl_hi, (cyloffset >> 8));
  788.         outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
  789.         outb(wdc+wd_command, WDCC_READ);
  790.         du->dk_state = RDLABEL;
  791.         return(0);
  792.  
  793.     case RDLABEL:
  794.         if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
  795.             if (++wdtab.b_errcnt < RETRIES)
  796.                 goto retry;
  797.             printf("wd%d: read label", unit);
  798.             goto badopen;
  799.         }
  800.  
  801.         insw(wdc+wd_data, bp->b_un.b_addr, DEV_BSIZE/2);
  802.  
  803.         if (((struct disklabel *)
  804.             (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) {
  805.                du->dk_dd =
  806.              * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
  807.         } else {
  808.             printf("wd%d: bad disk label\n", du->dk_unit);
  809.             du->dk_state = OPENRAW;
  810.         }
  811.  
  812.         s = splbio();        /* not called from intr level ... */
  813.         while ((stat = inb(wdc+wd_status)) & WDCS_BUSY);
  814.  
  815.         wdsetctlr(bp->b_dev, du);
  816.  
  817.         outb(wdc+wd_seccnt, 0);
  818.         splx(s);
  819.  
  820.         if (du->dk_state == RDLABEL)
  821.             du->dk_state = RDBADTBL;
  822.         /*
  823.          * The rest of the initialization can be done
  824.          * by normal means.
  825.          */
  826.         return(1);
  827.  
  828.     default:
  829.         panic("wdcontrol");
  830.     }
  831.     /* NOTREACHED */
  832.  
  833. badopen:
  834.     printf(": status %b error %b\n",
  835.         stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
  836.     du->dk_state = OPENRAW;
  837.     return(1);
  838. }
  839.  
  840. /*
  841.  * Now that we know what geometry the disk is, tell the controller
  842.  * (even and especially if it thinks it knows better, as in the
  843.  * case of "friendly" ones that silently assume DOS brain damage modes.
  844.  */
  845. wdsetctlr(dev, du) dev_t dev; struct disk *du; {
  846.     int stat;
  847.  
  848.     outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders);
  849.     outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8);
  850.     outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
  851.     outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
  852.     outb(wdc+wd_command, WDCC_SETGEOMETRY);
  853.  
  854.     while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ;
  855.     stat = inb(wdc+wd_error);
  856.     return(stat);
  857. }
  858.  
  859. /* ARGSUSED */
  860. wdclose(dev, flags, fmt)
  861.         dev_t dev;
  862.         int flags, fmt;
  863. {
  864.     register struct disk *du;
  865.  
  866.     du = &wddrives[wdunit(dev)];
  867.     du->dk_open-- ;
  868.     /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
  869. }
  870.  
  871. wdioctl(dev,cmd,addr,flag)
  872.     dev_t dev;
  873.     caddr_t addr;
  874. {
  875.     int unit = wdunit(dev);
  876.     register struct disk *du;
  877.     int error = 0;
  878.     struct uio auio;
  879.     struct iovec aiov;
  880.     /*int wdformat();*/
  881.  
  882.     du = &wddrives[unit];
  883.  
  884.     switch (cmd) {
  885.  
  886.     case DIOCGDINFO:
  887.         *(struct disklabel *)addr = du->dk_dd;
  888.         break;
  889.  
  890.         case DIOCGPART:
  891.                 ((struct partinfo *)addr)->disklab = &du->dk_dd;
  892.                 ((struct partinfo *)addr)->part =
  893.                     &du->dk_dd.d_partitions[wdpart(dev)];
  894.                 break;
  895.  
  896.         case DIOCSDINFO:
  897.                 if ((flag & FWRITE) == 0)
  898.                         error = EBADF;
  899.                 else
  900.                         error = setdisklabel(&du->dk_dd,
  901.                     (struct disklabel *)addr,
  902.                          0);
  903.         wdsetctlr(dev, du);
  904.                 break;
  905.  
  906.         case DIOCWLABEL:
  907.                 if ((flag & FWRITE) == 0)
  908.                         error = EBADF;
  909.                 else
  910.                         du->dk_wlabel = *(int *)addr;
  911.                 break;
  912.  
  913.         case DIOCWDINFO:
  914.                 if ((flag & FWRITE) == 0)
  915.                         error = EBADF;
  916.                 else if ((error = setdisklabel(&du->dk_dd,
  917.           (struct disklabel *)addr, 0)) == 0) {
  918.                         int wlab;
  919.  
  920.             wdsetctlr(dev, du);
  921.  
  922.                         /* simulate opening partition 0 so write succeeds */
  923.                         dk->dk_openpart |= (1 << 0);            /* XXX */
  924.                         wlab = du->dk_wlabel;
  925.                         du->dk_wlabel = 1;
  926.                         error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev));
  927.                         dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;
  928.                         du->dk_wlabel = wlab;
  929.                 }
  930.                 break;
  931.  
  932.     default:
  933.         error = ENOTTY;
  934.         break;
  935.     }
  936.     return (error);
  937. }
  938.  
  939. /*
  940.  * Routines to do raw IO for a unit.
  941.  */
  942. wdread(dev, uio)            /* character read routine */
  943.     dev_t dev;
  944.     struct uio *uio;
  945. {
  946.     int unit = wdunit(dev) ;
  947.  
  948.     if (unit >= NWD) return(ENXIO);
  949.     return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
  950. }
  951.  
  952.  
  953. wdwrite(dev, uio)            /* character write routine */
  954.     dev_t dev;
  955.     struct uio *uio;
  956. {
  957.     int unit = wdunit(dev) ;
  958.  
  959.     if (unit >= NWD) return(ENXIO);
  960.     return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
  961. }
  962.  
  963. /* return size of given paritition */
  964. wdsize(dev)
  965.     dev_t dev;
  966. {
  967.     register unit = wdunit(dev);
  968.     register part = wdpart(dev);
  969.     register struct disk *du;
  970.     register val ;
  971.  
  972.     if (unit >= NWD) return(-1);
  973.     if (wddrives[unit].dk_state == 0) {
  974.         val = wdopen (dev, 0);
  975.         if (val < 0)
  976.             return (-1);
  977.     }
  978.     du = &wddrives[unit];
  979.     return((int)((u_long)du->dk_dd.d_partitions[part].p_size *
  980.         du->dk_dd.d_secsize / DEV_BSIZE));
  981. }
  982.  
  983. extern        char *vmmap;            /* poor name! */
  984.  
  985. /* save contents of memeory after a system "crash" */
  986. wddump(dev)
  987.     dev_t dev;
  988. {
  989.     register struct disk *du;    /* disk unit to do the IO */
  990.     register struct bt_bad *bt_ptr;
  991.     long    num;            /* number of sectors to write */
  992.     int    unit, part;
  993.     long    cyloff, blknum, blkcnt;
  994.     long    cylin, head, sector, stat;
  995.     long    secpertrk, secpercyl, nblocks, i;
  996.     char *addr;
  997.     extern    int Maxmem;
  998.     static  wddoingadump = 0 ;
  999.     extern CMAP1;
  1000.     extern char CADDR1[];
  1001.     
  1002.     addr = (char *) 0;        /* starting address */
  1003.  
  1004.     /* size of memory to dump */
  1005.     num = Maxmem;
  1006.     unit = wdunit(dev);
  1007.     part = wdpart(dev);        /* file system */
  1008.  
  1009.     /* check for acceptable drive number */
  1010.     if (unit >= NWD) return(ENXIO);
  1011.  
  1012.     du = &wddrives[unit];
  1013.     /* was it ever initialized ? */
  1014.     if (du->dk_state < OPEN) return (ENXIO) ;
  1015.  
  1016.     /* Convert to disk sectors */
  1017.     num = (u_long) num * NBPG / du->dk_dd.d_secsize;
  1018.  
  1019.     if (wddoingadump) return(EFAULT);
  1020.  
  1021.     secpertrk = du->dk_dd.d_nsectors;
  1022.     secpercyl = du->dk_dd.d_secpercyl;
  1023.     nblocks = du->dk_dd.d_partitions[part].p_size;
  1024.     cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl;
  1025.  
  1026.     /* check transfer bounds against partition size */
  1027.     if ((dumplo < 0) || ((dumplo + num) > nblocks))
  1028.         return(EINVAL);
  1029.  
  1030.     wddoingadump = 1  ;  i = 100000 ;
  1031.     while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
  1032.     outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
  1033.     outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
  1034.     while (inb(wdc+wd_status) & WDCS_BUSY) ;
  1035.  
  1036.     /* some compaq controllers require this ... */
  1037.     wdsetctlr(dev, du);
  1038.     
  1039.     blknum = dumplo;
  1040.     while (num > 0) {
  1041.         pmap_enter(pmap_kernel(), vmmap, addr, VM_PROT_READ, TRUE);
  1042.  
  1043.         /* compute disk address */
  1044.         cylin = blknum / secpercyl;
  1045.         head = (blknum % secpercyl) / secpertrk;
  1046.         sector = blknum % secpertrk;
  1047.         cylin += cyloff;
  1048.  
  1049.         /* 
  1050.          * See if the current block is in the bad block list.
  1051.          * (If we have one.)
  1052.          */
  1053.                 for (bt_ptr = dkbad[unit].bt_bad;
  1054.                 bt_ptr->bt_cyl != -1; bt_ptr++) {
  1055.             if (bt_ptr->bt_cyl > cylin)
  1056.                 /* Sorted list, and we passed our cylinder.
  1057.                     quit. */
  1058.                 break;
  1059.             if (bt_ptr->bt_cyl == cylin &&
  1060.                 bt_ptr->bt_trksec == (head << 8) + sector) {
  1061.             /*
  1062.              * Found bad block.  Calculate new block addr.
  1063.              * This starts at the end of the disk (skip the
  1064.              * last track which is used for the bad block list),
  1065.              * and works backwards to the front of the disk.
  1066.              */
  1067.                 blknum = (du->dk_dd.d_secperunit)
  1068.                     - du->dk_dd.d_nsectors
  1069.                     - (bt_ptr - dkbad[unit].bt_bad) - 1;
  1070.                 cylin = blknum / secpercyl;
  1071.                 head = (blknum % secpercyl) / secpertrk;
  1072.                 sector = blknum % secpertrk;
  1073.                 break;
  1074.             }
  1075.  
  1076.         sector++;        /* origin 1 */
  1077.  
  1078.         /* select drive.     */
  1079.         outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
  1080.         while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
  1081.  
  1082.         /* transfer some blocks */
  1083.         outb(wdc+wd_sector, sector);
  1084.         outb(wdc+wd_seccnt,1);
  1085.         outb(wdc+wd_cyl_lo, cylin);
  1086.         outb(wdc+wd_cyl_hi, cylin >> 8);
  1087.         outb(wdc+wd_command, WDCC_WRITE);
  1088.         
  1089.         /* Ready to send data?    */
  1090.         while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
  1091.         if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
  1092.  
  1093.         outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), DEV_BSIZE/2);
  1094.         (int) addr += DEV_BSIZE;
  1095.  
  1096.         if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
  1097.         /* Check data request (should be done).         */
  1098.         if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
  1099.  
  1100.         /* wait for completion */
  1101.         for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
  1102.                 if (i < 0) return (EIO) ;
  1103.         }
  1104.         /* error check the xfer */
  1105.         if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
  1106.         /* update block count */
  1107.         num--;
  1108.         blknum++ ;
  1109.     }
  1110.     return(0);
  1111. }
  1112. #endif
  1113.  
  1114.