[comp.os.minix] Rewritten XT Wini driver

overby@plains.NoDak.edu (Glen Overby) (09/06/90)

I have largely rewritten the XT Wini disk driver.  It has already gone thru
two beta releases with several of the Referees.  I am releasing it to the net
in a finished form, but with a fair ammount of debugging code still in the
source, which can be compiled in by enabling some #defines.  There are still
many things I would like to do with the driver, but I feel it is fairly
stable, and I don't seem to be getting around to doing what I want.

If you get this driver working on your system, please drop me a note and
tell me what make and model of controler, drive and CPU you have.  I intend
to keep a mini-compatability report for this driver, so I know where I stand
compatability wise.  If it Doesn't work on your system, try enabling some of
the debugging information as described in the "Readme" file, and give me as
much information about what it DOES and DOESN'T do on your system.

Glen Overby

echo x - Readme
sed '/^X/s///' > Readme << '/'
X						7/8/90
X						7/21/90
X						9/5/90
XRE: New XT Winchester Driver for MINIX
X
XThis is the README file for my rewrite of the Minix XT wini driver.  It
Xcurrently has a large number of debugging options (enabled with
X#defines) which should probably be removed for a "production" driver.
XI have attempted (and, I think, succeeded) in cleaning up the old
Xdriver, making it more robust, and removed many outright bugs in the WD
XAutoconfigure area.
X
XThe XT wini driver is one of those places that have had a large number
Xof changes made by various people trying to fix many different
Xcompatability problems.  The result is a program with a lot of hacks in
Xit, and more floating around the net which will fix other problems.
XUnder DOS, these controler differences are hidden by the ROMs, but the
XROMs do not support multitasking well, and will not run in protected
Xmode, so a custom driver for MINIX is essential.
X
XI am attempting to write a driver which can easily adapt to a wider
Xrange of the compatability issues than did the old XT driver.  The new
Xdriver is based on the old one, so on the surface they may look the
Xsame, but if you look closer you will find that a majority (>75%) of the
Xcode has been rewritten.
X
XPart of the reason I undertook this task is I have seen a lot of 'quick
Xhack' solutions to compatability problems, such as changing the
X"NR_SECTORS" #define for RLL controlers.  As it frequently turns out,
Xthis information is available in the ROM tables!  The "AT" controler
XROM has a field for the number of sectors per track.
X
XThere were many things I did not like about the design of the old
Xdriver, among them is that the parameters for a drive was copied across
Xall partitions.  I have divided the Drive and Partition information
Xinto two separate structures.  Another very major design change I made
Xis to have one set of routines that execute controler commands, the
Xxt_* routines, whereas the old driver spread command execution out over
Xa wide set of routines.  I feel that my approach results in a driver
Xwhich is potentially much more portable to other controlers (such as
Xthe AT, Bios, and SCSI).  This "feature" has not yet been exercised.
X
XThe biggest mess of all is in the driver initialization.  Here I see
Xseveral areas of problems, most of them caused by Western Digital controlers
X(and I own one of these).  Some WD ROMs (Rev H) use three switches for
Xindicating which table to use, rather than two as IBM did.  Furthermore,
Xthere is the Auto Configure series of ROMs ("AutoConfigure", "SuperBios" and
X"GEN2").  When the old driver was compiled with the "AUTO_BIOS" option,
XTWO drive had to be specified in xt_wini.c, or the configuration would
Xoverwrite areas of memory outside the driver!  This generally fell somewhere
Xwithin a tty buffer and was harmless, but nevertheless it was bad.
X
XControler Compatability
X-----------------------
X
XI'd like to gather compatability information.  I know of the following
Xcontrolers:
X
X	IBM
X	Western Digital
X		ROM table (pre-autoconfigure)
X		Autoconfigure				Glen Overby
X		Super Bios
X		GEN2
X		RLL
X	Adaptec (RLL)
X	OMTI						Lars Fredrickson
X	Xebec (they still around?)
X	AT&T 6300
X
X
XOther WD-specific issues I have addressed, and which may be problems on
Xsom RLL controlers, are:
X
XSome controlers execute self-tests after being reset.  This can take
Xnearly a second, and the driver will time-out on fast (80386) CPUs.  WD
Xcontrolers with the WD1015-14 control processor (an Intel 8049 with
Xcustom ROM) exhibit this feature.  One solution to this is to have huge
Xtiming loops (which will wait more than 32,000 iterations, thus
Xrequiring the use of several "ints" for counting), but I chose to use a
Xsmaller timing loop (which should be enough when sending commands and
Xdata to the controler) and then waiting on a clock timer for a couple
Xmore seconds.
X
XWeirdnesses to Look For When Hacking on the Driver Yourself
X-----------------------------------------------------------
X
XI encountered a lot of subtile "features" in working with this driver which
XI feel I should pass along to others who want to try their hand at wini
Xdriver hacking.  A few of these are documented, while others are not.
X
X * When sending commands or data to the controler, always check the "REQ"
Xbit first.  Fast CPUs can send data faster than the controler can accept it,
Xso if you don't watch "REQ", the controler will miss a lot.
X
X * Do NOT enable the DMA controler's DRQ line until the controler's DMA/IRQ
Xhas been enabled.  The WD controlers leave the lines "floating" (tri-stated)
Xand will float "high".  The DMA controler uses a "HIGH" state for DMA Request,
Xso the controler will run right off and service your request before it ever
Xexists.
X
X * Some controlers execute diagnosticts after RESET, which can take a LONG
Xtime (up to a second).  I use a timer interrupt to wait that long.
X
X * The WD Autoconfigure ROMs store their drive parameters in low memory (this
Xhas changed on the GEN2).  Minix will overwrite this area, but Bruce Evans
Xadded support to copy this area before overwiriting it.  That works OK if
Xyou only have one drive, but if you have two drives, it's useless since the
Xparameter area pointed to by INT 41 is valid for ONLY the last drive accessed.
XI have found two adjacent drive parameter blocks, but I have verified that
Xthe Autoconf ROM switches between those areas.  What a mess.  Hopefully this
Xhas been fixed in the GEN2, which stores the drive parameters in the controler
XROM area (apparently they have some RAM shadowing an area of the ROM).
X
XDebugging the new XT wini.
X-------------------------
X
X[this was written to solve some problems that a few beta testers encountered]
XSince Minix will boot at 20MHZ with all the debugging messages enabled,
XI believe there is a problem of xt_wini not waiting for the controler
Xlong enough in the right places.  I have expanded the debugging #defines
Xgreatly to facilitate diagnosing exactly where the race is coming in
Xfrom.
X
XIn your first report, you talked of turning "DEBUG_L" on; there were
Xreally two _L sections: _L and _L1.  Did you ever turn _L1 on?  I'm
Xassuming you did, and that _L1 actually had more of an effect than did
Xplain _L.
X
XWithout autoconfigure on, the driver should ALWAYS print the ROM
Xparameter table.  I would like to know if the "Sectors" field is set
Xproperly for your drive (if it's 17 (0x11), then it is zero in your ROM
Xtable and you'll still have to define NR_SECTORS as 25).  If you need more
Xtime to read the info, uncomment line 825 and the driver will delay for
Xone minute (or whatever you change the 3600 to).
X
XMost of the dirty work in the driver is done in xt_do and xt_rw.  Most
Xof this will relate to those areas.
X
XThe first place I'm thinking of is when data is sent to the controller
Xunder program control (and not DMA).  This is used for sending
Xcommands, configuration paraameters, and reading detailed error
Xreturns.  Define DEBUG_RW3 to test this.  If that makes the system
Xwork, note the timeout values.  RW2 does about the same thing, but
Xdoesn't print out the timeout value.  You can actually use either (the
XRW3 line was commented out in the first driver), but I would like to
Xknow the timeout values.
X
XNext turn DEBUG_STAT on; this is in the routine which waits for status
Xbits to enter a certain state.  This routine will hit the clock when
Xthe controler takes a long time (you'll see messages about this).  If
Xthat times out, it's bad news.  I don't think this is the problem, so
Xif you want to take a short-cut, combine this with the next two tries
Xand back out if it starts working.
X
XLeaving RW{2 or 3} and STAT on, turn on DEBUG_RW1 (some trivial
Xdiagnosticts) and DEBUG_RESET on.  I don't really think it's a reset
Xproblem, so you might want to combine this one with what's next, and
Xwatch for the diagnosticts.
X
XGoing back to the "xt_do" routine, turn DEBUG_DMA on and it will add a
Xlonger delay between when the wini controler is given the command and
Xthe DMA controler is started.  This is specific to commands that wait
Xfor an interrupt; turn DEBUG_DO2 on to add the same for the programmed
Xdata transfer areas.
X
XYou now have everything on that was on before with the DEBUG_L and DEBUG_L1.
XIf the driver still doesn't work... umm... I dunno.  If it does start working
Xin a later step, start turning the DEBUG_*'s from previous steps off until
Xit quits working.
X
XFuture Directions
X-----------------
X
XThis is by no means the only things that this driver needs.  I'd like
Xto see the driver ported to other controlers (AT, ROM, SCSI, SMD), and
XI think this should be relatively easy, since I have a small number of
Xroutines which talk to the controler itself.  There are some other
Xfunctionality items that the driver could use:
X
X	* ability to move the head to the landing cylinder when asked,
X	  probably via an ioctl call (this is my sole use of DOS these days).
X
X	* re-read the partition table when asked (like after FDISK modifies
X	  the table)
X
X	* get/set the drive parameters.  Get is especially important to
X	  FDISK, so that it can find the number of sectors per track,
X	  heads, and cylinders.
X
X	* Error logging.  Keep a log of recent errors, and what the controler
X	  returned.  The error returns to Minix are much to generic to track
X	  down a disk drive or controler problem.
X
X	* support for multiple controlers.  This includes both multiple (ok,
X	  two -- that's all that's normally supported under DOS) XT 
X	  controlers, as well as multiple types of controlers in the system
X	  (i.e. XT and SCSI).  I might do the latter if I get a SCSI disk.
X
XGlen Overby	<overby@plains.nodak.edu>
X	{ogicse, umn-cs, uunet}!plains!overby (UUCP)	overby@plains (Bitnet)
X	overby@cobber.cord.edu (alternate internet)
/
echo x - xt2_wini.c
sed '/^X/s///' > xt2_wini.c << '/'
X/* This file contains a driver for IBM PC XT class winchester controllers.
X * Also supports Western Digital (WX-2 and related) controllers.
X *
X * Original code written by Adri Koppes.
X * Patches from Gary Oliver for use with the Western Digital WX-2.
X * Patches from Harry McGavran for robust operation on turbo clones.
X * Patches from Mike Mitchell for WX-2 auto configure operation.
X * Major Rewrite by Glen Overby 4/90
X *
X *	Version: 1.5.10-2.3.A 8/4/90
X *		Derived from xt_wini.c as of 1.5.10, 
X *		3rd author fix level
X *		No user fixes
X *
X * References:
X *  "Storage Management Products Handbook", 1986 Western Digital Corporation
X *  Specifically, the data sheets on the WD1002S-WX2 in section 6.
X *
X * The driver supports the following operations (using message format m2):
X *
X *	m_type	  DEVICE   PROC_NR	COUNT	 POSITION  ADRRESS
X * ----------------------------------------------------------------
X * |  DISK_READ | device  | proc nr |  bytes  |	 offset | buf ptr |
X * |------------+---------+---------+---------+---------+---------|
X * | DISK_WRITE | device  | proc nr |  bytes  |	 offset | buf ptr |
X * ----------------------------------------------------------------
X * |SCATTERED_IO| device  | proc nr | requests|         | iov ptr |
X * ----------------------------------------------------------------
X *
X * The file contains one entry point:
X *
X *	 winchester_task:	main entry when system is brought up
X *
X */
X
X#include "kernel.h"
X#include <minix/callnr.h>
X#include <minix/com.h>
X#include <minix/partition.h>
X
X/*#define DEBUG_CMD			/* w_do_rdwt */
X/*#define DEBUG_DMA			/* dma_set & other DMA mode in xt_do */
X/*#define DEBUG_RESET			/* controler reset related */
X/*#define DEBUG_STAT			/* stat_wait */
X/*#define DEBUG_DO1			/* xt_do, basics */
X/*#define DEBUG_DO2			/* xt_do, detail */
X/*#define DEBUG_RW1			/* xt_rw, basics */
X/*#define DEBUG_RW2			/* xt_rw, all bytes */
X/*#define DEBUG_RW3			/* xt_rw, controler delay */
X/*#define DEBUG_INIT			/* Init time stuff */
X#define DEBUG_CONF			/* system HW configuration */
X
X#define MAX_DRIVES         2	/* Maximum of two drives supported */
X
X/* I/O Ports used by winchester disk controler. */
X#define WIN_DATA       0x320	/* winchester disk controller data register */
X#define WIN_STATUS     0x321	/* winchester disk controller status register*/
X#define WIN_SELECT     0x322	/* winchester disk controller select port */
X#define WIN_DMA	       0x323	/* winchester disk controller dma register */
X
X/* I/O Ports used by DMA controler. */
X#define DMA_ADDR       0x006	/* port for low 16 bits of DMA address */
X#define DMA_TOP	       0x082	/* port for top 4 bits of 20-bit DMA addr */
X#define DMA_COUNT      0x007	/* port for DMA count (count =	bytes - 1) */
X#define DMA_M1	       0x00B	/* DMA mode register */
X#define DMA_M2	       0x00C	/* DMA byte pointer flip-flop */
X#define DMA_INIT       0x00A	/* DMA request mask register (en/disable DRQ) */
X
X/* Winchester disk controler status register bits */
X#define WST_REQ	       0x001	/* Request bit */
X#define WST_INPUT      0x002	/* Data Input or Output mode 
X				   (Set if controller is writing to CPU) */
X#define WST_CMD	       0x004	/* Command/Status mode */
X#define WST_BUSY       0x008	/* Set if controler is Busy */
X#define WST_DRQ        0x010	/* DMA request pending */
X#define WST_IRQ        0x020	/* Interrupt request pending  */
X
X/* Winchester disk controller commands. */
X#define WIN_RECALIBRATE	0x01	/* drive recalibrate */
X#define WIN_SENSE	0x03	/* return controler status */
X#define WIN_READ	0x08	/* drive read */
X#define WIN_WRITE	0x0A	/* drive write */
X#define WIN_SPECIFY	0x0C	/* set controler params	*/
X#define WIN_ECC_READ	0x0D	/* read ecc length after a correctable ECC error */
X#define WIN_ERROR	0x02	/* command completion byte error field */
X
X/* Error codes from XT Controller */
X#define ERR_OK		0x00	/* no error */
X#define ERR_SC		0x02	/* no SC* (select?) from drive */
X#define ERR_WFAULT	0x03	/* Write Fault */
X#define ERR_NOT_RDY	0x04	/* Drive Not Ready */
X#define ERR_TRK0	0x06	/* Track 0 Not Found */
X#define ERR_SEEKING	0x08	/* Drive Still Seeking */
X#define ERR_DATA	0x11	/* Uncorrectable Data Error */
X#define ERR_ADDR_MARK	0x12	/* Data Address Mark Not Found */
X#define ERR_SEEK	0x15	/* Seek Error */
X#define ERR_CORR	0x18	/* Correctable ECC Error */
X#define ERR_BADTRK	0x19	/* Track is flagged as BAD */
X#define ERR_INVALID	0x20	/* Invalid Command */
X#define ERR_SECT_ADDR	0x21	/* Illegal Sector Address */
X#define ERR_SECT_BUF	0x30	/* Sector Buffer Error (diags E0, E4) */
X#define ERR_ROM		0x31	/* Controler ROM Checksum (diags E4) */
X#define ERR_ECCPOLY	0x32	/* ECC generator failed (diags E4) */
X
X/* xt_command / Win_DMA port parameters */
X/* NOTE: bits 0 and 1 correspond to the controler INT & DMA Mask port */
X#define DMA_INT		   3	/* Command with dma and interrupt */
X#define INT		   2	/* Command with interrupt, no dma */
X#define DMA_		   1	/* DMA part of DMA_INT */
X#define NO_DMA_INT	   0	/* Command without dma and interrupt */
X#define PGM_READ	0x10	/* No DMA or Interrupt, read data manually */
X#define PGM_WRITE	0x20	/* No DMA or Interrupt, write data manually */
X	
X/* DMA channel commands. */
X#define DMA_READ	0x47	/* DMA read opcode */
X#define DMA_WRITE	0x4B	/* DMA write opcode */
X
X#define DMA_RESET_VAL	0x07	/* Disable DRQ */
X#define DMA_SET_VAL	0x03	/* Enable DRQ */
X
X/* Parameters for the disk drive. */
X#define SECTOR_SIZE	 512	/* physical sector size in bytes */
X#ifndef NR_SECTORS
X/* For RLL drives NR_SECTORS has to be defined in the makefile or in config.h.
X * There is some hope of getting it from the parameter table for these drives,
X * and then this driver should use wn_maxsec like at_wini.c.
X * Unfortunately it is not standard in XT parameter tables.
X */
X#define NR_SECTORS	0x11	/* number of sectors per track */
X#endif
X
X/* Error codes */
X#define ERR		  -1	/* general error */
X
X/* Miscellaneous. */
X#define MAX_ERRORS	   4	/* how often to try rd/wt before quitting */
X#define MAX_RESULTS	   4	/* max number of bytes controller returns */
X#define DEV_PER_DRIVE   (1 + NR_PARTITIONS)	/* whole drive & each partn */
X#define NR_DEVICES      (MAX_DRIVES * DEV_PER_DRIVE)
X#define MAX_WIN_RETRY  32000	/* max # times to try to output to WIN */
X#define MAX_WIN_RESET	240	/* max # clock ticks to wait for status */
X
X#if AUTO_BIOS
X#define AUTO_PARAM     0x1AD	/* drive parameter table starts here in sect 0	*/
X#define AUTO_ENABLE	0x10	/* auto bios enabled bit from status reg */
X/* some start up parameters in order to extract the drive parameter table */
X/* from the winchester. these should not need changed. */
X#define AUTO_CYLS	 306	/* default number of cylinders */
X#define AUTO_HEADS	   4	/* default number of heads */
X#define AUTO_RWC	 307	/* default reduced write cylinder */
X#define AUTO_WPC	 307	/* default write precomp cylinder */
X#define AUTO_ECC	  11	/* default ecc burst */
X#define AUTO_CTRL	   5	/* default winchester stepping speed byte */
X#endif
X
X/* Variables. */
XPUBLIC int using_bios = FALSE;	/* this disk driver does not use the BIOS */
X
XPRIVATE struct wini {	/* main drive struct, one entry per partition */
X  long wn_low;		/* lowest cylinder of partition */
X  long wn_size;		/* size of partition in blocks */
X  int wn_drive;		/* drive number addressed */
X
X  /* Saved state from most recent command for this partition */
X  int wn_procnr;		/* which proc wanted this operation? */
X  int wn_op;			/* Operation (read/write) */
X  int wn_count;			/* byte count */
X  vir_bytes wn_address;		/* user virtual address */
X} wini[NR_DEVICES];
X
Xstruct param {			/* ROM Winchester disk parameter table */
X  int	cylinders;	/* number of cylinders on the disk */
X  char	heads;		/* number of heads in the drive */
X  int	reduced_wr;	/* cylinder to start reduced write current */
X  int	wr_precomp;	/* cylinder to start write precompensation */
X  char	max_ecc;	/* maximum ECC burst length */
X  char	control;	/* control byte */
X	  			/* bits	definition */
X				/* 0-2	step option (XT) */
X				/*   3	set if > 8 heads */
X				/*   4	0 */
X				/*   5	set if defect map on maxcyl+1*/
X				/*   6	disable ECC retries */
X				/*   7	disable access retries */
X  char	timeout;	/* standard timeout (XT) */
X  char	ftimeout;	/* formatting timeout (XT) */
X  char	chktimeout;	/* drive check timeout (XT) */
X  int	landing;	/* landing zone (AT) */
X  char	sectors;	/* sectors per track (AT) */
X  char	zero;		/* 0 */
X} drive[MAX_DRIVES];
X
XPRIVATE int w_need_reset;	 /* TRUE when controller must be reset */
XPRIVATE int nr_drives;		 /* Number of drives */
X
XPRIVATE message w_mess;		/* message buffer for in and out */
X
X/* Format of a controler command */
XPRIVATE struct xt_cmd {
X  char	op,	/* OP code (read, write, etc) */
X	headdr,	/* Head and Drive */
X	sector,	/* sector of the cylinder */
X	cyl,	/* cylinder of the drive */
X	count,	/* number of sectors to transfer */
X	ctl;	/* control byte (head step rate, auto error retry */
X};
X
X/* Format of controler drive parameters */
XPRIVATE struct xt_param {
X  char cyl_h, cyl_l;	/* Max Cylinders */
X  char heads;		/* Heads */
X  char rwr_h, rwr_l;	/* Reduced Write */
X  char wpre_h, wpre_l;	/* Write Precompensation */
X  char ecc;		/* Max ECC Data burst length */
X};
X
XPRIVATE unsigned char buf[BLOCK_SIZE] = { 0 };
X	/* Buffer used by the startup routine */
X	/* Initialized to try to avoid DMA wrap. */
X
XFORWARD int w_do_rdwt();
XFORWARD void w_dma_setup();
XFORWARD int w_reset();
XFORWARD void w_wait_int();
XFORWARD int win_specify();
XFORWARD int read_ecc();
XFORWARD int xt_command();
XFORWARD int xt_do();
XFORWARD int xt_rw();
XFORWARD int stat_wait();
XFORWARD int w_clock_mess();
XFORWARD void st_check();
XFORWARD void init_params();
XFORWARD void rom_conf();
XFORWARD void wd_autoconf();
XFORWARD void copy_param();
XFORWARD void copy_prt();
XFORWARD void sort();
XFORWARD void cp();
X
X/*=========================================================================*
X *				winchester_task				   *
X *=========================================================================*/
XPUBLIC void winchester_task()
X{
X/* Main program of the winchester disk driver task. */
X
X  int r, caller, proc_nr;
X
X  /* First initialize the controller */
X  init_params();
X
X  /* Here is the main loop of the disk task.  It waits for a message, carries
X   * it out, and sends a reply.
X   */
X
X  while (TRUE) {
X	/* First wait for a request to read or write a disk block. */
X	receive(ANY, &w_mess);	/* get a request to do some work */
X	if (w_mess.m_source < 0) {
X		printf("winchester task got message from %d\n", w_mess.m_source);
X		continue;
X	}
X	caller = w_mess.m_source;
X	proc_nr = w_mess.PROC_NR;
X
X	/* Now carry out the work. */
X	switch(w_mess.m_type) {
X		case DISK_READ:
X		case DISK_WRITE:	r = w_do_rdwt(&w_mess);	break;
X		case SCATTERED_IO:	r = do_vrdwt(&w_mess, w_do_rdwt); break;
X		default:		r = EINVAL;		break;
X	}
X
X	/* Finally, prepare and send the reply message. */
X	w_mess.m_type = TASK_REPLY;	
X	w_mess.REP_PROC_NR = proc_nr;
X
X	w_mess.REP_STATUS = r;	/* # of bytes transferred or error code */
X	send(caller, &w_mess);	/* send reply to caller */
X  }
X}
X
X
X/*==========================================================================*
X *				w_do_rdwt				    *
X *==========================================================================*/
XPRIVATE int w_do_rdwt(m_ptr)
Xmessage *m_ptr;			/* pointer to read or write w_message */
X{
X/* Carry out a read or write request from the disk. */
X  register struct wini *wn;	/* partition */
X  register struct param *dr;	/* drive */
X  int r, device, errors;
X  int sec, cyl, head;
X  long sector;
X
X  struct xt_cmd command;
X	
X  /* Decode the w_message parameters. */
X  device = m_ptr->DEVICE;
X  if (device < 0 || device >= NR_DEVICES)
X	return(EIO);
X  if (m_ptr->COUNT != BLOCK_SIZE)
X	return(EINVAL);
X
X  wn = &wini[device];		/* 'wn' points to entry for this partition */
X  dr = &drive[wn->wn_drive];	/* 'dr' points to entry for this drive */
X
X  if (m_ptr->POSITION % BLOCK_SIZE != 0)
X	return(EINVAL);
X  sector = m_ptr->POSITION/SECTOR_SIZE;
X  if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size)
X	return(0);
X  sector += wn->wn_low;	/* offset from the beginning of the partition */
X  wn->wn_op = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
X  wn->wn_address = (vir_bytes) m_ptr->ADDRESS;	/* for w_dma_setup */
X  wn->wn_procnr = m_ptr->PROC_NR;
X  wn->wn_count = m_ptr->COUNT;
X  cyl = sector / (dr->heads * dr->sectors);
X  sec = (sector % dr->sectors);
X  head = ((sector % (dr->heads * dr->sectors) ) / dr->sectors);
X  command.op = (m_ptr->m_type == DISK_READ ? WIN_READ : WIN_WRITE);
X  command.cyl = cyl & 0xff;
X  command.sector = ((cyl & 0x300) >>2) | sec;
X  command.headdr = head | (wn->wn_drive << 5);
X  command.count = BLOCK_SIZE / SECTOR_SIZE;
X  command.ctl = dr->control;
X
X#ifdef DEBUG_CMD
X  printf("Sector %l Cyl=%d Sec=%d Head=%d\n", sector, cyl, sec, head);
X  printf("Cmd Op: 0x%x Drive: %d\nCmd Head: %d Sect: %d Cyl: %d Count: %d Ctl: 0x%x\n",
X	command.op, (command.headdr & 0x20)>>5, command.headdr & 0x0f,
X	command.sector, command.cyl, command.count, command.ctl);
X#endif
X
X  /* This loop allows a failed operation to be repeated. */
X  for(errors = MAX_ERRORS; errors; errors--) {
X	if (w_need_reset)	w_reset();
X
X	w_dma_setup(wn);	/* Now set up the DMA chip. */
X
X	/* Perform the transfer. */
X	if((r = xt_command(&command, DMA_INT, 0, 0)) == ERR_CORR) {
X		/* Correctable ECC error, read ECC burst length */
X		r = read_ecc();
X		/* do we read the data NOW, or has it already been read? */
X		printf("\nECC Corr (%d) Drive=%d Cyl=%d Head=%d Sect=%d\n",
X			r, command.headdr & 0x20, command.cyl,
X			command.headdr & 0x0f, command.sector);
X		r = OK;
X		break;
X	} else {
X	    if(r != OK)
X		printf("\nDisk Error (0x%x) Drive=%d Cyl=%d Head=%d Sect=%d\n",
X			r, command.headdr & 0x20, command.cyl,
X			command.headdr & 0x0f, command.sector);
X	}
X	if (r == OK) break;	/* if successful, exit loop */
X  }
X
X  return((r == OK) ? BLOCK_SIZE : EIO);
X}
X
X
X/*==========================================================================*
X *				w_dma_setup 				    *
X *==========================================================================*/
XPRIVATE void w_dma_setup(wn)
Xstruct wini *wn;		/* pointer to the drive struct */
X{
X/* The IBM PC can perform DMA operations by using the DMA chip.	 To use it,
X * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
X * to by read from or written to, the byte count minus 1, and a read or write
X * opcode.  This routine sets up the DMA chip.	Note that the chip is not
X * capable of doing a DMA across a 64K boundary (e.g., you can't read a 
X * 512-byte block starting at physical address 65520).
X */
X
X  int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end;
X  vir_bytes vir, ct;
X  phys_bytes user_phys;
X
X  mode = (wn->wn_op == DISK_READ ? DMA_READ : DMA_WRITE);
X  vir = (vir_bytes) wn->wn_address;
X  ct = (vir_bytes) wn->wn_count;
X  user_phys = numap(wn->wn_procnr, vir, ct);
X  low_addr	= (int) user_phys & BYTE;
X  high_addr = (int) (user_phys >>  8) & BYTE;
X  top_addr	= (int) (user_phys >> 16) & BYTE;
X  low_ct  = (int) (ct - 1) & BYTE;
X  high_ct = (int) ( (ct - 1) >> 8) & BYTE;
X
X  /* Check to see if the transfer will require the DMA address counter to
X   * go from one 64K segment to another.  If so, do not even start it, since
X   * the hardware does not carry from bit 15 to bit 16 of the DMA address.
X   * Also check for bad buffer address.	 These errors mean FS contains a bug.
X   */
X  if (user_phys == 0)
X	panic("FS gave winchester disk driver bad addr", (int) vir);
X  top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
X  if (top_end != top_addr)
X	 panic("Trying to DMA across 64K boundary", top_addr);
X
X  /* Now set up the DMA registers. */
X#ifdef DEBUG_DMA
X  printf("DMA Set: DMA_M2,M1 = 0x%x ", mode);	/* set the DMA mode */
X  printf("DMA_ADDR(l) = 0x%x ", low_addr);	/* output low-order 8 bits */
X  printf("DMA_ADDR(h) = 0x%x\n", high_addr);/* output next 8 bits */
X  printf("DMA Set: DMA_TOP = 0x%x ", top_addr);	/* output highest 4 bits */
X  printf("DMA_COUNT(l) = 0x%x ", low_ct);	/* output low 8 bits of count - 1 */
X  printf("DMA_COUNT(h) = 0x%x\n", high_ct);/* output high 8 bits of count - 1 */
X#endif
X  out_byte(DMA_INIT, DMA_RESET_VAL);        /* Disable DRQ */
X  out_byte(DMA_M2, mode);	/* set the DMA mode */
X  out_byte(DMA_M1, mode);	/* set it again */
X  out_byte(DMA_ADDR, low_addr);	/* output low-order 8 bits */
X  out_byte(DMA_ADDR, high_addr);/* output next 8 bits */
X  out_byte(DMA_TOP, top_addr);	/* output highest 4 bits */
X  out_byte(DMA_COUNT, low_ct);	/* output low 8 bits of count - 1 */
X  out_byte(DMA_COUNT, high_ct);	/* output high 8 bits of count - 1 */
X/*  out_byte(DMA_INIT, DMA_SET_VAL);	/* initialize DMA -- NOT HERE!! */
X}
X
X
X/*===========================================================================*
X *				w_reset					     * 
X *===========================================================================*/
XPRIVATE int w_reset()
X{
X/* Issue a reset to the controller.	 This is done after any catastrophe,
X * like the controller refusing to respond.
X */
X  int r, i;
X  struct xt_cmd command;
X
X#ifdef DEBUG_RESET
X  printf("Wini RESET\n");
X#endif
X  out_byte(WIN_STATUS, 0);  	/* Strobe reset bit low. */
X
X  for(i = MAX_WIN_RETRY/10; i; --i)
X  	;	/* Spin loop for a while */
X  out_byte(WIN_SELECT, 0);	/* Issue a select to the ctlr */
X
X  i=stat_wait(WST_BUSY | WST_CMD | WST_REQ);
X
X  if (i != OK) {	/* timeout */
X  	printf("Hard disk won't reset, status = %x\n", r);
X  	return(ERR);
X  }
X
X  /* Reset succeeded.  Tell WIN drive parameters. */
X  w_need_reset = FALSE;
X
X  for (i=0; i<nr_drives; i++) {
X  	if(win_specify(i, &drive[i]) != OK)
X  		return (ERR);
X
X	command.op	= WIN_RECALIBRATE;
X  	command.headdr	= i << 5;
X  	command.ctl	= drive[i].control;
X	if ((r=xt_command(&command, INT, 0, 0)) != OK) {
X		printf("Reset: recalibrate error 0x%s\n", r);
X  		return(ERR);
X	}
X  }
X  return(OK);
X}
X
X
X/*=========================================================================*
X *				win_specify				   *
X *=========================================================================*/
XPRIVATE int win_specify(drive, paramp)
X  int drive;
X  struct param *paramp;
X{ struct xt_cmd command;
X  struct xt_param config;
X  int r;
X
X  command.op = WIN_SPECIFY;		/* Specify some parameters */
X  command.headdr = drive << 5;		/* Drive number */
X
X  config.cyl_h = paramp->cylinders >> 8;/* No. of cylinders (high byte) */
X  config.cyl_l = paramp->cylinders;	/* No. of cylinders (low byte) */
X  config.heads = paramp->heads;		/* No. of heads */
X  config.rwr_h = paramp->reduced_wr >> 8;/* Start reduced write (high byte) */
X  config.rwr_l = paramp->reduced_wr;	/* Start reduced write (low byte) */
X  config.wpre_h= paramp->wr_precomp >> 8;/* Start write precompensation (high byte) */
X  config.wpre_l= paramp->wr_precomp;	/* Start write precompensation (low byte) */
X  config.ecc   = paramp->max_ecc;	/* Ecc burst length */
X
X  if((r=xt_command(&command, PGM_WRITE, 8, &config)) != OK) {
X	printf("Win_Specify Error: 0x%x\n", r);
X	w_need_reset = TRUE;
X	return(ERR);
X  } else
X	return(OK);
X}
X
X/*=========================================================================*
X *				read_ecc				   *
X *=========================================================================*/
XPRIVATE int read_ecc()
X{
X/* Read the ecc burst-length and let the controller correct the data */
X/* Is this really necessary?  I don't think so --GpO */
X  struct xt_cmd command;
X
X  command.op = WIN_ECC_READ;
X  if (xt_command(&command, PGM_READ, 1, buf) != OK) {
X	return(-1);	/* error reading ECC burst length!! */
X  }
X  return(buf[0]);
X}
X
X
X/*=========================================================================*
X *				stat_wait				   *
X *=========================================================================*/
X/* Wait for the controler status to change to some setting */
XPRIVATE int stat_wait(s)
X  int s;
X{ int i;
X  void st_check();
X
X  for (i = MAX_WIN_RETRY; i ; i--) {
X	if((in_byte(WIN_STATUS) & s) == s) {
X		return(OK);
X	}
X  }
X
X/* The controler did not select quickly (it might be a WD that does controler
Xdiagnostics when reset) so set a clock alarm and check it later.
X
XThe method for delaying on clock stolen from floppy.c.  Delaying on the clock
Xis required because fast CPUs can timeout a signed int much quicker than the
Xcontroler can complete it's diagnostics.  Software timing loops are never
Xa good thing to use, but for quick loops (like transfering data to the
Xcontroler and a fast select, etc) there is more overhead in setting up the
Xclock than just using a spin loop.
X
XThe approach I take here is to spin for one "RETRY" time (which should be 
Xshort) then start delaying on the clock (in 2/60 intervals).
X*/
X#ifdef DEBUG_STAT
X  printf("Stat_Wait: on Clock\n");
X#endif
X  for (i = MAX_WIN_RESET; i ; i--) {
X	if((in_byte(WIN_STATUS) & s) == s) {
X#ifdef DEBUG_STAT
X		printf("Stat_Wait: Took %d ticks\n", i);
X#endif
X		return(OK);
X	}
X	w_clock_mess(2, st_check);
X	receive(CLOCK, &w_mess);
X  }
X#ifdef DEBUG_STAT
X  printf("Stat_Wait: Timed OUT! 0x%x\n", in_byte(WIN_STATUS));
X#endif
X  return(ERR);
X}
X
X/*===========================================================================*
X *				w_clock_mess				     * 
X *===========================================================================*/
X/* Stolen from floppy.c */
XPRIVATE int w_clock_mess(ticks, func)
Xint ticks;			/* how many clock ticks to wait */
Xvoid (*func)();			/* function to call upon time out */
X{
X/* Send the clock task a message. */
X
X  w_mess.m_type = SET_ALARM;
X  w_mess.CLOCK_PROC_NR = WINCHESTER;
X  w_mess.DELTA_TICKS = (long) ticks;
X  w_mess.FUNC_TO_CALL = func;
X  sendrec(CLOCK, &w_mess);
X}
X
X/*===========================================================================*
X *				st_check				     * 
X *===========================================================================*/
X/* stolen from floppy.c */
XPRIVATE void st_check()
X{
X/* This routine is called when the clock task has timed out on motor startup.*/
X
X  send(WINCHESTER, &w_mess);
X}
X
X/*=========================================================================*
X *				xt_command				   *
X *=========================================================================*/
XPRIVATE int xt_command(command, mode, count, address)
X  struct xt_cmd *command;
X  int mode, count;
X  char *address;
X{ struct xt_cmd stat;
X  int rc;
X
X/* Execute a command and return a full error report if there was an error */
X  rc = xt_do(command, mode, count, address);
X
X  if (rc != OK) {	/* Error bit set!
X		Issue a "Return Controler Status" command for details */
X	stat.op = WIN_SENSE;
X	stat.headdr = command->headdr & 0x20;
X	xt_do(&stat, PGM_READ, 4, buf);
X	return(buf[0]);
X  }
X  return(OK);
X}
X
X/*=========================================================================*
X *				xt_do					   *
X *=========================================================================*/
XPRIVATE int xt_do(command, mode, count, address)
X  char *command;
X  int mode, count;
X  char *address;
X{
X/* Issue a command to the winchester controler and return the completion
X   status.  If data is to be transfered with programmed I/O, do that, too.
X   Otherwise rely on the DMA controler (which must be already set up).
X
X   If interrupts/dma are used, handle enabling/disabling of DRQ/INT on the
X   controler, and DMA IRQ in the DMAC.
X
X   If an error occurs, just return the fact that it has and let the caller
X   (usually xt_command) worry about issueing a "read status of last operation"
X   comand (using this routine again) and a read_ecc_burst.
X */
X
X  int rc;		/* Retry Count & Return Code */
X  int c, d, v;
X
X#ifdef DEBUG_DO1
X  printf("xt_do: command = 0x%x, mode 0x%x, %d long\n", command[0], mode, count);
X#endif
X
X  out_byte(WIN_DMA, mode);	/* enable DRQ and IRQ on controler */
X  out_byte(WIN_SELECT, mode);
X
X  rc = stat_wait(WST_BUSY);	/* wait for controler to ack select */
X
X  if (rc != OK) {
X	w_need_reset = TRUE;
X	return(ERR);
X  }
X
X  /* command to controler */
X  rc = xt_rw(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ,
X	     WST_BUSY | WST_CMD |             WST_REQ, 6, command);
X  if(rc == ERR) printf("xt_do: Command Output error!\n");
X
X  if (mode & INT) {  /* (no data) wait for interrupt at the end of operation */
X#ifdef DEBUG_DO2
X	printf("xt_do: wait-int");
X#endif
X	if(mode & DMA_) {
X		out_byte(DMA_INIT, 3);	/* allow DRQ in DMA controler */
X#ifdef DEBUG_DMA
X		printf("..dma..\n");
X	} else { printf("..no dma..\n");
X#endif
X	}
X	receive(HARDWARE, &w_mess);
X
X	if(mode & DMA_)
X		out_byte(DMA_INIT, 0x07);	/* Disable int from DMA */
X	out_byte(WIN_DMA, 0);			/* Disable DRQ and INT */
X	cim_xt_wini();
X  } else if (mode & PGM_READ) {		/* read from controler */
X#ifdef DEBUG_DO2
X	printf("xt_do: read\n");
X#endif
X	rc = xt_rw(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ,
X		   WST_BUSY |           WST_INPUT | WST_REQ,
X		   count, address);
X  } else if (mode & PGM_WRITE) {		/* write to controler */
X#ifdef DEBUG_DO2
X	printf("xt_do: write\n");
X#endif
X	rc = xt_rw(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ,
X		   WST_BUSY |                       WST_REQ,
X		   count, address);
X  }
X
X  rc = stat_wait(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ);
X
X  rc = in_byte(WIN_DATA);
X#ifdef DEBUG_DO1
X  printf("xt_do: RC 0x%x\n", rc);
X#endif
X/*w_clock_mess(240, st_check); receive(CLOCK, &w_mess);*/
X
X  return(OK);
X}
X
X/*=========================================================================*
X *				xt_rw					   *
X *=========================================================================*/
XPRIVATE int xt_rw(mask, bits, count, addr)
X  int mask, bits, count;
X  char *addr;
X{
X/* Send or receive commands and data to and from the controler.
X   Every time a byte is sent, we must wait for the "REQ" bit to be set.
X   We also must watch for an incorrect change of state (C/D, I/O) in the
X   controler in case it is doing something we don't want it to be.
X */
X  int rc;		/* Retry Count */
X
X#ifdef DEBUG_RW1
X  printf("xt_rw: mask=0x%x bits=0x%x count=%d addr=0x%x\n", mask, bits, count, addr);
X#endif
X
X  while (count--) {
X  	for (rc=MAX_WIN_RETRY;
X	    rc && ((in_byte(WIN_STATUS) & mask) != bits); rc--)
X		/* Wait for controler to get in the right mode */
X	    ;
X	if (!rc) {	/* oops! timed out.  Reset and try again */
X#ifdef DEBUG_RW1
X		printf("xt_rw: timed out 0x%x\n", in_byte(WIN_STATUS));
X#endif
X		w_need_reset = TRUE;
X		return(ERR);
X	}
X#ifdef DEBUG_RW3
X	printf("xt_rw: Waited %d (0x%x)\n", MAX_WIN_RETRY - rc, *addr );
X#endif
X#ifdef DEBUG_RW2
X	printf(" 0x%x ", *addr);
X#endif
X
X	if (bits & WST_INPUT)
X		*addr++ = in_byte(WIN_DATA);
X	else
X		out_byte(WIN_DATA,*addr++);
X  }
X  return(OK);
X}
X
X
X/*=========================================================================*
X *				init_params				   *
X *=========================================================================*/
X
X/*			Initialization Process
XFor ROM-table controlers:
X	look at the switch settings (some WD ROMs use more switches than IBM)
X	copy the table from ROM, based on the switch settings.
X
XFor WD Autoconfigure controlers:
X	read sector 0 and get the parameters from the 17 bytes preceeding the
X	partition table
X
XSet parameters in "wini" structure from (rom) "param" structure.
X
XRead partition table of each drive
X
X*/
X
XPRIVATE void init_params()
X{
X/* This routine is called at startup to initialize the partition table,
X * the number of drives and the controller
X */
X  unsigned int i, segment, offset;
X  int type_0, type_1;
X
X  /* Get the number of drives from the bios */
X  phys_copy(0x475L, umap(proc_ptr, D, buf, 1), 1L);
X  nr_drives = *buf & 0xFF;
X  if (nr_drives > MAX_DRIVES)	nr_drives = MAX_DRIVES;
X
X#ifdef DEBUG_CONF
X  printf("%d Winchester Drives\n", nr_drives);
X#endif
X
X  /* Read the switches from the controller */
X  i = in_byte(WIN_SELECT);
X
X#if WINI_EXT_SW
X  /* More WD compatability fun: WD "G" ROMs use THREE switches to indicate
X     which configuration table to use. */
X  type_0 = (i & 0x03) | (i & 0x40 >>4);
X  type_1 = ((i & 0x0c) >> 2) | ((i & 0x80) >> 5);
X#else
X  /* Calculate the drive types */
X  type_0 = i & 3;
X  type_1 = (i >> 2) & 3;
X#endif
X
X#if AUTO_BIOS
X  /* Get the drive parameters from sector zero of the drive if the
X     autoconfig mode of the controller has been selected */
X  if(i & AUTO_ENABLE) {
X	wd_autoconf(nr_drives, type_0, type_1);
X  } else {
X#endif /* AUTO_BIOS */
X
X	rom_conf(nr_drives, type_0, type_1);
X
X#if AUTO_BIOS
X  }
X#endif
X
X#ifdef DEBUG_CONF
X  /* Print Drive Table */
X  prdrive(&drive[0],0);	prdrive(&drive[1],1);
X/*w_clock_mess(3600, st_check); receive(CLOCK, &w_mess);*/
X  /* ----- ----- ----- */
X#endif
X
X  wini[0].wn_low	= 0L;
X  wini[0].wn_size	= (long)((long)drive[0].cylinders * 
X			  (long)drive[0].heads * (long) drive[0].sectors);
X
X  if(nr_drives > 1) {
X	  wini[DEV_PER_DRIVE].wn_drive = 1;
X	  wini[DEV_PER_DRIVE].wn_low = 0L;
X	  wini[DEV_PER_DRIVE].wn_size = (long)((long)drive[1].cylinders *
X			  (long)drive[1].heads * (long) drive[1].sectors);
X  }
X
X  /* Initialize the controller */
X  cim_xt_wini();		/* ready for XT wini interrupts */
X  if ((nr_drives > 0) && (w_reset() != OK)) nr_drives = 0;
X 
X  /* Read the partition table for each drive and save them */
X  for (i = 0; i < nr_drives; i++) {
X	w_mess.DEVICE = i * DEV_PER_DRIVE;
X	w_mess.POSITION = 0L;
X	w_mess.COUNT = BLOCK_SIZE;
X	w_mess.ADDRESS = (char *) buf;
X	w_mess.PROC_NR = WINCHESTER;
X	w_mess.m_type = DISK_READ;
X	if (w_do_rdwt(&w_mess) != BLOCK_SIZE) {
X		printf("Can't read partition table of winchester %d\n", i);
X		continue;
X	}
X
X	copy_prt(i, &wini[i * DEV_PER_DRIVE + 1], &buf[PART_TABLE_OFF]);
X
X#ifdef DEBUG_CONF
X	/* Print Partition Table */
X	prpart(&buf[PART_TABLE_OFF]);
X	/* ----- --------- ----- */
X	/* Print Wini Table */
X	prwini(&wini[i * DEV_PER_DRIVE]);
X	/* ----- ---- ----- */
X  /*w_clock_mess(3600, st_check); receive(CLOCK, &w_mess);*/
X#endif
X  }
X}
X
X
X/*=========================================================================*
X *				rom_conf 				   *
X *=========================================================================*/
XPRIVATE void rom_conf(nr_drives, type_0, type_1)
X  int nr_drives, type_0, type_1;
X{
X  unsigned int segment, offset;
X  phys_bytes address;
X
X  /* Copy the parameter vector from the saved vector table */
X  offset = vec_table[2 * WINI_0_PARM_VEC];
X  segment = vec_table[2 * WINI_0_PARM_VEC + 1];
X
X  /* Calculate the address off the parameters and copy them to buf */
X  address = hclick_to_physb(segment) + offset;
X  phys_copy(address, umap(proc_ptr, D, buf, 64), 64L);
X
X  /* Copy the parameters to the structures */
X  copy_param(&buf[type_0 * 16], &drive[0]);
X  if (nr_drives > 1) copy_param(&buf[type_1 * 16], &drive[1]);
X}
X
X#if AUTO_BIOS
X/*=========================================================================*
X *				wd_autoconf 				   *
X *=========================================================================*/
XPRIVATE void wd_autoconf(nr_drives, type_0, type_1)
X  int nr_drives, type_0, type_1;
X{	int rc;
X/* set up some phoney parameters so that we can read the first 
X   sector from the winchester.  All drives will have one cylinder 
X   and one head, but set up initially to the mini scribe drives
X   from ibm. */
X
X  drive[0].cylinders	= AUTO_CYLS;	drive[0].heads	= AUTO_HEADS;
X  drive[0].reduced_wr	= AUTO_RWC;	drive[0].max_ecc= AUTO_ECC;
X  drive[0].wr_precomp	= AUTO_WPC;	drive[0].control= AUTO_CTRL;
X  drive[0].sectors	= NR_SECTORS;
X
X  wini[0].wn_low	= 0L;		wini[0].wn_drive= 0;
X  wini[0].wn_size	=(long)AUTO_CYLS * (long)AUTO_HEADS * (long)NR_SECTORS;
X
X  if(nr_drives > 1) {
X	drive[1] = drive[0];
X	wini[DEV_PER_DRIVE] = wini[0];
X	wini[DEV_PER_DRIVE].wn_drive = 1;	/* set drive number */
X  }
X
X  cim_xt_wini();		/* ready for XT wini interrupts */
X  if(w_reset() != OK) {
X	printf("cannot setup for reading winchester parameter tables");
X	nr_drives = 0;
X  }
X
X  /* generate the request to read the first sector from the wini 0 */
X  w_mess.DEVICE = 0;		w_mess.POSITION = 0L;
X  w_mess.COUNT = BLOCK_SIZE;	w_mess.ADDRESS = (char *) buf;
X  w_mess.PROC_NR = WINCHESTER;	w_mess.m_type = DISK_READ;
X  if((rc=w_do_rdwt(&w_mess)) != BLOCK_SIZE)
X	panic("cannot read drive parameters from winchester 0", rc);
X
X  /* copy the parameter tables into the structures for later use */
X  copy_param(&buf[AUTO_PARAM], &drive[0]);
X
X  if (nr_drives > 1) {
X	/* generate the request to read the first sector from the winchester */
X	/*cp(wini[DEV_PER_DRIVE], wini[0], sizeof(struct wini));*/
X	w_mess.DEVICE	= DEV_PER_DRIVE;	w_mess.POSITION	= 0L;
X	w_mess.COUNT	= BLOCK_SIZE;		w_mess.m_type	= DISK_READ;
X	w_mess.ADDRESS	= (char *) buf;		w_mess.PROC_NR	= WINCHESTER;
X	if((rc=w_do_rdwt(&w_mess)) != BLOCK_SIZE)
X		panic("cannot read drive parameters from winchester 5", rc);
X	/* copy the parameter tables into the structures for later use */
X	copy_param(&buf[AUTO_PARAM], &drive[1]);
X  }
X}
X#endif /* AUTO_BIOS */
X
X/*=========================================================================*
X *				copy_param 				   *
X *=========================================================================*/
XPRIVATE void copy_param(src, dest)
Xregister unsigned char *src;
Xregister struct param *dest;
X{
X/* This routine copies the disk parameter table to the local "param"
X   structure (actually, a simple byte copy should suffice) and checks
X   some parameters.
X
X   If the number of sectors per track is zero, set it to NR_SECTORS */
X
X/*  cp(dest, src, sizeof(struct param));*/
X#ifdef DEBUG_INIT
X  printf("Param %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
X src[0], src[1], src[2], src[3], src[4], src[5], src[6], src[7], src[8], 
X src[9], src[10], src[11], src[12], src[13], src[14], src[15]);
X#endif
X
X  dest->cylinders = *(u16_t *)src;
X  dest->heads = src[2];
X  dest->reduced_wr = *(u16_t *)&src[3];
X  dest->wr_precomp = *(u16_t *)&src[5];
X  dest->max_ecc = src[7];
X  dest->control = src[8];
X  dest->timeout	= src[9];
X  dest->ftimeout= src[10];
X  dest->chktimeout= src[11];
X  dest->landing	= *(u16_t *)src[12];
X  dest->sectors	= src[14];
X
X  if(dest->sectors == 0)	dest->sectors = NR_SECTORS;
X  /*w_clock_mess(480, st_check); receive(CLOCK, &w_mess);*/
X}
X
X/*==========================================================================*
X *				copy_prt				    *
X *==========================================================================*/
XPRIVATE void copy_prt(dev, win, part)
X  int dev;
X  struct wini *win;
X  struct part_entry *part;
X{
X/* This routine copies the partition table for the selected drive to
X * the variables wn_low and wn_size
X */
X
X  register struct wini *wn;
X  register struct part_entry *pe;
X  int i;
X
X  wn = win;
X  pe = part;
X
X  wn->wn_drive = dev;
X  for (i=0; i < NR_PARTITIONS ; i++, pe++, wn++) {
X
X	wn->wn_drive = dev;
X	wn->wn_low = pe->lowsec;
X	wn->wn_size = pe->size;
X
X	/* Adjust low sector to a multiple of (BLOCK_SIZE/SECTOR_SIZE) for old
X	 * Minix partitions only.  We can assume the ratio is 2 and round to
X	 * even, which is slightly simpler.
X	 */
X	if (pe->sysind == OLD_MINIX_PART && wn->wn_low & 1) {
X		++wn->wn_low;
X		--wn->wn_size;
X	}
X  }
X  sort(win);
X}
X
X/*=========================================================================*
X *				sort					   *
X *=========================================================================*/
XPRIVATE void sort(wn)
Xregister struct wini wn[];
X{
X  register int i,j;
X  struct wini tmp;
X
X  for (i = 0; i < NR_PARTITIONS; i++)
X	for (j = 0; j < NR_PARTITIONS-1; j++)
X		if ((wn[j].wn_low == 0 && wn[j+1].wn_low != 0) ||
X		    (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)) {
X			tmp = wn[j];
X			wn[j] = wn[j+1];
X			wn[j+1] = tmp;
X		}
X}
X
X/*=========================================================================*
X *				cp					   *
X *=========================================================================*/
XPRIVATE void cp(dest, src, count)
X  register char *dest, *src;
X  register int count;
X{
X  while(count--)
X	*dest++ = *src++;
X}
X
X#ifdef DEBUG_CONF
Xprdrive(p, drive)
X  struct param *p;
X  int drive;
X{
X	printf("Drive %d\n", drive);
X#define BIT(A,B)	((A&B)?"1":"0")
X	printf("%-12s%-12s%-12s%-12s%-12s\n","","","Reduced","Write","");
X	printf("%-12s%-12s%-12s%-12s%-12s\n","Cylinders:","Total","Write","Precomp","Landing");
X	printf("%-12s%-12u%-12u%-12u%-12u\n","", p->cylinders, p->reduced_wr, p->wr_precomp, p->landing);
X/*	printf("%-12s%-12s%-12s%-12s\n","Timeout","Standard","Format","Check");
X	printf("%-12s%-12d%-12d%-12d\n","", p->timeout, p->ftimeout, p->chktimeout);*/
X	printf("%-12s%-12s%-12s%-12s\n","","Heads","Max ECC","Sectors");
X	printf("%-12s%-12d%-12d%-12d\n","", p->heads, p->max_ecc, p->sectors);
X
X	printf("%-12s%-6s%-6s%-12s%-12s%-12s\n","Control","ACC","ECC","Defect",">8 Heads","StepOp");
X	printf("%-12s%-6s%-6s%-12s%-12s%-12d\n\n","",
X		BIT(p->control, 0x80), BIT(p->control, 0x40),
X		BIT(p->control, 0x20), BIT(p->control, 0x08),
X		(p->control & 0x07));
X}
X
X
Xprpart(pp)
X  struct part_entry *pp;
X{	int i;
X	for(i=0;i<NR_PARTITIONS;i++, pp++)
X	    printf("[%d] %02x %02x %02x %02x %02x %02x %02x %02x %l %l\n", i,
X		pp->bootind, pp->start_head, pp->start_sec, pp->start_cyl,
X		pp->sysind, pp->last_head, pp->last_sec, pp->last_cyl,
X		pp->lowsec, pp->size);
X}
X
Xprwini(wn)
X  struct wini *wn;
X{
X  int i;
X  printf("Wini Struct...\n");
X  for(i=0; i < DEV_PER_DRIVE; i++, wn++) {
X     printf("[%d] Low %ld Size=%ld Drive=%d\n", i, wn->wn_low, wn->wn_size, wn->wn_drive);
X  }
X}
X#endif
/
-- 
		Glen Overby	<overby@plains.nodak.edu>
	uunet!plains!overby (UUCP)  overby@plains (Bitnet)