📄 xt_wini.c
字号:
/* 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 + -