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
53int 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
116void 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
143void 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
176void 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 IF_F=1_CLEAR_REMOTE_BUSY:
199				if ((type != I_CMD) || (fr->i_hdr.i_pflag == 0))
200					break;
201			case CLEAR_REMOTE_BUSY:
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 CONNECT_INDICATION:
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 CONNECT_CONFIRM:
224				lp->llc_mode = MODE_ABM;
225				lp->llc_callbacks|=LLC_CONN_CONFIRM;
226				break;
227			case DATA_INDICATION:
228				skb_pull(skb, 4);
229				lp->inc_skb=skb;
230				lp->llc_callbacks|=LLC_DATA_INDIC;
231				break;
232			case DISCONNECT_INDICATION:
233				lp->llc_mode = MODE_ADM;
234				lp->llc_callbacks|=LLC_DISC_INDICATION;
235				break;
236			case RESET_INDICATION(LOCAL):
237				lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
238				break;
239			case RESET_INDICATION(REMOTE):
240				lp->llc_callbacks|=LLC_RESET_INDIC_REM;
241				break;
242			case RESET_CONFIRM:
243				lp->llc_callbacks|=LLC_RST_CONFIRM;
244				break;
245			case REPORT_STATUS(FRMR_RECEIVED):
246				lp->llc_callbacks|=LLC_FRMR_RECV;
247				break;
248			case REPORT_STATUS(FRMR_SENT):
249				lp->llc_callbacks|=LLC_FRMR_SENT;
250				break;
251			case REPORT_STATUS(REMOTE_BUSY):
252				lp->llc_callbacks|=LLC_REMOTE_BUSY;
253				break;
254			case REPORT_STATUS(REMOTE_NOT_BUSY):
255				lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
256				break;
257			case SEND_DISC_CMD(P=X):
258				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
259				break;
260			case SEND_DM_RSP(F=X):
261				llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
262				break;
263			case SEND_FRMR_RSP(F=X):                        
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 RE-SEND_FRMR_RSP(F=0):
271				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
272				break;
273			case RE-SEND_FRMR_RSP(F=P):
274				llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
275					5, (char *) &lp->frmr_info_fld);
276				break;
277			case SEND_I_CMD(P=1):
278				llc_sendipdu(lp, I_CMD, 1, skb);   
279				break;
280			case RE-SEND_I_CMD(P=1):
281				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
282				break;
283			case RE-SEND_I_CMD(P=1)_OR_SEND_RR:
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 SEND_I_XXX(X=0):
291				llc_sendipdu(lp, I_CMD, 0, skb);   
292				break;
293			case RE-SEND_I_XXX(X=0):
294				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
295				break;
296			case RE-SEND_I_XXX(X=0)_OR_SEND_RR:
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 RE-SEND_I_RSP(F=1):
304				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
305				break;
306			case SEND_REJ_CMD(P=1):
307				llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
308				break;
309			case SEND_REJ_RSP(F=1):
310				llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
311				break;
312			case SEND_REJ_XXX(X=0):
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 SEND_RNR_CMD(F=1):
319				llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
320				break;
321			case SEND_RNR_RSP(F=1):
322				llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
323				break;
324			case SEND_RNR_XXX(X=0):
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 SET_REMOTE_BUSY:
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 OPTIONAL_SEND_RNR_XXX(X=0):
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 SEND_RR_CMD(P=1):
349				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
350				break;
351			case SEND_ACKNOWLEDGE_CMD(P=1):
352				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
353				break;
354			case SEND_RR_RSP(F=1):
355				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
356				break;
357			case SEND_ACKNOWLEDGE_RSP(F=1):
358				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
359				break;
360			case SEND_RR_XXX(X=0):
361				llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
362				break;
363			case SEND_ACKNOWLEDGE_XXX(X=0):
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 SEND_SABME_CMD(P=X):
370				llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
371				lp->f_flag = 0;
372				break;
373			case SEND_UA_RSP(F=X):
374				llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
375				break;
376			case S_FLAG:=0:
377				lp->s_flag = 0;
378				break;
379			case S_FLAG:=1:
380				lp->s_flag = 1;
381				break;
382			case START_P_TIMER:
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 START_ACK_TIMER_IF_NOT_RUNNING:
393				if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
394					llc_start_timer(lp, ACK_TIMER);
395				break;
396			case START_ACK_TIMER:
397				llc_start_timer(lp, ACK_TIMER);
398				break;
399			case START_REJ_TIMER:
400				llc_start_timer(lp, REJ_TIMER);
401				break;
402			case STOP_ACK_TIMER:
403				llc_stop_timer(lp, ACK_TIMER);
404				break;
405			case STOP_P_TIMER:
406				llc_stop_timer(lp, ACK_TIMER);
407				lp->p_flag = 0;
408				break;
409			case IF_DATA_FLAG=2_STOP_REJ_TIMER:
410				if (lp->data_flag == 2)
411					llc_stop_timer(lp, REJ_TIMER);
412				break;
413			case STOP_REJ_TIMER:
414				llc_stop_timer(lp, REJ_TIMER);
415				break;
416			case STOP_ALL_TIMERS:
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 STOP_OTHER_TIMERS:
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 UPDATE_N(R)_RECEIVED:             
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 UPDATE_P_FLAG:
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 DATA_FLAG:=2:
455				lp->data_flag = 2;
456				break;
457			case DATA_FLAG:=0:
458				lp->data_flag = 0;
459				break;
460			case DATA_FLAG:=1:
461				lp->data_flag = 1;
462				break;
463			case IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1:
464				if (lp->data_flag == 0)
465					lp->data_flag = 1;
466				break;
467			case P_FLAG:=0:
468				lp->p_flag = 0;
469				break;
470			case P_FLAG:=P:
471				lp->p_flag = lp->f_flag;
472				break;
473			case REMOTE_BUSY:=0:
474				lp->remote_busy = 0;
475				break;
476			case RETRY_COUNT:=0:
477				lp->retry_count = 0;
478				break;
479			case RETRY_COUNT:=RETRY_COUNT+1:
480				lp->retry_count++;
481				break;
482			case V(R):=0:
483				lp->vr = 0;
484				break;
485			case V(R):=V(R)+1:
486				lp->vr++;
487				break;
488			case V(S):=0:
489				lp->vs = 0;
490				break;
491			case V(S):=N(R):
492				lp->vs = fr->i_hdr.nr;
493				break;
494			case F_FLAG:=P:
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
512void 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 != NOP)
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
584void 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 != NOP)
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