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  *		Class 2 llc algorithm.
7  *		Pseudocode interpreter, transition table lookup,
8  *			data_request & indicate primitives...
9  *
10  *		Code for initialization, termination, registration and
11  *		MAC layer glue.
12  *
13  *		Copyright Tim Alpaerts,
14  *			<Tim_Alpaerts@toyota-motor-europe.com>
15  *
16  *		This program is free software; you can redistribute it and/or
17  *		modify it under the terms of the GNU General Public License
18  *		as published by the Free Software Foundation; either version
19  *		2 of the License, or (at your option) any later version.
20  *
21  *	Changes
22  *		Alan Cox	:	Chainsawed into Linux format
23  *					Modified to use llc_ names
24  *					Changed callbacks
25  *
26  *	This file must be processed by sed before it can be compiled.
27  */
28 
29 #include <linux/types.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/netdevice.h>
33 #include <linux/skbuff.h>
34 #include <net/p8022.h>
35 #include <linux/proc_fs.h>
36 #include <linux/stat.h>
37 #include <asm/byteorder.h>
38 
39 #include "pseudo/pseudocode.h"
40 #include "transit/pdutr.h"
41 #include "transit/timertr.h"
42 #include <net/llc_frame.h>
43 #include <net/llc.h>
44 
45 /*
46  *	Data_request() is called by the client to present a data unit
47  *	to the llc for transmission.
48  *	In the future this function should also check if the transmit window
49  *	allows the sending of another pdu, and if not put the skb on the atq
50  *	for deferred sending.
51  */
52 
llc_data_request(llcptr lp,struct sk_buff * skb)53 int llc_data_request(llcptr lp, struct sk_buff *skb)
54 {
55 	if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
56 		printk("cl2llc: data_request() not enough headroom in skb\n");
57 		return -1;
58 	};
59 
60 	skb_push(skb, 4);
61 
62 	if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
63 	{
64 		printk("cl2llc: data_request() while no llc connection\n");
65 		return -1;
66 	}
67 
68 	if (lp->remote_busy)
69 	{     /* if the remote llc is BUSY, */
70 		ADD_TO_ATQ(skb);      /* save skb in the await transmit queue */
71 		return 0;
72 	}
73 	else
74 	{
75 		/*
76 		 *	Else proceed with xmit
77 		 */
78 
79 		switch(lp->state)
80 		{
81 			case NORMAL:
82 				if(lp->p_flag)
83 					llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
84 				else
85 					llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
86 				break;
87 			case BUSY:
88 				if (lp->p_flag)
89 					llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
90 				else
91 					llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
92 				break;
93 			case REJECT:
94 				if (lp->p_flag)
95 					llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
96 				else
97 					llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
98 				break;
99 			default:;
100 		}
101 		if(lp->llc_callbacks)
102 		{
103 			lp->llc_event(lp);
104 			lp->llc_callbacks=0;
105 		}
106 		return 0;
107 	}
108 }
109 
110 
111 
112 /*
113  *	Disconnect_request() requests that the llc to terminate a connection
114  */
115 
disconnect_request(llcptr lp)116 void disconnect_request(llcptr lp)
117 {
118 	if ((lp->state == NORMAL) ||
119     		(lp->state == BUSY) ||
120 		(lp->state == REJECT) ||
121 		(lp->state == AWAIT) ||
122 		(lp->state == AWAIT_BUSY) ||
123 		(lp->state == AWAIT_REJECT))
124 	{
125 		lp->state = D_CONN;
126 		llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
127 		if(lp->llc_callbacks)
128 		{
129 			lp->llc_event(lp);
130 			lp->llc_callbacks=0;
131 		}
132 		/*
133  		 *	lp may be invalid after the callback
134 		 */
135 	}
136 }
137 
138 
139 /*
140  *	Connect_request() requests that the llc to start a connection
141  */
142 
connect_request(llcptr lp)143 void connect_request(llcptr lp)
144 {
145 	if (lp->state == ADM)
146 	{
147 		lp->state = SETUP;
148 		llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
149 		if(lp->llc_callbacks)
150 		{
151 			lp->llc_event(lp);
152 			lp->llc_callbacks=0;
153 		}
154 		/*
155  		 *	lp may be invalid after the callback
156 		 */
157 	}
158 }
159 
160 
161 /*
162  *	Interpret_pseudo_code() executes the actions in the connection component
163  *	state transition table. Table 4 in document on p88.
164  *
165  *	If this function is called to handle an incoming pdu, skb will point
166  *	to the buffer with the pdu and type will contain the decoded pdu type.
167  *
168  *	If called by data_request skb points to an skb that was skb_alloc-ed by
169  *	the llc client to hold the information unit to be transmitted, there is
170  *	no valid type in this case.
171  *
172  *	If called because a timer expired no skb is passed, and there is no
173  *	type.
174  */
175 
llc_interpret_pseudo_code(llcptr lp,int pc_label,struct sk_buff * skb,char type)176 void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb,
177 		char type)
178 {
179 	short int pc;	/* program counter in pseudo code array */
180 	char p_flag_received;
181 	frameptr fr;
182 	int resend_count;   /* number of pdus resend by llc_resend_ipdu() */
183 	int ack_count;      /* number of pdus acknowledged */
184 	struct sk_buff *skb2;
185 
186 	if (skb != NULL)
187 	{
188 		fr = (frameptr) skb->data;
189 	}
190 	else
191 		fr = NULL;
192 
193 	pc = pseudo_code_idx[pc_label];
194 	while(pseudo_code[pc])
195 	{
196 		switch(pseudo_code[pc])
197 		{
198 			case 9:
199 				if ((type != I_CMD) || (fr->i_hdr.i_pflag == 0))
200 					break;
201 			case 1:
202 				lp->remote_busy = 0;
203 				llc_stop_timer(lp, BUSY_TIMER);
204 				if ((lp->state == NORMAL) ||
205 					(lp->state == REJECT) ||
206 					(lp->state == BUSY))
207 				{
208 					skb2 = llc_pull_from_atq(lp);
209 					if (skb2 != NULL)
210 						llc_start_timer(lp, ACK_TIMER);
211 					while (skb2 != NULL)
212 					{
213 						llc_sendipdu( lp, I_CMD, 0, skb2);
214 						skb2 = llc_pull_from_atq(lp);
215 					}
216 				}
217 				break;
218 			case 2:
219 				lp->state = NORMAL;  /* needed to eliminate connect_response() */
220 				lp->llc_mode = MODE_ABM;
221 				lp->llc_callbacks|=LLC_CONN_INDICATION;
222 				break;
223 			case 3:
224 				lp->llc_mode = MODE_ABM;
225 				lp->llc_callbacks|=LLC_CONN_CONFIRM;
226 				break;
227 			case 4:
228 				skb_pull(skb, 4);
229 				lp->inc_skb=skb;
230 				lp->llc_callbacks|=LLC_DATA_INDIC;
231 				break;
232 			case 5:
233 				lp->llc_mode = MODE_ADM;
234 				lp->llc_callbacks|=LLC_DISC_INDICATION;
235 				break;
236 			case 70:
237 				lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
238 				break;
239 			case 71:
240 				lp->llc_callbacks|=LLC_RESET_INDIC_REM;
241 				break;
242 			case 7:
243 				lp->llc_callbacks|=LLC_RST_CONFIRM;
244 				break;
245 			case 66:
246 				lp->llc_callbacks|=LLC_FRMR_RECV;
247 				break;
248 			case 67:
249 				lp->llc_callbacks|=LLC_FRMR_SENT;
250 				break;
251 			case 68:
252 				lp->llc_callbacks|=LLC_REMOTE_BUSY;
253 				break;
254 			case 69:
255 				lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
256 				break;
257 			case 11:
258 				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
259 				break;
260 			case 12:
261 				llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
262 				break;
263 			case 13:
264 				lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
265 				lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
266 				lp->frmr_info_fld.vs = lp->vs;
267 				lp->frmr_info_fld.vr_cr = lp->vr;
268 				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
269 				break;
270 			case 14:
271 				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
272 				break;
273 			case 15:
274 				llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
275 					5, (char *) &lp->frmr_info_fld);
276 				break;
277 			case 16:
278 				llc_sendipdu(lp, I_CMD, 1, skb);
279 				break;
280 			case 17:
281 				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
282 				break;
283 			case 18:
284 				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
285 				if (resend_count == 0)
286 				{
287 					llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
288 				}
289 				break;
290 			case 19:
291 				llc_sendipdu(lp, I_CMD, 0, skb);
292 				break;
293 			case 20:
294 				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
295 				break;
296 			case 21:
297 				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
298 				if (resend_count == 0)
299 				{
300 					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
301 				}
302 				break;
303 			case 22:
304 				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
305 				break;
306 			case 23:
307 				llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
308 				break;
309 			case 24:
310 				llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
311 				break;
312 			case 25:
313 				if (IS_RSP(fr))
314 					llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
315 				else
316 					llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
317 				break;
318 			case 26:
319 				llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
320 				break;
321 			case 27:
322 				llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
323 				break;
324 			case 28:
325 				if (IS_RSP(fr))
326 					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
327 				else
328 					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
329 				break;
330 			case 29:
331 				if (lp->remote_busy == 0)
332 				{
333 					lp->remote_busy = 1;
334 					llc_start_timer(lp, BUSY_TIMER);
335 					lp->llc_callbacks|=LLC_REMOTE_BUSY;
336 				}
337 				else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
338 				{
339 					llc_start_timer(lp, BUSY_TIMER);
340 				}
341 				break;
342 			case 30:
343 				if (IS_RSP(fr))
344 					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
345 				else
346 					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
347 				break;
348 			case 31:
349 				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
350 				break;
351 			case 32:
352 				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
353 				break;
354 			case 33:
355 				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
356 				break;
357 			case 34:
358 				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
359 				break;
360 			case 35:
361 				llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
362 				break;
363 			case 36:
364 				if (IS_RSP(fr))
365 					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
366 				else
367 					llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
368 				break;
369 			case 37:
370 				llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
371 				lp->f_flag = 0;
372 				break;
373 			case 38:
374 				llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
375 				break;
376 			case 39:
377 				lp->s_flag = 0;
378 				break;
379 			case 40:
380 				lp->s_flag = 1;
381 				break;
382 			case 41:
383 				if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
384 					llc_stop_timer(lp, P_TIMER);
385 				llc_start_timer(lp, P_TIMER);
386 				if (lp->p_flag == 0)
387 				{
388 					lp->retry_count = 0;
389 					lp->p_flag = 1;
390 				}
391 				break;
392 			case 44:
393 				if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
394 					llc_start_timer(lp, ACK_TIMER);
395 				break;
396 			case 42:
397 				llc_start_timer(lp, ACK_TIMER);
398 				break;
399 			case 43:
400 				llc_start_timer(lp, REJ_TIMER);
401 				break;
402 			case 45:
403 				llc_stop_timer(lp, ACK_TIMER);
404 				break;
405 			case 46:
406 				llc_stop_timer(lp, ACK_TIMER);
407 				lp->p_flag = 0;
408 				break;
409 			case 10:
410 				if (lp->data_flag == 2)
411 					llc_stop_timer(lp, REJ_TIMER);
412 				break;
413 			case 47:
414 				llc_stop_timer(lp, REJ_TIMER);
415 				break;
416 			case 48:
417 				llc_stop_timer(lp, ACK_TIMER);
418 				llc_stop_timer(lp, P_TIMER);
419 				llc_stop_timer(lp, REJ_TIMER);
420 				llc_stop_timer(lp, BUSY_TIMER);
421 				break;
422 			case 49:
423 				llc_stop_timer(lp, P_TIMER);
424 				llc_stop_timer(lp, REJ_TIMER);
425 				llc_stop_timer(lp, BUSY_TIMER);
426 				break;
427 			case 50:
428 				ack_count = llc_free_acknowledged_skbs(lp,
429 					(unsigned char) fr->s_hdr.nr);
430 				if (ack_count > 0)
431 				{
432 					lp->retry_count = 0;
433 					llc_stop_timer(lp, ACK_TIMER);
434 					if (skb_peek(&lp->rtq) != NULL)
435 					{
436 						/*
437  						 *	Re-transmit queue not empty
438 						 */
439 						llc_start_timer(lp, ACK_TIMER);
440 					}
441 				}
442 				break;
443 			case 51:
444 				if (IS_UFRAME(fr))
445 					p_flag_received = fr->u_hdr.u_pflag;
446 				else
447 					p_flag_received = fr->i_hdr.i_pflag;
448 				if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
449 				{
450 					lp->p_flag = 0;
451 					llc_stop_timer(lp, P_TIMER);
452 				}
453 				break;
454 			case 52:
455 				lp->data_flag = 2;
456 				break;
457 			case 53:
458 				lp->data_flag = 0;
459 				break;
460 			case 54:
461 				lp->data_flag = 1;
462 				break;
463 			case 55:
464 				if (lp->data_flag == 0)
465 					lp->data_flag = 1;
466 				break;
467 			case 56:
468 				lp->p_flag = 0;
469 				break;
470 			case 57:
471 				lp->p_flag = lp->f_flag;
472 				break;
473 			case 58:
474 				lp->remote_busy = 0;
475 				break;
476 			case 59:
477 				lp->retry_count = 0;
478 				break;
479 			case 60:
480 				lp->retry_count++;
481 				break;
482 			case 61:
483 				lp->vr = 0;
484 				break;
485 			case 62:
486 				lp->vr++;
487 				break;
488 			case 63:
489 				lp->vs = 0;
490 				break;
491 			case 64:
492 				lp->vs = fr->i_hdr.nr;
493 				break;
494 			case 65:
495 				if (IS_UFRAME(fr))
496 					lp->f_flag = fr->u_hdr.u_pflag;
497 				else
498 					lp->f_flag = fr->i_hdr.i_pflag;
499 				break;
500 			default:;
501 		}
502 		pc++;
503 	}
504 }
505 
506 
507 /*
508  *	Process_otype2_frame will handle incoming frames
509  *	for 802.2 Type 2 Procedure.
510  */
511 
llc_process_otype2_frame(llcptr lp,struct sk_buff * skb,char type)512 void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
513 {
514 	int idx;		/*	index in transition table */
515 	int pc_label;		/*	action to perform, from tr tbl */
516 	int validation;		/*	result of validate_seq_nos */
517 	int p_flag_received;	/*	p_flag in received frame */
518 	frameptr fr;
519 
520 	fr = (frameptr) skb->data;
521 
522 	if (IS_UFRAME(fr))
523 		p_flag_received = fr->u_hdr.u_pflag;
524 	else
525 		p_flag_received = fr->i_hdr.i_pflag;
526 
527 	switch(lp->state)
528 	{
529 		/*	Compute index in transition table: */
530 		case ADM:
531 			idx = type;
532 			idx = (idx << 1) + p_flag_received;
533 			break;
534 		case CONN:
535 		case RESET_WAIT:
536 		case RESET_CHECK:
537 		case ERROR:
538 			idx = type;
539 			break;
540 		case SETUP:
541 		case RESET:
542 		case D_CONN:
543 			idx = type;
544 			idx = (idx << 1) + lp->p_flag;
545 			break;
546 		case NORMAL:
547 		case BUSY:
548 		case REJECT:
549 		case AWAIT:
550 		case AWAIT_BUSY:
551 		case AWAIT_REJECT:
552 			validation = llc_validate_seq_nos(lp, fr);
553 			if (validation > 3)
554 				type = BAD_FRAME;
555 			idx = type;
556 			idx = (idx << 1);
557 			if (validation & 1)
558 				idx = idx +1;
559 			idx = (idx << 1) + p_flag_received;
560 			idx = (idx << 1) + lp->p_flag;
561 		default:
562 			printk("llc_proc: bad state\n");
563 			return;
564 	}
565 	idx = (idx << 1) + pdutr_offset[lp->state];
566 	lp->state = pdutr_entry[idx +1];
567 	pc_label = pdutr_entry[idx];
568 	if (pc_label != 0)
569 	{
570 		llc_interpret_pseudo_code(lp, pc_label, skb, type);
571 		if(lp->llc_callbacks)
572 		{
573 			lp->llc_event(lp);
574 			lp->llc_callbacks=0;
575 		}
576 		/*
577  		 *	lp may no longer be valid after this point. Be
578 		 *	careful what is added!
579 		 */
580 	}
581 }
582 
583 
llc_timer_expired(llcptr lp,int t)584 void llc_timer_expired(llcptr lp, int t)
585 {
586 	int idx;		/* index in transition table	*/
587 	int pc_label;       	/* action to perform, from tr tbl */
588 
589 	lp->timer_state[t] = TIMER_EXPIRED;
590 	idx = lp->state;            /* Compute index in transition table: */
591 	idx = (idx << 2) + t;
592 	idx = idx << 1;
593 	if (lp->retry_count >= lp->n2)
594 		idx = idx + 1;
595 	idx = (idx << 1) + lp->s_flag;
596 	idx = (idx << 1) + lp->p_flag;
597 	idx = idx << 1;             /* 2 bytes per entry: action & newstate */
598 
599 	pc_label = timertr_entry[idx];
600 	if (pc_label != 0)
601 	{
602 		llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
603 		lp->state = timertr_entry[idx +1];
604 	}
605 	lp->timer_state[t] = TIMER_IDLE;
606 	if(lp->llc_callbacks)
607 	{
608 		lp->llc_event(lp);
609 		lp->llc_callbacks=0;
610 	}
611 	/*
612  	 *	And lp may have vanished in the event callback
613  	 */
614 }
615 
616