/* 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]; status = in_byte(WIN_DATA); out_byte(WIN_DMA, 0); if (!(status & 2)) /* Test "error" bit */ return(OK); command[0] = WIN_SENSE; command[1] = w_drive << 5; if (com_out(NO_DMA_INT, command) != OK) return(ERR); /* Loop, extracting bytes from WIN */ for (i = 0; i < MAX_RESULTS; i++) { if (hd_wait(WST_REQ) != OK) return(ERR); status = in_byte(WIN_DATA); w_results[i] = status & BYTE; } if (hd_wait(WST_REQ) != OK) /* Missing from */ return (ERR); /* Original. 11-Apr-87 G.O. */ status = in_byte(WIN_DATA); /* Read "error" flag */ if (((status & 2) != 0) || (w_results[0] & 0x3F)) { return(ERR); } else return(OK); } /*===========================================================================* * win_out * *===========================================================================*/ PRIVATE void win_out(val) int val; /* write this byte to winchester disk controller */ { /* Output a byte to the controller. This is not entirely trivial, since you * can only write to it when it is listening, and it decides when to listen. * If the controller refuses to listen, the WIN chip is given a hard reset. */ int r; if (w_need_reset) return; /* if controller is not listening, return */ do { r = in_byte(WIN_STATUS); } while((r & (WST_REQ | WST_BUSY)) == WST_BUSY); out_byte(WIN_DATA, val); } /*===========================================================================* * 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, drive; u8_t command[6]; message mess; /* Strobe reset bit low. */ out_byte(WIN_STATUS, 0); milli_delay(5); /* Wait for a while */ out_byte(WIN_SELECT, 0); /* Issue select pulse */ for (i = 0; i < MAX_WIN_RETRY; i++) { r = in_byte(WIN_STATUS); if (r & (WST_DRQ | WST_IRQ)) return(ERR); if ((r & (WST_BUSY | WST_BUS | WST_REQ)) == (WST_BUSY | WST_BUS | WST_REQ)) break; } if (i == MAX_WIN_RETRY) { printf("%s: reset failed, status = %x\n", w_name(), r); return(ERR); } /* Reset succeeded. Tell WIN drive parameters. */ w_need_reset = FALSE; for (drive = 0; drive < nr_drives; drive++) { if (win_specify(drive) != OK) return (ERR); command[0] = WIN_RECALIBRATE; command[1] = drive << 5; command[5] = wini[drive].wn_ctlbyte; if (com_out(INT, command) != OK) return(ERR); receive(HARDWARE, &mess); if (win_results() != OK) { /* No actual drive present? */ nr_drives = drive; } } return(nr_drives > 0 ? OK : ERR); } /*==========================================================================* * w_handler * *==========================================================================*/ PRIVATE int w_handler(irq) int irq; { /* Disk interrupt, send message to winchester task and reenable interrupts. */ int r, i; out_byte(DMA_INIT, 0x07); /* Disable int from DMA */ for (i = 0; i < MAX_WIN_RETRY; ++i) { r = in_byte(WIN_STATUS); if (r & WST_IRQ) break; /* Exit if end of int */ } interrupt(WINCHESTER); return 1; } /*============================================================================* * win_specify * *============================================================================*/ PRIVATE int win_specify(drive) int drive; { struct wini *wn = &wini[drive]; u8_t command[6]; command[0] = WIN_SPECIFY; /* Specify some parameters */ command[1] = drive << 5; /* Drive number */ if (com_out(NO_DMA_INT, command) != OK) /* Output command block */ return(ERR); /* No. of cylinders (high byte) */ win_out(wn->wn_cylinders >> 8); /* No. of cylinders (low byte) */ win_out(wn->wn_cylinders); /* No. of heads */ win_out(wn->wn_heads); /* Start reduced write (high byte) */ win_out(wn->wn_reduced_wr >> 8); /* Start reduced write (low byte) */ win_out(wn->wn_reduced_wr); /* Start write precompensation (high byte) */ win_out(wn->wn_precomp >> 8); /* Start write precompensation (low byte) */ win_out(wn->wn_precomp); /* Ecc burst length */ win_out(wn->wn_max_ecc); if (check_init() != OK) { /* See if controller accepted parameters */ w_need_reset = TRUE; return(ERR); } else return(OK); } /*============================================================================* * check_init * *============================================================================*/ PRIVATE int check_init() { /* Routine to check if controller accepted the parameters */ int r, s; if (hd_wait(WST_REQ | WST_INPUT) == OK) { r = in_byte(WIN_DATA); do { s = in_byte(WIN_STATUS); } while(s & WST_BUSY); /* Loop while still busy */ if (r & 2) /* Test error bit */ return(ERR); else return(OK); } else return (ERR); /* Missing from original: 11-Apr-87 G.O. */ } /*============================================================================* * read_ecc * *============================================================================*/ PRIVATE int read_ecc() { /* Read the ecc burst-length and let the controller correct the data */ int r; u8_t command[6]; command[0] = WIN_ECC_READ; if (com_out(NO_DMA_INT, command) == OK && hd_wait(WST_REQ) == OK) { r = in_byte(WIN_DATA); if (hd_wait(WST_REQ) == OK) { r = in_byte(WIN_DATA); if (r & 1) w_need_reset = TRUE; } } return(ERR); } /*============================================================================* * hd_wait * *============================================================================*/ PRIVATE int hd_wait(bits) int bits; { /* Wait until the controller is ready to receive a command or send status */ int r, i = 0; do { r = in_byte(WIN_STATUS) & bits; } while ((i++ < MAX_WIN_RETRY) && r != bits); /* Wait for ALL bits */ if (i >= MAX_WIN_RETRY) { w_need_reset = TRUE; return(ERR); } else return(OK); } /*============================================================================* * com_out * *============================================================================*/ PRIVATE int com_out(mode, commandp) int mode; u8_t *commandp; { /* Output the command block to the winchester controller and return status */ int i, r; out_byte(WIN_DMA, mode); out_byte(WIN_SELECT, mode); for (i = 0; i < MAX_WIN_RETRY; i++) { r = in_byte(WIN_STATUS); if (r & WST_BUSY) break; } if (i == MAX_WIN_RETRY) { w_need_reset = TRUE; return(ERR); } for (i = 0; i < 6; i++) { if (hd_wait(WST_REQ) != OK) break; /* No data request pending */ r = in_byte(WIN_STATUS); if ((r & (WST_BUSY | WST_BUS | WST_INPUT)) != (WST_BUSY | WST_BUS)) break; out_byte(WIN_DATA, commandp[i]); } if (i != 6) return(ERR); else return(OK); } /*==========================================================================* * init_params * *==========================================================================*/ PRIVATE void init_params() { /* This routine is called at startup to initialize the number of drives and * the controller. */ u16_t parv[2]; unsigned int drive; int dtype; phys_bytes address, buf_phys; char buf[16]; /* Get the number of drives from the bios */ buf_phys = vir2phys(buf); phys_copy(0x475L, buf_phys, 1L); nr_drives = buf[0] & 0xFF; if (nr_drives > MAX_DRIVES) nr_drives = MAX_DRIVES; /* Read the switches from the controller */ w_switches = in_byte(WIN_SELECT); #if AUTO_BIOS /* If no auto configuration or not enabled then go to the ROM. */ if (!(w_switches & AUTO_ENABLE)) { #endif for (drive = 0; drive < nr_drives; drive++) { /* Calculate the drive type */ dtype = (w_switches >> (2 * drive)) & 03; /* Copy the BIOS parameter vector */ phys_copy(WINI_0_PARM_VEC * 4L, vir2phys(parv), 4L); /* Calculate the parameters' address and copy them to buf */ address = hclick_to_physb(parv[1]) + parv[0] + 16 * dtype; phys_copy(address, buf_phys, 16L); /* Copy the parameters to the structure of the drive. */ copy_param(buf, &wini[drive]); } #if AUTO_BIOS } #endif } /*============================================================================* * w_do_open * *============================================================================*/ PRIVATE int w_do_open(dp, m_ptr) struct driver *dp; message *m_ptr; { /* Device open: Initialize the controller and read the partition table. */ static int init_done = FALSE; if (!init_done) { w_init(); init_done = TRUE; } if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); if (w_wn->wn_open_ct++ == 0) { /* Partition the disk. */ partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY); } return(OK); } /*============================================================================* * w_do_close * *============================================================================*/ PRIVATE int w_do_close(dp, m_ptr) struct driver *dp; message *m_ptr; { /* Device close: Release a device. */ if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); w_wn->wn_open_ct--; return(OK); } /*==========================================================================* * w_init * *==========================================================================*/ PRIVATE void w_init() { /* Initialize the controller. */ int drive; struct wini *wn; #if AUTO_BIOS message mess; for (drive = 0; drive < nr_drives; drive++) { /* Get the drive parameters from sector zero of the drive if the * autoconfig mode of the controller has been selected. */ if (w_switches & AUTO_ENABLE) { /* Set up some phony 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. */ wn = &wini[drive]; wn->wn_cylinders = AUTO_CYLS; wn->wn_heads = AUTO_HEADS; wn->wn_reduced_wr = AUTO_RWC; wn->wn_precomp = AUTO_WPC; wn->wn_max_ecc = AUTO_ECC; wn->wn_ctlbyte = AUTO_CTRL; wn->wn_part[0].dv_size = SECTOR_SIZE; } } /* Initialize controller to read parameters from the drives. */ if (nr_drives > 0 && w_reset() != OK) nr_drives = 0; for (drive = 0; drive < nr_drives; drive++) { if (w_switches & AUTO_ENABLE) { /* read the first sector from the drive */ mess.DEVICE = drive * DEV_PER_DRIVE; mess.POSITION = 0L; mess.COUNT = SECTOR_SIZE; mess.ADDRESS = (char *) tmp_buf; mess.PROC_NR = WINCHESTER; mess.m_type = DEV_READ; if (do_rdwt(&w_dtab, &mess) != SECTOR_SIZE) { printf("%s: can't read parameters\n", w_name()); nr_drives = drive; break; } /* save the parameter tables for later use */ copy_param(&tmp_buf[AUTO_PARAM], &wini[drive]); } } #endif if (nr_drives > 0 && w_reset() != OK) nr_drives = 0; /* Set the size of each disk. */ for (drive = 0; drive < nr_drives; drive++) { (void) w_prepare(drive * DEV_PER_DRIVE); wn = w_wn; wn->wn_part[0].dv_size = ((unsigned long) wn->wn_cylinders * wn->wn_heads * NR_SECTORS) << SECTOR_SHIFT; printf("%s: %d cylinders, %d heads, %d sectors per track\n", w_name(), wn->wn_cylinders, wn->wn_heads, NR_SECTORS); } } /*==========================================================================* * copy_param * *==========================================================================*/ PRIVATE void copy_param(src, dest) char *src; struct wini *dest; { /* This routine copies the parameters from src to dest. */ dest->wn_cylinders = bp_cylinders(src); dest->wn_heads = bp_heads(src); dest->wn_reduced_wr = bp_reduced_wr(src); dest->wn_precomp = bp_precomp(src); dest->wn_max_ecc = bp_max_ecc(src); dest->wn_ctlbyte = bp_ctlbyte(src); } /*============================================================================* * w_geometry * *============================================================================*/ PRIVATE void w_geometry(entry) struct partition *entry; { entry->cylinders = w_wn->wn_cylinders; entry->heads = w_wn->wn_heads; entry->sectors = NR_SECTORS; } #endif /* ENABLE_XT_WINI */