Path: utzoo!utgpu!water!watmath!clyde!rutgers!ames!ucbcad!ucbvax!decvax!decwrl!sun!pitstop!sundc!seismo!uunet!mcvax!botter!ast
From: ast@cs.vu.nl (Andy Tanenbaum)
Newsgroups: comp.os.minix
Subject: Reposting of at_wini.c
Message-ID: <1789@botter.cs.vu.nl>
Date: 16 Dec 87 10:01:19 GMT
Reply-To: ast@cs.vu.nl (Andy Tanenbaum)
Organization: VU Informatica, Amsterdam
Lines: 550
I have been informed that the posting of at_wini.c a while back was truncated
at a lot of sites for some unknown reason. Here it is again.
Andy Tanenbaum (ast@cs.vu.nl)
------------------------------ Version 1.2 at_wini.c ------------------------
/* This file contains a driver for the IBM-AT winchester controller.
* It was written by Adri Koppes.
*
* The driver supports two operations: read a block and
* write a block. It accepts two messages, one for reading and one for
* writing, both using message format m2 and with the same parameters:
*
* m_type DEVICE PROC_NR COUNT POSITION ADRRESS
* ----------------------------------------------------------------
* | DISK_READ | device | proc nr | bytes | offset | buf ptr |
* |------------+---------+---------+---------+---------+---------|
* | DISK_WRITE | device | proc nr | bytes | offset | buf ptr |
* ----------------------------------------------------------------
*
* The file contains one entry point:
*
* winchester_task: main entry when system is brought up
*
*/
#include "../h/const.h"
#include "../h/type.h"
#include "../h/callnr.h"
#include "../h/com.h"
#include "../h/error.h"
#include "const.h"
#include "type.h"
#include "proc.h"
/* I/O Ports used by winchester disk controller. */
#define WIN_REG1 0x1f0
#define WIN_REG2 0x1f1
#define WIN_REG3 0x1f2
#define WIN_REG4 0x1f3
#define WIN_REG5 0x1f4
#define WIN_REG6 0x1f5
#define WIN_REG7 0x1f6
#define WIN_REG8 0x1f7
#define WIN_REG9 0x3f6
/* Winchester disk controller command bytes. */
#define WIN_RECALIBRATE 0x10 /* command for the drive to recalibrate */
#define WIN_READ 0x20 /* command for the drive to read */
#define WIN_WRITE 0x30 /* command for the drive to write */
#define WIN_SPECIFY 0x91 /* command for the controller to accept params */
/* Parameters for the disk drive. */
#define SECTOR_SIZE 512 /* physical sector size in bytes */
/* Error codes */
#define ERR -1 /* general error */
/* Miscellaneous. */
#define MAX_ERRORS 4 /* how often to try rd/wt before quitting */
#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 */
/* Variables. */
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 */
int wn_maxsec; /* maximum number of sectors per track */
int wn_ctlbyte; /* control byte (steprate) */
int wn_precomp; /* write precompensation cylinder / 4 */
long wn_low; /* lowest cylinder of partition */
long wn_size; /* size of partition in blocks */
int wn_count; /* byte count */
vir_bytes wn_address; /* user virtual address */
} wini[NR_DEVICES];
PRIVATE int w_need_reset = FALSE; /* set to 1 when controller must be reset */
PRIVATE int nr_drives; /* Number of drives */
PRIVATE message w_mess; /* message buffer for in and out */
PRIVATE int command[8]; /* Common command block */
PRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
/*===========================================================================*
* winchester_task *
*===========================================================================*/
PUBLIC winchester_task()
{
/* Main program of the winchester disk driver task. */
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.
*/
while (TRUE) {
/* First wait for a request to read or write a disk block. */
receive(ANY, &w_mess); /* get a request to do some work */
if (w_mess.m_source < 0) {
printf("winchester task got message from %d ", w_mess.m_source);
continue;
}
caller = w_mess.m_source;
proc_nr = w_mess.PROC_NR;
/* Now carry out the work. */
switch(w_mess.m_type) {
case DISK_READ:
case DISK_WRITE: r = w_do_rdwt(&w_mess); break;
default: r = EINVAL; break;
}
/* Finally, prepare and send the reply message. */
w_mess.m_type = TASK_REPLY;
w_mess.REP_PROC_NR = proc_nr;
w_mess.REP_STATUS = r; /* # of bytes transferred or error code */
send(caller, &w_mess); /* send reply to caller */
}
}
/*===========================================================================*
* w_do_rdwt *
*===========================================================================*/
PRIVATE int w_do_rdwt(m_ptr)
message *m_ptr; /* pointer to read or write w_message */
{
/* Carry out a read or write request from the disk. */
register struct wini *wn;
int r, device, errors = 0;
long sector;
/* Decode the w_message parameters. */
device = m_ptr->DEVICE;
if (device < 0 || device >= NR_DEVICES)
return(EIO);
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);
sector = m_ptr->POSITION/SECTOR_SIZE;
if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size)
return(EOF);
sector += wn->wn_low;
wn->wn_cylinder = sector / (wn->wn_heads * wn->wn_maxsec);
wn->wn_sector = (sector % wn->wn_maxsec) + 1;
wn->wn_head = (sector % (wn->wn_heads * wn->wn_maxsec) )/wn->wn_maxsec;
wn->wn_count = m_ptr->COUNT;
wn->wn_address = (vir_bytes) m_ptr->ADDRESS;
wn->wn_procnr = m_ptr->PROC_NR;
/* This loop allows a failed operation to be repeated. */
while (errors <= MAX_ERRORS) {
errors++; /* increment count once per loop cycle */
if (errors > MAX_ERRORS)
return(EIO);
/* First check to see if a reset is needed. */
if (w_need_reset) w_reset();
/* Perform the transfer. */
r = w_transfer(wn);
if (r == OK) break; /* if successful, exit loop */
}
return(r == OK ? BLOCK_SIZE : EIO);
}
/*===========================================================================*
* w_transfer *
*===========================================================================*/
PRIVATE int w_transfer(wn)
register struct wini *wn; /* pointer to the drive struct */
{
extern phys_bytes umap();
phys_bytes usr_buf = umap(proc_addr(wn->wn_procnr), D, wn->wn_address, BLOCK_SIZE);
register int i,j;
int r = 0;
/* The command is issued by outputing 7 bytes to the controller chip. */
if (usr_buf == (phys_bytes)0)
return(ERR);
command[0] = wn->wn_ctlbyte;
command[1] = wn->wn_precomp;
command[2] = BLOCK_SIZE/SECTOR_SIZE;
command[3] = wn->wn_sector;
command[4] = wn->wn_cylinder & 0xFF;
command[5] = ((wn->wn_cylinder & 0x0300) >> 8);
command[6] = (wn->wn_drive << 4) | wn->wn_head | 0xA0;
command[7] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE);
if (com_out() != OK)
return(ERR);
/* Block, waiting for disk interrupt. */
if (wn->wn_opcode == DISK_READ) {
for (i=0; i> 4), (unsigned)(usr_buf & 0x0F));
unlock();
usr_buf += 0x200;
if (win_results() != OK) {
w_need_reset = TRUE;
return(ERR);
}
}
r = OK;
} else {
for (i=0; i> 4), (unsigned)(usr_buf & 0x0F));
unlock();
usr_buf += 0x200;
receive(HARDWARE, &w_mess);
if (win_results() != OK) {
w_need_reset = TRUE;
return(ERR);
}
}
r = OK;
}
if (r == ERR)
w_need_reset = TRUE;
return(r);
}
/*===========================================================================*
* w_reset *
*===========================================================================*/
PRIVATE w_reset()
{
/* Issue a reset to the controller. This is done after any catastrophe,
* like the controller refusing to respond.
*/
int i, r;
/* Strobe reset bit low. */
lock();
port_out(WIN_REG9, 4);
for (i = 0; i < 10; i++)
;
port_out(WIN_REG9, wini[0].wn_ctlbyte & 0x0F);
unlock();
for (i = 0; i < MAX_WIN_RETRY && drive_busy(); i++)
;
if (drive_busy()) {
printf("Winchester wouldn't reset, drive busy\n");
return(ERR);
}
port_in(WIN_REG2, &r);
if (r != 1) {
printf("Winchester wouldn't reset, drive error\n");
return(ERR);
}
/* 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] = wini[0].wn_ctlbyte;
command[1] = wini[0].wn_precomp;
command[2] = wini[0].wn_maxsec;
command[4] = 0;
command[6] = (wini[0].wn_heads - 1) | 0xA0;
command[7] = WIN_SPECIFY; /* Specify some parameters */
if (com_out() != OK) /* Output command block */
return(ERR);
receive(HARDWARE, &w_mess);
if (win_results() != OK) { /* See if controller accepted parameters */
w_need_reset = TRUE;
return(ERR);
}
if (nr_drives > 1) {
command[0] = wini[5].wn_ctlbyte;
command[1] = wini[5].wn_precomp;
command[2] = wini[5].wn_maxsec;
command[4] = 0;
command[6] = (wini[5].wn_heads - 1) | 0xB0;
command[7] = WIN_SPECIFY; /* Specify some parameters */
if (com_out() != OK) /* Output command block */
return(ERR);
receive(HARDWARE, &w_mess);
if (win_results() != OK) { /* See if controller accepted parameters */
w_need_reset = TRUE;
return(ERR);
}
}
for (i=0; i 0) && (win_init() != OK))
nr_drives = 0;
/* Read the partition table for each drive and save them */
for (i = 0; i < nr_drives; i++) {
w_mess.DEVICE = i * 5;
w_mess.POSITION = 0L;
w_mess.COUNT = BLOCK_SIZE;
w_mess.ADDRESS = (char *) buf;
w_mess.PROC_NR = WINCHESTER;
w_mess.m_type = DISK_READ;
if (w_do_rdwt(&w_mess) != BLOCK_SIZE)
panic("Can't read partition table of winchester ", i);
if (buf[510] != 0x55 || buf[511] != 0xAA) {
printf("Invalid partition table\n");
continue;
}
copy_prt(i*5);
}
}
/*============================================================================*
* copy_params *
*============================================================================*/
PRIVATE copy_params(src, dest)
register unsigned char *src;
register struct wini *dest;
{
/* This routine copies the parameters from src to dest
* and sets the parameters for partition 0 and 5
*/
register int i;
long cyl, heads, sectors;
for (i=0; i<5; i++) {
dest[i].wn_heads = (int)src[2];
dest[i].wn_precomp = *(int *)&src[5] >> 2;
dest[i].wn_ctlbyte = (int)src[8];
dest[i].wn_maxsec = (int)src[14];
}
cyl = (long)(*(int *)src);
heads = (long)dest[0].wn_heads;
sectors = (long)dest[0].wn_maxsec;
dest[0].wn_size = cyl * heads * sectors;
}
/*============================================================================*
* copy_prt *
*============================================================================*/
PRIVATE copy_prt(drive)
int drive;
{
/* This routine copies the partition table for the selected drive to
* the variables wn_low and wn_size
*/
register int i, offset;
struct wini *wn;
long adjust;
for (i=0; i<4; i++) {
adjust = 0;
wn = &wini[i + drive + 1];
offset = PART_TABLE + i * 0x10;
wn->wn_low = *(long *)&buf[offset];
if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) {
adjust = wn->wn_low;
wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE);
adjust = wn->wn_low - adjust;
}
wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust;
}
sort(&wini[drive + 1]);
}
sort(wn)
register struct wini *wn;
{
register int i,j;
for (i=0; i<4; i++)
for (j=0; j<3; j++)
if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0))
swap(&wn[j], &wn[j+1]);
else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)
swap(&wn[j], &wn[j+1]);
}
swap(first, second)
register struct wini *first, *second;
{
register struct wini tmp;
tmp = *first;
*first = *second;
*second = tmp;
}