|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - download
Length: 13261 (0x33cd) Types: TextFile Notes: UNIX file Names: »junkwd.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦2d53db1df⟧ UNIX Filesystem └─ ⟦this⟧ »sys/z8001/drv/junkwd.c«
/* (-lgl * The information contained herein is a trade secret of Mark Williams * Company, and is confidential information. It is provided under a * license agreement, and may be copied or disclosed only under the * terms of that agreement. Any reproduction or disclosure of this * material without the express written authorization of Mark Williams * Company or persuant to the license agreement is unlawful. * * COHERENT Version 0.7.3 * Copyright (c) 1982, 1983, 1984. * An unpublished work by Mark Williams Company, Chicago. * All rights reserved. -lgl) */ /* * Driver for Western Digital hard disk controller for the Z8000HR. * * Uses Commodore's SASI-like command block structure. * * Last Revision: January 29, 1985 (nrb) */ #include <coherent.h> #include <buf.h> #include <con.h> #include <stat.h> #include <uproc.h> #include <errno.h> int wdload(); int wduload(); int wdopen(); int wdread(); int wdwrite(); int wdblock(); int wdintr(); int nulldev(); int nonedev(); char *pfix(); /* map phys addr to a char * */ CON wdcon = { DFBLK|DFCHR, /* Flags */ 2, /* Major index */ wdopen, /* Open */ nulldev, /* Close */ wdblock, /* Block */ wdread, /* Read */ wdwrite, /* Write */ nonedev, /* Ioctl */ nulldev, /* Powerfail */ nulldev, /* Timeout */ wdload, /* Load */ wduload /* Unload */ }; /* * Western Digital Controller port addresses */ #define WDIO 0x0500 /* Data Register (word mode) */ #define WDTDR 0x00 /* test drive ready */ #define WDREST 0x01 /* restore to cyl 0 */ #define WDRSS 0x03 /* Request status */ #define WDCTF 0x05 /* check track format */ #define WDFMTT 0x06 /* format track */ #define WDREAD 0x08 /* Read */ #define WDWRITE 0x0A /* Write */ #define WDSDP 0x0C /* set drive parameters */ #define WDCCBA 0x0F /* change command block address */ #define WDDDIAG 0xE3 /* run drive diagnostics */ #define WDCDIAG 0xE4 /* run controller diagnostics */ #define NSEC 17 /* number of sectors per track */ #define WDVEC 0x80 /* base of wd interrupt vector */ #define NDRIVES 2 /* max drives per controller */ typedef struct wdcmd { /* command block layout */ union { struct { unsigned char c_opcode; /* command class & opcode */ unsigned char c_lunhiaddr; /* lun & addr [4:0] */ unsigned char c_midaddr; /* middle sector address */ unsigned char c_lowaddr; /* low sector address */ } wdcmdop; unsigned long c_lunaddr; /* lun and whole address */ } wdcmdu; unsigned char c_blockcnt; /* number of blocks in I/O */ unsigned char c_control; /* reserved control byte */ unsigned char c_highdma; /* high DMA addr (phys segment) */ unsigned char c_middma; /* middle DMA address */ unsigned char c_lowdma; /* low DMA address */ unsigned char c_rsvd1; /* reserved */ unsigned char c_rsvd2; /* reserved */ unsigned char c_rsvd3; /* reserved */ unsigned char c_errorbits; /* error infowdation */ unsigned char c_lunladd2; /* error lun and high sector addr */ unsigned char c_ladd1; /* error middle address */ unsigned char c_ladd0; /* error low address */ } WDCMD; #define c_op wdcmdu.wdcmdop.c_opcode #define bool int #define TRUE (0 == 0) #define FALSE (0 != 0) /* * minor device layout * bits 7-4: physical drive number * bits 3-0: pseudo-drive number (partition) within the physical drive */ #define drive(mdev) ((mdev) >> 4) /* get drive # */ #define pseudo(mdev) ((mdev) & 0xF) /* get partition # */ #define NPSEUDO (sizeof wdbtab/sizeof wdbtab[0]) struct { unsigned long bstart; /* starting block # */ unsigned long bcount; /* size in blocks */ } wdbtab[] = { 0L, 5168L, 5168L, 5168L, 10336L, 5168L, 15504L, 5168L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 20672L }; /* * drive & controller parameters used during initialization. * note that the parameters are used for both drives by the controller * (sorry, no mix and match on drive types). */ typedef struct wdp { unsigned char p_options; /* options <7:4>, step rate <3:0> */ unsigned char p_head_cyl; /* # heads <6:4>, MSB # cyl <3:0> */ unsigned char p_cyl; /* LSB # cylinders <7:0> */ unsigned char p_precomp; /* precomp cyl/16 <7:0> */ unsigned char p_reduced; /* reduced write cur. cyl/16 <7:0> */ unsigned char p_nsectors; /* sectors per track <7:0> */ } WDP; WDP wdparam = { /* patch these values as needed: */ 0xF, /* no options, 16 uS fast step */ (4 << 4) | 1, /* 4 heads, MSB of cyl = 1 */ 0x32, /* # cyl = 0x132 = 306 */ 8, /* precomp starts at cyl 128. */ 8, /* reduced write cur. at cyl 128. */ 17 /* number of sectors per track */ }; /* * housekeeping */ typedef struct wdunit { bool wdu_active; /* drive/controller busy */ bool wdu_dma_aligned;/* dma address is 512 aligned */ struct wdunit *wdu_forw; /* next drive in controller chain */ struct wdunit *wdu_back; /* last drive */ BUF *wdu_actf; /* next BUF in drive chain */ paddr_t wdu_paddr; /* physical address of buffer */ } WDU; WDU wdtab; /* controller header */ WDU wdutab[NDRIVES]; /* drive headers */ BUF wdbuf; /* BUF structure */ /* * cmdblk is a 3*512 byte block aligned on a 512 byte boundary in physical * memory. It is declared in md.s to use here. WDBUFPADDR must be a paddr_t * pointing to cmdblk+(2*512) for the code to work correctly !!! */ extern char cmdblk[]; #ifndef WDCMDBLKPADDR #define WDCMDBLKPADDR 0x20000L /* default to 2|0000 based RAM */ #endif #ifndef WDBUFPADDR #define WDBUFPADDR 0x20400L /* above cmd blk and ECC buffer */ #endif /* * set up the driver and the actual controller. this involves setting the * drive parameters for the two drives. */ wdload() { setivec(WDVEC, wdintr); wdsetparam(); } wduload() { clrivec(WDVEC); } /* * Open routine. Check that the minor # is in range. */ wdopen(dev, mode) dev_t dev; { if (drive(minor(dev)) >= NDRIVES || pseudo(minor(dev)) >= NPSEUDO) u.u_error = ENXIO; } /* * perform a RAW read operation */ wdread(dev, iop) dev_t dev; IO *iop; { ioreq(&wdbuf, iop, dev, BREAD, BFIOC|BFBLK|BFRAW); } /* * perform a RAW read operation */ wdwrite(dev, iop) dev_t dev; IO *iop; { ioreq(&wdbuf, iop, dev, BWRITE, BFIOC|BFBLK|BFRAW); } /* * block I/O routine. */ wdblock(bp) register BUF *bp; { register int d; /* drive # */ register int pd; /* partition # */ register long bno; /* block # */ register int s; /* saved machine status */ bool wdstart(); d = drive(minor(bp->b_dev)); pd = pseudo(minor(bp->b_dev)); bno = bp->b_bno; #if VERBOSE printf("wdblock: d=%d, pd=%d, bno=%d enter\n", d, pd, (int)bno); wdbufdump(bp); #endif if (d >= NDRIVES || pd >= NPSEUDO || bno >= wdbtab[pd].bcount) { bp->b_flag |= BFERR; bdone(bp); return; } bp->b_bno += wdbtab[pd].bstart; s = sphi(); disksort(&wdutab[d], bp, bp->b_bno); wdustart(d); while (!wdstart()) ; spl(s); } /* * start up a disk I/O request to the controller if one is not already * in progress. */ wdustart(d) register int d; /* drive # */ { register BUF *bp; if (wdutab[d].wdu_active) /* active already ? */ return; bp = wdutab[d].wdu_actf; if (bp == (BUF *)0) /* any work to do ? */ return; wdutab[d].wdu_active = TRUE; if (wdtab.wdu_forw != (WDU *)0) wdtab.wdu_back->wdu_forw = &wdutab[d]; else wdtab.wdu_forw = &wdutab[d]; wdtab.wdu_back = &wdutab[d]; wdutab[d].wdu_forw = (WDU *)0; return; } /* * actually set up the command block for the WD controller to perform * the I/O. Check to see if DMA address is aligned on a 512 byte * boundary as required by the Commodore (MOS Technology) DMA controller. * All RAW I/O must be aligned !!! All other I/O must be aligned if greater * than 512 bytes (1 block) of I/O is performed. If a non-RAW non-aligned * write, copy the user's data to our wdiobuf which is aligned. If a read, * do the copy in the interrupt routine after the data is in the wdiobuf * buffer. * This is the price we have to pay for a poorly designed DMA controller. */ bool wdstart() { register BUF *bp; register WDCMD *cbp; register int d; int dma_error = FALSE; if (wdtab.wdu_active) return (TRUE); if (wdtab.wdu_forw == (WDU *)0) return (TRUE); bp = wdtab.wdu_forw->wdu_actf; wdtab.wdu_paddr = bp->b_paddr; d = drive(minor(bp->b_dev)); wdtab.wdu_dma_aligned = TRUE; if ((wdtab.wdu_paddr&0x1FFL) != 0) { /* make sure aligned 512 */ wdtab.wdu_dma_aligned = FALSE; if (bp->b_flag&BFRAW) { printf("wd: non-aligned dma in RAW mode\n"); ++dma_error; } else if (bp->b_count > 512) { printf("wd: non-aligned multi-block dma error\n"); ++dma_error; } } if (dma_error) { bp->b_flag |= BFERR; bdone(bp); wdtab.wdu_forw->wdu_active = 0; wdtab.wdu_forw->wdu_actf = bp->b_actf; wdtab.wdu_forw = wdtab.wdu_forw->wdu_forw; wdustart(d); return (FALSE); } wdtab.wdu_active = TRUE; if (!wdtab.wdu_dma_aligned && bp->b_req == BWRITE) /* one block only! */ kkcopy(pfix(0x3C, wdtab.wdu_paddr), cmdblk+0x400, 512); cbp = (WDCMD *) pfix(0x3C, WDCMDBLKPADDR); /* map onto cmd block */ wdcmdclr(cbp); /* clear out block */ cbp->wdcmdu.c_lunaddr = bp->b_bno; if (d) cbp->wdcmdu.wdcmdop.c_lunhiaddr |= d << 5; cbp->c_blockcnt = bp->b_count >> 9; if (wdtab.wdu_dma_aligned) { cbp->c_highdma = wdtab.wdu_paddr >> 16; cbp->c_middma = wdtab.wdu_paddr >> 8; cbp->c_lowdma = wdtab.wdu_paddr; } else { cbp->c_highdma = (WDBUFPADDR >> 16) & 0xFF; cbp->c_middma = (WDBUFPADDR >> 8) & 0xFF; cbp->c_lowdma = (WDBUFPADDR) & 0xFF; } cbp->c_errorbits = 0xFF; /* indicate ready */ cbp->c_op = ((bp->b_req==BREAD) ? WDREAD : WDWRITE); #if VERBOSE wdcbdump(cbp); #endif out(WDIO, 1); /* wake up controller */ return (TRUE); } /* * got an interrupt from the controller - see if it was a valid I/O * completion or an error. */ wdintr() { register BUF *bp; register WDCMD *cbp; register int d; if (wdtab.wdu_active) { cbp = (WDCMD *) pfix(0x3C, WDCMDBLKPADDR); /* map onto cmd blk */ bp = wdtab.wdu_forw->wdu_actf; d = drive(minor(bp->b_dev)); if ((cbp->c_errorbits & 0x7F) == 0) { wdtab.wdu_forw->wdu_actf = bp->b_actf; if (!wdtab.wdu_dma_aligned) /* one block only !! */ if (bp->b_req == BREAD) kkcopy(cmdblk+0x400,pfix(0x3C,wdtab.wdu_paddr),512); bdone(bp); } else wdharderr(bp, cbp); wdtab.wdu_forw->wdu_active = 0; wdtab.wdu_forw = wdtab.wdu_forw->wdu_forw; wdtab.wdu_active = 0; wdustart(d); } out(WDIO, 0); /* reset IEO on DMA chip */ while (!wdstart()) ; } /* * got an error - inform the user and try to recover. */ wdharderr(bp, cbp) register BUF *bp; register WDCMD *cbp; { register int d; register unsigned char *ucp; register int pd; pd = pseudo(minor(bp->b_dev)); printf("wd: err=%x [ ", cbp->c_errorbits); for (d = 0, ucp = (unsigned char *)cbp; d < sizeof(WDCMD); ++d) printf("%x ", *ucp++); printf("] "); devmsg(bp->b_dev, " b=%d", (int)(bp->b_bno-wdbtab[pd].bstart)); d = drive(minor(bp->b_dev)); wdutab[d].wdu_actf = bp->b_actf; bp->b_flag |= BFERR; bdone(bp); } /* * clear out the command block pointed to by cbp. */ wdcmdclr(cbp) WDCMD *cbp; { register unsigned char *ucp; register int i; ucp = (unsigned char *)cbp; for (i = 0; i < sizeof(WDCMD); ++i) *ucp++ = 0; } /* * sort request into queue * 'bp is inserted in the queue of the device (should be a disk drive) * given by 'dp'. A weaving algorithm is used. */ disksort(dp, bp, bno) struct wdunit *dp; register BUF *bp; daddr_t bno; { register BUF *last; register BUF *next; next = dp->wdu_actf; if (next == (BUF *)0) { dp->wdu_actf = bp; bp->b_actf = next; return; } while (last=next, next=next->b_actf) if ((last->b_bno <= bno && bno <= next->b_bno) || (last->b_bno >= bno && bno >= next->b_bno)) { last->b_actf = bp; bp->b_actf = next; return; } last->b_actf = bp; bp->b_actf = next; } #if VERBOSE /* * dump a BUF struct to the terminal */ wdbufdump(bp) register BUF *bp; { printf("BUF: flag=%x dev=%x bno=%d req=%x count=%d\n", bp->b_flag, bp->b_dev, (int)bp->b_bno, bp->b_req, (int)bp->b_count); printf(" resid=%d vaddr=%p paddr=%p bp=%p\n", (int)bp->b_resid, bp->b_vaddr, bp->b_paddr, bp); } /* * dump a command block to the terminal */ wdcbdump(cbp) register WDCMD *cbp; { register int d; register unsigned char *ucp; printf("wd: cb: [ ", cbp->c_errorbits); for (d = 0, ucp = (unsigned char *)cbp; d < sizeof(WDCMD); ++d) printf("%x ", *ucp++); printf("] "); } #endif /* * perform controller and drive initialization. note that the parameters * given to the controller are used for both hard disk drives. */ wdsetparam() { register WDCMD *cbp; /* cmd block pointer */ register WDP *dpp; /* drive param pointer */ cbp = (WDCMD *) pfix(0x3C, WDCMDBLKPADDR); /* map onto cmd block */ wdcmdclr(cbp); /* clear out block */ cbp->c_highdma = (WDBUFPADDR >> 16) & 0xFF; cbp->c_middma = (WDBUFPADDR >> 8) & 0xFF; cbp->c_lowdma = (WDBUFPADDR) & 0xFF; cbp->c_op = WDSDP; /* set drive params */ cbp->c_errorbits = 0xFF; /* mark block as valid */ dpp = (WDP *) pfix(0x3C, WDBUFPADDR); *dpp = wdparam; /* to achieve DMA alignment */ out(WDIO, 1); /* strobe line to DMA part */ }