#ifndef lint static char sccsid[] = "@(#)fd.c 1.1 94/10/31 SMI"; #endif /* * Copyright 1989, Sun Microsystems, Inc. */ /* * Floppy Disk Driver * * Supports the Intel 82072 fdc (in non dma mode) * contains support for vpc software package. * * ASSUMES: * only one controller chip * (for now) only one drive - although multiple drives should be "easy" * only sun4c autoconf * controller is *always* present on the board * DESIGN NOTES: since (1) no one in their right mind would have more than * two floppy drives, and (2) ditto for more than one controller (especially * without dma support!!), and * (3) 3.5" drives don`t have RDY so you can't do overlapped seeks so * *all* operations are single threaded. * therefore --- this device driver is designed to be as simple as possible. * * data structures: * controller - "fdctlr" - * per unit (drive) - "fdunit" - one preallocated per drive configured, * hangs off fdctlr. * per partition (minor device) - "fdpart" - one preallocated (for * now 'cause they're short) for each possible minor device * command/status block - "fdcsb" - one preallocated, hangs off of fdctlr * a dk_label structure for each unit - hangs off fdunit * one buf struct for all physio/ioctls that need it. * * control flow: * for data xfr: * read/write - call physio, which calls strategy * strategy - checks operation, hangs buf struct off fdctlr, calls start * if not already busy. * start - if a bp is on queue, grab it. grab/wait-for the fdctlr and * its fdcsb. fill in args, call fdexec(). due to need to handle * cylinder crossings, we sleep for done in here. * fdexec - check DISKCHG, check command busy bit - TBD if these errors * else stuff commands into chip. if command has no interrupts * then immediately grab result. * To allow transparent retrys, all sleeps on operations done * are in here - since error checking for diskchanged has to * occur at process level (for recalibrate). * for ioctls/raw/open-label_read/attach - grab/wait-for the fdctlr and * its fdcsb. fill in args, call fdexec. ioctl may sleep for * done on fdcsb. * note: sleep for fdctlr (and its fdcsb) on address of fdctlr.c_flags, * sleep for operation on the fdcsb on csb pointed to by fdctlr.c_csb. * (this allows nested csb sleeping). * interrupt: * @level 11 * hardware: Operates like a DMA engine, see fd_asm.s for it. * The two parts of the driver (fd_asm.s and fd.c) communicate * via a set of shared variables, defined in fd_asm.s - so that * fd.c can be deconfigured from the system. see the comments * in fd_asm.s for a more complete description of the protocol. * @level4 * software: Invoked by hardware after op is done and results are all in. * It snarfs results back into fdcsb; if there was an error it * invokes fdrecover() to retry and returns. If no errors, * If someone is waiting on fdcsb done, then wake them. * Note that fdstart() will get activated if it was waiting on * the fdctlr/csb. * * ADDITION: 3rd density support was added if (fdctlr.c_units[fdctlr.c_csb->csb_unit]->un_chars->medium) * * */ #include "fd.h" #if NFD > 0 #if NFD > 3 DON'T MAKE NFD > 3, OK? - the driver doesn't support that #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(sun4c) || defined(sun4m) #include #include #endif sun4c || sun4m #include #include #include #include #include #include #include /* Sony MP-F17W-50D Drive Parameters * High Capacity * Capacity unformatted 2Mb * Capacity formatted 1.47Mb * Encoding method MFM * Recording density 17434 bpi * Track density 135 tpi * Cylinders 80 * Heads 2 * Tracks 160 * Rotational speed 300 rpm * Transfer rate 250/500 kbps * Latency (average) 100 ms * Access time * Average 95 ms * Track to track 3 ms * Head settling time 15 ms * Motor start time 500 ms * Head load time ? ms */ /* * DEFINES ******************************************************************** */ /* the auxiliary register */ #define Auxio (*(u_char *)AUXIO_REG) #define MEDIUM_DENSITY 0x40 #define SEC_SIZE_CODE (fdctlr.c_csb->csb_unit]->un_chars->medium ? 3 : 2) #define CMD_READ (MT + SK + FRAW_RDCMD + MFM) #define CMD_WRITE (MT + FRAW_WRCMD + MFM) /* for main status and fifo registers on chip */ #ifdef I82077 #define Msr *fd_msr #define Dsr *fd_dsr #define Dor *fd_dor #define Dir *fd_dir #define Fifo *fd_fifo #else #define Msr (fdctlr_reg->fdc_control) /* main status reg */ #define Dsr (fdctlr_reg->fdc_control) /* data rate select reg */ #define Fifo (fdctlr_reg->fdc_fifo) #endif #define FDREAD 1 /* for fdrw() flag */ #define FDWRITE 2 /* for fdrw() flag */ #define FDPRI PRIBIO /* at what priority to sleep */ #define FD_CRETRY 1000000 /* retry while sending comand */ #define FD_RRETRY 1000000 /* retry while getting results */ #define FDXC_SLEEP 0x1 /* tell fdexec to sleep 'till done */ #define FDXC_CHECKCHG 0x2 /* tell fdexec to check disk chnged */ #define IOCTL_DEBUG 0xaa /* For debugging only */ /* * EXTERNS ******************************************************************** */ /* * these are in fd_asm.s */ #ifndef lint #ifdef I82077 extern union fdcreg *fdctlr_reg; /* ptr to floppy controller registers */ extern u_char *fd_msr, /* ptr to msr */ *fd_dsr, /* ptr to dsr */ *fd_dor, /* ptr to dor - 82077 only */ *fd_dir, /* ptr to dir - 82077 only */ *fd_fifo; #else extern struct fdcreg *fdctlr_reg; /* ptr to floppy controller registers */ #endif I82077 /* for interface with low level interrupt handler */ /* I = input - set before operation, O = output - available after */ extern u_int fdintr_opmode; /* mode for interrupt handler to run in */ /* I: 1 = r/w/fmt, 2 = seek/recal; */ /* O: hw intr sets it to 4 to notify us */ extern u_char *fdintr_addr; /* I/O: (current) data xfer address */ extern u_int fdintr_len; /* I: data xfer length; O: remainder */ extern u_int fdintr_status; /* I: n/a; O: 0 = ok, TBD XXX NYD errors */ extern u_int fdintr_timeout; /* timeout value for fdc_hardintr ??? */ extern u_char fdintr_statbuf[10]; /* I: n/a; O: 10 bytes of status buffer */ extern u_char *fdintr_statp; /* I: pointer for fdintr_statbuf; O: n/a */ #else lint /* keep lint happy - pretend to declare these here */ #ifdef I82077 union fdcreg *fdctlr_reg; u_char *fd_msr, *fd_dsr, *fd_dor, *fd_dir, *fd_fifo; #else struct fdcreg *fdctlr_reg; #endif I82077 u_int fdintr_opmode; u_char *fdintr_addr; u_int fdintr_len; u_int fdintr_status; u_int fdintr_timeout; u_char fdintr_statbuf[10]; u_char *fdintr_statp; #endif lint /* * STATIC and other forward reference FUNCTIONS */ static void fdselect(); static fdeject(); static fdsense_chng(); fdwatch(); #if defined(sun4c) || defined(sun4m) /* campus sends TC in fdc_hardintr */ extern int fdc_hardintr(); /* defined in ../sun4c/fd_asm.s */ extern int fdc_intr(); /* defined in ../sun4c/fd_asm.s */ #else static fdterm_count(); static fdsense_dnsty(); #endif char *strcpy(); int fdopen(); int fdclose(); int fdstrategy(); int fddump(); int fdsize(); int fdread(); int fdwrite(); int fdioctl(); int fdtype = -1; /* Holds type of controller (i.e FD_82072 or FD_82077). * * XXX - This should be initialized in fdattach by * checking the OBP. */ extern int nulldev(); extern int seltrue(); /* * BSS UN-INITIALIZED DATA **************************************************** */ struct fdunit fdunits[NFD]; /* array of floppy unit state structs */ int fdpri; /* mask this level in critical sections */ struct buf fdkbuf; /* a buf structure for raw io and ioctls */ char fdkl_alabel[NFD][128]; /* space for packed label's ascii strings */ /* * INITIALIZED DATA *********************************************************** */ struct fdctlr fdctlr = { /* floppy ctlr state struct */ &fdunits[0], /* init pts to unit data structs */ #if NFD > 1 &fdunits[1], #endif #if NFD > 2 &fdunits[2], #endif #if NFD > 3 &fdunits[3], #endif 0 }; /* rest is uninitialized */ short rwretry = 25; short skretry = 25; /* * ERROR HANDLING ************************************************************* */ /* * flags/masks for error printing. * the levels are for severity */ #define FDEP_L0 0 /* chatty as can be - for debug! */ #define FDEP_L1 1 /* best for debug */ #define FDEP_L2 2 /* minor errors - retries, etc. */ #define FDEP_L3 3 /* major errors */ #define FDEP_L4 4 /* catastophic errors, don't mask! */ #define FDEP_LMAX 4 /* catastophic errors, don't mask! */ #define FDERRPRINT(l, m, args) \ { if (((l) >= fderrlevel) && ((m) & fderrmask)) printf args; } /* * for each function, we can mask off its printing by clearing its bit in * the fderrmask. Some functions (attach, ident) share a mask bit */ #define FDEM_IDEN 0x00000001 /* fdidentify */ #define FDEM_ATTA 0x00000001 /* fdattach */ #define FDEM_SIZE 0x00000002 /* fdsize */ #define FDEM_OPEN 0x00000004 /* fdopen */ #define FDEM_GETL 0x00000008 /* fdgetlabel */ #define FDEM_CLOS 0x00000010 /* fdclose */ #define FDEM_STRA 0x00000020 /* fdstrategy */ #define FDEM_STRT 0x00000040 /* fdstart */ #define FDEM_RDWR 0x00000080 /* fdrdwr */ #define FDEM_CMD 0x00000100 /* fdcmd */ #define FDEM_EXEC 0x00000200 /* fdexec */ #define FDEM_RECO 0x00000400 /* fdrecover */ #define FDEM_INTR 0x00000800 /* fdintr */ #define FDEM_WATC 0x00001000 /* fdwatch */ #define FDEM_IOCT 0x00002000 /* fdioctl */ #define FDEM_RAWI 0x00004000 /* fdrawioctl */ #define FDEM_DUMP 0x00008000 /* fddump */ #define FDEM_GETC 0x00010000 /* fdgetcsb */ #define FDEM_RETC 0x00020000 /* fdretcsb */ #define FDEM_RESE 0x00040000 /* fdreset */ #define FDEM_RECA 0x00080000 /* fdrecalseek */ #define FDEM_FORM 0x00100000 /* fdformat */ #define FDEM_RW 0x00200000 /* fdrw */ #define FDEM_CHEK 0x00400000 /* fdcheckdisk */ #define FDEM_DSEL 0x00800000 /* fdselect */ #define FDEM_EJEC 0x01000000 /* fdeject */ #define FDEM_SCHG 0x02000000 /* fdsense_chng */ #define FDEM_PACK 0x04000000 /* fdpacklabel */ #ifdef I82077 #define FDEM_MOFF 0x08000000 /* fdmotoff */ #endif I82077 int fderrmask = 0xFFFFFFFF; static short fderrlevel = 3; int fdhardtimeout = 100000; int tosec = 16; /* long timeouts for sundiag for now */ /* * SPECIFY/CONFIGURE CMD PARAMETERS ******************************************* */ /* so they can be easily changed */ u_char fdspec[2] = {0xc2, 0x33}; /* the "specify" parameters */ u_char fdconf[3] = {0x64, 0x58, 0x00}; /* the "configure" parameters */ /* * DEFAULT CHARACTERISTICS **************************************************** */ struct fdk_char fdtypes[] = { /* struct fdk_char fdchar_highdens = */ { 0, /* medium */ 500, /* transfer rate */ 80, /* number of cylinders */ 2, /* number of heads */ 512, /* sector size */ 18, /* sectors per track */ -1, /* (NA) # steps per data track */ }, /* struct fd_char fdchar_meddens */ { 1, /* medium */ 500, /* transfer rate */ 77, /* number of cylinders */ 2, /* number of heads */ 1024, /* sector size */ 8, /* sectors per track */ -1, /* (NA) # steps per data track */ }, /* struct fdk_char fdchar_lowdens = */ { 0, /* medium */ 250, /* transfer rate */ 80, /* number of cylinders */ 2, /* number of heads */ 512, /* sector size */ 9, /* sectors per track */ -1, /* (NA) # steps per data track */ }, }; int nfdtypes = sizeof (fdtypes)/sizeof (fdtypes[0]); int curfdtype; /* * DEFAULT LABEL & PARTITION MAPS ********************************************* */ /* * since a label occupies 512 bytes, and we need at least 2, no make that 4 * for 40 and 80 in both high and low density. = 2K of mostly 0's. * we stash them in a packed array, then call fdunpacklabel() to * expand them when needed. */ struct packed_label fdlbl_high_80 = { "3.5\" floppy cyl 80 alt 0 hd 2 sec 18", /* for compatibility */ 300, /* rotations per minute */ 80, /* # physical cylinders */ 0, /* alternates per cylinder */ 1, /* interleave factor */ 80, /* # of data cylinders */ 0, /* # of alternate cylinders */ 2, /* # of heads in this partition */ 18, /* # of 512 byte sectors per track */ { 0, 79 * 2 * 18 }, /* part 0 - all but last cyl */ { 79, 1 * 2 * 18 }, /* part 1 - just the last cyl */ { 0, 80 * 2 * 18 }, /* part 2 - "the whole thing" */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, }; struct packed_label fdlbl_medium_80 = { "3.5\" floppy cyl 77 alt 0 hd 2 sec 8", 360, /* rotations per minute */ 77, /* # physical cylinders */ 0, /* alternates per cylinder */ 1, /* interleave factor */ 77, /* # of data cylinders */ 0, /* # of alternate cylinders */ 2, /* # of heads in this partition */ 8, /* # of 512 byte sectors per track */ { 0, 76 * 2 * 8 * 2 }, /* part 0 - all but last cyl */ { 76, 1 * 2 * 8 * 2 }, /* part 1 - just the last cyl */ { 0, 77 * 2 * 8 * 2 }, /* part 2 - "the whole thing" */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, }; struct packed_label fdlbl_low_80 = { "3.5\" floppy cyl 80 alt 0 hd 2 sec 9", 300, /* rotations per minute */ 80, /* # physical cylinders */ 0, /* alternates per cylinder */ 1, /* interleave factor */ 80, /* # of data cylinders */ 0, /* # of alternate cylinders */ 2, /* # of heads in this partition */ 9, /* # of 512 byte sectors per track */ { 0, 79 * 2 * 9 }, /* part 0 - all but last cyl */ { 79, 1 * 2 * 9 }, /* part 1 - just the last cyl */ { 0, 80 * 2 * 9 }, /* part 2 - "the whole thing" */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, }; /* * SUN4C AUTOCONF SUPPORT ****************************************************** */ /* for sun4c autoconf stuff, referred to in ioconf.c */ int fdidentify(), fdintr(), fdattach(); struct dev_ops fd_ops = { 1, fdidentify, fdattach, }; /* * LOADABLE MODULE SUPPORT **************************************************** */ #ifndef lint struct bdevsw fd_bdevsw = { fdopen, fdclose, fdstrategy, fddump, fdsize, 0 }; struct cdevsw fd_cdevsw = { fdopen, fdclose, fdread, fdwrite, fdioctl, nulldev, seltrue, 0, 0 }; struct vdldrv fddrv = { VDMAGIC_DRV, /* Drv_magic */ #ifdef I82077 "Onboard 82072/82077 Floppy Driver", /* *Drv_name */ #else "Onboard 82072 Floppy Driver", /* *Drv_name */ #endif &fd_ops, /* *Drv_dev_ops */ &fd_bdevsw, /* *Drv_bdevsw */ &fd_cdevsw, /* *Drv_cdevsw */ 16, /* Drv_blockmajor */ 54 /* Drv_charmajor */ }; #endif !lint /* * FUNCTIONS ****************************************************************** */ #if defined(sun4c) || defined(sun4m) /* for new style autoconf stuff */ /* * fdidentify * identify ctlr */ int fdidentify(name) char *name; { FDERRPRINT(FDEP_L0, FDEM_IDEN, ("fdidentify: name %s\n", name)); if (strcmp(name, "fd") == 0 || strcmp(name, "SUNW,fdtwo") == 0) { return (1); } else return (0); } /* * fdattach * attach controller */ fdattach(dev) register struct dev_info *dev; { struct fdunit *un; #ifdef I82077 u_char *fdtestaddr; #endif I82077 FDERRPRINT(FDEP_L0, FDEM_ATTA, ("fdattach\n")); /* ASSUME we only have one controller, ever! */ dev->devi_unit = 0; /* * map in controller regs; ASSUME it's ALWAYS there */ #ifdef I82077 fdctlr_reg = (union fdcreg *) map_regs(dev->devi_reg->reg_addr, dev->devi_reg->reg_size, dev->devi_reg->reg_bustype); /* See if we have an 072 or 077. If we have an 072 the * dsr/msr will be at location 0x2 since 0x2 maps to 0x0. * If we have an 077, the dor will be at 0x2. */ fdtype = FD_82077; fdtestaddr = (u_char *) fdctlr_reg + 2; if (*fdtestaddr == 0x80) { /* We expect the msr to 0x80, but we'll do some * more checking just in case. */ *fdtestaddr = 0x2; /* default value of dsr */ DELAY(100); if (*fdtestaddr == 0x80) fdtype = FD_82072; } switch (fdtype) { case FD_82072: FDERRPRINT(FDEP_L0, FDEM_ATTA, ("fdattach: type is 82072\n")); /* Initialize addresses of key registers. */ fd_msr = &fdctlr_reg->fdc_82072_reg.fdc_control; fd_dsr = &fdctlr_reg->fdc_82072_reg.fdc_control; fd_fifo = &fdctlr_reg->fdc_82072_reg.fdc_fifo; FDERRPRINT(FDEP_L0, FDEM_ATTA, ("fdattach: msr/dsr at 0x%x\n", fd_msr)); /* Insure that the eject line is reset and TC is clear. */ set_auxioreg(AUX_EJECT, 1); set_auxioreg(AUX_TC, 0); break; case FD_82077: FDERRPRINT(FDEP_L0, FDEM_ATTA, ("fdattach: type is 82077\n")); /* Initialize addresses of key registers. */ fd_msr = &fdctlr_reg->fdc_82077_reg.fdc_control; fd_dsr = &fdctlr_reg->fdc_82077_reg.fdc_control; fd_dor = &fdctlr_reg->fdc_82077_reg.fdc_dor; fd_dir = &fdctlr_reg->fdc_82077_reg.fdc_dir; fd_fifo = &fdctlr_reg->fdc_82077_reg.fdc_fifo; FDERRPRINT(FDEP_L0, FDEM_ATTA, ("fdattach: msr/dsr at 0x%x\n", fd_msr)); /* The 82077 doesnt use the first configuration parameter * so let's adjust that while we know we're an 82077. */ fdconf[0] = 0; /* The reset bit must be cleared to take the 82077 out of * reset state. (Dor = 0x0f after Reset). */ fd_set_dor_reg(0xe8, 0); DELAY(5); fd_set_dor_reg(RESET | DRVSEL, 1); DELAY(5); /* Insure that the eject line is reset and TC is clear. */ set_auxioreg(AUX_TC, 0); break; } #else fdctlr_reg = (struct fdcreg *) map_regs(dev->devi_reg->reg_addr, dev->devi_reg->reg_size, dev->devi_reg->reg_bustype); set_auxioreg(AUX_EJECT, 1); /* insure that the eject line is reset */ #endif I82077 fdctlr.c_csb = (struct fdcsb *)kmem_zalloc(sizeof (struct fdcsb)); /* setup for commands will initialize the csb according to need */ un = fdctlr.c_units[0]; /* XXX unit 0 is hardcoded for now */ /* * set initial controller/drive/disk "characteristics/geometry" */ un->un_chars = (struct fdk_char *)kmem_zalloc(sizeof (struct fdk_char)); curfdtype = 0; /* init current fdtype pointer to beginning of tbl */ *(un->un_chars) = fdtypes[curfdtype]; /* structure copy */ un->un_label = (struct packed_label *)kmem_zalloc(sizeof (struct packed_label)); *(un->un_label) = fdlbl_high_80; /* structure copy */ if (fdctlr.c_csb == NULL || un->un_chars == NULL || un->un_label == NULL) { printf("fdattach%d: kmem_zalloc failed!\n", dev->devi_unit); return (-1); } /* Add a direct trap to the assembly language interrupt handler. */ fdctlr.c_intr = dev->devi_intr->int_pri; switch (fdtype) { case FD_82072: /* reset, specify and configure the controller */ fdgetcsb(); fdreset(); fdretcsb(); fdselect(0, 0); /* DE-select drive zero (used in fdreset) */ /* XXX unit 0 is hardcoded for now */ if (!settrap(fdctlr.c_intr, fdc_hardintr)) panic("fdattach: Cannot set fd trap vector"); break; case FD_82077: addintr(11, fdc_intr, dev->devi_name, 0); /* reset, specify and configure the controller * for Campus2s do the reset after adding ourselves to the * level 11 handler list. The reset causes an interrupt when * the driver is modloaded. Therefore I rearranged the * addintr and reset sequences. Since nobody seems * to why so, I have no choice but to rearrange the code. */ fdgetcsb(); fdreset(); fdretcsb(); fdselect(0, 0); /* DE-select drive zero (used in fdreset) */ /* XXX unit 0 is hardcoded for now */ break; default: panic("fdattach: cannot identify controller"); break; } /* Set up level 4 soft interrupt service routine. */ fdpri = ipltospl(4); addintr(4, fdintr, dev->devi_name, 0); report_dev(dev); return (0); } #ifndef lint /* * fdinit * for a loadable driver * modload modstat & modunload */ /*ARGSUSED*/ fdinit(fcn, vdp, vdi, vds) int fcn; register struct vddrv *vdp; caddr_t vdi; struct vdstat *vds; { struct fdunit *un; int status = 0; switch (fcn) { case VDLOAD: vdp->vdd_vdtab = (struct vdlinkage *)&fddrv; break; case VDUNLOAD: un = fdctlr.c_units[0]; /* XXX unit0 hardcoded */ /* * is anybody still open? */ if (un->un_openmask || un->un_exclmask) { status = EBUSY; } else { /* Remove the direct trap vector */ switch (fdtype) { case FD_82072: if (!settrap(fdctlr.c_intr, NULL)) panic("fdinit: Cannot reset fd trap vector"); break; default: break; } #ifdef VDDRV (void) remintr(4, fdintr); #endif /* free space allocated in fdattach */ kmem_free((caddr_t)fdctlr.c_csb, sizeof (struct fdcsb)); kmem_free((caddr_t)un->un_chars, sizeof (struct fdk_char)); kmem_free((caddr_t)un->un_label, sizeof (struct packed_label)); } break; case VDSTAT: break; default: printf("fdinit: unknown function 0x%x\n", fcn); status = EINVAL; } return (status); } #endif !lint #endif sun4c sun4m /* for new style autoconf stuff */ /* * fdsize * return the size of a logical partition */ fdsize(dev) dev_t dev; { struct fdunit *un; struct dk_map *lp; int unit; if ((unit = UNIT(dev)) >= NFD || fdtype == -1) return (-1); un = fdctlr.c_units[ unit ]; lp = &un->un_label->dkl_map[ PARTITION(dev) ]; FDERRPRINT(FDEP_L0, FDEM_SIZE, ("fdsize: unit %d, part %d, size 0x%x\n", unit, PARTITION(dev), lp->dkl_nblk)); /* * for now simply return what's there (from defaults set in fdattach) * XXX someday maybe attempt to read a label if it hasn't been done yet */ return ((int)lp->dkl_nblk); } /* * fdopen * */ fdopen(dev, flag) dev_t dev; int flag; { int unit, part; struct fdunit *un; u_char pmask; struct dk_map *dkm; int err; static int fdopening=0; int fderr = 0; int fderr2 = 0; /* check unit */ unit = UNIT(dev); if (unit >= NFD || fdtype == -1) { fderr = ENXIO; goto fdout; } un = fdctlr.c_units[ unit ]; /* check partition */ part = PARTITION(dev); pmask = 1 << part; dkm = &(un->un_label->dkl_map[part]); if (dkm->dkl_nblk == 0) { fderr = ENXIO; goto fdout; } FDERRPRINT(FDEP_L1, FDEM_OPEN, ("**********FDOPEN --- A --->: unit %d, part %d\n", unit, part)); /* go to sleep if there is a previous fdopen in process */ while (fdopening) (void) sleep((caddr_t)&fdopening, FDPRI); fdopening=1; /* * insure that drive is present with a recalibrate on first open. */ if (un->un_openmask == 0) { fdgetcsb(); err = fdrecalseek(unit, -1, 0); /* no check changed! */ fdretcsb(); if (err) { FDERRPRINT(FDEP_L3, FDEM_OPEN, ("fd%d: drive not ready\n", unit)); fdselect(unit, 0); /* deselect drv on last close */ fderr = ENXIO; goto fdout; } } fdctlr.c_csb->csb_unit = (u_char) UNIT(dev); /* * check for previous exclusive open, or trying to exclusive open */ if ((un->un_exclmask & pmask) || ((flag & FEXCL) && (un->un_openmask & pmask))) { fderr = EBUSY; goto fdout; } if (flag & FEXCL) { un->un_exclmask |= pmask; } /* don't attempt access, just return successfully */ if (flag & (FNDELAY | FNBIO)) { goto out; } if ((un->un_openmask == 0) && (fderr2 = fdgetlabel(unit))) { /* didn't find label (couldn't read anything) */ FDERRPRINT(FDEP_L3, FDEM_OPEN, ("fd%d: drive not ready\n", unit)); un->un_exclmask &= ~(pmask); if (un->un_openmask == 0) { fdselect(unit, 0); /* deselect drv on last close */ } fderr = fderr2; if (fderr == 0) fderr = EIO; goto fdout; } /* * if opening for writing, check write protect on diskette */ if (flag & FWRITE) { fdgetcsb(); err = fdsensedrv(unit) & WP_SR3; fdretcsb(); if (err) { fdselect (unit, 0); fderr = EROFS; goto fdout; } } /* mark open */ out: un->un_openmask |= pmask; fdout: fdopening = 0; wakeup((caddr_t)&fdopening); FDERRPRINT(FDEP_L1, FDEM_OPEN, ("___________ END FDOPEN\n")); return (fderr); } /* * fdgetlabel - read the SunOS lable off the diskette * if it can read a valid label it does so, else it will use a * default. If it can`t read the diskette - that is an error. * * RETURNS: 0 for ok - meaning that it could at least read the device, * !0 for error XXX TBD NYD error codes */ fdgetlabel(unit) int unit; { register struct dk_label *label; register struct fdunit *un; short *sp; short count; short xsum; int i, tries; int err = 0; #if NO short oldlvl; #endif FDERRPRINT(FDEP_L1, FDEM_GETL, ("fdgetlabel: unit %d\n", unit)); un = fdctlr.c_units[unit]; un->un_flags &= ~(FDUNIT_LABELOK|FDUNIT_UNLABELED); /* * get some space to play with the label */ label = (struct dk_label *)kmem_zalloc(sizeof (struct dk_label)); FDERRPRINT(FDEP_L0, FDEM_GETL, ("fdgetlabel, kmem_zalloc: ptr = 0x%x, size = 0x%x\n", label, sizeof (struct dk_label))); if (label == NULL) return (ENOMEM); /* * read block 0 (0/0/1) to find the label * (disk is potentially not present or unformatted) */ /* noerrprint since this is a private cmd */ #if NO oldlvl = fderrlevel; fderrlevel = FDEP_L4; #endif /* try different characteristics (ie densities) */ /* * if curfdtype is -1 then the current characteristics were set by * ioctl and need to try it as well as everything in the table * * Note: with new medium density, we need to check the content * of the label as well (medium/high could be mistaken * since the rotation speed is different (360 ->300 ) * (360rpm +medium could be confused with 300rpm + high) */ if (curfdtype == -1) tries = nfdtypes+1; else tries = nfdtypes; for (i=0; i < tries; i++) { FDERRPRINT(FDEP_L0, FDEM_GETL, ("TRYING READ LABEL curfdtype = %d\n", curfdtype)); err = fdrw(FDREAD, unit, 0, 0, 1, (caddr_t)label, sizeof (struct dk_label)); FDERRPRINT(FDEP_L0, FDEM_GETL, ("fdgetlabel: fdrw returned %d\n", err)); FDERRPRINT(FDEP_L0, FDEM_GETL, ("label: rpm = %d\n", label->dkl_rpm)); FDERRPRINT(FDEP_L0, FDEM_GETL, (" pcyl = %d\n", label->dkl_pcyl)); FDERRPRINT(FDEP_L0, FDEM_GETL, (" nsect = %d\n", label->dkl_nsect)); if (!err) break; /* try the next entry in the characteristics tbl */ /* if curfdtype is -1, the nxt entry in tbl is 0 (the first) */ curfdtype = (curfdtype + 1) % nfdtypes; *(un->un_chars) = fdtypes[curfdtype]; /* structure copy */ /* data transfer rate given to the controller in fdexec */ } #if NO fderrlevel = oldlvl; /* print errors again */ #endif if (err) goto out; /* couldn't read anything */ FDERRPRINT(FDEP_L0, FDEM_GETL, ("LABEL SUCCESSFULLY READ\n")); /* * _something_ was read - look for unixtype label */ if (label->dkl_magic != DKL_MAGIC) { /* not a label - no magic number */ goto nolabel; /* no errors, tho` no label */ } count = sizeof (struct dk_label)/sizeof (short); sp = (short *)label; xsum = 0; while (count--) xsum ^= *sp++; /* should add up to 0 */ if (xsum) { /* not a label - checksum didn't compute */ goto nolabel; /* no errors, tho` no label */ } /* * if label found pack it away */ fdpacklabel(label, un->un_label, unit); un->un_flags |= FDUNIT_LABELOK; goto out; nolabel: /* * if not found fill in label info from default (mark default used) */ un->un_flags |= FDUNIT_UNLABELED; switch (un->un_chars->secptrack) { case 9: *(un->un_label) = fdlbl_low_80; /* structure copy */ break; case 8: *(un->un_label) = fdlbl_medium_80; /* structure copy */ break; case 18: default: *(un->un_label) = fdlbl_high_80; /* structure copy */ break; } out: kmem_free((caddr_t)label, sizeof (struct dk_label)); return (err); } /* * fdclose * */ fdclose(dev) dev_t dev; { int unit; register struct fdunit *un; u_char npmask; /* "NOT" partition mask */ unit = UNIT(dev); FDERRPRINT(FDEP_L1, FDEM_CLOS, ("fdclose: dev=0x%x\n", dev)); un = fdctlr.c_units[ unit ]; /* * invalidate stuff * XXX - clean busy operations still waiting??? - HOW CAN THAT HAPPEN? */ npmask = ~(1 << PARTITION(dev)); un->un_exclmask &= npmask; un->un_openmask &= npmask; if (un->un_openmask == 0) { fdselect(unit, 0); /* deselect drive on last close */ un->un_flags &= ~FDUNIT_CHANGED; } return (0); } /* * fdstrategy * checks operation, hangs buf struct off fdctlr, calls fdstart * if not already busy. Note that if we call start, then the operation * will already be done on return (start sleeps). */ fdstrategy(bp) register struct buf *bp; { register struct fdunit *un; int unit, part; int oldpri; int err; struct dk_map *dkm; FDERRPRINT(FDEP_L1, FDEM_STRA, ("fdstrategy: bp = 0x%x, dev = 0x%x\n", bp, bp->b_dev)); unit = UNIT(bp->b_dev); un = fdctlr.c_units[ unit ]; /* at least unit # valid, get un */ part = PARTITION(bp->b_dev); dkm = &(un->un_label->dkl_map[ part ]); /* if this request is already off the end (existence checked in open) */ if ((bp->b_blkno > dkm->dkl_nblk)) { FDERRPRINT(FDEP_L3, FDEM_STRA, ("fd%d: block %d is past the end! (nblk=%d)\n", unit, bp->b_blkno, dkm->dkl_nblk)); bp->b_error = ENOSPC; goto bad; } /* if at end of file, skip out now */ if (bp->b_blkno == dkm->dkl_nblk) { if ((bp->b_flags & B_READ) == 0) { /* a write needs to get an error! */ bp->b_error = ENOSPC; goto bad; } bp->b_resid = bp->b_bcount; iodone(bp); return; } /* if operation not a multiple of sector size, is error! */ if (bp->b_bcount % 512) { FDERRPRINT(FDEP_L3, FDEM_STRA, ("fd%d: count %d must be a multiple of 512\n", unit, bp->b_bcount)); bp->b_error = EINVAL; goto bad; } /* * if the operation will go off the end of disk... * handled in fdstart() */ /* ******** START CRITICAL SECTION ******** */ oldpri = splr(fdpri); /* raise priority */ /* * queue the buf request on controller's queue - first in, first out * basis - nothing fancy here! * NOTE: list from head.b_forw is singly linked using av_forw, * also a pointer to the end of the queue is also kept in head.b_back */ bp->av_forw = (struct buf *)0; /* last guy points to nobody */ if (fdctlr.c_head.b_forw) { /* if someone already on queue... */ fdctlr.c_head.b_back->av_forw = bp; /* ...put on end... */ } else { fdctlr.c_head.b_forw = bp; /* ...else onto head */ } fdctlr.c_head.b_back = bp; /* and do back link also */ bp->av_back = (struct buf *)0; /* just to be clean */ /* if no operation in progress, call start */ if (!(fdctlr.c_flags & FDCFLG_BUSY)) { err = fdstart(); } if (err) goto bad; (void) splx(oldpri); /* ******** END CRITICAL SECTION ******* */ return; bad: /* bad operation - mark buffer with err and vamanos */ bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; if ((bp->b_flags & B_DONE) == 0) iodone(bp); return; } /* * fdstart * called from fdstrategy() or from fdintr() to setup and * start operations of read or write only (using buf structs). * Because the chip doesn't handle crossing cylinder boundaries on * the fly, this takes care of those boundary conditions. Note that * it sleeps until the operation is done *within fdstart* - so that * when fdstart returns, the operation is already done. * */ fdstart() { register struct buf *bp; register struct fdcsb *csb; register struct fdunit *un; register struct fdk_char *ch; struct dk_map *dkm; int err; u_int sec_size; u_int unit, part; u_int len; u_int logical_blk; u_int fd_action; u_int phys_blk, phys_blk_begin; u_int plus1; /* real data starts middle of 1st sector */ u_int minus1; /* real data ends middle of last sector */ u_int sector_count; /* Number of physical sectors to xfer */ u_int plus2; /* xfer not multiple of sec_size */ u_int phys_blk_begin_part, phys_blk_end_part; caddr_t addr_buf, temp_buf; err = 0; loop: /* * if no buf on queue, just bailout */ bp = fdctlr.c_head.b_forw; if (bp == (struct buf *)0) return (err); bp->b_flags &= ~B_ERROR; bp->b_error = 0; bp->b_resid = bp->b_bcount; /* init resid */ FDERRPRINT(FDEP_L1, FDEM_STRT, ("fdstart: bp=0x%x blkno=0x%x bcount=0x%x\n", bp, bp->b_blkno, bp->b_bcount)); /* we're here only if there are queued bufs */ fdgetcsb(); /* get csb (maybe wait for it) */ csb = fdctlr.c_csb; csb->c_current = bp; unit = UNIT(bp->b_dev); /* floppy unit number */ un = fdctlr.c_units[ unit ]; ch = un->un_chars; part = PARTITION(bp->b_dev); dkm = &(un->un_label->dkl_map[ part ]); sec_size = ch->sec_size; FDERRPRINT(FDEP_L1, FDEM_STRT, ("sec_size = %d secptrack = %d\n",ch->sec_size, ch->secptrack)); bp_mapin(bp); /* map in buffers */ /* * now fill in the fdcsb to send to controller */ csb->csb_un = un; /* ptr to fdunit struct */ csb->csb_unit = unit; /* floppy unit number */ csb->csb_part = part; /* floppy partition number */ /* fill in cmd parameters that won't change */ if (bp->b_flags & B_READ) fd_action = CMD_READ; else fd_action = CMD_WRITE; csb->csb_cmds[5] = ch->medium ? 3 : 2; csb->csb_cmds[6] = ch->secptrack; /* EOT - # of sectors/trk */ csb->csb_cmds[7] = GPLN; /* GPL - gap 3 size code */ csb->csb_cmds[8] = SSSDTL; /* DTL - be 0xFF if N != 0 */ csb->csb_ncmds = NCBRW; /* how many command bytes */ csb->csb_nrslts = NRBRW; /* number of result bytes */ /* opflags for hwintr handler et.al.*/ csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT; /* ================================================================ */ logical_blk = bp->b_blkno; /* start logicl block in part */ phys_blk = logical_blk >> ch->medium; /* physical start block */ plus1 = 0; plus2 = 0; minus1 = 0; sector_count = bp->b_bcount / sec_size; if (ch->medium) { plus1 = logical_blk % 2; /* Set if start block odd */ plus2 = (bp->b_bcount % ch->sec_size ? 1 : 0); minus1 = plus1 ^ plus2; if (plus1 | plus2) sector_count++; } phys_blk_begin_part = (dkm->dkl_cylno * ch->secptrack * ch->nhead); phys_blk_begin = phys_blk_begin_part + phys_blk; phys_blk_end_part = phys_blk_begin_part + (dkm->dkl_nblk >> ch->medium); /* * Truncate len if extend beyond partition */ if (phys_blk_begin + sector_count > phys_blk_end_part) len = (phys_blk_end_part - phys_blk_begin) * ch->sec_size; else len = sector_count * ch->sec_size; addr_buf = bp->b_un.b_addr; FDERRPRINT(FDEP_L1, FDEM_STRT, ("log %d phys %d secount %d len %d ppm %d%d%d\n", logical_blk, phys_blk, sector_count, len, plus1, plus2, minus1)); /* * Call transfer routine. * If we have medium density, we need to use temp_buf * and each write will be preceded by a read (unless plus1=minus1=0) */ if (plus1 || minus1) { /* * allocate len bytes in temp_buf, multiple of real sector */ temp_buf = (caddr_t)kmem_zalloc(len); /* * Transfer involving temp_buf * if READ: we read more in temp_buf, then throw away 512 bytes * if WRITE: we need to do a READ/MODIFY/WRITE */ if (fd_action == CMD_READ) { err = temp_disk(fd_action, phys_blk_begin, len, temp_buf); buf_temp(fd_action, plus1, bp, temp_buf); } else { err = temp_disk(CMD_READ, phys_blk_begin, len, temp_buf); buf_temp(fd_action, plus1, bp, temp_buf); if (err == 0) err = temp_disk(fd_action, phys_blk_begin, len, temp_buf); } kmem_free((caddr_t)temp_buf, len); } else { /* * Normal transfer directly from or to struct buf (addr_buf) */ FDERRPRINT(FDEP_L0, FDEM_STRT,("Normal transfer\n")); err = temp_disk(fd_action, phys_blk_begin, len, addr_buf); } /* finish off the buffer operation just done */ FDERRPRINT(FDEP_L0, FDEM_STRT, ("fdstart done: b_resid %d, b_count %d, csb_rlen %d\n", bp->b_resid, bp->b_bcount, csb->csb_rlen)); /* dequeue off of header */ if (bp->av_forw != (struct buf *)0) { fdctlr.c_head.b_forw = bp->av_forw; bp->av_forw = (struct buf *)0; /* c_head.b_back doesn't matter (we hope) */ } else { /* nobody else there, clear em all */ fdctlr.c_head.b_forw = (struct buf *)0; fdctlr.c_head.b_back = (struct buf *)0; } /* if (the special buffer) just wants woke, just wake it only */ /* raw io also comes here */ if (bp == &fdkbuf) { bp->b_flags |= B_DONE; wakeup((caddr_t)bp); } else { /* block io comes here */ /* NOTE: the iodone() does a bp_mapout() */ iodone(bp); } /* release csb */ fdctlr.c_flags &= ~FDCFLG_BUSY; fdretcsb(); goto loop; } /* * TRANSFER data from Disk to temp_buf or vice-versa * -------------------------------------------------- * * fd_action Type of transfer (Read from or Write to disk) * phys_block_begin_i phys block where to start on the disk * len_i Number of Bytes to transfer. * addr_i temp_buf address. * * plus1 real data starts middle of 1st sector * minus1 real data ends middle of last sector. * sector_count Number of physical sectors to xfer * */ temp_disk(fd_action, phys_blk_begin_i, len_i, addr_i) u_int fd_action; u_int phys_blk_begin_i; u_int len_i; caddr_t addr_i; { u_int secpcyl; /* number of sectors per cylinder */ u_int begin_sec; /* block begin xfer within cyl. */ u_int sec_size; u_int unit; u_int cyl, head, sect; u_int tlen; register struct fdcsb *csb = fdctlr.c_csb; register struct buf *bp = csb->c_current; register struct fdunit *un; register struct fdk_char *ch; unit = UNIT(bp->b_dev); /* floppy unit number */ un = fdctlr.c_units[ unit ]; ch = un->un_chars; sec_size = ch->sec_size; secpcyl = ch->nhead * ch->secptrack; /* sectors per cylinder */ while (len_i != 0) { cyl = phys_blk_begin_i / secpcyl; begin_sec = phys_blk_begin_i % secpcyl; head = begin_sec / ch->secptrack; sect = (begin_sec % ch->secptrack) + 1; /* * Truncate tlen if len extends beyond cylinder */ if (len_i > ((secpcyl - begin_sec) * sec_size)) tlen = (secpcyl - begin_sec) * sec_size; else tlen = len_i; FDERRPRINT(FDEP_L1, FDEM_STRT, (" phys_blk_begin_i 0x%x, addr_i 0x%x, len_i 0x%x\n", phys_blk_begin_i , addr_i, len_i)); FDERRPRINT(FDEP_L1, FDEM_STRT, (" cyl 0x%x, head 0x%x, sec 0x%x\n", cyl, head, sect)); FDERRPRINT(FDEP_L1, FDEM_STRT, (" resid 0x%x, tlen %d\n", bp->b_resid, tlen)); csb->csb_cmds[0] = fd_action; csb->csb_cmds[1] = (head << 2) | (unit & 0x3); csb->csb_cmds[2] = cyl; /* C - cylinder address */ csb->csb_cmds[3] = head; /* H - head number */ csb->csb_cmds[4] = sect; /* R - sector number */ csb->csb_len = tlen; csb->csb_addr = addr_i; csb->csb_maxretry = rwretry; /* retry this many times max */ csb->csb_retrys = 0; /* Execute Operation - failure returns an errno */ if ((bp->b_error = fdexec(FDXC_SLEEP | FDXC_CHECKCHG)) != 0) { FDERRPRINT(FDEP_L1, FDEM_STRT, ("fdstart: bad exec of bp: 0x%x, err %d\n", bp, bp->b_error)); bp->b_flags |= B_ERROR; break; } phys_blk_begin_i += tlen / sec_size; len_i -= tlen; addr_i += tlen; bp->b_resid -= tlen; } if (bp->b_resid < 0) bp->b_resid = 0; return (bp->b_error); } /* end temp_disk */ /* * TRANSFER data from buf to temp_buf or vice-versa * -------------------------------------------------- * * fd_action Type of transfer (Read from or Write to disk) * plus1 real data starts middle of 1st sector * bp os buf pointer. * addr_i temp_buf address. * * (minus1 real data ends middle of last sector.) */ buf_temp(fd_action, plus1, bp, addr_i) u_int fd_action; u_int plus1; struct buf *bp; caddr_t addr_i; { int i; caddr_t pt_temp, pt_buf; pt_buf = bp->b_un.b_addr; pt_temp = addr_i; FDERRPRINT(FDEP_L1, FDEM_STRT, ("buf_temp: b_bcount=%x plus1 %x\n",bp->b_bcount, plus1)); if (plus1) pt_temp += 512; if (fd_action == CMD_READ) { for (i=0; ib_bcount; i++) *pt_buf++ = *pt_temp++; } else { for (i=0; ib_bcount; i++) { *pt_temp++ = *pt_buf++; } } } /* end buf_temp */ /* * raw read and write operations share a common function to do their "work" :-) */ fdread(dev, uio) dev_t dev; struct uio *uio; { return (fdrdwr(dev, uio, B_READ)); } fdwrite(dev, uio) dev_t dev; struct uio *uio; { return (fdrdwr(dev, uio, B_WRITE)); } static fdrdwr(dev, uio, rw) dev_t dev; struct uio *uio; int rw; { FDERRPRINT(FDEP_L0, FDEM_RDWR, ("fdrdwr, rw = %d\n", rw)); FDERRPRINT(FDEP_L0, FDEM_RDWR, ("uio->uio_iov->iov_base = 0x%X\n", uio->uio_iov->iov_base)); FDERRPRINT(FDEP_L0, FDEM_RDWR, ("uio->uio_iov->iov_len = %d\n", uio->uio_iov->iov_len)); /* checks for UNIT(dev)) >= NFD, etc. done in fdstrategy */ return (physio(fdstrategy, &fdkbuf, dev, rw, minphys, uio)); } #ifdef I82077 static fdmotoff() { FDERRPRINT(FDEP_L1, FDEM_MOFF, ("fdmotoff\n")); if (!(Msr & CB) && (Dor & MOTEN)) fd_set_dor_reg(MOTEN, 0); } #endif I82077 /* * fdexec * all commands go thru here. Assumes the command block * fdctlr.c_csb is filled in. The bytes are sent to the * controller and then we do whatever else the csb says - * like wait for immediate results, etc. * * All waiting for operations done is in here - to allow retrys * and checking for disk changed - so we don't have to worry * about sleeping at interrupt level. * * RETURNS: 0 if all ok, * ENXIO - diskette not in drive * ETIMEDOUT - for immediate operations that timed out * EBUSY - if stupid chip is locked busy??? * ENOEXEC - for timeout during sending cmds to chip */ fdexec(flags) int flags; /* * to sleep: set FDXC_SLEEP, to check for disk * changed: set FDXC_CHECKCHG */ { register struct fdcsb *csb; register int i; int to; u_char mstat, tmp; int oldpri; int medium; medium = fdctlr.c_units[fdctlr.c_csb->csb_unit]->un_chars->medium; FDERRPRINT(FDEP_L1, FDEM_EXEC, ("\n******************** FDEXEC: flags 0x%x\n", flags)); csb = fdctlr.c_csb; retry: #ifdef I82077 switch (fdtype) { case FD_82077: FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: cmd is %s\n", fdcmds[csb->csb_cmds[0] & 0x1f].cmdname)); untimeout(fdmotoff, (caddr_t) 0); /* fd_set_dor(MEDIUM_DENSITY, 0); */ /* fd_set_dor_reg(MOTEN, 1); HR XXX NEW CHANGE */ if (!(Dor & MOTEN) || ((Dor & MEDIUM_DENSITY) >> 6) ^ medium ) { /* Turn on the motor. */ FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: turning on motor\n", fdcmds[csb->csb_cmds[0] & 0x1f].cmdname)); fd_set_dor_reg(MOTEN, 1); if (flags & FDXC_SLEEP) { timeout(wakeup, (caddr_t) fdctlr_reg, MOTON_DELAY); (void) sleep((caddr_t) fdctlr_reg, PZERO - 1); } else { DELAY(1000000); } } break; } #endif I82077 fdselect((int)csb->csb_unit, 1); /* select drive */ /* select data rate for this unit/command */ FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: medium = %d\n", csb->csb_un->un_chars->medium )); FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: transfer rate = %d\n", csb->csb_un->un_chars->transfer_rate)); FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: ncyl = %d\n", csb->csb_un->un_chars->ncyl)); FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: sec_size = %d\n", csb->csb_un->un_chars->sec_size )); FDERRPRINT(FDEP_L1, FDEM_EXEC, ("fdexec: secptrack = %d\n", csb->csb_un->un_chars->secptrack)); switch (csb->csb_un->un_chars->transfer_rate) { case 500: Dsr = 0; break; case 300: Dsr = 1; break; case 250: Dsr = 2; break; } DELAY(2); /* * if checking for changed is enabled (ie not seeking in checkdisk) * we sample the DSKCHG line to see if the diskette has wandered away */ if ((flags & FDXC_CHECKCHG) && fdsense_chng()) { FDERRPRINT(FDEP_L1, FDEM_EXEC, ("diskette changed!!!\n")); csb->csb_un->un_flags |= FDUNIT_CHANGED; /* if the diskette is still gone... so are we, adios! */ if (fdcheckdisk((int)csb->csb_unit)) return (ENXIO); } /* * gather some statistics */ switch (csb->csb_cmds[0] & 0x1f) { case FRAW_RDCMD: fdctlr.fdstats.rd++; break; case FRAW_WRCMD: fdctlr.fdstats.wr++; break; case FRAW_REZERO: fdctlr.fdstats.recal++; break; case FRAW_FORMAT: fdctlr.fdstats.form++; break; default: fdctlr.fdstats.other++; break; } /* * set info for level11 hardware interrupt handler */ /* if (operation is seek/relative-seek/recalibrate) ... */ if (csb->csb_opflags & CSB_OFSEEKOPS) { fdintr_opmode = 2; fdintr_addr = fdintr_statbuf; /* safe place to point */ fdintr_len = 0; } else /* if immediate mode ... */ if (csb->csb_opflags & CSB_OFIMMEDIATE) { fdintr_opmode = 0; fdintr_addr = fdintr_statbuf; /* safe place to point */ fdintr_len = 0; } else { fdintr_opmode = 1; /* normal data xfer commands */ fdintr_addr = (u_char *)csb->csb_addr; fdintr_len = csb->csb_len; } fdintr_status = 0; fdintr_statp = fdintr_statbuf; /* set pointer to statbuf !!!!! */ fdintr_timeout = fdhardtimeout; /* XXX load timeout value */ csb->csb_cmdstat = 0; /* * give cmd to controller */ /* check for a command in progress? - avoid catastrophic errs */ /* * XXX *** * i saw this (chip unexpectedly busy) happen when i shoved the * floppy into the drive while * running a dd if=/dev/rfd0c. so it *is* possible for this to happen. * we need to do a ctlr reset ... */ if ((mstat = Msr) & CB) { /* tried to give command to chip when it is busy! ***/ FDERRPRINT(FDEP_L3, FDEM_EXEC, ("fdc: unexpectedly busy - stat 0x%x\n", mstat)); #ifdef XXX XXX arf! needs to avoid recusion fdreset(); /* fdrecal already has csb */ fdrecalseek(unit, -1, 0); /* no check changed! */ #endif XXX csb->csb_cmdstat = -1; /* XXX TBD ERRS NYD for now */ return (EBUSY); } /* if sleeping, raise the priority */ /* ******** START CRITICAL SECTION ******** */ if (flags & FDXC_SLEEP) { oldpri = splr(fdpri); /* raise priority */ } FDERRPRINT(FDEP_L0, FDEM_EXEC, ("fdexec: C M D = ")); for (i = 0; i < csb->csb_ncmds; i++) { #ifdef I82077 DELAY(10); #endif I82077 /* is this loop really necessary? */ for (to = FD_CRETRY; to; to--) { /* insure that rqm of main status reg is ok */ if ((Msr & (DIO|RQM)) == RQM) break; } if (to == 0) { /* was a timeout - XXX TBD NYD */ FDERRPRINT(FDEP_L3, FDEM_EXEC, ("fdc: no RQM - stat 0x%x\n", Msr)); csb->csb_cmdstat = -1; /* XXX TBD ERRS NYD for now */ if (flags & FDXC_SLEEP) (void) splx(oldpri); return (ENOEXEC); } Fifo = csb->csb_cmds[i]; /* send command to controller */ FDERRPRINT(FDEP_L0, FDEM_EXEC, ("%x%c ", csb->csb_cmds[i],( Msr == 0x10 ? ' ':'?'))); } /* * start watchdog timer on data transfer type commands - required * in case a diskette is not present or is unformatted */ if (csb->csb_opflags & CSB_OFTIMEIT) { timeout(fdwatch, (caddr_t)0, tosec*hz); } FDERRPRINT(FDEP_L0, FDEM_EXEC, ("\nfdexec: cmd sent, Msr 0x%x\n", Msr)); /* * if the operation has no results - then just return */ if (csb->csb_opflags & CSB_OFNORESULTS) { if (flags & FDXC_SLEEP) /* not likely, but... */ (void) splx(oldpri); #ifdef I82077 if (fdtype == FD_82077) timeout(fdmotoff, (caddr_t) 0, MOTOFF_DELAY); #endif I82077 return (0); } /* * if this operation has no interrupt AND an immediate result * then we just busy wait for the results and stuff them into * the csb */ if (csb->csb_opflags & CSB_OFIMMEDIATE) { DELAY(10*hz); /* 10 sec delay */ to = FD_RRETRY; csb->csb_nrslts = 0; /* while this command is still going on */ while ((tmp = Msr) & CB) { /* if RQM + DIO, then a result byte is at hand */ if ((tmp & (RQM|DIO|CB)) == (RQM|DIO|CB)) { csb->csb_rslt[ csb->csb_nrslts++ ] = Fifo; /* XXX limit number of status bytes read? ***/ } else if (--to == 0) { FDERRPRINT(FDEP_L4, FDEM_EXEC, ("fdexec: rslt timeout, Msr 0x%x, nr %d\n", Msr, csb->csb_nrslts)); /* XXX what to do now? ***/ csb->csb_status = 2; /* should never be set, but... */ if (flags & FDXC_SLEEP) (void) splx(oldpri); #ifdef I82077 if (fdtype == FD_82077) timeout(fdmotoff, (caddr_t) 0, MOTOFF_DELAY); #endif I82077 return (ETIMEDOUT); } } } /* if told to sleep here, well then sleep! */ if (flags & FDXC_SLEEP) { /* we're splr'd, so we can set this without worry (we hope) */ fdctlr.c_flags |= FDCFLG_WAITING; /* Sleep till cmd complete */ while (fdctlr.c_flags & FDCFLG_WAITING) { (void)sleep((caddr_t)(fdctlr.c_csb), FDPRI); } (void) splx(oldpri); /* ******** END CRITICAL SECTION *****************************/ } /* * see if there was an error detected, if so, * fdrecover() will check it out and say what to do. */ if (((csb->csb_rslt[0] & IC_SR0) || (csb->csb_status)) && ((csb->csb_cmds[0] != FRAW_SENSE_DRV) && /* not sense drv status */ (csb->csb_cmds[0] != DUMPREG))) { /* not dumpreg */ /* if it can restarted OK, then do so, else return error */ if (fdrecover() != 0) { #ifdef I82077 if (fdtype == FD_82077) timeout(fdmotoff, (caddr_t) 0, MOTOFF_DELAY); #endif I82077 return (EIO); } else { /* ASSUMES that cmd is still intact in csb */ goto retry; } } /* things went ok */ #ifdef I82077 if (fdtype == FD_82077) timeout(fdmotoff, (caddr_t) 0, MOTOFF_DELAY); #endif I82077 FDERRPRINT(FDEP_L1, FDEM_EXEC, ("____________________END_FDEXEC\n")); return (0); } /* * fdrecover * see if possible to retry an operation. * All we can do is restart the operation. If we are out of allowed * retries - return non-zero so that the higher levels will be notified. * * RETURNS: 0 if ok to restart, !0 if can't or out of retries */ fdrecover() { struct fdcsb *ccsb; /* private csb for resetting */ struct fdcsb *csb; int unit; FDERRPRINT(FDEP_L1, FDEM_RECO, ("fdrecover\n")); csb = fdctlr.c_csb; if (fdctlr.c_flags & FDCFLG_TIMEDOUT) { fdctlr.c_flags &= ~FDCFLG_TIMEDOUT; csb->csb_rslt[1] |= 0x08; FDERRPRINT(FDEP_L1, FDEM_RECO, ("fd%d: %s timed out\n", csb->csb_unit, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname)); unit = csb->csb_unit; /* use private csb */ ccsb = (struct fdcsb *)kmem_zalloc(sizeof (struct fdcsb)); fdctlr.c_csb = ccsb; FDERRPRINT(FDEP_L1, FDEM_RECO, ("fdc: resetting\n")); fdreset(); /* check change first?? */ /* don't ckchg in fdexec, too convoluted */ (void)fdrecalseek(unit, -1, 0); fdctlr.c_csb = csb; /* restore original csb */ kmem_free((caddr_t)ccsb, sizeof (struct fdcsb)); } /* * gather statistics on errors */ if (csb->csb_rslt[1] & DE_SR1) { fdctlr.fdstats.de++; } if (csb->csb_rslt[1] & OR_SR1) { fdctlr.fdstats.run++; } if (csb->csb_rslt[1] & (ND_SR1+MA_SR1)) { fdctlr.fdstats.bfmt++; } if (csb->csb_rslt[1] & 0x08) { fdctlr.fdstats.to++; } /* if raw ioctl don't examine results just pass status back via fdraw */ /* raw commands are timed too, so put this after the above check */ if (csb->csb_opflags & CSB_OFRAWIOCTL) { return (1); } /* * if we have run out of retries, return an error * XXX need better status interp */ csb->csb_retrys++; if (csb->csb_retrys > csb->csb_maxretry) { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: %s failed (%x %x %x)\n", csb->csb_unit, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname, csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2])); FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 1.1\n")); if (csb->csb_rslt[1] & NW_SR1) { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: not writable\n", csb->csb_unit)); } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 1.2\n")); if (csb->csb_rslt[1] & DE_SR1) { if (fdctlr.c_head.b_forw) { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: crc error blk %d\n", csb->csb_unit, fdctlr.c_head.b_forw->b_blkno)); } else { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: crc error fdrw\n", csb->csb_unit)); } } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 1.3\n")); if (csb->csb_rslt[1] & OR_SR1) { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: overrun/underrun\n", csb->csb_unit)); } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 1.4\n")); if (csb->csb_rslt[1] & (ND_SR1+MA_SR1)) { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: bad format\n", csb->csb_unit)); } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 1.5\n")); if (csb->csb_rslt[1] & 0x08) { FDERRPRINT(FDEP_L3, FDEM_RECO, ("fd%d: timeout\n", csb->csb_unit)); } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 1.6\n")); csb->csb_cmdstat = -1; /* failed - give up */ return (1); } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 2\n")); if (csb->csb_opflags & CSB_OFSEEKOPS) { /* seek, recal type commands - just look at st0 */ FDERRPRINT(FDEP_L2, FDEM_RECO, ("fd%d: %s error : st0 0x%x\n", csb->csb_unit, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname, csb->csb_rslt[0])); } FDERRPRINT(FDEP_L0, FDEM_RECO, ("fdrecover 3\n")); if (csb->csb_opflags & CSB_OFXFEROPS) { /* rd, wr, fmt type commands - look at st0, st1, st2 */ FDERRPRINT(FDEP_L2, FDEM_RECO, ("fd%d: %s error : st0=0x%x st1=0x%x st2=0x%x\n", csb->csb_unit, fdcmds[csb->csb_cmds[0] & 0x1f].cmdname, csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2])); } return (0); /* tell fdexec to retry */ } /* * fdintr * this is the level4 sw interrupt handler triggered by the level11 * trap handler when the command is complete. we also get here from * fdwatch. * NOTE: this software interrupt level is shared. * NOTE: we must return 0 if not ours, 1 if it was. */ fdintr() { register struct fdcsb *csb; register int i; FDERRPRINT(FDEP_L1, FDEM_INTR, ("fdintr: opmode %d\n", fdintr_opmode)); /* check that lowlevel interrupt really meant to trigger us */ if (fdintr_opmode != 4) return (0); fdintr_opmode = 0; csb = fdctlr.c_csb; /* reset watchdog timer if armed and not already triggered */ if ((csb->csb_opflags & CSB_OFTIMEIT) && ! (fdctlr.c_flags & FDCFLG_TIMEDOUT)) untimeout(fdwatch, (caddr_t)0); /* * examine results of command just completed (it places status * bytes in buffer) - retry if there was a recoverable error. */ csb->csb_raddr = (caddr_t)fdintr_addr; /* (current) data xfer address */ csb->csb_rlen = fdintr_len; /* I: data xfer length; O: remainder */ csb->csb_status = fdintr_status; /* I: n/a; O: 0 = ok, TBD errors */ /* XXX - who to believe, chip or csb? */ for (i = 0; i < csb->csb_nrslts; i++) { csb->csb_rslt[i] = fdintr_statbuf[i]; } /*XXX what to do on a level11 timeout??? */ if (fdintr_timeout == 0) { FDERRPRINT(FDEP_L4, FDEM_INTR, ("fdintr: fdintr_timeout!, opmode %d, MSR 0x%x\n", fdintr_opmode, Msr)); } if (fdctlr.c_flags & FDCFLG_WAITING) { /* somebody's waiting on finish of fdctlr/csb, wake them */ fdctlr.c_flags &= ~FDCFLG_WAITING; wakeup((caddr_t)(fdctlr.c_csb)); /* * FDCFLG_BUSY is NOT cleared, NOR is the csb given back; so * the operation just finished can look at the csb */ return (1); } FDERRPRINT(FDEP_L3, FDEM_INTR, ("fdintr: nobody sleeping\n")); return (1); } /* * fdwatch * is called from timein() when a floppy operation has expired. */ fdwatch() { struct fdcsb *csb; csb = fdctlr.c_csb; FDERRPRINT(FDEP_L1, FDEM_WATC, ("fd%d: timeout, opmode = %d\n", csb->csb_unit, fdintr_opmode)); fdintr_opmode = 4; /* for fdintr @level 4 */ fdctlr.c_flags |= FDCFLG_TIMEDOUT; fdintr_status = CSB_CMDTO; /* trigger the level4 int @fdintr */ set_intreg(IR_SOFT_INT4, 1); } /* * fdioctl * implements more ioctls than a floppy disk ought to have * * RETURNS: an errno for user >>> at end of each case <<< */ /*ARGSUSED*/ fdioctl(dev, cmd, arg, flag) dev_t dev; int cmd; caddr_t arg; int flag; { int unit; struct fdunit *un; #ifdef XXX u_int sec_size; #endif XXX FDERRPRINT(FDEP_L1, FDEM_IOCT, ("fdioctl: cmd 0x%x, arg 0x%x\n", cmd, arg)); /* check for valid unit - failure should *never happen* */ unit = UNIT(dev); if (unit >= NFD || fdtype == -1) { return (ENXIO); } un = fdctlr.c_units[ unit ]; #ifdef XXX sec_size = ch->sec_size; #endif XXX switch (cmd) { case DKIOCINFO: /* return struct dk_info describing controller/unit */ { struct dk_info *dki = (struct dk_info *)arg; dki->dki_ctlr = OBIO_FDC_ADDR; /* addres of controller */ dki->dki_unit = unit; if (fdtype == FD_82072) dki->dki_ctype = DKC_INTEL82072; if (fdtype == FD_82077) dki->dki_ctype = DKC_INTEL82077; dki->dki_flags = DKI_FMTTRK; /* format track at a time */ } return (0); /* get disk geometry */ case DKIOCGGEOM: /* return struct dk_geom describing disk geometry */ { struct dk_geom *dkg = (struct dk_geom *)arg; dkg->dkg_ncyl = un->un_chars->ncyl; dkg->dkg_nhead = un->un_chars->nhead; dkg->dkg_nsect = un->un_chars->secptrack; dkg->dkg_intrlv = un->un_label->dkl_intrlv; dkg->dkg_rpm = un->un_label->dkl_rpm; dkg->dkg_pcyl = un->un_chars->ncyl; } return (0); case DKIOCSGEOM: FDERRPRINT(FDEP_L3, FDEM_IOCT, ("fdioctl: DKIOCSGEOM not supported\n")); return (EINVAL); /* * Return the map for the specified logical partition. * This has been made obsolete by the get all partitions command. */ case DKIOCGPART: /* structure copy! */ *(struct dk_map *)arg = un->un_label->dkl_map[ PARTITION(dev) ]; return (0); case DKIOCSPART: { struct dk_map *dkm_src = (struct dk_map *)arg; struct dk_map *dkm_dst = &(un->un_label->dkl_map[ PARTITION(dev) ]); *dkm_dst = *dkm_src; } return (0); /* * return the map of all logical partitions */ case DKIOCGAPART: /* structure copy! */ *(struct dk_allmap *)arg = *(struct dk_allmap *)un->un_label->dkl_map; return (0); /* * Set the map of all logical partitions */ case DKIOCSAPART: { struct dk_allmap *dkam_src = (struct dk_allmap *)arg; struct dk_allmap *dkam_dst = (struct dk_allmap *)un->un_label->dkl_map; *dkam_dst = *dkam_src; } return (0); case FDKIOGCHAR: /* structure copy! */ *(struct fdk_char *)arg = *(un->un_chars); return (0); case FDKIOSCHAR: { struct fdk_char *fdchar = (struct fdk_char *)arg; switch (fdchar->transfer_rate) { case 500: case 300: case 250: break; default: FDERRPRINT(FDEP_L3, FDEM_IOCT, ("fdioctl: FDKIOSCHAR unsupported transfer rate %d\n", fdchar->transfer_rate)); return (EINVAL); } /* structure copy! */ *(un->un_chars) = *fdchar; curfdtype = -1; FDERRPRINT(FDEP_L0, FDEM_IOCT, ("fdioctl: S E T T I N G C H A R A C T\n")); } return (0); case FDKEJECT: /* eject disk */ fdselect(UNIT(dev), 1); fdeject(); return (0); case FDKGETCHANGE: /* disk changed */ if (un->un_flags & FDUNIT_CHANGED) *(int *)arg |= FDKGC_HISTORY; else *(int *)arg &= ~FDKGC_HISTORY; un->un_flags &= ~FDUNIT_CHANGED; if (fdsense_chng()) { /* check disk only if changed */ if (fdcheckdisk(unit)) { *(int *)arg |= FDKGC_CURRENT; } else { *(int *)arg &= ~FDKGC_CURRENT; } } else { *(int *)arg &= ~FDKGC_CURRENT; } return (0); case FDKGETDRIVECHAR: { struct fdk_drive *drvchar = (struct fdk_drive *)arg; drvchar->fdd_ejectable = -1; /* we do support autoeject */ drvchar->fdd_maxsearch = nfdtypes; /* density:hi, m, lo */ /* the rest of the fdk_drive struct is meaningless to us */ } return (0); case FDKSETDRIVECHAR: FDERRPRINT(FDEP_L3, FDEM_IOCT, ("fdioctl: FDKSETDRIVECHAR not supported\n")); return (EINVAL); case DKIOCSCMD: case FDKIOCSCMD: { struct dk_cmd *dkc = (struct dk_cmd *)arg; int cyl, hd, spc, spt; spt = un->un_chars->secptrack; /* sec/trk */ spc = un->un_chars->nhead * spt; /* sec/cyl */ cyl = dkc->dkc_blkno / spc; hd = (dkc->dkc_blkno % spc) / spt; switch (dkc->dkc_cmd) { #ifdef XXX /* XXX need to mapin and as_fault lock user address! XXX */ case FKREAD: sec = ((dkc->dkc_blkno % spc) % spt) + 1; if (fdrw(FDREAD, unit, cyl, hd, sec, dkc->dkc_bufaddr, (u_int)dkc->dkc_secnt * sec_size)) return (EIO); return (0); case FKWRITE: sec = ((dkc->dkc_blkno % spc) % spt) + 1; if (fdrw(FDWRITE, unit, cyl, hd, sec, dkc->dkc_bufaddr, (u_int)dkc->dkc_secnt * sec_size)) return (EIO); return (0); #endif XXX case FKFORMAT_TRACK: if (fdformat(unit, cyl, hd)) return (EIO); return (0); default: FDERRPRINT(FDEP_L3, FDEM_IOCT, ("fdioctl: FDKIOCSCMD not yet complete\n")); return (EINVAL); } } /* * */ case F_RAW: return (fdrawioctl(dev, arg)); case IOCTL_DEBUG: if (fderrlevel == 0) fderrlevel = 3; if (fderrlevel == 3) fderrlevel = 0; printf("fdioctl: TOGGLING trace\n"); return(0); default: FDERRPRINT(FDEP_L2, FDEM_IOCT, ("fdioctl: invalid ioctl 0x%x\n", cmd)); return (ENOTTY); } #ifdef NEVER /* hopefuly nobody gets here! */ /*NOTREACHED*/ return (ENOTTY); #endif NEVER } /* end fdioctl */ /* * fdrawioctl * */ fdrawioctl(dev, arg) dev_t dev; caddr_t arg; { struct fdunit *un = fdctlr.c_units[ UNIT(dev) ]; struct fdraw *fdr = (struct fdraw *)arg; struct fdcsb *csb; struct buf *bp; int i; int flag = B_READ; int oldpri; caddr_t addr, fa; u_int fc; long count; FDERRPRINT(FDEP_L1, FDEM_RAWI, ("fdrawioctl: dev=0x%x cmd[0]=0x%x\n", dev, fdr->fr_cmd[0])); /* * check if medium density is asked but not supported */ if (un->un_chars->medium && fdtype == FD_82072) { printf("fdrawioctl: Medium density NOT supported\n"); return(ENXIO); } fdgetcsb(); csb = fdctlr.c_csb; csb->csb_un = un; csb->csb_unit = UNIT(dev); csb->csb_part = 0; /* copy cmd bytes into csb */ for (i=0; i<=fdr->fr_cnum; i++) { csb->csb_cmds[i] = fdr->fr_cmd[i]; } csb->csb_ncmds = fdr->fr_cnum; csb->csb_maxretry = 0; /* let the application deal with errors */ csb->csb_retrys = 0; switch (fdr->fr_cmd[0] & 0x0f) { case FRAW_SPECIFY: csb->csb_opflags = CSB_OFNORESULTS; csb->csb_nrslts = 0; break; case FRAW_SENSE_DRV: csb->csb_opflags = CSB_OFIMMEDIATE; csb->csb_nrslts = 1; break; case FRAW_REZERO: case FRAW_SEEK: csb->csb_opflags = CSB_OFSEEKOPS + CSB_OFTIMEIT; csb->csb_nrslts = 2; break; case FRAW_FORMAT: csb->csb_opflags = CSB_OFXFEROPS + CSB_OFTIMEIT; csb->csb_nrslts = NRBRW; flag = B_WRITE; fc = (u_int)(fdr->fr_nbytes + 16); fa = (caddr_t)kmem_zalloc(fc); (void) copyin(fdr->fr_addr, fa, (u_int)fdr->fr_nbytes); /*XXX handle copyin errors */ break; case FRAW_WRCMD: case FRAW_WRITEDEL: flag = B_WRITE; case FRAW_RDCMD: case FRAW_READDEL: case FRAW_READTRACK: csb->csb_opflags = CSB_OFXFEROPS + CSB_OFTIMEIT; csb->csb_nrslts = NRBRW; /* flag was set to B_READ above */ break; #ifdef XXX cmds not supported for now case FRAW_READID: break; case FRAW_SENSE_INT: break; #endif XXX default: fdretcsb(); return (EINVAL); } if ((csb->csb_opflags & CSB_OFXFEROPS) && (fdr->fr_nbytes == 0)) { fdretcsb(); return (EINVAL); } csb->csb_opflags |= CSB_OFRAWIOCTL; if ((fdr->fr_cmd[0] & 0x0f) != FRAW_FORMAT) { /* if data transfer involved need to use the special bp */ if (fdr->fr_nbytes > 0) { bp = &fdkbuf; oldpri = splr(fdpri); while (bp->b_flags & B_BUSY) { bp->b_flags |= B_WANTED; (void) sleep((caddr_t)bp, FDPRI); } bp->b_flags = B_BUSY; (void) splx(oldpri); bp->b_flags |= B_PHYS; addr = bp->b_un.b_addr = fdr->fr_addr; count = bp->b_bcount = (long)fdr->fr_nbytes; bp->b_proc = u.u_procp; u.u_procp->p_flag |= SPHYSIO; (void) as_fault(u.u_procp->p_as, addr, (u_int)count, F_SOFTLOCK, (flag == B_WRITE) ? S_WRITE : S_READ); /*XXX handle errors from as_fault ****/ bp_mapin(bp); csb->csb_addr = bp->b_un.b_addr; csb->csb_len = bp->b_bcount; } else { /*XXX or maybe point someplace safer?? */ csb->csb_addr = 0; csb->csb_len = 0; } } else { csb->csb_addr = fa; csb->csb_len = fc; } FDERRPRINT(FDEP_L1, FDEM_RAWI, ("cmd: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_cmds[0], csb->csb_cmds[1], csb->csb_cmds[2], csb->csb_cmds[3], csb->csb_cmds[4], csb->csb_cmds[5], csb->csb_cmds[6], csb->csb_cmds[7], csb->csb_cmds[8], csb->csb_cmds[9])); FDERRPRINT(FDEP_L1, FDEM_RAWI, ("nbytes: %x, opflags: %x, addr: %x, len: %x\n", csb->csb_ncmds, csb->csb_opflags, csb->csb_addr, csb->csb_len)); if ((csb->csb_opflags & CSB_OFNORESULTS) || (csb->csb_opflags & CSB_OFIMMEDIATE)) { i = fdexec(0); /* don't sleep, don't check change */ } else { i = fdexec(FDXC_SLEEP | FDXC_CHECKCHG); /* sleep till done */ } DELAY(1*hz); /* 1 sec delay */ FDERRPRINT(FDEP_L1, FDEM_RAWI, ("rslt: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_rslt[0], csb->csb_rslt[1], csb->csb_rslt[2], csb->csb_rslt[3], csb->csb_rslt[4], csb->csb_rslt[5], csb->csb_rslt[6], csb->csb_rslt[7], csb->csb_rslt[8], csb->csb_rslt[9])); if ((fdr->fr_cmd[0] & 0x0f) != FRAW_FORMAT) { if (fdr->fr_nbytes > 0) { bp_mapout(bp); (void) as_fault(u.u_procp->p_as, addr, (u_int)count, F_SOFTUNLOCK, (flag == B_WRITE)? S_WRITE: S_READ); /*XXX handle errors from as_fault ****/ oldpri = splr(fdpri); u.u_procp->p_flag &= ~SPHYSIO; if (bp->b_flags & B_WANTED) wakeup((caddr_t)bp); (void) splx(oldpri); bp->b_flags &= ~(B_BUSY|B_WANTED|B_PHYS); } } else { kmem_free(fa, fc); } /* copy cmd results into fdr */ for (i=0; i<=csb->csb_nrslts; i++) fdr->fr_result[i] = csb->csb_rslt[i]; fdr->fr_nbytes = csb->csb_rlen; /* return resid */ fdretcsb(); return (0); } /* * fddump * 'cause the config stuff wants it * it doesn't make sense to dump system on a floppy, so don't even * try, just return an error. * (dump occurs to swap area, and there isn't enough room on a floppy * to swap on) */ /*ARGSUSED*/ fddump(dev, addr, blkno, nblk) dev_t dev; caddr_t addr; daddr_t blkno, nblk; { return (ENOSPC); } /* * fdgetcsb * wait until the csb is free */ fdgetcsb() { int oldpri; FDERRPRINT(FDEP_L1, FDEM_GETC, ("fdgetcsb\n")); oldpri = splr(fdpri); /* raise priority */ while (fdctlr.c_flags & FDCFLG_BUSY) { fdctlr.c_flags |= FDCFLG_WANT; (void)sleep((caddr_t)&(fdctlr.c_flags), FDPRI); } fdctlr.c_flags |= FDCFLG_BUSY; /* got it! */ (void) splx(oldpri); } /* * fdretcsb * return csb */ fdretcsb() { FDERRPRINT(FDEP_L1, FDEM_RETC, ("fdretcsb\n")); fdctlr.c_flags &= ~FDCFLG_BUSY; /* let go */ if (fdctlr.c_flags & FDCFLG_WANT) { fdctlr.c_flags &= ~FDCFLG_WANT; wakeup((caddr_t)&(fdctlr.c_flags)); } } /* * fdreset * reset THE controller, and configure it to be * the way it ought to be * ASSUMES: that it already owns the csb/fdctlr! */ fdreset() { register struct fdcsb *csb = fdctlr.c_csb; FDERRPRINT(FDEP_L1, FDEM_RESE, ("fdreset\n")); /* count resets */ fdctlr.fdstats.reset++; /* toggle software reset */ Dsr = SWR; DELAY(5); /* * select data rate for this unit/command */ switch (fdctlr.c_units[csb->csb_unit]->un_chars->transfer_rate) { case 500: Dsr = 0; break; case 300: Dsr = 1; break; case 250: Dsr = 2; break; } DELAY(5); #ifdef I82077 switch (fdtype) { case FD_82077: untimeout(fdmotoff, (caddr_t) 0); /* The reset bit must be cleared to take the 077 out of * reset state and the DMAGATE bit must be high to enable * interrupts. */ fd_set_dor_reg(ZEROBITS, 0); fd_set_dor_reg(DMAGATE|RESET, 1); break; } #endif I82077 /* setup common things in csb */ csb->csb_un = fdctlr.c_units[0]; csb->csb_unit = 0; csb->csb_part = 0; csb->csb_nrslts = 0; csb->csb_opflags = CSB_OFNORESULTS; csb->csb_maxretry = 0; csb->csb_retrys = 0; /* send SPECIFY command to fdc */ /* csb->unit is don't care */ csb->csb_cmds[0] = FRAW_SPECIFY; csb->csb_cmds[1] = fdspec[0]; /* step rate, head unload time */ csb->csb_cmds[2] = fdspec[1]; /* head load time, DMA mode */ csb->csb_ncmds = 3; /* XXX for now ignore errors, they "CAN'T HAPPEN" */ (void) fdexec(0); /* no FDXC_CHECKCHG, ... */ /* no results */ /* send CONFIGURE command to fdc */ /* csb->unit is don't care */ csb->csb_cmds[0] = CONFIGURE; csb->csb_cmds[1] = fdconf[0]; /* motor info, motor delays */ csb->csb_cmds[2] = fdconf[1]; /* enaimplsk, disapoll, fifothru */ csb->csb_cmds[3] = fdconf[2]; /* track precomp */ csb->csb_ncmds = 4; csb->csb_retrys = 0; /* XXX for now ignore errors, they "CAN'T HAPPEN" */ (void) fdexec(0); /* no FDXC_CHECKCHG, ... */ /* no results */ } /* * fdrecalseek * performs recalibrates or seeks if the "arg" is -1 does a * recalibrate on a drive, else it seeks to the cylinder of * the drive. The recalibrate is also used to find a drive, * ie if the drive is not there, the controller says "error" * on the operation * NOTE: that there is special handling of this operation in the hardware * interrupt routine - it causes the operation to appear to have results; * ie the results of the SENSE INTERRUPT STATUS that the hardware interrupt * function did for us. * NOTE: because it uses sleep/wakeup it must be protected in a critical * section so create one before calling it! * * RETURNS: 0 for ok, * else errno from fdexec, * or ENODEV if error (infers hardware type error) */ fdrecalseek(unit, arg, execflg) int unit; int arg; /* -1 recalibrate, 0-? seek to here */ int execflg; /* bits to OR into flag sent to fdexec */ { register struct fdcsb *csb; int result; FDERRPRINT(FDEP_L1, FDEM_RECA, ("fdrecalseek to %d\n", arg)); /*XXX TODO: check see argument for <= num cyls OR < 256 ****/ csb = fdctlr.c_csb; csb->csb_un = fdctlr.c_units[unit]; csb->csb_unit = unit; csb->csb_part = 0; csb->csb_cmds[1] = unit & 0x03; if (arg == -1) { /* is recal... */ csb->csb_cmds[0] = FRAW_REZERO; csb->csb_ncmds = 2; } else { csb->csb_cmds[0] = FRAW_SEEK; csb->csb_cmds[2] = (u_char)arg; csb->csb_ncmds = 3; } csb->csb_nrslts = 2; /* 2 for SENSE INTERRUPTS */ csb->csb_opflags = CSB_OFSEEKOPS | CSB_OFTIMEIT; /* MAYBE NYD need to set retries to different values? - depending on * drive characteristics - if we get to high capacity drives */ csb->csb_maxretry = skretry; csb->csb_retrys = 0; /* send cmd off to fdexec */ if (result = fdexec(FDXC_SLEEP | execflg)) { goto out; } /* * if recal, test for equipment check error * ASSUMES result = 0 from above call */ if (arg == -1) { #ifdef I82077 result = 0; #else if (csb->csb_rslt[0] & EC_SR0) result = ENODEV; #endif I82077 } else { /* for seeks, any old error will do */ if ((csb->csb_rslt[0] & IC_SR0) || csb->csb_cmdstat) result = ENODEV; } out: return (result); } /* * fdformat * format a track - builds a table of sector data values with 16 bytes * (sizeof fdc's fifo) of dummy on end. This is so than when fdintr_len * goes to 0 and fdc_hardintr sends TC that all the real formatting will * have already been done. */ fdformat(unit, cyl, hd) int unit, cyl, hd; { register struct fdcsb *csb; register struct fdunit *un; register struct fdk_char *ch; int cmdresult; u_char *fmthdrs; register u_char *fd; int i; FDERRPRINT(FDEP_L1, FDEM_FORM, ("fdformat cyl %d, hd %d\n", cyl, hd)); fdgetcsb(); csb = fdctlr.c_csb; un = fdctlr.c_units[ unit ]; ch = un->un_chars; /* setup common things in csb */ csb->csb_un = un; csb->csb_unit = unit; csb->csb_part = 0; /* * stupid controller needs to do a seek before each format to get * to right cylinder. */ if (fdrecalseek(unit, cyl, FDXC_CHECKCHG)) { fdretcsb(); return (EIO); } /* * now do the format itself */ csb->csb_nrslts = NRBRW; csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT; csb->csb_cmds[0] = FRAW_FORMAT; /* always or in MFM bit */ csb->csb_cmds[0] |= MFM; csb->csb_cmds[1] = (hd << 2) | (unit & 0x03); csb->csb_cmds[2] = ch->medium ? 3 : 2; csb->csb_cmds[3] = ch->secptrack; csb->csb_cmds[4] = GPLF; csb->csb_cmds[5] = FDATA; csb->csb_ncmds = 6; csb->csb_maxretry = rwretry; csb->csb_retrys = 0; /* just kmem zalloc space for formattrk cmd */ /* NOTE: have to add size of fifo also - for dummy format action */ fd = fmthdrs = (u_char *)kmem_zalloc(((u_int)4 * ch->secptrack) + 16); csb->csb_addr = (caddr_t)fd; csb->csb_len = (4 * ch->secptrack) + 16; for (i = 1; i <= ch->secptrack; i++) { *fd++ = cyl; /* cylinder */ *fd++ = hd; /* head */ *fd++ = (u_char)i; /* sector number */ *fd++ = ch->medium ? 3 : 2; } if ((cmdresult = fdexec(FDXC_SLEEP | FDXC_CHECKCHG)) == 0) { if (csb->csb_cmdstat) cmdresult = EIO; /* XXX TBD NYD for now */ } fdretcsb(); kmem_free((caddr_t)fmthdrs, ((u_int)4 * ch->secptrack) + 16); return (cmdresult); } /* * fdrw * used for internal sorts of reads/writes, ie reading labels, * implementing raw commands thru ioctls, ... */ fdrw(flag, unit, cyl, head, sector, bufp, len) int flag, unit, cyl, head, sector; caddr_t bufp; u_int len; { register struct fdcsb *csb; int cmdresult; FDERRPRINT(FDEP_L1, FDEM_RW, ("fdrw\n")); fdgetcsb(); csb = fdctlr.c_csb; if (flag == FDREAD) csb->csb_cmds[0] = MT + SK + FRAW_RDCMD; else /* write */ csb->csb_cmds[0] = MT + FRAW_WRCMD; /* always or in MFM bit */ csb->csb_cmds[0] |= MFM; csb->csb_cmds[1] = (unit & 0x3) | ((head & 0x1) << 2); csb->csb_cmds[2] = cyl; csb->csb_cmds[3] = head; csb->csb_cmds[4] = sector; csb->csb_cmds[5] = ((fdctlr.c_units[unit])->un_chars->sec_size == 512) ? 2 : 3; csb->csb_cmds[6] = (fdctlr.c_units[unit])->un_chars->secptrack; csb->csb_cmds[7] = GPLN; csb->csb_cmds[8] = SSSDTL; csb->csb_ncmds = NCBRW; csb->csb_addr = bufp; csb->csb_len = len; csb->csb_maxretry = rwretry; csb->csb_retrys = 0; /* init result area ?? */ csb->csb_nrslts = NRBRW; csb->csb_opflags = CSB_OFXFEROPS | CSB_OFTIMEIT; cmdresult = 0; if (fdexec(FDXC_SLEEP | FDXC_CHECKCHG) != 0) { /* things was really hosed! */ cmdresult = EIO; goto out; } if (csb->csb_cmdstat) cmdresult = EIO; /* XXX TBD NYD for now */ out: fdretcsb(); return (cmdresult); } /* * fdsensedrv * do a sense_drive command. used by fdopen and fdcheckdisk. */ fdsensedrv(unit) int unit; { struct fdcsb *csb; csb = fdctlr.c_csb; /* setup common things in csb */ csb->csb_un = fdctlr.c_units[0]; csb->csb_unit = unit; csb->csb_part = 0; csb->csb_opflags = CSB_OFIMMEDIATE; csb->csb_cmds[0] = FRAW_SENSE_DRV; /* MOT bit set means don't delay */ csb->csb_cmds[1] = MOT | (unit & 0x03); csb->csb_ncmds = 2; csb->csb_nrslts = 1; csb->csb_maxretry = skretry; csb->csb_retrys = 0; /* XXX for now ignore errors, they "CAN'T HAPPEN" */ (void) fdexec(0); /* DON't check changed!, no sleep */ return (csb->csb_rslt[0]); /* return status byte 3 */ } /* * fdcheckdisk * check to see if the disk is still there - do a recalibrate, * then see if DSKCHG line went away, if so, diskette is in; else * it's (still) out. * INPUTS/ASSUMES: unit #, assumes it owns the csb/fdctlr. * * RETURNS: 0 if diskette is in drive, -1 if otherwise */ fdcheckdisk(unit) int unit; /* drive number to check */ { struct fdcsb *ccsb; /* private csb for recal op */ struct fdcsb *csb; /* ptr to "old" csb */ int err, st3; int seekto; /* where to seek for reset of DSKCHG */ FDERRPRINT(FDEP_L1, FDEM_CHEK, ("fdcheckdisk, unit %d\n", unit)); /* save old csb */ csb = fdctlr.c_csb; ccsb = (struct fdcsb *)kmem_zalloc(sizeof (struct fdcsb)); fdctlr.c_csb = ccsb; /* * read drive status to see if at TRK0, if so, seek to cyl 1, * else seek to cyl 0. We do this because the controller is * "smart" enough to not send any step pulses (which are how * the DSKCHG line gets reset) if it sees TRK0 'cause it * knows the drive is already recalibrated. */ st3 = fdsensedrv(unit); /* check TRK0 bit in status */ if (st3 & T0_SR3) seekto = 1; /* at TRK0, seek out */ else seekto = 0; err = fdrecalseek(unit, seekto, 0); /* DON'T recurse check changed */ /* "restore" old csb, check change state */ fdctlr.c_csb = csb; kmem_free((caddr_t)ccsb, sizeof (struct fdcsb)); /* any recal/seek errors are too serious to attend to */ if (err) { FDERRPRINT(FDEP_L2, FDEM_CHEK, ("fdcheckdisk err %d\n", err)); return (err); } /* if disk change still asserted, no diskette in drive! */ if (fdsense_chng()) { FDERRPRINT(FDEP_L2, FDEM_CHEK, ("fdcheckdisk no disk\n")); return (1); } return (0); } /* * the following functions are hw dependent * fdselect() - select drive, needed for external to chip select logic * fdeject() - ejects drive, must be previously selected * fdterm_count() - sends terminal count to previously selected drive * fdsense_chng() - sense disk changed line from previously selected drive * returns 1 is signal asserted, else 0 * fdsense_dnsty() - sense density line from previously selected drive * returns 1 is signal asserted, else 0 */ static void fdselect(unit, on) int unit; int on; /* non-zero = select, 0 = de-select */ { FDERRPRINT(FDEP_L1, FDEM_DSEL, ("fdselect, unit %d, on = %d\n", unit, on)); #if defined(sun4c) || defined(sun4m) #ifdef I82077 switch (fdtype) { case FD_82077: fd_set_dor_reg(DRVSEL, !on); break; case FD_82072: set_auxioreg(AUX_DRVSELECT, on); break; } #else set_auxioreg(AUX_DRVSELECT, on); #endif I82077 #endif sun4c sun4m } static fdeject() { FDERRPRINT(FDEP_L1, FDEM_EJEC, ("fdeject\n")); #if defined(sun4c) || defined(sun4m) /* assume delay of function calling sufficient settling time */ /* eject line is NOT driven by inverter so it is true low */ #ifdef I82077 switch (fdtype) { case FD_82077: DELAY(4); fd_set_dor_reg(EJECT, 1); DELAY(4); fd_set_dor_reg(EJECT, 0); break; case FD_82072: DELAY(4); set_auxioreg(AUX_EJECT, 0); DELAY(4); set_auxioreg(AUX_EJECT, 1); break; } #else DELAY(4); set_auxioreg(AUX_EJECT, 0); DELAY(4); set_auxioreg(AUX_EJECT, 1); #endif I82077 #endif sun4c sun4m /* * XXX set ejected state? */ } static fdsense_chng() { FDERRPRINT(FDEP_L1, FDEM_SCHG, ("fdsense_chng\n")); #if defined(sun4c) || defined(sun4m) #ifdef I82077 switch (fdtype) { case FD_82077: if (Dir & DISKCHG) return (1); break; case FD_82072: if (Auxio & AUX_DISKCHG) return (1); break; } #else if (Auxio & AUX_DISKCHG) return (1); #endif #endif sun4c sun4m return (0); } #ifdef NEVER /* NOT NEEDED for Campus */ /* term count is issued by the level11 handler on Campus (in fd_asm.s) */ static fdterm_count() { int i; #if defined(sun4c) || defined(sun4m) set_auxioreg(AUX_TC, 1); DELAY(5); set_auxioreg(AUX_TC, 0); #endif sun4c sun4m } static fdsense_dnsty() { #if defined(sun4c) || defined(sun4m) if (Auxio & AUX_DENSITY) return (1); #endif sun4c sun4m return (0); } #endif NEVER /* * fdpacklabel * this packs an (unpacked) struct dk_label into a packed label. */ fdpacklabel(from, to, unit) struct dk_label *from; struct packed_label *to; int unit; { FDERRPRINT(FDEP_L1, FDEM_PACK, ("fdpacklabel\n")); /* clear the destination */ bzero((caddr_t)to, sizeof (struct packed_label)); to->dkl_asciip = fdkl_alabel[unit]; /* point at something reasonable */ (void)strcpy(to->dkl_asciip, from->dkl_asciilabel); to->dkl_rpm = from->dkl_rpm; /* rotations per minute */ to->dkl_pcyl = from->dkl_pcyl; /* # physical cylinders */ to->dkl_apc = from->dkl_apc; /* alternates per cylinder */ to->dkl_intrlv = from->dkl_intrlv; /* interleave factor */ to->dkl_ncyl = from->dkl_ncyl; /* # of data cylinders */ to->dkl_acyl = from->dkl_acyl; /* # of alternate cylinders */ to->dkl_nhead = from->dkl_nhead; /* # of heads in this partition */ to->dkl_nsect = from->dkl_nsect; /* # of 512 byte sectors per track */ /* logical partitions */ bcopy((caddr_t)from->dkl_map, (caddr_t)to->dkl_map, sizeof (struct dk_map) * NDKMAP); } #ifdef NEVER /* * fdunpacklabel * this unpacks a packed default label into a struct dk_label, * complete with magic number and checksum */ fdunpacklabel(from, to) struct packed_label *from; struct dk_label *to; { short xsum; short *sp; int i; /* clear the destination */ bzero((caddr_t)to, sizeof (struct dk_label)); strcpy(to->dkl_asciilabel, from->dkl_asciip); to->dkl_rpm = from->dkl_rpm; /* rotations per minute */ to->dkl_pcyl = from->dkl_pcyl; /* # physical cylinders */ to->dkl_apc = from->dkl_apc; /* alternates per cylinder */ to->dkl_intrlv = from->dkl_intrlv; /* interleave factor */ to->dkl_ncyl = from->dkl_ncyl; /* # of data cylinders */ to->dkl_acyl = from->dkl_acyl; /* # of alternate cylinders */ to->dkl_nhead = from->dkl_nhead; /* # of heads in this partition */ to->dkl_nsect = from->dkl_nsect; /* # of 512 byte sectors per track */ /* logical partitions */ bcopy((caddr_t)from->dkl_map, (caddr_t)to->dkl_map, sizeof (struct dk_map) * NDKMAP); to->dkl_magic = DKL_MAGIC; xsum = 0; sp = (short *)to; for (i = (sizeof (struct dk_label)/sizeof (short)); i > 1; i--) xsum ^= *sp++; to->dkl_cksum = xsum; } #endif NEVER fd_set_dor_reg(bit, flag) int bit; int flag; { if (bit & MOTEN ) { /* deselect the drive according to Sony spec */ fd_set_dor(3,1); if (fdctlr.c_units[fdctlr.c_csb->csb_unit]->un_chars->medium) { bit |= MEDIUM_DENSITY; fd_set_dor(MEDIUM_DENSITY , 1); } else { fd_set_dor(MEDIUM_DENSITY, 0); } DELAY(20); /* select */ fd_set_dor(3,0); if (fderrlevel == 0) printf("----> M O T O R %d = %x\n",flag, bit); } fd_set_dor(bit, flag); /* * Give at least 500 ms MIN for the motor to spin */ if (flag) DELAY(650000); } #endif /* NFD > 0 */