1613 lines
35 KiB
C
1613 lines
35 KiB
C
/* Teile dieses Codes: */
|
|
/******************************************************************************
|
|
*
|
|
* (C)opyright 1993 BinTec Computersysteme GmbH
|
|
* All Rights Reserved
|
|
*
|
|
******************************************************************************/
|
|
|
|
/* Der Rest: */
|
|
/*
|
|
*
|
|
* ISDN driver for CAPI cards: BINTEC.
|
|
*
|
|
* Copyright (c) 1993-1995 Matthias Urlichs <urlichs@noris.de>.
|
|
*/
|
|
|
|
#ifdef linux
|
|
#define SLOW_IO_BY_JUMPING
|
|
#endif
|
|
|
|
#include "f_module.h"
|
|
#include "primitives.h"
|
|
#include "streams.h"
|
|
#include "isdn_12.h"
|
|
#include "isdn_34.h" /* CMD_CARDPROT */
|
|
#include "isdn_proto.h"
|
|
#include "smallq.h"
|
|
#include "isdn_limits.h"
|
|
#include "stream.h"
|
|
#include "streamlib.h"
|
|
#include "kernel.h"
|
|
#include "loader.h"
|
|
#include <stddef.h>
|
|
|
|
#ifdef linux
|
|
#include <asm/byteorder.h> /* htons and friends */
|
|
#include <linux/malloc.h>
|
|
#endif
|
|
|
|
#include "bri.h"
|
|
#include "bintec.h"
|
|
#include "capi.h"
|
|
|
|
#ifdef linux
|
|
#define SetSPL(x) splstr()
|
|
#else
|
|
#define SetSPL(x) spl((x))
|
|
#endif
|
|
|
|
extern void log_printmsg (void *log, const char *text, mblk_t * mp, const char*);
|
|
static void reset_card(struct _bintec *bp);
|
|
static void toss_unknown (struct _bintec *bp);
|
|
static void process_unknown (struct _bintec *bp);
|
|
|
|
void NAME(REALNAME,poll)(struct _bintec *bp);
|
|
|
|
#define MAXSEND 0x0D00 /* 3000 bytes. Split bigger blocks according to CAPI. */
|
|
|
|
|
|
static struct _bintec *binteclist = NULL;
|
|
|
|
|
|
/**** START of Bintec-provided code ******/
|
|
|
|
|
|
/*********************************************************************
|
|
bd_memchk
|
|
|
|
checks shared memory, for correct byte, word, dword access
|
|
**********************************************************************/
|
|
static int
|
|
bd_memchk(volatile void far *base, unsigned size)
|
|
{
|
|
unsigned i;
|
|
|
|
size /= sizeof(long);
|
|
|
|
{
|
|
volatile unchar far *p;
|
|
|
|
p = (volatile unchar far *) base;
|
|
for (i=0; i<256; ++i) {
|
|
p[i] = (unchar) i;
|
|
if (p[i] != i) {
|
|
printf("board not present, addr %p, %d is %d\n",p,i,p[i]);
|
|
return -EIO;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
volatile unchar far *p;
|
|
|
|
p = (volatile unchar far *) base;
|
|
for (i=0; i<8; ++i)
|
|
p[i] = (1 << i);
|
|
for (i=0; i<8; ++i) {
|
|
if (p[i] != (1 << i)) {
|
|
printf("byte access failed\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
volatile ushort far *p;
|
|
|
|
p = (volatile ushort far *) base;
|
|
for (i=0; i<16; ++i)
|
|
p[i] = (1 << i);
|
|
for (i=0; i<16; ++i) {
|
|
if (p[i] != (1 << i)) {
|
|
printf("word access failed\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
volatile ulong far *p;
|
|
|
|
p = (volatile ulong far *) base;
|
|
for (i=0; i<32; ++i)
|
|
p[i] = (1 << i);
|
|
for (i=0; i<32; ++i) {
|
|
if (p[i] != (1 << i)) {
|
|
printf("long access failed\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
volatile unchar far *b;
|
|
volatile ushort far *s;
|
|
|
|
b = (volatile unchar far *) base;
|
|
s = (volatile ushort far *) base;
|
|
|
|
b[0] = 0x12;
|
|
b[1] = 0x34;
|
|
if (s[0] != htons(0x1234)) {
|
|
printf("word order mismatch\n");
|
|
return -ENXIO;
|
|
}
|
|
}
|
|
{
|
|
ulong val;
|
|
volatile ulong far *ptr;
|
|
|
|
val = 0x31415926; /* set initial value */
|
|
ptr = base; /* get memory pointer */
|
|
for (i=0; i < size; ++i) { /* loop thru memory */
|
|
*ptr++ = val; /* store value */
|
|
val += 0x12345678; /* adjust value */
|
|
}
|
|
val = 0x31415926; /* set initial value */
|
|
ptr = base; /* get memory pointer */
|
|
for (i=0; i<size; ++i) { /* loop thru memory */
|
|
if (*ptr++ != val) { /* check value */
|
|
if(i != 0xFFF) {
|
|
printf("memory check failed: i 0x%x 0f 0x%x, val 0x%lx (wanted 0x%lx)\n",i*sizeof(long),size*sizeof(long),ptr[-1],val);
|
|
return -EIO;
|
|
} else {
|
|
printf("memory check: hmmm... at 0x%x, val 0x%lx (wanted 0x%lx)\n",i*sizeof(long),ptr[-1],val);
|
|
}
|
|
}
|
|
val += 0x12345678; /* adjust value */
|
|
}
|
|
}
|
|
return 0; /* success */
|
|
}
|
|
|
|
/*********************************************************************
|
|
bd_check
|
|
|
|
checks if board present
|
|
**********************************************************************/
|
|
static int
|
|
bd_check(struct _bintec *bp)
|
|
{
|
|
unsigned size;
|
|
int err;
|
|
|
|
if (!bp->info.memaddr) { /* board not present */
|
|
printf("BINTEC: no memory address given!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bp->base = (unchar far *) bp->info.memaddr;
|
|
|
|
bp->state = (unchar far *) (bp->base + 0x3ffc);
|
|
bp->debugtext = (unchar far *) (bp->base + 0x3ffd);
|
|
bp->rcv.p = (unchar far *) (bp->base + 0x0008);
|
|
bp->snd.p = (unchar far *) (bp->base + 0x2000);
|
|
bp->rcv.d = (icinfo_t far *) (bp->base + 0x3ff0);
|
|
bp->snd.d = (icinfo_t far *) (bp->base + 0x3ff6);
|
|
bp->ctrl = (unchar far *) (bp->base + 0x3ffe);
|
|
*bp->ctrl = 0xff;
|
|
if ((*(bp->ctrl) >> 5) != BOARD_ID_PMX) {
|
|
bp->type = *(bp->ctrl) >> 5;
|
|
size = 16 * 1024 - 4;
|
|
} else {
|
|
bp->type = BOARD_ID_PMX;
|
|
*bp->ctrl = 0;
|
|
bp->ctrl = (unchar far *) (bp->base + 0xfffe);
|
|
size = 64 * 1024 - 4;
|
|
}
|
|
*bp->ctrl = 0;
|
|
|
|
if (BOARD_TYPE(bp) != BOARD_ID_BRI
|
|
&& BOARD_TYPE(bp) != BOARD_ID_BRI4
|
|
&& BOARD_TYPE(bp) != BOARD_ID_PMX
|
|
&& BOARD_TYPE(bp) != BOARD_ID_X21
|
|
) {
|
|
printf( "BINTEC: board ctrl 0x%02x: ID unknown\n", *bp->ctrl);
|
|
return -ENXIO;
|
|
}
|
|
CTRL_RESET(bp); /* reset board */
|
|
|
|
err = bd_memchk((void *)bp->base, size); /* memory check */
|
|
if(err < 0) {
|
|
printf("%sID is %d %s\n", KERN_ERR, BOARD_TYPE(bp),((BOARD_TYPE(bp) == BOARD_ID_PMX) ? "PMX" : "BRI o.ae."));
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
bd_msgout
|
|
|
|
prints output from boards debug register
|
|
**********************************************************************/
|
|
static int
|
|
bd_msgout( struct _bintec *bp )
|
|
{
|
|
unsigned char c;
|
|
static char newline = 1;
|
|
int count = 0;
|
|
|
|
if(bp->waitmsg > 999)
|
|
return -EIO;
|
|
while ((c = *bp->debugtext) != 0) {
|
|
*bp->debugtext = 0; /* clear byte */
|
|
if(!newline || (c != '\n')) { /* do not print empty lines */
|
|
if (newline) printf("%sBINTEC: debug: ",KERN_DEBUG);
|
|
newline = (c == '\n');
|
|
printf("%c", c); /* display byte */
|
|
}
|
|
if(++count > 1000) {
|
|
printf("%sBINTEC: The board seems to be in an infinite loop.\n",KERN_ERR);
|
|
if(bp->registered)
|
|
isdn2_new_state(&bp->card,2);
|
|
CTRL_RESET(bp);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if (*bp->state & 0x80) { /* board failed */
|
|
CTRL_RESET(bp); /* reset board */
|
|
printf("%sBINTEC: msgout: board failed???\n",KERN_WARNING);
|
|
if(bp->registered)
|
|
isdn2_new_state(&bp->card,2);
|
|
bp->waitmsg++;
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
bd_init
|
|
|
|
initialize structures to point to boards memory
|
|
**********************************************************************/
|
|
static int
|
|
bd_init( struct _bintec *bp )
|
|
{
|
|
if (!bp->info.memaddr) {
|
|
printf("bd_init: board addr not present\n");
|
|
return -EFAULT;
|
|
}
|
|
bp->base = (unchar far *) bp->info.memaddr;
|
|
bp->ctrl = (unchar far *) (bp->base + 0x3ffe);
|
|
*bp->ctrl = 0xff;
|
|
if ((*(bp->ctrl) >> 5) != BOARD_ID_PMX) {
|
|
bp->type = *(bp->ctrl) >> 5;
|
|
bp->ctrl = (unchar far *) (bp->base + 0x3ffe);
|
|
bp->state = (unchar far *) (bp->base + 0x3ffc);
|
|
bp->debugtext = (unchar far *) (bp->base + 0x3ffd);
|
|
bp->ctrl = (unchar far *) (bp->base + 0x3ffe);
|
|
bp->rcv.p = (unchar far *) (bp->base + 0x0008);
|
|
bp->snd.p = (unchar far *) (bp->base + 0x2000);
|
|
bp->rcv.d = (icinfo_t far *) (bp->base + 0x3ff0);
|
|
bp->snd.d = (icinfo_t far *) (bp->base + 0x3ff6);
|
|
} else {
|
|
bp->type = BOARD_ID_PMX;
|
|
bp->ctrl = (unchar far *) (bp->base + 0xfffe);
|
|
bp->state = (unchar far *) (bp->base + 0x3ffc);
|
|
bp->debugtext = (unchar far *) (bp->base + 0x3ffd);
|
|
bp->rcv.p = (unchar far *) (bp->base + 0x0008);
|
|
bp->snd.p = (unchar far *) (bp->base + 0x2000);
|
|
bp->rcv.d = (icinfo_t far *) (bp->base + 0x3ff0);
|
|
bp->snd.d = (icinfo_t far *) (bp->base + 0x3ff6);
|
|
}
|
|
switch(bp->type) {
|
|
case BOARD_ID_PMX:
|
|
bp->card.nr_chans = 30;
|
|
bp->card.nr_dchans = 1;
|
|
break;
|
|
case BOARD_ID_BRI:
|
|
bp->card.nr_chans = 2;
|
|
bp->card.nr_dchans = 1;
|
|
break;
|
|
case BOARD_ID_BRI4:
|
|
bp->card.nr_chans = 8;
|
|
bp->card.nr_dchans = 4;
|
|
break;
|
|
default:
|
|
printf("BINTEC: unknown board ID %d\n",bp->type);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/**** END of Bintec-provided code *****/
|
|
|
|
static void
|
|
reset_card(struct _bintec *bp)
|
|
{
|
|
int i = 0;
|
|
long s = splstr();
|
|
|
|
*bp->state = 0; /* turn off interrupt generation */
|
|
*bp->ctrl = 0; /* halt and reset CPU */
|
|
|
|
for(i=0; i <= MAX_B; i++) {
|
|
S_flush(&bp->chan[i].q_in);
|
|
S_flush(&bp->chan[i].q_out);
|
|
bp->chan[i].mode = M_OFF;
|
|
if(bp->chan[i].in_more != NULL) {
|
|
freemsg(bp->chan[i].in_more);
|
|
bp->chan[i].in_more = NULL;
|
|
}
|
|
}
|
|
S_flush(&bp->q_unknown);
|
|
if(bp->unknown_timer) {
|
|
bp->unknown_timer = 0;
|
|
#ifdef NEW_TIMEOUT
|
|
untimeout(bp->timer_toss_unknown);
|
|
#else
|
|
untimeout(toss_unknown, bp);
|
|
#endif
|
|
}
|
|
|
|
bp->sndoffset = -1;
|
|
bp->rcvoffset = -1;
|
|
bp->msgnr = 0;
|
|
bp->waitmsg = 999;
|
|
splx(s);
|
|
}
|
|
|
|
inline static void
|
|
setctl(struct _bintec *bp, int bit)
|
|
{
|
|
bp->cflag |= (1<<bit);
|
|
*bp->ctrl = bp->cflag;
|
|
}
|
|
|
|
inline static void
|
|
clrctl(struct _bintec *bp, int bit)
|
|
{
|
|
bp->cflag &= ~(1<<bit);
|
|
*bp->ctrl = bp->cflag;
|
|
}
|
|
|
|
inline static void
|
|
put8(struct _bintec *bp, u8 byt)
|
|
{
|
|
bp->snd.p[bp->sndoffset] = byt;
|
|
bp->sndoffset++;
|
|
if(bp->sndoffset == bp->sndbufsize)
|
|
bp->sndoffset = 0;
|
|
}
|
|
|
|
inline static void
|
|
put16(struct _bintec *bp, u16 byt)
|
|
{
|
|
put8(bp, byt & 0xFF);
|
|
put8(bp, byt >> 8);
|
|
}
|
|
|
|
inline static void
|
|
put32(struct _bintec *bp, u32 byt)
|
|
{
|
|
put8(bp, byt & 0xFF);
|
|
put8(bp, byt >> 8);
|
|
put8(bp, byt >> 16);
|
|
put8(bp, byt >> 24);
|
|
}
|
|
|
|
inline static void
|
|
putmb(struct _bintec *bp, mblk_t *mb, int len)
|
|
{
|
|
while(mb != NULL && len > 0) {
|
|
streamchar *pos = mb->b_rptr;
|
|
while(pos < mb->b_wptr) {
|
|
put8(bp,*pos++);
|
|
if(--len == 0)
|
|
break;
|
|
}
|
|
mb = mb->b_cont;
|
|
}
|
|
}
|
|
|
|
static int
|
|
putstart(struct _bintec *bp, int len)
|
|
{
|
|
int wi = ntohs(bp->snd.d->wi); /* get write index */
|
|
int ri = ntohs(bp->snd.d->ri); /* get read index */
|
|
int sz = ntohs(bp->snd.d->sz); /* get buffer size */
|
|
int space = (ri - wi + sz - 1) % sz; /* calc size */
|
|
int err;
|
|
unsigned long ms = SetSPL(bp->ipl);
|
|
|
|
if((err = bd_msgout(bp)) < 0) {
|
|
splx(ms);
|
|
return err;
|
|
}
|
|
if ((sz < 2) || (wi >= sz) || (ri >= sz)) {
|
|
printf( "%sBINTEC error: write: ri %d, wi %d, sz %d\n",KERN_ERR,ri,wi,sz);
|
|
bp->sndoffset = sz;
|
|
splx(ms);
|
|
return -EIO;
|
|
}
|
|
if(len > sz-3) {
|
|
printf("%sBINTEC error: write len %d > sz-3 %d\n",KERN_ERR,len,sz-3);
|
|
bp->sndoffset = sz;
|
|
splx(ms);
|
|
return -ENXIO;
|
|
}
|
|
if(bp->sndoffset != -1) {
|
|
if(bp->sndoffset != bp->sndbufsize)
|
|
DEBUG(info) printf("%sBINTEC: busy sending\n",KERN_DEBUG);
|
|
splx(ms);
|
|
return -EAGAIN; /* busy */
|
|
}
|
|
if(len > space-2) {
|
|
DEBUG(info) printf("%sBINTEC: buffer full, %d < %d\n",KERN_DEBUG,len,space-2);
|
|
splx(ms);
|
|
return -EAGAIN;
|
|
}
|
|
if(0)DEBUG(capiout) printf("%sBINTEC write %d bytes at %d, free %d\n",KERN_DEBUG,len,wi,space);
|
|
bp->sndoffset = wi;
|
|
bp->sndbufsize = sz;
|
|
bp->sndend = (wi+len+2) % sz;
|
|
splx(ms);
|
|
put16(bp, htons(len));
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
putend(struct _bintec *bp)
|
|
{
|
|
int ms = SetSPL(bp->ipl);
|
|
int err;
|
|
|
|
if((err = bd_msgout(bp)) < 0) {
|
|
splx(ms);
|
|
return err;
|
|
}
|
|
if(bp->sndoffset != bp->sndend) {
|
|
DEBUG(info) printf("%sBINTEC error: send end %d, should be %d\n",KERN_ERR,bp->sndoffset,bp->sndend);
|
|
bp->sndoffset = bp->sndbufsize;
|
|
return -EIO;
|
|
}
|
|
bp->snd.d->wi = htons(bp->sndoffset);
|
|
if(bp->type != BOARD_ID_PMX) {
|
|
setctl(bp,4);
|
|
clrctl(bp,4);
|
|
}
|
|
bp->sndoffset = -1;
|
|
splx(ms);
|
|
return 0;
|
|
}
|
|
|
|
|
|
inline static u8
|
|
get8(struct _bintec *bp)
|
|
{
|
|
u8 byt;
|
|
byt = bp->rcv.p[bp->rcvoffset];
|
|
bp->rcvoffset++;
|
|
if(bp->rcvoffset == bp->rcvbufsize)
|
|
bp->rcvoffset = 0;
|
|
return byt;
|
|
}
|
|
|
|
inline static void
|
|
getflush(struct _bintec *bp, int len)
|
|
{
|
|
if(len > 0) {
|
|
bp->rcvoffset += len;
|
|
if (bp->rcvoffset >= bp->rcvbufsize)
|
|
bp->rcvoffset -= bp->rcvbufsize;
|
|
} else {
|
|
bp->rcvoffset = bp->rcvend;
|
|
}
|
|
}
|
|
|
|
inline static u16
|
|
get16(struct _bintec *bp)
|
|
{
|
|
u16 byt;
|
|
byt = get8(bp);
|
|
byt |= get8(bp) << 8;
|
|
return byt;
|
|
}
|
|
|
|
inline static u32
|
|
get32(struct _bintec *bp)
|
|
{
|
|
u32 byt;
|
|
byt = get8(bp);
|
|
byt |= get8(bp) << 8;
|
|
byt |= get8(bp) << 16;
|
|
byt |= get8(bp) << 24;
|
|
return byt;
|
|
}
|
|
|
|
inline static void
|
|
getmb(struct _bintec *bp, mblk_t *mb, int len)
|
|
{
|
|
while(len--)
|
|
*mb->b_wptr++ = get8(bp);
|
|
}
|
|
|
|
static int
|
|
getstart(struct _bintec *bp)
|
|
{
|
|
int wi = ntohs(bp->rcv.d->wi); /* get write index */
|
|
int ri = ntohs(bp->rcv.d->ri); /* get read index */
|
|
int sz = ntohs(bp->rcv.d->sz); /* get buffer size */
|
|
int space = (wi - ri + sz) % sz; /* calc size */
|
|
int err, len;
|
|
unsigned long ms = SetSPL(bp->ipl);
|
|
|
|
if((err = bd_msgout(bp)) < 0) {
|
|
splx(ms);
|
|
return err;
|
|
}
|
|
if(bp->rcvoffset != -1) {
|
|
if(bp->rcvoffset != sz)
|
|
DEBUG(info) printf("%sBINTEC: read: receiver busy\n",KERN_DEBUG);
|
|
return -EAGAIN; /* busy */
|
|
}
|
|
|
|
if (sz < 2) { /* invalid buffer size */
|
|
printf( "%sBINTEC error: read: invalid buffer size %d\n",KERN_ERR, sz);
|
|
bp->rcvoffset = sz;
|
|
splx(ms);
|
|
return -EIO;
|
|
}
|
|
if(space == 0) {
|
|
splx(ms);
|
|
/* no message to read */
|
|
return -ENODATA;
|
|
}
|
|
if (space > sz || space < 2) { /* we have a problem */
|
|
printf("%sBINTEC error: read: invalid space %d\n",KERN_ERR, space);
|
|
bp->rcvoffset = sz;
|
|
splx(ms);
|
|
return -EFAULT;
|
|
}
|
|
|
|
bp->rcvoffset = ri;
|
|
bp->rcvbufsize = sz;
|
|
splx(ms);
|
|
|
|
len = ntohs(get16(bp));
|
|
if(len > space-2) {
|
|
printf("%sBINTEC error: read: len %d > space-2 %d\n",KERN_ERR, len,space-2);
|
|
bp->rcv.d->ri = bp->rcv.d->wi;
|
|
bp->rcvoffset = sz;
|
|
return -EFAULT;
|
|
}
|
|
if(0)DEBUG(capi) printf("%sBINTEC: reading %d bytes at %d\n",KERN_DEBUG,len,ri);
|
|
bp->rcvend = (ri + len + 2) % sz;
|
|
return len;
|
|
}
|
|
|
|
static int
|
|
getend(struct _bintec *bp)
|
|
{
|
|
if(bp->rcvoffset != bp->rcvend) {
|
|
printf("%sBINTEC error: read: now at %d instead of %d!\n",KERN_ERR, bp->rcvoffset,bp->rcvend);
|
|
bp->rcvoffset = bp->rcvbufsize;
|
|
return -EIO;
|
|
}
|
|
bp->rcv.d->ri = htons(bp->rcvoffset); /* update read index */
|
|
bp->rcvoffset = -1;
|
|
if(bp->type != BOARD_ID_PMX) {
|
|
setctl(bp,4);
|
|
clrctl(bp,4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
init1 (struct _bintec *bp)
|
|
{
|
|
int err;
|
|
err = bd_init(bp);
|
|
if(err < 0)
|
|
return err;
|
|
err = bd_check(bp);
|
|
if(err < 0)
|
|
return err;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
boot(struct _isdn1_card * card, int step, int offset, mblk_t *data)
|
|
{
|
|
struct _bintec * bp = (struct _bintec *) card;
|
|
int err, i;
|
|
mblk_t *origdata = data;
|
|
bp->polled = 99;
|
|
|
|
if ((err = bd_msgout(bp)) < 0) {
|
|
bp->polled = 0;
|
|
return err;
|
|
}
|
|
DEBUG(info) if(offset == 0 || msgdsize(data) < 100) printf("%sBINTEC boot: step %d offset %d bytes %d for type %d\n",KERN_DEBUG,step,offset,msgdsize(data),bp->type);
|
|
switch(step) {
|
|
case 1:
|
|
if(offset == 0) {
|
|
volatile uchar_t *ptr;
|
|
{
|
|
mblk_t *mp;
|
|
|
|
if(msgdsize(data) < 2) {
|
|
err = -ENXIO;
|
|
break;
|
|
}
|
|
mp = pullupm(data,2);
|
|
if(mp == NULL) {
|
|
err = -ENOMEM;
|
|
goto Exi;
|
|
}
|
|
data = mp;
|
|
}
|
|
|
|
reset_card(bp);
|
|
|
|
err = bd_check(bp);
|
|
if(err < 0)
|
|
goto Exi;
|
|
|
|
bzero((void *)bp->base, 0x3ffe); /* clear shared memory */
|
|
bp->rcv.d->sz = htons(0x1ff8); /* set size */
|
|
bp->snd.d->sz = htons(0x1ff0); /* set size */
|
|
ptr = bp->base; /* get pointer to reset vector */
|
|
*((u32 *)ptr)++ = htonl(0x2000); /* SP */
|
|
*((u32 *)ptr)++ = htonl(0x0200); /* PC */
|
|
for (i=0; i<504; ++i) { /* clear vector table */
|
|
*ptr++ = 0xff;
|
|
}
|
|
|
|
data->b_rptr[0] = 0x70;
|
|
data->b_rptr[1] = bp->type;
|
|
}
|
|
if(msgdsize(data) != 0) {
|
|
if(0)DEBUG(capiout) {
|
|
printf("%sBINTEC: write %d bytes to card at %x",KERN_DEBUG, msgdsize(data),0x200+offset);
|
|
log_printmsg(NULL,": ",data,">>");
|
|
}
|
|
while(data != NULL) {
|
|
bcopy(data->b_rptr,(char *)(bp->info.memaddr) +0x200 + offset,data->b_wptr-data->b_rptr);
|
|
offset += data->b_wptr-data->b_rptr;
|
|
|
|
data = data->b_cont;
|
|
}
|
|
} else {
|
|
if (BOARD_TYPE(bp) != BOARD_ID_PMX) {
|
|
CTRL_SET(bp, 0x0e);
|
|
} else {
|
|
CTRL_SET(bp, 0x03);
|
|
}
|
|
DEBUG(info) printf("%sBINTEC: Boot phase 1 complete; card started\n",KERN_DEBUG);
|
|
err = bd_msgout(bp);
|
|
}
|
|
break;
|
|
case 2:
|
|
if(offset == 0) {
|
|
{
|
|
mblk_t *mp;
|
|
|
|
if(msgdsize(data) < 2) {
|
|
err = -ENXIO;
|
|
break;
|
|
}
|
|
mp = pullupm(data,2);
|
|
if(mp == NULL) {
|
|
err = -ENOMEM;
|
|
goto Exi;
|
|
}
|
|
data = mp;
|
|
}
|
|
|
|
data->b_rptr[0] = 0x70;
|
|
data->b_rptr[1] = bp->type;
|
|
}
|
|
{
|
|
int len = msgdsize(data);
|
|
int err = putstart(bp,len);
|
|
|
|
if(0)DEBUG(capiout) {
|
|
printf("%sBINTEC: write %d bytes to card",KERN_DEBUG,len);
|
|
log_printmsg(NULL,": ",data,">>");
|
|
}
|
|
|
|
if(err < 0)
|
|
goto Exi;
|
|
putmb(bp,data,len);
|
|
err = putend(bp);
|
|
if(err < 0)
|
|
goto Exi;
|
|
}
|
|
break;
|
|
case 3:
|
|
if(msgdsize(data) != 0) {
|
|
err = -EIO;
|
|
break;
|
|
}
|
|
err = -EAGAIN;
|
|
if(bp->waitmsg == 0) {
|
|
#if 0
|
|
if(jiffies & 0x7)
|
|
err = -EAGAIN;
|
|
else
|
|
#endif
|
|
err = 0;
|
|
} else if(bp->waitmsg > 2) {
|
|
bp->waitmsg = 2;
|
|
if(bp->info.irq != 0) {
|
|
if(bp->type != BOARD_ID_PMX)
|
|
setctl(bp,0); /* enable interrupts */
|
|
*bp->state = 3; /* ... send and receive */
|
|
}
|
|
}
|
|
break; /* boot is finished */
|
|
default:
|
|
err = -EFAULT;
|
|
}
|
|
Exi:
|
|
if(err == 0)
|
|
freemsg(origdata);
|
|
bp->polled = 0;
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
mode (struct _isdn1_card * card, short channel, char mode, char listen)
|
|
{
|
|
struct _bintec * bp = (struct _bintec *) card;
|
|
unsigned long ms = SetSPL(bp->ipl);
|
|
int err = 0;
|
|
|
|
switch(channel) {
|
|
case 0:
|
|
if(mode == M_OFF) {
|
|
reset_card(bp);
|
|
splx(ms);
|
|
return 0;
|
|
}
|
|
splx(ms);
|
|
return -ENXIO;
|
|
default:
|
|
if(channel > 0 && channel <= bp->card.nr_chans) {
|
|
if(0)DEBUG(info) printf("%sBINTEC: Chan%d %s<%d>%s\n",KERN_INFO ,channel,mode?"up":"down",mode,listen?" listen":"");
|
|
bp->chan[channel].mode = mode;
|
|
if(mode == M_OFF) {
|
|
bp->chan[channel].appID = 0;
|
|
bp->chan[channel].PLCI = 0;
|
|
bp->chan[channel].NCCI = 0;
|
|
}
|
|
splx(ms);
|
|
return 0;
|
|
} else {
|
|
printf("%sBintec badChan %d\n",KERN_WARNING ,channel);
|
|
splx(ms);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
NAME(REALNAME,poll)(bp);
|
|
splx(ms);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
prot (struct _isdn1_card * card, short channel, mblk_t * mp, int flags)
|
|
{
|
|
struct _bintec * bp = (struct _bintec *)card;
|
|
streamchar *origmp = mp->b_rptr;
|
|
ushort_t id;
|
|
int err = -ERESTART;
|
|
hdlc_buf chan = &bp->chan[channel];
|
|
|
|
if(0)DEBUG(info) printf("%sBintecProt chan %d flags 0%o\n",KERN_DEBUG,channel,flags);
|
|
|
|
if(!(flags & ~CHP_FROMSTACK)) { /* Nothing else set? */
|
|
if ((err = m_getid (mp, &id)) != 0)
|
|
goto err;
|
|
if(flags & CHP_FROMSTACK) { /* from the application */
|
|
switch(id) {
|
|
default:
|
|
err = -ERESTART;
|
|
break;
|
|
case PROTO_OFFSET:
|
|
{
|
|
long z;
|
|
if ((err = m_geti (mp, &z)) != 0)
|
|
goto err;
|
|
if (z < 0 || z >= 1024) {
|
|
err = -EINVAL;
|
|
goto err;
|
|
}
|
|
if(bp->maxoffset < z)
|
|
bp->maxoffset = z;
|
|
bp->chan[channel].offset = z;
|
|
}
|
|
err = -ERESTART;
|
|
break;
|
|
}
|
|
} else { /* from the system */
|
|
switch (id) {
|
|
default:
|
|
err = -ERESTART;
|
|
break;
|
|
case CMD_CARDPROT:
|
|
{
|
|
while(m_getsx(mp,&id) == 0) {
|
|
switch(id) {
|
|
case ARG_ASSOC:
|
|
{ /* appID plci ncci */
|
|
long a,n,p;
|
|
if((err = m_geti(mp,&a)) < 0)
|
|
break;
|
|
if((err = m_geti(mp,&p)) < 0)
|
|
break;
|
|
if((err = m_geti(mp,&n)) < 0)
|
|
break;
|
|
chan->appID = a;
|
|
chan->PLCI = p;
|
|
chan->NCCI = n;
|
|
chan->waitflow = 0;
|
|
DEBUG(capi) printf("%sBINTEC: chan %d: assoc %04lx %04lx %04lx\n",KERN_DEBUG,channel,a,p,n);
|
|
process_unknown(bp);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(err == 0) {
|
|
mblk_t *mz = make_reply(err);
|
|
if(mz != NULL) {
|
|
mp->b_rptr = origmp;
|
|
DATA_TYPE(mp) = DATA_TYPE(mz);
|
|
linkb(mz,mp);
|
|
if((err = isdn2_chprot(card,channel,mz,flags|CHP_FROMSTACK)) < 0)
|
|
freeb(mz);
|
|
else
|
|
mp = NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(err == 0) {
|
|
if(mp != NULL)
|
|
freemsg(mp);
|
|
return 0;
|
|
}
|
|
err:
|
|
mp->b_rptr = origmp;
|
|
return ((err != -ERESTART) ? err : isdn2_chprot(card,channel,mp,flags));
|
|
}
|
|
|
|
/*
|
|
* Check if buffer space is available
|
|
*/
|
|
static int
|
|
candata (struct _isdn1_card * card, short channel)
|
|
{
|
|
struct _bintec * bp = (struct _bintec *)card;
|
|
int ret;
|
|
DEBUG(info) if(channel == 0)printk("%sBintec: candata finds %d on chan %d polled %d\n",KERN_DEBUG,bp->chan[channel].q_out.nblocks,channel,bp->polled);
|
|
if(bp->waitmsg)
|
|
return 0;
|
|
ret = (bp->chan[channel].q_out.nblocks < 8);
|
|
DEBUG(info)if(bp->polled) bp->polled--;
|
|
if(!ret) { static int jif = 0;
|
|
if(jif < jiffies-HZ) printk("%scandata blocked for %d\n",KERN_DEBUG,channel);
|
|
jif = jiffies;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Enqueue the data.
|
|
*/
|
|
static int
|
|
data (struct _isdn1_card * card, short channel, mblk_t * data)
|
|
{
|
|
struct _bintec * bp = (struct _bintec *)card;
|
|
DEBUG(info) if(channel == 0)printk("%sBintec: data finds %d on chan %d polled %d\n",KERN_DEBUG,bp->chan[channel].q_out.nblocks,channel,bp->polled);
|
|
if(bp->waitmsg)
|
|
return -ENXIO;
|
|
S_enqueue(&bp->chan[channel].q_out, data);
|
|
NAME(REALNAME,poll)((struct _bintec *) card);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Flush the send queue.
|
|
*/
|
|
static int
|
|
flush (struct _isdn1_card * card, short channel)
|
|
{
|
|
struct _bintec * bp = (struct _bintec *)card;
|
|
if(channel > 0)
|
|
S_flush(&bp->chan[channel].q_out);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static inline int
|
|
recvone(struct _bintec *bp, int thechan)
|
|
{
|
|
mblk_t *mb;
|
|
struct _hdlc_buf *chan = &bp->chan[thechan];
|
|
int err;
|
|
|
|
if(chan->q_in.nblocks == 0)
|
|
return -ENOENT;
|
|
if(!isdn2_canrecv(&bp->card,thechan))
|
|
return -EAGAIN;
|
|
if((mb = S_dequeue(&chan->q_in)) == NULL)
|
|
return -ENXIO;
|
|
if((err = isdn2_recv(&bp->card,thechan,mb)) < 0) {
|
|
S_requeue(&chan->q_in,mb);
|
|
return err;
|
|
} else
|
|
DEBUG(info) if((thechan == 0) && (chan->q_in.nblocks == 0)) printf("%sBINTEC: upqueue cleared\n",KERN_INFO);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
sendone(struct _bintec *bp, int thechan)
|
|
{
|
|
int len, hlen, err;
|
|
struct _hdlc_buf *chan = &bp->chan[thechan];
|
|
mblk_t *mb;
|
|
|
|
if(thechan != 0 && chan->waitflow > 5)
|
|
return 0; /* XXX -EAGAIN ? */
|
|
mb = S_dequeue(&chan->q_out);
|
|
if(mb == NULL)
|
|
return 0;
|
|
else if(chan->q_out.nblocks == 4)
|
|
isdn2_backenable(&bp->card,thechan);
|
|
|
|
len = msgdsize(mb);
|
|
|
|
err = bd_msgout(bp);
|
|
if(err < 0) {
|
|
freemsg(mb);
|
|
return err;
|
|
}
|
|
if(thechan != 0)
|
|
hlen = 21;
|
|
else
|
|
hlen = 2;
|
|
DEBUG(capiout) {
|
|
if(thechan == 0) {
|
|
struct CAPI_every_header *capi;
|
|
capi = ((typeof(capi))mb->b_rptr);
|
|
if(capi->PRIM_type == CAPI_ALIVE_RESP)
|
|
goto foo;
|
|
}
|
|
printf("%sBINTEC: Send %d bytes on chan %d",KERN_DEBUG,len,thechan);
|
|
if(thechan == 0)
|
|
log_printmsg(NULL,": ",mb,">>");
|
|
else
|
|
printf("\n");
|
|
foo:;
|
|
}
|
|
err = putstart(bp,hlen + ((len > MAXSEND) ? MAXSEND : len)); /* auto-puts the length */
|
|
if(err >= 0) {
|
|
put16(bp, htons(1));
|
|
if(thechan != 0) { /* data */
|
|
put16(bp, 19); /* msg len, including header */
|
|
put16(bp, chan->appID); /* AppID */
|
|
put16(bp, CAPI_DATAB3_REQ);
|
|
put16(bp, bp->msgnr++ & 0x3FFF);
|
|
put16(bp, chan->NCCI);
|
|
put16(bp, (len > MAXSEND) ? MAXSEND : len);
|
|
put32(bp, 0);
|
|
put8 (bp, ++chan->dblock);
|
|
put16(bp, (len > MAXSEND) ? CAPI_MORE : 0); /* flags */
|
|
}
|
|
putmb(bp,mb,MAXSEND);
|
|
err = putend(bp);
|
|
if(err < 0)
|
|
S_requeue(&chan->q_out,mb);
|
|
else {
|
|
if(len > MAXSEND) {
|
|
adjmsg(mb,len-MAXSEND);
|
|
mb = pullupm(mb,0);
|
|
S_requeue(&chan->q_out,mb);
|
|
} else
|
|
freemsg(mb);
|
|
if(thechan != 0)
|
|
chan->waitflow++;
|
|
if(BOARD_TYPE(bp) != BOARD_ID_PMX) {
|
|
*bp->ctrl = bp->cflag | 0x10;
|
|
*bp->ctrl = bp->cflag;
|
|
} else
|
|
CTRL_SET(bp,1);
|
|
}
|
|
} else if(err == -EAGAIN) {
|
|
S_requeue(&chan->q_out,mb);
|
|
} else {
|
|
freemsg(mb);
|
|
/* TODO: Kill the board? */
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
static int
|
|
pushone(struct _bintec *bp, int thechan)
|
|
{
|
|
int err;
|
|
struct _hdlc_buf *chan = &bp->chan[thechan];
|
|
mblk_t *mb;
|
|
|
|
if(!isdn2_canrecv(&bp->card,thechan)) {
|
|
return -EAGAIN;
|
|
}
|
|
mb = S_dequeue(&chan->q_in);
|
|
if(mb == NULL)
|
|
return -ENOENT;
|
|
if(mb->b_cont == NULL) {
|
|
freeb(mb);
|
|
return -EINVAL;
|
|
}
|
|
{
|
|
struct CAPI_every_header *capi, *capi2;
|
|
struct CAPI_datab3_ind *ci;
|
|
struct CAPI_datab3_resp *cr;
|
|
mblk_t *mr = allocb(sizeof(*capi)+sizeof(*cr),BPRI_HI);
|
|
if(mr != NULL) {
|
|
capi = ((typeof(capi ))mb->b_rptr);
|
|
ci = (typeof(ci))(capi+1);
|
|
capi2 = ((typeof(capi2))mr->b_wptr)++;
|
|
cr = ((typeof(cr))mr->b_wptr)++;
|
|
capi2->len = mr->b_wptr-mr->b_rptr;
|
|
capi2->appl = capi->appl;
|
|
capi2->PRIM_type = CAPI_DATAB3_RESP;
|
|
capi2->messid = capi->messid;
|
|
cr->ncci = ci->ncci;
|
|
cr->blknum = ci->blknum;
|
|
|
|
if(ci->flags & CAPI_MORE) {
|
|
if(chan->in_more == NULL)
|
|
chan->in_more = mb->b_cont;
|
|
else
|
|
linkb(chan->in_more,mb->b_cont);
|
|
err = 0;
|
|
} else {
|
|
if(chan->in_more != NULL) {
|
|
linkb(chan->in_more,mb->b_cont);
|
|
mb->b_cont = chan->in_more;
|
|
chan->in_more = NULL;
|
|
}
|
|
err = isdn2_recv(&bp->card,thechan,mb->b_cont);
|
|
}
|
|
if(err == 0) {
|
|
freeb(mb);
|
|
S_enqueue(&bp->chan[0].q_out,mr);
|
|
sendone(bp,0);
|
|
} else if(err == -EAGAIN) {
|
|
S_requeue(&chan->q_in, mb);
|
|
freemsg(mr);
|
|
} else {
|
|
/* XXX TODO: send a toss message, or close the channel */
|
|
freemsg(mb);
|
|
freemsg(mr);
|
|
}
|
|
} else {
|
|
S_requeue(&chan->q_in, mb);
|
|
err = -ENOMEM;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
postproc(struct _bintec *bp, mblk_t *mb, int ch)
|
|
/* ch: pre-found channel; -1: not found; 0: unknown */
|
|
{
|
|
int err;
|
|
struct CAPI_every_header *capi;
|
|
|
|
capi = (typeof(capi))mb->b_rptr;
|
|
|
|
DEBUG(capi) if(capi->PRIM_type != CAPI_ALIVE_IND) log_printmsg(NULL,"BINTEC read packet:",mb,"> ");
|
|
|
|
switch(capi->PRIM_type) {
|
|
case CAPI_ALIVE_IND:
|
|
err = 0;
|
|
#if 0
|
|
if((bp->chan[0].q_out.nblocks > 10) || !isdn2_canrecv(&bp->card,0)) {
|
|
DEBUG(info)printf("%sBINTEC: keepalive on upqueue\n",KERN_INFO);
|
|
goto def;
|
|
}
|
|
#endif
|
|
capi->PRIM_type = CAPI_ALIVE_RESP;
|
|
S_enqueue(&bp->chan[0].q_out,mb);
|
|
sendone(bp,0);
|
|
if(0)DEBUG(info)printf(".L.");
|
|
break;
|
|
case CAPI_DATAB3_IND:
|
|
{
|
|
struct _hdlc_buf *chan = &bp->chan[1];
|
|
if(ch == 0) {
|
|
struct CAPI_datab3_ind *c2;
|
|
int i;
|
|
|
|
ch = -1;
|
|
c2 = (typeof(c2))(capi+1);
|
|
for(i=1;i<bp->card.nr_chans;i++) {
|
|
if(chan->appID == capi->appl && chan->NCCI == c2->ncci) {
|
|
ch = i;
|
|
break;
|
|
}
|
|
chan++;
|
|
}
|
|
if(ch == -1)
|
|
return -ERESTART;
|
|
} else if(ch > 0)
|
|
chan = &bp->chan[ch];
|
|
else
|
|
return -ERESTART;
|
|
if(chan->q_in.nblocks < 200)
|
|
S_enqueue(&chan->q_in,mb);
|
|
else {
|
|
freemsg(mb);
|
|
/* TODO: Throw the connection away. Right now we kill the
|
|
card instead. */
|
|
printf("%sBINTEC error: Incoming queue overflow, killed!\n",KERN_ERR);
|
|
isdn2_chstate(&bp->card,MDL_ERROR_IND,0);
|
|
}
|
|
pushone(bp,ch);
|
|
}
|
|
break;
|
|
case CAPI_DATAB3_CONF:
|
|
{
|
|
struct _hdlc_buf *chan = &bp->chan[1];
|
|
if(ch == 0) {
|
|
struct CAPI_datab3_conf *c2;
|
|
int i;
|
|
|
|
ch = -1;
|
|
c2 = (typeof(c2))(capi+1);
|
|
for(i=1;i<bp->card.nr_chans;i++) {
|
|
if(chan->appID == capi->appl && chan->NCCI == c2->ncci) {
|
|
ch = i;
|
|
break;
|
|
}
|
|
chan++;
|
|
}
|
|
if(ch == -1)
|
|
return -ERESTART;
|
|
} else if(ch > 0)
|
|
chan = &bp->chan[ch];
|
|
else
|
|
return -ERESTART;
|
|
if(chan->waitflow)
|
|
chan->waitflow--;
|
|
freemsg(mb);
|
|
sendone(bp,ch);
|
|
}
|
|
break;
|
|
default:
|
|
def:
|
|
if(!isdn2_canrecv(&bp->card,0))
|
|
return -EAGAIN;
|
|
else if((err = isdn2_recv(&bp->card,0,mb)) < 0) {
|
|
printf("%sBINTEC read error: err %d\n",KERN_WARNING,err);
|
|
return err;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
toss_unknown (struct _bintec *bp)
|
|
{
|
|
mblk_t *mb;
|
|
|
|
bp->unknown_timer = 0;
|
|
while((mb = S_dequeue(&bp->q_unknown)) != NULL)
|
|
freemsg(mb);
|
|
}
|
|
|
|
static void
|
|
process_unknown (struct _bintec *bp)
|
|
{
|
|
long s;
|
|
int do_timer = 0;
|
|
if(bp->q_unknown.nblocks == 0)
|
|
return;
|
|
|
|
s = splstr();
|
|
if(bp->unknown_timer) {
|
|
bp->unknown_timer = 0;
|
|
#ifdef NEW_TIMEOUT
|
|
untimeout(bp->timer_toss_unknown);
|
|
#else
|
|
untimeout(toss_unknown, bp);
|
|
#endif
|
|
}
|
|
splx(s);
|
|
|
|
{
|
|
struct _smallq sq = bp->q_unknown;
|
|
mblk_t *mb;
|
|
|
|
bzero(&bp->q_unknown,sizeof(bp->q_unknown));
|
|
while((mb = S_dequeue(&sq)) != NULL) {
|
|
switch(postproc(bp,mb,0)) {
|
|
case 0:
|
|
break;
|
|
default:
|
|
{
|
|
struct CAPI_every_header *capi, *capi2;
|
|
struct CAPI_datab3_ind *ci;
|
|
struct CAPI_datab3_resp *cr;
|
|
mblk_t *mr = allocb(sizeof(*capi)+sizeof(*cr),BPRI_HI);
|
|
if(mr != NULL) {
|
|
capi = ((typeof(capi ))mr->b_rptr);
|
|
capi2 = ((typeof(capi2))mr->b_wptr)++;
|
|
cr = ((typeof(cr))mr->b_wptr)++;
|
|
ci = (typeof(ci))(capi+1);
|
|
*capi2 = *capi;
|
|
capi2->PRIM_type = CAPI_DATAB3_RESP;
|
|
cr->ncci = ci->ncci;
|
|
cr->blknum = ci->blknum;
|
|
S_enqueue(&bp->chan[0].q_out,mr);
|
|
sendone(bp,0);
|
|
}
|
|
freemsg(mb);
|
|
}
|
|
case -EAGAIN:
|
|
case -ERESTART:
|
|
S_enqueue(&bp->q_unknown,mb);
|
|
do_timer = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
s = splstr();
|
|
if(do_timer && !bp->unknown_timer) {
|
|
bp->unknown_timer = 1;
|
|
#ifdef NEW_TIMEOUT
|
|
bp->timer_toss_unknown =
|
|
#endif
|
|
timeout((void *)toss_unknown,bp,10*HZ);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
static void
|
|
DoIRQ(struct _bintec *bp)
|
|
{
|
|
int lastpos;
|
|
int err = bd_msgout(bp);
|
|
if(err < 0)
|
|
return; /* HW fail */
|
|
if(bp->waitmsg > 9)
|
|
return; /* not yet */
|
|
|
|
CTRL_DISABLE(bp);
|
|
while(err >= 0) {
|
|
err = getstart(bp);
|
|
if(err >= 0) {
|
|
int len = err;
|
|
if((err < 4) || (bp->waitmsg > 0)) {
|
|
if((err == 1) && (bp->waitmsg > 0)) {
|
|
if(!--bp->waitmsg) {
|
|
DEBUG(info) printf("BINTEC: card is online\n");
|
|
isdn2_new_state(&bp->card,1);
|
|
}
|
|
}
|
|
getflush(bp,len);
|
|
err = getend(bp);
|
|
continue;
|
|
}
|
|
err = get16(bp);
|
|
len -= 2;
|
|
if(err == htons(1)) {
|
|
int ch = 0;
|
|
int capilen = get16(bp);
|
|
mblk_t *mb = allocb(capilen,BPRI_HI);
|
|
struct CAPI_every_header *capi;
|
|
if(mb == NULL) {
|
|
DEBUG(info)printf("BINTEC:read: no mem for %d bytes\n",err),
|
|
getflush(bp,len);
|
|
getend(bp);
|
|
err = -ENOMEM;
|
|
break;
|
|
}
|
|
*((ushort_t *)mb->b_wptr)++ = capilen;
|
|
getmb(bp,mb,capilen-2);
|
|
capi = (typeof(capi))mb->b_wptr;
|
|
|
|
if(len > capilen) { /* Data msg? */
|
|
int offset = -1;
|
|
switch(capi->PRIM_type) {
|
|
case CAPI_DATAB3_IND:
|
|
{
|
|
struct CAPI_datab3_ind *c2;
|
|
int i;
|
|
struct _hdlc_buf *chan = &bp->chan[1];
|
|
|
|
c2 = (typeof(c2))(capi+1);
|
|
for(i=1;i<bp->card.nr_chans;i++) {
|
|
if(chan->appID == capi->appl && chan->NCCI == c2->ncci) {
|
|
offset = chan->offset;
|
|
ch = i;
|
|
break;
|
|
}
|
|
chan++;
|
|
}
|
|
if(offset < 0) {
|
|
ch = -1;
|
|
offset = bp->maxoffset; /* Not yet set */
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
offset = 0;
|
|
break;
|
|
}
|
|
{
|
|
mblk_t *m2 = allocb(len-capilen+offset,BPRI_HI);
|
|
if(m2 == NULL) {
|
|
getflush(bp,len-capilen);
|
|
printf("%sBINTEC error: No memory for %d bytes\n",KERN_ERR,len-capilen);
|
|
err = -ENOMEM;
|
|
} else {
|
|
m2->b_rptr += offset;
|
|
m2->b_wptr += offset;
|
|
getmb(bp,m2,len-capilen);
|
|
linkb(mb,m2);
|
|
}
|
|
}
|
|
} else if(len < capilen) {
|
|
DEBUG(info)printf("BINTEC:read: want %d bytes, got %d\n",capilen,len),
|
|
freemsg(mb);
|
|
continue;
|
|
}
|
|
switch(capi->PRIM_type) {
|
|
case CAPI_DATAB3_CONF:
|
|
break;
|
|
}
|
|
if(err < 0)
|
|
getend(bp);
|
|
else
|
|
err = getend(bp);
|
|
if(err >= 0) {
|
|
err = postproc(bp,mb,ch);
|
|
if(err == -ERESTART) {
|
|
static int prcnt;
|
|
if(bp->q_unknown.nblocks > 100) {
|
|
prcnt++;
|
|
if(prcnt < 3) {
|
|
DEBUG(info)printf("BINTEC:read: 'unknown data' queue full\n");
|
|
}
|
|
} else {
|
|
S_enqueue(&bp->q_unknown,mb);
|
|
err = 0;
|
|
if(prcnt)
|
|
prcnt--;
|
|
}
|
|
} else if(err == -EAGAIN) {
|
|
if(bp->chan[0].q_in.nblocks < 100) {
|
|
DEBUG(info) if(bp->chan[0].q_in.nblocks == 0) printf("BINTEC read: queued info packet\n");
|
|
S_enqueue(&bp->chan[0].q_in,mb);
|
|
err = 0;
|
|
} else {
|
|
printf("BINTEC: incoming queue full\n");
|
|
isdn2_chstate(&bp->card,MDL_ERROR_IND,0);
|
|
}
|
|
}
|
|
}
|
|
if (err < 0)
|
|
freemsg(mb);
|
|
} else {
|
|
mblk_t *mb = allocb(len+2,BPRI_LO);
|
|
if(mb != NULL) {
|
|
*((ushort_t *)mb->b_wptr)++ = ntohs(err);
|
|
getmb(bp,mb,len);
|
|
log_printmsg(NULL,"BINTEC error: msgtype",mb,KERN_WARNING);
|
|
freemsg(mb);
|
|
} else {
|
|
printf("%sBINTEC error: msg type %04x\n",KERN_ERR,ntohs(err));
|
|
getflush(bp,len);
|
|
}
|
|
err = getend(bp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bp->waitmsg != 0)
|
|
return;
|
|
|
|
recvone(bp,0);
|
|
for(lastpos=1; lastpos <= bp->card.nr_chans; lastpos++) {
|
|
do {
|
|
err = pushone(bp,lastpos);
|
|
} while(err == 0);
|
|
}
|
|
|
|
lastpos = bp->lastout;
|
|
do {
|
|
err = sendone(bp,0);
|
|
if(err < 0)
|
|
break;
|
|
|
|
err = sendone(bp,bp->lastout);
|
|
|
|
if(bp->lastout == bp->card.nr_chans)
|
|
bp->lastout = 1;
|
|
else
|
|
bp->lastout++;
|
|
} while((err >= 0) && (bp->lastout != lastpos));
|
|
CTRL_ENABLE(bp);
|
|
}
|
|
|
|
static void
|
|
bintecintr(int irq, void *dev, struct pt_regs *regs)
|
|
{
|
|
struct _bintec *bp = dev;
|
|
|
|
if(!bp->polled++) {
|
|
DoIRQ(bp);
|
|
bp->polled --;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void NAME(REALNAME,poll)(struct _bintec *bp)
|
|
{
|
|
long s;
|
|
s = splstr();
|
|
if(!bp->polled++) {
|
|
do {
|
|
splx(s);
|
|
DoIRQ(bp);
|
|
splstr();
|
|
} while(--bp->polled);
|
|
#if 0
|
|
if(bp->info.irq != 0)
|
|
unblock_irq(bp->info.irq);
|
|
#endif
|
|
} else
|
|
bp->polled--;
|
|
splx(s);
|
|
}
|
|
|
|
|
|
static void
|
|
bintectimer(struct _bintec *bp)
|
|
{
|
|
NAME(REALNAME,poll)(bp);
|
|
#ifdef NEW_TIMEOUT
|
|
bp->timer =
|
|
#endif
|
|
timeout((void *)bintectimer,bp, ((bp->info.irq == 0) || (bp->waitmsg > 0)) ? (HZ/100) : (HZ/2));
|
|
}
|
|
|
|
int NAME(REALNAME,init)(struct cardinfo *inf)
|
|
{
|
|
struct _bintec *bp;
|
|
int err;
|
|
|
|
bp = kmalloc(sizeof(*bp),GFP_KERNEL);
|
|
if(bp == NULL) {
|
|
printf("%sBINTEC: no memory!\n",KERN_ERR);
|
|
return -ENOMEM;
|
|
}
|
|
bzero(bp,sizeof(*bp));
|
|
bp->info = *inf;
|
|
bp->infoptr = inf;
|
|
bp->card.ctl = bp;
|
|
bp->card.modes = CHM_INTELLIGENT;
|
|
bp->card.ch_mode = mode;
|
|
bp->card.ch_prot = prot;
|
|
bp->card.send = data;
|
|
bp->card.flush = flush;
|
|
bp->card.cansend = candata;
|
|
bp->card.poll = NULL;
|
|
bp->card.boot = boot;
|
|
bp->polled = -99;
|
|
bp->lastout = 1;
|
|
bp->registered = 0;
|
|
printf("ISDN: " STRING(REALNAME) " at mem 0x%lx irq %d: ",bp->info.memaddr,bp->info.irq);
|
|
bp->waitmsg = 999;
|
|
if((err = init1(bp)) < 0) {
|
|
printf("Card not initializable.\n");
|
|
kfree(bp);
|
|
return err;
|
|
}
|
|
if((bp->info.irq != 0) && request_irq(bp->info.irq,bintecintr,SA_SAMPLE_RANDOM,STRING(REALNAME),bp)) {
|
|
printf("IRQ not available.\n");
|
|
kfree(bp);
|
|
return -EIO;
|
|
}
|
|
|
|
if((err = isdn2_register(&bp->card, bp->info.ID)) != 0) {
|
|
printf("not installed (ISDN_2), err %d\n",err);
|
|
kfree(bp);
|
|
return err;
|
|
}
|
|
|
|
bp->polled = 0;
|
|
bp->registered = 1;
|
|
if(bp->info.irq == 0) {
|
|
printf("polling; ");
|
|
}
|
|
bintectimer(bp);
|
|
bp->next = binteclist;
|
|
binteclist = bp;
|
|
printf("installed at ");
|
|
if(bp->info.memaddr != 0)
|
|
printf("mem 0x%lx ",bp->info.memaddr);
|
|
if(bp->info.irq != 0)
|
|
printf("irq %d.\n",bp->info.irq);
|
|
else
|
|
printf("polled.\n");
|
|
MORE_USE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void NAME(REALNAME,exit)(struct cardinfo *inf)
|
|
{
|
|
unsigned long ms = SetSPL(inf->ipl);
|
|
struct _bintec *bp = NULL, **nbp;
|
|
|
|
nbp = &binteclist;
|
|
while(*nbp != NULL) {
|
|
|
|
if((*nbp)->infoptr == inf) {
|
|
bp = *nbp;
|
|
*nbp = bp->next;
|
|
break;
|
|
}
|
|
nbp = &((*nbp)->next);
|
|
}
|
|
|
|
if(bp == NULL) {
|
|
printf("%sBINTEC error: exit: info record not found!\n",KERN_ERR);
|
|
return;
|
|
}
|
|
#ifdef NEW_TIMEOUT
|
|
untimeout(bp->timer);
|
|
#else
|
|
untimeout(bintectimer,bp);
|
|
#endif
|
|
reset_card(bp);
|
|
|
|
isdn2_unregister(&bp->card);
|
|
if(bp->info.irq != 0)
|
|
free_irq(bp->info.irq,bp);
|
|
splx(ms);
|
|
kfree(bp);
|
|
LESS_USE;
|
|
}
|
|
|
|
|
|
#ifdef MODULE
|
|
static int do_init_module(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int do_exit_module(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|