u-isdn/alaw/alaw.c

477 lines
10 KiB
C

/**
** Streams Audio module
**/
/*
* Process sound. Handle basic conversion from signed 8-bit values to and from
* A-law (but _not_ expansion of the 8 bit values).
*/
#include "f_module.h"
#include "primitives.h"
#include "kernel.h"
#include "f_signal.h"
#include "f_malloc.h"
#include "streams.h"
#include "stropts.h"
/* #include <sys/user.h> */
#include "streamlib.h"
#include "alaw.h"
#include "isdn_proto.h"
/*
* Standard Streams stuff.
*/
static struct module_info alaw_minfo =
{
0, "alaw", 0, INFPSZ, 200,100
};
static qf_open alaw_open;
static qf_close alaw_close;
static qf_put alaw_rput, alaw_wput;
static qf_srv alaw_rsrv, alaw_wsrv;
static struct qinit alaw_rinit =
{
alaw_rput, alaw_rsrv, alaw_open, alaw_close, NULL, &alaw_minfo, NULL
};
static struct qinit alaw_winit =
{
alaw_wput, alaw_wsrv, NULL, NULL, NULL, &alaw_minfo, NULL
};
struct streamtab alawinfo =
{&alaw_rinit, &alaw_winit, NULL, NULL};
struct _vco {
short count; /* VCO turn-ogff counter. */
short run; /* Number of characters below threshold. Zero
* = mute. */
char on, off; /* Thresholds. */
};
struct alaw_ {
queue_t *q;
char connmode;
struct _vco r, w;
};
/*
* Initialize with reasonable default values.
*/
static int
alaw__init (struct alaw_ *alaw)
{
alaw->r.count = 0;
alaw->r.run = 1;
alaw->r.on = alaw->r.off = 0;
alaw->w.count = 0;
alaw->w.run = 1;
alaw->w.on = alaw->w.off = 0;
return 0;
}
static void
alaw_proto (queue_t * q, mblk_t * mp)
{
struct alaw_ *alaw = (struct alaw_ *) q->q_ptr;
streamchar *origmp = mp->b_rptr;
ushort_t id;
int error = 0;
/* In case we want to printf("%s") this... */
if (mp->b_wptr < DATA_END(mp))
*mp->b_wptr = '\0';
if (m_getid (mp, &id) != 0) {
mp->b_rptr = origmp;
putnext (q, mp);
return;
}
switch (id) {
case PROTO_MODULE:
if (strnamecmp (q, mp)) { /* Config information for me. */
long z;
while (mp != NULL && m_getsx (mp, &id) == 0)
switch (id) {
default:
goto err;
case PROTO_MODULE:
break;
case ALAW_LAW:
printf ("ALAW: Only A-Law supported\n");
goto err;
case ALAW_RVCO_ON:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 0 || z > 127)
goto err;
alaw->r.on = z;
break;
case ALAW_RVCO_OFF:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 0 || z > 127)
goto err;
alaw->r.off = z;
break;
case ALAW_RVCO_CNT:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 50 || z > 65535)
goto err;
alaw->r.count = z;
break;
case ALAW_XVCO_ON:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 0 || z > 127)
goto err;
alaw->w.on = z;
break;
case ALAW_XVCO_OFF:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 0 || z > 127)
goto err;
alaw->w.off = z;
break;
case ALAW_XVCO_CNT:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 50 || z > 65535)
goto err;
alaw->w.count = z;
break;
}
if (mp != NULL) {
freemsg (mp);
mp = NULL;
}
}
default:
break;
}
if (mp != NULL) {
if (origmp != NULL)
mp->b_rptr = origmp;
putnext (q, mp);
}
return;
err:
mp->b_rptr = origmp;
m_reply (q, mp, error ? error : -EINVAL);
}
/*
* Muting code.
*/
static mblk_t *
alaw_mute (mblk_t * mb, struct _vco *vco)
{
mblk_t *mx = NULL, *mxs = NULL;
if (vco->on == 0)
return mb;
while (mb != NULL) {
uchar_t *xcur = mb->b_rptr;
uchar_t *xend = mb->b_wptr;
uchar_t *blkend = NULL;
while (xcur < xend) {
if (vco->run == 0) {
for (; xcur < xend; xcur++) {
if (((*xcur > 0) ? *xcur : -*xcur) > vco->on) {
vco->run = 1;
break;
}
}
if (xcur < xend) {/* Start a new segment: Clone the old one. */
if (blkend != NULL) { /* Copy a previous segment */
mblk_t *mz = allocb (blkend - (uchar_t *) mb->b_rptr, BPRI_LO);
if (mz != NULL) {
short sz;
DATA_TYPE(mz) = DATA_TYPE(mb);
bcopy (mb->b_rptr, mz->b_wptr, (sz = blkend - (uchar_t *) mb->b_rptr));
mz->b_wptr += sz;
if (mx != NULL) {
mx->b_cont = mz;
mx = mz;
} else
mx = mxs = mz;
}
}
mb->b_rptr = xcur;
/* blkend is set to xend in the next pass */
}
} else {
short x = vco->run;
blkend = xend;
for (; xcur < xend; xcur++) {
if (((*xcur > 0) ? *xcur : -*xcur) > vco->off)
x = 1;
else {
if (++x >= vco->count) {
if ((uchar_t *) mb->b_rptr > (blkend = xcur - x))
blkend = NULL;
xcur++;
x = 0;
break;
}
}
}
vco->run = x;
}
}
if (blkend != NULL) {
mb->b_wptr = blkend;
if (mx != NULL) {
mx->b_cont = mb;
mx = mb;
} else
mx = mxs = mb;
mb = unlinkb (mx);
} else {
mblk_t *my = mb;
mb = unlinkb (mb);
freeb (my);
}
}
if (mx != NULL)
mx->b_cont = NULL;
return mxs;
}
static char cswap[256] =
{ /* Mirror bits */
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
};
/* Streams code to open the driver. */
static int
alaw_open (queue_t * q, dev_t dev, int flag, int sflag ERR_DECL)
{
register struct alaw_ *alaw;
alaw = malloc(sizeof(*alaw));
if(alaw == NULL)
ERR_RETURN(-ENOMEM);
memset(alaw,0,sizeof(*alaw));
WR (q)->q_ptr = (char *) alaw;
q->q_ptr = (char *) alaw;
alaw->q = q;
alaw__init (alaw);
MORE_USE;
return 0;
}
/* Streams code to close the driver. */
static void
alaw_close (queue_t * q, int dummy)
{
register struct alaw_ *alaw;
alaw = (struct alaw_ *) q->q_ptr;
flushq (q, FLUSHALL);
flushq (WR (q), FLUSHALL);
free(alaw);
LESS_USE;
return;
}
/* Streams code to write data. */
static void
alaw_wput (queue_t * q, mblk_t * mp)
{
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
case CASE_DATA:
putq (q, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq (q, FLUSHDATA);
/* FALL THRU */
default:
putnext (q, mp);
break;
}
return;
}
/* Streams code to scan the write queue. */
static void
alaw_wsrv (queue_t * q)
{
register struct alaw_ *alaw = (struct alaw_ *) q->q_ptr;
mblk_t *mp;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
alaw_proto (q, mp);
break;
case CASE_DATA:
{
mblk_t *mr;
if (!canput (q->q_next)) {
putbq (q, mp);
return;
}
mr = alaw_mute (mp, &alaw->w);
if (mr != NULL) {
for (mp = mr; mp != NULL; mp = mp->b_cont) {
streamchar *xcur = mp->b_rptr;
streamchar *xlim = mp->b_wptr;
for (; xcur < xlim; xcur++) {
char val = *xcur;
*xcur = (cswap[(val > 0) ? val : -val]
| ((val < 0) ? 0 : 1)) ^ 0xAA;
}
}
putnext (q, mr);
}
} break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq (q, FLUSHDATA);
/* FALL THRU */
default:
if (DATA_TYPE(mp) > QPCTL || canput (q->q_next)) {
putnext (q, mp);
continue;
} else {
putbq (q, mp);
return;
}
}
}
return;
}
/* Streams code to read data. */
static void
alaw_rput (queue_t * q, mblk_t * mp)
{
switch (DATA_TYPE(mp)) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHR) {
flushq (q, FLUSHDATA);
}
putnext (q, mp); /* send it along too */
break;
case MSG_PROTO:
case CASE_DATA:
putq (q, mp); /* queue it for my service routine */
break;
case M_ERROR:
case M_HANGUP:
default:
putnext (q, mp);
}
return;
}
/* Streams code to scan the read queue. */
static void
alaw_rsrv (queue_t * q)
{
mblk_t *mp;
register struct alaw_ *alaw = (struct alaw_ *) q->q_ptr;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
alaw_proto (q, mp);
break;
case CASE_DATA:
{
mblk_t *mr;
if (!canput (q->q_next)) {
putbq (q, mp);
return;
}
for (mr = mp; mr != NULL; mr = mr->b_cont) {
streamchar *xcur = mr->b_rptr;
streamchar *xlim = mr->b_wptr;
for (; xcur < xlim; xcur++) {
unsigned char val = *xcur ^ 0xAA;
if (val & 0x01)
*xcur = cswap[val] & 0x7F; /* high bit is set */
else
*xcur = -cswap[val]; /* high bit is clear */
}
}
if ((mr = alaw_mute (mp, &alaw->r)) != NULL)
putnext (q, mr);
}
break;
case M_HANGUP:
case M_ERROR:
/* FALL THRU */
default:
if (DATA_TYPE(mp) > QPCTL || canput (q->q_next)) {
putnext (q, mp);
continue;
} else {
putbq (q, mp);
return;
}
}
}
return;
}
#ifdef MODULE
static int do_init_module(void)
{
return register_strmod(&alawinfo);
}
static int do_exit_module(void)
{
return unregister_strmod(&alawinfo);
}
#endif