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