diff --git a/sys/arch/vax/mscp/files.mscp b/sys/arch/vax/mscp/files.mscp new file mode 100644 index 000000000000..ad85bcbebbc5 --- /dev/null +++ b/sys/arch/vax/mscp/files.mscp @@ -0,0 +1,20 @@ +# $NetBSD: files.mscp,v 1.1 1996/07/01 20:41:30 ragge Exp $ +# +# File and device description for MSCP devices. +# + +define mscp {} +file arch/vax/mscp/mscp.c +file arch/vax/mscp/mscp_subr.c + +device mscpbus {drive = -1} +attach mscpbus at mscp + +device ra: disk +attach ra at mscpbus +file arch/vax/mscp/mscp_disk.c ra needs-flag + +device mt: tape +attach mt at mscpbus +file arch/vax/mscp/mscp_tape.c mt needs-flag + diff --git a/sys/arch/vax/mscp/mscp.c b/sys/arch/vax/mscp/mscp.c new file mode 100644 index 000000000000..3fb91f210dec --- /dev/null +++ b/sys/arch/vax/mscp/mscp.c @@ -0,0 +1,497 @@ +/* $NetBSD: mscp.c,v 1.1 1996/07/01 20:41:32 ragge Exp $ */ + +/* + * Copyright (c) 1996 Ludd, University of Lule}, Sweden. + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)mscp.c 7.5 (Berkeley) 12/16/90 + */ + +/* + * MSCP generic driver routines + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ra.h" +#define NMT 0 /* XXX */ + +#define MAXMSCPDEV 255 /* Can there be more? */ +#define b_forw b_hash.le_next + +void mscp_hexdump __P((struct mscp *)); +int mscp_match __P((struct device *, void *, void *)); +void mscp_attach __P((struct device *, struct device *, void *)); +void mscp_start __P((struct mscp_softc *)); +int mscp_init __P((struct mscp_softc *)); +void mscp_initds __P((struct mscp_softc *)); + +#define PCMD PSWP /* priority for command packet waits */ + +/* + * During transfers, mapping info is saved in the buffer's b_resid. + */ +#define b_info b_resid + +/* + * Get a command packet. Second argument is true iff we are + * to wait if necessary. Return NULL if none are available and + * we cannot wait. + */ +struct mscp * +mscp_getcp(mi, canwait) + register struct mscp_softc *mi; + int canwait; +{ +#define mri (&mi->mi_cmd) + register struct mscp *mp; + register int i; + int s = splbio(); + +again: + /* + * Ensure that we have some command credits, and + * that the next command packet is free. + */ + if (mi->mi_credits <= MSCP_MINCREDITS) { + if (!canwait) { + splx(s); + return (NULL); + } + mi->mi_wantcredits = 1; + sleep((caddr_t) &mi->mi_wantcredits, PCMD); + goto again; + } + i = mri->mri_next; + if (mri->mri_desc[i] & MSCP_OWN) { + if (!canwait) { + splx(s); + return (NULL); + } + mi->mi_wantcmd = 1; + sleep((caddr_t) &mi->mi_wantcmd, PCMD); + goto again; + } + mi->mi_credits--; + mri->mri_desc[i] &= ~MSCP_INT; + mri->mri_next = (mri->mri_next + 1) % mri->mri_size; + splx(s); + mp = &mri->mri_ring[i]; + + /* + * Initialise some often-zero fields. + * ARE THE LAST TWO NECESSARY IN GENERAL? IT SURE WOULD BE + * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS. + */ + mp->mscp_msglen = MSCP_MSGLEN; + mp->mscp_flags = 0; + mp->mscp_modifier = 0; + mp->mscp_seq.seq_bytecount = 0; + mp->mscp_seq.seq_buffer = 0; + mp->mscp_seq.seq_mapbase = 0; +/*???*/ mp->mscp_sccc.sccc_errlgfl = 0; +/*???*/ mp->mscp_sccc.sccc_copyspd = 0; + return (mp); +#undef mri +} + +#ifdef AVOID_EMULEX_BUG +int mscp_aeb_xor = 0x8000bb80; +#endif + +/* + * Handle a response ring transition. + */ +void +mscp_dorsp(mi) + register struct mscp_softc *mi; +{ + struct device *drive; + struct mscp_device *me = mi->mi_me; + struct mscp_ctlr *mc = mi->mi_mc; + register struct buf *bp; + register struct mscp *mp; + register int nextrsp; + int st, error, info; + extern int cold; + extern struct mscp slavereply; + + nextrsp = mi->mi_rsp.mri_next; +loop: + if (mi->mi_rsp.mri_desc[nextrsp] & MSCP_OWN) { + /* + * No more responses. Remember the next expected + * response index. Check to see if we have some + * credits back, and wake up sleepers if so. + */ + mi->mi_rsp.mri_next = nextrsp; + if (mi->mi_wantcredits && mi->mi_credits > MSCP_MINCREDITS) { + mi->mi_wantcredits = 0; + wakeup((caddr_t) &mi->mi_wantcredits); + } + return; + } + + mp = &mi->mi_rsp.mri_ring[nextrsp]; + mi->mi_credits += MSCP_CREDITS(mp->mscp_msgtc); + /* + * Controllers are allowed to interrupt as any drive, so we + * must check the command before checking for a drive. + */ + if (mp->mscp_opcode == (M_OP_SETCTLRC | M_OP_END)) { + if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS) { + mi->mi_flags |= MSC_READY; + } else { + printf("%s: SETCTLRC failed: %d ", + mi->mi_dev.dv_xname, mp->mscp_status); + mscp_printevent(mp); + } + goto done; + } + + /* + * Found a response. Update credit information. If there is + * nothing else to do, jump to `done' to get the next response. + */ + drive = mi->mi_dp[mp->mscp_unit]; + + switch (MSCP_MSGTYPE(mp->mscp_msgtc)) { + + case MSCPT_SEQ: + break; + + case MSCPT_DATAGRAM: + (*me->me_dgram)(drive, mp); + goto done; + + case MSCPT_CREDITS: + goto done; + + case MSCPT_MAINTENANCE: + default: + printf("%s: unit %d: unknown message type 0x%x ignored\n", + mi->mi_dev.dv_xname, mp->mscp_unit, + MSCP_MSGTYPE(mp->mscp_msgtc)); + goto done; + } + + /* + * Handle individual responses. + */ + st = mp->mscp_status & M_ST_MASK; + error = 0; + switch (mp->mscp_opcode) { + + case M_OP_END: + /* + * The controller presents a bogus END packet when + * a read/write command is given with an illegal + * block number. This is contrary to the MSCP + * specification (ENDs are to be given only for + * invalid commands), but that is the way of it. + */ + if (st == M_ST_INVALCMD && mp->mscp_cmdref != 0) { + printf("%s: bad lbn (%d)?\n", drive->dv_xname, + (int)mp->mscp_seq.seq_lbn); + error = EIO; + goto rwend; + } + goto unknown; + + case M_OP_ONLINE | M_OP_END: + /* + * Finished an ON LINE request. Call the driver to + * find out whether it succeeded. If so, mark it on + * line. + */ + (*me->me_online)(drive, mp); + break; + + case M_OP_GETUNITST | M_OP_END: + /* + * Got unit status. If we are autoconfiguring, save + * the mscp struct so that mscp_attach know what to do. + * If the drive isn't configured, call config_found() + * to set it up, otherwise it's just a "normal" unit + * status. + */ + if (cold) + bcopy(mp, &slavereply, sizeof(struct mscp)); + + if (drive == 0) { + struct drive_attach_args da; + + da.da_mp = (struct mscp *)mp; + config_found(&mi->mi_dev, (void *)&da, mscp_print); + } else + (*me->me_gotstatus)(drive, mp); + break; + + case M_OP_AVAILATTN: + /* + * The drive went offline and we did not notice. + * Mark it off line now, to force an on line request + * next, so we can make sure it is still the same + * drive. + * + * IF THE UDA DRIVER HAS A COMMAND AWAITING UNIBUS + * RESOURCES, THAT COMMAND MAY GO OUT BEFORE THE ON + * LINE. IS IT WORTH FIXING?? + */ +#ifdef notyet + (*md->md_offline)(ui, mp); +#endif + break; + + case M_OP_READ | M_OP_END: + case M_OP_WRITE | M_OP_END: + /* + * A transfer finished. Get the buffer, and release its + * map registers via ubadone(). If the command finished + * with an off line or available status, the drive went + * off line (the idiot controller does not tell us until + * it comes back *on* line, or until we try to use it). + */ + if (mp->mscp_cmdref == 0) { + /* + * No buffer means there is a bug somewhere! + */ + printf("%s: io done, but no buffer?\n", + drive->dv_xname); + mscp_hexdump(mp); + break; + } + +rwend: + bp = (struct buf *) mp->mscp_cmdref; + + /* + * Mark any error-due-to-bad-LBN (via `goto rwend'). + * WHAT STATUS WILL THESE HAVE? IT SURE WOULD BE NICE + * IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS. + */ + if (error) { + bp->b_flags |= B_ERROR; + bp->b_error = error; + } + if (st == M_ST_OFFLINE || st == M_ST_AVAILABLE) { +#ifdef notyet + (*md->md_offline)(ui, mp); +#endif + } + + /* + * Unlink the transfer from the wait queue. + */ + remque(&bp->b_actf); + + /* + * If the transfer has something to do with bad + * block forwarding, let the driver handle the + * rest. + */ + if ((bp->b_flags & B_BAD) != 0 && me->me_bb != NULL) { + (*me->me_bb)(drive, mp, bp); + goto out; + } + + /* + * If the transfer failed, give the driver a crack + * at fixing things up. + */ + if (st != M_ST_SUCCESS) { + switch ((*me->me_ioerr)(drive, mp, bp)) { + + case MSCP_DONE: /* fixed */ + break; + + case MSCP_RESTARTED: /* still working on it */ + goto out; + + case MSCP_FAILED: /* no luck */ + /* XXX must move to ra.c */ + mscp_printevent(mp); + break; + } + } + + /* + * Set the residual count and mark the transfer as + * done. If the I/O wait queue is now empty, release + * the shared BDP, if any. + */ + info = bp->b_info; /* we are about to clobber it */ + bp->b_resid = bp->b_bcount - mp->mscp_seq.seq_bytecount; + + (*mc->mc_ctlrdone)(mi->mi_dev.dv_parent, info); + (*me->me_iodone)(drive, bp); +out: + break; + + case M_OP_REPLACE | M_OP_END: + /* + * A replace operation finished. Just let the driver + * handle it (if it does replaces). + */ + if (me->me_replace == NULL) + printf("%s: bogus REPLACE end\n", drive->dv_xname); + else + (*me->me_replace)(drive, mp); + break; + + default: + /* + * If it is not one of the above, we cannot handle it. + * (And we should not have received it, for that matter.) + */ +unknown: + printf("%s: unknown opcode 0x%x status 0x%x ignored\n", + drive->dv_xname, mp->mscp_opcode, mp->mscp_status); + mscp_hexdump(mp); + break; + } + + /* + * If the drive needs to be put back in the controller queue, + * do that now. (`bp' below ought to be `dp', but they are all + * struct buf *.) Note that b_active was cleared in the driver; + * we presume that there is something to be done, hence reassert it. + */ +#ifdef notyet /* XXX */ + if (ui->ui_flags & UNIT_REQUEUE) { + bp = &md->md_utab[ui->ui_unit]; + if (bp->b_active) panic("mscp_dorsp requeue"); + MSCP_APPEND(bp, mi->mi_XXXtab, b_hash.le_next); +/* Was: MSCP_APPEND(bp, mi->mi_XXXtab, b_forw); */ + bp->b_active = 1; + ui->ui_flags &= ~UNIT_REQUEUE; + } +#endif +done: + /* + * Give back the response packet, and take a look at the next. + */ + mp->mscp_msglen = MSCP_MSGLEN; + mi->mi_rsp.mri_desc[nextrsp] |= MSCP_OWN; + nextrsp = (nextrsp + 1) % mi->mi_rsp.mri_size; + goto loop; +} + +/* + * Requeue outstanding transfers, e.g., after bus reset. + * Also requeue any drives that have on line or unit status + * info pending. + */ +void +mscp_requeue(mi) + struct mscp_softc *mi; +{ + register struct mscp_device *me = mi->mi_me; + register struct buf *bp, *dp; + register int unit; + struct buf *nextbp; + +panic("mscp_requeue"); + /* + * Clear the controller chain. Mark everything un-busy; we + * will soon fix any that are in fact busy. + */ +#ifdef notyet /* XXX */ + mi->mi_XXXtab->b_actf = NULL; + mi->mi_XXXtab->b_active = 0; + for (unit = 0, dp = md->md_utab; unit < md->md_nunits; unit++, dp++) { + ui = md->md_dinfo[unit]; + if (ui == NULL || !ui->ui_alive || ui->ui_ctlr != mi->mi_ctlr) + continue; /* not ours */ + dp->b_hash.le_next = NULL; + dp->b_active = 0; + } + /* + * Scan the wait queue, linking buffers onto drive queues. + * Note that these must be put at the front of the drive queue, + * lest we reorder I/O operations. + */ + for (bp = *mi->mi_XXXwtab.b_actb; bp != &mi->mi_XXXwtab; bp = nextbp) { + nextbp = *bp->b_actb; + dp = &md->md_utab[minor(bp->b_dev) >> md->md_unitshift]; + bp->b_actf = dp->b_actf; + if (dp->b_actf == NULL) + dp->b_actb = (void *)bp; + dp->b_actf = bp; + } + mi->mi_XXXwtab.b_actf = *mi->mi_XXXwtab.b_actb = &mi->mi_XXXwtab; + + /* + * Scan for drives waiting for on line or status responses, + * and for drives with pending transfers. Put these on the + * controller queue, and mark the controller busy. + */ + for (unit = 0, dp = md->md_utab; unit < md->md_nunits; unit++, dp++) { + ui = md->md_dinfo[unit]; + if (ui == NULL || !ui->ui_alive || ui->ui_ctlr != mi->mi_ctlr) + continue; + ui->ui_flags &= ~(UNIT_HAVESTATUS | UNIT_ONLINE); + if ((ui->ui_flags & UNIT_REQUEUE) == 0 && dp->b_actf == NULL) + continue; + ui->ui_flags &= ~UNIT_REQUEUE; + MSCP_APPEND(dp, mi->mi_XXXtab, b_hash.le_next); + + dp->b_active = 1; + mi->mi_XXXtab->b_active = 1; + } + +#endif +#ifdef AVOID_EMULEX_BUG + /* + * ... and clear the index-to-buffer table. + */ + for (unit = 0; unit < AEB_MAX_BP; unit++) + mi->mi_bp[unit] = 0; +#endif +} + diff --git a/sys/arch/vax/mscp/mscp.h b/sys/arch/vax/mscp/mscp.h new file mode 100644 index 000000000000..4831e30ace25 --- /dev/null +++ b/sys/arch/vax/mscp/mscp.h @@ -0,0 +1,473 @@ +/* $NetBSD: mscp.h,v 1.1 1996/07/01 20:41:33 ragge Exp $ */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)mscp.h 7.5 (Berkeley) 6/28/90 + */ + +/* + * Definitions for the Mass Storage Control Protocol + * I WISH I KNEW WHAT MORE OF THESE WERE. IT SURE WOULD BE NICE + * IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS. + */ + +/* + * Control message opcodes + */ +#define M_OP_ABORT 0x01 /* Abort command */ +#define M_OP_GETCMDST 0x02 /* Get command status command */ +#define M_OP_GETUNITST 0x03 /* Get unit status command */ +#define M_OP_SETCTLRC 0x04 /* Set controller characteristics command */ +#define M_OP_SEREX 0x07 /* Serious exception end message */ +#define M_OP_AVAILABLE 0x08 /* Available command */ +#define M_OP_ONLINE 0x09 /* Online command */ +#define M_OP_SETUNITC 0x0a /* Set unit characteristics command */ +#define M_OP_DTACCPATH 0x0b /* Determine access paths command */ +#define M_OP_ACCESS 0x10 /* Access command */ +#define M_OP_COMPCD 0x11 /* Compare controller data command */ +#define M_OP_ERASE 0x12 /* Erase command */ +#define M_OP_FLUSH 0x13 /* Flush command */ +#define M_OP_REPLACE 0x14 /* Replace command */ +#define M_OP_COMPHD 0x20 /* Compare host data command */ +#define M_OP_READ 0x21 /* Read command */ +#define M_OP_WRITE 0x22 /* Write command */ +#define M_OP_POS 0x25 /* Positioning command */ +#define M_OP_AVAILATTN 0x40 /* Available attention message */ +#define M_OP_DUPUNIT 0x41 /* Duplicate unit number attention message */ +#define M_OP_ACCPATH 0x42 /* Access path attention message */ +#define M_OP_END 0x80 /* End message flag */ + + +/* + * Generic command modifiers + */ +#define M_MD_EXPRS 0x8000 /* Express request */ +#define M_MD_COMP 0x4000 /* Compare */ +#define M_MD_CLSEX 0x2000 /* Clear serious exception */ +#define M_MD_ERROR 0x1000 /* Force error */ +#define M_MD_SCCHH 0x0800 /* Suppress caching (high speed) */ +#define M_MD_SCCHL 0x0400 /* Suppress caching (low speed) */ +#define M_MD_SECOR 0x0200 /* Suppress error correction */ +#define M_MD_SEREC 0x0100 /* Suppress error recovery */ +#define M_MD_SSHDW 0x0080 /* Suppress shadowing */ +#define M_MD_WBKNV 0x0040 /* Write back (non-volatile) */ +#define M_MD_WBKVL 0x0020 /* Write back (volatile) */ +#define M_MD_WRSEQ 0x0010 /* Write shadow set one unit at a time */ + +/* + * AVAILABLE command modifiers + */ +#define M_AVM_ALLCD 0x0002 /* All class drivers */ +#define M_AVM_SPINDOWN 0x0001 /* Spin down */ + +/* + * FLUSH command modifiers + */ +#define M_FLM_FLUSHENU 0x0001 /* Flush entire unit */ +#define M_FLM_VOLATILE 0x0002 /* Volatile only */ + +/* + * GET UNIT STATUS command modifiers + */ +#define M_GUM_NEXTUNIT 0x0001 /* Next unit */ + +/* + * ONLINE command modifiers + */ +#define M_OLM_RIP 0x0001 /* Allow self destruction */ +#define M_OLM_IGNMF 0x0002 /* Ignore media format error */ + +/* + * ONLINE and SET UNIT CHARACTERISTICS command modifiers + */ +#define M_OSM_ALTERHI 0x0020 /* Alter host identifier */ +#define M_OSM_SHADOWSP 0x0010 /* Shadow unit specified */ +#define M_OSM_CLEARWBL 0x0008 /* Clear write-back data lost */ +#define M_OSM_SETWRPROT 0x0004 /* Set write protect */ + +/* + * REPLACE command modifiers + */ +#define M_RPM_PRIMARY 0x0001 /* Primary replacement block */ + +/* + * End message flags + */ +#define M_EF_BBLKR 0x80 /* Bad block reported */ +#define M_EF_BBLKU 0x40 /* Bad block unreported */ +#define M_EF_ERLOG 0x20 /* Error log generated */ +#define M_EF_SEREX 0x10 /* Serious exception */ + +/* + * Controller flags + */ +#define M_CF_ATTN 0x80 /* Enable attention messages */ +#define M_CF_MISC 0x40 /* Enable miscellaneous error log messages */ +#define M_CF_OTHER 0x20 /* Enable other host's error log messages */ +#define M_CF_THIS 0x10 /* Enable this host's error log messages */ +#define M_CF_MLTHS 0x04 /* Multi-host */ +#define M_CF_SHADW 0x02 /* Shadowing */ +#define M_CF_576 0x01 /* 576 byte sectors */ + +/* + * Unit flags + */ +#define M_UF_REPLC 0x8000 /* Controller initiated bad block replacement */ +#define M_UF_INACT 0x4000 /* Inactive shadow set unit */ +#define M_UF_WRTPH 0x2000 /* Write protect (hardware) */ +#define M_UF_WRTPS 0x1000 /* Write protect (software or volume) */ +#define M_UF_SCCHH 0x8000 /* Suppress caching (high speed) */ +#define M_UF_SCCHL 0x4000 /* Suppress caching (low speed) */ +#define M_UF_RMVBL 0x0080 /* Removable media */ +#define M_UF_WBKNV 0x0040 /* Write back (non-volatile) */ +#define M_UF_576 0x0004 /* 576 byte sectors */ +#define M_UF_CMPWR 0x0002 /* Compare writes */ +#define M_UF_CMPRD 0x0001 /* Compare reads */ + +/* + * Error Log message format codes + */ +#define M_FM_CTLRERR 0x00 /* Controller error */ +#define M_FM_BUSADDR 0x01 /* Host memory access error */ +#define M_FM_DISKTRN 0x02 /* Disk transfer error */ +#define M_FM_SDI 0x03 /* SDI error */ +#define M_FM_SMLDSK 0x04 /* Small disk error */ + +/* + * Error Log message flags + */ +#define M_LF_SUCC 0x80 /* Operation successful */ +#define M_LF_CONT 0x40 /* Operation continuing */ +#define M_LF_SQNRS 0x01 /* Sequence number reset */ + +/* + * Status codes + */ +#define M_ST_MASK 0x1f /* Status code mask */ +#define M_ST_SUCCESS 0x00 /* Success */ +#define M_ST_INVALCMD 0x01 /* Invalid command */ +#define M_ST_ABORTED 0x02 /* Command aborted */ +#define M_ST_OFFLINE 0x03 /* Unit offline */ +#define M_ST_AVAILABLE 0x04 /* Unit available */ +#define M_ST_MFMTERR 0x05 /* Media format error */ +#define M_ST_WRPROT 0x06 /* Write protected */ +#define M_ST_COMPERR 0x07 /* Compare error */ +#define M_ST_DATAERR 0x08 /* Data error */ +#define M_ST_HOSTBUFERR 0x09 /* Host buffer access error */ +#define M_ST_CTLRERR 0x0a /* Controller error */ +#define M_ST_DRIVEERR 0x0b /* Drive error */ +#define M_ST_DIAG 0x1f /* Message from an internal diagnostic */ + +/* + * Subcodes of M_ST_OFFLINE + */ +#define M_OFFLINE_UNKNOWN (0 << 5) /* unknown or on other ctlr */ +#define M_OFFLINE_UNMOUNTED (1 << 5) /* unmounted or RUN/STOP at STOP */ +#define M_OFFLINE_INOPERATIVE (2 << 5) /* inoperative? */ +#define M_OFFLINE_DUPLICATE (4 << 5) /* duplicate unit number */ +#define M_OFFLINE_INDIAGNOSTIC (8 << 5) /* disabled by FS or diagnostic */ + +/* + * An MSCP packet begins with a header giving the length of + * the entire packet (including the header itself)(?), two bytes + * of device specific data, and the a whole bunch of variants + * depending on message type. + * + * N.B.: In most cases we distinguish between a `command' and + * an `end' variant as well. The command variant is that which + * is given to the controller; the `end' variant is its response. + */ + +/* + * Generic sequential message variant (command and response). + */ +struct mscpv_seq { + long seq_bytecount; /* byte count */ +#define seq_rbn seq_bytecount /* aka RBN (replace) */ +#define seq_outref seq_bytecount /* aka outref (abort/get cmd status) */ + long seq_buffer; /* buffer descriptor */ + long seq_mapbase; /* page map (first PTE) phys address */ + long seq_xxx1; /* ? */ /* unused */ + long seq_lbn; /* logical block number */ + long seq_xxx2; /* ? */ /* unused */ + long *seq_addr; /* pointer to cmd descriptor */ + long seq_software[4]; /* reserved to software; unused */ +}; + +/* + * Set Controller Characteristics command variant + */ +struct mscpv_sccc { + u_short sccc_version; /* MSCP version number */ + u_short sccc_ctlrflags; /* controller flags */ + u_short sccc_hosttimo; /* host timeout */ + u_short sccc_usefrac; /* use fraction */ + long sccc_time; /* time and date */ + long sccc_xxx1; /* ? */ + long sccc_errlgfl; /* ? */ + short sccc_xxx2; /* ? */ + short sccc_copyspd; /* ? */ +}; + +/* + * Set Controller Characteristics end variant + */ +struct mscpv_scce { + u_short scce_version; /* MSCP version number */ + u_short scce_ctlrflags; /* controller flags */ + u_short scce_ctlrtimo; /* controller timeout */ + u_short scce_ctlrcmdl; /* ??? */ + quad_t scce_ctlrid; /* controller ID */ + long scce_xxx[3]; /* ? */ + long scce_volser; /* volume serial number */ +}; + +/* + * On Line command variant + */ +struct mscpv_onlc { + long onlc_xxx1[4]; /* ? */ + long onlc_errlgfl; /* error log flag? */ + short onlc_xxx2; /* ? */ + short onlc_copyspd; /* copy speed? */ +}; + +/* + * On Line end variant + */ +struct mscpv_onle { + long onle_xxx1[3]; /* ? */ +/*???*/ short onle_xxx2; /* ? */ + u_char onle_drivetype; /* drive type index (same in guse) */ + char onle_xxx3; /* ? */ + long onle_mediaid; /* media type id (same in guse) */ + long onle_xxx4; /* ? */ + long onle_unitsize; /* unit size in sectors */ + long onle_volser; /* volume serial number */ +}; + +/* + * Get Unit Status end variant (and Avail Attn?) + */ +struct mscpv_guse { + u_short guse_multunit; /* multi-unit code */ + u_short guse_unitflags; /* unit flags */ + long guse_hostid; /* host id */ + long guse_unitid0; /*???*/ + short guse_unitid1; /*???*/ + u_char guse_drivetype; /* drive type index */ + u_char guse_unitid2; /*???*/ + long guse_mediaid; /* media type id (encoded) */ + short guse_shadowunit; /* shadow unit */ + short guse_shadowstat; /* shadow status */ + u_short guse_nspt; /* sectors per track */ + u_short guse_group; /* track group size */ + u_short guse_ngpc; /* groups per cylinder */ + u_short guse_xxx; /* reserved */ + u_short guse_rctsize; /* RCT size (sectors) */ + u_char guse_nrpt; /* RBNs per track */ + u_char guse_nrct; /* number of RCTs */ +}; + +/* + * Macros to break up and build media IDs. An ID encodes the port + * type in the top 10 bits, and the drive type in the remaining 22. + * The 10 bits, and 15 of the 22, are in groups of 5, with the value + * 0 representing space and values 1..26 representing A..Z. The low + * 7 bits represent a number in 0..127. Hence an RA81 on a UDA50 + * is < >81, or 0x25641051. This encoding scheme is known + * in part in uda.c. + * + * The casts below are just to make pcc generate better code. + */ +#define MSCP_MEDIA_PORT(id) (((long)(id) >> 22) & 0x3ff) /* port */ +#define MSCP_MEDIA_DRIVE(id) ((long)(id) & 0x003fffff) /* drive */ +#define MSCP_MID_ECH(n, id) (((long)(id) >> ((n) * 5 + 7)) & 0x1f) +#define MSCP_MID_CHAR(n, id) \ + (MSCP_MID_ECH(n, id) ? MSCP_MID_ECH(n, id) + '@' : ' ') +#define MSCP_MID_NUM(id) ((id) & 0x7f) +/* for, e.g., RA81 */ +#define MSCP_MKDRIVE2(a, b, n) \ + (((a) - '@') << 17 | ((b) - '@') << 12 | (n)) +/* for, e.g., RRD50 */ +#define MSCP_MKDRIVE3(a, b, c, n) \ + (((a) - '@') << 17 | ((b) - '@') << 12 | ((c) - '@') << 7 | (n)) + +/* + * Error datagram variant. + */ +struct mscpv_erd { + quad_t erd_ctlrid; /* controller ID */ + u_char erd_ctlrsoftware; /* controller software version */ + u_char erd_ctlrhardware; /* controller hardware version */ + u_short erd_multiunit; /* multi-unit code (?) */ + union { + u_long un_busaddr; /* bus address, if mem access err */ + quad_t un_unitid; /* unit id, otherwise */ + } erd_un1; +#define erd_busaddr erd_un1.un_busaddr +#define erd_unitid erd_un1.un_unitid + u_char erd_unitsoftware; /* unit software version */ + u_char erd_unithardware; /* unit hardware version */ + union { + u_char un_b[2]; /* level, retry (if disk xfer err) */ + u_short un_s; /* cylinder (if small disk error) */ + } erd_un2; +#define erd_level erd_un2.un_b[0] +#define erd_retry erd_un2.un_b[1] +#define erd_sdecyl erd_un2.un_s + long erd_volser; /* volume serial number */ + u_long erd_hdr; /* `header' (block number) */ + u_char erd_sdistat[12]; /* SDI status information (?) */ +}; + +/* + * I am making brash assumptions about the first four bytes of all + * MSCP packets. These appear to be true for both UDA50s and TMSCP + * devices (TU81, TA81, TK50). DEC claim that these four bytes are + * not part of MSCP itself, yet at least the length is necessary + * for, e.g., error checking. + */ +struct mscp { + u_short mscp_msglen; /* length in bytes */ + u_char mscp_msgtc; /* type (high 4 bits) and credits */ + u_char mscp_vcid; /* virtual circuit ID */ + long mscp_cmdref; /* command reference number */ + u_short mscp_unit; /* unit number */ + u_short mscp_seqnum; /* sequence number */ + u_char mscp_opcode; /* opcode */ +#define mscp_format mscp_opcode /* aka format (datagrams) */ + u_char mscp_flags; /* flags */ + u_short mscp_modifier; /* modifier (commands) */ +#define mscp_status mscp_modifier /* aka status (ends) */ +#define mscp_event mscp_modifier /* aka event (datagrams) */ + union { + struct mscpv_seq un_seq; /* generic sequential msg */ + struct mscpv_sccc un_sccc; /* SCC command */ + struct mscpv_scce un_scce; /* SCC end */ + struct mscpv_onlc un_onlc; /* on line command */ + struct mscpv_onle un_onle; /* on line end */ + struct mscpv_guse un_guse; /* get unit status */ + struct mscpv_erd un_erd; /* error datagram */ + } mscp_un; +/*???*/ long mscp_xxx; /* pad to 64 bytes */ +}; + +/* + * Define message length according to the DEC specifications by dropping + * the four byte header. + */ +#define MSCP_MSGLEN (sizeof (struct mscp) - 4) + +/* + * Shorthand + */ + +/* + * Generic packet + */ +#define mscp_seq mscp_un.un_seq + +/* + * Set Controller Characteristics packet + */ +#define mscp_sccc mscp_un.un_sccc + +/* + * Set Controller Characteristics end packet + */ +#define mscp_scce mscp_un.un_scce + +/* + * Online / Set Unit Characteristics command packet + */ +#define mscp_onlc mscp_un.un_onlc + +/* + * Online end packet + */ +#define mscp_onle mscp_un.un_onle + +/* + * Get Unit Status end packet + */ +#define mscp_guse mscp_un.un_guse + +/* + * MSCP Error Log packet + */ +#define mscp_erd mscp_un.un_erd + +/* + * MSCP seq_addr field actually belongs to overall packet. + */ +#define mscp_addr mscp_seq.seq_addr + +/* + * Macros to break up mscp_msgtc, and types. + */ +#define MSCP_MSGTYPE(m) ((m) & 0xf0) +#define MSCP_CREDITS(m) ((m) & 0x0f) + +#define MSCPT_SEQ 0x00 /* sequential message */ +#define MSCPT_DATAGRAM 0x10 /* error datagram */ +#define MSCPT_CREDITS 0x20 /* credit notification */ +#define MSCPT_MAINTENANCE 0xf0 /* who knows */ + + +/* + * Here begin more perhaps brash assumptions about MSCP devices... + */ + +/* + * MSCP controllers have `command rings' and `response rings'. A + * command ring is a pool of MSCP packets that the host uses to give + * commands to the controller; a response ring is a pool of MSCP + * packets that the controller uses to give back responses. Entries + * in the command and response rings are `owned' by either the host + * or the controller; only the owner is allowed to alter any of the + * fields in the MSCP packet. Thus, free command packets are owned + * by the host, and free response packets by the controller. When + * the host gives a packet to the controller, it tells the controller + * by touching a device register; when the controller gives a response + * to the host, it generates an interrupt if enabled, and sets + * a device register as well. + * + * The pool is `described' by a set of pointers to the packets, along + * with the two flags below. + */ +#define MSCP_OWN 0x80000000 /* controller owns this packet */ +#define MSCP_INT 0x40000000 /* controller should interrupt */ diff --git a/sys/arch/vax/mscp/mscp_disk.c b/sys/arch/vax/mscp/mscp_disk.c new file mode 100644 index 000000000000..4c48ca6b00a2 --- /dev/null +++ b/sys/arch/vax/mscp/mscp_disk.c @@ -0,0 +1,916 @@ +/* $NetBSD: mscp_disk.c,v 1.1 1996/07/01 20:41:34 ragge Exp $ */ +/* + * Copyright (c) 1996 Ludd, University of Lule}, Sweden. + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)uda.c 7.32 (Berkeley) 2/13/91 + */ + +/* + * RA disk device driver + */ + +/* + * TODO + * write bad block forwarding code + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* + * Drive status, per drive + */ +struct ra_softc { + struct device ra_dev; /* Autoconf struct */ + struct disk ra_disk; + struct buf ra_buf; /* per-drive buffer */ + int ra_state; /* open/closed state */ + u_long ra_mediaid; /* media id */ + int ra_hwunit; /* Hardware unit number */ + int ra_havelabel; /* true if we have a label */ + int ra_wlabel; /* label sector is currently writable */ +}; + +void radgram __P((struct device *, struct mscp *)); +void raiodone __P((struct device *, struct buf *)); +int raonline __P((struct device *, struct mscp *)); +int ragotstatus __P((struct device *, struct mscp *)); +void rareplace __P((struct device *, struct mscp *)); +int raioerror __P((struct device *, struct mscp *, struct buf *)); +void rafillin __P((struct buf *, struct mscp *)); +void rabb __P((struct device *, struct mscp *, struct buf *)); +int raopen __P((dev_t, int, int, struct proc *)); +int raclose __P((dev_t, int, int, struct proc *)); +void rastrategy __P((struct buf *)); +void rastrat1 __P((struct buf *)); +int raread __P((dev_t, struct uio *)); +int rawrite __P((dev_t, struct uio *)); +int raioctl __P((dev_t, int, caddr_t, int, struct proc *)); +int radump __P((dev_t, daddr_t, caddr_t, size_t)); +int rasize __P((dev_t)); + +struct mscp_device ra_device = { + radgram, + raiodone, + raonline, + ragotstatus, + rareplace, + raioerror, + rabb, + rafillin, +}; + +/* + * Device to unit number and partition and back + */ +#define UNITSHIFT 3 +#define UNITMASK 7 +#define raunit(dev) (minor(dev) >> UNITSHIFT) +#define rapart(dev) (minor(dev) & UNITMASK) +#define raminor(u, p) (((u) << UNITSHIFT) | (p)) + + +int ramatch __P((struct device *, void *, void *)); +void raattach __P((struct device *, struct device *, void *)); + +struct cfdriver ra_cd = { + NULL, "ra", DV_DULL +}; + +struct cfattach ra_ca = { + sizeof(struct ra_softc), ramatch, raattach +}; + +/* + * Software state, per drive + */ +#define RA_OFFLINE 0 +#define RA_WANTOPEN 1 +#define RA_ONLINE 3 + +/* + * More driver definitions, for generic MSCP code. + */ +struct device **ra_dp; +extern int cold; + +int +ramatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + struct mscp *mp = aux; + + if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != mp->mscp_unit) + return 0; + return 1; +} + +/* + * The attach routine only checks and prints drive type. + * Bringing the disk online is done when the disk is accessed + * the first time. + */ +void +raattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct ra_softc *ra = (void *)self; + struct drive_attach_args *da = aux; + struct mscp *mp = da->da_mp; + struct disklabel *dl; + int d = mp->mscp_guse.guse_mediaid; + + ra->ra_state = RA_OFFLINE; + ra->ra_havelabel = 0; + ra->ra_hwunit = mp->mscp_unit; + ra_dp[mp->mscp_unit] = self; + disk_attach((struct disk *)&ra->ra_disk); + bzero((void *)&ra->ra_buf, sizeof(struct buf)); + + /* Fill in what we know. The actual size is gotten later */ + dl = ra->ra_disk.dk_label; + + dl->d_secsize = DEV_BSIZE; + dl->d_nsectors = mp->mscp_guse.guse_nspt; + dl->d_ntracks = mp->mscp_guse.guse_ngpc; + dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks; + + ra->ra_mediaid = mp->mscp_guse.guse_mediaid; + + printf(" drive %d: %c%c", mp->mscp_unit, (((d >> 17) & 037) + '@'), + (((d >> 12) & 037) + '@')); + if (((d>>7)& 037) == 0) + printf("%d\n", (d & 127)); + else + printf("%c%d\n", (((d >> 12) & 037) + '@'), (d & 127)); +} + +/* + * (Try to) put the drive online. This is done the first time the + * drive is opened, or if it har fallen offline. + */ +int +ra_putonline(ra) + struct ra_softc *ra; +{ + struct mscp *mp; + struct mscp_softc *mi = (struct mscp_softc *)ra->ra_dev.dv_parent; + struct disklabel *dl; + volatile int i; + char *msg; + int timo; + + dl = ra->ra_disk.dk_label; + + ra->ra_state = RA_WANTOPEN; + mp = mscp_getcp(mi, MSCP_WAIT); + mp->mscp_opcode = M_OP_ONLINE; + mp->mscp_unit = ra->ra_hwunit; + mp->mscp_cmdref = (long)&ra->ra_state; + *mp->mscp_addr |= MSCP_OWN | MSCP_INT; + + /* Poll away */ + i = *mi->mi_ip; + if (tsleep(&ra->ra_state, PRIBIO, "raonline", 100 * hz)) + return MSCP_FAILED; + + + dl->d_partitions[0].p_size = dl->d_partitions[2].p_size = + dl->d_secperunit; + dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0; + + printf("%s", ra->ra_dev.dv_xname); + if ((msg = readdisklabel(raminor(ra->ra_dev.dv_unit, 0), + rastrategy, dl, NULL)) != NULL) + printf(": %s", msg); + else + ra->ra_havelabel = 1; + ra->ra_state == RA_ONLINE; + + printf(": size %d sectors\n", dl->d_secperunit); + + return MSCP_DONE; +} +/* + * Open a drive. + */ +/*ARGSUSED*/ +int +raopen(dev, flag, fmt, p) + dev_t dev; + int flag, fmt; + struct proc *p; +{ + register struct disklabel *lp; + register struct partition *pp; + register struct ra_softc *ra; + int s, i, part, unit, mask, error = 0; + daddr_t start, end; + + /* + * Make sure this is a reasonable open request. + */ + unit = raunit(dev); + if (unit >= ra_cd.cd_ndevs) + return ENXIO; + ra = ra_cd.cd_devs[unit]; + if (ra == 0) + return ENXIO; + + /* + * If this is the first open; we must first try to put + * the disk online (and read the label). + */ + if (ra->ra_state == RA_OFFLINE) + if (ra_putonline(ra) == MSCP_FAILED) + return EIO; + + part = raunit(dev); + if (part >= ra->ra_disk.dk_label->d_npartitions) + return ENXIO; + + /* + * Wait for the state to settle + */ +#if notyet + while (ra->ra_state != RA_ONLINE) + if ((error = tsleep((caddr_t)ra, (PZERO + 1) | PCATCH, + devopn, 0))) { + splx(s); + return (error); + } +#endif + + mask = 1 << part; + + switch (fmt) { + case S_IFCHR: + ra->ra_disk.dk_copenmask |= mask; + break; + case S_IFBLK: + ra->ra_disk.dk_bopenmask |= mask; + break; + } + ra->ra_disk.dk_openmask |= mask; + return 0; +} + +/* ARGSUSED */ +int +raclose(dev, flags, fmt, p) + dev_t dev; + int flags, fmt; + struct proc *p; +{ + register int unit = raunit(dev); + register struct ra_softc *ra = ra_cd.cd_devs[unit]; + int s, mask = (1 << rapart(dev)); + + switch (fmt) { + case S_IFCHR: + ra->ra_disk.dk_copenmask &= ~mask; + break; + case S_IFBLK: + ra->ra_disk.dk_bopenmask &= ~mask; + break; + } + ra->ra_disk.dk_openmask = + ra->ra_disk.dk_copenmask | ra->ra_disk.dk_bopenmask; + + /* + * Should wait for I/O to complete on this partition even if + * others are open, but wait for work on blkflush(). + */ +#if 0 + if (ra->ra_openpart == 0) { + s = splbio(); + while (udautab[unit].b_actf) + sleep((caddr_t)&udautab[unit], PZERO - 1); + splx(s); + ra->ra_state = CLOSED; + ra->ra_wlabel = 0; + } +#endif + return (0); +} + +/* + * Queue a transfer request, and if possible, hand it to the controller. + * + * This routine is broken into two so that the internal version + * udastrat1() can be called by the (nonexistent, as yet) bad block + * revectoring routine. + */ +void +rastrategy(bp) + register struct buf *bp; +{ + register int unit; + register struct ra_softc *ra; + struct partition *pp; + int p; + daddr_t sz, maxsz; + /* + * Make sure this is a reasonable drive to use. + */ + unit = raunit(bp->b_dev); + if (unit > ra_cd.cd_ndevs || (ra = ra_cd.cd_devs[unit]) == NULL) { + bp->b_error = ENXIO; + goto bad; + } + /* + * If drive is open `raw' or reading label, let it at it. + */ + if (ra->ra_state < RA_ONLINE) { + mscp_strategy(bp, &ra->ra_buf, ra->ra_dev.dv_parent); + return; + } + p = rapart(bp->b_dev); + + /* + * Determine the size of the transfer, and make sure it is + * within the boundaries of the partition. + */ + if (bounds_check_with_label(bp, ra->ra_disk.dk_label, ra->ra_wlabel) + <= 0) { + bp->b_resid = bp->b_bcount; + biodone(bp); + } + + mscp_strategy(bp, &ra->ra_buf, ra->ra_dev.dv_parent); + return; + +bad: + bp->b_flags |= B_ERROR; + biodone(bp); +} + +int +raread(dev, uio) + dev_t dev; + struct uio *uio; +{ + + return (physio(rastrategy, NULL, dev, B_READ, minphys, uio)); +} + +int +rawrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + + return (physio(rastrategy, NULL, dev, B_WRITE, minphys, uio)); +} + +void +raiodone(usc, bp) + struct device *usc; + struct buf *bp; +{ + struct ra_softc *ra = (void *)usc; + + biodone(bp); +} + +/* + * Fill in disk addresses in a mscp packet waiting for transfer. + */ +void +rafillin(bp, mp) + struct buf *bp; + struct mscp *mp; +{ + int unit = raunit(bp->b_dev); + int part = rapart(bp->b_dev); + struct ra_softc *ra = ra_cd.cd_devs[unit]; + struct disklabel *lp = ra->ra_disk.dk_label; + + /* XXX more checks needed */ + mp->mscp_unit = ra->ra_hwunit; + mp->mscp_seq.seq_lbn = bp->b_blkno + lp->d_partitions[part].p_offset; + mp->mscp_seq.seq_bytecount = bp->b_bcount; +} + +/* + * Handle an error datagram. + * This can come from an unconfigured drive as well. + */ +void +radgram(usc, mp) + struct device *usc; + struct mscp *mp; +{ + mscp_decodeerror(usc == NULL ? "unconf ra" : usc->dv_xname, mp); + /* + * SDI status information bytes 10 and 11 are the microprocessor + * error code and front panel code respectively. These vary per + * drive type and are printed purely for field service information. + */ + if (mp->mscp_format == M_FM_SDI) + printf("\tsdi uproc error code 0x%x, front panel code 0x%x\n", + mp->mscp_erd.erd_sdistat[10], + mp->mscp_erd.erd_sdistat[11]); +} + +/* + * A drive came on line. Check its type and size. Return DONE if + * we think the drive is truly on line. In any case, awaken anyone + * sleeping on the drive on-line-ness. + */ +int +raonline(usc, mp) + struct device *usc; + struct mscp *mp; +{ + register struct ra_softc *ra = (void *)usc; + struct disklabel *dl; + + wakeup((caddr_t)&ra->ra_state); + if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) { + printf("%s: attempt to bring on line failed: ", + ra->ra_dev.dv_xname); + mscp_printevent(mp); + ra->ra_state = RA_OFFLINE; + return (MSCP_FAILED); + } + + /* + * Fill in the rest of disk size. + */ + ra->ra_state = RA_WANTOPEN; + dl = ra->ra_disk.dk_label; + dl->d_secperunit = (daddr_t)mp->mscp_onle.onle_unitsize; + dl->d_ncylinders = dl->d_secperunit/dl->d_secpercyl; + + return (MSCP_DONE); +} + +/* + * We got some (configured) unit's status. Return DONE if it succeeded. + */ +int +ragotstatus(usc, mp) + register struct device *usc; + register struct mscp *mp; +{ +#if 0 + if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) { + printf("uda%d: attempt to get status for ra%d failed: ", + ui->ui_ctlr, ui->ui_unit); + mscp_printevent(mp); + return (MSCP_FAILED); + } + /* record for (future) bad block forwarding and whatever else */ + uda_rasave(ui->ui_unit, mp, 1); +#endif + return (MSCP_DONE); +} + +/* + * A transfer failed. We get a chance to fix or restart it. + * Need to write the bad block forwaring code first.... + */ +/*ARGSUSED*/ +int +raioerror(usc, mp, bp) + register struct device *usc; + register struct mscp *mp; + struct buf *bp; +{ +#if 0 + if (mp->mscp_flags & M_EF_BBLKR) { + /* + * A bad block report. Eventually we will + * restart this transfer, but for now, just + * log it and give up. + */ + log(LOG_ERR, "ra%d: bad block report: %d%s\n", + ui->ui_unit, (int)mp->mscp_seq.seq_lbn, + mp->mscp_flags & M_EF_BBLKU ? " + others" : ""); + } else { + /* + * What the heck IS a `serious exception' anyway? + * IT SURE WOULD BE NICE IF DEC SOLD DOCUMENTATION + * FOR THEIR OWN CONTROLLERS. + */ + if (mp->mscp_flags & M_EF_SEREX) + log(LOG_ERR, "ra%d: serious exception reported\n", + ui->ui_unit); + } +#endif + return (MSCP_FAILED); +} + +/* + * A replace operation finished. + */ +/*ARGSUSED*/ +void +rareplace(usc, mp) + struct device *usc; + struct mscp *mp; +{ + + panic("udareplace"); +} + +/* + * A bad block related operation finished. + */ +/*ARGSUSED*/ +void +rabb(usc, mp, bp) + struct device *usc; + struct mscp *mp; + struct buf *bp; +{ + + panic("udabb"); +} + + +/* + * I/O controls. + */ +int +raioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + register int unit = raunit(dev); + register struct disklabel *lp; + register struct ra_softc *ra = ra_cd.cd_devs[unit]; + int error = 0; + + lp = ra->ra_disk.dk_label; + + switch (cmd) { + + case DIOCGDINFO: + bcopy(lp, data, sizeof (struct disklabel)); + break; + + case DIOCGPART: + ((struct partinfo *)data)->disklab = lp; + ((struct partinfo *)data)->part = + &lp->d_partitions[rapart(dev)]; + break; + + case DIOCSDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + else + error = setdisklabel(lp, (struct disklabel *)data,0,0); + break; + + case DIOCWLABEL: + if ((flag & FWRITE) == 0) + error = EBADF; + else + ra->ra_wlabel = 1; + break; + + case DIOCWDINFO: + if ((flag & FWRITE) == 0) + error = EBADF; + else { + ra->ra_wlabel = 1; + error = writedisklabel(dev, rastrategy, lp,0); + ra->ra_wlabel = 0; + } + break; + + default: + error = ENOTTY; + break; + } + return (error); +} + +#if 0 +/* + * Do a panic dump. We set up the controller for one command packet + * and one response packet, for which we use `struct uda1'. + */ +struct uda1 { + struct uda1ca uda1_ca; /* communications area */ + struct mscp uda1_rsp; /* response packet */ + struct mscp uda1_cmd; /* command packet */ +} uda1; +#endif + +#define DBSIZE 32 /* dump 16K at a time */ + +int +radump(dev, blkno, va, size) + dev_t dev; + daddr_t blkno; + caddr_t va; + size_t size; +{ +#if 0 + struct udadevice *udaddr; + struct uda1 *ud_ubaddr; + char *start; + int num, blk, unit, maxsz, blkoff, reg; + struct partition *pp; + struct uba_regs *uba; + struct uba_device *ui; + struct uda1 *ud; + struct pte *io; + int i; + + /* + * Make sure the device is a reasonable place on which to dump. + */ + unit = udaunit(dev); + if (unit >= NRA) + return (ENXIO); +#define phys(cast, addr) ((cast) ((int)addr & 0x7fffffff)) + ui = phys(struct uba_device *, udadinfo[unit]); + if (ui == NULL || ui->ui_alive == 0) + return (ENXIO); + + /* + * Find and initialise the UBA; get the physical address of the + * device registers, and of communications area and command and + * response packet. + */ + uba = phys(struct uba_softc *, ui->ui_hd)->uh_physuba; + ubainit(ui->ui_hd); + udaddr = (struct udadevice *)ui->ui_physaddr; + ud = phys(struct uda1 *, &uda1); + /* + * Map the ca+packets into Unibus I/O space so the UDA50 can get + * at them. Use the registers at the end of the Unibus map (since + * we will use the registers at the beginning to map the memory + * we are dumping). + */ + num = btoc(sizeof(struct uda1)) + 1; + reg = NUBMREG - num; + io = (void *)&uba->uba_map[reg]; + for (i = 0; i < num; i++) + *(int *)io++ = UBAMR_MRV | (btop(ud) + i); + ud_ubaddr = (struct uda1 *)(((int)ud & PGOFSET) | (reg << 9)); + + /* + * Initialise the controller, with one command and one response + * packet. + */ + udaddr->udaip = 0; + if (udadumpwait(udaddr, UDA_STEP1)) + return (EFAULT); + udaddr->udasa = UDA_ERR; + if (udadumpwait(udaddr, UDA_STEP2)) + return (EFAULT); + udaddr->udasa = (int)&ud_ubaddr->uda1_ca.ca_rspdsc; + if (udadumpwait(udaddr, UDA_STEP3)) + return (EFAULT); + udaddr->udasa = ((int)&ud_ubaddr->uda1_ca.ca_rspdsc) >> 16; + if (udadumpwait(udaddr, UDA_STEP4)) + return (EFAULT); + ((struct uda_softc *)uda_cd.cd_devs[ui->ui_ctlr])->sc_micro = udaddr->udasa & 0xff; + udaddr->udasa = UDA_GO; + + /* + * Set up the command and response descriptor, then set the + * controller characteristics and bring the drive on line. + * Note that all uninitialised locations in uda1_cmd are zero. + */ + ud->uda1_ca.ca_rspdsc = (long)&ud_ubaddr->uda1_rsp.mscp_cmdref; + ud->uda1_ca.ca_cmddsc = (long)&ud_ubaddr->uda1_cmd.mscp_cmdref; + /* ud->uda1_cmd.mscp_sccc.sccc_ctlrflags = 0; */ + /* ud->uda1_cmd.mscp_sccc.sccc_version = 0; */ + if (udadumpcmd(M_OP_SETCTLRC, ud, ui)) + return (EFAULT); + ud->uda1_cmd.mscp_unit = ui->ui_slave; + if (udadumpcmd(M_OP_ONLINE, ud, ui)) + return (EFAULT); + + pp = phys(struct partition *, + &udalabel[unit].d_partitions[udapart(dev)]); + maxsz = pp->p_size; + blkoff = pp->p_offset; + + /* + * Dump all of physical memory, or as much as will fit in the + * space provided. + */ + start = 0; + printf("Dumpar {r inte implementerade {n :) \n"); + asm("halt"); +/* num = maxfree; */ + if (dumplo + num >= maxsz) + num = maxsz - dumplo; + blkoff += dumplo; + + /* + * Write out memory, DBSIZE pages at a time. + * N.B.: this code depends on the fact that the sector + * size == the page size. + */ + while (num > 0) { + blk = num > DBSIZE ? DBSIZE : num; + io = (void *)uba->uba_map; + /* + * Map in the pages to write, leaving an invalid entry + * at the end to guard against wild Unibus transfers. + * Then do the write. + */ + for (i = 0; i < blk; i++) + *(int *)io++ = UBAMR_MRV | (btop(start) + i); + *(int *)io = 0; + ud->uda1_cmd.mscp_unit = ui->ui_slave; + ud->uda1_cmd.mscp_seq.seq_lbn = btop(start) + blkoff; + ud->uda1_cmd.mscp_seq.seq_bytecount = blk << PGSHIFT; + if (udadumpcmd(M_OP_WRITE, ud, ui)) + return (EIO); + start += blk << PGSHIFT; + num -= blk; + } + return (0); /* made it! */ +} + +/* + * Wait for some of the bits in `bits' to come on. If the error bit + * comes on, or ten seconds pass without response, return true (error). + */ +int +udadumpwait(udaddr, bits) + struct udadevice *udaddr; + register int bits; +{ + register int timo = todr() + 1000; + + while ((udaddr->udasa & bits) == 0) { + if (udaddr->udasa & UDA_ERR) { + printf("udasa=%b\ndump ", udaddr->udasa, udasr_bits); + return (1); + } + if (todr() >= timo) { + printf("timeout\ndump "); + return (1); + } + } + return (0); +} + +/* + * Feed a command to the UDA50, wait for its response, and return + * true iff something went wrong. + */ +int +udadumpcmd(op, ud, ui) + int op; + struct uda1 *ud; + struct uba_device *ui; +{ + volatile struct udadevice *udaddr; + volatile int n; +#define mp (&ud->uda1_rsp) + + udaddr = (struct udadevice *)ui->ui_physaddr; + ud->uda1_cmd.mscp_opcode = op; + ud->uda1_cmd.mscp_msglen = MSCP_MSGLEN; + ud->uda1_rsp.mscp_msglen = MSCP_MSGLEN; + ud->uda1_ca.ca_rspdsc |= MSCP_OWN | MSCP_INT; + ud->uda1_ca.ca_cmddsc |= MSCP_OWN | MSCP_INT; + if (udaddr->udasa & UDA_ERR) { + printf("udasa=%b\ndump ", udaddr->udasa, udasr_bits); + return (1); + } + n = udaddr->udaip; + n = todr() + 1000; + for (;;) { + if (todr() > n) { + printf("timeout\ndump "); + return (1); + } + if (ud->uda1_ca.ca_cmdint) + ud->uda1_ca.ca_cmdint = 0; + if (ud->uda1_ca.ca_rspint == 0) + continue; + ud->uda1_ca.ca_rspint = 0; + if (mp->mscp_opcode == (op | M_OP_END)) + break; + printf("\n"); + switch (MSCP_MSGTYPE(mp->mscp_msgtc)) { + + case MSCPT_SEQ: + printf("sequential"); + break; + + case MSCPT_DATAGRAM: + mscp_decodeerror("uda", ui->ui_ctlr, mp); + printf("datagram"); + break; + + case MSCPT_CREDITS: + printf("credits"); + break; + + case MSCPT_MAINTENANCE: + printf("maintenance"); + break; + + default: + printf("unknown (type 0x%x)", + MSCP_MSGTYPE(mp->mscp_msgtc)); + break; + } + printf(" ignored\ndump "); + ud->uda1_ca.ca_rspdsc |= MSCP_OWN | MSCP_INT; + } + if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) { + printf("error: op 0x%x => 0x%x status 0x%x\ndump ", op, + mp->mscp_opcode, mp->mscp_status); + return (1); + } +#endif + return (0); +#undef mp +} + +/* + * Return the size of a partition, if known, or -1 if not. + */ +int +rasize(dev) + dev_t dev; +{ + register int unit = raunit(dev); + struct ra_softc *ra; + + if (unit >= ra_cd.cd_ndevs || ra_cd.cd_devs[unit] == 0) + return -1; + + ra = ra_cd.cd_devs[unit]; + + if (ra->ra_state == RA_OFFLINE) + if (ra_putonline(ra) == MSCP_FAILED) + return -1; + + return ra->ra_disk.dk_label->d_partitions[rapart(dev)].p_size; +} diff --git a/sys/arch/vax/mscp/mscp_subr.c b/sys/arch/vax/mscp/mscp_subr.c new file mode 100644 index 000000000000..783a2c9e6fc6 --- /dev/null +++ b/sys/arch/vax/mscp/mscp_subr.c @@ -0,0 +1,838 @@ +/* $NetBSD: mscp_subr.c,v 1.1 1996/07/01 20:41:35 ragge Exp $ */ +/* + * Copyright (c) 1996 Ludd, University of Lule}, Sweden. + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)mscp.c 7.5 (Berkeley) 12/16/90 + */ + +/* + * MSCP generic driver routines + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "ra.h" +#define NMT 0 /* XXX */ + +#define MAXMSCPDEV 255 /* Can there be more? */ +#define b_forw b_hash.le_next + +void mscp_hexdump __P((struct mscp *)); +int mscp_match __P((struct device *, void *, void *)); +void mscp_attach __P((struct device *, struct device *, void *)); +void mscp_start __P((struct mscp_softc *)); +int mscp_init __P((struct mscp_softc *)); +void mscp_initds __P((struct mscp_softc *)); +int mscp_waitstep __P((struct mscp_softc *, int, int)); + +struct cfattach mscpbus_ca = { + sizeof(struct mscp_softc), mscp_match, mscp_attach +}; + +struct cfdriver mscpbus_cd = { + NULL, "mscpbus", DV_DULL +}; + +struct mscp slavereply; + +/* + * This function is for delay during init. Some MSCP clone card (Dilog) + * can't handle fast read from its registers, and therefore need + * a delay between them. + */ + +#define DELAYTEN 1000 +int +mscp_waitstep(mi, mask, result) + struct mscp_softc *mi; + int mask, result; +{ + int status = 1; + + if ((*mi->mi_sa & mask) != result) { + volatile int count = 0; + while ((*mi->mi_sa & mask) != result) { + DELAY(10000); + count += 1; + if (count > DELAYTEN) + break; + } + if (count > DELAYTEN) + status = 0; + } + return status; +} + +int +mscp_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct mscp_attach_args *ma = aux; + +#if NRA + if (ma->ma_type & MSCPBUS_DISK) + return 1; +#endif +#if NMT + if (ma->ma_type & MSCPBUS_TAPE) + return 1; +#endif + return 0; +}; + +void +mscp_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct mscp_attach_args *ma = aux; + struct mscp_softc *mi = (void *)self; + volatile struct mscp *mp; + volatile int i; + int timeout, next = 0; + + mi->mi_mc = ma->ma_mc; + mi->mi_me = NULL; + mi->mi_cbuf = ma->ma_cbuf; + mi->mi_type = ma->ma_type; + mi->mi_uuda = ma->ma_uuda; + mi->mi_uda = ma->ma_uda; + mi->mi_ip = ma->ma_ip; + mi->mi_sa = ma->ma_sa; + mi->mi_sw = ma->ma_sw; + mi->mi_ivec = ma->ma_ivec; + *ma->ma_softc = mi; + /* + * Go out to init the bus, so that we can give commands + * to its devices. + */ + mi->mi_cmd.mri_size = NCMD; + mi->mi_cmd.mri_desc = mi->mi_uda->mp_ca.ca_cmddsc; + mi->mi_cmd.mri_ring = mi->mi_uda->mp_cmd; + mi->mi_rsp.mri_size = NRSP; + mi->mi_rsp.mri_desc = mi->mi_uda->mp_ca.ca_rspdsc; + mi->mi_rsp.mri_ring = mi->mi_uda->mp_rsp; + mi->mi_actf = (void *)&mi->mi_actf; /* Circular wait queue */ + mi->mi_actb = (void *)&mi->mi_actf; + + if (mscp_init(mi)) { + printf("%s: can't init, controller hung\n", + mi->mi_dev.dv_xname); + return; + } + +#if NRA + if (ma->ma_type & MSCPBUS_DISK) { + extern struct mscp_device ra_device; + extern struct device **ra_dp; + + mi->mi_me = &ra_device; + if (ra_dp == NULL) { + ra_dp = (struct device **)malloc(MAXMSCPDEV * + sizeof(void *), M_DEVBUF, M_NOWAIT); + bzero(ra_dp, MAXMSCPDEV * sizeof(void *)); + } + mi->mi_dp = ra_dp; + } +#endif +#if NMT + if (ma->ma_type & MSCPBUS_TAPE) { + extern struct mscp_device mt_device; + extern struct device **mt_dp; + + mi->mi_me = &mt_device; + if (mt_dp == NULL) { + mt_dp = (struct device **)malloc(MAXMSCPDEV * + sizeof(void *), M_DEVBUF, M_NOWAIT); + bzero(mt_dp, MAXMSCPDEV * sizeof(void *)); + } + mi->mi_dp = mt_dp; + } +#endif + /* + * Go out and search for sub-units on this MSCP bus, + * and call config_found for each found. + */ +findunit: + mp = mscp_getcp(mi, MSCP_DONTWAIT); + if (mp == NULL) + panic("mscpattach: no packets"); + mp->mscp_opcode = M_OP_GETUNITST; + mp->mscp_unit = next; + mp->mscp_modifier = M_GUM_NEXTUNIT; + *mp->mscp_addr |= MSCP_OWN | MSCP_INT; + slavereply.mscp_opcode = 0; + + i = *mi->mi_ip; /* Kick off polling */ + mp = &slavereply; + timeout = 1000; + while (timeout-- > 0) { + DELAY(10000); + if (mp->mscp_opcode) + goto gotit; + } + printf("%s: no response to Get Unit Status request\n", + mi->mi_dev.dv_xname); + return; + +gotit: /* + * Got a slave response. If the unit is there, use it. + */ + switch (mp->mscp_status & M_ST_MASK) { + + case M_ST_SUCCESS: /* worked */ + case M_ST_AVAILABLE: /* found another drive */ + break; /* use it */ + + case M_ST_OFFLINE: + /* + * Figure out why it is off line. It may be because + * it is nonexistent, or because it is spun down, or + * for some other reason. + */ + switch (mp->mscp_status & ~M_ST_MASK) { + + case M_OFFLINE_UNKNOWN: + /* + * No such drive, and there are none with + * higher unit numbers either, if we are + * using M_GUM_NEXTUNIT. + */ + printf("offl unknown \n"); + return; + + case M_OFFLINE_UNMOUNTED: + /* + * The drive is not spun up. Use it anyway. + * + * N.B.: this seems to be a common occurrance + * after a power failure. The first attempt + * to bring it on line seems to spin it up + * (and thus takes several minutes). Perhaps + * we should note here that the on-line may + * take longer than usual. + */ + break; + + default: + /* + * In service, or something else equally unusable. + */ + printf("%s: unit %d off line: ", mi->mi_dev.dv_xname, + mp->mscp_unit); + mscp_printevent((struct mscp *)mp); + next++; + goto findunit; + } + break; + + default: + printf("%s: unable to get unit status: ", mi->mi_dev.dv_xname); + mscp_printevent((struct mscp *)mp); + return; + } + + /* + * If we get a lower number, we have circulated around all + * devices and are finished, otherwise try to find next unit. + */ + if (mp->mscp_unit < next) + return; + + next = mp->mscp_unit + 1; + goto findunit; +} + + +/* + * The ctlr gets initialised, normally after boot but may also be + * done if the ctlr gets in an unknown state. Returns 1 if init + * fails, 0 otherwise. + */ +int +mscp_init(mi) + struct mscp_softc *mi; +{ + struct mscp *mp; + volatile int i; + int status, count; + + /* + * While we are thinking about it, reset the next command + * and response indicies. + */ + mi->mi_cmd.mri_next = 0; + mi->mi_rsp.mri_next = 0; + + mi->mi_flags |= MSC_IGNOREINTR; + *mi->mi_ip = 0; /* Kick off */ + status = mscp_waitstep(mi, MP_STEP1, MP_STEP1);/* Wait to it wakes up */ + if (status == 0) + return 1; /* Init failed */ + if (*mi->mi_sa & MP_ERR) { + (*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0); + return 1; + } + + /* step1 */ + *mi->mi_sw = MP_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) | + MP_IE | (mi->mi_ivec >> 2); + status = mscp_waitstep(mi, STEP1MASK, STEP1GOOD); + if (status == 0) { + (*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0); + return 1; + } + + /* step2 */ + *mi->mi_sw = (int)&mi->mi_uuda->mp_ca.ca_rspdsc[0] | + (cpunumber == VAX_780 || cpunumber == VAX_8600 ? MP_PI : 0); + status = mscp_waitstep(mi, STEP2MASK, STEP2GOOD(mi->mi_ivec >> 2)); + if (status == 0) { + (*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0); + return 1; + } + + /* step3 */ + *mi->mi_sw = ((int)&mi->mi_uuda->mp_ca.ca_rspdsc[0]) >> 16; + status = mscp_waitstep(mi, STEP3MASK, STEP3GOOD); + if (status == 0) { + (*mi->mi_mc->mc_saerror)(mi->mi_dev.dv_parent, 0); + return 1; + } + i = *mi->mi_sa & 0377; + printf(": version %d model %d\n", i & 15, i >> 4); + +#define BURST 4 /* XXX */ + if (mi->mi_type & MSCPBUS_UDA) { + *mi->mi_sw = MP_GO | (BURST - 1) << 2; + *mi->mi_sw = MP_GO; + printf("%s: DMA burst size set to %d\n", + mi->mi_dev.dv_xname, BURST); + } + + mscp_initds(mi); + mi->mi_flags &= ~MSC_IGNOREINTR; + + /* + * Set up all necessary info in the bus softc struct, get a + * mscp packet and set characteristics for this controller. + */ + mi->mi_credits = MSCP_MINCREDITS + 1; + mp = mscp_getcp(mi, MSCP_DONTWAIT); + if (mp == NULL) /* `cannot happen' */ + panic("mscpbus: no packets"); + mi->mi_credits = 0; + mp->mscp_opcode = M_OP_SETCTLRC; + mp->mscp_unit = 0; + mp->mscp_sccc.sccc_ctlrflags = M_CF_ATTN | M_CF_MISC | M_CF_THIS; + *mp->mscp_addr |= MSCP_OWN | MSCP_INT; + i = *mi->mi_ip; + + count = 0; + while (count < DELAYTEN) { + if (((volatile)mi->mi_flags & MSC_READY) != 0) + break; + DELAY(10000); + count += 1; + } + if (count == DELAYTEN) { + printf(": couldn't set ctlr characteristics\n"); + return 1; + } + return 0; +} + +/* + * Initialise the various data structures that control the mscp protocol. + */ +void +mscp_initds(mi) + struct mscp_softc *mi; +{ + struct mscp_pack *uud = mi->mi_uuda; + struct mscp_pack *ud = mi->mi_uda; + struct mscp *mp; + int i; + + for (i = 0, mp = ud->mp_rsp; i < NRSP; i++, mp++) { + ud->mp_ca.ca_rspdsc[i] = MSCP_OWN | MSCP_INT | + (long)&uud->mp_rsp[i].mscp_cmdref; + mp->mscp_addr = &ud->mp_ca.ca_rspdsc[i]; + mp->mscp_msglen = MSCP_MSGLEN; + } + for (i = 0, mp = ud->mp_cmd; i < NCMD; i++, mp++) { + ud->mp_ca.ca_cmddsc[i] = MSCP_INT | + (long)&uud->mp_cmd[i].mscp_cmdref; + mp->mscp_addr = &ud->mp_ca.ca_cmddsc[i]; + mp->mscp_msglen = MSCP_MSGLEN; + } +} + +void +mscp_intr(mi) + struct mscp_softc *mi; +{ + struct mscp_pack *ud = mi->mi_uda; + + if (mi->mi_flags & MSC_IGNOREINTR) + return; + /* + * Check for response and command ring transitions. + */ + if (ud->mp_ca.ca_rspint) { + ud->mp_ca.ca_rspint = 0; + mscp_dorsp(mi); + } + if (ud->mp_ca.ca_cmdint) { + ud->mp_ca.ca_cmdint = 0; + MSCP_DOCMD(mi); + } +} + +int +mscp_print(aux, name) + void *aux; + char *name; +{ + return UNCONF; +} + +void +mscp_poll(usc) + struct device *usc; +{ + struct mscp_softc *mi = (void *)usc; + volatile int i; + + i = *mi->mi_ip; +} + +/* + * common strategy routine for all types of MSCP devices. + * bp is the current buf, dp is the drive queue. + */ +void +mscp_strategy(bp, dp, usc) + struct buf *bp, *dp; + struct device *usc; +{ + struct mscp_softc *mi = (void *)usc; + struct mscp *mp; + int j, s = spl6(); + + /* + * Append the buffer to the drive queue, and if it is not + * already there, the drive to the controller queue. (However, + * if the drive queue is marked to be requeued, we must be + * awaiting an on line or get unit status command; in this + * case, leave it off the controller queue.) + */ + if (dp->b_actf == 0) + MSCP_APPEND(dp, mi->mi_cbuf, b_forw); + MSCP_APPEND(bp, dp, b_actf); + + /* + * Ok; we are ready to try to start a xfer. Get a MSCP packet + * and try to start... + */ + if ((mp = mscp_getcp(mi, MSCP_DONTWAIT)) == NULL) { + if (mi->mi_credits > MSCP_MINCREDITS) + printf("%s: command ring too small, can't handle\n", + mi->mi_dev.dv_parent->dv_xname); + panic("mscp_strategy"); + } + + /* + * Set up the MSCP packet and go for it! + */ + mp->mscp_opcode = (bp->b_flags & B_READ) ? M_OP_READ : M_OP_WRITE; + (*mi->mi_me->me_fillin)(bp, mp); + mi->mi_mscp = mp; + + j = (*mi->mi_mc->mc_go)(mi->mi_dev.dv_parent); + if (j == 0) { + printf("Lacking uba resources, fix mscp driver\n"); + panic("mscp_strategy"); + } + + splx(s); +} + +void +mscp_dgo(mi, buffer, info) + struct mscp_softc *mi; + long buffer, info; +{ + volatile int i; + struct mscp *mp; + struct buf *bp, *dp; + + /* + * Fill in the MSCP packet and move the buffer to the + * I/O wait queue. Mark the controller as no longer on + * the resource queue, and remember to initiate polling. + */ + mp = mi->mi_mscp; + mp->mscp_seq.seq_buffer = buffer; + + dp = mi->mi_cbuf->b_forw; /* Get next drive on queue */ + bp = dp->b_actf; /* Get next buffer to play with. */ + dp->b_actf = bp->b_actf; /* Take buffer off drive queue */ + mi->mi_cbuf->b_forw = dp->b_forw; /* Drive off ctlr queue */ + if (dp->b_actf) /* If more xfers, put it back on queue */ + MSCP_APPEND(dp, mi->mi_cbuf, b_forw); + + insque(&bp->b_actf, &mi->mi_actf); + + bp->b_resid = info; + mp->mscp_cmdref = (long) bp; + + *mp->mscp_addr |= MSCP_OWN | MSCP_INT; + i = *mi->mi_ip; +} + +/* + * Dump the entire contents of an MSCP packet in hex. Mainly useful + * for debugging.... + */ +void +mscp_hexdump(mp) + register struct mscp *mp; +{ + register long *p = (long *) mp; + register int i = mp->mscp_msglen; + + if (i > 256) /* sanity */ + i = 256; + i /= sizeof (*p); /* ASSUMES MULTIPLE OF sizeof(long) */ + while (--i >= 0) + printf("0x%x ", (int)*p++); + printf("\n"); +} + + +/* + * MSCP error reporting + */ + +/* + * Messages for the various subcodes. + */ +static char unknown_msg[] = "unknown subcode"; + +/* + * Subcodes for Success (0) + */ +static char *succ_msgs[] = { + "normal", /* 0 */ + "spin down ignored", /* 1 = Spin-Down Ignored */ + "still connected", /* 2 = Still Connected */ + unknown_msg, + "dup. unit #", /* 4 = Duplicate Unit Number */ + unknown_msg, + unknown_msg, + unknown_msg, + "already online", /* 8 = Already Online */ + unknown_msg, + unknown_msg, + unknown_msg, + unknown_msg, + unknown_msg, + unknown_msg, + unknown_msg, + "still online", /* 16 = Still Online */ +}; + +/* + * Subcodes for Invalid Command (1) + */ +static char *icmd_msgs[] = { + "invalid msg length", /* 0 = Invalid Message Length */ +}; + +/* + * Subcodes for Command Aborted (2) + */ +/* none known */ + +/* + * Subcodes for Unit Offline (3) + */ +static char *offl_msgs[] = { + "unknown drive", /* 0 = Unknown, or online to other ctlr */ + "not mounted", /* 1 = Unmounted, or RUN/STOP at STOP */ + "inoperative", /* 2 = Unit Inoperative */ + unknown_msg, + "duplicate", /* 4 = Duplicate Unit Number */ + unknown_msg, + unknown_msg, + unknown_msg, + "in diagnosis", /* 8 = Disabled by FS or diagnostic */ +}; + +/* + * Subcodes for Unit Available (4) + */ +/* none known */ + +/* + * Subcodes for Media Format Error (5) + */ +static char *media_fmt_msgs[] = { + "fct unread - edc", /* 0 = FCT unreadable */ + "invalid sector header",/* 1 = Invalid Sector Header */ + "not 512 sectors", /* 2 = Not 512 Byte Sectors */ + "not formatted", /* 3 = Not Formatted */ + "fct ecc", /* 4 = FCT ECC */ +}; + +/* + * Subcodes for Write Protected (6) + * N.B.: Code 6 subcodes are 7 bits higher than other subcodes + * (i.e., bits 12-15). + */ +static char *wrprot_msgs[] = { + unknown_msg, + "software", /* 1 = Software Write Protect */ + "hardware", /* 2 = Hardware Write Protect */ +}; + +/* + * Subcodes for Compare Error (7) + */ +/* none known */ + +/* + * Subcodes for Data Error (8) + */ +static char *data_msgs[] = { + "forced error", /* 0 = Forced Error (software) */ + unknown_msg, + "header compare", /* 2 = Header Compare Error */ + "sync timeout", /* 3 = Sync Timeout Error */ + unknown_msg, + unknown_msg, + unknown_msg, + "uncorrectable ecc", /* 7 = Uncorrectable ECC */ + "1 symbol ecc", /* 8 = 1 bit ECC */ + "2 symbol ecc", /* 9 = 2 bit ECC */ + "3 symbol ecc", /* 10 = 3 bit ECC */ + "4 symbol ecc", /* 11 = 4 bit ECC */ + "5 symbol ecc", /* 12 = 5 bit ECC */ + "6 symbol ecc", /* 13 = 6 bit ECC */ + "7 symbol ecc", /* 14 = 7 bit ECC */ + "8 symbol ecc", /* 15 = 8 bit ECC */ +}; + +/* + * Subcodes for Host Buffer Access Error (9) + */ +static char *host_buffer_msgs[] = { + unknown_msg, + "odd xfer addr", /* 1 = Odd Transfer Address */ + "odd xfer count", /* 2 = Odd Transfer Count */ + "non-exist. memory", /* 3 = Non-Existent Memory */ + "memory parity", /* 4 = Memory Parity Error */ +}; + +/* + * Subcodes for Controller Error (10) + */ +static char *cntlr_msgs[] = { + unknown_msg, + "serdes overrun", /* 1 = Serialiser/Deserialiser Overrun */ + "edc", /* 2 = Error Detection Code? */ + "inconsistant internal data struct",/* 3 = Internal Error */ +}; + +/* + * Subcodes for Drive Error (11) + */ +static char *drive_msgs[] = { + unknown_msg, + "sdi command timeout", /* 1 = SDI Command Timeout */ + "ctlr detected protocol",/* 2 = Controller Detected Protocol Error */ + "positioner", /* 3 = Positioner Error */ + "lost rd/wr ready", /* 4 = Lost R/W Ready Error */ + "drive clock dropout", /* 5 = Lost Drive Clock */ + "lost recvr ready", /* 6 = Lost Receiver Ready */ + "drive detected error", /* 7 = Drive Error */ + "ctlr detected pulse or parity",/* 8 = Pulse or Parity Error */ +}; + +/* + * The following table correlates message codes with the + * decoding strings. + */ +struct code_decode { + char *cdc_msg; + int cdc_nsubcodes; + char **cdc_submsgs; +} code_decode[] = { +#define SC(m) sizeof (m) / sizeof (m[0]), m + {"success", SC(succ_msgs)}, + {"invalid command", SC(icmd_msgs)}, + {"command aborted", 0, 0}, + {"unit offline", SC(offl_msgs)}, + {"unit available", 0, 0}, + {"media format error", SC(media_fmt_msgs)}, + {"write protected", SC(wrprot_msgs)}, + {"compare error", 0, 0}, + {"data error", SC(data_msgs)}, + {"host buffer access error", SC(host_buffer_msgs)}, + {"controller error", SC(cntlr_msgs)}, + {"drive error", SC(drive_msgs)}, +#undef SC +}; + +/* + * Print the decoded error event from an MSCP error datagram. + */ +void +mscp_printevent(mp) + struct mscp *mp; +{ + register int event = mp->mscp_event; + register struct code_decode *cdc; + int c, sc; + char *cm, *scm; + + /* + * The code is the lower six bits of the event number (aka + * status). If that is 6 (write protect), the subcode is in + * bits 12-15; otherwise, it is in bits 5-11. + * I WONDER WHAT THE OTHER BITS ARE FOR. IT SURE WOULD BE + * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS. + */ + c = event & M_ST_MASK; + sc = (c != 6 ? event >> 5 : event >> 12) & 0x7ff; + if (c >= sizeof code_decode / sizeof code_decode[0]) + cm = "- unknown code", scm = "??"; + else { + cdc = &code_decode[c]; + cm = cdc->cdc_msg; + if (sc >= cdc->cdc_nsubcodes) + scm = unknown_msg; + else + scm = cdc->cdc_submsgs[sc]; + } + printf(" %s (%s) (code %d, subcode %d)\n", cm, scm, c, sc); +} + +/* + * Print the code and logical block number for an error packet. + * THIS IS PROBABLY PECULIAR TO DISK DRIVES. IT SURE WOULD BE + * NICE IF DEC SOLD DOCUMENTATION FOR THEIR OWN CONTROLLERS. + */ +void +mscp_decodeerror(name, mp) + char *name; + register struct mscp *mp; +{ + /* + * For bad blocks, mp->mscp_erd.erd_hdr identifies a code and + * the logical block number. Code 0 is a regular block; code 6 + * is a replacement block. The remaining codes are currently + * undefined. The code is in the upper four bits of the header + * (bits 0-27 are the lbn). + */ + int issoft = mp->mscp_flags & (M_LF_SUCC | M_LF_CONT); + static char *codemsg[16] = { + "lbn", "code 1", "code 2", "code 3", + "code 4", "code 5", "rbn", "code 7", + "code 8", "code 9", "code 10", "code 11", + "code 12", "code 13", "code 14", "code 15" + }; +#define BADCODE(h) (codemsg[(unsigned)(h) >> 28]) +#define BADLBN(h) ((h) & 0xfffffff) + + printf("%s: drive %d %s error datagram%s:", name, mp->mscp_unit, + issoft ? "soft" : "hard", + mp->mscp_flags & M_LF_CONT ? " (continuing)" : ""); + switch (mp->mscp_format & 0377) { + + case M_FM_CTLRERR: /* controller error */ + break; + + case M_FM_BUSADDR: /* host memory access error */ + printf(" memory addr 0x%x:", (int)mp->mscp_erd.erd_busaddr); + break; + + case M_FM_DISKTRN: + printf(" unit %d: level %d retry %d, %s %d:", + mp->mscp_unit, + mp->mscp_erd.erd_level, mp->mscp_erd.erd_retry, + BADCODE(mp->mscp_erd.erd_hdr), + (int)BADLBN(mp->mscp_erd.erd_hdr)); + break; + + case M_FM_SDI: + printf(" unit %d: %s %d:", mp->mscp_unit, + BADCODE(mp->mscp_erd.erd_hdr), + (int)BADLBN(mp->mscp_erd.erd_hdr)); + break; + + case M_FM_SMLDSK: + printf(" unit %d: small disk error, cyl %d:", + mp->mscp_unit, mp->mscp_erd.erd_sdecyl); + break; + + default: + printf(" unit %d: unknown error, format 0x%x:", + mp->mscp_unit, mp->mscp_format); + } + mscp_printevent(mp); +#undef BADCODE +#undef BADLBN +} diff --git a/sys/arch/vax/mscp/mscpreg.h b/sys/arch/vax/mscp/mscpreg.h new file mode 100644 index 000000000000..a5c6993a8836 --- /dev/null +++ b/sys/arch/vax/mscp/mscpreg.h @@ -0,0 +1,123 @@ +/* $NetBSD: mscpreg.h,v 1.1 1996/07/01 20:41:37 ragge Exp $ */ +/* + * Copyright (c) 1996 Ludd, University of Lule}, Sweden. + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)udareg.h 7.3 (Berkeley) 5/8/91 + */ + +/* + * NRSPL2 and NCMDL2 control the number of response and command + * packets respectively. They may be any value from 0 to 7, though + * setting them higher than 5 is unlikely to be of any value. + * If you get warnings about your command ring being too small, + * try increasing the values by one. + */ +#define NRSPL2 5 +#define NCMDL2 5 +#define NRSP (1 << NRSPL2) +#define NCMD (1 << NCMDL2) + +/* + * Communication area definition. This seems to be the same for + * all types of MSCP controllers. + */ + +struct mscp_ca { + short ca_xxx1; /* unused */ + char ca_xxx2; /* unused */ + char ca_bdp; /* BDP to purge */ + short ca_cmdint; /* command ring transition flag */ + short ca_rspint; /* response ring transition flag */ + long ca_rspdsc[NRSP];/* response descriptors */ + long ca_cmddsc[NCMD];/* command descriptors */ +}; + +/* + * Simplified routines (e.g., uddump) reprogram the UDA50 for one command + * and one response at a time; uda1ca is like udaca except that it provides + * exactly one command and response descriptor. + */ +struct mscp_1ca { + short ca_xxx1; + char ca_xxx2; + char ca_bdp; + short ca_cmdint; + short ca_rspint; + long ca_rspdsc; + long ca_cmddsc; +}; + +/* + * Combined communications area and MSCP packet pools, per controller. + * NRSP and NCMD must be defined before this struct is used. + */ + +struct mscp_pack { + struct mscp_ca mp_ca; /* communications area */ + struct mscp mp_rsp[NRSP]; /* response packets */ + struct mscp mp_cmd[NCMD]; /* command packets */ +}; + +/* + * Bits in UDA status register during initialisation + */ +#define MP_ERR 0x8000 /* error */ +#define MP_STEP4 0x4000 /* step 4 has started */ +#define MP_STEP3 0x2000 /* step 3 has started */ +#define MP_STEP2 0x1000 /* step 2 has started */ +#define MP_STEP1 0x0800 /* step 1 has started */ +#define MP_NV 0x0400 /* no host settable interrupt vector */ +#define MP_QB 0x0200 /* controller supports Q22 bus */ +#define MP_DI 0x0100 /* controller implements diagnostics */ +#define MP_IE 0x0080 /* interrupt enable */ +#define MP_NCNRMASK 0x003f /* in STEP1, bits 0-2=NCMDL2, 3-5=NRSPL2 */ +#define MP_IVECMASK 0x007f /* in STEP2, bits 0-6 are interruptvec / 4 */ +#define MP_PI 0x0001 /* host requests adapter purge interrupts */ +#define MP_GO 0x0001 /* Go command to ctlr */ + +#define ALLSTEPS (MP_ERR | MP_STEP4 | MP_STEP3 | MP_STEP2 | MP_STEP1) + +#define STEP0MASK (ALLSTEPS | MP_NV) + +#define STEP1MASK (ALLSTEPS | MP_IE | MP_NCNRMASK) +#define STEP1GOOD (MP_STEP2 | MP_IE | (NCMDL2 << 3) | NRSPL2) + +#define STEP2MASK (ALLSTEPS | MP_IE | MP_IVECMASK) +#define STEP2GOOD(iv) (MP_STEP3 | MP_IE | (iv)) + +#define STEP3MASK ALLSTEPS +#define STEP3GOOD MP_STEP4 + diff --git a/sys/arch/vax/mscp/mscpvar.h b/sys/arch/vax/mscp/mscpvar.h new file mode 100644 index 000000000000..d963a4345f90 --- /dev/null +++ b/sys/arch/vax/mscp/mscpvar.h @@ -0,0 +1,267 @@ +/* $NetBSD: mscpvar.h,v 1.1 1996/07/01 20:41:38 ragge Exp $ */ +/* + * Copyright (c) 1996 Ludd, University of Lule}, Sweden. + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * 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. + * + * @(#)mscpvar.h 7.3 (Berkeley) 6/28/90 + */ + +/* + * MSCP generic driver configuration + */ + +/* + * Enabling MSCP_PARANOIA makes the response code perform various checks + * on the hardware. (Right now it verifies only the buffer pointer in + * mscp_cmdref.) + * + * Enabling AVOID_EMULEX_BUG selects an alternative method of identifying + * transfers in progress, which gets around a rather peculiar bug in the + * SC41/MS. Enabling MSCP_PARANOIA instead should work, but will cause + * `extra' Unibus resets. + * + * Either of these flags can simply be included as an `options' line in + * your configuration file. + */ + +/* #define MSCP_PARANOIA */ +/* #define AVOID_EMULEX_BUG */ + +/* + * Ring information, per ring (one each for commands and responses). + */ +struct mscp_ri { + int mri_size; /* ring size */ + int mri_next; /* next (expected|free) */ + long *mri_desc; /* base address of descriptors */ + struct mscp *mri_ring; /* base address of packets */ +}; + +struct mscp_ctlr { + void (*mc_ctlrdone) /* controller operation complete */ + __P((struct device *, int)); + int (*mc_go) /* device-specific start routine */ + __P((struct device *)); + void (*mc_saerror) /* ctlr error handling */ + __P((struct device *, int)); +}; + +struct mscp_device { + void (*me_dgram) /* error datagram */ + __P((struct device *, struct mscp *)); + void (*me_iodone) /* normal I/O is done */ + __P((struct device *, struct buf *)); + int (*me_online) /* drive on line */ + __P((struct device *, struct mscp *)); + int (*me_gotstatus) /* got unit status */ + __P((struct device *, struct mscp *)); + void (*me_replace) /* replace done */ + __P((struct device *, struct mscp *)); + int (*me_ioerr) /* read or write failed */ + __P((struct device *, struct mscp *, struct buf *)); + void (*me_bb) /* B_BAD io done */ + __P((struct device *, struct mscp *, struct buf *)); + void (*me_fillin) /* Fill in mscp info for this drive */ + __P((struct buf *,struct mscp *)); +}; + +/* + * This struct is used when attaching a mscpbus. + */ +struct mscp_attach_args { + struct mscp_ctlr *ma_mc; /* Pointer to ctlr's mscp_ctlr */ + int ma_type; /* disk/tape bus type */ + struct buf *ma_cbuf; /* ctlr's active queue */ + struct mscp_pack *ma_uda; /* comm area virtual */ + struct mscp_pack *ma_uuda; /* comm area on bus */ + struct mscp_softc **ma_softc; /* backpointer to bus softc */ + short *ma_ip; /* initialisation and polling */ + short *ma_sa; /* status & address (read part) */ + short *ma_sw; /* status & address (write part) */ + short ma_ivec; /* Interrupt vector to use */ +}; +#define MSCPBUS_DISK 001 /* Bus is used for disk mounts */ +#define MSCPBUS_TAPE 002 /* Bus is used for tape mounts */ +#define MSCPBUS_UDA 004 /* ctlr is disk on unibus/qbus */ +#define MSCPBUS_KDB 010 /* ctlr is disk on BI */ +#define MSCPBUS_KLE 020 /* ctlr is tape on unibus/qbus */ + +/* + * Used when going for child devices. + */ +struct drive_attach_args { + struct mscp *da_mp; /* this devices response struct */ +}; + +/* + * Return values from functions. + * MSCP_RESTARTED is peculiar to I/O errors. + */ +#define MSCP_DONE 0 /* all ok */ +#define MSCP_FAILED 1 /* no go */ +#define MSCP_RESTARTED 2 /* transfer restarted */ + +/* + * Per device information. + * + * mi_ip is a pointer to the inverting pointers (things that get `ui's + * given unit numbers) FOR THIS CONTROLLER (NOT the whole set!). + * + * b_actf holds a queue of those transfers that were started but have + * not yet finished. Other Unibus drivers do not need this as they hand + * out requests one at a time. MSCP devices, however, take a slew of + * requests and pick their own order to execute them. This means that + * we have to have a place to move transfers that were given to the + * controller, so we can tell those apart from those that have not yet + * been handed out; b_actf is that place. + */ +struct mscp_softc { + struct device mi_dev; /* Autoconf stuff */ + struct mscp_ri mi_cmd; /* MSCP command ring info */ + struct mscp_ri mi_rsp; /* MSCP response ring info */ + short mi_credits; /* transfer credits */ + char mi_wantcmd; /* waiting for command packet */ + char mi_wantcredits; /* waiting for transfer credits */ + struct buf *mi_cbuf; /* this bus active queue (in ctlr) */ + struct buf *mi_actf; /* Pointer to buffers in */ + struct buf *mi_actb; /* circular wait queue */ + struct mscp_ctlr *mi_mc; /* Pointer to parent's mscp_ctlr */ + struct mscp_device *mi_me; /* Pointer to child's mscp_device */ + struct device **mi_dp; /* array of backpointers */ + struct mscp *mi_mscp; + int mi_flags; + struct mscp_pack *mi_uda; /* virtual address */ + struct mscp_pack *mi_uuda; /* (device-specific) address */ + int mi_type; + short mi_ivec; /* Interrupt vector to use */ + volatile short *mi_ip; /* initialisation and polling */ + volatile short *mi_sa; /* status & address (read part) */ + volatile short *mi_sw; /* status & address (write part) */ +}; + +/* mi_flags */ +#define MSC_STARTPOLL 1 +#define MSC_INSTART 2 +#define MSC_IGNOREINTR 4 +#define MSC_READY 8 + +/* + * We have run out of credits when mi_credits is <= MSCP_MINCREDITS. + * It is still possible to issue one command in this case, but it must + * not be a data transfer. E.g., `get command status' or `abort command' + * is legal, while `read' is not. + */ +#define MSCP_MINCREDITS 1 + +/* + * Flags for mscp_getcp(). + */ +#define MSCP_WAIT 1 +#define MSCP_DONTWAIT 0 + + /* get a command packet */ + +/* + * Unit flags + */ +#define UNIT_ONLINE 0x01 /* drive is on line */ +#define UNIT_HAVESTATUS 0x02 /* got unit status */ +#define UNIT_REQUEUE 0x04 /* requeue after response */ + +/* + * Handle a command ring transition: wake up sleepers for command packets. + * This is too simple to bother with a function call. + */ +#define MSCP_DOCMD(mi) { \ + if ((mi)->mi_wantcmd) { \ + (mi)->mi_wantcmd = 0; \ + wakeup((caddr_t) &(mi)->mi_wantcmd); \ + } \ +} + +/* + * The following macro appends a buffer to a drive queue or a drive to + * a controller queue, given the name of the forward link. Use as + * `APPEND(dp, &um->um_tab, b_forw)' or `APPEND(bp, dp, av_forw)', + * where `bp' is a transfer request, `dp' is a drive queue, and `um_tab' + * is a controller queue. (That is, the forward link for controller + * queues is `b_forw'; for drive queues, it is `av_forw'.) + * + * Changed to new buf structure 940605/Ragge + */ +#if 0 +#define MSCP_APPEND(bp, queue, link) { \ + struct buf *tmp; \ + \ + (bp)->link = NULL; \ + if ((queue)->b_actf == NULL) \ + (queue)->b_actf = (bp); \ + else { \ + tmp=(queue)->b_actf; \ + while(tmp->link) tmp=tmp->link; \ + tmp->link = (bp); \ + } \ +} +#endif + +/* Old APPEND macro */ +#if 0 +#define APPEND(bp, queue, link) { \ + (bp)->link = NULL; \ + if ((queue)->b_actf == NULL) \ + (queue)->b_actf = (bp); \ + else \ + (queue)->b_actl->link = (bp); \ + (queue)->b_actl = (bp); \ +} +#endif + +#define MSCP_APPEND(bp, queue, link) { \ + (bp)->link = NULL; \ + if ((queue)->link == NULL) \ + (queue)->link = (bp); \ + else \ + *(queue)->b_actb = (bp); \ + (queue)->b_actb = &(bp)->link; \ +} + +/* Prototypes */ +struct mscp *mscp_getcp __P((struct mscp_softc *, int)); +void mscp_printevent __P((struct mscp *)); +void mscp_go __P((struct mscp_softc *, struct mscp *, int)); +void mscp_requeue __P((struct mscp_softc *)); +void mscp_dorsp __P((struct mscp_softc *)); +void mscp_decodeerror __P((char *, struct mscp *)); +int mscp_print __P((void *, char *));