/***************************************************************************** @(#) $Id: ss7if_sm.c,v 0.7.2.3 2000/10/09 02:40:40 brian Exp $ ----------------------------------------------------------------------------- Copyright (C) 1997, 1998, 1999, 2000 Brian Bidulock All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Last Modified $Date: 2000/10/09 02:40:40 $ by $Author: brian $ ----------------------------------------------------------------------------- $Log: ss7if_sm.c,v $ Revision 0.7.2.3 2000/10/09 02:40:40 brian Pre-release 1. Revision 0.7.2.2 2000/09/21 05:05:07 brian Changed typo and removed redundant file. Revision 0.7.2.1 2000/09/11 23:35:27 brian Changed to work with new header files. Revision 0.7 2000/09/07 13:26:22 brian Initial import of this file into this directory. Revision 0.7.2.1 2000/09/07 12:49:16 brian Got ss7link interface support for drivers compiling. Revision 0.7 2000/09/07 11:12:13 brian Initial import of OpenSS7. Revision 1.2 2000/09/07 10:51:51 brian Got these files going. Revision 1.1.1.1 2000/09/05 11:00:20 brian Initial import of new OpenSS7 stack for Linux. *****************************************************************************/ #include #if 1 /* defined(CONFIG_SS7)||defined(CONFIG_SS7_MODULE) */ static char const ident[] = "$Id: ss7if_sm.c,v 0.7.2.3 2000/10/09 02:40:40 brian Exp $"; #define __NO_VERSION__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../net/ss7/af_ss7.h" #include "../../net/ss7/mtp.h" #include "../../net/ss7/mtp_route.h" #include "../../net/ss7/mtp_sm.h" #include "../../include/linux/ss7link.h" #include "../../include/linux/ss7_timers.h" #include "ss7if.h" #include /* * Some support functions. */ #define prep_for_fisu \ if ( !(p->tsu_skb = skb_clone(p->fisu_skb, GFP_ATOMIC)) ) return #define prep_for_lssu \ if ( !(p->tsu_skb = skb_clone(p->lssu_skb, GFP_ATOMIC)) ) return #define prep_for_msu \ if ( !(p->tsu_skb = skb_clone(p-> msu_skb, GFP_ATOMIC)) ) return static __inline void send_fisu(struct ss7_iface *p) { unsigned char* data = p->tsu_skb->data; data[0] = (p->tx.N.bsn|p->tx.N.bib); data[1] = (p->tx.N.fsn|p->tx.N.fib); data[2] = 0x00; p->if_xmit(p->tsu_skb,p->dev); return; } static __inline void send_lssu(struct ss7_iface *p) { unsigned char* data = p->tsu_skb->data; data[0] = (p->tx.N.bsn|p->tx.N.bib); data[1] = (p->tx.N.fsn|p->tx.N.fib); data[2] = 0x01; data[3] = (p->tx.sio); p->if_xmit(p->tsu_skb,p->dev); return; } static __inline void send_msu(struct ss7_iface *p) { unsigned char* data = p->tsu_skb->data; unsigned int len = (unsigned int)(p->tsu_skb->len - 3); data[0] = (p->tx.N.bsn|p->tx.N.bib); data[1] = (p->tx.N.fsn|p->tx.N.fib); data[2] = (len<64?len:63); p->if_xmit(p->tsu_skb,p->dev); return; } /* * Receive Buffer Operations. */ #define RB_INIT \ /* initialized by system */ #define RB_CLEAR \ /* can't, there gone... */ #define RB_DISCARD \ kfree_skb(p->rsu_skb) #define RB_WRITE \ Signal(L3_PDU); \ RB_DISCARD; /* * Transmit Buffer Operations. */ #define TB_CLEAR \ skb_queue_purge(&p->xmit_q) #define TB_INIT \ skb_queue_head_init(&p->xmit_q) #define TB_START \ p->retrans_cycle = 0; \ p->msu_skb = skb_peek(&p->xmit_q) #define TB_READ \ skb_queue_tail(&p->rexm_q,skb_dequeue(&p->xmit_q)) #define TB_EMPTY \ skb_queue_empty(&p->xmit_q) /* * Retransmit Buffer Operations. */ #define RTB_START \ p->msu_skb = skb_peek(&p->rexm_q); \ if (!skb_queue_empty(&p->rexm_q)) \ p->retrans_cycle = 1 #define RTB_CYCLE \ (p->retrans_cycle) #define RTB_AGAIN \ if ( p->msu_skb == (p->msu_skb = p->msu_skb->next) ) \ p->retrans_cycle = 0 #define RTB_EMPTY \ skb_queue_empty(&p->rexm_q) #define RTB_CLEAR \ skb_queue_purge(&p->rexm_q) #define RTB_ACK \ if (!skb_queue_empty(&p->rexm_q)) kfree_skb(skb_dequeue(&p->rexm_q)) #define RTB_READ \ p->tsu_skb = skb_dequeue(&p->rexm_q) #define RTP_REWRITE \ skb_queue_head(&p->rexm_q,p->tsu_skb); /* * Macro magic for debugging without killing readability. */ #define Mark(flag) p-> ## flag = 1 #define Cancel(flag) p-> ## flag = 0 #define Set(flag) p-> ## flag #define var(var) p-> ## var #define State(module,state) module ## _STATE_ ## state #define CurrentState(module) p-> ## module ## _state #ifdef DEBUG_PRINT_STATE #define NewState(module,state) \ { \ printk(KERN_INFO "SS7/LINK: " __FUNCTION__ " " #module " = " #state "\n"); \ CurrentState(module) = State(module,state); \ } #else #define NewState(module,state) CurrentState(module) = State(module,state); #endif #define Event(primitive) \ static __inline void ss7if_ ## primitive ## (struct ss7_iface *p) #ifdef DEBUG_PRINT_ASP #define Signal(action) \ { \ print(KERN_INFO "SS7LINK: " __FUNCTION__ " -> " #action "\n"); \ ss7if_ ## action ## (p); \ } #else #define Signal(action) ss7if_ ## action ## (p) #endif #define SetTimer(timer,value) ss7if_timer_set_duration(p,TIMER_ ## timer, TIMER_ ## value); #ifdef DEBUG_PRINT_TIMERS #define Stop(timer) \ { \ print(KERN_INFO "SS7LINK: " __FUNCTION__ " -> " #timer " Stop\n"); \ ss7if_timer_stop(p, TIMER_ ## timer); \ } #define Start(timer,timeout) \ { \ print(KERN_INFO "SS7LINK: " __FUNCTION__ " -> " #timer " Start (" #timeout ")\n"); \ ss7if_timer_start(p, TIMER_ ## timer, ss7if_ ## timeout); \ } #else #define Stop(timer) ss7if_timer_stop(p, TIMER_ ## timer); #define Start(timer,timeout) ss7if_timer_start(p, TIMER_ ## timer, ss7if_ ## timeout); #endif /* * ------------------------------------------------------------------------ * * L3 Primitives: to L3 * * ------------------------------------------------------------------------ */ static void make_primitive(struct ss7_iface *p, unsigned char cmd, unsigned char *ptr, unsigned int size); Event(L3_Link_Congested) { unsigned char c = var(cong_level); make_primitive(p,SS7_L3_LINK_CONGESTED,&c,sizeof(c)); } Event(L3_Link_Congestion_Ceased) { unsigned char c = var(cong_level); make_primitive(p,SS7_L3_LINK_CONGESTION_CEASED,&c,sizeof(c)); } Event(L3_BSNT) { unsigned char c = var(rx.T.bsn)|var(rx.T.bib); make_primitive(p,SS7_L3_BSNT,&c,sizeof(c)); } Event(L3_In_Service) { make_primitive(p,SS7_L3_IN_SERVICE,NULL,0); } Event(L3_Out_Of_Service) { make_primitive(p,SS7_L3_OUT_OF_SERVICE,NULL,0); } Event(L3_RB_Cleared) { make_primitive(p,SS7_L3_RB_CLEARED,NULL,0); } Event(L3_RTB_Cleared) { make_primitive(p,SS7_L3_RTB_CLEARED,NULL,0); } Event(L3_Remote_Processor_Outage) { make_primitive(p,SS7_L3_REMOTE_PROCESSOR_OUTAGE,NULL,0); } Event(L3_Remote_Processor_Recovered) { make_primitive(p,SS7_L3_REMOTE_PROCESSOR_RECOVERED,NULL,0); } Event(L3_Retreival_Complete) { make_primitive(p,SS7_L3_RETRIEVAL_COMPLETE,NULL,0); } Event(L3_Retreived_Message) { make_primitive(p,SS7_L3_RETRIEVED_MESSAGES,var(tsu_skb->data),var(tsu_skb->len)); } Event(L3_PDU) { make_primitive(p,SS7_L3_PDU,var(rsu_skb->data),var(rsu_skb->len)); } /* * ------------------------------------------------------------------------ * * State Machines * * The order of the state machines primitives below may seem somewhat * disorganized at first glance; however, they have been ordered by * dependency because they are all __inline functions. You see, the L2 state * machine does not require multiple threading because there is never a * requirement to invoke the individual state machines concurrently. This * works out good for the driver, because a primitive action expands __inline * to the necessary procedure, while the source still take the appearance * of the SDLs in the SS7 specification. * * ------------------------------------------------------------------------ */ /* * DAEDR_Start is responsible for setting up the reception of the first frame * and starting the receivers. * * The rest of the DAEDR state machine is embedded in the RxISR. */ Event(DAEDR_Start) { /* * Note that the receivers (DAEDR) are not started for SS7 on * power-up. It is only after L2 receives the LSC_Start() command * from L3 that the receivers are started. Once started, however, the * receivers are never stopped or reset. In contrast, the * transmitters (DAEDT) are started initially at power-on and are * never restarted. Transmitters are supposed to send SIOS on * power-on. */ /* * This setup is done by the driver, however, the receivers start up * at the same time as the transmitters. Therefore, we track Octet * Counting Mode while the receivers are idle and cannot reset it when * they go active or we will get out of sync with the driver. */ #if 0 Cancel(Octet_Counting_Mode); #endif NewState(DAEDR,Active); } Event(SUERM_Stop) { NewState(SUERM,Idle); } Event(CC_Normal) { if ( CurrentState(CC) == State(CC,Congested) ) { Stop(T5); NewState(CC,Normal); } } Event(RC_Stop) { if ( CurrentState(RC) != State(RC,Active) ) return; Signal(CC_Normal); NewState(RC,Power_On); } Event(IAC_Stop) { Stop(T2); Cancel(Emergency); NewState(IAC,Idle); } Event(DAEDT_Transmitter_Wakeup) { if ( CurrentState(DAEDT) != State(DAEDT,Sleeping) ) return; NewState(DAEDT,Active); } Event(TXC_Send_SIOS) { Stop(T7); Stop(T6); Mark(LSSU_Available); var(tx.sio) = LSSU_SIOS; Signal(DAEDT_Transmitter_Wakeup); } Event(LSC_Link_Failure) { switch ( CurrentState(LSC) ) { case State(LSC,Initial_Alignment): Signal(L3_Out_Of_Service); Signal(IAC_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Local_Processor_Outage); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Ready): Stop(T1); Signal(L3_Out_Of_Service); Signal(RC_Stop); Signal(SUERM_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Not_Ready): Stop(T1); Signal(L3_Out_Of_Service); Signal(TXC_Send_SIOS); Signal(SUERM_Stop); Signal(RC_Stop); Cancel(Emergency); Cancel(Local_Processor_Outage); NewState(LSC,Out_of_Service); return; case State(LSC,In_Service): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Processor_Outage): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); Cancel(Remote_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(TXC_T6_Timeout) { Signal(LSC_Link_Failure); Cancel(SIB_Received); Stop(T7); } Event(TXC_T7_Timeout) { Signal(LSC_Link_Failure); Stop(T6); } Event(TXC_Send_SIB) { Mark(LSSU_Available); var(tx.sio) = LSSU_SIB; Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_Send_SIPO) { Stop(T7); Stop(T6); Mark(LSSU_Available); var(tx.sio) = LSSU_SIPO; Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_Send_SIO) { Mark(LSSU_Available); var(tx.sio) = LSSU_SIO; Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_Send_SIN) { Mark(LSSU_Available); var(tx.sio) = LSSU_SIN; Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_Send_SIE) { Mark(LSSU_Available); var(tx.sio) = LSSU_SIE; Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_Send_MSU) { if ( !RTB_EMPTY ) { Start(T7, TXC_T7_Timeout); } Cancel(MSU_Inhibited); Cancel(LSSU_Available); Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_Send_FISU) { Stop(T7); Mark(MSU_Inhibited); Cancel(LSSU_Available); Stop(T6); Signal(DAEDT_Transmitter_Wakeup); } Event(TXC_FSNX_Value) { var(tx.X.fsn) = var(rx.X.fsn); } Event(TXC_NACK_To_Be_Sent) { var(tx.N.bib) = var(tx.N.bib)?0x00:0x80; } Event(LSC_RTB_Cleared) { switch ( CurrentState(LSC) ) { case State(LSC,Processor_Outage): Cancel(Remote_Processor_Outage); if ( Set(Local_Processor_Outage) ) return; Signal(L3_Remote_Processor_Recovered); Signal(L3_RTB_Cleared); Signal(TXC_Send_MSU); NewState(LSC,In_Service); return; } } Event(TXC_BSNR_and_BIBR) { var(tx.R.bsn) = var(rx.R.bsn); var(tx.R.bib) = var(rx.R.bib); if ( Set(Clear_RTB) ) { /* Erase all MSUs in RTB */ RTB_CLEAR; var(tx.F.fsn) = (var(tx.R.bsn)+1)&0x7f; var(tx.L.fsn) = var(tx.R.bsn); var(tx.N.fsn) = var(tx.R.bsn); var(tx.N.fib) = var(tx.R.bib); Signal(LSC_RTB_Cleared); Cancel(Clear_RTB); Cancel(RTB_Full); return; } if ( var(tx.F.fsn) != ((var(tx.R.bsn)+1)&0x7f) ) { /* * If we are here, we have received an ACK for FSN = var(tx.R.bsn) * * Remote end not busy anymore if it is acknowledging frames which * have been sent. The distant end withholds acknoledgements when it * is busy. */ if ( Set(SIB_Received) ) { Cancel(SIB_Received); Stop(T6); } /* * BSNR is an ACK for all outstanding messages up to BSNR and is * either an ACK or a NACK for BSNR depending upon sense of BIBR. * * Mark as acknowledged all frames with FSN less than BSNR but don't * go past end of RTB. */ do { RTB_ACK; var(tx.F.fsn) = (var(tx.F.fsn)+1)&0x7f; } while ( var(tx.F.fsn) != ((var(tx.R.bsn)+1)&0x7f) ); Signal(DAEDT_Transmitter_Wakeup); /* * Did we empty the RTB: if we did stop T7, otherwise restart T7 */ // if ( var(tx.L.fsn) == var(rx.F.fsn) ) { /* RTB empty */ if ( RTB_EMPTY ) { /* RTB empty */ Stop(T7); } else { /* * I don't know that this is quite right... SS7 timers normally * restart if they are started while they are running. This * should be fixed up a little better... */ // Stop(T7); Start(T7, TXC_T7_Timeout); } /* * The RTB can't be full anymore... */ Cancel(RTB_Full); } /* * If we NACK'ed a frame (inversion on bibr), set for retransmission... */ if ( var(tx.N.fib) != var(tx.R.bib) ) { /* * If we are here, we have received a NACK for FSN = var(tx.R.bsn) * and are starting a retransmission cycle. */ var(tx.N.fib) = var(tx.R.bib); var(tx.N.fsn) = (var(tx.F.fsn)-1)&0x7f; RTB_START; Signal(DAEDT_Transmitter_Wakeup); return; } return; } Event(TXC_SIB_Received) { if ( Set(LSSU_Available) ) if ( var(tx.sio) != LSSU_SIB ) return; if ( RTB_EMPTY ) return; if ( !Set(SIB_Received) ) { Start(T6, TXC_T6_Timeout); Mark(SIB_Received); } Start(T7, TXC_T7_Timeout); } Event(TXC_Clear_RTB) { Mark(Clear_RTB); } Event(TXC_Clear_TB) { TB_CLEAR; } Event(DAEDT_FISU) { send_fisu(p); NewState(DAEDT,Sleeping); } Event(DAEDT_LSSU) { send_lssu(p); if ( Set(LSSU_Available) ) NewState(DAEDT,Sleeping); /* this is not a SIB */ } Event(DAEDT_MSU) { send_msu(p); } Event(RC_FSNT_Value) { if ( CurrentState(RC) != State(RC,Active) ) return; var(rx.T.fsn) = var(tx.N.fsn); } #define SS7_RTB_BURST_MAX 10 Event(TXC_Retreival_Request_and_FSNC) { start_bh_atomic(); { int burst = SS7_RTB_BURST_MAX+1; /* number of messages in burst */ while (!TB_EMPTY ) TB_READ; while (!RTB_EMPTY) { if (burst--) goto more_later; /* give NET_BH time to run. */ RTB_READ; Signal(L3_Retreived_Message); if (netdev_dropping) { if (net_ratelimit()) printk(KERN_CRIT "SS7/LINK: network dropping %s retreived frames: burst of %d\n", p->dev->name, SS7_RTB_BURST_MAX); goto more_later; } } var(tx.F.fsn) = (var(tx.C.fsn)+1)&0x7f; var(tx.L.fsn) = var(tx.C.fsn); Cancel(RTB_Full); Signal(L3_Retreival_Complete); } end_bh_atomic(); return; more_later: ss7_sm_dispatch(&p->rx_taskq,ss7if_TXC_Retreival_Request_and_FSNC); end_bh_atomic(); return; } #undef SS7_RTB_BURST_MAX Event(TXC_Transmission_Request) { if ( Set(LSSU_Available) ) { prep_for_lssu; if ( var(tx.sio) == LSSU_SIB ) Cancel(LSSU_Available); /* Only send one SIB, repeat others */ var(tx.N.fsn) = var(tx.L.fsn); var(tx.N.bib) = var(tx.N.bib); var(tx.N.bsn) = (var(tx.X.fsn) - 1)&0x7f; var(tx.N.fib) = var(tx.N.fib); Signal(DAEDT_LSSU); return; } if ( Set(MSU_Inhibited) ) { prep_for_fisu; var(tx.N.fsn) = var(tx.L.fsn); var(tx.N.bib) = var(tx.N.bib); var(tx.N.bsn) = (var(tx.X.fsn) - 1)&0x7f; var(tx.N.fib) = var(tx.N.fib); Signal(DAEDT_FISU); return; } if ( RTB_CYCLE ) /* Retransmission cycle */ { prep_for_msu; RTB_AGAIN; var(tx.N.fsn) = (var(tx.N.fsn) + 1)&0x7f; var(tx.N.bib) = var(tx.N.bib); var(tx.N.bsn) = (var(tx.X.fsn) - 1)&0x7f; var(tx.N.fib) = var(tx.N.fib); Signal(DAEDT_MSU); return; } if ( TB_EMPTY || /* Transmission buffer empty */ Set(RTB_Full) ) /* Retransmission buffer full */ { prep_for_fisu; var(tx.N.fsn) = var(tx.L.fsn); var(tx.N.bib) = var(tx.N.bib); var(tx.N.bsn) = (var(tx.X.fsn) - 1)&0x7f; var(tx.N.fib) = var(tx.N.fib); Signal(DAEDT_FISU); return; } else { TB_START; prep_for_msu; var(tx.L.fsn) = (var(tx.L.fsn) + 1)&0x7f; var(tx.N.fsn) = var(tx.L.fsn); // if ( var(tx.L.fsn) == var(tx.F.fsn) ) /* RTB empty */ if ( RTB_EMPTY ) Start(T7, TXC_T7_Timeout); TB_READ; Signal(RC_FSNT_Value); if ( var(tx.L.fsn) == ((var(tx.F.fsn) - 2)&0x7f) ) /* RTB full */ Mark(RTB_Full); var(tx.N.bib) = var(tx.N.bib); var(tx.N.bsn) = (var(tx.X.fsn) - 1)&0x7f; var(tx.N.fib) = var(tx.N.fib); Signal(DAEDT_MSU); } } Event(RC_Start) { if ( CurrentState(RC) == State(RC,Active) ) return; var(rx.X.fsn) = 0x00; var(rx.X.fib) = 0x80; var(rx.F.fsn) = 0x00; var(rx.T.fsn) = 0x7f; var(rtr) = 0; Cancel(MSU_FISU_Accepted); Cancel(Abnormal_BSNR); Cancel(Abnormal_FIBR); Cancel(Congestion_Discard); Cancel(Congestion_Accept); RB_INIT; Signal(DAEDR_Start); NewState(RC,Active); } Event(RC_Reject_MSU_FISU) { if ( CurrentState(RC) != State(RC,Active)) return; Cancel(MSU_FISU_Accepted); } Event(RC_Accept_MSU_FISU) { if ( CurrentState(RC) != State(RC,Active)) return; Mark(MSU_FISU_Accepted); } Event(RC_Align_FSNX) { if ( CurrentState(RC) != State(RC,Active)) return; Signal(TXC_FSNX_Value); } Event(RC_Clear_RB) { if ( CurrentState(RC) != State(RC,Active)) return; RB_CLEAR; Signal(L3_RB_Cleared); } Event(RC_Retreive_BSNT) { var(l3_bsnt) = var(rx.T.bsn) = (var(rx.X.fsn) - 1)&0x7F; Signal(L3_BSNT); } Event(CC_T5_Timeout) { if ( CurrentState(CC) == State(CC,Congested) ) { Signal(TXC_Send_SIB); Start(T5, CC_T5_Timeout); } } Event(CC_Busy) { if ( ! CurrentState(CC) ) { Signal(TXC_Send_SIB); Start(T5, CC_T5_Timeout); NewState(CC,Congested); } } Event(CC_Stop) { if ( CurrentState(CC) == State(CC,Congested) ) { Stop(T5); NewState(CC,Normal); } } Event(RC_Congestion_Discard) { if ( CurrentState(RC) != State(RC,Active) ) return; Mark(Congestion_Discard); Signal(CC_Busy); } Event(RC_Congestion_Accept) { if ( CurrentState(RC) != State(RC,Active) ) return; Mark(Congestion_Accept); Signal(CC_Busy); } Event(RC_No_Congestion) { if ( CurrentState(RC) != State(RC,Active) ) return; Cancel(Congestion_Discard); Cancel(Congestion_Accept); Signal(CC_Normal); Signal(TXC_FSNX_Value); if ( var(rtr) == 1 ) { Signal(TXC_NACK_To_Be_Sent); var(rx.X.fib) = var(rx.X.fib)?0x00:0x80; } } Event(LSC_Congestion_Discard) { Signal(RC_Congestion_Discard); Mark(L3_Congestion_Detect); } Event(LSC_Congestion_Accept) { Signal(RC_Congestion_Accept); Mark(L3_Congestion_Detect); } Event(LSC_No_Congestion) { Signal(RC_No_Congestion); Cancel(L3_Congestion_Detect); } /* * ------------------------------------------------------------------------ * * These congestion functions are implementation dependent. We should define * a congestion onset level and set Congestion_Accept at that point. We * should also define a congestion panic level and set Congestion_Discard at * that point. Implemented levels are as follows: * * Congestion_Accept Onset = RB 2/3 full, Abatement = RB 1/2 full * Congestion_Discard Onset = RB 3/4 full, Abatement = RB 1/2 full * * Out concept of receive buffer occupancy is a little vague because we are * forwarding packets immediately to the packet scheduler. Perhaps a better * indication is how many tasks are backed up in the state machine wiatq. We * can use the same calculations but simplify them by setting the maximum * wait queue depth to 4. Then we can set congestion levels as follows: * * Congestion_Accept Onset = 2 tasks, Abatement = 1 task * Congestion_Discard Onset = 3 tasks, Abatement = 1 tasks * * Nevertheless, we better watch the packet scheduler backlog queue to make * sure that we are not overloading it. Unfortunately the backlog queue is * static, but the netdev_dropping is not. We can check to see if the packet * scheduler is dropping for discard congestion. I will do that where I * transmit the frame to L3. * * ------------------------------------------------------------------------ */ Event(RB_Congestion_Function) { if ( !Set(L3_Congestion_Detect) ) { if ( Set(L2_Congestion_Detect) ) { if ( skb_queue_len(&var(read_q)) <= 1 && !netdev_dropping ) { Signal(RC_No_Congestion); Cancel(L2_Congestion_Detect); } } else { if ( skb_queue_len(&var(read_q)) > 1) { Signal(RC_Congestion_Accept); Mark(L2_Congestion_Detect); } } if ( skb_queue_len(&var(read_q)) > 2 || netdev_dropping ) { Signal(RC_Congestion_Discard); Mark(L2_Congestion_Detect); } } } /* * ------------------------------------------------------------------------ */ Event(LSC_SIO) /* * SIOs do not need to be delivered to the LSC if the LSC is in initial * alignment (IA) or out of service (OOS) or off (OFF). No SUs are * delivered to the LSC when the LSC is in the off (OFF) state. */ { switch ( CurrentState(LSC) ) { case State(LSC,Aligned_Ready): Stop(T1); Signal(L3_Out_Of_Service); Signal(RC_Stop); Signal(SUERM_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Not_Ready): Stop(T1); Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); NewState(LSC,Out_of_Service); return; case State(LSC,In_Service): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Processor_Outage): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); Cancel(Remote_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(LSC_Alignment_Not_Possible) { switch ( CurrentState(LSC) ) { case State(LSC,Initial_Alignment): Signal(L3_Out_Of_Service); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Local_Processor_Outage); Cancel(Emergency); NewState(LSC,Out_of_Service); return; } } Event(IAC_T3_Timeout) { switch ( CurrentState(IAC) ) { case State(IAC,Aligned): Signal(LSC_Alignment_Not_Possible); Cancel(Emergency); return; } } Event(AERM_Start) { var(Ca) = 0; NewState(AERM,Active); } Event(AERM_Set_Ti_to_Tin) { var(Ti) = var(Tin); } Event(AERM_Set_Ti_to_Tie) { var(Ti) = var(Tie); } Event(AERM_Stop) { if ( CurrentState(AERM) != State(AERM,Active) ) return; var(Ti) = var(Tin); NewState(AERM,Idle); } Event(IAC_SIO) { switch ( CurrentState(IAC) ) { case State(IAC,Not_Aligned): Stop(T2); if ( Set(Emergency) ) { var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Signal(TXC_Send_SIE); } else { var(t4) = TIMER_T4NV; SetTimer(T4, T4NV); Signal(TXC_Send_SIN); } Start(T3, IAC_T3_Timeout); NewState(IAC,Aligned); return; case State(IAC,Proving): Stop(T4); Signal(AERM_Stop); Start(T3, IAC_T3_Timeout); NewState(IAC,Aligned); return; } } Event(IAC_SIOS) { switch ( CurrentState(IAC) ) { case State(IAC,Proving): Stop(T4); Signal(LSC_Alignment_Not_Possible); Signal(AERM_Stop); Cancel(Emergency); NewState(IAC,Idle); return; } } Event(LSC_SIOS) { switch ( CurrentState(LSC) ) { case State(LSC,Aligned_Ready): Stop(T1); Signal(L3_Out_Of_Service); Signal(RC_Stop); Signal(SUERM_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Not_Ready): Stop(T1); Signal(L3_Out_Of_Service); Signal(TXC_Send_SIOS); Signal(SUERM_Stop); Signal(RC_Stop); Cancel(Emergency); Cancel(Local_Processor_Outage); NewState(LSC,Out_of_Service); return; case State(LSC,In_Service): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Processor_Outage): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); Cancel(Remote_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(LSC_FISU_MSU_Received) { switch (CurrentState(LSC)) { case State(LSC,Aligned_Ready): Signal(L3_In_Service); Stop(T1); Signal(TXC_Send_MSU); NewState(LSC,In_Service); return; case State(LSC,Aligned_Not_Ready): Signal(L3_In_Service); Stop(T1); NewState(LSC,Processor_Outage); return; case State(LSC,Processor_Outage): /* * A modification has been placed here to avoid continual * indication to L3 on every FISU received until the system can * respond to the outage situation. A single indication is * sufficient. We have SU compression so it shouldn't be a * problem ;) */ #ifndef HAVE_SU_COMPRESSION if ( Set(Remote_Processor_Outage) ) { Cancel(Remote_Processor_Outage); #endif Signal(L3_Remote_Processor_Recovered); #ifndef HAVE_SU_COMPRESSION } #endif return; } } Event(LSC_SIPO) { switch ( CurrentState(LSC) ) { case State(LSC,Aligned_Ready): Stop(T1); Signal(L3_Remote_Processor_Outage); Mark(Remote_Processor_Outage); NewState(LSC,Processor_Outage); return; case State(LSC,Aligned_Not_Ready): Signal(L3_Remote_Processor_Outage); Mark(Remote_Processor_Outage); Stop(T1); NewState(LSC,Processor_Outage); return; case State(LSC,In_Service): Signal(TXC_Send_FISU); Signal(L3_Remote_Processor_Outage); Mark(Remote_Processor_Outage); Signal(RC_Align_FSNX); Signal(CC_Stop); NewState(LSC,Processor_Outage); return; case State(LSC,Processor_Outage): /* * A modification from the SDLs has been placed here to keep the * L2 state machine from delivering an indication to L3 for every * SIPO received. This should allow the system time to respond * to the condition before overflowing the service primitive * stack. A single indication should be sufficient. We have SU * compression so it shouldn't be a problem ;) */ #ifndef HAVE_SU_COMPRESSION if ( !Set(Remote_Processor_Outage) ) { Mark(Remote_Processor_Outage); #endif Signal(L3_Remote_Processor_Outage); #ifndef HAVE_SU_COMPRESSION } #endif return; } } Event(IAC_T2_Timeout) { Signal(LSC_Alignment_Not_Possible); Cancel(Emergency); NewState(IAC,Idle); return; } Event(LSC_T1_Timeout) { switch ( CurrentState(LSC) ) { case State(LSC,Aligned_Ready): Signal(L3_Out_Of_Service); Signal(RC_Stop); Signal(SUERM_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Not_Ready): Signal(L3_Out_Of_Service); Signal(TXC_Send_SIOS); Signal(SUERM_Stop); Signal(RC_Stop); Cancel(Emergency); Cancel(Local_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(SUERM_Start) { var(Cs) = 0; var(Ns) = 0; var(T) = 64; NewState(SUERM,Active); } Event(LSC_Alignment_Complete) { switch ( CurrentState(LSC) ) { case State(LSC,Initial_Alignment): Signal(SUERM_Start); Start(T1, LSC_T1_Timeout); if ( Set(Local_Processor_Outage) ) { Signal(TXC_Send_SIPO); Signal(RC_Reject_MSU_FISU); NewState(LSC,Aligned_Not_Ready); return; } else { Signal(TXC_Send_FISU); Signal(RC_Accept_MSU_FISU); NewState(LSC,Aligned_Ready); return; } } } Event(IAC_T4_Timeout) { switch ( CurrentState(IAC) ) { case State(IAC,Proving): if ( Set(Further_Proving) ) { Signal(AERM_Start); Cancel(Further_Proving); Start(T4, IAC_T4_Timeout); return; } else { Signal(LSC_Alignment_Complete); Signal(AERM_Stop); Cancel(Emergency); NewState(IAC,Idle); return; } } } Event(LSC_SIN) { switch ( CurrentState(LSC) ) { case State(LSC,In_Service): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Processor_Outage): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); Cancel(Remote_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(IAC_SIN) { switch ( CurrentState(IAC) ) { case State(IAC,Not_Aligned): Stop(T2); if ( Set(Emergency) ) { var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Signal(TXC_Send_SIE); } else { var(t4) = TIMER_T4NV; SetTimer(T4, T4NV); Signal(TXC_Send_SIN); } Start(T3, IAC_T3_Timeout); NewState(IAC,Aligned); return; case State(IAC,Aligned): Stop(T3); if ( var(t4) == TIMER_T4EV ) Signal(AERM_Set_Ti_to_Tie); Signal(AERM_Start); Start(T4, IAC_T4_Timeout); var(Cp) = 0; Cancel(Further_Proving); NewState(IAC,Proving); return; } } Event(LSC_SIE) { switch ( CurrentState(LSC) ) { case State(LSC,In_Service): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Processor_Outage): Signal(L3_Out_Of_Service); Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); Cancel(Remote_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(IAC_SIE) { switch ( CurrentState(IAC) ) { case State(IAC,Not_Aligned): Stop(T2); if ( Set(Emergency) ) { var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Signal(TXC_Send_SIE); } else { var(t4) = TIMER_T4EV; SetTimer(T4, T4NV); Signal(TXC_Send_SIN); } Start(T3, IAC_T3_Timeout); NewState(IAC,Aligned); return; case State(IAC,Aligned): var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Stop(T3); Signal(AERM_Set_Ti_to_Tie); Signal(AERM_Start); Start(T4, IAC_T4_Timeout); var(Cp) = 0; Cancel(Further_Proving); NewState(IAC,Proving); return; case State(IAC,Proving): if ( var(t4) == TIMER_T4EV ) return; Stop(T4); var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Signal(AERM_Stop); Signal(AERM_Set_Ti_to_Tie); Signal(AERM_Start); Cancel(Further_Proving); Start(T4, IAC_T4_Timeout); return; } } #define SS7_L2_UNB = 1; #define SS7_L2_UNF = 1; Event(RC_Signal_Unit) { if ( CurrentState(RC) != State(RC,Active) ) return; if ( ((var(rx.len))==1)||((var(rx.len))==2) ) { switch ( var(rx.sio) ) { case LSSU_SIO: { Signal(LSC_SIO); Signal(IAC_SIO); break; } case LSSU_SIN: { Signal(LSC_SIN); Signal(IAC_SIN); break; } case LSSU_SIE: { Signal(LSC_SIE); Signal(IAC_SIE); break; } case LSSU_SIOS: { Signal(LSC_SIOS); Signal(IAC_SIOS); break; } case LSSU_SIPO: { Signal(LSC_SIPO); break; } case LSSU_SIB: { Signal(TXC_SIB_Received); break; } } return; } if ( SN_OUTSIDE(((var(rx.F.fsn)-1)&0x7f), (var(rx.R.bsn)), var(rx.T.fsn)) ) { RB_DISCARD; if ( Set(Abnormal_BSNR) ) { Signal(LSC_Link_Failure); NewState(RC,Power_On); return; } else { Mark(Abnormal_BSNR); var(unb) = 0; return; } } if ( Set(Abnormal_BSNR) ) { if ( var(unb) == 1 ) { Cancel(Abnormal_BSNR); } else { var(unb) = 1; RB_DISCARD; return; } } if ( var(rx.R.fib) == var(rx.X.fib) ) { if ( Set(Abnormal_FIBR) ) { if ( var(unf) == 1 ) { Cancel(Abnormal_FIBR); } else { var(unf) = 1; RB_DISCARD; return; } } Signal(LSC_FISU_MSU_Received); Signal(TXC_BSNR_and_BIBR); var(rx.F.fsn) = (var(rx.R.bsn)+1)&0x7f; if ( !Set(MSU_FISU_Accepted) ) { RB_DISCARD; return; } Signal(RB_Congestion_Function); if ( Set(Congestion_Discard) ) { if ( var(rx.len) > 2 ) { var(rtr) = 1; } RB_DISCARD; Signal(CC_Busy); /* not really required anymore */ return; } if ( (var(rx.R.fsn) == var(rx.X.fsn)) && (var(rx.len) > 2) ) { var(rx.X.fsn) = (var(rx.X.fsn)+1)&0x7f; var(rtr) = 0; if ( Set(Congestion_Accept) ) { RB_WRITE; Signal(CC_Busy); /* not really required anymore */ } else { RB_WRITE; Signal(TXC_FSNX_Value); } return; } if ( (var(rx.R.fsn) == ((var(rx.X.fsn)-1)&0x7f)) ) { RB_DISCARD; return; } else { if ( Set(Congestion_Accept) ) { var(rtr) = 1; Signal(CC_Busy); /* not really required anymore */ RB_DISCARD; return; } else { Signal(TXC_NACK_To_Be_Sent); var(rtr) = 1; var(rx.X.fib) = var(rx.X.fib)?0x00:0x80; RB_DISCARD; return; } } } else { RB_DISCARD; if ( Set(Abnormal_FIBR) ) { Signal(LSC_Link_Failure); return; } if ( var(rtr) == 1 ) { Signal(TXC_BSNR_and_BIBR); var(rx.F.fsn) = (var(rx.R.bsn)+1)&0x7f; return; } Mark(Abnormal_FIBR); var(unf) = 0; return; } } Event(LSC_Stop) { switch ( CurrentState(LSC) ) { case State(LSC,Initial_Alignment): Signal(IAC_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Local_Processor_Outage); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Ready): Stop(T1); Signal(RC_Stop); Signal(SUERM_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Aligned_Not_Ready): Stop(T1); Signal(TXC_Send_SIOS); Signal(SUERM_Stop); Signal(RC_Stop); Cancel(Emergency); Cancel(Local_Processor_Outage); NewState(LSC,Out_of_Service); return; case State(LSC,In_Service): Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); NewState(LSC,Out_of_Service); return; case State(LSC,Processor_Outage): Signal(SUERM_Stop); Signal(RC_Stop); Signal(TXC_Send_SIOS); Cancel(Emergency); Cancel(Local_Processor_Outage); Cancel(Remote_Processor_Outage); NewState(LSC,Out_of_Service); return; } } Event(LSC_Clear_RTB) { switch ( CurrentState(LSC) ) { case State(LSC,Processor_Outage): Cancel(Local_Processor_Outage); Signal(TXC_Send_FISU); Signal(TXC_Clear_RTB); return; } } Event(IAC_Correct_SU) { switch ( CurrentState(IAC) ) { case State(IAC,Proving): if ( Set(Further_Proving) ) { Stop(T4); Signal(AERM_Start); Cancel(Further_Proving); Start(T4, IAC_T4_Timeout); return; } return; } } Event(IAC_Abort_Proving) { switch ( CurrentState(IAC) ) { case State(IAC,Proving): var(Cp)++; if ( var(Cp) == 5 ) { Signal(LSC_Alignment_Not_Possible); Stop(T4); Signal(AERM_Stop); Cancel(Emergency); NewState(IAC,Idle); return; } Mark(Further_Proving); return; } } Event(AERM_SU_In_Error) { if ( CurrentState(AERM) == State(AERM,Active) ) { var(Ca)++; if ( var(Ca) == var(Ti) ) { Signal(IAC_Abort_Proving); NewState(AERM,Idle); } } } Event(SUERM_SU_In_Error) { if ( CurrentState(SUERM) == State(SUERM,Active) ) { var(Cs)++; if ( var(Cs) == var(T) ) { Signal(LSC_Link_Failure); NewState(SUERM,Idle); return; } if ( var(Cs) ) { var(Ns)++; if ( var(Ns) == 256 ) { var(Cs)--; var(Ns) = 0; } } } } Event(SUERM_Correct_SU) { if ( CurrentState(SUERM) == State(SUERM,Active) ) { if ( var(Cs) ) { var(Ns)++; if ( var(Ns) == 256 ) { var(Cs)--; var(Ns) = 0; } } } } /* * When we are in octet counting mode we have to generate an error to the * SUERM/AERM every 16 octets from the line. At 64,000 bps, this is every * 2ms. Unfortunately, kernel timers are only accurate to 10ms. Therefore, * the SS7 device (which has a finer timing source) will have to generate the * pulses to us after being told to start octet counting. */ Event(DAEDR_16_Octets) { start_bh_atomic(); { if (p->DAEDR_state) while (var(Octets_Counted)) { var(Octets_Counted)--; if ( Set(Octet_Counting_Mode) ) { Signal(SUERM_SU_In_Error); Signal(AERM_SU_In_Error); } } else var(Octets_Counted) = 0; } end_bh_atomic(); } /* * Even if though the DAEDR may not be active, yet, we still have to track * the state of Octet Counting or we will get out of step with the driver. * Linux drivers are not so nice as to halt the receive while running the * transmit; i.e., there is no half-open command. */ Event(DAEDR_Loss_of_Sync) { Mark(Octet_Counting_Mode); } Event(DAEDR_Error_Frame) { start_bh_atomic(); { if ( p->DAEDR_state && !Set(Octet_Counting_Mode) ) { Signal(SUERM_SU_In_Error); Signal(AERM_SU_In_Error); } } end_bh_atomic(); } Event(DAEDR_Compressed_Frame) { start_bh_atomic(); { if (p->DAEDR_state) while (var(SUs_Repeated)) { var(SUs_Repeated)--; Signal(IAC_Correct_SU); Signal(SUERM_Correct_SU); } else var(SUs_Repeated) = 0; } end_bh_atomic(); } Event(DAEDR_Received_Frame) { struct sk_buff *skb; start_bh_atomic(); { while ((skb = skb_dequeue(&var(read_q)))) { if (p->DAEDR_state) { #if 0 unsigned int len = skb->len; /* * Note: the driver must check both of these * before we get here. */ if ( (len<3 || len>SS7_MTU) || ((len<64+3?len:63+3) != (var(rx.len) = skb->data[2]&0x3f))) { kfree_skb(skb); return Signal(DAEDR_Error_Frame); } else #endif { unsigned char* data = skb->data; var(rsu_skb) = skb; var(rx.R.bsn) = data[0] & 0x7f; var(rx.R.bib) = data[0] & 0x80; var(rx.R.fsn) = data[1] & 0x7f; var(rx.R.fib) = data[1] & 0x80; var(rx.len) = data[2] & 0x3f; var(rx.sio) = data[3] & 0x07; Cancel(Octet_Counting_Mode); Signal(RC_Signal_Unit); Signal(IAC_Correct_SU); Signal(SUERM_Correct_SU); } } else { Cancel(Octet_Counting_Mode); kfree_skb(skb); } } } end_bh_atomic(); } Event(LSC_Clear_Buffers) { switch ( CurrentState(LSC) ) { case State(LSC,Out_of_Service): Signal(L3_RTB_Cleared); return; case State(LSC,Initial_Alignment): Signal(L3_RTB_Cleared); Cancel(Local_Processor_Outage); return; case State(LSC,Aligned_Not_Ready): Signal(L3_RTB_Cleared); Cancel(Local_Processor_Outage); Signal(TXC_Send_FISU); NewState(LSC,Aligned_Ready); return; case State(LSC,Processor_Outage): Cancel(Local_Processor_Outage); Signal(RC_Clear_RB); Signal(RC_Accept_MSU_FISU); Signal(TXC_Send_FISU); Signal(TXC_Clear_TB); Signal(TXC_Clear_RTB); return; } } Event(LSC_Resume) { switch ( CurrentState(LSC) ) { case State(LSC,Out_of_Service): Cancel(Local_Processor_Outage); return; case State(LSC,Initial_Alignment): Cancel(Local_Processor_Outage); return; case State(LSC,Aligned_Ready): return; case State(LSC,Aligned_Not_Ready): Cancel(Local_Processor_Outage); Signal(TXC_Send_FISU); NewState(LSC,Aligned_Ready); return; case State(LSC,Processor_Outage): Cancel(Local_Processor_Outage); Signal(RC_Accept_MSU_FISU); if ( Set(Remote_Processor_Outage) ) { Signal(TXC_Send_FISU); Signal(L3_Remote_Processor_Outage); return; } Signal(TXC_Send_MSU); NewState(LSC,In_Service); return; } } Event(LSC_Local_Processor_Outage) { switch ( CurrentState(LSC) ) { case State(LSC,Out_of_Service): case State(LSC,Initial_Alignment): Mark(Local_Processor_Outage); return; case State(LSC,Aligned_Ready): Mark(Local_Processor_Outage); Signal(TXC_Send_SIPO); Signal(RC_Reject_MSU_FISU); NewState(LSC,Aligned_Not_Ready); return; case State(LSC,In_Service): Signal(TXC_Send_SIPO); Signal(RC_Reject_MSU_FISU); Mark(Local_Processor_Outage); Signal(RC_Align_FSNX); Signal(CC_Stop); NewState(LSC,Processor_Outage); return; case State(LSC,Processor_Outage): Mark(Local_Processor_Outage); Signal(TXC_Send_SIPO); return; } } Event(IAC_Emergency) { switch ( CurrentState(IAC) ) { case State(IAC,Proving): Signal(TXC_Send_SIE); Stop(T4); var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Signal(AERM_Stop); Signal(AERM_Set_Ti_to_Tie); Signal(AERM_Start); Cancel(Further_Proving); Start(T4, IAC_T4_Timeout); return; case State(IAC,Aligned): Signal(TXC_Send_SIE); var(t4) = TIMER_T4EV; SetTimer(T4, T4EV); Start(T4, IAC_T4_Timeout); return; default: Mark(Emergency); } } Event(LSC_Emergency) { switch ( CurrentState(LSC) ) { case State(LSC,Out_of_Service): Mark(Emergency); return; case State(LSC,Initial_Alignment): Mark(Emergency); Signal(IAC_Emergency); return; } } Event(LSC_Emergency_Ceases) { switch ( CurrentState(LSC) ) { case State(LSC,Out_of_Service): Cancel(Emergency); return; } } Event(IAC_Start) { Signal(TXC_Send_SIO); Start(T2, IAC_T2_Timeout); NewState(IAC,Not_Aligned); } Event(DAEDT_Start) /* * DAEDT_Start is responsible for preparing for transmission of the first * frame and starting the transmitters. The transmitters should be allowed * to idle flags until a call to TXC_Transmission_Request is invoked. * * The rest of the DAEDT state machine is embedded in the TxISR. */ { /* * Only do this once, if we are already running, don't start up again * unless we want to do a reset? This should be done at the ioctl() * level if we want to reset. * * Note that the transmitters are initialized on power-on. This means * that when the device driver is opened the transmitters will be set * up to send SIOS. The receivers will only be set up when LSC_Start() * has been called by L3. */ if ( CurrentState(DAEDT) == State(DAEDT,Active) ) return; /* * This should set up for the next frame (which is an SIOS). */ Signal(TXC_Transmission_Request); NewState(DAEDT,Active); return; } Event(DAEDT_Transmission_Request) { if ( CurrentState(DAEDT) == State(DAEDT,Active) ) Signal(TXC_Transmission_Request); } Event(TXC_Start) { /* * The transmitters and transmission control are only started once for * SS7, at power-on. The transmitters stay operational until the * device is physically powered down (or in our case, the device is * closed and the driver is removed). Transmitters stay blindly * sending SIOS when the LSC is stopped. */ if ( CurrentState(TXC) == State(TXC,Active) ) return; Cancel(RTB_Full); Cancel(SIB_Received); Cancel(Clear_RTB); var(tx.sio) = LSSU_SIOS; Mark(LSSU_Available); Cancel(MSU_Inhibited); var(tx.L.fsn) = var(tx.N.fsn) = 0x7f; var(tx.X.fsn) = 0x00; var(tx.N.fib) = var(tx.N.bib) = 0x80; var(tx.F.fsn) = 0x00; TB_INIT; Signal(DAEDT_Start); NewState(TXC,Active); } Event(LSC_Start) { if ( CurrentState(LSC) != State(LSC,Out_of_Service) ) return; Signal(RC_Start); Signal(TXC_Start); /* * This is rather redundant: transmission control and * the transmitters should have been started on * power-on. */ if ( Set(Emergency) ) Signal(IAC_Emergency); Signal(IAC_Start); NewState(LSC,Initial_Alignment); } Event(LSC_Retreive_BSNT) { Signal(RC_Retreive_BSNT); } Event(LSC_Retreival_Request_and_FSNC) { Signal(TXC_Retreival_Request_and_FSNC); } Event(LSC_Power_On) { /* * This power-on sequence should only be performed once, regardless of * how many times the device driver is opened or closed. This * initializes the transmitters to send SIOS and should never be * changed hence. */ if ( CurrentState(LSC) != State(LSC,Power_On) ) return; var(Tie) = AERM_TIE_VALUE; var(Tin) = AERM_TIN_VALUE; Signal(TXC_Start); Signal(TXC_Send_SIOS); Signal(AERM_Set_Ti_to_Tin); Cancel(Local_Processor_Outage); Cancel(Emergency); NewState(LSC,Out_of_Service); } /* * ------------------------------------------------------------------------ * * L2 Primitives: to L3 * * ------------------------------------------------------------------------ */ static void make_primitive(struct ss7_iface *p, unsigned char cmd, unsigned char *ptr, unsigned int size) { struct sk_buff *skb; if ((skb = alloc_skb(size+1, GFP_KERNEL)) == NULL) return; memcpy(skb->data+1, ptr, size); skb->data[0] = cmd; skb->dev = p->dev; skb->protocol = AF_SS7; skb->pkt_type = ETH_P_SS7; skb->priority = TC_PRIO_CONTROL; skb->mac.raw = skb->data; netif_rx(skb); if (netdev_dropping) { Signal(RC_Congestion_Discard); Mark(L2_Congestion_Detect); } } /* * ------------------------------------------------------------------------ * * L3 Primitives: from L3 * * ------------------------------------------------------------------------ */ /* * This is our transmission congestion check. We check the total buffer * occupancy and apply the necessary congestion control signal. * * Level Onset Abatement * ----------- ----------- ----------- * 0 N/A N/A * 1 1/2 1/3 * 2 3/4 2/3 * 3 Full Not Full * * This should really be rearranged to follow the documentation and should * include discard status. The threshold levels should be precalculated when * the buffers are allocated rather than here at run-time. Also, the same * check must be performed whenever frames are acknowledged and removed from * the retransmit buffer in RC_Signal_Unit or TXC_BSNR_and_BIBR. */ #define Q_MAX TB_Q_MAX_SIZE + RTB_Q_MAX_SIZE static __inline void check_congestion(struct ss7_iface *p) { unsigned int occupancy = skb_queue_len(&p->xmit_q) + skb_queue_len(&p->rexm_q); switch (p->cong_level) { /* is there a quicker way to do this? */ case 0: /* onset 1 */ if ( occupancy*2 >= Q_MAX*1 ) { p->cong_level = 1; ss7if_L3_Link_Congested(p); } /* onset 2 */ if ( occupancy*4 >= Q_MAX*3 ) { p->cong_level = 2; ss7if_L3_Link_Congested(p); } /* onset 3 */ if ( occupancy*1 >= Q_MAX*1 ) { p->cong_level = 3; ss7if_L3_Link_Congested(p); } break; case 1: /* onset 2 */ if ( occupancy*4 >= Q_MAX*3 ) { p->cong_level = 2; ss7if_L3_Link_Congested(p); } /* onset 3 */ if ( occupancy*1 >= Q_MAX*1 ) { p->cong_level = 3; ss7if_L3_Link_Congested(p); } /* abate 1 */ if ( occupancy*3 < Q_MAX*1 ) { p->cong_level = 0; ss7if_L3_Link_Congested(p); } break; case 2: /* onset 3 */ if ( occupancy*1 >= Q_MAX*1 ) { p->cong_level = 3; ss7if_L3_Link_Congested(p); } /* abate 2 */ if ( occupancy*3 < Q_MAX*2 ) { p->cong_level = 1; ss7if_L3_Link_Congested(p); } /* abate 1 */ if ( occupancy*3 < Q_MAX*1 ) { p->cong_level = 0; ss7if_L3_Link_Congested(p); } break; case 3: /* abate 3 */ if ( occupancy*1 < Q_MAX*1 ) { p->cong_level = 2; ss7if_L3_Link_Congested(p); } /* abate 2 */ if ( occupancy*3 < Q_MAX*2 ) { p->cong_level = 1; ss7if_L3_Link_Congested(p); } /* abate 1 */ if ( occupancy*3 < Q_MAX*1 ) { p->cong_level = 0; ss7if_L3_Link_Congested(p); } break; } } static void (*cmdjump[])(struct ss7_iface *) = { &ss7if_DAEDT_Transmitter_Wakeup, &ss7if_LSC_Emergency, &ss7if_LSC_Emergency_Ceases, &ss7if_LSC_Start, &ss7if_LSC_Stop, &ss7if_LSC_Retreive_BSNT, &ss7if_LSC_Retreival_Request_and_FSNC, &ss7if_LSC_Resume, &ss7if_LSC_Clear_Buffers, &ss7if_LSC_Local_Processor_Outage, &ss7if_LSC_Congestion_Discard, &ss7if_LSC_Congestion_Accept, &ss7if_LSC_No_Congestion }; #define SS7_HDRLEN_COMMAND 1 #define SS7_HDRLEN_L2 3 #define SS7_HDRLEN_L3 7 Event(LSC_L3_Primitive) { struct sk_buff *skb; start_bh_atomic(); { if ((skb = skb_dequeue(&p->writ_q))) { unsigned char cmd = skb->data[0]; if (cmd >= SS7_L2_LAST) { if (net_ratelimit()) printk(KERN_WARNING "SS7/LINK: %s received invalid command %d\n", p->dev->name,(int)cmd); kfree_skb(skb); return; } if (cmd == SS7_L2_PDU ) { check_congestion(p); if ( (((skb->data[3])>>4)&0x3) < p->cong_level ) { if (net_ratelimit()) printk(KERN_WARNING "SS7LINK: %s is discarding at congestion level %d\n", p->dev->name,p->cong_level); kfree_skb(skb); return; } skb_queue_tail(&p->xmit_q,skb); } cmdjump[cmd](p); } } end_bh_atomic(); } /* * TRANSMIT INTERFACE */ int ss7if_output(struct sk_buff *skb, struct device *dev) { struct ss7_iface *iface = dev->ss7_ptr; skb_queue_tail(&iface->writ_q,skb); ss7_sm_dispatch(&iface->tx_taskq,&ss7if_LSC_L3_Primitive); return 0; } /* * RECEIVE INTERRUPT SERVICE ROUTINE INTERFACE */ void ss7if_rx(struct sk_buff *skb) { struct ss7_iface *iface = skb->dev->ss7_ptr; skb_queue_tail(&iface->read_q,skb); ss7_sm_dispatch(&iface->rx_taskq,&ss7if_DAEDR_Received_Frame); } void ss7if_los(struct device *dev) { struct ss7_iface *iface = dev->ss7_ptr; ss7_sm_dispatch(&iface->rx_taskq,&ss7if_DAEDR_Loss_of_Sync); } void ss7if_err(struct device *dev) { struct ss7_iface *iface = dev->ss7_ptr; if (!iface->Octet_Counting_Mode) ss7_sm_dispatch(&iface->rx_taskq,&ss7if_DAEDR_Error_Frame); } void ss7if_oct(struct device *dev) { struct ss7_iface *iface = dev->ss7_ptr; if (iface->Octet_Counting_Mode) { iface->Octets_Counted++; ss7_sm_dispatch(&iface->rx_taskq,&ss7if_DAEDR_16_Octets); } } void ss7if_rpt(struct device *dev) { struct ss7_iface *iface = dev->ss7_ptr; iface->SUs_Repeated++; ss7_sm_dispatch(&iface->rx_taskq,&ss7if_DAEDR_Compressed_Frame); } #endif /* defined(CONFIG_SS7)||defined(CONFIG_SS7_MODULE) */