2517 lines
64 KiB
C
2517 lines
64 KiB
C
#ident "@(#) dbri_subr.c 1.1@(#) Copyright (c) 1989-92 Sun Microsystems, Inc."
|
|
|
|
#include <sys/errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/limits.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/ioccom.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/file.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"
|
|
|
|
unsigned int dbri_monitor_gain();
|
|
unsigned int dbri_outport();
|
|
unsigned int dbri_play_gain();
|
|
unsigned int dbri_record_gain();
|
|
void dbri_chil_intr();
|
|
void dbri_error_msg();
|
|
void dbri_fxdt_intr();
|
|
void mmcodec_init_pipes();
|
|
void mmcodec_reset();
|
|
void mmcodec_setup_ctrl_mode();
|
|
void dbri_te_unplug();
|
|
void dbri_primitive_mph_ei2();
|
|
void dbri_primitive_mph_ii_c();
|
|
void dbri_primitive_ph_di();
|
|
void dbri_primitive_mph_di();
|
|
void dbri_primitive_mph_ei2();
|
|
void dbri_primitive_mph_ei2();
|
|
void dbri_primitive_mph_ei2();
|
|
void dbri_bri_down();
|
|
void dbri_bri_up();
|
|
|
|
extern char *dbri_channel_name();
|
|
extern char *dbri_mode_name();
|
|
|
|
|
|
/*
|
|
* Reset the DBRI and MMCODEC chips to a known safe state. Chip needs to
|
|
* be in a fully reset state. This is currently called at attach time and
|
|
* unload time. When called from attach the streams have just been setup
|
|
* but nothing is connected. At UNLOAD time the chip could really be in
|
|
* any state.
|
|
*/
|
|
void
|
|
dbri_initchip(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
int i;
|
|
int unload;
|
|
dbri_reg_t *chip = driver->chip;
|
|
dbri_intq_t *qp, *nqp;
|
|
pipe_tab_t *pipep;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
unload = driver->init_chip; /* load or unload? */
|
|
chip->n.sts = DBRI_STS_R; /* reset DBRI chip */
|
|
driver->init_chip = FALSE;
|
|
|
|
/*
|
|
* Kill off all timers
|
|
*/
|
|
driver->keep_alive_running = FALSE;
|
|
untimeout((int(*)())dbri_keep_alive, (caddr_t)driver);
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_TE_D_IN];
|
|
if (ISPIPEINUSE(pipep))
|
|
untimeout((int(*)())dbri_te_timer,
|
|
(caddr_t)pipep->as->play_as);
|
|
pipep = &driver->ptab[DBRI_PIPE_NT_D_IN];
|
|
if (ISPIPEINUSE(pipep))
|
|
untimeout((int(*)())dbri_nt_timer,
|
|
(caddr_t)pipep->as->play_as);
|
|
|
|
/*
|
|
* Clear out structures. This also intializes the command
|
|
* queue to all DBRI WAIT's since the WAIT opcode is 0
|
|
*/
|
|
bzero((caddr_t)driver->intq.intq_bp,
|
|
DBRI_NUM_INTQS * sizeof (dbri_intq_t));
|
|
bzero((caddr_t)driver->cmdqp, sizeof (dbri_chip_cmdq_t));
|
|
|
|
/* Circularly chain up the intr queues and set status ptrs */
|
|
qp = driver->intq.intq_bp;
|
|
for (i = 0; i < (DBRI_NUM_INTQS - 1); ++i, qp++) {
|
|
nqp = qp + 1; /* can't do this calc in DBRI_ADDR */
|
|
qp->nextq = (dbri_intq_t *)DBRI_ADDR(driver, nqp);
|
|
}
|
|
/* last pts back to 1st */
|
|
qp->nextq = (dbri_intq_t *)DBRI_ADDR(driver, driver->intq.intq_bp);
|
|
|
|
driver->intq.curqp = driver->intq.intq_bp;
|
|
driver->intq.off = 0;
|
|
|
|
/* Now set status pointers for the command queue */
|
|
driver->cmdqp->cmdhp = 0;
|
|
driver->init_cmdq = FALSE;
|
|
|
|
bzero((caddr_t) &driver->ser_sts, sizeof (serial_status_t));
|
|
bzero((caddr_t) driver->ptab, sizeof (driver->ptab));
|
|
for (i = 0; i < 10; i++) { /* loop up to 2.5ms */
|
|
DELAY(250); /* don't bog down bus */
|
|
if (!(chip->n.sts & DBRI_STS_R)) /* brk on reset done */
|
|
break;
|
|
}
|
|
if (chip->n.sts & DBRI_STS_R) {
|
|
(void) printf("DBRI: error, unable to reset chip\n");
|
|
return;
|
|
}
|
|
driver->init_chip = TRUE;
|
|
/*
|
|
* If we are unloading the driver, just return
|
|
*/
|
|
if (unload)
|
|
return;
|
|
|
|
mmcodec_reset(driver);
|
|
|
|
dprintf("dbri_attach: intr queue setup\n");
|
|
cmd.cmd_wd1 = DBRI_CMD_IIQ;
|
|
cmd.cmd_wd2 = ((unsigned int)DBRI_ADDR(driver, driver->intq.curqp));
|
|
dbri_command(driver, cmd); /* Enable interrupts */
|
|
|
|
chip->n.sts |= (DBRI_STS_G|DBRI_STS_E);
|
|
|
|
return;
|
|
} /* dbri_initchip */
|
|
|
|
|
|
void
|
|
dbri_pr_conn_req(crp)
|
|
dbri_conn_req_t *crp;
|
|
{
|
|
dprintf("xchan=%s rchan=%s mode=%s\n",
|
|
dbri_channel_name((int)crp->xchan),
|
|
dbri_channel_name((int)crp->rchan),
|
|
dbri_mode_name(crp->mode));
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* dbri_con_stream - the top level routine that is called to connect
|
|
* up a system level stream. This involves determining what individual
|
|
* DBRI h/w streams must be setup and invoking the routines to actually
|
|
* configure the h/w and then pointing them to the software streams.
|
|
* Called from both dbri_ioctl and dbri_open. Note that the open call
|
|
* must form it's own conp struct.
|
|
*
|
|
* NOTE: There is no need to add support for fixed data pipes.
|
|
*/
|
|
int
|
|
dbri_con_stream(as, conp, error)
|
|
aud_stream_t *as;
|
|
isdn_conn_req_t *conp;
|
|
int *error;
|
|
{
|
|
unsigned int pipe;
|
|
unsigned int opipe;
|
|
aud_stream_t *oas;
|
|
pipe_tab_t *pipep;
|
|
dbri_conn_req_t dcon;
|
|
dbri_dev_t *driver;
|
|
dbri_stream_t *dsp;
|
|
aud_stream_t *p_as, *r_as;
|
|
|
|
*error = 0;
|
|
if ((conp->unit < 0) || (conp->unit >= Ndbri)) {
|
|
dprintf("dbri: Unit # %d invalid \n", conp->unit);
|
|
ATRACEI(dbri_con_stream, 'badu', as);
|
|
*error = ENODEV;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
driver = &Dbri_devices[conp->unit];
|
|
dprintf("dbri_con_stream: driver address=0x%x \n",
|
|
(unsigned int)driver);
|
|
|
|
/* Check the port values to insure that they are sane */
|
|
if (((int)conp->xmitport >= (int)ISDN_LAST_PORT) ||
|
|
((int)conp->xmitport < (int)ISDN_PORT_NONE) ||
|
|
((int)conp->recvport >= (int)ISDN_LAST_PORT) ||
|
|
((int)conp->recvport < (int)ISDN_PORT_NONE)) {
|
|
dprintf("dbri: Invalid transmit or receive port\n");
|
|
ATRACEI(dbri_con_stream, 'badp', conp);
|
|
*error = ENODEV;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/*
|
|
* Setup DBRI h/w specific connection request from generic ISDN
|
|
* connection request.
|
|
*/
|
|
dcon.xchan = Port_tab[(int)conp->xmitport].xchan;
|
|
dcon.rchan = Port_tab[(int)conp->recvport].rchan;
|
|
|
|
/*
|
|
* Assign mode
|
|
*
|
|
* NB: this doesn't deal with fixed pipes
|
|
*/
|
|
if ((conp->xmitport != ISDN_HOST) && (conp->recvport != ISDN_HOST)) {
|
|
dcon.mode = DBRI_MODE_SER;
|
|
} else if (conp->mode == ISDN_MODE_TRANSPARENT) {
|
|
dcon.mode = DBRI_MODE_XPRNT;
|
|
} else {
|
|
/* Grab mode of channel not connected to host from table */
|
|
if (dcon.xchan == DBRI_HOST_CHNL)
|
|
dcon.mode = Chan_tab[(int)dcon.rchan].mode;
|
|
else
|
|
dcon.mode = Chan_tab[(int)dcon.xchan].mode;
|
|
}
|
|
|
|
dprintf("dbri_con_stream: as=0x%x as->out=0x%x as->in=0x%x\n",
|
|
(unsigned int)as, (unsigned int)as->play_as,
|
|
(unsigned int)as->record_as);
|
|
|
|
if (ISDATASTREAM(as)) {
|
|
/* Get appropriate input or output stream for data streams */
|
|
as = (dcon.xchan == DBRI_HOST_CHNL) ? as->record_as
|
|
: as->play_as;
|
|
}
|
|
|
|
/* Check sanity of connection request */
|
|
dprintf("dbri_con_stream: first as=0x%x\n", (unsigned int)as);
|
|
dbri_pr_conn_req(&dcon);
|
|
|
|
if (dbri_check_req(as, conp, &dcon, error) == DBRI_BADCONN) {
|
|
ATRACEI(dbri_con_stream, 'badc', as);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/* If audio setup CHI interface first */
|
|
if (ISAUDIOCONNECTION(dcon.xchan) || ISAUDIOCONNECTION(dcon.rchan)) {
|
|
/*
|
|
* For a serial to serial connection on the control stream
|
|
* we need to mark the audio streams as using serial to
|
|
* serial pipes so that DBRI can be the master when we
|
|
* get a fxdt interrupt.
|
|
*/
|
|
if (ISCONTROLSTREAM(as)) {
|
|
driver->ser_sts.chi_flags |= CHI_FLAG_SER_STS;
|
|
audio_ser_to_ser_config(as, driver);
|
|
/*
|
|
* XXX Could be a race if connection succeeds after
|
|
* this times out.
|
|
*/
|
|
if (driver->ser_sts.chi_state != CHI_IN_DATA_MODE) {
|
|
driver->ser_sts.chi_flags &= ~CHI_FLAG_SER_STS;
|
|
*error = ENODEV;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
} else {
|
|
driver->ser_sts.chi_flags &= ~CHI_FLAG_SER_STS;
|
|
if (audio_con_stream(driver, as, error) == DBRI_BADCONN)
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
|
|
/* connect up DBRI serial side of pipe */
|
|
if ((pipe = dbri_setup_chnl(as, &dcon, error)) == DBRI_BAD_PIPE) {
|
|
if (ISCONTROLSTREAM(as) &&
|
|
(ISAUDIOCONNECTION(dcon.xchan) ||
|
|
ISAUDIOCONNECTION(dcon.rchan))) {
|
|
driver->ser_sts.chi_flags &= ~CHI_FLAG_SER_STS;
|
|
audio_ser_to_ser_config(as, driver);
|
|
}
|
|
ATRACEI(dbri_con_stream, 'badp', as);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
pipep = &driver->ptab[pipe];
|
|
AsToDs(as)->pipe = pipe; /* save pipe index */
|
|
/* XCARE for serial mode connections */
|
|
pipep->as = as; /* point back to as structure */
|
|
pipep->xchan = dcon.xchan;
|
|
pipep->rchan = dcon.rchan;
|
|
as->mode = (dcon.mode == DBRI_MODE_XPRNT) ? AUDMODE_AUDIO
|
|
: AUDMODE_HDLC;
|
|
|
|
dsp = &driver->ioc[(int)dcon.xchan];
|
|
dprintf("dbri_con_stream: pipe=%u, dsp=0x%x, head=0x%x, tail=0x%x\n",
|
|
pipe, (unsigned int)dsp, (unsigned int)&dsp->cmdptr,
|
|
(unsigned int)&dsp->cmdlast);
|
|
|
|
/* for two way stream need to connect another pipe in other dir */
|
|
if (conp->dir == ISDN_TWOWAY_STREAM) {
|
|
/*
|
|
* swap dir of channels requested and adjust mode for
|
|
* TE-D out
|
|
*/
|
|
dcon.rchan = Port_tab[(int)conp->xmitport].rchan;
|
|
dcon.xchan = Port_tab[(int)conp->recvport].xchan;
|
|
|
|
/*
|
|
* D-channel only case where modes are different in each
|
|
* dir
|
|
*/
|
|
if (dcon.xchan == DBRI_TE_D_OUT)
|
|
dcon.mode = Chan_tab[(int)dcon.xchan].mode;
|
|
|
|
opipe = pipe; /* save old pipe and "as" for error case */
|
|
oas = as;
|
|
/* For serial to serial pipes leave "as" intact */
|
|
if (dcon.mode != DBRI_MODE_SER) {
|
|
as = (dcon.xchan == DBRI_HOST_CHNL) ? as->record_as
|
|
: as->play_as;
|
|
}
|
|
dprintf("dbri_con_stream: second as=0x%x\n", (unsigned int)as);
|
|
|
|
pipe = dbri_setup_chnl(as, &dcon, error);
|
|
if (pipe == DBRI_BAD_PIPE) {
|
|
dbri_remove_dts(oas, opipe); /* xcon 1st pipe */
|
|
pipep = &driver->ptab[opipe];
|
|
AsToDs(oas)->pipe = DBRI_BAD_PIPE;
|
|
pipep->as = (aud_stream_t *)NULL;
|
|
pipep->xchan = DBRI_NO_CHNL;
|
|
pipep->rchan = DBRI_NO_CHNL;
|
|
oas->mode = AUDMODE_NONE;
|
|
ATRACEI(dbri_con_stream, 'bdp2', as);
|
|
if (ISCONTROLSTREAM(as) &&
|
|
(ISAUDIOCONNECTION(dcon.xchan) ||
|
|
ISAUDIOCONNECTION(dcon.rchan))) {
|
|
driver->ser_sts.chi_flags &= ~CHI_FLAG_SER_STS;
|
|
audio_ser_to_ser_config(as, driver);
|
|
}
|
|
return (DBRI_BADCONN);
|
|
}
|
|
pipep = &driver->ptab[pipe];
|
|
AsToDs(as)->pipe = pipe; /* save pipe index */
|
|
/* XCARE for serial mode connections */
|
|
pipep->as = as; /* point back to as structure */
|
|
pipep->xchan = dcon.xchan;
|
|
pipep->rchan = dcon.rchan;
|
|
as->mode = (dcon.mode == DBRI_MODE_XPRNT) ? AUDMODE_AUDIO
|
|
: AUDMODE_HDLC;
|
|
}
|
|
|
|
/* Marks ports that serial to serial connections use as busy */
|
|
if (dcon.mode == DBRI_MODE_SER) {
|
|
/* set first direction */
|
|
p_as = &driver->ioc[(int)dcon.xchan].as;
|
|
r_as = &driver->ioc[(int)dcon.rchan].as;
|
|
|
|
p_as->play_as->info.open = TRUE;
|
|
r_as->record_as->info.open = TRUE;
|
|
|
|
/* if bidirectional set second direction */
|
|
if (conp->dir == ISDN_TWOWAY_STREAM) {
|
|
p_as = &driver->ioc[(int)dcon.rchan].as;
|
|
r_as = &driver->ioc[(int)dcon.xchan].as;
|
|
|
|
p_as->play_as->info.open = TRUE;
|
|
r_as->record_as->info.open = TRUE;
|
|
}
|
|
}
|
|
|
|
return (DBRI_GOODCONN);
|
|
} /* dbri_con_stream */
|
|
|
|
|
|
/*
|
|
* dbri_xcon_stream - disconnect a stream from the hardware. For a
|
|
* serial to serial connection 2 way stream all the disconnect work is
|
|
* done in this routine. For a data stream 2 way connection, this routine
|
|
* is called twice by the close routine.
|
|
*/
|
|
int
|
|
dbri_xcon_stream(as, conp)
|
|
aud_stream_t *as;
|
|
isdn_conn_req_t *conp; /* NULL for data streams */
|
|
{
|
|
int s;
|
|
unsigned int pipe;
|
|
pipe_tab_t *pipep;
|
|
dbri_chnl_t xchan, rchan;
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *p_as, *r_as;
|
|
|
|
s = splstr();
|
|
if (ISDATASTREAM(as)) {
|
|
int result;
|
|
|
|
/* data stream so get information from as struct */
|
|
if (!ISHWCONNECTED(as)) {
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/*
|
|
* For a data stream only a single direction stream is
|
|
* dismantled at a time as called by the close routine.
|
|
*/
|
|
driver = (dbri_dev_t *) as->v->devdata;
|
|
|
|
/*
|
|
* If this is a D channel and the pipe doesn't point to
|
|
* itself, there are other pipes connected. Therefore,
|
|
* disconnect those pipes too.
|
|
*/
|
|
pipe = AsToDs(as)->pipe;
|
|
ATRACEI(dbri_xcon_stream, 'data', pipe);
|
|
pipep = &driver->ptab[pipe];
|
|
|
|
if (ISTEDCHANNEL(as) || ISNTDCHANNEL(as)) {
|
|
int inpipe;
|
|
unsigned int opipe;
|
|
aud_stream_t *oas;
|
|
|
|
/*
|
|
* Disconnect all channels hooked to the
|
|
* particular D-channel and then send a
|
|
* signal upstream with M_ERROR set.
|
|
*/
|
|
inpipe = (pipep->dts.r.vi == DBRI_DTS_VI)? TRUE: FALSE;
|
|
while ((inpipe && (pipep->dts.r.oldin != pipe)) ||
|
|
(!inpipe && (pipep->dts.r.oldout != pipe))) {
|
|
if (inpipe)
|
|
opipe = pipep->dts.r.oldin;
|
|
else
|
|
opipe = pipep->dts.r.oldout;
|
|
oas = driver->ptab[opipe].as;
|
|
if (dbri_disconnect(driver, oas, opipe) ==
|
|
DBRI_BADCONN) {
|
|
dprintf("dbrixcon: failed.\n");
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
/*
|
|
* Now disable any further reading or writing
|
|
* to the stream. Check if we want to also
|
|
* send a M_HANGUP message upstream.
|
|
*/
|
|
dbri_error_msg(oas, M_ERROR);
|
|
}
|
|
}
|
|
result = dbri_disconnect(driver, as, pipe);
|
|
(void) splx(s);
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* It's a control stream; must use connection request
|
|
*/
|
|
if ((conp->unit < 0) || (conp->unit >= Ndbri)) {
|
|
dprintf("dbri: Unit # %d invalid \n", conp->unit);
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
driver = &Dbri_devices[conp->unit];
|
|
xchan = Port_tab[(int)conp->xmitport].xchan;
|
|
rchan = Port_tab[(int)conp->recvport].rchan;
|
|
|
|
/*
|
|
* XXX - want findshortpipe to deal with data streams also. This is
|
|
* the loophole so if process dies then a control stream would be
|
|
* able to remove the channel. (This means long pipes) Also need
|
|
* to set pids
|
|
*/
|
|
pipe = dbri_findshortpipe(as, rchan, xchan);
|
|
if (pipe == DBRI_BAD_PIPE) {
|
|
dprintf("dbri: could not find pipe\n");
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/* Disconnect first pipe */
|
|
if (dbri_disconnect(driver, as, pipe) == DBRI_BADCONN) {
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
if (conp->dir == ISDN_TWOWAY_STREAM) {
|
|
rchan = Port_tab[(int)conp->xmitport].rchan;
|
|
xchan = Port_tab[(int)conp->recvport].xchan;
|
|
pipe = dbri_findshortpipe(as, rchan, xchan);
|
|
if (pipe == DBRI_BAD_PIPE) {
|
|
dprintf("dbri: could not find pipe\n");
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
if (dbri_disconnect(driver, as, pipe) == DBRI_BADCONN) {
|
|
(void) splx(s);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
|
|
/* Mark ports that serial to serial connections used as not busy */
|
|
/* clear first direction */
|
|
p_as = &driver->ioc[(int)xchan].as;
|
|
r_as = &driver->ioc[(int)rchan].as;
|
|
|
|
p_as->play_as->info.open = FALSE;
|
|
r_as->record_as->info.open = FALSE;
|
|
|
|
/* if bidirectional clear second direction */
|
|
if (conp->dir == ISDN_TWOWAY_STREAM) {
|
|
p_as = &driver->ioc[(int)rchan].as;
|
|
r_as = &driver->ioc[(int)xchan].as;
|
|
|
|
p_as->play_as->info.open = FALSE;
|
|
r_as->record_as->info.open = FALSE;
|
|
}
|
|
|
|
/* If audio setup restore CHI interface so that CODEC is master */
|
|
if (ISAUDIOCONNECTION(xchan) || ISAUDIOCONNECTION(rchan)) {
|
|
driver->ser_sts.chi_flags &= ~CHI_FLAG_SER_STS;
|
|
audio_ser_to_ser_config(as, driver);
|
|
p_as = &driver->ioc[(int) DBRI_AUDIO_OUT].as;
|
|
audio_sendsig(p_as->control_as);
|
|
wakeup((caddr_t)p_as->control_as); /* wakeup audio_open() */
|
|
}
|
|
(void) splx(s);
|
|
return (DBRI_GOODCONN);
|
|
} /* dbri_xcon_stream */
|
|
|
|
|
|
void
|
|
audio_ser_to_ser_config(as, driver)
|
|
aud_stream_t *as;
|
|
dbri_dev_t *driver;
|
|
{
|
|
int i;
|
|
int s;
|
|
|
|
mmcodec_set_audio_config(driver, as, 8000, 1, 8, AUDIO_ENCODING_ULAW);
|
|
/*
|
|
* XXX - Hack to delay up to 500ms waiting for
|
|
* CHI setup to complete.
|
|
*/
|
|
/* XXX - Check on this...I think this is dangerous */
|
|
s = splx(0); /* lower intr level */
|
|
for (i=0; i < 5000; ++i) {
|
|
DELAY(100); /* Delay 100 Us */
|
|
/* check if setup complete and successful */
|
|
if (driver->ser_sts.chi_state==CHI_IN_DATA_MODE)
|
|
break;
|
|
}
|
|
(void) splx(s); /* restore intr level */
|
|
} /* audio_ser_to_ser_config */
|
|
|
|
|
|
void
|
|
dbri_hold_f3(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
serial_status_t *serialp;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
serialp = &driver->ser_sts;
|
|
if ((serialp->te_cmd & DBRI_NTE_ACT) == 0) {
|
|
ATRACEI(dbri_hold_f3, 'noop', serialp->te_cmd);
|
|
return;
|
|
}
|
|
|
|
serialp->te_cmd &= ~(DBRI_NTE_ACT|DBRI_NTE_IRM_STATUS);
|
|
cmd.cmd_wd1 = serialp->te_cmd; /* NT command */
|
|
dbri_command(driver, cmd);
|
|
/* XXX must insure that the command has been processed */
|
|
cmd.cmd_wd1 = DBRI_CMD_REX | DBRI_CMDI
|
|
| (((driver-Dbri_devices) & 0x3f) << 10) /* 6 bit unit num */
|
|
| 1; /* 10 bit tag */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_exit_f3(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
serial_status_t *serialp;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
serialp = &driver->ser_sts;
|
|
if (serialp->te_cmd & DBRI_NTE_ACT) {
|
|
ATRACEI(dbri_exit_f3, 'noop', serialp->te_cmd);
|
|
return;
|
|
}
|
|
|
|
serialp->te_cmd |= DBRI_NTE_ACT;
|
|
serialp->te_cmd &= ~DBRI_NTE_IRM_STATUS;
|
|
cmd.cmd_wd1 = serialp->te_cmd; /* NT command */
|
|
dbri_command(driver, cmd);
|
|
/* XXX must insure that the command has been processed */
|
|
cmd.cmd_wd1 = DBRI_CMD_REX | DBRI_CMDI
|
|
| (((driver-Dbri_devices) & 0x3f) << 10) /* 6 bit unit num */
|
|
| 2; /* 10 bit tag */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_hold_g1(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
serial_status_t *serialp;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
serialp = &driver->ser_sts;
|
|
if ((serialp->nt_cmd & DBRI_NTE_ACT) == 0) {
|
|
ATRACEI(dbri_hold_g1, 'noop', serialp->nt_cmd);
|
|
return;
|
|
}
|
|
|
|
serialp->nt_cmd &= ~(DBRI_NTE_ACT|DBRI_NTE_IRM_STATUS);
|
|
cmd.cmd_wd1 = serialp->nt_cmd; /* NT command */
|
|
dbri_command(driver, cmd);
|
|
/* XXX must insure that the command has been processed */
|
|
cmd.cmd_wd1 = DBRI_CMD_REX | DBRI_CMDI
|
|
| (((driver-Dbri_devices) & 0x3f) << 10) /* 6 bit unit num */
|
|
| 3; /* 10 bit tag */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_exit_g1(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
serial_status_t *serialp;
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
serialp = &driver->ser_sts;
|
|
if (serialp->nt_cmd & DBRI_NTE_ACT) {
|
|
ATRACEI(dbri_exit_g1, 'noop', serialp->nt_cmd);
|
|
return;
|
|
}
|
|
|
|
serialp->nt_cmd |= DBRI_NTE_ACT;
|
|
serialp->nt_cmd &= ~DBRI_NTE_IRM_STATUS;
|
|
cmd.cmd_wd1 = serialp->nt_cmd; /* NT command */
|
|
dbri_command(driver, cmd);
|
|
/* XXX must insure that the command has been processed */
|
|
cmd.cmd_wd1 = DBRI_CMD_REX | DBRI_CMDI
|
|
| (((driver-Dbri_devices) & 0x3f) << 10) /* 6 bit unit num */
|
|
| 4; /* 10 bit tag */
|
|
dbri_command(driver, cmd);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
int
|
|
dbri_disconnect(driver, as, pipe)
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *as;
|
|
unsigned int pipe;
|
|
{
|
|
int s;
|
|
pipe_tab_t *pipep;
|
|
serial_status_t *serialp;
|
|
|
|
s = splstr();
|
|
pipep = &driver->ptab[pipe];
|
|
|
|
/*
|
|
* Clear out SDP command to gracefully stop data flow
|
|
*
|
|
* XXX - Needed?
|
|
*/
|
|
dbri_stop(as);
|
|
|
|
pipep->sdp = 0; /* clr sdp mem copy */
|
|
|
|
/* if this is a base pipe disable the serial interface */
|
|
serialp = &driver->ser_sts;
|
|
switch (pipe) {
|
|
case DBRI_PIPE_TE_D_IN:
|
|
/* check if D_OUT in use ... if so break */
|
|
pipep = &driver->ptab[DBRI_PIPE_TE_D_OUT];
|
|
if (pipep->sdp != 0)
|
|
break;
|
|
|
|
/*FALLTHROUGH*/
|
|
|
|
case DBRI_PIPE_TE_D_OUT:
|
|
/* check if D_IN in use ... if so break */
|
|
pipep = &driver->ptab[DBRI_PIPE_TE_D_IN];
|
|
if (pipep->sdp != 0)
|
|
break;
|
|
|
|
dbri_te_unplug(driver);
|
|
DELAY(250); /* XXX - delay for nice exit */
|
|
|
|
if (driver->init_chip) /* Disable interface */
|
|
dbri_disable_te(driver);
|
|
break;
|
|
|
|
case DBRI_PIPE_NT_D_IN:
|
|
/* check if D_OUT in use ... if so break */
|
|
pipep = &driver->ptab[DBRI_PIPE_NT_D_OUT];
|
|
if (pipep->sdp != 0)
|
|
break;
|
|
|
|
/*FALLTHROUGH*/
|
|
|
|
case DBRI_PIPE_NT_D_OUT:
|
|
/* check if D_IN in use ... if so break */
|
|
pipep = &driver->ptab[DBRI_PIPE_NT_D_IN];
|
|
if (pipep->sdp != 0)
|
|
break;
|
|
|
|
/*
|
|
* Disable NT serial interface
|
|
*/
|
|
dbri_hold_g1(driver);
|
|
|
|
if (driver->init_chip) { /* disable intf */
|
|
dbri_disable_nt(driver);
|
|
/* XXX Should relay control be in disable_nt? */
|
|
driver->chip->n.sts |= DBRI_STS_F;
|
|
}
|
|
serialp->nt_cmd = 0; /* clr cmd */
|
|
serialp->nt_sbri.tss = DBRI_NTINFO0_G1;
|
|
|
|
/*
|
|
* If NT D-channels not in use then kill alive
|
|
* timer and switch in relays.
|
|
*/
|
|
driver->keep_alive_running = FALSE;
|
|
untimeout((int(*)())dbri_keep_alive, (caddr_t)driver);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
} /* switch */
|
|
|
|
/* Now remove the dts timeslot definitions */
|
|
if (pipep->monflag) {
|
|
/*
|
|
* clear flag so that when monitor pipe is removed the
|
|
* monitor pipe can remove this pipe too.
|
|
*/
|
|
pipep->monflag = FALSE;
|
|
} else {
|
|
/* disconnect up DBRI serial side of pipe */
|
|
dbri_remove_dts(as, pipe);
|
|
}
|
|
|
|
if (ISAUDIOCONNECTION(pipep->xchan) || ISAUDIOCONNECTION(pipep->rchan))
|
|
audio_xcon_stream(driver, as);
|
|
|
|
pipep->xchan = DBRI_NO_CHNL; /* clear out pipe indicators */
|
|
pipep->rchan = DBRI_NO_CHNL;
|
|
pipep->as = (aud_stream_t *) NULL;
|
|
pipep->pid = DBRI_NO_TAG;
|
|
AsToDs(as)->pipe = DBRI_BAD_PIPE;
|
|
as->mode = AUDMODE_NONE;
|
|
|
|
(void) splx(s);
|
|
return (DBRI_GOODCONN);
|
|
} /* dbri_disconnect */
|
|
|
|
|
|
/*
|
|
* dbri_setup_chnl - calls the routines to get a new pipe, and then setup
|
|
* the in memory copy of the sdp, dts, and te/nt commands. Then issues
|
|
* the sdp, dts, and te/nt commands when needed.
|
|
*/
|
|
unsigned int
|
|
dbri_setup_chnl(as, dcon, error)
|
|
aud_stream_t *as;
|
|
dbri_conn_req_t *dcon;
|
|
int *error;
|
|
{
|
|
int s;
|
|
unsigned int npipe;
|
|
pipe_tab_t *pipep;
|
|
serial_status_t *serialp;
|
|
dbri_chip_cmd_t cmd;
|
|
dbri_chnl_t channel;
|
|
dbri_dev_t *driver;
|
|
|
|
*error = 0;
|
|
s = splstr();
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
/*
|
|
* Grab any non-host channel.
|
|
*/
|
|
channel = (dcon->xchan != DBRI_HOST_CHNL) ? dcon->xchan
|
|
: dcon->rchan;
|
|
|
|
/* get a new pipe */
|
|
npipe = dbri_getpipe(driver, channel, dcon->mode);
|
|
if (npipe == DBRI_BAD_PIPE) {
|
|
dprintf("dbri_setup_chnl: No available pipes\n");
|
|
*error = EPIPE;
|
|
(void) splx(s);
|
|
return (DBRI_BAD_PIPE);
|
|
}
|
|
pipep = &driver->ptab[npipe];
|
|
serialp = &driver->ser_sts;
|
|
dbri_setup_sdp(driver, npipe, dcon, Chan_tab[(int)channel].dir);
|
|
dprintf("dbri_setup_chnl: pipe=%u sdp=0x%x\n", npipe, pipep->sdp);
|
|
|
|
/*
|
|
* If this is a receive pipe then allow first SDP to point to
|
|
* posted receive descriptor chain instead of pointing to NULL.
|
|
*/
|
|
if ((Chan_tab[(int)channel].dir == DBRI_IN) && (dcon->mode != DBRI_MODE_SER)) {
|
|
AsToDs(as)->pipe = npipe;
|
|
|
|
/*
|
|
* Force EOL precondition so that initial SDP is issued.
|
|
*/
|
|
AsToDs(as)->recv_eol = TRUE;
|
|
|
|
dprintf("(dbri_setup_chnl:%u)", npipe);
|
|
audio_process_record(as);
|
|
} else {
|
|
cmd.cmd_wd1 = pipep->sdp;
|
|
cmd.cmd_wd2 = 0; /* NULL address */
|
|
dbri_command(driver, cmd); /* issue SDP command */
|
|
|
|
cmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, cmd);
|
|
}
|
|
|
|
/* attach pipe & setup dts */
|
|
if (dbri_setup_dts(as, npipe, channel) == DBRI_BADCONN) {
|
|
pipep->dts.word32 = 0; /* clear pipe in use */
|
|
pipep->in_tsd.word32 = 0;
|
|
pipep->out_tsd.word32 = 0;
|
|
pipep->sdp = 0;
|
|
*error = EPIPE;
|
|
(void) splx(s);
|
|
return (DBRI_BAD_PIPE);
|
|
}
|
|
|
|
/* For serial to serial connections setup other tsd of dts command */
|
|
if (dcon->mode == DBRI_MODE_SER) {
|
|
channel = dcon->rchan; /* get recv channel this time */
|
|
if (dbri_setup_dts(as, npipe, channel) == DBRI_BADCONN) {
|
|
pipep->dts.word32 = 0; /* clear pipe in use */
|
|
pipep->in_tsd.word32 = 0;
|
|
pipep->out_tsd.word32 = 0;
|
|
pipep->sdp = 0;
|
|
*error = EPIPE;
|
|
(void) splx(s);
|
|
return (DBRI_BAD_PIPE);
|
|
}
|
|
}
|
|
|
|
dprintf("dbri_setup_chnl: pipep=0x%x dts=0x%x\n", (unsigned int)pipep,
|
|
pipep->dts.word32);
|
|
dprintf("dbri_setup_chnl: in_tsd=0x%x out_tsd=0x%x\n",
|
|
pipep->in_tsd.word32, pipep->out_tsd.word32);
|
|
cmd.cmd_wd1 = pipep->dts.word32; /* DTS command */
|
|
cmd.cmd_wd2 = pipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = pipep->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
/* Setup serial interface if necessary and issue commands */
|
|
dbri_setup_ntte(as, channel, serialp);
|
|
|
|
(void) splx(s);
|
|
return (npipe); /* return pipe number */
|
|
} /* dbri_setup_chnl */
|
|
|
|
|
|
/*
|
|
* dbri_setup_sdp - sets up the in memory copy of the sdp and te/nt
|
|
* commands
|
|
*/
|
|
void
|
|
dbri_setup_sdp(driver, pipe, dcon, dir)
|
|
dbri_dev_t *driver;
|
|
unsigned int pipe;
|
|
dbri_conn_req_t *dcon;
|
|
int dir;
|
|
{
|
|
pipe_tab_t *pipep;
|
|
|
|
pipep = &driver->ptab[pipe];
|
|
pipep->sdp = DBRI_CMD_SDP | /* SDP command */
|
|
/* XXX5 - don't enable UNDR interrupt */
|
|
DBRI_SDP_EOL | /* Enable EOL intr */
|
|
/* LSBit first */
|
|
(unsigned int)dcon->mode |
|
|
DBRI_SDP_PTR | /* NULL pointer */
|
|
DBRI_SDP_CLR | /* Clear pipe */
|
|
pipe; /* new pipe number */
|
|
|
|
if (ISAUDIOCONNECTION(dcon->xchan) || ISAUDIOCONNECTION(dcon->rchan) ||
|
|
(dcon->mode == DBRI_MODE_XPRNT)) /*
|
|
* single new line to reverse
|
|
* B-channels too.
|
|
*/
|
|
pipep->sdp |= DBRI_SDP_B; /* CHI is always MSB */
|
|
|
|
if ((dir == DBRI_OUT) && (dcon->mode != DBRI_MODE_SER))
|
|
pipep->sdp |= DBRI_SDP_D; /* set transmit direction */
|
|
|
|
return;
|
|
} /* dbri_setup_sdp */
|
|
|
|
|
|
/*
|
|
* dbri_setup_ntte - setup serial interface but *DO NOT* issue activate
|
|
* command. Also start timeout timers.
|
|
*/
|
|
void
|
|
dbri_setup_ntte(as, channel, serialp)
|
|
aud_stream_t *as;
|
|
dbri_chnl_t channel;
|
|
serial_status_t *serialp;
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
pipe_tab_t *pipep1, *pipep2;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
if ((channel == DBRI_TE_D_IN) || (channel == DBRI_TE_D_OUT)) {
|
|
/* make sure both pipes setup before enabling interface */
|
|
pipep1 = &driver->ptab[DBRI_PIPE_TE_D_OUT];
|
|
pipep2 = &driver->ptab[DBRI_PIPE_TE_D_IN];
|
|
|
|
if ((pipep1->dts.r.id != DBRI_DTS_ID) ||
|
|
(pipep2->dts.r.id != DBRI_DTS_ID))
|
|
return;
|
|
|
|
dbri_disable_te(driver);
|
|
driver->ser_sts.te_sbri.tss = DBRI_TEINFO0_F1;
|
|
AsToDs(as)->i_info.activation = ISDN_DEACTIVATED;
|
|
|
|
/*
|
|
* Because of the DBRI implementation, it is proper and
|
|
* required to send MPH-INFORMATION.ind(connected) here.
|
|
*/
|
|
dbri_primitive_mph_ii_c(as);
|
|
|
|
/*
|
|
* Enable TE interface. This is only done once initially
|
|
* and then is not touched again until close is called.
|
|
* This enables F3 as well as allows NT to activate the
|
|
* interface.
|
|
*
|
|
* Note: The exception is on expiry of T3. In that case,
|
|
* DBRI_STS_T may be toggled to force DBRI TE into F3,
|
|
* depending on the state of the activation state
|
|
* machine.
|
|
*/
|
|
dbri_enable_te(driver); /* enable TE interface */
|
|
|
|
/* setup TE command here */
|
|
serialp->te_cmd = (DBRI_CMD_TE | /* TE command */
|
|
DBRI_NTE_IRM_STATUS | /* force immed intr */
|
|
DBRI_NTE_IRM_SBRI | /* enable SBRI intrs */
|
|
/* Not NT command */
|
|
/* No fixed timing */
|
|
/* No 0's in E chnl */
|
|
/* No IFA */
|
|
/* No Activate intf */
|
|
/* No multiframe */
|
|
/* No loopback */
|
|
/* No force activate */
|
|
DBRI_NTE_ABV); /* Detect ABV's */
|
|
|
|
if ( AsToDs(as)->i_var.maint == ISDN_PH_PARAM_MAINT_OFF)
|
|
serialp->te_cmd &= ~DBRI_TE_QE;
|
|
else
|
|
serialp->te_cmd |= DBRI_TE_QE;
|
|
|
|
cmd.cmd_wd1 = serialp->te_cmd; /* TE command */
|
|
dbri_command(driver, cmd);
|
|
|
|
dprintf("dbri_setup_ntte: serialp=0x%x te_cmd=0x%x\n",
|
|
(unsigned int)serialp, serialp->te_cmd);
|
|
|
|
} else if ((channel == DBRI_NT_D_IN) || (channel == DBRI_NT_D_OUT)) {
|
|
/*
|
|
* check if interface already enabled
|
|
*/
|
|
|
|
/* make sure both pipes setup before enabling interface */
|
|
pipep1 = &driver->ptab[DBRI_PIPE_NT_D_OUT];
|
|
pipep2 = &driver->ptab[DBRI_PIPE_NT_D_IN];
|
|
|
|
if ((pipep1->dts.r.id != DBRI_DTS_ID) ||
|
|
(pipep2->dts.r.id != DBRI_DTS_ID))
|
|
return;
|
|
|
|
driver->ser_sts.nt_sbri.tss = DBRI_NTINFO0_G1;
|
|
|
|
dbri_enable_nt(driver); /* NT on but not active */
|
|
|
|
if (!driver->keep_alive_running) {
|
|
timeout((int(*)())dbri_keep_alive, (caddr_t)driver,
|
|
hz*Keepalive_timer/1000);
|
|
}
|
|
driver->keep_alive_running = TRUE;
|
|
driver->chip->n.sts &= ~(DBRI_STS_F|DBRI_STS_X);
|
|
|
|
/* setup NT command) here */
|
|
serialp->nt_cmd = DBRI_CMD_NT | /* NT command */
|
|
DBRI_NTE_IRM_STATUS | /* force immed intr */
|
|
DBRI_NTE_IRM_SBRI | /* enable SBRI intrs */
|
|
DBRI_NTE_ISNT | /* NT command */
|
|
/* Adaptive timing */
|
|
/* No 0's in E chnl */
|
|
/* No IFA */
|
|
/* intf inactive */
|
|
/* No multiframe */
|
|
/* No loopback */
|
|
/* No force activate */
|
|
DBRI_NTE_ABV; /* Detect ABV's */
|
|
|
|
if ( AsToDs(as)->i_var.maint == ISDN_PH_PARAM_MAINT_OFF)
|
|
serialp->nt_cmd &= ~DBRI_NT_MFE;
|
|
else
|
|
serialp->nt_cmd |= DBRI_NT_MFE;
|
|
|
|
dprintf("dbri_setup_ntte: serialp=0x%x nt_cmd=0x%x\n",
|
|
(unsigned int)serialp, serialp->nt_cmd);
|
|
|
|
cmd.cmd_wd1 = serialp->nt_cmd; /* NT command */
|
|
dbri_command(driver, cmd);
|
|
}
|
|
|
|
return;
|
|
} /* dbri_setup_ntte */
|
|
|
|
|
|
/*
|
|
* dbri_setup_dts - Find place to attach pipe in chain to setup DTS
|
|
* command . words. Also patch up oldpipe and nextpipe in memory copies
|
|
* to reflect the fact that the new pipe was inserted.
|
|
*/
|
|
int
|
|
dbri_setup_dts(as, pipe, channel)
|
|
aud_stream_t *as;
|
|
unsigned int pipe;
|
|
dbri_chnl_t channel;
|
|
{
|
|
unsigned int offset;
|
|
unsigned int length;
|
|
int dir;
|
|
unsigned int oldpipe;
|
|
unsigned int nextpipe;
|
|
unsigned int bpipe;
|
|
unsigned int monpipe = 0;
|
|
pipe_tab_t *monpipep;
|
|
pipe_tab_t *nextpipep;
|
|
pipe_tab_t *oldpipep;
|
|
pipe_tab_t *bpipep;
|
|
unsigned int mode = DBRI_DTS_SINGLE; /* bias to single mode */
|
|
dbri_dev_t *driver;
|
|
pipe_tab_t *pipep;
|
|
|
|
extern unsigned int get_aud_pipe_length();
|
|
extern unsigned int get_aud_pipe_cycle();
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
pipep = &driver->ptab[pipe];
|
|
offset = Chan_tab[(int)channel].cycle; /* Set time slot bit offset */
|
|
length = Chan_tab[(int)channel].len; /* Set time slot length */
|
|
dir = Chan_tab[(int)channel].dir; /* set direction of channel */
|
|
bpipe = Chan_tab[(int)channel].bpipe; /* get base pipe */
|
|
bpipep = &driver->ptab[bpipe]; /* set base pipe pointer */
|
|
|
|
/*
|
|
* These fields are not found in the Channel table for audio
|
|
* data, get them from the current audio configuration
|
|
* information
|
|
*/
|
|
if (ISAUDIOCONNECTION(channel)) {
|
|
offset = get_aud_pipe_cycle(as, pipe, channel);
|
|
length = get_aud_pipe_length(as, pipe, channel);
|
|
}
|
|
|
|
/*
|
|
* If this is the base pipe or there is only a single pipe on the
|
|
* tsd queue. If so we don't need to traverse the linked list to
|
|
* discover oldpipe and nextpipe.
|
|
*/
|
|
if ((bpipe == pipe) ||
|
|
((dir == DBRI_IN) && (bpipep->in_tsd.r.next == bpipe)) ||
|
|
((dir == DBRI_OUT) && (bpipep->out_tsd.r.next == bpipe))) {
|
|
oldpipe = bpipe; /* output pipe */
|
|
nextpipe = bpipe; /* next pipe */
|
|
} else { /* more than 1 pipe in queue */
|
|
/*
|
|
* check bit offsets as well as setup oldpipe, nextpipe,
|
|
* and monpipe.
|
|
*/
|
|
if (dbri_find_oldnext(as, bpipe, dir, length, offset, &oldpipe,
|
|
&nextpipe, &monpipe) == DBRI_BADCONN)
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/*
|
|
* If monitor pipe and the modes do not match we have a problem
|
|
* We can also only have a single monitor pipe attached to a main
|
|
* pipe.
|
|
*/
|
|
if (monpipe != 0) {
|
|
monpipep = &driver->ptab[monpipe]; /* get mon pipe ptr */
|
|
|
|
/* check that modes match */
|
|
if ((monpipep->sdp & DBRI_SDP_MODEMASK) == DBRI_SDP_FIXED) {
|
|
dprintf("dbri: timeslot already in use\n");
|
|
return (DBRI_BADCONN);
|
|
} else if (((pipep->sdp&DBRI_SDP_MODEMASK) !=
|
|
DBRI_SDP_SERIAL) &&
|
|
((monpipep->sdp & DBRI_SDP_MODEMASK) !=
|
|
(pipep->sdp & DBRI_SDP_MODEMASK))) {
|
|
dprintf("dbri: channel modes conflict \n");
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/* check if already one monitor pipe hooked up. */
|
|
if (monpipep->monflag) {
|
|
dprintf("dbri_setup_dts: chnl %d: Mon pipe in use\n",
|
|
channel);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
monpipep->monflag = TRUE; /* tag main pipe having monitor */
|
|
mode = DBRI_DTS_MONITOR;
|
|
} else {
|
|
/*
|
|
* not a monitor pipe so patch up old and next pipe dts's
|
|
* and tsd's.
|
|
*/
|
|
oldpipep = &driver->ptab[oldpipe]; /* get old pipe ptr */
|
|
nextpipep = &driver->ptab[nextpipe]; /* get next pipe ptr */
|
|
if (dir == DBRI_IN) {
|
|
oldpipep->in_tsd.r.next = pipe;
|
|
nextpipep->dts.r.oldin = pipe;
|
|
} else {
|
|
oldpipep->out_tsd.r.next = pipe;
|
|
nextpipep->dts.r.oldout = pipe;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Setup dts command words
|
|
*
|
|
* Note that for serial to serial connections 1/2 of the dts
|
|
* command can already been setup. This is why we don't zero out
|
|
* anything. Upon release of tsd everything is cleared.
|
|
*/
|
|
pipep->dts.r.cmd = DBRI_OPCODE_DTS; /* DTS command */
|
|
pipep->dts.r.id = DBRI_DTS_ID; /* Add timeslot */
|
|
pipep->dts.r.pipe = pipe; /* pipe # */
|
|
if (dir == DBRI_IN) {
|
|
pipep->dts.r.vi = DBRI_DTS_VI;
|
|
pipep->dts.r.oldin = oldpipe;
|
|
|
|
pipep->in_tsd.r.len = length;
|
|
pipep->in_tsd.r.cycle = offset;
|
|
pipep->in_tsd.r.mode = mode;
|
|
pipep->in_tsd.r.mon = monpipe;
|
|
pipep->in_tsd.r.next = nextpipe; /* next pipe */
|
|
} else {
|
|
pipep->dts.r.vo = DBRI_DTS_VO;
|
|
pipep->dts.r.oldout = oldpipe;
|
|
|
|
pipep->out_tsd.r.len = length;
|
|
pipep->out_tsd.r.cycle = offset;
|
|
pipep->out_tsd.r.mode = mode;
|
|
pipep->out_tsd.r.mon = monpipe;
|
|
pipep->out_tsd.r.next = nextpipe; /* next pipe */
|
|
}
|
|
|
|
return (DBRI_GOODCONN);
|
|
} /* dbri_setup_dts */
|
|
|
|
|
|
/*
|
|
* dbri_find_oldnext - finds the oldpipe, nextpipe, and monitor pipes for
|
|
* a list of 2 or more pipes. (ie determines where in DTS linked list of
|
|
* tsd's new pipe should go.
|
|
*/
|
|
int
|
|
dbri_find_oldnext(as, bpipe, dir, len, cycle, oldpipe, nextpipe, monpipe)
|
|
aud_stream_t *as;
|
|
unsigned int bpipe;
|
|
int dir;
|
|
unsigned int len;
|
|
unsigned int cycle;
|
|
unsigned int *oldpipe, *nextpipe, *monpipe;
|
|
{
|
|
union dbri_dtstsd ttsd;
|
|
int minvalue = INT_MAX; /* larger than any cycle */
|
|
unsigned int tpipe = bpipe;
|
|
pipe_tab_t *tpipep;
|
|
unsigned int minpipe = bpipe;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
/*
|
|
* Skip over pipe 16 since it doesn't have a len or cycle
|
|
*/
|
|
if (bpipe == DBRI_PIPE_CHI) {
|
|
tpipep = &driver->ptab[bpipe];
|
|
ttsd.word32 = (dir == DBRI_IN) ? tpipep->in_tsd.word32
|
|
: tpipep->out_tsd.word32;
|
|
tpipe = ttsd.chi.next;
|
|
/* If this is the only one, then it is next and old as well */
|
|
if (tpipe == DBRI_PIPE_CHI) {
|
|
*oldpipe = DBRI_PIPE_CHI;
|
|
*nextpipe = DBRI_PIPE_CHI;
|
|
return (DBRI_GOODCONN);
|
|
}
|
|
|
|
/*
|
|
* CHI transmit pipes can't start at zero, they must
|
|
* start at the length of the frame so that they actually
|
|
* *start* at zero - receive pipes can start at zero.
|
|
* Having a cycle of eg.128 imply the beginning of the
|
|
* frame really screws up the list management logic too
|
|
* Notice the receive side will go through the regular
|
|
* code.
|
|
*
|
|
* XXX - This could be cleaner
|
|
*/
|
|
if (cycle == CHI_DATA_MODE_LEN) {
|
|
*oldpipe = DBRI_PIPE_CHI;
|
|
*nextpipe = tpipe;
|
|
return (DBRI_GOODCONN);
|
|
} else if (ttsd.r.cycle == CHI_DATA_MODE_LEN) {
|
|
*oldpipe = tpipe;
|
|
*nextpipe = ttsd.r.next;
|
|
return (DBRI_GOODCONN);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This first loop looks for potential monitor pipes, overlaps in
|
|
* timeslots and finds the minimum bit offset value in the
|
|
* circular linked list.
|
|
*/
|
|
do {
|
|
tpipep = &driver->ptab[tpipe];
|
|
ttsd.word32 = (dir == DBRI_IN) ? tpipep->in_tsd.word32
|
|
: tpipep->out_tsd.word32;
|
|
|
|
/*
|
|
* Check if we timeslots match..perhaps monitor pipe
|
|
* candidate
|
|
*
|
|
* XXX - for CHI, len and cycle are not there, but are
|
|
* reserved fields of zero; this works since they won't
|
|
* match
|
|
*/
|
|
if ((ttsd.r.len == len) && (ttsd.r.cycle == cycle)) {
|
|
/* Monitor pipes must tap off of an input pipe */
|
|
if (dir == DBRI_OUT) {
|
|
dprintf("dbri: Channel in use.\n");
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
*oldpipe = tpipep->dts.r.oldin;
|
|
*nextpipe = ttsd.r.next;
|
|
*monpipe = tpipe;
|
|
return (DBRI_GOODCONN);
|
|
}
|
|
|
|
/* check for illegal overlapping offsets and cycles */
|
|
if (dbri_tsd_overlap(cycle, (cycle+len), ttsd.r.cycle,
|
|
(ttsd.r.cycle + ttsd.r.len))) {
|
|
dprintf("attach_pipe: cycle %u, len overlap %u\n",
|
|
cycle, len);
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/* get minimum cycle bit offset in list */
|
|
if (ttsd.r.cycle < minvalue) {
|
|
minvalue = ttsd.r.cycle;
|
|
minpipe = tpipe;
|
|
}
|
|
tpipe = ttsd.r.next;
|
|
} while (tpipe != bpipe);
|
|
|
|
/*
|
|
* Find spot in list to insert before. Note that we are starting
|
|
* with the minimum offset element in list.
|
|
*/
|
|
tpipe = minpipe;
|
|
do {
|
|
tpipep = &driver->ptab[tpipe];
|
|
ttsd.word32 = (dir == DBRI_IN) ? tpipep->in_tsd.word32
|
|
: tpipep->out_tsd.word32;
|
|
|
|
if ((int)cycle < ttsd.r.cycle)
|
|
break;
|
|
|
|
tpipe = ttsd.r.next; /* advance to next pipe in list */
|
|
|
|
if ((bpipe == DBRI_PIPE_CHI) && (tpipe == DBRI_PIPE_CHI))
|
|
break;
|
|
|
|
} while (tpipe != minpipe);
|
|
|
|
/* Now set oldpipe and nextpipe variables */
|
|
*nextpipe = tpipe;
|
|
tpipep = &driver->ptab[tpipe];
|
|
*oldpipe = (dir == DBRI_IN) ? tpipep->dts.r.oldin
|
|
: tpipep->dts.r.oldout;
|
|
|
|
return (DBRI_GOODCONN);
|
|
} /* dbri_find_oldnext */
|
|
|
|
|
|
/*
|
|
* dbri_remove_dts - disconnect up DBRI serial side of pipe
|
|
*/
|
|
void
|
|
dbri_remove_dts(as, pipe)
|
|
aud_stream_t *as;
|
|
unsigned int pipe;
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
unsigned int basepipe;
|
|
unsigned int oldpipe;
|
|
unsigned int nextpipe;
|
|
pipe_tab_t *pipep;
|
|
pipe_tab_t *basepipep;
|
|
pipe_tab_t *oldpipep;
|
|
pipe_tab_t *nextpipep;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
/* check if this was a monitor pipe. */
|
|
pipep = &driver->ptab[pipe];
|
|
if ((pipep->dts.r.vi == DBRI_DTS_VI) &&
|
|
(pipep->in_tsd.r.mode == DBRI_DTS_MONITOR)) {
|
|
/*
|
|
* Monitor pipe so clear out monflag field of underlying
|
|
* base pipe if it is set, otherwise remove the underlying
|
|
* base pipe itself.
|
|
*/
|
|
basepipe = pipep->in_tsd.r.mon;
|
|
basepipep = &driver->ptab[basepipe];
|
|
if (basepipep->monflag) {
|
|
basepipep->monflag = FALSE;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Kill off underlying base pipe, find oldpipe and next pipe
|
|
* and patch them up
|
|
*/
|
|
if (basepipep->dts.r.vi == DBRI_DTS_VI) {
|
|
oldpipe = basepipep->dts.r.oldin;
|
|
oldpipep = &driver->ptab[oldpipe];
|
|
nextpipe = basepipep->in_tsd.r.next;
|
|
nextpipep = &driver->ptab[nextpipe];
|
|
oldpipep->in_tsd.r.next = basepipep->in_tsd.r.next;
|
|
nextpipep->dts.r.oldin = basepipep->dts.r.oldin;
|
|
}
|
|
if (basepipep->dts.r.vo == DBRI_DTS_VO) {
|
|
oldpipe = basepipep->dts.r.oldout;
|
|
oldpipep = &driver->ptab[oldpipe];
|
|
nextpipe = basepipep->out_tsd.r.next;
|
|
nextpipep = &driver->ptab[nextpipe];
|
|
oldpipep->out_tsd.r.next = basepipep->out_tsd.r.next;
|
|
nextpipep->dts.r.oldout = basepipep->dts.r.oldout;
|
|
}
|
|
|
|
/* The main pipe needs to be cleared out */
|
|
basepipep->dts.r.id = ~DBRI_DTS_ID & 1;
|
|
cmd.cmd_wd1 = basepipep->dts.word32; /* DTS cmd */
|
|
cmd.cmd_wd2 = basepipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = basepipep->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
/* clear out in memory copy of the dts command */
|
|
pipep->dts.word32 = 0;
|
|
pipep->in_tsd.word32 = pipep->out_tsd.word32 = 0;
|
|
}
|
|
|
|
/* find neighbors oldpipe and next pipe and patch them up */
|
|
if (pipep->dts.r.vi == DBRI_DTS_VI) {
|
|
oldpipe = pipep->dts.r.oldin;
|
|
oldpipep = &driver->ptab[oldpipe];
|
|
nextpipe = pipep->in_tsd.r.next;
|
|
nextpipep = &driver->ptab[nextpipe];
|
|
oldpipep->in_tsd.r.next = pipep->in_tsd.r.next;
|
|
nextpipep->dts.r.oldin = pipep->dts.r.oldin;
|
|
}
|
|
if (pipep->dts.r.vo == DBRI_DTS_VO) {
|
|
oldpipe = pipep->dts.r.oldout;
|
|
oldpipep = &driver->ptab[oldpipe];
|
|
nextpipe = pipep->out_tsd.r.next;
|
|
nextpipep = &driver->ptab[nextpipe];
|
|
oldpipep->out_tsd.r.next = pipep->out_tsd.r.next;
|
|
nextpipep->dts.r.oldout = pipep->dts.r.oldout;
|
|
}
|
|
|
|
/* Now clear the add/modify bit and issue the DTS command */
|
|
pipep->dts.r.id = ~DBRI_DTS_ID & 1;
|
|
cmd.cmd_wd1 = pipep->dts.word32; /* DTS command */
|
|
cmd.cmd_wd2 = pipep->in_tsd.word32;
|
|
cmd.cmd_wd3 = pipep->out_tsd.word32;
|
|
dbri_command(driver, cmd);
|
|
|
|
/* clear out in memory copy of the dts and sdp command */
|
|
pipep->dts.word32 = 0;
|
|
pipep->in_tsd.word32 = 0;
|
|
pipep->out_tsd.word32 = 0;
|
|
pipep->sdp = 0;
|
|
|
|
return;
|
|
} /* dbri_remove_dts */
|
|
|
|
|
|
/*
|
|
* dbri_getpipe - Get an available free pipe. This routine allocates the
|
|
* appropriate type of pipe {long or short} based on the data channel.
|
|
* Note that if the channel is of an unknown type then a short pipe is
|
|
* automatically allocated. This could be a problem in the future when
|
|
* unknown pipes are setup by the user to the host.
|
|
*/
|
|
unsigned int
|
|
dbri_getpipe(driver, chan, mode)
|
|
dbri_dev_t *driver;
|
|
dbri_chnl_t chan;
|
|
dbri_mode_t mode;
|
|
{
|
|
unsigned int pipe;
|
|
unsigned int maxpipe;
|
|
pipe_tab_t *pipep;
|
|
int pipetype;
|
|
|
|
/* D-channels are required to use predetermined pipes */
|
|
if ((chan == DBRI_TE_D_OUT) ||
|
|
(chan == DBRI_TE_D_IN) ||
|
|
(chan == DBRI_NT_D_OUT) ||
|
|
(chan == DBRI_NT_D_IN)) {
|
|
pipe = Chan_tab[(int)chan].bpipe;
|
|
pipep = &driver->ptab[pipe];
|
|
if (ISPIPEINUSE(pipep)) {
|
|
dprintf("dbri_getpipe: Pipe %d already in use.\n",
|
|
(int)pipe);
|
|
return (DBRI_BAD_PIPE); /* no free pipes */
|
|
}
|
|
pipep->dts.r.id = DBRI_DTS_ID; /* clear and mark pipe in use */
|
|
return (pipe);
|
|
}
|
|
|
|
/* Non-predetermined pipes */
|
|
if ((mode == DBRI_MODE_SER) || (mode == DBRI_MODE_FIXED)) {
|
|
pipetype = DBRI_SHORTPIPE;
|
|
} else {
|
|
pipetype = DBRI_LONGPIPE;
|
|
}
|
|
|
|
if (pipetype == DBRI_LONGPIPE) {
|
|
pipe = DBRI_PIPE_LONGFREE;
|
|
maxpipe = DBRI_PIPE_CHI;
|
|
} else {
|
|
pipe = DBRI_PIPE_SHORTFREE;
|
|
maxpipe = DBRI_MAX_PIPES;
|
|
}
|
|
|
|
/* Go down list of pipes looking for one not in use. */
|
|
for (; pipe < maxpipe; pipe++) {
|
|
pipep = &driver->ptab[pipe];
|
|
if (!ISPIPEINUSE(pipep))
|
|
break; /* free pipe available */
|
|
}
|
|
|
|
if (pipe == maxpipe)
|
|
return (DBRI_BAD_PIPE); /* no free pipes */
|
|
|
|
pipep->dts.r.id = DBRI_DTS_ID; /* clear and mark pipe in use */
|
|
|
|
return (pipe);
|
|
} /* dbri_getpipe */
|
|
|
|
|
|
/*
|
|
* dbri_findshortpipe - searches for a short pipe in use that matches
|
|
* channels requested
|
|
*/
|
|
unsigned int
|
|
dbri_findshortpipe(as, rchan, xchan)
|
|
aud_stream_t *as;
|
|
dbri_chnl_t rchan, xchan;
|
|
{
|
|
unsigned pipe;
|
|
pipe_tab_t *pipep;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
/* search short pipe list pointers for "as" to find a match. */
|
|
for (pipe = DBRI_PIPE_CHI; pipe < DBRI_MAX_PIPES; pipe++) {
|
|
pipep = &driver->ptab[pipe];
|
|
if ((pipep->as == as) &&
|
|
(xchan == pipep->xchan) &&
|
|
(rchan == pipep->rchan)) {
|
|
break; /* found a match */
|
|
}
|
|
}
|
|
if (pipe == DBRI_BAD_PIPE)
|
|
return (DBRI_BAD_PIPE); /* cannot find connection */
|
|
|
|
pipep = &driver->ptab[pipe];
|
|
if (!ISPIPEINUSE(pipep))
|
|
return (DBRI_BAD_PIPE);
|
|
|
|
return (pipe);
|
|
} /* dbri_findshortpipe */
|
|
|
|
|
|
static struct {
|
|
int length;
|
|
char *name;
|
|
} dbri_cmd_info[16] = {
|
|
{1, "WAIT"},
|
|
{1, "PAUSE"},
|
|
{2, "JMP"},
|
|
{2, "IIQ"},
|
|
{1, "REX"},
|
|
{2, "SDP"},
|
|
{1, "CDP"},
|
|
{3, "DTS"},
|
|
{2, "SSP"},
|
|
{1, "CHI"},
|
|
{1, "NT"},
|
|
{1, "TE"},
|
|
{1, "CDEC"},
|
|
{3, "TEST"}, /* SBus TEST/COPY is the only 3 word test command */
|
|
{1, "CDM"},
|
|
{0, "undefined"}
|
|
};
|
|
|
|
|
|
void
|
|
dbri_command(driver, cmd)
|
|
dbri_dev_t *driver;
|
|
dbri_chip_cmd_t cmd;
|
|
{
|
|
unsigned int unprocessed;
|
|
dbri_chip_cmdq_t *cmdq; /* base of cmdq */
|
|
int command_length;
|
|
unsigned int dbri_pc;
|
|
unsigned int *reg_cmdqp;
|
|
unsigned int tmp_cmdp;
|
|
|
|
cmdq = driver->cmdqp; /* base of cmdq */
|
|
|
|
/* Protect this routine if the chip has not initialized successfully */
|
|
if (!driver->init_chip) {
|
|
dprintf("**Entering dbri_command but chip not init \n");
|
|
return;
|
|
}
|
|
|
|
command_length = dbri_cmd_info[DBRI_OP(cmd.cmd_wd1)].length;
|
|
|
|
/*
|
|
* See if there is sufficient space in the queue for the command.
|
|
*/
|
|
|
|
/* Find DBRI's current position in the command queue */
|
|
if (!driver->init_cmdq) {
|
|
dbri_pc = 0; /* Not initialized yet, it will be at 0 */
|
|
} else {
|
|
reg_cmdqp = (unsigned *)KERN_ADDR(driver,
|
|
driver->chip->n.cmdqp);
|
|
dbri_pc = reg_cmdqp - &cmdq->cmd[0];
|
|
if (dbri_pc > DBRI_MAX_CMDS) {
|
|
dbri_panic(driver, "DBRI Reg8 bad!\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Calculate space at end of queue. cmdq->cmdhp always points to WAIT */
|
|
unprocessed = (cmdq->cmdhp >= dbri_pc)
|
|
? cmdq->cmdhp - dbri_pc
|
|
: DBRI_MAX_CMDS - (dbri_pc - cmdq->cmdhp);
|
|
|
|
/*
|
|
* If the command queue is almost full, DBRI has not been processing
|
|
* commands and this indicates a serious problem.
|
|
*
|
|
* XXX - bad test
|
|
*/
|
|
if (unprocessed > (DBRI_MAX_CMDS - 10 /* XXX - magic number */)) {
|
|
(void) printf("dbri_command: *** No space in cmdq.\n");
|
|
(void) printf("\tcmd=0x%x 0x%x\n",
|
|
cmd.cmd_wd1, cmd.cmd_wd2);
|
|
(void) printf("r8:0x%x index:%u, cmdhp:%u, MAX=%d unprocd:%u\n",
|
|
(unsigned int)reg_cmdqp, dbri_pc, cmdq->cmdhp,
|
|
DBRI_MAX_CMDS, unprocessed);
|
|
return; /* XXX - may want to return error here */
|
|
}
|
|
|
|
/*
|
|
* If at end of command queue, insert a JMP command, to wrap
|
|
* around to head of the command queue, followed by a WAIT
|
|
* command.
|
|
*/
|
|
if ((cmdq->cmdhp+command_length) > (DBRI_MAX_CMDS-DBRI_CMD_JMP_LEN)) {
|
|
cmdq->cmd[0] = DBRI_CMD_WAIT; /* jump to WAIT */
|
|
|
|
/*
|
|
* Stuff the JMP command into the current word and 1 more
|
|
*/
|
|
cmdq->cmd[cmdq->cmdhp + 1] = ((unsigned)
|
|
DBRI_ADDR(driver, &cmdq->cmd[0]));
|
|
cmdq->cmd[cmdq->cmdhp] = DBRI_CMD_JMP;
|
|
|
|
/*
|
|
* Point back to head of list
|
|
*/
|
|
cmdq->cmdhp = 0;
|
|
}
|
|
|
|
/*
|
|
* Write the command plus a WAIT command into the queue. Insert
|
|
* commands last to first to avoid race with DBRI.
|
|
*/
|
|
switch (command_length) {
|
|
case 1:
|
|
if (DBRI_OP(cmd.cmd_wd1) == DBRI_OP(DBRI_CMD_WAIT)) {
|
|
/* Don't advance pointer on WAIT command. */
|
|
cmdq->cmd[cmdq->cmdhp] = cmd.cmd_wd1;
|
|
} else {
|
|
tmp_cmdp = cmdq->cmdhp + 1;
|
|
cmdq->cmdhp = tmp_cmdp;
|
|
cmdq->cmd[tmp_cmdp] = DBRI_CMD_WAIT;
|
|
cmdq->cmd[--tmp_cmdp] = cmd.cmd_wd1;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
tmp_cmdp = cmdq->cmdhp + 2;
|
|
cmdq->cmdhp = tmp_cmdp;
|
|
cmdq->cmd[tmp_cmdp] = DBRI_CMD_WAIT;
|
|
cmdq->cmd[--tmp_cmdp] = cmd.cmd_wd2;
|
|
cmdq->cmd[--tmp_cmdp] = cmd.cmd_wd1;
|
|
break;
|
|
|
|
case 3:
|
|
tmp_cmdp = cmdq->cmdhp + 3;
|
|
cmdq->cmdhp = tmp_cmdp;
|
|
cmdq->cmd[tmp_cmdp] = DBRI_CMD_WAIT;
|
|
cmdq->cmd[--tmp_cmdp] = cmd.cmd_wd3;
|
|
cmdq->cmd[--tmp_cmdp] = cmd.cmd_wd2;
|
|
cmdq->cmd[--tmp_cmdp] = cmd.cmd_wd1;
|
|
break;
|
|
|
|
default:
|
|
dbri_panic(driver, "illegal dbri_command");
|
|
return;
|
|
} /* switch */
|
|
|
|
if (!driver->init_cmdq) {
|
|
driver->chip->n.cmdqp = ((unsigned)
|
|
DBRI_ADDR(driver, &cmdq->cmd[0]));
|
|
driver->init_cmdq = TRUE;
|
|
dprintf("dbri_command: cmdq is 0x%x, reg8 set to 0x%x\n",
|
|
(unsigned int)&cmdq->cmd[0], driver->chip->n.cmdqp);
|
|
} else {
|
|
driver->chip->n.sts |= DBRI_STS_P; /* Queue ptr valid */
|
|
}
|
|
|
|
return;
|
|
} /* dbri_command */
|
|
|
|
|
|
#define dbri_valid_channel(c) (((int)c) >= ((int)DBRI_HOST_CHNL) && \
|
|
(((int)c) < (int)DBRI_LAST_CHNL))
|
|
/*
|
|
* dbri_check_req - Checks on sanity of connection setup
|
|
*/
|
|
int
|
|
dbri_check_req(as, conp, dcon, error)
|
|
aud_stream_t *as;
|
|
isdn_conn_req_t *conp;
|
|
dbri_conn_req_t *dcon;
|
|
int *error;
|
|
{
|
|
pipe_tab_t *pipep;
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *p_as, *r_as;
|
|
|
|
if (as == NULL) {
|
|
*error = ENODEV;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
switch (dcon->mode) {
|
|
case DBRI_MODE_UNKNOWN:
|
|
case DBRI_MODE_XPRNT:
|
|
case DBRI_MODE_HDLC:
|
|
case DBRI_MODE_HDLC_D:
|
|
case DBRI_MODE_SER:
|
|
case DBRI_MODE_FIXED:
|
|
break;
|
|
default:
|
|
*error = EINVAL;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
/* Check permissions of operations */
|
|
if (as->openflag == FREAD) { /* Read only stream */
|
|
if (conp->dir != ISDN_ONEWAY_STREAM) {
|
|
dprintf("dbri: dev open O_READ yet 2 way strm reqd.\n");
|
|
*error = EACCES;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
/* check host dir is read */
|
|
if (conp->xmitport != ISDN_HOST) {
|
|
dprintf("dbri: transmit port not equal to host.\n");
|
|
*error = EACCES;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
} else if (as->openflag == FWRITE) { /* Write only stream */
|
|
if (conp->dir != ISDN_ONEWAY_STREAM) {
|
|
dprintf("dbri: open O_WRITE yet 2-way stream req\n");
|
|
*error = EACCES;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
/* check host direction is write */
|
|
if (conp->recvport != ISDN_HOST) {
|
|
dprintf("dbri: receive port not equal to host.\n");
|
|
*error = EACCES;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
} else { /* Read/write stream */
|
|
if (conp->dir != ISDN_TWOWAY_STREAM) {
|
|
dprintf("dbri: device opened RW yet one-way request\n");
|
|
*error = EACCES;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
|
|
if (ISHWCONNECTED(as)) {
|
|
dprintf("dbri: H/W channel already connected \n");
|
|
*error = EEXIST;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/* Check if data stream for serial to serial connections */
|
|
if ((dcon->mode == DBRI_MODE_SER) && ISDATASTREAM(as)) {
|
|
dprintf("dbri: Data channel req non-data connection.\n");
|
|
*error = ENOSTR;
|
|
return (DBRI_BADCONN); /* Must be a control stream */
|
|
}
|
|
|
|
/* Check if control stream for data connection */
|
|
if ((dcon->mode != DBRI_MODE_SER) && !ISDATASTREAM(as)) {
|
|
dprintf("dbri: Control channel req data connection.\n");
|
|
*error = ENOSTR;
|
|
return (DBRI_BADCONN); /* Must be a control stream */
|
|
}
|
|
|
|
/*
|
|
* Check that D channel requests are bidirectional and one side
|
|
* of the pipe's datastream goes to the host.
|
|
*/
|
|
if ((conp->xmitport == ISDN_TE_D) ||
|
|
(conp->xmitport == ISDN_NT_D) ||
|
|
(conp->recvport == ISDN_TE_D) ||
|
|
(conp->recvport == ISDN_NT_D)) {
|
|
/* D channel request must be bidirectional */
|
|
if (conp->dir != ISDN_TWOWAY_STREAM) {
|
|
dprintf("dbri: D-ch req not bidirectional.\n");
|
|
*error = EINVAL;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
/* One side of D channel must go to the host */
|
|
if ((conp->xmitport != ISDN_HOST) &&
|
|
(conp->recvport != ISDN_HOST)) {
|
|
dprintf("dbri: D-ch req not connected to host.\n");
|
|
*error = EINVAL;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
} else {
|
|
/* Check that base pipes of xmit and recv channels are setup */
|
|
if ((dcon->xchan != DBRI_NO_CHNL) &&
|
|
(dcon->xchan != DBRI_HOST_CHNL)) {
|
|
pipep = &driver->ptab[Chan_tab[(int)dcon->xchan].bpipe];
|
|
if (!ISPIPEINUSE(pipep)) {
|
|
dprintf("dbri: base channel not setup\n");
|
|
*error = ENOENT;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
if ((dcon->rchan != DBRI_NO_CHNL) &&
|
|
(dcon->rchan != DBRI_HOST_CHNL)) {
|
|
pipep = &driver->ptab[Chan_tab[(int)dcon->rchan].bpipe];
|
|
if (!ISPIPEINUSE(pipep)) {
|
|
dprintf("dbri: base channel not setup\n");
|
|
*error = ENOENT;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Here we check for channels not currently implemented.
|
|
*/
|
|
if ((dcon->xchan == DBRI_NO_CHNL) || (dcon->rchan == DBRI_NO_CHNL)) {
|
|
dprintf("dbri: channel request not implemented.\n");
|
|
*error = ENXIO;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
|
|
/* If audio, check that mode is transparent */
|
|
if ((dcon->mode != DBRI_MODE_SER) &&
|
|
(ISAUDIOCONNECTION(dcon->xchan) || ISAUDIOCONNECTION(dcon->rchan))){
|
|
if (dcon->mode != DBRI_MODE_XPRNT) {
|
|
dprintf("dbri_check_req: audio !transparent.\n");
|
|
*error = EINVAL;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
|
|
/* Checks on serial to serial connections to insure ports are not busy*/
|
|
if (dcon->mode == DBRI_MODE_SER) {
|
|
/* check first direction */
|
|
p_as = &driver->ioc[(int)dcon->xchan].as;
|
|
r_as = &driver->ioc[(int)dcon->rchan].as;
|
|
|
|
if (p_as->play_as->info.open || r_as->record_as->info.open) {
|
|
*error = EBUSY;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
/* now check second direction */
|
|
if (conp->dir == ISDN_TWOWAY_STREAM) {
|
|
p_as = &driver->ioc[(int)dcon->rchan].as;
|
|
r_as = &driver->ioc[(int)dcon->xchan].as;
|
|
|
|
if (p_as->play_as->info.open ||
|
|
r_as->record_as->info.open) {
|
|
*error = EBUSY;
|
|
return (DBRI_BADCONN);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (DBRI_GOODCONN); /* Everything ok */
|
|
} /* dbri_check_req */
|
|
|
|
|
|
/*
|
|
* dbri_chnl_activated - Checks that the base channel is activated and
|
|
* returns true.
|
|
*
|
|
* XXX - May have to be expanded to also check for CHI active
|
|
*/
|
|
int
|
|
dbri_chnl_activated(as, xchan, rchan)
|
|
aud_stream_t *as;
|
|
dbri_chnl_t xchan;
|
|
dbri_chnl_t rchan;
|
|
{
|
|
unsigned bpipe;
|
|
aud_stream_t *bas;
|
|
pipe_tab_t *pipep;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
/* Grab mode of channel not connected to host from table */
|
|
if ((xchan != DBRI_HOST_CHNL) && (xchan != DBRI_NO_CHNL))
|
|
bpipe = Chan_tab[(int)xchan].bpipe;
|
|
else if ((rchan != DBRI_HOST_CHNL) && (rchan != DBRI_NO_CHNL))
|
|
bpipe = Chan_tab[(int)rchan].bpipe;
|
|
else {
|
|
dprintf("dbri_chnl_activated: both channels invalid\n");
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((bpipe == DBRI_PIPE_TE_D_IN) ||
|
|
(bpipe == DBRI_PIPE_TE_D_OUT) ||
|
|
(bpipe == DBRI_PIPE_NT_D_IN) ||
|
|
(bpipe == DBRI_PIPE_NT_D_OUT)) {
|
|
/* need to get the stream of the base channel */
|
|
pipep = &driver->ptab[bpipe];
|
|
bas = pipep->as;
|
|
if (AsToDs(bas->play_as)->i_info.activation != ISDN_ACTIVATED)
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* Note that the default value is used for CHI.
|
|
*/
|
|
|
|
return (TRUE);
|
|
} /* dbri_chnl_activated */
|
|
|
|
|
|
/*
|
|
* dbri_tsd_overlap - check if anywhere the values of a1 thru a2
|
|
* inclusive lie within the values of x1 thru x2 inclusive.
|
|
*/
|
|
int
|
|
dbri_tsd_overlap(a1, a2, x1, x2)
|
|
unsigned int a1;
|
|
unsigned int a2;
|
|
unsigned int x1;
|
|
unsigned int x2;
|
|
{
|
|
return (((a1 < x1) && (a2 > x1)) || ((a1 > x1) && (a1 < x2)));
|
|
} /* dbri_tsd_overlap */
|
|
|
|
|
|
|
|
/*
|
|
* dbri_te_timer - expiry of T3.
|
|
*/
|
|
void
|
|
dbri_te_timer(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_dev_t *driver;
|
|
serial_status_t *serialp;
|
|
int s;
|
|
|
|
/* Don't do anything if H/W not connected */
|
|
if ((as == (aud_stream_t *) NULL) || !ISHWCONNECTED(as))
|
|
return;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
serialp = &driver->ser_sts;
|
|
|
|
s = splr(driver->intrlevel);
|
|
ATRACE(dbri_te_timer, 'timr', serialp->te_sbri.tss);
|
|
|
|
switch (serialp->te_sbri.tss) {
|
|
case DBRI_TEINFO3_F6:
|
|
if (AsToDs(as)->i_var.asmb == ISDN_TE_PH_PARAM_ASMB_CTS2) {
|
|
/* FALLTHROUGH */
|
|
} else {
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case DBRI_TEINFO0_F1: /* CCITT: "impossible", it happens in DBRI */
|
|
case DBRI_TEINFO0_F3: /* no effect */
|
|
case DBRI_TEINFO3_F7: /* no effect */
|
|
(void) splx(s);
|
|
return; /* Sending data */
|
|
|
|
case DBRI_TEINFO0_F8: /* blue book says "no effect", FT disagrees */
|
|
if ((AsToDs(as)->i_var.asmb == ISDN_TE_PH_PARAM_ASMB_CTS2) &&
|
|
AsToDs(as)->i_info.activation == ISDN_ACTIVATED) {
|
|
(void) splx(s);
|
|
return; /* Sending data */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
serialp->te_sbri.tss = DBRI_TEINFO0_F3;
|
|
AsToDs(as)->i_info.activation = ISDN_DEACTIVATED;
|
|
|
|
/*
|
|
* Force DBRI's TE state machine into F3 by resetting the TE
|
|
* activation bit and then setting.
|
|
*
|
|
* NB: Given DBRI's implementation, DBRI will report a transition
|
|
* to F1. This transition is to be interpreted as a transition to
|
|
* F3.
|
|
*/
|
|
dbri_disable_te(driver);
|
|
dbri_hold_f3(driver);
|
|
DELAY(250); /* XXX Stoltz bets that 2 frame times must go by */
|
|
dbri_enable_te(driver);
|
|
|
|
dbri_bri_down(driver, ISDN_TYPE_TE);
|
|
dbri_primitive_mph_di(as);
|
|
dbri_primitive_ph_di(as);
|
|
|
|
(void) splx(s);
|
|
return;
|
|
} /* dbri_te_timer */
|
|
|
|
|
|
/*
|
|
* dbri_nt_timer - doubles up for both T1 and T2 timer expirations. This
|
|
* assumes that both timers cannot be operating simultaneously. If this
|
|
* is not the case there must be two separate functions to deactivate the
|
|
* NT interface.
|
|
*/
|
|
void
|
|
dbri_nt_timer(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_dev_t *driver;
|
|
serial_status_t *serialp;
|
|
int s;
|
|
|
|
if ((as == (aud_stream_t *) NULL) || !ISHWCONNECTED(as))
|
|
return;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
s = splr(driver->intrlevel);
|
|
serialp = &driver->ser_sts;
|
|
|
|
ATRACEI(dbri_nt_timer, 'timr', serialp->nt_sbri.tss);
|
|
|
|
if (serialp->nt_sbri.tss == DBRI_NTINFO4_G3) {
|
|
/* If T101 or T102 expire in G3, then ignore them */
|
|
(void) splx(s);
|
|
return; /* Sending data */
|
|
} else if (serialp->nt_sbri.tss == DBRI_NTINFO2_G2) {
|
|
/* This is T1 expiry */
|
|
AsToDs(as)->i_info.activation = ISDN_DEACTIVATED;
|
|
dbri_bri_down(driver, ISDN_TYPE_NT);
|
|
dbri_primitive_ph_di(as);
|
|
|
|
dbri_disable_nt(driver);
|
|
dbri_hold_g1(driver);
|
|
DELAY(250); /* XXX Stoltz bets that 2 frame times must go by */
|
|
dbri_enable_nt(driver);
|
|
|
|
untimeout((int(*)())dbri_nt_timer, (caddr_t)as);
|
|
timeout((int(*)())dbri_nt_timer,
|
|
(caddr_t)as,
|
|
hz*AsToDs(as)->i_var.t102/1000);
|
|
ATRACEI(dbri_nt_timer, 't102', AsToDs(as)->i_var.t102);
|
|
|
|
driver->ser_sts.nt_sbri.tss = DBRI_NTINFO0_G4;
|
|
} else if (driver->ser_sts.nt_sbri.tss == DBRI_NTINFO0_G4) {
|
|
dbri_enable_nt(driver); /* This is T2 expiry */
|
|
} else {
|
|
/*
|
|
* XXX if we get here then a bug has to be fixed.
|
|
* Since both timers above leave the interface
|
|
* ON, make sure that it is on now.
|
|
*/
|
|
dbri_enable_nt(driver);
|
|
ATRACEI(dbri_nt_timer, 't10?', 0);
|
|
}
|
|
(void) splx(s);
|
|
|
|
return;
|
|
} /* dbri_nt_timer */
|
|
|
|
|
|
/*
|
|
* dbri_keep_alive - keep alive timer routine that reads reg0 every 4
|
|
* seconds to insure that there is activity on DBRI. This prevents the
|
|
* relays from bypassing the host.
|
|
*/
|
|
void
|
|
dbri_keep_alive(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
unsigned int tmp;
|
|
|
|
tmp = driver->chip->n.sts; /* dummy read to keep chip alive */
|
|
#ifdef lint
|
|
tmp = tmp;
|
|
#endif
|
|
|
|
/* restart keep alive timer */
|
|
if (driver->keep_alive_running) {
|
|
timeout((int(*)())dbri_keep_alive, (caddr_t)driver,
|
|
hz*Keepalive_timer/1000);
|
|
}
|
|
|
|
return;
|
|
} /* dbri_keep_alive */
|
|
|
|
|
|
/*
|
|
* dbri_te_unplug - adjusts the state of the isdnstate variable to
|
|
* reflect an unplugged condition as well as conditionally signals the
|
|
* user program and disables the "permit-activation" bit if this has not
|
|
* been previously done.
|
|
*/
|
|
void
|
|
dbri_te_unplug(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
pipe_tab_t *pipep;
|
|
aud_stream_t *as;
|
|
|
|
pipep = &driver->ptab[DBRI_PIPE_TE_D_IN];
|
|
|
|
/*
|
|
* XXX - Need to have semaphore valid variable to check if
|
|
* structure is valid.
|
|
*/
|
|
if (pipep->as == NULL)
|
|
return;
|
|
|
|
as = pipep->as->play_as; /* get control as */
|
|
|
|
untimeout((int(*)())dbri_te_timer, (caddr_t)as);
|
|
|
|
if (AsToDs(as)->i_info.activation != ISDN_UNPLUGGED) {
|
|
/*
|
|
* Only signal upper layer when there is a change from
|
|
* active to not active.
|
|
*/
|
|
dprintf("dbri: TE Interface down\n");
|
|
audio_sendsig(as); /* XXX - done during close, bogus */
|
|
}
|
|
AsToDs(as)->i_info.activation = ISDN_UNPLUGGED;
|
|
|
|
dbri_hold_f3(driver);
|
|
|
|
return;
|
|
} /* dbri_te_unplug */
|
|
|
|
|
|
/*
|
|
* dbri_ph_activate_req - Activate TE
|
|
*/
|
|
void
|
|
dbri_te_ph_activate_req(as)
|
|
aud_stream_t *as;
|
|
{
|
|
#ifdef AUDIOTRACE
|
|
serial_status_t *serialp;
|
|
#endif
|
|
dbri_dev_t *driver;
|
|
int s;
|
|
|
|
driver = (dbri_dev_t *)(as->v->devdata);
|
|
s = splr(driver->intrlevel);
|
|
|
|
#ifdef AUDIOTRACE
|
|
serialp = &driver->ser_sts;
|
|
#endif
|
|
|
|
ATRACEI(dbri_te_ph_activate_req, 'act+', serialp->te_cmd);
|
|
if ((AsToDs(as)->i_info.activation != ISDN_ACTIVATED) &&
|
|
(AsToDs(as)->i_info.activation != ISDN_ACTIVATE_REQ)) {
|
|
/*
|
|
* Activate TE interface
|
|
*
|
|
* XXX - Should not do anything unless in F3
|
|
*/
|
|
dbri_exit_f3(driver);
|
|
|
|
dprintf("TE activation request\n");
|
|
|
|
AsToDs(as)->i_info.activation = ISDN_ACTIVATE_REQ;
|
|
|
|
untimeout((int(*)())dbri_te_timer, (caddr_t)as);
|
|
timeout((int(*)())dbri_te_timer,
|
|
(caddr_t)as,
|
|
hz*AsToDs(as)->i_var.t103/1000);
|
|
ATRACEI(dbri_te_ph_activate_req,
|
|
't103', AsToDs(as)->i_var.t103);
|
|
}
|
|
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Activate NT interface
|
|
*/
|
|
void
|
|
dbri_nt_ph_activate_req(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_dev_t *driver;
|
|
int s;
|
|
|
|
driver = (dbri_dev_t *)(as->v->devdata);
|
|
s = splr(driver->intrlevel);
|
|
|
|
ATRACEI(dbri_nt_ph_activate_req, 'act+', as);
|
|
|
|
dbri_exit_g1((dbri_dev_t *)(as->v->devdata));
|
|
|
|
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_nt_ph_activate_req, 't101', AsToDs(as)->i_var.t101);
|
|
(void) splx(s);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* dbri_nt_mph_deactivate_req - Deactivate NT
|
|
*/
|
|
void
|
|
dbri_nt_mph_deactivate_req(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_dev_t *driver;
|
|
int s;
|
|
|
|
driver = (dbri_dev_t *)(as->v->devdata);
|
|
s = splr(driver->intrlevel);
|
|
|
|
ATRACEI(dbri_nt_mph_deactivate_req, 'act-', driver->chip->n.sts);
|
|
/*
|
|
* XXX need to examine the following conditional, was something
|
|
* XXX else intended?
|
|
*/
|
|
if ((driver->ser_sts.nt_sbri.tss == DBRI_NTINFO0_G4) ||
|
|
(driver->ser_sts.nt_sbri.tss == DBRI_NTINFO0_G4)) {
|
|
ATRACEI(dbri_nt_mph_deactivate_req, 'noop',
|
|
driver->ser_sts.nt_sbri.tss);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
|
|
dbri_disable_nt(driver);
|
|
dbri_hold_g1(driver);
|
|
dbri_bri_down(driver, ISDN_TYPE_NT);
|
|
|
|
driver->ser_sts.nt_sbri.tss = DBRI_NTINFO0_G4;
|
|
AsToDs(as)->i_info.activation = ISDN_DEACTIVATED;
|
|
dbri_primitive_ph_di(as);
|
|
|
|
untimeout((int(*)())dbri_nt_timer, (caddr_t)as);
|
|
timeout((int(*)())dbri_nt_timer,
|
|
(caddr_t)as,
|
|
hz*AsToDs(as)->i_var.t102/1000);
|
|
ATRACEI(dbri_nt_mph_deactivate_req, 't102', AsToDs(as)->i_var.t102);
|
|
(void) splx(s);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* dbri_panic - Print out a message and either cause a system panic or
|
|
* deactivate the current DBRI unit until the next time the system is
|
|
* booted/driver is reloaded.
|
|
*/
|
|
void
|
|
dbri_panic(driver, message)
|
|
dbri_dev_t *driver;
|
|
char *message;
|
|
{
|
|
dbri_reg_t *chip = driver->chip;
|
|
int i;
|
|
|
|
/*
|
|
* Prevent new opens on the device
|
|
*/
|
|
driver->openinhibit = TRUE;
|
|
|
|
/*
|
|
* Reset the chip and disallow it to become an SBus master
|
|
*/
|
|
chip->n.sts = DBRI_STS_R; /* reset DBRI chip */
|
|
DELAY(2); /* XXX - For safety? */
|
|
driver->init_chip = FALSE; /* no longer initialized */
|
|
chip->n.sts |= (DBRI_STS_F|DBRI_STS_D);
|
|
|
|
if (Dbri_panic) {
|
|
panic(message);
|
|
/* The system will probably not get to this point */
|
|
}
|
|
|
|
printf("dbri: %s\n", message);
|
|
|
|
/*
|
|
* Send M_ERROR messages up all streams associated with this
|
|
* device.
|
|
*/
|
|
for (i = 0; i < Nstreams; i++)
|
|
dbri_error_msg(driver->ioc[i].as.play_as, M_ERROR);
|
|
|
|
/*
|
|
* Device will no longer be used by this instance of the driver
|
|
*
|
|
* XXX - Is this all we need to do?
|
|
*/
|
|
return;
|
|
}
|
|
|
|
|
|
int
|
|
dbri_power_subr(as, arg)
|
|
aud_stream_t *as;
|
|
void *arg;
|
|
{
|
|
unsigned int power;
|
|
|
|
power = (unsigned int)arg;
|
|
|
|
AsToDs(as)->i_var.power = power;
|
|
AsToDs(as->play_as)->i_var.power = power;
|
|
AsToDs(as->record_as)->i_var.power = power;
|
|
AsToDs(as->control_as)->i_var.power = power;
|
|
|
|
if ((power == ISDN_PH_PARAM_POWER_OFF) && !ISCONTROLSTREAM(as)) {
|
|
/* Send an M_HANGUP upstream */
|
|
dbri_error_msg(as, M_HANGUP);
|
|
} else {
|
|
dbri_error_msg(as, M_UNHANGUP);
|
|
}
|
|
/* XXX For powerup case, should M_UNHANGUP be sent? */
|
|
return (0);
|
|
}
|
|
|
|
|
|
#define IOCEQ(x) (ds == AsToDs(driver->ioc[(int)x].as.play_as))
|
|
|
|
/*
|
|
* Turn interface "power" on or off.
|
|
* XXX For the NT, should this routine control the relay?
|
|
*/
|
|
int
|
|
dbri_power(ds, power_state)
|
|
dbri_stream_t *ds; /* pointer to the interface's D-channel */
|
|
unsigned int power_state;
|
|
{
|
|
isdn_interface_t bri_type;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *) DsToAs(ds)->v->devdata;
|
|
|
|
/* Check for invalid args or redundant work */
|
|
if (power_state != ISDN_PH_PARAM_POWER_OFF && power_state != ISDN_PH_PARAM_POWER_ON)
|
|
return (0);
|
|
if (!IOCEQ(DBRI_NT_D_OUT) && !IOCEQ(DBRI_TE_D_OUT))
|
|
return (0);
|
|
if (ds->i_var.power == power_state)
|
|
return (1);
|
|
|
|
ds->i_var.power = power_state;
|
|
|
|
if (IOCEQ(DBRI_NT_D_OUT)) {
|
|
bri_type = ISDN_TYPE_NT;
|
|
driver->ioc[(int)DBRI_NTMGT].i_var.power = power_state;
|
|
} else {
|
|
bri_type = ISDN_TYPE_TE;
|
|
driver->ioc[(int)DBRI_TEMGT].i_var.power = power_state;
|
|
}
|
|
|
|
ATRACEI(dbri_power, 'juce', power_state);
|
|
|
|
if (power_state == ISDN_PH_PARAM_POWER_OFF)
|
|
dbri_bri_down(driver, bri_type);
|
|
|
|
if (power_state == ISDN_PH_PARAM_POWER_ON) {
|
|
if (bri_type == ISDN_TYPE_NT)
|
|
dbri_enable_nt(driver);
|
|
else
|
|
dbri_enable_te(driver);
|
|
}
|
|
|
|
(void) dbri_bri_func(driver, bri_type, dbri_power_subr,
|
|
(void *)power_state);
|
|
|
|
if (power_state == ISDN_PH_PARAM_POWER_ON)
|
|
dbri_bri_up(driver, bri_type);
|
|
|
|
return (1);
|
|
}
|
|
#undef IOCEQ
|
|
|
|
/*
|
|
* An interface is being brought up or down, perform the appropriate actions
|
|
* on an aud_stream on that interface.
|
|
*
|
|
* If the interface is being brought down (arg == 0), stop IO on the pipe
|
|
* and flush all pending output.
|
|
*
|
|
* If the interface is coming up, reset the eol state and queue record buffers.
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
dbri_bri_up_down_subr(as, arg)
|
|
aud_stream_t *as;
|
|
void *arg;
|
|
{
|
|
int up = (int)arg;
|
|
extern void audio_flush_cmdlist();
|
|
|
|
if (up) {
|
|
if (as == as->record_as) {
|
|
ATRACEI(dbri_bri_up, 'rego', as);
|
|
AsToDs(as)->recv_eol = TRUE;
|
|
audio_process_record(as);
|
|
}
|
|
} else {
|
|
ATRACEI(dbri_bri_down, 'bri-', as);
|
|
dbri_stop(as);
|
|
audio_flush_cmdlist(as);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Flush all pipes associated with a TE or NT.
|
|
* XXX Should we flush output pipes only? Abort input pipes?
|
|
*/
|
|
void
|
|
dbri_bri_down(driver, bri_type)
|
|
dbri_dev_t *driver;
|
|
isdn_interface_t bri_type;
|
|
{
|
|
if (bri_type != ISDN_TYPE_TE && bri_type != ISDN_TYPE_NT)
|
|
return;
|
|
|
|
(void) dbri_bri_func(driver, bri_type, dbri_bri_up_down_subr,
|
|
(void *)0);
|
|
}
|
|
|
|
/*
|
|
* Restart all receive pipes associated with a TE or NT.
|
|
*/
|
|
void
|
|
dbri_bri_up(driver, bri_type)
|
|
dbri_dev_t *driver;
|
|
isdn_interface_t bri_type;
|
|
{
|
|
ATRACEI(dbri_bri_up, 'bri+', bri_type);
|
|
|
|
if (bri_type != ISDN_TYPE_TE && bri_type != ISDN_TYPE_NT)
|
|
return;
|
|
(void) dbri_bri_func(driver, bri_type, dbri_bri_up_down_subr,
|
|
(void *)1);
|
|
}
|
|
|
|
/*
|
|
* Run a function on all open BRI aud_streams. Return code is OR of all
|
|
* return codes.
|
|
*/
|
|
int
|
|
dbri_bri_func(driver, bri_type, func, arg)
|
|
dbri_dev_t *driver;
|
|
isdn_interface_t bri_type;
|
|
int (*func)();
|
|
void *arg;
|
|
{
|
|
int i;
|
|
int r = 0;
|
|
|
|
if (bri_type != ISDN_TYPE_TE && bri_type != ISDN_TYPE_NT)
|
|
return (r);
|
|
|
|
for (i = 0; i < Nstreams; ++i) {
|
|
aud_stream_t *asp;
|
|
|
|
if (bri_type != Chan_tab[i].isdntype)
|
|
continue;
|
|
|
|
asp = &driver->ioc[i].as;
|
|
|
|
if (!ISHWCONNECTED(asp))
|
|
continue;
|
|
r |= func(asp, arg);
|
|
}
|
|
return (r);
|
|
}
|
|
|
|
dbri_enable_te(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_stream_t *ds;
|
|
|
|
ds = &driver->ioc[(int)DBRI_TE_D_OUT];
|
|
|
|
ds->i_var.enable = TRUE;
|
|
if (ds->i_var.power == ISDN_PH_PARAM_POWER_ON)
|
|
driver->chip->n.sts |= DBRI_STS_T;
|
|
}
|
|
|
|
dbri_disable_te(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_stream_t *ds;
|
|
|
|
ds = &driver->ioc[(int)DBRI_TE_D_OUT];
|
|
|
|
ds->i_var.enable = FALSE;
|
|
driver->chip->n.sts &= ~DBRI_STS_T;
|
|
}
|
|
|
|
dbri_enable_nt(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_stream_t *ds;
|
|
|
|
ds = &driver->ioc[(int)DBRI_NT_D_OUT];
|
|
|
|
ds->i_var.enable = TRUE;
|
|
if (ds->i_var.power == ISDN_PH_PARAM_POWER_ON)
|
|
driver->chip->n.sts |= DBRI_STS_N;
|
|
}
|
|
|
|
dbri_disable_nt(driver)
|
|
dbri_dev_t *driver;
|
|
{
|
|
dbri_stream_t *ds;
|
|
|
|
ds = &driver->ioc[(int)DBRI_NT_D_OUT];
|
|
|
|
ds->i_var.enable = FALSE;
|
|
driver->chip->n.sts &= ~DBRI_STS_N;
|
|
}
|