chris@mimsy.UUCP (04/05/87)
This is (or, depending on article latency and ordering at your site, was or will be) a replacement for the 4.3BSD UDA50 driver. Why you should use this driver: It fixes a number of bugs in the existing driver. Most of these show up only when your hardware breaks. It decodes errors reasonably well. Older drivers print incomplete and/or incomprehensible data. Why you should *not* use this driver: It has no MicroVAX support. (There is a problem in the crash dump code on uVaxen, and it does not handle RD53s and such. If you add RD disk support, it will work as well as the 4.3BSD driver.) The driver itself is in three separate shell archives. These should be run in an empty directory. Read the file `Installation' for installation instructions. There is a replacement manual entry which describes everything I know about each of the various device errors that the driver handles. If you choose to accept this mission ... oops, wrong tape. If you run into trouble, or have changes for the driver, send me mail. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu
chris@mimsy.UUCP (04/05/87)
: Run this shell script with "sh" not "csh" PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH export PATH all=FALSE if [ x$1 = x-a ]; then all=TRUE fi echo Extracting Installation sed 's/^X//' <<'//go.sysin dd *' >Installation Installation instructions (such as they are): 1. Install the `vax' files in /sys/vax. These are: mscp.c (new) mscp.h (replacement - be sure to keep backups!) mscpvar.h (new) 2. Install the `vaxuba' files in /sys/vaxuba. These are: uba.c.diff (patch) ubavar.h.diff (patch) uda.c (replacement) udareg.h (replacement) You can use the `patch' program (in /usr/new) to apply the patches. 3. Install the `vaxif' file in /sys/vaxif: if_ec.c.diff (patch) 4. Edit /sys/conf/files.vax: add the line vax/mscp.c optional genmscp (in alphabetical order if you like to preseve such things). 5. Edit your configuration file (/sys/conf/PICKLE or whatnot): add the line pseudo-device genmscp You should also add the same line to the LINT configuration file, if you run lint on your kernel code. 6. Configure a new kernel (see `Installing and Operating 4.3BSD' in the System Manager's Manual). /etc/config PICKLE # or whatever cd ../PICKLE # or whatever make depend make This will take some time. 7. Put the new kernel in /, and try booting it. If it works, cheer. Put the new manual entry in /usr/man/man4/uda.4, and the standalone uda.c in /sys/stand/uda.c. //go.sysin dd * if [ `wc -c < Installation` != 1219 ]; then made=FALSE echo error transmitting Installation -- echo length should be 1219, not `wc -c < Installation` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 Installation echo -n ' '; ls -ld Installation fi echo Making directory vax mkdir vax echo Extracting vax/mscp.c sed 's/^X//' <<'//go.sysin dd *' >vax/mscp.c X/* * MSCP generic driver routines */ #include "param.h" #include "buf.h" #include "errno.h" #include "dk.h" #include "syslog.h" #include "../vaxuba/ubavar.h" #include "mscp.h" #include "mscpvar.h" #define PCMD PSWP /* priority for command packet waits */ X/* * During transfers, Unibus mapping info is saved in the buffer's * `ubinfo' field. */ #define b_ubinfo b_resid X/* * 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_info *mi; int canwait; { #define mri (&mi->mi_cmd) register struct mscp *mp; register int i; int s = spl5(); 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; X/*???*/ mp->mscp_sccc.sccc_errlgfl = 0; X/*???*/ mp->mscp_sccc.sccc_copyspd = 0; return (mp); #undef mri } #ifdef AVOID_EMULEX_BUG int mscp_aeb_xor = 0x8000bb80; #endif X/* * Do a device go. The driver calls this routine when its go * routine is called; that occurs when the Unibus code has allocated * resources for the transfer. Save the resource information in * bp->b_ubinfo, and finish the MSCP packet. * * N.B.: If we were blocked in ubago(), the drive could have gone * off line and might still be that way. We should probably handle * such a case by changing this command into an on line request, * freeing the Unibus resources, and requeuing the transfer. */ mscp_go(um, mi, mp) register struct uba_ctlr *um; register struct mscp_info *mi; register struct mscp *mp; { register struct buf *bp, *dp; /* * Now is also the time to move the transfer off the * controller and drive queues. However, if there are * more transfers on the drive queue, re-append the drive * to the controller queue. The point of all this, by * the way, is to try to keep as many drives busy as * possible---to deal the controller's credits out to the * drives in a `fair share' arrangement. (To do this fully * would be more trouble than it is worth, though.) */ dp = um->um_tab.b_actf; bp = dp->b_actf; dp->b_actf = bp->av_forw; /* transfer off drive queue */ um->um_tab.b_actf = dp->b_forw; /* drive off ctlr queue */ if (dp->b_actf != NULL) { /* if more on the drive queue */ APPEND(dp, &um->um_tab, b_forw);/* append to ctlr queue */ } else dp->b_active = 0; /* * Move the buffer to the I/O wait queue. */ bp->av_back = mi->mi_wtab.av_back; bp->av_forw = &mi->mi_wtab; mi->mi_wtab.av_back->av_forw = bp; mi->mi_wtab.av_back = bp; /* * Copy the mapping info, finish the command packet, and give * it to the device. The device's dgo routine should then * initiate polling. */ bp->b_ubinfo = um->um_ubinfo; #ifdef AVOID_EMULEX_BUG /* * The Emulex SC41/MS will occasionally zero the lower half word * of the command reference number. The upper half word remains * intact. To keep running, we convert the buffer address into * a small but nonzero integer that is unique over all pending * transfers, and store that value in the upper half word. To * catch occurrances of the bug (so that we can gripe to Emulex), * we also put a nonzero value in the lower word. */ { register u_int i = mi->mi_nextbp; do { /* find a free value */ if (mi->mi_bp[i] == 0) goto found; i = (i + 1) % AEB_MAX_BP; } while (i != mi->mi_nextbp); panic("mscp_go: AEB_MAX_BP too small"); found: mi->mi_bp[i++] = bp; mi->mi_nextbp = i % AEB_MAX_BP; mp->mscp_cmdref = (i << 16) ^ mscp_aeb_xor; } #else mp->mscp_cmdref = (long) bp; #endif mp->mscp_seq.seq_buffer = (bp->b_ubinfo & 0x3ffff) | (UBAI_BDP(bp->b_ubinfo) << 24); *mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT; } X/* * Handle a response ring transition. */ mscp_dorsp(mi) register struct mscp_info *mi; { register struct uba_ctlr *um = mi->mi_um; register struct uba_device *ui; register struct buf *bp; register struct mscp *mp; register int nextrsp; struct mscp_driver *md = mi->mi_md; char *ctlrname, *drivename; int st, error; ctlrname = um->um_driver->ud_mname; drivename = um->um_driver->ud_dname; 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; } /* * Found a response. Update credit information. If there is * nothing else to do, jump to `done' to get the next response. */ mp = &mi->mi_rsp.mri_ring[nextrsp]; mi->mi_credits += MSCP_CREDITS(mp->mscp_msgtc); switch (MSCP_MSGTYPE(mp->mscp_msgtc)) { case MSCPT_SEQ: break; case MSCPT_DATAGRAM: (*md->md_dgram)(um, mp); goto done; case MSCPT_CREDITS: goto done; case MSCPT_MAINTENANCE: default: printf("%s%d: unit %d: unknown message type 0x%x ignored\n", ctlrname, um->um_ctlr, mp->mscp_unit, MSCP_MSGTYPE(mp->mscp_msgtc)); goto done; } /* * 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)) { (*md->md_ctlrdone)(um, mp); goto done; } /* * Find the drive info. If there is none, and this is an * available attention response, try configuring a new drive. */ if (mp->mscp_unit > md->md_ndpc) { printf("%s%d: unit %d out of range\n", ctlrname, um->um_ctlr, mp->mscp_unit); goto done; } if ((ui = mi->mi_ip[mp->mscp_unit]) == NULL) { if ((*md->md_unconf)(um, mp) != MSCP_DONE) { printf("%s%d: unit %d not configured, ", ctlrname, um->um_ctlr, mp->mscp_unit); if (mp->mscp_opcode == M_OP_AVAILATTN) printf("available attn"); else printf("stray response op 0x%x status 0x%x", mp->mscp_opcode, mp->mscp_status); printf(" ignored\n"); } 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%d: bad lbn (%d)?\n", drivename, ui->ui_unit, 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. */ if (ui->ui_flags & UNIT_ONLINE) { printf("%s%d: duplicate ONLINE ignored\n", drivename, ui->ui_unit); break; } if ((*md->md_online)(ui, mp) == MSCP_DONE) ui->ui_flags |= UNIT_ONLINE; break; case M_OP_GETUNITST | M_OP_END: /* * Got unit status. Call the driver to find out * whether it succeeded, and if so, mark it. */ if ((*md->md_gotstatus)(ui, mp) == MSCP_DONE) ui->ui_flags |= UNIT_HAVESTATUS; 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?? */ ui->ui_flags &= ~(UNIT_ONLINE | UNIT_HAVESTATUS); 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%d: io done, but no buffer?\n", drivename, ui->ui_unit); mscp_hexdump(mp); break; } rwend: #ifdef AVOID_EMULEX_BUG { register u_short *p = (u_short *) &mp->mscp_cmdref; /* * Note any errors on the part of the controller. * The lower word should be zero after exclusive * or'ing with mscp_aeb_xor, and the upper should * then be in the range [1..AEB_MAX_BP]. */ mp->mscp_cmdref ^= mscp_aeb_xor; p[1]--; if (p[1] >= AEB_MAX_BP) panic("unrecoverable Emulex screwup"); if (p[0] == 0) mi->mi_ok++; else { /* * Calculate the expected response, * assuming p[1] is correct. The * actual response is then the expected * response xor p[0]. */ int sb = ((p[1] + 1) << 16) ^ mscp_aeb_xor; log(LOG_WARNING, "\ Emulex SC41/MS screwup: %s%d, got %d correct, then changed 0x%x to 0x%x\n", ctlrname, um->um_ctlr, mi->mi_ok, sb, sb ^ p[0]); mi->mi_ok = 0; } /* convert index back to buffer, and mark free */ bp = mi->mi_bp[p[1]]; mi->mi_bp[p[1]] = 0; } #else bp = (struct buf *) mp->mscp_cmdref; #ifdef MSCP_PARANOIA { register struct buf *q = mi->mi_wtab.av_forw; /* * Ensure that this response corresponds to * some outstanding request. If not, ignore * it entirely. This will likely cause a * Unibus reset soon, after which the controller * just might behave. */ while (q != bp && q != &mi->mi_wtab) q = q->av_forw; if (q != bp) { printf("%s%d: bad response packet ignored\n", ctlrname, um->um_ctlr); mscp_hexdump(mp); goto out; } } #endif MSCP_PARANOIA #endif AVOID_EMULEX_BUG /* * 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; } um->um_ubinfo = bp->b_ubinfo; ubadone(um); if (st == M_ST_OFFLINE || st == M_ST_AVAILABLE) ui->ui_flags &= ~(UNIT_ONLINE | UNIT_HAVESTATUS); /* * Unlink the transfer from the wait queue mi_wtab. * If there are no more transfers on the drive queue * for this drive, and it is a profiled disk, turn * off its busy bit. */ bp->av_back->av_forw = bp->av_forw; bp->av_forw->av_back = bp->av_back; if (ui->ui_dk >= 0 && md->md_utab[ui->ui_unit].b_forw == NULL) dk_busy &= ~(1 << ui->ui_dk); /* * If the transfer has something to do with bad * block forwarding, let the driver handle the * rest. */ if ((bp->b_flags & B_BAD) != 0 && md->md_bb != NULL) { (*md->md_bb)(ui, mp, bp); goto out; } /* * If the transfer failed, give the driver a crack * at fixing things up. */ if (st != M_ST_SUCCESS) { switch ((*md->md_ioerr)(ui, mp, bp)) { case MSCP_DONE: /* fixed */ break; case MSCP_RESTARTED: /* still working on it */ goto out; case MSCP_FAILED: /* no luck */ harderr(bp, drivename); mscp_printevent(mp); bp->b_flags |= B_ERROR; bp->b_error = EIO; 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. */ bp->b_resid = bp->b_bcount - mp->mscp_seq.seq_bytecount; biodone(bp); if (um->um_bdp && mi->mi_wtab.av_forw == &mi->mi_wtab) ubarelse(um->um_ubanum, &um->um_bdp); out: break; case M_OP_REPLACE | M_OP_END: /* * A replace operation finished. Just let the driver * handle it (if it does replaces). */ if (md->md_replace == NULL) printf("%s%d: bogus REPLACE end\n", drivename, ui->ui_unit); else (*md->md_replace)(ui, 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%d: unknown opcode 0x%x status 0x%x ignored\n", drivename, ui->ui_unit, 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. */ if (ui->ui_flags & UNIT_REQUEUE) { bp = &md->md_utab[ui->ui_unit]; if (bp->b_active) panic("mscp_dorsp requeue"); APPEND(bp, &um->um_tab, b_forw); bp->b_active = 1; ui->ui_flags &= ~UNIT_REQUEUE; } 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; } X/* * Dump the entire contents of an MSCP packet in hex. Mainly useful * for debugging.... */ 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 ", *p++); printf("\n"); } X/* * Requeue outstanding transfers, e.g., after Unibus reset. * Also requeue any drives that have on line or unit status * info pending. */ mscp_requeue(mi) struct mscp_info *mi; { register struct uba_ctlr *um = mi->mi_um; register struct uba_device *ui; register struct mscp_driver *md = mi->mi_md; register struct buf *bp, *dp; register int unit; struct buf *nextbp; /* * Clear the controller chain. Mark everything un-busy; we * will soon fix any that are in fact busy. */ um->um_tab.b_actf = NULL; um->um_tab.b_active = 0; for (unit = 0, dp = md->md_utab; unit < md->md_nunits; unit++, dp++) { dp->b_forw = 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_wtab.av_back; bp != &mi->mi_wtab; bp = nextbp) { nextbp = bp->av_back; dp = &md->md_utab[minor(bp->b_dev) >> md->md_unitshift]; bp->av_forw = dp->b_actf; if (dp->b_actf == NULL) dp->b_actl = bp; dp->b_actf = bp; } mi->mi_wtab.av_forw = mi->mi_wtab.av_back = &mi->mi_wtab; /* * 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++) { if ((ui = um->um_driver->ud_dinfo[unit]) == NULL || ui->ui_mi != um || ui->ui_alive == 0) continue; /* does this next line belong here?? */ ui->ui_flags &= ~(UNIT_HAVESTATUS | UNIT_ONLINE); if ((ui->ui_flags & UNIT_REQUEUE) == 0 && dp->b_actf == NULL) continue; ui->ui_flags &= ~UNIT_REQUEUE; APPEND(dp, &um->um_tab, b_forw); dp->b_active = 1; um->um_tab.b_active = 1; } #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 } X/* * MSCP error reporting */ X/* * Messages for the various subcodes. */ static char unknown_msg[] = "unknown subcode"; X/* * 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 */ }; X/* * Subcodes for Invalid Command (1) */ static char *icmd_msgs[] = { "invalid msg length", /* 0 = Invalid Message Length */ }; X/* * Subcodes for Command Aborted (2) */ X/* none known */ X/* * 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 */ }; X/* * Subcodes for Unit Available (4) */ X/* none known */ X/* * 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 */ }; X/* * 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 */ }; X/* * Subcodes for Compare Error (7) */ X/* none known */ X/* * 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 */ }; X/* * 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 */ }; X/* * 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 */ }; X/* * 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 */ }; X/* * 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 }; X/* * Print the decoded error event from an MSCP error datagram. */ 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); } X/* * 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. */ mscp_decodeerror(um, mp) register struct uba_ctlr *um; 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%d: %s error datagram%s: ", um->um_driver->ud_mname, um->um_ctlr, 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: ", 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), 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), 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 } //go.sysin dd * if [ `wc -c < vax/mscp.c` != 23071 ]; then made=FALSE echo error transmitting vax/mscp.c -- echo length should be 23071, not `wc -c < vax/mscp.c` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vax/mscp.c echo -n ' '; ls -ld vax/mscp.c fi echo Extracting vax/mscp.h sed 's/^X//' <<'//go.sysin dd *' >vax/mscp.h X/* mscp.h 6.1 83/07/29 */ X/* * 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. */ X/* * 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_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 */ X/* * 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 */ X/* * AVAILABLE command modifiers */ #define M_AVM_ALLCD 0x0002 /* All class drivers */ #define M_AVM_SPINDOWN 0x0001 /* Spin down */ X/* * FLUSH command modifiers */ #define M_FLM_FLUSHENU 0x0001 /* Flush entire unit */ #define M_FLM_VOLATILE 0x0002 /* Volatile only */ X/* * GET UNIT STATUS command modifiers */ #define M_GUM_NEXTUNIT 0x0001 /* Next unit */ X/* * ONLINE command modifiers */ #define M_OLM_RIP 0x0001 /* Allow self destruction */ #define M_OLM_IGNMF 0x0002 /* Ignore media format error */ X/* * 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 */ X/* * REPLACE command modifiers */ #define M_RPM_PRIMARY 0x0001 /* Primary replacement block */ X/* * 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 */ X/* * 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 */ X/* * 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 */ X/* * 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 */ X/* * 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 */ X/* * 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 */ X/* * 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 */ X/* * 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. */ X/* * 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_xxx1[2]; /* ? */ /* 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 */ }; X/* * 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; /* ? */ long *sccc_addr; /* pointer to cmd descriptor */ }; X/* * 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 scce_ctlrid; /* controller ID */ long scce_xxx[3]; /* ? */ long scce_volser; /* volume serial number */ }; X/* * 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? */ long *onlc_addr; /* pointer to cmd descriptor */ }; X/* * On Line end variant */ struct mscpv_onle { long onle_xxx1[3]; /* ? */ X/*???*/ 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 */ }; X/* * 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; /* group size (?) */ u_short guse_ntpc; /* tracks 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 */ }; X/* * Error datagram variant. */ struct mscpv_erd { quad 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 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) */ char erd_sdistat[12]; /* SDI status information (?) */ }; X/* * 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; X/*???*/ long mscp_xxx; /* pad to 64 bytes */ }; X/* * Define message length according to the DEC specifications by dropping * the four byte header. */ #define MSCP_MSGLEN (sizeof (struct mscp) - 4) X/* * Shorthand */ X/* * Generic packet */ #define mscp_seq mscp_un.un_seq X/* * Set Controller Characteristics packet */ #define mscp_sccc mscp_un.un_sccc X/* * Set Controller Characteristics end packet */ #define mscp_scce mscp_un.un_scce X/* * Online / Set Unit Characteristics command packet */ #define mscp_onlc mscp_un.un_onlc X/* * Online end packet */ #define mscp_onle mscp_un.un_onle X/* * Get Unit Status end packet */ #define mscp_guse mscp_un.un_guse X/* * MSCP Error Log packet */ #define mscp_erd mscp_un.un_erd X/* * 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 */ X/* * Here begin more perhaps brash assumptions about MSCP devices... */ X/* * 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 */ //go.sysin dd * if [ `wc -c < vax/mscp.h` != 13411 ]; then made=FALSE echo error transmitting vax/mscp.h -- echo length should be 13411, not `wc -c < vax/mscp.h` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vax/mscp.h echo -n ' '; ls -ld vax/mscp.h fi echo Extracting vax/mscpvar.h sed 's/^X//' <<'//go.sysin dd *' >vax/mscpvar.h X/* * MSCP generic driver configuration */ X/* * 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. */ X/* #define MSCP_PARANOIA */ X/* #define AVOID_EMULEX_BUG */ X/* * Per driver information. * * md_ndpc sets the maximum unit number allowed in response packets. * md_nunits is the number of drives attached to all controllers. * md_unitshift is the divisor for converting a minor device number * to a unit index for the device queues in md_utab. * * The routines are called from the generic response dispatcher. * The first three (dgram, ctlrdone, and unconf) get passed a pointer * to the uba_ctlr and to the packet; the rest get a pointer to the * uba_device and to the packet (`um, mp' and `ui, mp' respectively). * The routines unconf, online, gotstatus, and ioerr are functions * and should return one of the values given below. In addition, * the ioerr and bb routines get a third argument, `bp': a pointer * to the buffer describing the transfer in error. */ struct mscp_driver { int md_ndpc; /* number of drives per ctlr */ int md_nunits; /* total number drives (all ctlrs) */ int md_unitshift; /* device number to unit: >> count */ struct buf *md_utab; /* pointer to device queues */ int (*md_dgram)(); /* error datagram */ int (*md_ctlrdone)(); /* controller operation complete */ int (*md_unconf)(); /* response from unconfigured drive */ int (*md_online)(); /* drive on line */ int (*md_gotstatus)(); /* got unit status */ int (*md_replace)(); /* replace done */ int (*md_ioerr)(); /* read or write failed */ int (*md_bb)(); /* B_BAD io done */ }; X/* * 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 */ X/* * 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 */ }; X/* * 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!). * * mi_wtab 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; mi_wtab is that place. */ struct mscp_info { struct mscp_driver *mi_md; /* pointer to driver info */ struct uba_ctlr *mi_um; /* pointer to ctlr */ struct uba_device **mi_ip; /* pointer to inverting pointers */ 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_wtab; /* transfer wait queue */ #ifdef AVOID_EMULEX_BUG #define AEB_MAX_BP 32 /* max pend xfers (power of 2) XXX */ struct buf *mi_bp[AEB_MAX_BP]; /* xfer no. to buffer */ u_int mi_nextbp; /* generates unique xfer no's */ int mi_ok; /* for error rate statistics */ #endif AVOID_EMULEX_BUG }; X/* * 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 X/* * Flags for mscp_getcp(). */ #define MSCP_WAIT 1 #define MSCP_DONTWAIT 0 struct mscp *mscp_getcp(); /* get a command packet */ X/* * Unit flags */ #define UNIT_ONLINE 0x01 /* drive is on line */ #define UNIT_HAVESTATUS 0x02 /* got unit status */ #define UNIT_REQUEUE 0x04 /* requeue after response */ X/* * 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); \ } \ } X/* * 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'.) */ #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); \ } //go.sysin dd * if [ `wc -c < vax/mscpvar.h` != 5413 ]; then made=FALSE echo error transmitting vax/mscpvar.h -- echo length should be 5413, not `wc -c < vax/mscpvar.h` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vax/mscpvar.h echo -n ' '; ls -ld vax/mscpvar.h fi made=TRUE if [ $made = TRUE ]; then chmod 755 vax echo -n ' '; ls -ld vax fi -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu
chris@mimsy.UUCP (04/05/87)
: Run this shell script with "sh" not "csh" PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH export PATH all=FALSE if [ x$1 = x-a ]; then all=TRUE fi echo Making directory man mkdir man echo Extracting man/uda.4 sed 's/^X//' <<'//go.sysin dd *' >man/uda.4 X.TH UDA 4 "University of Maryland" X.UC 4 X.SH NAME uda \- UDA50 disk controller interface X.SH SYNOPSIS X.B "controller uda0 at uba0 csr 0172150 vector udintr" X.br X.b "disk ra0 at uda0 drive 0" X.SH DESCRIPTION This is a driver for the DEC UDA50 disk controller and other compatible controllers. The UDA50 communicates with the host through a packet protocol known as the Mass Storage Control Protocol (MSCP). Consult the file X.RI < vax/mscp.h > for a detailed description of this protocol. X.PP XFiles with minor device numbers 0 through 7 refer to various portions of drive 0; minor devices 8 through 15 refer to drive 1, etc. The standard device names begin with `ra' followed by the drive number and then a letter a-h for partitions 0-7 respectively. The character ? stands here for a drive number in the range 0-7. X.PP The block files access the disk via the system's normal buffering mechanism mechanism and may be read and written without regard to physical disk records. There is also a `raw' interface which provides for direct transmission between the disk and the user's read or write buffer. A single read or write call results in exactly one I/O operation and therefore raw I/O is considerably more efficient when many words are transmitted. The names of the raw files conventionally begin with an extra `r'. X.PP In raw I/O counts should be a multiple of 512 bytes (a disk sector). Likewise X.I seek calls should specify a multiple of 512 bytes. X.SH "DISK SUPPORT" This driver configures the type of each drive when it is first encountered. A partition table in the driver is required for each type of disk. The origin and size (in sectors) of the pseudo-disks on each drive are shown below. Not all partitions begin on cylinder boundaries, as on other drives, because previous drivers used one partition table for all drive types. Variants of the partition tables are common; check the driver and the file X.IR /etc/disktab ( disktab (5)) for other possibilities. X.PP X.nf X.ta .5i +\w'000000 'u +\w'000000 'u +\w'000000 'u +\w'000000 'u X.PP RA60 partitions disk start length ra?a 0 15884 ra?b 15884 33440 ra?c 0 400176 ra?d 49324 82080 same as 4.2BSD ra?g ra?e 131404 268772 same as 4.2BSD ra?h ra?f 49324 350852 ra?g 242606 157570 ra?h 49324 193282 X.PP RA80 partitions disk start length ra?a 0 15884 ra?b 15884 33440 ra?c 0 242606 ra?e 49324 193282 same as old Berkeley ra?g ra?f 49324 82080 same as 4.2BSD ra?g ra?g 49910 192696 ra?h 131404 111202 same as 4.2BSD X.PP RA81 partitions disk start length ra?a 0 15884 ra?b 16422 66880 ra?c 0 891072 ra?d 375564 15884 ra?e 391986 307200 ra?f 699720 191352 ra?g 375564 515508 ra?h 83538 291346 X.PP RA81 partitions with 4.2BSD-compatible partitions disk start length ra?a 0 15884 ra?b 16422 66880 ra?c 0 891072 ra?d 49324 82080 same as 4.2BSD ra?g ra?e 131404 759668 same as 4.2BSD ra?h ra?f 412490 478582 same as 4.2BSD ra?f ra?g 375564 515508 ra?h 83538 291346 X.DT X.fi X.PP The ra?a partition is normally used for the root file system, the ra?b partition as a paging area, and the ra?c partition for pack-pack copying (it maps the entire disk). X.SH FILES X/dev/ra[0-9][a-f] X.br X/dev/rra[0-9][a-f] X.SH DIAGNOSTICS X.TP panic: udaslave No command packets were available while the driver was looking for disk drives. The controller is not extending enough credits to use the drives. X.TP uda%d: no response to Get Unit Status request A disk drive was found, but did not respond to a status request. This is either a hardware problem or someone pulling unit number plugs very fast. X.TP uda%d: unit %d off line While searching for drives, the controller found one that seems to be manually disabled. It is ignored. X.TP uda%d: unable to get unit status Something went wrong while trying to determine the status of a disk drive. This is followed by an error detail. X.TP uda%d: unit %d, next %d This probably never happens, but I wanted to know if it did. I have no idea what one should do about it. X.TP uda%d: cannot handle unit number %d (max is %d) The controller found a drive whose unit number is too large. Valid unit numbers are those in the range [0..7]. X.TP uda%d: unit %d (media ID `%s') is of unknown type %d; ignored The controller found a drive whose type is not known, and thus has no partitioning. The drive has been ignored. You can add the type to the udatypes[] table, now that you know what it is: The media ID will be something like `DU RA25'. X.TP uda%d: uballoc map failed Unibus resource map allocation failed during initialisation. This can only happen if you have 496 devices on a Unibus. X.TP uda%d: timeout during init The controller did not initialise within ten seconds. A hardware problem, but it sometimes goes away if you try again. X.TP uda%d: init failed, sa=%b The controller refused to initalise. X.TP uda%d: controller hung The controller never finished initialisation. Retrying may sometimes fix it. X.TP ra%d: drive will not come on line The drive will not come on line, probably because it is spun down. This should be preceded by a message giving details as to why the drive stayed off line. X.TP uda%d: still hung When the controller hangs, the driver occasionally tries to reinitialise it. This means it just tried, without success. X.TP panic: udastart: bp==NULL A bug in the driver has put an empty drive queue on a controller queue. X.TP uda%d: command ring too small If you increase NCMDL2, you may see a performance improvement. (See /sys/vaxuba/uda.c.) X.TP panic: udastart A drive was found marked for status or on-line functions while performing status or on-line functions. This indicates a bug in the driver. X.TP uda%d: controller error, sa=%b The controller reported an error. The driver will reset it and retry pending I/O. X.TP uda%d: stray intr The controller interrupted when it should have stayed quiet. The interrupt has been ignored. X.TP uda%d: init step %d failed, sa=%b The controller reported an error during the named initialisation step. The driver will retry initialisation later. X.TP uda%d: version %d model %d An informational message giving the revision level of the controller. X.TP uda%d: DMA burst size set to %d An informational message showing the DMA burst size, in words. X.TP panic: udaintr Indicates a bug in the generic MSCP code. X.TP uda%d: driver bug, state %d The driver has a bogus value for the controller state. Something is quite wrong. This is immediately followed by a `panic: udastate'. X.TP uda%d: purge bdp %d A benign message tracing BDP purges. I have been trying to figure out what BDP purges are for. You might want to comment out this call to log() in /sys/vaxuba/uda.c. X.TP X.RI "uda%d: SETCTLRC failed: " detail The Set Controller Characteristics command (the last part of the controller initialisation sequence) failed. The X.I detail message tells why. X.TP X.RI "uda%d: attempt to bring ra%d on line failed: " detail The drive could not be brought on line. The X.I detail message tells why. X.TP uda%d: ra%d: unknown type %d The type index of the named drive is not known to the driver, so the drive will be ignored. X.TP ra%d: changed types! was %s A drive somehow changed from one kind to another, e.g., from an RA80 to an RA60. The driver believes the new type. X.TP ra%d: %s, size = %d sectors The named drive is of the given type, and has that many sectors of user-file area. This is printed during configuration. X.TP X.RI "uda%d: attempt to get status for ra%d failed: " detail A status request failed. The X.I detail message should tell why. X.TP ra%d: unit %d, nspt %d, group %d, ntpc %d, rctsize %d, X.br X.ti -5 nrpt %d, nrct %d X.br Information about the geometry of the named drive. This is not used by the driver, but can one setting up X.I disktab entries, e.g. Note that the sectors per track, group, and tracks per cylinder values are those after bad blocking is accounted for, and will differ slightly from the actual hardware setup. This message also reports the MSCP unit number for the drive. Errors tend to include only the MSCP unit number, rather than the drive number, since that is all the driver can tell at the time. X.TP ra%d: bad block report: %d The drive has reported the given block as bad. If there are multiple bad blocks, the drive will report only the first; in this case this message will be followed by `+ others'. Get DEC to forward the block with EVRLK. X.TP ra%d: serious exception reported I have no idea what this really means. X.TP panic: udareplace The controller reported completion of a REPLACE operation. The driver never issues any REPLACEs, so something is wrong. X.TP panic: udabb The controller reported completion of bad block related I/O. The driver never issues any such, so something is wrong. X.TP uda%d: lost interrupt The controller has gone out to lunch, and is being reset to try to bring it back. X.TP panic: mscp_go: AEB_MAX_BP too small You defined AVOID_EMULEX_BUG and increased NCMDL2 and Emulex has new firmware. Raise AEB_MAX_BP or turn off AVOID_EMULEX_BUG. X.TP uda%d: unit %d: unknown message type 0x%x ignored The controller responded with a mysterious message type. See X/sys/vax/mscp.h for a list of known message types. This is probably a controller hardware problem. X.TP uda%d: unit %d out of range The disk drive unit number (the unit plug) is higher than the maximum number the driver allows (currently 7). X.TP uda%d: unit %d not configured, \fImessage\fP ignored The named disk drive has announced its presence to the controller, but was not, or cannot now be, configured into the running system. X.I Message is one of `available attention' (an `I am here' message) or `stray response op 0x%x status 0x%x' (anything else). X.TP ra%d: bad lbn (%d)? The drive has reported an invalid command error, probably due to an invalid block number. If the lbn value is very much greater than the size reported by the drive, this is the problem. It is probably due to an improperly configured partition table. Other invalid commands indicate a bug in the driver, or hardware trouble. X.TP ra%d: duplicate ONLINE ignored The drive has come on-line while already on-line. This condition can probably be ignored (and has been). X.TP ra%d: io done, but no buffer? Hardware trouble, or a bug; the drive has finished an I/O request, but the response has an invalid (zero) command reference number. X.TP Emulex SC41/MS screwup: uda%d, got %d correct, then X.br X.ti -5 changed 0x%x to 0x%x X.br You turned on AVOID_EMULEX_BUG, and the driver successfully avoided the bug. The number of correctly-handled requests is reported, along with the expected and actual values relating to the bug being avoided. X.TP panic: unrecoverable Emulex screwup You turned on AVOID_EMULEX_BUG, but Emulex was too clever and avoided the avoidance. Try turning on MSCP_PARANOIA instead. X.TP uda%d: bad response packet ignored You turned on MSCP_PARANOIA, and the driver caught the controller in a lie. The lie has been ignored, and the controller will soon be reset (after a `lost' interrupt). This is followed by a hex dump of the offending packet. X.TP ra%d: bogus REPLACE end The drive has reported finishing a bad sector replacement, but the driver never issues bad sector replacement commands. The report is ignored. This is likely a hardware problem. X.TP ra%d: unknown opcode 0x%x status 0x%x ignored The drive has reported something that the driver cannot understand. Perhaps DEC has been inventive, or perhaps your hardware is ill. This is followed by a hex dump of the offending packet. X.TP uda%d: %s error datagram The controller has reported some kind of error, either `hard' (unrecoverable) or `soft' (recoverable). If the controller is going on (attempting to fix the problem), this message includes the remark `(continuing)'. Emulex controllers wrongly claim that all soft errors are hard errors. This message may be followed by one of the following 5 messages, depending on its type, and will always be followed by a failure detail message (also listed below). X.RS X.TP memory addr 0x%x A host memory access error; this is the address that could not be read. X.TP unit %d: level %d retry %d, %s %d A typical disk error; the retry count and error recovery levels are printed, along with the block type (`lbn', or logical block; or `rbn', or replacement block) and number. If the string is something else, DEC has been clever, or your hardware has gone to Australia for vacation (unless you live there; then it might be in New Zealand, or Brazil). X.TP unit %d: %s %d Also a disk error, but an `SDI' error, whatever that is. (I doubt it has anything to do with Ronald Reagan.) This lists the block type (`lbn' or `rbn') and number. X.TP unit %d: small disk error, cyl %d Yet another kind of disk error, but for small disks. (`That's what it says, guv'nor. Dunnask me what it means.') X.TP unit %d: unknown error, format 0x%x A mysterious error: the given format code is not known. X.RE X.PP The detail messages are as follows: X.RS X.TP success (%s) (code 0, subcode %d) Everything worked, but the controller thought it would let you know that something went wrong. No matter what subcode, this can probably be ignored. X.TP invalid command (%s) (code 1, subcode %d) This probably cannot occur unless the hardware is out; %s should be `invalid msg length', meaning some command was too short or too long. X.TP command aborted (unknown subcode) (code 2, subcode %d) This should never occur, as the driver never aborts commands. X.TP unit offline (%s) (code 3, subcode %d) The drive is offline, either because it is not around (`unknown drive'), stopped (`not mounted'), out of order (`inoperative'), has the same unit number as some other drive (`duplicate'), or has been disabled for diagnostics (`in diagnosis'). X.TP unit available (unknown subcode) (code 4, subcode %d) The controller has decided to report a perfectly normal event as an error. (Why?) X.TP media format error (%s) (code 5, subcode %d) The drive cannot be used without reformatting. The Format Control Table cannot be read (`fct unread - edc'), there is a bad sector header (`invalid sector header'), the drive is not set for 512-byte sectors (`not 512 sectors'), the drive is not formatted (`not formatted'), or the FCT has an uncorrectable ECC error (`fct ecc'). X.TP write protected (%s) (code 6, subcode %d) The drive is write protected, either by the front panel switch (`hardware') or via the driver (`software'). The driver never sets software write protect. X.TP compare error (unknown subcode) (code 7, subcode %d) A compare operation showed some sort of difference. The driver never uses compare operations. X.TP data error (%s) (code 7, subcode %d) Something went wrong reading or writing a data sector. A `forced error' is a software-asserted error used to mark a sector that contains suspect data. Rewriting the sector will clear the forced error. This is normally set only during bad block replacment, and the driver does no bad block replacement, so these should not occur. A `header compare' error probably means the block is shot. A `sync timeout' presumably has something to do with sector synchronisation. An `uncorrectable ecc' error is an ordinary data error that cannot be fixed via ECC logic. A `%d symbol ecc' error is a data error that can be (and presumably has been) corrected by the ECC logic. It might indicate a sector that is imperfect but usable, or that is starting to go bad. If any of these errors recur, the sector may need to be replaced. X.TP host buffer access error (%s) (code %d, subcode %d) Something went wrong while trying to copy data to or from the host (Vax). The subcode is one of `odd xfer addr', `odd xfer count', `non-exist. memory', or `memory parity'. The first two could be a software glitch; the last two indicate hardware problems. X.TP controller error (%s) (code %d, subcode %d) The controller has detected a hardware error in itself. A `serdes overrun' is a serialiser / deserialiser overrun; `edc' probably stands for `error detection code'; and `inconsistent internal data struct' is obvious. X.TP drive error (%s) (code %d, subcode %d) Either the controller or the drive has detected a hardware error in the drive. I am not sure what an `sdi command timeout' is, but these seem to occur benignly on occasion. A `ctlr detected protocol' error means that the controller and drive do not agree on a protocol; this could be a cabling problem, or a version mismatch. A `positioner' error means the drive seek hardware is ailing; `lost rd/wr ready' means the drive read/write logic is sick; and `drive clock dropout' means that the drive clock logic is bad, or the media is hopelessly scrambled. I have no idea what `lost recvr ready' means. A `drive detected error' is a catch-all for drive hardware trouble; `ctlr detected pulse or parity' errors are often caused by cabling problems. X.RE X.SH BUGS The partition tables attempt to combine compatibility with previous drivers and functionality; this is impossible. The best solution would be to read the partition tables off the drive. X.PP This version of the driver does not support RA25s, RD52s, and RD53s. //go.sysin dd * if [ `wc -c < man/uda.4` != 17142 ]; then made=FALSE echo error transmitting man/uda.4 -- echo length should be 17142, not `wc -c < man/uda.4` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 man/uda.4 echo -n ' '; ls -ld man/uda.4 fi made=TRUE if [ $made = TRUE ]; then chmod 755 man echo -n ' '; ls -ld man fi echo Making directory stand mkdir stand echo Extracting stand/uda.c sed 's/^X//' <<'//go.sysin dd *' >stand/uda.c X/* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)uda.c 7.1 (Berkeley) 6/5/86 */ X/* * UDA50/RAxx disk device driver */ #include "../machine/pte.h" #include "../h/param.h" #include "../h/inode.h" #include "../h/fs.h" #include "saio.h" #include "savax.h" #define NRA 4 X/* * Parameters for the communications area */ #define NRSPL2 0 #define NCMDL2 0 #define NRSP (1<<NRSPL2) #define NCMD (1<<NCMDL2) #include "../vaxuba/udareg.h" #include "../vaxuba/ubareg.h" #include "../vax/mscp.h" u_short udastd[] = { 0772150 }; struct iob cudbuf; struct udadevice *udaddr = 0; struct uda { struct udaca uda_ca; struct mscp uda_rsp; struct mscp uda_cmd; } uda; struct uda *ud_ubaddr; /* Unibus address of uda structure */ X/* int ra25_off[] = { 0, 15884, 0, -1, -1, -1, 25916, -1 }; */ int ra60_off[] = { 0, 15884, 0, 49324, 131404, 49324, 242606, 49324 }; int ra80_off[] = { 0, 15884, 0, -1, 49324, 49324, 49910, 131404 }; #ifndef UCBRA #ifdef RA_COMPAT int ra81_off[] = { 0, 16422, 0, 49324, 131404, 412490, 375564, 83538 }; #else int ra81_off[] = { 0, 16422, 0, 375564, 391986, 699720, 375564, 83538 }; #endif #else int ra81_off[] = { 0, 15884, 0, 242606, 258490, 565690, 242606, 49324 }; #endif int *ra_off[] = { 0, ra80_off, /* 1 = ra80 */ ra81_off, /* 2 = old ra81 microcode */ 0, /* 3 = old ra60?? */ ra60_off, /* 4 = ra60 */ ra81_off, /* 5 = ra81 */ /* WHAT TYPE IS ra25? */ }; #define NTYPES (sizeof(ra_off) / sizeof(ra_off[0])) static int ra_type[NRA]; raopen(io) register struct iob *io; { register struct mscp *mp; static int udainit, udadriveinit[NRA]; register int i, t; daddr_t off; if (udaddr == 0) udaddr = (struct udadevice *)ubamem(io->i_unit, udastd[0]); if (ud_ubaddr == 0) { /* * Initialise cudbuf.i_unit so that controllers * on UNIBUSes other than 0 can be used. */ cudbuf.i_unit = io->i_unit; cudbuf.i_ma = (caddr_t)&uda; cudbuf.i_cc = sizeof(uda); ud_ubaddr = (struct uda *)ubasetup(&cudbuf, 2); } if (udainit == 0) { udaddr->udaip = 0; while ((udaddr->udasa & UDA_STEP1) == 0) ; udaddr->udasa = UDA_ERR; while ((udaddr->udasa & UDA_STEP2) == 0) ; udaddr->udasa = (short)&ud_ubaddr->uda_ca.ca_rspdsc[0]; while ((udaddr->udasa & UDA_STEP3) == 0) ; udaddr->udasa = (short)(((int)&ud_ubaddr->uda_ca.ca_rspdsc[0]) >> 16); while ((udaddr->udasa & UDA_STEP4) == 0) ; udaddr->udasa = UDA_GO; uda.uda_ca.ca_rspdsc[0] = (long)&ud_ubaddr->uda_rsp.mscp_cmdref; uda.uda_ca.ca_cmddsc[0] = (long)&ud_ubaddr->uda_cmd.mscp_cmdref; if (udcmd(M_OP_SETCTLRC)) { _stop("ra: open error, SETCTLRC"); return; } udainit = 1; } i = io->i_unit & 7; if (udadriveinit[i] == 0) { uda.uda_cmd.mscp_unit = i; if (udcmd(M_OP_ONLINE)) { _stop("ra: open error, ONLINE"); return; } t = ra_type[i] = uda.uda_rsp.mscp_onle.onle_drivetype; if (t < 0 || t >= NTYPES || ra_off[t] == 0) { printf("uda%d ra%d: disk type %d unknown\n", io->i_unit >> 3, i, t); _stop("ra: bad type"); } udadriveinit[i] = 1; } if ((t = io->i_boff) < 0 || t > 7) _stop("ra: bad unit"); off = ra_off[ra_type[i]][t]; if (off == -1) _stop("ra: bad partition"); io->i_boff = off; } udcmd(op) int op; { int i; uda.uda_cmd.mscp_opcode = op; uda.uda_rsp.mscp_msglen = MSCP_MSGLEN; uda.uda_cmd.mscp_msglen = MSCP_MSGLEN; uda.uda_ca.ca_rspdsc[0] |= MSCP_OWN | MSCP_INT; uda.uda_ca.ca_cmddsc[0] |= MSCP_OWN | MSCP_INT; i = udaddr->udaip; for (;;) { if (uda.uda_ca.ca_cmdint) uda.uda_ca.ca_cmdint = 0; /* should ignore trash a la uda dump code */ if (uda.uda_ca.ca_rspint) break; } uda.uda_ca.ca_rspint = 0; if (uda.uda_rsp.mscp_opcode != (op | M_OP_END) || (uda.uda_rsp.mscp_status & M_ST_MASK) != M_ST_SUCCESS) return (1); return (0); } rastrategy(io, func) register struct iob *io; { register struct mscp *mp; int ubinfo; ubinfo = ubasetup(io, 1); mp = &uda.uda_cmd; mp->mscp_unit = io->i_unit&7; mp->mscp_seq.seq_lbn = io->i_bn; mp->mscp_seq.seq_bytecount = io->i_cc; mp->mscp_seq.seq_buffer = (ubinfo & 0x3ffff) | ((ubinfo >> 4) & (0xf << 24)); if (udcmd(func == READ ? M_OP_READ : M_OP_WRITE)) { printf("ra: I/O error\n"); ubafree(io, ubinfo); return(-1); } ubafree(io, ubinfo); return(io->i_cc); } X/*ARGSUSED*/ raioctl(io, cmd, arg) struct iob *io; int cmd; caddr_t arg; { return (ECMD); } //go.sysin dd * if [ `wc -c < stand/uda.c` != 4510 ]; then made=FALSE echo error transmitting stand/uda.c -- echo length should be 4510, not `wc -c < stand/uda.c` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 stand/uda.c echo -n ' '; ls -ld stand/uda.c fi made=TRUE if [ $made = TRUE ]; then chmod 755 stand echo -n ' '; ls -ld stand fi echo Making directory vaxif mkdir vaxif echo Extracting vaxif/if_ec.c.diff sed 's/^X//' <<'//go.sysin dd *' >vaxif/if_ec.c.diff *** vaxif/if_ec.c.4.3 Thu Jun 5 20:04:36 1986 --- vaxif/if_ec.c Tue Feb 10 20:57:32 1987 *************** *** 58,62 **** u_short ecstd[] = { 0 }; struct uba_driver ecdriver = ! { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo, 0, 0, 0, ecubamem }; int ecinit(),ecioctl(),ecoutput(),ecreset(); --- 58,62 ---- u_short ecstd[] = { 0 }; struct uba_driver ecdriver = ! { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo, 0, 0, 0, 0, ecubamem }; int ecinit(),ecioctl(),ecoutput(),ecreset(); //go.sysin dd * if [ `wc -c < vaxif/if_ec.c.diff` != 499 ]; then made=FALSE echo error transmitting vaxif/if_ec.c.diff -- echo length should be 499, not `wc -c < vaxif/if_ec.c.diff` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vaxif/if_ec.c.diff echo -n ' '; ls -ld vaxif/if_ec.c.diff fi made=TRUE if [ $made = TRUE ]; then chmod 755 vaxif echo -n ' '; ls -ld vaxif fi -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu
chris@mimsy.UUCP (04/05/87)
: Run this shell script with "sh" not "csh" PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH export PATH all=FALSE if [ x$1 = x-a ]; then all=TRUE fi echo Making directory vaxuba mkdir vaxuba echo Extracting vaxuba/uba.c.diff sed 's/^X//' <<'//go.sysin dd *' >vaxuba/uba.c.diff *** vaxuba/uba.c.4.3 Thu Jun 5 04:19:32 1986 --- vaxuba/uba.c Tue Feb 10 21:14:26 1987 *************** *** 33,36 **** --- 33,38 ---- #define spluba spl7 /* IPL 17 */ + #define BDPMASK 0xf0000000 /* see ubavar.h */ + /* * Do transfer on device argument. The controller *************** *** 38,66 **** * We queue for resource wait in the uba code if necessary. * We return 1 if the transfer was started, 0 if it was not. ! * If you call this routine with the head of the queue for a ! * UBA, it will automatically remove the device from the UBA ! * queue before it returns. If some other device is given ! * as argument, it will be added to the request queue if the ! * request cannot be started immediately. This means that ! * passing a device which is on the queue but not at the head ! * of the request queue is likely to be a disaster. */ ! ubago(ui) register struct uba_device *ui; { register struct uba_ctlr *um = ui->ui_mi; register struct uba_hd *uh; register int s, unit; uh = &uba_hd[um->um_ubanum]; s = spluba(); ! if (um->um_driver->ud_xclu && uh->uh_users > 0 || uh->uh_xclu) goto rwait; ! um->um_ubinfo = ubasetup(um->um_ubanum, um->um_tab.b_actf->b_actf, ! UBA_NEEDBDP|UBA_CANTWAIT); if (um->um_ubinfo == 0) goto rwait; uh->uh_users++; ! if (um->um_driver->ud_xclu) uh->uh_xclu = 1; splx(s); --- 40,93 ---- * We queue for resource wait in the uba code if necessary. * We return 1 if the transfer was started, 0 if it was not. ! * ! * The onq argument must be zero iff the device is not on the ! * queue for this UBA. If onq is set, the device must be at the ! * head of the queue. In any case, if the transfer is started, ! * the device will be off the queue, and if not, it will be on. ! * ! * Drivers that allocate one BDP and hold it for some time should ! * set ud_keepbdp. In this case um_bdp tells which BDP is allocated ! * to the controller, unless it is zero, indicating that the controller ! * does not now have a BDP. */ ! ubaqueue(ui, onq) register struct uba_device *ui; + int onq; { register struct uba_ctlr *um = ui->ui_mi; register struct uba_hd *uh; + register struct uba_driver *ud; register int s, unit; uh = &uba_hd[um->um_ubanum]; + ud = um->um_driver; s = spluba(); ! /* ! * Honor exclusive BDP use requests. ! */ ! if (ud->ud_xclu && uh->uh_users > 0 || uh->uh_xclu) goto rwait; ! if (ud->ud_keepbdp) { ! /* ! * First get just a BDP (though in fact it comes with ! * one map register too). ! */ ! if (um->um_bdp == 0) { ! um->um_bdp = uballoc(um->um_ubanum, ! (caddr_t)0, 0, UBA_NEEDBDP|UBA_CANTWAIT); ! if (um->um_bdp == 0) ! goto rwait; ! } ! /* now share it with this transfer */ ! um->um_ubinfo = ubasetup(um->um_ubanum, ! um->um_tab.b_actf->b_actf, ! um->um_bdp|UBA_HAVEBDP|UBA_CANTWAIT); ! } else ! um->um_ubinfo = ubasetup(um->um_ubanum, ! um->um_tab.b_actf->b_actf, UBA_NEEDBDP|UBA_CANTWAIT); if (um->um_ubinfo == 0) goto rwait; uh->uh_users++; ! if (ud->ud_xclu) uh->uh_xclu = 1; splx(s); *************** *** 71,80 **** dk_wds[unit] += um->um_tab.b_actf->b_actf->b_bcount>>6; } ! if (uh->uh_actf == ui) uh->uh_actf = ui->ui_forw; ! (*um->um_driver->ud_dgo)(um); return (1); rwait: ! if (uh->uh_actf != ui) { ui->ui_forw = NULL; if (uh->uh_actf == NULL) --- 98,107 ---- dk_wds[unit] += um->um_tab.b_actf->b_actf->b_bcount>>6; } ! if (onq) uh->uh_actf = ui->ui_forw; ! (*ud->ud_dgo)(um); return (1); rwait: ! if (!onq) { ui->ui_forw = NULL; if (uh->uh_actf == NULL) *************** *** 96,99 **** --- 123,128 ---- uh->uh_xclu = 0; uh->uh_users--; + if (um->um_driver->ud_keepbdp) + um->um_ubinfo &= ~BDPMASK; /* keep BDP for misers */ ubarelse(um->um_ubanum, &um->um_ubinfo); } *************** *** 273,277 **** wakeup((caddr_t)&uh->uh_mrwant); } ! while (uh->uh_actf && ubago(uh->uh_actf)) ; } --- 302,306 ---- wakeup((caddr_t)&uh->uh_mrwant); } ! while (uh->uh_actf && ubaqueue(uh->uh_actf, 1)) ; } //go.sysin dd * if [ `wc -c < vaxuba/uba.c.diff` != 4146 ]; then made=FALSE echo error transmitting vaxuba/uba.c.diff -- echo length should be 4146, not `wc -c < vaxuba/uba.c.diff` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vaxuba/uba.c.diff echo -n ' '; ls -ld vaxuba/uba.c.diff fi echo Extracting vaxuba/ubavar.h.diff sed 's/^X//' <<'//go.sysin dd *' >vaxuba/ubavar.h.diff *** vaxuba/ubavar.h.4.3 Thu Jun 5 04:20:06 1986 --- vaxuba/ubavar.h Tue Feb 10 20:56:55 1987 *************** *** 60,64 **** }; - #ifndef LOCORE /* * Per-controller structure. --- 60,63 ---- *************** *** 83,86 **** --- 82,86 ---- int um_cmd; /* communication to dgo() */ int um_ubinfo; /* save unibus registers, etc */ + int um_bdp; /* for controllers that hang on to bdp's */ struct buf um_tab; /* queue of devices for this controller */ }; *************** *** 121,125 **** struct uba_hd *ui_hd; }; - #endif /* --- 121,124 ---- *************** *** 141,144 **** --- 140,144 ---- struct uba_ctlr **ud_minfo; /* backpointers to ubminit structs */ short ud_xclu; /* want exclusive use of bdp's */ + short ud_keepbdp; /* hang on to bdp's once allocated */ int (*ud_ubamem)(); /* see if dedicated memory is present */ }; *************** *** 164,167 **** --- 164,169 ---- #ifndef LOCORE #ifdef KERNEL + #define ubago(ui) ubaqueue(ui, 0) + /* * UBA related kernel variables //go.sysin dd * if [ `wc -c < vaxuba/ubavar.h.diff` != 1033 ]; then made=FALSE echo error transmitting vaxuba/ubavar.h.diff -- echo length should be 1033, not `wc -c < vaxuba/ubavar.h.diff` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vaxuba/ubavar.h.diff echo -n ' '; ls -ld vaxuba/ubavar.h.diff fi echo Extracting vaxuba/uda.c sed 's/^X//' <<'//go.sysin dd *' >vaxuba/uda.c X/* * UDA50/MSCP device driver */ X/* * TODO * write bad block forwarding code */ #include "ra.h" #if NUDA > 0 X/* * CONFIGURATION OPTIONS. The next three defines are tunable -- tune away! * * 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. * * MAXUNIT controls the maximum unit number (number of drives per * controller) we are prepared to handle. * * DEFAULT_BURST must be at least 1. */ #define NRSPL2 5 /* log2 number of response packets */ #define NCMDL2 5 /* log2 number of command packets */ #define MAXUNIT 8 /* maximum allowed unit number */ #define DEFAULT_BURST 4 /* default DMA burst size */ #include "../machine/pte.h" #include "param.h" #include "systm.h" #include "buf.h" #include "conf.h" #include "dir.h" #include "user.h" #include "map.h" #include "vm.h" #include "dk.h" #include "cmap.h" #include "syslog.h" #include "../vax/cpu.h" #include "ubareg.h" #include "ubavar.h" #define NRSP (1 << NRSPL2) #define NCMD (1 << NCMDL2) #include "udareg.h" #include "../vax/mscp.h" #include "../vax/mscpvar.h" #include "../vax/mtpr.h" X/* * Backwards compatibility: Reuse the old names. Should fix someday. */ #define udaprobe udprobe #define udaslave udslave #define udaattach udattach #define udaopen udopen #define udastrategy udstrategy #define udaread udread #define udawrite udwrite #define udareset udreset #define udaintr udintr #define udadump uddump #define udasize udsize X/* * UDA communications area and MSCP packet pools, per controller. */ struct uda { struct udaca uda_ca; /* communications area */ struct mscp uda_rsp[NRSP]; /* response packets */ struct mscp uda_cmd[NCMD]; /* command packets */ } uda[NUDA]; X/* * Software status, per controller. */ struct uda_softc { struct uda *sc_uda; /* Unibus address of uda struct */ short sc_state; /* UDA50 state; see below */ short sc_flags; /* flags; see below */ int sc_micro; /* microcode revision */ int sc_ivec; /* interrupt vector address */ struct mscp_info sc_mi;/* MSCP info (per mscpvar.h) */ int sc_wticks; /* watchdog timer ticks */ } uda_softc[NUDA]; X/* * Controller states */ #define ST_IDLE 0 /* uninitialised */ #define ST_STEP1 1 /* in `STEP 1' */ #define ST_STEP2 2 /* in `STEP 2' */ #define ST_STEP3 3 /* in `STEP 3' */ #define ST_SETCHAR 4 /* in `Set Controller Characteristics' */ #define ST_RUN 5 /* up and running */ X/* * Flags */ #define SC_MAPPED 0x01 /* mapped in Unibus I/O space */ #define SC_INSTART 0x02 /* inside udastart() */ #define SC_GRIPED 0x04 /* griped about cmd ring too small */ #define SC_INSLAVE 0x08 /* inside udaslave() */ #define SC_DOWAKE 0x10 /* wakeup when ctlr init done */ X/* * Device to unit number and partition: */ #define UNITSHIFT 3 #define UNITMASK 7 #define udaunit(dev) (minor(dev) >> UNITSHIFT) #define udapart(dev) (minor(dev) & UNITMASK) X/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ struct size { daddr_t nblocks; daddr_t blkoff; } ra81_sizes[8] = { #ifdef MARYLAND #ifdef ENEEVAX 30706, 0, /* A=cyl 0 thru 42 + 2 sectors */ 40696, 30706, /* B=cyl 43 thru 99 - 2 sectors */ -1, 0, /* C=cyl 0 thru 1247 */ -1, 71400, /* D=cyl 100 thru 1247 */ 15884, 0, /* E=blk 0 thru 15883 */ 33440, 15884, /* F=blk 15884 thru 49323 */ 82080, 49324, /* G=blk 49324 thru 131403 */ -1, 131404, /* H=blk 131404 thru end */ #else 67832, 0, /* A=cyl 0 thru 94 + 2 sectors */ 67828, 67832, /* B=cyl 95 thru 189 - 2 sectors */ -1, 0, /* C=cyl 0 thru 1247 */ -1, 135660, /* D=cyl 190 thru 1247 */ 0, 0, 0, 0, 0, 0, 0, 0, #endif ENEEVAX #else /* THE FOLLOWING ARE STRAIGHT FROM THE 4.3BSD uda.c */ /* THIS KIND OF GARBAGE IS WHY THIS SHOULD BE READ FROM THE PACK */ X/* * These are the new standard partition sizes for ra81's. * An RA_COMPAT system is compiled with D, E, and F corresponding * to the 4.2 partitions for G, H, and F respectively. */ #ifndef UCBRA 15884, 0, /* A=sectors 0 thru 15883 */ 66880, 16422, /* B=sectors 16422 thru 83301 */ 891072, 0, /* C=sectors 0 thru 891071 */ #ifdef RA_COMPAT 82080, 49324, /* 4.2 G => D=sectors 49324 thru 131403 */ 759668, 131404, /* 4.2 H => E=sectors 131404 thru 891071 */ 478582, 412490, /* 4.2 F => F=sectors 412490 thru 891071 */ #else 15884, 375564, /* D=sectors 375564 thru 391447 */ 307200, 391986, /* E=sectors 391986 thru 699185 */ 191352, 699720, /* F=sectors 699720 thru 891071 */ #endif RA_COMPAT 515508, 375564, /* G=sectors 375564 thru 891071 */ 291346, 83538, /* H=sectors 83538 thru 374883 */ X/* * These partitions correspond to the sizes used by sites at Berkeley, * and by those sites that have received copies of the Berkeley driver * with deltas 6.2 or greater (11/15/83). */ #else UCBRA 15884, 0, /* A=sectors 0 thru 15883 */ 33440, 15884, /* B=sectors 15884 thru 49323 */ 891072, 0, /* C=sectors 0 thru 891071 */ 15884, 242606, /* D=sectors 242606 thru 258489 */ 307200, 258490, /* E=sectors 258490 thru 565689 */ 325382, 565690, /* F=sectors 565690 thru 891071 */ 648466, 242606, /* G=sectors 242606 thru 891071 */ 193282, 49324, /* H=sectors 49324 thru 242605 */ #endif UCBRA #endif MARYLAND }, #if GYRE cdc9771_sizes[8] = { /* HACK: treat some RA81s as 9771s on gyre */ 79680, 0, /* A = cyl 0 thru 59 */ 79680, 79680, /* B = cyl 60 thru 119 */ -1, 0, /* C = cyl 0 thru 1021 */ -1, 159360, /* D = cyl 120 thru 1021 */ 0, 0, 0, 0, 0, 0, 0, 0, }, #endif ra80_sizes[8] = { 15884, 0, /* A=blk 0 thru 15883 */ 33440, 15884, /* B=blk 15884 thru 49323 */ -1, 0, /* C=blk 0 thru end */ 0, 0, 0, 0, 0, 0, 82080, 49324, /* G=blk 49324 thru 131403 */ -1, 131404, /* H=blk 131404 thru end */ }, ra60_sizes[8] = { 15884, 0, /* A=blk 0 thru 15883 */ 33440, 15884, /* B=blk 15884 thru 49323 */ -1, 0, /* C=blk 0 thru end */ -1, 49324, /* D=blk 49324 thru end */ 0, 0, 0, 0, 82080, 49324, /* G=blk 49324 thru 131403 */ -1, 131404, /* H=blk 131404 thru end */ }; X/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ X/* * Drive type index decoding table. `ut_name' is null iff the * type is not known. */ struct udatypes { char *ut_name; /* drive type name */ struct size *ut_sizes; /* partition tables */ } udatypes[] = { NULL, NULL, "ra80", ra80_sizes, /* 1 = ra80 */ "old ra81", ra81_sizes, /* 2 = old ra81 microcode */ NULL, NULL, /* 3 = old ra60?? */ "ra60", ra60_sizes, /* 4 = ra60 */ "ra81", ra81_sizes, /* 5 = ra81 */ #if GYRE /* * This CDC partition hack depends on the fact that the * Emulex SC41/MS controller is `version 6 model 6' and * the current DEC devices are `version 5 model 6'. */ "cdc9771", cdc9771_sizes, #define CDCTYPE 6 /* note that this is past the last real type */ #define ISCDC(sc) ((sc)->sc_micro == 0x66) #endif }; #define NTYPES 6 X/* * Definition of the driver for autoconf. */ int udaprobe(), udaslave(), udaattach(), udadgo(), udaintr(); struct uba_ctlr *udaminfo[NUDA]; struct uba_device *udadinfo[NRA]; u_short udastd[] = { 0772150, 0772550, 0777550, 0 }; struct uba_driver udadriver = { udaprobe, udaslave, udaattach, udadgo, udastd, "ra", udadinfo, "uda", udaminfo }; X/* * More driver definitions, for generic MSCP code. */ int udadgram(), udactlrdone(), udaunconf(), udaonline(), udagotstatus(); int udaioerror(), udareplace(), udabb(); struct buf udautab[NRA]; /* per drive transfer queue */ struct mscp_driver udamscpdriver = { MAXUNIT, NRA, UNITSHIFT, udautab, udadgram, udactlrdone, udaunconf, udaonline, udagotstatus, udareplace, udaioerror, udabb }; X/* * Miscellaneous private variables. */ struct buf rudabuf[NRA]; /* raw I/O buffer headers */ char udasr_bits[] = UDASR_BITS; struct uba_device *udaip[NUDA][MAXUNIT]; /* inverting pointers: ctlr & unit => Unibus device pointer */ int udaburst[NUDA] = {0}; /* burst size, per UDA50, zero => default; in data space so patchable via adb */ daddr_t ra_dsize[NRA]; /* drive sizes, from on line end packets */ struct mscp udaslavereply; /* get unit status response packet, set for udaslave by udaunconf, via udaintr */ static struct uba_ctlr *probeum;/* this is a hack---autoconf should pass ctlr info to slave routine; instead, we remember the last ctlr argument to probe */ int udawstart, udawatch(); /* watchdog timer */ X/* * Externals */ int wakeup(); int hz; X/* * Poke at a supposed UDA50 to see if it is there. * This routine duplicates some of the code in udainit() only * because autoconf has not set up the right information yet. * We have to do everything `by hand'. */ udaprobe(reg, ctlr, um) caddr_t reg; int ctlr; struct uba_ctlr *um; { register int br, cvec; register struct uda_softc *sc; register struct udadevice *udaddr; register struct mscp_info *mi; int timeout, tries; #ifdef VAX750 /* * The UDA50 wants to share BDPs on 750s, but not on 780s or * 8600s. (730s have no BDPs anyway.) Toward this end, we * here set the `keep bdp' flag in the per-driver information * if this is a 750. (We just need to do it once, but it is * easiest to do it now, for each UDA50.) */ if (cpu == VAX_750) udadriver.ud_keepbdp = 1; #endif probeum = um; /* remember for udaslave() */ #ifdef lint br = 0; cvec = br; br = cvec; udaintr(0); #endif /* * Set up the controller-specific generic MSCP driver info. * Note that this should really be done in the (nonexistent) * controller attach routine. */ sc = &uda_softc[ctlr]; mi = &sc->sc_mi; mi->mi_md = &udamscpdriver; mi->mi_um = um; mi->mi_ip = udaip[ctlr]; mi->mi_cmd.mri_size = NCMD; mi->mi_cmd.mri_desc = uda[ctlr].uda_ca.ca_cmddsc; mi->mi_cmd.mri_ring = uda[ctlr].uda_cmd; mi->mi_rsp.mri_size = NRSP; mi->mi_rsp.mri_desc = uda[ctlr].uda_ca.ca_rspdsc; mi->mi_rsp.mri_ring = uda[ctlr].uda_rsp; mi->mi_wtab.av_forw = mi->mi_wtab.av_back = &mi->mi_wtab; /* * More controller specific variables. Again, this should * be in the controller attach routine. */ if (udaburst[ctlr] == 0) udaburst[ctlr] = DEFAULT_BURST; /* * Get an interrupt vector. Note that even if the controller * does not respond, we keep the vector. This is not a serious * problem; but it would be easily fixed if we had a controller * attach routine. Sigh. */ sc->sc_ivec = (uba_hd[numuba].uh_lastiv -= 4); udaddr = (struct udadevice *) reg; /* * Initialise the controller (partially). The UDA50 programmer's * manual states that if initialisation fails, it should be retried * at least once, but after a second failure the port should be * considered `down'; it also mentions that the controller should * initialise within ten seconds. Or so I hear; I have not seen * this manual myself. * * N.B.: mfpr(TODR) will not work on uVaxen. */ tries = 0; again: udaddr->udaip = 0; /* start initialisation */ timeout = mfpr(TODR) + 1000; /* timeout in 10 seconds */ while ((udaddr->udasa & UDA_STEP1) == 0) if (mfpr(TODR) > timeout) goto bad; udaddr->udasa = UDA_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) | UDA_IE | (sc->sc_ivec >> 2); while ((udaddr->udasa & UDA_STEP2) == 0) if (mfpr(TODR) > timeout) goto bad; /* should have interrupted by now */ return (sizeof (struct udadevice)); bad: if (++tries < 2) goto again; return (0); } X/* * Find a slave. We allow wildcard slave numbers (something autoconf * is not really prepared to deal with); and we need to know the * controller number to talk to the UDA. For the latter, we keep * track of the last controller probed, since a controller probe * immediately precedes all slave probes for that controller. For the * former, we simply put the unit number into ui->ui_slave after we * have found one. * * Note that by the time udaslave is called, the interrupt vector * for the UDA50 has been set up (so that udaunconf() will be called). */ udaslave(ui, reg) register struct uba_device *ui; caddr_t reg; { register struct uba_ctlr *um = probeum; register struct mscp *mp; register struct uda_softc *sc; int next = 0, type, timeout, tries, i; #ifdef lint i = 0; i = i; #endif /* * Make sure the controller is fully initialised, by waiting * for it if necessary. */ sc = &uda_softc[um->um_ctlr]; if (sc->sc_state == ST_RUN) goto findunit; tries = 0; again: if (udainit(ui->ui_ctlr)) return (0); timeout = mfpr(TODR) + 1000; /* 10 seconds */ while (mfpr(TODR) < timeout) if (sc->sc_state == ST_RUN) /* made it */ goto findunit; if (++tries < 2) goto again; printf("uda%d: controller hung\n", um->um_ctlr); return (0); /* * The controller is all set; go find the unit. Grab an * MSCP packet and send out a Get Unit Status command, with * the `next unit' modifier if we are looking for a generic * unit. We set the `in slave' flag so that udaunconf() * knows to copy the response to `udaslavereply'. */ findunit: udaslavereply.mscp_opcode = 0; sc->sc_flags |= SC_INSLAVE; if ((mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT)) == NULL) panic("udaslave"); /* `cannot happen' */ mp->mscp_opcode = M_OP_GETUNITST; if (ui->ui_slave == '?') { mp->mscp_unit = next; mp->mscp_modifier = M_GUM_NEXTUNIT; } else { mp->mscp_unit = ui->ui_slave; mp->mscp_modifier = 0; } *mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT; i = ((struct udadevice *) reg)->udaip; /* initiate polling */ mp = &udaslavereply; timeout = mfpr(TODR) + 1000; while (mfpr(TODR) < timeout) if (mp->mscp_opcode) goto gotit; printf("uda%d: no response to Get Unit Status request\n", um->um_ctlr); sc->sc_flags &= ~SC_INSLAVE; return (0); gotit: sc->sc_flags &= ~SC_INSLAVE; /* * 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. */ return (0); 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("uda%d: unit %d off line: ", um->um_ctlr, mp->mscp_unit); mscp_printevent(mp); goto try_another; } break; default: printf("uda%d: unable to get unit status: ", um->um_ctlr); mscp_printevent(mp); return (0); } /* * Does this ever happen? What (if anything) does it mean? */ if (mp->mscp_unit < next) { printf("uda%d: unit %d, next %d\n", um->um_ctlr, mp->mscp_unit, next); return (0); } if (mp->mscp_unit >= MAXUNIT) { printf("uda%d: cannot handle unit number %d (max is %d)\n", um->um_ctlr, mp->mscp_unit, MAXUNIT - 1); return (0); } /* * See if we already handle this drive. * (Only likely if ui->ui_slave=='?'.) */ if (udaip[um->um_ctlr][mp->mscp_unit] != NULL) goto try_another; /* * Make sure we know about this kind of drive. * Others say we should treat unknowns as RA81s; I am * not sure this is safe. */ type = mp->mscp_guse.guse_drivetype; if (type >= NTYPES || udatypes[type].ut_name == 0) { printf("uda%d: unit %d (media ID `", um->um_ctlr, mp->mscp_unit); uda_decode_media(mp->mscp_guse.guse_mediaid); printf("') is of unknown type %d; ignored\n", type); try_another: if (ui->ui_slave != '?') return (0); next = mp->mscp_unit + 1; goto findunit; } #if GYRE if (ISCDC(sc)) type = CDCTYPE; #endif /* * Voila! */ ui->ui_type = type; ui->ui_flags = 0; /* not on line, nor anything else */ ui->ui_slave = mp->mscp_unit; return (1); } X/* * Decode and print the media ID. It is made up of five 5-bit * `characters' and 7 bits of numeric information. BITS(i) * selects character i's bits; CHAR returns the corresponding * character. */ uda_decode_media(id) register long id; { int c4, c3, c2, c1, c0; #define BITS(i) ((id >> ((i) * 5 + 7)) & 0x1f) #define CHAR(c) ((c) ? (c) + '@' : ' ') c4 = BITS(4); c3 = BITS(3); c2 = BITS(2); c1 = BITS(1); c0 = BITS(0); printf("%c%c %c%c%c%d", CHAR(c4), CHAR(c3), CHAR(c2), CHAR(c1), CHAR(c0), id & 0x7f); #undef BITS #undef CHAR } X/* * Attach a found slave. Make sure the watchdog timer is running. * If this disk is being profiled, fill in the `mspw' value (used by * what?). Set up the inverting pointer, and attempt to bring the * drive on line. */ udaattach(ui) register struct uba_device *ui; { if (udawstart == 0) { timeout(udawatch, (caddr_t) 0, hz); udawstart++; } if (ui->ui_dk >= 0) dk_mspw[ui->ui_dk] = 1.0 / (60 * 31 * 256); /* approx */ udaip[ui->ui_ctlr][ui->ui_slave] = ui; (void) uda_bringonline(&uda_softc[ui->ui_ctlr], ui, 1); /* should we get its status too? */ } X/* * Initialise a UDA50. Return true iff something goes wrong. */ udainit(ctlr) int ctlr; { register struct uda_softc *sc; register struct udadevice *udaddr; struct uba_ctlr *um; int timo, ubinfo; sc = &uda_softc[ctlr]; um = udaminfo[ctlr]; if ((sc->sc_flags & SC_MAPPED) == 0) { /* * Map the communication area and command and * response packets into Unibus space. */ ubinfo = uballoc(um->um_ubanum, (caddr_t) &uda[ctlr], sizeof (struct uda), UBA_CANTWAIT); if (ubinfo == 0) { printf("uda%d: uballoc map failed\n", ctlr); return (-1); } sc->sc_uda = (struct uda *) (ubinfo & 0x3ffff); sc->sc_flags |= SC_MAPPED; } /* * While we are thinking about it, reset the next command * and response indicies. */ sc->sc_mi.mi_cmd.mri_next = 0; sc->sc_mi.mi_rsp.mri_next = 0; /* * Start up the hardware initialisation sequence. */ #define STEP0MASK (UDA_ERR | UDA_STEP4 | UDA_STEP3 | UDA_STEP2 | \ UDA_STEP1 | UDA_NV) sc->sc_state = ST_IDLE; /* in case init fails */ udaddr = (struct udadevice *) um->um_addr; udaddr->udaip = 0; timo = mfpr(TODR) + 1000; while ((udaddr->udasa & STEP0MASK) == 0) { if (mfpr(TODR) > timo) { printf("uda%d: timeout during init\n", ctlr); return (-1); } } if ((udaddr->udasa & STEP0MASK) != UDA_STEP1) { printf("uda%d: init failed, sa=%b\n", ctlr, udaddr->udasa, udasr_bits); return (-1); } /* * Success! Record new state, and start step 1 initialisation. * The rest is done in the interrupt handler. */ sc->sc_state = ST_STEP1; udaddr->udasa = UDA_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) | UDA_IE | (sc->sc_ivec >> 2); return (0); } X/* * Open a drive. */ X/*ARGSUSED*/ udaopen(dev, flag) dev_t dev; int flag; { register int unit; register struct uba_device *ui; register struct uda_softc *sc; int s; /* * Make sure this is a reasonable open request. */ unit = udaunit(dev); if (unit >= NRA || (ui = udadinfo[unit]) == 0 || ui->ui_alive == 0) return (ENXIO); /* * Make sure the controller is running, by (re)initialising it if * necessary. */ sc = &uda_softc[ui->ui_ctlr]; s = spl5(); if (sc->sc_state != ST_RUN) { if (sc->sc_state == ST_IDLE && udainit(ui->ui_ctlr)) { splx(s); return (EIO); } /* * In case it does not come up, make sure we will be * restarted in 10 seconds. This corresponds to the * 10 second timeouts in udaprobe() and udaslave(). */ sc->sc_flags |= SC_DOWAKE; timeout(wakeup, (caddr_t) sc, 10 * hz); sleep((caddr_t) sc, PRIBIO); if (sc->sc_state != ST_RUN) { splx(s); printf("uda%d: controller hung\n", ui->ui_ctlr); return (EIO); } untimeout(wakeup, (caddr_t) sc); } if ((ui->ui_flags & UNIT_ONLINE) == 0) { /* * Bring the drive on line so we can find out how * big it is. If it is not spun up, it will not * come on line; this cannot really be considered * an `error condition'. */ if (uda_bringonline(sc, ui, 0)) { splx(s); printf("ra%d: drive will not come on line\n", unit); return (EIO); } } splx(s); return (0); } X/* * Bring a drive on line. In case it fails to respond, we set * a timeout on it. The `nosleep' parameter should be set if * we are to spin-wait; otherwise this must be called at spl5(). */ uda_bringonline(sc, ui, nosleep) register struct uda_softc *sc; register struct uba_device *ui; int nosleep; { register struct mscp *mp; int i; if (nosleep) { mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT); if (mp == NULL) return (-1); } else mp = mscp_getcp(&sc->sc_mi, MSCP_WAIT); mp->mscp_opcode = M_OP_ONLINE; mp->mscp_unit = ui->ui_slave; mp->mscp_cmdref = (long) &ui->ui_flags; *mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT; i = ((struct udadevice *) ui->ui_addr)->udaip; if (nosleep) { i = mfpr(TODR) + 1000; while ((ui->ui_flags & UNIT_ONLINE) == 0) if (mfpr(TODR) > i) return (-1); } else { timeout(wakeup, (caddr_t) &ui->ui_flags, 10 * hz); sleep((caddr_t) &ui->ui_flags, PRIBIO); if ((ui->ui_flags & UNIT_ONLINE) == 0) return (-1); untimeout(wakeup, (caddr_t) &ui->ui_flags); } return (0); /* made it */ } X/* * 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. */ udastrategy(bp) register struct buf *bp; { register int unit; register struct uba_device *ui; register struct size *st; daddr_t sz, maxsz; /* * Make sure this is a reasonable drive to use. */ if ((unit = udaunit(bp->b_dev)) >= NRA || (ui = udadinfo[unit]) == NULL || ui->ui_alive == 0) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; biodone(bp); return; } /* * Determine the size of the transfer, and make sure it is * within the boundaries of the drive. */ sz = (bp->b_bcount + 511) >> 9; st = &udatypes[ui->ui_type].ut_sizes[udapart(bp->b_dev)]; if ((maxsz = st->nblocks) < 0) maxsz = ra_dsize[unit] - st->blkoff; if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz || st->blkoff >= ra_dsize[unit]) { /* if exactly at end of disk, return an EOF */ if (bp->b_blkno == maxsz) bp->b_resid = bp->b_bcount; else { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; } biodone(bp); return; } udastrat1(bp); } X/* * Work routine for udastrategy. */ udastrat1(bp) register struct buf *bp; { register int unit = udaunit(bp->b_dev); register struct uba_ctlr *um; register struct buf *dp; struct uba_device *ui; int s = spl5(); /* * 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.) */ um = (ui = udadinfo[unit])->ui_mi; dp = &udautab[unit]; APPEND(bp, dp, av_forw); if (dp->b_active == 0 && (ui->ui_flags & UNIT_REQUEUE) == 0) { APPEND(dp, &um->um_tab, b_forw); dp->b_active++; } /* * Start activity on the controller. Note that unlike other * Unibus drivers, we must always do this, not just when the * controller is not active. */ udastart(um); splx(s); } X/* * Start up whatever transfers we can find. * Note that udastart() must be called at spl5(). */ udastart(um) register struct uba_ctlr *um; { register struct uda_softc *sc = &uda_softc[um->um_ctlr]; register struct buf *bp, *dp; register struct mscp *mp; struct uba_device *ui; struct udadevice *udaddr; int i; #ifdef lint i = 0; i = i; #endif /* * If it is not running, try (again and again...) to initialise * it. If it is currently initialising just ignore it for now. */ if (sc->sc_state != ST_RUN) { if (sc->sc_state == ST_IDLE && udainit(um->um_ctlr)) printf("uda%d: still hung\n", um->um_ctlr); return; } /* * If um_cmd is nonzero, this controller is on the Unibus * resource wait queue. It will not help to try more requests; * instead, when the Unibus unblocks and calls udadgo(), we * will call udastart() again. */ if (um->um_cmd) return; sc->sc_flags |= SC_INSTART; udaddr = (struct udadevice *) um->um_addr; loop: /* * Service the drive at the head of the queue. We take exactly * one transfer from this drive, then move it to the end of the * controller queue, so as to get more drive overlap. */ if ((dp = um->um_tab.b_actf) == NULL) { um->um_tab.b_active = 0; goto out; } /* * Get the first request from the drive queue. There must be * one, or something is rotten. */ if ((bp = dp->b_actf) == NULL) panic("udastart: bp==NULL\n"); if (udaddr->udasa & UDA_ERR) { /* ctlr fatal error */ udasaerror(um); goto out; } /* * Get an MSCP packet, then figure out what to do. If * we cannot get a command packet, the command ring may * be too small: We should have at least as many command * packets as credits, for best performance. */ if ((mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT)) == NULL) { if (sc->sc_mi.mi_credits > MSCP_MINCREDITS && (sc->sc_flags & SC_GRIPED) == 0) { log(LOG_NOTICE, "uda%d: command ring too small\n", um->um_ctlr); sc->sc_flags |= SC_GRIPED;/* complain only once */ } goto out; } /* * Mark the controller active, but do not move the drive off * the controller queue: ubago() wants it there. */ um->um_tab.b_active = 1; /* * Bring the drive on line if it is not already. Get its status * if we do not already have it. Otherwise just start the transfer. */ ui = udadinfo[udaunit(bp->b_dev)]; if ((ui->ui_flags & UNIT_ONLINE) == 0) { mp->mscp_opcode = M_OP_ONLINE; goto common; } if ((ui->ui_flags & UNIT_HAVESTATUS) == 0) { mp->mscp_opcode = M_OP_GETUNITST; common: if (ui->ui_flags & UNIT_REQUEUE) panic("udastart"); /* * Take the drive off the controller queue. When the * command finishes, make sure the drive is requeued. */ um->um_tab.b_actf = dp->b_forw; dp->b_active = 0; ui->ui_flags |= UNIT_REQUEUE; mp->mscp_unit = ui->ui_slave; *mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT; i = udaddr->udaip; goto loop; } mp->mscp_opcode = (bp->b_flags & B_READ) ? M_OP_READ : M_OP_WRITE; mp->mscp_unit = ui->ui_slave; mp->mscp_seq.seq_lbn = bp->b_blkno + udatypes[ui->ui_type].ut_sizes[udapart(bp->b_dev)].blkoff; mp->mscp_seq.seq_bytecount = bp->b_bcount; /* mscp_cmdref and mscp_buffer are filled in by mscp_go() */ /* * Drop the packet pointer into the `command' field so udadgo() * can tell what to start. If ubago returns 1, we can do another * transfer. If not, um_cmd will still point at mp, so we will * know that we are waiting for resources. */ um->um_cmd = (int) mp; if (ubago(ui)) goto loop; /* * All done, or blocked in ubago(). */ out: sc->sc_flags &= ~SC_INSTART; } X/* * Start a transfer. * * If we are not called from within udastart(), we must have been * blocked, so call udastart to do more requests (if any). If * this calls us again immediately we will not recurse, because * that time we will be in udastart(). Clever.... */ udadgo(um) register struct uba_ctlr *um; { struct uda_softc *sc = &uda_softc[um->um_ctlr]; int i; #ifdef lint i = 0; i = i; #endif /* * 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 initiate polling. */ mscp_go(um, &sc->sc_mi, (struct mscp *) um->um_cmd); um->um_cmd = 0; i = ((struct udadevice *) um->um_addr)->udaip; if ((sc->sc_flags & SC_INSTART) == 0) udastart(um); } X/* * The error bit was set in the controller status register. Gripe, * reset the controller, requeue pending transfers. */ udasaerror(um) register struct uba_ctlr *um; { printf("uda%d: controller error, sa=%b\n", um->um_ctlr, ((struct udadevice *) um->um_addr)->udasa, udasr_bits); mscp_requeue(&uda_softc[um->um_ctlr].sc_mi); (void) udainit(um->um_ctlr); } X/* * Interrupt routine. Depending on the state of the controller, * continue initialisation, or acknowledge command and response * interrupts, and process responses. */ udaintr(ctlr) int ctlr; { register struct uba_ctlr *um = udaminfo[ctlr]; register struct uda_softc *sc = &uda_softc[ctlr]; register struct udadevice *udaddr = (struct udadevice *) um->um_addr; register struct uda *ud; register struct mscp *mp; register int i; sc->sc_wticks = 0; /* reset interrupt watchdog */ /* * Combinations during steps 1, 2, and 3: STEPnMASK * corresponds to which bits should be tested; * STEPnGOOD corresponds to the pattern that should * appear after the interrupt from STEPn initialisation. * All steps test the bits in ALLSTEPS. */ #define ALLSTEPS (UDA_ERR | UDA_STEP4 | UDA_STEP3 | UDA_STEP2 | \ UDA_STEP1 | UDA_IE) #define STEP1MASK (ALLSTEPS | UDA_NCNRMASK) #define STEP1GOOD (UDA_STEP2 | UDA_IE | (NCMDL2 << 3) | NRSPL2) #define STEP2MASK (ALLSTEPS | UDA_IVECMASK) #define STEP2GOOD (UDA_STEP3 | UDA_IE | (sc->sc_ivec >> 2)) #define STEP3MASK ALLSTEPS #define STEP3GOOD UDA_STEP4 switch (sc->sc_state) { case ST_IDLE: /* * Ignore unsolicited interrupts. */ log(LOG_WARNING, "uda%d: stray intr\n", ctlr); return; case ST_STEP1: /* * Begin step two initialisation. */ if ((udaddr->udasa & STEP1MASK) != STEP1GOOD) { i = 1; initfailed: printf("uda%d: init step %d failed, sa=%b\n", ctlr, i, udaddr->udasa, udasr_bits); sc->sc_state = ST_IDLE; if (sc->sc_flags & SC_DOWAKE) { sc->sc_flags &= ~SC_DOWAKE; wakeup((caddr_t) sc); } return; } udaddr->udasa = (int) &sc->sc_uda->uda_ca.ca_rspdsc[0] | (cpu == VAX_780 || cpu == VAX_8600 ? UDA_PI : 0); sc->sc_state = ST_STEP2; return; case ST_STEP2: /* * Begin step 3 initialisation. */ if ((udaddr->udasa & STEP2MASK) != STEP2GOOD) { i = 2; goto initfailed; } udaddr->udasa = ((int) &sc->sc_uda->uda_ca.ca_rspdsc[0]) >> 16; sc->sc_state = ST_STEP3; return; case ST_STEP3: /* * Set controller characteristics (finish initialisation). */ if ((udaddr->udasa & STEP3MASK) != STEP3GOOD) { i = 3; goto initfailed; } i = udaddr->udasa & 0xff; if (i != sc->sc_micro) { sc->sc_micro = i; printf("uda%d: version %d model %d\n", ctlr, i & 0xf, i >> 4); } /* * Present the burst size, then remove it. Why this * should be done this way, I have no idea. * * Note that this assumes udaburst[ctlr] > 0. */ udaddr->udasa = UDA_GO | (udaburst[ctlr] - 1) << 2; udaddr->udasa = UDA_GO; printf("uda%d: DMA burst size set to %d\n", ctlr, udaburst[ctlr]); udainitds(ctlr); /* initialise data structures */ /* * Before we can get a command packet, we need some * credits. Fake some up to keep mscp_getcp() happy, * get a packet, and cancel all credits (the right * number should come back in the response to the * SCC packet). */ sc->sc_mi.mi_credits = MSCP_MINCREDITS + 1; mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT); if (mp == NULL) /* `cannot happen' */ panic("udaintr"); sc->sc_mi.mi_credits = 0; mp->mscp_opcode = M_OP_SETCTLRC; /* * WHICH OF THE FOLLOWING ARE REQUIRED?? * IT SURE WOULD BE NICE IF DEC SOLD DOCUMENTATION * FOR THEIR OWN CONTROLLERS. */ mp->mscp_unit = 0; mp->mscp_flags = 0; mp->mscp_modifier = 0; mp->mscp_seq.seq_bytecount = 0; mp->mscp_seq.seq_buffer = 0; mp->mscp_sccc.sccc_errlgfl = 0; mp->mscp_sccc.sccc_copyspd = 0; mp->mscp_sccc.sccc_ctlrflags = M_CF_ATTN | M_CF_MISC | M_CF_THIS; *mp->mscp_sccc.sccc_addr |= MSCP_OWN | MSCP_INT; i = udaddr->udaip; sc->sc_state = ST_SETCHAR; return; case ST_SETCHAR: case ST_RUN: /* * Handle Set Ctlr Characteristics responses and operational * responses (via mscp_dorsp). */ break; default: printf("uda%d: driver bug, state %d\n", ctlr, sc->sc_state); panic("udastate"); } if (udaddr->udasa & UDA_ERR) { /* ctlr fatal error */ udasaerror(um); return; } ud = &uda[ctlr]; /* * Handle buffer purge requests. * I have never seen these to work usefully, thus the log(). */ if (ud->uda_ca.ca_bdp) { log(LOG_DEBUG, "uda%d: purge bdp %d\n", ctlr, ud->uda_ca.ca_bdp); UBAPURGE(um->um_hd->uh_uba, ud->uda_ca.ca_bdp); ud->uda_ca.ca_bdp = 0; udaddr->udasa = 0; /* signal purge complete */ } /* * Check for response and command ring transitions. */ if (ud->uda_ca.ca_rspint) { ud->uda_ca.ca_rspint = 0; mscp_dorsp(&sc->sc_mi); } if (ud->uda_ca.ca_cmdint) { ud->uda_ca.ca_cmdint = 0; MSCP_DOCMD(&sc->sc_mi); } udastart(um); } X/* * Initialise the various data structures that control the UDA50. */ udainitds(ctlr) int ctlr; { register struct uda *ud = &uda[ctlr]; register struct uda *uud = uda_softc[ctlr].sc_uda; register struct mscp *mp; register int i; for (i = 0, mp = ud->uda_rsp; i < NRSP; i++, mp++) { ud->uda_ca.ca_rspdsc[i] = MSCP_OWN | MSCP_INT | (long) &uud->uda_rsp[i].mscp_cmdref; mp->mscp_seq.seq_addr = &ud->uda_ca.ca_rspdsc[i]; mp->mscp_msglen = MSCP_MSGLEN; } for (i = 0, mp = ud->uda_cmd; i < NCMD; i++, mp++) { ud->uda_ca.ca_cmddsc[i] = MSCP_INT | (long) &uud->uda_cmd[i].mscp_cmdref; mp->mscp_seq.seq_addr = &ud->uda_ca.ca_cmddsc[i]; mp->mscp_msglen = MSCP_MSGLEN; } } X/* * Handle an error datagram. All we do now is decode it. */ udadgram(um, mp) struct uba_ctlr *um; struct mscp *mp; { mscp_decodeerror(um, mp); } X/* * The Set Controller Characteristics command finished. * Record the new state of the controller. */ udactlrdone(um, mp) register struct uba_ctlr *um; struct mscp *mp; { register struct uda_softc *sc = &uda_softc[um->um_ctlr]; if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS) sc->sc_state = ST_RUN; else { printf("uda%d: SETCTLRC failed: ", um->um_ctlr, mp->mscp_status); mscp_printevent(mp); sc->sc_state = ST_IDLE; } um->um_tab.b_active = 0; if (sc->sc_flags & SC_DOWAKE) { sc->sc_flags &= ~SC_DOWAKE; wakeup((caddr_t) sc); } } X/* * Received a response from an as-yet unconfigured drive. Configure it * in, if possible. */ udaunconf(um, mp) struct uba_ctlr *um; register struct mscp *mp; { /* * If it is a slave response, copy it to udaslavereply for * udaslave() to look at. */ if (mp->mscp_opcode == (M_OP_GETUNITST | M_OP_END) && (uda_softc[um->um_ctlr].sc_flags & SC_INSLAVE) != 0) { udaslavereply = *mp; return (MSCP_DONE); } /* * Otherwise, it had better be an available attention response. */ if (mp->mscp_opcode != M_OP_AVAILATTN) return (MSCP_FAILED); /* do what autoconf does */ return (MSCP_FAILED); /* not yet, arwhite, not yet */ } X/* * 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. */ udaonline(ui, mp) register struct uba_device *ui; struct mscp *mp; { register int type; wakeup((caddr_t) &ui->ui_flags); if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) { printf("uda%d: attempt to bring ra%d on line failed: ", ui->ui_ctlr, ui->ui_unit); mscp_printevent(mp); return (MSCP_FAILED); } type = mp->mscp_onle.onle_drivetype; if (type >= NTYPES || udatypes[type].ut_name == 0) { printf("uda%d: ra%d: unknown type %d\n", ui->ui_ctlr, ui->ui_unit, type); return (MSCP_FAILED); } #if GYRE /* special partition hack */ if (ISCDC(&uda_softc[ui->ui_ctlr])) type = CDCTYPE; #endif /* * Note any change of types. Not sure if we should do * something special about them, or if so, what.... */ if (type != ui->ui_type) { printf("ra%d: changed types! was %s\n", ui->ui_unit, udatypes[ui->ui_type].ut_name); ui->ui_type = type; } ra_dsize[ui->ui_unit] = (daddr_t) mp->mscp_onle.onle_unitsize; printf("ra%d: %s, size = %d sectors\n", ui->ui_unit, udatypes[type].ut_name, ra_dsize[ui->ui_unit]); return (MSCP_DONE); } X/* * We got some (configured) unit's status. Return DONE if it succeeded. */ udagotstatus(ui, mp) register struct uba_device *ui; register struct mscp *mp; { 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); } /* need to record later for bad block forwarding - for now, print */ printf("\ ra%d: unit %d, nspt %d, group %d, ntpc %d, rctsize %d, nrpt %d, nrct %d\n", ui->ui_unit, mp->mscp_unit, mp->mscp_guse.guse_nspt, mp->mscp_guse.guse_group, mp->mscp_guse.guse_ntpc, mp->mscp_guse.guse_rctsize, mp->mscp_guse.guse_nrpt, mp->mscp_guse.guse_nrct); return (MSCP_DONE); } X/* * A transfer failed. We get a chance to fix or restart it. * Need to write the bad block forwaring code first.... */ X/*ARGSUSED*/ udaioerror(ui, mp, bp) register struct uba_device *ui; register struct mscp *mp; struct buf *bp; { 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, 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); } return (MSCP_FAILED); } X/* * A replace operation finished. */ X/*ARGSUSED*/ udareplace(ui, mp) struct uba_device *ui; struct mscp *mp; { panic("udareplace"); } X/* * A bad block related operation finished. */ X/*ARGSUSED*/ udabb(ui, mp, bp) struct uba_device *ui; struct mscp *mp; struct buf *bp; { panic("udabb"); } X/* * Raw read request. */ udaread(dev, uio) dev_t dev; struct uio *uio; { register int unit = udaunit(dev); if (unit >= NRA) return (ENXIO); return (physio(udastrategy, &rudabuf[unit], dev, B_READ, minphys, uio)); } X/* * Raw write request. */ udawrite(dev, uio) dev_t dev; struct uio *uio; { register int unit = udaunit(dev); if (unit >= NRA) return (ENXIO); return (physio(udastrategy, &rudabuf[unit], dev, B_WRITE, minphys, uio)); } #ifdef notyet X/* * I/O controls. Not yet! */ udaioctl(dev, cmd, flag, data) dev_t dev; int cmd, flag; caddr_t data; { int error = 0; register int unit = udaunit(dev); if (unit >= NRA || uddinfo[unit] == NULL) return (ENXIO); switch (cmd) { case UDAIOCREPLACE: /* * Initiate bad block replacement for the given LBN. * (Should we allow modifiers?) */ error = EOPNOTSUPP; break; case UDAIOCGMICRO: /* * Return the microcode revision for the UDA50 running * this drive. */ *(int *) data = uda_softc[uddinfo[unit]->ui_ctlr].sc_micro; break; case UDAIOCGSIZE: /* * Return the size (in 512 byte blocks) of this * disk drive. */ *(daddr_t *) data = ra_dsize[unit]; break; default: error = EINVAL; break; } return (error); } #endif X/* * A Unibus reset has occurred on UBA uban. Reinitialise the controller(s) * on that Unibus, and requeue outstanding I/O. */ udareset(uban) int uban; { register struct uba_ctlr *um; register struct uba_device *ui; register struct uda_softc *sc; register int unit, ctlr; struct buf *dp; for (ctlr = 0, sc = uda_softc; ctlr < NUDA; ctlr++, sc++) { if ((um = udaminfo[ctlr]) == NULL || um->um_ubanum != uban || um->um_alive == 0) continue; printf(" uda%d", ctlr); /* * Our BDP (if any) is gone; our command (if any) is * flushed; the device is no longer mapped; and the * UDA50 is not yet initialised. */ if (um->um_bdp) { printf("<%d>", (um->um_bdp >> 28) & 15); um->um_bdp = 0; } um->um_ubinfo = 0; um->um_cmd = 0; sc->sc_flags &= ~SC_MAPPED; sc->sc_state = ST_IDLE; /* reset queues and requeue pending transfers */ mscp_requeue(&sc->sc_mi); /* * If it fails to initialise we will notice later and * try again (and again...). Do not call udastart() * here; it will be done after the controller finishes * initialisation. */ if (udainit(ctlr)) printf(" (hung)"); } } X/* * Watchdog timer: If the controller is active, and no interrupts * have occurred for 30 seconds, assume it has gone away. */ udawatch() { register int i, unit; register struct uba_ctlr *um; register struct uda_softc *sc; timeout(udawatch, (caddr_t) 0, hz); /* every second */ for (i = 0, sc = uda_softc; i < NUDA; i++, sc++) { if ((um = udaminfo[i]) == 0 || um->um_alive == 0) continue; if (um->um_tab.b_active || sc->sc_mi.mi_wtab.av_forw != &sc->sc_mi.mi_wtab || sc->sc_state != ST_IDLE && sc->sc_state != ST_RUN) goto active; for (unit = 0; unit < NRA; unit++) if (udautab[unit].b_active && udadinfo[unit]->ui_mi == um) goto active; sc->sc_wticks = 0; continue; active: if (++sc->sc_wticks >= 30) { sc->sc_wticks = 0; printf("uda%d: lost interrupt\n", i); ubareset(um->um_ubanum); } } } X/* * 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; #define DBSIZE 32 /* dump 16K at a time */ udadump(dev) dev_t dev; { struct udadevice *udaddr; struct uda1 *ud_ubaddr; char *start; int num, blk, unit, maxsz, blkoff, reg; register struct uba_regs *uba; register struct uba_device *ui; register struct uda1 *ud; register struct pte *io; register 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_hd *, ui->ui_hd)->uh_physuba; ubainit(uba); 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 = &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); uda_softc[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); /* * Pick up the drive type from the on line end packet; * convert that to a dump area size and a disk offset. */ i = ud->uda1_rsp.mscp_onle.onle_drivetype; if (i >= NTYPES || udatypes[i].ut_name == 0) { printf("disk type %d unknown\ndump "); return (EINVAL); } #if GYRE if (ISCDC(&uda_softc[ui->ui_ctlr])) i = CDCTYPE; #endif printf("on %s ", udatypes[i].ut_name); maxsz = udatypes[i].ut_sizes[udapart(dev)].nblocks; blkoff = udatypes[i].ut_sizes[udapart(dev)].blkoff; /* * Dump all of physical memory, or as much as will fit in the * space provided. */ start = 0; num = maxfree; if (dumplo < 0) return (EINVAL); 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 = 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! */ } X/* * 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). */ udadumpwait(udaddr, bits) register struct udadevice *udaddr; register int bits; { register int timo = mfpr(TODR) + 1000; while ((udaddr->udasa & bits) == 0) { if (udaddr->udasa & UDA_ERR) { printf("udasa=%b\ndump ", udaddr->udasa, udasr_bits); return (1); } if (mfpr(TODR) >= timo) { printf("timeout\ndump "); return (1); } } return (0); } X/* * Feed a command to the UDA50, wait for its response, and return * true iff something went wrong. */ udadumpcmd(op, ud, ui) int op; register struct uda1 *ud; struct uba_device *ui; { register struct udadevice *udaddr; register 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 = mfpr(TODR) + 1000; for (;;) { if (mfpr(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(ui->ui_mi, 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); } return (0); #undef mp } X/* * Return the size of a partition, if known, or -1 if not. */ udasize(dev) dev_t dev; { register int unit = udaunit(dev); register struct uba_device *ui; register struct size *st; if (unit >= NRA || (ui = udadinfo[unit]) == NULL || ui->ui_alive == 0) return (-1); st = &udatypes[ui->ui_type].ut_sizes[udapart(dev)]; if (st->nblocks == -1) { int s = spl5(); /* * We need to have the drive on line to find the size * of this particular partition. * IS IT OKAY TO GO TO SLEEP IN THIS ROUTINE? * (If not, better not page on one of these...) */ if ((ui->ui_flags & UNIT_ONLINE) == 0) { if (uda_bringonline(&uda_softc[unit], ui, 0)) { splx(s); return (-1); } } splx(s); if (st->blkoff > ra_dsize[unit]) return (-1); return (ra_dsize[unit] - st->blkoff); } return (st->nblocks); } #endif //go.sysin dd * if [ `wc -c < vaxuba/uda.c` != 48872 ]; then made=FALSE echo error transmitting vaxuba/uda.c -- echo length should be 48872, not `wc -c < vaxuba/uda.c` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vaxuba/uda.c echo -n ' '; ls -ld vaxuba/uda.c fi echo Extracting vaxuba/udareg.h sed 's/^X//' <<'//go.sysin dd *' >vaxuba/udareg.h X/* * UDA50 registers and structures */ X/* * Writing any value to udaip starts initialisation. Reading from it * when the UDA is running makes the UDA look through the command ring * to find any new commands. Reading udasa gives status; writing it * during initialisation sets things up. */ struct udadevice { u_short udaip; /* initialisation and polling */ u_short udasa; /* status and address */ }; X/* * Bits in UDA status register during initialisation */ #define UDA_ERR 0x8000 /* error */ #define UDA_STEP4 0x4000 /* step 4 has started */ #define UDA_STEP3 0x2000 /* step 3 has started */ #define UDA_STEP2 0x1000 /* step 2 has started */ #define UDA_STEP1 0x0800 /* step 1 has started */ #define UDA_NV 0x0400 /* no host settable interrupt vector */ #define UDA_QB 0x0200 /* controller supports Q22 bus */ #define UDA_DI 0x0100 /* controller implements diagnostics */ #define UDA_IE 0x0080 /* interrupt enable */ #define UDA_NCNRMASK 0x003f /* in STEP1, bits 0-2=NCMDL2, 3-5=NRSPL2 */ #define UDA_IVECMASK 0x007f /* in STEP2, bits 0-6 are interruptvec / 4 */ #define UDA_PI 0x0001 /* host requests adapter purge interrupts */ X/* * Bits in UDA status register after initialisation */ #define UDA_GO 0x0001 /* run */ #define UDASR_BITS \ "\20\20ERR\17STEP4\16STEP3\15STEP2\14STEP1\13NV\12QB\11DI\10IE\1GO" X/* * UDA Communications Area. Note that this structure definition * requires NRSP and NCMD to be defined already. */ struct udaca { 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 */ }; X/* * 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 uda1ca { short ca_xxx1; char ca_xxx2; char ca_bdp; short ca_cmdint; short ca_rspint; long ca_rspdsc; long ca_cmddsc; }; //go.sysin dd * if [ `wc -c < vaxuba/udareg.h` != 2112 ]; then made=FALSE echo error transmitting vaxuba/udareg.h -- echo length should be 2112, not `wc -c < vaxuba/udareg.h` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 vaxuba/udareg.h echo -n ' '; ls -ld vaxuba/udareg.h fi made=TRUE if [ $made = TRUE ]; then chmod 755 vaxuba echo -n ' '; ls -ld vaxuba fi -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu