/* * Datakit driver * KMC assistance, with or without DR11C * @(#)dkit_kmc.c 1.4 (Berkeley) 12/16/90 */ #include "dkitkmc.h" #if NDKITKMC>0 #include "datakit.h" #include "../include/pte.h" #include "sys/param.h" #include "sys/syslog.h" #include "sys/time.h" #include "sys/kernel.h" #include "sys/buf.h" #include "sys/mbuf.h" #include "sys/errno.h" #include "sys/socket.h" #include "net/if.h" #include "../if/if_uba.h" #include "../uba/ubareg.h" #include "../uba/ubavar.h" #include "dkit.h" #include "dkkmc.h" #include "dk.h" #define KMXSMALL 0 #define KMXBIG 1 #define MONITOR 1 #ifdef MONITOR static int dummy ; int *DKP = &dummy ; #define M_ON(a) *DKP |= (a) #define M_OFF(a) *DKP &= ~(a) #define M_TRACE(a) *DKP |= (a);*DKP &= ~(a) #define Mxmit 01 #define Mrecv 02 #define Mkint 04 #define Mint 010 #define Mcmd 020 #else #define M_ON(a) #define M_OFF(a) #define M_TRACE(a) #endif extern int dk_nchan; struct dkchan dkit[NDATAKIT]; #define DKNCMDB 20 #define DKNSTB 20 int dkk_ncmd = DKNCMDB; struct dkkin dkkcmdbuf[DKNCMDB]; int dkk_nstat = DKNSTB; struct dkkin dkkstat[DKNSTB]; char dkkbuf[16*1024]; int dkubmbuf; static struct kdevice *dkkaddr; /* * initial information to the KMC */ struct dkinit { caddr_t cmdaddr; /* command buffer */ caddr_t stataddr; /* status buffer */ caddr_t bufaddr ; /* kmc workspace */ caddr_t csraddr; /* for KMC/DR DR register address */ } dkkmcinit; /* * structure of data in first mbuf on chain (type DKM_HDR) * */ struct mpacket { short mp_len; /* Total length left */ char mp_ctl; /* Control character */ int mp_eob; /* Send end-of-block indicator */ int (*mp_endfcn)(); /* End-action function */ caddr_t mp_endparm; /* Parameter to above function */ }; /* * The circular buffer, cmdbuf, is used to pass command to kmc: * while the circular buffer statbuf is used to report status. * There are 8 control and status registers (csr) accessible to * both cpu and kmc. * Csr4-csr5 are used to indicate the head and tail respectively * of the cmdbuf. Likewise, csr6-csr7 for statbuf. * At initialization time, the cpu and kmc would agree on the beginning * address of both buffers and their sizes. */ #define csr0 dkkaddr->un.bsel.bsel0 /* kmc state */ #define csr1 dkkaddr->un.bsel.bsel1 #define csr2 dkkaddr->un.bsel.bsel2 /* used at init time, to pass */ #define csr3 dkkaddr->un.bsel.bsel3 /* addresses to the kmc */ #define csr4 dkkaddr->un.bsel.bsel4 /* head cmdbuf */ #define csr5 dkkaddr->un.bsel.bsel5 /* tail cmdbuf */ #define csr6 dkkaddr->un.bsel.bsel6 /* head statbuf */ #define csr7 dkkaddr->un.bsel.bsel7 /* tail statbuf */ /* * kmc device registers */ struct kdevice { union { struct { short sel0, sel2, sel4, sel6;} wsel ; struct { char bsel0, bsel1, bsel2, bsel3; char bsel4, bsel5, bsel6, bsel7; } bsel ; } un; }; /* * For the moment, only support one kmc (kmc0) * More need be done for multiple kmc's */ int dkdebug = 512 ; int dkactive = 0 ; int dkbindex ; static int kseqchk = 0; /* used to check seq. #'s in statbuf */ static int dkpanic = 0; /* # of dk_close(0)'s in this run */ static int kseq = 0; /* # kmc responses mod 0377 */ static int pseq = 0; static struct dkkin *cmd4; /* dkkcmdbuf[csr4] pointer */ static struct dkkin *stat7; /* dkkstat[csr7] pointer */ int dkk_cnt ; static struct uba_device *ui; dkkmc_attach(kui) struct uba_device *kui; { ui = kui; dkk_cnt = kui->ui_unit; } dk_init() { int t, kt ; extern dkkint() ; /* * On the first open of the hardware interface */ if (!ui) return -ENXIO; if (kmcset((dkk_cnt)<<6,03,dkkint)) { /* debug */ log(LOG_ERR, "dkkmcinit bad: kmcset failed\n"); return -1; } dkkaddr = ((struct kdevice *) ui->ui_addr); /* if csr0 != 0, then error else pass the address of struct init in csr2~4 and set csr0 to 1 */ if ((csr0 & 3) != 0) { /* debug */ log(LOG_ERR, "dkkmcinit: csr0 != 0\n"); return EIO; } /* Map UBA registers to point to our stuff */ kt = dk_ubainit(); if (kt == 0) { log(LOG_ERR, "dkkmcinit: no uba resources\n"); return ENOBUFS; } /* Address of DR11-C (if any) */ t = ui->ui_flags & ~03 ; dkkmcinit.csraddr = (caddr_t) ((t<<16) + 3) ; /* bits 17 + 18 must be 1 */ /* append new init info here, if it is needed */ dkkaddr->un.wsel.sel2 = (short)(UBAI_ADDR(kt) & 0xFFFF);/* bits 0-15 */ dkkaddr->un.bsel.bsel4 = (char)(UBAI_ADDR(kt) >> 16); /* bits 16-17 */ csr0 = 1; /* tell KMC to read csr2 */ kseq = 0 ; cmd4 = &dkkcmdbuf[0] ; /* driver's pointers into cmdbuf and statbuf */ stat7 = &dkkstat[0] ; dkactive = 1 ; return 0 ; } int dk_ubaed = 0; dk_ubainit() { int t; static int kt; if (dk_ubaed) { if (dkdebug < dk_nchan) log(LOG_ERR, "dk_ubainit: reinit\n"); return kt; } dk_ubaed = 1; /* Initialization buffer */ kt = uballoc(ui->ui_ubanum, (caddr_t) &dkkmcinit, sizeof dkkmcinit, UBA_CANTWAIT); if (kt == 0) return 0; /* Command input buffer */ t = uballoc(ui->ui_ubanum, (caddr_t) dkkcmdbuf, sizeof dkkcmdbuf, UBA_CANTWAIT) ; if (t == 0) return 0; dkkmcinit.cmdaddr = (caddr_t)((UBAI_ADDR(t)<<16) | ((UBAI_ADDR(t)>>16))); /* must swap bytes for unibus */ /* Status out buffer */ t = uballoc(ui->ui_ubanum, (caddr_t) dkkstat, sizeof dkkstat, UBA_CANTWAIT); if (t == 0) return 0; dkkmcinit.stataddr = (caddr_t)((UBAI_ADDR(t)<<16) | (UBAI_ADDR(t)>>16)); /* KMC buffer */ dkubmbuf = uballoc(ui->ui_ubanum, (caddr_t) dkkbuf, sizeof dkkbuf, UBA_CANTWAIT); if (dkubmbuf == 0) return 0; dkkmcinit.bufaddr = (caddr_t) ((UBAI_ADDR(dkubmbuf)<<16) | (UBAI_ADDR(dkubmbuf)>>16)); if (dkdebug < dk_nchan) log(LOG_ERR, "dk_ubainit: bufaddr %x mapped %x\n", (caddr_t)dkkbuf, dkubmbuf); return kt; } dk_open(chan, supfcn) register chan ; int (*supfcn)() ; { register struct dkchan *dkp; register s ; extern dkkint() ; extern int commchan; int init; if (chan >= dk_nchan) return -ENXIO ; if (dkactive == -1) /* fail request if reset is in progress */ return(-ENETRESET) ; dkp = &dkit[chan] ; s = splimp() ; /* * Channel 0 (0-3 in ISN) is reserved for maintenance. * An open on channel 0 is interpreted as a request * for an unused channel. Channel 1 (4 in ISN or RADIAN) * is the common supervisory channel. */ if (chan == 0) { chan = commchan+1; /* Start above commchan */ while (1) { dkp = &dkit[chan] ; if (dkp->dk_state == 0) break ; chan++ ; if (chan >= dk_nchan) { splx(s) ; return -EADDRNOTAVAIL ; } } } splx(s) ; if (dkactive == 0) if ((init = dk_init()) < 0) return init ; /* * On first use of a channel, we must * allocate per-channel UBA resource for transmit. * Some day, when we convert the receivers to use mbufs, * we'll do the same for reads. * Note that these resources can't easily be freed (I think). */ if (!dkp->dk_uba.ifu_uba) { dkp->dk_uba.ifu_flags = UBA_CANTWAIT; if (if_ubaminit(&dkp->dk_uba.ifu_info, ui->ui_ubanum, 0, btoc(CLBYTES), &dkp->dk_uba.ifu_r, 0, &dkp->dk_uba.ifu_xmt, 1) == 0) { log(LOG_ERR, "dkkmc: no ubamap for channel %d\n", chan); return -ENOBUFS; } dkp->dk_outq.ifq_maxlen = 20; } /* * Finish setting up dkp struct. */ if ((dkp->dk_state & DK_OPEN) ==0) { dkcmd(KC_XINIT, chan, (caddr_t)0, (unsigned) 0, KMXBIG, 0); flushall(dkp, 0); dkp->dk_state |= DK_OPEN; dkp->dk_state &= ~(DK_LINGR | DK_RESET); dkactive++ ; } dkp->dk_supfcn = supfcn ; return chan ; } /* * Close a channel: */ dk_close(chan) { register struct dkchan *dkp; register s ; int init; if (dkkaddr == 0) return(-ENODEV); /* if no init, can't close */ /* ie: can't do dkmaint */ s = splimp() ; dkp = &dkit[chan] ; if (chan == 0) { for (dkp = &dkit[1]; dkp < &dkit[dk_nchan]; dkp++) { if (dkp->dk_state & (DK_OPEN|DK_BUSY|DK_RCV)) { dkp->dk_state |= DK_RESET ; flushall(dkp, 0) ; dkp->dk_state = DK_RESET ; } } dkpanic++ ; kseq = 0 ; pseq = 0 ; #ifdef notdef if(dkubmbuf){ /* only deallocate mem if still allocated */ ubarelse(ui->ui_ubanum, &dkubmbuf); dkubmbuf = NULL; } #endif /* wait for protocols to close channels */ dkactive = -1 ; DELAY(4 * hz) ; /* do a dk_free for all channels */ for (dkp = &dkit[1]; dkp < &dkit[dk_nchan]; dkp++) { dkp->dk_state &= ~DK_LINGR ; } dkactive = 0 ; csr0 = 0 ; /* set kmc to idle mode */ if ((init = dk_init()) < 0) { splx(s) ; return init ; } } else { flushall(dkp, 0) ; dkp->dk_state = DK_LINGR ; /* set while UNIXP protocol closes up channel with DK */ } splx(s) ; return 0; } /* * Close phase 2 - mark available for reassignment */ dk_free(chan) { if (chan > dkdebug) log(LOG_ERR, "dk_free %d\n", chan) ; dkit[chan].dk_state &= ~(DK_LINGR | DK_RESET); } /* * Reset a channel * prevents further I/O until close */ dk_reset(chan) { register struct dkchan *dkp ; register s ; if (chan > dkdebug) log(LOG_ERR, "dk_reset %d\n", chan) ; s = splimp() ; dkp = &dkit[chan] ; dkp->dk_state |= DK_RESET ; flushall(dkp, 0) ; splx(s) ; } /* * Xmit a short control (interrupt) packet (max 2 bytes) */ dk_xint(chan, intr) { register s ; s = splimp() ; dkcmd(KC_SOI, chan, (caddr_t)0, (unsigned) intr, 0, 0) ; splx(s) ; return 0 ; } /* * Adjust window size */ /*ARGSUSED*/ dk_winsize(chan, win) struct diocxwin *win; { return EINVAL; /* For now... */ } /* * Xmit data on a channel */ dk_xmit(chan, m, eob, ctlchar, endfcn, endparm) struct mbuf *m ; int (*endfcn)() ; caddr_t endparm ; { register struct dkchan *dkp ; short s ; register struct mpacket *mbp ; register struct mbuf *mb; int len; M_ON(Mxmit) ; s = splimp() ; dkp = &dkit[chan] ; if ((dkp->dk_state & DK_RESET) || (mb = m_get(M_DONTWAIT,DKMT_HDR)) == NULL) { m_freem(m); splx(s) ; return 0 ; } mb->m_len = 0; mbp = mtod(mb, struct mpacket *); mbp->mp_endfcn = endfcn ; mbp->mp_endparm = endparm ; mbp->mp_eob = eob; mbp->mp_ctl = ctlchar; if ((dkp->dk_state & DK_BUSY) == 0) { dkp->dk_state |= DK_BUSY ; dkp->dk_obuf = mbp ; mb->m_next = NULL; len = if_wubaput(&dkp->dk_uba, m); dkcmd(KC_SEND, chan, (caddr_t) UBAI_ADDR(dkp->dk_uba.ifu_w.ifrw_info), (unsigned) len, eob ? SBOT : SBOTM, ctlchar) ; splx(s) ; M_OFF(Mxmit) ; return dkp->dk_state ; } mb->m_next = m; if (IF_QFULL(&dkp->dk_outq)) { IF_DROP(&dkp->dk_outq); m_freem(mb); } else IF_ENQUEUE(&dkp->dk_outq, mb); splx(s) ; M_OFF(Mxmit) ; return dkp->dk_state ; } /* * Receive into a block buffer */ dk_recv(chan, addr, len, mode, endfcn, endparm) int len; int (*endfcn)() ; caddr_t addr, endparm ; { register struct dkchan *dkp ; int s; s = splimp() ; M_ON(Mrecv) ; dkp = &dkit[chan] ; if (dkp->dk_state & (DK_RCV | DK_RESET)) { splx(s) ; return(0) ; } dkp->dk_ubmbase = uballoc(ui->ui_ubanum, addr, len, UBA_CANTWAIT); if (dkp->dk_ubmbase == NULL) { splx(s) ; return(0) ; } dkp->dk_state |= DK_RCV ; dkp->dk_endfcn = endfcn ; dkp->dk_endparm = endparm ; dkp->dk_rlen = len; dkcmd(KC_RCVB, chan, (caddr_t) UBAI_ADDR(dkp->dk_ubmbase), (unsigned) len, mode&0377, mode>>8); M_OFF(Mrecv) ; splx(s); return dkp->dk_state ; } /* Abort pending receive */ dk_rabort(chan, nendfcn, nendparm) int (*nendfcn)() ; caddr_t nendparm; { register struct dkchan *dkp ; register s ; dkp = &dkit[chan] ; s = splimp() ; if (dkp->dk_state & DK_RCV) { /* cancel outstanding receive */ dkcmd(KC_RCVB, chan, (caddr_t)0, (unsigned) 0, 0, 0) ; dkp->dk_endfcn = nendfcn ; dkp->dk_endparm = nendparm ; } splx(s) ; return dkp->dk_state ; } dk_status(chan) { if (chan >= dk_nchan) return 0 ; return dkit[chan].dk_state ; } /* * Various control commands to KMC */ dk_cmd(chan, cmd) { register struct dkchan *dkp ; register s ; dkp = &dkit[chan] ; if (cmd & (DKC_XINIT|DKC_FLUSH)) { /*for either command do the same thing: * reinit the transmitter and flush any pending output. * NOTE: for the kmc, there is no response to XINIT * and no send complete for flush */ s = splimp() ; dkcmd(KC_XINIT, chan, (caddr_t)0, (unsigned) 0, KMXBIG, 0) ; flushall(dkp, -1) ; dkcmd(KC_CMD, chan, (caddr_t)0, (unsigned) DKC_FLUSH, 0, 0) ; splx(s); cmd &= ~(DKC_XINIT|DKC_FLUSH) ; } if (cmd) dkcmd(KC_CMD, chan, (caddr_t)0, (unsigned) cmd, 0, 0) ; } /* * Note that flushall is often recursive when a tty driver * is involved. */ static flushall(dkp, rwflag) register struct dkchan *dkp ; { register s ; register struct mpacket *mbp ; int (*endfcn)(); s = splimp() ; if ((dkp->dk_state & DK_RCV) && (rwflag >= 0)) { dkcmd(KC_RCVB, dkp-dkit, (caddr_t)0, (unsigned) 0, 0, 0) ; dkp->dk_state &= ~DK_RCV ; if (dkp->dk_ubmbase) { ubarelse(ui->ui_ubanum, &dkp->dk_ubmbase); dkp->dk_ubmbase = NULL; } if (endfcn = dkp->dk_endfcn) { dkp->dk_endfcn = NULL ; (*endfcn)(dkp->dk_endparm, dkp-dkit, dkp->dk_rlen, DKR_ABORT, 0) ; } } /* flush all writes current and pending */ if ((dkp->dk_state & DK_BUSY) && (rwflag <= 0)) { register struct mbuf *m ; /* flush current write */ if (mbp = dkp->dk_obuf) { dkp->dk_obuf = NULL; if (endfcn = mbp->mp_endfcn) { mbp->mp_endfcn = NULL; (endfcn)(mbp->mp_endparm, dkp-dkit); } m_free(dtom(mbp)); } /* flush any pending writes which may be queued up */ while (1) { IF_DEQUEUE(&dkp->dk_outq, m); if (!m) break; mbp = mtod(m, struct mpacket *); if (endfcn = mbp->mp_endfcn) { mbp->mp_endfcn = NULL; (endfcn)(mbp->mp_endparm, dkp-dkit); } m_freem(m); } /* mark channel as not busy */ dkp->dk_state &= ~DK_BUSY ; } if ((dkp->dk_state & DK_OPEN) && (rwflag >= 0)) { dkcmd(KC_CLOSE, dkp-dkit, (caddr_t)0, (unsigned) 0, 0, 0) ; if (dkp->dk_state & DK_BUSY){ mbp = dkp->dk_obuf; dkp->dk_obuf = NULL; if (endfcn = mbp->mp_endfcn) { mbp->mp_endfcn = NULL; (endfcn)(mbp->mp_endparm, dkp-dkit); } m_free(dtom(mbp)); dkp->dk_state &= ~DK_BUSY ; } } splx(s) ; } short dup_count = 0; /* counter for number of duplicate sends */ /* * Routine to handle interrupts from the KMC * * This routine is called when * the KMC generates an unsolicited interrupt (VEC4 == 1) * * These interrupts are used by the KMC to notify dkit_kmc.c * of events such as output buffer completions * csr6 & csr7 point to dkkstat */ dkkint() { register struct dkchan *dkp; register struct dkkin *sp; register chan; struct mpacket *mbp ; int (*endfcn)(); M_ON(Mkint) ; chan = csr0 ; /* temp for cc -O bug */ if((chan & 01) == 1) /* 1 or 3 -> ignore */ return; sp = stat7 ; /* next response to be processed */ while (csr6 != csr7) { if (kseqchk) if ((((sp->k_chan >> 8)&0377) != kseq) || (((sp->k_type >> 8)&0377) != kseq)) { log(LOG_ERR, "dkkint: kseq %x chan %d type %x\n", kseq, sp->k_chan, sp->k_type) ; goto reset ; } kseq = (kseq + 1) & 0377 ; sp->k_addr = pseq ; pseq++ ; chan = sp->k_chan & 0377 ; /* mask off seq. # */ dkp = &dkit[chan]; if (chan > dkdebug) { log(LOG_ERR, " dkkint: head %d tail %d", csr6, csr7) ; log(LOG_ERR, " type %x chan %d len %d mode %x ctl %x\n", sp->k_type&0377, sp->k_chan&0377, sp->k_len, sp->k_mode, sp->k_ctl) ; } switch(sp->k_type & 0377) { case KS_CNTL: if (dkp->dk_supfcn) (*dkp->dk_supfcn)(chan, sp->k_ctl) ; break ; case KS_EOI: break ; case KS_SEND: mbp = dkp->dk_obuf ; if (mbp == NULL) { if (dkp->dk_state & (DK_RESET|DK_LINGR)) break; /* flushall was already called */ log(LOG_ERR, "dkkint: xbufout chan %d state %x\n", chan, dkp->dk_state) ; log(LOG_ERR, "head %d tail %d", csr6, csr7) ; log(LOG_ERR, " type %x len %d mode %x ctl %x\n", sp->k_type&0377, sp->k_len, sp->k_mode, sp->k_ctl) ; break ; } dkp->dk_state &= ~DK_BUSY; dkp->dk_obuf = NULL ; if (endfcn = mbp->mp_endfcn) { mbp->mp_endfcn = NULL; (endfcn)(mbp->mp_endparm, chan) ; } m_free(dtom(mbp)) ; if (dkp->dk_uba.ifu_xtofree) { m_freem(dkp->dk_uba.ifu_xtofree); dkp->dk_uba.ifu_xtofree = 0; } if (dkp->dk_outq.ifq_head) dkstart(dkp) ; break; case KS_RDB: if (((dkp->dk_state & DK_RCV) == 0) && dkp->dk_endfcn) { log(LOG_ERR, "dkkint: rbufin chan %d state %x\n", chan, dkp->dk_state) ; log(LOG_ERR, " head %d tail %d\n", csr6, csr7) ; log(LOG_ERR, " type %x len %d mode %x ctl %x\n", sp->k_type&0377, sp->k_len, sp->k_mode, sp->k_ctl) ; if (sp->k_ctl) break ; else { stat7 = sp ; /* save it for dump */ csr0 = 3 ; /* stop KMC */ panic("") ; /* KMC probably wrote into a mbuf we don't own */ } } dkp->dk_state &= ~DK_RCV ; if (dkp->dk_ubmbase) { ubarelse(ui->ui_ubanum, &dkp->dk_ubmbase); dkp->dk_ubmbase = NULL; } if (endfcn = dkp->dk_endfcn) { dkp->dk_endfcn = NULL; (endfcn)(dkp->dk_endparm, chan, sp->k_len, sp->k_mode, sp->k_ctl) ; } break; case KS_ERR: log(LOG_ERR, "dkkint: err : chan %d, code %x\nchead: %d, ctail: %d, rhead: %d, rtail: %d\n", chan, sp->k_len, csr4, csr5, csr6, csr7); /* if error is duplicate send, only close that chan, */ /* not the whole interface */ if (sp->k_len == E_DUP) { dup_count++; if (dkp->dk_state & DK_OPEN) { dk_reset(chan); } break; } reset: (void) dk_close(0) ; return ; default: log(LOG_ERR, "dkkint: chan %d, type %x, len %d, ctl %x, mode %x\n", chan, sp->k_type&0377, sp->k_len, sp->k_ctl, sp->k_mode); goto reset ; } /* end switch */ if (csr7 == dkk_nstat-1) { csr7 = 0 ; sp = &dkkstat[0] ; } else { csr7++ ; sp++ ; } } /* end while */ stat7 = sp ; M_OFF(Mkint) ; } /* * Start (Restart) transmission on the given line */ dkstart(dkp) register struct dkchan *dkp; { register struct mpacket *mbp; register struct mbuf *m; int len; /* * If it is currently active, just return */ if (dkp->dk_state&DK_BUSY) return; IF_DEQUEUE(&dkp->dk_outq, m); if (!m) return; mbp = mtod(m, struct mpacket *); dkp->dk_state |= DK_BUSY ; dkp->dk_obuf = mbp ; len = if_wubaput(&dkp->dk_uba, m->m_next); dkcmd(KC_SEND, dkp-dkit, (caddr_t) UBAI_ADDR(dkp->dk_uba.ifu_w.ifrw_info), (unsigned) len, mbp->mp_eob ? SBOT : SBOTM, mbp->mp_ctl) ; } /* * Put command in dkkcmdbuf which is pointed by csr4~5 */ dkcmd(type, chan, addr, len, mode, ctl) int type, chan; caddr_t addr ; unsigned len ; { register struct dkkin *sp; register s; register next; register loop; struct timeval tv1, tv2; M_ON(Mcmd) ; s = csr0 ; if ((s & 3) != 2) return; s = splimp(); next = (csr4+1)%dkk_ncmd; loop = 0; while (csr5 == next) { /* give it a chance to empty the buffer */ if (loop++>10000000) { log(LOG_ERR, "KMC DIED, restart\n"); dk_close(0); splx(s); return; } log(LOG_ERR, "KMC cmd overrun for %ld\n", loop); } sp = cmd4 ; sp->k_type = type | ((pseq&0177)<<9) ; sp->k_chan = chan | ((kseq&0377)<<8) ; sp->k_addr = ((int)addr << 16) + ((int)addr >> 16) ; sp->k_len = len ; sp->k_mode = mode ; sp->k_ctl = ctl ; pseq++ ; csr4 = next; cmd4 = &dkkcmdbuf[next]; if (chan > dkdebug) { log(LOG_ERR, " dkcmd: head %d, tail %d", csr4, csr5); log(LOG_ERR, " type %x, chan %d, addr %x, len %d", type, chan, addr, len); log(LOG_ERR, " mode %x, ctl %x\n", mode, ctl); } splx(s) ; M_OFF(Mcmd) ; } #endif