/* * 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 #include #include #include #include #include #include #include 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); }