u-isdn/x75/x75.c

787 lines
17 KiB
C

/**
** Streams X75 module
**/
/*
* Just a Streams wrapper for the X75 library, with special care taken to
* handle ignorant people who, using the Siemens HCRX chip in auto mode, assign
* the same values to command and response addresses...
*/
#include "f_module.h"
#include "primitives.h"
#include <sys/types.h>
#include <sys/time.h>
#include "f_signal.h"
#include "f_malloc.h"
#include <sys/param.h>
#include <sys/sysmacros.h>
#include "streams.h"
#include <sys/errno.h>
#ifdef DONT_ADDERROR
#include "f_user.h"
#endif
#include "streamlib.h"
#include "x75lib.h"
#include "x75.h"
#include "isdn_proto.h"
extern void log_printmsg (void *log, const char *, mblk_t *, const char*);
/*
* Standard Streams stuff.
*/
static struct module_info x75_minfo =
{
0, "x75", 0, INFPSZ, 3000, 800
};
static qf_open x75_open;
static qf_close x75_close;
static qf_put x75_rput,x75_wput;
static qf_srv x75_rsrv,x75_wsrv;
static struct qinit x75_rinit =
{
x75_rput, x75_rsrv, x75_open, x75_close, NULL, &x75_minfo, NULL
};
static struct qinit x75_winit =
{
x75_wput, x75_wsrv, NULL, NULL, NULL, &x75_minfo, NULL
};
struct streamtab x75info =
{&x75_rinit, &x75_winit, NULL, NULL};
struct x75_ {
struct _x75 x75;
char flags;
#define X75_INUSE 01
#define X75_CONN 02
#define X75_ADRWARN 04
#define X75_DISC 010
#define X75_CONNECT 020 /* channel connected? */
#define X75_INTR 040 /* interrupt instead of disconnect */
char connmode;
queue_t *q;
int nr;
ushort_t concat;
ushort_t offset;
ushort_t radr_cmd, radr_meld, xadr_cmd, xadr_meld;
unsigned swapped:1; /* Marker that we're receiving. */
unsigned wide:1; /* addresses */
unsigned didSend:1;
unsigned didSendNew:1; /* Remember whether we were sending anything
* recently. If so, the incoming packet is
* assumed to be a response. This is
* necessary when command and response have
* the same address assigned to them. (See
* above.) */
};
/*
* X75 changed state. Probably send an MSG_PROTO down.
*/
static int
x75__state (struct x75_ *x_75, uchar_t ind, ushort_t add)
{
printf ("%sx75__state %d: Ind %d/%o\n",KERN_DEBUG , x_75 ->nr, ind, add);
switch (ind) {
case DL_ESTABLISH_IND:
case DL_ESTABLISH_CONF:{
mblk_t *mb;
if (!(x_75->flags & X75_CONN)) {
x_75->flags |= X75_CONN;
if ((mb = allocb (3, BPRI_HI)) != NULL) {
*((ushort_t *) mb->b_wptr)++ = PROTO_CONNECTED;
DATA_TYPE(mb) = MSG_PROTO;
putnext (x_75->q, mb);
}
}
} break;
case DL_RELEASE_IND:
case DL_RELEASE_CONF:{
mblk_t *mb;
x_75->flags &= ~X75_CONN;
if (!(x_75->flags & X75_DISC)) {
x_75->flags |= X75_DISC;
if ((mb = allocb (3, BPRI_HI)) != NULL) {
*((ushort_t *) mb->b_wptr)++ = (x_75->flags & X75_INTR) ? PROTO_INTERRUPT : PROTO_DISCONNECT;
DATA_TYPE(mb) = MSG_PROTO;
putnext (WR (x_75->q), mb);
}
}
} break;
}
return 0;
}
/*
* Check if we can send data downstream.
*/
static int
x75__cansend (struct x75_ *x_75)
{
return (x_75->q != NULL && canput (WR (x_75->q)->q_next));
}
/*
* Check if we can send data upstream. I.e., set/clear RNR.
*/
static int
x75__canrecv (struct x75_ *x_75)
{
return (x_75->q != NULL && canput (x_75->q->q_next));
}
/*
* Flush downstream.
*/
static int
x75__flush (struct x75_ *x_75)
{
return (x_75->q != NULL && putctlx1 (WR (x_75->q), M_FLUSH, FLUSHW));
}
/*
* Send data block downstream.
*/
static int
x75__send (struct x75_ *x_75, char iscmd, mblk_t * mb2)
{
mblk_t *mb;
if (x_75->q == NULL) {
printf("%sX75_send EIO\n",KERN_DEBUG );
return -EIO;
}
if(x_75->wide) {
if (/* XXX */ 0 || DATA_START(mb2)+2 > mb2->b_rptr
|| DATA_REFS(mb2) > 2 ) { /* Compromise... */
mb = allocb (x_75->offset, BPRI_HI);
if (mb == NULL)
return -ENOMEM;
mb->b_rptr += x_75->offset + 2;
mb->b_wptr += x_75->offset + 2;
linkb (mb, mb2);
} else
mb = mb2;
*--mb->b_rptr = (iscmd ? x_75->xadr_cmd : x_75->xadr_meld) & 0xFF;
*--mb->b_rptr = (iscmd ? x_75->xadr_cmd : x_75->xadr_meld) >> 8;
} else {
if (/* XXX */ 0 || DATA_START(mb2)+1 > mb2->b_rptr
|| DATA_REFS(mb2) > 2 ) { /* Compromise... */
mb = allocb (x_75->offset, BPRI_HI);
if (mb == NULL)
return -ENOMEM;
mb->b_wptr += x_75->offset + 1;
linkb (mb, mb2);
} else
mb = mb2;
*--mb->b_rptr = (iscmd ? x_75->xadr_cmd : x_75->xadr_meld);
}
putnext (WR (x_75->q), mb);
x_75->didSendNew = 1;
return 0;
}
/*
* Send data block upstream.
*/
static int
x75__recv (struct x75_ *x_75, char isUI, mblk_t * mp)
{
if (x_75->q == NULL)
freemsg (mp);
else if ((isUI != 0) != ((x_75->connmode & X75CONN_UI) != 0)) {
if (!(x_75->flags & X75_ADRWARN)) {
x_75->flags |= X75_ADRWARN;
printf ("%sX75: Got %s frame, but want %s\n",KERN_WARNING,
isUI ? "UI" : "I", isUI ? "I" : "UI");
}
freemsg (mp);
} else
putnext (x_75->q, mp);
return 0;
}
/* Reenable blocked Streams queue when xmit buffer empties. */
static int
x75__backenable (struct x75_ *x_75)
{
if (x_75->q != NULL) {
WR(x_75->q)->q_flag |= QWANTR;
qenable (x_75->q);
qenable (WR (x_75->q));
}
return 0;
}
/*
* Initialize with reasonable default values.
*/
static int
x75__init (struct x75_ *x_75)
{
x_75->xadr_cmd = x_75->radr_meld = 1;
x_75->radr_cmd = x_75->xadr_meld = 3;
x_75->connmode = X75CONN_OUT;
x_75->swapped = 0;
x_75->concat = 0;
x_75->offset = 0;
x_75->x75.offset = 1;
bzero (&x_75->x75, sizeof (struct _x75));
x_75->x75.cansend = (P_candata) & x75__cansend;
x_75->x75.canrecv = (P_candata) & x75__canrecv;
x_75->x75.send = (P_data) & x75__send;
x_75->x75.recv = (P_data) & x75__recv;
x_75->x75.state = (P_state) & x75__state;
x_75->x75.flush = (P_flush) & x75__flush;
x_75->x75.backenable = (P_backenable) & x75__backenable;
x_75->x75.ref = x_75;
x_75->x75.debugnr = (x_75->nr) + 100;
x75_initconn (&x_75->x75);
return 0;
}
/* Streams code to open the driver. */
static int
x75_open (queue_t * q, dev_t dev, int flag, int sflag ERR_DECL)
{
struct x75_ *x_75;
int nr = 1;
if (q->q_ptr) {
return 0;
}
x_75 = malloc(sizeof(*x_75));
if(x_75 == NULL)
ERR_RETURN(-ENOMEM);
memset(x_75,0,sizeof(*x_75));
WR (q)->q_ptr = (char *) x_75;
q->q_ptr = (char *) x_75;
x_75->q = q;
x_75->flags = X75_INUSE;
x_75->didSend = x_75->didSendNew = 0;
x_75->nr = nr++;
x75__init (x_75);
MORE_USE;
return 0;
}
/* Streams code to close the driver. */
static void
x75_close (queue_t * q, int dummy)
{
register struct x75_ *x_75;
x_75 = (struct x75_ *) q->q_ptr;
x75_changestate (&x_75->x75, PH_DEACTIVATE_IND, 1);
flushq (q, FLUSHALL);
flushq (WR (q), FLUSHALL);
if (0)
printf ("X75 driver closed.\n");
free(x_75);
LESS_USE;
return;
}
static void
x75_proto (queue_t * q, mblk_t * mp, char down)
{
register struct x75_ *x_75 = (struct x75_ *) q->q_ptr;
char dont = 0;
streamchar *origmp = mp->b_rptr;
ushort_t id;
int error = 0;
if (m_getid (mp, &id) != 0) {
mp->b_rptr = origmp;
putnext (q, mp);
return;
}
switch (id) {
default:
break;
case PROTO_OFFSET: {
long z;
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 0 || z >= 1024)
goto err;
if(!down) {
x_75->offset = z;
z += 4;
x_75->x75.offset = z;
z += 2;
freemsg(mp);
if((mp = allocb(10,BPRI_MED)) != NULL) {
m_putid(mp,PROTO_OFFSET);
m_puti(mp,z);
DATA_TYPE(mp) = M_EXPROTO;
origmp = NULL;
}
}
} break;
case PROTO_CONNECTED:
x_75->flags &= ~X75_DISC;
if (!down)
x_75->flags |= X75_CONNECT;
if (x_75->flags & X75_CONN)
break; /* Already connected */
else if (down)
x_75->flags |= X75_CONN;
else /* Upstream message.. */
dont = 1;
/*
* Now figure out what to do.
*/
if (x_75->connmode & X75CONN_UI) {
dont = 0;
} else if ((x_75->connmode & X75CONN_OUT) && !x_75->swapped) {
x75_changestate (&x_75->x75, DL_ESTABLISH_REQ, 0);
} else if ((x_75->connmode & X75CONN_IN) && x_75->swapped) {
x75_changestate (&x_75->x75, DL_ESTABLISH_REQ, 0);
} else if (x_75->connmode == 0) {
x75_changestate (&x_75->x75, DL_ESTABLISH_CONF, 0);
} else {
;
}
break;
case PROTO_LISTEN:
break;
case PROTO_INTERRUPT:
case PROTO_DISCONNECT:
x_75->flags &= ~X75_CONN;
if (!down)
x_75->flags &= ~X75_CONNECT;
if (x_75->connmode & X75CONN_UI)
dont = 0;
else if (down && !(x_75->flags & X75_DISC)) {
x75_changestate (&x_75->x75, DL_RELEASE_REQ, 0);
dont = 1;
} else {
x_75->flags |= X75_DISC;
x75_changestate (&x_75->x75, DL_RELEASE_IND, 1);
}
break;
case PROTO_INCOMING:
if (x_75->flags & X75_CONN)
break;
if (!x_75->swapped) {
uchar_t x;
/* Swap send and receive addresses. */
x_75->swapped = 1;
x = x_75->xadr_cmd;
x_75->xadr_cmd = x_75->radr_cmd;
x_75->radr_cmd = x;
x = x_75->xadr_meld;
x_75->xadr_meld = x_75->radr_meld;
x_75->radr_meld = x;
}
break;
case PROTO_OUTGOING:
if (x_75->flags & X75_CONN)
break;
if (x_75->swapped) {
uchar_t x;
/* Unswap send and receive addresses. */
x_75->swapped = 0;
x = x_75->xadr_cmd;
x_75->xadr_cmd = x_75->radr_cmd;
x_75->radr_cmd = x;
x = x_75->xadr_meld;
x_75->xadr_meld = x_75->radr_meld;
x_75->radr_meld = x;
}
break;
#if 0
case PROTO_CLOSED:
x75_changestate (&x_75->x75, DL_RELEASE_REQ, 0);
x_75->flags &= ~X75_CONN;
break;
#endif
case PROTO_MODULE:
if (strnamecmp (q, mp)) { /* Config information for me. */
ulong_t x, y;
long z;
if (mp == NULL) {
putbqf (q, mp);
return;
}
while (mp != NULL && m_getsx (mp, &id) == 0) {
switch (id) {
default:
error = -EINVAL;
goto err;
case PROTO_MODULE:
break;
case X75_SENDINTR:
x_75->flags |= X75_INTR;
break;
case X75_SENDDISC:
x_75->flags &= ~X75_INTR;
break;
case X75_IGNORESABM:
x_75->x75.ignoresabm = 1;
break;
case X75_NOIGNORESABM:
x_75->x75.ignoresabm = 0;
break;
case X75_POLL:
x_75->x75.poll = 1;
break;
case X75_NOPOLL:
x_75->x75.poll = 0;
break;
case X75_WIDEADDR:
if ((x_75->flags & X75_CONN) && (x_75->wide != 1))
goto err;
x_75->wide = 1;
break;
case X75_NOTWIDEADDR:
if ((x_75->flags & X75_CONN) && (x_75->wide != 0))
goto err;
x_75->wide = 0;
break;
case X75_WIDE:
if ((x_75->flags & X75_CONN) && (x_75->x75.wide != 1))
goto err;
x_75->x75.wide = 1;
break;
case X75_NOTWIDE:
if ((x_75->flags & X75_CONN) && (x_75->x75.wide != 0))
goto err;
x_75->x75.wide = 0;
break;
case X75_K:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 1 || z >= (x_75->x75.wide ? 128 : 8))
goto err;
if ((x_75->flags & X75_CONN) && (x_75->x75.k != z))
goto err;
x_75->x75.k = z;
break;
case X75_N1:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 1 || z >= 100)
goto err;
x_75->x75.N1 = z;
break;
case X75_T1:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 1 || z >= 100)
goto err;
x_75->x75.RUN_T1 = z;
break;
case X75_T3:
if ((error = m_geti (mp, &z)) != 0)
goto err;
if (z < 1 || z >= 1000)
goto err;
x_75->x75.RUN_T3 = z;
break;
case X75_CONCAT:
if ((error = m_geti (mp, &z)) != 0)
goto err;
x_75->concat = z;
break;
case X75_DEBUG:
if ((error = m_getx (mp, &x)) != 0)
goto err;
x_75->x75.debug = x;
break;
case X75_ADR:
if ((error = m_getx (mp, &x)) != 0)
goto err;
if ((error = m_getx (mp, &y)) != 0)
goto err;
if ((x_75->flags & X75_CONN)) {
if(x_75->swapped) {
if(x_75->xadr_cmd != y || x_75->radr_meld != y) goto err;
if(x_75->radr_cmd != x || x_75->xadr_meld != x) goto err;
} else {
if(x_75->xadr_cmd != x || x_75->radr_meld != x) goto err;
if(x_75->radr_cmd != y || x_75->xadr_meld != y) goto err;
}
goto err;
}
if (x < 1 || x >= 256)
goto err;
if (y < 1 || y >= 256)
goto err;
#if 0
if (!(x & 0x01))
goto err;
if (!(y & 0x01))
goto err;
#endif
x_75->xadr_cmd = x_75->radr_meld = x;
x_75->radr_cmd = x_75->xadr_meld = y;
x_75->swapped = 0;
break;
case X75_CONNMODE:
if ((error = m_getx (mp, &x)) != 0)
goto err;
if ( /* x < 0 || */ x > 15)
goto err;
if ((x_75->flags & X75_CONN) && (x_75->connmode != x))
goto err;
x_75->connmode = x;
}
}
if(mp != NULL) {
mp->b_rptr = origmp;
m_reply(q,mp,0);
mp = NULL;
}
}
}
if (mp != NULL) {
if (dont)
freemsg (mp);
else {
if(origmp != NULL)
mp->b_rptr = origmp;
putnext (q, mp);
}
}
return;
err:
mp->b_rptr = origmp;
m_reply (q, mp, error ? error : -EINVAL);
}
/* Streams code to write data. */
static void
x75_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);
putnext (q, mp);
break;
default:
putq (q, mp);
break;
}
return;
}
/* Streams code to scan the write queue. */
static void
x75_wsrv (queue_t * q)
{
register struct x75_ *x_75 = (struct x75_ *) q->q_ptr;
mblk_t *mp;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
x75_proto (q, mp, 1);
break;
case CASE_DATA:
{
mblk_t *mp2;
int err;
char isUI = ((x_75->connmode & X75CONN_UI) != 0);
DATA_TYPE(mp) = MSG_DATA;
/*
* Concatenate data blocks; useful if a line discipline
* insists on sening one line or even one char at a time.
*/
if(q->q_first != NULL && x_75->concat > 0) {
int sz=dsize(mp);
while(sz < x_75->concat && q->q_first != NULL && DATA_TYPE(q->q_first) == M_DATA && (sz+=dsize(q->q_first)) <= x_75->concat) {
mp2 = getq(q);
if(mp2 != NULL)
linkb(mp,mp2);
}
}
/*
* Not connecting? Forget it...
*/
if (!(x_75->flags & X75_CONNECT)) {
putbq (q, mp);
return;
}
if (x_75->x75.status == S_down && ((x_75->connmode & (X75CONN_DATA)) == X75CONN_DATA))
x75_changestate (&x_75->x75, DL_ESTABLISH_REQ, 0);
if (!x75_cansend (&x_75->x75, isUI)) {
putbq (q, mp);/* assume backenable gets called */
return;
} else if ((err = x75_send (&x_75->x75, isUI, mp)) == 0)
break;
else {
printf ("%sx75_send: Err %d\n",KERN_DEBUG , err);
putctlerr (RD (q), err);
freemsg (mp);
}
}
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;
}
}
}
x75_check_pending (&x_75->x75,0);
return;
}
/* Streams code to read data. */
static void
x75_rput (queue_t * q, mblk_t * mp)
{
struct x75_ *x_75 = (struct x75_ *) q->q_ptr;
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:
x75_changestate (&x_75->x75, PH_DEACTIVATE_IND, 1);
default:
putq (q, mp);
}
return;
}
/* Streams code to scan the read queue. */
static void
x75_rsrv (queue_t * q)
{
mblk_t *mp;
register struct x75_ *x_75 = (struct x75_ *) q->q_ptr;
int err;
int isCmd;
ushort_t adr;
x_75->didSend |= x_75->didSendNew; /* if we had a timeout */
x_75->didSendNew = 0;
while ((mp = getq (q)) != NULL) {
switch (DATA_TYPE(mp)) {
case MSG_PROTO:
x75_proto (q, mp, 0);
break;
case CASE_DATA:
/* Pass data if no connection? */
if (mp->b_rptr + 2 > mp->b_wptr || !(x_75->flags & X75_CONNECT)) {
freemsg (mp);
continue;
}
if(x_75->wide) {
adr = (uchar_t) * mp->b_rptr++ << 8;
adr |= (uchar_t) * mp->b_rptr++;
} else {
adr = (uchar_t) * mp->b_rptr++;
}
if (adr != x_75->radr_cmd && adr != x_75->radr_meld) {
if (!(x_75->flags & X75_ADRWARN)) {
x_75->flags |= X75_ADRWARN;
printf ("%sX75: got address %x, expected %x or %x\n",KERN_WARNING , adr, x_75->radr_cmd, x_75->radr_meld);
}
if(0) log_printmsg (NULL, "Bad Addr", mp, KERN_INFO);
freemsg (mp);
continue;
}
isCmd = (adr == x_75->radr_cmd);
#if 1 /* an ugly hack, useful when send and recv addresses are identical */
if (isCmd && (adr == x_75->radr_meld) && x_75->didSend
&& (dsize (mp) == (x_75->x75.wide ? 2 : 1)))
isCmd = 0;
#endif
if ((err = x75_recv (&x_75->x75, isCmd, mp)) != 0) {
printf ("%sx75_recv err %d\n",KERN_DEBUG , err);
/* putbq(q,mp); return; */
/* always freed, so forget it */
}
break;
case M_HANGUP:
case M_ERROR:
x75_changestate (&x_75->x75, PH_DEACTIVATE_IND, 0);
/* FALL THRU */
default:
if (DATA_TYPE(mp) > QPCTL || canput (q->q_next)) {
putnext (q, mp);
continue;
} else {
putbq (q, mp);
return;
}
}
}
x_75->didSend = x_75->didSendNew;
x_75->didSendNew = 0;
x75_check_pending (&x_75->x75,0);
return;
}
#ifdef MODULE
static int do_init_module(void)
{
return register_strmod(&x75info);
}
static int do_exit_module(void)
{
return unregister_strmod(&x75info);
}
#endif