2077 lines
49 KiB
C
2077 lines
49 KiB
C
#ident "@(#) dbri.c 1.1@(#) Copyright (c) 1991-92 Sun Microsystems, Inc."
|
|
|
|
/*
|
|
* AUDIO/ISDN Chip driver - for ATT T5900FC (DBRI) and MMCODEC
|
|
*/
|
|
|
|
|
|
#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 <machine/intreg.h>
|
|
#include <machine/mmu.h>
|
|
#include <sundev/mbvar.h>
|
|
|
|
#include <sun/audioio.h>
|
|
#include <sbusdev/audiovar.h>
|
|
#include <sbusdev/dbri_reg.h>
|
|
#include <sbusdev/mmcodec_reg.h>
|
|
|
|
#define DBRI_UNSUPPORTED /* Defines SETPORT, RELPORT */
|
|
#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();
|
|
unsigned char dbri_output_muted();
|
|
void dbri_chil_intr();
|
|
void dbri_error_msg();
|
|
void dbri_fxdt_intr();
|
|
void mmcodec_init_pipes();
|
|
void mmcodec_reset();
|
|
void mmcodec_setup_ctrl_mode();
|
|
unsigned int dbri_inport();
|
|
void dbri_te_ph_activate_req();
|
|
void dbri_nt_ph_activate_req();
|
|
void dbri_nt_mph_deactivate_req();
|
|
void dbri_pr_dbri_cmd();
|
|
void dbri_xmitqcmd();
|
|
void dbri_recvqcmd();
|
|
int dbri_loopback();
|
|
|
|
|
|
/*
|
|
* dbri_minortoas - Search channel table for a match on the minor device
|
|
* number then grab the audio stream from that channel.
|
|
*/
|
|
aud_stream_t *
|
|
dbri_minortoas(driver, minordev)
|
|
dbri_dev_t *driver;
|
|
int minordev;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < Nstreams; ++i) {
|
|
if ((int) Chan_tab[i].minordev == minordev)
|
|
break;
|
|
}
|
|
|
|
dprintf("dbri_minortoas: minordev = 0x%x chnl = %d \n",
|
|
(unsigned int)minordev, i);
|
|
if (i == Nstreams)
|
|
return (NULL);
|
|
|
|
return (&driver->ioc[i].as);
|
|
} /* dbri_minortoas */
|
|
|
|
|
|
/*
|
|
* dbri_open - Set device structure ptr and call generic routine.
|
|
*/
|
|
int
|
|
dbri_open(q, dev, flag, sflag)
|
|
queue_t *q;
|
|
dev_t dev;
|
|
int flag;
|
|
int sflag;
|
|
{
|
|
dbri_dev_t *driver;
|
|
aud_stream_t *as;
|
|
isdn_conn_req_t conn;
|
|
int unit;
|
|
dev_t origdev;
|
|
int i;
|
|
int s;
|
|
int busy = 0;
|
|
dbri_chan_tab_t *ctep; /* Channel Table Entry Pointer */
|
|
int error = 0;
|
|
|
|
origdev = dev;
|
|
unit = MINORUNIT(dev);
|
|
|
|
if ((unit < 0) || (unit >= Ndbri)) {
|
|
dprintf("dbri: Invalid unit number 0x%x.\n",
|
|
(unsigned int)unit);
|
|
u.u_error = ENODEV;
|
|
return (OPENFAIL);
|
|
}
|
|
driver = &Dbri_devices[unit];
|
|
|
|
if (driver->openinhibit) {
|
|
/*
|
|
* This unit is unusable for some reason such as a hardware
|
|
* failure.
|
|
*/
|
|
u.u_error = ENXIO;
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
dprintf("dbri_open: q=0x%x, dev=0x%x, flag=0x%x, sflag=0x%x\n",
|
|
(unsigned int)q, (unsigned int)dev, (unsigned int)flag,
|
|
(unsigned int)sflag);
|
|
|
|
/*
|
|
* XXX - This is necessary for use with the aclone driver
|
|
* since we *must* return a different minor number than
|
|
* was passed to us. It could be implemented cleaner.
|
|
*/
|
|
if (MINORDEV(dev) > Nstreams) {
|
|
/*
|
|
* XXX What is the logic for the minor number calculation
|
|
* XXX in this case?
|
|
*/
|
|
dev = dbrimakedev(major(dev), unit, MINORDEV(dev) - Nstreams);
|
|
}
|
|
|
|
/*
|
|
* Handle clone device opens
|
|
*/
|
|
if (sflag == CLONEOPEN) {
|
|
switch (MINORDEV(dev)) {
|
|
case 0: /* Normal clone open */
|
|
case DBRI_MINOR_AUDIO_RW: /* Audio clone open */
|
|
if (flag & FWRITE) {
|
|
dev = dbrimakedev(major(dev), unit,
|
|
DBRI_MINOR_AUDIO_RW);
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case DBRI_MINOR_AUDIO_RO: /* Audio clone open */
|
|
dev = dbrimakedev(major(dev), unit,
|
|
DBRI_MINOR_AUDIO_RO);
|
|
break;
|
|
|
|
default:
|
|
u.u_error = ENODEV;
|
|
return (OPENFAIL);
|
|
}
|
|
} else if (minor(dev) == 0) {
|
|
/*
|
|
* Because the system temporarily uses the streamhead
|
|
* corresponding to major,0 during a clone open, and because
|
|
* audio_open() can sleep, audio drivers are not allowed
|
|
* to use major,0 as a valid device number.
|
|
*
|
|
* A sleeping clone open and a non-clone use of maj,0
|
|
* can mess up the reference counts on vnodes/snodes
|
|
*/
|
|
u.u_error = ENODEV;
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
/*
|
|
* Check for legal device.
|
|
*/
|
|
if ((as = dbri_minortoas(driver, MINORDEV(dev))) == NULL) {
|
|
u.u_error = ENODEV;
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
/*
|
|
* "as" is not the play_as if the aud_stream is a read-only
|
|
* data device.
|
|
*/
|
|
if (ISDATASTREAM(as) && ((flag & (FREAD|FWRITE)) == FREAD)) {
|
|
dev = dbrimakedev(major(dev), unit,
|
|
as->record_as->info.minordev);
|
|
if ((as = dbri_minortoas(driver, MINORDEV(dev))) == NULL) {
|
|
u.u_error = ENODEV;
|
|
return (OPENFAIL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there are still open streams and initchip is FALSE this
|
|
* means that we have done a reset of DBRI. Do not allow any
|
|
* more streams to open the device until the last of the
|
|
* original streams finally closes the device.
|
|
*/
|
|
s = splr(driver->intrlevel);
|
|
for (i = 0; i < Nstreams; ++i) {
|
|
aud_stream_t *asp;
|
|
|
|
asp = &driver->ioc[i].as;
|
|
if (asp->info.open)
|
|
++busy;
|
|
}
|
|
(void) splx(s);
|
|
if (busy && !driver->init_chip) {
|
|
dprintf("dbri:\tChip reset. All open streams must\n");
|
|
dprintf("\tbe closed before new open()'s.\n");
|
|
ATRACE(dbri_open, '!rdy', busy);
|
|
u.u_error = EBUSY;
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
if (audio_open(as, q, flag, sflag) == OPENFAIL) {
|
|
ATRACE(dbri_open, 'fail', as);
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
if (as->writeq == NULL) {
|
|
/* XXX This should never happen */
|
|
ATRACE(dbri_open, ATR_WHY, as->writeq);
|
|
u.u_error = EBUSY;
|
|
call_debug("as->writeq == NULL");
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
/* Associate a stream with the appropriate hardware. */
|
|
s = splr(driver->intrlevel);
|
|
|
|
/*
|
|
* Loop thru conf table searching for matching minordev.
|
|
*/
|
|
for (i = 0, ctep = NULL; i < Nstreams; ++i) {
|
|
if ((u_int)Chan_tab[i].minordev == MINORDEV(dev)) {
|
|
ctep = &Chan_tab[i];
|
|
dprintf("Minor %d = Chan_tab[%d] {\
|
|
chan:%d, port:%d, bpipe:%d, cycle:%d, len:%d, dir:%d, \
|
|
mode:0x%x, samplerate:%d, channels:%d, in_as:%d, \
|
|
out_as:%d, control_as:%d, audtype:%d, minordev:%d, \
|
|
isdntype:%d, numbufs:%d, name:%s, input_size:%d}\n",
|
|
MINORDEV(dev), i,
|
|
ctep->chan, ctep->port, ctep->bpipe, ctep->cycle, ctep->len, ctep->dir,
|
|
ctep->mode, ctep->samplerate, ctep->channels, ctep->in_as, ctep->out_as,
|
|
ctep->control_as, ctep->audtype, ctep->minordev, ctep->isdntype, ctep->numbufs,
|
|
ctep->name, ctep->input_size);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Minor number not found in configuration table.
|
|
*/
|
|
if (ctep == NULL) {
|
|
u.u_error = ENODEV;
|
|
return (OPENFAIL);
|
|
}
|
|
|
|
/*
|
|
* Form the defaults for a connection request here
|
|
*/
|
|
conn.unit = unit;
|
|
conn.xmitport = ISDN_HOST; /* read from host perspective */
|
|
conn.recvport = ctep->port;
|
|
if (ctep->dir == DBRI_OUT) {
|
|
conn.dir = ISDN_TWOWAY_STREAM;
|
|
} else {
|
|
conn.dir = ISDN_ONEWAY_STREAM;
|
|
}
|
|
/*
|
|
* Convert DBRI mode from channel table to ISDN mode type
|
|
* to compose a generic ISDN connection request.
|
|
*/
|
|
switch (ctep->mode) {
|
|
case DBRI_MODE_XPRNT:
|
|
conn.mode = ISDN_MODE_TRANSPARENT;
|
|
break;
|
|
case DBRI_MODE_HDLC:
|
|
case DBRI_MODE_HDLC_D:
|
|
conn.mode = ISDN_MODE_HDLC;
|
|
break;
|
|
case DBRI_MODE_SER:
|
|
case DBRI_MODE_UNKNOWN:
|
|
default:
|
|
conn.mode = ISDN_MODE_UNKNOWN;
|
|
dprintf("dbri_open: unknown mode %x\n", ctep->mode);
|
|
break;
|
|
} /* switch */
|
|
|
|
/*
|
|
* Adjustment for unidirectional write only connection request.
|
|
*/
|
|
if ((ctep->dir == DBRI_OUT) && ((flag & FREAD) == 0)) {
|
|
conn.dir = ISDN_ONEWAY_STREAM;
|
|
conn.xmitport = conn.recvport; /* swap ports for write only */
|
|
conn.recvport = ISDN_HOST;
|
|
}
|
|
|
|
if (ISDATASTREAM(as)) {
|
|
/* connect up the h/w for a data stream */
|
|
if (dbri_con_stream(as, &conn, &error) == DBRI_BADCONN) {
|
|
ATRACEI(dbri_open, ATR_WHY, minor(dev));
|
|
/* Close to clean up and indicate stream not busy */
|
|
audio_close(as->writeq, 0); /* flag doesn't matter */
|
|
u.u_error = error;
|
|
return (OPENFAIL);
|
|
}
|
|
}
|
|
|
|
ATRACEI(dbri_open, 'port', minor(dev));
|
|
(void) splx(s);
|
|
|
|
/*
|
|
* NOTE: If this is a cloneopen and the minor passed to us and
|
|
* the minor we return are the same, we will end up corrupting
|
|
* the vnode/snode structures and eventually crash the system.
|
|
*/
|
|
if (sflag == CLONEOPEN && dev == origdev) {
|
|
printf("dbri_open: clone open returns original minor dev, good luck!\n");
|
|
}
|
|
ASSERT(q->q_ptr != NULL);
|
|
|
|
ATRACEI(dbri_ioctl, 'dev ', dev);
|
|
return (minor(dev));
|
|
} /* dbri_open */
|
|
|
|
|
|
void
|
|
dbri_close(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_dev_t *driver = (dbri_dev_t *)as->v->devdata;
|
|
int s;
|
|
|
|
dprintf("dbri_close: as=0x%x\n", (unsigned int)as);
|
|
if (!ISDATASTREAM(as))
|
|
return; /* No h/w to disconnect so just return */
|
|
|
|
s = splr(driver->intrlevel);
|
|
|
|
/* XXX - check what cleanup needs to be done here */
|
|
AsToDs(as)->samples = 0;
|
|
AsToDs(as)->recv_eol = 0;
|
|
AsToDs(as)->audio_uflow = 0;
|
|
AsToDs(as)->last_flow_err = 0;
|
|
bzero((caddr_t)&AsToDs(as)->d_stats, (unsigned)sizeof(dbri_stat_t));
|
|
|
|
/*
|
|
* Disassociate the stream from the HW.
|
|
*/
|
|
(void) dbri_xcon_stream(as, (isdn_conn_req_t *) NULL);
|
|
(void) splx(s);
|
|
return;
|
|
} /* dbri_close */
|
|
|
|
|
|
/*
|
|
* Process ioctls not already handled by the generic audio handler.
|
|
*/
|
|
aud_return_t
|
|
dbri_ioctl(as, mp, iocp)
|
|
aud_stream_t *as;
|
|
mblk_t *mp;
|
|
struct iocblk *iocp;
|
|
{
|
|
aud_return_t change;
|
|
isdn_conn_req_t *conp;
|
|
dbri_dev_t *driver = (dbri_dev_t *)as->v->devdata;
|
|
int s;
|
|
int error;
|
|
|
|
dprintf("dbri_ioctl: as=%x, control_as=%x\n", (unsigned int)as,
|
|
(unsigned int)as->control_as);
|
|
|
|
s = splr(driver->intrlevel);
|
|
change = AUDRETURN_NOCHANGE; /* detect device state change */
|
|
ATRACEI(dbri_ioctl, 'ioct', iocp->ioc_cmd);
|
|
|
|
switch (iocp->ioc_cmd) {
|
|
case AUDIO_GETDEV:
|
|
{
|
|
int *ip;
|
|
|
|
ip = ((int *)mp->b_cont->b_rptr);
|
|
mp->b_cont->b_wptr += sizeof (int);
|
|
*ip = mmcodec_getdev(driver);
|
|
iocp->ioc_count = sizeof (int);
|
|
}
|
|
break;
|
|
|
|
case ISDN_PH_ACTIVATE_REQ:
|
|
as = as->play_as; /* get control stream */
|
|
dprintf("before test activation request\n");
|
|
|
|
if (!ISHWCONNECTED(as)) {
|
|
dprintf("dbri: error in D-ch act. req. (no pipe)\n");
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (AsToDs(as)->i_var.power != ISDN_PH_PARAM_POWER_ON) {
|
|
iocp->ioc_error = ENXIO;
|
|
break;
|
|
} else if (ISTEDCHANNEL(as)) {
|
|
audio_sendsig(as); /* XXX broken D-ch protocol */
|
|
dbri_te_ph_activate_req(as); /* Activate TE */
|
|
} else if (ISNTDCHANNEL(as)) {
|
|
dbri_nt_ph_activate_req(as); /* Activate NT */
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ISDN_MPH_DEACTIVATE_REQ:
|
|
as = as->play_as; /* get control stream */
|
|
|
|
if (AsToDs(as)->i_var.power != ISDN_PH_PARAM_POWER_ON) {
|
|
iocp->ioc_error = ENXIO;
|
|
break;
|
|
} else if (ISNTDCHANNEL(as) && ISHWCONNECTED(as)) {
|
|
dbri_nt_mph_deactivate_req(as); /* Deactivate NT */
|
|
} else {
|
|
/*
|
|
* There is no such thing as PH_DEACTIVATE_REQ
|
|
* for TEs, so this handles that also.
|
|
*/
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
#ifdef DBRI_UNSUPPORTED
|
|
case ISDN_SETPORT: /* connect stream to h/w */
|
|
conp = (isdn_conn_req_t *) mp->b_cont->b_rptr;
|
|
ATRACEI(dbri_ioctl, 'setp', (int) conp);
|
|
/*
|
|
* XXX This needs to be uncommented out as soon as sparcphone uses DBRIMGT
|
|
* if (as != &driver->ioc[DBRI_DBRIMGT].as) {
|
|
* iocp->ioc_error = EPERM;
|
|
* break;
|
|
* }
|
|
*/
|
|
if (dbri_con_stream(as, conp, &error) == DBRI_BADCONN) {
|
|
dprintf("dbri: Setup port request failed\n");
|
|
iocp->ioc_error = error;
|
|
break;
|
|
}
|
|
change = AUDRETURN_CHANGE;
|
|
break;
|
|
|
|
case ISDN_RELPORT: /* disconnect stream from h/w */
|
|
conp = (isdn_conn_req_t *)mp->b_cont->b_rptr;
|
|
ATRACEI(dbri_ioctl, 'relp', (int) conp);
|
|
/*
|
|
* XXX This needs to be uncommented out as soon as sparcphone uses DBRIMGT
|
|
* if (as != &driver->ioc[DBRI_DBRIMGT].as) {
|
|
* iocp->ioc_error = EPERM;
|
|
* break;
|
|
* }
|
|
*/
|
|
if (dbri_xcon_stream(as, conp) == DBRI_BADCONN) {
|
|
dprintf("dbri: Release port request failed\n");
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
change = AUDRETURN_CHANGE;
|
|
break;
|
|
#endif DBRI_UNSUPPORTED
|
|
|
|
case ISDN_SET_PARAM:
|
|
{
|
|
isdn_ph_param_t *ip;
|
|
#define IOCEQ(x) (as == driver->ioc[(int)x].as.play_as)
|
|
#define IOCEQMGT(x) (as == driver->ioc[(int)x].as.control_as)
|
|
|
|
ATRACEI(dbri_ioctl, 'dev ', as->info.minordev);
|
|
|
|
ip = ((isdn_ph_param_t *)mp->b_cont->b_rptr);
|
|
switch (ip->tag) {
|
|
case ISDN_NT_PH_PARAM_T101: /* Set */
|
|
if (IOCEQ(DBRI_NT_D_OUT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
AsToDs(as)->i_var.t101 = ip->value.ms;
|
|
change = AUDRETURN_CHANGE;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
case ISDN_NT_PH_PARAM_T102: /* Set */
|
|
if (IOCEQ(DBRI_NT_D_OUT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
AsToDs(as)->i_var.t102 = ip->value.ms;
|
|
change = AUDRETURN_CHANGE;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
case ISDN_TE_PH_PARAM_T103: /* Set */
|
|
if (IOCEQ(DBRI_TE_D_OUT) || IOCEQMGT(DBRI_TEMGT)) {
|
|
AsToDs(as)->i_var.t103 = ip->value.ms;
|
|
change = AUDRETURN_CHANGE;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
case ISDN_TE_PH_PARAM_T104: /* Set */
|
|
if (IOCEQ(DBRI_TE_D_OUT) || IOCEQMGT(DBRI_TEMGT)) {
|
|
AsToDs(as)->i_var.t104 = ip->value.ms;
|
|
change = AUDRETURN_CHANGE;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
case ISDN_PH_PARAM_MAINT: /* Set */
|
|
if (IOCEQMGT(DBRI_TEMGT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
dbri_stream_t *ds;
|
|
|
|
ds = AsToDs(as);
|
|
if (IOCEQMGT(DBRI_TEMGT))
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_TE_D_OUT]
|
|
.as.play_as);
|
|
else if (IOCEQMGT(DBRI_NTMGT))
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_NT_D_OUT]
|
|
.as.play_as);
|
|
switch (ip->value.maint) {
|
|
case ISDN_PH_PARAM_MAINT_OFF:
|
|
case ISDN_PH_PARAM_MAINT_ECHO:
|
|
case ISDN_PH_PARAM_MAINT_ON:
|
|
ds->i_var.maint = ip->value.maint;
|
|
change = AUDRETURN_CHANGE;
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
case ISDN_PH_PARAM_ASMB: /* Set */
|
|
{
|
|
/*
|
|
* If optional behaviors are allowed for NT side,
|
|
* then add more conditionals here.
|
|
*/
|
|
dbri_stream_t *ds;
|
|
|
|
if (!IOCEQMGT(DBRI_TEMGT)) {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
|
|
ds = AsToDs(driver->ioc[(int)DBRI_TE_D_OUT].as.play_as);
|
|
switch (ip->value.maint) {
|
|
case ISDN_TE_PH_PARAM_ASMB_CCITT88:
|
|
case ISDN_TE_PH_PARAM_ASMB_CTS2:
|
|
ds->i_var.asmb = ip->value.asmb;
|
|
change = AUDRETURN_CHANGE;
|
|
break;
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ISDN_PH_PARAM_POWER: /* Set */
|
|
if ( IOCEQMGT(DBRI_TEMGT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
dbri_stream_t *ds;
|
|
|
|
ds = AsToDs(as);
|
|
if (IOCEQMGT(DBRI_TEMGT)) {
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_TE_D_OUT]
|
|
.as.play_as);
|
|
} else if (IOCEQMGT(DBRI_NTMGT)) {
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_NT_D_OUT]
|
|
.as.play_as);
|
|
}
|
|
|
|
switch (ip->value.flag) {
|
|
case ISDN_PH_PARAM_POWER_OFF:
|
|
case ISDN_PH_PARAM_POWER_ON:
|
|
if (ds->i_var.power != ip->value.flag) {
|
|
(void)dbri_power(ds, ip->value.flag);
|
|
change = AUDRETURN_CHANGE;
|
|
}
|
|
break;
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
|
|
case ISDN_PH_PARAM_PAUSE: /* Pause IO */
|
|
{
|
|
/*
|
|
* XXX Do B1 and B1_56 point to the
|
|
* XXX same place?
|
|
*/
|
|
switch (ip->value.pause.port) {
|
|
case ISDN_NT_B1:
|
|
case ISDN_NT_B2:
|
|
case ISDN_NT_H:
|
|
if (IOCEQMGT(DBRI_DBRIMGT)
|
|
|| IOCEQMGT(DBRI_TEMGT)) {
|
|
as = driver->ioc[(int)ip->value.pause.port].as.play_as;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
|
|
case ISDN_TE_B1:
|
|
case ISDN_TE_B2:
|
|
case ISDN_TE_H:
|
|
if (IOCEQMGT(DBRI_DBRIMGT)
|
|
|| IOCEQMGT(DBRI_TEMGT)) {
|
|
as = driver->ioc[(int)ip->value.pause.port].as.play_as;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
|
|
if (iocp->ioc_error != EINVAL) {
|
|
if (ip->value.pause.paused) {
|
|
audio_pause_record(as);
|
|
audio_pause_play(as);
|
|
} else {
|
|
audio_resume_record(as);
|
|
audio_resume_play(as);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#ifdef DBRI_UNSUPPORTED
|
|
case ISDN_PH_PARAM_GLITCH: /* DBRI specific, unsupported */
|
|
{
|
|
unsigned int save_sts;
|
|
|
|
if (IOCEQ(DBRI_TE_D_OUT) || IOCEQ(DBRI_NT_D_OUT)) {
|
|
save_sts = driver->chip->n.sts;
|
|
if (IOCEQ(DBRI_TE_D_OUT)) {
|
|
driver->chip->n.sts &= ~DBRI_STS_T;
|
|
DELAY((int)ip->value.us);
|
|
driver->chip->n.sts = save_sts;
|
|
} else {
|
|
driver->chip->n.sts &= ~DBRI_STS_N;
|
|
DELAY((int)ip->value.us);
|
|
driver->chip->n.sts = save_sts;
|
|
}
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ISDN_PH_PARAM_NORESTART: /* DBRI specific, unsupported */
|
|
{
|
|
if (IOCEQ(DBRI_TE_D_OUT) || (IOCEQ(DBRI_NT_D_OUT))) {
|
|
AsToDs(as)->i_var.norestart =
|
|
(ip->value.flag != 0);
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
}
|
|
#endif DBRI_UNSUPPORTED
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ISDN_GET_PARAM:
|
|
{
|
|
isdn_ph_param_t *ip;
|
|
|
|
ATRACEI(dbri_ioctl, 'dev ', as->info.minordev);
|
|
|
|
ip = ((isdn_ph_param_t *)mp->b_cont->b_rptr);
|
|
iocp->ioc_count = sizeof (isdn_ph_param_t);
|
|
|
|
switch (ip->tag) {
|
|
case ISDN_NT_PH_PARAM_T101: /* Query */
|
|
if (IOCEQ(DBRI_NT_D_OUT) || IOCEQ(DBRI_NTMGT)) {
|
|
/* XXX get value from D-Ch */
|
|
ip->value.ms = AsToDs(as)->i_var.t101;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
}
|
|
break;
|
|
case ISDN_NT_PH_PARAM_T102: /* Query */
|
|
if (IOCEQ(DBRI_NT_D_OUT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
/* XXX get value from D-Ch */
|
|
ip->value.ms = AsToDs(as)->i_var.t102;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
}
|
|
break;
|
|
case ISDN_TE_PH_PARAM_T103: /* Query */
|
|
if (IOCEQ(DBRI_TE_D_OUT) || IOCEQMGT(DBRI_TEMGT)) {
|
|
/* XXX get value from D-Ch */
|
|
ip->value.ms = AsToDs(as)->i_var.t103;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
}
|
|
break;
|
|
case ISDN_TE_PH_PARAM_T104: /* Query */
|
|
if (IOCEQ(DBRI_TE_D_OUT) || IOCEQMGT(DBRI_TEMGT)) {
|
|
/* XXX get value from D-Ch */
|
|
ip->value.ms = AsToDs(as)->i_var.t104;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
}
|
|
break;
|
|
case ISDN_PH_PARAM_MAINT: /* Query */
|
|
if (IOCEQMGT(DBRI_TEMGT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
dbri_stream_t *ds;
|
|
|
|
ds = AsToDs(as);
|
|
if (IOCEQMGT(DBRI_TEMGT))
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_TE_D_OUT]
|
|
.as.play_as);
|
|
else if (IOCEQMGT(DBRI_NTMGT))
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_NT_D_OUT]
|
|
.as.play_as);
|
|
ip->value.maint = ds->i_var.maint;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
}
|
|
break;
|
|
case ISDN_PH_PARAM_ASMB: /* Query */
|
|
{
|
|
dbri_stream_t *ds;
|
|
|
|
if (!IOCEQMGT(DBRI_TEMGT)) {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
|
|
ds = AsToDs(driver->ioc[(int)DBRI_TE_D_OUT].as.play_as);
|
|
ip->value.asmb = ds->i_var.asmb;
|
|
break;
|
|
}
|
|
|
|
case ISDN_PH_PARAM_POWER: /* Query */
|
|
if (IOCEQMGT(DBRI_TEMGT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
dbri_stream_t *ds;
|
|
|
|
ds = AsToDs(as);
|
|
if (IOCEQMGT(DBRI_TEMGT))
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_TE_D_OUT]
|
|
.as.play_as);
|
|
else if (IOCEQMGT(DBRI_NTMGT))
|
|
ds = AsToDs(
|
|
driver->ioc[(int)DBRI_NT_D_OUT]
|
|
.as.play_as);
|
|
ip->value.flag = ds->i_var.power;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
|
|
case ISDN_PH_PARAM_PAUSE: /* Query paused state */
|
|
{
|
|
/*
|
|
* XXX Do B1 and B1_56 point to the
|
|
* XXX same place?
|
|
*/
|
|
switch (ip->value.pause.port) {
|
|
case ISDN_NT_B1:
|
|
case ISDN_NT_B2:
|
|
case ISDN_NT_H:
|
|
if (IOCEQMGT(DBRI_DBRIMGT)
|
|
|| IOCEQMGT(DBRI_TEMGT)) {
|
|
as = driver->ioc[(int)ip->value.pause.port].as.play_as;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
|
|
case ISDN_TE_B1:
|
|
case ISDN_TE_B2:
|
|
case ISDN_TE_H:
|
|
if (IOCEQMGT(DBRI_DBRIMGT)
|
|
|| IOCEQMGT(DBRI_TEMGT)) {
|
|
as = driver->ioc[(int)ip->value.pause.port].as.play_as;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
change = AUDRETURN_NOCHANGE;
|
|
break;
|
|
}
|
|
|
|
if (iocp->ioc_error != EINVAL) {
|
|
if (as->play_as->info.pause !=
|
|
as->record_as->info.pause) {
|
|
/* XXX This does not seem good */
|
|
printf("isdn pause play != record\n");;
|
|
}
|
|
ip->value.pause.paused = as->info.pause;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef DBRI_UNSUPPORTED
|
|
case ISDN_PH_PARAM_NORESTART: /* Query */
|
|
{
|
|
if (IOCEQ(DBRI_TE_D_OUT) || (IOCEQ(DBRI_NT_D_OUT))) {
|
|
ip->value.flag = AsToDs(as)->i_var.norestart;
|
|
} else {
|
|
iocp->ioc_error = EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
#endif /* DBRI_UNSUPPORTED */
|
|
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ISDN_MESSAGE_SET:
|
|
{
|
|
int *ip;
|
|
|
|
ip = ((int *)mp->b_cont->b_rptr);
|
|
|
|
/*
|
|
* XXX - If stream is not a D-Channel, or other control
|
|
* channel, then this ioctl should probably be rejected.
|
|
*/
|
|
|
|
if (IOCEQMGT(DBRI_TEMGT) || IOCEQMGT(DBRI_NTMGT)) {
|
|
as = as->play_as; /* Get D-channel as */
|
|
|
|
switch (*ip) {
|
|
case ISDN_MESSAGE_SET_SIGNAL:
|
|
AsToDs(as)->i_var.message_type = 0;
|
|
break;
|
|
case ISDN_MESSAGE_SET_PROTO:
|
|
AsToDs(as)->i_var.message_type = M_PROTO;
|
|
break;
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
} else {
|
|
/* Not TE or NT so it's invalid */
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ISDN_ACTIVATION_STATUS: /* get ISDN status */
|
|
{
|
|
isdn_activation_status_t *ip;
|
|
|
|
ip = ((isdn_activation_status_t *)mp->b_cont->b_rptr);
|
|
|
|
switch (ip->type) {
|
|
case ISDN_TYPE_SELF: /* This stream */
|
|
as = as->play_as;
|
|
if ((as !=
|
|
driver->ioc[(int)DBRI_TE_D_OUT].as.play_as) &&
|
|
(as != driver->ioc[(int)DBRI_NT_D_OUT].as.play_as))
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
|
|
case ISDN_TYPE_TE:
|
|
as = driver->ioc[(int)DBRI_TE_D_OUT].as.play_as;
|
|
break;
|
|
|
|
case ISDN_TYPE_NT:
|
|
as = driver->ioc[(int)DBRI_NT_D_OUT].as.play_as;
|
|
break;
|
|
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (iocp->ioc_error != EINVAL) {
|
|
ip->type = AsToDs(as)->i_info.type;
|
|
ip->activation = AsToDs(as)->i_info.activation;
|
|
iocp->ioc_count = sizeof (isdn_activation_status_t);
|
|
} else {
|
|
ip->type = ISDN_TYPE_UNKNOWN;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ISDN_STATUS: /* get ISDN status */
|
|
{
|
|
isdn_info_t *ip;
|
|
aud_stream_t *das;
|
|
|
|
ip = ((isdn_info_t *)mp->b_cont->b_rptr);
|
|
|
|
ATRACEI(dbri_ioctl, 'dev ', as->info.minordev);
|
|
|
|
switch (ip->type) {
|
|
case ISDN_TYPE_SELF: /* Self */
|
|
switch (AsToDs(as->play_as)->i_info.type) {
|
|
case ISDN_TYPE_TE:
|
|
das = driver->ioc[(int)DBRI_TE_D_OUT].as.play_as;
|
|
break;
|
|
|
|
case ISDN_TYPE_NT:
|
|
das = driver->ioc[(int)DBRI_NT_D_OUT].as.play_as;
|
|
break;
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ISDN_TYPE_TE:
|
|
das = driver->ioc[(int)DBRI_TE_D_OUT].as.play_as;
|
|
break;
|
|
|
|
case ISDN_TYPE_NT:
|
|
das = driver->ioc[(int)DBRI_NT_D_OUT].as.play_as;
|
|
break;
|
|
|
|
default:
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (iocp->ioc_error != EINVAL) {
|
|
iocp->ioc_count = sizeof (isdn_info_t);
|
|
*ip = AsToDs(as)->i_info;
|
|
|
|
ip->type = AsToDs(as)->i_info.type;
|
|
|
|
ip->activation = AsToDs(das)->i_info.activation;
|
|
ip->ph_ai = AsToDs(das)->i_info.ph_ai;
|
|
ip->ph_di = AsToDs(das)->i_info.ph_di;
|
|
ip->mph_ai = AsToDs(das)->i_info.mph_ai;
|
|
ip->mph_di = AsToDs(das)->i_info.mph_di;
|
|
ip->mph_ei1 = AsToDs(das)->i_info.mph_ei1;
|
|
ip->mph_ei2 = AsToDs(das)->i_info.mph_ei2;
|
|
ip->mph_ii_c = AsToDs(das)->i_info.mph_ii_c;
|
|
ip->mph_ii_d = AsToDs(das)->i_info.mph_ii_d;
|
|
|
|
ip->iostate = AsToDs(as)->i_info.iostate;
|
|
|
|
printf("play xmit %d, record xmit %d\n",
|
|
AsToDs(as)->i_info.transmit.packets,
|
|
AsToDs(as->record_as)->i_info.transmit.packets);
|
|
|
|
ip->transmit.packets =
|
|
AsToDs(as)->i_info.transmit.packets;
|
|
ip->transmit.octets =
|
|
AsToDs(as)->i_info.transmit.octets;
|
|
ip->transmit.errors =
|
|
AsToDs(as)->i_info.transmit.errors;
|
|
ip->receive.packets =
|
|
AsToDs(as->record_as)->i_info.receive.packets;
|
|
ip->receive.octets =
|
|
AsToDs(as->record_as)->i_info.receive.octets;
|
|
ip->receive.errors =
|
|
AsToDs(as->record_as)->i_info.receive.errors;
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ISDN_SET_LOOPBACK:
|
|
{
|
|
isdn_loopback_request_t *lr;
|
|
|
|
lr = ((isdn_loopback_request_t *)mp->b_cont->b_rptr);
|
|
|
|
if (dbri_loopback(as, lr, 0) == FALSE) {
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ISDN_RESET_LOOPBACK:
|
|
{
|
|
isdn_loopback_request_t *lr;
|
|
|
|
lr = ((isdn_loopback_request_t *)mp->b_cont->b_rptr);
|
|
|
|
if (dbri_loopback(as, lr, 1) == FALSE) {
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef DBRI_UNSUPPORTED
|
|
case ISDN_MODPORT: /* mod predefined port */
|
|
/* XXX - nothing for now */
|
|
#endif DBRI_UNSUPPORTED
|
|
|
|
default:
|
|
dprintf("dbri: Unsupported ioctl\n");
|
|
ATRACEI(dbri_ioctl, 'bioc', iocp->ioc_cmd);
|
|
iocp->ioc_error = EINVAL;
|
|
break;
|
|
} /* switch */
|
|
|
|
(void) splx(s);
|
|
return (change);
|
|
} /* dbri_ioctl */
|
|
|
|
|
|
/*
|
|
* dbri_start - used to start/resume reads or writes for an entire list.
|
|
*
|
|
* NOTE: This is not called to CONTINUE lists from dbri_queuecmd.
|
|
*/
|
|
void
|
|
dbri_start(as)
|
|
aud_stream_t *as;
|
|
{
|
|
int s;
|
|
dbri_chip_cmd_t dbricmd;
|
|
pipe_tab_t *pipep;
|
|
dbri_dev_t *driver;
|
|
dbri_cmd_t *dcp;
|
|
|
|
s = splstr();
|
|
|
|
if ((AsToDs(as)->cmdptr == NULL) || as->info.pause) {
|
|
/* No commands queued up */
|
|
ATRACEI(dbri_start, ATR_NONE, as->info.pause);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
if ((AsToDs(as)->pipe == DBRI_BAD_PIPE)) {
|
|
/* Bad ju-ju */
|
|
ATRACEI(dbri_start, 'Bpip', as);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
ATRACEI(dbri_start, '[go]', AsToDs(as)->pipe);
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
pipep = &driver->ptab[AsToDs(as)->pipe];
|
|
pipep->sdp &= ~DBRI_SDP_CMDMASK; /* kill off any cmd bits set */
|
|
/* XXXPAS potentially kill of CLR bit */
|
|
pipep->sdp |= (DBRI_SDP_PTR | DBRI_SDP_CLR);
|
|
|
|
dbricmd.cmd_wd1 = pipep->sdp;
|
|
|
|
/*
|
|
* Scan list for 1st non-complete MD.
|
|
*
|
|
* Note: the first thing on the chain could be a done, skip, etc.
|
|
*
|
|
* Note: This is necessary for the transmit direction
|
|
* because when paused, we end up with 'done' descriptors
|
|
* and the next dbri_start will have to skip over them.
|
|
*/
|
|
for (dcp = AsToDs(as)->cmdptr;
|
|
dcp != NULL;
|
|
dcp = dcp->nextio) {
|
|
|
|
/*
|
|
* cmd.lastfragment is not set until packets are
|
|
* assembled in the ISR.
|
|
*/
|
|
if (dcp->cmd.skip || dcp->cmd.done)
|
|
continue;
|
|
if (dcp->md.r.f.com)
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
if (dcp == NULL) {
|
|
ATRACEI(dbri_start, 'bore', 0);
|
|
dprintf("dbri_start: pipe%u, nothing interesting to do\n",
|
|
AsToDs(as)->pipe);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
|
|
dprintf("dbri_start: sdp %u\n", AsToDs(as)->pipe);
|
|
|
|
dbricmd.cmd_wd2 = (unsigned int)DBRI_ADDR(driver, &dcp->md.t);
|
|
dbri_command(driver, dbricmd); /* issue SDP command */
|
|
ATRACEI(dbri_start, 'sdp ', dbricmd.cmd_wd2);
|
|
|
|
dbricmd.cmd_wd1 = DBRI_CMD_PAUSE; /* PAUSE command */
|
|
dbri_command(driver, dbricmd);
|
|
|
|
as->info.active = TRUE;
|
|
|
|
/* On receive when we finally issue a SDP, clear eolflag */
|
|
if (AsToDs(as)->recv_eol)
|
|
AsToDs(as)->recv_eol = FALSE;
|
|
|
|
(void) splx(s);
|
|
return;
|
|
} /* dbri_start */
|
|
|
|
|
|
/*
|
|
* dbri_stop - stop reads or writes. Simply issues a SDP command with a
|
|
* NULL pointer causing data to stop at the end of the current buffer.
|
|
*/
|
|
void
|
|
dbri_stop(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
pipe_tab_t *pipep;
|
|
dbri_dev_t *driver;
|
|
int s;
|
|
|
|
s = splstr();
|
|
|
|
if (AsToDs(as)->cmdptr == NULL) {
|
|
(void) splx(s);
|
|
return; /* No commands queued up */
|
|
}
|
|
if ((AsToDs(as)->pipe == DBRI_BAD_PIPE)) {
|
|
/* Bad ju-ju */
|
|
ATRACEI(dbri_stop, 'Bpip', as);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
pipep = &driver->ptab[AsToDs(as)->pipe];
|
|
|
|
if (pipep->sdp == 0) {
|
|
ATRACEI(dbri_stop, '-na-', AsToDs(as)->pipe);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
ATRACEI(dbri_stop, 'STOP', AsToDs(as)->pipe);
|
|
|
|
pipep->sdp &= ~DBRI_SDP_CMDMASK; /* kill off any cmd bits set */
|
|
pipep->sdp |= (DBRI_SDP_PTR | DBRI_SDP_CLR);
|
|
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);
|
|
|
|
as->info.active = FALSE;
|
|
AsToDs(as)->recv_eol = TRUE; /* for start to get called later */
|
|
|
|
/* XXX There is a delay in dbri_flushcmd before releasing memory */
|
|
|
|
(void) splx(s);
|
|
return;
|
|
} /* dbri_stop */
|
|
|
|
|
|
/* dbri_setflag - Get or set a particular flag value */
|
|
unsigned int
|
|
dbri_setflag(as, op, val)
|
|
aud_stream_t *as;
|
|
enum aud_opflag op;
|
|
unsigned int val;
|
|
{
|
|
dbri_dev_t *driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
switch (op) {
|
|
case AUD_ERRORRESET:
|
|
{
|
|
int s;
|
|
|
|
s = splr(driver->intrlevel); /* reset error flag atomically */
|
|
val = AsToDs(as)->audio_uflow;
|
|
AsToDs(as)->audio_uflow = FALSE;
|
|
(void) splx(s);
|
|
break;
|
|
}
|
|
|
|
/* GET only */
|
|
case AUD_ACTIVE:
|
|
/*
|
|
* XXX - need to use status here like OVRN/UNDR and if
|
|
* not paused. Actually, a new routine.
|
|
*/
|
|
val = as->info.active;
|
|
break;
|
|
}
|
|
|
|
return (val);
|
|
} /* dbri_setflag */
|
|
|
|
|
|
/*
|
|
* dbri_setinfo - Get or set device-specific information in the
|
|
* audio state structure. Returns TRUE if there is an error.
|
|
*/
|
|
aud_return_t
|
|
dbri_setinfo(as, mp, iocp)
|
|
aud_stream_t *as;
|
|
mblk_t *mp;
|
|
struct iocblk *iocp;
|
|
{
|
|
int s;
|
|
dbri_stream_t *iocp_out;
|
|
dbri_stream_t *iocp_in;
|
|
dbri_stream_t *iocp_as;
|
|
dbri_dev_t *driver;
|
|
audio_info_t *ip;
|
|
int error, set_config;
|
|
unsigned int sample_rate, channels, precision, encoding;
|
|
unsigned int gain;
|
|
unsigned char balance;
|
|
|
|
error = 0;
|
|
driver = (dbri_dev_t *)(as->v->devdata);
|
|
|
|
iocp_out = AsToDs(as->play_as);
|
|
iocp_in = AsToDs(as->record_as);
|
|
iocp_as = AsToDs(as);
|
|
|
|
/* Set device-specific info into device-independent structure */
|
|
|
|
iocp_out->as.info.samples = iocp_out->samples;
|
|
iocp_in->as.info.samples = iocp_in->samples;
|
|
|
|
/* If getinfo, 'mp' is NULL...we're done */
|
|
if (mp == NULL)
|
|
return (AUDRETURN_NOCHANGE); /* no error */
|
|
|
|
s = splr(driver->intrlevel); /* load chip registers atomically */
|
|
ip = (audio_info_t *)mp->b_cont->b_rptr;
|
|
|
|
if (iocp_out == &driver->ioc[(int) DBRI_AUDIO_OUT]) {
|
|
if ((Modify(ip->play.gain)) || (Modifyc(ip->play.balance))) {
|
|
if (Modify(ip->play.gain))
|
|
gain = ip->play.gain;
|
|
else
|
|
gain = iocp_out->as.info.gain;
|
|
if (Modifyc(ip->play.balance))
|
|
balance = ip->play.balance;
|
|
else
|
|
balance = iocp_out->as.info.balance;
|
|
iocp_out->as.info.gain =
|
|
dbri_play_gain(driver, gain, balance);
|
|
iocp_out->as.info.balance = balance;
|
|
}
|
|
|
|
if (Modifyc(ip->output_muted)) {
|
|
driver->hwi_state.output_muted =
|
|
dbri_output_muted(driver, ip->output_muted);
|
|
}
|
|
|
|
if (Modify(ip->play.port)) {
|
|
iocp_out->as.info.port =
|
|
dbri_outport(driver, ip->play.port);
|
|
}
|
|
}
|
|
|
|
if (iocp_in == &driver->ioc[(int) DBRI_AUDIO_IN]) {
|
|
if ((Modify(ip->record.gain))||(Modifyc(ip->record.balance))) {
|
|
if (Modify(ip->record.gain))
|
|
gain = ip->record.gain;
|
|
else
|
|
gain = iocp_in->as.info.gain;
|
|
if (Modifyc(ip->record.balance))
|
|
balance = ip->record.balance;
|
|
else
|
|
balance = iocp_in->as.info.balance;
|
|
iocp_in->as.info.gain =
|
|
dbri_record_gain(driver, gain, balance);
|
|
iocp_in->as.info.balance = balance;
|
|
}
|
|
|
|
if (Modify(ip->monitor_gain)) {
|
|
driver->hwi_state.monitor_gain =
|
|
dbri_monitor_gain(driver, ip->monitor_gain);
|
|
}
|
|
if (Modify(ip->record.port)) {
|
|
switch (ip->record.port) {
|
|
case AUDIO_MICROPHONE:
|
|
case AUDIO_LINE_IN:
|
|
iocp_in->as.info.port =
|
|
dbri_inport(driver, ip->record.port);
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the sample counters atomically, returning the old values.
|
|
*/
|
|
if (iocp_out->as.info.open) {
|
|
iocp_out->as.info.samples = iocp_out->samples;
|
|
if (Modify(ip->play.samples))
|
|
iocp_out->samples = ip->play.samples;
|
|
}
|
|
|
|
if (iocp_in->as.info.open) {
|
|
iocp_in->as.info.samples = iocp_in->samples;
|
|
if (Modify(ip->record.samples))
|
|
iocp_in->samples = ip->record.samples;
|
|
}
|
|
|
|
/*
|
|
* First, get the right values to use. XXX - as an optimization,
|
|
* if all new values matching existing ones, we should just
|
|
* return
|
|
*/
|
|
if (Modify(ip->play.sample_rate))
|
|
sample_rate = ip->play.sample_rate;
|
|
else if (Modify(ip->record.sample_rate))
|
|
sample_rate = ip->record.sample_rate;
|
|
else
|
|
sample_rate = iocp_as->as.info.sample_rate;
|
|
if (Modify(ip->play.channels))
|
|
channels = ip->play.channels;
|
|
else if (Modify(ip->record.channels))
|
|
channels = ip->record.channels;
|
|
else
|
|
channels = iocp_as->as.info.channels;
|
|
if (Modify(ip->play.precision))
|
|
precision = ip->play.precision;
|
|
else if (Modify(ip->record.precision))
|
|
precision = ip->record.precision;
|
|
else
|
|
precision = iocp_as->as.info.precision;
|
|
if (Modify(ip->play.encoding))
|
|
encoding = ip->play.encoding;
|
|
else if (Modify(ip->record.encoding))
|
|
encoding = ip->record.encoding;
|
|
else
|
|
encoding = iocp_as->as.info.encoding;
|
|
|
|
/*
|
|
* Now, if setting the same existing format, or not touching
|
|
* the "big four" parameters, don't do anything
|
|
*/
|
|
if ((sample_rate == iocp_as->as.info.sample_rate) &&
|
|
(channels == iocp_as->as.info.channels) &&
|
|
(precision == iocp_as->as.info.precision) &&
|
|
(encoding == iocp_as->as.info.encoding)) {
|
|
|
|
set_config = FALSE;
|
|
} else if (Modify(ip->play.sample_rate) || Modify(ip->play.precision) ||
|
|
Modify(ip->play.channels) || Modify(ip->play.encoding)) {
|
|
|
|
/*
|
|
* So, a process wants to modify the play format - check
|
|
* and see if another process has it open for recording.
|
|
* If not, see if the new requested config is possible
|
|
* (Can't make changes on the control device however)
|
|
*/
|
|
if (iocp_in->as.info.open && iocp_out->as.info.open &&
|
|
(iocp_in->as.readq != iocp_out->as.readq)) {
|
|
dprintf("setinfo:play change but already recording\n");
|
|
error = EBUSY;
|
|
} else if ((iocp_as != iocp_in) && (iocp_as != iocp_out)) {
|
|
error = EINVAL;
|
|
} else {
|
|
set_config = TRUE;
|
|
error = mmcodec_check_audio_config(driver,
|
|
sample_rate, channels, precision, encoding);
|
|
}
|
|
} else if (Modify(ip->record.sample_rate) ||
|
|
Modify(ip->record.precision) ||
|
|
Modify(ip->record.channels) ||
|
|
Modify(ip->record.encoding)){
|
|
|
|
/*
|
|
* A process wants to modify the recording format - check
|
|
* to see if someone else is playing. If not, see if the
|
|
* new requested config is possible
|
|
*/
|
|
if (iocp_in->as.info.open && iocp_out->as.info.open &&
|
|
(iocp_in->as.readq != iocp_out->as.readq)) {
|
|
dprintf("setinfo:record change but already playing\n");
|
|
error = EBUSY;
|
|
} else if ((iocp_as != iocp_in) && (iocp_as != iocp_out)) {
|
|
error = EINVAL;
|
|
} else {
|
|
set_config = TRUE;
|
|
error = mmcodec_check_audio_config(driver,
|
|
sample_rate, channels, precision, encoding);
|
|
}
|
|
} else {
|
|
set_config = FALSE; /* don't know what to do */
|
|
}
|
|
if (!error && set_config) {
|
|
/*
|
|
* Update the "real" info structure (and others) accordingly
|
|
*/
|
|
ip->play.sample_rate = ip->record.sample_rate = sample_rate;
|
|
ip->play.channels = ip->record.channels = channels;
|
|
ip->play.precision = ip->record.precision = precision;
|
|
ip->play.encoding = ip->record.encoding = encoding;
|
|
mmcodec_set_audio_config(driver, as,
|
|
sample_rate, channels, precision, encoding);
|
|
/*
|
|
* Don't ack the ioctl until it's actually done; save the
|
|
* mp on this stream and when it's finished, then ack
|
|
*/
|
|
iocp_as->mp = mp;
|
|
(void) splx(s);
|
|
return (AUDRETURN_DELAYED);
|
|
}
|
|
(void) splx(s);
|
|
iocp->ioc_error = error;
|
|
return (AUDRETURN_CHANGE);
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine is called whenever a new packet is added to the cmd chain.
|
|
* It ties in the chip specific message descriptors then tells the chip to go.
|
|
*/
|
|
void
|
|
dbri_queuecmd(as, cmdp)
|
|
aud_stream_t *as;
|
|
aud_cmd_t *cmdp;
|
|
{
|
|
dbri_dev_t *driver = (dbri_dev_t *)(as->v->devdata);
|
|
int s;
|
|
|
|
s = splr(driver->intrlevel);
|
|
|
|
if (as == as->play_as) {
|
|
dbri_xmitqcmd(as, cmdp); /* fill and chain up xmit md's */
|
|
} else if (as == as->record_as) {
|
|
dbri_recvqcmd(as, cmdp); /* fill and chain up recv md's */
|
|
} else {
|
|
(void) printf("dbriqueuecmd: Non play or record as = 0x%x\n",
|
|
(unsigned int)as);
|
|
}
|
|
|
|
(void) splx(s);
|
|
return;
|
|
} /* dbri_queuecmd */
|
|
|
|
|
|
/*
|
|
* dbri_recvqcmd - Fill in a receive md's in the dbri command structure
|
|
*/
|
|
void
|
|
dbri_recvqcmd(as, cmdp)
|
|
aud_stream_t *as;
|
|
aud_cmd_t *cmdp;
|
|
{
|
|
dbri_cmd_t *dcp;
|
|
dbri_dev_t *driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
ATRACEI(dbri_recvqcmd, 'strt', cmdp);
|
|
for (dcp = (dbri_cmd_t *)cmdp;
|
|
dcp != NULL;
|
|
dcp = (dbri_cmd_t *)dcp->cmd.next) {
|
|
|
|
/*
|
|
* Initialize bit fields and pointers
|
|
*/
|
|
dcp->nextio = NULL;
|
|
dcp->md.r.word32[0] = 0; /* clear out all bits */
|
|
dcp->md.r.f.bufp = 0; /* buffer pointer */
|
|
dcp->md.r.f.fp = NULL; /* forward md pointer */
|
|
dcp->md.r.word32[3] = 0; /* clear out all bits */
|
|
|
|
/* Set bit fields and pointers */
|
|
#ifdef sun4m
|
|
/* XXX - add failure recovery code */
|
|
#ifndef lint /* XXX lint barfs on arg 1 */
|
|
dcp->sb_addr = (caddr_t)mb_nbmapalloc((struct map *)dvmamap,
|
|
(caddr_t)dcp->cmd.data,
|
|
as->input_size,
|
|
MDR_BIGSBUS|MB_CANTWAIT,
|
|
(func_t)0, (caddr_t)0);
|
|
#endif /* lint */
|
|
if (dcp->sb_addr == (caddr_t)0) {
|
|
printf("dbri_recvqcmd: out of DVMA resources!\n");
|
|
return;
|
|
}
|
|
dcp->md.r.f.bufp = (unsigned char *)dcp->sb_addr;
|
|
#else sun4m
|
|
dcp->md.r.f.bufp = dcp->cmd.data; /* buffer pointer */
|
|
#endif /* sun4m */
|
|
dcp->md.r.f.fint = 1; /* Frame interrupt */
|
|
|
|
ASSERT((dcp->cmd.enddata - dcp->cmd.data) >= as->input_size);
|
|
|
|
dcp->md.r.f.bcnt = as->input_size;
|
|
|
|
if (AsToDs(as)->cmdptr == NULL) {
|
|
AsToDs(as)->cmdptr = dcp;
|
|
AsToDs(as)->cmdlast = dcp;
|
|
ATRACEI(dbri_recvqcmd, ATR_FIRST, AsToDs(as)->pipe);
|
|
} else {
|
|
AsToDs(as)->cmdlast->md.r.f.fp =
|
|
(dbri_rmd_t *)DBRI_ADDR(driver, &(dcp->md.r));
|
|
/*
|
|
* The dbri_cmd_t is now visible to DBRI. Append
|
|
* to end of IO list.
|
|
*/
|
|
ATRACEI(dbri_recvqcmd, 'next', AsToDs(as)->cmdlast);
|
|
AsToDs(as)->cmdlast->nextio = dcp;
|
|
AsToDs(as)->cmdlast = dcp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Don't start IO if paused.
|
|
* Only when EOL is set do we issue a SDP.
|
|
*/
|
|
if (!as->info.pause && (AsToDs(as)->recv_eol))
|
|
dbri_start(as); /* issues SDP with new chain */
|
|
|
|
return;
|
|
} /* dbri_recvqcmd */
|
|
|
|
|
|
/*
|
|
* dbri_xmitqcmd - Fill in a transmit md's in the dbri command structure.
|
|
*
|
|
* Currently chucks packets when D-channels are not activated.
|
|
*/
|
|
void
|
|
dbri_xmitqcmd(as, cmdp)
|
|
aud_stream_t *as;
|
|
aud_cmd_t *cmdp;
|
|
{
|
|
dbri_cmd_t *dcp; /* general purpose DBRI command pointer */
|
|
struct {
|
|
dbri_cmd_t *head; /* 1st fragment in packet */
|
|
dbri_cmd_t *tail; /* last fragment in packet */
|
|
} packet;
|
|
dbri_dev_t *driver; /* Driver data structure */
|
|
int activated; /* Serial interface state */
|
|
enum {NOTSET, IDLE, CONTINUE, START} action; /* Action needed */
|
|
pipe_tab_t *pipep;
|
|
int notify;
|
|
|
|
action = NOTSET;
|
|
packet.head = NULL;
|
|
packet.tail = NULL;
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
|
|
/*
|
|
* Determine the state of the serial interface
|
|
*/
|
|
pipep = &driver->ptab[AsToDs(as)->pipe];
|
|
activated = dbri_chnl_activated(as, pipep->xchan, pipep->rchan);
|
|
|
|
/*
|
|
* Process a chain of one or more fragments making a single
|
|
* packet.
|
|
*
|
|
* audio_process_play() hides zero length fragments that are
|
|
* part of a non-zero length packet.
|
|
*/
|
|
ATRACEI(dbri_xmitqcmd, 'pipe', AsToDs(as)->pipe);
|
|
|
|
for (dcp = (dbri_cmd_t *)cmdp;
|
|
dcp != NULL;
|
|
dcp = (dbri_cmd_t *)dcp->cmd.next) {
|
|
int fraglen; /* length of a frag */
|
|
|
|
dcp->nextio = NULL;
|
|
fraglen = dcp->cmd.enddata - dcp->cmd.data;
|
|
if (fraglen > DBRI_MAXPACKET) {
|
|
(void) printf("dbri: fraglen is %d\n", fraglen);
|
|
ASSERT(fraglen <= DBRI_MAXPACKET);
|
|
}
|
|
|
|
if (dcp->cmd.done || dcp->cmd.skip || !activated) {
|
|
dcp->cmd.done = TRUE;
|
|
dcp->cmd.skip = TRUE;
|
|
|
|
dcp->md.t.word32[0] = 0; /* Clear bit fields */
|
|
dcp->md.t.f.bufp = NULL; /* data */
|
|
dcp->md.t.f.fp = NULL; /* forward md pointer */
|
|
dcp->md.t.f.status = 0; /* status */
|
|
|
|
if (!activated) {
|
|
ATRACEI(dbri_xmitqcmd, '!act', dcp);
|
|
} else {
|
|
ATRACEI(dbri_xmitqcmd, 'skip', dcp);
|
|
}
|
|
} else {
|
|
/*
|
|
* This fragment is part of a packet to transmit.
|
|
*/
|
|
dcp->md.t.word32[0] = 0; /* Clr out bit fields */
|
|
dcp->md.t.f.fp = NULL; /* forward md pointer */
|
|
dcp->md.t.f.status = 0; /* status */
|
|
dcp->md.t.f.cnt = fraglen;
|
|
dcp->md.t.f.idl = 0; /* 1st frag gets fixed later */
|
|
dcp->md.t.f.fcnt = 0; /* 1st frag gets fixed later */
|
|
|
|
ATRACEI(dbri_xmitqcmd, 'dcp ', dcp);
|
|
ATRACEI(dbri_xmitqcmd, 'frag', fraglen);
|
|
|
|
#ifdef sun4m
|
|
#ifndef lint /* XXX - lint barfs on arg 1 */
|
|
dcp->sb_addr = (caddr_t)mb_nbmapalloc(
|
|
(struct map *)dvmamap,
|
|
(caddr_t)dcp->cmd.data,
|
|
fraglen,
|
|
MDR_BIGSBUS|MB_CANTWAIT,
|
|
(func_t)0, (caddr_t)0);
|
|
#endif /* lint */
|
|
if (dcp->sb_addr == (caddr_t)0) {
|
|
printf("dbri_xmitqcmd: out of DVMA space!\n");
|
|
return;
|
|
}
|
|
dcp->md.t.f.bufp = (unsigned char *)dcp->sb_addr;
|
|
#else /* sun4m */
|
|
dcp->md.t.f.bufp = dcp->cmd.data;
|
|
#endif /* sun4m */
|
|
|
|
vac_flush((caddr_t)dcp->cmd.data,
|
|
dcp->cmd.enddata - dcp->cmd.data);
|
|
|
|
ASSERT(dcp->cmd.lastfragment != NULL);
|
|
|
|
if (&dcp->cmd == dcp->cmd.lastfragment) {
|
|
dcp->md.t.f.eof = 1; /* Tag last pkt */
|
|
dcp->md.t.f.fint = 1; /* Frame intr */
|
|
}
|
|
|
|
/*
|
|
* Link the fragments together
|
|
*/
|
|
if (packet.head == NULL) {
|
|
packet.head = dcp;
|
|
packet.tail = dcp;
|
|
packet.tail->md.t.f.fp = 0;
|
|
} else {
|
|
packet.tail->nextio = dcp;
|
|
packet.tail->md.t.f.fp =
|
|
(dbri_tmd_t *)DBRI_ADDR(driver,
|
|
&dcp->md.t);
|
|
packet.tail = dcp;
|
|
}
|
|
|
|
if (Dbri_debug > 1)
|
|
dbri_pr_dbri_cmd(dcp, 0);
|
|
}
|
|
}
|
|
|
|
if (packet.head) {
|
|
if (ISTEDCHANNEL(as)) {
|
|
/*
|
|
* CCITT Fascicle III.8 - Rec. I.430, p180, 6.1.4
|
|
*
|
|
* The priority is set on the first packet fragment.
|
|
* Always negotiate access.
|
|
*/
|
|
packet.head->md.t.f.idl = 1;
|
|
|
|
/*
|
|
* Use the correct priority for this SAPI.
|
|
* SAPI 0, call control, uses Class 1.
|
|
* All other SAPIs get Class 2.
|
|
*/
|
|
if ((packet.head->cmd.data[0] & 0xfc) == 0)
|
|
packet.head->md.t.f.fcnt = DBRI_D_CLASS_1;
|
|
else
|
|
packet.head->md.t.f.fcnt = DBRI_D_CLASS_2;
|
|
} else {
|
|
ASSERT(packet.tail && packet.tail->md.t.f.eof);
|
|
/*
|
|
* The "fcnt" field only takes effect in fragments
|
|
* with eof set and HDLC mode.
|
|
*
|
|
* For HDLC mode, send idles between frames.
|
|
*/
|
|
packet.tail->md.t.f.idl = 1;
|
|
packet.tail->md.t.f.fcnt = 1;
|
|
}
|
|
|
|
if (AsToDs(as)->cmdptr == NULL) {
|
|
AsToDs(as)->cmdptr = packet.head;
|
|
AsToDs(as)->cmdlast = packet.tail;
|
|
ATRACEI(dbri_xmitqcmd, ATR_FIRST, packet.head);
|
|
action = START;
|
|
} else {
|
|
ATRACEI(dbri_xmitqcmd, 'apnd', packet.head);
|
|
ATRACEI(dbri_xmitqcmd, '(to)', AsToDs(as)->cmdlast);
|
|
AsToDs(as)->cmdlast->nextio = packet.head;
|
|
|
|
AsToDs(as)->cmdlast->md.t.f.fp =
|
|
(dbri_tmd_t *)DBRI_ADDR(driver,
|
|
&packet.head->md.t);
|
|
|
|
AsToDs(as)->cmdlast = packet.tail;
|
|
action = CONTINUE;
|
|
}
|
|
if (packet.tail->md.t.f.eof != 1) {
|
|
dprintf("NO EOF! head=%x tail=%x\n",
|
|
(unsigned int)packet.head,
|
|
(unsigned int)packet.tail);
|
|
call_debug("no eof");
|
|
}
|
|
} else {
|
|
action = IDLE;
|
|
ATRACEI(dbri_xmitqcmd, 'nada', AsToDs(as)->pipe);
|
|
}
|
|
|
|
if (as->info.pause) { /* don't start IO if paused */
|
|
action = IDLE;
|
|
}
|
|
|
|
/* Give md's to chip to start or continue I/O */
|
|
switch (action) {
|
|
case CONTINUE:
|
|
{
|
|
dbri_chip_cmd_t cmd;
|
|
|
|
ATRACEI(dbri_xmitqcmd, 'cdp ', AsToDs(as)->pipe);
|
|
cmd.cmd_wd1 = DBRI_CMD_CDP | AsToDs(as)->pipe;
|
|
dbri_command(driver, cmd); /* issue CDP command */
|
|
|
|
as->info.active = TRUE;
|
|
}
|
|
break;
|
|
|
|
case START:
|
|
dbri_start(as); /* issues SDP with new chain */
|
|
break;
|
|
|
|
case IDLE:
|
|
/* XXX - need to insure that garbage is collected */
|
|
notify = audio_xmit_garbage_collect(as);
|
|
if (notify)
|
|
audio_sendsig(as->control_as);
|
|
break;
|
|
case NOTSET:
|
|
(void) printf("dbri: dbri_xmitqcmd with no action given\n");
|
|
break;
|
|
}
|
|
|
|
return;
|
|
} /* dbri_xmitqcmd */
|
|
|
|
|
|
/*
|
|
* dbri_flushcmd - Flush the device's notion of queued commands.
|
|
* AUD_STOP() mush be called before this routine.
|
|
*/
|
|
void
|
|
dbri_flushcmd(as)
|
|
aud_stream_t *as;
|
|
{
|
|
dbri_cmd_t *dcp;
|
|
|
|
dprintf("dbri_flush: pipe=%d\n", AsToDs(as)->pipe);
|
|
|
|
/*
|
|
* XXX - This delay is bogus and needs to be reworked but makes
|
|
* things a little safer for now. It's purpose is to insure
|
|
* that all SBus accesses by the chip are complete *BEFORE*
|
|
* freeing up memory.
|
|
*/
|
|
DELAY(250);
|
|
for (dcp = AsToDs(as)->cmdptr;
|
|
dcp != NULL;
|
|
dcp = dcp->nextio) {
|
|
#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 sun4m
|
|
}
|
|
|
|
AsToDs(as)->cmdptr = NULL;
|
|
AsToDs(as)->cmdlast = NULL;
|
|
|
|
return;
|
|
} /* dbri_flushcmd */
|
|
|
|
|
|
/*
|
|
* Send a M_ERROR message up the specified stream.
|
|
*/
|
|
void
|
|
dbri_error_msg(as, msg)
|
|
aud_stream_t *as; /* always points to write side */
|
|
int msg;
|
|
{
|
|
mblk_t *notify_mp;
|
|
|
|
/* If stream is not open, simply return */
|
|
if (as->readq == 0)
|
|
return;
|
|
|
|
/* Init a message to send a SIGPOLL upstream */
|
|
if ((notify_mp = allocb(sizeof (char), BPRI_HI)) == NULL)
|
|
return;
|
|
|
|
notify_mp->b_datap->db_type = msg;
|
|
*notify_mp->b_wptr++ = SIGPOLL;
|
|
|
|
/* Signal the specified stream */
|
|
putnext(as->readq, notify_mp);
|
|
|
|
return;
|
|
} /* dbri_error_msg */
|
|
|
|
|
|
/*
|
|
* TE sends: PH-AI, PH-DI, MPH-AI, MPH-DI, MPH_EI1, MPH_EI2, MPH_II_C, MPH_II_D
|
|
* NT sends: PH_AI, PH_DI, MPH_AI, MPH_DI, MPH_EI
|
|
*/
|
|
|
|
/*
|
|
* Send a CCITT PH or MPH indication upstream.
|
|
*/
|
|
void
|
|
dbri_send_primitive(as, message)
|
|
aud_stream_t *as;
|
|
isdn_message_type_t message;
|
|
{
|
|
mblk_t *primitive_mp;
|
|
dbri_stream_t *ds = AsToDs(as);
|
|
isdn_message_t *ip;
|
|
|
|
if (ds->i_var.message_type != M_PROTO) {
|
|
audio_sendsig(as);
|
|
if (as != as->control_as)
|
|
audio_sendsig(as->control_as);
|
|
return;
|
|
}
|
|
|
|
if ((primitive_mp = allocb(sizeof (isdn_message_t), BPRI_HI)) == NULL) {
|
|
ATRACE(dbri_send_primitive, 'fail', as);
|
|
return;
|
|
}
|
|
|
|
primitive_mp->b_datap->db_type = ds->i_var.message_type;
|
|
ip = (isdn_message_t *)primitive_mp->b_wptr;
|
|
primitive_mp->b_wptr += sizeof (*ip);
|
|
|
|
bzero((caddr_t)ip, sizeof (*ip));
|
|
ip->magic = ISDN_PROTO_MAGIC;
|
|
ip->type = ds->i_info.type;
|
|
ip->message = message;
|
|
|
|
if (as->control_as != NULL && as->control_as->info.open) {
|
|
as = as->control_as;
|
|
putnext(as->readq, primitive_mp);
|
|
ATRACE(dbri_send_primitive, 'sent', as);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* MPH-ACTIVATE Indication
|
|
*/
|
|
void
|
|
dbri_primitive_mph_ai(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.mph_ai;
|
|
ATRACE(dbri_primitive_mph_ai, 'prim', as);
|
|
dbri_send_primitive(as, MPH_AI);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* MPH-DEACTIVATE Indication
|
|
*/
|
|
void
|
|
dbri_primitive_mph_di(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.mph_di;
|
|
ATRACE(dbri_primitive_mph_di, 'prim', as);
|
|
dbri_send_primitive(as, MPH_DI);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_primitive_mph_ei1(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.mph_ei1;
|
|
ATRACE(dbri_primitive_mph_ei1, 'prim', as);
|
|
dbri_send_primitive(as, MPH_EI1);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_primitive_mph_ei2(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.mph_ei2;
|
|
ATRACE(dbri_primitive_mph_ei2, 'prim', as);
|
|
dbri_send_primitive(as, MPH_EI2);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_primitive_mph_ii_c(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.mph_ii_c;
|
|
ATRACE(dbri_primitive_mph_ii_c, 'prim', as);
|
|
dbri_send_primitive(as, MPH_II_C);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_primitive_mph_ii_d(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.mph_ii_d;
|
|
ATRACE(dbri_primitive_mph_ii_d, 'prim', as);
|
|
dbri_send_primitive(as, MPH_II_D);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_primitive_ph_ai(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.ph_ai;
|
|
ATRACE(dbri_primitive_ph_ai, 'prim', as);
|
|
dbri_send_primitive(as, PH_AI);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
dbri_primitive_ph_di(as)
|
|
aud_stream_t *as;
|
|
{
|
|
++AsToDs(as)->i_info.ph_di;
|
|
ATRACE(dbri_primitive_ph_ai, 'prim', as);
|
|
dbri_send_primitive(as, PH_DI);
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* dbri_loopback - Set up a loopback (flag = 0) or clear loopback
|
|
* (flag = 1).
|
|
*/
|
|
int
|
|
dbri_loopback(as, lr, flag)
|
|
aud_stream_t *as;
|
|
isdn_loopback_request_t *lr;
|
|
int flag;
|
|
{
|
|
int lb_bits; /* loopback bits for NT or TE channel */
|
|
serial_status_t *serialp;
|
|
dbri_chip_cmd_t cmd;
|
|
dbri_dev_t *driver;
|
|
|
|
driver = (dbri_dev_t *)as->v->devdata;
|
|
serialp = &driver->ser_sts;
|
|
lb_bits = 0;
|
|
|
|
/*
|
|
* The request must occur on a D channel on either the NT or the
|
|
* TE interface.
|
|
*/
|
|
if (!ISTEDCHANNEL(as) && !ISNTDCHANNEL(as))
|
|
return (FALSE);
|
|
|
|
/*
|
|
* Check for a request that specifies channels we are not
|
|
* supporting.
|
|
*/
|
|
if (lr->channels &
|
|
~(ISDN_LOOPBACK_D | ISDN_LOOPBACK_B1 | ISDN_LOOPBACK_B2))
|
|
return (FALSE);
|
|
|
|
/*
|
|
* Make a mask for the proper loopback bits in the NT/TE command
|
|
*/
|
|
if (lr->channels & (int)ISDN_LOOPBACK_D)
|
|
lb_bits |= DBRI_NTE_LLB_D;
|
|
if (lr->channels & (int)ISDN_LOOPBACK_B1)
|
|
lb_bits |= DBRI_NTE_LLB_B1;
|
|
if (lr->channels & (int)ISDN_LOOPBACK_B2)
|
|
lb_bits |= DBRI_NTE_LLB_B2;
|
|
|
|
switch (lr->type) {
|
|
case ISDN_LOOPBACK_LOCAL:
|
|
/* Bits are already okay, don't move them */
|
|
break;
|
|
|
|
case ISDN_LOOPBACK_REMOTE:
|
|
/* Shift bits over 3 positions to get Remote mask */
|
|
lb_bits <<= 3;
|
|
break;
|
|
|
|
default:
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* Build a DBRI TE or NT command and execute it.
|
|
*/
|
|
if (ISTEDCHANNEL(as)) {
|
|
if (flag)
|
|
serialp->te_cmd &= ~(lb_bits);
|
|
else
|
|
serialp->te_cmd |= lb_bits;
|
|
cmd.cmd_wd1 = serialp->te_cmd;
|
|
} else if (ISNTDCHANNEL(as)) {
|
|
if (flag)
|
|
serialp->nt_cmd &= ~(lb_bits);
|
|
else
|
|
serialp->nt_cmd |= lb_bits;
|
|
cmd.cmd_wd1 = serialp->nt_cmd;
|
|
} else {
|
|
return (FALSE);
|
|
}
|
|
dbri_command(driver, cmd);
|
|
|
|
return (TRUE);
|
|
} /* dbri_loopback */
|