home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / sys / i386 / stand / wd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-04  |  11.7 KB  |  435 lines

  1. /*-
  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 PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  *
  36.  *    @(#)wd.c    7.3 (Berkeley) 5/4/91
  37.  */
  38.  
  39. /*  device driver for winchester disk  */
  40.  
  41. #include "param.h"
  42. #include "dkbad.h"
  43. #include "disklabel.h"
  44. #include "i386/isa/isa.h"
  45. #include "i386/isa/wdreg.h"
  46. #include "saio.h"
  47.  
  48. #define    NWD        2    /* number of hard disk units supported, max 2 */
  49. #define    RETRIES        5    /* number of retries before giving up */
  50.  
  51. int noretries, wdquiet;
  52. /*#define WDDEBUG*/
  53.  
  54. #ifdef    SMALL
  55. extern struct disklabel disklabel;
  56. #else
  57. struct disklabel wdsizes[NWD];
  58. #endif
  59.  
  60. extern cyloffset ;        /* bootstrap's idea of cylinder for disklabel */
  61.  
  62. /*
  63.  * Record for the bad block forwarding code.
  64.  * This is initialized to be empty until the bad-sector table
  65.  * is read from the disk.
  66.  */
  67. #define TRKSEC(trk,sec)    ((trk << 8) + sec)
  68.  
  69. struct    dkbad    dkbad[NWD];
  70. static wdcport;
  71.  
  72. wdopen(io)
  73.     register struct iob *io;
  74. {
  75.         register struct disklabel *dd;
  76.  
  77. #ifdef WDDEBUG
  78.     printf("wdopen ");
  79. #endif
  80. #ifdef SMALL
  81.         dd = &disklabel;
  82. #else
  83.         dd = &wdsizes[io->i_unit];
  84.     if (io->i_part > 8)
  85.                 _stop("Invalid partition number");
  86.     if(io->i_ctlr > 1)
  87.                 _stop("Invalid controller number");
  88. #endif
  89.         if (wdinit(io))
  90.                 _stop("wd initialization error");
  91.     io->i_boff = dd->d_partitions[io->i_part].p_offset ;
  92.     return(0);
  93. }
  94.  
  95. wdstrategy(io,func)
  96.     register struct iob *io;
  97. {
  98.     register int iosize;    /* number of sectors to do IO for this loop */
  99.     register daddr_t sector;
  100.     int nblocks, cyloff;
  101.     int unit, partition;
  102.     char *address;
  103.     register struct disklabel *dd;
  104.  
  105.     unit = io->i_unit;
  106.     partition = io->i_part;
  107. #ifdef WDDEBUG
  108.     printf("wdstrat %d %d ", unit, partition);
  109. #endif
  110. #ifdef    SMALL
  111.     dd = &disklabel;
  112. #else
  113.     dd = &wdsizes[unit];
  114. #endif
  115.         iosize = io->i_cc / dd->d_secsize;
  116.     /*
  117.      * Convert PGSIZE "blocks" to sectors.
  118.      * Note: doing the conversions this way limits the partition size
  119.      * to about 8 million sectors (1-8 Gb).
  120.      */
  121.     sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->d_secsize;
  122.     nblocks = dd->d_partitions[partition].p_size;
  123. #ifndef SMALL
  124.         if (iosize < 0 || sector + iosize > nblocks || sector < 0) {
  125. #ifdef WDDEBUG
  126.         printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n",
  127.             io->i_bn, iosize, partition, nblocks);
  128. #endif
  129.                 printf("wdstrategy - I/O out of filesystem boundaries\n");
  130.         return(-1);
  131.     }
  132.     if (io->i_bn * DEV_BSIZE % dd->d_secsize) {
  133.         printf("wdstrategy - transfer starts in midsector\n");
  134.         return(-1);
  135.     }
  136.         if (io->i_cc % dd->d_secsize) {
  137.         printf("wd: transfer of partial sector\n");
  138.         return(-1);
  139.     }
  140. #endif
  141.  
  142.     address = io->i_ma;
  143.         while (iosize > 0) {
  144.                 if (wdio(func, unit, sector, address))
  145.                         return(-1);
  146.         iosize--;
  147.         sector++;
  148.                 address += dd->d_secsize;
  149.         }
  150.         return(io->i_cc);
  151. }
  152.  
  153. /* 
  154.  * Routine to do a one-sector I/O operation, and wait for it
  155.  * to complete.
  156.  */
  157. wdio(func, unit, blknm, addr)
  158.         short *addr;
  159. {
  160.     struct disklabel *dd;
  161.     register wdc = wdcport;
  162.     struct bt_bad *bt_ptr;
  163.         int    i;
  164.     int retries = 0;
  165.         long    cylin, head, sector;
  166.         u_char opcode, erro;
  167.  
  168. #ifdef    SMALL
  169.     dd = &disklabel;
  170. #else
  171.     dd = &wdsizes[unit];
  172. #endif
  173.         if (func == F_WRITE)
  174.                 opcode = WDCC_WRITE;
  175.         else
  176.                 opcode = WDCC_READ;
  177.  
  178.         /* Calculate data for output.           */
  179.         cylin = blknm / dd->d_secpercyl;
  180.         head = (blknm % dd->d_secpercyl) / dd->d_nsectors;
  181.         sector = blknm % dd->d_nsectors;
  182.  
  183.     /* 
  184.      * See if the current block is in the bad block list.
  185.      */
  186.     if (blknm > BBSIZE/DEV_BSIZE)    /* should be BBSIZE */
  187.         for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
  188.         if (bt_ptr->bt_cyl > cylin)
  189.             /* Sorted list, and we passed our cylinder. quit. */
  190.             break;
  191.         if (bt_ptr->bt_cyl == cylin &&
  192.             bt_ptr->bt_trksec == (head << 8) + sector) {
  193.             /*
  194.              * Found bad block.  Calculate new block addr.
  195.              * This starts at the end of the disk (skip the
  196.              * last track which is used for the bad block list),
  197.              * and works backwards to the front of the disk.
  198.              */
  199. #ifdef WDDEBUG
  200.                 printf("--- badblock code -> Old = %d; ",
  201.                 blknm);
  202. #endif
  203.             blknm = dd->d_secperunit - dd->d_nsectors
  204.                 - (bt_ptr - dkbad[unit].bt_bad) - 1;
  205.             cylin = blknm / dd->d_secpercyl;
  206.             head = (blknm % dd->d_secpercyl) / dd->d_nsectors;
  207.             sector = blknm % dd->d_nsectors;
  208. #ifdef WDDEBUG
  209.                 printf("new = %d\n", blknm);
  210. #endif
  211.             break;
  212.         }
  213.     }
  214.  
  215.         sector += 1;
  216. retry:
  217. #ifdef WDDEBUG
  218.     printf("sec %d sdh %x cylin %d ", sector,
  219.         WDSD_IBM | (unit<<4) | (head & 0xf), cylin);
  220. #endif
  221.     outb(wdc+wd_precomp, 0xff);
  222.     outb(wdc+wd_seccnt, 1);
  223.     outb(wdc+wd_sector, sector);
  224.     outb(wdc+wd_cyl_lo, cylin);
  225.     outb(wdc+wd_cyl_hi, cylin >> 8);
  226.  
  227.     /* Set up the SDH register (select drive).     */
  228.     outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
  229.     while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
  230.  
  231.     outb(wdc+wd_command, opcode);
  232.     while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY))
  233.         ;
  234.     /* Did we get an error?         */
  235.     if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR))
  236.         goto error;
  237.  
  238.     /* Ready to remove data?        */
  239.     while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
  240.  
  241.     if (opcode == WDCC_READ)
  242.         insw(wdc+wd_data,addr,256);
  243.     else    outsw(wdc+wd_data,addr,256);
  244.  
  245.     /* Check data request (should be done).         */
  246.     if (inb(wdc+wd_status) & WDCS_DRQ) goto error;
  247.  
  248.     while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ;
  249.  
  250.     if (inb(wdc+wd_status) & WDCS_ERR) goto error;
  251.  
  252. #ifdef WDDEBUG
  253. printf("+");
  254. #endif
  255.         return (0);
  256. error:
  257.     erro = inb(wdc+wd_error);
  258.     if (++retries < RETRIES)
  259.         goto retry;
  260.     if (!wdquiet)
  261. #ifdef    SMALL
  262.         printf("wd%d: hard error: sector %d status %x error %x\n", unit,
  263.         blknm, inb(wdc+wd_status), erro);
  264. #else
  265.         printf("wd%d: hard %s error: sector %d status %b error %b\n", unit,
  266.         opcode == WDCC_READ? "read" : "write", blknm, 
  267.         inb(wdc+wd_status), WDCS_BITS, erro, WDERR_BITS);
  268. #endif
  269.     return (-1);
  270. }
  271.  
  272. wdinit(io)
  273.     struct iob *io;
  274. {
  275.     register wdc;
  276.     struct disklabel *dd;
  277.         unsigned int   unit;
  278.     struct dkbad *db;
  279.     int i, errcnt = 0;
  280.     char buf[512];
  281.     static open[NWD];
  282.  
  283.     unit = io->i_unit;
  284.     if (open[unit]) return(0);
  285.  
  286.     wdcport = io->i_ctlr ? IO_WD2 : IO_WD1;
  287.     wdc = wdcport;
  288.  
  289. #ifdef    SMALL
  290.     dd = &disklabel;
  291. #else
  292.     /* reset controller */
  293.     outb(wdc+wd_ctlr, 12); wait(10); outb(wdc+wd_ctlr, 8);
  294.     wdwait();
  295.  
  296.     dd = &wdsizes[unit];
  297.  
  298. tryagainrecal:
  299.     /* set SDH, step rate, do restore to recalibrate drive */
  300.     outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
  301.     wdwait();
  302.     outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
  303.     wdwait();
  304.     if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
  305. /*#ifdef SMALL
  306.         printf("wd%d: recal status %x error %x\n",
  307.             unit, i, inb(wdc+wd_error));
  308. #else*/
  309.         printf("wd%d: recal status %b error %b\n",
  310.             unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
  311. /*#endif*/
  312.         if (++errcnt < 10)
  313.             goto tryagainrecal;
  314.         return(-1);
  315.     }
  316.  
  317.     /*
  318.      * Some controllers require this (after a recal they
  319.      * revert to a logical translation mode to compensate for
  320.      * dos limitation on 10-bit cylinders -- *shudder* -wfj)
  321.      * note: cylinders *must* be fewer than or equal to 8 to
  322.      * compensate for some IDE drives that latch this for all time.
  323.      */
  324.     outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1);
  325.     outb(wdc+wd_seccnt, 35 );
  326.     outb(wdc+wd_cyl_lo, 1224);
  327.     outb(wdc+wd_cyl_hi, 1224/256);
  328.     outb(wdc+wd_command, 0x91);
  329.     while (inb(wdc+wd_status) & WDCS_BUSY) ;
  330.  
  331.     errcnt = 0;
  332. retry:
  333.     /*
  334.      * Read in LABELSECTOR to get the pack label and geometry.
  335.      */
  336.     outb(wdc+wd_precomp, 0xff);    /* sometimes this is head bit 3 */
  337.     outb(wdc+wd_seccnt, 1);
  338.     outb(wdc+wd_sector, LABELSECTOR + 1);
  339.     outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
  340.     outb(wdc+wd_cyl_hi, (cyloffset >> 8));
  341.     outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
  342.     wdwait();
  343.     outb(wdc+wd_command, WDCC_READ);
  344.     wdwait();
  345.     if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
  346.         int err;
  347.  
  348.         err = inb(wdc+wd_error);
  349.         if (++errcnt < RETRIES)
  350.             goto retry;
  351.         if (!wdquiet)
  352. /*#ifdef SMALL
  353.             printf("wd%d: reading label, status %x error %x\n",
  354.             unit, i, err);
  355. #else*/
  356.             printf("wd%d: reading label, status %b error %b\n",
  357.             unit, i, WDCS_BITS, err, WDERR_BITS);
  358. /*#endif*/
  359.         return(-1);
  360.     }
  361.  
  362.     /* Ready to remove data?        */
  363.     while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
  364.  
  365.     i = insw(wdc+wd_data, buf, 256);
  366.  
  367. #ifdef WDDEBUG
  368.     printf("magic %x,insw %x, %x\n",
  369.     ((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf);
  370. #endif
  371.     if (((struct disklabel *) (buf + LABELOFFSET))->d_magic == DISKMAGIC) {
  372.         *dd = * (struct disklabel *) (buf + LABELOFFSET);
  373.         open[unit] = 1;
  374.     } else {
  375.         if (!wdquiet)
  376.             printf("wd%d: bad disk label\n", unit);
  377.         if (io->i_flgs & F_FILE) return(-1);
  378.         dkbad[unit].bt_bad[0].bt_cyl = -1;
  379.         dd->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ;
  380.         dd->d_secsize = 512;
  381.         outb(wdc+wd_precomp, 0xff);    /* force head 3 bit off */
  382.         return (0) ;
  383.     }
  384. #endif SMALL
  385. #ifdef SMALL
  386. #ifdef WDDEBUG
  387.     printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors);
  388. #endif
  389. #endif    SMALL
  390.  
  391.  
  392.     /* now that we know the disk geometry, tell the controller */
  393.     outb(wdc+wd_cyl_lo, dd->d_ncylinders);
  394.     outb(wdc+wd_cyl_hi, (dd->d_ncylinders)>>8);
  395.     outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1);
  396.     outb(wdc+wd_seccnt, dd->d_nsectors);
  397.     outb(wdc+wd_command, 0x91);
  398.     while (inb(wdc+wd_status) & WDCS_BUSY) ;
  399.  
  400.     dkbad[unit].bt_bad[0].bt_cyl = -1;
  401.     outb(wdc+wd_precomp, dd->d_precompcyl / 4);
  402.  
  403.     /*
  404.      * Read bad sector table into memory.
  405.      */
  406.     i = 0;
  407.     do {
  408.         int blknm = dd->d_secperunit - dd->d_nsectors + i;
  409.         errcnt = wdio(F_READ, unit, blknm, buf);
  410.     } while (errcnt && (i += 2) < 10 && i < dd->d_nsectors);
  411.     db = (struct dkbad *)(buf);
  412. #define DKBAD_MAGIC 0x4321
  413.     if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC)
  414.         dkbad[unit] = *db;
  415.     else {
  416.         if (!wdquiet)
  417.             printf("wd%d: error in bad-sector file\n", unit);
  418.         dkbad[unit].bt_bad[0].bt_cyl = -1;
  419.     }
  420.     return(0);
  421. }
  422.  
  423. wdwait()
  424. {
  425.     register wdc = wdcport;
  426.     register i = 0;
  427.     
  428.     while (inb(wdc+wd_status) & WDCS_BUSY)
  429.         ;
  430.     while ((inb(wdc+wd_status) & WDCS_READY) == 0)
  431.         if (i++ > 100000)
  432.             return(-1);
  433.     return(0);
  434. }
  435.