Path: tut!sunic!mcsun!uunet!uvm-gen!ackerman
From: ackerman@newton.uvm.edu (Steve Ackerman)
Newsgroups: comp.os.minix
Subject: Re: new ps_wini.c
Message-ID: <1489@uvm-gen.UUCP>
Date: 19 Apr 90 16:25:03 GMT
Sender: nobody@uvm-gen.UUCP
Organization: Division of EMBA, University of Vermont
Lines: 840


Dr. Tanenbaum has requested that I post the new ps_wini.c to help
speed up the testing process.  Attached is a shar file with a cdiff to
config and the new ps_wini.c.  I'm hoping that this will work on ALL
PS/2s.  If you can, please test it out and send me back the results.

Thanks!


-----------------------------------------------------------------------------
echo x - config.cdif
sed '/^X/s///' > config.cdif << '/'
X*** config.orig	Thu Apr 19 00:00:53 1990
X--- config	Thu Apr 19 00:02:49 1990
X***************
X*** 16,21 ****
X--- 16,26 ----
X  		what_bits=16
X  		what_cpu=8088
X  		what_wini=XT;;
X+ 	ps)	cpu=88
X+ 		wini=ps_wini
X+ 		what_bits=16
X+ 		what_cpu=8088
X+ 		what_wini=PS;;
X  	386)	cpu=386
X  		wini=at_wini
X  		what_bits=32
X***************
X*** 25,31 ****
X  		rm -f klib.x mpx.x wini.c
X  		exit 0;;
X  	*)	
X! 		echo "$prog: usage: $prog [at, bios, xt, 386, clean]"
X  		exit 1;;
X  esac
X  
X--- 30,36 ----
X  		rm -f klib.x mpx.x wini.c
X  		exit 0;;
X  	*)	
X! 		echo "$prog: usage: $prog [at, bios, xt, ps, 386, clean]"
X  		exit 1;;
X  esac
X  
X***************
X*** 62,64 ****
X--- 67,70 ----
X  echo "Now set up for the $what_wini wini driver  ($what_bits-bit) kernel"
X  
X  exit 0
X+ 
/
echo x - ps_wini.c
sed '/^X/s///' > ps_wini.c << '/'
X/* This file contains a driver for the IBM PS/2 ST506 types 1 and 2 adapters.
X * The original version was written by Wim van Leersum.  It has been
X * modified extensively to make it run the PS/2 that support use the 
X * MCA.
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/* I/O Ports used by winchester disk controller. */
X#define DATA		0x320	/* data register */
X#define ASR		0x322	/* Attachment Status Register */
X#define ATT_REG		0x324	/* Attention register */
X#define ISR		0x324	/* Interrupt status register */
X#define ACR		0x322	/* Attachment control register */
X
X/* Winchester disk controller status bytes. */
X#define BUSY		0x04	/* controller busy? */
X#define DATA_REQUEST	0x10	/* controller asking for data */
X#define IR		0x02	/* Interrupt Request */
X
X/* Winchester disk controller command bytes. */
X#define CSB	        0x40	/* Get controlers attention for a CSB */
X#define DR	        0x10	/* Get controlers attention for data transfer*/
X#define CCB	        0x80	/* same for command control block */
X#define WIN_READ  	0x15	/* command for the drive to read */
X#define WIN_WRITE 	0x95	/* command for the drive to write */
X
X/* Parameters for the disk drive. */
X#define SECTOR_SIZE      512	/* physical sector size in bytes */
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_DRIVES	2	/* maximum amount of drives we can handle */
X#define NR_DEVICES      (MAX_DRIVES * DEV_PER_DRIVE)
X#define MAX_WIN_RETRY 	10000	/* max # times to try to output to WIN */
X#define DEV_PER_DRIVE   (1 + NR_PARTITIONS)	/* whole drive & each partn */
X#define NUM_COM_BYTES	14	/* number of command bytes controller expects*/
X#define SYS_PORTA	0x92	/* MCA System Port A */
X#define LIGHT_ON	0xC0    /* Bits to turn drive light on */
X#define DRIVE_2		0x4	/* Bit to select Drive_2 */
X
X/* This driver uses DMA */
X#define DMA_RESET_VAL   0x06    /* DMA reset value */
X#define DMA_READ	0x47	/* DMA read opcode */
X#define DMA_WRITE	0x4B	/* DMA write opcode */
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_M2	       0x00C	/* DMA status port */
X#define DMA_M1	       0x00B	/* DMA status port */
X#define DMA_INIT       0x00A	/* DMA init port */
X#define DMA_STATUS	0x08    /* DMA status port for channels 0-3 */
X
X/*
X * This driver is written to run on all versions of the PS/2.  The
X * models < 50 are based on the XT.  The models >= 50 are based on the AT.
X * en_wini_int will call the appropriate cim routine to enable interrupts.
X */
X#define	en_wini_int()	{ \
X				if (ps_mca) \
X					cim_at_wini(); \
X				else \
X					cim_xt_wini(); \
X			}
X
X/* Variables. */
XPRIVATE struct wini {		/* main drive struct, one entry per drive */
X  int wn_opcode;		/* DISK_READ or DISK_WRITE */
X  int wn_procnr;		/* which proc wanted this operation? */
X  int wn_drive;			/* drive number addressed */
X  int wn_cylinder;		/* cylinder number addressed */
X  int wn_sector;		/* sector addressed */
X  int wn_head;			/* head number addressed */
X  int wn_heads;			/* maximum number of heads */
X  int wn_maxsec;		/* maximum number of sectors per track */
X  int wn_ctlbyte;		/* control byte (steprate) */
X  int wn_precomp;		/* write precompensation cylinder / 4 */
X  long wn_low;			/* lowest cylinder of partition */
X  long wn_size;			/* size of partition in blocks */
X  int wn_count;			/* byte count */
X  vir_bytes wn_address;		/* user virtual address */
X} wini[NR_DEVICES];
X
XPUBLIC int using_bios = FALSE;	/* this disk driver does not use the BIOS */
X
XPRIVATE int w_need_reset = FALSE;      /* 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
XPRIVATE int command[NUM_COM_BYTES];    /* Common command block */
X
XPRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
X
XFORWARD void ch_select();
XFORWARD void ch_unselect();
XFORWARD int com_out();
XFORWARD int controller_ready();
XFORWARD void copy_params();
XFORWARD void copy_prt();
XFORWARD int drive_busy();
XFORWARD void init_params();
XFORWARD void sort();
XFORWARD int w_do_rdwt();
XFORWARD int w_reset();
XFORWARD int w_transfer();
XFORWARD int win_init();
XFORWARD int win_results();
XFORWARD void set_command();
XFORWARD void w_dma_setup();
XFORWARD void abort_com();
XFORWARD int status();
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  /* 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 ", 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;
X  int r, device, errors = 0;
X  long sector;
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 drive */
X  wn->wn_drive = device/DEV_PER_DRIVE;	/* save drive number */
X  if (wn->wn_drive >= nr_drives)
X	return(EIO);
X  wn->wn_opcode = m_ptr->m_type;	/* DISK_READ or DISK_WRITE */
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;
X  wn->wn_cylinder = sector / (wn->wn_heads * wn->wn_maxsec);
X  wn->wn_sector =  (sector % wn->wn_maxsec) + 1;
X  wn->wn_head = (sector % (wn->wn_heads * wn->wn_maxsec) )/wn->wn_maxsec;
X  wn->wn_count = m_ptr->COUNT;
X  wn->wn_address = (vir_bytes) m_ptr->ADDRESS;
X  wn->wn_procnr = m_ptr->PROC_NR;
X
X  ch_select();		/* Select fixed disk chip */
X
X  /* This loop allows a failed operation to be repeated. */
X  while (errors <= MAX_ERRORS) {
X	errors++;		/* increment count once per loop cycle */
X	if (errors > MAX_ERRORS)
X		return(EIO);
X
X	/* First check to see if a reset is needed. */
X	if (w_need_reset) w_reset();
X
X	/* Perform the transfer. */
X	r = w_transfer(wn);
X	if (r == OK) break;	/* if successful, exit loop */
X
X  }
X
X  ch_unselect();		/* Do not select fixed disk chip anymore */
X
X  return(r == OK ? BLOCK_SIZE : EIO);
X}
X
X/*===========================================================================*
X *				w_transfer				     * 
X *===========================================================================*/
XPRIVATE int w_transfer(wn)
Xregister struct wini *wn;	/* pointer to the drive struct */
X{
X	register int i, j, r;	/* indices */
X	message dummy;		/* dummy message to recieve interrupts */
X
X	set_command(wn);	/* setup command block */
X
X	if (com_out(6, (wn->wn_drive) ? CCB | DRIVE_2 : CCB) != OK) 
X		return(ERR);	/* output CCB to controller */
X
X	for (i = 0; i < MAX_WIN_RETRY; i++)
X		if (in_byte(ASR) & IR) break;	/* wait for acknowledgment */
X
X	if (i == MAX_WIN_RETRY) {
X		w_need_reset = TRUE;	/* ACK! this shouldn't happen -- bug?*/
X		return(ERR);
X	}
X
X	if (win_results() != OK) {
X		w_need_reset = TRUE;
X		return(ERR);
X	}
X
X	w_dma_setup(wn);		/* setup DMA */
X	out_byte(ACR, 0x03);		/* turn on interrupts and DMA */
X	if (com_out(0, DR) != OK) return(ERR); /* ask for data transfer */
X
X	r = OK;				/* be optimistic! */
X
X	/*
X	 * This loop should be explained.  The exact causes of interrupts
X         * has not been documented.  According to the slim information I have,
X	 * only one interrupt should occur.  However, sometimes two or more
X   	 * do occur.  Hence, the DMA transfer count is checked at the end
X	 * of the loop.  If there are still some bytes left to be transferred,
X  	 * the loop is re-executed.
X	 */
X
X	 do {
X		en_wini_int();		/* OK to receive interrupts */
X
X		receive(HARDWARE, &dummy);	/* receive interrupt */
X
X	  	if (win_results() != OK) {
X			abort_com();		/* stop the current command */
X			r = ERR;
X			break;
X	  	} 
X
X	} while ((in_byte(DMA_STATUS) & 0x8) == 0);
X
X	out_byte(ACR, 0x0);	/* disable interrupt and dma */
X
X  	return(r);
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
X  int i, r;
X
X  out_byte(ACR, 0x80);	/* Strobe reset bit high. */
X  out_byte(ACR, 0);	/* Strobe reset bit low. */
X
X  for (i = 0; i < MAX_WIN_RETRY; i++) {
X	if ((in_byte(ASR) & IR) == IR) break;
X	milli_delay(20);
X  }
X
X  if (i == MAX_WIN_RETRY) {
X	printf("Winchester won't reset\n");
X	return(ERR);
X  }
X
X  /* Reset succeeded.  Tell WIN drive parameters. */
X  if (win_init() != OK) {		/* Initialize the controler */
X	printf("Winchester wouldn't accept parameters\n");
X	return(ERR);
X  }
X  
X  w_need_reset = FALSE;
X  return(OK);
X}
X
X/*===========================================================================*
X *				win_init				     * 
X *===========================================================================*/
XPRIVATE int win_init()
X{
X/* Routine to initialize the drive parameters after boot or reset */
X
X  register int i, cyl;
X
X/*
X * Command Specify Bytes
X */
X#define CSB0	0x8F		/* Command Specify Block byte 0 */
X#define	CSB1	0x83		/* ST506/2 Interface, 5 Mbps transfer rate */
X#define CSB2	0x0B		/* Drive Gap 1 (value gotten from bios) */
X#define CSB3	0x04		/* Drive Gap 2 (value gotten from bios) */
X#define CSB4	0x1B		/* Drive Gap 3 (value gotten from bios) */
X#define CSB5	0x0D		/* Sync field length */
X#define CSB6	0x0		/* Step rate in 50 microseconds */
X#define CSB7	0x0		/* IBM reserved */
X#define CSB10	0x03		/* Max Cylinders of 1024 Cylinders */
X#define CSB11	0xff	
X	
X  command[0] = CSB0;
X
X  if (ps_mca) { 
X	/* Setup the Command Specify Block */
X  	command[1] = CSB1;
X  	command[2] = CSB2;
X  	command[3] = CSB3;
X  	command[4] = CSB4;
X  	command[5] = CSB5;
X  	command[6] = CSB6;
X  	command[7] = CSB7;
X  	command[8] = wini[0].wn_precomp >> 12;	/* set upper write precomp */
X  	command[9] = wini[0].wn_precomp & 0xFF;	/* set lower write precomp */
X  	command[10] = CSB10;
X  	command[11] = CSB11;
X  	command[12] = wini[0].wn_maxsec;
X  	command[13] = wini[0].wn_heads;
X  } else {
X	/*
X	 * The original ps_wini driver set all the bytes in the Command
X	 * Specify Block to 0.  According to my documentation, the adapter
X  	 * should have complained.  However, supposedly it did work on a
X	 * model 30, so if we're running on a NON-mca machine (i.e. model 30)
X	 * set the CSB to all 0's
X	 */
X	for (i = 1; i < 14; i ++)
X		command[i] = 0;
X  }
X
X  out_byte(ACR, 0);			/* Make sure accepts 8 bits */
X  if (com_out(14, CSB) != OK)		/* Output command block */
X	return(ERR);
X
X  if (nr_drives > 1) {
X  	command[8] = wini[5].wn_precomp >> 12;	/* set upper write precomp */
X  	command[9] = wini[5].wn_precomp & 0xFF;	/* set lower write precomp */
X  	command[12] = wini[5].wn_maxsec;
X  	command[13] = wini[0].wn_heads;
X
X	if (com_out(14, CSB | DRIVE_2) != OK)	/* Output command block */
X		return(ERR);
X
X  }
X
X  out_byte(ACR, 0);		/* no interrupts and no DMA */
X  return(OK);
X}
X
X/*============================================================================*
X *				win_results				      *
X *============================================================================*/
XPRIVATE int win_results()
X{
X/* Extract results from the controller after an operation.
X */
X
X	if ((in_byte(ISR) & 0xE3) != 0) return(ERR);
X	return(OK);
X}
X
X/*==========================================================================*
X *				controller_ready			    *
X *==========================================================================*/
XPRIVATE int controller_ready()
X{
X/* Wait until controller is ready for output; return zero if this times out. */
X
X#define MAX_CONTROLLER_READY_RETRIES	1000	/* should calibrate this */
X
X  register int retries;
X
X  retries = MAX_CONTROLLER_READY_RETRIES + 1;
X  while ((--retries != 0) && ((status() & BUSY) == BUSY))
X	;			/* wait until not busy */
X  return(retries);		/* nonzero if ready */
X}
X
X/*============================================================================*
X *				com_out					      *
X *============================================================================*/
XPRIVATE int com_out(nr_words, attention)
Xint nr_words;
Xint attention;
X{
X/* Output the command block to the winchester controller and return status */
X
X	register int i, j;
X
X  	if (!controller_ready()) {
X		printf("Controller not ready in com_out\n");
X		dump_isr();
X		w_need_reset = TRUE;
X		return(ERR);
X  	}
X
X	out_byte(ATT_REG, attention);	/* get controller's attention */
X
X	if (nr_words == 0) return(OK);	/* may not want to output command */
X
X	for (i = 0; i < nr_words; i++) {	/* output command block */
X		for (j = 0; j < MAX_WIN_RETRY; j++)	/* wait  */
X			if (status() & DATA_REQUEST) break;
X
X		if (j == MAX_WIN_RETRY) {
X			w_need_reset = TRUE;
X			dump_isr();
X			return(ERR);
X		}
X		out_byte(DATA, command[i]);   	/* issue command */
X  	}
X
X  return(OK);
X
X}
X
X/*============================================================================*
X *				init_params				      *
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  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, (vir_bytes)buf, 16), 16L);
X
X  /* Copy the parameters to the structures */
X  copy_params(buf, &wini[0]);
X
X  /* Copy the parameter vector from the saved vector table */
X  offset = vec_table[2 * WINI_1_PARM_VEC];
X  segment = vec_table[2 * WINI_1_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, (vir_bytes)buf, 16), 16L);
X
X  /* Copy the parameters to the structures */
X  copy_params(buf, &wini[5]);
X
X  /* Get the nummer of drives from the bios */
X  phys_copy(0x475L, umap(proc_ptr, D, (vir_bytes)buf, 1), 1L);
X  nr_drives = (int) *buf;
X  if (nr_drives > MAX_DRIVES) nr_drives = MAX_DRIVES;
X
X  /* Set the parameters in the drive structure */
X  wini[0].wn_low = wini[5].wn_low = 0L;
X
X  /* Initialize the controller */
X  ch_select();  /* select fixed disk chip */
X  win_init();   /* output parameters to controller */
X  ch_unselect(); /* unselect the fixed disk chip */
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 * 5;
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 on winchester %d\n",i);
X		milli_delay(20000);
X		continue;
X	}
X	if (buf[510] != 0x55 || buf[511] != 0xAA) {
X		printf("Invalid partition table on winchester %d\n",i);
X		milli_delay(20000);
X		continue;
X	}
X	copy_prt((int)i*5);
X  }
X
X}
X
X/*============================================================================*
X *				copy_params				      *
X *============================================================================*/
XPRIVATE void copy_params(src, dest)
Xregister unsigned char *src;
Xregister struct wini *dest;
X{
X/* This routine copies the parameters from src to dest
X * and sets the parameters for partition 0 and 5
X*/
X  register int i;
X  long cyl, heads, sectors;
X
X  for (i=0; i<5; i++) {
X	dest[i].wn_heads = (int)src[2];
X	dest[i].wn_precomp = *(u16_t *)&src[5] >> 2;
X	dest[i].wn_ctlbyte = (int)src[8];
X	dest[i].wn_maxsec = (int)src[14];
X  }
X  cyl = (long)(*(u16_t *)src);
X  heads = (long)dest[0].wn_heads;
X  sectors = (long)dest[0].wn_maxsec;
X  dest[0].wn_size = cyl * heads * sectors;
X}
X
X/*===========================================================================*
X *				copy_prt				     *
X *===========================================================================*/
XPRIVATE void copy_prt(base_dev)
Xint base_dev;			/* base device for drive */
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 part_entry *pe;
X  register struct wini *wn;
X
X  for (pe = (struct part_entry *) &buf[PART_TABLE_OFF],
X       wn = &wini[base_dev + 1];
X       pe < ((struct part_entry *) &buf[PART_TABLE_OFF]) + NR_PARTITIONS;
X       ++pe, ++wn) {
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(&wini[base_dev + 1]);
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 *				ch_select	 		  	     * 
X *===========================================================================*/
XPRIVATE void ch_select() 
X{
X/*
X * The original comment said that this function select the fixed disk
X * drive "chip".  I have no idea what this does, but it sounds important.
X * For mca, this function will turn on the fixed disk activity light.
X */
X
X if (ps_mca)	/* If MCA, turn on fixed disk activity light */
X	out_byte(SYS_PORTA, in_byte(SYS_PORTA) | LIGHT_ON); 
X else		/* else bit 1 of Planar Control Reg selects it (disk?) */
X        out_byte(PCR, in_byte(PCR) | 1); 
X}
X
X/*==========================================================================*
X *			ch_unselect				 	    * 
X *==========================================================================*/
XPRIVATE void ch_unselect() 
X{
X/*
X * The original comment said that this function unselects the fixed disk
X * drive "chip".  I have no idea what this does, but it sounds important.
X * For mca, this function will turn off the fixed disk activity light.
X */
X if (ps_mca)	/* If MCA, turn off fixed disk activity light */
X	out_byte(SYS_PORTA, in_byte(SYS_PORTA)  & ~LIGHT_ON); 
X else		/* else bit 1 of Planar Control Reg selects it (disk?) */
X 	out_byte(PCR, in_byte(PCR) & ~1);
X}
X
X/*==========================================================================*
X *			w_dma_setup				  	    * 
X *==========================================================================*/
XPRIVATE void w_dma_setup(wn)
Xregister struct wini *wn;
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 PS/2 MCA 
X * chip IS capable of doing a DMA across a 64K boundary, but not sure about
X * model 30, so test for it anyway.
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_opcode == DISK_READ ? DMA_READ : DMA_WRITE);
X  vir = (vir_bytes) wn->wn_address;
X  ct = (vir_bytes) BLOCK_SIZE;
X  user_phys = numap(wn->wn_procnr, vir, BLOCK_SIZE);
X
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  (void)in_byte(DMA_STATUS);	/* clear the status byte */
X
X  /* Now set up the DMA registers. */
X  out_byte(DMA_INIT, DMA_RESET_VAL); /* reset the dma controller */
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, 3);	/* initialize DMA */
X}
X
X/*===========================================================================*
X *				abort_com				     *
X *===========================================================================*/
XPRIVATE void abort_com()
X{
X/*
X * Abort_com will terminate the current command the controller is working
X * on.  Most likely, a bad sector has been found on the disk.
X */
X
X	message dummy;		/* dummy message for receive() */
X	
X	out_byte(ACR, 0x2);	/* Turn off everything except interrupts */
X	if (com_out(0, 0x1) != OK)
X		panic("Winchester controller not accepting abort command", 0);
X
X	en_wini_int();			/* can now allow interrupts */
X	receive(HARDWARE, &dummy);	/* wait for the interrupt */
X
X	if (win_results() != OK)
X		w_reset();		/* this should not be necessary */
X
X	out_byte(ACR, 0x0);	/* shut off interrupts */
X
X}
X
X/*===========================================================================*
X *				dump_isr				     *
X *===========================================================================*/
XPRIVATE dump_isr()
X{
X/*
X * Dump_isr will print out an informative message of what the controller
X * has reported to the system.  This is for use in debugging and in case
X * of hardware error.
X */
X	register int stat = in_byte(ISR);
X
X#define ISR_TERM_ERROR(x)	(x & 0x80)
X#define	ISR_INVALID_COM(x)	(x & 0x40)
X#define ISR_COMMAND_REJ(x)	(x & 0x20)
X#define ISR_DRIVE(x)		(x & 0x04) ? 2 : 1
X#define ISR_ERROR_REC(x)	(x & 0x02)
X#define ISR_EQP_CHECK(x)	(x & 0x01)
X
X	printf("Drive #: %d, ST506 WINI adapter reports:\n", ISR_DRIVE(stat));
X	if (ISR_TERM_ERROR(stat)) printf("\t\tTermination Error\n");
X	if (ISR_INVALID_COM(stat)) printf("\t\tInvalid Command Sent\n");
X	if (ISR_COMMAND_REJ(stat)) printf("\t\tCommand was rejected\n");
X	if (ISR_ERROR_REC(stat)) printf("\t\tError rec. procedure invoked\n");
X	if (ISR_EQP_CHECK(stat)) printf("\t\tEquipment check, reset needed\n");
X
X}
X
X/*===========================================================================*
X *				set_command			             *
X *===========================================================================*/
XPRIVATE void set_command(wn)
Xregister struct wini *wn;
X{
X	command[0] = wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE;
X	command[1] = ((wn->wn_head << 4) & 0xF0) | 
X					((wn->wn_cylinder >> 8) & 0x03);
X	command[2] = wn->wn_cylinder & 0xFF;
X	command[3] = wn->wn_sector;
X	command[4] = 2;
X	command[5] = BLOCK_SIZE/SECTOR_SIZE;	
X}
X
X
X/*===========================================================================*
X *				status					     *
X *===========================================================================*/
XPRIVATE int status()
X{
X/* Get status of the controler */
X
X  return in_byte(ASR);
X}
X
/
--
Steven Ackerman (ackerman@uvm.edu) 
