1 /*
2 * NET An implementation of the IEEE 802.2 LLC protocol for the
3 * LINUX operating system. LLC is implemented as a set of
4 * state machines and callbacks for higher networking layers.
5 *
6 * Small utilities, Linux timer handling.
7 *
8 * Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
14 *
15 * Changes
16 * Alan Cox : Chainsawed into Linux form.
17 * Added llc_ function name prefixes.
18 * Fixed bug in stop/start timer.
19 * Added llc_cancel_timers for closing
20 * down an llc
21 */
22
23 #include <linux/types.h>
24 #include <linux/kernel.h>
25 #include <linux/netdevice.h>
26 #include <linux/skbuff.h>
27 #include <linux/proc_fs.h>
28 #include <linux/stat.h>
29 #include <net/llc_frame.h>
30 #include <net/llc.h>
31
llc_decode_frametype(frameptr fr)32 int llc_decode_frametype(frameptr fr)
33 {
34 if (IS_UFRAME(fr))
35 { /* unnumbered cmd/rsp */
36 switch(fr->u_mm.mm & 0x3B)
37 {
38 case 0x1B:
39 return(SABME_CMD);
40 break;
41 case 0x10:
42 return(DISC_CMD);
43 break;
44 case 0x18:
45 return(UA_RSP);
46 break;
47 case 0x03:
48 return(DM_RSP);
49 break;
50 case 0x21:
51 return(FRMR_RSP);
52 break;
53 case 0x00:
54 return(UI_CMD);
55 break;
56 case 0x2B:
57 if (IS_RSP(fr))
58 return(XID_RSP);
59 else
60 return(XID_CMD);
61 break;
62 case 0x38:
63 if (IS_RSP(fr))
64 return(TEST_RSP);
65 else
66 return(TEST_CMD);
67 break;
68 default:
69 return(BAD_FRAME);
70 }
71 }
72 else if (IS_SFRAME(fr))
73 { /* supervisory cmd/rsp */
74 switch(fr->s_hdr.ss)
75 {
76 case 0x00:
77 if (IS_RSP(fr))
78 return(RR_RSP);
79 else
80 return(RR_CMD);
81 break;
82 case 0x02:
83 if (IS_RSP(fr))
84 return(REJ_RSP);
85 else
86 return(REJ_CMD);
87 break;
88 case 0x01:
89 if (IS_RSP(fr))
90 return(RNR_RSP);
91 else
92 return(RNR_CMD);
93 break;
94 default:
95 return(BAD_FRAME);
96 }
97 }
98 else
99 { /* information xfer */
100 if (IS_RSP(fr))
101 return(I_RSP);
102 else
103 return(I_CMD);
104 }
105 }
106
107
108 /*
109 * Validate_seq_nos will check N(S) and N(R) to see if they are
110 * invalid or unexpected.
111 * "unexpected" is explained on p44 Send State Variable.
112 * The return value is:
113 * 4 * invalid N(R) +
114 * 2 * invalid N(S) +
115 * 1 * unexpected N(S)
116 */
117
llc_validate_seq_nos(llcptr lp,frameptr fr)118 int llc_validate_seq_nos(llcptr lp, frameptr fr)
119 {
120 int res;
121
122 /*
123 * A U-frame is always good
124 */
125
126 if (IS_UFRAME(fr))
127 return(0);
128
129 /*
130 * For S- and I-frames check N(R):
131 */
132
133 if (fr->i_hdr.nr == lp->vs)
134 { /* if N(R) = V(S) */
135 res = 0; /* N(R) is good */
136 }
137 else
138 { /* lp->k = transmit window size */
139 if (lp->vs >= lp->k)
140 { /* if window not wrapped around 127 */
141 if ((fr->i_hdr.nr < lp->vs) &&
142 (fr->i_hdr.nr > (lp->vs - lp->k)))
143 res = 0;
144 else
145 res = 4; /* N(R) invalid */
146 }
147 else
148 { /* window wraps around 127 */
149 if ((fr->i_hdr.nr < lp->vs) ||
150 (fr->i_hdr.nr > (128 + lp->vs - lp->k)))
151 res = 0;
152 else
153 res = 4; /* N(R) invalid */
154 }
155 }
156
157 /*
158 * For an I-frame, must check N(S) also:
159 */
160
161 if (IS_IFRAME(fr))
162 {
163 if (fr->i_hdr.ns == lp->vr)
164 return res; /* N(S) good */
165 if (lp->vr >= lp->rw)
166 {
167 /* if receive window not wrapped */
168
169 if ((fr->i_hdr.ns < lp->vr) &&
170 (fr->i_hdr.ns > (lp->vr - lp->k)))
171 res = res +1; /* N(S) unexpected */
172 else
173 res = res +2; /* N(S) invalid */
174 }
175 else
176 {
177 /* Window wraps around 127 */
178
179 if ((fr->i_hdr.ns < lp->vr) ||
180 (fr->i_hdr.ns > (128 + lp->vr - lp->k)))
181 res = res +1; /* N(S) unexpected */
182 else
183 res = res +2; /* N(S) invalid */
184 }
185 }
186 return(res);
187 }
188
189 /* **************** timer management routines ********************* */
190
llc_p_timer_expired(unsigned long ulp)191 static void llc_p_timer_expired(unsigned long ulp)
192 {
193 llc_timer_expired((llcptr) ulp, P_TIMER);
194 }
195
llc_rej_timer_expired(unsigned long ulp)196 static void llc_rej_timer_expired(unsigned long ulp)
197 {
198 llc_timer_expired((llcptr) ulp, REJ_TIMER);
199 }
200
llc_ack_timer_expired(unsigned long ulp)201 static void llc_ack_timer_expired(unsigned long ulp)
202 {
203 llc_timer_expired((llcptr) ulp, ACK_TIMER);
204 }
205
llc_busy_timer_expired(unsigned long ulp)206 static void llc_busy_timer_expired(unsigned long ulp)
207 {
208 llc_timer_expired((llcptr) ulp, BUSY_TIMER);
209 }
210
211 /* exp_fcn is an array holding the 4 entry points of the
212 timer expiry routines above.
213 It is required to keep start_timer() generic.
214 Thank you cdecl.
215 */
216
217 static void (* exp_fcn[])(unsigned long) =
218 {
219 llc_p_timer_expired,
220 llc_rej_timer_expired,
221 llc_ack_timer_expired,
222 llc_busy_timer_expired
223 };
224
llc_start_timer(llcptr lp,int t)225 void llc_start_timer(llcptr lp, int t)
226 {
227 if (lp->timer_state[t] == TIMER_IDLE)
228 {
229 lp->tl[t].expires = jiffies + lp->timer_interval[t];
230 lp->tl[t].data = (unsigned long) lp;
231 lp->tl[t].function = exp_fcn[t];
232 add_timer(&lp->tl[t]);
233 lp->timer_state[t] = TIMER_RUNNING;
234 }
235 }
236
llc_stop_timer(llcptr lp,int t)237 void llc_stop_timer(llcptr lp, int t)
238 {
239 if (lp->timer_state[t] == TIMER_RUNNING)
240 {
241 del_timer(&lp->tl[t]);
242 lp->timer_state[t] = TIMER_IDLE;
243 }
244 }
245
llc_cancel_timers(llcptr lp)246 void llc_cancel_timers(llcptr lp)
247 {
248 llc_stop_timer(lp, P_TIMER);
249 llc_stop_timer(lp, REJ_TIMER);
250 llc_stop_timer(lp, ACK_TIMER);
251 llc_stop_timer(lp, BUSY_TIMER);
252 }
253
254