/* $Id: eicon_idi.c,v 1.42 2001/02/07 10:44:11 armin Exp $
 *
 * ISDN lowlevel-module for Eicon active cards.
 *        IDI interface 
 *
 * Copyright 1998-2000  by Armin Schindler (mac@melware.de)
 * Copyright 1999,2000  Cytronics & Melware (info@melware.de)
 *
 * Thanks to	Deutsche Mailbox Saar-Lor-Lux GmbH
 *		for sponsoring and testing fax
 *		capabilities with Diva Server cards.
 *		(dor@deutschemailbox.de)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <linux/config.h>
#define __NO_VERSION__
#include "eicon.h"
#include "eicon_idi.h"
#include "eicon_dsp.h"
#include "uxio.h"

#undef EICON_FULL_SERVICE_OKTETT

char *eicon_idi_revision = "$Revision: 1.42 $";

eicon_manifbuf *manbuf;

int eicon_idi_manage_assign(eicon_card *card);
int eicon_idi_manage_remove(eicon_card *card);
int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer);

int
idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
{
	int l = 0;
	int tmp;

	tmp = 0;
  if (!signet) {
	/* Signal Layer */
	reqbuf->XBuffer.P[l++] = CAI;
	reqbuf->XBuffer.P[l++] = 1;
	reqbuf->XBuffer.P[l++] = 0;
	reqbuf->XBuffer.P[l++] = KEY;
	reqbuf->XBuffer.P[l++] = 3;
	reqbuf->XBuffer.P[l++] = 'I';
	reqbuf->XBuffer.P[l++] = '4';
	reqbuf->XBuffer.P[l++] = 'L';
	reqbuf->XBuffer.P[l++] = SHIFT|6;
	reqbuf->XBuffer.P[l++] = SIN;
	reqbuf->XBuffer.P[l++] = 2;
	reqbuf->XBuffer.P[l++] = 0;
	reqbuf->XBuffer.P[l++] = 0;
	reqbuf->XBuffer.P[l++] = 0; /* end */
	reqbuf->Req = ASSIGN;
	reqbuf->ReqCh = 0;
	reqbuf->ReqId = DSIG_ID;
	reqbuf->XBuffer.length = l;
	reqbuf->Reference = 0; /* Sig Entity */
  }
  else {
	/* Network Layer */
	reqbuf->XBuffer.P[l++] = CAI;
	reqbuf->XBuffer.P[l++] = 1;
	reqbuf->XBuffer.P[l++] = chan->e.D3Id;
	reqbuf->XBuffer.P[l++] = LLC;
	reqbuf->XBuffer.P[l++] = 2;
	switch(chan->l2prot) {
		case ISDN_PROTO_L2_V11096:
		case ISDN_PROTO_L2_V11019:
		case ISDN_PROTO_L2_V11038:
		case ISDN_PROTO_L2_TRANS:
			reqbuf->XBuffer.P[l++] = 2; /* transparent */
			break;
		case ISDN_PROTO_L2_X75I:
		case ISDN_PROTO_L2_X75UI:
		case ISDN_PROTO_L2_X75BUI:
			reqbuf->XBuffer.P[l++] = 5; /* X.75 */ 
			break;
		case ISDN_PROTO_L2_MODEM:
  			if (chan->fsm_state == EICON_STATE_IWAIT)
				reqbuf->XBuffer.P[l++] = 9; /* V.42 incoming */
			else
				reqbuf->XBuffer.P[l++] = 10; /* V.42 */
			break;
		case ISDN_PROTO_L2_HDLC:
		case ISDN_PROTO_L2_FAX:
  			if (chan->fsm_state == EICON_STATE_IWAIT)
				reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */
			else
				reqbuf->XBuffer.P[l++] = 2; /* transparent */
			break;
		default:
			reqbuf->XBuffer.P[l++] = 1;
	}
	switch(chan->l3prot) {
		case ISDN_PROTO_L3_FCLASS2:
#ifdef CONFIG_ISDN_TTY_FAX
			reqbuf->XBuffer.P[l++] = 6;
			reqbuf->XBuffer.P[l++] = NLC;
			tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]);
			reqbuf->XBuffer.P[l++] = tmp; 
			l += tmp;
			break;
#endif
		case ISDN_PROTO_L3_TRANS:
		default:
			reqbuf->XBuffer.P[l++] = 4;
	}
	reqbuf->XBuffer.P[l++] = 0; /* end */
	reqbuf->Req = ASSIGN;
	reqbuf->ReqCh = 0;
	reqbuf->ReqId = NL_ID;
	reqbuf->XBuffer.length = l;
	reqbuf->Reference = 1; /* Net Entity */
  }
   return(0);
}

int
idi_put_req(eicon_REQ *reqbuf, int rq, int signet, int Ch)
{
	reqbuf->Req = rq;
	reqbuf->ReqCh = Ch;
	reqbuf->ReqId = 1;
	reqbuf->XBuffer.length = 1;
	reqbuf->XBuffer.P[0] = 0;
	reqbuf->Reference = signet;
   return(0);
}

int
idi_put_suspend_req(eicon_REQ *reqbuf, eicon_chan *chan)
{
	reqbuf->Req = SUSPEND;
	reqbuf->ReqCh = 0;
	reqbuf->ReqId = 1;
	reqbuf->XBuffer.P[0] = CAI;
	reqbuf->XBuffer.P[1] = 1;
	reqbuf->XBuffer.P[2] = chan->No;
	reqbuf->XBuffer.P[3] = 0;
	reqbuf->XBuffer.length = 4;
	reqbuf->Reference = 0; /* Sig Entity */
   return(0);
}

int
idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
{
	int l = 9;
	reqbuf->Req = CALL_RES;
	reqbuf->ReqCh = 0;
	reqbuf->ReqId = 1;
	reqbuf->XBuffer.P[0] = CAI;
	reqbuf->XBuffer.P[1] = 6;
	reqbuf->XBuffer.P[2] = 9;
	reqbuf->XBuffer.P[3] = 0;
	reqbuf->XBuffer.P[4] = 0;
	reqbuf->XBuffer.P[5] = 0;
	reqbuf->XBuffer.P[6] = 32;
	reqbuf->XBuffer.P[7] = 0;
	switch(chan->l2prot) {
		case ISDN_PROTO_L2_X75I:
		case ISDN_PROTO_L2_X75UI:
		case ISDN_PROTO_L2_X75BUI:
		case ISDN_PROTO_L2_HDLC:
			reqbuf->XBuffer.P[1] = 1;
			reqbuf->XBuffer.P[2] = 0x05;
			l = 4;
			break;
		case ISDN_PROTO_L2_V11096:
			reqbuf->XBuffer.P[2] = 0x0d;
			reqbuf->XBuffer.P[3] = 5;
			reqbuf->XBuffer.P[4] = 0;
			break;
		case ISDN_PROTO_L2_V11019:
			reqbuf->XBuffer.P[2] = 0x0d;
			reqbuf->XBuffer.P[3] = 6;
			reqbuf->XBuffer.P[4] = 0;
			break;
		case ISDN_PROTO_L2_V11038:
			reqbuf->XBuffer.P[2] = 0x0d;
			reqbuf->XBuffer.P[3] = 7;
			reqbuf->XBuffer.P[4] = 0;
			break;
		case ISDN_PROTO_L2_MODEM:
			reqbuf->XBuffer.P[2] = 0x11;
			reqbuf->XBuffer.P[3] = 7;
			reqbuf->XBuffer.P[4] = 0;
			reqbuf->XBuffer.P[5] = 0;
			reqbuf->XBuffer.P[6] = 128;
			reqbuf->XBuffer.P[7] = 0;
			break;
		case ISDN_PROTO_L2_FAX:
			reqbuf->XBuffer.P[2] = 0x10;
			reqbuf->XBuffer.P[3] = 0;
			reqbuf->XBuffer.P[4] = 0;
			reqbuf->XBuffer.P[5] = 0;
			reqbuf->XBuffer.P[6] = 128;
			reqbuf->XBuffer.P[7] = 0;
			break;
		case ISDN_PROTO_L2_TRANS:
			switch(chan->l3prot) {
				case ISDN_PROTO_L3_TRANSDSP:
					reqbuf->XBuffer.P[2] = 22; /* DTMF, audio events on */
			}
			break;
	}
	reqbuf->XBuffer.P[8] = 0;
	reqbuf->XBuffer.length = l;
	reqbuf->Reference = 0; /* Sig Entity */
	eicon_log(NULL, 8, "idi_req: Ch%d: Call_Res\n", chan->No);
   return(0);
}

int
idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
{
        struct sk_buff *skb;
        struct sk_buff *skb2;
	eicon_REQ *reqbuf;
	eicon_chan_ptr *chan2;

        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

        if ((!skb) || (!skb2)) {
               	eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No);
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
                return -ENOMEM; 
	}

	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
	chan2->ptr = chan;

	reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
	eicon_log(card, 8, "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
	if (layer) cmd |= 0x700;
	switch(cmd) {
		case ASSIGN:
		case ASSIGN|0x700:
			idi_assign_req(reqbuf, layer, chan);
			break;
		case REMOVE:
		case REMOVE|0x700:
			idi_put_req(reqbuf, REMOVE, layer, 0);
			break;
		case INDICATE_REQ:
			idi_put_req(reqbuf, INDICATE_REQ, 0, 0);
			break;
		case HANGUP:
			idi_put_req(reqbuf, HANGUP, 0, 0);
			break;
		case SUSPEND:
			idi_put_suspend_req(reqbuf, chan);
			break;
		case RESUME:
			idi_put_req(reqbuf, RESUME, 0 ,0);
			break;
		case REJECT:
			idi_put_req(reqbuf, REJECT, 0 ,0);
			break;
		case CALL_ALERT:
			idi_put_req(reqbuf, CALL_ALERT, 0, 0);
			break;
		case CALL_RES:
			idi_call_res_req(reqbuf, chan);
			break;
		case CALL_HOLD:
			idi_put_req(reqbuf, CALL_HOLD, 0, 0);
			break;
		case N_CONNECT|0x700:
			idi_put_req(reqbuf, N_CONNECT, 1, 0);
			break;
		case N_CONNECT_ACK|0x700:
			idi_put_req(reqbuf, N_CONNECT_ACK, 1, 0);
			break;
		case N_DISC|0x700:
			idi_put_req(reqbuf, N_DISC, 1, chan->e.IndCh);
			break;
		case N_DISC_ACK|0x700:
			idi_put_req(reqbuf, N_DISC_ACK, 1, chan->e.IndCh);
			break;
		default:
			eicon_log(card, 1, "idi_req: Ch%d: Unknown request\n", chan->No);
			dev_kfree_skb(skb);
			dev_kfree_skb(skb2);
			return(-1);
	}

	skb_queue_tail(&chan->e.X, skb);
	skb_queue_tail(&card->sndq, skb2); 
	eicon_schedule_tx(card);
	return(0);
}

int
eicon_idi_listen_req(eicon_card *card, eicon_chan *chan)
{
	if ((!card) || (!chan))
		return 1;

	eicon_log(card, 16, "idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask);
	if (!chan->e.D3Id) {
		idi_do_req(card, chan, ASSIGN, 0); 
	}
	if (chan->fsm_state == EICON_STATE_NULL) {
		if (!(chan->statectrl & HAVE_CONN_REQ)) {
			idi_do_req(card, chan, INDICATE_REQ, 0);
			chan->fsm_state = EICON_STATE_LISTEN;
		}
	}
  return(0);
}

unsigned char
idi_si2bc(int si1, int si2, char *bc, char *hlc)
{
  hlc[0] = 0;
  switch(si1) {
	case 1:
		bc[0] = 0x90;		/* 3,1 kHz audio */
		bc[1] = 0x90;		/* 64 kbit/s */
		bc[2] = 0xa3;		/* G.711 A-law */
#ifdef EICON_FULL_SERVICE_OKTETT
		if (si2 == 1) {
			bc[0] = 0x80;	/* Speech */
			hlc[0] = 0x02;	/* hlc len */
			hlc[1] = 0x91;	/* first hic */
			hlc[2] = 0x81;	/* Telephony */
		}
#endif
		return(3);
	case 2:
		bc[0] = 0x90;		/* 3,1 kHz audio */
		bc[1] = 0x90;		/* 64 kbit/s */
		bc[2] = 0xa3;		/* G.711 A-law */
#ifdef EICON_FULL_SERVICE_OKTETT
		if (si2 == 2) {
			hlc[0] = 0x02;	/* hlc len */
			hlc[1] = 0x91;	/* first hic */
			hlc[2] = 0x84;	/* Fax Gr.2/3 */
		}
#endif
		return(3);
	case 5:
	case 7:
	default:
		bc[0] = 0x88;
		bc[1] = 0x90;
		return(2);
  }
 return (0);
}

int
idi_hangup(eicon_card *card, eicon_chan *chan)
{
	if ((!card) || (!chan))
		return 1;

	if ((chan->fsm_state == EICON_STATE_ACTIVE) ||
	    (chan->fsm_state == EICON_STATE_WMCONN)) {
  		if (chan->e.B2Id) idi_do_req(card, chan, N_DISC, 1);
	}
	if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
	if (chan->fsm_state != EICON_STATE_NULL) {
		chan->statectrl |= WAITING_FOR_HANGUP;
		idi_do_req(card, chan, HANGUP, 0);
		chan->fsm_state = EICON_STATE_NULL;
	}
	eicon_log(card, 8, "idi_req: Ch%d: Hangup\n", chan->No);
#ifdef CONFIG_ISDN_TTY_FAX
	chan->fax = 0;
#endif
  return(0);
}

int
capipmsg(eicon_card *card, eicon_chan *chan, capi_msg *cm)
{
	if ((cm->para[0] != 3) || (cm->para[1] != 0))
		return -1;
	if (cm->para[2] < 3)
		return -1;
	if (cm->para[4] != 0)
		return -1;
	switch(cm->para[3]) {
		case 4: /* Suspend */
			eicon_log(card, 8, "idi_req: Ch%d: Call Suspend\n", chan->No);
			if (cm->para[5]) {
				idi_do_req(card, chan, SUSPEND, 0);
			} else {
				idi_do_req(card, chan, CALL_HOLD, 0);
			}
			break;
		case 5: /* Resume */
			eicon_log(card, 8, "idi_req: Ch%d: Call Resume\n", chan->No);
			idi_do_req(card, chan, RESUME, 0);
			break;
        }
	return 0;
}

int
idi_connect_res(eicon_card *card, eicon_chan *chan)
{
	if ((!card) || (!chan))
		return 1;

	chan->fsm_state = EICON_STATE_IWAIT;
	
	/* check if old NetID has been removed */
	if (chan->e.B2Id) {
		eicon_log(card, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n",
			chan->No, chan->e.B2Id);
		idi_do_req(card, chan, REMOVE, 1);
	}

	idi_do_req(card, chan, ASSIGN, 1);
	idi_do_req(card, chan, CALL_RES, 0);
	return(0);
}

int
idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
                    char *eazmsn, int si1, int si2)
{
	int l = 0;
	int i;
	unsigned char tmp;
	unsigned char *sub, *sp;
	unsigned char bc[5];
	unsigned char hlc[5];
        struct sk_buff *skb;
        struct sk_buff *skb2;
	eicon_REQ *reqbuf;
	eicon_chan_ptr *chan2;

	if ((!card) || (!chan))
		return 1;

        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

        if ((!skb) || (!skb2)) {
               	eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No);
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
                return -ENOMEM; 
	}

	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
	chan2->ptr = chan;

	reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
	reqbuf->Req = CALL_REQ;
	reqbuf->ReqCh = 0;
	reqbuf->ReqId = 1;

	sub = NULL;
	sp = phone;
	while (*sp) {
		if (*sp == '.') {
			sub = sp + 1;
			*sp = 0;
		} else
			sp++;
	}
	reqbuf->XBuffer.P[l++] = CPN;
	reqbuf->XBuffer.P[l++] = strlen(phone) + 1;
	reqbuf->XBuffer.P[l++] = 0x81;
	for(i=0; i<strlen(phone);i++) 
		reqbuf->XBuffer.P[l++] = phone[i] & 0x7f;
	if (sub) {
		reqbuf->XBuffer.P[l++] = DSA;
		reqbuf->XBuffer.P[l++] = strlen(sub) + 2;
		reqbuf->XBuffer.P[l++] = 0x80; /* NSAP coded */
		reqbuf->XBuffer.P[l++] = 0x50; /* local IDI format */
		while (*sub)
			reqbuf->XBuffer.P[l++] = *sub++ & 0x7f;
	}

	sub = NULL;
	sp = eazmsn;
	while (*sp) {
		if (*sp == '.') {
			sub = sp + 1;
			*sp = 0;
		} else
			sp++;
	}
	reqbuf->XBuffer.P[l++] = OAD;
	reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2;
	reqbuf->XBuffer.P[l++] = 0x01;
	reqbuf->XBuffer.P[l++] = 0x80;
	for(i=0; i<strlen(eazmsn);i++) 
		reqbuf->XBuffer.P[l++] = eazmsn[i] & 0x7f;
	if (sub) {
		reqbuf->XBuffer.P[l++] = OSA;
		reqbuf->XBuffer.P[l++] = strlen(sub) + 2;
		reqbuf->XBuffer.P[l++] = 0x80; /* NSAP coded */
		reqbuf->XBuffer.P[l++] = 0x50; /* local IDI format */
		while (*sub)
			reqbuf->XBuffer.P[l++] = *sub++ & 0x7f;
	}

	if (si2 > 2) {
		reqbuf->XBuffer.P[l++] = SHIFT|6;
		reqbuf->XBuffer.P[l++] = SIN;
		reqbuf->XBuffer.P[l++] = 2;
		reqbuf->XBuffer.P[l++] = si1;
		reqbuf->XBuffer.P[l++] = si2;
	}
	else if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) {
		reqbuf->XBuffer.P[l++] = BC;
		reqbuf->XBuffer.P[l++] = tmp;
		for(i=0; i<tmp;i++) 
			reqbuf->XBuffer.P[l++] = bc[i];
		if ((tmp=hlc[0])) {
			reqbuf->XBuffer.P[l++] = HLC;
			reqbuf->XBuffer.P[l++] = tmp;
			for(i=1; i<=tmp;i++) 
				reqbuf->XBuffer.P[l++] = hlc[i];
		}
	}

        reqbuf->XBuffer.P[l++] = CAI;
        reqbuf->XBuffer.P[l++] = 6;
        reqbuf->XBuffer.P[l++] = 0x09;
	reqbuf->XBuffer.P[l++] = 0;
	reqbuf->XBuffer.P[l++] = 0;
	reqbuf->XBuffer.P[l++] = 0;
	reqbuf->XBuffer.P[l++] = 32;
	reqbuf->XBuffer.P[l++] = 0;
        switch(chan->l2prot) {
		case ISDN_PROTO_L2_X75I:
		case ISDN_PROTO_L2_X75UI:
		case ISDN_PROTO_L2_X75BUI:
                case ISDN_PROTO_L2_HDLC:
                        reqbuf->XBuffer.P[l-6] = 5;
                        reqbuf->XBuffer.P[l-7] = 1;
			l -= 5; 
                        break;
                case ISDN_PROTO_L2_V11096:
                        reqbuf->XBuffer.P[l-7] = 3;
                        reqbuf->XBuffer.P[l-6] = 0x0d;
                        reqbuf->XBuffer.P[l-5] = 5;
                        reqbuf->XBuffer.P[l-4] = 0;
                        l -= 3;
                        break;
                case ISDN_PROTO_L2_V11019:
                        reqbuf->XBuffer.P[l-7] = 3;
                        reqbuf->XBuffer.P[l-6] = 0x0d;
                        reqbuf->XBuffer.P[l-5] = 6;
                        reqbuf->XBuffer.P[l-4] = 0;
                        l -= 3;
                        break;
                case ISDN_PROTO_L2_V11038:
                        reqbuf->XBuffer.P[l-7] = 3;
                        reqbuf->XBuffer.P[l-6] = 0x0d;
                        reqbuf->XBuffer.P[l-5] = 7;
                        reqbuf->XBuffer.P[l-4] = 0;
                        l -= 3;
                        break;
                case ISDN_PROTO_L2_MODEM:
			reqbuf->XBuffer.P[l-6] = 0x11;
			reqbuf->XBuffer.P[l-5] = 7;
			reqbuf->XBuffer.P[l-4] = 0;
			reqbuf->XBuffer.P[l-3] = 0;
			reqbuf->XBuffer.P[l-2] = 128;
			reqbuf->XBuffer.P[l-1] = 0;
                        break;
                case ISDN_PROTO_L2_FAX:
			reqbuf->XBuffer.P[l-6] = 0x10;
			reqbuf->XBuffer.P[l-5] = 0;
			reqbuf->XBuffer.P[l-4] = 0;
			reqbuf->XBuffer.P[l-3] = 0;
			reqbuf->XBuffer.P[l-2] = 128;
			reqbuf->XBuffer.P[l-1] = 0;
                        break;
		case ISDN_PROTO_L2_TRANS:
			switch(chan->l3prot) {
				case ISDN_PROTO_L3_TRANSDSP:
					reqbuf->XBuffer.P[l-6] = 22; /* DTMF, audio events on */
			}
			break;
        }
	
	reqbuf->XBuffer.P[l++] = 0; /* end */
	reqbuf->XBuffer.length = l;
	reqbuf->Reference = 0; /* Sig Entity */

	if (chan->statectrl & WAITING_FOR_HANGUP) {
		/*	If the line did not disconnect yet,
			we have to delay this command		*/
		eicon_log(card, 32, "idi_req: Ch%d: delaying conn_req\n", chan->No);
		chan->statectrl |= HAVE_CONN_REQ;
		chan->tskb1 = skb;
		chan->tskb2 = skb2;
	} else {
		skb_queue_tail(&chan->e.X, skb);
		skb_queue_tail(&card->sndq, skb2); 
		eicon_schedule_tx(card);
	}

	eicon_log(card, 8, "idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
   return(0);
}


void
idi_IndParse(eicon_card *ccard, eicon_chan *chan, idi_ind_message *message, unsigned char *buffer, int len)
{
	int i,j;
	int pos = 0;
	int codeset = 0;
	int wlen = 0;
	int lock = 0;
	__u8 w;
	__u16 code;
	isdn_ctrl cmd;

	memset(message, 0, sizeof(idi_ind_message));

	if ((!len) || (!buffer[pos])) return;

  while(pos <= len) {
	w = buffer[pos++];
	if (!w) return;
	if (w & 0x80) {
		wlen = 0;
	}
	else {
		wlen = buffer[pos++];
	}

	if (pos > len) return;

	if (lock & 0x80) lock &= 0x7f;
	else codeset = lock;

	if((w&0xf0) == SHIFT) {
		codeset = w;
		if(!(codeset & 0x08)) lock = codeset & 7;
		codeset &= 7;
		lock |= 0x80;
	}
	else {
		if (w==ESC && wlen >=2) {
			code = buffer[pos++]|0x800;
			wlen--;
		}
		else code = w;
		code |= (codeset<<8);

		if (pos + wlen > len) {
			eicon_log(ccard, 1, "idi_err: Ch%d: IElen %d of %x exceeds Ind_Length (+%d)\n", chan->No, 
					wlen, code, (pos + wlen) - len);
			return;
		}

		switch(code) {
			case OAD:
				if (wlen > sizeof(message->oad)) {
					pos += wlen;
					break;
				}
				j = 1;
				if (wlen) {
					message->plan = buffer[pos++];
					if (message->plan &0x80) 
						message->screen = 0;
					else {
						message->screen = buffer[pos++];
						j = 2;
					}
				}
				for(i=0; i < wlen-j; i++) 
					message->oad[i] = buffer[pos++];
				eicon_log(ccard, 2, "idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, 
					message->plan, message->screen, message->oad);
				break;
			case RDN:
				if (wlen > sizeof(message->rdn)) {
					pos += wlen;
					break;
				}
				j = 1;
				if (wlen) {
					if (!(buffer[pos++] & 0x80)) {
						pos++; 
						j = 2;
					}
				}
				for(i=0; i < wlen-j; i++) 
					message->rdn[i] = buffer[pos++];
				eicon_log(ccard, 2, "idi_inf: Ch%d: RDN= %s\n", chan->No, 
						message->rdn);
				break;
			case CPN:
				if (wlen > sizeof(message->cpn)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->cpn[i] = buffer[pos++];
				eicon_log(ccard, 2, "idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No,
					(__u8)message->cpn[0], message->cpn + 1);
				break;
			case DSA:
				if (wlen > sizeof(message->dsa)) {
					pos += wlen;
					break;
				}
				pos += 2;
				for(i=0; i < wlen-2; i++) 
					message->dsa[i] = buffer[pos++];
				eicon_log(ccard, 2, "idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
				break;
			case OSA:
				if (wlen > sizeof(message->osa)) {
					pos += wlen;
					break;
				}
				pos += 2;
				for(i=0; i < wlen-2; i++) 
					message->osa[i] = buffer[pos++];
				eicon_log(ccard, 2, "idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
				break;
			case CAD:
				pos += wlen;
				eicon_log(ccard, 2, "idi_inf: Ch%d: Connected Address in ind, len:%x\n", 
					chan->No, wlen);
				break;
			case BC:
				if (wlen > sizeof(message->bc)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->bc[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No,
					message->bc[0],message->bc[1],message->bc[2]);
				break;
			case 0x800|BC:
				if (wlen > sizeof(message->e_bc)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->e_bc[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]);
				break;
			case LLC:
				if (wlen > sizeof(message->llc)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->llc[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: LLC=%d %d %d %d ...\n", chan->No, message->llc[0],
					message->llc[1],message->llc[2],message->llc[3]);
				break;
			case HLC:
				if (wlen > sizeof(message->hlc)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->hlc[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: HLC=%x %x %x %x %x ...\n", chan->No,
					message->hlc[0], message->hlc[1],
					message->hlc[2], message->hlc[3], message->hlc[4]);
				break;
			case DSP:
			case 0x600|DSP:
				if (wlen > sizeof(message->display)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->display[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: Display: %s\n", chan->No,
					message->display);
				break;
			case 0x600|KEY:
				if (wlen > sizeof(message->keypad)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->keypad[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: Keypad: %s\n", chan->No,
					message->keypad);
				break;
			case NI:
			case 0x600|NI:
				if (wlen) {
					switch(buffer[pos] & 127) {
						case 0:
							eicon_log(ccard, 4, "idi_inf: Ch%d: User suspended.\n", chan->No);
							break;
						case 1:
							eicon_log(ccard, 4, "idi_inf: Ch%d: User resumed.\n", chan->No);
							break;
						case 2:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Bearer service change.\n", chan->No);
							break;
						default:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Notification %x.\n", 
									chan->No, buffer[pos] & 127);
					}
					pos += wlen;
				}
				break;
			case PI:
			case 0x600|PI:
				if (wlen > 1) {
					switch(buffer[pos+1] & 127) {
						case 1:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No);
							break;
						case 2:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No);
							break;
						case 3:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No);
							break;
						case 4:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No);
							break;
						case 5:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Interworking has occurred.\n", chan->No);
							break;
						case 8:
							eicon_log(ccard, 4, "idi_inf: Ch%d: In-band information available.\n", chan->No);
							break;
						default:
							eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Progress %x.\n", 
									chan->No, buffer[pos+1] & 127);
					}
				}
				pos += wlen;
				break;
			case CAU:
				if (wlen > sizeof(message->cau)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->cau[i] = buffer[pos++];
				memcpy(&chan->cause, &message->cau, 2);
				eicon_log(ccard, 4, "idi_inf: Ch%d: CAU=%d %d\n", chan->No,
					message->cau[0],message->cau[1]);
				break;
			case 0x800|CAU:
				if (wlen > sizeof(message->e_cau)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->e_cau[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: ECAU=%d %d\n", chan->No,
					message->e_cau[0],message->e_cau[1]);
				break;
			case 0x800|CHI:
				if (wlen > sizeof(message->e_chi)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->e_chi[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/CHI=%d\n", chan->No,
					message->e_cau[0]);
				break;
			case 0x800|0x7a:
				pos ++;
				message->e_mt=buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt);
				break;
			case DT:
				if (wlen > sizeof(message->dt)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->dt[i] = buffer[pos++];
				eicon_log(ccard, 4, "idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No,
					message->dt[2], message->dt[1], message->dt[0],
					message->dt[3], message->dt[4], message->dt[5]);
				break;
			case 0x600|SIN:
				if (wlen > sizeof(message->sin)) {
					pos += wlen;
					break;
				}
				for(i=0; i < wlen; i++) 
					message->sin[i] = buffer[pos++];
				eicon_log(ccard, 2, "idi_inf: Ch%d: SIN=%d %d\n", chan->No,
					message->sin[0],message->sin[1]);
				break;
			case 0x600|CPS:
				eicon_log(ccard, 2, "idi_inf: Ch%d: Called Party Status in ind\n", chan->No);
				pos += wlen;
				break;
			case 0x600|CIF:
				for (i = 0; i < wlen; i++)
					if (buffer[pos + i] != '0') break;
				memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i);
				cmd.parm.num[wlen - i] = 0;
				eicon_log(ccard, 2, "idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num);
				pos += wlen;
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_CINF;
				cmd.arg = chan->No;
				ccard->interface.statcallb(&cmd);
				break;
			case 0x600|DATE:
				eicon_log(ccard, 2, "idi_inf: Ch%d: Date in ind\n", chan->No);
				pos += wlen;
				break;
			case 0xa1: 
				eicon_log(ccard, 2, "idi_inf: Ch%d: Sending Complete in ind.\n", chan->No);
				pos += wlen;
				break;
			case 0xe08: 
			case 0xe7a: 
			case 0xe04: 
			case 0xe00: 
				/* *** TODO *** */
			case CHA:
				/* Charge advice */
			case FTY:
			case 0x600|FTY:
			case CHI:
			case 0x800:
				/* Not yet interested in this */
				pos += wlen;
				break;
			case 0x880:
				/* Managment Information Element */
				if (!manbuf) {
					eicon_log(ccard, 1, "idi_err: manbuf not allocated\n");
				}
				else {
					memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen);
					manbuf->length[manbuf->count] = wlen;
					manbuf->count++;
					manbuf->pos += wlen;
				}
				pos += wlen;
				break;
			default:
				pos += wlen;
				eicon_log(ccard, 6, "idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n", 
					chan->No, code, wlen);
		}
	}
  }
}

void
idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *sin, unsigned char *si1, unsigned char *si2)
{
	si1[0] = 0;
	si2[0] = 0;

	switch (bc[0] & 0x7f) {
		case 0x00: /* Speech */
			si1[0] = 1;
#ifdef EICON_FULL_SERVICE_OKTETT
			si1[0] = sin[0];
			si2[0] = sin[1];
#endif
			break;
		case 0x10: /* 3.1 Khz audio */
			si1[0] = 1;
#ifdef EICON_FULL_SERVICE_OKTETT
			si1[0] = sin[0];
			si2[0] = sin[1];
#endif
			break;
		case 0x08: /* Unrestricted digital information */
			si1[0] = 7;
			si2[0] = sin[1];
			break;
		case 0x09: /* Restricted digital information */
			si1[0] = 2;
			break;
		case 0x11:
			/* Unrestr. digital information  with
			 * tones/announcements ( or 7 kHz audio
			 */
			si1[0] = 3;
			break;
		case 0x18: /* Video */
			si1[0] = 4;
			break;
	}
	switch (bc[1] & 0x7f) {
		case 0x40: /* packed mode */
			si1[0] = 8;
			break;
		case 0x10: /* 64 kbit */
		case 0x11: /* 2*64 kbit */
		case 0x13: /* 384 kbit */
		case 0x15: /* 1536 kbit */
		case 0x17: /* 1920 kbit */
			/* moderate = bc[1] & 0x7f; */
			break;
	}
}

/********************* FAX stuff ***************************/

#ifdef CONFIG_ISDN_TTY_FAX

int
idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer)
{
	eicon_t30_s	*t30 = (eicon_t30_s *) buffer;

	if (!chan->fax) {
		eicon_log(NULL, 1,"idi_T30: fill_in with NULL fax struct, ERROR\n");
		return 0;
	}
	memset(t30, 0, sizeof(eicon_t30_s));
	t30->station_id_len = EICON_FAXID_LEN;
	memcpy(&t30->station_id[0], &chan->fax->id[0], EICON_FAXID_LEN);
	t30->resolution = chan->fax->resolution;
	t30->rate = chan->fax->rate + 1;	/* eicon rate starts with 1 */
	t30->format = T30_FORMAT_SFF;
	t30->pages_low = 0;
	t30->pages_high = 0;
	t30->atf = 1;				/* optimised for AT+F command set */
	t30->code = 0;
	t30->feature_bits_low = 0;
	t30->feature_bits_high = 0;
	t30->control_bits_low = 0;
	t30->control_bits_high = 0;

	if (chan->fax->nbc) {
		/* set compression by DCC value */
  	  switch(chan->fax->compression) {
		case (0):	/* 1-D modified */
			break;
		case (1):	/* 2-D modified Read */
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
			t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
			break;
		case (2):	/* 2-D uncompressed */
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
			t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
			t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
			break;
		case (3):	/* 2-D modified Read */
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING;
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
			t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
			t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING;
			t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
			t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
			break;
	  }
	} else {
		/* set compression to best */
		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING;
		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
		t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
		t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING;
		t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
		t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
		t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
	}
	switch(chan->fax->ecm) {
		case (0):	/* disable ECM */
			break;
		case (1):
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
			t30->control_bits_low |= T30_CONTROL_BIT_ECM_64_BYTES;
			t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
			t30->feature_bits_low |= T30_FEATURE_BIT_ECM_64_BYTES;
			break;
		case (2):
			t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
			t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
			break;
	}

	if (DebugVar & 128) {
		char st[40];
		eicon_log(NULL, 128, "sT30:code = %x\n", t30->code);
		eicon_log(NULL, 128, "sT30:rate = %x\n", t30->rate);
		eicon_log(NULL, 128, "sT30:res  = %x\n", t30->resolution);
		eicon_log(NULL, 128, "sT30:format = %x\n", t30->format);
		eicon_log(NULL, 128, "sT30:pages_low = %x\n", t30->pages_low);
		eicon_log(NULL, 128, "sT30:pages_high = %x\n", t30->pages_high);
		eicon_log(NULL, 128, "sT30:atf  = %x\n", t30->atf);
		eicon_log(NULL, 128, "sT30:control_bits_low = %x\n", t30->control_bits_low);
		eicon_log(NULL, 128, "sT30:control_bits_high = %x\n", t30->control_bits_high);
		eicon_log(NULL, 128, "sT30:feature_bits_low = %x\n", t30->feature_bits_low);
		eicon_log(NULL, 128, "sT30:feature_bits_high = %x\n", t30->feature_bits_high);
		//eicon_log(NULL, 128, "sT30:universal_5 = %x\n", t30->universal_5);
		//eicon_log(NULL, 128, "sT30:universal_6 = %x\n", t30->universal_6);
		//eicon_log(NULL, 128, "sT30:universal_7 = %x\n", t30->universal_7);
		eicon_log(NULL, 128, "sT30:station_id_len = %x\n", t30->station_id_len);
		eicon_log(NULL, 128, "sT30:head_line_len = %x\n", t30->head_line_len);
		strncpy(st, t30->station_id, t30->station_id_len);
		st[t30->station_id_len] = 0;
		eicon_log(NULL, 128, "sT30:station_id = <%s>\n", st);
	}
	return(sizeof(eicon_t30_s));
}

/* send fax struct */
int
idi_send_edata(eicon_card *card, eicon_chan *chan)
{
	struct sk_buff *skb;
	struct sk_buff *skb2;
	eicon_REQ *reqbuf;
	eicon_chan_ptr *chan2;

	if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) {
		eicon_log(card, 1, "idi_snd: Ch%d: send edata on state %d !\n", chan->No, chan->fsm_state);
		return -ENODEV;
	}
	eicon_log(card, 128, "idi_snd: Ch%d: edata (fax)\n", chan->No);

	skb = alloc_skb(sizeof(eicon_REQ) + sizeof(eicon_t30_s), GFP_ATOMIC);
	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

	if ((!skb) || (!skb2)) {
		eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_edata()\n", chan->No);
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
		return -ENOMEM;
	}

	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
	chan2->ptr = chan;

	reqbuf = (eicon_REQ *)skb_put(skb, sizeof(eicon_t30_s) + sizeof(eicon_REQ));

	reqbuf->Req = N_EDATA;
	reqbuf->ReqCh = chan->e.IndCh;
	reqbuf->ReqId = 1;

	reqbuf->XBuffer.length = idi_fill_in_T30(chan, reqbuf->XBuffer.P);
	reqbuf->Reference = 1; /* Net Entity */

	skb_queue_tail(&chan->e.X, skb);
	skb_queue_tail(&card->sndq, skb2);
	eicon_schedule_tx(card);
	return (0);
}

void
idi_parse_edata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
{
	eicon_t30_s *p = (eicon_t30_s *)buffer;
	int i;

	if (DebugVar & 128) {
		char st[40];
		eicon_log(ccard, 128, "rT30:len %d , size %d\n", len, sizeof(eicon_t30_s));
		eicon_log(ccard, 128, "rT30:code = %x\n", p->code);
		eicon_log(ccard, 128, "rT30:rate = %x\n", p->rate);
		eicon_log(ccard, 128, "rT30:res  = %x\n", p->resolution);
		eicon_log(ccard, 128, "rT30:format = %x\n", p->format);
		eicon_log(ccard, 128, "rT30:pages_low = %x\n", p->pages_low);
		eicon_log(ccard, 128, "rT30:pages_high = %x\n", p->pages_high);
		eicon_log(ccard, 128, "rT30:atf  = %x\n", p->atf);
		eicon_log(ccard, 128, "rT30:control_bits_low = %x\n", p->control_bits_low);
		eicon_log(ccard, 128, "rT30:control_bits_high = %x\n", p->control_bits_high);
		eicon_log(ccard, 128, "rT30:feature_bits_low = %x\n", p->feature_bits_low);
		eicon_log(ccard, 128, "rT30:feature_bits_high = %x\n", p->feature_bits_high);
		//eicon_log(ccard, 128, "rT30:universal_5 = %x\n", p->universal_5);
		//eicon_log(ccard, 128, "rT30:universal_6 = %x\n", p->universal_6);
		//eicon_log(ccard, 128, "rT30:universal_7 = %x\n", p->universal_7);
		eicon_log(ccard, 128, "rT30:station_id_len = %x\n", p->station_id_len);
		eicon_log(ccard, 128, "rT30:head_line_len = %x\n", p->head_line_len);
		strncpy(st, p->station_id, p->station_id_len);
		st[p->station_id_len] = 0;
		eicon_log(ccard, 128, "rT30:station_id = <%s>\n", st);
	}
	if (!chan->fax) {
		eicon_log(ccard, 1, "idi_edata: parse to NULL fax struct, ERROR\n");
		return;
	}
	chan->fax->code = p->code;
	i = (p->station_id_len < FAXIDLEN) ? p->station_id_len : (FAXIDLEN - 1);
	memcpy(chan->fax->r_id, p->station_id, i);
	chan->fax->r_id[i] = 0;
	chan->fax->r_resolution = p->resolution;
	chan->fax->r_rate = p->rate - 1;
	chan->fax->r_binary = 0; /* no binary support */
	chan->fax->r_width = 0;
	chan->fax->r_length = 2;
	chan->fax->r_scantime = 0;
	chan->fax->r_compression = 0;
	chan->fax->r_ecm = 0;
	if (p->feature_bits_low & T30_FEATURE_BIT_2D_CODING) {
		chan->fax->r_compression = 1;
		if (p->feature_bits_low & T30_FEATURE_BIT_UNCOMPR_ENABLED) {
			chan->fax->r_compression = 2;
		}
	}
	if (p->feature_bits_low & T30_FEATURE_BIT_T6_CODING) {
		chan->fax->r_compression = 3;
	}

	if (p->feature_bits_low & T30_FEATURE_BIT_ECM) {
		chan->fax->r_ecm = 2;
		if (p->feature_bits_low & T30_FEATURE_BIT_ECM_64_BYTES)
			chan->fax->r_ecm = 1;
	}
}

void
idi_fax_send_header(eicon_card *card, eicon_chan *chan, int header)
{
	static __u16 wd2sff[] = {
		1728, 2048, 2432, 1216, 864
	};
	static __u16 ln2sff[2][3] = {
		{ 1143, 1401, 0 } , { 2287, 2802, 0 }
	};
	struct sk_buff *skb;
	eicon_sff_dochead *doc;
	eicon_sff_pagehead *page;
	u_char *docp;

	if (!chan->fax) {
		eicon_log(card, 1, "idi_fax: send head with NULL fax struct, ERROR\n");
		return;
	}
	if (header == 2) { /* DocHeader + PageHeader */
		skb = alloc_skb(sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead), GFP_ATOMIC);
	} else {
		skb = alloc_skb(sizeof(eicon_sff_pagehead), GFP_ATOMIC);
	}
	if (!skb) {
		eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_header()\n", chan->No);
		return;
	}

	if (header == 2) { /* DocHeader + PageHeader */
		docp = skb_put(skb, sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead));
		doc = (eicon_sff_dochead *) docp;
		page = (eicon_sff_pagehead *) (docp + sizeof(eicon_sff_dochead));
		memset(docp, 0,sizeof(eicon_sff_dochead)  + sizeof(eicon_sff_pagehead));
		doc->id = 0x66666653;
		doc->version = 0x01;
		doc->off1pagehead = sizeof(eicon_sff_dochead);
	} else {
		page = (eicon_sff_pagehead *)skb_put(skb, sizeof(eicon_sff_pagehead));
		memset(page, 0, sizeof(eicon_sff_pagehead));
	}

	switch(header) {
		case 1:	/* PageHeaderEnd */
			page->pageheadid = 254;
			page->pageheadlen = 0; 
			break;
		case 0: /* PageHeader */
		case 2: /* DocHeader + PageHeader */
			page->pageheadid = 254;
			page->pageheadlen = sizeof(eicon_sff_pagehead) - 2;
			page->resvert = chan->fax->resolution;
			page->reshoriz = 0; /* always 203 dpi */
			page->coding = 0; /* always 1D */
			page->linelength = wd2sff[chan->fax->width];
			page->pagelength = ln2sff[chan->fax->resolution][chan->fax->length]; 
			eicon_log(card, 128, "sSFF-Head: linelength = %d\n", page->linelength);
			eicon_log(card, 128, "sSFF-Head: pagelength = %d\n", page->pagelength);
			break;
	}
	idi_send_data(card, chan, 0, skb, 0, 0);
}

void
idi_fax_cmd(eicon_card *card, eicon_chan *chan) 
{
	isdn_ctrl cmd;

	if ((!card) || (!chan))
		return;

	if (!chan->fax) {
		eicon_log(card, 1, "idi_fax: cmd with NULL fax struct, ERROR\n");
		return;
	}
	switch (chan->fax->code) {
		case ISDN_TTY_FAX_DT:
			if (chan->fax->phase == ISDN_FAX_PHASE_B) {
				idi_send_edata(card, chan);
				break;
			}
			if (chan->fax->phase == ISDN_FAX_PHASE_D) {
				idi_send_edata(card, chan);
				break;
			}
			break;

		case ISDN_TTY_FAX_DR:
			if (chan->fax->phase == ISDN_FAX_PHASE_B) {
				idi_send_edata(card, chan);

				cmd.driver = card->myid;
				cmd.command = ISDN_STAT_FAXIND;
				cmd.arg = chan->No;
				chan->fax->r_code = ISDN_TTY_FAX_CFR;
				card->interface.statcallb(&cmd);

				cmd.driver = card->myid;
				cmd.command = ISDN_STAT_FAXIND;
				cmd.arg = chan->No;
				chan->fax->r_code = ISDN_TTY_FAX_RID;
				card->interface.statcallb(&cmd);

				/* telling 1-D compression */
				chan->fax->r_compression = 0;
				cmd.driver = card->myid;
				cmd.command = ISDN_STAT_FAXIND;
				cmd.arg = chan->No;
				chan->fax->r_code = ISDN_TTY_FAX_DCS;
				card->interface.statcallb(&cmd);

				chan->fax2.NextObject = FAX_OBJECT_DOCU;
				chan->fax2.PrevObject = FAX_OBJECT_DOCU;

				break;
			}
			if (chan->fax->phase == ISDN_FAX_PHASE_D) {
				idi_send_edata(card, chan);
				break;
			}
			break;

		case ISDN_TTY_FAX_ET:
				switch(chan->fax->fet) {
					case 0:
					case 1:
						idi_fax_send_header(card, chan, 0);
						break;
					case 2:
						idi_fax_send_header(card, chan, 1);
						break;
				}
			break;
	}
}

void
idi_edata_rcveop(eicon_card *card, eicon_chan *chan)
{
	isdn_ctrl cmd;

	if (!chan->fax) {
		eicon_log(card, 1, "idi_edata: rcveop with NULL fax struct, ERROR\n");
		return;
	}
	cmd.driver = card->myid;
	cmd.command = ISDN_STAT_FAXIND;
	cmd.arg = chan->No;
	chan->fax->r_code = ISDN_TTY_FAX_ET;
	card->interface.statcallb(&cmd);
}

void
idi_reset_fax_stat(eicon_chan *chan)
{
	chan->fax2.LineLen = 0;
	chan->fax2.LineData = 0;
	chan->fax2.LineDataLen = 0;
	chan->fax2.NullByteExist = 0;
	chan->fax2.Dle = 0;
	chan->fax2.PageCount = 0;
	chan->fax2.Eop = 0;
}

void
idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len)
{
	isdn_ctrl cmd;

	if (!chan->fax) {
		eicon_log(ccard, 1, "idi_edata: action with NULL fax struct, ERROR\n");
		return;
	}
	if (chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) {
		idi_parse_edata(ccard, chan, buffer, len);

		if (chan->fax->phase == ISDN_FAX_PHASE_A) {
			idi_reset_fax_stat(chan);

			chan->fsm_state = EICON_STATE_ACTIVE;
			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_BCONN;
			cmd.arg = chan->No;
			strcpy(cmd.parm.num, "");
			ccard->interface.statcallb(&cmd);

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_FCON;
			ccard->interface.statcallb(&cmd);

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_RID;
			ccard->interface.statcallb(&cmd);

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_DIS;
			ccard->interface.statcallb(&cmd);

			if (chan->fax->r_compression != 0) {
			/* telling fake compression in second DIS message */
				chan->fax->r_compression = 0;
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_FAXIND;
				cmd.arg = chan->No;
				chan->fax->r_code = ISDN_TTY_FAX_DIS;
				ccard->interface.statcallb(&cmd);
			}

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_SENT; /* OK message */
			ccard->interface.statcallb(&cmd);
		} else
		if (chan->fax->phase == ISDN_FAX_PHASE_D) {

			if ((chan->fax->code == EDATA_T30_MCF) &&
			    (chan->fax->fet != 2)) {
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_FAXIND;
				cmd.arg = chan->No;
				chan->fax->r_code = ISDN_TTY_FAX_PTS;
				ccard->interface.statcallb(&cmd);
			}

			switch(chan->fax->fet) {
				case 0:	/* new page */
					/* stay in phase D , wait on cmd +FDT */
					break;
				case 1:	/* new document */
					/* link-level switch to phase B */
					break;
				case 2:	/* session end */
				default:
					/* send_edata produces error on some */
					/* fax-machines here, so we don't */
					/* idi_send_edata(ccard, chan); */
					break;
			}
		}
	}

	if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
		idi_parse_edata(ccard, chan, buffer, len);

		if ((chan->fax->code == EDATA_T30_DCS) &&
		    (chan->fax->phase == ISDN_FAX_PHASE_A)) {
			idi_reset_fax_stat(chan);

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_BCONN;
			cmd.arg = chan->No;
			strcpy(cmd.parm.num, "");
			ccard->interface.statcallb(&cmd);

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_FCON_I;
			ccard->interface.statcallb(&cmd);
		} else
		if ((chan->fax->code == EDATA_T30_TRAIN_OK) &&
		    (chan->fax->phase == ISDN_FAX_PHASE_A)) {
			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_RID;
			ccard->interface.statcallb(&cmd);

			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK;
			ccard->interface.statcallb(&cmd);
		} else
		if ((chan->fax->code == EDATA_T30_TRAIN_OK) &&
		    (chan->fax->phase == ISDN_FAX_PHASE_B)) {
			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_FAXIND;
			cmd.arg = chan->No;
			chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK;
			ccard->interface.statcallb(&cmd);
		} else
		if (chan->fax->phase == ISDN_FAX_PHASE_C) {
			switch(chan->fax->code) {
				case EDATA_T30_TRAIN_OK:
					idi_send_edata(ccard, chan);
					break;
				case EDATA_T30_MPS:
					chan->fax->fet = 0;
					idi_edata_rcveop(ccard, chan);
					break;
				case EDATA_T30_EOM:
					chan->fax->fet = 1;
					idi_edata_rcveop(ccard, chan);
					break;
				case EDATA_T30_EOP:
					chan->fax->fet = 2;
					idi_edata_rcveop(ccard, chan);
					break;
			}
		}
	}
}

void
fax_put_rcv(eicon_card *ccard, eicon_chan *chan, u_char *Data, int len)
{
	struct sk_buff *skb;
	
        skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC);
	if (!skb) {
		eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_put_rcv()\n", chan->No);
		return;
	}
	skb_reserve(skb, MAX_HEADER_LEN);
	memcpy(skb_put(skb, len), Data, len);
	ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
}

void
idi_faxdata_rcv(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb)
{
	eicon_OBJBUFFER InBuf;
	eicon_OBJBUFFER LineBuf;
	unsigned int Length = 0;
	unsigned int aLength = 0;
	unsigned int ObjectSize = 0;
	unsigned int ObjHeadLen = 0;
	unsigned int ObjDataLen = 0;
	__u8 Recordtype;
	__u8 PageHeaderLen;	
	__u8 Event;
	eicon_sff_pagehead *ob_page;

	__u16 Cl2Eol = 0x8000;

#	define EVENT_NONE	0
#	define EVENT_NEEDDATA	1

	if (!chan->fax) {
		eicon_log(ccard, 1, "idi_fax: rcvdata with NULL fax struct, ERROR\n");
		return;
	}

#if 0 
	eicon_sff_dochead *doc = (eicon_sff_dochead *)skb->data;
	eicon_sff_pagehead *page = (eicon_sff_pagehead *)skb->data + sizeof(eicon_sff_dochead);

	printk(KERN_DEBUG"SFF: doc %d / page %d (skb : %d)\n", 
		sizeof(eicon_sff_dochead), 
		sizeof(eicon_sff_pagehead), skb->len);

	if (skb->len >= sizeof(eicon_sff_dochead)) {
		printk(KERN_DEBUG"SFF: id = 0x%x\n", doc->id);
		printk(KERN_DEBUG"SFF: version = 0x%x\n", doc->version);
		printk(KERN_DEBUG"SFF: reserved1 = 0x%x\n", doc->reserved1);
		printk(KERN_DEBUG"SFF: userinfo = 0x%x\n", doc->userinfo);
		printk(KERN_DEBUG"SFF: pagecount = 0x%x\n", doc->pagecount);
		printk(KERN_DEBUG"SFF: off1pagehead = 0x%x\n", doc->off1pagehead);
		printk(KERN_DEBUG"SFF: offnpagehead = 0x%x\n", doc->offnpagehead);
		printk(KERN_DEBUG"SFF: offdocend = 0x%x\n", doc->offdocend);
	}
	if (skb->len >= (sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead))) {
		printk(KERN_DEBUG"SFFp: id = 0x%x\n", page->pageheadid);
		printk(KERN_DEBUG"SFFp: len = 0x%x\n", page->pageheadlen);
		printk(KERN_DEBUG"SFFp: resvert = 0x%x\n", page->resvert);
		printk(KERN_DEBUG"SFFp: reshoriz = 0x%x\n", page->reshoriz);
		printk(KERN_DEBUG"SFFp: coding = 0x%x\n", page->coding);
		printk(KERN_DEBUG"SFFp: reserved2 = 0x%x\n", page->reserved2);
		printk(KERN_DEBUG"SFFp: linelength = 0x%x\n", page->linelength);
		printk(KERN_DEBUG"SFFp: pagelength = 0x%x\n", page->pagelength);
		printk(KERN_DEBUG"SFFp: offprevpage = 0x%x\n", page->offprevpage);
		printk(KERN_DEBUG"SFFp: offnextpage = 0x%x\n", page->offnextpage);
	}
#endif

	
	if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
		InBuf.Data = skb->data;
		InBuf.Size = skb->len;
		InBuf.Len  = 0;
		InBuf.Next = InBuf.Data;
		LineBuf.Data = chan->fax2.abLine;
		LineBuf.Size = sizeof(chan->fax2.abLine);
		LineBuf.Len  = chan->fax2.LineLen;
		LineBuf.Next = LineBuf.Data + LineBuf.Len;

		Event = EVENT_NONE;
		while (Event == EVENT_NONE) {
			switch(chan->fax2.NextObject) {
				case FAX_OBJECT_DOCU:
						Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
						if (Length < sizeof(eicon_sff_dochead)) {
							Event = EVENT_NEEDDATA;
							break;
						}
						ObjectSize = sizeof(eicon_sff_dochead);
						Length = ObjectSize;
						if (LineBuf.Len < Length) {
							Length -= LineBuf.Len;
							LineBuf.Len = 0;
							LineBuf.Next = LineBuf.Data;
							InBuf.Len += Length;
							InBuf.Next += Length;
						} else {
							LineBuf.Len -= Length;
							LineBuf.Next = LineBuf.Data + LineBuf.Len;
							memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
						}
						chan->fax2.PrevObject = FAX_OBJECT_DOCU;
						chan->fax2.NextObject = FAX_OBJECT_PAGE;
					break;

				case FAX_OBJECT_PAGE:
						Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
						if (Length < 2) {
							Event = EVENT_NEEDDATA;
							break;
						}
						if (LineBuf.Len == 0) {
							*LineBuf.Next++ = *InBuf.Next++;
							LineBuf.Len++;
							InBuf.Len++;
						}
						if (LineBuf.Len == 1) {
							*LineBuf.Next++ = *InBuf.Next++;
							LineBuf.Len++;
							InBuf.Len++;
						}
						PageHeaderLen = *(LineBuf.Data + 1);
						ObjectSize = (PageHeaderLen == 0) ? 2 : sizeof(eicon_sff_pagehead);
						if (Length < ObjectSize) {
							Event = EVENT_NEEDDATA;
							break;
						}
						Length = ObjectSize;
						/* extract page dimensions */
						if (LineBuf.Len < Length) {
							aLength = Length - LineBuf.Len;
							memcpy(LineBuf.Next, InBuf.Next, aLength);
							LineBuf.Next += aLength;
							InBuf.Next += aLength;
							LineBuf.Len += aLength;
							InBuf.Len += aLength;
						}
						if (Length > 2) {
							ob_page = (eicon_sff_pagehead *)LineBuf.Data;
							switch(ob_page->linelength) {
								case 2048:
									chan->fax->r_width = 1;
									break;
								case 2432:
									chan->fax->r_width = 2;
									break;
								case 1216:
									chan->fax->r_width = 3;
									break;
								case 864:
									chan->fax->r_width = 4;
									break;
								case 1728:
								default:
									chan->fax->r_width = 0;
							}
							switch(ob_page->pagelength) {
								case 1143:
								case 2287:
									chan->fax->r_length = 0;
									break;
								case 1401:
								case 2802:
									chan->fax->r_length = 1;
									break;
								default:
									chan->fax->r_length = 2;
							}
							eicon_log(ccard, 128, "rSFF-Head: linelength = %d\n", ob_page->linelength);
							eicon_log(ccard, 128, "rSFF-Head: pagelength = %d\n", ob_page->pagelength);
						}
						LineBuf.Len -= Length;
						LineBuf.Next = LineBuf.Data + LineBuf.Len;
						memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);

						chan->fax2.PrevObject = FAX_OBJECT_PAGE;
						chan->fax2.NextObject = FAX_OBJECT_LINE;
					break;

				case FAX_OBJECT_LINE:
						Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
						if (Length < 1) {
							Event = EVENT_NEEDDATA;
							break;
						}
						if (LineBuf.Len == 0) {
							*LineBuf.Next++ = *InBuf.Next++;
							LineBuf.Len++;
							InBuf.Len++;
						}
						Recordtype = *LineBuf.Data;
						if (Recordtype == 0) {
							/* recordtype pixel row (2 byte length) */
							ObjHeadLen = 3;
							if (Length < ObjHeadLen) {
								Event = EVENT_NEEDDATA;
								break;
							}
							while (LineBuf.Len < ObjHeadLen) {
								*LineBuf.Next++ = *InBuf.Next++;
								LineBuf.Len++;
								InBuf.Len++;
							}
							ObjDataLen = *((__u16*) (LineBuf.Data + 1));
							ObjectSize = ObjHeadLen + ObjDataLen;
							if (Length < ObjectSize) {
								Event = EVENT_NEEDDATA;
								break;
							}
						} else
						if ((Recordtype >= 1) && (Recordtype <= 216)) {
							/* recordtype pixel row (1 byte length) */
							ObjHeadLen = 1;
							ObjDataLen = Recordtype;
							ObjectSize = ObjHeadLen + ObjDataLen;
							if (Length < ObjectSize) {
								Event = EVENT_NEEDDATA;
								break;
							}
						} else
						if ((Recordtype >= 217) && (Recordtype <= 253)) {
							/* recordtype empty lines */
							ObjHeadLen = 1;
							ObjDataLen = 0;
							ObjectSize = ObjHeadLen + ObjDataLen;
							LineBuf.Len--;
							LineBuf.Next = LineBuf.Data + LineBuf.Len;
							memmove(LineBuf.Data, LineBuf.Data + 1, LineBuf.Len);
							break;
						} else
						if (Recordtype == 254) {
							/* recordtype page header */
							chan->fax2.PrevObject = FAX_OBJECT_LINE;
							chan->fax2.NextObject = FAX_OBJECT_PAGE;
							break;
						} else {
							/* recordtype user information */
							ObjHeadLen = 2;
							if (Length < ObjHeadLen) {
								Event = EVENT_NEEDDATA;
								break;
							}
							while (LineBuf.Len < ObjHeadLen) {
								*LineBuf.Next++ = *InBuf.Next++;
								LineBuf.Len++;
								InBuf.Len++;
							}
							ObjDataLen = *(LineBuf.Data + 1);
							ObjectSize = ObjHeadLen + ObjDataLen;
							if (ObjDataLen == 0) {
								/* illegal line coding */
								LineBuf.Len -= ObjHeadLen;
								LineBuf.Next = LineBuf.Data + LineBuf.Len;
								memmove(LineBuf.Data, LineBuf.Data + ObjHeadLen, LineBuf.Len);
								break;
							} else {
								/* user information */
								if (Length < ObjectSize) {
									Event = EVENT_NEEDDATA;
									break;
								}
								Length = ObjectSize;
								if (LineBuf.Len < Length) {
									Length -= LineBuf.Len;
									LineBuf.Len = 0;
									LineBuf.Next = LineBuf.Data;
									InBuf.Len += Length;
									InBuf.Next += Length;
								} else {
									LineBuf.Len -= Length;
									LineBuf.Next = LineBuf.Data + LineBuf.Len;
									memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
								}
							}
							break;	
						}
						Length = ObjectSize;
						if (LineBuf.Len > ObjHeadLen) {
							fax_put_rcv(ccard, chan, LineBuf.Data + ObjHeadLen,
									(LineBuf.Len - ObjHeadLen));
						}
						Length -= LineBuf.Len;
						LineBuf.Len = 0;
						LineBuf.Next = LineBuf.Data;
						if (Length > 0) {
							fax_put_rcv(ccard, chan, InBuf.Next, Length);
							InBuf.Len += Length;
							InBuf.Next += Length;
						}
						fax_put_rcv(ccard, chan, (__u8 *)&Cl2Eol, sizeof(Cl2Eol));
					break;
			} /* end of switch (chan->fax2.NextObject) */
		} /* end of while (Event==EVENT_NONE) */
		if (InBuf.Len < InBuf.Size) {
			Length = InBuf.Size - InBuf.Len;
			if ((LineBuf.Len + Length) > LineBuf.Size) {
				eicon_log(ccard, 1, "idi_fax: Ch%d: %d bytes dropping, small buffer\n", chan->No,
					Length);
				} else {
					memcpy(LineBuf.Next, InBuf.Next, Length);
					LineBuf.Len += Length;
				}
		}
		chan->fax2.LineLen = LineBuf.Len;
	} else { /* CONN_OUT */
		/* On CONN_OUT we do not need incoming data, drop it */
		/* maybe later for polling */
	}

#	undef EVENT_NONE
#	undef EVENT_NEEDDATA

	return;
}

int
idi_fax_send_outbuf(eicon_card *ccard, eicon_chan *chan, eicon_OBJBUFFER *OutBuf)
{
	struct sk_buff *skb;

	skb = alloc_skb(OutBuf->Len, GFP_ATOMIC);
	if (!skb) {
		eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_outbuf()\n", chan->No);
		return(-1);
	}
	memcpy(skb_put(skb, OutBuf->Len), OutBuf->Data, OutBuf->Len);

	OutBuf->Len = 0;
	OutBuf->Next = OutBuf->Data;

	return(idi_send_data(ccard, chan, 0, skb, 1, 0));
}

int
idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb)
{
	isdn_ctrl cmd;
	eicon_OBJBUFFER InBuf;
	__u8 InData;
	__u8 InMask;
	eicon_OBJBUFFER OutBuf;
	eicon_OBJBUFFER LineBuf;
	__u32 LineData;
	unsigned int LineDataLen;
	__u8 Byte;
	__u8 Event;
	int ret = 1;

#	define EVENT_NONE	0
#	define EVENT_EOD	1
#	define EVENT_EOL 	2
#	define EVENT_EOP 	3

	if ((!ccard) || (!chan))
		return -1;

	if (!chan->fax) {
		eicon_log(ccard, 1, "idi_fax: senddata with NULL fax struct, ERROR\n");
		return -1;
	}

	if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
		/* Simply ignore any data written in data mode when receiving a fax.    */
		/* This is not completely correct because only XON's should come here.  */
        	dev_kfree_skb(skb);
		return 1;
	}

	if (chan->fax->phase != ISDN_FAX_PHASE_C) {
        	dev_kfree_skb(skb);
		return 1;
	}

        if (chan->queued + skb->len > 1200)
                return 0;
	if (chan->pqueued > 1)
		return 0;

	InBuf.Data = skb->data;
	InBuf.Size = skb->len;
	InBuf.Len  = 0;
	InBuf.Next = InBuf.Data;
	InData = 0;
	InMask = 0;

	LineBuf.Data = chan->fax2.abLine;
	LineBuf.Size = sizeof(chan->fax2.abLine);
	LineBuf.Len  = chan->fax2.LineLen;
	LineBuf.Next = LineBuf.Data + LineBuf.Len;
	LineData = chan->fax2.LineData;
	LineDataLen = chan->fax2.LineDataLen;

	OutBuf.Data = chan->fax2.abFrame;
	OutBuf.Size = sizeof(chan->fax2.abFrame);
	OutBuf.Len = 0;
	OutBuf.Next = OutBuf.Data;

	Event = EVENT_NONE;

	chan->fax2.Eop = 0;

	for (;;) {
	  for (;;) {
		if (InMask == 0) {
			if (InBuf.Len >= InBuf.Size) {
				Event = EVENT_EOD;
				break;
			}
			if ((chan->fax2.Dle != _DLE_) && *InBuf.Next == _DLE_) {
				chan->fax2.Dle = _DLE_;
				InBuf.Next++;
				InBuf.Len++;
				if (InBuf.Len >= InBuf.Size) {
					Event = EVENT_EOD;
					break;
				}
			}
			if (chan->fax2.Dle == _DLE_) {
				chan->fax2.Dle = 0;
				if (*InBuf.Next == _ETX_) {
					Event = EVENT_EOP;
					break;
				} else
				if (*InBuf.Next == _DLE_) {
					/* do nothing */
				} else {
					eicon_log(ccard, 1,
						"idi_err: Ch%d: unknown DLE escape %02x found\n",
							chan->No, *InBuf.Next);
					InBuf.Next++;
					InBuf.Len++;
					if (InBuf.Len >= InBuf.Size) {
						Event = EVENT_EOD;
						break;
					}
				}
			}
			InBuf.Len++;
			InData = *InBuf.Next++;
			InMask = (chan->fax->bor) ? 0x80 : 0x01;
		}
		while (InMask) {
			LineData >>= 1;
			LineDataLen++;
			if (InData & InMask)
				LineData |= 0x80000000;
			if (chan->fax->bor)
				InMask >>= 1;
			else
				InMask <<= 1;

			if ((LineDataLen >= T4_EOL_BITSIZE) &&
			   ((LineData & T4_EOL_MASK_DWORD) == T4_EOL_DWORD)) {
				Event = EVENT_EOL;
				if (LineDataLen > T4_EOL_BITSIZE) {
					Byte = (__u8)
						((LineData & ~T4_EOL_MASK_DWORD) >>
						(32 - LineDataLen));
					if (Byte == 0) {
						if (! chan->fax2.NullByteExist) {
							chan->fax2.NullBytesPos = LineBuf.Len;
							chan->fax2.NullByteExist = 1;
						}
					} else {
						chan->fax2.NullByteExist = 0;
					}
					if (LineBuf.Len < LineBuf.Size) {
						*LineBuf.Next++  = Byte;
						LineBuf.Len++;
					}
				}
				LineDataLen = 0;
				break;
			}
			if (LineDataLen >= T4_EOL_BITSIZE + 8) {
				Byte = (__u8)
					((LineData & ~T4_EOL_MASK_DWORD) >>
					(32 - T4_EOL_BITSIZE - 8));
				LineData &= T4_EOL_MASK_DWORD;
				LineDataLen = T4_EOL_BITSIZE;
				if (Byte == 0) {
					if (! chan->fax2.NullByteExist) {
						chan->fax2.NullBytesPos = LineBuf.Len;
						chan->fax2.NullByteExist = 1;
					}
				} else {
					chan->fax2.NullByteExist = 0;
				}
				if (LineBuf.Len < LineBuf.Size) {
					*LineBuf.Next++  = Byte; 
					LineBuf.Len++;
				}
			}
		}
		if (Event != EVENT_NONE)
			break;
	  }

		if ((Event != EVENT_EOL) && (Event != EVENT_EOP))
			break;

		if ((Event == EVENT_EOP) && (LineDataLen > 0)) {
			LineData >>= 32 - LineDataLen;
			LineDataLen = 0;
			while (LineData != 0) {
				Byte = (__u8) LineData;
				LineData >>= 8;
				if (Byte == 0) {
					if (! chan->fax2.NullByteExist) {
						chan->fax2.NullBytesPos = LineBuf.Len;
						chan->fax2.NullByteExist = 1;
					}
				} else {
					chan->fax2.NullByteExist = 0;
				}
				if (LineBuf.Len < LineBuf.Size) {
					*LineBuf.Next++  = Byte;
					LineBuf.Len++;
				}
				
			}
		}
		if (chan->fax2.NullByteExist) {
			if (chan->fax2.NullBytesPos == 0) {
				LineBuf.Len = 0;
			} else {
				LineBuf.Len = chan->fax2.NullBytesPos + 1;
			}
		}
		if (LineBuf.Len > 0) {
			if (OutBuf.Len + LineBuf.Len + SFF_LEN_FLD_SIZE > OutBuf.Size) {
				ret = idi_fax_send_outbuf(ccard, chan, &OutBuf);
			}
			if (LineBuf.Len <= 216) {
				*OutBuf.Next++ = (__u8) LineBuf.Len;
				OutBuf.Len++;
			} else {
				*OutBuf.Next++ = 0;
				*((__u16 *) OutBuf.Next)++ = (__u16) LineBuf.Len;
				OutBuf.Len += 3;
			}
			memcpy(OutBuf.Next, LineBuf.Data, LineBuf.Len);
			OutBuf.Next += LineBuf.Len;
			OutBuf.Len  += LineBuf.Len;
		}
		LineBuf.Len = 0;
		LineBuf.Next = LineBuf.Data;
		chan->fax2.NullByteExist = 0;
		if (Event == EVENT_EOP)
			break;

		Event = EVENT_NONE;
	}

	if (Event == EVENT_EOP) {
		chan->fax2.Eop = 1;
		chan->fax2.PageCount++;
		cmd.driver = ccard->myid;
		cmd.command = ISDN_STAT_FAXIND;
		cmd.arg = chan->No;
		chan->fax->r_code = ISDN_TTY_FAX_EOP;
		ccard->interface.statcallb(&cmd);
	}
	if (OutBuf.Len > 0) {
		ret = idi_fax_send_outbuf(ccard, chan, &OutBuf);
	}

	chan->fax2.LineLen = LineBuf.Len;
	chan->fax2.LineData = LineData;
	chan->fax2.LineDataLen = LineDataLen;

#	undef EVENT_NONE
#	undef EVENT_EOD
#	undef EVENT_EOL
#	undef EVENT_EOP

	if (ret >= 0)
	        dev_kfree_skb(skb);
	if (ret == 0)
		ret = 1;
	return(ret);
}

void
idi_fax_hangup(eicon_card *ccard, eicon_chan *chan)
{
	isdn_ctrl cmd;

	if (!chan->fax) {
		eicon_log(ccard, 1, "idi_fax: hangup with NULL fax struct, ERROR\n");
		return;
	}
	if ((chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) &&
	    (chan->fax->code == 0)) {
		cmd.driver = ccard->myid;
		cmd.command = ISDN_STAT_FAXIND;
		cmd.arg = chan->No;
		chan->fax->r_code = ISDN_TTY_FAX_PTS;
		ccard->interface.statcallb(&cmd);
	}
	if ((chan->fax->code > 1) && (chan->fax->code < 120))
		chan->fax->code += 120;
	eicon_log(ccard, 8, "idi_fax: Ch%d: Hangup (code=%d)\n", chan->No, chan->fax->code);
	chan->fax->r_code = ISDN_TTY_FAX_HNG;
	cmd.driver = ccard->myid;
	cmd.command = ISDN_STAT_FAXIND;
	cmd.arg = chan->No;
	ccard->interface.statcallb(&cmd);
}

#endif	/******** FAX ********/

int
idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len)
{
	struct sk_buff *skb;
	struct sk_buff *skb2;
	eicon_REQ *reqbuf;
	eicon_chan_ptr *chan2;

	if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) {
		eicon_log(card, 1, "idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state);
		return -ENODEV;
	}
	eicon_log(card, 8, "idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No,
			UReq, buffer[0], buffer[1], buffer[2], buffer[3]);

	skb = alloc_skb(sizeof(eicon_REQ) + len + 1, GFP_ATOMIC);
	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

	if ((!skb) || (!skb2)) {
		eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No);
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
		return -ENOMEM;
	}

	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
	chan2->ptr = chan;

	reqbuf = (eicon_REQ *)skb_put(skb, 1 + len + sizeof(eicon_REQ));

	reqbuf->Req = N_UDATA;
	reqbuf->ReqCh = chan->e.IndCh;
	reqbuf->ReqId = 1;

	reqbuf->XBuffer.length = len + 1;
	reqbuf->XBuffer.P[0] = UReq;
	memcpy(&reqbuf->XBuffer.P[1], buffer, len);
	reqbuf->Reference = 1; /* Net Entity */

	skb_queue_tail(&chan->e.X, skb);
	skb_queue_tail(&card->sndq, skb2);
	eicon_schedule_tx(card);
	return (0);
}

void
idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value)
{
	u_char buf[6];
	struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf;

	if ((!ccard) || (!chan))
		return;

	memset(buf, 0, 6);
	switch(cmd) {
		case ISDN_AUDIO_SETDD:
			if (value[0]) {
				dtmf_buf->tone = (__u16) (value[1] * 5);
				dtmf_buf->gap = (__u16) (value[1] * 5);
				idi_send_udata(ccard, chan,
					DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER,
					buf, 4);
			} else {
				idi_send_udata(ccard, chan,
					DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER,
					buf, 0);
			}
			break;
	}
}

void
idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
{
	isdn_ctrl cmd;
	eicon_dsp_ind *p = (eicon_dsp_ind *) (&buffer[1]);
        static char *connmsg[] =
        {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34",
         "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90",
         "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"};
	static u_char dtmf_code[] = {
	'1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D'
	};

	if ((!ccard) || (!chan))
		return;

	switch (buffer[0]) {
		case DSP_UDATA_INDICATION_SYNC:
			eicon_log(ccard, 16, "idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time);
			break;
		case DSP_UDATA_INDICATION_DCD_OFF:
			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time);
			break;
		case DSP_UDATA_INDICATION_DCD_ON:
			if ((chan->l2prot == ISDN_PROTO_L2_MODEM) &&
			    (chan->fsm_state == EICON_STATE_WMCONN)) {
				chan->fsm_state = EICON_STATE_ACTIVE;
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_BCONN;
				cmd.arg = chan->No;
				sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]);
				ccard->interface.statcallb(&cmd);
			}
			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time);
			eicon_log(ccard, 8, "idi_ind: Ch%d: %d %d %d %d\n", chan->No,
				p->norm, p->options, p->speed, p->delay); 
			break;
		case DSP_UDATA_INDICATION_CTS_OFF:
			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time);
			break;
		case DSP_UDATA_INDICATION_CTS_ON:
			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time);
			eicon_log(ccard, 8, "idi_ind: Ch%d: %d %d %d %d\n", chan->No,
				p->norm, p->options, p->speed, p->delay); 
			break;
		case DSP_UDATA_INDICATION_DISCONNECT:
			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]);
			break;
		case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED:
			eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No,
				dtmf_code[buffer[1]]);
			cmd.driver = ccard->myid;
			cmd.command = ISDN_STAT_AUDIO;
			cmd.parm.num[0] = ISDN_AUDIO_DTMF;
			cmd.parm.num[1] = dtmf_code[buffer[1]];
			cmd.arg = chan->No;
			ccard->interface.statcallb(&cmd);
			break;
		default:
			eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
	}
}

void
eicon_parse_trace(eicon_card *ccard, unsigned char *buffer, int len)
{
	int i,j,n;
	int buflen = len * 3 + 30;
	char *p;
	struct trace_s {
		unsigned long time;
		unsigned short size;
		unsigned short code;
		unsigned char data[1];
	} *q;

	if (!(p = kmalloc(buflen, GFP_ATOMIC))) {
		eicon_log(ccard, 1, "idi_err: Ch??: could not allocate trace buffer\n");
		return;
	}
	memset(p, 0, buflen);
	q = (struct trace_s *)buffer;

	if (DebugVar & 512) {
		if ((q->code == 3) || (q->code == 4)) {
			n = (short) *(q->data);
			if (n) {
				j = sprintf(p, "DTRC:");
				for (i = 0; i < n; i++) {
					j += sprintf(p + j, "%02x ", q->data[i+2]);
				}
				j += sprintf(p + j, "\n");
			}
		}
	} else {
		j = sprintf(p, "XLOG: %lx %04x %04x ",
			q->time, q->size, q->code);

		for (i = 0; i < q->size; i++) {
			j += sprintf(p + j, "%02x ", q->data[i]);
		}
		j += sprintf(p + j, "\n");
	}
	if (strlen(p))
		eicon_putstatus(ccard, p);
	kfree(p);
}

void
idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
{
	int tmp;
	char tnum[64];
	int dlev;
	int free_buff;
	ulong flags;
	struct sk_buff *skb2;
        eicon_IND *ind = (eicon_IND *)skb->data;
	eicon_chan *chan;
	idi_ind_message message;
	isdn_ctrl cmd;

	if (!ccard) {
		eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ind\n");
  		dev_kfree_skb(skb);
		return;
	}

	if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
		eicon_log(ccard, 1, "idi_err: Ch??: null chan in handle_ind\n");
  		dev_kfree_skb(skb);
		return;
	}
	
	if ((ind->Ind != 8) && (ind->Ind != 0xc))
		dlev = 144;
	else
		dlev = 128;

       	eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%x Id=%x Ch=%x MInd=%x MLen=%x Len=%x\n", chan->No,
	        ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);

	free_buff = 1;
	/* Signal Layer */
	if (chan->e.D3Id == ind->IndId) {
		idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
		switch(ind->Ind) {
			case HANGUP:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Hangup\n", chan->No);
		                while((skb2 = skb_dequeue(&chan->e.X))) {
					dev_kfree_skb(skb2);
				}
				spin_lock_irqsave(&eicon_lock, flags);
				chan->queued = 0;
				chan->pqueued = 0;
				chan->waitq = 0;
				chan->waitpq = 0;
				spin_unlock_irqrestore(&eicon_lock, flags);
				if (message.e_cau[0] & 0x7f) {
					cmd.driver = ccard->myid;
					cmd.arg = chan->No;
					sprintf(cmd.parm.num,"E%02x%02x", 
						chan->cause[0]&0x7f, message.e_cau[0]&0x7f); 
					cmd.command = ISDN_STAT_CAUSE;
					ccard->interface.statcallb(&cmd);
				}
				chan->cause[0] = 0; 
				if (((chan->fsm_state == EICON_STATE_ACTIVE) ||
				    (chan->fsm_state == EICON_STATE_WMCONN)) ||
				    ((chan->l2prot == ISDN_PROTO_L2_FAX) &&
				    (chan->fsm_state == EICON_STATE_OBWAIT))) {
					chan->fsm_state = EICON_STATE_NULL;
				} else {
					if (chan->e.B2Id)
						idi_do_req(ccard, chan, REMOVE, 1);
#if 0
					if (chan->e.D3Id) {
						idi_do_req(ccard, chan, REMOVE, 0);
						idi_do_req(ccard, chan, ASSIGN, 0);
					}
#endif
					chan->statectrl &= ~WAITING_FOR_HANGUP;
					chan->statectrl &= ~IN_HOLD;
					if (chan->statectrl & HAVE_CONN_REQ) {
						eicon_log(ccard, 32, "idi_req: Ch%d: queueing delayed conn_req\n", chan->No);
						chan->statectrl &= ~HAVE_CONN_REQ;
						if ((chan->tskb1) && (chan->tskb2)) {
							skb_queue_tail(&chan->e.X, chan->tskb1);
							skb_queue_tail(&ccard->sndq, chan->tskb2); 
							eicon_schedule_tx(ccard);
						}
						chan->tskb1 = NULL;
						chan->tskb2 = NULL;
					} else {
						chan->fsm_state = EICON_STATE_NULL;
						cmd.driver = ccard->myid;
						cmd.arg = chan->No;
						cmd.command = ISDN_STAT_DHUP;
						ccard->interface.statcallb(&cmd);
						eicon_idi_listen_req(ccard, chan);
#ifdef CONFIG_ISDN_TTY_FAX
						chan->fax = 0;
#endif
					}
				}
				break;
			case INDICATE_IND:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Indicate_Ind\n", chan->No);
				if (chan->fsm_state != EICON_STATE_LISTEN) {
					eicon_log(ccard, 1, "idi_err: Ch%d: Incoming call on wrong state (%d).\n",
						chan->No, chan->fsm_state);
					idi_do_req(ccard, chan, HANGUP, 0);
					break;
				}
				chan->fsm_state = EICON_STATE_ICALL;
				idi_bc2si(message.bc, message.hlc, message.sin, &chan->si1, &chan->si2);
				strcpy(chan->cpn, message.cpn + 1);
				strcpy(chan->oad, message.oad);
				strcpy(chan->dsa, message.dsa);
				strcpy(chan->osa, message.osa);
				chan->plan = message.plan;
				chan->screen = message.screen;
				try_stat_icall_again: 
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_ICALL;
				cmd.arg = chan->No;
				cmd.parm.setup.si1 = chan->si1;
				cmd.parm.setup.si2 = chan->si2;
				strcpy(tnum, chan->cpn);
				if (strlen(chan->dsa)) {
					strcat(tnum, ".");
					strcat(tnum, chan->dsa);
				}
				tnum[ISDN_MSNLEN - 1] = 0;
				strcpy(cmd.parm.setup.eazmsn, tnum);
				strcpy(tnum, chan->oad);
				if (strlen(chan->osa)) {
					strcat(tnum, ".");
					strcat(tnum, chan->osa);
				}
				tnum[ISDN_MSNLEN - 1] = 0;
				strcpy(cmd.parm.setup.phone, tnum);
				cmd.parm.setup.plan = chan->plan;
				cmd.parm.setup.screen = chan->screen;
				tmp = ccard->interface.statcallb(&cmd);
				switch(tmp) {
					case 0: /* no user responding */
						idi_do_req(ccard, chan, HANGUP, 0);
						chan->fsm_state = EICON_STATE_NULL;
						break;
					case 1: /* alert */
						eicon_log(ccard, 8, "idi_req: Ch%d: Call Alert\n", chan->No);
						if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) {
							chan->fsm_state = EICON_STATE_ICALL;
							idi_do_req(ccard, chan, CALL_ALERT, 0);
						}
						break;
					case 2: /* reject */
						eicon_log(ccard, 8, "idi_req: Ch%d: Call Reject\n", chan->No);
						idi_do_req(ccard, chan, REJECT, 0);
						break;
					case 3: /* incomplete number */
						eicon_log(ccard, 8, "idi_req: Ch%d: Incomplete Number\n", chan->No);
						chan->fsm_state = EICON_STATE_ICALLW;
						break;
				}
				break;
			case INFO_IND:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Info_Ind\n", chan->No);
				if ((chan->fsm_state == EICON_STATE_ICALLW) &&
				    (message.cpn[0])) {
					strcat(chan->cpn, message.cpn + 1);
					goto try_stat_icall_again;
				}
				break;
			case CALL_IND:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Ind\n", chan->No);
				if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) {
					chan->fsm_state = EICON_STATE_IBWAIT;
					cmd.driver = ccard->myid;
					cmd.command = ISDN_STAT_DCONN;
					cmd.arg = chan->No;
					ccard->interface.statcallb(&cmd);
					switch(chan->l2prot) {
						case ISDN_PROTO_L2_FAX:
#ifdef CONFIG_ISDN_TTY_FAX
							if (chan->fax)
								chan->fax->phase = ISDN_FAX_PHASE_A;
#endif
							break;
						case ISDN_PROTO_L2_MODEM:
							/* do nothing, wait for connect */
							break;
						case ISDN_PROTO_L2_V11096:
						case ISDN_PROTO_L2_V11019:
						case ISDN_PROTO_L2_V11038:
						case ISDN_PROTO_L2_TRANS:
							idi_do_req(ccard, chan, N_CONNECT, 1);
							break;
						default:;
							/* On most incoming calls we use automatic connect */
							/* idi_do_req(ccard, chan, N_CONNECT, 1); */
					}
				} else {
					if (chan->fsm_state != EICON_STATE_ACTIVE)
						idi_hangup(ccard, chan);
				}
				break;
			case CALL_CON:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Con\n", chan->No);
				if (chan->fsm_state == EICON_STATE_OCALL) {
					/* check if old NetID has been removed */
					if (chan->e.B2Id) {
						eicon_log(ccard, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n",
							chan->No, chan->e.B2Id);
						idi_do_req(ccard, chan, REMOVE, 1);
					}
#ifdef CONFIG_ISDN_TTY_FAX
					if (chan->l2prot == ISDN_PROTO_L2_FAX) {
						if (chan->fax) {
							chan->fax->phase = ISDN_FAX_PHASE_A;
						} else {
							eicon_log(ccard, 1, "idi_ind: Call_Con with NULL fax struct, ERROR\n");
							idi_hangup(ccard, chan);
							break;
						}
					}
#endif
					chan->fsm_state = EICON_STATE_OBWAIT;
					cmd.driver = ccard->myid;
					cmd.command = ISDN_STAT_DCONN;
					cmd.arg = chan->No;
					ccard->interface.statcallb(&cmd);

					idi_do_req(ccard, chan, ASSIGN, 1); 
					idi_do_req(ccard, chan, N_CONNECT, 1);
				} else
					idi_hangup(ccard, chan);
				break;
			case AOC_IND:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Advice of Charge\n", chan->No);
				break;
			case CALL_HOLD_ACK:
				chan->statectrl |= IN_HOLD;
				eicon_log(ccard, 8, "idi_ind: Ch%d: Call Hold Ack\n", chan->No);
				break;
			case SUSPEND_REJ:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Suspend Rejected\n", chan->No);
				break;
			case SUSPEND:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Suspend Ack\n", chan->No);
				break;
			case RESUME:
				eicon_log(ccard, 8, "idi_ind: Ch%d: Resume Ack\n", chan->No);
				break;
			default:
				eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
		}
	}
	/* Network Layer */
	else if (chan->e.B2Id == ind->IndId) {

		if (chan->No == ccard->nchannels) {
			/* Management Indication */
			if (ind->Ind == 0x04) { /* Trace_Ind */
				eicon_parse_trace(ccard, ind->RBuffer.P, ind->RBuffer.length);
			} else {
				idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
				chan->fsm_state = 1;
			}
		} 
		else
		switch(ind->Ind) {
			case N_CONNECT_ACK:
				eicon_log(ccard, 16, "idi_ind: Ch%d: N_Connect_Ack\n", chan->No);
				if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
					chan->fsm_state = EICON_STATE_WMCONN;
					break;
				}
				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
#ifdef CONFIG_ISDN_TTY_FAX
					chan->fsm_state = EICON_STATE_ACTIVE;
					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
					if (chan->fax) {
						if (chan->fax->phase == ISDN_FAX_PHASE_B) {
							idi_fax_send_header(ccard, chan, 2);
							cmd.driver = ccard->myid;
							cmd.command = ISDN_STAT_FAXIND;
							cmd.arg = chan->No;
							chan->fax->r_code = ISDN_TTY_FAX_DCS;
							ccard->interface.statcallb(&cmd);
						}
					}
					else {
						eicon_log(ccard, 1, "idi_ind: N_Connect_Ack with NULL fax struct, ERROR\n");
					}
#endif
					break;
				}
				chan->fsm_state = EICON_STATE_ACTIVE;
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_BCONN;
				cmd.arg = chan->No;
				strcpy(cmd.parm.num, "64000");
				ccard->interface.statcallb(&cmd);
				break; 
			case N_CONNECT:
				eicon_log(ccard, 16,"idi_ind: Ch%d: N_Connect\n", chan->No);
				chan->e.IndCh = ind->IndCh;
				if (chan->e.B2Id) idi_do_req(ccard, chan, N_CONNECT_ACK, 1);
				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
					break;
				}
				if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
					chan->fsm_state = EICON_STATE_WMCONN;
					break;
				}
				chan->fsm_state = EICON_STATE_ACTIVE;
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_BCONN;
				cmd.arg = chan->No;
				strcpy(cmd.parm.num, "64000");
				ccard->interface.statcallb(&cmd);
				break; 
			case N_DISC:
				eicon_log(ccard, 16, "idi_ind: Ch%d: N_Disc\n", chan->No);
				if (chan->e.B2Id) {
		                	while((skb2 = skb_dequeue(&chan->e.X))) {
						dev_kfree_skb(skb2);
					}
					idi_do_req(ccard, chan, N_DISC_ACK, 1);
					idi_do_req(ccard, chan, REMOVE, 1);
				}
#ifdef CONFIG_ISDN_TTY_FAX
				if ((chan->l2prot == ISDN_PROTO_L2_FAX) && (chan->fax)){
					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
					idi_fax_hangup(ccard, chan);
				}
#endif
				chan->e.IndCh = 0;
				spin_lock_irqsave(&eicon_lock, flags);
				chan->queued = 0;
				chan->pqueued = 0;
				chan->waitq = 0;
				chan->waitpq = 0;
				spin_unlock_irqrestore(&eicon_lock, flags);
				if (!(chan->statectrl & IN_HOLD)) {
					idi_do_req(ccard, chan, HANGUP, 0);
				}
				if (chan->fsm_state == EICON_STATE_ACTIVE) {
					cmd.driver = ccard->myid;
					cmd.command = ISDN_STAT_BHUP;
					cmd.arg = chan->No;
					ccard->interface.statcallb(&cmd);
					chan->fsm_state = EICON_STATE_NULL;
					if (!(chan->statectrl & IN_HOLD)) {
						chan->statectrl |= WAITING_FOR_HANGUP;
					}
				}
#ifdef CONFIG_ISDN_TTY_FAX
				chan->fax = 0;
#endif
				break; 
			case N_DISC_ACK:
				eicon_log(ccard, 16, "idi_ind: Ch%d: N_Disc_Ack\n", chan->No);
#ifdef CONFIG_ISDN_TTY_FAX
				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
					idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
					idi_fax_hangup(ccard, chan);
				}
#endif
				break; 
			case N_DATA_ACK:
				eicon_log(ccard, 128, "idi_ind: Ch%d: N_Data_Ack\n", chan->No);
				break;
			case N_DATA:
				skb_pull(skb, sizeof(eicon_IND) - 1);
				eicon_log(ccard, 128, "idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
				if (chan->l2prot == ISDN_PROTO_L2_FAX) {
#ifdef CONFIG_ISDN_TTY_FAX
					idi_faxdata_rcv(ccard, chan, skb);
#endif
				} else {
					ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
					free_buff = 0; 
				}
				break; 
			case N_UDATA:
				idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
				break; 
#ifdef CONFIG_ISDN_TTY_FAX
			case N_EDATA:
				idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
				break; 
#endif
			default:
				eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
		}
	}
	else {
		eicon_log(ccard, 1, "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No);
	}
   if (free_buff)
	dev_kfree_skb(skb);
}

int
idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack)
{
	ulong flags;
	isdn_ctrl cmd;
	int tqueued = 0;
	int twaitpq = 0;

	if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
		/* I dont know why this happens, should not ! */
		/* just ignoring this RC */
		eicon_log(ccard, 16, "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, 
			ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id);
		return 1;
	}

	/* Management Interface */	
	if (chan->No == ccard->nchannels) {
		/* Managementinterface: changing state */
		if (chan->e.Req != 0x02)
			chan->fsm_state = 1;
	}

	/* Remove an Id */
	if (chan->e.Req == REMOVE) {
		if (ack->Reference != chan->e.ref) {
			/* This should not happen anymore */
			eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
				ack->Reference, chan->e.ref);
		}
		spin_lock_irqsave(&eicon_lock, flags);
		ccard->IdTable[ack->RcId] = NULL;
		if (!chan->e.ReqCh) 
			chan->e.D3Id = 0;
		else
			chan->e.B2Id = 0;
		spin_unlock_irqrestore(&eicon_lock, flags);
		eicon_log(ccard, 16, "idi_ack: Ch%d: Removed : Id=%x Ch=%d (%s)\n", chan->No,
			ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
		return 1;
	}

	/* Signal layer */
	if (!chan->e.ReqCh) {
		eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
			ack->RcId, ack->RcCh, ack->Reference);
	} else {
	/* Network layer */
		switch(chan->e.Req & 0x0f) {
			case N_CONNECT:
				chan->e.IndCh = ack->RcCh;
				eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
					ack->RcId, ack->RcCh, ack->Reference);
				break;
			case N_MDATA:
			case N_DATA:
				tqueued = chan->queued;
				twaitpq = chan->waitpq;
				if ((chan->e.Req & 0x0f) == N_DATA) {
					spin_lock_irqsave(&eicon_lock, flags);
					chan->waitpq = 0;
					if(chan->pqueued)
						chan->pqueued--;
					spin_unlock_irqrestore(&eicon_lock, flags);
#ifdef CONFIG_ISDN_TTY_FAX
					if (chan->l2prot == ISDN_PROTO_L2_FAX) {
						if (((chan->queued - chan->waitq) < 1) &&
						    (chan->fax2.Eop)) {
							chan->fax2.Eop = 0;
							if (chan->fax) {
								cmd.driver = ccard->myid;
								cmd.command = ISDN_STAT_FAXIND;
								cmd.arg = chan->No;
								chan->fax->r_code = ISDN_TTY_FAX_SENT;
								ccard->interface.statcallb(&cmd);
							}
							else {
								eicon_log(ccard, 1, "idi_ack: Sent with NULL fax struct, ERROR\n");
							}
						}
					}
#endif
				}
				spin_lock_irqsave(&eicon_lock, flags);
				chan->queued -= chan->waitq;
				if (chan->queued < 0) chan->queued = 0;
				spin_unlock_irqrestore(&eicon_lock, flags);
				if (((chan->e.Req & 0x0f) == N_DATA) && (tqueued)) {
					cmd.driver = ccard->myid;
					cmd.command = ISDN_STAT_BSENT;
					cmd.arg = chan->No;
					cmd.parm.length = twaitpq;
					ccard->interface.statcallb(&cmd);
				}
				break;
			default:
				eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
					ack->RcId, ack->RcCh, ack->Reference);
		}
	}
	return 1;
}

void
idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
{
	int j;
	ulong flags;
        eicon_RC *ack = (eicon_RC *)skb->data;
	eicon_chan *chan;
	isdn_ctrl cmd;
	int dCh = -1;

	if (!ccard) {
		eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ack\n");
		dev_kfree_skb(skb);
		return;
	}

	spin_lock_irqsave(&eicon_lock, flags);
	if ((chan = ccard->IdTable[ack->RcId]) != NULL)
		dCh = chan->No;
	spin_unlock_irqrestore(&eicon_lock, flags);

	switch (ack->Rc) {
		case OK_FC:
		case N_FLOW_CONTROL:
		case ASSIGN_RC:
			eicon_log(ccard, 1, "idi_ack: Ch%d: unhandled RC 0x%x\n",
				dCh, ack->Rc);
			break;
		case READY_INT:
		case TIMER_INT:
			/* we do nothing here */
			break;

		case OK:
			if (!chan) {
				eicon_log(ccard, 1, "idi_ack: Ch%d: OK on chan without Id\n", dCh);
				break;
			}
			if (!idi_handle_ack_ok(ccard, chan, ack))
				chan = NULL;
			break;

		case ASSIGN_OK:
			if (chan) {
				eicon_log(ccard, 1, "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n",
					chan->No, chan->e.D3Id, chan->e.B2Id);
			}
			spin_lock_irqsave(&eicon_lock, flags);
			for(j = 0; j < ccard->nchannels + 1; j++) {
				if ((ccard->bch[j].e.ref == ack->Reference) &&
					(ccard->bch[j].e.Req == ASSIGN)) {
					if (!ccard->bch[j].e.ReqCh) 
						ccard->bch[j].e.D3Id  = ack->RcId;
					else
						ccard->bch[j].e.B2Id  = ack->RcId;
					ccard->IdTable[ack->RcId] = &ccard->bch[j];
					chan = &ccard->bch[j];
					break;
				}
			}
			spin_unlock_irqrestore(&eicon_lock, flags);
			eicon_log(ccard, 16, "idi_ack: Ch%d: Id %x assigned (%s)\n", j, 
				ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
			if (j > ccard->nchannels) {
				eicon_log(ccard, 24, "idi_ack: Ch??: ref %d not found for Id %d\n", 
					ack->Reference, ack->RcId);
			}
			break;

		case OUT_OF_RESOURCES:
		case UNKNOWN_COMMAND:
		case WRONG_COMMAND:
		case WRONG_ID:
		case ADAPTER_DEAD:
		case WRONG_CH:
		case UNKNOWN_IE:
		case WRONG_IE:
		default:
			if (!chan) {
				eicon_log(ccard, 1, "idi_ack: Ch%d: Not OK !! on chan without Id\n", dCh);
				break;
			} else
			switch (chan->e.Req) {
				case 12:	/* Alert */
					eicon_log(ccard, 2, "eicon_err: Ch%d: Alert Not OK : Rc=%d Id=%x Ch=%d\n",
						dCh, ack->Rc, ack->RcId, ack->RcCh);
					break;
				default:
					if (dCh != ccard->nchannels)
						eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
							dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
			}
			if (dCh == ccard->nchannels) { /* Management */
				chan->fsm_state = 2;
				eicon_log(ccard, 8, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
					dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
			} else if (dCh >= 0) {
					/* any other channel */
					/* card reports error: we hangup */
				idi_hangup(ccard, chan);
				cmd.driver = ccard->myid;
				cmd.command = ISDN_STAT_DHUP;
				cmd.arg = chan->No;
				ccard->interface.statcallb(&cmd);
			}
	}
	spin_lock_irqsave(&eicon_lock, flags);
	if (chan) {
		chan->e.ref = 0;
		chan->e.busy = 0;
	}
	spin_unlock_irqrestore(&eicon_lock, flags);
	dev_kfree_skb(skb);
	eicon_schedule_tx(ccard);
}

int
idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que, int chk)
{
        struct sk_buff *xmit_skb;
        struct sk_buff *skb2;
        eicon_REQ *reqbuf;
        eicon_chan_ptr *chan2;
        int len, plen = 0, offset = 0;
	unsigned long flags;

	if ((!card) || (!chan)) {
		eicon_log(card, 1, "idi_err: Ch??: null card/chan in send_data\n");
		return -1;
	}

        if (chan->fsm_state != EICON_STATE_ACTIVE) {
		eicon_log(card, 1, "idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state);
                return -ENODEV;
	}

        len = skb->len;
	if (len > EICON_MAX_QUEUE)	/* too much for the shared memory */
		return -1;
        if (!len)
                return 0;

	if ((chk) && (chan->pqueued > 1))
		return 0;

	eicon_log(card, 128, "idi_snd: Ch%d: %d bytes (Pqueue=%d)\n",
		chan->No, len, chan->pqueued);

	spin_lock_irqsave(&eicon_lock, flags);
	while(offset < len) {

		plen = ((len - offset) > 270) ? 270 : len - offset;

	        xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC);
        	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

	        if ((!xmit_skb) || (!skb2)) {
			spin_unlock_irqrestore(&eicon_lock, flags);
        	        eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No);
			if (xmit_skb) 
				dev_kfree_skb(skb);
			if (skb2) 
				dev_kfree_skb(skb2);
                	return -ENOMEM;
	        }

	        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
        	chan2->ptr = chan;

	        reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
		if ((len - offset) > 270) { 
		        reqbuf->Req = N_MDATA;
		} else {
		        reqbuf->Req = N_DATA;
			/* if (ack) reqbuf->Req |= N_D_BIT; */
		}	
        	reqbuf->ReqCh = chan->e.IndCh;
	        reqbuf->ReqId = 1;
		memcpy(&reqbuf->XBuffer.P, skb->data + offset, plen);
		reqbuf->XBuffer.length = plen;
		reqbuf->Reference = 1; /* Net Entity */

		skb_queue_tail(&chan->e.X, xmit_skb);
		skb_queue_tail(&card->sndq, skb2); 

		offset += plen;
	}
	if (que) {
		chan->queued += len;
		chan->pqueued++;
	}
	spin_unlock_irqrestore(&eicon_lock, flags);
	eicon_schedule_tx(card);
        dev_kfree_skb(skb);
        return len;
}


int
eicon_idi_manage_assign(eicon_card *card)
{
        struct sk_buff *skb;
        struct sk_buff *skb2;
        eicon_REQ  *reqbuf;
        eicon_chan     *chan;
        eicon_chan_ptr *chan2;

        chan = &(card->bch[card->nchannels]);

        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

        if ((!skb) || (!skb2)) {
		eicon_log(card, 1, "idi_err: alloc_skb failed in manage_assign()\n");
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
                return -ENOMEM;
        }

        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
        chan2->ptr = chan;

        reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));

        reqbuf->XBuffer.P[0] = 0;
        reqbuf->Req = ASSIGN;
        reqbuf->ReqCh = 0;
        reqbuf->ReqId = MAN_ID;
        reqbuf->XBuffer.length = 1;
        reqbuf->Reference = 2; /* Man Entity */

        skb_queue_tail(&chan->e.X, skb);
        skb_queue_tail(&card->sndq, skb2);
        eicon_schedule_tx(card);
        return(0);
}


int
eicon_idi_manage_remove(eicon_card *card)
{
        struct sk_buff *skb;
        struct sk_buff *skb2;
        eicon_REQ  *reqbuf;
        eicon_chan     *chan;
        eicon_chan_ptr *chan2;

        chan = &(card->bch[card->nchannels]);

        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

        if ((!skb) || (!skb2)) {
               	eicon_log(card, 1, "idi_err: alloc_skb failed in manage_remove()\n");
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
                return -ENOMEM;
        }

        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
        chan2->ptr = chan;

        reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));

        reqbuf->Req = REMOVE;
        reqbuf->ReqCh = 0;
        reqbuf->ReqId = 1;
        reqbuf->XBuffer.length = 0;
        reqbuf->Reference = 2; /* Man Entity */

        skb_queue_tail(&chan->e.X, skb);
        skb_queue_tail(&card->sndq, skb2);
        eicon_schedule_tx(card);
        return(0);
}


int
eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
{
	int l = 0;
	int ret = 0;
	int timeout;
	int i;
        struct sk_buff *skb;
        struct sk_buff *skb2;
        eicon_REQ  *reqbuf;
        eicon_chan     *chan;
        eicon_chan_ptr *chan2;

        chan = &(card->bch[card->nchannels]);

	if (!(chan->e.D3Id)) {
		chan->e.D3Id = 1;
		while((skb2 = skb_dequeue(&chan->e.X)))
			dev_kfree_skb(skb2);
		chan->e.busy = 0;
 
		if ((ret = eicon_idi_manage_assign(card))) {
			chan->e.D3Id = 0;
			return(ret); 
		}

	        timeout = jiffies + 50;
        	while (timeout > jiffies) {
	                if (chan->e.B2Id) break;
        	        SLEEP(10);
	        }
	        if (!chan->e.B2Id) {
			chan->e.D3Id = 0;
			return -EIO;
		}
	}

	chan->fsm_state = 0;

	if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
               	eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n");
		return -ENOMEM;
	}
	if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
		kfree(manbuf);
		return -EFAULT;
	}

        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);

        if ((!skb) || (!skb2)) {
               	eicon_log(card, 1, "idi_err_manif: alloc_skb failed in manage()\n");
		if (skb) 
			dev_kfree_skb(skb);
		if (skb2) 
			dev_kfree_skb(skb2);
		kfree(manbuf);
                return -ENOMEM;
        }

        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
        chan2->ptr = chan;

        reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));

        reqbuf->XBuffer.P[l++] = ESC;
        reqbuf->XBuffer.P[l++] = 6;
        reqbuf->XBuffer.P[l++] = 0x80;
	for (i = 0; i < manbuf->length[0]; i++)
	        reqbuf->XBuffer.P[l++] = manbuf->data[i];
        reqbuf->XBuffer.P[1] = manbuf->length[0] + 1;

        reqbuf->XBuffer.P[l++] = 0;
        reqbuf->Req = (manbuf->count) ? manbuf->count : MAN_READ;
        reqbuf->ReqCh = 0;
        reqbuf->ReqId = 1;
        reqbuf->XBuffer.length = l;
        reqbuf->Reference = 2; /* Man Entity */

        skb_queue_tail(&chan->e.X, skb);
        skb_queue_tail(&card->sndq, skb2);

	manbuf->count = 0;
	manbuf->pos = 0;

        eicon_schedule_tx(card);

        timeout = jiffies + 50;
        while (timeout > jiffies) {
                if (chan->fsm_state) break;
                SLEEP(10);
        }
        if ((!chan->fsm_state) || (chan->fsm_state == 2)) {
		kfree(manbuf);
		return -EIO;
	}
	if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
		kfree(manbuf);
		return -EFAULT;
	}

	kfree(manbuf);
  return(0);
}
