File: /usr/src/linux/net/802/llc_utility.c

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     
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     
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     
191     static void llc_p_timer_expired(unsigned long ulp)
192     {
193     	llc_timer_expired((llcptr) ulp, P_TIMER);
194     }
195     
196     static void llc_rej_timer_expired(unsigned long ulp)
197     {
198     	llc_timer_expired((llcptr) ulp, REJ_TIMER);
199     }
200     
201     static void llc_ack_timer_expired(unsigned long ulp)
202     {
203     	llc_timer_expired((llcptr) ulp, ACK_TIMER);
204     } 
205     
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     
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     
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     
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