1484 lines
36 KiB
C
1484 lines
36 KiB
C
#ident "@(#) dbri_isr.c 1.1@(#) Copyright (c) 1991-92 Sun Microsystems, Inc."
|
|
|
|
/*
|
|
* Interrupt service routines for the AT&T DBRI chip.
|
|
*/
|
|
|
|
|
|
#include <sys/errno.h>
|
|
#include <sys/param.h>
|
|
#include <sys/user.h>
|
|
#include <sys/types.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/file.h>
|
|
#include <sys/debug.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <machine/mmu.h>
|
|
|
|
#include <sun/audioio.h>
|
|
#include <sbusdev/audiovar.h>
|
|
#include <sbusdev/dbri_reg.h>
|
|
#include <sbusdev/mmcodec_reg.h>
|
|
#include <sun/isdnio.h>
|
|
#include <sun/dbriio.h>
|
|
#include <sbusdev/dbrivar.h>
|
|
#include "audiodebug.h"
|
|
|
|
|
|
void dbri_receive_eol();
|
|
void dbri_chil_intr();
|
|
void dbri_error_msg();
|
|
void dbri_fxdt_intr();
|
|
void dbri_hold_f3();
|
|
void dbri_hold_g1();
|
|
void dbri_primitive_ph_ai();
|
|
void dbri_primitive_ph_di();
|
|
void dbri_primitive_mph_ai();
|
|
void dbri_primitive_mph_di();
|
|
void dbri_primitive_mph_ei1();
|
|
void dbri_primitive_mph_ei2();
|
|
void dbri_primitive_mph_ii_c();
|
|
void dbri_pr_packet();
|
|
void dbri_primitive_mph_ii_d();
|
|
void dbri_bri_up();
|
|
void dbri_bri_down();
|
|
|
|
|
|
int Dbri_rverbose = 0; /* Receive packet-level debugging */
|
|
int Dbri_debug_sbri = 0; /* SBRI Activation/Deactivation messages */
|
|
|
|
#define R3(v) driver->chip->r.reg3=(v&0xffffff)<<8
|
|
|
|
/*
|
|
* Need to remember that interrupts can occur without any devices open as
|
|
* they are enabled at the end of the attach routine. Only upon unload
|
|
* will they be disabled.
|
|
*/
|
|
int
|
|
dbri_intr()
|
|
{
|
|
int s;
|
|
int unit;
|
|
unsigned int intr_reg; /* intr register status */
|
|
dbri_intrq_ent_t intr; /* interrupt queue word */
|
|
dbri_dev_t *driver;
|
|
pipe_tab_t *pipep;
|
|
serial_status_t *serialp;
|
|
int serviced = 0;
|
|
|
|
s = splstr();
|
|
|
|
driver = &Dbri_devices[0];
|
|
for (unit = 0; unit < Ndbri; unit++, driver++) {
|
|
if ((driver == NULL) || (driver->chip == NULL))
|
|
continue;
|
|
|
|
intr_reg = driver->chip->n.intr; /* auto clr on read */
|
|
|
|
if (intr_reg)
|
|
serviced = 1;
|
|
|
|
if (!(intr_reg & (DBRI_INTR_REQ |
|
|
DBRI_INTR_LATE_ERR |
|
|
DBRI_INTR_BUS_GRANT_ERR |
|
|
DBRI_INTR_BURST_ERR |
|
|
DBRI_INTR_MRR_ERR))) {
|
|
/* No interrupt from this DBRI */
|
|
continue;
|
|
}
|
|
|
|
if (intr_reg & DBRI_INTR_LATE_ERR) {
|
|
/*
|
|
* XXX - There is nothing we are going to be able to do
|
|
* about this one. If the system is really
|
|
* hosed, we're sure to hit more serious
|
|
* interrupts later. So just re-enable DBRI
|
|
* master mode and continue on.
|
|
*/
|
|
ATRACEI(dbri_intr, 'LATE', intr_reg);
|
|
printf("dbri: Multiple Late Error on SBus");
|
|
driver->chip->n.sts &= ~(DBRI_STS_D);
|
|
}
|
|
|
|
if (intr_reg & DBRI_INTR_BUS_GRANT_ERR) {
|
|
/*
|
|
* XXX - we don't know where this occurred so we
|
|
* cannot set any error flags to let a user
|
|
* process know that there is a data
|
|
* discontinuity. This is BAD.
|
|
*/
|
|
ATRACEI(dbri_intr, 'BGNT', intr_reg);
|
|
printf("dbri: Lost Bus Grant on SBus");
|
|
}
|
|
|
|
if (intr_reg & DBRI_INTR_BURST_ERR) {
|
|
/*
|
|
* XXX - This is mostly informational since
|
|
* nothing bad happens until we get the MRR
|
|
* interrupt. For now, ignore it. We set the
|
|
* burst-mode enables at initialization time and
|
|
* when this interrupt occurs DBRI automatically
|
|
* figures out the next smaller size burst to
|
|
* use. We probably do not want to re-enable
|
|
* burst sizes that have failed.
|
|
*/
|
|
ATRACEI(dbri_intr, 'BRST', intr_reg);
|
|
printf("dbri: Burst Error on SBus\n");
|
|
}
|
|
|
|
if (intr_reg & DBRI_INTR_MRR_ERR) {
|
|
ATRACEI(dbri_intr, 'MRR ', intr_reg);
|
|
printf("dbri: Multiple Error Ack on SBus\n");
|
|
|
|
if (driver->chip->n.sts & DBRI_STS_S) {
|
|
printf("dbri: 16-word burst disabled\n");
|
|
driver->chip->n.sts &= ~(DBRI_STS_S|DBRI_STS_D);
|
|
} else if (driver->chip->n.sts & DBRI_STS_E) {
|
|
printf("dbri: 8-word bursts disabled\n");
|
|
driver->chip->n.sts &= ~(DBRI_STS_E|DBRI_STS_D);
|
|
} else if (driver->chip->n.sts & DBRI_STS_G) {
|
|
printf("dbri: 4-word bursts disabled\n");
|
|
driver->chip->n.sts &= ~(DBRI_STS_G|DBRI_STS_D);
|
|
} else {
|
|
printf("dbri: single-word transfer failed?!\n");
|
|
driver->chip->r.reg3 = 0xbfbfbf00;
|
|
driver->chip->n.sts &= ~(DBRI_STS_D);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we don't have to check the queue, don't go to memory.
|
|
*/
|
|
if (!(intr_reg & DBRI_INTR_REQ))
|
|
continue;
|
|
|
|
intr = driver->intq.curqp->intr[driver->intq.off];
|
|
if (intr.f.channel >= DBRI_INT_MAX_CHNL)
|
|
pipep = 0;
|
|
else
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
|
|
while (intr.f.ibits == DBRI_INT_IBITS) {
|
|
|
|
intr.f.ibits = unit; /* XXX debug */
|
|
driver->chip->r.reg3 = intr.word32 & ~0xFF; /* XXX */
|
|
|
|
/* Clear out IBITS in interrupt queue */
|
|
driver->intq.curqp->intr[driver->intq.off].f.ibits = 0;
|
|
|
|
switch (intr.f.code) {
|
|
case DBRI_INT_BRDY: /* Receive buffer ready */
|
|
ATRACEI(dbri_recv_intr, ATR_BRDY, intr.word32);
|
|
dbri_recv_intr(driver, intr);
|
|
break;
|
|
|
|
case DBRI_INT_MINT: /* Marked interrupt in RD/TD */
|
|
ATRACEI(dbri_intr, 'MINT', intr.word32);
|
|
/* XXX - Currently not used (masked) */
|
|
dprintf("dbri_intr: intr=0x%x MINT interrupt\n",
|
|
intr.word32);
|
|
break;
|
|
|
|
case DBRI_INT_IBEG: /* Flag to idle transition */
|
|
serialp = &driver->ser_sts;
|
|
serialp->ibeg++;
|
|
ATRACEI(dbri_intr, 'IBEG', intr.word32);
|
|
break;
|
|
|
|
case DBRI_INT_IEND: /* Idle to flag transition */
|
|
serialp = &driver->ser_sts;
|
|
serialp->iend++;
|
|
ATRACEI(dbri_intr, 'IEND', intr.word32);
|
|
break;
|
|
|
|
case DBRI_INT_EOL: /* End of list */
|
|
ATRACEI(dbri_intr, ATR_EOL, intr.word32);
|
|
|
|
/* Check if stream closed or invalid EOL */
|
|
if (pipep->as == NULL) {
|
|
ATRACEI(dbri_intr, 'EOL-',
|
|
intr.f.channel);
|
|
break;
|
|
}
|
|
|
|
/* check if receive direction */
|
|
if (!ISXMITDIRECTION(pipep->as))
|
|
dbri_receive_eol(AsToDs(pipep->as));
|
|
pipep->as->info.active = FALSE;
|
|
|
|
break;
|
|
|
|
case DBRI_INT_CMDI: /* 1st word cmd read by DBRI */
|
|
ATRACEI(dbri_intr, 'CMDI', (int)intr.word32);
|
|
/*
|
|
* If REX commands are ever used for
|
|
* synchronization, they will be processed
|
|
* here.
|
|
*/
|
|
driver->ser_sts.cmdi++;
|
|
break;
|
|
|
|
case DBRI_INT_XCMP: /* Xmit Frame complete */
|
|
ATRACEI(dbri_intr, ATR_XCMP,
|
|
(int)intr.word32);
|
|
dbri_xmit_intr(driver, intr);
|
|
break;
|
|
|
|
case DBRI_INT_SBRI: /* BRI status change info */
|
|
ATRACEI(dbri_intr, 'SBRI', intr.word32);
|
|
dbri_sbri_intr(driver, intr);
|
|
break;
|
|
|
|
case DBRI_INT_FXDT: /* Fixed data change */
|
|
ATRACEI(dbri_intr, 'FXDT', intr.word32);
|
|
|
|
/*
|
|
* XXX - this is in dbri_mmcodec.c. It
|
|
* will get moved into this file with the
|
|
* rest of the handlers later
|
|
*/
|
|
dbri_fxdt_intr(driver, intr);
|
|
break;
|
|
|
|
case DBRI_INT_CHIL: /* CHI lost frame sync */
|
|
ATRACEI(dbri_intr, 'CHIL', intr.word32);
|
|
if (intr.f.channel == DBRI_INT_CHI_CHNL) {
|
|
dbri_chil_intr(driver, intr);
|
|
} else {
|
|
/*
|
|
* XXX need to match code here to
|
|
* XXX DBRI 2/27/92 spec
|
|
*/
|
|
ATRACEI(dbri_intr, 'COLL', intr.word32);
|
|
if (!ISTEDCHANNEL(pipep->as)) {
|
|
printf("dbri: Unrecoverable TE D-channel collision on an illegal channel!\n");
|
|
break;
|
|
}
|
|
printf("dbri: Unrecoverable TE D-channel collision!\n");
|
|
dbri_stop(pipep->as);
|
|
DELAY(250);
|
|
dbri_start(pipep->as);
|
|
}
|
|
break;
|
|
|
|
case DBRI_INT_DBYT: /* Dropped byte frame slip */
|
|
ATRACEI(dbri_intr, 'DBYT', intr.word32);
|
|
/*
|
|
* This can only happen on a
|
|
* serial-to-serial pipe.
|
|
*
|
|
* XXX - Do we need to set an error flag
|
|
* somewhere?
|
|
*/
|
|
printf("dbri: Dropped byte frame slip.\n");
|
|
break;
|
|
|
|
case DBRI_INT_RBYT: /* Repeated byte frame slip */
|
|
ATRACEI(dbri_intr, 'RBYT', intr.word32);
|
|
/*
|
|
* This can only happen on a
|
|
* serial-to-serial pipe.
|
|
*
|
|
* XXX - Do we need to set an error flag
|
|
* somewhere?
|
|
*/
|
|
printf("dbri: Repeated byte frame slip\n");
|
|
break;
|
|
|
|
case DBRI_INT_LINT: /* Lost interrupt */
|
|
ATRACEI(dbri_intr, 'LINT', intr.word32);
|
|
printf("dbri: Lost interrupts\n");
|
|
break;
|
|
|
|
case DBRI_INT_UNDR: /* DMA underrun */
|
|
ATRACEI(dbri_intr, 'UNDR', intr.word32);
|
|
|
|
dprintf("dbri_intr: intr = 0x%x DMA underrun\n",
|
|
intr.word32);
|
|
break;
|
|
|
|
default:
|
|
ATRACEI(dbri_intr, 'unkn', intr.word32);
|
|
printf("dbri: No interrupt code given\n");
|
|
break;
|
|
} /* switch */
|
|
|
|
/*
|
|
* Advance intqp to point to next available
|
|
* intr status word
|
|
*/
|
|
if (driver->intq.off == (DBRI_MAX_QWORDS - 1)) {
|
|
driver->intq.curqp = ((dbri_intq_t *)
|
|
KERN_ADDR(driver,
|
|
driver->intq.curqp->nextq));
|
|
driver->intq.off = 0;
|
|
} else {
|
|
driver->intq.off++;
|
|
}
|
|
|
|
/* get next intr status word */
|
|
intr = driver->intq.curqp->intr[driver->intq.off];
|
|
if (intr.f.channel >= DBRI_INT_MAX_CHNL)
|
|
pipep = 0;
|
|
else
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
} /* while */
|
|
} /* for each interface */
|
|
|
|
(void) splx(s);
|
|
return (serviced);
|
|
} /* dbri_intr */
|
|
|
|
|
|
void
|
|
dbri_receive_eol(ds)
|
|
dbri_stream_t *ds;
|
|
{
|
|
dbri_cmd_t *dcp;
|
|
int n;
|
|
int neof = 0;
|
|
int ncom = 0;
|
|
int done = 0;
|
|
|
|
ATRACEI(dbri_receive_eol, ATR_EOL, ds->pipe);
|
|
AsToDs(ds->as.control_as)->d_stats.recv_eol++;
|
|
ds->recv_eol = TRUE;
|
|
|
|
if (!ds->cmdptr) {
|
|
/*
|
|
* There are no receive buffers on the list. Call
|
|
* process_record to try to queue some more (if that is
|
|
* appropriate.)
|
|
*/
|
|
ATRACEI(dbri_receive_eol, ATR_DROPPACKET, ds->pipe);
|
|
if (ds->as.info.active)
|
|
ds->audio_uflow = TRUE;
|
|
audio_process_record((aud_stream_t *)ds);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Scan the receive buffer list for complete frames. If there are
|
|
* some complete frames, then normal processing will clear the
|
|
* EOL condition. If the list is full of completed fragments, but
|
|
* no completed frames, and the freelist is empty, then special
|
|
* action needs to be taken.
|
|
*/
|
|
for (dcp = ds->cmdptr, n = 0; dcp; dcp = dcp->nextio, ++n) {
|
|
if (dcp->md.r.f.com)
|
|
ncom++;
|
|
if (dcp->md.r.f.eof)
|
|
neof++;
|
|
if (dcp->cmd.done)
|
|
done++;
|
|
}
|
|
|
|
dprintf("dbri:Rcv EOL; %d frags, %d complete, %d marked eof, %d done\n",
|
|
n, ncom, neof, done);
|
|
|
|
/*
|
|
* If there is no hope of audio_process_record solving this EOL
|
|
* problem, then mark the entire list for discarding.
|
|
*/
|
|
if ((ncom == n) && (neof == 0) && (ds->as.cmdlist.free == NULL)) {
|
|
int total = 0;
|
|
|
|
ATRACEI(dbri_receive_eol, ATR_NONE, ds->pipe);
|
|
(void) printf("dbri: REOL: %d frags, %d com, %d eof.\n",
|
|
n, ncom, neof);
|
|
/*
|
|
* The receive list has been completely filled with a "huge"
|
|
* packet (huge means larger that the list.)
|
|
*/
|
|
for (dcp = ds->cmdptr, n = 0;
|
|
dcp != NULL;
|
|
dcp = dcp->nextio, ++n) {
|
|
dcp->cmd.done = TRUE;
|
|
dcp->cmd.skip = TRUE;
|
|
total += dcp->md.r.f.cnt;
|
|
#ifdef sun4m
|
|
#ifndef lint /* XXX lint barfs on arg 1 */
|
|
mb_mapfree(dvmamap, (int *)&dcp->sb_addr);
|
|
#endif /* lint */
|
|
#endif /* sun4m */
|
|
}
|
|
ds->d_stats.recv_error_octets += total;
|
|
}
|
|
|
|
audio_process_record((aud_stream_t *)ds);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* dbri_recv_intr - process a single packet or a single audio buffer. */
|
|
void
|
|
dbri_recv_intr(driver, intr)
|
|
dbri_dev_t *driver;
|
|
dbri_intrq_ent_t intr; /* interrupt queue word */
|
|
{
|
|
unsigned int status;
|
|
pipe_tab_t *pipep;
|
|
dbri_cmd_t *dcp;
|
|
struct {
|
|
dbri_cmd_t *head;
|
|
dbri_cmd_t *tail;
|
|
} packet;
|
|
aud_stream_t *as;
|
|
dbri_stream_t *ds;
|
|
int error = FALSE;
|
|
unsigned int cnt;
|
|
int total = 0;
|
|
int hdlc;
|
|
#if defined(DBRI_SWFCS)
|
|
extern int Dbri_fcscheck;
|
|
#endif
|
|
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
as = pipep->as;
|
|
|
|
/* Check if stream closed */
|
|
if (as == NULL) {
|
|
/* Some other piece of code needs to flush the aud_cmd list. */
|
|
ATRACEI(dbri_recv_intr, '-na-', intr.word32);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* "Processed" commands never show up on the DBRI's IO list.
|
|
* Completed packets are removed from the DBRI IO list by this
|
|
* routine.
|
|
*/
|
|
ds = AsToDs(as); /* Assign dbri stream pointer */
|
|
|
|
/* Just go through list to end to check for bad status */
|
|
dcp = ds->cmdptr; /* current head of DBRI cmdlist */
|
|
if (dcp == NULL) {
|
|
ATRACEI(dbri_recv_intr, 'null', intr.word32);
|
|
return;
|
|
}
|
|
if (!dcp->md.r.f.com) {
|
|
/*
|
|
* XXX - may want to put a printf here since this if
|
|
* we get a BRDY but the first one isn't complete, then
|
|
* something is very wrong
|
|
*/
|
|
ATRACEI(dbri_recv_intr, 'ncmp', dcp->md.r.word32[0]);
|
|
return;
|
|
}
|
|
|
|
packet.head = dcp; /* Any new packet will start here */
|
|
packet.tail = NULL;
|
|
|
|
if (dcp == NULL) {
|
|
driver->chip->r.reg3 = 0xd0a00000;
|
|
ATRACEI(dbri_recv_intr, 'emty', 0);
|
|
}
|
|
|
|
/*
|
|
* Search for a valid EOF
|
|
*/
|
|
while (dcp != NULL && dcp->md.r.f.com) { /* buffer complete */
|
|
if (dcp->md.r.f.eof) {
|
|
ATRACEI(dbri_recv_intr, 'eof ', dcp);
|
|
packet.tail = dcp; /* Save pointer to end of packet */
|
|
break;
|
|
} else {
|
|
driver->chip->r.reg3 = 0x00feeb00;
|
|
ATRACEI(dbri_recv_intr, '!eof', dcp);
|
|
}
|
|
dcp = dcp->nextio;
|
|
}
|
|
|
|
if (packet.tail == NULL) {
|
|
ATRACEI(dbri_recv_intr, 'part', dcp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* There is at least one complete packet on the receive chain.
|
|
* Process one packet only.
|
|
*/
|
|
|
|
/*
|
|
* Start and end of packet have been found. Mark fragments as
|
|
* done and set lastfragment pointer.
|
|
*/
|
|
for (dcp = packet.head; dcp != NULL; dcp = dcp->nextio) {
|
|
dcp->cmd.done = TRUE;
|
|
dcp->cmd.lastfragment = &packet.tail->cmd;
|
|
|
|
#ifdef sun4m
|
|
#ifndef lint /* XXX lint barfs on arg 1 */
|
|
mb_mapfree(dvmamap, (int *)&dcp->sb_addr);
|
|
#endif /* lint */
|
|
#endif
|
|
#ifdef sun4c
|
|
/*
|
|
* XXX - Could probably do this after we check for errors
|
|
* and if we aren't going to send the data anywhere, don't
|
|
* bother wasting the cycles
|
|
*/
|
|
vac_flush((caddr_t)dcp->cmd.data,
|
|
dcp->cmd.enddata - dcp->cmd.data);
|
|
#endif
|
|
|
|
if (dcp == packet.tail)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Process receive errors
|
|
*/
|
|
status = packet.tail->md.r.f.status; /* check status */
|
|
|
|
if (Dbri_rverbose) {
|
|
dbri_pr_packet(packet.head, 1);
|
|
(void) printf ("\tstatus: %x\n", status);
|
|
}
|
|
|
|
if ((pipep->sdp & DBRI_SDP_MODEMASK) != DBRI_SDP_TRANSPARENT)
|
|
hdlc = TRUE;
|
|
else
|
|
hdlc = FALSE;
|
|
|
|
if (status & (DBRI_RMD_CRC|DBRI_RMD_BBC|DBRI_RMD_ABT|DBRI_RMD_OVRN)) {
|
|
|
|
|
|
if (Dbri_rverbose) {
|
|
/*
|
|
* %b format spec: number, then string; 1st byte
|
|
* of string is base, each bit spec is "\XXname"
|
|
* where XX is octal (power of 2)+1
|
|
*/
|
|
(void) printf("[RE%b:%u]",
|
|
status, "\20\10CRC\7BBC\6ABT\4OVRN",
|
|
intr.f.channel);
|
|
}
|
|
|
|
if (status & DBRI_RMD_OVRN) {
|
|
ds->audio_uflow = TRUE;
|
|
ds->d_stats.dma_underflow++;
|
|
}
|
|
if (status & DBRI_RMD_CRC) {
|
|
error = TRUE;
|
|
ds->d_stats.crc_error++;
|
|
}
|
|
if (status & DBRI_RMD_BBC) {
|
|
error = TRUE;
|
|
ds->d_stats.badbyte_error++;
|
|
}
|
|
if (status & DBRI_RMD_ABT) {
|
|
error = TRUE;
|
|
ds->d_stats.abort_error++;
|
|
}
|
|
}
|
|
|
|
#if defined(DBRI_SWFCS)
|
|
if (Dbri_fcscheck && !error && hdlc) {
|
|
error = dbri_checkfcs(packet.head);
|
|
|
|
if (error)
|
|
printf("dbri: Dropped packet with bad SW FCS\n");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Second pass thru data to adjust data pointers,
|
|
* and adjust sample count.
|
|
*/
|
|
total = 0;
|
|
|
|
for (dcp = packet.head; dcp; dcp = dcp->nextio) {
|
|
ASSERT(dcp->md.r.f.com);
|
|
|
|
cnt = dcp->md.r.f.cnt;
|
|
|
|
/*
|
|
* For HDLC mode, remove the 2-byte CRC at the end of the
|
|
* frame.
|
|
*
|
|
* XXX - Will CRC ever be given to driver users?
|
|
*/
|
|
if (hdlc) {
|
|
/*
|
|
* If HDLC mode, remove CRC from end of packet.
|
|
* If the last fragment contains one byte, it is
|
|
* from the CRC
|
|
*/
|
|
if (dcp->md.r.f.eof)
|
|
cnt -= (dcp->md.r.f.cnt == 1) ? 1 : 2;
|
|
else if (dcp->nextio->md.r.f.eof &&
|
|
dcp->nextio->md.r.f.cnt == 1)
|
|
cnt -= 1;
|
|
}
|
|
|
|
if (error)
|
|
dcp->cmd.skip = TRUE;
|
|
|
|
dcp->cmd.data += cnt;
|
|
total += cnt;
|
|
|
|
if (dcp == packet.tail)
|
|
break;
|
|
}
|
|
ASSERT(dcp != NULL); /* above loop stays on last cmd */
|
|
|
|
/*
|
|
* Update dbri stream structure command pointers
|
|
* Delete the complete packet from the DBRI IO list.
|
|
*
|
|
* The "processed" fragment doesn't show up on the DBRI IO list
|
|
* even though the DBRI still partially "owns" it. This is
|
|
* correct behavior.
|
|
*/
|
|
ds->cmdptr = packet.tail->nextio;
|
|
if (packet.tail->nextio == NULL)
|
|
ds->cmdlast = NULL;
|
|
|
|
/*
|
|
* Updata audio samples counter
|
|
*/
|
|
if (!hdlc) {
|
|
if (as->info.channels == 0 || (as->info.precision/8) == 0)
|
|
total /= 1; /* XXX */
|
|
else
|
|
total /= (as->info.channels * (as->info.precision/8));
|
|
} else {
|
|
total = 1; /* packets */
|
|
}
|
|
|
|
if (error) {
|
|
ATRACEI(dbri_recv_intr, 'ERR ', ds->cmdptr);
|
|
ds->d_stats.recv_error_octets += total;
|
|
ds->i_info.receive.errors += 1;
|
|
} else {
|
|
ATRACEI(dbri_recv_intr, 'totl', total);
|
|
ds->i_info.receive.octets += total;
|
|
ds->i_info.receive.packets++;
|
|
}
|
|
|
|
if (!error || !hdlc)
|
|
ds->samples += total;
|
|
|
|
audio_process_record(pipep->as);
|
|
|
|
return;
|
|
} /* dbri_recv_intr */
|
|
|
|
|
|
/*
|
|
* dbri_xmit_intr - process a single packet or a single audio buffer
|
|
*/
|
|
void
|
|
dbri_xmit_intr(driver, intr)
|
|
dbri_dev_t *driver;
|
|
dbri_intrq_ent_t intr; /* interrupt queue word */
|
|
{
|
|
dbri_cmd_t *dcp;
|
|
unsigned int status;
|
|
pipe_tab_t *pipep;
|
|
dbri_stream_t *ds;
|
|
dbri_cmd_t *err_dcp = NULL;
|
|
int hdlc;
|
|
int total;
|
|
int octets;
|
|
|
|
pipep = &driver->ptab[intr.f.channel];
|
|
|
|
/* Check if stream closed */
|
|
if (pipep->as == NULL) {
|
|
ATRACEI(dbri_xmit_intr, 'bad!', intr.word32);
|
|
driver->chip->r.reg3 = 0x00dead00;
|
|
dprintf("dbri_xmit_intr: XCMP on closed stream, 0x%x\n",
|
|
intr.word32);
|
|
return;
|
|
}
|
|
ds = AsToDs(pipep->as); /* Get dbri stream pointer */
|
|
|
|
/*
|
|
* Go thru list to end to check for bad status marking all md's done
|
|
*/
|
|
dcp = ds->cmdptr; /* head of DBRI cmdlist */
|
|
|
|
/*
|
|
* Check if 1st cmd already processed.
|
|
*/
|
|
while ((dcp != NULL) && dcp->cmd.processed) {
|
|
/*
|
|
* NB: Device routines follow the nextio list, the aud_cmd
|
|
* "next" pointer is irrelevent here.
|
|
*/
|
|
dcp = dcp->nextio;
|
|
}
|
|
|
|
if (dcp == NULL) {
|
|
dprintf("dbri_xmit_intr: dcp == NULL\n");
|
|
return;
|
|
}
|
|
|
|
if ((pipep->sdp & DBRI_SDP_MODEMASK) != DBRI_SDP_TRANSPARENT)
|
|
hdlc = TRUE;
|
|
else
|
|
hdlc = FALSE;
|
|
|
|
status = dcp->md.t.f.status;
|
|
octets = 0;
|
|
total = 0;
|
|
while ((dcp != NULL) && (status & (DBRI_TMD_TBC | DBRI_TMD_ABT))) {
|
|
int samples;
|
|
|
|
dcp->cmd.done = TRUE;
|
|
|
|
if (status & DBRI_TMD_UNR) {
|
|
if (!hdlc && !ds->last_flow_err) {
|
|
ds->audio_uflow = TRUE;
|
|
}
|
|
}
|
|
/*
|
|
* XXX DBRI[de] DBRI HW BUG #3034, BugId 1089299
|
|
* DBRI continues to report flow errors when only one error
|
|
* ocurrs. last_flow_err is used to supress these
|
|
* repeated error reports.
|
|
*/
|
|
switch (driver->dbri_version) {
|
|
case 'd':
|
|
case 'e':
|
|
ds->last_flow_err = status & DBRI_TMD_UNR;
|
|
break;
|
|
default:
|
|
ds->last_flow_err = 0;
|
|
break;
|
|
} /* switch */
|
|
|
|
if (((status & DBRI_TMD_ABT) &&
|
|
((pipep->sdp & DBRI_SDP_MODEMASK) !=
|
|
DBRI_SDP_TRANSPARENT)) ||
|
|
(status & DBRI_TMD_UNR)) {
|
|
if (err_dcp == NULL) /* Grab first error status */
|
|
err_dcp = dcp;
|
|
}
|
|
#ifdef sun4m
|
|
if (dcp->sb_addr != 0) {
|
|
#ifndef lint /* XXX lint barfs on arg 1 */
|
|
mb_mapfree(dvmamap, (int *)&dcp->sb_addr);
|
|
#endif /* lint */
|
|
}
|
|
#endif
|
|
/*
|
|
* Updata audio samples counter
|
|
*/
|
|
samples = dcp->md.t.f.cnt;
|
|
octets += samples;
|
|
if (!hdlc) {
|
|
if ((pipep->as->info.precision/8) == 0)
|
|
samples /= 1; /* XXX */
|
|
else
|
|
samples /= pipep->as->info.channels *
|
|
(pipep->as->info.precision/8);
|
|
} else {
|
|
samples = 1; /* packets */
|
|
}
|
|
total += samples;
|
|
|
|
if (dcp->md.t.f.eof)
|
|
break;
|
|
|
|
dcp = dcp->nextio;
|
|
if (dcp != NULL)
|
|
status = dcp->md.t.f.status;
|
|
}
|
|
|
|
if (dcp == NULL) {
|
|
dprintf("dbri_intr: dcp NULL on transmit\n");
|
|
return;
|
|
}
|
|
|
|
/* If there was an error, retrieve the md where the error occurred */
|
|
if (err_dcp != NULL) {
|
|
dcp = err_dcp;
|
|
status = dcp->md.t.f.status;
|
|
|
|
if (status & DBRI_TMD_UNR) {
|
|
ds->d_stats.dma_underflow++;
|
|
ds->samples += total;
|
|
}
|
|
if (status & DBRI_TMD_ABT)
|
|
ds->d_stats.abort_error++;
|
|
ds->i_info.transmit.errors++;
|
|
} else {
|
|
ds->i_info.transmit.packets++;
|
|
ds->i_info.transmit.octets += octets;
|
|
ds->samples += total; /* adjust sample count */
|
|
}
|
|
|
|
/* Update dbri stream structure command pointers */
|
|
ds->cmdptr = dcp;
|
|
if (dcp == NULL)
|
|
ds->cmdlast = NULL;
|
|
|
|
audio_process_play(pipep->as); /* EOF - release xmit buffers */
|
|
|
|
return;
|
|
} /* dbri_xmit_intr */
|
|
|
|
|
|
static char *
|
|
pr_code(code)
|
|
int code;
|
|
{
|
|
static char *codes[16] = {
|
|
"****", "BRDY", "MINT", "IBEG",
|
|
"IEND", "EOL", "CMDI", "****",
|
|
"XCMP", "SBRI", "FXDT", "CHIL",
|
|
"DBYT", "RBYT", "LINT", "UNDR",
|
|
};
|
|
|
|
return (codes[code]);
|
|
}
|
|
|
|
|
|
static char *
|
|
pr_channel(ch)
|
|
int ch;
|
|
{
|
|
static char buf[100];
|
|
caddr_t tbuf = buf; /* for lint */
|
|
|
|
switch (ch) {
|
|
case DBRI_INT_TE_CHNL:
|
|
return ("TE_status");
|
|
case DBRI_INT_NT_CHNL:
|
|
return ("NT_status:");
|
|
case DBRI_INT_CHI_CHNL:
|
|
return ("CHI_status:");
|
|
case DBRI_INT_REPORT_CHNL:
|
|
return ("Report_Command_channel_intr_status");
|
|
case DBRI_INT_OTHER_CHNL:
|
|
return ("Other_status");
|
|
default:
|
|
(void) sprintf(tbuf, "Channel_%d", ch);
|
|
return (buf);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
pr_interrupt(intr)
|
|
dbri_intrq_ent_t *intr;
|
|
{
|
|
static char *tss[8] = {
|
|
"G1/F1", /* 0 */
|
|
"**/**", /* 1 */
|
|
"**/F8", /* 2 */
|
|
"**/F3", /* 3 */
|
|
"**/F4", /* 4 */
|
|
"**/F5", /* 5 */
|
|
"G2/F6", /* 6 */
|
|
"G3/F7", /* 7 */
|
|
};
|
|
|
|
(void) printf("I=%d channel=%s code=%s, field=0x%x",
|
|
intr->f.ibits,
|
|
pr_channel((int)intr->f.channel),
|
|
pr_code((int)intr->f.code),
|
|
(unsigned int)intr->f.field);
|
|
|
|
if (intr->f.code != DBRI_INT_SBRI) {
|
|
(void) printf("\n");
|
|
return;
|
|
}
|
|
|
|
(void) printf(" %b %s\n",
|
|
intr->f.field,
|
|
"\20\13vta\12berr\11ferr\10mfm\07fsc\06rif4\05rif0\04act",
|
|
tss[intr->code_sbri.tss]);
|
|
}
|
|
|
|
/*
|
|
* dbri_sbri_intr - process a SBRI interrupt DBRI always has the say as
|
|
* to what the current state is. However, old state must be known in
|
|
* order to properly implement Primitives.
|
|
*/
|
|
void
|
|
dbri_sbri_intr(driver, intr)
|
|
dbri_dev_t *driver;
|
|
dbri_intrq_ent_t intr;
|
|
{
|
|
aud_stream_t *as;
|
|
pipe_tab_t *pipep;
|
|
serial_status_t *serialp;
|
|
|
|
serialp = &driver->ser_sts;
|
|
|
|
/*
|
|
* Detectible Bit Error
|
|
*/
|
|
if (intr.code_sbri.berr) {
|
|
ATRACEI(dbri_sbri_intr, 'BERR', intr.word32);
|
|
if (intr.f.channel == DBRI_INT_TE_CHNL)
|
|
serialp->te_primitives.berr++;
|
|
else if (intr.f.channel == DBRI_INT_NT_CHNL)
|
|
serialp->nt_primitives.berr++;
|
|
/* XXX - any other action? */
|
|
}
|
|
|
|
/*
|
|
* Frame Sync Error
|
|
*/
|
|
if (intr.code_sbri.ferr) {
|
|
ATRACEI(dbri_sbri_intr, 'FERR', intr.word32);
|
|
if (intr.f.channel == DBRI_INT_TE_CHNL)
|
|
serialp->te_primitives.ferr++;
|
|
else if (intr.f.channel == DBRI_INT_NT_CHNL)
|
|
serialp->nt_primitives.ferr++;
|
|
/* XXX - any other action? */
|
|
}
|
|
|
|
if (Dbri_debug_sbri)
|
|
pr_interrupt(&intr);
|
|
|
|
if (intr.f.channel == DBRI_INT_TE_CHNL) {
|
|
struct dbri_code_sbri old_te_sbri;
|
|
|
|
old_te_sbri = serialp->te_sbri;
|
|
serialp->te_sbri = intr.code_sbri;
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_TE_D_IN];
|
|
|
|
/* don't do anything if pipe not connected */
|
|
if (!ISPIPEINUSE(pipep))
|
|
return;
|
|
|
|
as = pipep->as->play_as; /* get control as */
|
|
|
|
/*
|
|
* If new state is not F3, then remove PH-ACTIVATEreq
|
|
* since it's only use, for the TE, is to get out of F3.
|
|
*/
|
|
if (serialp->te_sbri.tss != DBRI_TEINFO0_F3)
|
|
dbri_hold_f3(driver);
|
|
|
|
/*
|
|
* XXX DBRI hangs if synchronization is lost.
|
|
* XXX So, flush queues and stop IO if this is a transition
|
|
* XXX out of F7 and this hack is enabled.
|
|
*/
|
|
if ((AsToDs(as)->i_var.norestart == 0) &&
|
|
(old_te_sbri.tss == DBRI_TEINFO3_F7) &&
|
|
(serialp->te_sbri.tss != DBRI_TEINFO3_F7)) {
|
|
ATRACEI(dbri_sbri_intr, 'Thck', intr.word32);
|
|
dbri_bri_down(driver, ISDN_TYPE_TE);
|
|
}
|
|
|
|
if (old_te_sbri.tss != serialp->te_sbri.tss) {
|
|
switch (serialp->te_sbri.tss) {
|
|
case DBRI_TEINFO0_F1:
|
|
#ifdef lint /* XXX - Because the next part is if'd out */
|
|
dbri_primitive_mph_ii_d(as);
|
|
#endif
|
|
#if 0
|
|
AsToDs(as)->i_info.activation = ISDN_UNPLUGGED;
|
|
/*
|
|
* F1 and F2 are not distinguishable by
|
|
* the driver
|
|
*
|
|
* f3_f1 mph-ii(d)
|
|
* f4_f1 mph-ii(d), mph-di, ph-di
|
|
* f5_f1 mph-ii(d), mph-di, ph-di
|
|
* f6_f1 mph-ii(d), mph-di, ph-di
|
|
* f7_f1 mph-ii(d), mph-di, ph-di
|
|
* f8_f1 mph-ii(d), mph-di, ph-di
|
|
*/
|
|
dbri_primitive_mph_ii_d(as);
|
|
if (old_te_sbri.tss != DBRI_TEINFO0_F3) {
|
|
dbri_bri_down(driver, ISDN_TYPE_TE);
|
|
dbri_primitive_mph_di(as);
|
|
dbri_primitive_ph_di(as);
|
|
}
|
|
break;
|
|
#else /* 0 */
|
|
/*
|
|
* Given the implementation of this
|
|
* driver and the implementation of DBRI,
|
|
* transitions to F1 reported by DBRI are
|
|
* to be interpreted as transitions to
|
|
* F3.
|
|
*/
|
|
serialp->te_sbri.tss = DBRI_TEINFO0_F3;
|
|
/* FALL THROUGH */
|
|
#endif /* !0 */
|
|
|
|
case DBRI_TEINFO0_F3:
|
|
AsToDs(as)->i_info.activation =
|
|
ISDN_DEACTIVATED;
|
|
/*
|
|
* f1_f3 mph-ii(c)
|
|
* f4_f3 mph-di, ph-di // T3 expiry
|
|
* f5_f3 mph-di, ph-di // T3 expiry
|
|
* f6_f3 mph-di, ph-di // T3 expiry
|
|
* or recv I0
|
|
* f7_f3 mph-di, ph-di // receive I0
|
|
* f8_f3 mph-di, ph-di, mph-ei2
|
|
*/
|
|
if (old_te_sbri.tss != DBRI_TEINFO0_F1) {
|
|
dbri_bri_down(driver, ISDN_TYPE_TE);
|
|
dbri_primitive_mph_di(as);
|
|
dbri_primitive_ph_di(as);
|
|
if (old_te_sbri.tss ==
|
|
DBRI_TEINFO0_F8) {
|
|
dbri_primitive_mph_ei2(as);
|
|
}
|
|
} else {
|
|
/* really from F2 */
|
|
#if 0
|
|
/*
|
|
* DBRI "falsely" reports F1 when
|
|
* T3 expires and then driver
|
|
* toggles the T bit in reg0.
|
|
*
|
|
* Since dbri_primitive_mph_ii_c
|
|
* should only/always happen once
|
|
* immediately after open, this
|
|
* line has been moved into the
|
|
* TE initialization code in
|
|
* dbri_setup_ntte().
|
|
*/
|
|
dbri_primitive_mph_ii_c(as);
|
|
#endif /* 0 */
|
|
}
|
|
break;
|
|
|
|
case DBRI_TEINFO1_F4:
|
|
AsToDs(as)->i_info.activation =
|
|
ISDN_ACTIVATE_REQ;
|
|
break;
|
|
|
|
case DBRI_TEINFO0_F5:
|
|
AsToDs(as)->i_info.activation =
|
|
ISDN_ACTIVATE_REQ;
|
|
break;
|
|
|
|
case DBRI_TEINFO3_F6:
|
|
/* AsToDs(as)->i_info.activation=no change; */
|
|
#if 0
|
|
untimeout((int(*)())dbri_te_timer, (caddr_t)as);
|
|
#endif
|
|
|
|
/*
|
|
* f1_f6 mph-ii(c)
|
|
* f7_f6 mph-ei1 // report error
|
|
* f8_f6 mph-ei2 // report recovery
|
|
* from error
|
|
* f3_f6 no-action
|
|
* f5_f6 no-action
|
|
*/
|
|
switch (old_te_sbri.tss) {
|
|
case DBRI_TEINFO0_F1:
|
|
/* really from F2 */
|
|
dbri_primitive_mph_ii_c(as);
|
|
break;
|
|
case DBRI_TEINFO3_F7:
|
|
dbri_primitive_mph_ei1(as);
|
|
break;
|
|
case DBRI_TEINFO0_F8:
|
|
dbri_primitive_mph_ei2(as);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DBRI_TEINFO3_F7:
|
|
AsToDs(as)->i_info.activation = ISDN_ACTIVATED;
|
|
untimeout((int(*)())dbri_te_timer, (caddr_t)as);
|
|
|
|
/*
|
|
* f1_f7 mph-ii(c), ph-ai, mph-ai
|
|
* f3_f7 ph-ai, mph-ai
|
|
* f5_f7 ph-ai, mph-ai
|
|
* f6_f7 ph-ai, mph-ai, mph-ei2
|
|
* f8_f7 ph-ai, mph-ai, mph-ei2
|
|
*/
|
|
|
|
if (old_te_sbri.tss == DBRI_TEINFO0_F1) {
|
|
/* really from F2 */
|
|
dbri_primitive_mph_ii_c(as);
|
|
}
|
|
dbri_bri_up(driver, ISDN_TYPE_TE);
|
|
dbri_primitive_ph_ai(as);
|
|
dbri_primitive_mph_ai(as);
|
|
|
|
if ((old_te_sbri.tss == DBRI_TEINFO3_F6) ||
|
|
(old_te_sbri.tss == DBRI_TEINFO0_F8)) {
|
|
/* recovered from error */
|
|
dbri_primitive_mph_ei2(as);
|
|
}
|
|
break;
|
|
|
|
case DBRI_TEINFO0_F8:
|
|
/*
|
|
* AsToDs(as)->i_info.activation = no change
|
|
*
|
|
* f7_f8 mph-e11
|
|
* f6_f8 mph-ei1
|
|
*/
|
|
|
|
/* report error */
|
|
dbri_primitive_mph_ei1(as);
|
|
|
|
break;
|
|
} /* switch */
|
|
}
|
|
} else { /* NT channel */
|
|
struct dbri_code_sbri old_nt_sbri;
|
|
|
|
serialp = &driver->ser_sts;
|
|
old_nt_sbri = serialp->te_sbri;
|
|
serialp->nt_sbri = intr.code_sbri;
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_NT_D_IN];
|
|
|
|
/* don't do anything if pipe not connected */
|
|
if (!ISPIPEINUSE(pipep))
|
|
return;
|
|
|
|
as = pipep->as->play_as; /* get control as */
|
|
|
|
/*
|
|
* If new state is not G1, then remove PH-ACTIVATEreq
|
|
* since it's only use, for the NT, is to get out of G1.
|
|
*/
|
|
if (serialp->nt_sbri.tss != DBRI_NTINFO0_G1)
|
|
dbri_hold_g1(driver);
|
|
|
|
|
|
/* If no state change, then no actions taken */
|
|
if (old_nt_sbri.tss != serialp->nt_sbri.tss) {
|
|
/*
|
|
* XXX DBRI hangs if synchronization is lost.
|
|
* XXX So, flush queues and stop IO if this is a
|
|
* XXX transition G3 and this hack is enabled.
|
|
*/
|
|
if ((AsToDs(as)->i_var.norestart == 0) &&
|
|
(old_nt_sbri.tss == DBRI_NTINFO4_G3) &&
|
|
(serialp->nt_sbri.tss != DBRI_NTINFO4_G3)) {
|
|
ATRACEI(dbri_sbri_intr, 'Nhck', intr.word32);
|
|
dbri_bri_down(driver, ISDN_TYPE_NT);
|
|
}
|
|
|
|
switch (serialp->nt_sbri.tss) {
|
|
case DBRI_NTINFO0_G1:
|
|
AsToDs(as)->i_info.activation =
|
|
ISDN_DEACTIVATED;
|
|
/*
|
|
* g2_g1 (really g4_g1)
|
|
* g3_g1 (really g4_g1)
|
|
* g4_g1 (this will appear as either
|
|
* g2_g1, or g3_g1)
|
|
*/
|
|
break;
|
|
|
|
case DBRI_NTINFO2_G2:
|
|
AsToDs(as)->i_info.activation =
|
|
ISDN_DEACTIVATED;
|
|
if (old_nt_sbri.tss == DBRI_NTINFO0_G1) {
|
|
untimeout((int(*)())dbri_nt_timer,
|
|
(caddr_t)as);
|
|
timeout((int(*)())dbri_nt_timer,
|
|
(caddr_t)as,
|
|
hz*AsToDs(as)->i_var.t101/1000);
|
|
ATRACEI(dbri_sbri_intr,
|
|
't101',
|
|
AsToDs(as)->i_var.t101);
|
|
} else if (old_nt_sbri.tss ==
|
|
DBRI_NTINFO4_G3) {
|
|
dbri_primitive_mph_di(as);
|
|
dbri_primitive_mph_ei1(as);
|
|
} else if (old_nt_sbri.tss == DBRI_NTINFO0_G4) {
|
|
/* XXX - start timer T1 */
|
|
}
|
|
|
|
break;
|
|
|
|
case DBRI_NTINFO4_G3:
|
|
AsToDs(as)->i_info.activation = ISDN_ACTIVATED;
|
|
dbri_bri_up(driver, ISDN_TYPE_NT);
|
|
if (old_nt_sbri.tss == DBRI_NTINFO2_G2) {
|
|
/* XXX - cancel T1 */
|
|
dbri_bri_up(driver, ISDN_TYPE_NT);
|
|
dbri_primitive_ph_ai(as);
|
|
dbri_primitive_mph_ai(as);
|
|
}
|
|
break;
|
|
|
|
case DBRI_NTINFO0_G4:
|
|
AsToDs(as)->i_info.activation =
|
|
ISDN_DEACTIVATED;
|
|
/*
|
|
* XXX - DBRI does not generate
|
|
* transitions to this state. Transition
|
|
* to G4 is done only through expiry of
|
|
* T1 or receipt of MPH-DEACTIVATEreq.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
} /* dbri_sbri_int */
|
|
|
|
|
|
void
|
|
dbri_pr_dbri_cmd(dcp, receive)
|
|
dbri_cmd_t *dcp;
|
|
int receive;
|
|
{
|
|
(void) printf("0x%x:\n", (unsigned int)dcp);
|
|
|
|
(void) printf("\tcmd.data=0x%x", (unsigned int)dcp->cmd.data);
|
|
(void) printf(" cmd.enddata=0x%x", (unsigned int)dcp->cmd.enddata);
|
|
(void) printf(" cmd.next=0x%x", (unsigned int)dcp->cmd.next);
|
|
(void) printf(" cmd.lastfragment=0x%x\n",
|
|
(unsigned int)dcp->cmd.lastfragment);
|
|
|
|
(void) printf("\tcmd.skip=%d", dcp->cmd.skip);
|
|
(void) printf(" cmd.done=%d", dcp->cmd.done);
|
|
(void) printf(" cmd.iotype=%d\n", dcp->cmd.iotype);
|
|
(void) printf("\tcmd.processed=%d", dcp->cmd.processed);
|
|
(void) printf(" cmd.dihandle=0x%x", (unsigned int)dcp->cmd.dihandle);
|
|
(void) printf(" nextio=0x%x\n", (unsigned int)dcp->nextio);
|
|
|
|
if (receive) {
|
|
(void) printf("\tmd.r.f {");
|
|
(void) printf(" eof=%d", dcp->md.r.f.eof);
|
|
(void) printf(" com=%d", dcp->md.r.f.com);
|
|
(void) printf(" cnt=%d", dcp->md.r.f.cnt);
|
|
(void) printf(" status=0x%x\n", dcp->md.r.f.status);
|
|
(void) printf("\tbufp=0x%x", (unsigned int)dcp->md.r.f.bufp);
|
|
(void) printf(" fp=0x%x", (unsigned int)dcp->md.r.f.fp);
|
|
(void) printf(" fint=%d", dcp->md.r.f.fint);
|
|
(void) printf(" mint=%d", dcp->md.r.f.mint);
|
|
(void) printf(" bcnt=%d", dcp->md.r.f.bcnt);
|
|
(void) printf(" }\n");
|
|
} else {
|
|
(void) printf("\tmd.t.f {\n");
|
|
(void) printf("\teof=%d", dcp->md.t.f.eof);
|
|
(void) printf(" dcrc=%d", dcp->md.t.f.dcrc);
|
|
(void) printf(" cnt=%d", dcp->md.t.f.cnt);
|
|
(void) printf(" fint=%d", dcp->md.t.f.fint);
|
|
(void) printf(" mint=%d", dcp->md.t.f.mint);
|
|
(void) printf(" idl=%d\n", dcp->md.t.f.idl);
|
|
(void) printf("\tfcnt=%d", dcp->md.t.f.fcnt);
|
|
(void) printf(" bufp=0x%x\n", (unsigned int)dcp->md.t.f.bufp);
|
|
(void) printf("\tfp=0x%x", (unsigned int)dcp->md.t.f.fp);
|
|
(void) printf(" status=0x%x\n", dcp->md.t.f.status);
|
|
(void) printf("\t}\n");
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
dbri_pr_packet(dcp, receive)
|
|
dbri_cmd_t *dcp;
|
|
int receive;
|
|
{
|
|
unsigned char *p;
|
|
unsigned char *end;
|
|
|
|
if (receive)
|
|
(void) printf("RECEIVE PACKET:\n");
|
|
else
|
|
(void) printf("TRANSMIT PACKET:\n");
|
|
|
|
for (dcp = dcp; dcp; dcp = dcp->nextio) { /* buffer complete */
|
|
dbri_pr_dbri_cmd(dcp, receive);
|
|
|
|
p = dcp->cmd.data;
|
|
if (receive) {
|
|
end = p + dcp->md.r.f.cnt;
|
|
} else {
|
|
end = p + dcp->md.t.f.cnt;
|
|
}
|
|
(void) printf("%x: ", (unsigned int)p);
|
|
while (p < end) {
|
|
if (((int)p % 16) == 0) {
|
|
(void) printf("\n%x: ", (unsigned int)p);
|
|
}
|
|
(void) printf("%x ", (unsigned int)*p);
|
|
++p;
|
|
}
|
|
(void) printf("\n%x; \n", (unsigned int)p);
|
|
|
|
if (dcp == (dbri_cmd_t *)dcp->cmd.lastfragment)
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
#if defined(DBRI_SWFCS)
|
|
static unsigned short fcstab[256] = {
|
|
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
|
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
|
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
|
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
|
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
|
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
|
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
|
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
|
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
|
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
|
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
|
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
|
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
|
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
|
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
|
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
|
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
|
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
|
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
|
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
|
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
|
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
|
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
|
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
|
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
|
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
|
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
|
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
|
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
|
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
|
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
|
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
|
|
};
|
|
|
|
|
|
/*
|
|
* dbri_checkfcs - paranoid re-checking of the FCS in a HDLC packet
|
|
* to inform us of bugs in DBRI. Returns TRUE if there is an error.
|
|
*/
|
|
int
|
|
dbri_checkfcs(dcp)
|
|
dbri_cmd_t *dcp; /* DBRI Command Pointer */
|
|
{
|
|
register int databytes;
|
|
register unsigned short fcs = 0xffff; /* Initial FCS value */
|
|
register unsigned char *p;
|
|
unsigned short dbri_fcs;
|
|
unsigned long first4bytes;
|
|
int error = FALSE;
|
|
|
|
p = dcp->cmd.data;
|
|
|
|
/*
|
|
* There are five types of fragment:
|
|
* 1) Data only
|
|
* 2) Data and complete FCS
|
|
* 3) Data and 1 byte of FCS
|
|
* 4) One byte of FCS only
|
|
* 5) FCS only
|
|
*/
|
|
databytes = dcp->md.r.f.cnt;
|
|
if (databytes > 4) {
|
|
first4bytes = (*p << 24) | (*(p+1) << 16) | (*(p+2) << 8) |
|
|
(*(p+3));
|
|
}
|
|
if (dcp->md.r.f.eof) {
|
|
if (databytes == 1) {
|
|
/* Type 4 */
|
|
databytes = 0;
|
|
} else if (databytes >= 2) {
|
|
/* Type 2 and Type 5 */
|
|
databytes -= 2;
|
|
} else {
|
|
panic("dbri: unexpected receive packet type");
|
|
}
|
|
} else if (dcp->nextio->md.r.f.eof && (dcp->nextio->md.r.f.cnt == 1)) {
|
|
/* Type 3 */
|
|
--databytes;
|
|
} else {
|
|
/* Not eof and next is not eof: Type 1 */
|
|
;
|
|
}
|
|
|
|
|
|
while (dcp != NULL && dcp->md.r.f.com) {
|
|
while (databytes-- > 0)
|
|
fcs = (fcs >> 8) ^ fcstab[(fcs ^ *p++) & 0xff];
|
|
|
|
if (dcp->md.r.f.eof)
|
|
break;
|
|
|
|
/*
|
|
* Before advancing to the next fragment, check for an FCS byte
|
|
* in current fragment.
|
|
*/
|
|
if (!dcp->md.r.f.eof &&
|
|
dcp->nextio->md.r.f.eof &&
|
|
dcp->nextio->md.r.f.cnt == 1) {
|
|
dbri_fcs = *p++ << 8;
|
|
}
|
|
|
|
if (dcp->nextio == NULL)
|
|
break;
|
|
|
|
dcp = dcp->nextio;
|
|
p = dcp->cmd.data;
|
|
databytes = dcp->md.r.f.cnt;
|
|
|
|
if (dcp->md.r.f.eof) {
|
|
if (databytes == 1) {
|
|
/* Type 4 */
|
|
databytes = 0;
|
|
} else if (databytes >= 2) {
|
|
/* Type 2 and Type 5 */
|
|
databytes -= 2;
|
|
} else {
|
|
panic("dbri: unexpected receive packet type");
|
|
}
|
|
} else if (dcp->nextio->md.r.f.eof &&
|
|
(dcp->nextio->md.r.f.cnt == 1)) {
|
|
/* Type 3 */
|
|
--databytes;
|
|
} else {
|
|
/* Not eof and next is not eof: Type 1 */
|
|
;
|
|
}
|
|
}
|
|
/* p is left pointing at the first FCS octet */
|
|
|
|
/*
|
|
* Calculate SW FCS
|
|
*/
|
|
fcs ^= 0xffff;
|
|
fcs = ((fcs & 0xff) << 8) | ((fcs >> 8) & 0xff);
|
|
|
|
/*
|
|
* Retrieve HW FCS
|
|
*/
|
|
if (dcp->md.r.f.cnt > 1) {
|
|
dbri_fcs = *p++ << 8;
|
|
dbri_fcs |= *p++;
|
|
} else {
|
|
dbri_fcs = *p++ << 8;
|
|
if (dcp->md.r.f.eof || dcp->nextio == NULL ||
|
|
!dcp->nextio->md.r.f.eof)
|
|
panic("dbri: unexpected error while getting fcs");
|
|
p = dcp->nextio->cmd.data;
|
|
dbri_fcs |= *p++;
|
|
}
|
|
|
|
if (fcs != dbri_fcs) {
|
|
error = TRUE; /* Discard this packet */
|
|
(void) printf("dbri: fcs mismatch, fcs: %x != dbri_fcs: %x\n",
|
|
fcs, dbri_fcs);
|
|
(void) printf("dbri: first4bytes = %x\n", first4bytes);
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
#endif /* DBRI_SWFCS */
|