/* This file contains a driver for IBM PC XT class winchester controllers. * Also supports Western Digital (WX-2 and related) controllers. * * Original code written by Adri Koppes. * Patches from Gary Oliver for use with the Western Digital WX-2. * Patches from Harry McGavran for robust operation on turbo clones. * Patches from Mike Mitchell for WX-2 auto configure operation. * Major Rewrite by Glen Overby 4/90 * * Version: 1.5.10-2.3.A 8/4/90 * Derived from xt_wini.c as of 1.5.10, * 3rd author fix level * No user fixes * * References: * "Storage Management Products Handbook", 1986 Western Digital Corporation * Specifically, the data sheets on the WD1002S-WX2 in section 6. * * The driver supports the following operations (using message format m2): * * 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 | * ---------------------------------------------------------------- * |SCATTERED_IO| device | proc nr | requests| | iov ptr | * ---------------------------------------------------------------- * * The file contains one entry point: * * winchester_task: main entry when system is brought up * */ #include "kernel.h" #include #include #include /*#define DEBUG_CMD /* w_do_rdwt */ /*#define DEBUG_DMA /* dma_set & other DMA mode in xt_do */ /*#define DEBUG_RESET /* controler reset related */ /*#define DEBUG_STAT /* stat_wait */ /*#define DEBUG_DO1 /* xt_do, basics */ /*#define DEBUG_DO2 /* xt_do, detail */ /*#define DEBUG_RW1 /* xt_rw, basics */ /*#define DEBUG_RW2 /* xt_rw, all bytes */ /*#define DEBUG_RW3 /* xt_rw, controler delay */ /*#define DEBUG_INIT /* Init time stuff */ #define DEBUG_CONF /* system HW configuration */ #define MAX_DRIVES 2 /* Maximum of two drives supported */ /* I/O Ports used by winchester disk controler. */ #define WIN_DATA 0x320 /* winchester disk controller data register */ #define WIN_STATUS 0x321 /* winchester disk controller status register*/ #define WIN_SELECT 0x322 /* winchester disk controller select port */ #define WIN_DMA 0x323 /* winchester disk controller dma register */ /* I/O Ports used by DMA controler. */ #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_M1 0x00B /* DMA mode register */ #define DMA_M2 0x00C /* DMA byte pointer flip-flop */ #define DMA_INIT 0x00A /* DMA request mask register (en/disable DRQ) */ /* Winchester disk controler status register bits */ #define WST_REQ 0x001 /* Request bit */ #define WST_INPUT 0x002 /* Data Input or Output mode (Set if controller is writing to CPU) */ #define WST_CMD 0x004 /* Command/Status mode */ #define WST_BUSY 0x008 /* Set if controler is Busy */ #define WST_DRQ 0x010 /* DMA request pending */ #define WST_IRQ 0x020 /* Interrupt request pending */ /* Winchester disk controller commands. */ #define WIN_RECALIBRATE 0x01 /* drive recalibrate */ #define WIN_SENSE 0x03 /* return controler status */ #define WIN_READ 0x08 /* drive read */ #define WIN_WRITE 0x0A /* drive write */ #define WIN_SPECIFY 0x0C /* set controler params */ #define WIN_ECC_READ 0x0D /* read ecc length after a correctable ECC error */ #define WIN_ERROR 0x02 /* command completion byte error field */ /* Error codes from XT Controller */ #define ERR_OK 0x00 /* no error */ #define ERR_SC 0x02 /* no SC* (select?) from drive */ #define ERR_WFAULT 0x03 /* Write Fault */ #define ERR_NOT_RDY 0x04 /* Drive Not Ready */ #define ERR_TRK0 0x06 /* Track 0 Not Found */ #define ERR_SEEKING 0x08 /* Drive Still Seeking */ #define ERR_DATA 0x11 /* Uncorrectable Data Error */ #define ERR_ADDR_MARK 0x12 /* Data Address Mark Not Found */ #define ERR_SEEK 0x15 /* Seek Error */ #define ERR_CORR 0x18 /* Correctable ECC Error */ #define ERR_BADTRK 0x19 /* Track is flagged as BAD */ #define ERR_INVALID 0x20 /* Invalid Command */ #define ERR_SECT_ADDR 0x21 /* Illegal Sector Address */ #define ERR_SECT_BUF 0x30 /* Sector Buffer Error (diags E0, E4) */ #define ERR_ROM 0x31 /* Controler ROM Checksum (diags E4) */ #define ERR_ECCPOLY 0x32 /* ECC generator failed (diags E4) */ /* xt_command / Win_DMA port parameters */ /* NOTE: bits 0 and 1 correspond to the controler INT & DMA Mask port */ #define DMA_INT 3 /* Command with dma and interrupt */ #define INT 2 /* Command with interrupt, no dma */ #define DMA_ 1 /* DMA part of DMA_INT */ #define NO_DMA_INT 0 /* Command without dma and interrupt */ #define PGM_READ 0x10 /* No DMA or Interrupt, read data manually */ #define PGM_WRITE 0x20 /* No DMA or Interrupt, write data manually */ /* DMA channel commands. */ #define DMA_READ 0x47 /* DMA read opcode */ #define DMA_WRITE 0x4B /* DMA write opcode */ #define DMA_RESET_VAL 0x07 /* Disable DRQ */ #define DMA_SET_VAL 0x03 /* Enable DRQ */ /* Parameters for the disk drive. */ #define SECTOR_SIZE 512 /* physical sector size in bytes */ #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_maxsec like at_wini.c. * Unfortunately it is not standard in XT parameter tables. */ #define NR_SECTORS 0x11 /* number of sectors per track */ #endif /* Error codes */ #define ERR -1 /* general error */ /* Miscellaneous. */ #define MAX_ERRORS 4 /* how often to try rd/wt before quitting */ #define MAX_RESULTS 4 /* max number of bytes controller returns */ #define DEV_PER_DRIVE (1 + NR_PARTITIONS) /* whole drive & each partn */ #define NR_DEVICES (MAX_DRIVES * DEV_PER_DRIVE) #define MAX_WIN_RETRY 32000 /* max # times to try to output to WIN */ #define MAX_WIN_RESET 240 /* max # clock ticks to wait for status */ #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. */ PUBLIC int using_bios = FALSE; /* this disk driver does not use the BIOS */ PRIVATE struct wini { /* main drive struct, one entry per partition */ long wn_low; /* lowest cylinder of partition */ long wn_size; /* size of partition in blocks */ int wn_drive; /* drive number addressed */ /* Saved state from most recent command for this partition */ int wn_procnr; /* which proc wanted this operation? */ int wn_op; /* Operation (read/write) */ int wn_count; /* byte count */ vir_bytes wn_address; /* user virtual address */ } wini[NR_DEVICES]; struct param { /* ROM Winchester disk parameter table */ int cylinders; /* number of cylinders on the disk */ char heads; /* number of heads in the drive */ int reduced_wr; /* cylinder to start reduced write current */ int wr_precomp; /* cylinder to start write precompensation */ char max_ecc; /* maximum ECC burst length */ char control; /* control byte */ /* bits definition */ /* 0-2 step option (XT) */ /* 3 set if > 8 heads */ /* 4 0 */ /* 5 set if defect map on maxcyl+1*/ /* 6 disable ECC retries */ /* 7 disable access retries */ char timeout; /* standard timeout (XT) */ char ftimeout; /* formatting timeout (XT) */ char chktimeout; /* drive check timeout (XT) */ int landing; /* landing zone (AT) */ char sectors; /* sectors per track (AT) */ char zero; /* 0 */ } drive[MAX_DRIVES]; PRIVATE int w_need_reset; /* TRUE when controller must be reset */ PRIVATE int nr_drives; /* Number of drives */ PRIVATE message w_mess; /* message buffer for in and out */ /* Format of a controler command */ PRIVATE struct xt_cmd { char op, /* OP code (read, write, etc) */ headdr, /* Head and Drive */ sector, /* sector of the cylinder */ cyl, /* cylinder of the drive */ count, /* number of sectors to transfer */ ctl; /* control byte (head step rate, auto error retry */ }; /* Format of controler drive parameters */ PRIVATE struct xt_param { char cyl_h, cyl_l; /* Max Cylinders */ char heads; /* Heads */ char rwr_h, rwr_l; /* Reduced Write */ char wpre_h, wpre_l; /* Write Precompensation */ char ecc; /* Max ECC Data burst length */ }; PRIVATE unsigned char buf[BLOCK_SIZE] = { 0 }; /* Buffer used by the startup routine */ /* Initialized to try to avoid DMA wrap. */ FORWARD int w_do_rdwt(); FORWARD void w_dma_setup(); FORWARD int w_reset(); FORWARD void w_wait_int(); FORWARD int win_specify(); FORWARD int read_ecc(); FORWARD int xt_command(); FORWARD int xt_do(); FORWARD int xt_rw(); FORWARD int stat_wait(); FORWARD int w_clock_mess(); FORWARD void st_check(); FORWARD void init_params(); FORWARD void rom_conf(); FORWARD void wd_autoconf(); FORWARD void copy_param(); FORWARD void copy_prt(); FORWARD void sort(); FORWARD void cp(); /*=========================================================================* * winchester_task * *=========================================================================*/ PUBLIC void winchester_task() { /* Main program of the winchester disk driver task. */ int r, caller, proc_nr; /* First initialize the controller */ init_params(); /* 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\n", 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; case SCATTERED_IO: r = do_vrdwt(&w_mess, w_do_rdwt); 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; /* partition */ register struct param *dr; /* drive */ int r, device, errors; int sec, cyl, head; long sector; struct xt_cmd command; /* 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 partition */ dr = &drive[wn->wn_drive]; /* 'dr' points to entry for this drive */ 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(0); sector += wn->wn_low; /* offset from the beginning of the partition */ wn->wn_op = m_ptr->m_type; /* DISK_READ or DISK_WRITE */ wn->wn_address = (vir_bytes) m_ptr->ADDRESS; /* for w_dma_setup */ wn->wn_procnr = m_ptr->PROC_NR; wn->wn_count = m_ptr->COUNT; cyl = sector / (dr->heads * dr->sectors); sec = (sector % dr->sectors); head = ((sector % (dr->heads * dr->sectors) ) / dr->sectors); command.op = (m_ptr->m_type == DISK_READ ? WIN_READ : WIN_WRITE); command.cyl = cyl & 0xff; command.sector = ((cyl & 0x300) >>2) | sec; command.headdr = head | (wn->wn_drive << 5); command.count = BLOCK_SIZE / SECTOR_SIZE; command.ctl = dr->control; #ifdef DEBUG_CMD printf("Sector %l Cyl=%d Sec=%d Head=%d\n", sector, cyl, sec, head); printf("Cmd Op: 0x%x Drive: %d\nCmd Head: %d Sect: %d Cyl: %d Count: %d Ctl: 0x%x\n", command.op, (command.headdr & 0x20)>>5, command.headdr & 0x0f, command.sector, command.cyl, command.count, command.ctl); #endif /* This loop allows a failed operation to be repeated. */ for(errors = MAX_ERRORS; errors; errors--) { if (w_need_reset) w_reset(); w_dma_setup(wn); /* Now set up the DMA chip. */ /* Perform the transfer. */ if((r = xt_command(&command, DMA_INT, 0, 0)) == ERR_CORR) { /* Correctable ECC error, read ECC burst length */ r = read_ecc(); /* do we read the data NOW, or has it already been read? */ printf("\nECC Corr (%d) Drive=%d Cyl=%d Head=%d Sect=%d\n", r, command.headdr & 0x20, command.cyl, command.headdr & 0x0f, command.sector); r = OK; break; } else { if(r != OK) printf("\nDisk Error (0x%x) Drive=%d Cyl=%d Head=%d Sect=%d\n", r, command.headdr & 0x20, command.cyl, command.headdr & 0x0f, command.sector); } if (r == OK) break; /* if successful, exit loop */ } return((r == OK) ? BLOCK_SIZE : EIO); } /*==========================================================================* * w_dma_setup * *==========================================================================*/ PRIVATE void w_dma_setup(wn) struct wini *wn; /* pointer to the drive struct */ { /* 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). */ int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end; vir_bytes vir, ct; phys_bytes user_phys; mode = (wn->wn_op == DISK_READ ? DMA_READ : DMA_WRITE); vir = (vir_bytes) wn->wn_address; ct = (vir_bytes) wn->wn_count; user_phys = numap(wn->wn_procnr, vir, ct); low_addr = (int) user_phys & BYTE; high_addr = (int) (user_phys >> 8) & BYTE; top_addr = (int) (user_phys >> 16) & BYTE; low_ct = (int) (ct - 1) & BYTE; high_ct = (int) ( (ct - 1) >> 8) & BYTE; /* Check to see if the transfer will require the DMA address counter to * go from one 64K segment to another. If so, do not even start it, since * the hardware does not carry from bit 15 to bit 16 of the DMA address. * Also check for bad buffer address. These errors mean FS contains a bug. */ if (user_phys == 0) panic("FS gave winchester disk driver bad addr", (int) vir); top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE); if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr); /* Now set up the DMA registers. */ #ifdef DEBUG_DMA printf("DMA Set: DMA_M2,M1 = 0x%x ", mode); /* set the DMA mode */ printf("DMA_ADDR(l) = 0x%x ", low_addr); /* output low-order 8 bits */ printf("DMA_ADDR(h) = 0x%x\n", high_addr);/* output next 8 bits */ printf("DMA Set: DMA_TOP = 0x%x ", top_addr); /* output highest 4 bits */ printf("DMA_COUNT(l) = 0x%x ", low_ct); /* output low 8 bits of count - 1 */ printf("DMA_COUNT(h) = 0x%x\n", high_ct);/* output high 8 bits of count - 1 */ #endif out_byte(DMA_INIT, DMA_RESET_VAL); /* Disable DRQ */ out_byte(DMA_M2, mode); /* set the DMA mode */ out_byte(DMA_M1, mode); /* set it again */ out_byte(DMA_ADDR, low_addr); /* output low-order 8 bits */ out_byte(DMA_ADDR, high_addr);/* output next 8 bits */ out_byte(DMA_TOP, top_addr); /* output highest 4 bits */ out_byte(DMA_COUNT, low_ct); /* output low 8 bits of count - 1 */ out_byte(DMA_COUNT, high_ct); /* output high 8 bits of count - 1 */ /* out_byte(DMA_INIT, DMA_SET_VAL); /* initialize DMA -- NOT HERE!! */ } /*===========================================================================* * w_reset * *===========================================================================*/ PRIVATE int w_reset() { /* Issue a reset to the controller. This is done after any catastrophe, * like the controller refusing to respond. */ int r, i; struct xt_cmd command; #ifdef DEBUG_RESET printf("Wini RESET\n"); #endif out_byte(WIN_STATUS, 0); /* Strobe reset bit low. */ for(i = MAX_WIN_RETRY/10; i; --i) ; /* Spin loop for a while */ out_byte(WIN_SELECT, 0); /* Issue a select to the ctlr */ i=stat_wait(WST_BUSY | WST_CMD | WST_REQ); if (i != OK) { /* timeout */ printf("Hard disk won't reset, status = %x\n", r); return(ERR); } /* Reset succeeded. Tell WIN drive parameters. */ w_need_reset = FALSE; for (i=0; icylinders >> 8;/* No. of cylinders (high byte) */ config.cyl_l = paramp->cylinders; /* No. of cylinders (low byte) */ config.heads = paramp->heads; /* No. of heads */ config.rwr_h = paramp->reduced_wr >> 8;/* Start reduced write (high byte) */ config.rwr_l = paramp->reduced_wr; /* Start reduced write (low byte) */ config.wpre_h= paramp->wr_precomp >> 8;/* Start write precompensation (high byte) */ config.wpre_l= paramp->wr_precomp; /* Start write precompensation (low byte) */ config.ecc = paramp->max_ecc; /* Ecc burst length */ if((r=xt_command(&command, PGM_WRITE, 8, &config)) != OK) { printf("Win_Specify Error: 0x%x\n", r); w_need_reset = TRUE; return(ERR); } else return(OK); } /*=========================================================================* * read_ecc * *=========================================================================*/ PRIVATE int read_ecc() { /* Read the ecc burst-length and let the controller correct the data */ /* Is this really necessary? I don't think so --GpO */ struct xt_cmd command; command.op = WIN_ECC_READ; if (xt_command(&command, PGM_READ, 1, buf) != OK) { return(-1); /* error reading ECC burst length!! */ } return(buf[0]); } /*=========================================================================* * stat_wait * *=========================================================================*/ /* Wait for the controler status to change to some setting */ PRIVATE int stat_wait(s) int s; { int i; void st_check(); for (i = MAX_WIN_RETRY; i ; i--) { if((in_byte(WIN_STATUS) & s) == s) { return(OK); } } /* The controler did not select quickly (it might be a WD that does controler diagnostics when reset) so set a clock alarm and check it later. The method for delaying on clock stolen from floppy.c. Delaying on the clock is required because fast CPUs can timeout a signed int much quicker than the controler can complete it's diagnostics. Software timing loops are never a good thing to use, but for quick loops (like transfering data to the controler and a fast select, etc) there is more overhead in setting up the clock than just using a spin loop. The approach I take here is to spin for one "RETRY" time (which should be short) then start delaying on the clock (in 2/60 intervals). */ #ifdef DEBUG_STAT printf("Stat_Wait: on Clock\n"); #endif for (i = MAX_WIN_RESET; i ; i--) { if((in_byte(WIN_STATUS) & s) == s) { #ifdef DEBUG_STAT printf("Stat_Wait: Took %d ticks\n", i); #endif return(OK); } w_clock_mess(2, st_check); receive(CLOCK, &w_mess); } #ifdef DEBUG_STAT printf("Stat_Wait: Timed OUT! 0x%x\n", in_byte(WIN_STATUS)); #endif return(ERR); } /*===========================================================================* * w_clock_mess * *===========================================================================*/ /* Stolen from floppy.c */ PRIVATE int w_clock_mess(ticks, func) int ticks; /* how many clock ticks to wait */ void (*func)(); /* function to call upon time out */ { /* Send the clock task a message. */ w_mess.m_type = SET_ALARM; w_mess.CLOCK_PROC_NR = WINCHESTER; w_mess.DELTA_TICKS = (long) ticks; w_mess.FUNC_TO_CALL = func; sendrec(CLOCK, &w_mess); } /*===========================================================================* * st_check * *===========================================================================*/ /* stolen from floppy.c */ PRIVATE void st_check() { /* This routine is called when the clock task has timed out on motor startup.*/ send(WINCHESTER, &w_mess); } /*=========================================================================* * xt_command * *=========================================================================*/ PRIVATE int xt_command(command, mode, count, address) struct xt_cmd *command; int mode, count; char *address; { struct xt_cmd stat; int rc; /* Execute a command and return a full error report if there was an error */ rc = xt_do(command, mode, count, address); if (rc != OK) { /* Error bit set! Issue a "Return Controler Status" command for details */ stat.op = WIN_SENSE; stat.headdr = command->headdr & 0x20; xt_do(&stat, PGM_READ, 4, buf); return(buf[0]); } return(OK); } /*=========================================================================* * xt_do * *=========================================================================*/ PRIVATE int xt_do(command, mode, count, address) char *command; int mode, count; char *address; { /* Issue a command to the winchester controler and return the completion status. If data is to be transfered with programmed I/O, do that, too. Otherwise rely on the DMA controler (which must be already set up). If interrupts/dma are used, handle enabling/disabling of DRQ/INT on the controler, and DMA IRQ in the DMAC. If an error occurs, just return the fact that it has and let the caller (usually xt_command) worry about issueing a "read status of last operation" comand (using this routine again) and a read_ecc_burst. */ int rc; /* Retry Count & Return Code */ int c, d, v; #ifdef DEBUG_DO1 printf("xt_do: command = 0x%x, mode 0x%x, %d long\n", command[0], mode, count); #endif out_byte(WIN_DMA, mode); /* enable DRQ and IRQ on controler */ out_byte(WIN_SELECT, mode); rc = stat_wait(WST_BUSY); /* wait for controler to ack select */ if (rc != OK) { w_need_reset = TRUE; return(ERR); } /* command to controler */ rc = xt_rw(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ, WST_BUSY | WST_CMD | WST_REQ, 6, command); if(rc == ERR) printf("xt_do: Command Output error!\n"); if (mode & INT) { /* (no data) wait for interrupt at the end of operation */ #ifdef DEBUG_DO2 printf("xt_do: wait-int"); #endif if(mode & DMA_) { out_byte(DMA_INIT, 3); /* allow DRQ in DMA controler */ #ifdef DEBUG_DMA printf("..dma..\n"); } else { printf("..no dma..\n"); #endif } receive(HARDWARE, &w_mess); if(mode & DMA_) out_byte(DMA_INIT, 0x07); /* Disable int from DMA */ out_byte(WIN_DMA, 0); /* Disable DRQ and INT */ cim_xt_wini(); } else if (mode & PGM_READ) { /* read from controler */ #ifdef DEBUG_DO2 printf("xt_do: read\n"); #endif rc = xt_rw(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ, WST_BUSY | WST_INPUT | WST_REQ, count, address); } else if (mode & PGM_WRITE) { /* write to controler */ #ifdef DEBUG_DO2 printf("xt_do: write\n"); #endif rc = xt_rw(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ, WST_BUSY | WST_REQ, count, address); } rc = stat_wait(WST_BUSY | WST_CMD | WST_INPUT | WST_REQ); rc = in_byte(WIN_DATA); #ifdef DEBUG_DO1 printf("xt_do: RC 0x%x\n", rc); #endif /*w_clock_mess(240, st_check); receive(CLOCK, &w_mess);*/ return(OK); } /*=========================================================================* * xt_rw * *=========================================================================*/ PRIVATE int xt_rw(mask, bits, count, addr) int mask, bits, count; char *addr; { /* Send or receive commands and data to and from the controler. Every time a byte is sent, we must wait for the "REQ" bit to be set. We also must watch for an incorrect change of state (C/D, I/O) in the controler in case it is doing something we don't want it to be. */ int rc; /* Retry Count */ #ifdef DEBUG_RW1 printf("xt_rw: mask=0x%x bits=0x%x count=%d addr=0x%x\n", mask, bits, count, addr); #endif while (count--) { for (rc=MAX_WIN_RETRY; rc && ((in_byte(WIN_STATUS) & mask) != bits); rc--) /* Wait for controler to get in the right mode */ ; if (!rc) { /* oops! timed out. Reset and try again */ #ifdef DEBUG_RW1 printf("xt_rw: timed out 0x%x\n", in_byte(WIN_STATUS)); #endif w_need_reset = TRUE; return(ERR); } #ifdef DEBUG_RW3 printf("xt_rw: Waited %d (0x%x)\n", MAX_WIN_RETRY - rc, *addr ); #endif #ifdef DEBUG_RW2 printf(" 0x%x ", *addr); #endif if (bits & WST_INPUT) *addr++ = in_byte(WIN_DATA); else out_byte(WIN_DATA,*addr++); } return(OK); } /*=========================================================================* * init_params * *=========================================================================*/ /* Initialization Process For ROM-table controlers: look at the switch settings (some WD ROMs use more switches than IBM) copy the table from ROM, based on the switch settings. For WD Autoconfigure controlers: read sector 0 and get the parameters from the 17 bytes preceeding the partition table Set parameters in "wini" structure from (rom) "param" structure. Read partition table of each drive */ PRIVATE void init_params() { /* This routine is called at startup to initialize the partition table, * the number of drives and the controller */ unsigned int i, segment, offset; int type_0, type_1; /* Get the number of drives from the bios */ phys_copy(0x475L, umap(proc_ptr, D, buf, 1), 1L); nr_drives = *buf & 0xFF; if (nr_drives > MAX_DRIVES) nr_drives = MAX_DRIVES; #ifdef DEBUG_CONF printf("%d Winchester Drives\n", nr_drives); #endif /* Read the switches from the controller */ i = in_byte(WIN_SELECT); #if WINI_EXT_SW /* More WD compatability fun: WD "G" ROMs use THREE switches to indicate which configuration table to use. */ type_0 = (i & 0x03) | (i & 0x40 >>4); type_1 = ((i & 0x0c) >> 2) | ((i & 0x80) >> 5); #else /* Calculate the drive types */ type_0 = i & 3; type_1 = (i >> 2) & 3; #endif #if AUTO_BIOS /* Get the drive parameters from sector zero of the drive if the autoconfig mode of the controller has been selected */ if(i & AUTO_ENABLE) { wd_autoconf(nr_drives, type_0, type_1); } else { #endif /* AUTO_BIOS */ rom_conf(nr_drives, type_0, type_1); #if AUTO_BIOS } #endif #ifdef DEBUG_CONF /* Print Drive Table */ prdrive(&drive[0],0); prdrive(&drive[1],1); /*w_clock_mess(3600, st_check); receive(CLOCK, &w_mess);*/ /* ----- ----- ----- */ #endif wini[0].wn_low = 0L; wini[0].wn_size = (long)((long)drive[0].cylinders * (long)drive[0].heads * (long) drive[0].sectors); if(nr_drives > 1) { wini[DEV_PER_DRIVE].wn_drive = 1; wini[DEV_PER_DRIVE].wn_low = 0L; wini[DEV_PER_DRIVE].wn_size = (long)((long)drive[1].cylinders * (long)drive[1].heads * (long) drive[1].sectors); } /* Initialize the controller */ cim_xt_wini(); /* ready for XT wini interrupts */ if ((nr_drives > 0) && (w_reset() != 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 * DEV_PER_DRIVE; 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) { printf("Can't read partition table of winchester %d\n", i); continue; } copy_prt(i, &wini[i * DEV_PER_DRIVE + 1], &buf[PART_TABLE_OFF]); #ifdef DEBUG_CONF /* Print Partition Table */ prpart(&buf[PART_TABLE_OFF]); /* ----- --------- ----- */ /* Print Wini Table */ prwini(&wini[i * DEV_PER_DRIVE]); /* ----- ---- ----- */ /*w_clock_mess(3600, st_check); receive(CLOCK, &w_mess);*/ #endif } } /*=========================================================================* * rom_conf * *=========================================================================*/ PRIVATE void rom_conf(nr_drives, type_0, type_1) int nr_drives, type_0, type_1; { unsigned int segment, offset; phys_bytes address; /* Copy the parameter vector from the saved vector table */ offset = vec_table[2 * WINI_0_PARM_VEC]; segment = vec_table[2 * WINI_0_PARM_VEC + 1]; /* Calculate the address off the parameters and copy them to buf */ address = hclick_to_physb(segment) + offset; phys_copy(address, umap(proc_ptr, D, buf, 64), 64L); /* Copy the parameters to the structures */ copy_param(&buf[type_0 * 16], &drive[0]); if (nr_drives > 1) copy_param(&buf[type_1 * 16], &drive[1]); } #if AUTO_BIOS /*=========================================================================* * wd_autoconf * *=========================================================================*/ PRIVATE void wd_autoconf(nr_drives, type_0, type_1) int nr_drives, type_0, type_1; { int rc; /* set up some phoney parameters so that we can read the first sector from the winchester. All drives will have one cylinder and one head, but set up initially to the mini scribe drives from ibm. */ drive[0].cylinders = AUTO_CYLS; drive[0].heads = AUTO_HEADS; drive[0].reduced_wr = AUTO_RWC; drive[0].max_ecc= AUTO_ECC; drive[0].wr_precomp = AUTO_WPC; drive[0].control= AUTO_CTRL; drive[0].sectors = NR_SECTORS; wini[0].wn_low = 0L; wini[0].wn_drive= 0; wini[0].wn_size =(long)AUTO_CYLS * (long)AUTO_HEADS * (long)NR_SECTORS; if(nr_drives > 1) { drive[1] = drive[0]; wini[DEV_PER_DRIVE] = wini[0]; wini[DEV_PER_DRIVE].wn_drive = 1; /* set drive number */ } cim_xt_wini(); /* ready for XT wini interrupts */ if(w_reset() != OK) { printf("cannot setup for reading winchester parameter tables"); nr_drives = 0; } /* generate the request to read the first sector from the wini 0 */ w_mess.DEVICE = 0; 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((rc=w_do_rdwt(&w_mess)) != BLOCK_SIZE) panic("cannot read drive parameters from winchester 0", rc); /* copy the parameter tables into the structures for later use */ copy_param(&buf[AUTO_PARAM], &drive[0]); if (nr_drives > 1) { /* generate the request to read the first sector from the winchester */ /*cp(wini[DEV_PER_DRIVE], wini[0], sizeof(struct wini));*/ w_mess.DEVICE = DEV_PER_DRIVE; w_mess.POSITION = 0L; w_mess.COUNT = BLOCK_SIZE; w_mess.m_type = DISK_READ; w_mess.ADDRESS = (char *) buf; w_mess.PROC_NR = WINCHESTER; if((rc=w_do_rdwt(&w_mess)) != BLOCK_SIZE) panic("cannot read drive parameters from winchester 5", rc); /* copy the parameter tables into the structures for later use */ copy_param(&buf[AUTO_PARAM], &drive[1]); } } #endif /* AUTO_BIOS */ /*=========================================================================* * copy_param * *=========================================================================*/ PRIVATE void copy_param(src, dest) register unsigned char *src; register struct param *dest; { /* This routine copies the disk parameter table to the local "param" structure (actually, a simple byte copy should suffice) and checks some parameters. If the number of sectors per track is zero, set it to NR_SECTORS */ /* cp(dest, src, sizeof(struct param));*/ #ifdef DEBUG_INIT printf("Param %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", src[0], src[1], src[2], src[3], src[4], src[5], src[6], src[7], src[8], src[9], src[10], src[11], src[12], src[13], src[14], src[15]); #endif dest->cylinders = *(u16_t *)src; dest->heads = src[2]; dest->reduced_wr = *(u16_t *)&src[3]; dest->wr_precomp = *(u16_t *)&src[5]; dest->max_ecc = src[7]; dest->control = src[8]; dest->timeout = src[9]; dest->ftimeout= src[10]; dest->chktimeout= src[11]; dest->landing = *(u16_t *)src[12]; dest->sectors = src[14]; if(dest->sectors == 0) dest->sectors = NR_SECTORS; /*w_clock_mess(480, st_check); receive(CLOCK, &w_mess);*/ } /*==========================================================================* * copy_prt * *==========================================================================*/ PRIVATE void copy_prt(dev, win, part) int dev; struct wini *win; struct part_entry *part; { /* This routine copies the partition table for the selected drive to * the variables wn_low and wn_size */ register struct wini *wn; register struct part_entry *pe; int i; wn = win; pe = part; wn->wn_drive = dev; for (i=0; i < NR_PARTITIONS ; i++, pe++, wn++) { wn->wn_drive = dev; wn->wn_low = pe->lowsec; wn->wn_size = pe->size; /* Adjust low sector to a multiple of (BLOCK_SIZE/SECTOR_SIZE) for old * Minix partitions only. We can assume the ratio is 2 and round to * even, which is slightly simpler. */ if (pe->sysind == OLD_MINIX_PART && wn->wn_low & 1) { ++wn->wn_low; --wn->wn_size; } } sort(win); } /*=========================================================================* * sort * *=========================================================================*/ PRIVATE void sort(wn) register struct wini wn[]; { register int i,j; struct wini tmp; for (i = 0; i < NR_PARTITIONS; i++) for (j = 0; j < NR_PARTITIONS-1; j++) if ((wn[j].wn_low == 0 && wn[j+1].wn_low != 0) || (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)) { tmp = wn[j]; wn[j] = wn[j+1]; wn[j+1] = tmp; } } /*=========================================================================* * cp * *=========================================================================*/ PRIVATE void cp(dest, src, count) register char *dest, *src; register int count; { while(count--) *dest++ = *src++; } #ifdef DEBUG_CONF prdrive(p, drive) struct param *p; int drive; { printf("Drive %d\n", drive); #define BIT(A,B) ((A&B)?"1":"0") printf("%-12s%-12s%-12s%-12s%-12s\n","","","Reduced","Write",""); printf("%-12s%-12s%-12s%-12s%-12s\n","Cylinders:","Total","Write","Precomp","Landing"); printf("%-12s%-12u%-12u%-12u%-12u\n","", p->cylinders, p->reduced_wr, p->wr_precomp, p->landing); /* printf("%-12s%-12s%-12s%-12s\n","Timeout","Standard","Format","Check"); printf("%-12s%-12d%-12d%-12d\n","", p->timeout, p->ftimeout, p->chktimeout);*/ printf("%-12s%-12s%-12s%-12s\n","","Heads","Max ECC","Sectors"); printf("%-12s%-12d%-12d%-12d\n","", p->heads, p->max_ecc, p->sectors); printf("%-12s%-6s%-6s%-12s%-12s%-12s\n","Control","ACC","ECC","Defect",">8 Heads","StepOp"); printf("%-12s%-6s%-6s%-12s%-12s%-12d\n\n","", BIT(p->control, 0x80), BIT(p->control, 0x40), BIT(p->control, 0x20), BIT(p->control, 0x08), (p->control & 0x07)); } prpart(pp) struct part_entry *pp; { int i; for(i=0;ibootind, pp->start_head, pp->start_sec, pp->start_cyl, pp->sysind, pp->last_head, pp->last_sec, pp->last_cyl, pp->lowsec, pp->size); } prwini(wn) struct wini *wn; { int i; printf("Wini Struct...\n"); for(i=0; i < DEV_PER_DRIVE; i++, wn++) { printf("[%d] Low %ld Size=%ld Drive=%d\n", i, wn->wn_low, wn->wn_size, wn->wn_drive); } } #endif