[comp.os.minix] harddisk driver fixes 1 of 2

hgm@beta.UUCP (Harry McGavran) (05/06/87)

Here is part one of two.



I have a WD 1002A-WX1 hard disk controller on one PC and a BASIC TIME
(same chip set as WD) controller in another. The BASIC TIME is in
an XT with a 6 Mhz 80286 board in it, the WX1 is in a regular IBM PC-2.

I picked up the changes to xt_wini.c posted by Gary Oliver for the WX2
and got the manual for WD. It turns out that the WX1 and the WX2 should
be the same for the purposes of MINIX. But the BASIC TIME on the faster
board would not work with Gary's mods installed. The WX1 did. I discovered
that the faster CPU would not wait long enough with maximum retries
set to 10000 for all the status bits to come up properly during the
reset. The solution was to change to 32000. I also moved the
select command out of the status loop in the reset function and put
it just in front of the status loop. It didn't seem that one should
select over and over. I might be wrong about this, but both systems
now work great. The higher maximum doesn't seem to hurt the
performance of the slower systems, so I just use the higher number
everywhere. I also increased the time for the spin loop before the
status loop, by changing the limit from 1000, to the maximum retries
divided by 10.

In pursuing this, I learned alot about the WD controller and the code.
I fixed a couple typos in Gary's changes and in the original code.
The disk table pointers (type_0 and type_1)
are reversed according to my documentation
(in both the original and in Gary's changes), so I fixed that.

I also produced a diff of the old and the new, suitable for using
the latest version of "patch".

Move a copy of the original xt_wini.c to a system with the latest
"patch". Cut out the patch below and call it "wini.fixes".

Then type "patch -l xt_wini.c <wini.fixes".

This will get you a new version of xt_wini.c which you should move
to the "kernel" subdirectory on MINIX (or your development system)
and call it wini.c there. Then make a new kernel following the
instructions in the book.

I am most grateful for all the work Gary did.

I make no claims for the suitability of these changes for anything,
but hopefully you will find them helpful.

--------------------------------Cut here--------------------------------
*** xt_wini.c	Wed May  6 13:07:26 1987
--- xt_wini.new	Wed May  6 09:38:33 1987
***************
*** 27,35 ****
--- 27,43 ----
  #include "type.h"
  #include "proc.h"
  
+ #define DEBUG	       FALSE	/* TRUE: enable debug messages		   */
+ #define MONITOR        TRUE	/* TRUE: monitor performance of busy loops */
+ 
  /* I/O Ports used by winchester disk task. */
  #define WIN_DATA       0x320	/* winchester disk controller data register */
  #define WIN_STATUS     0x321	/* winchester disk controller status register */
+ #define WST_REQ        0x001	/* Request bit */
+ #define WST_INPUT      0x002	/* Set if controller is writing to cpu */
+ #define WST_BUS        0x004	/* Command/status bit */
+ #define WST_BUSY       0x008	/* Busy */
+ #define WST_INTERRUPT  0x020	/* Interrupt generated ?? */
  #define WIN_SELECT     0x322	/* winchester disk controller select port */
  #define WIN_DMA        0x323	/* winchester disk controller dma register */
  #define DMA_ADDR       0x006	/* port for low 16 bits of DMA address */
***************
*** 50,56 ****
  #define DMA_INT		   3 /* Command with dma and interrupt */
  #define INT		   2	/* Command with interrupt, no dma */
  #define NO_DMA_INT	   0	/* Command without dma and interrupt */
- #define CTRL_BYTE	   5 /* Control byte for controller */
  
  /* DMA channel commands. */
  #define DMA_READ        0x47	/* DMA read opcode */
--- 58,63 ----
***************
*** 67,73 ****
  #define MAX_ERRORS         4	/* how often to try rd/wt before quitting */
  #define MAX_RESULTS        4	/* max number of bytes controller returns */
  #define NR_DEVICES        10	/* maximum number of drives */
! #define MAX_WIN_RETRY  10000	/* max # times to try to output to WIN */
  #define PART_TABLE     0x1C6	/* IBM partition table starts here in sect 0 */
  #define DEV_PER_DRIVE      5	/* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */
  
--- 74,80 ----
  #define MAX_ERRORS	   4	/* how often to try rd/wt before quitting */
  #define MAX_RESULTS	   4	/* max number of bytes controller returns */
  #define NR_DEVICES	  10	/* maximum number of drives */
! #define MAX_WIN_RETRY  32000	/* max # times to try to output to WIN */
  #define PART_TABLE     0x1C6	/* IBM partition table starts here in sect 0 */
  #define DEV_PER_DRIVE	   5	/* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */
  
***************
*** 75,85 ****
  PRIVATE struct wini {		/* main drive struct, one entry per drive */
    int wn_opcode;		/* DISK_READ or DISK_WRITE */
    int wn_procnr;		/* which proc wanted this operation? */
!   int wn_drive;			/* drive number addressed */
    int wn_cylinder;		/* cylinder number addressed */
    int wn_sector;		/* sector addressed */
    int wn_head;			/* head number addressed */
    int wn_heads;			/* maximum number of heads */
    long wn_low;			/* lowest cylinder of partition */
    long wn_size;			/* size of partition in blocks */
    int wn_count;			/* byte count */
--- 82,93 ----
  PRIVATE struct wini {		/* main drive struct, one entry per drive */
    int wn_opcode;		/* DISK_READ or DISK_WRITE */
    int wn_procnr;		/* which proc wanted this operation? */
!   int wn_drive; 		/* drive number addressed (<< 5) */
    int wn_cylinder;		/* cylinder number addressed */
    int wn_sector;		/* sector addressed */
    int wn_head;			/* head number addressed */
    int wn_heads; 		/* maximum number of heads */
+   int wn_ctrl_byte;		/* Control byte for COMMANDS (10-Apr-87 GO) */
    long wn_low;			/* lowest cylinder of partition */
    long wn_size; 		/* size of partition in blocks */
    int wn_count; 		/* byte count */
***************
*** 102,109 ****
--- 110,121 ----
  	int reduced_wr; 	/* First cylinder with reduced write current */
  	int wr_precomp; 	/* First cylinder with write precompensation */
  	int max_ecc;		/* Maximum ECC burst length */
+ 	int ctrl_byte;		/* Copied control-byte from bios tables */
  } param0, param1;
  
+ #if	DEBUG
+ #define 	port_out(port, val)	xport_out(port, val)
+ #endif	/* DEBUG */
  /*===========================================================================*
   *				winchester_task 			     *
   *===========================================================================*/
***************
*** 114,120 ****
    int r, caller, proc_nr;
  
    /* First initialize the controller */
!   init_param();
  
    /* Here is the main loop of the disk task.  It waits for a message, carries
     * it out, and sends a reply.
--- 126,132 ----
    int r, caller, proc_nr;
  
    /* First initialize the controller */
!   init_params();
  
    /* Here is the main loop of the disk task.  It waits for a message, carries
     * it out, and sends a reply.
***************
*** 165,173 ****
    if (m_ptr->COUNT != BLOCK_SIZE)
  	return(EINVAL);
    wn = &wini[device];		/* 'wn' points to entry for this drive */
!   wn->wn_drive = device/DEV_PER_DRIVE;	/* save drive number */
!   if (wn->wn_drive >= nr_drives)
! 	return(EIO);
    wn->wn_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
    if (m_ptr->POSITION % BLOCK_SIZE != 0)
  	return(EINVAL);
--- 177,183 ----
    if (m_ptr->COUNT != BLOCK_SIZE)
  	return(EINVAL);
    wn = &wini[device];		/* 'wn' points to entry for this drive */
! 
    wn->wn_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
    if (m_ptr->POSITION % BLOCK_SIZE != 0)
  	return(EINVAL);
***************
*** 264,280 ****
  
    /* The command is issued by outputing 6 bytes to the controller chip. */
    command[0] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE);
!   command[1] = (wn->wn_head | (wn->wn_drive << 5));
    command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector);
    command[3] = (wn->wn_cylinder & 0xFF);
    command[4] = BLOCK_SIZE/SECTOR_SIZE;
!   command[5] = CTRL_BYTE;
    if (com_out(DMA_INT) != OK)
  	return(ERR);
  
    port_out(DMA_INIT, 3);	/* initialize DMA */
    /* Block, waiting for disk interrupt. */
!   receive(HARDWARE, &w_mess);
  
    /* Get controller status and check for errors. */
    if (win_results(wn) == OK)
--- 274,291 ----
  
    /* The command is issued by outputing 6 bytes to the controller chip. */
    command[0] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE);
!   command[1] = wn->wn_head | wn->wn_drive;
    command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector);
    command[3] = (wn->wn_cylinder & 0xFF);
    command[4] = BLOCK_SIZE/SECTOR_SIZE;
!   command[5] = wn->wn_ctrl_byte;
! 
    if (com_out(DMA_INT) != OK)
  	return(ERR);
  
    port_out(DMA_INIT, 3);	/* initialize DMA */
    /* Block, waiting for disk interrupt. */
!   w_wait_int();
  
    /* Get controller status and check for errors. */
    if (win_results(wn) == OK)
***************
*** 300,322 ****
  
    port_in(WIN_DATA, &status);
    port_out(WIN_DMA, 0);
!   if (!(status & 2))
  	return(OK);
    command[0] = WIN_SENSE;
!   command[1] = (wn->wn_drive << 5);
    if (com_out(NO_DMA_INT) != OK)
  	return(ERR);
  
    /* Loop, extracting bytes from WIN */
    for (i = 0; i < MAX_RESULTS; i++) {
! 	if (hd_wait(1) != OK)
  		return(ERR);
  	port_in(WIN_DATA, &status);
  	wn->wn_results[i] = status & BYTE;
    }
!   if (wn->wn_results[0] & 63)
  	return(ERR);
!   else
  	return(OK);
  }
  
--- 311,341 ----
  
    port_in(WIN_DATA, &status);
    port_out(WIN_DMA, 0);
!   if (!(status & 2))		/* Test "error" bit */
  	return(OK);
    command[0] = WIN_SENSE;
!   command[1] = wn->wn_drive;
    if (com_out(NO_DMA_INT) != OK)
  	return(ERR);
  
    /* Loop, extracting bytes from WIN */
    for (i = 0; i < MAX_RESULTS; i++) {
! 	if (hd_wait(WST_REQ) != OK)
  		return(ERR);
  	port_in(WIN_DATA, &status);
  	wn->wn_results[i] = status & BYTE;
    }
!   if(hd_wait(WST_REQ) != OK)	/* Missing from 		*/
!      return (ERR);		/* Original.  11-Apr-87 G.O.	*/
! 
!   port_in(WIN_DATA, &status);	     /* Read "error" flag */
! 
!   if(((status & 2) != 0) || (wn->wn_results[0] & 0x3F)) {
! #if	DEBUG
! 	printf("\nwin_results: results[0] = %x", wn->wn_results[0]);
! #endif	/* DEBUG */
  	return(ERR);
!   } else
  	return(OK);
  }
  
***************
*** 331,339 ****
   * can only write to it when it is listening, and it decides when to listen.
   * If the controller refuses to listen, the WIN chip is given a hard reset.
   */
  
    if (w_need_reset) return;	/* if controller is not listening, return */
!   if (hd_wait(1) == OK)
  	port_out(WIN_DATA, val);
  }
  
--- 350,363 ----
   * can only write to it when it is listening, and it decides when to listen.
   * If the controller refuses to listen, the WIN chip is given a hard reset.
   */
+   int r;
  
    if (w_need_reset) return;	/* if controller is not listening, return */
! 
!   do {
! 	port_in(WIN_STATUS, &r);
!   } while((r & (WST_REQ | WST_BUSY)) == WST_BUSY);
! 
    port_out(WIN_DATA, val);
  }
  
***************
*** 346,361 ****
   * like the controller refusing to respond.
   */
  
!   int r = 1, i;
  
    /* Strobe reset bit low. */
!   port_out(WIN_STATUS, r);
!   for (i = 0; i < 10000; i++) {
  	port_in(WIN_STATUS, &r);
! 	if ( (r&01) == 0)break;
    }
!   if (r & 2) {
! 	printf("Hard disk won't reset\n");
  	return(ERR);
    }
  
--- 370,396 ----
   * like the controller refusing to respond.
   */
  
!   int r = 0, i;
  
    /* Strobe reset bit low. */
!   port_out(WIN_STATUS, 0);
! 
!   for(i = MAX_WIN_RETRY/10; i; --i)
! 	;	/* Spin loop for a while */
! 
!   port_out(WIN_SELECT, 0);	/* Issue select pulse */
!   for (i = 0; i < MAX_WIN_RETRY; i++) {
  	port_in(WIN_STATUS, &r);
! 	if(r & 0x30)		/* What is 10? 20 = INTERRUPT */
! 		return (ERR);
! 
! 	if((r & (WST_BUSY | WST_BUS | WST_REQ)) ==
! 		(WST_BUSY | WST_BUS | WST_REQ))
! 		break;
    }
! 
!   if (i == MAX_WIN_RETRY) {
! 	printf("Hard disk won't reset, status = %x\n", r);
  	return(ERR);
    }
  
***************
*** 362,462 ****
    /* Reset succeeded.  Tell WIN drive parameters. */
    w_need_reset = FALSE;
  
!   return(win_init());
! }
  
! /*===========================================================================*
!  *				win_init				     * 
!  *===========================================================================*/
! PRIVATE win_init()
! {
! /* Routine to initialize the drive parameters after boot or reset */
  
!   register int i;
! 
!   command[0] = WIN_SPECIFY;		/* Specify some parameters */
!   command[1] = 0;			/* Drive 0 */
!   if (com_out(NO_DMA_INT) != OK)	/* Output command block */
  	return(ERR);
-   lock();
  
!   /* No. of cylinders (high byte) */
!   win_out(param0.nr_cyl >> 8);
  
!   /* No. of cylinders (low byte) */
!   win_out(param0.nr_cyl & 0xFF);
  
!   /* No. of heads */
!   win_out(param0.nr_heads);
  
!   /* Start reduced write (high byte) */
!   win_out(param0.reduced_wr >> 8);
  
!   /* Start reduced write (low byte) */
!   win_out(param0.reduced_wr & 0xFF);
  
!   /* Start write precompensation (high byte) */
!   win_out(param0.wr_precomp >> 8);
  
-   /* Start write precompensation (low byte) */
-   win_out(param0.wr_precomp & 0xFF);
  
!   /* Ecc burst length */
!   win_out(param0.max_ecc);
!   unlock();
  
!   if (check_init() != OK) {	/* See if controller accepted parameters */
! 	w_need_reset = TRUE;
! 	return(ERR);
    }
  
!   if (nr_drives > 1) {
! 	command[1] = (1 << 5);			/* Drive 1 */
  	if (com_out(NO_DMA_INT) != OK)		/* Output command block */
  		return(ERR);
  	lock();
  
  	/* No. of cylinders (high byte) */
! 	win_out(param1.nr_cyl >> 8);
  
  	/* No. of cylinders (low byte) */
! 	win_out(param1.nr_cyl & 0xFF);
  
  	/* No. of heads */
! 	win_out(param1.nr_heads);
  
  	/* Start reduced write (high byte) */
! 	win_out(param1.reduced_wr >> 8);
  
  	/* Start reduced write (low byte) */
! 	win_out(param1.reduced_wr & 0xFF);
  
  	/* Start write precompensation (high byte) */
! 	win_out(param1.wr_precomp >> 8);
  
  	/* Start write precompensation (low byte) */
! 	win_out(param1.wr_precomp & 0xFF);
  
  	/* Ecc burst length */
! 	win_out(param1.max_ecc);
  	unlock();
  	if (check_init() != OK) {  /* See if controller accepted parameters */
  		w_need_reset = TRUE;
  		return(ERR);
  	}
!   }
!   for (i=0; i<nr_drives; i++) {
! 	command[0] = WIN_RECALIBRATE;
! 	command[1] = i << 5;
! 	command[5] = CTRL_BYTE;
! 	if (com_out(INT) != OK)
! 		return(ERR);
! 	receive(HARDWARE, &w_mess);
! 	if (win_results() != OK) {
! 		w_need_reset = TRUE;
! 		return(ERR);
! 	}
!   }
    return(OK);
  }
  
--- 397,516 ----
    /* Reset succeeded.  Tell WIN drive parameters. */
    w_need_reset = FALSE;
  
!   if(win_specify(0, &param0) != OK)
! 	return (ERR);
  
! #if	DEBUG
!   printf("\nw_reset: drive 0 specified");
! #endif	/* DEBUG */
  
!   if ((nr_drives > 1) && (win_specify(1, &param1) != OK))
  	return (ERR);
  
! #if	DEBUG
!   printf("\nw_reset: drive 1 specified");
! #endif	/* DEBUG */
  
!   for (i=0; i<nr_drives; i++) {
! 	command[0] = WIN_RECALIBRATE;
! 	command[1] = i << 5;
! 	command[5] = wini[i * DEV_PER_DRIVE].wn_ctrl_byte;
  
! #if	DEBUG
! 	printf("\nw_reset: recal %d, ctrl_byte = %x", i, command[5]);
! #endif	/* DEBUG */
  
! 	if (com_out(INT) != OK)
! 		return(ERR);
  
! 	w_wait_int();
  
! 	if (win_results(&wini[i * DEV_PER_DRIVE]) != OK) {
! 		w_need_reset = TRUE;
! #if	DEBUG
! 		printf("\nw_reset: Recal error");
! #endif	/* DEBUG */
! 		return(ERR);
! 	}
!      }
!      return(OK);
! }
  
  
! /*===========================================================================*
!  *				w_wait_int				     *
!  *===========================================================================*/
! PRIVATE w_wait_int()
! {
!    /*DEBUG: loop looking for 0x20 in status (I don't know what that is!!) */
!    /*		 10-Apr-87. G. Oliver					  */
!    int r, i; /* Some local storage */
  
!    receive(HARDWARE, &w_mess);
! 
!    port_out(DMA_INIT, 0x07);	/* Disable int from DMA */
! 
!    for(i=0; i<MAX_WIN_RETRY; ++i) {
--------------------Concatenate second part here------------------------

Harry McGavran
hgm@LANL.GOV
Los Alamos National Laboratory
MS-B294, Group C-8
Los Alamos, New Mexico 87545
505/667-4050