⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xt_wini.c

📁 MINIX2.0操作系统源码 MINIX2.0操作系统源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* This file contains the device dependent part of a driver for the WD
 * winchester controller from Western Digital (WX-2 and related controllers).
 * It was written by Adri Koppes.
 *
 * The file contains one entry point:
 *
 *	 xt_winchester_task:	main entry when system is brought up
 *
 *
 * Changes:
 *	10 Apr 1987 by Gary Oliver: use with the Western Digital WX-2.
 *		  ? by Harry McGavran: robust operation on turbo clones.
 *		  ? by Mike Mitchell: WX-2 auto configure operation.
 *	 2 May 1992 by Kees J. Bot: device dependent/independent split.
 */

#include "kernel.h"
#include "driver.h"
#include "drvlib.h"

#if ENABLE_XT_WINI

/* If the DMA buffer is large enough then use it always. */
#define USE_BUF		(DMA_BUF_SIZE > BLOCK_SIZE)

/* 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_DRQ        0x010	/* DMA request */
#define WST_IRQ        0x020	/* Interrupt request */
#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 */
#define DMA_TOP	       0x082	/* port for top 4 bits of 20-bit DMA addr */
#define DMA_COUNT      0x007	/* port for DMA count (count =	bytes - 1) */
#define DMA_FLIPFLOP   0x00C	/* DMA byte pointer flop-flop */
#define DMA_MODE       0x00B	/* DMA mode port */
#define DMA_INIT       0x00A	/* DMA init port */

/* Winchester disk controller command bytes. */
#define WIN_RECALIBRATE	0x01	/* command for the drive to recalibrate */
#define WIN_SENSE	0x03	/* command for the controller to get its status */
#define WIN_READ	0x08	/* command for the drive to read */
#define WIN_WRITE	0x0a	/* command for the drive to write */
#define WIN_SPECIFY	0x0C	/* command for the controller to accept params	*/
#define WIN_ECC_READ	0x0D	/* command for the controller to read ecc length */

#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 */

/* DMA channel commands. */
#define DMA_READ	0x47	/* DMA read opcode */
#define DMA_WRITE	0x4B	/* DMA write opcode */

/* Parameters for the disk drive. */
#ifndef NR_SECTORS
/* For RLL drives NR_SECTORS has to be defined in the makefile or in config.h.
 * There is some hope of getting it from the parameter table for these drives,
 * and then this driver should use wn_sectors like at_wini.c.
 * Unfortunately it is not standard in XT parameter tables.
 */
#define NR_SECTORS	  17	/* number of sectors per track */
#endif

/* Error codes */
#define ERR		 (-1)	/* general error */
#define ERR_BAD_SECTOR	 (-2)	/* block marked bad detected */

/* Miscellaneous. */
#define MAX_DRIVES         2	/* this driver support two drives (hd0 - hd9) */
#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      (MAX_DRIVES * DEV_PER_DRIVE)
#define SUB_PER_DRIVE	(NR_PARTITIONS * NR_PARTITIONS)
#define NR_SUBDEVS	(MAX_DRIVES * SUB_PER_DRIVE)
#define MAX_WIN_RETRY  32000	/* max # times to try to output to WIN */
#if AUTO_BIOS
#define AUTO_PARAM     0x1AD	/* drive parameter table starts here in sect 0	*/
#define AUTO_ENABLE	0x10	/* auto bios enabled bit from status reg */
/* some start up parameters in order to extract the drive parameter table */
/* from the winchester. these should not need changed. */
#define AUTO_CYLS	 306	/* default number of cylinders */
#define AUTO_HEADS	   4	/* default number of heads */
#define AUTO_RWC	 307	/* default reduced write cylinder */
#define AUTO_WPC	 307	/* default write precomp cylinder */
#define AUTO_ECC	  11	/* default ecc burst */
#define AUTO_CTRL	   5	/* default winchester stepping speed byte */
#endif

/* Variables. */
PRIVATE struct wini {		/* main drive struct, one entry per drive */
  unsigned wn_cylinders;	/* number of cylinders */
  unsigned wn_heads;		/* number of heads */
  unsigned wn_reduced_wr;	/* first cylinder with reduced write current */
  unsigned wn_precomp;		/* first cylinder with write precompensation */
  unsigned wn_max_ecc;		/* maximum ECC burst length */
  unsigned wn_ctlbyte;		/* control byte for COMMANDS (10-Apr-87 GO) */
  unsigned wn_open_ct;		/* in-use count */
  struct device wn_part[DEV_PER_DRIVE];    /* primary partitions: hd[0-4] */
  struct device wn_subpart[SUB_PER_DRIVE]; /* subpartitions: hd[1-4][a-d] */
} wini[MAX_DRIVES], *w_wn;

PRIVATE struct trans {
  struct iorequest_s *tr_iop;	/* belongs to this I/O request */
  unsigned long tr_block;	/* first sector to transfer */
  unsigned tr_count;		/* byte count */
  phys_bytes tr_phys;		/* user physical address */
  phys_bytes tr_dma;		/* DMA physical address */
} wtrans[NR_IOREQS];

PRIVATE int w_need_reset = FALSE;	/* set when controller must be reset */
PRIVATE int nr_drives;			/* Number of drives */
PRIVATE int w_switches;			/* Drive type switches */
PRIVATE struct trans *w_tp;		/* to add transfer requests */
PRIVATE unsigned w_count;		/* number of bytes to transfer */
PRIVATE unsigned long w_nextblock;	/* next block on disk to transfer */
PRIVATE int w_opcode;			/* DEV_READ or DEV_WRITE */
PRIVATE int w_drive;			/* selected drive */
PRIVATE struct device *w_dv;		/* device's base and size */
PRIVATE char w_results[MAX_RESULTS];/* the controller can give lots of output */


FORWARD _PROTOTYPE( struct device *w_prepare, (int device) );
FORWARD _PROTOTYPE( char *w_name, (void) );
FORWARD _PROTOTYPE( int w_schedule, (int proc_nr, struct iorequest_s *iop) );
FORWARD _PROTOTYPE( int w_finish, (void) );
FORWARD _PROTOTYPE( void w_dma_setup, (struct trans *tp, unsigned count) );
FORWARD _PROTOTYPE( int w_transfer, (struct trans *tp, unsigned count) );
FORWARD _PROTOTYPE( int win_results, (void) );
FORWARD _PROTOTYPE( void win_out, (int val) );
FORWARD _PROTOTYPE( int w_reset, (void) );
FORWARD _PROTOTYPE( int w_handler, (int irq) );
FORWARD _PROTOTYPE( int win_specify, (int drive) );
FORWARD _PROTOTYPE( int check_init, (void) );
FORWARD _PROTOTYPE( int read_ecc, (void) );
FORWARD _PROTOTYPE( int hd_wait, (int bits) );
FORWARD _PROTOTYPE( int com_out, (int mode, u8_t *command) );
FORWARD _PROTOTYPE( void init_params, (void) );
FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );
FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) );
FORWARD _PROTOTYPE( void w_init, (void) );
FORWARD _PROTOTYPE( void copy_param, (char *src, struct wini *dest) );
FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry));


/* Entry points to this driver. */
PRIVATE struct driver w_dtab = {
  w_name,	/* current device's name */
  w_do_open,	/* open or mount request, initialize device */
  w_do_close,	/* release device */
  do_diocntl,	/* get or set a partition's geometry */
  w_prepare,	/* prepare for I/O on a given minor device */
  w_schedule,	/* precompute cylinder, head, sector, etc. */
  w_finish,	/* do the I/O */
  nop_cleanup,	/* no cleanup needed */
  w_geometry	/* tell the geometry of the disk */
};


/*===========================================================================*
 *				xt_winchester_task			     *
 *===========================================================================*/
PUBLIC void xt_winchester_task()
{
  init_params();

  put_irq_handler(XT_WINI_IRQ, w_handler);
  enable_irq(XT_WINI_IRQ);	/* ready for winchester interrupts */

  driver_task(&w_dtab);
}


/*===========================================================================*
 *				w_prepare				     *
 *===========================================================================*/
PRIVATE struct device *w_prepare(device)
int device;
{
/* Prepare for I/O on a device. */

  /* Nothing to transfer as yet. */
  w_count = 0;

  if (device < NR_DEVICES) {			/* hd0, hd1, ... */
	w_drive = device / DEV_PER_DRIVE;	/* save drive number */
	w_wn = &wini[w_drive];
	w_dv = &w_wn->wn_part[device % DEV_PER_DRIVE];
  } else
  if ((unsigned) (device -= MINOR_hd1a) < NR_SUBDEVS) {	/* hd1a, hd1b, ... */
	w_drive = device / SUB_PER_DRIVE;
	w_wn = &wini[w_drive];
	w_dv = &w_wn->wn_subpart[device % SUB_PER_DRIVE];
  } else {
	return(NIL_DEV);
  }
  return(w_drive < nr_drives ? w_dv : NIL_DEV);
}


/*===========================================================================*
 *				w_name					     *
 *===========================================================================*/
PRIVATE char *w_name()
{
/* Return a name for the current device. */
  static char name[] = "xt-hd5";

  name[5] = '0' + w_drive * DEV_PER_DRIVE;
  return name;
}


/*===========================================================================*
 *				w_schedule				     *
 *===========================================================================*/
PRIVATE int w_schedule(proc_nr, iop)
int proc_nr;			/* process doing the request */
struct iorequest_s *iop;	/* pointer to read or write request */
{
/* Gather I/O requests on consecutive blocks so they may be read/written
 * in one command if using a buffer.  Check and gather all the requests
 * and try to finish them as fast as possible if unbuffered.
 */
  int r, opcode;
  unsigned long pos;
  unsigned nbytes, count, dma_count;
  unsigned long block;
  phys_bytes user_phys, dma_phys;

  /* This many bytes to read/write */
  nbytes = iop->io_nbytes;
  if ((nbytes & SECTOR_MASK) != 0) return(iop->io_nbytes = EINVAL);

  /* From/to this position on the device */
  pos = iop->io_position;
  if ((pos & SECTOR_MASK) != 0) return(iop->io_nbytes = EINVAL);

  /* To/from this user address */
  user_phys = numap(proc_nr, (vir_bytes) iop->io_buf, nbytes);
  if (user_phys == 0) return(iop->io_nbytes = EINVAL);

  /* Read or write? */
  opcode = iop->io_request & ~OPTIONAL_IO;

  /* Which block on disk and how close to EOF? */
  if (pos >= w_dv->dv_size) return(OK);		/* At EOF */
  if (pos + nbytes > w_dv->dv_size) nbytes = w_dv->dv_size - pos;
  block = (w_dv->dv_base + pos) >> SECTOR_SHIFT;

  if (USE_BUF && w_count > 0 && block != w_nextblock) {
	/* This new request can't be chained to the job being built */
	if ((r = w_finish()) != OK) return(r);
  }

  /* The next consecutive block */
  if (USE_BUF) w_nextblock = block + (nbytes >> SECTOR_SHIFT);

  /* While there are "unscheduled" bytes in the request: */
  do {
	count = nbytes;

	if (USE_BUF) {
		if (w_count == DMA_BUF_SIZE) {
			/* Can't transfer more than the buffer allows. */
			if ((r = w_finish()) != OK) return(r);
		}

		if (w_count + count > DMA_BUF_SIZE)
			count = DMA_BUF_SIZE - w_count;
	} else {
		if (w_tp == wtrans + NR_IOREQS) {
			/* All transfer slots in use. */
			if ((r = w_finish()) != OK) return(r);
		}
	}

	if (w_count == 0) {
		/* The first request in a row, initialize. */
		w_opcode = opcode;
		w_tp = wtrans;
	}

	if (USE_BUF) {
		dma_phys = tmp_phys + w_count;
	} else {
		/* Memory chunk to DMA. */
		dma_phys = user_phys;
		dma_count = dma_bytes_left(dma_phys);

		if (dma_count < count) {
			/* Nearing a 64K boundary. */
			if (dma_count >= SECTOR_SIZE) {
				/* Can read a few sectors before hitting the
				 * boundary.
				 */
				count = dma_count & ~SECTOR_MASK;
			} else {
				/* Must use the special buffer for this. */
				count = SECTOR_SIZE;
				dma_phys = tmp_phys;
			}
		}
	}

	/* Store I/O parameters */
	w_tp->tr_iop = iop;
	w_tp->tr_block = block;
	w_tp->tr_count = count;
	w_tp->tr_phys = user_phys;
	w_tp->tr_dma = dma_phys;

	/* Update counters */
	w_tp++;
	w_count += count;
	block += count >> SECTOR_SHIFT;
	user_phys += count;
	nbytes -= count;
  } while (nbytes > 0);

  return(OK);
}


/*===========================================================================*
 *				w_finish				     *
 *===========================================================================*/
PRIVATE int w_finish()
{
/* Carry out the I/O requests gathered in wtrans[]. */

  struct trans *tp = wtrans, *tp2;
  unsigned count;
  int r, errors = 0, many = USE_BUF;

  if (w_count == 0) return(OK);	/* Spurious finish. */

  do {
	if (w_opcode == DEV_WRITE) {
		tp2 = tp;
		count = 0;
		do {
			if (USE_BUF || tp2->tr_dma == tmp_phys) {
				phys_copy(tp2->tr_phys, tp2->tr_dma,
						(phys_bytes) tp2->tr_count);
			}
			count += tp2->tr_count;
			tp2++;
		} while (many && count < w_count);
	} else {
		count = many ? w_count : tp->tr_count;
	}

	/* First check to see if a reset is needed. */
	if (w_need_reset) w_reset();

	/* Now set up the DMA chip. */
	w_dma_setup(tp, count);

	/* Perform the transfer. */
	r = w_transfer(tp, count);

	if (r != OK) {
		/* An error occurred, try again block by block unless */
		if (r == ERR_BAD_SECTOR || ++errors == MAX_ERRORS)
			return(tp->tr_iop->io_nbytes = EIO);

		/* Reset if halfway, but bail out if optional I/O. */
		if (errors == MAX_ERRORS / 2) {
			w_need_reset = TRUE;
			if (tp->tr_iop->io_request & OPTIONAL_IO)
				return(tp->tr_iop->io_nbytes = EIO);
		}
		many = 0;
		continue;
	}
	errors = 0;

	w_count -= count;

	do {
		if (w_opcode == DEV_READ) {
			if (USE_BUF || tp->tr_dma == tmp_phys) {
				phys_copy(tp->tr_dma, tp->tr_phys,
						(phys_bytes) tp->tr_count);
			}
		}
		tp->tr_iop->io_nbytes -= tp->tr_count;
		count -= tp->tr_count;
		tp++;
	} while (count > 0);
  } while (w_count > 0);

  return(OK);
}


/*==========================================================================*
 *				w_dma_setup				    *
 *==========================================================================*/
PRIVATE void w_dma_setup(tp, count)
struct trans *tp;		/* pointer to the transfer struct */
unsigned count;			/* bytes to transfer */
{
/* The IBM PC can perform DMA operations by using the DMA chip.  To use it,
 * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
 * to by read from or written to, the byte count minus 1, and a read or write
 * opcode.  This routine sets up the DMA chip.  Note that the chip is not
 * capable of doing a DMA across a 64K boundary (e.g., you can't read a
 * 512-byte block starting at physical address 65520).
 */

  /* Set up the DMA registers. */
  out_byte(DMA_FLIPFLOP, 0);		/* write anything to reset it */
  out_byte(DMA_MODE, w_opcode == DEV_WRITE ? DMA_WRITE : DMA_READ);
  out_byte(DMA_ADDR, (int) tp->tr_dma >>  0);
  out_byte(DMA_ADDR, (int) tp->tr_dma >>  8);
  out_byte(DMA_TOP, (int) (tp->tr_dma >> 16));
  out_byte(DMA_COUNT, (count - 1) >> 0);
  out_byte(DMA_COUNT, (count - 1) >> 8);
}


/*=========================================================================*
 *				w_transfer				   *
 *=========================================================================*/
PRIVATE int w_transfer(tp, count)
struct trans *tp;		/* pointer to the transfer struct */
unsigned count;			/* transferring count bytes */
{
/* Read or write count bytes starting with tp->tr_block. */

  unsigned cylinder, sector, head, secspcyl = w_wn->wn_heads * NR_SECTORS;
  u8_t command[6];
  message mess;

  cylinder = tp->tr_block / secspcyl;
  head = (tp->tr_block % secspcyl) / NR_SECTORS;
  sector = tp->tr_block % NR_SECTORS;

  /* The command is issued by outputting 6 bytes to the controller chip. */
  command[0] = w_opcode == DEV_WRITE ? WIN_WRITE : WIN_READ;
  command[1] = head | (w_drive << 5);
  command[2] = ((cylinder & 0x0300) >> 2) | sector;
  command[3] = cylinder & 0xFF;
  command[4] = count >> SECTOR_SHIFT;
  command[5] = w_wn->wn_ctlbyte;

  if (com_out(DMA_INT, command) != OK)
	return(ERR);

  out_byte(DMA_INIT, 3);	/* initialize DMA */

  /* Block, waiting for disk interrupt. */
  receive(HARDWARE, &mess);

  /* Get controller status and check for errors. */
  if (win_results() == OK)
	return(OK);
  if ((w_results[0] & 63) == 24)
	read_ecc();
  else
	w_need_reset = TRUE;
  return(ERR);
}


/*==========================================================================*
 *				win_results				    *
 *==========================================================================*/
PRIVATE int win_results()
{
/* Extract results from the controller after an operation. */

  int i, status;
  u8_t command[6];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -