summaryrefslogtreecommitdiffstats
path: root/net/802/llc_utility.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/802/llc_utility.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/802/llc_utility.c')
-rw-r--r--net/802/llc_utility.c253
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);
+}
+