2005-04-17 02:20:36 +04:00
/* $Id: l3dss1.c,v 2.32.2.3 2004/01/13 14:31:25 keil Exp $
*
* EURO / DSS1 D - channel protocol
*
* German 1 TR6 D - channel protocol
*
* Author Karsten Keil
* based on the teles driver from Jan den Ouden
* Copyright by Karsten Keil < keil @ isdn4linux . de >
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* For changes and modifications please read
* Documentation / isdn / HiSax . cert
*
* Thanks to Jan den Ouden
* Fritz Elfert
*
*/
# include "hisax.h"
# include "isdnl3.h"
# include "l3dss1.h"
# include <linux/ctype.h>
extern char * HiSax_getrev ( const char * revision ) ;
2005-06-26 01:59:18 +04:00
static const char * dss1_revision = " $Revision: 2.32.2.3 $ " ;
2005-04-17 02:20:36 +04:00
# define EXT_BEARER_CAPS 1
# define MsgHead(ptr, cref, mty) \
* ptr + + = 0x8 ; \
if ( cref = = - 1 ) { \
* ptr + + = 0x0 ; \
} else { \
* ptr + + = 0x1 ; \
* ptr + + = cref ^ 0x80 ; \
} \
* ptr + + = mty
/**********************************************/
/* get a new invoke id for remote operations. */
/* Only a return value != 0 is valid */
/**********************************************/
static unsigned char new_invoke_id ( struct PStack * p )
{
unsigned char retval ;
int i ;
i = 32 ; /* maximum search depth */
retval = p - > prot . dss1 . last_invoke_id + 1 ; /* try new id */
while ( ( i ) & & ( p - > prot . dss1 . invoke_used [ retval > > 3 ] = = 0xFF ) ) {
p - > prot . dss1 . last_invoke_id = ( retval & 0xF8 ) + 8 ;
i - - ;
}
if ( i ) {
while ( p - > prot . dss1 . invoke_used [ retval > > 3 ] & ( 1 < < ( retval & 7 ) ) )
retval + + ;
} else
retval = 0 ;
p - > prot . dss1 . last_invoke_id = retval ;
p - > prot . dss1 . invoke_used [ retval > > 3 ] | = ( 1 < < ( retval & 7 ) ) ;
return ( retval ) ;
} /* new_invoke_id */
/*************************/
/* free a used invoke id */
/*************************/
static void free_invoke_id ( struct PStack * p , unsigned char id )
{
if ( ! id ) return ; /* 0 = invalid value */
p - > prot . dss1 . invoke_used [ id > > 3 ] & = ~ ( 1 < < ( id & 7 ) ) ;
} /* free_invoke_id */
/**********************************************************/
/* create a new l3 process and fill in dss1 specific data */
/**********************************************************/
static struct l3_process
* dss1_new_l3_process ( struct PStack * st , int cr )
{ struct l3_process * proc ;
if ( ! ( proc = new_l3_process ( st , cr ) ) )
return ( NULL ) ;
proc - > prot . dss1 . invoke_id = 0 ;
proc - > prot . dss1 . remote_operation = 0 ;
proc - > prot . dss1 . uus1_data [ 0 ] = ' \0 ' ;
return ( proc ) ;
} /* dss1_new_l3_process */
/************************************************/
/* free a l3 process and all dss1 specific data */
/************************************************/
static void
dss1_release_l3_process ( struct l3_process * p )
{
free_invoke_id ( p - > st , p - > prot . dss1 . invoke_id ) ;
release_l3_process ( p ) ;
} /* dss1_release_l3_process */
/********************************************************/
/* search a process with invoke id id and dummy callref */
/********************************************************/
static struct l3_process *
l3dss1_search_dummy_proc ( struct PStack * st , int id )
{ struct l3_process * pc = st - > l3 . proc ; /* start of processes */
if ( ! id ) return ( NULL ) ;
while ( pc )
{ if ( ( pc - > callref = = - 1 ) & & ( pc - > prot . dss1 . invoke_id = = id ) )
return ( pc ) ;
pc = pc - > next ;
}
return ( NULL ) ;
} /* l3dss1_search_dummy_proc */
/*******************************************************************/
/* called when a facility message with a dummy callref is received */
/* and a return result is delivered. id specifies the invoke id. */
/*******************************************************************/
static void
l3dss1_dummy_return_result ( struct PStack * st , int id , u_char * p , u_char nlen )
{ isdn_ctrl ic ;
struct IsdnCardState * cs ;
struct l3_process * pc = NULL ;
if ( ( pc = l3dss1_search_dummy_proc ( st , id ) ) )
{ L3DelTimer ( & pc - > timer ) ; /* remove timer */
cs = pc - > st - > l1 . hardware ;
ic . driver = cs - > myid ;
ic . command = ISDN_STAT_PROT ;
ic . arg = DSS1_STAT_INVOKE_RES ;
ic . parm . dss1_io . hl_id = pc - > prot . dss1 . invoke_id ;
ic . parm . dss1_io . ll_id = pc - > prot . dss1 . ll_id ;
ic . parm . dss1_io . proc = pc - > prot . dss1 . proc ;
ic . parm . dss1_io . timeout = 0 ;
ic . parm . dss1_io . datalen = nlen ;
ic . parm . dss1_io . data = p ;
free_invoke_id ( pc - > st , pc - > prot . dss1 . invoke_id ) ;
pc - > prot . dss1 . invoke_id = 0 ; /* reset id */
cs - > iif . statcallb ( & ic ) ;
dss1_release_l3_process ( pc ) ;
}
else
l3_debug ( st , " dummy return result id=0x%x result len=%d " , id , nlen ) ;
} /* l3dss1_dummy_return_result */
/*******************************************************************/
/* called when a facility message with a dummy callref is received */
/* and a return error is delivered. id specifies the invoke id. */
/*******************************************************************/
static void
l3dss1_dummy_error_return ( struct PStack * st , int id , ulong error )
{ isdn_ctrl ic ;
struct IsdnCardState * cs ;
struct l3_process * pc = NULL ;
if ( ( pc = l3dss1_search_dummy_proc ( st , id ) ) )
{ L3DelTimer ( & pc - > timer ) ; /* remove timer */
cs = pc - > st - > l1 . hardware ;
ic . driver = cs - > myid ;
ic . command = ISDN_STAT_PROT ;
ic . arg = DSS1_STAT_INVOKE_ERR ;
ic . parm . dss1_io . hl_id = pc - > prot . dss1 . invoke_id ;
ic . parm . dss1_io . ll_id = pc - > prot . dss1 . ll_id ;
ic . parm . dss1_io . proc = pc - > prot . dss1 . proc ;
ic . parm . dss1_io . timeout = error ;
ic . parm . dss1_io . datalen = 0 ;
ic . parm . dss1_io . data = NULL ;
free_invoke_id ( pc - > st , pc - > prot . dss1 . invoke_id ) ;
pc - > prot . dss1 . invoke_id = 0 ; /* reset id */
cs - > iif . statcallb ( & ic ) ;
dss1_release_l3_process ( pc ) ;
}
else
l3_debug ( st , " dummy return error id=0x%x error=0x%lx " , id , error ) ;
} /* l3dss1_error_return */
/*******************************************************************/
/* called when a facility message with a dummy callref is received */
/* and a invoke is delivered. id specifies the invoke id. */
/*******************************************************************/
static void
l3dss1_dummy_invoke ( struct PStack * st , int cr , int id ,
int ident , u_char * p , u_char nlen )
{ isdn_ctrl ic ;
struct IsdnCardState * cs ;
l3_debug ( st , " dummy invoke %s id=0x%x ident=0x%x datalen=%d " ,
( cr = = - 1 ) ? " local " : " broadcast " , id , ident , nlen ) ;
if ( cr > = - 1 ) return ; /* ignore local data */
cs = st - > l1 . hardware ;
ic . driver = cs - > myid ;
ic . command = ISDN_STAT_PROT ;
ic . arg = DSS1_STAT_INVOKE_BRD ;
ic . parm . dss1_io . hl_id = id ;
ic . parm . dss1_io . ll_id = 0 ;
ic . parm . dss1_io . proc = ident ;
ic . parm . dss1_io . timeout = 0 ;
ic . parm . dss1_io . datalen = nlen ;
ic . parm . dss1_io . data = p ;
cs - > iif . statcallb ( & ic ) ;
} /* l3dss1_dummy_invoke */
static void
l3dss1_parse_facility ( struct PStack * st , struct l3_process * pc ,
int cr , u_char * p )
{
int qd_len = 0 ;
unsigned char nlen = 0 , ilen , cp_tag ;
int ident , id ;
ulong err_ret ;
if ( pc )
st = pc - > st ; /* valid Stack */
else
if ( ( ! st ) | | ( cr > = 0 ) ) return ; /* neither pc nor st specified */
p + + ;
qd_len = * p + + ;
if ( qd_len = = 0 ) {
l3_debug ( st , " qd_len == 0 " ) ;
return ;
}
if ( ( * p & 0x1F ) ! = 0x11 ) { /* Service discriminator, supplementary service */
l3_debug ( st , " supplementary service != 0x11 " ) ;
return ;
}
while ( qd_len > 0 & & ! ( * p & 0x80 ) ) { /* extension ? */
p + + ;
qd_len - - ;
}
if ( qd_len < 2 ) {
l3_debug ( st , " qd_len < 2 " ) ;
return ;
}
p + + ;
qd_len - - ;
if ( ( * p & 0xE0 ) ! = 0xA0 ) { /* class and form */
l3_debug ( st , " class and form != 0xA0 " ) ;
return ;
}
cp_tag = * p & 0x1F ; /* remember tag value */
p + + ;
qd_len - - ;
if ( qd_len < 1 )
{ l3_debug ( st , " qd_len < 1 " ) ;
return ;
}
if ( * p & 0x80 )
{ /* length format indefinite or limited */
nlen = * p + + & 0x7F ; /* number of len bytes or indefinite */
if ( ( qd_len - - < ( ( ! nlen ) ? 3 : ( 1 + nlen ) ) ) | |
( nlen > 1 ) )
{ l3_debug ( st , " length format error or not implemented " ) ;
return ;
}
if ( nlen = = 1 )
{ nlen = * p + + ; /* complete length */
qd_len - - ;
}
else
{ qd_len - = 2 ; /* trailing null bytes */
if ( ( * ( p + qd_len ) ) | | ( * ( p + qd_len + 1 ) ) )
{ l3_debug ( st , " length format indefinite error " ) ;
return ;
}
nlen = qd_len ;
}
}
else
{ nlen = * p + + ;
qd_len - - ;
}
if ( qd_len < nlen )
{ l3_debug ( st , " qd_len < nlen " ) ;
return ;
}
qd_len - = nlen ;
if ( nlen < 2 )
{ l3_debug ( st , " nlen < 2 " ) ;
return ;
}
if ( * p ! = 0x02 )
{ /* invoke identifier tag */
l3_debug ( st , " invoke identifier tag !=0x02 " ) ;
return ;
}
p + + ;
nlen - - ;
if ( * p & 0x80 )
{ /* length format */
l3_debug ( st , " invoke id length format 2 " ) ;
return ;
}
ilen = * p + + ;
nlen - - ;
if ( ilen > nlen | | ilen = = 0 )
{ l3_debug ( st , " ilen > nlen || ilen == 0 " ) ;
return ;
}
nlen - = ilen ;
id = 0 ;
while ( ilen > 0 )
{ id = ( id < < 8 ) | ( * p + + & 0xFF ) ; /* invoke identifier */
ilen - - ;
}
switch ( cp_tag ) { /* component tag */
case 1 : /* invoke */
if ( nlen < 2 ) {
l3_debug ( st , " nlen < 2 22 " ) ;
return ;
}
if ( * p ! = 0x02 ) { /* operation value */
l3_debug ( st , " operation value !=0x02 " ) ;
return ;
}
p + + ;
nlen - - ;
ilen = * p + + ;
nlen - - ;
if ( ilen > nlen | | ilen = = 0 ) {
l3_debug ( st , " ilen > nlen || ilen == 0 22 " ) ;
return ;
}
nlen - = ilen ;
ident = 0 ;
while ( ilen > 0 ) {
ident = ( ident < < 8 ) | ( * p + + & 0xFF ) ;
ilen - - ;
}
if ( ! pc )
{ l3dss1_dummy_invoke ( st , cr , id , ident , p , nlen ) ;
return ;
}
2005-07-27 22:45:17 +04:00
# ifdef HISAX_DE_AOC
2005-04-17 02:20:36 +04:00
{
# define FOO1(s,a,b) \
while ( nlen > 1 ) { \
int ilen = p [ 1 ] ; \
if ( nlen < ilen + 2 ) { \
l3_debug ( st , " FOO1 nlen < ilen+2 " ) ; \
return ; \
} \
nlen - = ilen + 2 ; \
if ( ( * p & 0xFF ) = = ( a ) ) { \
int nlen = ilen ; \
p + = 2 ; \
b ; \
} else { \
p + = ilen + 2 ; \
} \
}
switch ( ident ) {
case 0x22 : /* during */
FOO1 ( " 1A " , 0x30 , FOO1 ( " 1C " , 0xA1 , FOO1 ( " 1D " , 0x30 , FOO1 ( " 1E " , 0x02 , ( {
ident = 0 ;
nlen = ( nlen ) ? nlen : 0 ; /* Make gcc happy */
while ( ilen > 0 ) {
ident = ( ident < < 8 ) | * p + + ;
ilen - - ;
}
if ( ident > pc - > para . chargeinfo ) {
pc - > para . chargeinfo = ident ;
st - > l3 . l3l4 ( st , CC_CHARGE | INDICATION , pc ) ;
}
if ( st - > l3 . debug & L3_DEB_CHARGE ) {
if ( * ( p + 2 ) = = 0 ) {
l3_debug ( st , " charging info during %d " , pc - > para . chargeinfo ) ;
}
else {
l3_debug ( st , " charging info final %d " , pc - > para . chargeinfo ) ;
}
}
}
) ) ) ) )
break ;
case 0x24 : /* final */
FOO1 ( " 2A " , 0x30 , FOO1 ( " 2B " , 0x30 , FOO1 ( " 2C " , 0xA1 , FOO1 ( " 2D " , 0x30 , FOO1 ( " 2E " , 0x02 , ( {
ident = 0 ;
nlen = ( nlen ) ? nlen : 0 ; /* Make gcc happy */
while ( ilen > 0 ) {
ident = ( ident < < 8 ) | * p + + ;
ilen - - ;
}
if ( ident > pc - > para . chargeinfo ) {
pc - > para . chargeinfo = ident ;
st - > l3 . l3l4 ( st , CC_CHARGE | INDICATION , pc ) ;
}
if ( st - > l3 . debug & L3_DEB_CHARGE ) {
l3_debug ( st , " charging info final %d " , pc - > para . chargeinfo ) ;
}
}
) ) ) ) ) )
break ;
default :
l3_debug ( st , " invoke break invalid ident %02x " , ident ) ;
break ;
}
# undef FOO1
}
# else /* not HISAX_DE_AOC */
l3_debug ( st , " invoke break " ) ;
# endif /* not HISAX_DE_AOC */
break ;
case 2 : /* return result */
/* if no process available handle separately */
if ( ! pc )
{ if ( cr = = - 1 )
l3dss1_dummy_return_result ( st , id , p , nlen ) ;
return ;
}
if ( ( pc - > prot . dss1 . invoke_id ) & & ( pc - > prot . dss1 . invoke_id = = id ) )
{ /* Diversion successful */
free_invoke_id ( st , pc - > prot . dss1 . invoke_id ) ;
pc - > prot . dss1 . remote_result = 0 ; /* success */
pc - > prot . dss1 . invoke_id = 0 ;
pc - > redir_result = pc - > prot . dss1 . remote_result ;
st - > l3 . l3l4 ( st , CC_REDIR | INDICATION , pc ) ; } /* Diversion successful */
else
l3_debug ( st , " return error unknown identifier " ) ;
break ;
case 3 : /* return error */
err_ret = 0 ;
if ( nlen < 2 )
{ l3_debug ( st , " return error nlen < 2 " ) ;
return ;
}
if ( * p ! = 0x02 )
{ /* result tag */
l3_debug ( st , " invoke error tag !=0x02 " ) ;
return ;
}
p + + ;
nlen - - ;
if ( * p > 4 )
{ /* length format */
l3_debug ( st , " invoke return errlen > 4 " ) ;
return ;
}
ilen = * p + + ;
nlen - - ;
if ( ilen > nlen | | ilen = = 0 )
{ l3_debug ( st , " error return ilen > nlen || ilen == 0 " ) ;
return ;
}
nlen - = ilen ;
while ( ilen > 0 )
{ err_ret = ( err_ret < < 8 ) | ( * p + + & 0xFF ) ; /* error value */
ilen - - ;
}
/* if no process available handle separately */
if ( ! pc )
{ if ( cr = = - 1 )
l3dss1_dummy_error_return ( st , id , err_ret ) ;
return ;
}
if ( ( pc - > prot . dss1 . invoke_id ) & & ( pc - > prot . dss1 . invoke_id = = id ) )
{ /* Deflection error */
free_invoke_id ( st , pc - > prot . dss1 . invoke_id ) ;
pc - > prot . dss1 . remote_result = err_ret ; /* result */
pc - > prot . dss1 . invoke_id = 0 ;
pc - > redir_result = pc - > prot . dss1 . remote_result ;
st - > l3 . l3l4 ( st , CC_REDIR | INDICATION , pc ) ;
} /* Deflection error */
else
l3_debug ( st , " return result unknown identifier " ) ;
break ;
default :
l3_debug ( st , " facility default break tag=0x%02x " , cp_tag ) ;
break ;
}
}
static void
l3dss1_message ( struct l3_process * pc , u_char mt )
{
struct sk_buff * skb ;
u_char * p ;
if ( ! ( skb = l3_alloc_skb ( 4 ) ) )
return ;
p = skb_put ( skb , 4 ) ;
MsgHead ( p , pc - > callref , mt ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
}
static void
l3dss1_message_cause ( struct l3_process * pc , u_char mt , u_char cause )
{
struct sk_buff * skb ;
u_char tmp [ 16 ] ;
u_char * p = tmp ;
int l ;
MsgHead ( p , pc - > callref , mt ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = cause | 0x80 ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
}
static void
l3dss1_status_send ( struct l3_process * pc , u_char pr , void * arg )
{
u_char tmp [ 16 ] ;
u_char * p = tmp ;
int l ;
struct sk_buff * skb ;
MsgHead ( p , pc - > callref , MT_STATUS ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = pc - > para . cause | 0x80 ;
* p + + = IE_CALL_STATE ;
* p + + = 0x1 ;
* p + + = pc - > state & 0x3f ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
}
static void
l3dss1_msg_without_setup ( struct l3_process * pc , u_char pr , void * arg )
{
/* This routine is called if here was no SETUP made (checks in dss1up and in
* l3dss1_setup ) and a RELEASE_COMPLETE have to be sent with an error code
* MT_STATUS_ENQUIRE in the NULL state is handled too
*/
u_char tmp [ 16 ] ;
u_char * p = tmp ;
int l ;
struct sk_buff * skb ;
switch ( pc - > para . cause ) {
case 81 : /* invalid callreference */
case 88 : /* incomp destination */
case 96 : /* mandory IE missing */
case 100 : /* invalid IE contents */
case 101 : /* incompatible Callstate */
MsgHead ( p , pc - > callref , MT_RELEASE_COMPLETE ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = pc - > para . cause | 0x80 ;
break ;
default :
printk ( KERN_ERR " HiSax l3dss1_msg_without_setup wrong cause %d \n " ,
pc - > para . cause ) ;
return ;
}
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
dss1_release_l3_process ( pc ) ;
}
static int ie_ALERTING [ ] = { IE_BEARER , IE_CHANNEL_ID | IE_MANDATORY_1 ,
IE_FACILITY , IE_PROGRESS , IE_DISPLAY , IE_SIGNAL , IE_HLC ,
IE_USER_USER , - 1 } ;
static int ie_CALL_PROCEEDING [ ] = { IE_BEARER , IE_CHANNEL_ID | IE_MANDATORY_1 ,
IE_FACILITY , IE_PROGRESS , IE_DISPLAY , IE_HLC , - 1 } ;
static int ie_CONNECT [ ] = { IE_BEARER , IE_CHANNEL_ID | IE_MANDATORY_1 ,
IE_FACILITY , IE_PROGRESS , IE_DISPLAY , IE_DATE , IE_SIGNAL ,
IE_CONNECT_PN , IE_CONNECT_SUB , IE_LLC , IE_HLC , IE_USER_USER , - 1 } ;
static int ie_CONNECT_ACKNOWLEDGE [ ] = { IE_CHANNEL_ID , IE_DISPLAY , IE_SIGNAL , - 1 } ;
static int ie_DISCONNECT [ ] = { IE_CAUSE | IE_MANDATORY , IE_FACILITY ,
IE_PROGRESS , IE_DISPLAY , IE_SIGNAL , IE_USER_USER , - 1 } ;
static int ie_INFORMATION [ ] = { IE_COMPLETE , IE_DISPLAY , IE_KEYPAD , IE_SIGNAL ,
IE_CALLED_PN , - 1 } ;
static int ie_NOTIFY [ ] = { IE_BEARER , IE_NOTIFY | IE_MANDATORY , IE_DISPLAY , - 1 } ;
static int ie_PROGRESS [ ] = { IE_BEARER , IE_CAUSE , IE_FACILITY , IE_PROGRESS |
IE_MANDATORY , IE_DISPLAY , IE_HLC , IE_USER_USER , - 1 } ;
static int ie_RELEASE [ ] = { IE_CAUSE | IE_MANDATORY_1 , IE_FACILITY , IE_DISPLAY ,
IE_SIGNAL , IE_USER_USER , - 1 } ;
/* a RELEASE_COMPLETE with errors don't require special actions
static int ie_RELEASE_COMPLETE [ ] = { IE_CAUSE | IE_MANDATORY_1 , IE_DISPLAY , IE_SIGNAL , IE_USER_USER , - 1 } ;
*/
static int ie_RESUME_ACKNOWLEDGE [ ] = { IE_CHANNEL_ID | IE_MANDATORY , IE_FACILITY ,
IE_DISPLAY , - 1 } ;
static int ie_RESUME_REJECT [ ] = { IE_CAUSE | IE_MANDATORY , IE_DISPLAY , - 1 } ;
static int ie_SETUP [ ] = { IE_COMPLETE , IE_BEARER | IE_MANDATORY ,
IE_CHANNEL_ID | IE_MANDATORY , IE_FACILITY , IE_PROGRESS ,
IE_NET_FAC , IE_DISPLAY , IE_KEYPAD , IE_SIGNAL , IE_CALLING_PN ,
IE_CALLING_SUB , IE_CALLED_PN , IE_CALLED_SUB , IE_REDIR_NR ,
IE_LLC , IE_HLC , IE_USER_USER , - 1 } ;
static int ie_SETUP_ACKNOWLEDGE [ ] = { IE_CHANNEL_ID | IE_MANDATORY , IE_FACILITY ,
IE_PROGRESS , IE_DISPLAY , IE_SIGNAL , - 1 } ;
static int ie_STATUS [ ] = { IE_CAUSE | IE_MANDATORY , IE_CALL_STATE |
IE_MANDATORY , IE_DISPLAY , - 1 } ;
static int ie_STATUS_ENQUIRY [ ] = { IE_DISPLAY , - 1 } ;
static int ie_SUSPEND_ACKNOWLEDGE [ ] = { IE_DISPLAY , IE_FACILITY , - 1 } ;
static int ie_SUSPEND_REJECT [ ] = { IE_CAUSE | IE_MANDATORY , IE_DISPLAY , - 1 } ;
/* not used
* static int ie_CONGESTION_CONTROL [ ] = { IE_CONGESTION | IE_MANDATORY ,
* IE_CAUSE | IE_MANDATORY , IE_DISPLAY , - 1 } ;
* static int ie_USER_INFORMATION [ ] = { IE_MORE_DATA , IE_USER_USER | IE_MANDATORY , - 1 } ;
* static int ie_RESTART [ ] = { IE_CHANNEL_ID , IE_DISPLAY , IE_RESTART_IND |
* IE_MANDATORY , - 1 } ;
*/
static int ie_FACILITY [ ] = { IE_FACILITY | IE_MANDATORY , IE_DISPLAY , - 1 } ;
static int comp_required [ ] = { 1 , 2 , 3 , 5 , 6 , 7 , 9 , 10 , 11 , 14 , 15 , - 1 } ;
static int l3_valid_states [ ] = { 0 , 1 , 2 , 3 , 4 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 15 , 17 , 19 , 25 , - 1 } ;
struct ie_len {
int ie ;
int len ;
} ;
static
struct ie_len max_ie_len [ ] = {
{ IE_SEGMENT , 4 } ,
{ IE_BEARER , 12 } ,
{ IE_CAUSE , 32 } ,
{ IE_CALL_ID , 10 } ,
{ IE_CALL_STATE , 3 } ,
{ IE_CHANNEL_ID , 34 } ,
{ IE_FACILITY , 255 } ,
{ IE_PROGRESS , 4 } ,
{ IE_NET_FAC , 255 } ,
{ IE_NOTIFY , 3 } ,
{ IE_DISPLAY , 82 } ,
{ IE_DATE , 8 } ,
{ IE_KEYPAD , 34 } ,
{ IE_SIGNAL , 3 } ,
{ IE_INFORATE , 6 } ,
{ IE_E2E_TDELAY , 11 } ,
{ IE_TDELAY_SEL , 5 } ,
{ IE_PACK_BINPARA , 3 } ,
{ IE_PACK_WINSIZE , 4 } ,
{ IE_PACK_SIZE , 4 } ,
{ IE_CUG , 7 } ,
{ IE_REV_CHARGE , 3 } ,
{ IE_CALLING_PN , 24 } ,
{ IE_CALLING_SUB , 23 } ,
{ IE_CALLED_PN , 24 } ,
{ IE_CALLED_SUB , 23 } ,
{ IE_REDIR_NR , 255 } ,
{ IE_TRANS_SEL , 255 } ,
{ IE_RESTART_IND , 3 } ,
{ IE_LLC , 18 } ,
{ IE_HLC , 5 } ,
{ IE_USER_USER , 131 } ,
{ - 1 , 0 } ,
} ;
static int
getmax_ie_len ( u_char ie ) {
int i = 0 ;
while ( max_ie_len [ i ] . ie ! = - 1 ) {
if ( max_ie_len [ i ] . ie = = ie )
return ( max_ie_len [ i ] . len ) ;
i + + ;
}
return ( 255 ) ;
}
static int
ie_in_set ( struct l3_process * pc , u_char ie , int * checklist ) {
int ret = 1 ;
while ( * checklist ! = - 1 ) {
if ( ( * checklist & 0xff ) = = ie ) {
if ( ie & 0x80 )
return ( - ret ) ;
else
return ( ret ) ;
}
ret + + ;
checklist + + ;
}
return ( 0 ) ;
}
static int
check_infoelements ( struct l3_process * pc , struct sk_buff * skb , int * checklist )
{
int * cl = checklist ;
u_char mt ;
u_char * p , ie ;
int l , newpos , oldpos ;
int err_seq = 0 , err_len = 0 , err_compr = 0 , err_ureg = 0 ;
u_char codeset = 0 ;
u_char old_codeset = 0 ;
u_char codelock = 1 ;
p = skb - > data ;
/* skip cr */
p + + ;
l = ( * p + + ) & 0xf ;
p + = l ;
mt = * p + + ;
oldpos = 0 ;
while ( ( p - skb - > data ) < skb - > len ) {
if ( ( * p & 0xf0 ) = = 0x90 ) { /* shift codeset */
old_codeset = codeset ;
codeset = * p & 7 ;
if ( * p & 0x08 )
codelock = 0 ;
else
codelock = 1 ;
if ( pc - > debug & L3_DEB_CHECK )
l3_debug ( pc - > st , " check IE shift%scodeset %d->%d " ,
codelock ? " locking " : " " , old_codeset , codeset ) ;
p + + ;
continue ;
}
if ( ! codeset ) { /* only codeset 0 */
if ( ( newpos = ie_in_set ( pc , * p , cl ) ) ) {
if ( newpos > 0 ) {
if ( newpos < oldpos )
err_seq + + ;
else
oldpos = newpos ;
}
} else {
if ( ie_in_set ( pc , * p , comp_required ) )
err_compr + + ;
else
err_ureg + + ;
}
}
ie = * p + + ;
if ( ie & 0x80 ) {
l = 1 ;
} else {
l = * p + + ;
p + = l ;
l + = 2 ;
}
if ( ! codeset & & ( l > getmax_ie_len ( ie ) ) )
err_len + + ;
if ( ! codelock ) {
if ( pc - > debug & L3_DEB_CHECK )
l3_debug ( pc - > st , " check IE shift back codeset %d->%d " ,
codeset , old_codeset ) ;
codeset = old_codeset ;
codelock = 1 ;
}
}
if ( err_compr | err_ureg | err_len | err_seq ) {
if ( pc - > debug & L3_DEB_CHECK )
l3_debug ( pc - > st , " check IE MT(%x) %d/%d/%d/%d " ,
mt , err_compr , err_ureg , err_len , err_seq ) ;
if ( err_compr )
return ( ERR_IE_COMPREHENSION ) ;
if ( err_ureg )
return ( ERR_IE_UNRECOGNIZED ) ;
if ( err_len )
return ( ERR_IE_LENGTH ) ;
if ( err_seq )
return ( ERR_IE_SEQUENCE ) ;
}
return ( 0 ) ;
}
/* verify if a message type exists and contain no IE error */
static int
l3dss1_check_messagetype_validity ( struct l3_process * pc , int mt , void * arg )
{
switch ( mt ) {
case MT_ALERTING :
case MT_CALL_PROCEEDING :
case MT_CONNECT :
case MT_CONNECT_ACKNOWLEDGE :
case MT_DISCONNECT :
case MT_INFORMATION :
case MT_FACILITY :
case MT_NOTIFY :
case MT_PROGRESS :
case MT_RELEASE :
case MT_RELEASE_COMPLETE :
case MT_SETUP :
case MT_SETUP_ACKNOWLEDGE :
case MT_RESUME_ACKNOWLEDGE :
case MT_RESUME_REJECT :
case MT_SUSPEND_ACKNOWLEDGE :
case MT_SUSPEND_REJECT :
case MT_USER_INFORMATION :
case MT_RESTART :
case MT_RESTART_ACKNOWLEDGE :
case MT_CONGESTION_CONTROL :
case MT_STATUS :
case MT_STATUS_ENQUIRY :
if ( pc - > debug & L3_DEB_CHECK )
l3_debug ( pc - > st , " l3dss1_check_messagetype_validity mt(%x) OK " , mt ) ;
break ;
case MT_RESUME : /* RESUME only in user->net */
case MT_SUSPEND : /* SUSPEND only in user->net */
default :
if ( pc - > debug & ( L3_DEB_CHECK | L3_DEB_WARN ) )
l3_debug ( pc - > st , " l3dss1_check_messagetype_validity mt(%x) fail " , mt ) ;
pc - > para . cause = 97 ;
l3dss1_status_send ( pc , 0 , NULL ) ;
return ( 1 ) ;
}
return ( 0 ) ;
}
static void
l3dss1_std_ie_err ( struct l3_process * pc , int ret ) {
if ( pc - > debug & L3_DEB_CHECK )
l3_debug ( pc - > st , " check_infoelements ret %d " , ret ) ;
switch ( ret ) {
case 0 :
break ;
case ERR_IE_COMPREHENSION :
pc - > para . cause = 96 ;
l3dss1_status_send ( pc , 0 , NULL ) ;
break ;
case ERR_IE_UNRECOGNIZED :
pc - > para . cause = 99 ;
l3dss1_status_send ( pc , 0 , NULL ) ;
break ;
case ERR_IE_LENGTH :
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , 0 , NULL ) ;
break ;
case ERR_IE_SEQUENCE :
default :
break ;
}
}
static int
l3dss1_get_channel_id ( struct l3_process * pc , struct sk_buff * skb ) {
u_char * p ;
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , IE_CHANNEL_ID , 0 ) ) ) {
p + + ;
if ( * p ! = 1 ) { /* len for BRI = 1 */
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " wrong chid len %d " , * p ) ;
return ( - 2 ) ;
}
p + + ;
if ( * p & 0x60 ) { /* only base rate interface */
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " wrong chid %x " , * p ) ;
return ( - 3 ) ;
}
return ( * p & 0x3 ) ;
} else
return ( - 1 ) ;
}
static int
l3dss1_get_cause ( struct l3_process * pc , struct sk_buff * skb ) {
u_char l , i = 0 ;
u_char * p ;
p = skb - > data ;
pc - > para . cause = 31 ;
pc - > para . loc = 0 ;
if ( ( p = findie ( p , skb - > len , IE_CAUSE , 0 ) ) ) {
p + + ;
l = * p + + ;
if ( l > 30 )
return ( 1 ) ;
if ( l ) {
pc - > para . loc = * p + + ;
l - - ;
} else {
return ( 2 ) ;
}
if ( l & & ! ( pc - > para . loc & 0x80 ) ) {
l - - ;
p + + ; /* skip recommendation */
}
if ( l ) {
pc - > para . cause = * p + + ;
l - - ;
if ( ! ( pc - > para . cause & 0x80 ) )
return ( 3 ) ;
} else
return ( 4 ) ;
while ( l & & ( i < 6 ) ) {
pc - > para . diag [ i + + ] = * p + + ;
l - - ;
}
} else
return ( - 1 ) ;
return ( 0 ) ;
}
static void
l3dss1_msg_with_uus ( struct l3_process * pc , u_char cmd )
{
struct sk_buff * skb ;
u_char tmp [ 16 + 40 ] ;
u_char * p = tmp ;
int l ;
MsgHead ( p , pc - > callref , cmd ) ;
if ( pc - > prot . dss1 . uus1_data [ 0 ] )
{ * p + + = IE_USER_USER ; /* UUS info element */
* p + + = strlen ( pc - > prot . dss1 . uus1_data ) + 1 ;
* p + + = 0x04 ; /* IA5 chars */
strcpy ( p , pc - > prot . dss1 . uus1_data ) ;
p + = strlen ( pc - > prot . dss1 . uus1_data ) ;
pc - > prot . dss1 . uus1_data [ 0 ] = ' \0 ' ;
}
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
} /* l3dss1_msg_with_uus */
static void
l3dss1_release_req ( struct l3_process * pc , u_char pr , void * arg )
{
StopAllL3Timer ( pc ) ;
newl3state ( pc , 19 ) ;
if ( ! pc - > prot . dss1 . uus1_data [ 0 ] )
l3dss1_message ( pc , MT_RELEASE ) ;
else
l3dss1_msg_with_uus ( pc , MT_RELEASE ) ;
L3AddTimer ( & pc - > timer , T308 , CC_T308_1 ) ;
}
static void
l3dss1_release_cmpl ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
if ( ( ret = l3dss1_get_cause ( pc , skb ) ) > 0 ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " RELCMPL get_cause ret(%d) " , ret ) ;
} else if ( ret < 0 )
pc - > para . cause = NO_CAUSE ;
StopAllL3Timer ( pc ) ;
newl3state ( pc , 0 ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | CONFIRM , pc ) ;
dss1_release_l3_process ( pc ) ;
}
2005-07-27 22:45:17 +04:00
# ifdef EXT_BEARER_CAPS
2005-04-17 02:20:36 +04:00
static u_char *
EncodeASyncParams ( u_char * p , u_char si2 )
{ // 7c 06 88 90 21 42 00 bb
p [ 0 ] = 0 ;
p [ 1 ] = 0x40 ; // Intermediate rate: 16 kbit/s jj 2000.02.19
p [ 2 ] = 0x80 ;
if ( si2 & 32 ) // 7 data bits
p [ 2 ] + = 16 ;
else // 8 data bits
p [ 2 ] + = 24 ;
if ( si2 & 16 ) // 2 stop bits
p [ 2 ] + = 96 ;
else // 1 stop bit
p [ 2 ] + = 32 ;
if ( si2 & 8 ) // even parity
p [ 2 ] + = 2 ;
else // no parity
p [ 2 ] + = 3 ;
switch ( si2 & 0x07 ) {
case 0 :
p [ 0 ] = 66 ; // 1200 bit/s
break ;
case 1 :
p [ 0 ] = 88 ; // 1200/75 bit/s
break ;
case 2 :
p [ 0 ] = 87 ; // 75/1200 bit/s
break ;
case 3 :
p [ 0 ] = 67 ; // 2400 bit/s
break ;
case 4 :
p [ 0 ] = 69 ; // 4800 bit/s
break ;
case 5 :
p [ 0 ] = 72 ; // 9600 bit/s
break ;
case 6 :
p [ 0 ] = 73 ; // 14400 bit/s
break ;
case 7 :
p [ 0 ] = 75 ; // 19200 bit/s
break ;
}
return p + 3 ;
}
static u_char
EncodeSyncParams ( u_char si2 , u_char ai )
{
switch ( si2 ) {
case 0 :
return ai + 2 ; // 1200 bit/s
case 1 :
return ai + 24 ; // 1200/75 bit/s
case 2 :
return ai + 23 ; // 75/1200 bit/s
case 3 :
return ai + 3 ; // 2400 bit/s
case 4 :
return ai + 5 ; // 4800 bit/s
case 5 :
return ai + 8 ; // 9600 bit/s
case 6 :
return ai + 9 ; // 14400 bit/s
case 7 :
return ai + 11 ; // 19200 bit/s
case 8 :
return ai + 14 ; // 48000 bit/s
case 9 :
return ai + 15 ; // 56000 bit/s
case 15 :
return ai + 40 ; // negotiate bit/s
default :
break ;
}
return ai ;
}
static u_char
DecodeASyncParams ( u_char si2 , u_char * p )
{
u_char info ;
switch ( p [ 5 ] ) {
case 66 : // 1200 bit/s
break ; // si2 don't change
case 88 : // 1200/75 bit/s
si2 + = 1 ;
break ;
case 87 : // 75/1200 bit/s
si2 + = 2 ;
break ;
case 67 : // 2400 bit/s
si2 + = 3 ;
break ;
case 69 : // 4800 bit/s
si2 + = 4 ;
break ;
case 72 : // 9600 bit/s
si2 + = 5 ;
break ;
case 73 : // 14400 bit/s
si2 + = 6 ;
break ;
case 75 : // 19200 bit/s
si2 + = 7 ;
break ;
}
info = p [ 7 ] & 0x7f ;
if ( ( info & 16 ) & & ( ! ( info & 8 ) ) ) // 7 data bits
si2 + = 32 ; // else 8 data bits
if ( ( info & 96 ) = = 96 ) // 2 stop bits
si2 + = 16 ; // else 1 stop bit
if ( ( info & 2 ) & & ( ! ( info & 1 ) ) ) // even parity
si2 + = 8 ; // else no parity
return si2 ;
}
static u_char
DecodeSyncParams ( u_char si2 , u_char info )
{
info & = 0x7f ;
switch ( info ) {
case 40 : // bit/s negotiation failed ai := 165 not 175!
return si2 + 15 ;
case 15 : // 56000 bit/s failed, ai := 0 not 169 !
return si2 + 9 ;
case 14 : // 48000 bit/s
return si2 + 8 ;
case 11 : // 19200 bit/s
return si2 + 7 ;
case 9 : // 14400 bit/s
return si2 + 6 ;
case 8 : // 9600 bit/s
return si2 + 5 ;
case 5 : // 4800 bit/s
return si2 + 4 ;
case 3 : // 2400 bit/s
return si2 + 3 ;
case 23 : // 75/1200 bit/s
return si2 + 2 ;
case 24 : // 1200/75 bit/s
return si2 + 1 ;
default : // 1200 bit/s
return si2 ;
}
}
static u_char
DecodeSI2 ( struct sk_buff * skb )
{
u_char * p ; //, *pend=skb->data + skb->len;
if ( ( p = findie ( skb - > data , skb - > len , 0x7c , 0 ) ) ) {
switch ( p [ 4 ] & 0x0f ) {
case 0x01 :
if ( p [ 1 ] = = 0x04 ) // sync. Bitratenadaption
return DecodeSyncParams ( 160 , p [ 5 ] ) ; // V.110/X.30
else if ( p [ 1 ] = = 0x06 ) // async. Bitratenadaption
return DecodeASyncParams ( 192 , p ) ; // V.110/X.30
break ;
case 0x08 : // if (p[5] == 0x02) // sync. Bitratenadaption
if ( p [ 1 ] > 3 )
return DecodeSyncParams ( 176 , p [ 5 ] ) ; // V.120
break ;
}
}
return 0 ;
}
# endif
static void
l3dss1_setup_req ( struct l3_process * pc , u_char pr ,
void * arg )
{
struct sk_buff * skb ;
u_char tmp [ 128 ] ;
u_char * p = tmp ;
u_char channel = 0 ;
u_char send_keypad ;
u_char screen = 0x80 ;
u_char * teln ;
u_char * msn ;
u_char * sub ;
u_char * sp ;
int l ;
MsgHead ( p , pc - > callref , MT_SETUP ) ;
teln = pc - > para . setup . phone ;
# ifndef CONFIG_HISAX_NO_KEYPAD
send_keypad = ( strchr ( teln , ' * ' ) | | strchr ( teln , ' # ' ) ) ? 1 : 0 ;
# else
send_keypad = 0 ;
# endif
# ifndef CONFIG_HISAX_NO_SENDCOMPLETE
if ( ! send_keypad )
* p + + = 0xa1 ; /* complete indicator */
# endif
/*
* Set Bearer Capability , Map info from 1 TR6 - convention to EDSS1
*/
switch ( pc - > para . setup . si1 ) {
case 1 : /* Telephony */
* p + + = IE_BEARER ;
* p + + = 0x3 ; /* Length */
* p + + = 0x90 ; /* Coding Std. CCITT, 3.1 kHz audio */
* p + + = 0x90 ; /* Circuit-Mode 64kbps */
* p + + = 0xa3 ; /* A-Law Audio */
break ;
case 5 : /* Datatransmission 64k, BTX */
case 7 : /* Datatransmission 64k */
default :
* p + + = IE_BEARER ;
* p + + = 0x2 ; /* Length */
* p + + = 0x88 ; /* Coding Std. CCITT, unrestr. dig. Inform. */
* p + + = 0x90 ; /* Circuit-Mode 64kbps */
break ;
}
if ( send_keypad ) {
* p + + = IE_KEYPAD ;
* p + + = strlen ( teln ) ;
while ( * teln )
* p + + = ( * teln + + ) & 0x7F ;
}
/*
* What about info2 ? Mapping to High - Layer - Compatibility ?
*/
if ( ( * teln ) & & ( ! send_keypad ) ) {
/* parse number for special things */
if ( ! isdigit ( * teln ) ) {
switch ( 0x5f & * teln ) {
case ' C ' :
channel = 0x08 ;
case ' P ' :
channel | = 0x80 ;
teln + + ;
if ( * teln = = ' 1 ' )
channel | = 0x01 ;
else
channel | = 0x02 ;
break ;
case ' R ' :
screen = 0xA0 ;
break ;
case ' D ' :
screen = 0x80 ;
break ;
default :
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " Wrong MSN Code " ) ;
break ;
}
teln + + ;
}
}
if ( channel ) {
* p + + = IE_CHANNEL_ID ;
* p + + = 1 ;
* p + + = channel ;
}
msn = pc - > para . setup . eazmsn ;
sub = NULL ;
sp = msn ;
while ( * sp ) {
if ( ' . ' = = * sp ) {
sub = sp ;
* sp = 0 ;
} else
sp + + ;
}
if ( * msn ) {
* p + + = IE_CALLING_PN ;
* p + + = strlen ( msn ) + ( screen ? 2 : 1 ) ;
/* Classify as AnyPref. */
if ( screen ) {
* p + + = 0x01 ; /* Ext = '0'B, Type = '000'B, Plan = '0001'B. */
* p + + = screen ;
} else
* p + + = 0x81 ; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
while ( * msn )
* p + + = * msn + + & 0x7f ;
}
if ( sub ) {
* sub + + = ' . ' ;
* p + + = IE_CALLING_SUB ;
* p + + = strlen ( sub ) + 2 ;
* p + + = 0x80 ; /* NSAP coded */
* p + + = 0x50 ; /* local IDI format */
while ( * sub )
* p + + = * sub + + & 0x7f ;
}
sub = NULL ;
sp = teln ;
while ( * sp ) {
if ( ' . ' = = * sp ) {
sub = sp ;
* sp = 0 ;
} else
sp + + ;
}
if ( ! send_keypad ) {
* p + + = IE_CALLED_PN ;
* p + + = strlen ( teln ) + 1 ;
/* Classify as AnyPref. */
* p + + = 0x81 ; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
while ( * teln )
* p + + = * teln + + & 0x7f ;
if ( sub ) {
* sub + + = ' . ' ;
* p + + = IE_CALLED_SUB ;
* p + + = strlen ( sub ) + 2 ;
* p + + = 0x80 ; /* NSAP coded */
* p + + = 0x50 ; /* local IDI format */
while ( * sub )
* p + + = * sub + + & 0x7f ;
}
}
2005-07-27 22:45:17 +04:00
# ifdef EXT_BEARER_CAPS
2005-04-17 02:20:36 +04:00
if ( ( pc - > para . setup . si2 > = 160 ) & & ( pc - > para . setup . si2 < = 175 ) ) { // sync. Bitratenadaption, V.110/X.30
* p + + = IE_LLC ;
* p + + = 0x04 ;
* p + + = 0x88 ;
* p + + = 0x90 ;
* p + + = 0x21 ;
* p + + = EncodeSyncParams ( pc - > para . setup . si2 - 160 , 0x80 ) ;
} else if ( ( pc - > para . setup . si2 > = 176 ) & & ( pc - > para . setup . si2 < = 191 ) ) { // sync. Bitratenadaption, V.120
* p + + = IE_LLC ;
* p + + = 0x05 ;
* p + + = 0x88 ;
* p + + = 0x90 ;
* p + + = 0x28 ;
* p + + = EncodeSyncParams ( pc - > para . setup . si2 - 176 , 0 ) ;
* p + + = 0x82 ;
} else if ( pc - > para . setup . si2 > = 192 ) { // async. Bitratenadaption, V.110/X.30
* p + + = IE_LLC ;
* p + + = 0x06 ;
* p + + = 0x88 ;
* p + + = 0x90 ;
* p + + = 0x21 ;
p = EncodeASyncParams ( p , pc - > para . setup . si2 - 192 ) ;
# ifndef CONFIG_HISAX_NO_LLC
} else {
switch ( pc - > para . setup . si1 ) {
case 1 : /* Telephony */
* p + + = IE_LLC ;
* p + + = 0x3 ; /* Length */
* p + + = 0x90 ; /* Coding Std. CCITT, 3.1 kHz audio */
* p + + = 0x90 ; /* Circuit-Mode 64kbps */
* p + + = 0xa3 ; /* A-Law Audio */
break ;
case 5 : /* Datatransmission 64k, BTX */
case 7 : /* Datatransmission 64k */
default :
* p + + = IE_LLC ;
* p + + = 0x2 ; /* Length */
* p + + = 0x88 ; /* Coding Std. CCITT, unrestr. dig. Inform. */
* p + + = 0x90 ; /* Circuit-Mode 64kbps */
break ;
}
# endif
}
# endif
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
L3DelTimer ( & pc - > timer ) ;
L3AddTimer ( & pc - > timer , T303 , CC_T303 ) ;
newl3state ( pc , 1 ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
}
static void
l3dss1_call_proc ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int id , ret ;
if ( ( id = l3dss1_get_channel_id ( pc , skb ) ) > = 0 ) {
if ( ( 0 = = id ) | | ( ( 3 = = id ) & & ( 0x10 = = pc - > para . moderate ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup answer with wrong chid %x " , id ) ;
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
pc - > para . bchannel = id ;
} else if ( 1 = = pc - > state ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup answer wrong chid (ret %d) " , id ) ;
if ( id = = - 1 )
pc - > para . cause = 96 ;
else
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
/* Now we are on none mandatory IEs */
ret = check_infoelements ( pc , skb , ie_CALL_PROCEEDING ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ;
newl3state ( pc , 3 ) ;
L3AddTimer ( & pc - > timer , T310 , CC_T310 ) ;
if ( ret ) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err ( pc , ret ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_PROCEEDING | INDICATION , pc ) ;
}
static void
l3dss1_setup_ack ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int id , ret ;
if ( ( id = l3dss1_get_channel_id ( pc , skb ) ) > = 0 ) {
if ( ( 0 = = id ) | | ( ( 3 = = id ) & & ( 0x10 = = pc - > para . moderate ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup answer with wrong chid %x " , id ) ;
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
pc - > para . bchannel = id ;
} else {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup answer wrong chid (ret %d) " , id ) ;
if ( id = = - 1 )
pc - > para . cause = 96 ;
else
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
/* Now we are on none mandatory IEs */
ret = check_infoelements ( pc , skb , ie_SETUP_ACKNOWLEDGE ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ;
newl3state ( pc , 2 ) ;
L3AddTimer ( & pc - > timer , T304 , CC_T304 ) ;
if ( ret ) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err ( pc , ret ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_MORE_INFO | INDICATION , pc ) ;
}
static void
l3dss1_disconnect ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
u_char * p ;
int ret ;
u_char cause = 0 ;
StopAllL3Timer ( pc ) ;
if ( ( ret = l3dss1_get_cause ( pc , skb ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " DISC get_cause ret(%d) " , ret ) ;
if ( ret < 0 )
cause = 96 ;
else if ( ret > 0 )
cause = 100 ;
}
if ( ( p = findie ( skb - > data , skb - > len , IE_FACILITY , 0 ) ) )
l3dss1_parse_facility ( pc - > st , pc , pc - > callref , p ) ;
ret = check_infoelements ( pc , skb , ie_DISCONNECT ) ;
if ( ERR_IE_COMPREHENSION = = ret )
cause = 96 ;
else if ( ( ! cause ) & & ( ERR_IE_UNRECOGNIZED = = ret ) )
cause = 99 ;
ret = pc - > state ;
newl3state ( pc , 12 ) ;
if ( cause )
newl3state ( pc , 19 ) ;
if ( 11 ! = ret )
pc - > st - > l3 . l3l4 ( pc - > st , CC_DISCONNECT | INDICATION , pc ) ;
else if ( ! cause )
l3dss1_release_req ( pc , pr , NULL ) ;
if ( cause ) {
l3dss1_message_cause ( pc , MT_RELEASE , cause ) ;
L3AddTimer ( & pc - > timer , T308 , CC_T308_1 ) ;
}
}
static void
l3dss1_connect ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
ret = check_infoelements ( pc , skb , ie_CONNECT ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ; /* T310 */
newl3state ( pc , 10 ) ;
pc - > para . chargeinfo = 0 ;
/* here should inserted COLP handling KKe */
if ( ret )
l3dss1_std_ie_err ( pc , ret ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP | CONFIRM , pc ) ;
}
static void
l3dss1_alerting ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
ret = check_infoelements ( pc , skb , ie_ALERTING ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ; /* T304 */
newl3state ( pc , 4 ) ;
if ( ret )
l3dss1_std_ie_err ( pc , ret ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_ALERTING | INDICATION , pc ) ;
}
static void
l3dss1_setup ( struct l3_process * pc , u_char pr , void * arg )
{
u_char * p ;
int bcfound = 0 ;
char tmp [ 80 ] ;
struct sk_buff * skb = arg ;
int id ;
int err = 0 ;
/*
* Bearer Capabilities
*/
p = skb - > data ;
/* only the first occurence 'll be detected ! */
if ( ( p = findie ( p , skb - > len , 0x04 , 0 ) ) ) {
if ( ( p [ 1 ] < 2 ) | | ( p [ 1 ] > 11 ) )
err = 1 ;
else {
pc - > para . setup . si2 = 0 ;
switch ( p [ 2 ] & 0x7f ) {
case 0x00 : /* Speech */
case 0x10 : /* 3.1 Khz audio */
pc - > para . setup . si1 = 1 ;
break ;
case 0x08 : /* Unrestricted digital information */
pc - > para . setup . si1 = 7 ;
/* JIM, 05.11.97 I wanna set service indicator 2 */
2005-07-27 22:45:17 +04:00
# ifdef EXT_BEARER_CAPS
2005-04-17 02:20:36 +04:00
pc - > para . setup . si2 = DecodeSI2 ( skb ) ;
# endif
break ;
case 0x09 : /* Restricted digital information */
pc - > para . setup . si1 = 2 ;
break ;
case 0x11 :
/* Unrestr. digital information with
* tones / announcements ( or 7 kHz audio
*/
pc - > para . setup . si1 = 3 ;
break ;
case 0x18 : /* Video */
pc - > para . setup . si1 = 4 ;
break ;
default :
err = 2 ;
break ;
}
switch ( p [ 3 ] & 0x7f ) {
case 0x40 : /* packed mode */
pc - > para . setup . si1 = 8 ;
break ;
case 0x10 : /* 64 kbit */
case 0x11 : /* 2*64 kbit */
case 0x13 : /* 384 kbit */
case 0x15 : /* 1536 kbit */
case 0x17 : /* 1920 kbit */
pc - > para . moderate = p [ 3 ] & 0x7f ;
break ;
default :
err = 3 ;
break ;
}
}
if ( pc - > debug & L3_DEB_SI )
l3_debug ( pc - > st , " SI=%d, AI=%d " ,
pc - > para . setup . si1 , pc - > para . setup . si2 ) ;
if ( err ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup with wrong bearer(l=%d:%x,%x) " ,
p [ 1 ] , p [ 2 ] , p [ 3 ] ) ;
pc - > para . cause = 100 ;
l3dss1_msg_without_setup ( pc , pr , NULL ) ;
return ;
}
} else {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup without bearer capabilities " ) ;
/* ETS 300-104 1.3.3 */
pc - > para . cause = 96 ;
l3dss1_msg_without_setup ( pc , pr , NULL ) ;
return ;
}
/*
* Channel Identification
*/
if ( ( id = l3dss1_get_channel_id ( pc , skb ) ) > = 0 ) {
if ( ( pc - > para . bchannel = id ) ) {
if ( ( 3 = = id ) & & ( 0x10 = = pc - > para . moderate ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup with wrong chid %x " ,
id ) ;
pc - > para . cause = 100 ;
l3dss1_msg_without_setup ( pc , pr , NULL ) ;
return ;
}
bcfound + + ;
} else
{ if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup without bchannel, call waiting " ) ;
bcfound + + ;
}
} else {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " setup with wrong chid ret %d " , id ) ;
if ( id = = - 1 )
pc - > para . cause = 96 ;
else
pc - > para . cause = 100 ;
l3dss1_msg_without_setup ( pc , pr , NULL ) ;
return ;
}
/* Now we are on none mandatory IEs */
err = check_infoelements ( pc , skb , ie_SETUP ) ;
if ( ERR_IE_COMPREHENSION = = err ) {
pc - > para . cause = 96 ;
l3dss1_msg_without_setup ( pc , pr , NULL ) ;
return ;
}
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , 0x70 , 0 ) ) )
iecpy ( pc - > para . setup . eazmsn , p , 1 ) ;
else
pc - > para . setup . eazmsn [ 0 ] = 0 ;
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , 0x71 , 0 ) ) ) {
/* Called party subaddress */
if ( ( p [ 1 ] > = 2 ) & & ( p [ 2 ] = = 0x80 ) & & ( p [ 3 ] = = 0x50 ) ) {
tmp [ 0 ] = ' . ' ;
iecpy ( & tmp [ 1 ] , p , 2 ) ;
strcat ( pc - > para . setup . eazmsn , tmp ) ;
} else if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " wrong called subaddress " ) ;
}
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , 0x6c , 0 ) ) ) {
pc - > para . setup . plan = p [ 2 ] ;
if ( p [ 2 ] & 0x80 ) {
iecpy ( pc - > para . setup . phone , p , 1 ) ;
pc - > para . setup . screen = 0 ;
} else {
iecpy ( pc - > para . setup . phone , p , 2 ) ;
pc - > para . setup . screen = p [ 3 ] ;
}
} else {
pc - > para . setup . phone [ 0 ] = 0 ;
pc - > para . setup . plan = 0 ;
pc - > para . setup . screen = 0 ;
}
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , 0x6d , 0 ) ) ) {
/* Calling party subaddress */
if ( ( p [ 1 ] > = 2 ) & & ( p [ 2 ] = = 0x80 ) & & ( p [ 3 ] = = 0x50 ) ) {
tmp [ 0 ] = ' . ' ;
iecpy ( & tmp [ 1 ] , p , 2 ) ;
strcat ( pc - > para . setup . phone , tmp ) ;
} else if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " wrong calling subaddress " ) ;
}
newl3state ( pc , 6 ) ;
if ( err ) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err ( pc , err ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP | INDICATION , pc ) ;
}
static void
l3dss1_reset ( struct l3_process * pc , u_char pr , void * arg )
{
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_disconnect_req ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb ;
u_char tmp [ 16 + 40 ] ;
u_char * p = tmp ;
int l ;
u_char cause = 16 ;
if ( pc - > para . cause ! = NO_CAUSE )
cause = pc - > para . cause ;
StopAllL3Timer ( pc ) ;
MsgHead ( p , pc - > callref , MT_DISCONNECT ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = cause | 0x80 ;
if ( pc - > prot . dss1 . uus1_data [ 0 ] )
{ * p + + = IE_USER_USER ; /* UUS info element */
* p + + = strlen ( pc - > prot . dss1 . uus1_data ) + 1 ;
* p + + = 0x04 ; /* IA5 chars */
strcpy ( p , pc - > prot . dss1 . uus1_data ) ;
p + = strlen ( pc - > prot . dss1 . uus1_data ) ;
pc - > prot . dss1 . uus1_data [ 0 ] = ' \0 ' ;
}
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
newl3state ( pc , 11 ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
L3AddTimer ( & pc - > timer , T305 , CC_T305 ) ;
}
static void
l3dss1_setup_rsp ( struct l3_process * pc , u_char pr ,
void * arg )
{
if ( ! pc - > para . bchannel )
{ if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " D-chan connect for waiting call " ) ;
l3dss1_disconnect_req ( pc , pr , arg ) ;
return ;
}
newl3state ( pc , 8 ) ;
l3dss1_message ( pc , MT_CONNECT ) ;
L3DelTimer ( & pc - > timer ) ;
L3AddTimer ( & pc - > timer , T313 , CC_T313 ) ;
}
static void
l3dss1_connect_ack ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
ret = check_infoelements ( pc , skb , ie_CONNECT_ACKNOWLEDGE ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
newl3state ( pc , 10 ) ;
L3DelTimer ( & pc - > timer ) ;
if ( ret )
l3dss1_std_ie_err ( pc , ret ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP_COMPL | INDICATION , pc ) ;
}
static void
l3dss1_reject_req ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb ;
u_char tmp [ 16 ] ;
u_char * p = tmp ;
int l ;
u_char cause = 21 ;
if ( pc - > para . cause ! = NO_CAUSE )
cause = pc - > para . cause ;
MsgHead ( p , pc - > callref , MT_RELEASE_COMPLETE ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = cause | 0x80 ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | INDICATION , pc ) ;
newl3state ( pc , 0 ) ;
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_release ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
u_char * p ;
int ret , cause = 0 ;
StopAllL3Timer ( pc ) ;
if ( ( ret = l3dss1_get_cause ( pc , skb ) ) > 0 ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " REL get_cause ret(%d) " , ret ) ;
} else if ( ret < 0 )
pc - > para . cause = NO_CAUSE ;
if ( ( p = findie ( skb - > data , skb - > len , IE_FACILITY , 0 ) ) ) {
l3dss1_parse_facility ( pc - > st , pc , pc - > callref , p ) ;
}
if ( ( ret < 0 ) & & ( pc - > state ! = 11 ) )
cause = 96 ;
else if ( ret > 0 )
cause = 100 ;
ret = check_infoelements ( pc , skb , ie_RELEASE ) ;
if ( ERR_IE_COMPREHENSION = = ret )
cause = 96 ;
else if ( ( ERR_IE_UNRECOGNIZED = = ret ) & & ( ! cause ) )
cause = 99 ;
if ( cause )
l3dss1_message_cause ( pc , MT_RELEASE_COMPLETE , cause ) ;
else
l3dss1_message ( pc , MT_RELEASE_COMPLETE ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | INDICATION , pc ) ;
newl3state ( pc , 0 ) ;
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_alert_req ( struct l3_process * pc , u_char pr ,
void * arg )
{
newl3state ( pc , 7 ) ;
if ( ! pc - > prot . dss1 . uus1_data [ 0 ] )
l3dss1_message ( pc , MT_ALERTING ) ;
else
l3dss1_msg_with_uus ( pc , MT_ALERTING ) ;
}
static void
l3dss1_proceed_req ( struct l3_process * pc , u_char pr ,
void * arg )
{
newl3state ( pc , 9 ) ;
l3dss1_message ( pc , MT_CALL_PROCEEDING ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_PROCEED_SEND | INDICATION , pc ) ;
}
static void
l3dss1_setup_ack_req ( struct l3_process * pc , u_char pr ,
void * arg )
{
newl3state ( pc , 25 ) ;
L3DelTimer ( & pc - > timer ) ;
L3AddTimer ( & pc - > timer , T302 , CC_T302 ) ;
l3dss1_message ( pc , MT_SETUP_ACKNOWLEDGE ) ;
}
/********************************************/
/* deliver a incoming display message to HL */
/********************************************/
static void
l3dss1_deliver_display ( struct l3_process * pc , int pr , u_char * infp )
{ u_char len ;
isdn_ctrl ic ;
struct IsdnCardState * cs ;
char * p ;
if ( * infp + + ! = IE_DISPLAY ) return ;
if ( ( len = * infp + + ) > 80 ) return ; /* total length <= 82 */
if ( ! pc - > chan ) return ;
p = ic . parm . display ;
while ( len - - )
* p + + = * infp + + ;
* p = ' \0 ' ;
ic . command = ISDN_STAT_DISPLAY ;
cs = pc - > st - > l1 . hardware ;
ic . driver = cs - > myid ;
ic . arg = pc - > chan - > chan ;
cs - > iif . statcallb ( & ic ) ;
} /* l3dss1_deliver_display */
static void
l3dss1_progress ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int err = 0 ;
u_char * p ;
if ( ( p = findie ( skb - > data , skb - > len , IE_PROGRESS , 0 ) ) ) {
if ( p [ 1 ] ! = 2 ) {
err = 1 ;
pc - > para . cause = 100 ;
} else if ( ! ( p [ 2 ] & 0x70 ) ) {
switch ( p [ 2 ] ) {
case 0x80 :
case 0x81 :
case 0x82 :
case 0x84 :
case 0x85 :
case 0x87 :
case 0x8a :
switch ( p [ 3 ] ) {
case 0x81 :
case 0x82 :
case 0x83 :
case 0x84 :
case 0x88 :
break ;
default :
err = 2 ;
pc - > para . cause = 100 ;
break ;
}
break ;
default :
err = 3 ;
pc - > para . cause = 100 ;
break ;
}
}
} else {
pc - > para . cause = 96 ;
err = 4 ;
}
if ( err ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " progress error %d " , err ) ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
/* Now we are on none mandatory IEs */
err = check_infoelements ( pc , skb , ie_PROGRESS ) ;
if ( err )
l3dss1_std_ie_err ( pc , err ) ;
if ( ERR_IE_COMPREHENSION ! = err )
pc - > st - > l3 . l3l4 ( pc - > st , CC_PROGRESS | INDICATION , pc ) ;
}
static void
l3dss1_notify ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int err = 0 ;
u_char * p ;
if ( ( p = findie ( skb - > data , skb - > len , IE_NOTIFY , 0 ) ) ) {
if ( p [ 1 ] ! = 1 ) {
err = 1 ;
pc - > para . cause = 100 ;
} else {
switch ( p [ 2 ] ) {
case 0x80 :
case 0x81 :
case 0x82 :
break ;
default :
pc - > para . cause = 100 ;
err = 2 ;
break ;
}
}
} else {
pc - > para . cause = 96 ;
err = 3 ;
}
if ( err ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " notify error %d " , err ) ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
/* Now we are on none mandatory IEs */
err = check_infoelements ( pc , skb , ie_NOTIFY ) ;
if ( err )
l3dss1_std_ie_err ( pc , err ) ;
if ( ERR_IE_COMPREHENSION ! = err )
pc - > st - > l3 . l3l4 ( pc - > st , CC_NOTIFY | INDICATION , pc ) ;
}
static void
l3dss1_status_enq ( struct l3_process * pc , u_char pr , void * arg )
{
int ret ;
struct sk_buff * skb = arg ;
ret = check_infoelements ( pc , skb , ie_STATUS_ENQUIRY ) ;
l3dss1_std_ie_err ( pc , ret ) ;
pc - > para . cause = 30 ; /* response to STATUS_ENQUIRY */
l3dss1_status_send ( pc , pr , NULL ) ;
}
static void
l3dss1_information ( struct l3_process * pc , u_char pr , void * arg )
{
int ret ;
struct sk_buff * skb = arg ;
u_char * p ;
char tmp [ 32 ] ;
ret = check_infoelements ( pc , skb , ie_INFORMATION ) ;
if ( ret )
l3dss1_std_ie_err ( pc , ret ) ;
if ( pc - > state = = 25 ) { /* overlap receiving */
L3DelTimer ( & pc - > timer ) ;
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , 0x70 , 0 ) ) ) {
iecpy ( tmp , p , 1 ) ;
strcat ( pc - > para . setup . eazmsn , tmp ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_MORE_INFO | INDICATION , pc ) ;
}
L3AddTimer ( & pc - > timer , T302 , CC_T302 ) ;
}
}
/******************************/
/* handle deflection requests */
/******************************/
static void l3dss1_redir_req ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb ;
u_char tmp [ 128 ] ;
u_char * p = tmp ;
u_char * subp ;
u_char len_phone = 0 ;
u_char len_sub = 0 ;
int l ;
strcpy ( pc - > prot . dss1 . uus1_data , pc - > chan - > setup . eazmsn ) ; /* copy uus element if available */
if ( ! pc - > chan - > setup . phone [ 0 ] )
{ pc - > para . cause = - 1 ;
l3dss1_disconnect_req ( pc , pr , arg ) ; /* disconnect immediately */
return ;
} /* only uus */
if ( pc - > prot . dss1 . invoke_id )
free_invoke_id ( pc - > st , pc - > prot . dss1 . invoke_id ) ;
if ( ! ( pc - > prot . dss1 . invoke_id = new_invoke_id ( pc - > st ) ) )
return ;
MsgHead ( p , pc - > callref , MT_FACILITY ) ;
for ( subp = pc - > chan - > setup . phone ; ( * subp ) & & ( * subp ! = ' . ' ) ; subp + + ) len_phone + + ; /* len of phone number */
if ( * subp + + = = ' . ' ) len_sub = strlen ( subp ) + 2 ; /* length including info subaddress element */
* p + + = 0x1c ; /* Facility info element */
* p + + = len_phone + len_sub + 2 + 2 + 8 + 3 + 3 ; /* length of element */
* p + + = 0x91 ; /* remote operations protocol */
* p + + = 0xa1 ; /* invoke component */
* p + + = len_phone + len_sub + 2 + 2 + 8 + 3 ; /* length of data */
* p + + = 0x02 ; /* invoke id tag, integer */
* p + + = 0x01 ; /* length */
* p + + = pc - > prot . dss1 . invoke_id ; /* invoke id */
* p + + = 0x02 ; /* operation value tag, integer */
* p + + = 0x01 ; /* length */
* p + + = 0x0D ; /* Call Deflect */
* p + + = 0x30 ; /* sequence phone number */
* p + + = len_phone + 2 + 2 + 3 + len_sub ; /* length */
* p + + = 0x30 ; /* Deflected to UserNumber */
* p + + = len_phone + 2 + len_sub ; /* length */
* p + + = 0x80 ; /* NumberDigits */
* p + + = len_phone ; /* length */
for ( l = 0 ; l < len_phone ; l + + )
* p + + = pc - > chan - > setup . phone [ l ] ;
if ( len_sub )
{ * p + + = 0x04 ; /* called party subaddress */
* p + + = len_sub - 2 ;
while ( * subp ) * p + + = * subp + + ;
}
* p + + = 0x01 ; /* screening identifier */
* p + + = 0x01 ;
* p + + = pc - > chan - > setup . screen ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) ) return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
} /* l3dss1_redir_req */
/********************************************/
/* handle deflection request in early state */
/********************************************/
static void l3dss1_redir_req_early ( struct l3_process * pc , u_char pr , void * arg )
{
l3dss1_proceed_req ( pc , pr , arg ) ;
l3dss1_redir_req ( pc , pr , arg ) ;
} /* l3dss1_redir_req_early */
/***********************************************/
/* handle special commands for this protocol. */
/* Examples are call independant services like */
/* remote operations with dummy callref. */
/***********************************************/
static int l3dss1_cmd_global ( struct PStack * st , isdn_ctrl * ic )
{ u_char id ;
u_char temp [ 265 ] ;
u_char * p = temp ;
int i , l , proc_len ;
struct sk_buff * skb ;
struct l3_process * pc = NULL ;
switch ( ic - > arg )
{ case DSS1_CMD_INVOKE :
if ( ic - > parm . dss1_io . datalen < 0 ) return ( - 2 ) ; /* invalid parameter */
for ( proc_len = 1 , i = ic - > parm . dss1_io . proc > > 8 ; i ; i + + )
i = i > > 8 ; /* add one byte */
l = ic - > parm . dss1_io . datalen + proc_len + 8 ; /* length excluding ie header */
if ( l > 255 )
return ( - 2 ) ; /* too long */
if ( ! ( id = new_invoke_id ( st ) ) )
return ( 0 ) ; /* first get a invoke id -> return if no available */
i = - 1 ;
MsgHead ( p , i , MT_FACILITY ) ; /* build message head */
* p + + = 0x1C ; /* Facility IE */
* p + + = l ; /* length of ie */
* p + + = 0x91 ; /* remote operations */
* p + + = 0xA1 ; /* invoke */
* p + + = l - 3 ; /* length of invoke */
* p + + = 0x02 ; /* invoke id tag */
* p + + = 0x01 ; /* length is 1 */
* p + + = id ; /* invoke id */
* p + + = 0x02 ; /* operation */
* p + + = proc_len ; /* length of operation */
for ( i = proc_len ; i ; i - - )
* p + + = ( ic - > parm . dss1_io . proc > > ( i - 1 ) ) & 0xFF ;
memcpy ( p , ic - > parm . dss1_io . data , ic - > parm . dss1_io . datalen ) ; /* copy data */
l = ( p - temp ) + ic - > parm . dss1_io . datalen ; /* total length */
if ( ic - > parm . dss1_io . timeout > 0 )
if ( ! ( pc = dss1_new_l3_process ( st , - 1 ) ) )
{ free_invoke_id ( st , id ) ;
return ( - 2 ) ;
}
pc - > prot . dss1 . ll_id = ic - > parm . dss1_io . ll_id ; /* remember id */
pc - > prot . dss1 . proc = ic - > parm . dss1_io . proc ; /* and procedure */
if ( ! ( skb = l3_alloc_skb ( l ) ) )
{ free_invoke_id ( st , id ) ;
if ( pc ) dss1_release_l3_process ( pc ) ;
return ( - 2 ) ;
}
memcpy ( skb_put ( skb , l ) , temp , l ) ;
if ( pc )
{ pc - > prot . dss1 . invoke_id = id ; /* remember id */
L3AddTimer ( & pc - > timer , ic - > parm . dss1_io . timeout , CC_TDSS1_IO | REQUEST ) ;
}
l3_msg ( st , DL_DATA | REQUEST , skb ) ;
ic - > parm . dss1_io . hl_id = id ; /* return id */
return ( 0 ) ;
case DSS1_CMD_INVOKE_ABORT :
if ( ( pc = l3dss1_search_dummy_proc ( st , ic - > parm . dss1_io . hl_id ) ) )
{ L3DelTimer ( & pc - > timer ) ; /* remove timer */
dss1_release_l3_process ( pc ) ;
return ( 0 ) ;
}
else
{ l3_debug ( st , " l3dss1_cmd_global abort unknown id " ) ;
return ( - 2 ) ;
}
break ;
default :
l3_debug ( st , " l3dss1_cmd_global unknown cmd 0x%lx " , ic - > arg ) ;
return ( - 1 ) ;
} /* switch ic-> arg */
return ( - 1 ) ;
} /* l3dss1_cmd_global */
static void
l3dss1_io_timer ( struct l3_process * pc )
{ isdn_ctrl ic ;
struct IsdnCardState * cs = pc - > st - > l1 . hardware ;
L3DelTimer ( & pc - > timer ) ; /* remove timer */
ic . driver = cs - > myid ;
ic . command = ISDN_STAT_PROT ;
ic . arg = DSS1_STAT_INVOKE_ERR ;
ic . parm . dss1_io . hl_id = pc - > prot . dss1 . invoke_id ;
ic . parm . dss1_io . ll_id = pc - > prot . dss1 . ll_id ;
ic . parm . dss1_io . proc = pc - > prot . dss1 . proc ;
ic . parm . dss1_io . timeout = - 1 ;
ic . parm . dss1_io . datalen = 0 ;
ic . parm . dss1_io . data = NULL ;
free_invoke_id ( pc - > st , pc - > prot . dss1 . invoke_id ) ;
pc - > prot . dss1 . invoke_id = 0 ; /* reset id */
cs - > iif . statcallb ( & ic ) ;
dss1_release_l3_process ( pc ) ;
} /* l3dss1_io_timer */
static void
l3dss1_release_ind ( struct l3_process * pc , u_char pr , void * arg )
{
u_char * p ;
struct sk_buff * skb = arg ;
int callState = 0 ;
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , IE_CALL_STATE , 0 ) ) ) {
p + + ;
if ( 1 = = * p + + )
callState = * p ;
}
if ( callState = = 0 ) {
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
* set down layer 3 without sending any message
*/
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | INDICATION , pc ) ;
newl3state ( pc , 0 ) ;
dss1_release_l3_process ( pc ) ;
} else {
pc - > st - > l3 . l3l4 ( pc - > st , CC_IGNORE | INDICATION , pc ) ;
}
}
static void
l3dss1_dummy ( struct l3_process * pc , u_char pr , void * arg )
{
}
static void
l3dss1_t302 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . loc = 0 ;
pc - > para . cause = 28 ; /* invalid number */
l3dss1_disconnect_req ( pc , pr , NULL ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP_ERR , pc ) ;
}
static void
l3dss1_t303 ( struct l3_process * pc , u_char pr , void * arg )
{
if ( pc - > N303 > 0 ) {
pc - > N303 - - ;
L3DelTimer ( & pc - > timer ) ;
l3dss1_setup_req ( pc , pr , arg ) ;
} else {
L3DelTimer ( & pc - > timer ) ;
l3dss1_message_cause ( pc , MT_RELEASE_COMPLETE , 102 ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_NOSETUP_RSP , pc ) ;
dss1_release_l3_process ( pc ) ;
}
}
static void
l3dss1_t304 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . loc = 0 ;
pc - > para . cause = 102 ;
l3dss1_disconnect_req ( pc , pr , NULL ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP_ERR , pc ) ;
}
static void
l3dss1_t305 ( struct l3_process * pc , u_char pr , void * arg )
{
u_char tmp [ 16 ] ;
u_char * p = tmp ;
int l ;
struct sk_buff * skb ;
u_char cause = 16 ;
L3DelTimer ( & pc - > timer ) ;
if ( pc - > para . cause ! = NO_CAUSE )
cause = pc - > para . cause ;
MsgHead ( p , pc - > callref , MT_RELEASE ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = cause | 0x80 ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
newl3state ( pc , 19 ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
L3AddTimer ( & pc - > timer , T308 , CC_T308_1 ) ;
}
static void
l3dss1_t310 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . loc = 0 ;
pc - > para . cause = 102 ;
l3dss1_disconnect_req ( pc , pr , NULL ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP_ERR , pc ) ;
}
static void
l3dss1_t313 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . loc = 0 ;
pc - > para . cause = 102 ;
l3dss1_disconnect_req ( pc , pr , NULL ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_CONNECT_ERR , pc ) ;
}
static void
l3dss1_t308_1 ( struct l3_process * pc , u_char pr , void * arg )
{
newl3state ( pc , 19 ) ;
L3DelTimer ( & pc - > timer ) ;
l3dss1_message ( pc , MT_RELEASE ) ;
L3AddTimer ( & pc - > timer , T308 , CC_T308_2 ) ;
}
static void
l3dss1_t308_2 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE_ERR , pc ) ;
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_t318 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . cause = 102 ; /* Timer expiry */
pc - > para . loc = 0 ; /* local */
pc - > st - > l3 . l3l4 ( pc - > st , CC_RESUME_ERR , pc ) ;
newl3state ( pc , 19 ) ;
l3dss1_message ( pc , MT_RELEASE ) ;
L3AddTimer ( & pc - > timer , T308 , CC_T308_1 ) ;
}
static void
l3dss1_t319 ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . cause = 102 ; /* Timer expiry */
pc - > para . loc = 0 ; /* local */
pc - > st - > l3 . l3l4 ( pc - > st , CC_SUSPEND_ERR , pc ) ;
newl3state ( pc , 10 ) ;
}
static void
l3dss1_restart ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | INDICATION , pc ) ;
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_status ( struct l3_process * pc , u_char pr , void * arg )
{
u_char * p ;
struct sk_buff * skb = arg ;
int ret ;
u_char cause = 0 , callState = 0 ;
if ( ( ret = l3dss1_get_cause ( pc , skb ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " STATUS get_cause ret(%d) " , ret ) ;
if ( ret < 0 )
cause = 96 ;
else if ( ret > 0 )
cause = 100 ;
}
if ( ( p = findie ( skb - > data , skb - > len , IE_CALL_STATE , 0 ) ) ) {
p + + ;
if ( 1 = = * p + + ) {
callState = * p ;
if ( ! ie_in_set ( pc , * p , l3_valid_states ) )
cause = 100 ;
} else
cause = 100 ;
} else
cause = 96 ;
if ( ! cause ) { /* no error before */
ret = check_infoelements ( pc , skb , ie_STATUS ) ;
if ( ERR_IE_COMPREHENSION = = ret )
cause = 96 ;
else if ( ERR_IE_UNRECOGNIZED = = ret )
cause = 99 ;
}
if ( cause ) {
u_char tmp ;
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " STATUS error(%d/%d) " , ret , cause ) ;
tmp = pc - > para . cause ;
pc - > para . cause = cause ;
l3dss1_status_send ( pc , 0 , NULL ) ;
if ( cause = = 99 )
pc - > para . cause = tmp ;
else
return ;
}
cause = pc - > para . cause ;
if ( ( ( cause & 0x7f ) = = 111 ) & & ( callState = = 0 ) ) {
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
* if received MT_STATUS with cause = = 111 and call
* state = = 0 , then we must set down layer 3
*/
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | INDICATION , pc ) ;
newl3state ( pc , 0 ) ;
dss1_release_l3_process ( pc ) ;
}
}
static void
l3dss1_facility ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
ret = check_infoelements ( pc , skb , ie_FACILITY ) ;
l3dss1_std_ie_err ( pc , ret ) ;
{
u_char * p ;
if ( ( p = findie ( skb - > data , skb - > len , IE_FACILITY , 0 ) ) )
l3dss1_parse_facility ( pc - > st , pc , pc - > callref , p ) ;
}
}
static void
l3dss1_suspend_req ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb ;
u_char tmp [ 32 ] ;
u_char * p = tmp ;
u_char i , l ;
u_char * msg = pc - > chan - > setup . phone ;
MsgHead ( p , pc - > callref , MT_SUSPEND ) ;
l = * msg + + ;
if ( l & & ( l < = 10 ) ) { /* Max length 10 octets */
* p + + = IE_CALL_ID ;
* p + + = l ;
for ( i = 0 ; i < l ; i + + )
* p + + = * msg + + ;
} else if ( l ) {
l3_debug ( pc - > st , " SUS wrong CALL_ID len %d " , l ) ;
return ;
}
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
newl3state ( pc , 15 ) ;
L3AddTimer ( & pc - > timer , T319 , CC_T319 ) ;
}
static void
l3dss1_suspend_ack ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
L3DelTimer ( & pc - > timer ) ;
newl3state ( pc , 0 ) ;
pc - > para . cause = NO_CAUSE ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SUSPEND | CONFIRM , pc ) ;
/* We don't handle suspend_ack for IE errors now */
if ( ( ret = check_infoelements ( pc , skb , ie_SUSPEND_ACKNOWLEDGE ) ) )
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " SUSPACK check ie(%d) " , ret ) ;
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_suspend_rej ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
if ( ( ret = l3dss1_get_cause ( pc , skb ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " SUSP_REJ get_cause ret(%d) " , ret ) ;
if ( ret < 0 )
pc - > para . cause = 96 ;
else
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
ret = check_infoelements ( pc , skb , ie_SUSPEND_REJECT ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SUSPEND_ERR , pc ) ;
newl3state ( pc , 10 ) ;
if ( ret ) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err ( pc , ret ) ;
}
static void
l3dss1_resume_req ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb ;
u_char tmp [ 32 ] ;
u_char * p = tmp ;
u_char i , l ;
u_char * msg = pc - > para . setup . phone ;
MsgHead ( p , pc - > callref , MT_RESUME ) ;
l = * msg + + ;
if ( l & & ( l < = 10 ) ) { /* Max length 10 octets */
* p + + = IE_CALL_ID ;
* p + + = l ;
for ( i = 0 ; i < l ; i + + )
* p + + = * msg + + ;
} else if ( l ) {
l3_debug ( pc - > st , " RES wrong CALL_ID len %d " , l ) ;
return ;
}
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
newl3state ( pc , 17 ) ;
L3AddTimer ( & pc - > timer , T318 , CC_T318 ) ;
}
static void
l3dss1_resume_ack ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int id , ret ;
if ( ( id = l3dss1_get_channel_id ( pc , skb ) ) > 0 ) {
if ( ( 0 = = id ) | | ( ( 3 = = id ) & & ( 0x10 = = pc - > para . moderate ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " resume ack with wrong chid %x " , id ) ;
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
pc - > para . bchannel = id ;
} else if ( 1 = = pc - > state ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " resume ack without chid (ret %d) " , id ) ;
pc - > para . cause = 96 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
ret = check_infoelements ( pc , skb , ie_RESUME_ACKNOWLEDGE ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RESUME | CONFIRM , pc ) ;
newl3state ( pc , 10 ) ;
if ( ret ) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err ( pc , ret ) ;
}
static void
l3dss1_resume_rej ( struct l3_process * pc , u_char pr , void * arg )
{
struct sk_buff * skb = arg ;
int ret ;
if ( ( ret = l3dss1_get_cause ( pc , skb ) ) ) {
if ( pc - > debug & L3_DEB_WARN )
l3_debug ( pc - > st , " RES_REJ get_cause ret(%d) " , ret ) ;
if ( ret < 0 )
pc - > para . cause = 96 ;
else
pc - > para . cause = 100 ;
l3dss1_status_send ( pc , pr , NULL ) ;
return ;
}
ret = check_infoelements ( pc , skb , ie_RESUME_REJECT ) ;
if ( ERR_IE_COMPREHENSION = = ret ) {
l3dss1_std_ie_err ( pc , ret ) ;
return ;
}
L3DelTimer ( & pc - > timer ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RESUME_ERR , pc ) ;
newl3state ( pc , 0 ) ;
if ( ret ) /* STATUS for none mandatory IE errors after actions are taken */
l3dss1_std_ie_err ( pc , ret ) ;
dss1_release_l3_process ( pc ) ;
}
static void
l3dss1_global_restart ( struct l3_process * pc , u_char pr , void * arg )
{
u_char tmp [ 32 ] ;
u_char * p ;
u_char ri , ch = 0 , chan = 0 ;
int l ;
struct sk_buff * skb = arg ;
struct l3_process * up ;
newl3state ( pc , 2 ) ;
L3DelTimer ( & pc - > timer ) ;
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , IE_RESTART_IND , 0 ) ) ) {
ri = p [ 2 ] ;
l3_debug ( pc - > st , " Restart %x " , ri ) ;
} else {
l3_debug ( pc - > st , " Restart without restart IE " ) ;
ri = 0x86 ;
}
p = skb - > data ;
if ( ( p = findie ( p , skb - > len , IE_CHANNEL_ID , 0 ) ) ) {
chan = p [ 2 ] & 3 ;
ch = p [ 2 ] ;
if ( pc - > st - > l3 . debug )
l3_debug ( pc - > st , " Restart for channel %d " , chan ) ;
}
newl3state ( pc , 2 ) ;
up = pc - > st - > l3 . proc ;
while ( up ) {
if ( ( ri & 7 ) = = 7 )
up - > st - > lli . l4l3 ( up - > st , CC_RESTART | REQUEST , up ) ;
else if ( up - > para . bchannel = = chan )
up - > st - > lli . l4l3 ( up - > st , CC_RESTART | REQUEST , up ) ;
up = up - > next ;
}
p = tmp ;
MsgHead ( p , pc - > callref , MT_RESTART_ACKNOWLEDGE ) ;
if ( chan ) {
* p + + = IE_CHANNEL_ID ;
* p + + = 1 ;
* p + + = ch | 0x80 ;
}
* p + + = 0x79 ; /* RESTART Ind */
* p + + = 1 ;
* p + + = ri ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
newl3state ( pc , 0 ) ;
l3_msg ( pc - > st , DL_DATA | REQUEST , skb ) ;
}
static void
l3dss1_dl_reset ( struct l3_process * pc , u_char pr , void * arg )
{
pc - > para . cause = 0x29 ; /* Temporary failure */
pc - > para . loc = 0 ;
l3dss1_disconnect_req ( pc , pr , NULL ) ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_SETUP_ERR , pc ) ;
}
static void
l3dss1_dl_release ( struct l3_process * pc , u_char pr , void * arg )
{
newl3state ( pc , 0 ) ;
pc - > para . cause = 0x1b ; /* Destination out of order */
pc - > para . loc = 0 ;
pc - > st - > l3 . l3l4 ( pc - > st , CC_RELEASE | INDICATION , pc ) ;
release_l3_process ( pc ) ;
}
static void
l3dss1_dl_reestablish ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
L3AddTimer ( & pc - > timer , T309 , CC_T309 ) ;
l3_msg ( pc - > st , DL_ESTABLISH | REQUEST , NULL ) ;
}
static void
l3dss1_dl_reest_status ( struct l3_process * pc , u_char pr , void * arg )
{
L3DelTimer ( & pc - > timer ) ;
pc - > para . cause = 0x1F ; /* normal, unspecified */
l3dss1_status_send ( pc , 0 , NULL ) ;
}
/* *INDENT-OFF* */
static struct stateentry downstatelist [ ] =
{
{ SBIT ( 0 ) ,
CC_SETUP | REQUEST , l3dss1_setup_req } ,
{ SBIT ( 0 ) ,
CC_RESUME | REQUEST , l3dss1_resume_req } ,
{ SBIT ( 1 ) | SBIT ( 2 ) | SBIT ( 3 ) | SBIT ( 4 ) | SBIT ( 6 ) | SBIT ( 7 ) | SBIT ( 8 ) | SBIT ( 9 ) | SBIT ( 10 ) | SBIT ( 25 ) ,
CC_DISCONNECT | REQUEST , l3dss1_disconnect_req } ,
{ SBIT ( 12 ) ,
CC_RELEASE | REQUEST , l3dss1_release_req } ,
{ ALL_STATES ,
CC_RESTART | REQUEST , l3dss1_restart } ,
{ SBIT ( 6 ) | SBIT ( 25 ) ,
CC_IGNORE | REQUEST , l3dss1_reset } ,
{ SBIT ( 6 ) | SBIT ( 25 ) ,
CC_REJECT | REQUEST , l3dss1_reject_req } ,
{ SBIT ( 6 ) | SBIT ( 25 ) ,
CC_PROCEED_SEND | REQUEST , l3dss1_proceed_req } ,
{ SBIT ( 6 ) ,
CC_MORE_INFO | REQUEST , l3dss1_setup_ack_req } ,
{ SBIT ( 25 ) ,
CC_MORE_INFO | REQUEST , l3dss1_dummy } ,
{ SBIT ( 6 ) | SBIT ( 9 ) | SBIT ( 25 ) ,
CC_ALERTING | REQUEST , l3dss1_alert_req } ,
{ SBIT ( 6 ) | SBIT ( 7 ) | SBIT ( 9 ) | SBIT ( 25 ) ,
CC_SETUP | RESPONSE , l3dss1_setup_rsp } ,
{ SBIT ( 10 ) ,
CC_SUSPEND | REQUEST , l3dss1_suspend_req } ,
{ SBIT ( 7 ) | SBIT ( 9 ) | SBIT ( 25 ) ,
CC_REDIR | REQUEST , l3dss1_redir_req } ,
{ SBIT ( 6 ) ,
CC_REDIR | REQUEST , l3dss1_redir_req_early } ,
{ SBIT ( 9 ) | SBIT ( 25 ) ,
CC_DISCONNECT | REQUEST , l3dss1_disconnect_req } ,
{ SBIT ( 25 ) ,
CC_T302 , l3dss1_t302 } ,
{ SBIT ( 1 ) ,
CC_T303 , l3dss1_t303 } ,
{ SBIT ( 2 ) ,
CC_T304 , l3dss1_t304 } ,
{ SBIT ( 3 ) ,
CC_T310 , l3dss1_t310 } ,
{ SBIT ( 8 ) ,
CC_T313 , l3dss1_t313 } ,
{ SBIT ( 11 ) ,
CC_T305 , l3dss1_t305 } ,
{ SBIT ( 15 ) ,
CC_T319 , l3dss1_t319 } ,
{ SBIT ( 17 ) ,
CC_T318 , l3dss1_t318 } ,
{ SBIT ( 19 ) ,
CC_T308_1 , l3dss1_t308_1 } ,
{ SBIT ( 19 ) ,
CC_T308_2 , l3dss1_t308_2 } ,
{ SBIT ( 10 ) ,
CC_T309 , l3dss1_dl_release } ,
} ;
# define DOWNSLLEN \
( sizeof ( downstatelist ) / sizeof ( struct stateentry ) )
static struct stateentry datastatelist [ ] =
{
{ ALL_STATES ,
MT_STATUS_ENQUIRY , l3dss1_status_enq } ,
{ ALL_STATES ,
MT_FACILITY , l3dss1_facility } ,
{ SBIT ( 19 ) ,
MT_STATUS , l3dss1_release_ind } ,
{ ALL_STATES ,
MT_STATUS , l3dss1_status } ,
{ SBIT ( 0 ) ,
MT_SETUP , l3dss1_setup } ,
{ SBIT ( 6 ) | SBIT ( 7 ) | SBIT ( 8 ) | SBIT ( 9 ) | SBIT ( 10 ) | SBIT ( 11 ) | SBIT ( 12 ) |
SBIT ( 15 ) | SBIT ( 17 ) | SBIT ( 19 ) | SBIT ( 25 ) ,
MT_SETUP , l3dss1_dummy } ,
{ SBIT ( 1 ) | SBIT ( 2 ) ,
MT_CALL_PROCEEDING , l3dss1_call_proc } ,
{ SBIT ( 1 ) ,
MT_SETUP_ACKNOWLEDGE , l3dss1_setup_ack } ,
{ SBIT ( 2 ) | SBIT ( 3 ) ,
MT_ALERTING , l3dss1_alerting } ,
{ SBIT ( 2 ) | SBIT ( 3 ) ,
MT_PROGRESS , l3dss1_progress } ,
{ SBIT ( 2 ) | SBIT ( 3 ) | SBIT ( 4 ) | SBIT ( 7 ) | SBIT ( 8 ) | SBIT ( 9 ) | SBIT ( 10 ) |
SBIT ( 11 ) | SBIT ( 12 ) | SBIT ( 15 ) | SBIT ( 17 ) | SBIT ( 19 ) | SBIT ( 25 ) ,
MT_INFORMATION , l3dss1_information } ,
{ SBIT ( 10 ) | SBIT ( 11 ) | SBIT ( 15 ) ,
MT_NOTIFY , l3dss1_notify } ,
{ SBIT ( 0 ) | SBIT ( 1 ) | SBIT ( 2 ) | SBIT ( 3 ) | SBIT ( 4 ) | SBIT ( 7 ) | SBIT ( 8 ) | SBIT ( 10 ) |
SBIT ( 11 ) | SBIT ( 12 ) | SBIT ( 15 ) | SBIT ( 17 ) | SBIT ( 19 ) | SBIT ( 25 ) ,
MT_RELEASE_COMPLETE , l3dss1_release_cmpl } ,
{ SBIT ( 1 ) | SBIT ( 2 ) | SBIT ( 3 ) | SBIT ( 4 ) | SBIT ( 7 ) | SBIT ( 8 ) | SBIT ( 9 ) | SBIT ( 10 ) | SBIT ( 11 ) | SBIT ( 12 ) | SBIT ( 15 ) | SBIT ( 17 ) | SBIT ( 25 ) ,
MT_RELEASE , l3dss1_release } ,
{ SBIT ( 19 ) , MT_RELEASE , l3dss1_release_ind } ,
{ SBIT ( 1 ) | SBIT ( 2 ) | SBIT ( 3 ) | SBIT ( 4 ) | SBIT ( 7 ) | SBIT ( 8 ) | SBIT ( 9 ) | SBIT ( 10 ) | SBIT ( 11 ) | SBIT ( 15 ) | SBIT ( 17 ) | SBIT ( 25 ) ,
MT_DISCONNECT , l3dss1_disconnect } ,
{ SBIT ( 19 ) ,
MT_DISCONNECT , l3dss1_dummy } ,
{ SBIT ( 1 ) | SBIT ( 2 ) | SBIT ( 3 ) | SBIT ( 4 ) ,
MT_CONNECT , l3dss1_connect } ,
{ SBIT ( 8 ) ,
MT_CONNECT_ACKNOWLEDGE , l3dss1_connect_ack } ,
{ SBIT ( 15 ) ,
MT_SUSPEND_ACKNOWLEDGE , l3dss1_suspend_ack } ,
{ SBIT ( 15 ) ,
MT_SUSPEND_REJECT , l3dss1_suspend_rej } ,
{ SBIT ( 17 ) ,
MT_RESUME_ACKNOWLEDGE , l3dss1_resume_ack } ,
{ SBIT ( 17 ) ,
MT_RESUME_REJECT , l3dss1_resume_rej } ,
} ;
# define DATASLLEN \
( sizeof ( datastatelist ) / sizeof ( struct stateentry ) )
static struct stateentry globalmes_list [ ] =
{
{ ALL_STATES ,
MT_STATUS , l3dss1_status } ,
{ SBIT ( 0 ) ,
MT_RESTART , l3dss1_global_restart } ,
/* {SBIT(1),
MT_RESTART_ACKNOWLEDGE , l3dss1_restart_ack } ,
*/
} ;
# define GLOBALM_LEN \
( sizeof ( globalmes_list ) / sizeof ( struct stateentry ) )
static struct stateentry manstatelist [ ] =
{
{ SBIT ( 2 ) ,
DL_ESTABLISH | INDICATION , l3dss1_dl_reset } ,
{ SBIT ( 10 ) ,
DL_ESTABLISH | CONFIRM , l3dss1_dl_reest_status } ,
{ SBIT ( 10 ) ,
DL_RELEASE | INDICATION , l3dss1_dl_reestablish } ,
{ ALL_STATES ,
DL_RELEASE | INDICATION , l3dss1_dl_release } ,
} ;
# define MANSLLEN \
( sizeof ( manstatelist ) / sizeof ( struct stateentry ) )
/* *INDENT-ON* */
static void
global_handler ( struct PStack * st , int mt , struct sk_buff * skb )
{
u_char tmp [ 16 ] ;
u_char * p = tmp ;
int l ;
int i ;
struct l3_process * proc = st - > l3 . global ;
proc - > callref = skb - > data [ 2 ] ; /* cr flag */
for ( i = 0 ; i < GLOBALM_LEN ; i + + )
if ( ( mt = = globalmes_list [ i ] . primitive ) & &
( ( 1 < < proc - > state ) & globalmes_list [ i ] . state ) )
break ;
if ( i = = GLOBALM_LEN ) {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " dss1 global state %d mt %x unhandled " ,
proc - > state , mt ) ;
}
MsgHead ( p , proc - > callref , MT_STATUS ) ;
* p + + = IE_CAUSE ;
* p + + = 0x2 ;
* p + + = 0x80 ;
* p + + = 81 | 0x80 ; /* invalid cr */
* p + + = 0x14 ; /* CallState */
* p + + = 0x1 ;
* p + + = proc - > state & 0x3f ;
l = p - tmp ;
if ( ! ( skb = l3_alloc_skb ( l ) ) )
return ;
memcpy ( skb_put ( skb , l ) , tmp , l ) ;
l3_msg ( proc - > st , DL_DATA | REQUEST , skb ) ;
} else {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " dss1 global %d mt %x " ,
proc - > state , mt ) ;
}
globalmes_list [ i ] . rout ( proc , mt , skb ) ;
}
}
static void
dss1up ( struct PStack * st , int pr , void * arg )
{
int i , mt , cr , cause , callState ;
char * ptr ;
u_char * p ;
struct sk_buff * skb = arg ;
struct l3_process * proc ;
switch ( pr ) {
case ( DL_DATA | INDICATION ) :
case ( DL_UNIT_DATA | INDICATION ) :
break ;
case ( DL_ESTABLISH | CONFIRM ) :
case ( DL_ESTABLISH | INDICATION ) :
case ( DL_RELEASE | INDICATION ) :
case ( DL_RELEASE | CONFIRM ) :
l3_msg ( st , pr , arg ) ;
return ;
break ;
default :
printk ( KERN_ERR " HiSax dss1up unknown pr=%04x \n " , pr ) ;
return ;
}
if ( skb - > len < 3 ) {
l3_debug ( st , " dss1up frame too short(%d) " , skb - > len ) ;
dev_kfree_skb ( skb ) ;
return ;
}
if ( skb - > data [ 0 ] ! = PROTO_DIS_EURO ) {
if ( st - > l3 . debug & L3_DEB_PROTERR ) {
l3_debug ( st , " dss1up%sunexpected discriminator %x message len %d " ,
( pr = = ( DL_DATA | INDICATION ) ) ? " " : " (broadcast) " ,
skb - > data [ 0 ] , skb - > len ) ;
}
dev_kfree_skb ( skb ) ;
return ;
}
cr = getcallref ( skb - > data ) ;
if ( skb - > len < ( ( skb - > data [ 1 ] & 0x0f ) + 3 ) ) {
l3_debug ( st , " dss1up frame too short(%d) " , skb - > len ) ;
dev_kfree_skb ( skb ) ;
return ;
}
mt = skb - > data [ skb - > data [ 1 ] + 2 ] ;
if ( st - > l3 . debug & L3_DEB_STATE )
l3_debug ( st , " dss1up cr %d " , cr ) ;
if ( cr = = - 2 ) { /* wrong Callref */
if ( st - > l3 . debug & L3_DEB_WARN )
l3_debug ( st , " dss1up wrong Callref " ) ;
dev_kfree_skb ( skb ) ;
return ;
} else if ( cr = = - 1 ) { /* Dummy Callref */
if ( mt = = MT_FACILITY )
if ( ( p = findie ( skb - > data , skb - > len , IE_FACILITY , 0 ) ) ) {
l3dss1_parse_facility ( st , NULL ,
( pr = = ( DL_DATA | INDICATION ) ) ? - 1 : - 2 , p ) ;
dev_kfree_skb ( skb ) ;
return ;
}
if ( st - > l3 . debug & L3_DEB_WARN )
l3_debug ( st , " dss1up dummy Callref (no facility msg or ie) " ) ;
dev_kfree_skb ( skb ) ;
return ;
} else if ( ( ( ( skb - > data [ 1 ] & 0x0f ) = = 1 ) & & ( 0 = = ( cr & 0x7f ) ) ) | |
( ( ( skb - > data [ 1 ] & 0x0f ) = = 2 ) & & ( 0 = = ( cr & 0x7fff ) ) ) ) { /* Global CallRef */
if ( st - > l3 . debug & L3_DEB_STATE )
l3_debug ( st , " dss1up Global CallRef " ) ;
global_handler ( st , mt , skb ) ;
dev_kfree_skb ( skb ) ;
return ;
} else if ( ! ( proc = getl3proc ( st , cr ) ) ) {
/* No transaction process exist, that means no call with
* this callreference is active
*/
if ( mt = = MT_SETUP ) {
/* Setup creates a new transaction process */
if ( skb - > data [ 2 ] & 0x80 ) {
/* Setup with wrong CREF flag */
if ( st - > l3 . debug & L3_DEB_STATE )
l3_debug ( st , " dss1up wrong CRef flag " ) ;
dev_kfree_skb ( skb ) ;
return ;
}
if ( ! ( proc = dss1_new_l3_process ( st , cr ) ) ) {
/* May be to answer with RELEASE_COMPLETE and
* CAUSE 0x2f " Resource unavailable " , but this
* need a new_l3_process too . . . arghh
*/
dev_kfree_skb ( skb ) ;
return ;
}
} else if ( mt = = MT_STATUS ) {
cause = 0 ;
if ( ( ptr = findie ( skb - > data , skb - > len , IE_CAUSE , 0 ) ) ! = NULL ) {
ptr + + ;
if ( * ptr + + = = 2 )
ptr + + ;
cause = * ptr & 0x7f ;
}
callState = 0 ;
if ( ( ptr = findie ( skb - > data , skb - > len , IE_CALL_STATE , 0 ) ) ! = NULL ) {
ptr + + ;
if ( * ptr + + = = 2 )
ptr + + ;
callState = * ptr ;
}
/* ETS 300-104 part 2.4.1
* if setup has not been made and a message type
* MT_STATUS is received with call state = = 0 ,
* we must send nothing
*/
if ( callState ! = 0 ) {
/* ETS 300-104 part 2.4.2
* if setup has not been made and a message type
* MT_STATUS is received with call state ! = 0 ,
* we must send MT_RELEASE_COMPLETE cause 101
*/
if ( ( proc = dss1_new_l3_process ( st , cr ) ) ) {
proc - > para . cause = 101 ;
l3dss1_msg_without_setup ( proc , 0 , NULL ) ;
}
}
dev_kfree_skb ( skb ) ;
return ;
} else if ( mt = = MT_RELEASE_COMPLETE ) {
dev_kfree_skb ( skb ) ;
return ;
} else {
/* ETS 300-104 part 2
* if setup has not been made and a message type
* ( except MT_SETUP and RELEASE_COMPLETE ) is received ,
* we must send MT_RELEASE_COMPLETE cause 81 */
dev_kfree_skb ( skb ) ;
if ( ( proc = dss1_new_l3_process ( st , cr ) ) ) {
proc - > para . cause = 81 ;
l3dss1_msg_without_setup ( proc , 0 , NULL ) ;
}
return ;
}
}
if ( l3dss1_check_messagetype_validity ( proc , mt , skb ) ) {
dev_kfree_skb ( skb ) ;
return ;
}
if ( ( p = findie ( skb - > data , skb - > len , IE_DISPLAY , 0 ) ) ! = NULL )
l3dss1_deliver_display ( proc , pr , p ) ; /* Display IE included */
for ( i = 0 ; i < DATASLLEN ; i + + )
if ( ( mt = = datastatelist [ i ] . primitive ) & &
( ( 1 < < proc - > state ) & datastatelist [ i ] . state ) )
break ;
if ( i = = DATASLLEN ) {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " dss1up%sstate %d mt %#x unhandled " ,
( pr = = ( DL_DATA | INDICATION ) ) ? " " : " (broadcast) " ,
proc - > state , mt ) ;
}
if ( ( MT_RELEASE_COMPLETE ! = mt ) & & ( MT_RELEASE ! = mt ) ) {
proc - > para . cause = 101 ;
l3dss1_status_send ( proc , pr , skb ) ;
}
} else {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " dss1up%sstate %d mt %x " ,
( pr = = ( DL_DATA | INDICATION ) ) ? " " : " (broadcast) " ,
proc - > state , mt ) ;
}
datastatelist [ i ] . rout ( proc , pr , skb ) ;
}
dev_kfree_skb ( skb ) ;
return ;
}
static void
dss1down ( struct PStack * st , int pr , void * arg )
{
int i , cr ;
struct l3_process * proc ;
struct Channel * chan ;
if ( ( DL_ESTABLISH | REQUEST ) = = pr ) {
l3_msg ( st , pr , NULL ) ;
return ;
} else if ( ( ( CC_SETUP | REQUEST ) = = pr ) | | ( ( CC_RESUME | REQUEST ) = = pr ) ) {
chan = arg ;
cr = newcallref ( ) ;
cr | = 0x80 ;
if ( ( proc = dss1_new_l3_process ( st , cr ) ) ) {
proc - > chan = chan ;
chan - > proc = proc ;
memcpy ( & proc - > para . setup , & chan - > setup , sizeof ( setup_parm ) ) ;
proc - > callref = cr ;
}
} else {
proc = arg ;
}
if ( ! proc ) {
printk ( KERN_ERR " HiSax dss1down without proc pr=%04x \n " , pr ) ;
return ;
}
if ( pr = = ( CC_TDSS1_IO | REQUEST ) ) {
l3dss1_io_timer ( proc ) ; /* timer expires */
return ;
}
for ( i = 0 ; i < DOWNSLLEN ; i + + )
if ( ( pr = = downstatelist [ i ] . primitive ) & &
( ( 1 < < proc - > state ) & downstatelist [ i ] . state ) )
break ;
if ( i = = DOWNSLLEN ) {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " dss1down state %d prim %#x unhandled " ,
proc - > state , pr ) ;
}
} else {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " dss1down state %d prim %#x " ,
proc - > state , pr ) ;
}
downstatelist [ i ] . rout ( proc , pr , arg ) ;
}
}
static void
dss1man ( struct PStack * st , int pr , void * arg )
{
int i ;
struct l3_process * proc = arg ;
if ( ! proc ) {
printk ( KERN_ERR " HiSax dss1man without proc pr=%04x \n " , pr ) ;
return ;
}
for ( i = 0 ; i < MANSLLEN ; i + + )
if ( ( pr = = manstatelist [ i ] . primitive ) & &
( ( 1 < < proc - > state ) & manstatelist [ i ] . state ) )
break ;
if ( i = = MANSLLEN ) {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " cr %d dss1man state %d prim %#x unhandled " ,
proc - > callref & 0x7f , proc - > state , pr ) ;
}
} else {
if ( st - > l3 . debug & L3_DEB_STATE ) {
l3_debug ( st , " cr %d dss1man state %d prim %#x " ,
proc - > callref & 0x7f , proc - > state , pr ) ;
}
manstatelist [ i ] . rout ( proc , pr , arg ) ;
}
}
void
setstack_dss1 ( struct PStack * st )
{
char tmp [ 64 ] ;
int i ;
st - > lli . l4l3 = dss1down ;
st - > lli . l4l3_proto = l3dss1_cmd_global ;
st - > l2 . l2l3 = dss1up ;
st - > l3 . l3ml3 = dss1man ;
st - > l3 . N303 = 1 ;
st - > prot . dss1 . last_invoke_id = 0 ;
st - > prot . dss1 . invoke_used [ 0 ] = 1 ; /* Bit 0 must always be set to 1 */
i = 1 ;
while ( i < 32 )
st - > prot . dss1 . invoke_used [ i + + ] = 0 ;
if ( ! ( st - > l3 . global = kmalloc ( sizeof ( struct l3_process ) , GFP_ATOMIC ) ) ) {
printk ( KERN_ERR " HiSax can't get memory for dss1 global CR \n " ) ;
} else {
st - > l3 . global - > state = 0 ;
st - > l3 . global - > callref = 0 ;
st - > l3 . global - > next = NULL ;
st - > l3 . global - > debug = L3_DEB_WARN ;
st - > l3 . global - > st = st ;
st - > l3 . global - > N303 = 1 ;
st - > l3 . global - > prot . dss1 . invoke_id = 0 ;
L3InitTimer ( st - > l3 . global , & st - > l3 . global - > timer ) ;
}
strcpy ( tmp , dss1_revision ) ;
printk ( KERN_INFO " HiSax: DSS1 Rev. %s \n " , HiSax_getrev ( tmp ) ) ;
}