Index: head/sys/pc98/cbus/fdc.c =================================================================== --- head/sys/pc98/cbus/fdc.c (revision 17349) +++ head/sys/pc98/cbus/fdc.c (revision 17350) @@ -1,2492 +1,2482 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.1.1.1 1996/06/14 10:04:43 asami Exp $ + * $Id: fd.c,v 1.2 1996/07/23 07:46:11 asami Exp $ * */ #include "ft.h" #if NFT < 1 #undef NFDC #endif #include "fd.h" #if NFDC > 0 #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #include #else #include #include #include #include #include #endif #include #if NFT > 0 #include #ifdef PC98 #include #else #include #endif #endif #ifdef DEVFS #include #endif static int fd_goaway(struct kern_devconf *, int); static int fdc_goaway(struct kern_devconf *, int); static int fd_externalize(struct kern_devconf *, struct sysctl_req *); /* * Templates for the kern_devconf structures used when we attach. */ static struct kern_devconf kdc_fd[NFD] = { { 0, 0, 0, /* filled in by kern_devconf.c */ "fd", 0, { MDDT_DISK, 0 }, fd_externalize, 0, fd_goaway, DISK_EXTERNALLEN, 0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "floppy disk", DC_CLS_DISK /* class */ } }; struct kern_devconf kdc_fdc[NFDC] = { { 0, 0, 0, /* filled in by kern_devconf.c */ #ifdef PC98 "fdc", 0, { MDDT_PC98, 0, "bio" }, pc98_generic_externalize, 0, fdc_goaway, PC98_EXTERNALLEN, #else "fdc", 0, { MDDT_ISA, 0, "bio" }, isa_generic_externalize, 0, fdc_goaway, ISA_EXTERNALLEN, #endif 0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "floppy disk/tape controller", DC_CLS_MISC /* class */ } }; static inline void fd_registerdev(int ctlr, int unit) { if(unit != 0) kdc_fd[unit] = kdc_fd[0]; kdc_fd[unit].kdc_unit = unit; kdc_fd[unit].kdc_parent = &kdc_fdc[ctlr]; kdc_fd[unit].kdc_parentdata = 0; dev_attach(&kdc_fd[unit]); } static inline void #ifdef PC98 fdc_registerdev(struct pc98_device *dvp) #else fdc_registerdev(struct isa_device *dvp) #endif { int unit = dvp->id_unit; if(unit != 0) kdc_fdc[unit] = kdc_fdc[0]; kdc_fdc[unit].kdc_unit = unit; #ifdef PC98 kdc_fdc[unit].kdc_parent = &kdc_nec0; #else kdc_fdc[unit].kdc_parent = &kdc_isa0; #endif kdc_fdc[unit].kdc_parentdata = dvp; dev_attach(&kdc_fdc[unit]); } static int fdc_goaway(struct kern_devconf *kdc, int force) { if(force) { dev_detach(kdc); return 0; } else { return EBUSY; /* XXX fix */ } } static int fd_goaway(struct kern_devconf *kdc, int force) { dev_detach(kdc); return 0; } #define b_cylin b_resid /* XXX now spelled b_cylinder elsewhere */ /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* * this biotab field doubles as a field for the physical unit number * on the controller */ #define id_physid id_scsiid /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #ifdef PC98 #define NUMTYPES 5 #define NUMDENS NUMTYPES #else #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) #endif /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #ifdef PC98 #define FDT_NONE 0 /* none present */ #define FDT_12M 1 /* 1M/640K FDD */ #define FDT_144M 2 /* 1.44M/1M/640K FDD */ #define FD_1200 1 #define FD_1232 2 #define FD_720 3 #define FD_640 4 #define FD_1440 5 #else #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 #endif static struct fd_type fd_types[NUMTYPES] = { #ifdef PC98 { 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2 meg HD floppy */ { 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.2 meg HD floppy 1024/sec */ { 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720k floppy in 1.2meg drive */ { 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640k floppy in 1.2meg drive */ { 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44 meg HD 3.5in floppy */ #else { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ #endif }; #ifdef PC98 #define DRVS_PER_CTLR 4 /* 4 floppies */ #else #define DRVS_PER_CTLR 2 /* 2 floppies */ #endif /***********************************************************************\ * Per controller structure. * \***********************************************************************/ struct fdc_data fdc_data[NFDC]; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ static struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ int dkunit; /* disk stats unit number */ #ifdef DEVFS void *bdevs[1 + NUMDENS + MAXPARTITIONS]; void *cdevs[1 + NUMDENS + MAXPARTITIONS]; #endif #ifdef PC98 int pc98_trans; #endif } fd_data[NFD]; #ifdef EPSON_NRDISK typedef unsigned int nrd_t; #define P_NRD_ADDRH 0xc24 #define P_NRD_ADDRM 0xc22 #define P_NRD_ADDRL 0xc20 #define P_NRD_CHECK 0xc20 #define P_NRD_DATA 0xc26 #define P_NRD_LED 0xc36 #define B_NRD_CHK 0x80 #define B_NRD_LED 0x40 #define A_NRD_INFO 0x2 #define A_NRD_BASE 0x400 #define NRD_STATUS 0x0 #define NRD_ST0_HD 0x04 static fdu_t nrdu=-1; static int nrdsec=0; static nrd_t nrdblkn=0; static nrd_t nrdaddr=0x0; #define nrd_check_ready() ({ \ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \ }) #define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED) #define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED) #define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff)) #define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff)) #define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff)) #define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff)) #define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE)) static inline void nrd_addr(addr) nrd_t addr; { epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f)); epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff)); epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff)); } static inline u_short nrd_info(addr) nrd_t addr; { u_short tmp; nrd_addr(addr); outb(0x43f, 0x42); tmp = (short)inw(P_NRD_DATA); outb(0x43f, 0x40); return ((u_short)tmp); } #endif /* EPSON_NRDISK */ /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #if NFT > 0 int ftopen(dev_t, int); int ftintr(ftu_t ftu); int ftclose(dev_t, int); void ftstrategy(struct buf *); int ftioctl(dev_t, int, caddr_t, int, struct proc *); int ftdump(dev_t); int ftsize(dev_t); #ifdef PC98 int ftattach(struct pc98_device *, struct pc98_device *, int); #else int ftattach(struct isa_device *, struct isa_device *, int); #endif #endif /* autoconfig functions */ #ifdef PC98 static int fdprobe(struct pc98_device *); static int fdattach(struct pc98_device *); #else static int fdprobe(struct isa_device *); static int fdattach(struct isa_device *); #endif /* needed for ft driver, thus exported */ int in_fdc(fdcu_t); int out_fdc(fdcu_t, int); /* internal functions */ static void set_motor(fdcu_t, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(fdu_t); static void fdc_reset(fdc_p); static int fd_in(fdcu_t, int *); static void fdstart(fdcu_t); static timeout_t fd_timeout; static timeout_t fd_pseudointr; static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); static int fdformat(dev_t, struct fd_formb *, struct proc *); #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #ifdef DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT" }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* DEBUG */ /* autoconfig structure */ #ifdef PC98 struct pc98_driver fdcdriver = { #else struct isa_driver fdcdriver = { #endif fdprobe, fdattach, "fdc", }; static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 -extern struct cdevsw fd_cdevsw; +static struct cdevsw fd_cdevsw; static struct bdevsw fd_bdevsw = { Fdopen, fdclose, fdstrategy, fdioctl, /*2*/ nodump, nopsize, 0, "fd", &fd_cdevsw, -1 }; -static struct cdevsw fd_cdevsw = - { Fdopen, fdclose, rawread, rawwrite, /*9*/ - fdioctl, nostop, nullreset, nodevtotty, - seltrue, nommap, fdstrategy, "fd", - &fd_bdevsw, -1 }; #ifdef PC98 static struct pc98_device *fdcdevs[NFDC]; #else static struct isa_device *fdcdevs[NFDC]; #endif /* * Provide hw.devconf information. */ static int fd_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { return disk_externalize(fd_data[kdc->kdc_unit].fdsu, req); } static int fdc_err(fdcu_t fdcu, const char *s) { fdc_data[fdcu].fdc_errs++; if(s) { if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX) printf("fdc%d: %s", fdcu, s); else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX) printf("fdc%d: too many errors, not logging any more\n", fdcu); } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(fdcu_t fdcu, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdcu, va_arg(ap, int)) < 0) { char msg[50]; sprintf(msg, "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdcu, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdcu, ptr) < 0) { char msg[50]; sprintf(msg, "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdcu, msg); } } return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc->fdcu, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int st0, cyl; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8; else nrdaddr = 0x0; *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS; *cylp = nrd_trac(); } else { #endif /* EPSON_NRDISK */ int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc->fdcu, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc->fdcu, &cyl) < 0) { return fdc_err(fdc->fdcu, "can't get cyl num\n"); } if (cylp) *cylp = cyl; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc_status wants u_longs and * fd_in() gives ints. */ int status; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { switch (i) { case 0: fdc->status[i] = nrd_head() ? NRD_ST0_HD : NRD_STATUS; break; case 1: fdc->status[i] = NRD_STATUS; break; case 2: fdc->status[i] = NRD_STATUS; break; case 3: fdc->status[i] = nrd_trac(); break; case 4: fdc->status[i] = nrd_head(); break; case 5: fdc->status[i] = nrdsec; break; case 6: fdc->status[i] = nrd_secsize(); break; } ret = 0; } else { #endif /* EPSON_NRDISK */ ret = fd_in(fdc->fdcu, &status); fdc->status[i] = status; if (ret != 0) break; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ #ifdef PC98 static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */ static int pc98_trans_prev = 0; static void set_density(fdcu_t, fdu_t); static int pc98_fd_check_ready(fdu_t); static void set_density(fdcu, fdu) fdcu_t fdcu; fdu_t fdu; { /* always motor on */ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC); DELAY(100); outb(fdc_data[fdcu].baseport + FDOUT, FDO_RST | FDO_DMAE); /* in the case of note W, always inhibit 100ms timer */ } static int pc98_fd_check_ready(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; fdcu_t fdcu = fd->fdc->fdcu; int retry = 0; #ifdef EPSON_NRDISK if (fdu == nrdu) { if (nrd_check_ready()) return 0; else return -1; } #endif while (retry++ < 30000) { set_motor(fdcu, fd->fdsu, TURNON); out_fdc(fdcu, NE7CMD_SENSED); /* Sense Drive Status */ DELAY(100); out_fdc(fdcu, fdu); /* Drive number */ DELAY(100); if ((in_fdc(fdcu) & NE7_ST3_RD)){ outb(fdc_data[fdcu].baseport + FDOUT, FDO_DMAE | FDO_MTON); DELAY(10); return 0; } } return -1; } #endif /* * probe for existance of controller */ static int #ifdef PC98 fdprobe(struct pc98_device *dev) #else fdprobe(struct isa_device *dev) #endif { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { printf("fdc%d: unit used multiple times\n", fdcu); return 0; } fdcdevs[fdcu] = dev; fdc_data[fdcu].baseport = dev->id_iobase; #ifndef DEV_LKM fdc_registerdev(dev); #endif #ifndef PC98 /* First - lets reset the floppy controller */ outb(dev->id_iobase+FDOUT, 0); DELAY(100); outb(dev->id_iobase+FDOUT, FDO_FRST); #endif /* see if it can handle a command */ #ifdef PC98 if (fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0)) #else if (fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) #endif { return(0); } kdc_fdc[fdcu].kdc_state = DC_IDLE; return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ static int #ifdef PC98 fdattach(struct pc98_device *dev) #else fdattach(struct isa_device *dev) #endif { unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; int fdsu, st0, st3, i; #if NFT > 0 int unithasfd; #endif #ifdef PC98 struct pc98_device *fdup; #else struct isa_device *fdup; #endif int ic_type = 0; #ifdef DEVFS int mynor; int typemynor; int typesize; #endif fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; #ifdef PC98 fdc->dmachan = 2; if (fdc->dmachan != dev->id_drq) { dev->id_drq = fdc->dmachan; printf(" [dma is changed to #%d]", fdc->dmachan); } /* Acquire the DMA channel forever, The driver will do the rest */ pc98_dma_acquire(fdc->dmachan); pc98_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; fdc_reset(fdc); #else fdc->dmachan = dev->id_drq; /* Acquire the DMA channel forever, The driver will do the rest */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); #endif TAILQ_INIT(&fdc->head); /* check for each floppy drive */ #ifdef PC98 for (fdup = pc98_biotab_fdc; fdup->id_driver != 0; fdup++) { #else for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) { #endif if (fdup->id_iobase != dev->id_iobase) continue; fdu = fdup->id_unit; fd = &fd_data[fdu]; if (fdu >= (NFD+NFT)) continue; fdsu = fdup->id_physid; /* look up what bios thinks we have */ switch (fdu) { #ifdef PC98 case 0: case 1: case 2: case 3: if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fdu) & 0x01) fdt = FDT_144M; #ifdef EPSON_NRDISK else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fdu) & 0x01) { if (nrd_check_ready()) { nrd_LED_on(); nrdu = fdu; } else fdt = FDT_NONE; } } } #else /* !EPSON_NRDISK */ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fdu) & 0x01) fdt = FDT_NONE; } } #endif /* EPSON_NRDISK */ else fdt = FDT_NONE; break; default: fdt = FDT_NONE; break; #else case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; #endif } /* is there a unit? */ #ifdef PC98 if ((fdt == FDT_NONE) #else if ((fdt == RTCFDT_NONE) #endif #if NFT > 0 || (fdsu >= DRVS_PER_CTLR)) { #else ) { #ifdef PC98 fd->fdc = fdc; #endif fd->type = NO_TYPE; #endif #if NFT > 0 /* If BIOS says no floppy, or > 2nd device */ /* Probe for and attach a floppy tape. */ /* Tell FT if there was already a disk */ /* with this unit number found. */ unithasfd = 0; if (fdu < NFD && fd->type != NO_TYPE) unithasfd = 1; if (ftattach(dev, fdup, unithasfd)) continue; if (fdsu < DRVS_PER_CTLR) fd->type = NO_TYPE; #endif continue; } #ifdef PC98 kdc_fdc[fdcu].kdc_description = "NEC 765 floppy disk/tape controller"; #else /* select it */ set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ if (ic_type == 0 && fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { printf("fdc%d: ", fdcu); ic_type = (u_char)ic_type; switch( ic_type ) { case 0x80: printf("NEC 765\n"); fdc->fdct = FDC_NE765; kdc_fdc[fdcu].kdc_description = "NEC 765 floppy disk/tape controller"; break; case 0x81: printf("Intel 82077\n"); fdc->fdct = FDC_I82077; kdc_fdc[fdcu].kdc_description = "Intel 82077 floppy disk/tape controller"; break; case 0x90: printf("NEC 72065B\n"); fdc->fdct = FDC_NE72065; kdc_fdc[fdcu].kdc_description = "NEC 72065B floppy disk/tape controller"; break; default: printf("unknown IC type %02x\n", ic_type); fdc->fdct = FDC_UNKNOWN; break; } } if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; #endif fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fd%d: ", fdu); fd_registerdev(fdcu, fdu); switch (fdt) { #ifdef PC98 case FDT_12M: #ifdef EPSON_NRDISK if (fdu == nrdu) { printf("EPSON RAM DRIVE\n"); nrd_LED_off(); } else printf("1M/640M FDD\n"); #else /* !EPSON_NRDISK */ printf("1M/640K FDD\n"); #endif /* EPSON_NRDISK */ fd->type = FD_1200; fd->pc98_trans = 0; kdc_fd[fdu].kdc_description = "1M/640K floppy disk drive"; #ifdef DEVFS sprintf(name,"rfd%d.1200",fdu); #endif /* DEVFS */ break; case FDT_144M: printf("1.44M FDD\n"); fd->type = FD_1200; fd->pc98_trans = 0; outb(0x4be, (fdu << 5) | 0x10); kdc_fd[fdu].kdc_description = "1.44MB (1440K) 3.5in floppy disk drive"; #ifdef DEVFS sprintf(name,"rfd%d.1440",fdu); #endif /* DEVFS */ break; #else case RTCFDT_12M: printf("1.2MB 5.25in\n"); fd->type = FD_1200; kdc_fd[fdu].kdc_description = "1.2MB (1200K) 5.25in floppy disk drive"; break; case RTCFDT_144M: printf("1.44MB 3.5in\n"); fd->type = FD_1440; kdc_fd[fdu].kdc_description = "1.44MB (1440K) 3.5in floppy disk drive"; break; case RTCFDT_288M: case RTCFDT_288M_1: printf("2.88MB 3.5in - 1.44MB mode\n"); fd->type = FD_1440; kdc_fd[fdu].kdc_description = "2.88MB (2880K) 3.5in floppy disk drive in 1.44 mode"; break; case RTCFDT_360K: printf("360KB 5.25in\n"); fd->type = FD_360; kdc_fd[fdu].kdc_description = "360KB 5.25in floppy disk drive"; break; case RTCFDT_720K: printf("720KB 3.5in\n"); fd->type = FD_720; kdc_fd[fdu].kdc_description = "720KB 3.5in floppy disk drive"; break; #endif default: printf("unknown\n"); fd->type = NO_TYPE; dev_detach(&kdc_fd[fdu]); continue; } kdc_fd[fdu].kdc_state = DC_IDLE; #ifdef DEVFS mynor = fdu << 6; fd->bdevs[0] = devfs_add_devswf(&fd_bdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d", fdu); fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fdu); for (i = 1; i < 1 + NUMDENS; i++) { /* * XXX this and the lookup in Fdopen() should be * data driven. */ switch (fd->type) { case FD_360: if (i != FD_360) continue; break; case FD_720: if (i != FD_720 && i != FD_800 && i != FD_820) continue; break; case FD_1200: if (i != FD_360 && i != FD_720 && i != FD_800 && i != FD_820 && i != FD_1200 && i != FD_1440 && i != FD_1480) continue; break; case FD_1440: if (i != FD_720 && i != FD_800 && i != FD_820 && i != FD_1200 && i != FD_1440 && i != FD_1480 && i != FD_1720) continue; break; } typemynor = mynor | i; typesize = fd_types[i - 1].size / 2; /* * XXX all these conversions give bloated code and * confusing names. */ if (typesize == 1476) typesize = 1480; if (typesize == 1722) typesize = 1720; fd->bdevs[i] = devfs_add_devswf(&fd_bdevsw, typemynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d.%d", fdu, typesize); fd->cdevs[i] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d.%d", fdu, typesize); } for (i = 0; i < MAXPARTITIONS; i++) { fd->bdevs[1 + NUMDENS + i] = devfs_link(fd->bdevs[0], "fd%d%c", fdu, 'a' + i); fd->cdevs[1 + NUMDENS + i] = devfs_link(fd->cdevs[0], "rfd%d%c", fdu, 'a' + i); } #endif /* DEVFS */ if (dk_ndrive < DK_NDRIVE) { sprintf(dk_names[dk_ndrive], "fd%d", fdu); fd->dkunit = dk_ndrive++; /* * XXX assume rate is FDC_500KBPS. */ dk_wpms[dk_ndrive] = 500000 / 8 / 2; } else { fd->dkunit = -1; } } return (1); } /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(fdcu_t fdcu, int fdsu, int turnon) { int fdout = fdc_data[fdcu].fdout; int needspecify = 0; #ifdef PC98 outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC); DELAY(10); fdout = FDO_DMAE|FDO_MTON; #else if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } #endif outb(fdc_data[fdcu].baseport+FDOUT, fdout); DELAY(10); fdc_data[fdcu].fdout = fdout; #ifndef PC98 kdc_fdc[fdcu].kdc_state = (fdout & FDO_FRST)? DC_BUSY: DC_IDLE; #endif TRACE1("[0x%x->FDOUT]", fdout); if(needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ #ifdef PC98 (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif } } static void fd_turnoff(void *arg1) { fdu_t fdu = (fdu_t)arg1; int s; fd_p fd = fd_data + fdu; TRACE1("[fd%d: turnoff]", fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) { timeout(fd_turnoff, arg1, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *arg1) { fdu_t fdu = (fdu_t)arg1; int s; fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdintr(fd->fdc->fdcu); } splx(s); } static void fd_turnon(fdu_t fdu) { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { fdcu_t fdcu = fdc->fdcu; /* Try a reset, keep motor on */ #ifdef PC98 set_density(fdcu, 0); if (pc98_machine_type & M_EPSON_PC98) outb(fdc->baseport + FDOUT, 0xe8); else outb(fdc->baseport + FDOUT, 0xd8); DELAY(200); outb(fdc->baseport + FDOUT, 0x18); DELAY(10); #else outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); outb(fdc->baseport + FDOUT, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); #endif /* XXX after a reset, silently believe the FDC will accept commands */ #ifdef PC98 (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(fdcu_t fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 1000000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) { if (i == NE7_RQM) return fdc_err(fdcu, "ready for output in input\n"); } if (j <= 0) return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); #ifdef DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else return inb(baseport+FDDATA); #endif } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(fdcu_t fdcu, int *ptr) { int baseport = fdc_data[fdcu].baseport; int i, j = 1000000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) { DELAY(10); if (i == NE7_RQM) return fdc_err(fdcu, "ready for output in input\n"); } if (j <= 0) return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); #ifdef DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else i = inb(baseport+FDDATA); if (ptr) *ptr = i; return 0; #endif } int out_fdc(fdcu_t fdcu, int x) { int baseport = fdc_data[fdcu].baseport; int i; /* Check that the direction bit is set */ i = 1000000; while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0) DELAY(10); if (i <= 0) return fdc_err(fdcu, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 1000000; while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0) DELAY(10); if (i <= 0) return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ outb(baseport+FDDATA, x); DELAY(10); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fdc_p fdc; #if NFT > 0 /* check for a tape open */ if (type & F_TAPE_TYPE) return(ftopen(dev, flags)); #endif /* check bounds */ if (fdu >= NFD) return(ENXIO); fdc = fd_data[fdu].fdc; if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE)) return(ENXIO); if (type > NUMDENS) return(ENXIO); #ifdef PC98 if (pc98_fd_check_ready(fdu) == -1) return(EIO); #endif if (type == 0) type = fd_data[fdu].type; #ifndef PC98 else { if (type != fd_data[fdu].type) { switch (fd_data[fdu].type) { case FD_360: return(ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) return(ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } #endif fd_data[fdu].ft = fd_types + type - 1; fd_data[fdu].flags |= FD_OPEN; kdc_fd[fdu].kdc_state = DC_BUSY; return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); #if NFT > 0 int type = FDTYPE(minor(dev)); if (type & F_TAPE_TYPE) return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; fd_data[fdu].options &= ~FDOPT_NORETRY; kdc_fd[fdu].kdc_state = DC_IDLE; return(0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { long nblocks, blknum; int s; fdcu_t fdcu; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); fd = &fd_data[fdu]; fdc = fd->fdc; fdcu = fdc->fdcu; #if NFT > 0 if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { /* ft tapes do not (yet) support strategy i/o */ bp->b_error = ENODEV; bp->b_flags |= B_ERROR; goto bad; } /* check for controller already busy with tape */ if (fdc->flags & FDC_TAPE_BUSY) { bp->b_error = EBUSY; bp->b_flags |= B_ERROR; goto bad; } #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { if ((fdu >= NFD) || (bp->b_blkno < 0)) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; #ifdef PC98 #define B_XXX2 0x8000000 if (bp->b_flags & B_XXX2) { blknum *= 2; bp->b_blkno *= 2; bp->b_flags &= ~B_XXX2; } #endif if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum == nblocks) { bp->b_resid = bp->b_bcount; } else { bp->b_error = ENOSPC; bp->b_flags |= B_ERROR; } goto bad; } bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); bp->b_pblkno = bp->b_blkno; s = splbio(); tqdisksort(&fdc->head, bp); untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ fdstart(fdcu); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(fdcu_t fdcu) { int s; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) { fdintr(fdcu); } splx(s); } static void fd_timeout(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; int baseport = fdc_data[fdcu].baseport; struct buf *bp; int s; bp = TAILQ_FIRST(&fdc_data[fdcu].head); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! */ s = splbio(); TRACE1("fd%d[fd_timeout()]", fdu); /* See if the controller is still busy (patiently awaiting data) */ if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) { DELAY(5); TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); /* yup, it is; kill it now */ fdc_reset(&fdc_data[fdcu]); printf("fd%d: Operation timeout\n", fdu); } if (bp) { retrier(fdcu); fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; } else { fdc_data[fdcu].fd = (fd_p) 0; fdc_data[fdcu].fdu = -1; fdc_data[fdcu].state = DEVIDLE; } fdintr(fdcu); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; int s; s = splbio(); fdintr(fdcu); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ void fdintr(fdcu_t fdcu) { fdc_p fdc = fdc_data + fdcu; #if NFT > 0 fdu_t fdu = fdc->fdu; if (fdc->flags & FDC_TAPE_BUSY) (ftintr(fdu)); else #endif while(fdstate(fdcu, fdc)) ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdcu_t fdcu, fdc_p fdc) { int read, format, head, sec = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = TAILQ_FIRST(&fdc->head); if(!bp) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if(fdc->fd) { printf("fd%d: unexpected valid fd pointer\n", fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("fd%d: confused fd pointers\n", fdu); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; #ifdef PC98 pc98_trans = fd->ft->trans; if (pc98_trans_prev != pc98_trans) { int i; set_density(fdcu, fdu); for (i = 0; i < 10; i++) { outb(0x5f, 0); outb(0x5f, 0); } pc98_trans_prev = pc98_trans; } if (pc98_trans != fd->pc98_trans) { if (pc98_trans != 1 && (PC98_SYSTEM_PARAMETER(0x5ae) >> fdu) & 0x01) { outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1)); outb(0x5f, 0); outb(0x5f, 0); } fd->pc98_trans = pc98_trans; } #else outb(fdc->baseport+FDCTL, fd->ft->trans); #endif TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return(0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ #ifdef EPSON_NRDISK if (fdu != nrdu) { if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } } #else /* !EPSON_NRDISK */ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } #endif fdc->state = DOSEEK; break; case DOSEEK: if (bp->b_cylin == fd->track) { fdc->state = SEEKCOMPLETE; break; } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fd->fdsu, bp->b_cylin * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdcu)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = bp->b_cylin * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; #ifdef EPSON_NRDISK if (fdu == nrdu) st3 = NE7_ST3_T0; #endif /* EPSON_NRDISK */ if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } } #ifdef EPSON_NRDISK if (fdu == nrdu) cyl = descyl; #endif if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); return(retrier(fdcu)); } } fd->track = bp->b_cylin; if(format) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef PC98 pc98_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, #else isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, #endif format ? bp->b_bcount : fdblk, fdc->dmachan); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ fdc->retry = 6; /* reset the beast */ return(retrier(fdcu)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if(format) { /* formatting */ if(fd_cmd(fdcu, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ fdc->retry = 6; return(retrier(fdcu)); } } else { if (fd_cmd(fdcu, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ fdc->retry = 6; return(retrier(fdcu)); } } fdc->state = IOCOMPLETE; timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ #ifdef EPSON_NRDISK } else { nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk); nrd_LED_on(); nrd_addrset(fdblk * nrdblkn); while (!nrd_check_ready()) DELAY(1); if (read) epson_insw(P_NRD_DATA, bp->b_un.b_addr + fd->skip, fdblk / sizeof(short)); else epson_outsw(P_NRD_DATA, bp->b_un.b_addr + fd->skip, (format ? bp->b_bcount : fdblk) / sizeof(short)); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if (nrdsec++ >= nrd_sec()) nrdaddr = (nrd_t)(fd->track * 8 + head * 4); nrdsec = sec; fdc->state = IOCOMPLETE; } #endif case IOCOMPLETE: /* IO DONE, post-analyze */ #ifdef EPSON_NRDISK if (fdu != nrdu) untimeout(fd_timeout, (caddr_t)fdcu); #else untimeout(fd_timeout, (caddr_t)fdcu); #endif if (fd_read_status(fdc, fd->fdsu)) { if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return retrier(fdcu); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef PC98 pc98_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, #else isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, #endif format ? bp->b_bcount : fdblk, fdc->dmachan); #ifdef EPSON_NRDISK } else nrd_LED_off(); #endif /* EPSON_NRDISK */ if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; #ifdef EPSON_NRDISK nrdblkn = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; #endif bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; bp->b_resid = 0; TAILQ_REMOVE(&fdc->head, bp, b_act); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return(1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: /* XXX clear the fdc results from the last reset, if any. */ { int i; for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if(fd_cmd(fdcu, 2, NE7CMD_RECAL, fdu, 0)) /* Recalibrate Function */ { /* arrgl */ fdc->retry = 6; return(retrier(fdcu)); } fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); #ifdef EPSON_NRDISK if (fdu == nrdu) { st0 = NE7_ST0_IC_NT; cyl = 0; } #endif if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } /* * since the controller was off, it has lost its * idea about the current track it were; thus, * recalibrate the bastard */ fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("fdc%d: Unexpected FD int->", fdcu); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%lx %lx %lx %lx %lx %lx %lx ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return(0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return(0); } /*XXX confusing: some branches return immediately, others end up here*/ return(1); /* Come back immediatly to new state */ } static int retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; register struct buf *bp; bp = TAILQ_FIRST(&fdc->head); if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) goto fail; switch(fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %ld hd %ld sec %ld)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid = bp->b_bcount - fdc->fd->skip; TAILQ_REMOVE(&fdc->head, bp, b_act); fdc->fd->skip = 0; biodone(bp); fdc->state = FINDWORK; fdc->fd = (fd_p) 0; fdc->fdu = -1; /* XXX abort current command, if any. */ return(1); } fdc->retry++; return(1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ p->p_flag |= P_PHYSIO; bzero((void *)bp, sizeof(struct buf)); bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; bp->b_proc = p; bp->b_dev = dev; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; /* now do the format */ fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if(rv == EWOULDBLOCK) break; } splx(s); if(rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } if(bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ p->p_flag &= ~P_PHYSIO; free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ int fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = &fd_data[fdu]; size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); /* check for a tape ioctl */ if (type & F_TAPE_TYPE) return ftioctl(dev, cmd, addr, flag, p); #endif fdblk = 128 << fd->ft->secsize; #ifdef PC98 pc98_fd_check_ready(fdu); #endif switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer); break; case FD_FORM: if((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if(((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if(suser(p->p_ucred, &p->p_acflag) != 0) return EPERM; *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd_data[FDUNIT(minor(dev))].options; break; case FD_SOPTS: /* set drive options */ fd_data[FDUNIT(minor(dev))].options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } static fd_devsw_installed = 0; static void fd_drvinit(void *notused ) { - dev_t dev; if( ! fd_devsw_installed ) { - dev = makedev(CDEV_MAJOR, 0); - cdevsw_add(&dev,&fd_cdevsw, NULL); - dev = makedev(BDEV_MAJOR, 0); - bdevsw_add(&dev,&fd_bdevsw, NULL); + bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_bdevsw); fd_devsw_installed = 1; } } SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL) #endif /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/pc98/pc98/if_edreg.h =================================================================== Property changes on: head/sys/pc98/pc98/if_edreg.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/fd.c =================================================================== --- head/sys/pc98/pc98/fd.c (revision 17349) +++ head/sys/pc98/pc98/fd.c (revision 17350) @@ -1,2492 +1,2482 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.1.1.1 1996/06/14 10:04:43 asami Exp $ + * $Id: fd.c,v 1.2 1996/07/23 07:46:11 asami Exp $ * */ #include "ft.h" #if NFT < 1 #undef NFDC #endif #include "fd.h" #if NFDC > 0 #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #include #else #include #include #include #include #include #endif #include #if NFT > 0 #include #ifdef PC98 #include #else #include #endif #endif #ifdef DEVFS #include #endif static int fd_goaway(struct kern_devconf *, int); static int fdc_goaway(struct kern_devconf *, int); static int fd_externalize(struct kern_devconf *, struct sysctl_req *); /* * Templates for the kern_devconf structures used when we attach. */ static struct kern_devconf kdc_fd[NFD] = { { 0, 0, 0, /* filled in by kern_devconf.c */ "fd", 0, { MDDT_DISK, 0 }, fd_externalize, 0, fd_goaway, DISK_EXTERNALLEN, 0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "floppy disk", DC_CLS_DISK /* class */ } }; struct kern_devconf kdc_fdc[NFDC] = { { 0, 0, 0, /* filled in by kern_devconf.c */ #ifdef PC98 "fdc", 0, { MDDT_PC98, 0, "bio" }, pc98_generic_externalize, 0, fdc_goaway, PC98_EXTERNALLEN, #else "fdc", 0, { MDDT_ISA, 0, "bio" }, isa_generic_externalize, 0, fdc_goaway, ISA_EXTERNALLEN, #endif 0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "floppy disk/tape controller", DC_CLS_MISC /* class */ } }; static inline void fd_registerdev(int ctlr, int unit) { if(unit != 0) kdc_fd[unit] = kdc_fd[0]; kdc_fd[unit].kdc_unit = unit; kdc_fd[unit].kdc_parent = &kdc_fdc[ctlr]; kdc_fd[unit].kdc_parentdata = 0; dev_attach(&kdc_fd[unit]); } static inline void #ifdef PC98 fdc_registerdev(struct pc98_device *dvp) #else fdc_registerdev(struct isa_device *dvp) #endif { int unit = dvp->id_unit; if(unit != 0) kdc_fdc[unit] = kdc_fdc[0]; kdc_fdc[unit].kdc_unit = unit; #ifdef PC98 kdc_fdc[unit].kdc_parent = &kdc_nec0; #else kdc_fdc[unit].kdc_parent = &kdc_isa0; #endif kdc_fdc[unit].kdc_parentdata = dvp; dev_attach(&kdc_fdc[unit]); } static int fdc_goaway(struct kern_devconf *kdc, int force) { if(force) { dev_detach(kdc); return 0; } else { return EBUSY; /* XXX fix */ } } static int fd_goaway(struct kern_devconf *kdc, int force) { dev_detach(kdc); return 0; } #define b_cylin b_resid /* XXX now spelled b_cylinder elsewhere */ /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX /* * this biotab field doubles as a field for the physical unit number * on the controller */ #define id_physid id_scsiid /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #ifdef PC98 #define NUMTYPES 5 #define NUMDENS NUMTYPES #else #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) #endif /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #ifdef PC98 #define FDT_NONE 0 /* none present */ #define FDT_12M 1 /* 1M/640K FDD */ #define FDT_144M 2 /* 1.44M/1M/640K FDD */ #define FD_1200 1 #define FD_1232 2 #define FD_720 3 #define FD_640 4 #define FD_1440 5 #else #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_1480in5_25 9 #define FD_1440in5_25 10 #define FD_820in5_25 11 #define FD_800in5_25 12 #define FD_720in5_25 13 #define FD_360in5_25 14 #endif static struct fd_type fd_types[NUMTYPES] = { #ifdef PC98 { 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2 meg HD floppy */ { 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.2 meg HD floppy 1024/sec */ { 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720k floppy in 1.2meg drive */ { 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640k floppy in 1.2meg drive */ { 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44 meg HD 3.5in floppy */ #else { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ #endif }; #ifdef PC98 #define DRVS_PER_CTLR 4 /* 4 floppies */ #else #define DRVS_PER_CTLR 2 /* 2 floppies */ #endif /***********************************************************************\ * Per controller structure. * \***********************************************************************/ struct fdc_data fdc_data[NFDC]; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ static struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ int dkunit; /* disk stats unit number */ #ifdef DEVFS void *bdevs[1 + NUMDENS + MAXPARTITIONS]; void *cdevs[1 + NUMDENS + MAXPARTITIONS]; #endif #ifdef PC98 int pc98_trans; #endif } fd_data[NFD]; #ifdef EPSON_NRDISK typedef unsigned int nrd_t; #define P_NRD_ADDRH 0xc24 #define P_NRD_ADDRM 0xc22 #define P_NRD_ADDRL 0xc20 #define P_NRD_CHECK 0xc20 #define P_NRD_DATA 0xc26 #define P_NRD_LED 0xc36 #define B_NRD_CHK 0x80 #define B_NRD_LED 0x40 #define A_NRD_INFO 0x2 #define A_NRD_BASE 0x400 #define NRD_STATUS 0x0 #define NRD_ST0_HD 0x04 static fdu_t nrdu=-1; static int nrdsec=0; static nrd_t nrdblkn=0; static nrd_t nrdaddr=0x0; #define nrd_check_ready() ({ \ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \ }) #define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED) #define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED) #define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff)) #define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff)) #define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff)) #define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff)) #define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE)) static inline void nrd_addr(addr) nrd_t addr; { epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f)); epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff)); epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff)); } static inline u_short nrd_info(addr) nrd_t addr; { u_short tmp; nrd_addr(addr); outb(0x43f, 0x42); tmp = (short)inw(P_NRD_DATA); outb(0x43f, 0x40); return ((u_short)tmp); } #endif /* EPSON_NRDISK */ /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ #if NFT > 0 int ftopen(dev_t, int); int ftintr(ftu_t ftu); int ftclose(dev_t, int); void ftstrategy(struct buf *); int ftioctl(dev_t, int, caddr_t, int, struct proc *); int ftdump(dev_t); int ftsize(dev_t); #ifdef PC98 int ftattach(struct pc98_device *, struct pc98_device *, int); #else int ftattach(struct isa_device *, struct isa_device *, int); #endif #endif /* autoconfig functions */ #ifdef PC98 static int fdprobe(struct pc98_device *); static int fdattach(struct pc98_device *); #else static int fdprobe(struct isa_device *); static int fdattach(struct isa_device *); #endif /* needed for ft driver, thus exported */ int in_fdc(fdcu_t); int out_fdc(fdcu_t, int); /* internal functions */ static void set_motor(fdcu_t, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(fdu_t); static void fdc_reset(fdc_p); static int fd_in(fdcu_t, int *); static void fdstart(fdcu_t); static timeout_t fd_timeout; static timeout_t fd_pseudointr; static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); static int fdformat(dev_t, struct fd_formb *, struct proc *); #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #ifdef DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT" }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* DEBUG */ /* autoconfig structure */ #ifdef PC98 struct pc98_driver fdcdriver = { #else struct isa_driver fdcdriver = { #endif fdprobe, fdattach, "fdc", }; static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 #define BDEV_MAJOR 2 -extern struct cdevsw fd_cdevsw; +static struct cdevsw fd_cdevsw; static struct bdevsw fd_bdevsw = { Fdopen, fdclose, fdstrategy, fdioctl, /*2*/ nodump, nopsize, 0, "fd", &fd_cdevsw, -1 }; -static struct cdevsw fd_cdevsw = - { Fdopen, fdclose, rawread, rawwrite, /*9*/ - fdioctl, nostop, nullreset, nodevtotty, - seltrue, nommap, fdstrategy, "fd", - &fd_bdevsw, -1 }; #ifdef PC98 static struct pc98_device *fdcdevs[NFDC]; #else static struct isa_device *fdcdevs[NFDC]; #endif /* * Provide hw.devconf information. */ static int fd_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { return disk_externalize(fd_data[kdc->kdc_unit].fdsu, req); } static int fdc_err(fdcu_t fdcu, const char *s) { fdc_data[fdcu].fdc_errs++; if(s) { if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX) printf("fdc%d: %s", fdcu, s); else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX) printf("fdc%d: too many errors, not logging any more\n", fdcu); } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(fdcu_t fdcu, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdcu, va_arg(ap, int)) < 0) { char msg[50]; sprintf(msg, "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdcu, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdcu, ptr) < 0) { char msg[50]; sprintf(msg, "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdcu, msg); } } return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc->fdcu, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int st0, cyl; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8; else nrdaddr = 0x0; *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS; *cylp = nrd_trac(); } else { #endif /* EPSON_NRDISK */ int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc->fdcu, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc->fdcu, &cyl) < 0) { return fdc_err(fdc->fdcu, "can't get cyl num\n"); } if (cylp) *cylp = cyl; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc_status wants u_longs and * fd_in() gives ints. */ int status; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { switch (i) { case 0: fdc->status[i] = nrd_head() ? NRD_ST0_HD : NRD_STATUS; break; case 1: fdc->status[i] = NRD_STATUS; break; case 2: fdc->status[i] = NRD_STATUS; break; case 3: fdc->status[i] = nrd_trac(); break; case 4: fdc->status[i] = nrd_head(); break; case 5: fdc->status[i] = nrdsec; break; case 6: fdc->status[i] = nrd_secsize(); break; } ret = 0; } else { #endif /* EPSON_NRDISK */ ret = fd_in(fdc->fdcu, &status); fdc->status[i] = status; if (ret != 0) break; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ #ifdef PC98 static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */ static int pc98_trans_prev = 0; static void set_density(fdcu_t, fdu_t); static int pc98_fd_check_ready(fdu_t); static void set_density(fdcu, fdu) fdcu_t fdcu; fdu_t fdu; { /* always motor on */ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC); DELAY(100); outb(fdc_data[fdcu].baseport + FDOUT, FDO_RST | FDO_DMAE); /* in the case of note W, always inhibit 100ms timer */ } static int pc98_fd_check_ready(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; fdcu_t fdcu = fd->fdc->fdcu; int retry = 0; #ifdef EPSON_NRDISK if (fdu == nrdu) { if (nrd_check_ready()) return 0; else return -1; } #endif while (retry++ < 30000) { set_motor(fdcu, fd->fdsu, TURNON); out_fdc(fdcu, NE7CMD_SENSED); /* Sense Drive Status */ DELAY(100); out_fdc(fdcu, fdu); /* Drive number */ DELAY(100); if ((in_fdc(fdcu) & NE7_ST3_RD)){ outb(fdc_data[fdcu].baseport + FDOUT, FDO_DMAE | FDO_MTON); DELAY(10); return 0; } } return -1; } #endif /* * probe for existance of controller */ static int #ifdef PC98 fdprobe(struct pc98_device *dev) #else fdprobe(struct isa_device *dev) #endif { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { printf("fdc%d: unit used multiple times\n", fdcu); return 0; } fdcdevs[fdcu] = dev; fdc_data[fdcu].baseport = dev->id_iobase; #ifndef DEV_LKM fdc_registerdev(dev); #endif #ifndef PC98 /* First - lets reset the floppy controller */ outb(dev->id_iobase+FDOUT, 0); DELAY(100); outb(dev->id_iobase+FDOUT, FDO_FRST); #endif /* see if it can handle a command */ #ifdef PC98 if (fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0)) #else if (fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) #endif { return(0); } kdc_fdc[fdcu].kdc_state = DC_IDLE; return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ static int #ifdef PC98 fdattach(struct pc98_device *dev) #else fdattach(struct isa_device *dev) #endif { unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; int fdsu, st0, st3, i; #if NFT > 0 int unithasfd; #endif #ifdef PC98 struct pc98_device *fdup; #else struct isa_device *fdup; #endif int ic_type = 0; #ifdef DEVFS int mynor; int typemynor; int typesize; #endif fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; #ifdef PC98 fdc->dmachan = 2; if (fdc->dmachan != dev->id_drq) { dev->id_drq = fdc->dmachan; printf(" [dma is changed to #%d]", fdc->dmachan); } /* Acquire the DMA channel forever, The driver will do the rest */ pc98_dma_acquire(fdc->dmachan); pc98_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; fdc_reset(fdc); #else fdc->dmachan = dev->id_drq; /* Acquire the DMA channel forever, The driver will do the rest */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); #endif TAILQ_INIT(&fdc->head); /* check for each floppy drive */ #ifdef PC98 for (fdup = pc98_biotab_fdc; fdup->id_driver != 0; fdup++) { #else for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) { #endif if (fdup->id_iobase != dev->id_iobase) continue; fdu = fdup->id_unit; fd = &fd_data[fdu]; if (fdu >= (NFD+NFT)) continue; fdsu = fdup->id_physid; /* look up what bios thinks we have */ switch (fdu) { #ifdef PC98 case 0: case 1: case 2: case 3: if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fdu) & 0x01) fdt = FDT_144M; #ifdef EPSON_NRDISK else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fdu) & 0x01) { if (nrd_check_ready()) { nrd_LED_on(); nrdu = fdu; } else fdt = FDT_NONE; } } } #else /* !EPSON_NRDISK */ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fdu) & 0x01) fdt = FDT_NONE; } } #endif /* EPSON_NRDISK */ else fdt = FDT_NONE; break; default: fdt = FDT_NONE; break; #else case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; #endif } /* is there a unit? */ #ifdef PC98 if ((fdt == FDT_NONE) #else if ((fdt == RTCFDT_NONE) #endif #if NFT > 0 || (fdsu >= DRVS_PER_CTLR)) { #else ) { #ifdef PC98 fd->fdc = fdc; #endif fd->type = NO_TYPE; #endif #if NFT > 0 /* If BIOS says no floppy, or > 2nd device */ /* Probe for and attach a floppy tape. */ /* Tell FT if there was already a disk */ /* with this unit number found. */ unithasfd = 0; if (fdu < NFD && fd->type != NO_TYPE) unithasfd = 1; if (ftattach(dev, fdup, unithasfd)) continue; if (fdsu < DRVS_PER_CTLR) fd->type = NO_TYPE; #endif continue; } #ifdef PC98 kdc_fdc[fdcu].kdc_description = "NEC 765 floppy disk/tape controller"; #else /* select it */ set_motor(fdcu, fdsu, TURNON); DELAY(1000000); /* 1 sec */ if (ic_type == 0 && fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { printf("fdc%d: ", fdcu); ic_type = (u_char)ic_type; switch( ic_type ) { case 0x80: printf("NEC 765\n"); fdc->fdct = FDC_NE765; kdc_fdc[fdcu].kdc_description = "NEC 765 floppy disk/tape controller"; break; case 0x81: printf("Intel 82077\n"); fdc->fdct = FDC_I82077; kdc_fdc[fdcu].kdc_description = "Intel 82077 floppy disk/tape controller"; break; case 0x90: printf("NEC 72065B\n"); fdc->fdct = FDC_NE72065; kdc_fdc[fdcu].kdc_description = "NEC 72065B floppy disk/tape controller"; break; default: printf("unknown IC type %02x\n", ic_type); fdc->fdct = FDC_UNKNOWN; break; } } if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ (void)fd_sense_int(fdc, 0, 0); } } for(i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0? 1000000: 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdcu, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; #endif fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; printf("fd%d: ", fdu); fd_registerdev(fdcu, fdu); switch (fdt) { #ifdef PC98 case FDT_12M: #ifdef EPSON_NRDISK if (fdu == nrdu) { printf("EPSON RAM DRIVE\n"); nrd_LED_off(); } else printf("1M/640M FDD\n"); #else /* !EPSON_NRDISK */ printf("1M/640K FDD\n"); #endif /* EPSON_NRDISK */ fd->type = FD_1200; fd->pc98_trans = 0; kdc_fd[fdu].kdc_description = "1M/640K floppy disk drive"; #ifdef DEVFS sprintf(name,"rfd%d.1200",fdu); #endif /* DEVFS */ break; case FDT_144M: printf("1.44M FDD\n"); fd->type = FD_1200; fd->pc98_trans = 0; outb(0x4be, (fdu << 5) | 0x10); kdc_fd[fdu].kdc_description = "1.44MB (1440K) 3.5in floppy disk drive"; #ifdef DEVFS sprintf(name,"rfd%d.1440",fdu); #endif /* DEVFS */ break; #else case RTCFDT_12M: printf("1.2MB 5.25in\n"); fd->type = FD_1200; kdc_fd[fdu].kdc_description = "1.2MB (1200K) 5.25in floppy disk drive"; break; case RTCFDT_144M: printf("1.44MB 3.5in\n"); fd->type = FD_1440; kdc_fd[fdu].kdc_description = "1.44MB (1440K) 3.5in floppy disk drive"; break; case RTCFDT_288M: case RTCFDT_288M_1: printf("2.88MB 3.5in - 1.44MB mode\n"); fd->type = FD_1440; kdc_fd[fdu].kdc_description = "2.88MB (2880K) 3.5in floppy disk drive in 1.44 mode"; break; case RTCFDT_360K: printf("360KB 5.25in\n"); fd->type = FD_360; kdc_fd[fdu].kdc_description = "360KB 5.25in floppy disk drive"; break; case RTCFDT_720K: printf("720KB 3.5in\n"); fd->type = FD_720; kdc_fd[fdu].kdc_description = "720KB 3.5in floppy disk drive"; break; #endif default: printf("unknown\n"); fd->type = NO_TYPE; dev_detach(&kdc_fd[fdu]); continue; } kdc_fd[fdu].kdc_state = DC_IDLE; #ifdef DEVFS mynor = fdu << 6; fd->bdevs[0] = devfs_add_devswf(&fd_bdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d", fdu); fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d", fdu); for (i = 1; i < 1 + NUMDENS; i++) { /* * XXX this and the lookup in Fdopen() should be * data driven. */ switch (fd->type) { case FD_360: if (i != FD_360) continue; break; case FD_720: if (i != FD_720 && i != FD_800 && i != FD_820) continue; break; case FD_1200: if (i != FD_360 && i != FD_720 && i != FD_800 && i != FD_820 && i != FD_1200 && i != FD_1440 && i != FD_1480) continue; break; case FD_1440: if (i != FD_720 && i != FD_800 && i != FD_820 && i != FD_1200 && i != FD_1440 && i != FD_1480 && i != FD_1720) continue; break; } typemynor = mynor | i; typesize = fd_types[i - 1].size / 2; /* * XXX all these conversions give bloated code and * confusing names. */ if (typesize == 1476) typesize = 1480; if (typesize == 1722) typesize = 1720; fd->bdevs[i] = devfs_add_devswf(&fd_bdevsw, typemynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "fd%d.%d", fdu, typesize); fd->cdevs[i] = devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rfd%d.%d", fdu, typesize); } for (i = 0; i < MAXPARTITIONS; i++) { fd->bdevs[1 + NUMDENS + i] = devfs_link(fd->bdevs[0], "fd%d%c", fdu, 'a' + i); fd->cdevs[1 + NUMDENS + i] = devfs_link(fd->cdevs[0], "rfd%d%c", fdu, 'a' + i); } #endif /* DEVFS */ if (dk_ndrive < DK_NDRIVE) { sprintf(dk_names[dk_ndrive], "fd%d", fdu); fd->dkunit = dk_ndrive++; /* * XXX assume rate is FDC_500KBPS. */ dk_wpms[dk_ndrive] = 500000 / 8 / 2; } else { fd->dkunit = -1; } } return (1); } /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(fdcu_t fdcu, int fdsu, int turnon) { int fdout = fdc_data[fdcu].fdout; int needspecify = 0; #ifdef PC98 outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC); DELAY(10); fdout = FDO_DMAE|FDO_MTON; #else if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } #endif outb(fdc_data[fdcu].baseport+FDOUT, fdout); DELAY(10); fdc_data[fdcu].fdout = fdout; #ifndef PC98 kdc_fdc[fdcu].kdc_state = (fdout & FDO_FRST)? DC_BUSY: DC_IDLE; #endif TRACE1("[0x%x->FDOUT]", fdout); if(needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ #ifdef PC98 (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif } } static void fd_turnoff(void *arg1) { fdu_t fdu = (fdu_t)arg1; int s; fd_p fd = fd_data + fdu; TRACE1("[fd%d: turnoff]", fdu); /* * Don't turn off the motor yet if the drive is active. * XXX shouldn't even schedule turnoff until drive is inactive * and nothing is queued on it. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) { timeout(fd_turnoff, arg1, 4 * hz); return; } s = splbio(); fd->flags &= ~FD_MOTOR; set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *arg1) { fdu_t fdu = (fdu_t)arg1; int s; fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdintr(fd->fdc->fdcu); } splx(s); } static void fd_turnon(fdu_t fdu) { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { fdcu_t fdcu = fdc->fdcu; /* Try a reset, keep motor on */ #ifdef PC98 set_density(fdcu, 0); if (pc98_machine_type & M_EPSON_PC98) outb(fdc->baseport + FDOUT, 0xe8); else outb(fdc->baseport + FDOUT, 0xd8); DELAY(200); outb(fdc->baseport + FDOUT, 0x18); DELAY(10); #else outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); outb(fdc->baseport + FDOUT, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); #endif /* XXX after a reset, silently believe the FDC will accept commands */ #ifdef PC98 (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(fdcu_t fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 1000000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) { if (i == NE7_RQM) return fdc_err(fdcu, "ready for output in input\n"); } if (j <= 0) return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); #ifdef DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else return inb(baseport+FDDATA); #endif } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(fdcu_t fdcu, int *ptr) { int baseport = fdc_data[fdcu].baseport; int i, j = 1000000; while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) { DELAY(10); if (i == NE7_RQM) return fdc_err(fdcu, "ready for output in input\n"); } if (j <= 0) return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0); #ifdef DEBUG i = inb(baseport+FDDATA); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else i = inb(baseport+FDDATA); if (ptr) *ptr = i; return 0; #endif } int out_fdc(fdcu_t fdcu, int x) { int baseport = fdc_data[fdcu].baseport; int i; /* Check that the direction bit is set */ i = 1000000; while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0) DELAY(10); if (i <= 0) return fdc_err(fdcu, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 1000000; while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0) DELAY(10); if (i <= 0) return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ outb(baseport+FDDATA, x); DELAY(10); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fdc_p fdc; #if NFT > 0 /* check for a tape open */ if (type & F_TAPE_TYPE) return(ftopen(dev, flags)); #endif /* check bounds */ if (fdu >= NFD) return(ENXIO); fdc = fd_data[fdu].fdc; if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE)) return(ENXIO); if (type > NUMDENS) return(ENXIO); #ifdef PC98 if (pc98_fd_check_ready(fdu) == -1) return(EIO); #endif if (type == 0) type = fd_data[fdu].type; #ifndef PC98 else { if (type != fd_data[fdu].type) { switch (fd_data[fdu].type) { case FD_360: return(ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 ) return(ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 ) return(ENXIO); break; } } } #endif fd_data[fdu].ft = fd_types + type - 1; fd_data[fdu].flags |= FD_OPEN; kdc_fd[fdu].kdc_state = DC_BUSY; return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); #if NFT > 0 int type = FDTYPE(minor(dev)); if (type & F_TAPE_TYPE) return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; fd_data[fdu].options &= ~FDOPT_NORETRY; kdc_fd[fdu].kdc_state = DC_IDLE; return(0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct buf *bp) { long nblocks, blknum; int s; fdcu_t fdcu; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->b_dev)); fd = &fd_data[fdu]; fdc = fd->fdc; fdcu = fdc->fdcu; #if NFT > 0 if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { /* ft tapes do not (yet) support strategy i/o */ bp->b_error = ENODEV; bp->b_flags |= B_ERROR; goto bad; } /* check for controller already busy with tape */ if (fdc->flags & FDC_TAPE_BUSY) { bp->b_error = EBUSY; bp->b_flags |= B_ERROR; goto bad; } #endif fdblk = 128 << (fd->ft->secsize); if (!(bp->b_flags & B_FORMAT)) { if ((fdu >= NFD) || (bp->b_blkno < 0)) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } if ((bp->b_bcount % fdblk) != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } } /* * Set up block calculations. */ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; #ifdef PC98 #define B_XXX2 0x8000000 if (bp->b_flags & B_XXX2) { blknum *= 2; bp->b_blkno *= 2; bp->b_flags &= ~B_XXX2; } #endif if (blknum + (bp->b_bcount / fdblk) > nblocks) { if (blknum == nblocks) { bp->b_resid = bp->b_bcount; } else { bp->b_error = ENOSPC; bp->b_flags |= B_ERROR; } goto bad; } bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); bp->b_pblkno = bp->b_blkno; s = splbio(); tqdisksort(&fdc->head, bp); untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ fdstart(fdcu); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(fdcu_t fdcu) { int s; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) { fdintr(fdcu); } splx(s); } static void fd_timeout(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; int baseport = fdc_data[fdcu].baseport; struct buf *bp; int s; bp = TAILQ_FIRST(&fdc_data[fdcu].head); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! */ s = splbio(); TRACE1("fd%d[fd_timeout()]", fdu); /* See if the controller is still busy (patiently awaiting data) */ if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) { DELAY(5); TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); /* yup, it is; kill it now */ fdc_reset(&fdc_data[fdcu]); printf("fd%d: Operation timeout\n", fdu); } if (bp) { retrier(fdcu); fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; } else { fdc_data[fdcu].fd = (fd_p) 0; fdc_data[fdcu].fdu = -1; fdc_data[fdcu].state = DEVIDLE; } fdintr(fdcu); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; int s; s = splbio(); fdintr(fdcu); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ void fdintr(fdcu_t fdcu) { fdc_p fdc = fdc_data + fdcu; #if NFT > 0 fdu_t fdu = fdc->fdu; if (fdc->flags & FDC_TAPE_BUSY) (ftintr(fdu)); else #endif while(fdstate(fdcu, fdc)) ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdcu_t fdcu, fdc_p fdc) { int read, format, head, sec = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; register struct buf *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = TAILQ_FIRST(&fdc->head); if(!bp) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if(fdc->fd) { printf("fd%d: unexpected valid fd pointer\n", fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("fd%d: confused fd pointers\n", fdu); } read = bp->b_flags & B_READ; format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; #ifdef PC98 pc98_trans = fd->ft->trans; if (pc98_trans_prev != pc98_trans) { int i; set_density(fdcu, fdu); for (i = 0; i < 10; i++) { outb(0x5f, 0); outb(0x5f, 0); } pc98_trans_prev = pc98_trans; } if (pc98_trans != fd->pc98_trans) { if (pc98_trans != 1 && (PC98_SYSTEM_PARAMETER(0x5ae) >> fdu) & 0x01) { outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1)); outb(0x5f, 0); outb(0x5f, 0); } fd->pc98_trans = pc98_trans; } #else outb(fdc->baseport+FDCTL, fd->ft->trans); #endif TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return(0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ #ifdef EPSON_NRDISK if (fdu != nrdu) { if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } } #else /* !EPSON_NRDISK */ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } #endif fdc->state = DOSEEK; break; case DOSEEK: if (bp->b_cylin == fd->track) { fdc->state = SEEKCOMPLETE; break; } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fd->fdsu, bp->b_cylin * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdcu)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = bp->b_cylin * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; #ifdef EPSON_NRDISK if (fdu == nrdu) st3 = NE7_ST3_T0; #endif /* EPSON_NRDISK */ if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } } #ifdef EPSON_NRDISK if (fdu == nrdu) cyl = descyl; #endif if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); return(retrier(fdcu)); } } fd->track = bp->b_cylin; if(format) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef PC98 pc98_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, #else isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, #endif format ? bp->b_bcount : fdblk, fdc->dmachan); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ fdc->retry = 6; /* reset the beast */ return(retrier(fdcu)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if(format) { /* formatting */ if(fd_cmd(fdcu, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ fdc->retry = 6; return(retrier(fdcu)); } } else { if (fd_cmd(fdcu, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ fdc->retry = 6; return(retrier(fdcu)); } } fdc->state = IOCOMPLETE; timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ #ifdef EPSON_NRDISK } else { nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk); nrd_LED_on(); nrd_addrset(fdblk * nrdblkn); while (!nrd_check_ready()) DELAY(1); if (read) epson_insw(P_NRD_DATA, bp->b_un.b_addr + fd->skip, fdblk / sizeof(short)); else epson_outsw(P_NRD_DATA, bp->b_un.b_addr + fd->skip, (format ? bp->b_bcount : fdblk) / sizeof(short)); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if (nrdsec++ >= nrd_sec()) nrdaddr = (nrd_t)(fd->track * 8 + head * 4); nrdsec = sec; fdc->state = IOCOMPLETE; } #endif case IOCOMPLETE: /* IO DONE, post-analyze */ #ifdef EPSON_NRDISK if (fdu != nrdu) untimeout(fd_timeout, (caddr_t)fdcu); #else untimeout(fd_timeout, (caddr_t)fdcu); #endif if (fd_read_status(fdc, fd->fdsu)) { if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return retrier(fdcu); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ #ifdef PC98 pc98_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, #else isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, #endif format ? bp->b_bcount : fdblk, fdc->dmachan); #ifdef EPSON_NRDISK } else nrd_LED_off(); #endif /* EPSON_NRDISK */ if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; #ifdef EPSON_NRDISK nrdblkn = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; #endif bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; bp->b_resid = 0; TAILQ_REMOVE(&fdc->head, bp, b_act); biodone(bp); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return(1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: /* XXX clear the fdc results from the last reset, if any. */ { int i; for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if(fd_cmd(fdcu, 2, NE7CMD_RECAL, fdu, 0)) /* Recalibrate Function */ { /* arrgl */ fdc->retry = 6; return(retrier(fdcu)); } fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); #ifdef EPSON_NRDISK if (fdu == nrdu) { st0 = NE7_ST0_IC_NT; cyl = 0; } #endif if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } /* * since the controller was off, it has lost its * idea about the current track it were; thus, * recalibrate the bastard */ fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("fdc%d: Unexpected FD int->", fdcu); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%lx %lx %lx %lx %lx %lx %lx ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return(0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return(0); } /*XXX confusing: some branches return immediately, others end up here*/ return(1); /* Come back immediatly to new state */ } static int retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; register struct buf *bp; bp = TAILQ_FIRST(&fdc->head); if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) goto fail; switch(fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->b_dev = sav_b_dev; if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %ld hd %ld sec %ld)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid = bp->b_bcount - fdc->fd->skip; TAILQ_REMOVE(&fdc->head, bp, b_act); fdc->fd->skip = 0; biodone(bp); fdc->state = FINDWORK; fdc->fd = (fd_p) 0; fdc->fdu = -1; /* XXX abort current command, if any. */ return(1); } fdc->retry++; return(1); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct buf *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); if(bp == 0) return ENOBUFS; /* * keep the process from being swapped */ p->p_flag |= P_PHYSIO; bzero((void *)bp, sizeof(struct buf)); bp->b_flags = B_BUSY | B_PHYS | B_FORMAT; bp->b_proc = p; bp->b_dev = dev; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; /* now do the format */ fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); while(!(bp->b_flags & B_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if(rv == EWOULDBLOCK) break; } splx(s); if(rv == EWOULDBLOCK) { /* timed out */ rv = EIO; biodone(bp); } if(bp->b_flags & B_ERROR) rv = bp->b_error; /* * allow the process to be swapped */ p->p_flag &= ~P_PHYSIO; free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ int fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = &fd_data[fdu]; size_t fdblk; struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); /* check for a tape ioctl */ if (type & F_TAPE_TYPE) return ftioctl(dev, cmd, addr, flag, p); #endif fdblk = 128 << fd->ft->secsize; #ifdef PC98 pc98_fd_check_ready(fdu); #endif switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, fdstrategy, (struct disklabel *)buffer); break; case FD_FORM: if((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if(((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if(suser(p->p_ucred, &p->p_acflag) != 0) return EPERM; *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd_data[FDUNIT(minor(dev))].options; break; case FD_SOPTS: /* set drive options */ fd_data[FDUNIT(minor(dev))].options = *(int *)addr; break; default: error = ENOTTY; break; } return (error); } static fd_devsw_installed = 0; static void fd_drvinit(void *notused ) { - dev_t dev; if( ! fd_devsw_installed ) { - dev = makedev(CDEV_MAJOR, 0); - cdevsw_add(&dev,&fd_cdevsw, NULL); - dev = makedev(BDEV_MAJOR, 0); - bdevsw_add(&dev,&fd_bdevsw, NULL); + bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &fd_bdevsw); fd_devsw_installed = 1; } } SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL) #endif /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/pc98/pc98/if_ed98.h =================================================================== --- head/sys/pc98/pc98/if_ed98.h (nonexistent) +++ head/sys/pc98/pc98/if_ed98.h (revision 17350) @@ -0,0 +1,805 @@ +/* + * Copyright (c) KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * PC-9801 specific definitions for National Semiconductor DP8390 NIC + */ +#ifndef __PC98_PC98_IF_ED98_H__ +#define __PC98_PC98_IF_ED98_H__ + +/* PC98 only */ +#ifndef PC98 +#error Why you include if_ed98.h? +#endif + +static void pc98_set_register __P((struct pc98_device *dev, + int unit, int type)); + +/* + * Vendor types + */ +#define ED_VENDOR_MISC 0xf0 /* others */ + +/* + * Register offsets/total + */ +#ifdef ED_NOVELL_NIC_OFFSET +#undef ED_NOVELL_NIC_OFFSET +#endif +#define ED_NOVELL_NIC_OFFSET ed_novell_nic_offset[unit] +#ifdef ED_NOVELL_ASIC_OFFSET +#undef ED_NOVELL_ASIC_OFFSET +#endif +#define ED_NOVELL_ASIC_OFFSET ed_novell_asic_offset[unit] + +/* + * Remote DMA data register; for reading or writing to the NIC mem + * via programmed I/O (offset from ASIC base) + */ +#ifdef ED_NOVELL_DATA +#undef ED_NOVELL_DATA +#endif +#define ED_NOVELL_DATA ed_novell_data[unit] + +/* + * Reset register; reading from this register causes a board reset + */ +#ifdef ED_NOVELL_RESET +#undef ED_NOVELL_RESET +#endif +#define ED_NOVELL_RESET ed_novell_reset[unit] + +/* + * Card type + * + * Type Card + * 0 Allied Telesis CenterCom LA-98-T + * 1 MELCO LPC-TJ, LPC-TS / IO-DATA PCLA/T + * 2 PLANET SMART COM 98 EN-2298 / ELECOM LANEED LD-BDN[123]A + * 3 MELCO EGY-98 + * 4 MELCO LGY-98, IND-SP, IND-SS / MACNICA NE2098(XXX) + * 5 ICM DT-ET-25, DT-ET-T5, IF-2766ET, IF-2771ET / + * D-Link DE-298P{T,CAT}, DE-298{T,TP,CAT} + * 6 Allied Telesis SIC-98 + * 8 NEC PC-9801-108 + * 9 IO-DATA LA-98 + */ +#define ED_TYPE98_BASE 0x10 + +#define ED_TYPE98_GENERIC 0x10 +#define ED_TYPE98_LPC 0x11 +#define ED_TYPE98_BDN 0x12 +#define ED_TYPE98_EGY 0x13 +#define ED_TYPE98_LGY 0x14 +#define ED_TYPE98_ICM 0x15 +#define ED_TYPE98_SIC 0x16 +#define ED_TYPE98_108 0x18 +#define ED_TYPE98_LA98 0x19 + +#define ED_TYPE98(x) (((x->id_flags & 0xffff0000) >> 16) | ED_TYPE98_BASE) + +/* + * Page 0 register offsets + */ +#undef ED_P0_CR +#define ED_P0_CR edp[unit][0x00] + +#undef ED_P0_CLDA0 +#define ED_P0_CLDA0 edp[unit][0x01] +#undef ED_P0_PSTART +#define ED_P0_PSTART edp[unit][0x01] + +#undef ED_P0_CLDA1 +#define ED_P0_CLDA1 edp[unit][0x02] +#undef ED_P0_PSTOP +#define ED_P0_PSTOP edp[unit][0x02] + +#undef ED_P0_BNRY +#define ED_P0_BNRY edp[unit][0x03] + +#undef ED_P0_TSR +#define ED_P0_TSR edp[unit][0x04] +#undef ED_P0_TPSR +#define ED_P0_TPSR edp[unit][0x04] + +#undef ED_P0_NCR +#define ED_P0_NCR edp[unit][0x05] +#undef ED_P0_TBCR0 +#define ED_P0_TBCR0 edp[unit][0x05] + +#undef ED_P0_FIFO +#define ED_P0_FIFO edp[unit][0x06] +#undef ED_P0_TBCR1 +#define ED_P0_TBCR1 edp[unit][0x06] + +#undef ED_P0_ISR +#define ED_P0_ISR edp[unit][0x07] + +#undef ED_P0_CRDA0 +#define ED_P0_CRDA0 edp[unit][0x08] +#undef ED_P0_RSAR0 +#define ED_P0_RSAR0 edp[unit][0x08] + +#undef ED_P0_CRDA1 +#define ED_P0_CRDA1 edp[unit][0x09] +#undef ED_P0_RSAR1 +#define ED_P0_RSAR1 edp[unit][0x09] + +#undef ED_P0_RBCR0 +#define ED_P0_RBCR0 edp[unit][0x0a] + +#undef ED_P0_RBCR1 +#define ED_P0_RBCR1 edp[unit][0x0b] + +#undef ED_P0_RSR +#define ED_P0_RSR edp[unit][0x0c] +#undef ED_P0_RCR +#define ED_P0_RCR edp[unit][0x0c] + +#undef ED_P0_CNTR0 +#define ED_P0_CNTR0 edp[unit][0x0d] +#undef ED_P0_TCR +#define ED_P0_TCR edp[unit][0x0d] + +#undef ED_P0_CNTR1 +#define ED_P0_CNTR1 edp[unit][0x0e] +#undef ED_P0_DCR +#define ED_P0_DCR edp[unit][0x0e] + +#undef ED_P0_CNTR2 +#define ED_P0_CNTR2 edp[unit][0x0f] +#undef ED_P0_IMR +#define ED_P0_IMR edp[unit][0x0f] + +/* + * Page 1 register offsets + */ +#undef ED_P1_CR +#define ED_P1_CR edp[unit][0x00] +#undef ED_P1_PAR0 +#define ED_P1_PAR0 edp[unit][0x01] +#undef ED_P1_PAR1 +#define ED_P1_PAR1 edp[unit][0x02] +#undef ED_P1_PAR2 +#define ED_P1_PAR2 edp[unit][0x03] +#undef ED_P1_PAR3 +#define ED_P1_PAR3 edp[unit][0x04] +#undef ED_P1_PAR4 +#define ED_P1_PAR4 edp[unit][0x05] +#undef ED_P1_PAR5 +#define ED_P1_PAR5 edp[unit][0x06] +#undef ED_P1_CURR +#define ED_P1_CURR edp[unit][0x07] +#undef ED_P1_MAR0 +#define ED_P1_MAR0 edp[unit][0x08] +#undef ED_P1_MAR1 +#define ED_P1_MAR1 edp[unit][0x09] +#undef ED_P1_MAR2 +#define ED_P1_MAR2 edp[unit][0x0a] +#undef ED_P1_MAR3 +#define ED_P1_MAR3 edp[unit][0x0b] +#undef ED_P1_MAR4 +#define ED_P1_MAR4 edp[unit][0x0c] +#undef ED_P1_MAR5 +#define ED_P1_MAR5 edp[unit][0x0d] +#undef ED_P1_MAR6 +#define ED_P1_MAR6 edp[unit][0x0e] +#undef ED_P1_MAR7 +#define ED_P1_MAR7 edp[unit][0x0f] + +/* + * Page 2 register offsets + */ +#undef ED_P2_CR +#define ED_P2_CR edp[unit][0x00] +#undef ED_P2_PSTART +#define ED_P2_PSTART edp[unit][0x01] +#undef ED_P2_CLDA0 +#define ED_P2_CLDA0 edp[unit][0x01] +#undef ED_P2_PSTOP +#define ED_P2_PSTOP edp[unit][0x02] +#undef ED_P2_CLDA1 +#define ED_P2_CLDA1 edp[unit][0x02] +#undef ED_P2_RNPP +#define ED_P2_RNPP edp[unit][0x03] +#undef ED_P2_TPSR +#define ED_P2_TPSR edp[unit][0x04] +#undef ED_P2_LNPP +#define ED_P2_LNPP edp[unit][0x05] +#undef ED_P2_ACU +#define ED_P2_ACU edp[unit][0x06] +#undef ED_P2_ACL +#define ED_P2_ACL edp[unit][0x07] +#undef ED_P2_RCR +#define ED_P2_RCR edp[unit][0x0c] +#undef ED_P2_TCR +#define ED_P2_TCR edp[unit][0x0d] +#undef ED_P2_DCR +#define ED_P2_DCR edp[unit][0x0e] +#undef ED_P2_IMR +#define ED_P2_IMR edp[unit][0x0f] + +/* PCCARD */ +#ifdef ED_PC_MISC +#undef ED_PC_MISC +#endif +#define ED_PC_MISC ed_pc_misc[unit] + + +/* LPC-T support */ +#define LPCT_1d0_ON() \ +{ \ + outb(0x2a8e, 0x84); \ + outw(0x4a8e, 0x1d0); \ + outw(0x5a8e, 0x0310); \ +} + +#define LPCT_1d0_OFF() \ +{ \ + outb(0x2a8e, 0xa4); \ + outw(0x4a8e, 0xd0); \ + outw(0x5a8e, 0x0300); \ +} + + +/* register offsets */ +static unsigned int *edp[NED]; +static unsigned int pc98_io_skip[NED]; +static int ed_novell_nic_offset[NED]; +static int ed_novell_asic_offset[NED]; +static int ed_novell_data[NED]; +static int ed_novell_reset[NED]; +static int ed_pc_misc[NED]; + + +/* NE2000, LGY-98, ICM, LPC-T */ +static unsigned int edp_generic[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +/* EGY-98 */ +static unsigned int edp_egy98[16] = { + 0, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, + 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e +}; + +/* LD-BDN */ +static unsigned int edp_bdn98[16] = { + 0x00000, 0x01000, 0x02000, 0x03000, 0x04000, 0x05000, 0x06000, 0x07000, + 0x08000, 0x0a000, 0x0b000, 0x0c000, 0x0d000, 0x0d000, 0x0e000, 0x0f000 +}; + +/* SIC-98 */ +static unsigned int edp_sic98[16] = { + 0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0a00, 0x0c00, 0x0e00, + 0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1a00, 0x1c00, 0x1e00 +}; + +/* IO-DATA LA-98 */ +static unsigned int edp_la98[16] = { + 0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, + 0x8000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000 +}; + +/* NEC PC-9801-108 */ +static unsigned int edp_nec108[16] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, + 0x1000, 0x1002, 0x1004, 0x1006, 0x1008, 0x100a, 0x100c, 0x100e +}; + +static void pc98_set_register(struct pc98_device *dev, int unit, int type) +{ + int adj; + + switch (type) { + case ED_TYPE98_GENERIC: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 1; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0010; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x000f; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_LGY: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 1; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0200; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x0100; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_EGY: + edp[unit] = edp_egy98; + pc98_io_skip[unit] = 2; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = 0x0200; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x0100; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_ICM: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 1; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = 0x0100; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x000f; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_BDN: + edp[unit] = edp_bdn98; + pc98_io_skip[unit] = 0x1000; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0100; + ED_NOVELL_DATA = 0; + ED_NOVELL_RESET = 0xc100; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_SIC: + edp[unit] = edp_sic98; + pc98_io_skip[unit] = 0x200; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x2000; + ED_NOVELL_DATA = 0x00; /* dummy */ + ED_NOVELL_RESET = 0x00; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_LPC: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 0x1; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0100; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x010f; + ED_PC_MISC = 0x108; + break; + + case ED_TYPE98_108: + edp[unit] = edp_nec108; + pc98_io_skip[unit] = 2; + adj = (dev->id_iobase & 0xf000) / 2; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = (0x888 | adj) - dev->id_iobase; + ED_NOVELL_DATA = 0; + ED_NOVELL_RESET = 4; + ED_PC_MISC = 0x18; + break; + + case ED_TYPE98_LA98: + edp[unit] = edp_la98; + pc98_io_skip[unit] = 0x1000; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = 0x100; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0xf000; + ED_PC_MISC = 0x18; + break; + } +} + +#endif /* __PC98_PC98_IF_ED98_H__ */ +/* + * Copyright (c) KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * PC-9801 specific definitions for National Semiconductor DP8390 NIC + */ +#ifndef __PC98_PC98_IF_ED98_H__ +#define __PC98_PC98_IF_ED98_H__ + +/* PC98 only */ +#ifndef PC98 +#error Why you include if_ed98.h? +#endif + +static void pc98_set_register __P((struct pc98_device *dev, + int unit, int type)); + +/* + * Vendor types + */ +#define ED_VENDOR_MISC 0xf0 /* others */ + +/* + * Register offsets/total + */ +#ifdef ED_NOVELL_NIC_OFFSET +#undef ED_NOVELL_NIC_OFFSET +#endif +#define ED_NOVELL_NIC_OFFSET ed_novell_nic_offset[unit] +#ifdef ED_NOVELL_ASIC_OFFSET +#undef ED_NOVELL_ASIC_OFFSET +#endif +#define ED_NOVELL_ASIC_OFFSET ed_novell_asic_offset[unit] + +/* + * Remote DMA data register; for reading or writing to the NIC mem + * via programmed I/O (offset from ASIC base) + */ +#ifdef ED_NOVELL_DATA +#undef ED_NOVELL_DATA +#endif +#define ED_NOVELL_DATA ed_novell_data[unit] + +/* + * Reset register; reading from this register causes a board reset + */ +#ifdef ED_NOVELL_RESET +#undef ED_NOVELL_RESET +#endif +#define ED_NOVELL_RESET ed_novell_reset[unit] + +/* + * Card type + * + * Type Card + * 0 Allied Telesis CenterCom LA-98-T + * 1 MELCO LPC-TJ, LPC-TS / IO-DATA PCLA/T + * 2 PLANET SMART COM 98 EN-2298 / ELECOM LANEED LD-BDN[123]A + * 3 MELCO EGY-98 + * 4 MELCO LGY-98, IND-SP, IND-SS / MACNICA NE2098(XXX) + * 5 ICM DT-ET-25, DT-ET-T5, IF-2766ET, IF-2771ET / + * D-Link DE-298P{T,CAT}, DE-298{T,TP,CAT} + * 6 Allied Telesis SIC-98 + * 8 NEC PC-9801-108 + * 9 IO-DATA LA-98 + */ +#define ED_TYPE98_BASE 0x10 + +#define ED_TYPE98_GENERIC 0x10 +#define ED_TYPE98_LPC 0x11 +#define ED_TYPE98_BDN 0x12 +#define ED_TYPE98_EGY 0x13 +#define ED_TYPE98_LGY 0x14 +#define ED_TYPE98_ICM 0x15 +#define ED_TYPE98_SIC 0x16 +#define ED_TYPE98_108 0x18 +#define ED_TYPE98_LA98 0x19 + +#define ED_TYPE98(x) (((x->id_flags & 0xffff0000) >> 16) | ED_TYPE98_BASE) + +/* + * Page 0 register offsets + */ +#undef ED_P0_CR +#define ED_P0_CR edp[unit][0x00] + +#undef ED_P0_CLDA0 +#define ED_P0_CLDA0 edp[unit][0x01] +#undef ED_P0_PSTART +#define ED_P0_PSTART edp[unit][0x01] + +#undef ED_P0_CLDA1 +#define ED_P0_CLDA1 edp[unit][0x02] +#undef ED_P0_PSTOP +#define ED_P0_PSTOP edp[unit][0x02] + +#undef ED_P0_BNRY +#define ED_P0_BNRY edp[unit][0x03] + +#undef ED_P0_TSR +#define ED_P0_TSR edp[unit][0x04] +#undef ED_P0_TPSR +#define ED_P0_TPSR edp[unit][0x04] + +#undef ED_P0_NCR +#define ED_P0_NCR edp[unit][0x05] +#undef ED_P0_TBCR0 +#define ED_P0_TBCR0 edp[unit][0x05] + +#undef ED_P0_FIFO +#define ED_P0_FIFO edp[unit][0x06] +#undef ED_P0_TBCR1 +#define ED_P0_TBCR1 edp[unit][0x06] + +#undef ED_P0_ISR +#define ED_P0_ISR edp[unit][0x07] + +#undef ED_P0_CRDA0 +#define ED_P0_CRDA0 edp[unit][0x08] +#undef ED_P0_RSAR0 +#define ED_P0_RSAR0 edp[unit][0x08] + +#undef ED_P0_CRDA1 +#define ED_P0_CRDA1 edp[unit][0x09] +#undef ED_P0_RSAR1 +#define ED_P0_RSAR1 edp[unit][0x09] + +#undef ED_P0_RBCR0 +#define ED_P0_RBCR0 edp[unit][0x0a] + +#undef ED_P0_RBCR1 +#define ED_P0_RBCR1 edp[unit][0x0b] + +#undef ED_P0_RSR +#define ED_P0_RSR edp[unit][0x0c] +#undef ED_P0_RCR +#define ED_P0_RCR edp[unit][0x0c] + +#undef ED_P0_CNTR0 +#define ED_P0_CNTR0 edp[unit][0x0d] +#undef ED_P0_TCR +#define ED_P0_TCR edp[unit][0x0d] + +#undef ED_P0_CNTR1 +#define ED_P0_CNTR1 edp[unit][0x0e] +#undef ED_P0_DCR +#define ED_P0_DCR edp[unit][0x0e] + +#undef ED_P0_CNTR2 +#define ED_P0_CNTR2 edp[unit][0x0f] +#undef ED_P0_IMR +#define ED_P0_IMR edp[unit][0x0f] + +/* + * Page 1 register offsets + */ +#undef ED_P1_CR +#define ED_P1_CR edp[unit][0x00] +#undef ED_P1_PAR0 +#define ED_P1_PAR0 edp[unit][0x01] +#undef ED_P1_PAR1 +#define ED_P1_PAR1 edp[unit][0x02] +#undef ED_P1_PAR2 +#define ED_P1_PAR2 edp[unit][0x03] +#undef ED_P1_PAR3 +#define ED_P1_PAR3 edp[unit][0x04] +#undef ED_P1_PAR4 +#define ED_P1_PAR4 edp[unit][0x05] +#undef ED_P1_PAR5 +#define ED_P1_PAR5 edp[unit][0x06] +#undef ED_P1_CURR +#define ED_P1_CURR edp[unit][0x07] +#undef ED_P1_MAR0 +#define ED_P1_MAR0 edp[unit][0x08] +#undef ED_P1_MAR1 +#define ED_P1_MAR1 edp[unit][0x09] +#undef ED_P1_MAR2 +#define ED_P1_MAR2 edp[unit][0x0a] +#undef ED_P1_MAR3 +#define ED_P1_MAR3 edp[unit][0x0b] +#undef ED_P1_MAR4 +#define ED_P1_MAR4 edp[unit][0x0c] +#undef ED_P1_MAR5 +#define ED_P1_MAR5 edp[unit][0x0d] +#undef ED_P1_MAR6 +#define ED_P1_MAR6 edp[unit][0x0e] +#undef ED_P1_MAR7 +#define ED_P1_MAR7 edp[unit][0x0f] + +/* + * Page 2 register offsets + */ +#undef ED_P2_CR +#define ED_P2_CR edp[unit][0x00] +#undef ED_P2_PSTART +#define ED_P2_PSTART edp[unit][0x01] +#undef ED_P2_CLDA0 +#define ED_P2_CLDA0 edp[unit][0x01] +#undef ED_P2_PSTOP +#define ED_P2_PSTOP edp[unit][0x02] +#undef ED_P2_CLDA1 +#define ED_P2_CLDA1 edp[unit][0x02] +#undef ED_P2_RNPP +#define ED_P2_RNPP edp[unit][0x03] +#undef ED_P2_TPSR +#define ED_P2_TPSR edp[unit][0x04] +#undef ED_P2_LNPP +#define ED_P2_LNPP edp[unit][0x05] +#undef ED_P2_ACU +#define ED_P2_ACU edp[unit][0x06] +#undef ED_P2_ACL +#define ED_P2_ACL edp[unit][0x07] +#undef ED_P2_RCR +#define ED_P2_RCR edp[unit][0x0c] +#undef ED_P2_TCR +#define ED_P2_TCR edp[unit][0x0d] +#undef ED_P2_DCR +#define ED_P2_DCR edp[unit][0x0e] +#undef ED_P2_IMR +#define ED_P2_IMR edp[unit][0x0f] + +/* LPC-T support */ +#define LPCT_1d0_ON() \ +{ \ + outb(0x2a8e, 0x84); \ + outw(0x4a8e, 0x1d0); \ + outw(0x5a8e, 0x0310); \ +} + +#define LPCT_1d0_OFF() \ +{ \ + outb(0x2a8e, 0xa4); \ + outw(0x4a8e, 0xd0); \ + outw(0x5a8e, 0x0300); \ +} + +/* register offsets */ +static unsigned int *edp[NED]; +static unsigned int pc98_io_skip[NED]; +static int ed_novell_nic_offset[NED]; +static int ed_novell_asic_offset[NED]; +static int ed_novell_data[NED]; +static int ed_novell_reset[NED]; + +/* NE2000, LGY-98, ICM, LPC-T */ +static unsigned int edp_generic[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +/* EGY-98 */ +static unsigned int edp_egy98[16] = { + 0, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, + 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e +}; + +/* LD-BDN */ +static unsigned int edp_bdn98[16] = { + 0x00000, 0x01000, 0x02000, 0x03000, 0x04000, 0x05000, 0x06000, 0x07000, + 0x08000, 0x0a000, 0x0b000, 0x0c000, 0x0d000, 0x0d000, 0x0e000, 0x0f000 +}; + +/* SIC-98 */ +static unsigned int edp_sic98[16] = { + 0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0a00, 0x0c00, 0x0e00, + 0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1a00, 0x1c00, 0x1e00 +}; + +/* IO-DATA LA-98 */ +static unsigned int edp_la98[16] = { + 0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, + 0x8000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000 +}; + +/* NEC PC-9801-108 */ +static unsigned int edp_nec108[16] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, + 0x1000, 0x1002, 0x1004, 0x1006, 0x1008, 0x100a, 0x100c, 0x100e +}; + +static void pc98_set_register(struct pc98_device *dev, int unit, int type) +{ + int adj; + + switch (type) { + case ED_TYPE98_GENERIC: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 1; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0010; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x000f; + break; + + case ED_TYPE98_LGY: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 1; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0200; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x0100; + break; + + case ED_TYPE98_EGY: + edp[unit] = edp_egy98; + pc98_io_skip[unit] = 2; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = 0x0200; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x0100; + break; + + case ED_TYPE98_ICM: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 1; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = 0x0100; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x000f; + break; + + case ED_TYPE98_BDN: + edp[unit] = edp_bdn98; + pc98_io_skip[unit] = 0x1000; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0100; + ED_NOVELL_DATA = 0; + ED_NOVELL_RESET = 0xc100; + break; + + case ED_TYPE98_SIC: + edp[unit] = edp_sic98; + pc98_io_skip[unit] = 0x200; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x2000; + ED_NOVELL_DATA = 0x00; /* dummy */ + ED_NOVELL_RESET = 0x00; + break; + + case ED_TYPE98_LPC: + edp[unit] = edp_generic; + pc98_io_skip[unit] = 0x1; + ED_NOVELL_NIC_OFFSET = 0x0000; + ED_NOVELL_ASIC_OFFSET = 0x0100; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0x0200; + break; + + case ED_TYPE98_108: + edp[unit] = edp_nec108; + pc98_io_skip[unit] = 2; + adj = (dev->id_iobase & 0xf000) / 2; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = (0x888 | adj) - dev->id_iobase; + ED_NOVELL_DATA = 0; + ED_NOVELL_RESET = 4; + break; + + case ED_TYPE98_LA98: + edp[unit] = edp_la98; + pc98_io_skip[unit] = 0x1000; + ED_NOVELL_NIC_OFFSET = 0; + ED_NOVELL_ASIC_OFFSET = 0x100; + ED_NOVELL_DATA = 0x0000; + ED_NOVELL_RESET = 0xf000; + break; + } +} + +#endif /* __PC98_PC98_IF_ED98_H__ */ Property changes on: head/sys/pc98/pc98/if_ed98.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/if_ep.c =================================================================== --- head/sys/pc98/pc98/if_ep.c (revision 17349) +++ head/sys/pc98/pc98/if_ep.c (revision 17350) @@ -1,1716 +1,1716 @@ /* * Copyright (c) 1994 Herb Peyerl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Herb Peyerl. * 4. The name of Herb Peyerl may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * if_ep.c,v 1.19 1995/01/24 20:53:45 davidg Exp */ /* * Modified from the FreeBSD 1.1.5.1 version by: * Andres Vega Garcia * INRIA - Sophia Antipolis, France * avega@sophia.inria.fr */ /* - * $Id: if_ep.c,v 1.1.1.1 1996/06/14 10:04:44 asami Exp $ + * $Id: if_ep.c,v 1.2 1996/07/23 07:46:17 asami Exp $ * * Promiscuous mode added and interrupt logic slightly changed * to reduce the number of adapter failures. Transceiver select * logic changed to use value from EEPROM. Autoconfiguration * features added. * Done by: * Serge Babkin * Chelindbank (Chelyabinsk, Russia) * babkin@hq.icb.chel.su */ /* * Pccard support for 3C589 by: * HAMADA Naoki * nao@tom-yam.or.jp */ #include "ep.h" #if NEP > 0 #include "bpfilter.h" #include #if defined(__FreeBSD__) #include #include #include #include #endif #include #include #include #include #include #if defined(__NetBSD__) #include #endif #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef IPX #include #include #endif #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #include #endif #if defined(__FreeBSD__) #include #endif #ifdef PC98 #include #include #else #include #include #endif #include /* Exported variables */ u_long ep_unit; int ep_boards; struct ep_board ep_board[EP_MAX_BOARDS + 1]; static int eeprom_rdy __P((struct ep_softc *sc)); #ifdef PC98 static int ep_isa_probe __P((struct pc98_device *)); static struct ep_board * ep_look_for_board_at __P((struct pc98_device *is)); static int ep_isa_attach __P((struct pc98_device *)); static void ep_isa_registerdev __P((struct ep_softc *sc, struct pc98_device *id)); #else static int ep_isa_probe __P((struct isa_device *)); static struct ep_board * ep_look_for_board_at __P((struct isa_device *is)); static int ep_isa_attach __P((struct isa_device *)); static void ep_isa_registerdev __P((struct ep_softc *sc, struct isa_device *id)); #endif static int epioctl __P((struct ifnet * ifp, int, caddr_t)); static void epmbuffill __P((caddr_t, int)); static void epmbufempty __P((struct ep_softc *)); static void epinit __P((struct ep_softc *)); static void epread __P((struct ep_softc *)); void epreset __P((int)); static void epstart __P((struct ifnet *)); static void epstop __P((struct ep_softc *)); static void epwatchdog __P((struct ifnet *)); #if 0 static int send_ID_sequence __P((int)); #endif static int get_eeprom_data __P((int, int)); static struct ep_softc* ep_softc[NEP]; static int ep_current_tag = EP_LAST_TAG + 1; static char *ep_conn_type[] = {"UTP", "AUI", "???", "BNC"}; #define ep_ftst(f) (sc->stat&(f)) #define ep_fset(f) (sc->stat|=(f)) #define ep_frst(f) (sc->stat&=~(f)) #ifdef PC98 struct pc98_driver epdriver = { #else struct isa_driver epdriver = { #endif ep_isa_probe, ep_isa_attach, "ep", 0 }; static struct kern_devconf kdc_isa_ep = { 0, 0, 0, /* filled in by dev_attach */ #ifdef PC98 "ep", 0, { MDDT_PC98, 0, "net" }, pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN, &kdc_nec0, /* parent */ #else "ep", 0, { MDDT_ISA, 0, "net" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ #endif 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "3Com 3C509 Ethernet adapter", DC_CLS_NETIF /* class */ }; #include "crd.h" #if NCRD > 0 #include "apm.h" #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int card_intr __P((struct pccard_dev *)); static void ep_unload __P((struct pccard_dev *)); static void ep_suspend __P((struct pccard_dev *)); static int ep_pccard_init __P((struct pccard_dev *, int)); static int ep_pccard_attach __P((struct pccard_dev *)); static struct pccard_drv ep_info = { "ep", card_intr, ep_unload, ep_suspend, ep_pccard_init, 0, /* Attributes - presently unused */ &net_imask }; /* Resume is done by executing ep_pccard_init(dp, 0). */ static void ep_suspend(dp) struct pccard_dev *dp; { struct ep_softc *sc = ep_softc[dp->isahd.id_unit]; printf("ep%d: suspending\n", dp->isahd.id_unit); sc->gone = 1; } /* * */ static int ep_pccard_init(dp, first) struct pccard_dev *dp; int first; { #ifdef PC98 struct pc98_device *is = &dp->isahd; #else struct isa_device *is = &dp->isahd; #endif struct ep_softc *sc = ep_softc[is->id_unit]; struct ep_board *epb; int i; epb = &ep_board[is->id_unit]; if (sc == 0) { if ((sc = ep_alloc(is->id_unit, epb)) == 0) { return (ENXIO); } ep_unit++; ep_isa_registerdev(sc, is); } /* get_e() requires these. */ sc->ep_io_addr = is->id_iobase; sc->unit = is->id_unit; epb->epb_addr = is->id_iobase; epb->epb_used = 1; epb->prod_id = get_e(sc, EEPROM_PROD_ID); if (epb->prod_id != 0x9058) { /* 3C589's product id */ if (first) { printf("ep%d: failed to come ready.\n", is->id_unit); } else { printf("ep%d: failed to resume.\n", is->id_unit); } return (ENXIO); } epb->res_cfg = get_e(sc, EEPROM_RESOURCE_CFG); for (i = 0; i < 3; i++) { sc->epb->eth_addr[i] = get_e(sc, EEPROM_NODE_ADDR_0 + i); } if (first) { if (ep_pccard_attach(dp) == 0) { return (ENXIO); } sc->arpcom.ac_if.if_snd.ifq_maxlen = ifqmaxlen; } if (!first) { sc->kdc->kdc_state = DC_IDLE; sc->gone = 0; printf("ep%d: resumed.\n", is->id_unit); epinit(sc); } return (0); } static int ep_pccard_attach(dp) struct pccard_dev *dp; { #ifdef PC98 struct pc98_device *is = &dp->isahd; #else struct isa_device *is = &dp->isahd; #endif struct ep_softc *sc = ep_softc[is->id_unit]; u_short config; sc->ep_connectors = 0; config = inw(IS_BASE + EP_W0_CONFIG_CTRL); if (config & IS_BNC) { sc->ep_connectors |= BNC; } if (config & IS_UTP) { sc->ep_connectors |= UTP; } if (!(sc->ep_connectors & 7)) printf("no connectors!"); sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS; /* ROM size = 0, ROM base = 0 */ /* For now, ignore AUTO SELECT feature of 3C589B and later. */ outw(BASE + EP_W0_ADDRESS_CFG, get_e(sc, EEPROM_ADDR_CFG) & 0xc000); /* Fake IRQ must be 3 */ outw(BASE + EP_W0_RESOURCE_CFG, (sc->epb->res_cfg & 0x0fff) | 0x3000); outw(BASE + EP_W0_PRODUCT_ID, sc->epb->prod_id); ep_attach(sc); return 1; } static void ep_unload(dp) struct pccard_dev *dp; { struct ep_softc *sc = ep_softc[dp->isahd.id_unit]; if (sc->kdc->kdc_state == DC_UNCONFIGURED) { printf("ep%d: already unloaded\n", dp->isahd.id_unit); return; } sc->kdc->kdc_state = DC_UNCONFIGURED; sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING; sc->gone = 1; printf("ep%d: unload\n", dp->isahd.id_unit); } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(dp) struct pccard_dev *dp; { epintr(dp->isahd.id_unit); return(1); } #endif /* NCRD > 0 */ static void ep_isa_registerdev(sc, id) struct ep_softc *sc; #ifdef PC98 struct pc98_device *id; #else struct isa_device *id; #endif { sc->kdc = (struct kern_devconf *)malloc(sizeof(struct kern_devconf), M_DEVBUF, M_NOWAIT); if (!sc->kdc) { printf("WARNING: ep_isa_registerdev unable to malloc! " "Device kdc will not be registerd\n"); return; } bcopy(&kdc_isa_ep, sc->kdc, sizeof(kdc_isa_ep)); sc->kdc->kdc_unit = sc->unit; sc->kdc->kdc_parentdata = id; dev_attach(sc->kdc); } static int eeprom_rdy(sc) struct ep_softc *sc; { int i; for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++); if (i >= MAX_EEPROMBUSY) { printf("ep%d: eeprom failed to come ready.\n", sc->unit); return (0); } return (1); } static struct ep_board * ep_look_for_board_at(is) #ifdef PC98 struct pc98_device *is; #else struct isa_device *is; #endif { int data, i, j, id_port = ELINK_ID_PORT; int count = 0; if (ep_current_tag == (EP_LAST_TAG + 1)) { /* Come here just one time */ ep_current_tag--; /* Look for the ISA boards. Init and leave them actived */ outb(id_port, 0); outb(id_port, 0); elink_idseq(0xCF); elink_reset(); DELAY(10000); for (i = 0; i < EP_MAX_BOARDS; i++) { outb(id_port, 0); outb(id_port, 0); elink_idseq(0xCF); data = get_eeprom_data(id_port, EEPROM_MFG_ID); if (data != MFG_ID) break; /* resolve contention using the Ethernet address */ for (j = 0; j < 3; j++) get_eeprom_data(id_port, j); /* and save this address for later use */ for (j = 0; j < 3; j++) ep_board[ep_boards].eth_addr[j] = get_eeprom_data(id_port, j); ep_board[ep_boards].res_cfg = get_eeprom_data(id_port, EEPROM_RESOURCE_CFG); ep_board[ep_boards].prod_id = get_eeprom_data(id_port, EEPROM_PROD_ID); ep_board[ep_boards].epb_used = 0; ep_board[ep_boards].epb_addr = (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200; if(ep_board[ep_boards].epb_addr > 0x3E0) /* Board in EISA configuration mode */ continue; outb(id_port, ep_current_tag); /* tags board */ outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); ep_boards++; count++; ep_current_tag--; } ep_board[ep_boards].epb_addr = 0; if (count) { printf("%d 3C5x9 board(s) on ISA found at", count); for (j = 0; ep_board[j].epb_addr; j++) if (ep_board[j].epb_addr <= 0x3E0) printf(" 0x%x", ep_board[j].epb_addr); printf("\n"); } } /* we have two cases: * * 1. Device was configured with 'port ?' * In this case we search for the first unused card in list * * 2. Device was configured with 'port xxx' * In this case we search for the unused card with that address * */ if(IS_BASE==-1) { /* port? */ for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_used; i++); if(ep_board[i].epb_addr==0) return 0; IS_BASE=ep_board[i].epb_addr; ep_board[i].epb_used=1; return &ep_board[i]; } else { for (i=0; ep_board[i].epb_addr && ep_board[i].epb_addr != IS_BASE; i++); if( ep_board[i].epb_used || ep_board[i].epb_addr != IS_BASE) return 0; if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE) printf("ep%d: 3c5x9 at 0x%x in PnP mode. Disable PnP mode!\n", is->id_unit, IS_BASE); ep_board[i].epb_used=1; return &ep_board[i]; } } /* * get_e: gets a 16 bits word from the EEPROM. we must have set the window * before */ u_int16_t get_e(sc, offset) struct ep_softc *sc; int offset; { if (!eeprom_rdy(sc)) return (0xffff); outw(BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset); if (!eeprom_rdy(sc)) return (0xffff); return (inw(BASE + EP_W0_EEPROM_DATA)); } struct ep_softc * ep_alloc(unit, epb) int unit; struct ep_board *epb; { struct ep_softc *sc; if (unit >= NEP) { printf("ep: unit number (%d) too high\n", unit); return NULL; } /* * Allocate a storage area for us */ if (ep_softc[unit]) { printf("ep%d: unit number already allocated to another " "adaptor\n", unit); return NULL; } sc = malloc(sizeof(struct ep_softc), M_DEVBUF, M_NOWAIT); if(!sc) { printf("ep%d: cannot malloc!\n", unit); return NULL; } bzero(sc, sizeof(struct ep_softc)); ep_softc[unit] = sc; sc->unit = unit; sc->ep_io_addr = epb->epb_addr; sc->epb = epb; return(sc); } void ep_free(sc) struct ep_softc *sc; { ep_softc[sc->unit] = NULL; free(sc, M_DEVBUF); return; } int ep_isa_probe(is) #ifdef PC98 struct pc98_device *is; #else struct isa_device *is; #endif { struct ep_softc *sc; struct ep_board *epb; u_short k; #if NCRD > 0 pccard_add_driver(&ep_info); #endif /* NCRD > 0 */ if(( epb=ep_look_for_board_at(is) )==0) return (0); /* * Allocate a storage area for us */ sc = ep_alloc(ep_unit, epb); if( !sc ) return (0); is->id_unit = ep_unit++; ep_isa_registerdev(sc, is); /* * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be * 0x9[0-f]50 */ GO_WINDOW(0); k = sc->epb->prod_id; if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) { printf("ep_isa_probe: ignoring model %04x\n", k); ep_free(sc); return (0); } k = sc->epb->res_cfg; k >>= 12; /* Now we have two cases again: * * 1. Device was configured with 'irq?' * In this case we use irq read from the board * * 2. Device was configured with 'irq xxx' * In this case we set up the board to use specified interrupt * */ if(is->id_irq==0) { /* irq? */ is->id_irq= 1 << ( (k==2) ? 9 : k ); } sc->stat = 0; /* 16 bit access */ /* By now, the adapter is already activated */ return (EP_IOSIZE); /* 16 bytes of I/O space used. */ } static int ep_isa_attach(is) #ifdef PC98 struct pc98_device *is; #else struct isa_device *is; #endif { struct ep_softc *sc = ep_softc[is->id_unit]; u_short config; int irq; sc->ep_connectors = 0; config = inw(IS_BASE + EP_W0_CONFIG_CTRL); if (config & IS_AUI) { sc->ep_connectors |= AUI; } if (config & IS_BNC) { sc->ep_connectors |= BNC; } if (config & IS_UTP) { sc->ep_connectors |= UTP; } if (!(sc->ep_connectors & 7)) printf("no connectors!"); sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS; /* * Write IRQ value to board */ irq = ffs(is->id_irq) - 1; if(irq == -1) { printf(" invalid irq... cannot attach\n"); return 0; } GO_WINDOW(0); if(irq == 9) irq = 2; SET_IRQ(BASE, irq); ep_attach(sc); return 1; } int ep_attach(sc) struct ep_softc *sc; { struct ifaddr *ifa; struct ifnet *ifp = &sc->arpcom.ac_if; struct sockaddr_dl *sdl; u_short *p; int i; int attached; sc->gone = 0; attached = (ifp->if_softc != 0); printf("ep%d: ", sc->unit); /* * Current media type */ if(sc->ep_connectors & AUI) { printf("aui"); if(sc->ep_connectors & ~AUI) printf("/"); } if(sc->ep_connectors & UTP) { printf("utp"); if(sc->ep_connectors & BNC) printf("/"); } if(sc->ep_connectors & BNC) { printf("bnc"); } printf("[*%s*]", ep_conn_type[sc->ep_connector]); /* * Setup the station address */ p = (u_short *) & sc->arpcom.ac_enaddr; GO_WINDOW(2); for (i = 0; i < 3; i++) { p[i] = htons(sc->epb->eth_addr[i]); outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(p[i])); } printf(" address %6D\n", sc->arpcom.ac_enaddr, ":"); ifp->if_softc = sc; ifp->if_unit = sc->unit; ifp->if_name = "ep"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_output = ether_output; ifp->if_start = epstart; ifp->if_ioctl = epioctl; ifp->if_watchdog = epwatchdog; if (!attached) { if_attach(ifp); ether_ifattach(ifp); } /* device attach does transition from UNCONFIGURED to IDLE state */ sc->kdc->kdc_state=DC_IDLE; /* * Fill the hardware address into ifa_addr if we find an AF_LINK entry. * We need to do this so bpf's can get the hardware addr of this card. * netstat likes this too! */ ifa = ifp->if_addrlist; while ((ifa != 0) && (ifa->ifa_addr != 0) && (ifa->ifa_addr->sa_family != AF_LINK)) ifa = ifa->ifa_next; if ((ifa != 0) && (ifa->ifa_addr != 0)) { sdl = (struct sockaddr_dl *) ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; sdl->sdl_slen = 0; bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); } /* we give some initial parameters */ sc->rx_avg_pkt = 128; /* * NOTE: In all this I multiply everything by 64. * W_s = the speed the CPU is able to write to the TX FIFO. * T_s = the speed the board sends the info to the Ether. * W_s/T_s = 16 (represents 16/64) => W_s = 25 % of T_s. * This will give us for a packet of 1500 bytes * tx_start_thresh=1125 and for a pkt of 64 bytes tx_start_threshold=48. * We prefer to start thinking the CPU is much slower than the Ethernet * transmission. */ sc->tx_rate = TX_INIT_RATE; sc->tx_counter = 0; sc->rx_latency = RX_INIT_LATENCY; sc->rx_early_thresh = RX_INIT_EARLY_THRESH; #ifdef EP_LOCAL_STATS sc->rx_no_first = sc->rx_no_mbuf = sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0; #endif ep_fset(F_RX_FIRST); sc->top = sc->mcur = 0; #if NBPFILTER > 0 if (!attached) { bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); } #endif return 0; } /* * The order in here seems important. Otherwise we may not receive * interrupts. ?! */ static void epinit(sc) struct ep_softc *sc; { register struct ifnet *ifp = &sc->arpcom.ac_if; int s, i, j; if (sc->gone) return; /* if (ifp->if_addrlist == (struct ifaddr *) 0) return; */ s = splimp(); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); GO_WINDOW(0); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); GO_WINDOW(4); outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP); GO_WINDOW(0); /* Disable the card */ outw(BASE + EP_W0_CONFIG_CTRL, 0); /* Enable the card */ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); GO_WINDOW(2); /* Reload the ether_addr. */ for (i = 0; i < 6; i++) outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); /* Window 1 is operating window */ GO_WINDOW(1); for (i = 0; i < 31; i++) inb(BASE + EP_W1_TX_STATUS); /* get rid of stray intr's */ outw(BASE + EP_COMMAND, ACK_INTR | 0xff); outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); if(ifp->if_flags & IFF_PROMISC) outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_GROUP | FIL_BRDCST | FIL_ALL); else outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_GROUP | FIL_BRDCST); /* * S.B. * * Now behavior was slightly changed: * * if any of flags link[0-2] is used and its connector is * physically present the following connectors are used: * * link0 - AUI * highest precedence * link1 - BNC * link2 - UTP * lowest precedence * * If none of them is specified then * connector specified in the EEPROM is used * (if present on card or AUI if not). * */ /* Set the xcvr. */ if(ifp->if_flags & IFF_LINK0 && sc->ep_connectors & AUI) { i = ACF_CONNECTOR_AUI; } else if(ifp->if_flags & IFF_LINK1 && sc->ep_connectors & BNC) { i = ACF_CONNECTOR_BNC; } else if(ifp->if_flags & IFF_LINK2 && sc->ep_connectors & UTP) { i = ACF_CONNECTOR_UTP; } else { i = sc->ep_connector; } GO_WINDOW(0); j = inw(BASE + EP_W0_ADDRESS_CFG) & 0x3fff; outw(BASE + EP_W0_ADDRESS_CFG, j | (i << ACF_CONNECTOR_BITS)); switch(i) { case ACF_CONNECTOR_UTP: if(sc->ep_connectors & UTP) { GO_WINDOW(4); outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); } break; case ACF_CONNECTOR_BNC: if(sc->ep_connectors & BNC) { outw(BASE + EP_COMMAND, START_TRANSCEIVER); DELAY(1000); } break; case ACF_CONNECTOR_AUI: /* nothing to do */ break; default: printf("ep%d: strange connector type in EEPROM: assuming AUI\n", sc->unit); break; } outw(BASE + EP_COMMAND, RX_ENABLE); outw(BASE + EP_COMMAND, TX_ENABLE); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ sc->tx_rate = TX_INIT_RATE; sc->tx_counter = 0; sc->rx_latency = RX_INIT_LATENCY; sc->rx_early_thresh = RX_INIT_EARLY_THRESH; #ifdef EP_LOCAL_STATS sc->rx_no_first = sc->rx_no_mbuf = sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0; #endif ep_fset(F_RX_FIRST); ep_frst(F_RX_TRAILER); if (sc->top) { m_freem(sc->top); sc->top = sc->mcur = 0; } outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | sc->rx_early_thresh); /* * These clever computations look very interesting * but the fixed threshold gives near no output errors * and if it as low as 16 bytes it gives the max. throughput. * We think that processor is anyway quicker than Ethernet * (and this should be true for any 386 and higher) */ outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16); /* * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up * any that we had in case we're being called from intr or somewhere * else. */ sc->last_mb = 0; sc->next_mb = 0; epmbuffill((caddr_t) sc, 0); GO_WINDOW(1); epstart(ifp); splx(s); } static const char padmap[] = {0, 3, 2, 1}; static void epstart(ifp) struct ifnet *ifp; { register struct ep_softc *sc = ifp->if_softc; register u_int len; register struct mbuf *m; struct mbuf *top; int s, pad; if (sc->gone) { return; } s = splimp(); if (ifp->if_flags & IFF_OACTIVE) { splx(s); return; } startagain: /* Sneak a peek at the next packet */ m = ifp->if_snd.ifq_head; if (m == 0) { splx(s); return; } for (len = 0, top = m; m; m = m->m_next) len += m->m_len; pad = padmap[len & 3]; /* * The 3c509 automatically pads short packets to minimum ethernet length, * but we drop packets that are too large. Perhaps we should truncate * them instead? */ if (len + pad > ETHER_MAX_LEN) { /* packet is obviously too large: toss it */ ++ifp->if_oerrors; IF_DEQUEUE(&ifp->if_snd, m); m_freem(m); goto readcheck; } if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { /* no room in FIFO */ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); ifp->if_flags |= IFF_OACTIVE; splx(s); return; } IF_DEQUEUE(&ifp->if_snd, m); - outw(BASE + EP_W1_TX_PIO_WR_1, len | 0x8000); /* XXX */ + outw(BASE + EP_W1_TX_PIO_WR_1, len); outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ /* compute the Tx start threshold for this packet */ sc->tx_start_thresh = len = (((len * (64 - sc->tx_rate)) >> 6) & ~3) + 16; #if 0 /* * The following string does something strange with the card and * we get a lot of output errors due to it so it's commented out * and we use fixed threshold (see above) */ outw(BASE + EP_COMMAND, SET_TX_START_THRESH | len); #endif for (top = m; m != 0; m = m->m_next) if(ep_ftst(F_ACCESS_32_BITS)) { outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4); if (m->m_len & 3) outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3); } else { outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2); if (m->m_len & 1) outb(BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, caddr_t) + m->m_len - 1)); } while (pad--) outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, top); } #endif ifp->if_timer=2; ifp->if_opackets++; m_freem(top); /* * Every 1024*4 packets we increment the tx_rate if we haven't had * errors, that in the case it has abnormaly goten too low */ if (!(++sc->tx_counter & (1024 * 4 - 1)) && sc->tx_rate < TX_INIT_MAX_RATE) sc->tx_rate++; /* * Is another packet coming in? We don't want to overflow the tiny RX * fifo. */ readcheck: if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { /* * we check if we have packets left, in that case we prepare to come * back later */ if (ifp->if_snd.ifq_head) { outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | sc->tx_start_thresh); } splx(s); return; } goto startagain; } void epintr(unit) int unit; { register struct ep_softc *sc = ep_softc[unit]; if (sc->gone) { return; } ep_intr(sc); } void ep_intr(arg) void *arg; { struct ep_softc *sc; register int status; struct ifnet *ifp; int x; x=splbio(); sc = (struct ep_softc *)arg; ifp = &sc->arpcom.ac_if; outw(BASE + EP_COMMAND, SET_INTR_MASK); /* disable all Ints */ rescan: while ((status = inw(BASE + EP_STATUS)) & S_5_INTS) { /* first acknowledge all interrupt sources */ outw(BASE + EP_COMMAND, ACK_INTR | (status & S_MASK)); if (status & (S_RX_COMPLETE | S_RX_EARLY)) { epread(sc); continue; } if (status & S_TX_AVAIL) { /* we need ACK */ ifp->if_timer=0; ifp->if_flags &= ~IFF_OACTIVE; GO_WINDOW(1); inw(BASE + EP_W1_FREE_TX); epstart(ifp); } if (status & S_CARD_FAILURE) { ifp->if_timer=0; #ifdef EP_LOCAL_STATS printf("\nep%d:\n\tStatus: %x\n", sc->unit, status); GO_WINDOW(4); printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG)); printf("\tStat: %x\n", sc->stat); printf("\tIpackets=%d, Opackets=%d\n", ifp->if_ipackets, ifp->if_opackets); printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n", sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf, sc->rx_overrunl, sc->tx_underrun); #else #ifdef DIAGNOSTIC printf("ep%d: Status: %x (input buffer overflow)\n", sc->unit, status); #else ++ifp->if_ierrors; #endif #endif epinit(sc); splx(x); return; } if (status & S_TX_COMPLETE) { ifp->if_timer=0; /* we need ACK. we do it at the end */ /* * We need to read TX_STATUS until we get a 0 status in order to * turn off the interrupt flag. */ while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { if (status & TXS_SUCCES_INTR_REQ); else if (status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION)) { outw(BASE + EP_COMMAND, TX_RESET); if (status & TXS_UNDERRUN) { if (sc->tx_rate > 1) { sc->tx_rate--; /* Actually in steps of 1/64 */ sc->tx_counter = 0; /* We reset it */ } #ifdef EP_LOCAL_STATS sc->tx_underrun++; #endif } else { if (status & TXS_JABBER); else /* TXS_MAX_COLLISION - we shouldn't get here */ ++ifp->if_collisions; } ++ifp->if_oerrors; outw(BASE + EP_COMMAND, TX_ENABLE); /* * To have a tx_avail_int but giving the chance to the * Reception */ if (ifp->if_snd.ifq_head) { outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); } } outb(BASE + EP_W1_TX_STATUS, 0x0); /* pops up the next * status */ } /* while */ ifp->if_flags &= ~IFF_OACTIVE; GO_WINDOW(1); inw(BASE + EP_W1_FREE_TX); epstart(ifp); } /* end TX_COMPLETE */ } outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */ if ((status = inw(BASE + EP_STATUS)) & S_5_INTS) goto rescan; /* re-enable Ints */ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); splx(x); } static void epread(sc) register struct ep_softc *sc; { struct ether_header *eh; struct mbuf *top, *mcur, *m; struct ifnet *ifp; int lenthisone; short rx_fifo2, status; register short delta; register short rx_fifo; ifp = &sc->arpcom.ac_if; status = inw(BASE + EP_W1_RX_STATUS); read_again: if (status & ERR_RX) { ++ifp->if_ierrors; if (status & ERR_RX_OVERRUN) { /* * we can think the rx latency is actually greather than we * expect */ #ifdef EP_LOCAL_STATS if (ep_ftst(F_RX_FIRST)) sc->rx_overrunf++; else sc->rx_overrunl++; #endif if (sc->rx_latency < ETHERMTU) sc->rx_latency += 16; } goto out; } rx_fifo = rx_fifo2 = status & RX_BYTES_MASK; if (ep_ftst(F_RX_FIRST)) { if (m = sc->mb[sc->next_mb]) { sc->mb[sc->next_mb] = 0; sc->next_mb = (sc->next_mb + 1) % MAX_MBS; m->m_data = m->m_pktdat; m->m_flags = M_PKTHDR; } else { MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) goto out; } sc->top = sc->mcur = top = m; #define EROUND ((sizeof(struct ether_header) + 3) & ~3) #define EOFF (EROUND - sizeof(struct ether_header)) top->m_data += EOFF; /* Read what should be the header. */ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), sizeof(struct ether_header) / 2); top->m_len = sizeof(struct ether_header); rx_fifo -= sizeof(struct ether_header); sc->cur_len = rx_fifo2; } else { /* come here if we didn't have a complete packet last time */ top = sc->top; m = sc->mcur; sc->cur_len += rx_fifo2; if (ep_ftst(F_RX_TRAILER)) /* We don't read the trailer */ rx_fifo -= sizeof(struct ether_header); } /* Reads what is left in the RX FIFO */ while (rx_fifo > 0) { lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); if (lenthisone == 0) { /* no room in this one */ mcur = m; if (m = sc->mb[sc->next_mb]) { sc->mb[sc->next_mb] = 0; sc->next_mb = (sc->next_mb + 1) % MAX_MBS; } else { MGET(m, M_DONTWAIT, MT_DATA); if (!m) goto out; } if (rx_fifo >= MINCLSIZE) MCLGET(m, M_DONTWAIT); m->m_len = 0; mcur->m_next = m; lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); } if (ep_ftst(F_ACCESS_32_BITS)) { /* default for EISA configured cards*/ insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 4); m->m_len += (lenthisone & ~3); if (lenthisone & 3) insb(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone & 3); m->m_len += (lenthisone & 3); } else { insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 2); m->m_len += lenthisone; if (lenthisone & 1) *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); } rx_fifo -= lenthisone; } if (ep_ftst(F_RX_TRAILER)) {/* reads the trailer */ if (m = sc->mb[sc->next_mb]) { sc->mb[sc->next_mb] = 0; sc->next_mb = (sc->next_mb + 1) % MAX_MBS; m->m_data = m->m_pktdat; m->m_flags = M_PKTHDR; } else { MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) goto out; } insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t), sizeof(struct ether_header)); m->m_len = sizeof(struct ether_header); m->m_next = top; sc->top = top = m; /* XXX Accomodate for type and len from beginning of trailer */ sc->cur_len -= (2 * sizeof(u_short)); ep_frst(F_RX_TRAILER); goto all_pkt; } if (status & ERR_RX_INCOMPLETE) { /* we haven't received the complete * packet */ sc->mcur = m; #ifdef EP_LOCAL_STATS sc->rx_no_first++; /* to know how often we come here */ #endif /* * Re-compute rx_latency, the factor used is 1/4 to go up and 1/32 to * go down */ delta = rx_fifo2 - sc->rx_early_thresh; /* last latency seen LLS */ delta -= sc->rx_latency;/* LLS - estimated_latency */ if (delta >= 0) sc->rx_latency += (delta / 4); else sc->rx_latency += (delta / 32); ep_frst(F_RX_FIRST); if (!((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE)) { /* we see if by now, the packet has completly arrived */ goto read_again; } /* compute rx_early_threshold */ delta = (sc->rx_avg_pkt - sc->cur_len - sc->rx_latency - 16) & ~3; if (delta < MIN_RX_EARLY_THRESHL) delta = MIN_RX_EARLY_THRESHL; outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | (sc->rx_early_thresh = delta)); return; } all_pkt: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); /* * recompute average packet's length, the factor used is 1/8 to go down * and 1/32 to go up */ delta = sc->cur_len - sc->rx_avg_pkt; if (delta > 0) sc->rx_avg_pkt += (delta / 32); else sc->rx_avg_pkt += (delta / 8); delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3; if (delta < MIN_RX_EARLY_THRESHF) delta = MIN_RX_EARLY_THRESHF; sc->rx_early_thresh = delta; ++ifp->if_ipackets; ep_fset(F_RX_FIRST); ep_frst(F_RX_TRAILER); top->m_pkthdr.rcvif = &sc->arpcom.ac_if; top->m_pkthdr.len = sc->cur_len; #if NBPFILTER > 0 if (ifp->if_bpf) { bpf_mtap(ifp, top); /* * Note that the interface cannot be in promiscuous mode if there are * no BPF listeners. And if we are in promiscuous mode, we have to * check if this packet is really ours. */ eh = mtod(top, struct ether_header *); if ((ifp->if_flags & IFF_PROMISC) && (eh->ether_dhost[0] & 1) == 0 && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { if (sc->top) { m_freem(sc->top); sc->top = 0; } ep_fset(F_RX_FIRST); ep_frst(F_RX_TRAILER); #ifdef EP_LOCAL_STATS sc->rx_bpf_disc++; #endif while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta); return; } } #endif eh = mtod(top, struct ether_header *); m_adj(top, sizeof(struct ether_header)); ether_input(ifp, eh, top); if (!sc->mb[sc->next_mb]) epmbuffill((caddr_t) sc, 0); sc->top = 0; while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta); return; out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); if (sc->top) { m_freem(sc->top); sc->top = 0; #ifdef EP_LOCAL_STATS sc->rx_no_mbuf++; #endif } delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3; if (delta < MIN_RX_EARLY_THRESHF) delta = MIN_RX_EARLY_THRESHF; ep_fset(F_RX_FIRST); ep_frst(F_RX_TRAILER); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | (sc->rx_early_thresh = delta)); } /* * Look familiar? */ static int epioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *) data; struct ep_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int s, error = 0; s = splimp(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; /* netifs are BUSY when UP */ sc->kdc->kdc_state=DC_BUSY; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: epinit(sc); /* before arpwhohas */ arp_ifinit((struct arpcom *)ifp, ifa); break; #endif #ifdef IPX case AF_IPX: { register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) (sc->arpcom.ac_enaddr); else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->arpcom.ac_enaddr, sizeof(sc->arpcom.ac_enaddr)); } epinit(sc); break; } #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) (sc->arpcom.ac_enaddr); else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->arpcom.ac_enaddr, sizeof(sc->arpcom.ac_enaddr)); } epinit(sc); break; } #endif default: epinit(sc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy((caddr_t) sc->arpcom.ac_enaddr, (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFFLAGS: /* UP controls BUSY/IDLE */ sc->kdc->kdc_state= ( (ifp->if_flags & IFF_UP) ? DC_BUSY : DC_IDLE ); if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { ifp->if_flags &= ~IFF_RUNNING; epstop(sc); epmbufempty(sc); break; } else { /* reinitialize card on any parameter change */ epinit(sc); break; } /* NOTREACHED */ break; #ifdef notdef case SIOCGHWADDR: bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data, sizeof(sc->sc_addr)); break; #endif case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; case SIOCADDMULTI: case SIOCDELMULTI: /* Now this driver has no support for programmable * multicast filters. If some day it will gain this * support this part of code must be extended. */ error=0; break; default: error = EINVAL; } splx(s); return (error); } static void epwatchdog(ifp) struct ifnet *ifp; { struct ep_softc *sc = ifp->if_softc; /* printf("ep: watchdog\n"); log(LOG_ERR, "ep%d: watchdog\n", ifp->if_unit); ifp->if_oerrors++; */ if (sc->gone) { return; } ifp->if_flags &= ~IFF_OACTIVE; epstart(ifp); ep_intr(ifp->if_softc); } static void epstop(sc) struct ep_softc *sc; { if (sc->gone) { return; } outw(BASE + EP_COMMAND, RX_DISABLE); outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); outw(BASE + EP_COMMAND, TX_DISABLE); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); outw(BASE + EP_COMMAND, RX_RESET); outw(BASE + EP_COMMAND, TX_RESET); outw(BASE + EP_COMMAND, C_INTR_LATCH); outw(BASE + EP_COMMAND, SET_RD_0_MASK); outw(BASE + EP_COMMAND, SET_INTR_MASK); outw(BASE + EP_COMMAND, SET_RX_FILTER); } #if 0 static int send_ID_sequence(port) int port; { int cx, al; for (al = 0xff, cx = 0; cx < 255; cx++) { outb(port, al); al <<= 1; if (al & 0x100) al ^= 0xcf; } return (1); } #endif /* * We get eeprom data from the id_port given an offset into the eeprom. * Basically; after the ID_sequence is sent to all of the cards; they enter * the ID_CMD state where they will accept command requests. 0x80-0xbf loads * the eeprom data. We then read the port 16 times and with every read; the * cards check for contention (ie: if one card writes a 0 bit and another * writes a 1 bit then the host sees a 0. At the end of the cycle; each card * compares the data on the bus; if there is a difference then that card goes * into ID_WAIT state again). In the meantime; one bit of data is returned in * the AX register which is conveniently returned to us by inb(). Hence; we * read 16 times getting one bit of data with each read. */ static int get_eeprom_data(id_port, offset) int id_port; int offset; { int i, data = 0; outb(id_port, 0x80 + offset); DELAY(1000); for (i = 0; i < 16; i++) data = (data << 1) | (inw(id_port) & 1); return (data); } /* * We suppose this is always called inside a splimp(){...}splx() region */ static void epmbuffill(sp, dummy_arg) caddr_t sp; int dummy_arg; { struct ep_softc *sc = (struct ep_softc *) sp; int i; i = sc->last_mb; do { if (sc->mb[i] == NULL) MGET(sc->mb[i], M_DONTWAIT, MT_DATA); if (sc->mb[i] == NULL) break; i = (i + 1) % MAX_MBS; } while (i != sc->next_mb); sc->last_mb = i; } static void epmbufempty(sc) struct ep_softc *sc; { int s, i; s = splimp(); for (i = 0; i < MAX_MBS; i++) { if (sc->mb[i]) { m_freem(sc->mb[i]); sc->mb[i] = NULL; } } sc->last_mb = sc->next_mb = 0; splx(s); } #endif /* NEP > 0 */ Index: head/sys/pc98/pc98/matcd/matcd.c =================================================================== --- head/sys/pc98/pc98/matcd/matcd.c (revision 17349) +++ head/sys/pc98/pc98/matcd/matcd.c (revision 17350) @@ -1,2748 +1,2736 @@ /*matcd.c-------------------------------------------------------------------- Matsushita(Panasonic) / Creative CD-ROM Driver (matcd) Authored by Frank Durda IV Copyright 1994, 1995 Frank Durda IV. All rights reserved. "FDIV" is a trademark of Frank Durda IV. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice positioned at the very beginning of this file without modification, all copyright strings, all related programming codes that display the copyright strings, this list of conditions and the following disclaimer. 2. Redistributions in binary form must contain all copyright strings and related programming code that display the copyright strings. 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 4. All advertising materials mentioning features or use of this software must display the following acknowledgement: "The Matsushita/Panasonic CD-ROM driver was developed by Frank Durda IV for use with "FreeBSD" and similar operating systems." "Similar operating systems" includes mainly non-profit oriented systems for research and education, including but not restricted to "NetBSD", "386BSD", and "Mach" (by CMU). The wording of the acknowledgement (in electronic form or printed text) may not be changed without permission from the author. 5. Absolutely no warranty of function, fitness or purpose is made by the author Frank Durda IV. 6. Neither the name of the author nor the name "FreeBSD" may be used to endorse or promote products derived from this software without specific prior written permission. (The author can be reached at bsdmail@nemesis.lonestar.org) 7. The product containing this software must meet all of these conditions even if it is unsupported, not a complete system and/or does not contain compiled code. 8. These conditions will be in force for the full life of the copyright. 9. If all the above conditions are met, modifications to other parts of this file may be freely made, although any person or persons making changes do not receive the right to add their name or names to the copyright strings and notices in this software. Persons making changes are encouraged to insert edit history in matcd.c and to put your name and details of the change there. 10. You must have prior written permission from the author to deviate from these terms. Vendors who produce product(s) containing this code are encouraged (but not required) to provide copies of the finished product(s) to the author and to correspond with the author about development activity relating to this code. Donations of development hardware and/or software are also welcome. (This is one of the faster ways to get a driver developed for a device.) THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- Dedicated to: My family, my Grandfather, and Max, my Golden Retriever Thanks to: Jordon Hubbard (jkh) for getting me ramped-up to 2.x system quickly enough to make the 2.1 release. He put up with plenty of silly questions and might get the post of ambassador some day. and The people who donated equipment and other material to make development of this driver possible. Donations and sponsors for projects are appreciated. -----No changes are allowed above this line------------------------------------ Edit History - (should be in sync with any source control log entries) Never seen one of these before? Ok, here is how it works. Every time you change the code, you increment the edit number, that number over there in the <%d> and in the (%d) in the version string. You never set this number lower than it is. Near, or preferably on lines that change, insert the edit number. If there is a number there already, you can replace it with a newer one. This makes searches for code changes very fast. In the edit history, start with the edit number, and a good description of what changes were made. Then follow it with the date, your name and an EMAIL address where you can be reached. Please follow this practice; it helps leave understandable code in your wake. FYI, you have major and minor release codes. Major releases numbered 1 thru n. Major feature additions should get a new major release number. Minor releases start with a null and then letters A thru Z. So 3A(456) is Major release 3, Minor release 1, Edit 456 (in Microsoft-ese that would be 03.01.456), and 5(731) is Major release 5, Minor release 0, Edit 731. Typically only the author will change the major and minor release codes in small projects. EDIT edit Edit HISTORY history History <1> This initial version is to get basic filesystem I/O working using the SoundBlaster 16 interface. The stand-alone adapter card doesn't work yet. December 1994 Frank Durda IV bsdmail@nemesis.lonestar.org <2> Corrections to resolve a race condition when multiple drives on the same controller was active. Fixed drive 1 & 2 swap problem. See selectdrive(). 21-Jan-95 Frank Durda IV bsdmail@nemesis.lonestar.org <3> Added automatic probing and support for all Creative Labs sound cards with the Creative/Panasonic interface and the stand-alone interface adapters. See AUTOHUNT and FULLCONFIG conditionals for more information. 21-Jan-95 Frank Durda IV bsdmail@nemesis.lonestar.org <4> Rebundled debug conditionals. 14-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org <5> Changes needed to work on FreeBSD 2.1. Also added draincmd since some conditions cause the drive to produce surprise data. See setmode and draincmd 19-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org <6> Got rid of some redundant error code by creating chk_error(). Also built a nice generic bus-lock function. 20-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org <7> Improved comments, general structuring. Fixed a problem with disc eject not working if LOCKDRIVE was set. Apparently the drive will reject an EJECT command if the drive is LOCKED. 21-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org Edit number code marking begins here - earlier edits were during development. <8> Final device name selected and actually made to compile under >2.0. For newer systems, it is "matcd", for older it is "mat". 24-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org <9> Added some additional disk-related ioctl functions that didn't make it into earlier versions. 26-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org <10> Updated some conditionals so the code will compile under 1.1.5.1, although this is not the supported platform. Also found that some other devices probe code was changing the settings for the port 0x302 debug board, so added code to set it to a sane state before we use it. 26-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org <11> The Copyright and Use statement has been replaced in all files with a new version. 1-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org <12> Added ioctls having to do with audio volume, routing and playback speed. Also added some code I think is for dynamic loading. 12-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org <13> Added ioctls to return TOC headers and entries. 19-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org <14> More ioctls to finish out general audio support and some clean-up. Also fixed a bug in open where CD label information would not always be cleared after a disc change. Added a check to block attempts to resume audio if already playing. The resulting sound is a cross between Kryten and Max Headroom. But, if you *want* this "feature", enable #define KRYTEN in options.h. So it is not BSD-ish enough, eh? What, too many comments? :-) 21-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org <15> LOCKDRIVE has been modified so that a new series of minor numbers are created. When these are opened, the selected drive will have its door locked and the device must be completely closed to unlock the media. The EJECT ioctl will be refused when the drive is locked this way. This is useful for servers and other places where the media needs to remain in the drive. Bit 7 of the minor number controls locking. As of this edit, the code compiles with no warnings with -Wall set. 22-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org <16> Added a new check in the probe code that looks for the drive interface being in an idle state after issuing a reset. If this isn't the case, then the device at this location isn't a Matsushita CD-ROM drive. This will prevent hangs in draincmd later. Added the tray close ioctl. This required modifications to open to allow the character devices to be "partially" opened so that the close ioctl could be issued when the open would otherwise fail. Close also delays slightly after completing because the drive doesn't update its disc and media status instantly. Also created the capability ioctl that lets an application find out up front what things a drive can do. Fixed a global spelling error. Changed matcddriver structure to simply say "matcd". The original string "matcd interface " broke the kernel -c boot mechanism. Updated the #includes in response to a complaint in first release. Updated and tested conditionals so that driver will still compile under FreeBSD 1.1.5.1 as well as 2.0 and early 2.1. 4-Apr-95 Frank Durda IV bsdmail@nemesis.lonestar.org <17> The function matcd_toc_entries which is executed in response to the CDIOREADTOCENTRYS ioctl didn't cope with programs that only requested part of the TOC. This change is based on code submitted by Doug Robson (dfr@render.com). (This change was introduced out of order and exists in FreeBSD 2.0.5 without the version stamp being updated. I.N.M.F.) 1-Jun-95 Frank Durda IV bsdmail@nemesis.lonestar.org <18> While working on the TEAC CD-ROM driver (teaccd) that is reusing chunks of code from this driver, I discovered several functions, arrays and other things that should have been declared 'static'. These changes are necessary if the TEAC CD-ROM driver is to be present at the same time as matcd. Also fixed the residual buss vs bus symbols and strings. There are no functional code changes in this edit. 2-May-95 Frank Durda IV bsdmail@nemesis.lonestar.org <19> Creative has changed the Status port slightly in their sound boards based on the Vibra-16 (and probably the Vibra-16S) chipset. This change masks some unused bits that were formally on all the time and are doing different things in this design. The changes are transparent to all other supported boards. 20-Jun-95 Frank Durda IV bsdmail@nemesis.lonestar.org <20> Code was added to detect non-Creative (SoundBlaster) host interfaces, and the driver will switch to code compatible with the detected host interface. This should add support for MediaVision, IBM, Reveal, and other compatible adapters with split data/status-ports. This code allows a mix of SoundBlaster (Type 0) and non-SoundBlaster (Type 1) boards in the same system with no special configuration. I also updated the attach code to display the interface type and changed the host interface probe messages to reflect the "c" for controller in controller-specific messages as the existing messages were confusing when a second card was in place . The kernel -c tables have been updated accordingly, so you now have a matcdc%d controller to change settings on. 24-Jun-95 Frank Durda IV bsdmail@nemesis.lonestar.org <21> Added interface handling code in two of those "this should not happen" routines, draincmd and get_stat. Since these routines are called by functions during probing that may not know what type interface is out there, the code assumes that a given adapter is both a type 0 and a type 1 adapter at the same time. Plus, this code gets executed once in a very long time so the cost of assuming both host adapter types is not significant. 4-Jul-95 Frank Durda IV bsdmail@nemesis.lonestar.org <22> Four external interface prototypes were altered by someone else. I believe these changes are for making GCC and/or the linker shut-up when building some other part of the system since matcd already compiles -Wall with no warnings... 8-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org <23> This change implements the ioctls for preventing media removal and allowing media removal. Currently, these calls will work according to the following rules: No "l" devs opened Any "l" dev open CDALLOW accepted always rejected always CDPREVENT accepted always accepted always One refinement might be to allow CDALLOW/CDPREVENT to always work if UID 0 issued the ioctl, but that will wait for later. I also made a change to the information that the toc_entry code returns so that xcdplayer won't malfunction. (It would not play the last track on a non-mixed mode audio CD.) Unlike cdplayer, xcdplayer asks for track information one track at a time, and calls for information on the lead-out track by its official number (0xaa), rather than referring to the "after last" (n+1) track as cdplayer does. Anyway, this change should make both players happy. 16-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org <24> In Edit 15 when the extra devs were created for selective locking, the door locking was broken if a non-locking dev on the drive is closed. The problem was caused by not tracking locked devs and non-locking devs as being different partitions. The change is to simply use the locking dev bit to flag a set of shadow partitions when it comes to lock operations. All other operations treat the locked and unlocked partitions as being identical. 18-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org <25> During work on Edit 23, I noted that on slow and very busy systems, sometimes the driver would go to sleep forever. The problem appears to have been a race condition caused by doing separate timeout/sleep calls without using SPL first. The change here is to use tsleep which provides the equivalent of timeout/sleep timeout/tsleep if the last paremeter is tsleep is set to the time value that would have been given to timeout. I also fixed some duplicate location strings in the tsleep calls. 24-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org <26> Moved a function declaration that generated two warnings with the FULLCONFIG/FULLDRIVER conditionals disabled. Updated the igot function so that it correctly reports limited functions when a sub-set driver is compiled. Eliminated FULLCONFIG conditional and now set controller counts based on the NMATCD #define produced by the config process. Also, disable the audio-related ioctls based on the BOOTMFS conditional to help make the boot floppy kernel smaller. 18-Oct-95 Frank Durda IV bsdmail@nemesis.lonestar.org <27> Incorporated changes needed to move the cdevsw and bdevsw entries into the drivers (including this one). Also include a quick first pass cut at DEVFS suppport. ---------------------------------------------------------------------------*/ /*Match this format: Version_dc(d)__dd-mmm-yy */ static char MATCDVERSION[]="Version 1(26) 18-Oct-95"; /* The following strings may not be changed*/ static char MATCDCOPYRIGHT[] = "Matsushita CD-ROM driver, Copr. 1994,1995 Frank Durda IV"; /* The proceeding strings may not be changed*/ -/* $Id: matcd.c,v 1.18 1996/06/08 09:17:51 bde Exp $ */ +/* $Id: matcd.c,v 1.1.1.1 1996/06/14 10:04:51 asami Exp $ */ /*--------------------------------------------------------------------------- Include declarations ---------------------------------------------------------------------------*/ #include "matcd.h" #include #include #include #include #include #include #include #include #include #ifdef PC98 #include "pc98/pc98/matcd/options.h" /*Conditional compile options and probe port hints*/ #include "pc98/pc98/matcd/matcddrv.h" /*Drive-related defs & strings*/ #include "pc98/pc98/matcd/creative.h" /*Host interface related defs*/ #else #include "i386/isa/matcd/options.h" /*Conditional compile options and probe port hints*/ #include "i386/isa/matcd/matcddrv.h" /*Drive-related defs & strings*/ #include "i386/isa/matcd/creative.h" /*Host interface related defs*/ #endif #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ /*--------------------------------------------------------------------------- Defines and structures ---------------------------------------------------------------------------*/ #define DRIVESPERC 4 /*This is a constant*/ #define TOTALDRIVES NUMCTRLRS*DRIVESPERC /*Max possible drives*/ #define TICKRES 10 /*Our coarse timer resolution*/ #define ISABUSKHZ 8330 /*Number of IN/OUT ISA/sec*/ #define MAXTRKS 101 /*Maximum possible tracks*/ #define RAW_DEVICE 46 /*<16>Dev number for raw device*/ #define MATCDBLK 2048 /*Standard block size*/ #define MATCDRBLK 2352 /*Raw and/or DA block size*/ #define MATCD_RETRYS 5 /*Number of retries for read ops*/ #define MATCD_READ_1 0x80 /*Read state machine defines*/ #define MATCD_READ_2 0x90 /*Read state machine defines*/ struct matcd_volinfo { unsigned char type; /*00 CD-DA or CD-ROM 10 CD-I 20 XA */ unsigned char trk_low; /*Normally 1*/ unsigned char trk_high; /*Highest track number*/ unsigned char vol_msf[3]; /*Size of disc in min/sec/frame*/ }; struct matcd_mbx { short controller; short ldrive; short partition; short port; short iftype; /*<20>Host interface type*/ short retry; short nblk; int sz; u_long skip; struct buf *bp; int p_offset; short count; }; static struct matcd_data { short drivemode; /*Last state drive was set to*/ short flags; short status; /*Last audio-related function*/ int blksize; u_long disksize; short iobase; short iftype; /*<20>Host interface type*/ struct disklabel dlabel; unsigned int partflags[MAXPARTITIONS]; unsigned int openflags; struct matcd_volinfo volinfo; struct matcd_mbx mbx; u_char patch[2]; /*<12>Last known audio routing*/ u_char volume[2]; /*<12>Last known volume setting*/ #ifdef DEVFS void *ra_devfs_token; /* handle for devfs entry */ void *rc_devfs_token; void *a_devfs_token; void *c_devfs_token; void *rla_devfs_token; void *rlc_devfs_token; void *la_devfs_token; void *lc_devfs_token; #endif DEVFS } matcd_data[TOTALDRIVES]; /* Bit equates for matcd_data.flags*/ #define MATCDINIT 0x0001 /*Probe ran on host adapter*/ #define MATCDLABEL 0x0004 /*Valid TOC exists*/ #define MATCDLOCK 0x0008 /*<15>Drive door is locked*/ #define MATCDWARN 0x0020 /*Have reported an open disc change*/ /* Bit equates for matcd_data.partflags*/ #define MATCDOPEN 0x0001 #define MATCDREADRAW 0x0002 /* Error classes returned by chk_error()*/ #define ERR_RETRY 1 /*A retry might recover this*/ #define ERR_INIT 2 /*A retry certainly will get this*/ #define ERR_FATAL 3 /*This cannot be recovered from*/ static struct buf_queue_head request_head[NUMCTRLRS]; /*<18>A queue for each host interface*/ static int nextcontroller=0; /*<18>Number of interface units found*/ static int drivepresent=0; /*<18>Don't change this - see license*/ static int iftype; /*<20>Probe/Attach i.f. type relay*/ static unsigned char if_state[4]={0,0,0,0}; /*<18>State of the host I/F and bus*/ /* Flags in the if_state array */ #define BUSBUSY 0x01 /*<18>Bus is already busy*/ struct matcd_read2 { unsigned char start_msf[3]; unsigned char end_msf[3]; }; /* This mystery structure is supposed to make dynamic driver loading possible. */ static struct kern_devconf kdc_matcd[TOTALDRIVES] = { { 0,0,0, /*Filled in by dev_attach*/ #ifdef PC98 "matcdc",0,{MDDT_PC98,0,"bio"}, pc98_generic_externalize,0,0,PC98_EXTERNALLEN, &kdc_nec0, /*<12>Parent*/ #else "matcdc",0,{MDDT_ISA,0,"bio"}, isa_generic_externalize,0,0,ISA_EXTERNALLEN, &kdc_isa0, /*<12>Parent*/ #endif 0, /*<12>Parent Data*/ DC_IDLE, /*<12>Status*/ "Matsushita CD-ROM Controller" /*<12>This is the description*/ } }; /*<12>*/ /*--------------------------------------------------------------------------- These macros take apart the minor number and yield the partition, drive on controller, and controller. This must match the settings in /dev/MAKEDEV. ---------------------------------------------------------------------------*/ #define matcd_partition(dev) ((minor(dev)) & 0x07) #define matcd_ldrive(dev) (((minor(dev)) & 0x78) >> 3) #define matcd_cdrive(dev) (((minor(dev)) & 0x18) >> 3) #define matcd_controller(dev) (((minor(dev)) & 0x60) >> 5) #ifdef LOCKDRIVE #define matcd_lockable(dev) (((minor(dev)) & 0x80) >> 5) #endif /*LOCKDRIVE*/ /*--------------------------------------------------------------------------- Entry points and other connections to/from kernel - see also conf.h --- not any more :) ---------------------------------------------------------------------------*/ #ifdef PC98 static int matcd_probe(struct pc98_device *dev); static int matcd_attach(struct pc98_device *dev); struct pc98_driver matcddriver={matcd_probe, matcd_attach, "matcdc"}; #else static int matcd_probe(struct isa_device *dev); static int matcd_attach(struct isa_device *dev); struct isa_driver matcddriver={matcd_probe, matcd_attach, "matcdc"}; #endif static d_open_t matcdopen; static d_close_t matcdclose; static d_ioctl_t matcdioctl; static d_psize_t matcdsize; static d_strategy_t matcdstrategy; #define CDEV_MAJOR 46 #define BDEV_MAJOR 17 -extern struct cdevsw matcd_cdevsw; +static struct cdevsw matcd_cdevsw; static struct bdevsw matcd_bdevsw = { matcdopen, matcdclose, matcdstrategy, matcdioctl, /*17*/ nodump, matcdsize, 0, "matcd", &matcd_cdevsw, -1 }; -static struct cdevsw matcd_cdevsw = - { matcdopen, matcdclose, rawread, nowrite, /*46*/ - matcdioctl, nostop, nullreset, nodevtotty,/* SB cd */ - seltrue, nommap, matcdstrategy, "matcd", - &matcd_bdevsw, -1}; - - - /*--------------------------------------------------------------------------- Internal function declarations ---------------------------------------------------------------------------*/ static void matcd_drvinit(void *unused); static void matcd_start(int controller); static void zero_cmd(char *); static void matcd_pread(int port, int count, unsigned char * data); static int matcd_fastcmd(int port,int ldrive,int cdrive, unsigned char * cp); static void matcd_slowcmd(int port,int ldrive,int cdrive, unsigned char * cp); static void matcd_blockread(int state); static void selectdrive(int port,int drive); static void doreset(int port,int cdrive); static int doprobe(int port,int cdrive); static void lockbus(int controller, int ldrive); static void unlockbus(int controller, int ldrive); static int matcd_volinfo(int ldrive); static void draincmd(int port,int cdrive,int ldrive); static int get_error(int port, int ldrive, int cdrive); static int chk_error(int errnum); static int msf_to_blk(unsigned char * cd); #ifdef FULLDRIVER static int matcd_playtracks(int ldrive, int cdrive, int controller, struct ioc_play_track *pt); static int matcd_playmsf(int ldrive, int cdrive, int controller, struct ioc_play_msf *pt); static int matcd_pause(int ldrive, int cdrive, int controller, int action); static int matcd_stop(int ldrive, int cdrive, int controller); static int matcd_level(int ldrive, int cdrive, int controller, struct ioc_vol * volume, int action); static int matcd_patch(int ldrive, int cdrive, int controller, struct ioc_patch * routing); static int matcd_route(int ldrive, int cdrive, int controller, int command); static int matcd_pitch(int ldrive, int cdrive, int controller, struct ioc_pitch * speed); #endif /*FULLDRIVER*/ static int matcd_toc_header(int ldrive, int cdrive, int controller, struct ioc_toc_header * toc); static int matcd_toc_entries(int ldrive, int cdrive, int controller, struct ioc_read_toc_entry *ioc_entry); static int matcd_read_subq(int ldrive, int cdrive, int controller, struct ioc_read_subchannel * sqp); static int matcd_igot(struct ioc_capability * sqp); static int waitforit(int timelimit, int state, int port, char * where); static int get_stat(int port, int ldrive); static int media_chk(struct matcd_data *cd,int errnum, int ldrive,int test); static int matcd_eject(int ldrive, int cdrive, int controller); static int matcd_doorclose(int ldrive, int cdrive, int controller); static int matcd_dlock(int ldrive, int cdrive, int controller, int action); static int docmd(char * cmd, int ldrive, int cdrive, int controller, int port); /*--------------------------------------------------------------------------- matcdopen - Open the device This routine actually gets called every time anybody opens any partition on a drive. But the first call is the one that does all the work. <15> If LOCKDRIVE is enabled, additional minor number devices allow <15> the drive to be locked while being accessed. ---------------------------------------------------------------------------*/ int matcdopen(dev_t dev, int flags, int fmt, struct proc *p) { int cdrive,ldrive,partition,controller,lock; struct matcd_data *cd; int i,z,port; unsigned char cmd[MAXCMDSIZ]; ldrive=matcd_ldrive(dev); cdrive=matcd_cdrive(dev); partition=matcd_partition(dev); controller=matcd_controller(dev); lock=matcd_lockable(dev); cd= &matcd_data[ldrive]; port=cd->iobase; /*and port#*/ if (ldrive >= TOTALDRIVES) return(ENXIO); #ifdef DEBUGOPEN printf("matcd%d: Open: dev %x partition %x controller %x flags %x cdrive %x\n", ldrive,(int)dev,partition,controller,cd->flags, matcd_cdrive(dev)); #endif /*DEBUGOPEN*/ if (!(cd->flags & MATCDINIT)) { /*Did probe find this drive*/ return(ENXIO); } if (!(cd->flags & MATCDLABEL) && cd->openflags) { /*Has drive completely closed?*/ return(ENXIO); /*No, all partitions must close*/ } /* Now, test to see if the media is ready */ lockbus(controller,ldrive); zero_cmd(cmd); cmd[0]=NOP; /*Test drive*/ matcd_slowcmd(port,ldrive,cdrive,cmd); i=waitforit(10*TICKRES,DTEN,port,"matopen"); z=get_stat(port,ldrive); /*Read status byte*/ #ifdef DEBUGOPEN printf("matcd%d Result of NOP is %x %x\n",ldrive,i,z); #endif /*DEBUGOPEN*/ if ((z & MATCD_ST_DSKIN)==0) { /*Is there a disc in the drive?*/ #ifdef DEBUGOPEN printf("matcd%d: No Disc in open\n",ldrive); #endif /*DEBUGOPEN*/ unlockbus(controller, ldrive); /*Release bus lock*/ cd->flags &= ~MATCDLABEL; /*<16>Mark label as invalid*/ if (major(dev)==RAW_DEVICE) { /*<16>Is the char device?*/ return(0); /*<16>Allow Semi open*/ } else { return(ENXIO); /*<16>Normally blow off*/ } } if (z & MATCD_ST_ERROR) { /*Was there an error*/ i=get_error(port,ldrive,cdrive);/*Find out what it was*/ #ifdef DEBUGOPEN printf("matcd%d NOP Error was %x\n",ldrive,i); #endif /*DEBUGOPEN*/ if (cd->openflags) { /*Any parts open?*/ if (media_chk(cd,i,ldrive,0)) { /*<14>Was it a disc chg?*/ #ifdef DEBUGOPEN printf("matcd%d: Disc change detected i %x z %x\n", ldrive,i,z); #endif /*DEBUGOPEN*/ unlockbus(controller, ldrive); /*Release bus lock*/ return(ENOTTY); } } else { media_chk(cd,i,ldrive,1);/*<14>Was it a disc chg?*/ /*<14>Clear volume info*/ } } unlockbus(controller, ldrive); /*Release bus lock*/ /* Here we fill in the disklabel structure although most is hardcoded. */ if ((cd->flags & MATCDLABEL)==0) { bzero(&cd->dlabel,sizeof(struct disklabel)); /* Now we query the drive for the actual size of the media. This is where we find out of there is any media or if the media isn't a Mode 1 or Mode 2/XA disc. See version information about Mode 2/XA support. */ lockbus(controller,ldrive); i=matcdsize(dev); unlockbus(controller, ldrive); /*Release bus lock*/ #ifdef DEBUGOPEN printf("matcd%d: Bus unlocked in open\n",ldrive); #endif /*DEBUGOPEN*/ if (i < 0) { printf("matcd%d: Could not read the disc size\n",ldrive); return(ENXIO); } /*matcdsize filled in rest of dlabel*/ /* Based on the results, fill in the variable entries in the disklabel */ cd->dlabel.d_secsize=cd->blksize; cd->dlabel.d_ncylinders=(cd->disksize/100)+1; cd->dlabel.d_secperunit=cd->disksize; cd->dlabel.d_partitions[0].p_size=cd->disksize; cd->dlabel.d_checksum=dkcksum(&cd->dlabel); /* Now fill in the hardcoded section */ /*123456789012345678*/ strncpy(cd->dlabel.d_typename,"Matsushita CDR ",16); strncpy(cd->dlabel.d_packname,"(c) 1994, fdiv ",16); cd->dlabel.d_magic=DISKMAGIC; cd->dlabel.d_magic2=DISKMAGIC; cd->dlabel.d_nsectors=100; cd->dlabel.d_secpercyl=100; cd->dlabel.d_ntracks=1; cd->dlabel.d_interleave=1; cd->dlabel.d_rpm=300; cd->dlabel.d_npartitions=1; /*See note below*/ cd->dlabel.d_partitions[0].p_offset=0; cd->dlabel.d_partitions[0].p_fstype=9; cd->dlabel.d_flags=D_REMOVABLE; /* I originally considered allowing the partition match tracks or sessions on the media, but since you are allowed up to 99 tracks in the RedBook world, this would not fit in with the BSD fixed partition count scheme. So ioctls will be used to shift the track to be accessed into partition 1. */ cd->flags |= MATCDLABEL; /*Mark drive as having TOC*/ } #ifdef DEBUGOPEN printf("matcd%d open2: partition=%d disksize=%d blksize=%x flags=%x\n", ldrive,partition,(int)cd->disksize,cd->blksize,cd->flags); #endif /*DEBUGOPEN*/ #ifdef LOCKDRIVE if (cd->openflags==0 && lock) { zero_cmd(cmd); cmd[0]=LOCK; /*Lock drive*/ cmd[1]=1; docmd(cmd,ldrive,cdrive,controller,port);/*<15>Issue cmd*/ cd->flags |= MATCDLOCK; /*<15>Drive is now locked*/ } #endif /*LOCKDRIVE*/ cd->openflags |= (1<<(partition+lock));/*<24>Mark partition open*/ if (partition==RAW_PART || (partition < cd->dlabel.d_npartitions && cd->dlabel.d_partitions[partition].p_fstype != FS_UNUSED)) { cd->partflags[partition] |= MATCDOPEN; if (partition == RAW_PART) { cd->partflags[partition] |= MATCDREADRAW; } #ifdef DEBUGOPEN printf("matcd%d: Open is complete - openflags %x\n", ldrive,cd->openflags); #endif /*DEBUGOPEN*/ return(0); } #ifdef DEBUGOPEN printf("matcd%d: Open FAILED\n",ldrive); #endif /*DEBUGOPEN*/ return(ENXIO); } /*--------------------------------------------------------------------------- matcdclose - Close the device Close may not do much other than clear some driver settings. Note that audio playback will continue. <15> If you define LOCKDRIVE, and the drive has been opened using <15> one of the locking minor numbers, code in close will unlock <15> the drive. ---------------------------------------------------------------------------*/ int matcdclose(dev_t dev, int flags, int fmt, struct proc *p) { int ldrive,cdrive,port,partition,controller,lock; struct matcd_data *cd; #ifdef LOCKDRIVE unsigned char cmd[MAXCMDSIZ]; #endif /*LOCKDRIVE*/ ldrive=matcd_ldrive(dev); cdrive=matcd_cdrive(dev); lock=matcd_lockable(dev); cd=matcd_data+ldrive; port=cd->iobase; /*and port#*/ if (ldrive >= TOTALDRIVES) return(ENXIO); partition = matcd_partition(dev); controller=matcd_controller(dev); #ifdef DEBUGOPEN printf("matcd%d: Close partition=%d flags %x openflags %x partflags %x\n", ldrive,partition,cd->flags,cd->openflags, cd->partflags[partition]); #endif /*DEBUGOPEN*/ if (!(cd->flags & MATCDINIT)) return(ENXIO); cd->partflags[partition] &= ~(MATCDOPEN|MATCDREADRAW); cd->openflags &= ~(1<<(partition+lock)); if (cd->openflags==0) { /*<24>Really last close?*/ #ifdef LOCKDRIVE if (cd->flags & MATCDLOCK) { /*<24>Was drive locked?*/ zero_cmd(cmd); /*Yes, so unlock it*/ cmd[0]=LOCK; /*Unlock drive*/ docmd(cmd,ldrive,cdrive,controller,port); } #endif /*LOCKDRIVE*/ cd->flags &= ~(MATCDWARN|MATCDLOCK); /*<15>Clear warning flag*/ } return(0); } /*--------------------------------------------------------------------------- matcdstrategy - Accepts I/O requests from kernel for processing This routine accepts a read request block pointer (historically but somewhat inaccurately called *bp for buffer pointer). Various sanity checks are performed on the request. When we are happy with the request and the state of the device, the request is added to the queue of requests for the interface that the drive is connected to. We support multiple interfaces so there are multiple queues. Once the request is added, we call the matcd_start routine to start the device in case it isn't doing something already. All I/O including ioctl requests rely on the current request starting the next one before exiting. ---------------------------------------------------------------------------*/ void matcdstrategy(struct buf *bp) { struct matcd_data *cd; int s; int ldrive,controller; ldrive=matcd_ldrive(bp->b_dev); controller=matcd_controller(bp->b_dev); cd= &matcd_data[ldrive]; #ifdef DEBUGIO printf("matcd%d: Strategy: buf=0x%lx, block#=%ld bcount=%ld\n", ldrive,(unsigned long)bp,bp->b_blkno,bp->b_bcount); #endif /*DEBUGIO*/ if (ldrive >= TOTALDRIVES || bp->b_blkno < 0) { printf("matcd%d: Bogus parameters received - kernel may be corrupted\n",ldrive); bp->b_error=EINVAL; goto bad; } if (!(cd->flags & MATCDLABEL)) { bp->b_error = EIO; goto bad; } if (!(bp->b_flags & B_READ)) { bp->b_error = EROFS; goto bad; } if (bp->b_bcount==0) /*Request is zero-length - all done*/ goto done; if (matcd_partition(bp->b_dev) != RAW_PART) { if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { goto done; } } else { bp->b_pblkno=bp->b_blkno; bp->b_resid=0; } s=splbio(); /*Make sure we don't get intr'ed*/ tqdisksort(&request_head[controller], bp);/*Add new request (bp) to queue (dp and sort the requests in a way that may not be ideal for CD-ROM media*/ matcd_start(controller); /*Ok, with our newly sorted queue, see if we can start an I/O operation right now*/ splx(s); /*Return priorities to normal*/ return; /*All done*/ bad: bp->b_flags |= B_ERROR; /*Request bad in some way*/ done: bp->b_resid = bp->b_bcount; /*Show amount of data un read*/ biodone(bp); /*Signal we have done all we plan to*/ return; } /*--------------------------------------------------------------------------- matcd_start - Pull a request from the queue and consider doing it. ---------------------------------------------------------------------------*/ static void matcd_start(int controller) { struct matcd_data *cd; struct buf *bp; struct partition *p; int part,ldrive; bp = TAILQ_FIRST(&request_head[controller]); if (bp == NULL) { /*Nothing on read queue to do?*/ wakeup((caddr_t)&matcd_data->status); /*Wakeup any blocked*/ return; /* opens, ioctls, etc*/ } ldrive=matcd_ldrive(bp->b_dev); /*Get logical drive#*/ cd=&matcd_data[ldrive]; /*Get pointer to data for this drive*/ #ifdef DEBUGIO printf("matcd%d: In start controller %d\n",ldrive,controller); #endif /*DEBUGIO*/ if (if_state[controller] & BUSBUSY) { #ifdef DEBUGIO printf("matcd%d: Dropping thread in start, controller %d\n", ldrive,controller); #endif /*DEBUGIO*/ return; } /* Ok, the controller is idle (not necessarily the drive) and so get the command to do and issue it */ TAILQ_REMOVE(&request_head[controller], bp, b_act); part=matcd_partition(bp->b_dev); p=cd->dlabel.d_partitions + part; if_state[controller] |= BUSBUSY;/*<18>Mark bus as busy*/ cd->mbx.ldrive=ldrive; /*Save current logical drive*/ cd->mbx.controller=controller; /*and controller*/ cd->mbx.partition=part; /*and partition (2048 vs 2532)*/ cd->mbx.port=cd->iobase; /*and port#*/ cd->mbx.iftype=cd->iftype; /*<20>interface type*/ cd->mbx.retry=MATCD_RETRYS; /*and the retry count*/ cd->mbx.bp=bp; /*and the bp*/ cd->mbx.p_offset=p->p_offset; /*and where the data will go*/ matcd_blockread(MATCD_READ_1+ldrive); /*Actually start the read*/ return; /*Dropping thread. matcd_blockread must have scheduled a timeout or we will go to sleep forever*/ } /*--------------------------------------------------------------------------- matcdioctl - Process things that aren't block reads In this driver, ioctls are used mainly to change the mode the drive is running in, play audio and other things that don't fit into the block read scheme of things. ---------------------------------------------------------------------------*/ int matcdioctl(dev_t dev, int command, caddr_t addr, int flags, struct proc *p) { struct matcd_data *cd; int ldrive,cdrive,partition; int port, controller; #ifdef DEBUGIOCTL int i; #endif /*DEBUGIOCTL*/ ldrive=matcd_ldrive(dev); cdrive=matcd_cdrive(dev); partition=matcd_partition(dev); controller=ldrive>>2; cd = &matcd_data[ldrive]; port=cd->iobase; #ifdef DEBUGIOCTL printf("matcd%d: ioctl %x cdrive %x parms ",ldrive,command,cdrive); for (i=0;i<10;i++) { printf("%02x ",(unsigned int)addr[i]); } printf(" flags %x\n",cd->flags); #endif /*DEBUGIOCTL*/ if (command==CDIOCCLOSE) /*<16>Allow close if door open*/ return(matcd_doorclose(ldrive, cdrive, controller)); if (!(cd->flags & MATCDLABEL)) /*Did we read TOC OK?*/ return(EIO); /*<16>then drive really isn't ready*/ switch(command) { case DIOCSBAD: return(EINVAL); case DIOCGDINFO: *(struct disklabel *) addr = cd->dlabel; return(0); case DIOCGPART: ((struct partinfo *) addr)->disklab=&cd->dlabel; ((struct partinfo *) addr)->part= &cd->dlabel.d_partitions[matcd_partition(dev)]; return(0); case DIOCWDINFO: case DIOCSDINFO: if ((flags & FWRITE) == 0) { return(EBADF); } else { return setdisklabel(&cd->dlabel, (struct disklabel *) addr, 0); } case DIOCWLABEL: return(EBADF); case CDIOCEJECT: return(matcd_eject(ldrive, cdrive, controller)); case CDIOCALLOW: return(matcd_dlock(ldrive, cdrive, controller,0)); case CDIOCPREVENT: return(matcd_dlock(ldrive, cdrive, controller, MATCDLOCK)); #ifdef FULLDRIVER case CDIOCPLAYTRACKS: return(matcd_playtracks(ldrive, cdrive, controller, (struct ioc_play_track *) addr)); case CDIOCPLAYMSF: return(matcd_playmsf(ldrive, cdrive, controller, (struct ioc_play_msf *) addr)); case CDIOCRESUME: return(matcd_pause(ldrive, cdrive, controller,RESUME)); case CDIOCPAUSE: return(matcd_pause(ldrive, cdrive, controller,0)); case CDIOCSTOP: return(matcd_stop(ldrive, cdrive, controller)); case CDIOCGETVOL: case CDIOCSETVOL: return(matcd_level(ldrive, cdrive, controller, (struct ioc_vol *) addr, command)); case CDIOCSETMONO: /*<12>This drive can't do mono*/ return(EINVAL); /*<12>but it looks like it should*/ /*<12>SRC OUT SRC OUT*/ case CDIOCSETSTEREO: /*<12>0 -> L 1 -> R*/ case CDIOCSETMUTE: /*<12>0 -> NULL 1 -> NULL*/ case CDIOCSETLEFT: /*<12>0 -> L&R 1 -> NULL*/ case CDIOCSETRIGHT: /*<12>0 -> NULL 1 -> L&R*/ /*<12>Adjust audio routing*/ return(matcd_route(ldrive, cdrive, controller, command)); case CDIOCSETPATCH: /*<12>Allow precise routing*/ return(matcd_patch(ldrive, cdrive, controller, (struct ioc_patch *) addr)); case CDIOCPITCH: /*<12>Adjust playback speed*/ return(matcd_pitch(ldrive, cdrive, controller, (struct ioc_pitch *) addr)); case CDIOCSTART: /*<12>Only reason this isn't*/ return(EINVAL); /*<12>implemented is I can't find out*/ /*<12>what it should do!*/ #endif /*FULLDRIVER*/ case CDIOREADTOCHEADER: return(matcd_toc_header(ldrive, cdrive, controller, (struct ioc_toc_header *) addr)); case CDIOREADTOCENTRYS: return(matcd_toc_entries(ldrive, cdrive, controller, (struct ioc_read_toc_entry *) addr)); case CDIOCREADSUBCHANNEL: return(matcd_read_subq(ldrive, cdrive, controller, (struct ioc_read_subchannel *) addr)); case CDIOCCAPABILITY: /*<16>Request drive/driver capability*/ return(matcd_igot((struct ioc_capability *) addr)); case CDIOCRESET: /*<12>There is no way to hard reset*/ return(EINVAL); /*<12>just one drive*/ default: return(ENOTTY); } } /*--------------------------------------------------------------------------- matcdsize - Reports how many blocks exist on the disc. ---------------------------------------------------------------------------*/ int matcdsize(dev_t dev) { int size,blksize; int ldrive,part; struct matcd_data *cd; ldrive=matcd_ldrive(dev); part=matcd_partition(dev); if (part==RAW_PART) blksize=MATCDRBLK; /*2352*/ else blksize=MATCDBLK; /*2048*/ cd = &matcd_data[ldrive]; if (matcd_volinfo(ldrive) >= 0) { cd->blksize=blksize; size=msf_to_blk((char * )&cd->volinfo.vol_msf); cd->disksize=size*(blksize/DEV_BSIZE); #ifdef DEBUGOPEN printf("matcd%d: Media size %d\n",ldrive,size); #endif /*DEBUGOPEN*/ return(0); } return(-1); } /*--------------------------------------------------------------------------- matcd_probe - Search for host interface/adapters The probe routine hunts for the first drive on the interface since there is no way to locate just the adapter. It also resets the entire drive chain while it is there. matcd_attach() takes care of the rest of the initialization. The probe routine can be compiled two ways. In AUTOHUNT mode, the kernel config file can say "port?" and we will check all ports listed in the port_hint array (see above). Without AUTOHUNT set, the config file must list a specific port address to check. Note that specifying the explicit addresses makes boot-up a lot faster. The probe will locate Panasonic/Creative interface on the following Creative adapter boards: #1330A Sound Blaster PRO #1730 Sound Blaster 16 #1740 Sound Blaster 16 (cost reduced) #2230 Sound Blaster 16 (cost reduced) #2770 Sound Blaster 16 Value (cost reduced) #1810 omniCD upgrade kit adapter card (stand-alone CD) #3100 PhoneBlaster SB16 + Sierra 14.4K modem combo Creative releases a newer and cheaper-to-make Sound Blaster board every few months, so by the original release date of this software, there are probably 8 different board models called Sound Blaster 16. These include "Vibra", "Value", etc. Please report additional part numbers and board descriptions and new port numbers that work to the author. ---------------------------------------------------------------------------*/ static int #ifdef PC98 matcd_probe(struct pc98_device *dev) #else matcd_probe(struct isa_device *dev) #endif { int i,cdrive; unsigned char y; int port = dev->id_iobase; /*Take port hint from config file*/ cdrive=nextcontroller; /*Controller defined by pass for now*/ if (nextcontroller==NUMCTRLRS) { printf("matcdc%d: - Too many interfaces specified in config\n", nextcontroller); return(0); } if (nextcontroller==0) { /*Very first time to be called*/ for (i=0; iid_iobase=-1; /*Put port ? back*/ return(0);/*Nothing left to try*/ } if (port!=0) { /*Untested port found*/ dev->id_iobase=port; port_hints[i]=0;/*Don't use that port again*/ if (doprobe(port,cdrive)==0) return(NUMPORTS); } } dev->id_iobase=-1; /*Put port ? back as it was*/ return(0); /*Interface not found*/ } else { /*Config specified a port*/ i=0; /*so eliminate it from the hint list*/ for(i=0;;i++) { /*or we might try to assign it again*/ if (port_hints[i]== -1) break; /*End of list*/ if (port_hints[i]==port) { port_hints[i]=0; /*Clear duplicate*/ break; } } if (doprobe(port,cdrive)==0) return(NUMPORTS); else return(0); } #else /*AUTOHUNT*/ if (port==-1) { printf("matcdc%d: AUTOHUNT disabled but port? specified in config\n", nextcontroller); return(0); } if (doprobe(port,cdrive)==0) return(NUMPORTS); else return(0); #endif /*AUTOHUNT*/ } /*--------------------------------------------------------------------------- doprobe - Common probe code that actually checks the ports we have decided to test. <20> Edit 20 changes adds code to determine if the host interface is one that behaves like the Creative SoundBlaster cards, or whether the host interface like those used by some boards made by Media Vision and a version known as Lasermate. ---------------------------------------------------------------------------*/ int doprobe(int port,int cdrive) { unsigned char cmd[MAXCMDSIZ]; int i; #ifdef RESETONBOOT doreset(port,cdrive); /*Reset what might be our device*/ #endif /*RESETONBOOT*/ outb(port+PHASE,0); /*<16>Guarantee status phase*/ zero_cmd(cmd); cmd[0]=NOP; /*A reasonably harmless command. This command will fail after power-up or after reset. That's OK*/ #ifdef RESETONBOOT if (((inb(port+STATUS) & (DTEN|STEN)) != (DTEN|STEN)) || (inb(port+DATA) != 0xff)) return(-1); /*<20>Something detected but it isn't the device we wanted*/ #endif /*RESETONBOOT*/ if (matcd_fastcmd(port,0,0,cmd)==0) {/*Issue command*/ outb(port+PHASE,1); /*<20>Switch to Creative Data phase*/ i=inb(port+CMD); /*<20>Read a byte in data phase*/ outb(port+PHASE,0); /*<20>Switch to Creative Status phase*/ if ((inb(port+STATUS) & (DTEN|STEN)) == (DTEN|STEN)) { /*<20>Drive went idle*/ iftype=1; /*<20>It is not a Creative interface.*/ } else { /*<20>Status byte still available*/ iftype=0; inb(port+CMD); /*<20>Read status byte*/ } #ifdef DEBUGPROBE printf("matcdc%d: Probe found something\n",nextcontroller); #endif /*DEBUGPROBE*/ /*------Don't change anything below this line - see license -----------------*/ if (drivepresent==0) { /*Don't change*/ printf("matcd - Matsushita (Panasonic) CD-ROM Driver by FDIV, %s\n", /*Don't change*/ MATCDVERSION); /*Don't change*/ drivepresent++; /*Don't change*/ if (drivepresent==0) /*Don't change - make LINT happy*/ printf("%s\n",MATCDCOPYRIGHT); /*Don't change*/ } /*Don't change*/ /*------Don't change anything above this line - see license -----------------*/ return(0); /*Drive 0 detected*/ } #ifdef DEBUGPROBE printf("matcdc%d: Probe DID NOT find something\n",nextcontroller); #endif /*DEBUGPROBE*/ return(1); } /*--------------------------------------------------------------------------- <12> matcd_register - Something to handle dynamic driver loading. Sorry for the lousy description but no one could point me to anything that explained what it is for either. Added in Edit 12. ---------------------------------------------------------------------------*/ #ifdef PC98 static inline void matcd_register(struct pc98_device *id) #else static inline void matcd_register(struct isa_device *id) #endif { if(id->id_unit) { kdc_matcd[id->id_unit]=kdc_matcd[0]; } kdc_matcd[id->id_unit].kdc_unit=id->id_unit; #ifdef PC98 kdc_matcd[id->id_unit].kdc_pc98=id; #else kdc_matcd[id->id_unit].kdc_isa=id; #endif dev_attach(&kdc_matcd[id->id_unit]); return; } /*--------------------------------------------------------------------------- matcd_attach - Locates drives on the adapters that were located. If we got here, we located an interface and at least one drive. Now we figure out how many drives are under that interface. The Panasonic interface is too simple to call it a controller, but in the existing PDP model, that is what it would be. ---------------------------------------------------------------------------*/ static int #ifdef PC98 matcd_attach(struct pc98_device *dev) #else matcd_attach(struct isa_device *dev) #endif { int i; unsigned int z,cdrive; unsigned char cmd[MAXCMDSIZ]; unsigned char data[12]; struct matcd_data *cd; int port = dev->id_iobase; /*Take port ID selected in probe()*/ #ifdef DEBUGPROBE printf("matcdc: Attach dev %x id_unit %d\n", (unsigned int)dev,dev->id_unit); #endif /*DEBUGPROBE*/ printf("matcdc%d Host interface type %d\n", nextcontroller,iftype); TAILQ_INIT(&request_head[nextcontroller]); for (cdrive=0; cdrive<4; cdrive++) { /*We're hunting drives...*/ zero_cmd(cmd); cmd[0]=NOP; /*A reasonably harmless command. This command will fail after power-up or after reset. It's OK*/ i=cdrive+(DRIVESPERC*nextcontroller); if (matcd_fastcmd(port,i,cdrive,cmd)==0) { /*Issue cmd*/ z=get_stat(port,cdrive);/*Read status byte*/ if ((z & MATCD_ST_ERROR)) { /*If there was an error, we must ask for error info or subsequent cmds fail*/ zero_cmd(cmd); cmd[0]=READERROR; /*Inquire*/ matcd_fastcmd(port,i,cdrive,cmd); matcd_pread(port,8,data);/*Read data returned*/ z=get_stat(port,i);/*Read status byte*/ #ifdef DEBUGPROBE printf("matcd%d: Status byte %x ",i,z); #endif /*DEBUGPROBE*/ } zero_cmd(cmd); cmd[0]=READID; /*Get drive ID*/ matcd_fastcmd(port,i,cdrive,cmd); matcd_pread(port,10,data);/*Read Drive Parm*/ get_stat(port,i); /*Read and toss status byte*/ data[10]=0; /*Build ASCIZ string*/ printf("matcd%d: [%s] ",i,data); cd=&matcd_data[i]; cd->flags |= MATCDINIT; cd->iobase=dev->id_iobase; cd->iftype=iftype; cd->openflags=0; cd->volume[0]=cd->volume[1]=DEFVOL; /*<12>Match volume drive resets to*/ cd->patch[0]=0x01; /*<12>Channel 0 to Left*/ cd->patch[1]=0x02; /*<12>Channel 1 to Right*/ cd->status=CD_AS_NO_STATUS; for (i=0; ipartflags[i]=0; } #ifdef DEVFS cd->ra_devfs_token = devfs_add_devswf(&matcd_cdevsw, dkmakeminor(i, 0, 0), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rmatcd%da", i); cd->rc_devfs_token = devfs_add_devswf(&matcd_cdevsw, dkmakeminor(i, 0, RAW_PART), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rmatcd%dc", i); cd->a_devfs_token = devfs_add_devswf(&matcd_bdevsw, dkmakeminor(i, 0, 0), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "matcd%da", i); cd->c_devfs_token = devfs_add_devswf(&matcd_bdevsw, dkmakeminor(i, 0, RAW_PART), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "matcd%dc", i); cd->rla_devfs_token = devfs_add_devswf(&matcd_cdevsw, 0x80 | dkmakeminor(i, 0, 0), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rmatcd%dla", i); cd->rlc_devfs_token = devfs_add_devswf(&matcd_cdevsw, 0x80 | dkmakeminor(i, 0, RAW_PART), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rmatcd%dc", i); cd->la_devfs_token = devfs_add_devswf(&matcd_bdevsw, 0x80 | dkmakeminor(i, 0, 0), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "matcd%dla", i); cd->lc_devfs_token = devfs_add_devswf(&matcd_bdevsw, 0x80 | dkmakeminor(i, 0, RAW_PART), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "matcd%dlc", i); #endif } } nextcontroller++; /*Bump ctlr assign to next number*/ printf("\n"); /*End line of drive reports*/ return(1); } /*--------------------------------------------------------------------------- zero_cmd - Initialize command buffer ---------------------------------------------------------------------------*/ void zero_cmd(char * lcmd) { int i; for (i=0; iEnable data read*/ while ((inb(port+STATUS) & (DTEN|STEN)) == STEN) { inb(port+DATA); /*<21>Ok for Creative*/ inb(port+ALTDATA); /*<21>Ok for others*/ i++; } outb(port+PHASE,0); #ifdef DEBUGCMD printf("%d bytes read\n",i); #endif /*DEBUGCMD*/ } #ifdef DEBUGCMD printf("matcd%d: Now read status: ",ldrive); #endif /*DEBUGCMD*/ i=get_stat(port,ldrive); /*Read status byte*/ z=inb(port+STATUS); /*Read bus status*/ #ifdef DEBUGCMD printf("Data byte %x and status is now %x\n",i,z); #endif /*DEBUGCMD*/ if ((z & (DTEN|STEN)) != (DTEN|STEN)) { printf("matcd%d: Bus not idle %x - resetting\n", cdrive,inb(port+STATUS)); doreset(port,cdrive); } return; } /*--------------------------------------------------------------------------- selectdrive - Swaps drive select bits On Creative SB/SB16/stand-alone adapters, possibly to make them hard to reverse engineer, the drive select signals are swapped. ---------------------------------------------------------------------------*/ void selectdrive(int port,int drive) { switch(drive) { case 0: /*0x00 -> 0x00*/ outb(port+SELECT,CRDRIVE0); break; case 1: /*0x01 -> 0x02*/ outb(port+SELECT,CRDRIVE1); break; case 2: /*0x02 -> 0x01*/ outb(port+SELECT,CRDRIVE2); break; case 3: /*0x03 -> 0x03*/ outb(port+SELECT,CRDRIVE3); break; } return; } /*--------------------------------------------------------------------------- matcd_pread - Read small blocks of control data from a drive ---------------------------------------------------------------------------*/ void matcd_pread(int port, int count, unsigned char * data) { int i; for (i=0; iiobase; if (cd->drivemode==mode) { return(0); /*Drive already set*/ } /* The drive is not in the right mode, so we need to set it. */ zero_cmd(cmd); cmd[0]=MODESELECT; /*Set drive transfer modes*/ /* cmd[1]=BLOCKPARAM; BLOCKPARAM==0*/ cmd[2]=mode; switch(mode) { case MODE_DATA: cmd[3]=0x08; /*2048 bytes*/ break; case MODE_USER: cmd[3]=0x09; /*2352 bytes*/ cmd[4]=0x30; break; case MODE_DA: cmd[3]=0x09; /*2352 bytes*/ cmd[4]=0x30; break; } i=0; while(retries-- > 0) { i=matcd_fastcmd(port,ldrive,cdrive,cmd); get_stat(port,ldrive); /*Read and toss status byte*/ if (i==0) { cd->drivemode=mode; /*Set new mode*/ return(i); } get_error(port,ldrive,cdrive); } cd->drivemode=MODE_UNKNOWN; /*We failed*/ return(i); } /*--------------------------------------------------------------------------- matcd_volinfo - Read information from disc Table of Contents ---------------------------------------------------------------------------*/ static int matcd_volinfo(int ldrive) { struct matcd_data *cd; int port,i; int z,cdrive; int retry; unsigned char cmd[MAXCMDSIZ]; unsigned char data[12]; retry=10; /*<16>This may take a long time*/ cd = &matcd_data[ldrive]; cdrive=ldrive&0x03; port=cd->iobase; #ifdef DEBUGOPEN printf("matcd%d: In volinfo, port %x\n",ldrive,port); #endif /*DEBUGOPEN*/ while(retry>0) { zero_cmd(cmd); cmd[0]=READDINFO; /*Read Disc Info*/ matcd_slowcmd(port,ldrive,cdrive,cmd); i=waitforit(10*TICKRES,DTEN,port,"matvinf"); if (i) { /*THIS SHOULD NOT HAPPEN*/ z=get_stat(port,ldrive);/*Read status byte*/ printf("matcd%d: command failed, status %x\n", ldrive,z); return(-1); } matcd_pread(port, 6, data); /*Read data returned*/ z=get_stat(port,ldrive);/*Read status byte*/ #ifdef DEBUGOPEN printf("matcd%d: Data got was %x %x %x %x %x %x ",ldrive, data[0],data[1],data[2], data[3],data[4],data[5]); printf("status byte %x\n",z); #endif /*DEBUGOPEN*/ if ((z & MATCD_ST_ERROR)==0) break; /*No Error*/ /* If media change or other error, you have to read error data or the drive will reject subsequent commands. */ if (chk_error(get_error(port, ldrive, cdrive))==ERR_FATAL) { #ifdef DEBUGOPEN printf("matcd%d: command failed, status %x\n", ldrive,z); #endif /*DEBUGOPEN*/ return(-1); } tsleep((caddr_t)&nextcontroller, PRIBIO, "matvi2", hz); if ((--retry)==0) return(-1); #ifdef DEBUGOPEN printf("matcd%d: Retrying",ldrive); #endif /*DEBUGOPEN*/ } #ifdef DEBUGOPEN printf("matcd%d: Status port %x \n",ldrive,inb(port+STATUS)); #endif /*DEBUGOPEN*/ cd->volinfo.type=data[0]; cd->volinfo.trk_high=data[2]; cd->volinfo.trk_low=data[1]; cd->volinfo.vol_msf[0]=data[3]; cd->volinfo.vol_msf[1]=data[4]; cd->volinfo.vol_msf[2]=data[5]; if (cd->volinfo.trk_low + cd->volinfo.trk_high) { cd->flags |= MATCDLABEL; return(0); } return(-1); } /*--------------------------------------------------------------------------- blk_to_msf - Convert block numbers into CD disk block ids ---------------------------------------------------------------------------*/ static void blk_to_msf(int blk, unsigned char *msf) { blk=blk+150; /*2 seconds skip required to reach ISO data*/ msf[0]=blk/4500; blk=blk%4500; msf[1]=blk/75; msf[2]=blk%75; return; } /*--------------------------------------------------------------------------- msf_to_blk - Convert CD disk block ids into block numbers ---------------------------------------------------------------------------*/ static int msf_to_blk(unsigned char * cd) { return(((cd[0]*60) /*Convert MSF to*/ +cd[1])*75 /*Blocks minus 2*/ +cd[2]-150); /*seconds*/ } /*--------------------------------------------------------------------------- matcd_blockread - Performs actual background disc I/O operations This routine is handed the block number to read, issues the command to the drive, waits for it to complete, reads the data or error, retries if needed, and returns the results to the host. ---------------------------------------------------------------------------*/ static void matcd_blockread(int state) { struct matcd_mbx *mbx; int ldrive,cdrive; int port, controller; short iftype; struct buf *bp; struct matcd_data *cd; int i; struct matcd_read2 rbuf; int blknum; caddr_t addr; int status; int errtyp; int phase; unsigned char cmd[MAXCMDSIZ]; mbx = &matcd_data[state & 0x0f].mbx; ldrive=mbx->ldrive; /*ldrive is logical drive #*/ cdrive=ldrive & 0x03; /*cdrive is drive # on a controller*/ port=mbx->port; /*port is base port for i/f*/ iftype=mbx->iftype; bp= mbx->bp; cd=&matcd_data[ldrive]; controller = cd->mbx.controller; #ifdef DEBUGIO printf("matcd%d: Show state %x cdrive %d partition %d\n", ldrive,state,cdrive,mbx->partition); #endif /*DEBUGIO*/ loop: #ifdef DEBUGIO printf("matcd%d: Top dp %x\n",ldrive,(unsigned int)dp); #endif /*DEBUGIO*/ switch (state & 0xf0) { case MATCD_READ_1: #ifdef DEBUGIO printf("matcd%d: State 1 cd->flags %x\n",ldrive,cd->flags); #endif /*DEBUGIO*/ /* to check for raw/cooked mode */ if (cd->partflags[mbx->partition] & MATCDREADRAW) { mbx->sz = MATCDRBLK; i=matcd_setmode(ldrive, MODE_DA); #ifdef DEBUGIO printf("matcd%d: Set MODE_DA result %d\n",ldrive,i); #endif /*DEBUGIO*/ } else { mbx->sz = cd->blksize; i=matcd_setmode(ldrive, MODE_DATA); #ifdef DEBUGIO printf("matcd%d: Set MODE_DATA result %d\n",ldrive,i); #endif /*DEBUGIO*/ } /*for first block*/ #ifdef DEBUGIO printf("matcd%d: A mbx %x bp %x b_bcount %x sz %x\n", ldrive,(unsigned int)mbx,(unsigned int)bp, (unsigned int)bp->b_bcount,mbx->sz); #endif /*DEBUGIO*/ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; mbx->skip=0; nextblock: #ifdef DEBUGIO printf("matcd%d: at Nextblock b_blkno %d\n", ldrive,(unsigned int)bp->b_blkno); #endif /*DEBUGIO*/ blknum=(bp->b_blkno / (mbx->sz/DEV_BSIZE)) + mbx->p_offset + mbx->skip/mbx->sz; blk_to_msf(blknum,rbuf.start_msf); zero_cmd(cmd); cmd[0]=READ; /*Get drive ID*/ cmd[1]=rbuf.start_msf[0]; cmd[2]=rbuf.start_msf[1]; cmd[3]=rbuf.start_msf[2]; cmd[6]=1; /*Xfer only one block*/ matcd_slowcmd(port,ldrive,cdrive,cmd); /* Now that we have issued the command, check immediately to see if data is ready. The drive has read-ahead caching, so it is possible the data is already in the drive buffer. If the data is not ready, schedule a wakeup and later on this code will run again to see if the data is ready then. */ case MATCD_READ_2: state=MATCD_READ_2+ldrive; phase = (inb(port+STATUS)) & (DTEN|STEN); #ifdef DEBUGIO printf("matcd%d: In state 2 status %x ",ldrive,phase); #endif /*DEBUGIO*/ switch(phase) { case (DTEN|STEN): /*DTEN==H STEN==H*/ #ifdef DEBUGIO printf("matcd%d: Sleeping\n",ldrive); #endif /*DEBUGIO*/ timeout((timeout_func_t)matcd_blockread, (caddr_t)MATCD_READ_2+ldrive,hz/100); return; case STEN: /*DTEN=L STEN=H*/ case 0: /*DTEN=L STEN=L*/ #ifdef DEBUGIO printf("matcd%d: Data Phase\n",ldrive); #endif /*DEBUGIO*/ addr=bp->b_un.b_addr + mbx->skip; #ifdef DEBUGIO printf("matcd%d: Xfer Addr %x size %x", ldrive,(unsigned int)addr,mbx->sz); i=0; /*<20>Reset read count*/ #endif /*DEBUGIO*/ if (iftype==0) { /*<20>Creative host I/F*/ outb(port+PHASE,1); /*Enable data read*/ while((inb(port+STATUS) & (DTEN|STEN))==STEN) { *addr++=inb(port+DATA); #ifdef DEBUGIO i++; #endif /*DEBUGIO*/ } outb(port+PHASE,0); /*Disable read*/ } else { /*<20>Not Creative interface*/ while((inb(port+STATUS) & (DTEN|STEN))==STEN) { *addr++=inb(port+ALTDATA); #ifdef DEBUGIO i++; #endif /*DEBUGIO*/ } } #ifdef DEBUGIO printf("matcd%d: Read %d bytes\n",ldrive,i); #endif /*DEBUGIO*/ /* Now, wait for the Status phase to arrive. This will also tell us if any went wrong with the request. */ while((inb(port+STATUS)&(DTEN|STEN)) != DTEN); status=get_stat(port,ldrive); /*Read status byte*/ #ifdef DEBUGIO printf("matcd%d: Status port %x byte %x ", ldrive,i,status); #endif /*DEBUGIO*/ if (status & MATCD_ST_ERROR) { i=get_error(port,ldrive,cdrive); printf("matcd%d: %s while reading block %d [Soft]\n", ldrive,matcderrors[i],(int)bp->b_blkno); media_chk(cd,i,ldrive,0);/*<14>was wrong place*/ } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; /*Oooooh, you flunk the course*/ } bp->b_resid=0; biodone(bp); /*Signal transfer complete*/ unlockbus(ldrive>>2, ldrive); /*Release bus lock*/ matcd_start(controller);/*See if other drives have work*/ return; /* Here we skipped the data phase and went directly to status. This indicates a hard error. */ case DTEN: /*DTEN=H STEN=L*/ status=get_stat(port,ldrive); /*Read status byte*/ #ifdef DEBUGIO printf("matcd%d: error, status was %x\n", ldrive,status); #endif /*DEBUGIO*/ /* Ok, we need more details, so read error. This is needed to issue any further commands anyway */ errtyp=get_error(port,ldrive,cdrive); printf("matcd%d: %s while reading block %d\n", ldrive,matcderrors[errtyp],(int)bp->b_blkno); if (media_chk(cd,errtyp,ldrive,0)==0) { errtyp=chk_error(errtyp); if (errtyp==ERR_RETRY) {/*<14>We can retry*/ /*<14>this error but the drive*/ /*<14>probably has already*/ if (mbx->retry-- > 0 ) { state=MATCD_READ_1+ldrive; #ifdef DEBUGIO printf("matcd%d: Attempting retry\n", ldrive); #endif /*DEBUGIO*/ goto loop; } } } /*<14> The other error types are either something very bad or the media <14> has been removed by the user. In both cases there is no retry <14> for this call. We will invalidate the label in both cases. */ bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; biodone(bp); unlockbus(ldrive>>2, ldrive); matcd_start(controller); return; } } } /*--------------------------------------------------------------------------- docmd - Get the bus, do the command, wait for completion, attempt retries, give up the bus. For commands that do not return data. ---------------------------------------------------------------------------*/ int docmd(char * cmd, int ldrive, int cdrive, int controller, int port) { int retries,i,z; lockbus(controller, ldrive); /*Request bus*/ retries=3; while(retries-- > 0) { matcd_slowcmd(port,ldrive,cdrive,cmd); i=waitforit(80*TICKRES,DTEN,port,"matcmd"); z=get_stat(port,ldrive);/*Read status byte*/ if ((z & MATCD_ST_ERROR)==0) break; i=chk_error(get_error(port,ldrive,cdrive)); if (i!=ERR_INIT) { unlockbus(controller, ldrive); /*Release bus*/ return(EFAULT); } } unlockbus(controller, ldrive); /*Release bus*/ return(i); } /*--------------------------------------------------------------------------- get_error - Read the error that aborted a command. Created in Edit 6 ---------------------------------------------------------------------------*/ int get_error(int port, int ldrive, int cdrive) { int status,errnum; unsigned char cmd1[MAXCMDSIZ]; unsigned char data[12]; zero_cmd(cmd1); cmd1[0]=READERROR; /*Enquire*/ matcd_fastcmd(port,ldrive,cdrive,cmd1); matcd_pread(port, 8, data); /*Read data returned*/ errnum=data[2]; /*Caller wants it classified*/ status=get_stat(port,ldrive); /*Read status byte*/ #ifdef DEBUGCMD printf("matcd%d: Chkerror found %x on command %x addrval %x statusdata %x statusport %x\n", ldrive,errnum,data[1],data[0],status,inb(port+STATUS)); #endif /*DEBUGCMD*/ return(errnum); } /*--------------------------------------------------------------------------- chk_error - Classify the error that the drive reported Created in Edit 6 ---------------------------------------------------------------------------*/ int chk_error(int errnum) { switch(errnum) { /* These are errors we can attempt a retry for, although the drive has already done so. */ case UNRECV_ERROR: case SEEK_ERROR: case TRACK_ERROR: case FOCUS_ERROR: case CLV_ERROR: case DATA_ERROR: case MODE_ERROR: /*<16>Make this retryable*/ return(ERR_RETRY); /* These errors usually indicate the user took the media from the drive while the dev was open. We will invalidate the unit until it closes when we see this. */ case NOT_READY: case MEDIA_CHANGED: case DISC_OUT: case HARD_RESET: return (ERR_INIT); /* These errors indicate the system is confused about the drive or media, and point to bugs in the driver or OS. These errors cannot be retried since you will always get the same error. */ case RAM_ERROR: case DIAG_ERROR: case CDB_ERROR: case END_ADDRESS: case ILLEGAL_REQ: case ADDRESS_ERROR: default: return (ERR_FATAL); } } /*--------------------------------------------------------------------------- get_stat - Reads status byte This routine should be totally unnecessary, performing the task with a single line of in-line code. However in special cases, the drives return blocks of data that are not associated with the command in question. This appears to be at least one firmware error and the rest of the driver makes an effort to avoid triggering the fault. However, reading and throwing this bogus data is faster and less destructive than resetting all the drives on a given controller, plus it leaves the other drives unaffected. ---------------------------------------------------------------------------*/ int get_stat(int port,int ldrive) { int status,busstat; status=inb(port+DATA); /*Read status byte, last step of cmd*/ busstat=inb(port+STATUS); /*<16>Get bus status - should be 0xff*/ while ((busstat & (DTEN|STEN)) != (DTEN|STEN)) { printf("matcd%d: get_stat: After reading status byte, bus didn't go idle %x %x %x\n",ldrive,status,busstat,port); if (( busstat & (DTEN|STEN)) == STEN) { int k; k=0; #ifdef DEBUGCMD printf("matcd%d: DATA PRESENT!!!! DISCARDING\n",ldrive); #endif /*DEBUGCMD*/ outb(port+PHASE,1); /*Enable data read*/ while ((inb(port+STATUS) & (DTEN|STEN)) == STEN) { inb(port+DATA); inb(port+ALTDATA); /* printf("%2x ",inb(port+DATA));*/ k++; } outb(port+PHASE,0); #ifdef DEBUGCMD printf("\nmatcd%d: BYTES READ IN DATA was %d\n", ldrive,k); #endif /*DEBUGCMD*/ } status=inb(port+DATA); /*Read the status byte again*/ #ifdef DEBUGCMD printf("matcd%d: Next status byte is %x\n",ldrive,status); #endif /*DEBUGCMD*/ busstat=inb(port+STATUS); } return(status); } /*--------------------------------------------------------------------------- waitforit - Waits for a command started by slowcmd to complete. ---------------------------------------------------------------------------*/ int waitforit(int timelimit, int state, int port, char * where) { int i,j; j=i=0; #ifdef DEBUGCMD printf("matcd: waitforit port %x timelimit %x hz %x\n", port,timelimit,hz); #endif /*DEBUGCMD*/ while (istatus, PRIBIO, "matlck", 0); } if_state[controller] |= BUSBUSY; /*<18>It's ours NOW*/ #ifdef DEBUGSLEEP printf("matcd%d: BUS locked in lockbus\n",ldrive); #endif /*DEBUGSLEEP*/ } /*--------------------------------------------------------------------------- unlockbus - Release the host interface bus we already have so someone else can use it. Created in Edit 6 ---------------------------------------------------------------------------*/ void unlockbus(int controller, int ldrive) { if_state[controller] &= ~BUSBUSY; #ifdef DEBUGSLEEP printf("matcd%d: bus unlocked\n",ldrive); #endif /*DEBUGSLEEP*/ wakeup((caddr_t)&matcd_data->status); /*Wakeup other users*/ matcd_start(controller); /*Wake up any block I/O*/ } /*--------------------------------------------------------------------------- media_chk - Checks error for types related to media changes. ---------------------------------------------------------------------------*/ int media_chk(struct matcd_data *cd,int errnum,int ldrive,int test) { if (errnum==NOT_READY || errnum==MEDIA_CHANGED || errnum==HARD_RESET || errnum==DISC_OUT) { cd->flags &= ~MATCDLABEL; /*Mark label as invalid*/ if (test==0) { /*<14>Do warn by default*/ if ((cd->flags & MATCDWARN)==0) {/*<14>Msg already?*/ printf("matcd%d: Media changed - Further I/O aborted until device closed\n",ldrive); cd->flags |= MATCDWARN; } } return(1); } if (errnum==MODE_ERROR) /*<16>Maybe the setting is*/ cd->drivemode=MODE_UNKNOWN; /*<16>wrong so force a reset*/ return(0); } /*--------------------------------------------------------------------------- matcd_eject - Open drive tray ---------------------------------------------------------------------------*/ int matcd_eject(int ldrive, int cdrive, int controller) { int i,port; struct matcd_data *cd; unsigned char cmd[MAXCMDSIZ]; cd=&matcd_data[ldrive]; port=cd->iobase; /*Get I/O port base*/ #ifdef LOCKDRIVE if (cd->flags & MATCDLOCK) { /*<15>Drive was locked via open*/ return(EINVAL); /*<15>so don't allow the eject*/ } #endif /*LOCKDRIVE*/ zero_cmd(cmd); /*Initialize command buffer*/ cmd[0]=LOCK; /*Unlock drive*/ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/ cmd[0]=DOOROPEN; /*Open Door*/ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/ cd->flags &= ~(MATCDLABEL|MATCDLOCK); /*<15>Mark vol info invalid*/ return(i); /*Return result we got*/ } /*--------------------------------------------------------------------------- matcd_doorclose - Close drive tray <16> Added in Edit 16 ---------------------------------------------------------------------------*/ int matcd_doorclose(int ldrive, int cdrive, int controller) { int i,port; struct matcd_data *cd; unsigned char cmd[MAXCMDSIZ]; cd=&matcd_data[ldrive]; port=cd->iobase; /*Get I/O port base*/ zero_cmd(cmd); /*Initialize command buffer*/ cmd[0]=DOORCLOSE; /*Open Door*/ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/ cd->flags &= ~(MATCDLABEL|MATCDLOCK); /*Mark vol info invalid*/ tsleep((caddr_t)&nextcontroller, PRIBIO, "matclos", hz); return(i); /*Return result we got*/ } /*--------------------------------------------------------------------------- <23> matcd_dlock - Honor/Reject drive tray requests ---------------------------------------------------------------------------*/ int matcd_dlock(int ldrive, int cdrive, int controller, int action) { int i,port; struct matcd_data *cd; unsigned char cmd[MAXCMDSIZ]; cd=&matcd_data[ldrive]; port=cd->iobase; /*<23>Get I/O port base*/ zero_cmd(cmd); /*<23>Initialize command buffer*/ cmd[0]=LOCK; /*<23>Unlock drive*/ if (action) { /*<23>They want to lock the door?*/ cd->flags |= MATCDLOCK; /*<23>Remember we did this*/ cmd[1]=1; /*<23>Lock Door command*/ } else { cd->flags &= ~MATCDLOCK;/*<23>Remember we did this*/ /*<23>Unlock Door command*/ } i=docmd(cmd,ldrive,cdrive,controller,port); /*<23>Issue command*/ return(i); /*<23>Return result we got*/ } /*--------------------------------------------------------------------------- matcd_toc_header - Return Table of Contents header to caller <13> New for Edit 13 ---------------------------------------------------------------------------*/ static int matcd_toc_header(int ldrive, int cdrive, int controller, struct ioc_toc_header * toc) { struct matcd_data *cd; cd=&matcd_data[ldrive]; if ((cd->flags & MATCDLABEL)==0) return(EIO); /*Refuse after chg error*/ toc->len=msf_to_blk(cd->volinfo.vol_msf); /*In frames*/ toc->starting_track=cd->volinfo.trk_low; /*1*/ toc->ending_track=cd->volinfo.trk_high; /*Last track*/ return(0); } /*--------------------------------------------------------------------------- matcd_toc_entries - Read all of the TOC entries These entries are cached by the drive, but it might be worth the space investment to have the driver cache these as well. For a disc with 40 tracks, it means 41 command calls to get this information from the drive. <13> New for Edit 13 ---------------------------------------------------------------------------*/ static int matcd_toc_entries(int ldrive, int cdrive, int controller, struct ioc_read_toc_entry * ioc_entry) { struct matcd_data *cd; struct cd_toc_entry entries[MAXTRKS]; struct cd_toc_entry *from; struct cd_toc_entry *to; int len,trk,i,z,port; unsigned char cmd[MAXCMDSIZ]; unsigned char data[5]; cd=&matcd_data[ldrive]; port=cd->iobase; if ((cd->flags & MATCDLABEL)==0) return(EIO); /*Refuse after chg error*/ zero_cmd(cmd); cmd[0]=READTOC; for(trk=cd->volinfo.trk_low-1; trkvolinfo.trk_high; trk++) { cmd[2]=trk+1; lockbus(controller, ldrive); /*Request bus*/ matcd_slowcmd(port,ldrive,cdrive,cmd); i=waitforit(10*TICKRES,DTEN,port,"mats1"); matcd_pread(port, 8, data); /*Read data returned*/ z=get_stat(port,ldrive); /*Read status byte*/ if ((z & MATCD_ST_ERROR)) { /*Something went wrong*/ i=get_error(port, ldrive, cdrive); unlockbus(controller, ldrive); /*Release bus*/ return(EIO); } unlockbus(controller, ldrive); /*Release bus*/ #ifdef DEBUGIOCTL printf("Track %d addr/ctrl %x m %x s %x f %x\n",data[2], data[1],data[4],data[5],data[6]); #endif /*DEBUGIOCTL*/ entries[trk].control=data[1]; /*Track type*/ entries[trk].addr_type=ioc_entry->address_format;/*Type*/ entries[trk].track=data[2]; /*Track #, can be Out of Order*/ if (ioc_entry->address_format == CD_MSF_FORMAT) { entries[trk].addr.msf.unused=0; entries[trk].addr.msf.minute=data[4]; /*Min*/ entries[trk].addr.msf.second=data[5]; /*Sec*/ entries[trk].addr.msf.frame=data[6]; /*Frame*/ } } entries[trk].control=data[2]; /*Copy from last valid track*/ entries[trk].track=0xaa; /*<23>Lead-out*/ entries[trk].addr.msf.unused=0; /*Fill*/ entries[trk].addr.msf.minute=cd->volinfo.vol_msf[0]; entries[trk].addr.msf.second=cd->volinfo.vol_msf[1]; entries[trk].addr.msf.frame=cd->volinfo.vol_msf[2]; trk++; /*Bump to include leadout track*/ /* Now that we have read all the data from the drive, copy the array from the kernel address space into the user address space */ len=ioc_entry->data_len; i=ioc_entry->starting_track; /*<23>What did they want?*/ if (i==0xaa) i=trk-1; /*<23>Give them lead-out info*/ else i=ioc_entry->starting_track - 1; /*<23>start where they asked*/ from = &entries[i]; to = ioc_entry->data; while (i < trk && len >= sizeof(struct cd_toc_entry)) { if (copyout(from,to,sizeof(struct cd_toc_entry)) != 0) { return (EFAULT); } i++; len -= sizeof(struct cd_toc_entry); from++; to++; } return(0); } /*--------------------------------------------------------------------------- matcd_subq - Read the Sub-Q packet - (where are we?) This call gives a snapshot state of where the optical pick-up is when the command is issued. <14> New for Edit 14 ---------------------------------------------------------------------------*/ static int matcd_read_subq(int ldrive, int cdrive, int controller, struct ioc_read_subchannel * sqp) { struct matcd_data *cd; int i,z,port; unsigned char cmd[MAXCMDSIZ]; unsigned char data[12]; struct cd_sub_channel_info subq; /*Build result here*/ cd=&matcd_data[ldrive]; port=cd->iobase; if ((cd->flags & MATCDLABEL)==0) return(EIO); /*Refuse after chg error*/ /* We only support the ioctl functions we could get information on, so test for the things we can do */ if (sqp->data_format!=CD_CURRENT_POSITION || sqp->address_format!=CD_MSF_FORMAT) { return(EINVAL); } zero_cmd(cmd); cmd[0]=READSUBQ; lockbus(controller, ldrive); /*Request bus*/ matcd_slowcmd(port,ldrive,cdrive,cmd); /* While we wait, fill in the hard-coded entries of the table*/ subq.what.position.data_format=CD_MSF_FORMAT; subq.what.position.absaddr.msf.unused=0; subq.what.position.reladdr.msf.unused=0; i=waitforit(10*TICKRES,DTEN,port,"mats2"); matcd_pread(port, 11, data); /*Read data returned*/ z=get_stat(port,ldrive); /*Read status byte*/ if ((z & MATCD_ST_ERROR)) { /*Something went wrong*/ i=get_error(port, ldrive, cdrive); unlockbus(controller, ldrive); /*Release bus*/ return(EIO); } unlockbus(controller, ldrive); /*Release bus*/ #ifdef DEBUGIOCTL printf("Subq track %d index %d adr/ctl %x abs %d:%2d:%2d rel %d:%2d:%2d UPC %x\n", data[2],data[3],data[1],data[4],data[5],data[6], data[7],data[8],data[9],data[10]); #endif /*DEBUGIOCTL*/ if (z & MATCD_ST_AUDIOBSY) { /*Drive playing or paused*/ if (cd->status==CD_AS_PLAY_PAUSED) { /*Have we issued*/ i=cd->status; /*a pause command?*/ } else { i=CD_AS_PLAY_IN_PROGRESS;/*No, we really are playing*/ } } else { if (cd->status==CD_AS_PLAY_IN_PROGRESS) {/*It was playing*/ i=CD_AS_PLAY_COMPLETED; /*so it finished*/ } else { /*Any other status reported*/ i=cd->status; /*as we get it*/ } } subq.header.audio_status=cd->status=i; /*Store status we selected*/ subq.what.position.track_number=data[2]; subq.what.position.index_number=data[3]; subq.what.position.absaddr.msf.minute=data[4]; subq.what.position.absaddr.msf.second=data[5]; subq.what.position.absaddr.msf.frame=data[6]; subq.what.position.reladdr.msf.minute=data[7]; subq.what.position.reladdr.msf.second=data[8]; subq.what.position.reladdr.msf.frame=data[9]; /* Ok, now copy our nicely-built structure from the kernel address space into the user address space (we hope) */ if (copyout(&subq, sqp->data, min(sizeof(struct cd_sub_channel_info), sqp->data_len))!=0) { return(EFAULT); } return(0); } /*--------------------------------------------------------------------------- matcd_igot - Like the song, report the capabilities that the drive/driver has available. This call returns a structure of flags indicating what functions are available so that the application can offer only the functions the drive is actually capable of. <16> New for Edit 16 ---------------------------------------------------------------------------*/ static int matcd_igot(struct ioc_capability * sqp) { #ifdef FULLDRIVER sqp->play_function=(CDDOPLAYTRK | /*Can play trks/indx*/ CDDOPLAYMSF | /*Can play msf to msf*/ CDDOPAUSE | /*Can pause playback*/ CDDORESUME | /*Can resume playback*/ CDDOSTOP | /*Can stop playback*/ CDDOPITCH); /*Can change play pitch*/ sqp->routing_function=(CDREADVOLUME | /*Can read volume*/ CDSETVOLUME | /*Can set volume*/ CDSETSTEREO | /*Can select stereo play*/ CDSETLEFT | /*Can select left-only*/ CDSETRIGHT | /*Can select right-only*/ CDSETMUTE | /*Can mute audio*/ CDSETPATCH); /*Direct patch settings*/ #else /*FULLDRIVER*/ sqp->play_function=0; /*No audio capability*/ sqp->routing_function=0; /*No audio capability*/ #endif /*FULLDRIVER*/ sqp->special_function=(CDDOEJECT | /*Door can be opened*/ CDDOCLOSE | /*Door can be closed*/ CDDOLOCK | /*Door can be locked*/ CDREADSUBQ | /*Can read subchannel*/ CDREADENTRIES | /*Can read TOC entries*/ CDREADHEADER); /*Can read TOC*/ return(0); } #ifdef FULLDRIVER #ifdef PC98 #include "pc98/pc98/matcd/audio.c" /*<15>ioctls related to audio are here*/ #else #include "i386/isa/matcd/audio.c" /*<15>ioctls related to audio are here*/ #endif #endif /*FULLDRIVER*/ static matcd_devsw_installed = 0; static void matcd_drvinit(void *unused) { - dev_t dev; if( ! matcd_devsw_installed ) { - dev = makedev(CDEV_MAJOR,0); - cdevsw_add(&dev,&matcd_cdevsw,NULL); - dev = makedev(BDEV_MAJOR,0); - bdevsw_add(&dev,&matcd_bdevsw,NULL); + bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &matcd_bdevsw); matcd_devsw_installed = 1; } } SYSINIT(matcddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,matcd_drvinit,NULL) /*End of matcd.c*/ Index: head/sys/pc98/pc98/pc98_machdep.c =================================================================== --- head/sys/pc98/pc98/pc98_machdep.c (nonexistent) +++ head/sys/pc98/pc98/pc98_machdep.c (revision 17350) @@ -0,0 +1,388 @@ +/* + * Copyright (c) KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern int Maxmem; +extern int Maxmem_under16M; + +void init_cpu_accel_mem __P((void)); +void init_pc98_dmac __P((void)); + +#ifdef EPSON_MEMWIN +void init_epson_memwin __P((void)); + +void init_epson_memwin(void) +{ + if (pc98_machine_type & M_EPSON_PC98) { + if (Maxmem > 3840) { + if (Maxmem == Maxmem_under16M) { + Maxmem = 3840; + Maxmem_under16M = 3840; + } else if (Maxmem_under16M > 3840) { + Maxmem_under16M = 3840; + } + } + + /* Disable 15MB-16MB caching */ + switch (epson_machine_id) { + case 0x34: /* PC486HX */ + case 0x35: /* PC486HG */ + case 0x3B: /* PC486HA */ + /* Cache control start */ + outb(0x43f, 0x42); + outw(0xc40, 0x0033); + + /* Disable 0xF00000-0xFFFFFF */ + outb(0xc48, 0x49); outb(0xc4c, 0x00); + outb(0xc48, 0x48); outb(0xc4c, 0xf0); + outb(0xc48, 0x4d); outb(0xc4c, 0x00); + outb(0xc48, 0x4c); outb(0xc4c, 0xff); + outb(0xc48, 0x4f); outb(0xc4c, 0x00); + + /* Cache control end */ + outb(0x43f, 0x40); + break; + + case 0x2B: /* PC486GR/GF */ + case 0x30: /* PC486P */ + case 0x31: /* PC486GRSuper */ + case 0x32: /* PC486GR+ */ + case 0x37: /* PC486SE */ + case 0x38: /* PC486SR */ + /* Disable 0xF00000-0xFFFFFF */ + outb(0x43f, 0x42); + outb(0x467, 0xe0); + outb(0x567, 0xd8); + + outb(0x43f, 0x40); + outb(0x467, 0xe0); + outb(0x567, 0xe0); + break; + } + + /* Disable 15MB-16MB RAM and enable memory window */ + outb(0x43b, inb(0x43b) & 0xfd); /* clear bit1 */ + } +} +#endif + +void init_cpu_accel_mem(void) +{ + int target_page; + /* + * Certain 'CPU accelerator' supports over 16MB memory on + * the machines whose BIOS doesn't store true size. + * To support this, we don't trust BIOS values if Maxmem < 4096. + */ + if (Maxmem < 4096) { + for (target_page = ptoa(4096); /* 16MB */ + target_page < ptoa(32768); /* 128MB */ + target_page += 256 * PAGE_SIZE /* 1MB step */) { + int tmp, page_bad = FALSE, OrigMaxmem = Maxmem; + + *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page; + pmap_update(); + + tmp = *(int *)CADDR1; + /* + * Test for alternating 1's and 0's + */ + *(volatile int *)CADDR1 = 0xaaaaaaaa; + if (*(volatile int *)CADDR1 != 0xaaaaaaaa) { + page_bad = TRUE; + } + /* + * Test for alternating 0's and 1's + */ + *(volatile int *)CADDR1 = 0x55555555; + if (*(volatile int *)CADDR1 != 0x55555555) { + page_bad = TRUE; + } + /* + * Test for all 1's + */ + *(volatile int *)CADDR1 = 0xffffffff; + if (*(volatile int *)CADDR1 != 0xffffffff) { + page_bad = TRUE; + } + /* + * Test for all 0's + */ + *(volatile int *)CADDR1 = 0x0; + if (*(volatile int *)CADDR1 != 0x0) { + /* + * test of page failed + */ + page_bad = TRUE; + } + /* + * Restore original value. + */ + *(int *)CADDR1 = tmp; + if (page_bad == TRUE) { + if (target_page > ptoa(4096)) + Maxmem = atop(target_page); + else + Maxmem = OrigMaxmem; + + break; + } + } + *(int *)CMAP1 = 0; + pmap_update(); + + /* XXX */ + if (Maxmem > 3840) { + Maxmem_under16M = 3840; + if (Maxmem < 4096) { + Maxmem = 3840; + } + } + } +} + +int dma_init_flag = 1; /* dummy */ + +void init_pc98_dmac(void) +{ + outb(0x439, (inb(0x439) & 0xfb)); /* DMA Accsess Control over 1MB */ + outb(0x29, (0x0c | 0)); /* Bank Mode Reg. 16M mode */ + outb(0x29, (0x0c | 1)); /* Bank Mode Reg. 16M mode */ + outb(0x29, (0x0c | 2)); /* Bank Mode Reg. 16M mode */ + outb(0x29, (0x0c | 3)); /* Bank Mode Reg. 16M mode */ + outb(0x11, 0x50); /* PC98 must be 0x40 */ +} +/* + * Copyright (c) KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern int Maxmem; +extern int Maxmem_under16M; + +void init_cpu_accel_mem __P((void)); +void init_pc98_dmac __P((void)); + +#ifdef EPSON_MEMWIN +void init_epson_memwin __P((void)); + +void init_epson_memwin(void) +{ + if (pc98_machine_type & M_EPSON_PC98) { + if (Maxmem > 3840) { + if (Maxmem == Maxmem_under16M) { + Maxmem = 3840; + Maxmem_under16M = 3840; + } else if (Maxmem_under16M > 3840) { + Maxmem_under16M = 3840; + } + } + + /* Disable 15MB-16MB caching */ + switch (epson_machine_id) { + case 0x34: /* PC486HX */ + case 0x35: /* PC486HG */ + case 0x3B: /* PC486HA */ + /* Cache control start */ + outb(0x43f, 0x42); + outw(0xc40, 0x0033); + + /* Disable 0xF00000-0xFFFFFF */ + outb(0xc48, 0x49); outb(0xc4c, 0x00); + outb(0xc48, 0x48); outb(0xc4c, 0xf0); + outb(0xc48, 0x4d); outb(0xc4c, 0x00); + outb(0xc48, 0x4c); outb(0xc4c, 0xff); + outb(0xc48, 0x4f); outb(0xc4c, 0x00); + + /* Cache control end */ + outb(0x43f, 0x40); + break; + + case 0x2B: /* PC486GR/GF */ + case 0x30: /* PC486P */ + case 0x31: /* PC486GRSuper */ + case 0x32: /* PC486GR+ */ + case 0x37: /* PC486SE */ + case 0x38: /* PC486SR */ + /* Disable 0xF00000-0xFFFFFF */ + outb(0x43f, 0x42); + outb(0x467, 0xe0); + outb(0x567, 0xd8); + + outb(0x43f, 0x40); + outb(0x467, 0xe0); + outb(0x567, 0xe0); + break; + } + + /* Disable 15MB-16MB RAM and enable memory window */ + outb(0x43b, inb(0x43b) & 0xfd); /* clear bit1 */ + } +} +#endif + +void init_cpu_accel_mem(void) +{ + int target_page; + /* + * Certain 'CPU accelerator' supports over 16MB memory on + * the machines whose BIOS doesn't store true size. + * To support this, we don't trust BIOS values if Maxmem < 4096. + */ + if (Maxmem < 4096) { + for (target_page = ptoa(4096); /* 16MB */ + target_page < ptoa(32768); /* 128MB */ + target_page += 256 * PAGE_SIZE /* 1MB step */) { + int tmp, page_bad = FALSE, OrigMaxmem = Maxmem; + + *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page; + pmap_update(); + + tmp = *(int *)CADDR1; + /* + * Test for alternating 1's and 0's + */ + *(volatile int *)CADDR1 = 0xaaaaaaaa; + if (*(volatile int *)CADDR1 != 0xaaaaaaaa) { + page_bad = TRUE; + } + /* + * Test for alternating 0's and 1's + */ + *(volatile int *)CADDR1 = 0x55555555; + if (*(volatile int *)CADDR1 != 0x55555555) { + page_bad = TRUE; + } + /* + * Test for all 1's + */ + *(volatile int *)CADDR1 = 0xffffffff; + if (*(volatile int *)CADDR1 != 0xffffffff) { + page_bad = TRUE; + } + /* + * Test for all 0's + */ + *(volatile int *)CADDR1 = 0x0; + if (*(volatile int *)CADDR1 != 0x0) { + /* + * test of page failed + */ + page_bad = TRUE; + } + /* + * Restore original value. + */ + *(int *)CADDR1 = tmp; + if (page_bad == TRUE) { + if (target_page > ptoa(4096)) + Maxmem = atop(target_page); + else + Maxmem = OrigMaxmem; + + break; + } + } + *(int *)CMAP1 = 0; + pmap_update(); + + /* XXX */ + if (Maxmem > 3840) { + Maxmem_under16M = 3840; + if (Maxmem < 4096) { + Maxmem = 3840; + } + } + } +} + +int dma_init_flag = 1; /* dummy */ + +void init_pc98_dmac(void) +{ + outb(0x439, (inb(0x439) & 0xfb)); /* DMA Accsess Control over 1MB */ + outb(0x29, (0x0c | 0)); /* Bank Mode Reg. 16M mode */ + outb(0x29, (0x0c | 1)); /* Bank Mode Reg. 16M mode */ + outb(0x29, (0x0c | 2)); /* Bank Mode Reg. 16M mode */ + outb(0x29, (0x0c | 3)); /* Bank Mode Reg. 16M mode */ + outb(0x11, 0x50); /* PC98 must be 0x40 */ +} Property changes on: head/sys/pc98/pc98/pc98_machdep.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/pc98_machdep.h =================================================================== --- head/sys/pc98/pc98/pc98_machdep.h (nonexistent) +++ head/sys/pc98/pc98/pc98_machdep.h (revision 17350) @@ -0,0 +1,74 @@ +/* + * Copyright (c) KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PC98_PC98_PC98_MACHDEP_H__ +#define __PC98_PC98_PC98_MACHDEP_H__ + +#ifdef EPSON_MEMWIN +void init_epson_memwin __P((void)); +#endif +void init_cpu_accel_mem __P((void)); +void init_pc98_dmac __P((void)); + +#endif /* __PC98_PC98_PC98_MACHDEP_H__ */ +/* + * Copyright (c) KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PC98_PC98_PC98_MACHDEP_H__ +#define __PC98_PC98_PC98_MACHDEP_H__ + +#ifdef EPSON_MEMWIN +void init_epson_memwin __P((void)); +#endif +void init_cpu_accel_mem __P((void)); +void init_pc98_dmac __P((void)); + +#endif /* __PC98_PC98_PC98_MACHDEP_H__ */ Property changes on: head/sys/pc98/pc98/pc98_machdep.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/pc98/scd.c =================================================================== --- head/sys/pc98/pc98/scd.c (revision 17349) +++ head/sys/pc98/pc98/scd.c (revision 17350) @@ -1,1599 +1,1588 @@ /*- * Copyright (c) 1995 Mikael Hybsch * All rights reserved. * * Portions of this file are copied from mcd.c * which has the following copyrights: * * Copyright 1993 by Holger Veit (data part) * Copyright 1993 by Brian Moore (audio part) * Changes Copyright 1993 by Gary Clark II * Changes Copyright (C) 1994 by Andrew A. Chernov * * Rewrote probe routine to work on newer Mitsumi drives. * Additional changes (C) 1994 by Jordan K. Hubbard * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -/* $Id: scd.c,v 1.1.1.1 1996/06/14 10:04:45 asami Exp $ */ +/* $Id: scd.c,v 1.2 1996/07/23 07:46:37 asami Exp $ */ /* Please send any comments to micke@dynas.se */ #define SCD_DEBUG 0 #include "scd.h" #if NSCD > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #ifdef PC98 #include #else #include #endif #include #define scd_part(dev) ((minor(dev)) & 7) #define scd_unit(dev) (((minor(dev)) & 0x38) >> 3) #define scd_phys(dev) (((minor(dev)) & 0x40) >> 6) #define RAW_PART 2 /* flags */ #define SCDOPEN 0x0001 /* device opened */ #define SCDVALID 0x0002 /* parameters loaded */ #define SCDINIT 0x0004 /* device is init'd */ #define SCDPROBING 0x0020 /* probing */ #define SCDTOC 0x0100 /* already read toc */ #define SCDMBXBSY 0x0200 /* local mbx is busy */ #define SCDSPINNING 0x0400 /* drive is spun up */ #define SCD_S_BEGIN 0 #define SCD_S_BEGIN1 1 #define SCD_S_WAITSTAT 2 #define SCD_S_WAITFIFO 3 #define SCD_S_WAITSPIN 4 #define SCD_S_WAITREAD 5 #define SCD_S_WAITPARAM 6 #define RDELAY_WAIT 300 #define RDELAY_WAITREAD 300 #define SCDBLKSIZE 2048 #ifdef SCD_DEBUG static int scd_debuglevel = SCD_DEBUG; # define XDEBUG(level, data) {if (scd_debuglevel >= level) printf data;} #else # define XDEBUG(level, data) #endif struct scd_mbx { short unit; short port; short retry; short nblk; int sz; u_long skip; struct buf *bp; int p_offset; short count; }; static struct scd_data { int iobase; char double_speed; char *name; short flags; int blksize; u_long disksize; struct disklabel dlabel; int openflag; struct { unsigned char adr :4; unsigned char ctl :4; /* xcdplayer needs this */ unsigned char start_msf[3]; } toc[MAX_TRACKS]; short first_track; short last_track; struct ioc_play_msf last_play; short audio_status; struct buf_queue_head head; /* head of buf queue */ struct scd_mbx mbx; #ifdef DEVFS void *ra_devfs_token; void *rc_devfs_token; void *a_devfs_token; void *c_devfs_token; #endif } scd_data[NSCD]; /* prototypes */ static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf); static void process_attention(unsigned unit); static inline void write_control(unsigned port, unsigned data); static int waitfor_status_bits(int unit, int bits_set, int bits_clear); static int send_cmd(u_int unit, u_char cmd, u_int nargs, ...); static void init_drive(unsigned unit); static int spin_up(unsigned unit); static int read_toc(dev_t dev); static int get_result(u_int unit, int result_len, u_char *result); static void print_error(int unit, int errcode); static void scd_start(int unit); static void scd_doread(int state, struct scd_mbx *mbxin); static int scd_eject(int unit); static int scd_stop(int unit); static int scd_pause(int unit); static int scd_resume(int unit); static int scd_playtracks(int unit, struct ioc_play_track *pt); static int scd_playmsf(int unit, struct ioc_play_msf *msf); static int scd_play(int unit, struct ioc_play_msf *msf); static int scd_subchan(int unit, struct ioc_read_subchannel *sc); static int read_subcode(int unit, struct sony_subchannel_position_data *sc); /* for xcdplayer */ static int scd_toc_header(int unit, struct ioc_toc_header *th); static int scd_toc_entrys(int unit, struct ioc_read_toc_entry *te); #define SCD_LASTPLUS1 170 /* don't ask, xcdplayer passes this in */ #ifdef PC98 static int scd_probe(struct pc98_device *dev); static int scd_attach(struct pc98_device *dev); struct pc98_driver scddriver = { scd_probe, scd_attach, "scd" }; #else static int scd_probe(struct isa_device *dev); static int scd_attach(struct isa_device *dev); struct isa_driver scddriver = { scd_probe, scd_attach, "scd" }; #endif static d_open_t scdopen; static d_close_t scdclose; static d_ioctl_t scdioctl; static d_strategy_t scdstrategy; #define CDEV_MAJOR 45 #define BDEV_MAJOR 16 -extern struct cdevsw scd_cdevsw; +static struct cdevsw scd_cdevsw; static struct bdevsw scd_bdevsw = { scdopen, scdclose, scdstrategy, scdioctl, /*16*/ nodump, nopsize, 0, "scd", &scd_cdevsw, -1 }; -static struct cdevsw scd_cdevsw = - { scdopen, scdclose, rawread, nowrite, /*45*/ - scdioctl, nostop, nullreset, nodevtotty,/* sony cd */ - seltrue, nommap, scdstrategy, "scd", - &scd_bdevsw, -1 }; - - static struct kern_devconf kdc_scd[NSCD] = { { 0, 0, 0, /* filled in by dev_attach */ #ifdef PC98 "scd", 0, { MDDT_PC98, 0, "bio" }, pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN, &kdc_nec0, /* parent */ #else "scd", 0, { MDDT_ISA, 0, "bio" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ #endif 0, /* parentdata */ DC_UNCONFIGURED, /* status */ "Sony CD-ROM drive", /* properly filled later */ DC_CLS_RDISK /* class */ } }; static inline void #ifdef PC98 scd_registerdev(struct pc98_device *id) #else scd_registerdev(struct isa_device *id) #endif { if(id->id_unit) kdc_scd[id->id_unit] = kdc_scd[0]; kdc_scd[id->id_unit].kdc_unit = id->id_unit; #ifdef PC98 kdc_scd[id->id_unit].kdc_pc98 = id; #else kdc_scd[id->id_unit].kdc_isa = id; #endif dev_attach(&kdc_scd[id->id_unit]); } #ifdef PC98 int scd_attach(struct pc98_device *dev) #else int scd_attach(struct isa_device *dev) #endif { int unit = dev->id_unit; struct scd_data *cd = scd_data + unit; cd->iobase = dev->id_iobase; /* Already set by probe, but ... */ kdc_scd[dev->id_unit].kdc_state = DC_IDLE; /* name filled in probe */ kdc_scd[dev->id_unit].kdc_description = scd_data[dev->id_unit].name; printf("scd%d: <%s>\n", dev->id_unit, scd_data[dev->id_unit].name); init_drive(dev->id_unit); cd->flags = SCDINIT; cd->audio_status = CD_AS_AUDIO_INVALID; TAILQ_INIT(&cd->head); #ifdef DEVFS cd->ra_devfs_token = devfs_add_devswf(&scd_cdevsw, dkmakeminor(unit, 0, 0), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rscd%da", unit); cd->rc_devfs_token = devfs_add_devswf(&scd_cdevsw, dkmakeminor(unit, 0, RAW_PART), DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rscd%dc", unit); cd->a_devfs_token = devfs_add_devswf(&scd_bdevsw, dkmakeminor(unit, 0, 0), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "scd%da", unit); cd->c_devfs_token = devfs_add_devswf(&scd_bdevsw, dkmakeminor(unit, 0, RAW_PART), DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "scd%dc", unit); #endif return 1; } static int scdopen(dev_t dev, int flags, int fmt, struct proc *p) { int unit,part,phys; int rc; struct scd_data *cd; unit = scd_unit(dev); if (unit >= NSCD) return ENXIO; cd = scd_data + unit; part = scd_part(dev); phys = scd_phys(dev); /* not initialized*/ if (!(cd->flags & SCDINIT)) return ENXIO; /* invalidated in the meantime? mark all open part's invalid */ if (cd->openflag) return ENXIO; XDEBUG(1,("scd%d: DEBUG: status = 0x%x\n", unit, inb(cd->iobase+IREG_STATUS))); if ((rc = spin_up(unit)) != 0) { print_error(unit, rc); return EIO; } if (!(cd->flags & SCDTOC)) { int loop_count = 3; while (loop_count-- > 0 && (rc = read_toc(dev)) != 0) { if (rc == ERR_NOT_SPINNING) { rc = spin_up(unit); if (rc) { print_error(unit, rc);\ return EIO; } continue; } printf("scd%d: TOC read error 0x%x\n", unit, rc); return EIO; } } cd->openflag = 1; cd->flags |= SCDVALID; kdc_scd[unit].kdc_state = DC_BUSY; return 0; } static int scdclose(dev_t dev, int flags, int fmt, struct proc *p) { int unit,part,phys; struct scd_data *cd; unit = scd_unit(dev); if (unit >= NSCD) return ENXIO; cd = scd_data + unit; part = scd_part(dev); phys = scd_phys(dev); if (!(cd->flags & SCDINIT) || !cd->openflag) return ENXIO; if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) { (void)send_cmd(unit, CMD_SPIN_DOWN, 0); cd->flags &= ~SCDSPINNING; } kdc_scd[unit].kdc_state = DC_IDLE; /* close channel */ cd->openflag = 0; return 0; } static void scdstrategy(struct buf *bp) { struct scd_data *cd; int s; int unit = scd_unit(bp->b_dev); cd = scd_data + unit; XDEBUG(2, ("scd%d: DEBUG: strategy: block=%ld, bcount=%ld\n", unit, bp->b_blkno, bp->b_bcount)); if (unit >= NSCD || bp->b_blkno < 0 || (bp->b_bcount % SCDBLKSIZE)) { printf("scd%d: strategy failure: blkno = %ld, bcount = %ld\n", unit, bp->b_blkno, bp->b_bcount); bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto bad; } /* if device invalidated (e.g. media change, door open), error */ if (!(cd->flags & SCDVALID)) { printf("scd%d: media changed\n", unit); bp->b_error = EIO; goto bad; } /* read only */ if (!(bp->b_flags & B_READ)) { bp->b_error = EROFS; goto bad; } /* no data to read */ if (bp->b_bcount == 0) goto done; if (!(cd->flags & SCDTOC)) { bp->b_error = EIO; goto bad; } /* adjust transfer if necessary */ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) goto done; bp->b_pblkno = bp->b_blkno; bp->b_resid = 0; /* queue it */ s = splbio(); tqdisksort(&cd->head, bp); splx(s); /* now check whether we can perform processing */ scd_start(unit); return; bad: bp->b_flags |= B_ERROR; done: bp->b_resid = bp->b_bcount; biodone(bp); return; } static void scd_start(int unit) { struct scd_data *cd = scd_data + unit; struct buf *bp; struct partition *p; register s = splbio(); if (cd->flags & SCDMBXBSY) { splx(s); return; } bp = TAILQ_FIRST(&cd->head); if (bp != 0) { /* block found to process, dequeue */ TAILQ_REMOVE(&cd->head, bp, b_act); cd->flags |= SCDMBXBSY; splx(s); } else { /* nothing to do */ splx(s); return; } p = cd->dlabel.d_partitions + scd_part(bp->b_dev); cd->mbx.unit = unit; cd->mbx.port = cd->iobase; cd->mbx.retry = 3; cd->mbx.bp = bp; cd->mbx.p_offset = p->p_offset; splx(s); scd_doread(SCD_S_BEGIN,&(cd->mbx)); return; } static int scdioctl(dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p) { struct scd_data *cd; int unit,part; unit = scd_unit(dev); part = scd_part(dev); cd = scd_data + unit; XDEBUG(1, ("scd%d: ioctl: cmd=0x%x\n", unit, cmd)); if (!(cd->flags & SCDVALID)) return EIO; switch (cmd) { case DIOCSBAD: return EINVAL; case DIOCGDINFO: *(struct disklabel *)addr = cd->dlabel; return 0; case DIOCGPART: ((struct partinfo *)addr)->disklab = &cd->dlabel; ((struct partinfo *)addr)->part = &cd->dlabel.d_partitions[0]; return 0; case CDIOCPLAYTRACKS: return scd_playtracks(unit, (struct ioc_play_track *) addr); case CDIOCPLAYBLOCKS: return EINVAL; case CDIOCPLAYMSF: return scd_playmsf(unit, (struct ioc_play_msf *) addr); case CDIOCREADSUBCHANNEL: return scd_subchan(unit, (struct ioc_read_subchannel *) addr); case CDIOREADTOCHEADER: return scd_toc_header (unit, (struct ioc_toc_header *) addr); case CDIOREADTOCENTRYS: return scd_toc_entrys (unit, (struct ioc_read_toc_entry*) addr); case CDIOCSETPATCH: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTERIO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: return EINVAL; case CDIOCRESUME: return scd_resume(unit); case CDIOCPAUSE: return scd_pause(unit); case CDIOCSTART: return EINVAL; case CDIOCSTOP: return scd_stop(unit); case CDIOCEJECT: return scd_eject(unit); case CDIOCALLOW: return 0; case CDIOCSETDEBUG: #ifdef SCD_DEBUG scd_debuglevel++; #endif return 0; case CDIOCCLRDEBUG: #ifdef SCD_DEBUG scd_debuglevel = 0; #endif return 0; default: printf("scd%d: unsupported ioctl (cmd=0x%x)\n", unit, cmd); return ENOTTY; } } /*************************************************************** * lower level of driver starts here **************************************************************/ static int scd_playtracks(int unit, struct ioc_play_track *pt) { struct scd_data *cd = scd_data + unit; struct ioc_play_msf msf; int a = pt->start_track; int z = pt->end_track; int rc; if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) { if (rc == -ERR_NOT_SPINNING) { if (spin_up(unit) != 0) return EIO; rc = read_toc(unit); } if (rc != 0) { print_error(unit, rc); return EIO; } } XDEBUG(1, ("scd%d: playtracks from %d:%d to %d:%d\n", unit, a, pt->start_index, z, pt->end_index)); if ( a < cd->first_track || a > cd->last_track || a > z || z > cd->last_track) return EINVAL; bcopy(cd->toc[a].start_msf, &msf.start_m, 3); hsg2msf(msf2hsg(cd->toc[z+1].start_msf)-1, &msf.end_m); return scd_play(unit, &msf); } /* The start/end msf is expected to be in bin format */ static int scd_playmsf(int unit, struct ioc_play_msf *msfin) { struct ioc_play_msf msf; msf.start_m = bin2bcd(msfin->start_m); msf.start_s = bin2bcd(msfin->start_s); msf.start_f = bin2bcd(msfin->start_f); msf.end_m = bin2bcd(msfin->end_m); msf.end_s = bin2bcd(msfin->end_s); msf.end_f = bin2bcd(msfin->end_f); return scd_play(unit, &msf); } /* The start/end msf is expected to be in bcd format */ static int scd_play(int unit, struct ioc_play_msf *msf) { struct scd_data *cd = scd_data + unit; int i, rc; XDEBUG(1, ("scd%d: playing: %02x:%02x:%02x -> %02x:%02x:%02x\n", unit, msf->start_m, msf->start_s, msf->start_f, msf->end_m, msf->end_s, msf->end_f)); for (i = 0; i < 2; i++) { rc = send_cmd(unit, CMD_PLAY_AUDIO, 7, 0x03, msf->start_m, msf->start_s, msf->start_f, msf->end_m, msf->end_s, msf->end_f); if (rc == -ERR_NOT_SPINNING) { cd->flags &= ~SCDSPINNING; if (spin_up(unit) != 0) return EIO; } else if (rc < 0) { print_error(unit, rc); return EIO; } else { break; } } cd->audio_status = CD_AS_PLAY_IN_PROGRESS; bcopy((char *)msf, (char *)&cd->last_play, sizeof(struct ioc_play_msf)); return 0; } static int scd_stop(int unit) { struct scd_data *cd = scd_data + unit; (void)send_cmd(unit, CMD_STOP_AUDIO, 0); cd->audio_status = CD_AS_PLAY_COMPLETED; return 0; } static int scd_pause(int unit) { struct scd_data *cd = scd_data + unit; struct sony_subchannel_position_data subpos; if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) return EINVAL; if (read_subcode(unit, &subpos) != 0) return EIO; if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0) return EIO; cd->last_play.start_m = subpos.abs_msf[0]; cd->last_play.start_s = subpos.abs_msf[1]; cd->last_play.start_f = subpos.abs_msf[2]; cd->audio_status = CD_AS_PLAY_PAUSED; XDEBUG(1, ("scd%d: pause @ %02x:%02x:%02x\n", unit, cd->last_play.start_m, cd->last_play.start_s, cd->last_play.start_f)); return 0; } static int scd_resume(int unit) { if (scd_data[unit].audio_status != CD_AS_PLAY_PAUSED) return EINVAL; return scd_play(unit, &scd_data[unit].last_play); } static int scd_eject(int unit) { struct scd_data *cd = scd_data + unit; cd->audio_status = CD_AS_AUDIO_INVALID; cd->flags &= ~(SCDSPINNING|SCDTOC); if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0 || send_cmd(unit, CMD_SPIN_DOWN, 0) != 0 || send_cmd(unit, CMD_EJECT, 0) != 0) { return EIO; } return 0; } static int scd_subchan(int unit, struct ioc_read_subchannel *sc) { struct scd_data *cd = scd_data + unit; struct sony_subchannel_position_data q; struct cd_sub_channel_info data; XDEBUG(1, ("scd%d: subchan af=%d, df=%d\n", unit, sc->address_format, sc->data_format)); if (sc->address_format != CD_MSF_FORMAT) return EINVAL; if (sc->data_format != CD_CURRENT_POSITION) return EINVAL; if (read_subcode(unit, &q) != 0) return EIO; data.header.audio_status = cd->audio_status; data.what.position.data_format = CD_MSF_FORMAT; data.what.position.track_number = bcd2bin(q.track_number); data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.rel_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.rel_msf[1]); data.what.position.reladdr.msf.frame = bcd2bin(q.rel_msf[2]); data.what.position.absaddr.msf.unused = 0; data.what.position.absaddr.msf.minute = bcd2bin(q.abs_msf[0]); data.what.position.absaddr.msf.second = bcd2bin(q.abs_msf[1]); data.what.position.absaddr.msf.frame = bcd2bin(q.abs_msf[2]); if (copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len))!=0) return EFAULT; return 0; } int #ifdef PC98 scd_probe(struct pc98_device *dev) #else scd_probe(struct isa_device *dev) #endif { struct sony_drive_configuration drive_config; int unit = dev->id_unit; int rc; static char namebuf[8+16+8+3]; char *s = namebuf; int loop_count = 0; scd_data[unit].flags = SCDPROBING; scd_data[unit].iobase = dev->id_iobase; bzero(&drive_config, sizeof(drive_config)); scd_registerdev(dev); again: /* Reset drive */ write_control(dev->id_iobase, CBIT_RESET_DRIVE); /* Calm down */ DELAY(300000); /* Only the ATTENTION bit may be set */ if ((inb(dev->id_iobase+IREG_STATUS) & ~1) != 0) { XDEBUG(1, ("scd: too many bits set. probe failed.\n")); return 0; } rc = send_cmd(unit, CMD_GET_DRIVE_CONFIG, 0); if (rc != sizeof(drive_config)) { /* Sometimes if the drive is playing audio I get */ /* the bad result 82. Fix by repeating the reset */ if (rc > 0 && loop_count++ == 0) goto again; return 0; } if (get_result(unit, rc, (u_char *)&drive_config) != 0) return 0; bcopy(drive_config.vendor, namebuf, 8); s = namebuf+8; while (*(s-1) == ' ') /* Strip trailing spaces */ s--; *s++ = ' '; bcopy(drive_config.product, s, 16); s += 16; while (*(s-1) == ' ') s--; *s++ = ' '; bcopy(drive_config.revision, s, 8); s += 8; while (*(s-1) == ' ') s--; *s = 0; scd_data[unit].name = namebuf; if (drive_config.config & 0x10) scd_data[unit].double_speed = 1; else scd_data[unit].double_speed = 0; return 4; } static int read_subcode(int unit, struct sony_subchannel_position_data *sc) { int rc; rc = send_cmd(unit, CMD_GET_SUBCHANNEL_DATA, 0); if (rc < 0 || rc < sizeof(*sc)) return EIO; if (get_result(unit, rc, (u_char *)sc) != 0) return EIO; return 0; } /* State machine copied from mcd.c */ /* This (and the code in mcd.c) will not work with more than one drive */ /* because there is only one mbxsave below. Should fix that some day. */ /* (mbxsave & state should probably be included in the scd_data struct and */ /* the unit number used as first argument to scd_doread().) /Micke */ /* state machine to process read requests * initialize with SCD_S_BEGIN: reset state machine * SCD_S_WAITSTAT: wait for ready (!busy) * SCD_S_WAITSPIN: wait for drive to spin up (if not spinning) * SCD_S_WAITFIFO: wait for param fifo to get ready, them exec. command. * SCD_S_WAITREAD: wait for data ready, read data * SCD_S_WAITPARAM: wait for command result params, read them, error if bad data read. */ static struct scd_mbx *mbxsave; static void scd_doread(int state, struct scd_mbx *mbxin) { struct scd_mbx *mbx = (state!=SCD_S_BEGIN) ? mbxsave : mbxin; int unit = mbx->unit; int port = mbx->port; struct buf *bp = mbx->bp; struct scd_data *cd = scd_data + unit; int reg,i; int blknum; caddr_t addr; static char sdata[3]; /* Must be preserved between calls to this function */ loop: switch (state) { case SCD_S_BEGIN: mbx = mbxsave = mbxin; case SCD_S_BEGIN1: /* get status */ mbx->count = RDELAY_WAIT; process_attention(unit); goto trystat; case SCD_S_WAITSTAT: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITSTAT); if (mbx->count-- <= 0) { printf("scd%d: timeout. drive busy.\n",unit); goto harderr; } trystat: if (IS_BUSY(port)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSTAT,hz/100); /* XXX */ return; } process_attention(unit); /* reject, if audio active */ if (cd->audio_status & CD_AS_PLAY_IN_PROGRESS) { printf("scd%d: audio is active\n",unit); goto harderr; } mbx->sz = cd->blksize; /* for first block */ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; mbx->skip = 0; nextblock: if (!(cd->flags & SCDVALID)) goto changed; blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) + mbx->p_offset + mbx->skip/mbx->sz; XDEBUG(2, ("scd%d: scd_doread: read blknum=%d\n", unit, blknum)); /* build parameter block */ hsg2msf(blknum, sdata); write_control(port, CBIT_RESULT_READY_CLEAR); write_control(port, CBIT_RPARAM_CLEAR); write_control(port, CBIT_DATA_READY_CLEAR); if (FSTATUS_BIT(port, FBIT_WPARAM_READY)) goto writeparam; mbx->count = 100; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITFIFO,hz/100); /* XXX */ return; case SCD_S_WAITSPIN: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITSPIN); if (mbx->count-- <= 0) { printf("scd%d: timeout waiting for drive to spin up.\n", unit); goto harderr; } if (!STATUS_BIT(port, SBIT_RESULT_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */ return; } write_control(port, CBIT_RESULT_READY_CLEAR); switch ((i = inb(port+IREG_RESULT)) & 0xf0) { case 0x20: i = inb(port+IREG_RESULT); print_error(unit, i); goto harderr; case 0x00: (void)inb(port+IREG_RESULT); cd->flags |= SCDSPINNING; break; } XDEBUG(1, ("scd%d: DEBUG: spin up complete\n", unit)); state = SCD_S_BEGIN1; goto loop; case SCD_S_WAITFIFO: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITFIFO); if (mbx->count-- <= 0) { printf("scd%d: timeout. write param not ready.\n",unit); goto harderr; } if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITFIFO,hz/100); /* XXX */ return; } XDEBUG(1, ("scd%d: mbx->count (writeparamwait) = %d(%d)\n", unit, mbx->count, 100)); writeparam: /* The reason this test isn't done 'till now is to make sure */ /* that it is ok to send the SPIN_UP cmd below. */ if (!(cd->flags & SCDSPINNING)) { XDEBUG(1, ("scd%d: spinning up drive ...\n", unit)); outb(port+OREG_COMMAND, CMD_SPIN_UP); mbx->count = 300; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */ return; } reg = port + OREG_WPARAMS; /* send the read command */ disable_intr(); outb(reg, sdata[0]); outb(reg, sdata[1]); outb(reg, sdata[2]); outb(reg, 0); outb(reg, 0); outb(reg, 1); outb(port+OREG_COMMAND, CMD_READ); enable_intr(); mbx->count = RDELAY_WAITREAD; for (i = 0; i < 50; i++) { if (STATUS_BIT(port, SBIT_DATA_READY)) goto got_data; DELAY(100); } timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITREAD,hz/100); /* XXX */ return; case SCD_S_WAITREAD: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITREAD); if (mbx->count-- <= 0) { if (STATUS_BIT(port, SBIT_RESULT_READY)) goto got_param; printf("scd%d: timeout while reading data\n",unit); goto readerr; } if (!STATUS_BIT(port, SBIT_DATA_READY)) { process_attention(unit); if (!(cd->flags & SCDVALID)) goto changed; timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITREAD,hz/100); /* XXX */ return; } XDEBUG(2, ("scd%d: mbx->count (after RDY_BIT) = %d(%d)\n", unit, mbx->count, RDELAY_WAITREAD)); got_data: /* data is ready */ addr = bp->b_un.b_addr + mbx->skip; write_control(port, CBIT_DATA_READY_CLEAR); insb(port+IREG_DATA, addr, mbx->sz); mbx->count = 100; for (i = 0; i < 20; i++) { if (STATUS_BIT(port, SBIT_RESULT_READY)) goto waitfor_param; DELAY(100); } goto waitfor_param; case SCD_S_WAITPARAM: untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITPARAM); if (mbx->count-- <= 0) { printf("scd%d: timeout waiting for params\n",unit); goto readerr; } waitfor_param: if (!STATUS_BIT(port, SBIT_RESULT_READY)) { timeout((timeout_func_t)scd_doread, (caddr_t)SCD_S_WAITPARAM,hz/100); /* XXX */ return; } #if SCD_DEBUG if (mbx->count < 100 && scd_debuglevel > 0) printf("scd%d: mbx->count (paramwait) = %d(%d)\n", unit, mbx->count, 100); #endif got_param: write_control(port, CBIT_RESULT_READY_CLEAR); switch ((i = inb(port+IREG_RESULT)) & 0xf0) { case 0x50: switch (i) { case ERR_FATAL_READ_ERROR1: case ERR_FATAL_READ_ERROR2: printf("scd%d: unrecoverable read error 0x%x\n", unit, i); goto harderr; } break; case 0x20: i = inb(port+IREG_RESULT); switch (i) { case ERR_NOT_SPINNING: XDEBUG(1, ("scd%d: read error: drive not spinning\n", unit)); if (mbx->retry-- > 0) { state = SCD_S_BEGIN1; cd->flags &= ~SCDSPINNING; goto loop; } goto harderr; default: print_error(unit, i); goto readerr; } case 0x00: i = inb(port+IREG_RESULT); break; } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; } /* return buffer */ bp->b_resid = 0; biodone(bp); cd->flags &= ~SCDMBXBSY; scd_start(mbx->unit); return; } readerr: if (mbx->retry-- > 0) { printf("scd%d: retrying ...\n",unit); state = SCD_S_BEGIN1; goto loop; } harderr: /* invalidate the buffer */ bp->b_error = EIO; bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; biodone(bp); cd->flags &= ~SCDMBXBSY; scd_start(mbx->unit); return; changed: printf("scd%d: media changed\n", unit); goto harderr; } static void hsg2msf(int hsg, bcd_t *msf) { hsg += 150; M_msf(msf) = bin2bcd(hsg / 4500); hsg %= 4500; S_msf(msf) = bin2bcd(hsg / 75); F_msf(msf) = bin2bcd(hsg % 75); } static int msf2hsg(bcd_t *msf) { return (bcd2bin(M_msf(msf)) * 60 + bcd2bin(S_msf(msf))) * 75 + bcd2bin(F_msf(msf)) - 150; } static void process_attention(unsigned unit) { unsigned port = scd_data[unit].iobase; unsigned char code; int count = 0; while (IS_ATTENTION(port) && count++ < 30) { write_control(port, CBIT_ATTENTION_CLEAR); code = inb(port+IREG_RESULT); #if SCD_DEBUG if (scd_debuglevel > 0) { if (count == 1) printf("scd%d: DEBUG: ATTENTIONS = 0x%x", unit, code); else printf(",0x%x", code); } #endif switch (code) { case ATTEN_SPIN_DOWN: scd_data[unit].flags &= ~SCDSPINNING; break; case ATTEN_SPIN_UP_DONE: scd_data[unit].flags |= SCDSPINNING; break; case ATTEN_AUDIO_DONE: scd_data[unit].audio_status = CD_AS_PLAY_COMPLETED; break; case ATTEN_DRIVE_LOADED: scd_data[unit].flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); scd_data[unit].audio_status = CD_AS_AUDIO_INVALID; break; case ATTEN_EJECT_PUSHED: scd_data[unit].flags &= ~SCDVALID; break; } DELAY(100); } #if SCD_DEBUG if (scd_debuglevel > 0 && count > 0) printf("\n"); #endif } /* Returns 0 OR sony error code */ static int spin_up(unsigned unit) { unsigned char res_reg[12]; unsigned int res_size; int rc; int loop_count = 0; again: rc = send_cmd(unit, CMD_SPIN_UP, 0, 0, res_reg, &res_size); if (rc != 0) { XDEBUG(2, ("scd%d: CMD_SPIN_UP error 0x%x\n", unit, rc)); return rc; } if (!(scd_data[unit].flags & SCDTOC)) { rc = send_cmd(unit, CMD_READ_TOC, 0); if (rc == ERR_NOT_SPINNING) { if (loop_count++ < 3) goto again; return rc; } if (rc != 0) return rc; } scd_data[unit].flags |= SCDSPINNING; return 0; } static struct sony_tracklist * get_tl(struct sony_toc *toc, int size) { struct sony_tracklist *tl = &toc->tracks[0]; if (tl->track != 0xb0) return tl; (char *)tl += 9; if (tl->track != 0xb1) return tl; (char *)tl += 9; if (tl->track != 0xb2) return tl; (char *)tl += 9; if (tl->track != 0xb3) return tl; (char *)tl += 9; if (tl->track != 0xb4) return tl; (char *)tl += 9; if (tl->track != 0xc0) return tl; (char *)tl += 9; return tl; } static int read_toc(dev_t dev) { unsigned unit; struct scd_data *cd; unsigned part = 0; /* For now ... */ struct sony_toc toc; struct sony_tracklist *tl; int rc, i, j; u_long first, last; unit = scd_unit(dev); cd = scd_data + unit; rc = send_cmd(unit, CMD_GET_TOC, 1, part+1); if (rc < 0) return rc; if (rc > sizeof(toc)) { printf("scd%d: program error: toc too large (%d)\n", unit, rc); return EIO; } if (get_result(unit, rc, (u_char *)&toc) != 0) return EIO; XDEBUG(1, ("scd%d: toc read. len = %d, sizeof(toc) = %d\n", unit, rc, sizeof(toc))); tl = get_tl(&toc, rc); first = msf2hsg(tl->start_msf); last = msf2hsg(toc.lead_out_start_msf); cd->blksize = SCDBLKSIZE; cd->disksize = last*cd->blksize/DEV_BSIZE; XDEBUG(1, ("scd%d: firstsector = %ld, lastsector = %ld", unit, first, last)); cd->first_track = bcd2bin(toc.first_track); cd->last_track = bcd2bin(toc.last_track); if (cd->last_track > (MAX_TRACKS-2)) cd->last_track = MAX_TRACKS-2; for (j = 0, i = cd->first_track; i <= cd->last_track; i++, j++) { cd->toc[i].adr = tl[j].adr; cd->toc[i].ctl = tl[j].ctl; /* for xcdplayer */ bcopy(tl[j].start_msf, cd->toc[i].start_msf, 3); #ifdef SCD_DEBUG if (scd_debuglevel > 0) { if ((j % 3) == 0) printf("\nscd%d: tracks ", unit); printf("[%03d: %2d %2d %2d] ", i, bcd2bin(cd->toc[i].start_msf[0]), bcd2bin(cd->toc[i].start_msf[1]), bcd2bin(cd->toc[i].start_msf[2])); } #endif } bcopy(toc.lead_out_start_msf, cd->toc[cd->last_track+1].start_msf, 3); #ifdef SCD_DEBUG if (scd_debuglevel > 0) { i = cd->last_track+1; printf("[END: %2d %2d %2d]\n", bcd2bin(cd->toc[i].start_msf[0]), bcd2bin(cd->toc[i].start_msf[1]), bcd2bin(cd->toc[i].start_msf[2])); } #endif bzero(&cd->dlabel,sizeof(struct disklabel)); /* filled with spaces first */ strncpy(cd->dlabel.d_typename," ", sizeof(cd->dlabel.d_typename)); strncpy(cd->dlabel.d_typename, cd->name, min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1)); strncpy(cd->dlabel.d_packname,"unknown ", sizeof(cd->dlabel.d_packname)); cd->dlabel.d_secsize = cd->blksize; cd->dlabel.d_nsectors = 100; cd->dlabel.d_ntracks = 1; cd->dlabel.d_ncylinders = (cd->disksize/100)+1; cd->dlabel.d_secpercyl = 100; cd->dlabel.d_secperunit = cd->disksize; cd->dlabel.d_rpm = 300; cd->dlabel.d_interleave = 1; cd->dlabel.d_flags = D_REMOVABLE; cd->dlabel.d_npartitions= 1; cd->dlabel.d_partitions[0].p_offset = 0; cd->dlabel.d_partitions[0].p_size = cd->disksize; cd->dlabel.d_partitions[0].p_fstype = 9; cd->dlabel.d_magic = DISKMAGIC; cd->dlabel.d_magic2 = DISKMAGIC; cd->dlabel.d_checksum = dkcksum(&cd->dlabel); cd->flags |= SCDTOC; return 0; } static inline void write_control(unsigned port, unsigned data) { outb(port + OREG_CONTROL, data); } static void init_drive(unsigned unit) { int rc; rc = send_cmd(unit, CMD_SET_DRIVE_PARAM, 2, 0x05, 0x03 | ((scd_data[unit].double_speed) ? 0x04: 0)); if (rc != 0) printf("scd%d: Unable to set parameters. Errcode = 0x%x\n", unit, rc); } /* Returns 0 or errno */ static int get_result(u_int unit, int result_len, u_char *result) { unsigned int port = scd_data[unit].iobase; unsigned int res_reg = port + IREG_RESULT; int loop_index = 2; /* send_cmd() reads two bytes ... */ XDEBUG(1, ("scd%d: DEBUG: get_result: bytes=%d\n", unit, result_len)); while (result_len-- > 0) { if (loop_index++ >= 10) { loop_index = 1; if (waitfor_status_bits(unit, SBIT_RESULT_READY, 0)) return EIO; write_control(port, CBIT_RESULT_READY_CLEAR); } if (result) *result++ = inb(res_reg); else (void)inb(res_reg); } return 0; } /* Returns -0x100 for timeout, -(drive error code) OR number of result bytes */ static int send_cmd(u_int unit, u_char cmd, u_int nargs, ...) { va_list ap; u_int port = scd_data[unit].iobase; u_int reg; u_char c; int rc; int i; if (waitfor_status_bits(unit, 0, SBIT_BUSY)) { printf("scd%d: drive busy\n", unit); return -0x100; } XDEBUG(1,("scd%d: DEBUG: send_cmd: cmd=0x%x nargs=%d", unit, cmd, nargs)); write_control(port, CBIT_RESULT_READY_CLEAR); write_control(port, CBIT_RPARAM_CLEAR); for (i = 0; i < 100; i++) if (FSTATUS_BIT(port, FBIT_WPARAM_READY)) break; if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) { XDEBUG(1, ("\nscd%d: wparam timeout\n", unit)); return -EIO; } va_start(ap, nargs); reg = port + OREG_WPARAMS; for (i = 0; i < nargs; i++) { c = (u_char)va_arg(ap, int); outb(reg, c); XDEBUG(1, (",{0x%x}", c)); } va_end(ap); XDEBUG(1, ("\n")); outb(port+OREG_COMMAND, cmd); rc = waitfor_status_bits(unit, SBIT_RESULT_READY, SBIT_BUSY); if (rc) return -0x100; reg = port + IREG_RESULT; write_control(port, CBIT_RESULT_READY_CLEAR); switch ((rc = inb(reg)) & 0xf0) { case 0x20: rc = inb(reg); /* FALL TROUGH */ case 0x50: XDEBUG(1, ("scd%d: DEBUG: send_cmd: drive_error=0x%x\n", unit, rc)); return -rc; case 0x00: default: rc = inb(reg); XDEBUG(1, ("scd%d: DEBUG: send_cmd: result_len=%d\n", unit, rc)); return rc; } } static void print_error(int unit, int errcode) { switch (errcode) { case -ERR_CD_NOT_LOADED: printf("scd%d: door is open\n", unit); break; case -ERR_NO_CD_INSIDE: printf("scd%d: no cd inside\n", unit); break; default: if (errcode == -0x100 || errcode > 0) printf("scd%d: device timeout\n", unit); else printf("scd%d: unexpected error 0x%x\n", unit, -errcode); break; } } /* Returns 0 or errno value */ static int waitfor_status_bits(int unit, int bits_set, int bits_clear) { u_int port = scd_data[unit].iobase; u_int flags = scd_data[unit].flags; u_int reg = port + IREG_STATUS; u_int max_loop; u_char c = 0; if (flags & SCDPROBING) { max_loop = 0; while (max_loop++ < 1000) { c = inb(reg); if (c == 0xff) return EIO; if (c & SBIT_ATTENTION) { process_attention(unit); continue; } if ((c & bits_set) == bits_set && (c & bits_clear) == 0) { break; } DELAY(10000); } } else { max_loop = 100; while (max_loop-- > 0) { c = inb(reg); if (c & SBIT_ATTENTION) { process_attention(unit); continue; } if ((c & bits_set) == bits_set && (c & bits_clear) == 0) { break; } tsleep(waitfor_status_bits, PZERO - 1, "waitfor", hz/10); } } if ((c & bits_set) == bits_set && (c & bits_clear) == 0) { return 0; } #ifdef SCD_DEBUG if (scd_debuglevel > 0) printf("scd%d: DEBUG: waitfor: TIMEOUT (0x%x,(0x%x,0x%x))\n", unit, c, bits_set, bits_clear); else #endif printf("scd%d: timeout.\n", unit); return EIO; } /* these two routines for xcdplayer - "borrowed" from mcd.c */ static int scd_toc_header (int unit, struct ioc_toc_header* th) { struct scd_data *cd = scd_data + unit; int rc; if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) { print_error(unit, rc); return EIO; } th->starting_track = cd->first_track; th->ending_track = cd->last_track; th->len = 0; /* not used */ return 0; } static int scd_toc_entrys (int unit, struct ioc_read_toc_entry *te) { struct scd_data *cd = scd_data + unit; struct cd_toc_entry toc_entry; int rc, i, len = te->data_len; if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) { print_error(unit, rc); return EIO; } /* find the toc to copy*/ i = te->starting_track; if (i == SCD_LASTPLUS1) i = cd->last_track + 1; /* verify starting track */ if (i < cd->first_track || i > cd->last_track+1) return EINVAL; /* valid length ? */ if (len < sizeof(struct cd_toc_entry) || (len % sizeof(struct cd_toc_entry)) != 0) return EINVAL; /* copy the toc data */ toc_entry.control = cd->toc[i].ctl; toc_entry.addr_type = te->address_format; toc_entry.track = i; if (te->address_format == CD_MSF_FORMAT) { toc_entry.addr.msf.unused = 0; toc_entry.addr.msf.minute = bcd2bin(cd->toc[i].start_msf[0]); toc_entry.addr.msf.second = bcd2bin(cd->toc[i].start_msf[1]); toc_entry.addr.msf.frame = bcd2bin(cd->toc[i].start_msf[2]); } /* copy the data back */ if (copyout(&toc_entry, te->data, sizeof(struct cd_toc_entry)) != 0) return EFAULT; return 0; } static scd_devsw_installed = 0; static void scd_drvinit(void *unused) { - dev_t dev; if( ! scd_devsw_installed ) { - dev = makedev(CDEV_MAJOR, 0); - cdevsw_add(&dev,&scd_cdevsw, NULL); - dev = makedev(BDEV_MAJOR, 0); - bdevsw_add(&dev,&scd_bdevsw, NULL); + bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &scd_bdevsw); scd_devsw_installed = 1; } } SYSINIT(scddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,scd_drvinit,NULL) #endif /* NSCD > 0 */ Index: head/sys/pc98/pc98/wd.c =================================================================== --- head/sys/pc98/pc98/wd.c (revision 17349) +++ head/sys/pc98/pc98/wd.c (revision 17350) @@ -1,2487 +1,2472 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 - * $Id: wd.c,v 1.1.1.1 1996/06/14 10:04:48 asami Exp $ + * $Id: wd.c,v 1.2 1996/07/23 07:46:44 asami Exp $ */ /* TODO: * o Bump error count after timeout. * o Satisfy ATA timing in all cases. * o Finish merging berry/sos timeout code (bump error count...). * o Merge/fix TIH/NetBSD bad144 code. * o Don't use polling except for initialization. Need to * reorganize the state machine. Then "extra" interrupts * shouldn't happen (except maybe one for initialization). * o Fix disklabel, boot and driver inconsistencies with * bad144 in standard versions. * o Support extended DOS partitions. * o Support swapping to DOS partitions. * o Handle bad sectors, clustering, disklabelling, DOS * partitions and swapping driver-independently. Use * i386/dkbad.c for bad sectors. Swapping will need new * driver entries for polled reinit and polled write). */ #include "wd.h" #ifdef NWDC #undef NWDC #endif #include "wdc.h" #if NWDC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #ifdef PC98 #include #include #include #else #include #include #include #include #endif #include #include #include #include #include #include #ifdef ATAPI #ifdef PC98 #include #else #include #endif #endif extern void wdstart(int ctrlr); #define TIMEOUT 10000 #define RETRIES 5 /* number of retries before giving up */ #define RECOVERYTIME 500000 /* usec for controller to recover after err */ #define MAXTRANSFER 255 /* max size of transfer in sectors */ /* correct max is 256 but some controllers */ /* can't handle that in all cases */ #define WDOPT_32BIT 0x8000 #define WDOPT_SLEEPHACK 0x4000 #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff static int wd_goaway(struct kern_devconf *, int); static int wdc_goaway(struct kern_devconf *, int); static int wd_externalize(struct kern_devconf *, struct sysctl_req *); /* * Templates for the kern_devconf structures used when we attach. */ static struct kern_devconf kdc_wd[NWD] = { { 0, 0, 0, /* filled in by kern_devconf.c */ "wd", 0, { MDDT_DISK, 0 }, wd_externalize, 0, wd_goaway, DISK_EXTERNALLEN, 0, /* parent */ 0, /* parentdata */ DC_UNKNOWN, /* state */ #ifdef PC98 "IDE disk", #else "ST506/ESDI/IDE disk", #endif DC_CLS_DISK /* class */ } }; static struct kern_devconf kdc_wdc[NWDC] = { { 0, 0, 0, /* filled in by kern_devconf.c */ #ifdef PC98 "wdc", 0, { MDDT_PC98, 0 }, pc98_generic_externalize, 0, wdc_goaway, PC98_EXTERNALLEN, &kdc_nec0, /* parent */ #else "wdc", 0, { MDDT_ISA, 0 }, isa_generic_externalize, 0, wdc_goaway, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ #endif 0, /* parentdata */ DC_UNCONFIGURED, /* state */ #ifdef PC98 "IDE disk controller", #else "ST506/ESDI/IDE disk controller", #endif DC_CLS_MISC /* just an ordinary device */ } }; static inline void wd_registerdev(int ctlr, int unit) { if(unit != 0) { kdc_wd[unit] = kdc_wd[0]; kdc_wd[unit].kdc_state = DC_IDLE; } kdc_wd[unit].kdc_unit = unit; kdc_wd[unit].kdc_parent = &kdc_wdc[ctlr]; kdc_wdc[ctlr].kdc_state = DC_BUSY; dev_attach(&kdc_wd[unit]); } static inline void #ifdef PC98 wdc_registerdev(struct pc98_device *dvp) #else wdc_registerdev(struct isa_device *dvp) #endif { int unit = dvp->id_unit; if(unit != 0) { kdc_wdc[unit] = kdc_wdc[0]; kdc_wdc[unit].kdc_state = DC_IDLE; } kdc_wdc[unit].kdc_unit = unit; kdc_wdc[unit].kdc_parentdata = dvp; dev_attach(&kdc_wdc[unit]); } static int wdc_goaway(struct kern_devconf *kdc, int force) { if(force) { dev_detach(kdc); return 0; } else { return EBUSY; /* XXX fix */ } } static int wd_goaway(struct kern_devconf *kdc, int force) { dev_detach(kdc); return 0; } static inline u_char epson_errorf(int wdc) { u_char wdc_error; outb(wdc, inb(0x82) | 0x40); wdc_error = (u_char)epson_inb(wdc); outb(wdc, inb(0x82) & ~0x40); return ((u_char)wdc_error); } /* * This biotab field doubles as a field for the physical unit number on * the controller. */ #define id_physid id_scsiid /* * Drive states. Used to initialize drive. */ #define CLOSED 0 /* disk is closed. */ #define WANTOPEN 1 /* open requested, not started */ #define RECAL 2 /* doing restore */ #define OPEN 3 /* done with open */ /* * Disk geometry. A small part of struct disklabel. * XXX disklabel.5 contains an old clone of disklabel.h. */ struct diskgeom { u_long d_secsize; /* # of bytes per sector */ u_long d_nsectors; /* # of data sectors per track */ u_long d_ntracks; /* # of tracks per cylinder */ u_long d_ncylinders; /* # of data cylinders per unit */ u_long d_secpercyl; /* # of data sectors per cylinder */ u_long d_secperunit; /* # of data sectors per unit */ u_long d_precompcyl; /* XXX always 0 */ }; /* * The structure of a disk drive. */ struct disk { long dk_bc; /* byte count left */ short dk_skip; /* blocks already transferred */ int dk_ctrlr; /* physical controller number */ int dk_unit; /* physical unit number */ int dk_lunit; /* logical unit number */ char dk_state; /* control state */ u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ u_char dk_timeout; /* countdown to next timeout */ short dk_port; /* i/o port base */ #ifdef DEVFS void *dk_bdev; /* devfs token for whole disk */ void *dk_cdev; /* devfs token for raw whole disk */ #endif u_long cfg_flags; /* configured characteristics */ short dk_flags; /* drive characteristics found */ #define DKFL_SINGLE 0x00004 /* sector at a time mode */ #define DKFL_ERROR 0x00008 /* processing a disk error */ #define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */ #define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */ #define DKFL_MULTI 0x00200 /* use multi-i/o mode */ #define DKFL_BADSCAN 0x00400 /* report all errors */ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ int dk_dkunit; /* disk stats unit number */ int dk_multi; /* multi transfers */ int dk_currentiosize; /* current io size */ struct diskgeom dk_dd; /* device configuration data */ struct diskslices *dk_slices; /* virtual drives */ #ifdef PC98 short single_sector; #endif }; #define WD_COUNT_RETRIES static int wdtest = 0; static struct disk *wddrives[NWD]; /* table of units */ static struct buf_queue_head drive_queue[NWD]; /* head of queue per drive */ static struct { int b_errcnt; int b_active; } wdutab[NWD]; /* static struct buf wdtab[NWDC]; */ static struct { struct buf_queue_head controller_queue; int b_errcnt; int b_active; } wdtab[NWDC]; struct wddma wddma; #ifdef notyet static struct buf rwdbuf[NWD]; /* buffers for raw IO */ #endif #ifdef PC98 static short wd_ctlr; static int old_epson_note; #endif #ifdef PC98 static int wdprobe(struct pc98_device *dvp); static int wdattach(struct pc98_device *dvp); #else static int wdprobe(struct isa_device *dvp); static int wdattach(struct isa_device *dvp); #endif static void wdustart(struct disk *du); static int wdcontrol(struct buf *bp); static int wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command); static int wdsetctlr(struct disk *du); #if 0 static int wdwsetctlr(struct disk *du); #endif static int wdgetctlr(struct disk *du); static void wderror(struct buf *bp, struct disk *du, char *mesg); static void wdflushirq(struct disk *du, int old_ipl); static int wdreset(struct disk *du); static void wdsleep(int ctrlr, char *wmesg); static void wdstrategy1(struct buf *bp); static timeout_t wdtimeout; static int wdunwedge(struct disk *du); static int wdwait(struct disk *du, u_char bits_wanted, int timeout); static d_open_t wdopen; static d_close_t wdclose; static d_strategy_t wdstrategy; static d_ioctl_t wdioctl; static d_dump_t wddump; static d_psize_t wdsize; #define CDEV_MAJOR 3 #define BDEV_MAJOR 0 -extern struct cdevsw wd_cdevsw; +static struct cdevsw wd_cdevsw; static struct bdevsw wd_bdevsw = { wdopen, wdclose, wdstrategy, wdioctl, /*0*/ wddump, wdsize, 0, "wd", &wd_cdevsw, -1 }; -static struct cdevsw wd_cdevsw = - { wdopen, wdclose, rawread, rawwrite, /*3*/ - wdioctl, nostop, nullreset, nodevtotty,/* wd */ - seltrue, nommap, wdstrategy, "wd", - &wd_bdevsw, -1 }; - - /* * Provide hw.devconf information. */ static int wd_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { return disk_externalize(wddrives[kdc->kdc_unit]->dk_unit, req); } #ifdef PC98 struct pc98_driver wdcdriver = { #else struct isa_driver wdcdriver = { #endif wdprobe, wdattach, "wdc", }; /* * Probe for controller. */ static int wdprobe(struct pc98_device *dvp) { int unit = dvp->id_unit; struct disk *du; if (unit >= NWDC) return (0); du = malloc(sizeof *du, M_TEMP, M_NOWAIT); if (du == NULL) return (0); bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; du->dk_port = dvp->id_iobase; wdc_registerdev(dvp); /* check if we have registers that work */ #ifdef PC98 /* XXX ATAPI support isn't imported */ wd_ctlr = wd_ctlr_nec; /* wdreg.h */ old_epson_note=0; if (pc98_machine_type & M_EPSON_PC98 ) { switch (epson_machine_id) { case 0x20: case 0x22: case 0x2a: /* note A/W/WR */ du->dk_port = IO_WD1_EPSON; /* pc98.h */ dvp->id_iobase = IO_WD1_EPSON; /* pc98.h */ wd_ctlr = wd_ctlr_epson; /* wdreg.h */ old_epson_note = 1; /* for OLD EPSON NOTE */ break; default: break; } } if ((PC98_SYSTEM_PARAMETER(0x55d) & 3) == 0) { goto nodevice; } outb(0x432,(du->dk_unit)%2); #else /* IBM-PC */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */ outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */ if (inb(du->dk_port + wd_cyl_lo) == 0xff) { /* XXX too weak */ #ifdef ATAPI /* There is no master, try the ATAPI slave. */ outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); outb(du->dk_port + wd_cyl_lo, 0xa5); if (inb(du->dk_port + wd_cyl_lo) == 0xff) #endif goto nodevice; } #endif /* PC98 */ if (wdreset(du) == 0) goto reset_ok; #ifdef ATAPI /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; du->dk_unit = 1; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #endif DELAY(RECOVERYTIME); if (wdreset(du) != 0) goto nodevice; reset_ok: /* execute a controller only command */ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 || wdwait(du, 0, TIMEOUT) < 0) goto nodevice; /* * drive(s) did not time out during diagnostic : * Get error status and check that both drives are OK. * Table 9-2 of ATA specs suggests that we must check for * a value of 0x01 * * Strangely, some controllers will return a status of * 0x81 (drive 0 OK, drive 1 failure), and then when * the DRV bit is set, return status of 0x01 (OK) for * drive 2. (This seems to contradict the ATA spec.) */ if (old_epson_note) du->dk_error = epson_errorf(du->dk_port + wd_error); else du->dk_error = inb(du->dk_port + wd_error); /* printf("Error : %x\n", du->dk_error); */ if(du->dk_error != 0x01) { if(du->dk_error & 0x80) { /* drive 1 failure */ /* first set the DRV bit */ u_int sdh; if (old_epson_note) sdh = epson_inb(du->dk_port+ wd_sdh); else sdh = inb(du->dk_port+ wd_sdh); sdh = sdh | 0x10; if (old_epson_note) epson_outb(du->dk_port+ wd_sdh, sdh); else outb(du->dk_port+ wd_sdh, sdh); /* Wait, to make sure drv 1 has completed diags */ if ( wdwait(du, 0, TIMEOUT) < 0) goto nodevice; /* Get status for drive 1 */ if (old_epson_note) du->dk_error = epson_errorf(du->dk_port + wd_error); else du->dk_error = inb(du->dk_port + wd_error); /* printf("Error (drv 1) : %x\n", du->dk_error); */ if(du->dk_error != 0x01) goto nodevice; } else /* drive 0 fail */ goto nodevice; } free(du, M_TEMP); return (IO_WDCSIZE); nodevice: free(du, M_TEMP); return (0); } /* * Attach each drive if possible. */ static int wdattach(struct pc98_device *dvp) { #ifdef DEVFS int mynor; #endif int unit, lunit; struct pc98_device *wdup; struct disk *du; if (dvp->id_unit >= NWDC) return (0); kdc_wdc[dvp->id_unit].kdc_state = DC_UNKNOWN; /* XXX */ TAILQ_INIT( &wdtab[dvp->id_unit].controller_queue); for (wdup = pc98_biotab_wdc; wdup->id_driver != 0; wdup++) { if (!old_epson_note) { if (wdup->id_iobase != dvp->id_iobase) continue; } lunit = wdup->id_unit; if (lunit >= NWD) continue; unit = wdup->id_physid; #ifdef PC98 if ((lunit%2)!=0) { if ((PC98_SYSTEM_PARAMETER(0x457) & 0x40)==0) { continue; } } #endif du = malloc(sizeof *du, M_TEMP, M_NOWAIT); if (du == NULL) continue; if (wddrives[lunit] != NULL) panic("drive attached twice"); wddrives[lunit] = du; TAILQ_INIT( &drive_queue[lunit]); bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; du->dk_unit = unit; du->dk_lunit = lunit; du->dk_port = dvp->id_iobase; /* * Use the individual device flags or the controller * flags. */ du->cfg_flags = wdup->id_flags | ((dvp->id_flags) >> (16 * unit)); if (wdgetctlr(du) == 0) { /* * Print out description of drive. * wdp_model may not be null terminated. */ printf("wdc%d: unit %d (wd%d): <%.*s>", dvp->id_unit, unit, lunit, sizeof du->dk_params.wdp_model, du->dk_params.wdp_model); if (du->dk_flags & DKFL_32BIT) printf(", 32-bit"); if (du->dk_multi > 1) printf(", multi-block-%d", du->dk_multi); if (du->cfg_flags & WDOPT_SLEEPHACK) printf(", sleep-hack"); printf("\n"); if (du->dk_params.wdp_heads == 0) printf("wd%d: size unknown, using %s values\n", lunit, du->dk_dd.d_secperunit > 17 ? "BIOS" : "fake"); printf( "wd%d: %luMB (%lu sectors), %lu cyls, %lu heads, %lu S/T, %lu B/S\n", lunit, du->dk_dd.d_secperunit * du->dk_dd.d_secsize / (1024 * 1024), du->dk_dd.d_secperunit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors, du->dk_dd.d_secsize); /* * Start timeout routine for this drive. * XXX timeout should be per controller. */ wdtimeout(du); wd_registerdev(dvp->id_unit, lunit); #ifdef DEVFS mynor = dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART); du->dk_bdev = devfs_add_devswf(&wd_bdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, "wd%d", unit); du->dk_cdev = devfs_add_devswf(&wd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, "rwd%d", unit); #endif if (dk_ndrive < DK_NDRIVE) { sprintf(dk_names[dk_ndrive], "wd%d", lunit); /* * XXX we don't know the transfer rate of the * drive. Guess the maximum ISA rate of * 4MB/sec. `wpms' is words per _second_ * according to iostat. */ dk_wpms[dk_ndrive] = 4 * 1024 * 1024 / 2; du->dk_dkunit = dk_ndrive++; } else { du->dk_dkunit = -1; } } else { free(du, M_TEMP); wddrives[lunit] = NULL; } } #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif #ifdef ATAPI /* * Probe all free IDE units, searching for ATAPI drives. */ #ifdef PC98 for (unit=0; unit<4; ++unit) { outb(0x432,unit%2); #else for (unit=0; unit<2; ++unit) { #endif /* PC98 */ for (lunit=0; lunitdk_ctrlr == dvp->id_unit && wddrives[lunit]->dk_unit == unit) goto next; atapi_attach (dvp->id_unit, unit, dvp->id_iobase, &kdc_wdc[dvp->id_unit]); next: } #endif /* * Discard any interrupts generated by wdgetctlr(). wdflushirq() * doesn't work now because the ambient ipl is too high. */ wdtab[dvp->id_unit].b_active = 2; return (1); } /* Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ void wdstrategy(register struct buf *bp) { struct disk *du; int lunit = dkunit(bp->b_dev); int s; /* valid unit, controller, and request? */ if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL || bp->b_bcount % DEV_BSIZE != 0) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; goto done; } #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* * Do bounds checking, adjust transfer, set b_cylin and b_pbklno. */ if (dscheck(bp, du->dk_slices) <= 0) goto done; /* * Check for *any* block on this transfer being on the bad block list * if it is, then flag the block as a transfer that requires * bad block handling. Also, used as a hint for low level disksort * clustering code to keep from coalescing a bad transfer into * a normal transfer. Single block transfers for a large number of * blocks associated with a cluster I/O are undesirable. * * XXX the old disksort() doesn't look at B_BAD. Coalescing _is_ * desirable. We should split the results at bad blocks just * like we should split them at MAXTRANSFER boundaries. */ if (dsgetbad(bp->b_dev, du->dk_slices) != NULL) { long *badsect = dsgetbad(bp->b_dev, du->dk_slices)->bi_bad; int i; int nsecs = howmany(bp->b_bcount, DEV_BSIZE); /* XXX pblkno is too physical. */ daddr_t nspblkno = bp->b_pblkno - du->dk_slices->dss_slices[dkslice(bp->b_dev)].ds_offset; int blkend = nspblkno + nsecs; for (i = 0; badsect[i] != -1 && badsect[i] < blkend; i++) { if (badsect[i] >= nspblkno) { bp->b_flags |= B_BAD; break; } } } /* queue transfer on drive, activate drive and controller if idle */ s = splbio(); tqdisksort(&drive_queue[lunit], bp); if (wdutab[lunit].b_active == 0) wdustart(du); /* start drive */ /* Pick up changes made by readdisklabel(). */ if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) { wdsleep(du->dk_ctrlr, "wdlab"); du->dk_state = WANTOPEN; } if (wdtab[du->dk_ctrlr].b_active == 0) wdstart(du->dk_ctrlr); /* start controller */ if (du->dk_dkunit >= 0) { /* * XXX perhaps we should only count successful transfers. */ dk_xfer[du->dk_dkunit]++; /* * XXX we can't count seeks correctly but we can do better * than this. E.g., assume that the geometry is correct * and count 1 seek if the starting cylinder of this i/o * differs from the starting cylinder of the previous i/o, * or count 1 seek if the starting bn of this i/o doesn't * immediately follow the ending bn of the previos i/o. */ dk_seek[du->dk_dkunit]++; } splx(s); return; done: s = splbio(); /* toss transfer, we're done early */ biodone(bp); splx(s); } static void wdstrategy1(struct buf *bp) { /* * XXX - do something to make wdstrategy() but not this block while * we're doing dsinit() and dsioctl(). */ wdstrategy(bp); } /* * Routine to queue a command to the controller. The unit's * request is linked into the active list for the controller. * If the controller is idle, the transfer is started. */ static void wdustart(register struct disk *du) { register struct buf *bp; int ctrlr = du->dk_ctrlr; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* unit already active? */ if (wdutab[du->dk_lunit].b_active) return; bp = drive_queue[du->dk_lunit].tqh_first; if (bp == NULL) { /* yes, an assign */ return; } TAILQ_REMOVE( &drive_queue[du->dk_lunit], bp, b_act); /* link onto controller queue */ TAILQ_INSERT_TAIL( &wdtab[ctrlr].controller_queue, bp, b_act); /* mark the drive unit as busy */ wdutab[du->dk_lunit].b_active = 1; } /* * Controller startup routine. This does the calculation, and starts * a single-sector read or write operation. Called to start a transfer, * or from the interrupt routine to continue a multi-sector transfer. * RESTRICTIONS: * 1. The transfer length must be an exact multiple of the sector size. */ void wdstart(int ctrlr) { register struct disk *du; register struct buf *bp; struct diskgeom *lp; /* XXX sic */ long blknum; long secpertrk, secpercyl; int lunit; int count; #ifdef ATAPI if (wdtab[ctrlr].b_active == 2) wdtab[ctrlr].b_active = 0; if (wdtab[ctrlr].b_active) return; #endif /* is there a drive for the controller to do a transfer with? */ bp = wdtab[ctrlr].controller_queue.tqh_first; if (bp == NULL) { #ifdef ATAPI if (atapi_start && atapi_start (ctrlr)) /* mark controller active in ATAPI mode */ wdtab[ctrlr].b_active = 3; #endif return; } /* obtain controller and drive information */ lunit = dkunit(bp->b_dev); du = wddrives[lunit]; #ifdef PC98 outb(0x432,(du->dk_unit)%2); if ( du->single_sector == 1 ) { du->dk_flags |= DKFL_SINGLE; /* XXX */ } #endif /* if not really a transfer, do control operations specially */ if (du->dk_state < OPEN) { if (du->dk_state != WANTOPEN) printf("wd%d: wdstart: weird dk_state %d\n", du->dk_lunit, du->dk_state); if (wdcontrol(bp) != 0) printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n", du->dk_lunit, du->dk_state); return; } /* calculate transfer details */ blknum = bp->b_pblkno + du->dk_skip; #ifdef WDDEBUG if (du->dk_skip == 0) printf("wd%d: wdstart: %s %d@%d; map ", lunit, (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, blknum); else { if (old_epson_note) printf(" %d)%x", du->dk_skip, epson_inb(du->dk_port + wd_altsts)); else printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts)); } #endif lp = &du->dk_dd; secpertrk = lp->d_nsectors; secpercyl = lp->d_secpercyl; if (du->dk_skip == 0) { du->dk_bc = bp->b_bcount; if (bp->b_flags & B_BAD /* * XXX handle large transfers inefficiently instead * of crashing on them. */ || howmany(du->dk_bc, DEV_BSIZE) > MAXTRANSFER) du->dk_flags |= DKFL_SINGLE; } if (du->dk_flags & DKFL_SINGLE && dsgetbad(bp->b_dev, du->dk_slices) != NULL) { /* XXX */ u_long ds_offset = du->dk_slices->dss_slices[dkslice(bp->b_dev)].ds_offset; blknum = transbad144(dsgetbad(bp->b_dev, du->dk_slices), blknum - ds_offset) + ds_offset; } wdtab[ctrlr].b_active = 1; /* mark controller active */ /* if starting a multisector transfer, or doing single transfers */ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) { u_int command; u_int count; long cylin, head, sector; cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += DEV_BSIZE; count = howmany( du->dk_bc, DEV_BSIZE); du->dk_flags &= ~DKFL_MULTI; #ifdef B_FORMAT if (bp->b_flags & B_FORMAT) { command = WDCC_FORMAT; count = lp->d_nsectors; sector = lp->d_gap3 - 1; /* + 1 later */ } else #endif { if (du->dk_flags & DKFL_SINGLE) { command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE; count = 1; du->dk_currentiosize = 1; } else { if( (count > 1) && (du->dk_multi > 1)) { du->dk_flags |= DKFL_MULTI; if( bp->b_flags & B_READ) { command = WDCC_READ_MULTI; } else { command = WDCC_WRITE_MULTI; } du->dk_currentiosize = du->dk_multi; if( du->dk_currentiosize > count) du->dk_currentiosize = count; } else { if( bp->b_flags & B_READ) { command = WDCC_READ; } else { command = WDCC_WRITE; } du->dk_currentiosize = 1; } } } /* * XXX this loop may never terminate. The code to handle * counting down of retries and eventually failing the i/o * is in wdintr() and we can't get there from here. */ if (wdtest != 0) { if (--wdtest == 0) { wdtest = 100; printf("dummy wdunwedge\n"); wdunwedge(du); } } if(du->dk_dkunit >= 0) { dk_busy |= 1 << du->dk_dkunit; } while (wdcommand(du, cylin, head, sector, count, command) != 0) { wderror(bp, du, "wdstart: timeout waiting to give command"); wdunwedge(du); } #ifdef WDDEBUG printf("cylin %ld head %ld sector %ld addr %x sts ", cylin, head, sector, (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE) if (old_epson_note) printf("%x\n, epson_inb(du->dk_port + wd_altsts)); else printf("%x\n, inb(du->dk_port + wd_altsts)); #endif } /* * Schedule wdtimeout() to wake up after a few seconds. Retrying * unmarked bad blocks can take 3 seconds! Then it is not good that * we retry 5 times. * * XXX wdtimeout() doesn't increment the error count so we may loop * forever. More seriously, the loop isn't forever but causes a * crash. * * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive * counts being discarded after there is an error (in physio I * think). Discarding them would be OK if the (special) file offset * was not advanced. */ du->dk_timeout = 1 + 3; /* If this is a read operation, just go away until it's done. */ if (bp->b_flags & B_READ) return; /* Ready to send data? */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) { wderror(bp, du, "wdstart: timeout waiting for DRQ"); /* * XXX what do we do now? If we've just issued the command, * then we can treat this failure the same as a command * failure. But if we are continuing a multi-sector write, * the command was issued ages ago, so we can't simply * restart it. * * XXX we waste a lot of time unnecessarily translating block * numbers to cylin/head/sector for continued i/o's. */ } count = 1; if( du->dk_flags & DKFL_MULTI) { count = howmany(du->dk_bc, DEV_BSIZE); if( count > du->dk_multi) count = du->dk_multi; if( du->dk_currentiosize > count) du->dk_currentiosize = count; } if (!old_epson_note) { if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(long)); else outsw(du->dk_port + wd_data, (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); } else epson_outsw(du->dk_port + wd_data, (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); du->dk_bc -= DEV_BSIZE * count; if (du->dk_dkunit >= 0) { /* * `wd's are blocks of 32 16-bit `word's according to * iostat. dk_wds[] is the one disk i/o statistic that * we can record correctly. * XXX perhaps we shouldn't record words for failed * transfers. */ dk_wds[du->dk_dkunit] += (count * DEV_BSIZE) >> 6; } } /* Interrupt routine for the controller. Acknowledge the interrupt, check for * errors on the current operation, mark it done if necessary, and start * the next request. Also check for a partially done transfer, and * continue with the next chunk if so. */ void wdintr(int unit) { register struct disk *du; register struct buf *bp; if (wdtab[unit].b_active == 2) return; /* intr in wdflushirq() */ if (!wdtab[unit].b_active) { #ifdef WDDEBUG /* * These happen mostly because the power-mgt part of the * bios shuts us down, and we just manage to see the * interrupt from the "SLEEP" command. */ printf("wdc%d: extra interrupt\n", unit); #endif return; } #ifdef ATAPI if (wdtab[unit].b_active == 3) { /* process an ATAPI interrupt */ if (atapi_intr && atapi_intr (unit)) /* ATAPI op continues */ return; /* controller is free, start new op */ wdtab[unit].b_active = 0; wdstart (unit); return; } #endif bp = wdtab[unit].controller_queue.tqh_first; du = wddrives[dkunit(bp->b_dev)]; du->dk_timeout = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif if (wdwait(du, 0, TIMEOUT) < 0) { wderror(bp, du, "wdintr: timeout waiting for status"); du->dk_status |= WDCS_ERR; /* XXX */ } /* is it not a transfer, but a control operation? */ if (du->dk_state < OPEN) { wdtab[unit].b_active = 0; switch (wdcontrol(bp)) { case 0: return; case 1: wdstart(unit); return; case 2: goto done; } } /* have we an error? */ if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) { oops: /* * XXX bogus inb() here, register 0 is assumed and intr status * is reset. */ if (old_epson_note) { if( (du->dk_status & DKFL_MULTI) && (epson_inb(du->dk_port) & WDERR_ABORT)) { wderror(bp, du, "reverting to non-multi sector mode"); du->dk_multi = 1; } } else { if( (du->dk_status & DKFL_MULTI) && (inb(du->dk_port) & WDERR_ABORT)) { wderror(bp, du, "reverting to non-multi sector mode"); du->dk_multi = 1; } } #ifdef WDDEBUG wderror(bp, du, "wdintr"); #endif if ((du->dk_flags & DKFL_SINGLE) == 0) { du->dk_flags |= DKFL_ERROR; goto outt; } #ifdef B_FORMAT if (bp->b_flags & B_FORMAT) { bp->b_error = EIO; bp->b_flags |= B_ERROR; goto done; } #endif if (du->dk_flags & DKFL_BADSCAN) { bp->b_error = EIO; bp->b_flags |= B_ERROR; } else if (du->dk_status & WDCS_ERR) { if (++wdtab[unit].b_errcnt < RETRIES) { wdtab[unit].b_active = 0; } else { wderror(bp, du, "hard error"); bp->b_error = EIO; bp->b_flags |= B_ERROR; /* flag the error */ } } else wderror(bp, du, "soft ecc"); } /* * If this was a successful read operation, fetch the data. */ if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab[unit].b_active) { int chk, dummy, multisize; multisize = chk = du->dk_currentiosize * DEV_BSIZE; if( du->dk_bc < chk) { chk = du->dk_bc; if( ((chk + DEV_BSIZE - 1) / DEV_BSIZE) < du->dk_currentiosize) { du->dk_currentiosize = (chk + DEV_BSIZE - 1) / DEV_BSIZE; multisize = du->dk_currentiosize * DEV_BSIZE; } } /* ready to receive data? */ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) wderror(bp, du, "wdintr: read intr arrived early"); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { wderror(bp, du, "wdintr: read error detected late"); goto oops; } /* suck in data */ if (!old_epson_note) { if( du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), chk / sizeof(long)); else insw(du->dk_port + wd_data, (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), chk / sizeof(short)); } else epson_insw(du->dk_port + wd_data, (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), chk / sizeof(short)); du->dk_bc -= chk; /* XXX for obsolete fractional sector reads. */ while (chk < multisize) { if (!old_epson_note) insw(du->dk_port + wd_data, &dummy, 1); else epson_insw(du->dk_port + wd_data, &dummy, 1); chk += sizeof(short); } if (du->dk_dkunit >= 0) dk_wds[du->dk_dkunit] += chk >> 6; } outt: if (wdtab[unit].b_active) { if ((bp->b_flags & B_ERROR) == 0) { du->dk_skip += du->dk_currentiosize;/* add to successful sectors */ if (wdtab[unit].b_errcnt) wderror(bp, du, "soft error"); wdtab[unit].b_errcnt = 0; /* see if more to transfer */ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { wdtab[unit].b_active = 0; wdstart(unit); return; /* next chunk is started */ } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR)) == DKFL_ERROR) { du->dk_skip = 0; du->dk_flags &= ~DKFL_ERROR; du->dk_flags |= DKFL_SINGLE; wdtab[unit].b_active = 0; wdstart(unit); return; /* redo xfer sector by sector */ } } done: ; /* done with this transfer, with or without error */ du->dk_flags &= ~DKFL_SINGLE; TAILQ_REMOVE(&wdtab[unit].controller_queue, bp, b_act); wdtab[unit].b_errcnt = 0; bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE; wdutab[du->dk_lunit].b_active = 0; wdutab[du->dk_lunit].b_errcnt = 0; du->dk_skip = 0; biodone(bp); } if(du->dk_dkunit >= 0) { dk_busy &= ~(1 << du->dk_dkunit); } /* controller idle */ wdtab[unit].b_active = 0; /* anything more on drive queue? */ wdustart(du); /* anything more for controller to do? */ #ifndef ATAPI /* This is not valid in ATAPI mode. */ if (wdtab[unit].controller_queue.tqh_first) #endif wdstart(unit); } /* * Initialize a drive. */ int wdopen(dev_t dev, int flags, int fmt, struct proc *p) { register unsigned int lunit; register struct disk *du; int error; lunit = dkunit(dev); if (lunit >= NWD || dktype(dev) != 0) return (ENXIO); du = wddrives[lunit]; if (du == NULL) return (ENXIO); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Finish flushing IRQs left over from wdattach(). */ if (wdtab[du->dk_ctrlr].b_active == 2) wdtab[du->dk_ctrlr].b_active = 0; du->dk_flags &= ~DKFL_BADSCAN; while (du->dk_flags & DKFL_LABELLING) tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1); #if 1 kdc_wd[lunit].kdc_state = DC_BUSY; wdsleep(du->dk_ctrlr, "wdopn1"); du->dk_flags |= DKFL_LABELLING; du->dk_state = WANTOPEN; { struct disklabel label; bzero(&label, sizeof label); label.d_secsize = du->dk_dd.d_secsize; label.d_nsectors = du->dk_dd.d_nsectors; label.d_ntracks = du->dk_dd.d_ntracks; label.d_ncylinders = du->dk_dd.d_ncylinders; label.d_secpercyl = du->dk_dd.d_secpercyl; label.d_secperunit = du->dk_dd.d_secperunit; error = dsopen("wd", dev, fmt, &du->dk_slices, &label, wdstrategy1, (ds_setgeom_t *)NULL, &wd_bdevsw, &wd_cdevsw); } du->dk_flags &= ~DKFL_LABELLING; wdsleep(du->dk_ctrlr, "wdopn2"); return (error); #else if ((du->dk_flags & DKFL_BSDLABEL) == 0) { /* * wdtab[ctrlr].b_active != 0 implies XXX applicable now ?? * drive_queue[lunit].b_act == NULL (?) XXX applicable now ?? * so the following guards most things (until the next i/o). * It doesn't guard against a new i/o starting and being * affected by the label being changed. Sigh. */ wdsleep(du->dk_ctrlr, "wdopn1"); du->dk_flags |= DKFL_LABELLING; du->dk_state = WANTOPEN; error = dsinit(dkmodpart(dev, RAW_PART), wdstrategy, &du->dk_dd, &du->dk_slices); if (error != 0) { du->dk_flags &= ~DKFL_LABELLING; return (error); } /* XXX check value returned by wdwsetctlr(). */ wdwsetctlr(du); if (dkslice(dev) == WHOLE_DISK_SLICE) { dsopen(dev, fmt, du->dk_slices); return (0); } /* * Read label using RAW_PART partition. * * If the drive has an MBR, then the current geometry (from * wdgetctlr()) is used to read it; then the BIOS/DOS * geometry is inferred and used to read the label off the * 'c' partition. Otherwise the label is read using the * current geometry. The label gives the final geometry. * If bad sector handling is enabled, then this geometry * is used to read the bad sector table. The geometry * changes occur inside readdisklabel() and are propagated * to the driver by resetting the state machine. * * XXX can now handle changes directly since dsinit() doesn't * do too much. */ msg = correct_readdisklabel(dkmodpart(dev, RAW_PART), wdstrategy, &du->dk_dd); /* XXX check value returned by wdwsetctlr(). */ wdwsetctlr(du); if (msg == NULL && du->dk_dd.d_flags & D_BADSECT) msg = readbad144(dkmodpart(dev, RAW_PART), wdstrategy, &du->dk_dd, &du->dk_bad); du->dk_flags &= ~DKFL_LABELLING; if (msg != NULL) { log(LOG_WARNING, "wd%d: cannot find label (%s)\n", lunit, msg); if (part != RAW_PART) return (EINVAL); /* XXX needs translation */ /* * Soon return. This is how slices without labels * are allowed. They only work on the raw partition. */ } else { unsigned long newsize, offset, size; #if 0 /* * Force RAW_PART partition to be the whole disk. */ offset = du->dk_dd.d_partitions[RAW_PART].p_offset; if (offset != 0) { printf( "wd%d: changing offset of '%c' partition from %lu to 0\n", du->dk_lunit, 'a' + RAW_PART, offset); du->dk_dd.d_partitions[RAW_PART].p_offset = 0; } size = du->dk_dd.d_partitions[RAW_PART].p_size; newsize = du->dk_dd.d_secperunit; /* XXX */ if (size != newsize) { printf( "wd%d: changing size of '%c' partition from %lu to %lu\n", du->dk_lunit, 'a' + RAW_PART, size, newsize); du->dk_dd.d_partitions[RAW_PART].p_size = newsize; } #endif } /* Pick up changes made by readdisklabel(). */ wdsleep(du->dk_ctrlr, "wdopn2"); du->dk_state = WANTOPEN; } /* * Warn if a partion is opened that overlaps another partition which * is open unless one is the "raw" partition (whole disk). */ if ((du->dk_openpart & mask) == 0 && part != RAW_PART) { int start, end; pp = &du->dk_dd.d_partitions[part]; start = pp->p_offset; end = pp->p_offset + pp->p_size; for (pp = du->dk_dd.d_partitions; pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; pp++) { if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end) continue; if (pp - du->dk_dd.d_partitions == RAW_PART) continue; if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions))) log(LOG_WARNING, "wd%d%c: overlaps open partition (%c)\n", lunit, part + 'a', pp - du->dk_dd.d_partitions + 'a'); } } if (part >= du->dk_dd.d_npartitions && part != RAW_PART) return (ENXIO); dsopen(dev, fmt, du->dk_slices); return (0); #endif } /* * Implement operations other than read/write. * Called from wdstart or wdintr during opens and formats. * Uses finite-state-machine to track progress of operation in progress. * Returns 0 if operation still in progress, 1 if completed, 2 if error. */ static int wdcontrol(register struct buf *bp) { register struct disk *du; int ctrlr; du = wddrives[dkunit(bp->b_dev)]; ctrlr = du->dk_ctrlr; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif switch (du->dk_state) { case WANTOPEN: tryagainrecal: wdtab[ctrlr].b_active = 1; if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) { wderror(bp, du, "wdcontrol: wdcommand failed"); goto maybe_retry; } du->dk_state = RECAL; return (0); case RECAL: if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) { wderror(bp, du, "wdcontrol: recal failed"); maybe_retry: if (du->dk_status & WDCS_ERR) wdunwedge(du); du->dk_state = WANTOPEN; if (++wdtab[ctrlr].b_errcnt < RETRIES) goto tryagainrecal; bp->b_error = ENXIO; /* XXX needs translation */ bp->b_flags |= B_ERROR; return (2); } wdtab[ctrlr].b_errcnt = 0; du->dk_state = OPEN; /* * The rest of the initialization can be done by normal * means. */ return (1); } panic("wdcontrol"); return (2); } /* * Wait uninterruptibly until controller is not busy, then send it a command. * The wait usually terminates immediately because we waited for the previous * command to terminate. */ static int wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command) { u_int wdc; #ifdef PC98 unsigned char u_addr; #endif wdc = du->dk_port; if (du->cfg_flags & WDOPT_SLEEPHACK) { if (old_epson_note) { if(epson_inb(wdc + wd_status) == WDCS_BUSY) wdunwedge(du); } else { if(inb(wdc + wd_status) == WDCS_BUSY) wdunwedge(du); } } if (wdwait(du, 0, TIMEOUT) < 0) return (1); #ifdef PC98 /* u_addr = (du->dk_unit & 0xfe); */ u_addr = ((du->dk_unit)/2)<<4; #endif /* PC98 */ if( command == WDCC_FEATURES) { if (old_epson_note) epson_outb(wdc + wd_features, count); else outb(wdc + wd_features, count); } else { if (old_epson_note) { epson_outb(wdc + wd_precomp, du->dk_dd.d_precompcyl/4); epson_outb(wdc + wd_cyl_lo, cylinder); epson_outb(wdc + wd_cyl_hi, cylinder >> 8); epson_outb(wdc + wd_sdh, WDSD_IBM | u_addr | head); epson_outb(wdc + wd_sector, sector + 1); epson_outb(wdc + wd_seccnt, count); } else { outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4); outb(wdc + wd_cyl_lo, cylinder); outb(wdc + wd_cyl_hi, cylinder >> 8); #ifdef PC98 outb(wdc + wd_sdh, WDSD_IBM | u_addr | head); #else outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit<<4) | head); #endif outb(wdc + wd_sector, sector + 1); outb(wdc + wd_seccnt, count); } } if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC ? 0 : WDCS_READY, TIMEOUT) < 0) return (1); if (old_epson_note) epson_outb(wdc + wd_command, command); else outb(wdc + wd_command, command); return (0); } /* * issue IDC to drive to tell it just what geometry it is to be. */ static int wdsetctlr(struct disk *du) { int error = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif #ifdef WDDEBUG printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n", du->dk_ctrlr, du->dk_unit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors); #endif if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) { struct wdparams *wp; printf("wd%d: can't handle %lu heads from partition table ", du->dk_lunit, du->dk_dd.d_ntracks); /* obtain parameters */ wp = &du->dk_params; if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) { printf("(controller value %u restored)\n", wp->wdp_heads); du->dk_dd.d_ntracks = wp->wdp_heads; } else { printf("(truncating to 16)\n"); du->dk_dd.d_ntracks = 16; } } if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { printf("wd%d: cannot handle %lu sectors (max 255)\n", du->dk_lunit, du->dk_dd.d_nsectors); error = 1; } if (error) { wdtab[du->dk_ctrlr].b_errcnt += RETRIES; return (1); } if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, du->dk_dd.d_nsectors, WDCC_IDC) != 0 || wdwait(du, WDCS_READY, TIMEOUT) < 0) { wderror((struct buf *)NULL, du, "wdsetctlr failed"); return (1); } + + /* + * The config option flags low 8 bits define the maximum multi-block + * transfer size. If the user wants the maximum that the drive + * is capable of, just set the low bits of the config option to + * 0x00ff. + */ + if ((du->cfg_flags & WDOPT_MULTIMASK) != 0 && (du->dk_multi > 1)) { + if (du->dk_multi > (du->cfg_flags & WDOPT_MULTIMASK)) + du->dk_multi = du->cfg_flags & WDOPT_MULTIMASK; + if (wdcommand(du, 0, 0, 0, du->dk_multi, WDCC_SET_MULTI)) { + du->dk_multi = 1; + } + } else { + du->dk_multi = 1; + } + +#ifdef NOTYET +/* set read caching and write caching */ + wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES); + wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES); +#endif + return (0); } #if 0 /* * Wait until driver is inactive, then set up controller. */ static int wdwsetctlr(struct disk *du) { int stat; int x; wdsleep(du->dk_ctrlr, "wdwset"); x = splbio(); stat = wdsetctlr(du); wdflushirq(du, x); splx(x); return (stat); } #endif /* * issue READP to drive to ask it what it is. */ static int wdgetctlr(struct disk *du) { int i; char tb[DEV_BSIZE], tb2[DEV_BSIZE]; struct wdparams *wp = NULL; u_long flags = du->cfg_flags; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif again: if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { #ifdef PC98 if ( du->dk_unit > 1 ) return(1); #endif /* * if we failed on the second try, assume non-32bit */ if( du->dk_flags & DKFL_32BIT) goto failed; /* XXX need to check error status after final transfer. */ /* * Old drives don't support WDCC_READP. Try a seek to 0. * Some IDE controllers return trash if there is no drive * attached, so first test that the drive can be selected. * This also avoids long waits for nonexistent drives. */ if (wdwait(du, 0, TIMEOUT) < 0) return (1); if (old_epson_note) { epson_outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ if ((epson_inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); } else { outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); } if (du->dk_unit == bootinfo.bi_n_bios_used) { du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; du->dk_dd.d_ntracks = ((bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff) + 1; /* XXX Why 2 ? */ du->dk_dd.d_ncylinders = (bootinfo.bi_bios_geom[du->dk_unit] >> 16) + 2; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; #if 0 du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit; du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Bios geometry", sizeof du->dk_dd.d_typename); strncpy(du->dk_params.wdp_model, "ST506", sizeof du->dk_params.wdp_model); #endif bootinfo.bi_n_bios_used ++; return 0; } /* * Fake minimal drive geometry for reading the MBR. * readdisklabel() may enlarge it to read the label and the * bad sector table. */ du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = 17; du->dk_dd.d_ntracks = 1; du->dk_dd.d_ncylinders = 1; du->dk_dd.d_secpercyl = 17; du->dk_dd.d_secperunit = 17; #if 0 /* * Fake maximal drive size for writing the label. */ du->dk_dd.d_partitions[RAW_PART].p_size = 64 * 16 * 1024; /* * Fake some more of the label for printing by disklabel(1) * in case there is no real label. */ du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Fake geometry", sizeof du->dk_dd.d_typename); #endif /* Fake the model name for printing by wdattach(). */ strncpy(du->dk_params.wdp_model, "unknown", sizeof du->dk_params.wdp_model); return (0); } /* obtain parameters */ wp = &du->dk_params; if (!old_epson_note) { if (du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(long)); else insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); } else epson_insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); /* try 32-bit data path (VLB IDE controller) */ if (flags & WDOPT_32BIT) { if (! (du->dk_flags & DKFL_32BIT)) { bcopy(tb, tb2, sizeof(struct wdparams)); du->dk_flags |= DKFL_32BIT; goto again; } /* check that we really have 32-bit controller */ if (bcmp (tb, tb2, sizeof(struct wdparams)) != 0) { failed: /* test failed, use 16-bit i/o mode */ bcopy(tb2, tb, sizeof(struct wdparams)); du->dk_flags &= ~DKFL_32BIT; } } bcopy(tb, wp, sizeof(struct wdparams)); /* shuffle string byte order */ for (i = 0; i < sizeof(wp->wdp_model); i += 2) { u_short *p; p = (u_short *) (wp->wdp_model + i); *p = ntohs(*p); } /* * Clean up the wdp_model by converting nulls to spaces, and * then removing the trailing spaces. */ for (i=0; i < sizeof(wp->wdp_model); i++) { if (wp->wdp_model[i] == '\0') { wp->wdp_model[i] = ' '; } } for (i=sizeof(wp->wdp_model)-1; i>=0 && wp->wdp_model[i]==' '; i--) { wp->wdp_model[i] = '\0'; } #ifdef WDDEBUG printf( "\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", du->dk_ctrlr, du->dk_unit, wp->wdp_config, wp->wdp_cylinders, wp->wdp_heads, wp->wdp_sectors, wp->wdp_buffertype, wp->wdp_buffersize, wp->wdp_model); #endif #ifdef PC98 /* for larger than 40MB */ { long cyl = wp->wdp_cylinders * wp->wdp_heads * wp->wdp_sectors; if ( du->dk_unit > 1 ) { wp->wdp_sectors = 17; wp->wdp_heads = 8; } else { wp->wdp_sectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; wp->wdp_heads = (bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff; } wp->wdp_cylinders = cyl / (wp->wdp_heads * wp->wdp_sectors); } #endif /* update disklabel given drive information */ du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */ du->dk_dd.d_ntracks = wp->wdp_heads; du->dk_dd.d_nsectors = wp->wdp_sectors; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; if (WDOPT_FORCEHD(du->cfg_flags)) { du->dk_dd.d_ntracks = WDOPT_FORCEHD(du->cfg_flags); du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } #if 0 du->dk_dd.d_partitions[RAW_PART].p_size = du->dk_dd.d_secperunit; /* dubious ... */ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9); bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1); /* better ... */ du->dk_dd.d_type = DTYPE_ESDI; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; #endif - /* - * find out the drives maximum multi-block transfer capability - */ - du->dk_multi = wp->wdp_nsecperint & 0xff; - - /* - * The config option flags low 8 bits define the maximum multi-block - * transfer size. If the user wants the maximum that the drive - * is capable of, just set the low bits of the config option to - * 0x00ff. - */ - if ((flags & WDOPT_MULTIMASK) != 0 && (du->dk_multi > 1)) { - if (du->dk_multi > (flags & WDOPT_MULTIMASK)) - du->dk_multi = flags & WDOPT_MULTIMASK; - if (wdcommand(du, 0, 0, 0, du->dk_multi, WDCC_SET_MULTI)) { - du->dk_multi = 1; - } - } else { - du->dk_multi = 1; - } - -#ifdef NOTYET -/* set read caching and write caching */ - wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES); - wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES); -#endif - return (0); } int wdclose(dev_t dev, int flags, int fmt, struct proc *p) { dsclose(dev, fmt, wddrives[dkunit(dev)]->dk_slices); kdc_wd[wddrives[dkunit(dev)]->dk_lunit].kdc_state = DC_IDLE; return (0); } int wdioctl(dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p) { int lunit = dkunit(dev); register struct disk *du; int error; #ifdef notyet struct uio auio; struct iovec aiov; struct format_op *fop; #endif du = wddrives[lunit]; wdsleep(du->dk_ctrlr, "wdioct"); error = dsioctl("wd", dev, cmd, addr, flags, &du->dk_slices, wdstrategy1, (ds_setgeom_t *)NULL); if (error != -1) return (error); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif switch (cmd) { case DIOCSBADSCAN: if (*(int *)addr) du->dk_flags |= DKFL_BADSCAN; else du->dk_flags &= ~DKFL_BADSCAN; return (0); #ifdef notyet case DIOCWFORMAT: if (!(flag & FWRITE)) return (EBADF); fop = (struct format_op *)addr; aiov.iov_base = fop->df_buf; aiov.iov_len = fop->df_count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = fop->df_count; auio.uio_segflg = 0; auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize; #error /* XXX the 386BSD interface is different */ error = physio(wdformat, &rwdbuf[lunit], 0, dev, B_WRITE, minphys, &auio); fop->df_count -= auio.uio_resid; fop->df_reg[0] = du->dk_status; fop->df_reg[1] = du->dk_error; return (error); #endif default: return (ENOTTY); } return (error); } #ifdef B_FORMAT int wdformat(struct buf *bp) { bp->b_flags |= B_FORMAT; wdstrategy(bp); /* * phk put this here, better that return(wdstrategy(bp)); * XXX */ return -1; } #endif int wdsize(dev_t dev) { struct disk *du; int lunit; lunit = dkunit(dev); if (lunit >= NWD || dktype(dev) != 0) return (-1); du = wddrives[lunit]; if (du == NULL) return (-1); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif return (dssize(dev, &du->dk_slices, wdopen, wdclose)); } /* * Dump core after a system crash. */ int wddump(dev_t dev) { #ifdef PC98 /* do nothing */ printf("wddump!!\n"); return(0); #else register struct disk *du; struct disklabel *lp; long num; /* number of sectors to write */ int lunit, part; long blkoff, blknum; long blkchk, blkcnt, blknext; long cylin, head, sector; long secpertrk, secpercyl, nblocks; u_long ds_offset; char *addr; static int wddoingadump = 0; /* Toss any characters present prior to dump. */ while (cncheckc()) ; /* Check for acceptable device. */ /* XXX should reset to maybe allow du->dk_state < OPEN. */ lunit = dkunit(dev); /* eventually support floppies? */ part = dkpart(dev); if (lunit >= NWD || (du = wddrives[lunit]) == NULL || du->dk_state < OPEN || (lp = dsgetlabel(dev, du->dk_slices)) == NULL) return (ENXIO); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Size of memory to dump, in disk sectors. */ num = (u_long)Maxmem * PAGE_SIZE / du->dk_dd.d_secsize; secpertrk = du->dk_dd.d_nsectors; secpercyl = du->dk_dd.d_secpercyl; nblocks = lp->d_partitions[part].p_size; blkoff = lp->d_partitions[part].p_offset; /* XXX */ ds_offset = du->dk_slices->dss_slices[dkslice(dev)].ds_offset; blkoff += ds_offset; #if 0 pg("part %x, nblocks %d, dumplo %d num %d\n", part, nblocks, dumplo, num); #endif /* Check transfer bounds against partition size. */ if (dumplo < 0 || dumplo + num > nblocks) return (EINVAL); /* Check if we are being called recursively. */ if (wddoingadump) return (EFAULT); #if 0 /* Mark controller active for if we panic during the dump. */ wdtab[du->dk_ctrlr].b_active = 1; #endif wddoingadump = 1; /* Recalibrate the drive. */ DELAY(5); /* ATA spec XXX NOT */ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 || wdsetctlr(du) != 0) { wderror((struct buf *)NULL, du, "wddump: recalibrate failed"); return (EIO); } du->dk_flags |= DKFL_SINGLE; addr = (char *) 0; blknum = dumplo + blkoff; while (num > 0) { blkcnt = num; if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; /* Keep transfer within current cylinder. */ if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) blkcnt = secpercyl - (blknum % secpercyl); blknext = blknum + blkcnt; /* * See if one of the sectors is in the bad sector list * (if we have one). If the first sector is bad, then * reduce the transfer to this one bad sector; if another * sector is bad, then reduce reduce the transfer to * avoid any bad sectors. */ if (du->dk_flags & DKFL_SINGLE && dsgetbad(dev, du->dk_slices) != NULL) { for (blkchk = blknum; blkchk < blknum + blkcnt; blkchk++) { daddr_t blknew; blknew = transbad144(dsgetbad(dev, du->dk_slices), blkchk - ds_offset) + ds_offset; if (blknew != blkchk) { /* Found bad block. */ blkcnt = blkchk - blknum; if (blkcnt > 0) { blknext = blknum + blkcnt; goto out; } blkcnt = 1; blknext = blknum + blkcnt; #if 1 || defined(WDDEBUG) printf("bad block %lu -> %lu\n", blknum, blknew); #endif break; } } } out: /* Compute disk address. */ cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; #if 0 /* Let's just talk about this first... */ pg("cylin l%d head %ld sector %ld addr 0x%x count %ld", cylin, head, sector, addr, blkcnt); #endif /* Do the write. */ if (wdcommand(du, cylin, head, sector, blkcnt, WDCC_WRITE) != 0) { wderror((struct buf *)NULL, du, "wddump: timeout waiting to to give command"); return (EIO); } while (blkcnt != 0) { pmap_enter(kernel_pmap, (vm_offset_t)CADDR1, trunc_page(addr), VM_PROT_READ, TRUE); /* Ready to send data? */ DELAY(5); /* ATA spec */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) { wderror((struct buf *)NULL, du, "wddump: timeout waiting for DRQ"); return (EIO); } if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, CADDR1 + ((int)addr & PAGE_MASK), DEV_BSIZE / sizeof(long)); else outsw(du->dk_port + wd_data, CADDR1 + ((int)addr & PAGE_MASK), DEV_BSIZE / sizeof(short)); addr += DEV_BSIZE; if ((unsigned)addr % (1024 * 1024) == 0) printf("%ld ", num / (1024 * 1024 / DEV_BSIZE)); num--; blkcnt--; } /* Wait for completion. */ DELAY(5); /* ATA spec XXX NOT */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) < 0) { wderror((struct buf *)NULL, du, "wddump: timeout waiting for status"); return (EIO); } /* Check final status. */ if (du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ | WDCS_ERR) != (WDCS_READY | WDCS_SEEKCMPLT)) { wderror((struct buf *)NULL, du, "wddump: extra DRQ, or error"); return (EIO); } /* Update block count. */ blknum = blknext; /* Operator aborting dump? */ if (cncheckc()) return (EINTR); } return (0); #endif } static void wderror(struct buf *bp, struct disk *du, char *mesg) { if (bp == NULL) printf("wd%d: %s:\n", du->dk_lunit, mesg); else diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, dsgetlabel(bp->b_dev, du->dk_slices)); printf("wd%d: status %b error %b\n", du->dk_lunit, du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS); } /* * Discard any interrupts that were latched by the interrupt system while * we were doing polled i/o. */ static void wdflushirq(struct disk *du, int old_ipl) { wdtab[du->dk_ctrlr].b_active = 2; splx(old_ipl); (void)splbio(); wdtab[du->dk_ctrlr].b_active = 0; } /* * Reset the controller. */ static int wdreset(struct disk *du) { int wdc, err = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif wdc = du->dk_port; (void)wdwait(du, 0, TIMEOUT); #ifdef PC98 if (old_epson_note) { epson_outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); epson_outb(wdc + wd_ctlr, WDCTL_IDS); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 || (du->dk_error = epson_errorf(wdc + wd_error)) != 0x01) return (1); epson_outb(wdc + wd_ctlr, WDCTL_4BIT); err = 0; } else { #endif outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); outb(wdc + wd_ctlr, WDCTL_IDS); #ifdef ATAPI if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) err = 1; /* no IDE drive found */ du->dk_error = inb(wdc + wd_error); if (du->dk_error != 0x01) err = 1; /* the drive is incompatible */ #else if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 || (du->dk_error = inb(wdc + wd_error)) != 0x01) return (1); #endif outb(wdc + wd_ctlr, WDCTL_4BIT); #ifdef PC98 } #endif return (err); } /* * Sleep until driver is inactive. * This is used only for avoiding rare race conditions, so it is unimportant * that the sleep may be far too short or too long. */ static void wdsleep(int ctrlr, char *wmesg) { int s = splbio(); while (wdtab[ctrlr].b_active) tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1); splx(s); } static void wdtimeout(void *cdu) { struct disk *du; int x; static int timeouts; du = (struct disk *)cdu; x = splbio(); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif if (du->dk_timeout != 0 && --du->dk_timeout == 0) { if(timeouts++ == 5) wderror((struct buf *)NULL, du, "Last time I say: interrupt timeout. Probably a portable PC."); else if(timeouts++ < 5) wderror((struct buf *)NULL, du, "interrupt timeout"); wdunwedge(du); wdflushirq(du, x); du->dk_skip = 0; du->dk_flags |= DKFL_SINGLE; wdstart(du->dk_ctrlr); } timeout(wdtimeout, cdu, hz); splx(x); } /* * Reset the controller after it has become wedged. This is different from * wdreset() so that wdreset() can be used in the probe and so that this * can restore the geometry . */ static int wdunwedge(struct disk *du) { struct disk *du1; int lunit; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Schedule other drives for recalibration. */ for (lunit = 0; lunit < NWD; lunit++) if ((du1 = wddrives[lunit]) != NULL && du1 != du && du1->dk_ctrlr == du->dk_ctrlr && du1->dk_state > WANTOPEN) du1->dk_state = WANTOPEN; DELAY(RECOVERYTIME); if (wdreset(du) == 0) { /* * XXX - recalibrate current drive now because some callers * aren't prepared to have its state change. */ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0 && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0 && wdsetctlr(du) == 0) return (0); } wderror((struct buf *)NULL, du, "wdunwedge failed"); return (1); } /* * Wait uninterruptibly until controller is not busy and either certain * status bits are set or an error has occurred. * The wait is usually short unless it is for the controller to process * an entire critical command. * Return 1 for (possibly stale) controller errors, -1 for timeout errors, * or 0 for no errors. * Return controller status in du->dk_status and, if there was a controller * error, return the error code in du->dk_error. */ #ifdef WD_COUNT_RETRIES static int min_retries[NWDC]; #endif static int wdwait(struct disk *du, u_char bits_wanted, int timeout) { int wdc; u_char status; #define POLLING 1000 wdc = du->dk_port; timeout += POLLING; /* * This delay is really too long, but does not impact the performance * as much when using the NSECS_MULTI option. Shorter delays have * caused I/O errors on some drives and system configs. This should * probably be fixed if we develop a better short term delay mechanism. */ DELAY(1); do { #ifdef WD_COUNT_RETRIES if (min_retries[du->dk_ctrlr] > timeout || min_retries[du->dk_ctrlr] == 0) min_retries[du->dk_ctrlr] = timeout; #endif #ifdef PC98 if (old_epson_note) du->dk_status = status = epson_inb(wdc + wd_status); else du->dk_status = status = inb(wdc + wd_status); #else du->dk_status = status = inb(wdc + wd_status); #endif #ifdef ATAPI /* * Atapi drives have a very interesting feature, when attached * as a slave on the IDE bus, and there is no master. * They release the bus after getting the command. * We should reselect the drive here to get the status. */ if (status == 0xff) { outb(wdc + wd_sdh, WDSD_IBM | du->dk_unit << 4); du->dk_status = status = inb(wdc + wd_status); } #endif if (!(status & WDCS_BUSY)) { if (status & WDCS_ERR) { if (old_epson_note) du->dk_error = epson_errorf(wdc + wd_error); else du->dk_error = inb(wdc + wd_error); /* * We once returned here. This is wrong * because the error bit is apparently only * valid after the controller has interrupted * (e.g., the error bit is stale when we wait * for DRQ for writes). So we can't depend * on the error bit at all when polling for * command completion. */ } if ((status & bits_wanted) == bits_wanted) return (status & WDCS_ERR); } if (timeout < TIMEOUT) /* * Switch to a polling rate of about 1 KHz so that * the timeout is almost machine-independent. The * controller is taking a long time to respond, so * an extra msec won't matter. */ DELAY(1000); else DELAY(1); } while (--timeout != 0); return (-1); } static wd_devsw_installed = 0; static void wd_drvinit(void *unused) { - dev_t dev; if( ! wd_devsw_installed ) { - dev = makedev(CDEV_MAJOR,0); - cdevsw_add(&dev,&wd_cdevsw,NULL); - dev = makedev(BDEV_MAJOR,0); - bdevsw_add(&dev,&wd_bdevsw,NULL); + bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &wd_bdevsw); wd_devsw_installed = 1; } } SYSINIT(wddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wd_drvinit,NULL) #endif /* NWDC > 0 */