mISDN/drivers/isdn/hardware/mISDN/faxl3.c

2183 lines
51 KiB
C

/* $Id$
*
* Linux ISDN subsystem, Fax Layer 3
*
* Author Karsten Keil (kkeil@suse.de)
*
* Copyright 2003 by Karsten Keil (kkeil@suse.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/module.h>
#include "layer1.h"
#include "m_capi.h"
#include "helper.h"
#include "debug.h"
static int ttt=180;
typedef struct _faxl3 {
struct list_head list;
spinlock_t lock;
u_long state;
int debug;
mISDNinstance_t inst;
u16 options;
u16 format;
u8 stationID[24];
u8 headline[64];
u8 DIS[12];
u8 CIS[24]; // only max 20 are used
u8 NSF[12];
u8 DTC[12];
u8 DCS[12];
u8 CIG[24];
u8 NSC[12];
u_int peer_rate_mask;
u_int own_rate_mask;
int current_rate_idx;
int current_mod;
int current_rate;
int pending_mod;
int pending_rate;
int result;
struct FsmInst main;
struct FsmTimer deltimer;
struct FsmTimer timer1;
struct FsmInst mod;
struct FsmTimer modtimer;
struct sk_buff_head downq;
struct sk_buff_head dataq;
struct sk_buff_head pageq;
struct sk_buff_head saveq;
struct sk_buff *data_skb;
struct sk_buff *page_skb;
int entity;
int next_id;
int maxdatalen;
int up_headerlen;
int down_headerlen;
u32 ncci;
// SFFHEADER
int pages;
u32 offset_lpage;
u32 offset_dend;
int current_page;
// SFF page header
u8 page_vres;
u8 page_hres;
u8 page_code;
u8 page_rsv1;
u16 page_llen;
u16 page_plen;
u32 page_oprv;
u32 page_onxt;
u8 lasttyp;
int lastlen;
int line_cnt;
int page_retry;
} faxl3_t;
#define FAXL3_STATE_OUTGOING 1
#define FAXL3_STATE_SENT_TIS 2
#define FAXL3_STATE_CAPICONNECT 3
#define FAXL3_STATE_GOT_DIS 8
#define FAXL3_STATE_GOT_CIS 9
#define FAXL3_STATE_GOT_NSF 10
#define FAXL3_STATE_SFFHEADER 16
#define FAXL3_STATE_PAGEHEADER 17
#define FAXL3_STATE_NEWPAGE 18
#define FAXL3_STATE_LASTPAGE 19
#define FAXL3_STATE_CONTINUE 20
#define FAXL3_STATE_HAVEDATA 21
#define FAXL3_STATE_DATABUSY 24
#define FAXL3_STATE_DATALAST 25
#define FAXL3_STATE_DATAREADY 26
#define FAXL3_RESULT_NONE 0
#define FAXL3_RESULT_CFR 1
#define FAXL3_RESULT_FTT 2
#define FAXL3_RESULT_MCF 3
#define FAXL3_RESULT_RTP 4
#define FAXL3_RESULT_RTN 5
static char logbuf[2000];
static int debug = 0;
#define DEBUG_FAXL3_FUNC 0x0001
#define DEBUG_FAXL3_MGR 0x0010
#define DEBUG_FAXL3_CFG 0x0020
#define DEBUG_FAXL3_MSG 0x0100
#define DEBUG_FAXL3_SIG 0x0200
#define DEBUG_FAXL3_PAGEPREPARE 0x1000
static mISDNobject_t faxl3_obj;
static char *mISDN_faxl3_revision = "$Revision$";
static u_char FaxModulation[] = {24,48,72,74,96,98,122,146};
static u_char FaxModulationTrain[] = {24,48,72,73,96,97,121,145};
static int FaxModulationBaud[] = {2400,4800,7200,7200,9600,9600,12000,14400};
#define MAX_FAXMODULATION_INDEX 7
#define FAXMODULATION_MASK 0xff
#define FAXMODM_UNDEF 0x00
#define FAXMODM_V27 0x03
#define FAXMODM_V27_V29 0x17
#define FAXMODM_V27_V29_V33 0x17 //We don't have V.33 definition yet
#define FAXMODM_V27_V29_V33_V17 0xff
static u_int FaxModulationRates[16] = {
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_V27,
FAXMODM_V27_V29,
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_V27_V29_V33,
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_V27_V29_V33_V17,
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_UNDEF,
FAXMODM_UNDEF
};
static u8 FaxModulationRates_DCS[8] = {
0x0,
0x2,
0x3,
0xb,
0x1,
0x9,
0xa,
0x8
};
#define Dxx_TYPE_DIS 0
#define Dxx_TYPE_DTC 1
#define Dxx_TYPE_DCS 2
static void l3m_debug(struct FsmInst *fi, char *fmt, ...);
static int send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len);
static int sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb);
static int send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info);
static int prepare_page_data(faxl3_t *fl3);
static int copy_page_data4retry(faxl3_t *fl3);
static
struct Fsm faxl3fsm = {NULL, 0, 0, NULL, NULL};
enum {
ST_L3_IDLE,
ST_L3_WAIT_RECVDIS,
ST_L3_RECV_DIS,
ST_L3_WAIT_SENDDCS,
ST_L3_SEND_DCS,
ST_L3_WAIT_SENDTRAIN,
ST_L3_SEND_TRAIN,
ST_L3_WAIT_TRAINSTATE,
ST_L3_RECV_TRAINSTATE,
ST_L3_WAIT_SENDPAGE,
ST_L3_SEND_PAGE,
ST_L3_WAIT_PAGESTATE,
ST_L3_RECV_PAGESTATE,
ST_L3_WAIT_SENDEOP,
ST_L3_SEND_EOP,
ST_L3_WAIT_RECVMCF,
ST_L3_RECV_MCF,
ST_L3_WAIT_SENDDCN,
ST_L3_SEND_DCN,
ST_L3_CLEARING,
};
#define FAXL3_STATE_COUNT (ST_L3_CLEARING+1)
static char *strfaxl3State[] =
{
"ST_L3_IDLE",
"ST_L3_WAIT_RECVDIS",
"ST_L3_RECV_DIS",
"ST_L3_WAIT_SENDDCS",
"ST_L3_SEND_DCS",
"ST_L3_WAIT_SENDTRAIN",
"ST_L3_SEND_TRAIN",
"ST_L3_WAIT_TRAINSTATE",
"ST_L3_RECV_TRAINSTATE",
"ST_L3_WAIT_SENDPAGE",
"ST_L3_SEND_PAGE",
"ST_L3_WAIT_PAGESTATE",
"ST_L3_RECV_PAGESTATE",
"ST_L3_WAIT_SENDEOP",
"ST_L3_SEND_EOP",
"ST_L3_WAIT_RECVMCF",
"ST_L3_RECV_MCF",
"ST_L3_WAIT_SENDDCN",
"ST_L3_SEND_DCN",
"ST_L3_CLEARING",
};
enum {
EV_CALL_OUT,
EV_MODEM_ACTIV,
EV_MODEM_IDLE,
EV_MODEM_ERROR,
EV_DATA,
EV_NEXT_DATA,
EV_DELAYTIMER,
EV_CLEARING,
};
#define FAXL3_EVENT_COUNT (EV_CLEARING + 1)
static char *strfaxl3Event[] =
{
"EV_CALL_OUT",
"EV_MODEM_ACTIV",
"EV_MODEM_IDLE",
"EV_MODEM_ERROR",
"EV_DATA",
"EV_NEXT_DATA",
"EV_DELAYTIMER",
"EV_CLEARING",
};
static
struct Fsm modfsm = {NULL, 0, 0, NULL, NULL};
enum {
ST_MOD_NULL,
ST_MOD_IDLE,
ST_MOD_WAITCONNECT,
ST_MOD_CONNECTED,
ST_MOD_WAITDISCONNECT,
};
#define MOD_STATE_COUNT (ST_MOD_WAITDISCONNECT + 1)
static char *strmodState[] =
{
"ST_MOD_NULL",
"ST_MOD_IDLE",
"ST_MOD_WAITCONNECT",
"ST_MOD_CONNECTED",
"ST_MOD_WAITDISCONNECT",
};
enum {
EV_MOD_READY,
EV_MOD_NEW,
EV_MOD_CONNECT,
EV_MOD_DISCONNECT,
EV_MOD_NOCARRIER,
EV_MOD_ERROR,
EV_MOD_TIMEOUT,
};
#define MOD_EVENT_COUNT (EV_MOD_TIMEOUT + 1)
static char *strmodEvent[] =
{
"EV_MOD_READY",
"EV_MOD_NEW",
"EV_MOD_CONNECT",
"EV_MOD_DISCONNECT",
"EV_MOD_NOCARRIER",
"EV_MOD_ERROR",
"EV_MOD_TIMEOUT",
};
static int
data_next_id(faxl3_t *fl3)
{
u_long flags;
int id;
spin_lock_irqsave(&fl3->lock, flags);
id = fl3->next_id++;
if (id == 0x0fff)
fl3->next_id = 1;
spin_unlock_irqrestore(&fl3->lock, flags);
id |= (fl3->entity << 16);
return(id);
}
static void
print_hexdata(faxl3_t *fl3, char *head, int len, char *data)
{
char *t = logbuf;
t += sprintf(logbuf, "%s", head);
if (len > 650)
len = 650;
mISDN_QuickHex(t, data, len);
printk(KERN_DEBUG "%s\n", logbuf);
}
static char *rate_1[16] = {
"undef",
"undef",
"2400,4800 V.27ter",
"9600,7200,4800,2400 V.27ter/V.29",
"undef",
"undef",
"undef",
"14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33",
"undef",
"undef",
"undef",
"14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33/V.17",
"undef",
"undef",
"undef",
"undef"
};
static char *rate_2[16] = {
"2400(V.27ter)",
"9600(V.29)",
"4800(V.27ter)",
"7200(V.29)",
"14400(V.33)",
"undef",
"12000(V.33)",
"undef",
"14400(V.17)",
"9600(V.17)",
"12000(V.17)",
"7200(V.17)",
"undef",
"undef",
"undef",
"undef"
};
static char *pwidth_1[4] = {
"1728/A4",
"1728/A4 2048/B4",
"1728/A4 2048/B4 2432/A3",
"undef"
};
static char *pwidth_2[4] = {
"1728 A4",
"2048 B4",
"2432 A3",
"undef"
};
static char *plength[4] = {
"297/A4",
"364/B4",
"unlimited",
"undef"
};
static char *minrowtime_1[8] = {
"20ms",
"5ms",
"10ms",
"20ms*",
"40ms",
"40ms*",
"10ms*",
"0ms"
};
static char *minrowtime_2[8] = {
"20ms",
"5ms",
"10ms",
" ",
"40ms",
" ",
" ",
"0ms"
};
static void
print_Dxx(faxl3_t *fl3, int typ)
{
char *ts;
u8 *p, v1,v2,v3;
switch (typ) {
case Dxx_TYPE_DIS:
ts = "DIS";
p = fl3->DIS;
break;
case Dxx_TYPE_DTC:
ts = "DTC";
p = fl3->DTC;
break;
case Dxx_TYPE_DCS:
ts = "DCS";
p = fl3->DCS;
break;
default:
int_error();
return;
}
/* OK byte one is only for group 1/2 compatibility */
printk(KERN_DEBUG "%s: byte1 %02X\n", ts, *p);
v1 = (p[1] >> 2) & 0xf;
printk(KERN_DEBUG "%s:%s%s %s%s%s\n", ts,
(test_bit(8, (u_long *)p) && (typ != Dxx_TYPE_DCS)) ? " SendG3" : "",
(test_bit(9, (u_long *)p)) ? " RecvG3" : "",
(typ == Dxx_TYPE_DCS) ? rate_2[v1] : rate_1[v1],
(test_bit(14, (u_long *)p)) ? " 7,7Row/mm" : "",
(test_bit(15, (u_long *)p)) ? " 2-Dim" : "");
v1 = p[2] & 3;
v2 = (p[2] >> 2) & 3;
v3 = (p[2] >> 4) & 7;
printk(KERN_DEBUG "%s: width(%s) plength(%s) MinRow(%s)\n", ts,
(typ == Dxx_TYPE_DCS) ? pwidth_2[v1] : pwidth_1[v1],
plength[v2],
(typ == Dxx_TYPE_DCS) ? minrowtime_2[v3] : minrowtime_1[v3]);
if (!test_bit(23, (u_long *)p))
return;
if (typ == Dxx_TYPE_DCS)
printk(KERN_DEBUG "%s:%s%s%s BS(%s)%s\n", ts,
(test_bit(24, (u_long *)p)) ? " 2400" : "",
(test_bit(25, (u_long *)p)) ? " uncompressed" : "",
(test_bit(26, (u_long *)p)) ? " ECM" : "",
(test_bit(27, (u_long *)p)) ? "64" : "256",
(test_bit(30, (u_long *)p)) ? " MMR" : "");
else
printk(KERN_DEBUG "%s:%s%s%s%s\n", ts,
(test_bit(24, (u_long *)p)) ? " 2400" : "",
(test_bit(25, (u_long *)p)) ? " uncompressed" : "",
(test_bit(26, (u_long *)p)) ? " ECM" : "",
(test_bit(30, (u_long *)p)) ? " MMR" : "");
if (!test_bit(31, (u_long *)p))
return;
/* byte is reseved */
if (!test_bit(39, (u_long *)p))
return;
// TODO
if (!test_bit(47, (u_long *)p))
return;
// TODO
if (!test_bit(55, (u_long *)p))
return;
// TODO
if (!test_bit(63, (u_long *)p))
return;
// TODO
}
static u8
calc_dtcrate(faxl3_t *fl3)
{
if ((FAXMODM_V27_V29_V33_V17 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33_V17)
return(11);
if ((FAXMODM_V27_V29_V33 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33)
return(7);
if ((FAXMODM_V27_V29 & fl3->own_rate_mask) == FAXMODM_V27_V29)
return(3);
if ((FAXMODM_V27& fl3->own_rate_mask) == FAXMODM_V27)
return(2);
return(0);
}
static u8
calc_dcsrate(faxl3_t *fl3)
{
if ((fl3->current_rate_idx > MAX_FAXMODULATION_INDEX) ||
(fl3->current_rate_idx < 0)) {
int_errtxt("current_rate_idx(%d)", fl3->current_rate_idx);
return(0xf);
}
return(FaxModulationRates_DCS[fl3->current_rate_idx]);
}
static void
fill_Dxx(faxl3_t *fl3, int typ)
{
u8 *p, v1,v2,v3;
switch (typ) {
case Dxx_TYPE_DIS:
p = fl3->DIS;
break;
case Dxx_TYPE_DTC:
p = fl3->DTC;
break;
case Dxx_TYPE_DCS:
p = fl3->DCS;
break;
default:
int_error();
return;
}
memset(p, 0, 12); // clear all bits
/* OK byte one is only for group 1/2 compatibility, skipped */
if (typ == Dxx_TYPE_DCS)
v1 = calc_dcsrate(fl3);
else
v1 = calc_dtcrate(fl3);
p[1] = v1 << 2;
if (typ == Dxx_TYPE_DCS)
test_and_set_bit(9, (u_long *)p);
else
test_and_set_bit(8, (u_long *)p);
if (fl3->options & 1)
test_and_set_bit(14, (u_long *)p);
// TODO: calc
test_and_set_bit(14, (u_long *)p);
v1 = 0; // A4, TODO: calc
v2 = 2; // unlimited, TODO: calc
v3 = 7; // 0 ms, TODO: calc
p[2] = v1 | (v2 << 2) | (v3 << 4);
test_and_set_bit(23, (u_long *)p); // next byte exist
p[3] = 0; // TODO: calc
}
static int
send_Dxx(faxl3_t *fl3, int typ, int last)
{
u8 *p, fcf, hdlc_cf = last ? 0x13 : 3;
int len;
switch (typ) {
case Dxx_TYPE_DIS:
p = fl3->DIS;
fcf = 80;
break;
case Dxx_TYPE_DTC:
p = fl3->DTC;
fcf = 0x81;
break;
case Dxx_TYPE_DCS:
p = fl3->DCS;
fcf = 0x83;
break;
default:
int_error();
return(-EINVAL);
}
if (!test_bit(23, (u_long *)p))
len = 3;
else if (!test_bit(31, (u_long *)p))
len = 4;
else if (!test_bit(39, (u_long *)p))
len = 5;
else if (!test_bit(47, (u_long *)p))
len = 6;
else if (!test_bit(55, (u_long *)p))
len = 7;
else if (!test_bit(63, (u_long *)p))
len = 8;
else
len = 9;
return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, p, len));
}
static int
send_char20(faxl3_t *fl3, u8 *p, int fcf, int last)
{
u8 buf[20], *s, hdlc_cf = last ? 0x13 : 3;
int len, i;
memset(buf, ' ', 20);
len = strlen(p);
if (len > 20)
len = 20;
s = buf;
for (i=len; i>0; i--)
*s++ = p[i-1];
return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, buf, 20));
}
static int
is_valid_rate_idx(faxl3_t *fl3, int ridx)
{
if ((ridx > MAX_FAXMODULATION_INDEX) || (ridx < 0))
return(0);
if (((1<<ridx) & fl3->own_rate_mask & fl3->peer_rate_mask) == 0)
return(0);
else
return(1);
}
static int
fallback_rate(faxl3_t *fl3)
{
fl3->current_rate_idx--;
while ((fl3->current_rate_idx >= 0) &&
!is_valid_rate_idx(fl3, fl3->current_rate_idx)) {
fl3->current_rate_idx--;
}
return(is_valid_rate_idx(fl3, fl3->current_rate_idx));
}
static int
calc_max_rate(faxl3_t *fl3)
{
int i;
for (i = MAX_FAXMODULATION_INDEX; i >= 0; i--) {
if (is_valid_rate_idx(fl3, i))
return(i);
}
return(-1);
}
static int
send_data_down(faxl3_t *fl3, struct sk_buff *skb) {
int ret = 0;
mISDNif_t *down = &fl3->inst.down;
if (test_and_set_bit(FAXL3_STATE_DATABUSY, &fl3->state)) {
skb_queue_tail(&fl3->downq, skb);
} else {
mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb);
ret = down->func(down, skb);
if (ret) {
int_errtxt("down: error(%d)", ret);
}
}
return(ret);
}
static int
send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len)
{
struct sk_buff *skb;
u_char *p;
int ret;
if (!(skb = alloc_stack_skb(3 + len, 1)))
return(-ENOMEM);
p = skb_put(skb, 3);
*p++ = adr;
*p++ = hcf;
*p++ = fcf;
if (len)
memcpy(skb_put(skb, len), para, len);
ret = send_data_down(fl3, skb);
if (ret)
dev_kfree_skb(skb);
return(ret);
}
static void
mod_init(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
int err;
err = if_link(&fl3->inst.down, PH_ACTIVATE | REQUEST, 0, 0, NULL, 0);
if (err) {
int_error();
return;
}
}
static void
set_new_modulation(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
int err;
if ((fl3->pending_mod < 0) || (fl3->pending_rate <0)) {
if (event == EV_MOD_READY)
mISDN_FsmChangeState(fi, ST_MOD_IDLE);
else
int_error();
return;
}
mISDN_FsmChangeState(fi, ST_MOD_WAITCONNECT);
err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, fl3->pending_mod, sizeof(int), &fl3->pending_rate, 0);
if (err) {
int_error();
return;
}
}
static void
mod_activ(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
mISDN_FsmChangeState(fi, ST_MOD_CONNECTED);
fl3->current_mod = fl3->pending_mod;
fl3->pending_mod = -1;
fl3->current_rate = fl3->pending_rate;
fl3->pending_rate = -1;
mISDN_FsmEvent(&fl3->main, EV_MODEM_ACTIV, NULL);
}
static void
mod_disconnect(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_MOD_WAITDISCONNECT);
}
static void
mod_error(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
mISDN_FsmChangeState(fi, ST_MOD_IDLE);
mISDN_FsmEvent(&fl3->main, EV_MODEM_ERROR, NULL);
}
static void
mod_nocarrier(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
mISDN_FsmChangeState(fi, ST_MOD_IDLE);
mISDN_FsmEvent(&fl3->main, EV_MODEM_IDLE, NULL);
}
static struct FsmNode ModFnList[] =
{
{ST_MOD_NULL, EV_MOD_NEW, mod_init},
{ST_MOD_NULL, EV_MOD_READY, set_new_modulation},
{ST_MOD_IDLE, EV_MOD_READY, set_new_modulation},
{ST_MOD_IDLE, EV_MOD_NEW, set_new_modulation},
{ST_MOD_WAITCONNECT, EV_MOD_CONNECT, mod_activ},
{ST_MOD_WAITCONNECT, EV_MOD_ERROR, mod_error},
{ST_MOD_CONNECTED, EV_MOD_NOCARRIER, mod_nocarrier},
{ST_MOD_CONNECTED, EV_MOD_DISCONNECT, mod_disconnect},
{ST_MOD_WAITDISCONNECT, EV_MOD_NOCARRIER, mod_nocarrier},
};
#define MOD_FN_COUNT (sizeof(ModFnList)/sizeof(struct FsmNode))
static void
l3m_callout(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVDIS);
}
static void
l3m_activ_dis(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L3_RECV_DIS);
}
static int
get_Dxx(u8 *dst, u8 *src, int len) {
if (len < 3) {
int_errtxt("Dxx too short %d", len);
return(-1);
}
if (len > 12)
len = 12; // normally max 9 bytes
memcpy(dst, src, len);
return(0);
}
static int
get_CHAR20(u8 *dst, u8 *src, int len) {
int i;
if (len <= 0) {
int_errtxt("string too short %d", len);
return(-1);
}
if (len > 20) {
int_errtxt("string too big (%d) rest ignored", len);
len = 20;
}
for (i = 20; i > len; i--)
dst[20-i] = ' ';
for (; i > 0; i--)
dst[20-i] = src[i-1];
dst[20] = 0;
return(0);
}
static int
get_Nxx(u8 *dst, u8 *src, int len) {
if (len < 2) {
int_errtxt("Nxx too short %d", len);
return(-1);
}
if (len > 12) {
int_errtxt("Nxx too big (%d) ignored", len);
return(-2);
}
memcpy(dst, src, len);
return(0);
}
static void
init_newpage(faxl3_t *fl3)
{
fl3->page_retry = 0;
fl3->line_cnt = 0;
fl3->result = 0;
discard_queue(&fl3->pageq);
discard_queue(&fl3->saveq);
test_and_clear_bit(FAXL3_STATE_NEWPAGE, &fl3->state);
}
static void
l3m_receive_dis(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
struct sk_buff *skb = arg;
u8 end, *p = skb->data;
if (skb->len < 3) {
int_errtxt("HDLC too short %d", skb->len);
return;
}
if (*p != 0xff) {
int_errtxt("HDLC addr not FF (%02X)", *p);
}
p++;
if (*p == 0x03) {
end = 0;
} else if (*p == 0x13) {
end = 1;
} else {
int_errtxt("wrong HDLC CTRL (%02X)", *p);
}
p++;
skb_pull(skb, 3);
switch(*p) {
case 0x80: // DIS
if (0 == get_Dxx(fl3->DIS, p+1, skb->len))
test_and_set_bit(FAXL3_STATE_GOT_DIS, &fl3->state);
break;
case 0x40: // CIS
if (0 == get_CHAR20(fl3->CIS, p+1, skb->len))
test_and_set_bit(FAXL3_STATE_GOT_CIS, &fl3->state);
break;
case 0x20: // NSF
if (0 == get_Nxx(fl3->NSF, p+1, skb->len))
test_and_set_bit(FAXL3_STATE_GOT_NSF, &fl3->state);
break;
default:
int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len);
break;
}
}
static void
l3m_finish_dis(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
if (fl3->debug & DEBUG_FAXL3_SIG) {
if (test_bit(FAXL3_STATE_GOT_DIS, &fl3->state))
print_Dxx(fl3, Dxx_TYPE_DIS);
if (test_bit(FAXL3_STATE_GOT_CIS, &fl3->state))
printk(KERN_DEBUG "CIS: %s\n", fl3->CIS);
if (test_bit(FAXL3_STATE_GOT_NSF, &fl3->state))
print_hexdata(fl3, "NSF: ", 12, fl3->NSF);
}
fl3->peer_rate_mask = FaxModulationRates[(fl3->DIS[1]>>2) &0xf];
fl3->current_rate_idx = calc_max_rate(fl3);
if (fl3->current_rate_idx > 0) {
fill_Dxx(fl3, Dxx_TYPE_DCS);
if (fl3->debug & DEBUG_FAXL3_SIG)
print_Dxx(fl3, Dxx_TYPE_DCS);
fl3->pending_mod = HW_MOD_FTH;
fl3->pending_rate = 3;
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS);
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
} else // ABORT
mISDN_FsmChangeState(fi, ST_L3_IDLE);
}
static void
l3m_send_dcs(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L3_SEND_DCS);
if (!test_and_set_bit(FAXL3_STATE_SENT_TIS, &fl3->state))
send_char20(fl3, &fl3->stationID[1], 0x43, 0);
send_Dxx(fl3, Dxx_TYPE_DCS, 1);
}
static void
l3m_send_lastdata(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
int err;
if (test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state)) {
err = 0;
err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0);
if (err) {
int_error();
return;
}
}
}
static void
l3m_finish_dcs(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDTRAIN);
/* wait 75 ms */
mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2);
}
static void
l3m_setup_train(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) {
fl3->pending_mod = HW_MOD_FTM;
fl3->pending_rate = FaxModulationTrain[fl3->current_rate_idx];
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
} else // ABORT
mISDN_FsmChangeState(fi, ST_L3_IDLE);
}
static void
l3m_send_train(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
struct sk_buff *skb;
int len, ret;
mISDN_FsmChangeState(fi, ST_L3_SEND_TRAIN);
len = 3*(FaxModulationBaud[fl3->current_rate_idx]/16); // 1,5 sec
if (!(skb = alloc_stack_skb(len, 1)))
return;
memset(skb_put(skb, len), 0, len);
test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
ret = send_data_down(fl3, skb);
if (ret) {
dev_kfree_skb(skb);
}
}
static void
l3m_finish_train(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
fl3->pending_mod = HW_MOD_FRH;
fl3->pending_rate = 3;
mISDN_FsmChangeState(fi, ST_L3_WAIT_TRAINSTATE);
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
}
static void
l3m_activ_trainstate(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L3_RECV_TRAINSTATE);
}
static void
l3m_receive_trainstate(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
struct sk_buff *skb = arg;
u8 end, *p = skb->data;
if (skb->len < 3) {
int_errtxt("HDLC too short %d", skb->len);
return;
}
if (*p != 0xff) {
int_errtxt("HDLC addr not FF (%02X)", *p);
}
p++;
if (*p == 0x03) {
end = 0;
} else if (*p == 0x13) {
end = 1;
} else {
int_errtxt("wrong HDLC CTRL (%02X)", *p);
}
p++;
skb_pull(skb, 3);
switch(*p) {
case 0x84: // CFR
fl3->result = FAXL3_RESULT_CFR;
printk(KERN_DEBUG "training successfull\n");
if (!test_and_set_bit(FAXL3_STATE_CAPICONNECT, &fl3->state))
send_capi_msg_ncpi(fl3, CAPI_CONNECT_B3_ACTIVE_IND, 0);
break;
case 0x44: // FTT
fl3->result = FAXL3_RESULT_FTT;
printk(KERN_DEBUG "training failed\n");
break;
default:
fl3->result = FAXL3_RESULT_NONE;
int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len);
break;
}
}
static void
l3m_finish_trainstate(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
if (fl3->result == FAXL3_RESULT_FTT) {
fl3->pending_mod = HW_MOD_FTH;
fl3->pending_rate = 3;
if (fallback_rate(fl3)) {
fill_Dxx(fl3, Dxx_TYPE_DCS);
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS);
} else {
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCN);
}
} else {
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE);
mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2);
}
}
static void
l3m_setup_sendpage(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) {
fl3->pending_mod = HW_MOD_FTM;
fl3->pending_rate = FaxModulation[fl3->current_rate_idx];
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
} else // ABORT
mISDN_FsmChangeState(fi, ST_L3_IDLE);
}
static void
l3m_ready_sendpage(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
struct sk_buff *skb;
int ret;
mISDN_FsmChangeState(fi, ST_L3_SEND_PAGE);
if (!(skb = alloc_stack_skb(4000, 1)))
return;
// memset(skb_put(skb, 1000), 0xff, 1000);
// memset(skb_put(skb, 1000), 0, 1000);
// memset(skb_put(skb, 100), 0xff, 100);
memset(skb_put(skb, ttt), 0, ttt);
test_and_set_bit(FAXL3_STATE_DATAREADY, &fl3->state);
ret = send_data_down(fl3, skb);
if (ret) {
dev_kfree_skb(skb);
}
}
static void
l3m_send_next_pagedata(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
struct sk_buff *skb;
int err;
start:
if (test_bit(FAXL3_STATE_DATALAST, &fl3->state)) {
if (skb_queue_empty(&fl3->pageq)) {
err = 0;
err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0);
if (err) {
int_error();
return;
}
test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state);
} else {
while((skb = skb_dequeue(&fl3->pageq)))
send_data_down(fl3, skb);
}
} else {
if (!fl3->page_retry) {
prepare_page_data(fl3);
} else if (skb_queue_empty(&fl3->pageq)) {
test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
goto start;
}
if ((skb = skb_dequeue(&fl3->pageq)))
send_data_down(fl3, skb);
}
}
static void
l3m_finish_page(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
test_and_clear_bit(FAXL3_STATE_DATAREADY, &fl3->state);
fl3->pending_mod = HW_MOD_FTH;
fl3->pending_rate = 3;
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDEOP);
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
}
static void
l3m_send_endofpage(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
u8 fcf;
mISDN_FsmChangeState(fi, ST_L3_SEND_EOP);
if (test_bit(FAXL3_STATE_LASTPAGE, &fl3->state))
fcf = 0x2f; // EOP
else
fcf = 0x4f; // MPS
send_hdlc_data(fl3, 0xff, 0x13, fcf, NULL, 0);
}
static void
l3m_finish_eop(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
fl3->pending_mod = HW_MOD_FRH;
fl3->pending_rate = 3;
mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVMCF);
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
}
static void
l3m_activ_mcf(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L3_RECV_MCF);
}
static void
l3m_receive_mcf(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
struct sk_buff *skb = arg;
u8 end, *p = skb->data;
if (skb->len < 3) {
int_errtxt("HDLC too short %d", skb->len);
return;
}
if (*p != 0xff) {
int_errtxt("HDLC addr not FF (%02X)", *p);
}
p++;
if (*p == 0x03) {
end = 0;
} else if (*p == 0x13) {
end = 1;
} else {
int_errtxt("wrong HDLC CTRL (%02X)", *p);
}
p++;
skb_pull(skb, 3);
switch(*p) {
case 0x8C: // MCF
fl3->result = FAXL3_RESULT_MCF;
printk(KERN_DEBUG "got MCF\n");
break;
case 0xCC: // RTP
fl3->result = FAXL3_RESULT_RTP;
printk(KERN_DEBUG "got RTP\n");
break;
case 0x4C: // RTN
fl3->result = FAXL3_RESULT_RTN;
printk(KERN_DEBUG "got RTN\n");
break;
default:
fl3->result = FAXL3_RESULT_NONE;
int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len);
break;
}
}
static void
l3m_finish_mcf(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
int newstate = ST_L3_WAIT_SENDDCN;
if (fl3->result == FAXL3_RESULT_RTN) {
fl3->page_retry++;
if ((fl3->page_retry < 5) && fallback_rate(fl3)) {
newstate = ST_L3_WAIT_SENDDCS;
fill_Dxx(fl3, Dxx_TYPE_DCS);
copy_page_data4retry(fl3);
}
} else if (fl3->result == FAXL3_RESULT_MCF) {
if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) {
init_newpage(fl3);
prepare_page_data(fl3);
mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE);
mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2);
return;
}
} else if (fl3->result == FAXL3_RESULT_RTP) {
if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) {
init_newpage(fl3);
prepare_page_data(fl3);
newstate = ST_L3_WAIT_SENDDCS;
fill_Dxx(fl3, Dxx_TYPE_DCS);
}
} else {
int_errtxt("unhandled result %d abort", fl3->result);
}
fl3->pending_mod = HW_MOD_FTH;
fl3->pending_rate = 3;
mISDN_FsmChangeState(fi, newstate);
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
}
static void
l3m_send_dcn(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
send_hdlc_data(fl3, 0xff, 0x13, 0xfb, NULL, 0);
}
static void
l3m_finish_dcn(struct FsmInst *fi, int event, void *arg)
{
faxl3_t *fl3 = fi->userdata;
send_capi_msg_ncpi(fl3, CAPI_DISCONNECT_B3_IND, 0);
mISDN_FsmChangeState(fi, ST_L3_CLEARING);
}
static struct FsmNode FaxL3FnList[] =
{
{ST_L3_IDLE, EV_CALL_OUT, l3m_callout},
{ST_L3_WAIT_RECVDIS, EV_MODEM_ACTIV, l3m_activ_dis},
{ST_L3_RECV_DIS, EV_DATA, l3m_receive_dis},
{ST_L3_RECV_DIS, EV_MODEM_IDLE, l3m_finish_dis},
{ST_L3_WAIT_SENDDCS, EV_MODEM_ACTIV, l3m_send_dcs},
{ST_L3_SEND_DCS, EV_NEXT_DATA, l3m_send_lastdata},
{ST_L3_SEND_DCS, EV_MODEM_IDLE, l3m_finish_dcs},
{ST_L3_WAIT_SENDTRAIN, EV_DELAYTIMER, l3m_setup_train},
{ST_L3_WAIT_SENDTRAIN, EV_MODEM_ACTIV, l3m_send_train},
{ST_L3_SEND_TRAIN, EV_MODEM_IDLE, l3m_finish_train},
{ST_L3_SEND_TRAIN, EV_NEXT_DATA, l3m_send_lastdata},
{ST_L3_WAIT_TRAINSTATE, EV_MODEM_ACTIV, l3m_activ_trainstate},
{ST_L3_RECV_TRAINSTATE, EV_DATA, l3m_receive_trainstate},
{ST_L3_RECV_TRAINSTATE, EV_MODEM_IDLE, l3m_finish_trainstate},
{ST_L3_WAIT_SENDPAGE, EV_DELAYTIMER, l3m_setup_sendpage},
{ST_L3_WAIT_SENDPAGE, EV_MODEM_ACTIV, l3m_ready_sendpage},
{ST_L3_SEND_PAGE, EV_NEXT_DATA, l3m_send_next_pagedata},
{ST_L3_SEND_PAGE, EV_MODEM_IDLE, l3m_finish_page},
{ST_L3_WAIT_SENDEOP, EV_MODEM_ACTIV, l3m_send_endofpage},
{ST_L3_SEND_EOP, EV_NEXT_DATA, l3m_send_lastdata},
{ST_L3_SEND_EOP, EV_MODEM_IDLE, l3m_finish_eop},
{ST_L3_WAIT_RECVMCF, EV_MODEM_ACTIV, l3m_activ_mcf},
{ST_L3_RECV_MCF, EV_DATA, l3m_receive_mcf},
{ST_L3_RECV_MCF, EV_MODEM_IDLE, l3m_finish_mcf},
{ST_L3_WAIT_SENDDCN, EV_MODEM_ACTIV, l3m_send_dcn},
{ST_L3_SEND_DCN, EV_NEXT_DATA, l3m_send_lastdata},
{ST_L3_SEND_DCN, EV_MODEM_IDLE, l3m_finish_dcn},
};
#define FAXL3_FN_COUNT (sizeof(FaxL3FnList)/sizeof(struct FsmNode))
static int
data_b3_conf(faxl3_t *fl3, struct sk_buff *skb)
{
mISDN_head_t *hh = mISDN_HEAD_P(skb);
u8 buf[4];
hh++;
capimsg_setu16(buf, 0, hh->prim); // datahandle
hh--;
capimsg_setu16(buf, 2, 0); // Info
sendL4frame(fl3, CAPI_DATA_B3_CONF, hh->dinfo, 4, buf, NULL);
dev_kfree_skb(skb);
return(0);
}
static int
copy_page_data4retry(faxl3_t *fl3) {
struct sk_buff_head tmpq;
struct sk_buff *skb, *nskb;
int err = 0;
skb_queue_head_init(&tmpq);
discard_queue(&fl3->pageq);
while((skb = skb_dequeue(&fl3->saveq))) {
nskb = skb_clone(skb, GFP_ATOMIC);
skb_queue_tail(&fl3->pageq, skb);
if (!nskb) {
int_error();
err = -ENOMEM;
} else
skb_queue_tail(&tmpq, nskb);
}
if (err) {
discard_queue(&tmpq);
} else {
while((skb = skb_dequeue(&tmpq)))
skb_queue_tail(&fl3->saveq, skb);
}
return(err);
}
#define PAGE_SKB_LEN 1024
static int
collect_page_data(faxl3_t *fl3, int len, u8 *buf, int flush)
{
int l = len;
struct sk_buff *skb;
if (!fl3->page_skb) {
fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1);
if (!fl3->page_skb)
return(-ENOMEM);
}
if ((fl3->page_skb->len + len) >= PAGE_SKB_LEN) {
l = PAGE_SKB_LEN - fl3->page_skb->len;
memcpy(skb_put(fl3->page_skb, l), buf, l);
buf += l;
skb = skb_clone(fl3->page_skb, GFP_ATOMIC);
if (!skb) {
int_error();
} else {
// for resend pages
skb_queue_tail(&fl3->saveq, skb);
}
skb_queue_tail(&fl3->pageq, fl3->page_skb);
fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1);
if (!fl3->page_skb) {
int_error();
return(-ENOMEM);
}
l = len - l;
}
if (l) {
memcpy(skb_put(fl3->page_skb, l), buf, l);
}
if (flush) {
skb = skb_clone(fl3->page_skb, GFP_ATOMIC);
if (!skb) {
int_error();
} else {
// for resend pages
skb_queue_tail(&fl3->saveq, skb);
}
skb_queue_tail(&fl3->pageq, fl3->page_skb);
fl3->page_skb = NULL;
}
return(0);
}
static int
fill_empty_lines(faxl3_t *fl3, int cnt) {
u8 buf[4], *p;
int l,ret = 0;
if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
printk(KERN_DEBUG "%s %d\n", __FUNCTION__, cnt);
p = buf;
if (fl3->page_llen == 1728) {
*p++ = 0x00;
*p++ = 0x40;
*p++ = 0xd9;
*p++ = 0xa4;
l = 4;
} else {
int_error();
return(-EINVAL);
}
while(cnt) {
ret = collect_page_data(fl3, l, buf, 0);
fl3->line_cnt++;
cnt--;
}
return(ret);
}
static int
fill_rtc(faxl3_t *fl3) {
u8 buf[8] = {0, 0x08, 0x80,};
int cnt = 3, ret = 0;
if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
printk(KERN_DEBUG "%s\n", __FUNCTION__);
while(cnt) {
ret = collect_page_data(fl3, 3, buf, 0);
cnt--;
}
memset(buf, 0 ,8);
ret = collect_page_data(fl3, 8, buf, 1);
return(ret);
}
static int
fill_line(faxl3_t *fl3, int cnt1, u8 *data1, int cnt2, u8 *data2) {
u8 eol[2] = {0x00, 0x80};
int ret = 0;
if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
printk(KERN_DEBUG "%s: %d/%d\n", __FUNCTION__, cnt1, cnt2);
ret = collect_page_data(fl3, 2, eol, 0);
if (cnt1)
ret = collect_page_data(fl3, cnt1, data1, 0);
if (cnt2)
ret = collect_page_data(fl3, cnt2, data2, 0);
fl3->line_cnt++;
return(ret);
}
static int
next_data_skb(faxl3_t *fl3) {
struct sk_buff *skb;
int cnt = 3, ret = 0;
skb = skb_dequeue(&fl3->dataq);
if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
printk(KERN_DEBUG "%s: %p/%p %d %d %lx\n", __FUNCTION__, fl3->data_skb, skb,
fl3->lasttyp, fl3->lastlen, fl3->state);
if (!skb) {
if (fl3->data_skb && (fl3->data_skb->len == 0)) {
data_b3_conf(fl3, fl3->data_skb);
fl3->data_skb = NULL;
test_and_clear_bit(FAXL3_STATE_HAVEDATA, &fl3->state);
}
return(-EAGAIN);
}
if (fl3->data_skb) {
if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
printk(KERN_DEBUG "%s: len(%d) hl(%d)\n", __FUNCTION__,
fl3->data_skb->len, skb_headroom(skb));
if (fl3->data_skb->len) {
if (fl3->data_skb->len <= skb_headroom(skb)) {
memcpy(skb_push(skb, fl3->data_skb->len), fl3->data_skb->data, fl3->data_skb->len);
skb_pull(fl3->data_skb, fl3->data_skb->len);
}
if (test_and_clear_bit(FAXL3_STATE_CONTINUE, &fl3->state)) {
cnt = fl3->lastlen - fl3->data_skb->len;
fill_line(fl3, fl3->data_skb->len, fl3->data_skb->data, cnt, skb->data);
skb_pull(fl3->data_skb, fl3->data_skb->len);
skb_pull(skb, cnt);
}
if (fl3->data_skb->len) {
int_error();
return(-EINVAL);
}
}
data_b3_conf(fl3, fl3->data_skb);
}
fl3->data_skb = skb;
return(ret);
}
static int
prepare_page_data(faxl3_t *fl3)
{
u32 tmp32;
u16 tmp16;
u8 ver, len8;
if (!fl3->data_skb) {
fl3->data_skb = skb_dequeue(&fl3->dataq);
if (!fl3->data_skb)
return(-EAGAIN);
}
if (test_bit(FAXL3_STATE_CONTINUE, &fl3->state)) {
if (next_data_skb(fl3))
return(-EAGAIN);
}
if (!test_and_set_bit(FAXL3_STATE_SFFHEADER, &fl3->state)) {
if (fl3->data_skb->len < 0x14) {
int_error();
return(-EINVAL);
}
tmp32 = CAPIMSG_U32(fl3->data_skb->data, 0);
ver = CAPIMSG_U8(fl3->data_skb->data, 4);
len8 = CAPIMSG_U8(fl3->data_skb->data, 5);
tmp16 = CAPIMSG_U16(fl3->data_skb->data, 6);
printk(KERN_DEBUG "SFFHEADER(%x,%x,%x,%x)\n", tmp32, ver, len8, tmp16);
if (tmp32 != 0x66666653) { // SFFF
int_error();
return(-EINVAL);
}
if (ver != 1) { // only ver 1 supported
int_error();
return(-EINVAL);
}
fl3->pages = CAPIMSG_U16(fl3->data_skb->data, 8);
tmp16 = CAPIMSG_U16(fl3->data_skb->data, 10);
fl3->offset_lpage = CAPIMSG_U32(fl3->data_skb->data, 12);
fl3->offset_dend = CAPIMSG_U32(fl3->data_skb->data, 16);
printk(KERN_DEBUG "SFFHEADER pages %d ofP(%x) o_lpage(%x) o_dend(%x)\n",
fl3->pages, tmp16, fl3->offset_lpage, fl3->offset_dend);
if (tmp16 != 0x14) {
int_error();
return(-EINVAL);
}
skb_pull(fl3->data_skb, 0x14);
}
if (fl3->data_skb->len < 2) {
if (next_data_skb(fl3))
return(-EAGAIN);
}
while (fl3->data_skb->len > 1) {
fl3->lasttyp = CAPIMSG_U8(fl3->data_skb->data, 0);
fl3->lastlen = 0;
if (fl3->lasttyp == 255) {
int_error();
return(-EINVAL);
} else if (fl3->lasttyp == 254) {
// page header
len8 = CAPIMSG_U8(fl3->data_skb->data, 1);
printk(KERN_DEBUG "current page end: %d lines\n", fl3->line_cnt);
if (len8 == 0) {
// doc end
printk(KERN_DEBUG "SFF doc end found\n");
test_and_set_bit(FAXL3_STATE_LASTPAGE, &fl3->state);
test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
skb_pull(fl3->data_skb, 2);
fill_rtc(fl3);
// TODO clean up skb
return(0);
}
if (test_and_set_bit(FAXL3_STATE_NEWPAGE, &fl3->state)) {
// next page
fill_rtc(fl3);
test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
return(0);
}
if (fl3->data_skb->len < (2 + len8)) {
if (next_data_skb(fl3))
return(-EAGAIN);
}
printk(KERN_DEBUG "SFF page header len %d\n", len8);
if (len8 < 10) {
int_error();
return(-EINVAL);
}
fl3->page_vres = CAPIMSG_U8(fl3->data_skb->data, 2);
fl3->page_hres = CAPIMSG_U8(fl3->data_skb->data, 3);
fl3->page_code = CAPIMSG_U8(fl3->data_skb->data, 4);
fl3->page_rsv1 = CAPIMSG_U8(fl3->data_skb->data, 5);
fl3->page_llen = CAPIMSG_U16(fl3->data_skb->data, 6);
fl3->page_plen = CAPIMSG_U16(fl3->data_skb->data, 8);
fl3->page_oprv = CAPIMSG_U32(fl3->data_skb->data, 10);
fl3->page_onxt = CAPIMSG_U32(fl3->data_skb->data, 14);
skb_pull(fl3->data_skb, len8 +2);
printk(KERN_DEBUG "SFF page header: vres(%x) hres(%x) code(%x) resrv(%x)\n",
fl3->page_vres, fl3->page_hres, fl3->page_code, fl3->page_rsv1);
printk(KERN_DEBUG "SFF page header: llen(%d) plen(%d) op(%x) on(%x)\n",
fl3->page_llen, fl3->page_plen, fl3->page_oprv, fl3->page_onxt);
continue;
} else if (fl3->lasttyp == 0) {
if (fl3->data_skb->len < 3) {
if (next_data_skb(fl3))
return(-EAGAIN);
}
fl3->lastlen = CAPIMSG_U16(fl3->data_skb->data, 1);
skb_pull(fl3->data_skb, 3);
} else if (fl3->lasttyp < 216) {
fl3->lastlen = fl3->lasttyp;
skb_pull(fl3->data_skb, 1);
} else if (fl3->lasttyp < 253) {
// white lines
skb_pull(fl3->data_skb, 1);
fill_empty_lines(fl3, fl3->lasttyp - 216);
continue;
}
if (fl3->data_skb->len < fl3->lastlen) {
test_and_set_bit(FAXL3_STATE_CONTINUE, &fl3->state);
break;
}
fill_line(fl3, fl3->lastlen, fl3->data_skb->data, 0, NULL);
skb_pull(fl3->data_skb, fl3->lastlen);
}
return(0);
}
static u16
data_b3_req(faxl3_t *fl3, struct sk_buff *skb)
{
__u16 size;
mISDN_head_t *hh = mISDN_HEAD_P(skb);
if (skb->len < 10) {
int_errtxt("skb too short");
return(0x2007);
}
size = CAPIMSG_U16(skb->data, 4);
/* we save DataHandle and Flags in a area after normal mISDN_HEAD */
hh++;
hh->prim = CAPIMSG_U16(skb->data, 6);
hh->dinfo = CAPIMSG_U16(skb->data, 8);
/* the data begins behind the header, we don't use Data32/Data64 here */
if ((skb->len - size) == 18)
skb_pull(skb, 18);
else if ((skb->len - size) == 10) // old format
skb_pull(skb, 10);
else {
int_errtxt("skb data_b3 header len mismatch len %d", skb->len - size);
return(0x2007);
}
if (test_and_set_bit(FAXL3_STATE_HAVEDATA, &fl3->state)) {
skb_queue_tail(&fl3->dataq, skb);
} else {
fl3->data_skb = skb;
prepare_page_data(fl3);
}
return(0);
}
static int
data_b3_resp(faxl3_t *faxl3, u_int di, struct sk_buff *skb)
{
dev_kfree_skb(skb);
return(0);
}
static int
connect_b3_req(faxl3_t *fl3, u_int di, u32 addr, struct sk_buff *skb)
{
u16 info = 0;
print_hexdata(fl3, "NCPI: ", skb->len, skb->data);
fl3->pending_mod = HW_MOD_FRH;
fl3->pending_rate = 3;
mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
mISDN_FsmEvent(&fl3->main, EV_CALL_OUT, NULL);
fl3->ncci = 0x10000 | addr;
skb_push(skb, 4);
sendL4frame(fl3, CAPI_CONNECT_B3_CONF, di, 2, &info, skb);
return(0);
}
static int
sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb)
{
u_char *p;
int ret;
if (!skb) {
skb = alloc_stack_skb(len + 20, fl3->up_headerlen);
if (!skb)
return(-ENOMEM);
} else {
skb_trim(skb, 0);
}
capimsg_setu32(skb_put(skb, 4), 0, fl3->ncci);
switch(prim) {
case CAPI_CONNECT_B3_CONF:
capimsg_setu16(skb_put(skb, 2), 0, *((u16 *)arg));
break;
case CAPI_DISCONNECT_B3_IND:
// capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff);
case CAPI_CONNECT_B3_IND:
case CAPI_RESET_B3_IND:
case CAPI_CONNECT_B3_ACTIVE_IND:
case CAPI_DATA_B3_CONF:
if (len) {
p = skb_put(skb, len);
memcpy(p, arg, len);
} else {
p = skb_put(skb, 1);
*p = 0;
}
break;
default:
int_error();
dev_kfree_skb(skb);
return(-EINVAL);
}
ret = if_newhead(&fl3->inst.up, prim, di, skb);
if (ret) {
printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret);
dev_kfree_skb(skb);
}
return(ret);
}
static int
send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info)
{
u8 ncpi[36], *p, off=0, len = 0;
u16 lastmod = 0;
memset(ncpi, 0, 36);
switch(prim) {
case CAPI_CONNECT_B3_ACTIVE_IND:
if (is_valid_rate_idx(fl3, fl3->current_rate_idx))
lastmod = FaxModulationBaud[fl3->current_rate_idx];
p = fl3->CIS;
break;
case CAPI_DISCONNECT_B3_IND:
if (is_valid_rate_idx(fl3, fl3->current_rate_idx))
lastmod = FaxModulationBaud[fl3->current_rate_idx];
p = fl3->CIS;
capimsg_setu16(ncpi, 0, Info);
off = 2;
break;
default:
int_error();
return(-EINVAL);
}
capimsg_setu16(ncpi, off+1, lastmod);
capimsg_setu16(ncpi, off+3, 0x8000); // FIXME no ECM
capimsg_setu16(ncpi, off+5, 0);
capimsg_setu16(ncpi, off+7, 0);
len = strlen(p);
if (len > 20)
len = 20;
capimsg_setu8(ncpi, off+9, len);
if (len)
memcpy(&ncpi[off+10], p, len);
len += 9; // 8*u16 + lenfield
capimsg_setu8(ncpi, off, len);
return(sendL4frame(fl3, prim, 0, len + off, ncpi, NULL));
}
static int
faxl3_from_up(mISDNif_t *hif, struct sk_buff *skb)
{
faxl3_t *faxl3;
mISDN_head_t *hh;
__u32 addr;
__u16 info = 0;
int err = 0;
if (!hif || !hif->fdata || !skb)
return(-EINVAL);
faxl3 = hif->fdata;
if (!faxl3->inst.down.func) {
return(-ENXIO);
}
hh = mISDN_HEAD_P(skb);
if (faxl3->debug & DEBUG_FAXL3_FUNC)
printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len);
if (skb->len < 4) {
printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len);
return(-EINVAL);
} else {
addr = CAPIMSG_U32(skb->data, 0);
skb_pull(skb, 4);
}
if (faxl3->debug & DEBUG_FAXL3_FUNC)
printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr);
switch(hh->prim) {
case CAPI_DATA_B3_REQ:
info = data_b3_req(faxl3, skb);
if (info) {
} else
err = 0;
break;
case CAPI_DATA_B3_RESP:
return(data_b3_resp(faxl3, hh->dinfo, skb));
case CAPI_CONNECT_B3_REQ:
return(connect_b3_req(faxl3, hh->dinfo, addr, skb));
case CAPI_RESET_B3_REQ:
break;
case CAPI_DISCONNECT_B3_REQ:
break;
case CAPI_CONNECT_B3_RESP:
if (skb->len <= 2) {
printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n",
__FUNCTION__, skb->len);
skb_push(skb, 4);
return(-EINVAL);
}
info = CAPIMSG_U16(skb->data, 0);
skb_pull(skb, 2);
if (info == 0)
;
if (skb->len <= 4) { // default NCPI
} else {
}
dev_kfree_skb(skb);
err = 0;
break;
case CAPI_CONNECT_B3_ACTIVE_RESP:
// nothing to do
dev_kfree_skb(skb);
err = 0;
break;
case CAPI_RESET_B3_RESP:
dev_kfree_skb(skb);
err = 0;
break;
case CAPI_DISCONNECT_B3_RESP:
dev_kfree_skb(skb);
err = 0;
break;
default:
printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n",
__FUNCTION__, hh->prim, hh->dinfo);
err = -EINVAL;
}
return(err);
}
static void
ph_status_ind(faxl3_t *fl3, int status)
{
switch(status) {
case HW_MOD_READY:
mISDN_FsmEvent(&fl3->mod, EV_MOD_READY, NULL);
break;
case HW_MOD_CONNECT:
mISDN_FsmEvent(&fl3->mod, EV_MOD_CONNECT, NULL);
break;
case HW_MOD_OK:
case HW_MOD_NOCARR:
mISDN_FsmEvent(&fl3->mod, EV_MOD_NOCARRIER, NULL);
break;
default:
int_errtxt("unhandled status(%x)", status);
break;
}
}
static void
ph_data_cnf(faxl3_t *fl3, int status)
{
struct sk_buff *skb ;
mISDNif_t *down = &fl3->inst.down;
int ret;
if (!test_bit(FAXL3_STATE_DATABUSY, &fl3->state)) {
int_errtxt("PH_DATA | CONFIRM without DATABUSY");
return;
}
skb = skb_dequeue(&fl3->downq);
if ((skb == NULL) && test_bit(FAXL3_STATE_DATAREADY, &fl3->state)) {
skb = skb_dequeue(&fl3->pageq);
}
if (skb) {
mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb);
ret = down->func(down, skb);
if (ret) {
dev_kfree_skb(skb);
int_errtxt("down: error(%d)", ret);
return;
}
} else {
test_and_clear_bit(FAXL3_STATE_DATABUSY, &fl3->state);
mISDN_FsmEvent(&fl3->main, EV_NEXT_DATA, NULL);
}
}
static int
faxl3_from_down(mISDNif_t *hif, struct sk_buff *skb)
{
faxl3_t *faxl3;
mISDN_head_t *hh;
int err = 0;
if (!hif || !hif->fdata || !skb)
return(-EINVAL);
faxl3 = hif->fdata;
if (!faxl3->inst.up.func) {
return(-ENXIO);
}
hh = mISDN_HEAD_P(skb);
if (faxl3->debug & DEBUG_FAXL3_FUNC)
printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len);
switch(hh->prim) {
case PH_ACTIVATE | INDICATION:
case PH_ACTIVATE | CONFIRM:
break;
case PH_STATUS | INDICATION:
ph_status_ind(faxl3, hh->dinfo);
break;
case PH_DATA | INDICATION:
mISDN_FsmEvent(&faxl3->main, EV_DATA, skb);
break;
case PH_DATA | CONFIRM:
ph_data_cnf(faxl3, hh->dinfo);
break;
default:
printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n",
__FUNCTION__, hh->prim, hh->dinfo);
err = -EINVAL;
break;
}
if (!err)
dev_kfree_skb(skb);
return(err);
}
static char MName[] = "FAXL3";
#ifdef MODULE
MODULE_AUTHOR("Karsten Keil");
module_param(debug, uint, S_IRUGO | S_IWUSR);
module_param(ttt, uint, S_IRUGO | S_IWUSR);
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
#endif
static void
l3m_debug(struct FsmInst *fi, char *fmt, ...)
{
faxl3_t *fl3 = fi->userdata;
logdata_t log;
va_start(log.args, fmt);
log.fmt = fmt;
log.head = fl3->inst.name;
mISDN_ctrl(&fl3->inst, MGR_DEBUGDATA | REQUEST, &log);
va_end(log.args);
}
static void
release_faxl3(faxl3_t *faxl3) {
mISDNinstance_t *inst = &faxl3->inst;
if (inst->up.peer) {
mISDN_ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
}
if (inst->down.peer) {
mISDN_ctrl(inst->down.peer, MGR_DISCONNECT | REQUEST, &inst->down);
}
list_del(&faxl3->list);
discard_queue(&faxl3->downq);
discard_queue(&faxl3->dataq);
discard_queue(&faxl3->pageq);
discard_queue(&faxl3->saveq);
mISDN_FsmDelTimer(&faxl3->deltimer, 99);
mISDN_FsmDelTimer(&faxl3->timer1, 99);
mISDN_FsmDelTimer(&faxl3->modtimer, 99);
if (faxl3->entity != MISDN_ENTITY_NONE)
mISDN_ctrl(inst, MGR_DELENTITY | REQUEST, (void *)faxl3->entity);
mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
kfree(faxl3);
}
static int
new_faxl3(mISDNstack_t *st, mISDN_pid_t *pid) {
faxl3_t *n_faxl3;
u8 *p;
int err;
if (!st || !pid)
return(-EINVAL);
if (!(n_faxl3 = kmalloc(sizeof(faxl3_t), GFP_ATOMIC))) {
printk(KERN_ERR "kmalloc faxl3_t failed\n");
return(-ENOMEM);
}
memset(n_faxl3, 0, sizeof(faxl3_t));
n_faxl3->entity = MISDN_ENTITY_NONE;
n_faxl3->next_id = 1;
spin_lock_init(&n_faxl3->lock);
memcpy(&n_faxl3->inst.pid, pid, sizeof(mISDN_pid_t));
if (n_faxl3->inst.pid.global == 1)
test_and_set_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state);
p = n_faxl3->inst.pid.param[3];
if (p) {
if (*p < 6) {
int_errtxt("B3cfg too shoort(%d)", *p);
} else {
n_faxl3->options = CAPIMSG_U16(p, 1);
n_faxl3->format = CAPIMSG_U16(p, 3);
p += 5;
if (*p && (*p <= 20))
memcpy(n_faxl3->stationID, p, *p + 1);
p += (*p +1);
if (*p && (*p <= 62))
memcpy(n_faxl3->headline, p, *p + 1);
}
}
if (debug & DEBUG_FAXL3_CFG) {
printk(KERN_DEBUG "%s opt(%x) fmt(%x) id=%s head=%s\n",
test_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state) ? "out" : "in",
n_faxl3->options,
n_faxl3->format,
&n_faxl3->stationID[1],
&n_faxl3->headline[1]);
if (n_faxl3->inst.pid.param[1]) {
p = n_faxl3->inst.pid.param[1];
printk(KERN_DEBUG "B1 param len %d rate %d\n", *p,
(*p > 1) ? *((u16 *)(p+1)) : -1);
}
}
mISDN_init_instance(&n_faxl3->inst, &faxl3_obj, n_faxl3);
if (!mISDN_SetHandledPID(&faxl3_obj, &n_faxl3->inst.pid)) {
int_error();
kfree(n_faxl3);
return(-ENOPROTOOPT);
}
n_faxl3->own_rate_mask = FAXMODM_V27_V29_V33_V17;
n_faxl3->current_rate_idx = -1;
n_faxl3->current_mod = -1;
n_faxl3->current_rate = -1;
n_faxl3->pending_mod = -1;
n_faxl3->pending_rate = -1;
n_faxl3->debug = debug;
n_faxl3->main.fsm = &faxl3fsm;
n_faxl3->main.state = ST_L3_IDLE;
n_faxl3->main.debug = debug;
n_faxl3->main.userdata = n_faxl3;
n_faxl3->main.userint = 0;
n_faxl3->main.printdebug = l3m_debug;
mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->deltimer);
mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->timer1);
n_faxl3->mod.fsm = &modfsm;
n_faxl3->mod.state = ST_MOD_NULL;
n_faxl3->mod.debug = debug;
n_faxl3->mod.userdata = n_faxl3;
n_faxl3->mod.userint = 0;
n_faxl3->mod.printdebug = l3m_debug;
mISDN_FsmInitTimer(&n_faxl3->mod, &n_faxl3->modtimer);
skb_queue_head_init(&n_faxl3->downq);
skb_queue_head_init(&n_faxl3->dataq);
skb_queue_head_init(&n_faxl3->pageq);
skb_queue_head_init(&n_faxl3->saveq);
list_add_tail(&n_faxl3->list, &faxl3_obj.ilist);
n_faxl3->entity = MISDN_ENTITY_NONE;
err = mISDN_ctrl(&n_faxl3->inst, MGR_NEWENTITY | REQUEST, NULL);
if (err) {
printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n",
__FUNCTION__, err);
}
err = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &n_faxl3->inst);
if (err) {
list_del(&n_faxl3->list);
kfree(n_faxl3);
} else {
if (st->para.maxdatalen)
n_faxl3->maxdatalen = st->para.maxdatalen;
if (st->para.up_headerlen)
n_faxl3->up_headerlen = st->para.up_headerlen;
if (st->para.down_headerlen)
n_faxl3->down_headerlen = st->para.down_headerlen;
if (debug)
printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__,
n_faxl3->maxdatalen, n_faxl3->up_headerlen, n_faxl3->down_headerlen);
}
return(err);
}
static int
faxl3_manager(void *data, u_int prim, void *arg) {
mISDNinstance_t *inst = data;
faxl3_t *faxl3_l;
int err = -EINVAL;
if (debug & DEBUG_FAXL3_MGR)
printk(KERN_DEBUG "faxl3_manager data:%p prim:%x arg:%p\n", data, prim, arg);
if (!data)
return(err);
list_for_each_entry(faxl3_l, &faxl3_obj.ilist, list) {
if (&faxl3_l->inst == inst) {
err = 0;
break;
}
}
if (prim == (MGR_NEWLAYER | REQUEST))
return(new_faxl3(data, arg));
if (err) {
printk(KERN_WARNING "faxl3_manager prim(%x) no instance\n", prim);
return(err);
}
switch(prim) {
case MGR_CLRSTPARA | INDICATION:
case MGR_CLONELAYER | REQUEST:
break;
case MGR_ADDSTPARA | INDICATION:
{
mISDN_stPara_t *stp = arg;
if (stp->down_headerlen)
faxl3_l->down_headerlen = stp->down_headerlen;
if (stp->up_headerlen)
faxl3_l->up_headerlen = stp->up_headerlen;
printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n",
stp->maxdatalen, stp->down_headerlen, stp->up_headerlen);
}
break;
case MGR_CONNECT | REQUEST:
return(mISDN_ConnectIF(inst, arg));
case MGR_NEWENTITY | CONFIRM:
faxl3_l->entity = (int)arg;
break;
case MGR_SETIF | REQUEST:
case MGR_SETIF | INDICATION:
return(mISDN_SetIF(inst, arg, prim, faxl3_from_up, faxl3_from_down, faxl3_l));
case MGR_DISCONNECT | REQUEST:
case MGR_DISCONNECT | INDICATION:
return(mISDN_DisConnectIF(inst, arg));
case MGR_UNREGLAYER | REQUEST:
case MGR_RELEASE | INDICATION:
if (debug & DEBUG_FAXL3_MGR)
printk(KERN_DEBUG "release_faxl3 id %x\n", faxl3_l->inst.st->id);
release_faxl3(faxl3_l);
break;
default:
if (debug & DEBUG_FAXL3_MGR)
printk(KERN_WARNING "faxl3_manager prim %x not handled\n", prim);
return(-EINVAL);
}
return(0);
}
static int faxl3_init(void)
{
int err;
printk(KERN_INFO "%s modul version %s\n", MName, mISDN_getrev(mISDN_faxl3_revision));
#ifdef MODULE
faxl3_obj.owner = THIS_MODULE;
#endif
faxl3_obj.name = MName;
faxl3_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_T30;
faxl3_obj.own_ctrl = faxl3_manager;
INIT_LIST_HEAD(&faxl3_obj.ilist);
if ((err = mISDN_register(&faxl3_obj))) {
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
return(err);
}
faxl3fsm.state_count = FAXL3_STATE_COUNT;
faxl3fsm.event_count = FAXL3_EVENT_COUNT;
faxl3fsm.strEvent = strfaxl3Event;
faxl3fsm.strState = strfaxl3State;
mISDN_FsmNew(&faxl3fsm, FaxL3FnList, FAXL3_FN_COUNT);
modfsm.state_count = MOD_STATE_COUNT;
modfsm.event_count = MOD_EVENT_COUNT;
modfsm.strEvent = strmodEvent;
modfsm.strState = strmodState;
mISDN_FsmNew(&modfsm, ModFnList, MOD_FN_COUNT);
return(err);
}
static void faxl3_cleanup(void)
{
faxl3_t *l3, *nl3;
int err;
if ((err = mISDN_unregister(&faxl3_obj))) {
printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err);
}
if(!list_empty(&faxl3_obj.ilist)) {
printk(KERN_WARNING "faxl3 inst list not empty\n");
list_for_each_entry_safe(l3, nl3, &faxl3_obj.ilist, list)
release_faxl3(l3);
}
mISDN_FsmFree(&faxl3fsm);
mISDN_FsmFree(&modfsm);
}
module_init(faxl3_init);
module_exit(faxl3_cleanup);