diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/802/llc_utility.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/802/llc_utility.c')
-rw-r--r-- | net/802/llc_utility.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/net/802/llc_utility.c b/net/802/llc_utility.c new file mode 100644 index 000000000..d0a58018f --- /dev/null +++ b/net/802/llc_utility.c @@ -0,0 +1,253 @@ +/* + * NET An implementation of the IEEE 802.2 LLC protocol for the + * LINUX operating system. LLC is implemented as a set of + * state machines and callbacks for higher networking layers. + * + * Small utilities, Linux timer handling. + * + * Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com + * + * 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. + * + * Changes + * Alan Cox : Chainsawed into Linux form. + * Added llc_ function name prefixes. + * Fixed bug in stop/start timer. + * Added llc_cancel_timers for closing + * down an llc + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> +#include <net/llc_frame.h> +#include <net/llc.h> + +int llc_decode_frametype(frameptr fr) +{ + if (IS_UFRAME(fr)) + { /* unnumbered cmd/rsp */ + switch(fr->u_mm.mm & 0x3B) + { + case 0x1B: + return(SABME_CMD); + break; + case 0x10: + return(DISC_CMD); + break; + case 0x18: + return(UA_RSP); + break; + case 0x03: + return(DM_RSP); + break; + case 0x21: + return(FRMR_RSP); + break; + case 0x00: + return(UI_CMD); + break; + case 0x2B: + if (IS_RSP(fr)) + return(XID_RSP); + else + return(XID_CMD); + break; + case 0x38: + if (IS_RSP(fr)) + return(TEST_RSP); + else + return(TEST_CMD); + break; + default: + return(BAD_FRAME); + } + } + else if (IS_SFRAME(fr)) + { /* supervisory cmd/rsp */ + switch(fr->s_hdr.ss) + { + case 0x00: + if (IS_RSP(fr)) + return(RR_RSP); + else + return(RR_CMD); + break; + case 0x02: + if (IS_RSP(fr)) + return(REJ_RSP); + else + return(REJ_CMD); + break; + case 0x01: + if (IS_RSP(fr)) + return(RNR_RSP); + else + return(RNR_CMD); + break; + default: + return(BAD_FRAME); + } + } + else + { /* information xfer */ + if (IS_RSP(fr)) + return(I_RSP); + else + return(I_CMD); + } +} + + +/* + * Validate_seq_nos will check N(S) and N(R) to see if they are + * invalid or unexpected. + * "unexpected" is explained on p44 Send State Variable. + * The return value is: + * 4 * invalid N(R) + + * 2 * invalid N(S) + + * 1 * unexpected N(S) + */ + +int llc_validate_seq_nos(llcptr lp, frameptr fr) +{ + int res; + + /* + * A U-frame is always good + */ + + if (IS_UFRAME(fr)) + return(0); + + /* + * For S- and I-frames check N(R): + */ + + if (fr->i_hdr.nr == lp->vs) + { /* if N(R) = V(S) */ + res = 0; /* N(R) is good */ + } + else + { /* lp->k = transmit window size */ + if (lp->vs >= lp->k) + { /* if window not wrapped around 127 */ + if ((fr->i_hdr.nr < lp->vs) && + (fr->i_hdr.nr > (lp->vs - lp->k))) + res = 0; + else + res = 4; /* N(R) invalid */ + } + else + { /* window wraps around 127 */ + if ((fr->i_hdr.nr < lp->vs) || + (fr->i_hdr.nr > (128 + lp->vs - lp->k))) + res = 0; + else + res = 4; /* N(R) invalid */ + } + } + + /* + * For an I-frame, must check N(S) also: + */ + + if (IS_IFRAME(fr)) + { + if (fr->i_hdr.ns == lp->vr) + return res; /* N(S) good */ + if (lp->vr >= lp->rw) + { + /* if receive window not wrapped */ + + if ((fr->i_hdr.ns < lp->vr) && + (fr->i_hdr.ns > (lp->vr - lp->k))) + res = res +1; /* N(S) unexpected */ + else + res = res +2; /* N(S) invalid */ + } + else + { + /* Window wraps around 127 */ + + if ((fr->i_hdr.ns < lp->vr) || + (fr->i_hdr.ns > (128 + lp->vr - lp->k))) + res = res +1; /* N(S) unexpected */ + else + res = res +2; /* N(S) invalid */ + } + } + return(res); +} + +/* **************** timer management routines ********************* */ + +static void llc_p_timer_expired(unsigned long ulp) +{ + llc_timer_expired((llcptr) ulp, P_TIMER); +} + +static void llc_rej_timer_expired(unsigned long ulp) +{ + llc_timer_expired((llcptr) ulp, REJ_TIMER); +} + +static void llc_ack_timer_expired(unsigned long ulp) +{ + llc_timer_expired((llcptr) ulp, ACK_TIMER); +} + +static void llc_busy_timer_expired(unsigned long ulp) +{ + llc_timer_expired((llcptr) ulp, BUSY_TIMER); +} + +/* exp_fcn is an array holding the 4 entry points of the + timer expiry routines above. + It is required to keep start_timer() generic. + Thank you cdecl. + */ + +static void (* exp_fcn[])(unsigned long) = +{ + llc_p_timer_expired, + llc_rej_timer_expired, + llc_ack_timer_expired, + llc_busy_timer_expired +}; + +void llc_start_timer(llcptr lp, int t) +{ + if (lp->timer_state[t] == TIMER_IDLE) + { + lp->tl[t].expires = jiffies + lp->timer_interval[t]; + lp->tl[t].data = (unsigned long) lp; + lp->tl[t].function = exp_fcn[t]; + add_timer(&lp->tl[t]); + lp->timer_state[t] = TIMER_RUNNING; + } +} + +void llc_stop_timer(llcptr lp, int t) +{ + if (lp->timer_state[t] == TIMER_RUNNING) + { + del_timer(&lp->tl[t]); + lp->timer_state[t] = TIMER_IDLE; + } +} + +void llc_cancel_timers(llcptr lp) +{ + llc_stop_timer(lp, P_TIMER); + llc_stop_timer(lp, REJ_TIMER); + llc_stop_timer(lp, ACK_TIMER); + llc_stop_timer(lp, BUSY_TIMER); +} + |