Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!seismo!rutgers!rochester!udel!mmdf From: jts@ornl-msr.arpa (Jim Simmons) Newsgroups: comp.os.minix Subject: Fix for hard disk (at_wini.c) Message-ID: <410@louie.udel.EDU> Date: Tue, 4-Aug-87 08:59:22 EDT Article-I.D.: louie.410 Posted: Tue Aug 4 08:59:22 1987 Date-Received: Thu, 6-Aug-87 01:18:38 EDT Sender: mmdf@udel.EDU Lines: 626 I have a Zenith Z-241 AT compatible machine with a Seagate 4051 drive that I have had trouble making work with Minix. The 4051 drive has 5 heads with 17 sectors/track, which was causing the problems. After applying Mark Schwenk's patches for winchesters with an odd number of heads, I believe I finally have the drive working successfully. Unfortunately, there was at a serious bug in his routine. It comes from the statement in copy_prt he had as: wn->wn_low = (wn->wn_low/(SECS_PER_BLK+1))*SECS_PER_BLK; It should be: wn->wn_low = (wn->wn->low/SECS_PER_BLK+1)*SECS_PER_BLK; The extra set of parens was causing Minix to write to the middle of my DOS partition, instead of where it was supposed to be writing. Also, you need to patch fsck if you want to do a file system check on the hard disk--the 4051 has 85 sectors/cylinder instead of 68. I didn't find any other problems with fsck. In the process of finding and fixing this bug, I made several other changes. I don't know if any of them contributed to the final working version or not, so I am sending the entire source for the driver. I can't make any promises, but it seems to be working for me. Jim Simmons (jts@ornl-msr.arpa) ------------------------ Cut here for new 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 */ #define SECS_PER_BLK (BLOCK_SIZE/SECTOR_SIZE) /* 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 20000 /* 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 sector of partition */ long wn_size; /* size of partition in sectors */ 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+SECS_PER_BLK) > 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 win_buf = umap(proc_addr(WINCHESTER), D, buf, BLOCK_SIZE); phys_bytes usr_buf = umap(proc_addr(wn->wn_procnr), D, wn->wn_address, BLOCK_SIZE); int remain, offset; if (win_buf == (phys_bytes)0 || usr_buf == (phys_bytes)0) return(ERR); if (wn->wn_opcode == DISK_WRITE) phys_copy(usr_buf, win_buf, (phys_bytes)BLOCK_SIZE); /* The command is issued by outputing 7 bytes to the controller chip. */ command[0] = wn->wn_ctlbyte; command[1] = wn->wn_precomp; 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 transfer crosses a cylinder boundary, break it into 2 requests */ remain = wn->wn_maxsec - wn->wn_sector + 1; if (wn->wn_head == wn->wn_heads-1 && SECS_PER_BLK > remain) { #ifdef DEBUG printf("splitting: %d/%d/%d %d %d\n", wn->wn_cylinder,wn->wn_head,wn->wn_sector, remain,SECS_PER_BLK-remain); #endif /*DEBUG*/ command[2] = SECS_PER_BLK - remain; offset = command[2]; if (w_dotrans(wn,win_buf,usr_buf,0) == ERR) return(ERR); command[0] = 0; /* head */ command[2] = remain; command[3] = 1; /* sector */ command[4] = (wn->wn_cylinder+1) & 0xFF; command[5] = (((wn->wn_cylinder+1) & 0x0300) >> 8); command[6] = (wn->wn_drive << 4) | 0xA0; if (w_dotrans(wn,win_buf,usr_buf,offset) == ERR) return(ERR); } else { command[2] = SECS_PER_BLK; if (w_dotrans(wn,win_buf,usr_buf,0) == ERR) return(ERR); } if (wn->wn_opcode == DISK_READ) phys_copy(win_buf, usr_buf, (phys_bytes)BLOCK_SIZE); return(OK); } /*===========================================================================* * w_dotrans * *===========================================================================*/ /* note that command[] must be set up before calling this routine */ PRIVATE int w_dotrans(wn,win_buf,usr_buf,offset) register struct wini *wn; /* pointer to the drive struct */ phys_bytes win_buf, usr_buf; int offset; /* sectors of BLOCK already transferred */ { register int i,j; int r; if (com_out() != OK) return(ERR); /* Block, waiting for disk interrupt. */ if (wn->wn_opcode == DISK_READ) { for (i=offset; i 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); } } /*============================================================================* * copy_param * *============================================================================*/ PRIVATE copy_param(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] / 4; dest[i].wn_ctlbyte = (int)src[10]; 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*5 + 1]; offset = PART_TABLE + i * 0x10; wn->wn_low = *(long *)&buf[offset]; if ((wn->wn_low % (SECS_PER_BLK)) != 0) { adjust = wn->wn_low; wn->wn_low = (wn->wn_low/SECS_PER_BLK+1)*SECS_PER_BLK; adjust = wn->wn_low - adjust; } wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust; } sort(&wini[drive*5 + 1]); #ifdef DEBUG for (i=drive*5; i 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; } #ifdef DEBUG PRIVATE showwini(device) register int device; { register struct wini *w = &wini[device]; printf("DEVICE %d\n", device); printf("op: 0x%x proc: %d drive: %d cyl: %d sec: %d hd: %d\n", w->wn_opcode, w->wn_procnr, w->wn_drive, w->wn_cylinder, w->wn_sector, w->wn_head); printf("maxhd: %d maxsec: %d ctl: %d precmp %d\n", w->wn_heads, w->wn_maxsec, w->wn_ctlbyte, w->wn_precomp); printf("low: %D size: %D cnt: %d\n", w->wn_low, w->wn_size, w->wn_count); } #endif /*DEBUG*/