patch-2.1.15 linux/net/802/cl2llc.pre

Next file: linux/net/802/llc.c
Previous file: linux/net/802/cl2llc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.14/linux/net/802/cl2llc.pre linux/net/802/cl2llc.pre
@@ -0,0 +1,589 @@
+/*
+ * NET		An implementation of the IEEE 802.2 LLC protocol for the
+ *		LINUX operating system.  LLC is implemented as a set of 
+ *		state machines and callbacks for higher networking layers.
+ *
+ *		Class 2 llc algorithm.
+ *		Pseudocode interpreter, transition table lookup,
+ *			data_request & indicate primitives...
+ *
+ *		Code for initialization, termination, registration and 
+ *		MAC layer glue.
+ *
+ *		Copyright Tim Alpaerts, 
+ *			<Tim_Alpaerts@toyota-motor-europe.com>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	Changes
+ *		Alan Cox	:	Chainsawed into Linux format
+ *					Modified to use llc_ names
+ *
+ *	This file must be processed by sed before it can be compiled.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+
+#include "pseudo/pseudocode.h"
+#include "transit/pdutr.h"
+#include "transit/timertr.h"
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ *	Data_request() is called by the client to present a data unit
+ *	to the llc for transmission.
+ *	In the future this function should also check if the transmit window
+ *	allows the sending of another pdu, and if not put the skb on the atq
+ *	for deferred sending.
+ */
+
+int llc_data_request(llcptr lp, struct sk_buff *skb)
+{
+	if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
+		printk("cl2llc: data_request() not enough headroom in skb\n");
+		return -1;
+	};
+
+	skb_push(skb, 4);
+
+	if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
+	{
+		printk("cl2llc: data_request() while no llc connection\n"); 
+		return -1;  
+	}
+
+	if (lp->remote_busy)
+	{     /* if the remote llc is BUSY, */
+		ADD_TO_ATQ(skb);      /* save skb in the await transmit queue */
+		return 0;
+	}                           
+	else
+	{
+		/*
+		 *	Else proceed with xmit 
+		 */
+
+		switch(lp->state)
+		{
+			case NORMAL:
+				if(lp->p_flag)
+					llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
+				break;
+			case BUSY:
+				if (lp->p_flag)
+					llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
+				break;
+			case REJECT:
+				if (lp->p_flag)
+					llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
+				break;
+			default:
+		}
+		return 0;  
+	}              
+}
+
+
+
+/* 
+ *	Disconnect_request() requests that the llc to terminate a connection
+ */
+
+void disconnect_request(llcptr lp)
+{
+	if ((lp->state == NORMAL) ||
+    		(lp->state == BUSY) ||
+		(lp->state == REJECT) ||
+		(lp->state == AWAIT) ||
+		(lp->state == AWAIT_BUSY) ||
+		(lp->state == AWAIT_REJECT))
+	{
+		lp->state = D_CONN;
+		llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+	}
+}
+
+
+/*
+ *	Connect_request() requests that the llc to start a connection
+ */
+
+void connect_request(llcptr lp)
+{
+	if (lp->state == ADM)
+	{
+		lp->state = SETUP;
+		llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+	}
+}
+
+
+/*
+ *	Interpret_pseudo_code() executes the actions in the connection component
+ *	state transition table. Table 4 in document on p88.
+ *
+ *	If this function is called to handle an incomming pdu, skb will point
+ *	to the buffer with the pdu and type will contain the decoded pdu type.
+ *
+ *	If called by data_request skb points to an skb that was skb_alloc-ed by 
+ *	the llc client to hold the information unit to be transmitted, there is
+ *	no valid type in this case.  
+ *
+ *	If called because a timer expired no skb is passed, and there is no 
+ *	type.
+ */
+
+void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, 
+		char type)
+{    
+	short int pc;	/* program counter in pseudo code array */ 
+	char p_flag_received;
+	frameptr fr;
+	int resend_count;   /* number of pdus resend by llc_resend_ipdu() */
+	int ack_count;      /* number of pdus acknowledged */
+	struct sk_buff *skb2;
+
+	if (skb != NULL) 
+	{
+		fr = (frameptr) skb->data;
+	}
+	else
+		fr = NULL;
+
+	pc = pseudo_code_idx[pc_label];
+	while(pseudo_code[pc])
+	{
+		switch(pseudo_code[pc])
+		{
+			case IF_F=1_CLEAR_REMOTE_BUSY:
+				if ((type != I_CMD) || (fr->i_hdr.i_pflag =0))
+					break;
+			case CLEAR_REMOTE_BUSY:
+				lp->remote_busy = 0;
+				llc_stop_timer(lp, BUSY_TIMER);
+				if ((lp->state == NORMAL) ||
+					(lp->state == REJECT) ||
+					(lp->state == BUSY))
+				{
+					skb2 = llc_pull_from_atq(lp);
+					if (skb2 != NULL) 
+						llc_start_timer(lp, ACK_TIMER);
+					while (skb2 != NULL)
+					{
+						llc_sendipdu( lp, I_CMD, 0, skb2);
+						skb2 = llc_pull_from_atq(lp);
+					}
+				}	   
+				break;
+			case CONNECT_INDICATION:
+				lp->state = NORMAL;  /* needed to eliminate connect_response() */
+				lp->llc_mode = MODE_ABM;
+				if (lp->ops->connect_indication_ep != NULL)
+					lp->ops->connect_indication_ep(lp);
+				break;
+			case CONNECT_CONFIRM:
+				lp->llc_mode = MODE_ABM;
+				if (lp->ops->connect_confirm_ep != NULL)
+					lp->ops->connect_confirm_ep(lp);
+				break;
+			case DATA_INDICATION:
+				if (lp->ops->data_indication_ep != NULL)
+				{
+					skb_pull(skb, 4);
+					lp->ops->data_indication_ep(lp, skb);
+				}
+				break;
+			case DISCONNECT_INDICATION:
+				lp->llc_mode = MODE_ADM;
+				if (lp->ops->disconnect_indication_ep != NULL)
+					lp->ops->disconnect_indication_ep(lp);
+				break;
+			case RESET_INDICATION(LOCAL):
+				if (lp->ops->reset_indication_ep != NULL)
+					lp->ops->reset_indication_ep(lp, LOCAL);
+				break;
+			case RESET_INDICATION(REMOTE):
+				if (lp->ops->reset_indication_ep != NULL)
+					lp->ops->reset_indication_ep(lp, REMOTE);
+				break;
+			case RESET_CONFIRM:
+				if (lp->ops->reset_confirm_ep != NULL)
+					lp->ops->reset_confirm_ep(lp, REMOTE);
+				break;
+			case REPORT_STATUS(FRMR_RECEIVED):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, FRMR_RECEIVED);
+				break;
+			case REPORT_STATUS(FRMR_SENT):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, FRMR_SENT);
+				break;
+			case REPORT_STATUS(REMOTE_BUSY):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				break;
+			case REPORT_STATUS(REMOTE_NOT_BUSY):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY);
+				break;
+			case SEND_DISC_CMD(P=X):
+				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
+				break;
+			case SEND_DM_RSP(F=X):
+				llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
+				break;
+			case SEND_FRMR_RSP(F=X):                        
+				lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
+				lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
+				lp->frmr_info_fld.vs = lp->vs;
+				lp->frmr_info_fld.vr_cr = lp->vr;
+				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+				break;
+			case RE-SEND_FRMR_RSP(F=0):
+				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+				break;
+			case RE-SEND_FRMR_RSP(F=P):
+				llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
+					5, (char *) &lp->frmr_info_fld);
+				break;
+			case SEND_I_CMD(P=1):
+				llc_sendipdu(lp, I_CMD, 1, skb);   
+				break;
+			case RE-SEND_I_CMD(P=1):
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+				break;
+			case RE-SEND_I_CMD(P=1)_OR_SEND_RR:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+				if (resend_count == 0) 
+				{
+					llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				}    
+				break;
+			case SEND_I_XXX(X=0):
+				llc_sendipdu(lp, I_CMD, 0, skb);   
+				break;
+			case RE-SEND_I_XXX(X=0):
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+				break;
+			case RE-SEND_I_XXX(X=0)_OR_SEND_RR:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+				if (resend_count == 0) 
+				{
+					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+				}    
+				break;
+			case RE-SEND_I_RSP(F=1):
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
+				break;
+			case SEND_REJ_CMD(P=1):
+				llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
+				break;
+			case SEND_REJ_RSP(F=1):
+				llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
+				break;
+			case SEND_REJ_XXX(X=0):
+				if (IS_RSP(fr))
+					llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
+				break;
+			case SEND_RNR_CMD(F=1):
+				llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
+				break;
+			case SEND_RNR_RSP(F=1):
+				llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
+				break;
+			case SEND_RNR_XXX(X=0):
+				if (IS_RSP(fr))
+					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+				break;
+			case SET_REMOTE_BUSY:
+				if (lp->remote_busy == 0)
+				{
+					lp->remote_busy = 1;
+					llc_start_timer(lp, BUSY_TIMER);
+					if (lp->ops->report_status_ep != NULL)
+						lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				}
+				else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
+				{
+					llc_start_timer(lp, BUSY_TIMER);
+				}
+				break;
+			case OPTIONAL_SEND_RNR_XXX(X=0):
+				if (IS_RSP(fr)) 
+					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+				break;
+			case SEND_RR_CMD(P=1):
+				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				break;
+			case SEND_ACKNOWLEDGE_CMD(P=1):
+				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				break;
+			case SEND_RR_RSP(F=1):
+				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+				break;
+			case SEND_ACKNOWLEDGE_RSP(F=1):
+				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+				break;
+			case SEND_RR_XXX(X=0):
+				llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+				break;
+			case SEND_ACKNOWLEDGE_XXX(X=0):
+				if (IS_RSP(fr)) 
+					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+				break;
+			case SEND_SABME_CMD(P=X):
+				llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
+				lp->f_flag = 0;
+				break;
+			case SEND_UA_RSP(F=X):
+				llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
+				break;
+			case S_FLAG:=0:
+				lp->s_flag = 0;
+				break;
+			case S_FLAG:=1:
+				lp->s_flag = 1;
+				break;
+			case START_P_TIMER:
+				if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
+					llc_stop_timer(lp, P_TIMER);
+				llc_start_timer(lp, P_TIMER);
+				if (lp->p_flag == 0)
+				{
+					lp->retry_count = 0;
+					lp->p_flag = 1;
+				}
+				break;
+			case START_ACK_TIMER_IF_NOT_RUNNING:
+				if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
+					llc_start_timer(lp, ACK_TIMER);
+				break;
+			case START_ACK_TIMER:
+				llc_start_timer(lp, ACK_TIMER);
+				break;
+			case START_REJ_TIMER:
+				llc_start_timer(lp, REJ_TIMER);
+				break;
+			case STOP_ACK_TIMER:
+				llc_stop_timer(lp, ACK_TIMER);
+				break;
+			case STOP_P_TIMER:
+				llc_stop_timer(lp, ACK_TIMER);
+				lp->p_flag = 0;
+				break;
+			case IF_DATA_FLAG=2_STOP_REJ_TIMER:
+				if (lp->data_flag == 2)
+					llc_stop_timer(lp, REJ_TIMER);
+				break;
+			case STOP_REJ_TIMER:
+				llc_stop_timer(lp, REJ_TIMER);
+				break;
+			case STOP_ALL_TIMERS:
+				llc_stop_timer(lp, ACK_TIMER);
+				llc_stop_timer(lp, P_TIMER);
+				llc_stop_timer(lp, REJ_TIMER);
+				llc_stop_timer(lp, BUSY_TIMER);
+				break;
+			case STOP_OTHER_TIMERS:
+				llc_stop_timer(lp, P_TIMER);
+				llc_stop_timer(lp, REJ_TIMER);
+				llc_stop_timer(lp, BUSY_TIMER);
+				break;
+			case UPDATE_N(R)_RECEIVED:             
+				ack_count = llc_free_acknowledged_skbs(lp,
+					(unsigned char) fr->s_hdr.nr);
+				if (ack_count > 0)
+				{
+					lp->retry_count = 0;
+					llc_stop_timer(lp, ACK_TIMER);  
+					if (lp->rtq_front != NULL)
+					{
+						/*
+ 						 *	Re-transmit queue not empty 
+						 */
+						llc_start_timer(lp, ACK_TIMER);  
+					}
+				}        
+				break;
+			case UPDATE_P_FLAG:
+				if (IS_UFRAME(fr)) 
+					p_flag_received = fr->u_hdr.u_pflag;
+				else
+					p_flag_received = fr->i_hdr.i_pflag;
+				if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
+				{
+					lp->p_flag = 0;
+					llc_stop_timer(lp, P_TIMER);  
+				}
+				break;
+			case DATA_FLAG:=2:
+				lp->data_flag = 2;
+				break;
+			case DATA_FLAG:=0:
+				lp->data_flag = 0;
+				break;
+			case DATA_FLAG:=1:
+				lp->data_flag = 1;
+				break;
+			case IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1:
+				if (lp->data_flag == 0)
+					lp->data_flag = 1;
+				break;
+			case P_FLAG:=0:
+				lp->p_flag = 0;
+				break;
+			case P_FLAG:=P:
+				lp->p_flag = lp->f_flag;
+				break;
+			case REMOTE_BUSY:=0:
+				lp->remote_busy = 0;
+				break;
+			case RETRY_COUNT:=0:
+				lp->retry_count = 0;
+				break;
+			case RETRY_COUNT:=RETRY_COUNT+1:
+				lp->retry_count++;
+				break;
+			case V(R):=0:
+				lp->vr = 0;
+				break;
+			case V(R):=V(R)+1:
+				lp->vr++;
+				break;
+			case V(S):=0:
+				lp->vs = 0;
+				break;
+			case V(S):=N(R):
+				lp->vs = fr->i_hdr.nr;
+				break;
+			case F_FLAG:=P:
+				if (IS_UFRAME(fr)) 
+					lp->f_flag = fr->u_hdr.u_pflag;
+				else
+					lp->f_flag = fr->i_hdr.i_pflag;
+				break;
+			default:
+		}
+		pc++;	
+	}
+}
+
+
+/*
+ *	Process_otype2_frame will handle incoming frames
+ *	for 802.2 Type 2 Procedure.
+ */
+
+void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
+{
+	int idx;		/*	index in transition table */
+	int pc_label;		/*	action to perform, from tr tbl */
+	int validation;		/*	result of validate_seq_nos */
+	int p_flag_received;	/*	p_flag in received frame */
+	frameptr fr;
+
+	fr = (frameptr) skb->data;
+
+	if (IS_UFRAME(fr))
+		p_flag_received = fr->u_hdr.u_pflag;
+	else
+		p_flag_received = fr->i_hdr.i_pflag;
+
+	switch(lp->state)
+	{
+		/*	Compute index in transition table: */
+		case ADM:
+			idx = type;
+			idx = (idx << 1) + p_flag_received;
+			break;
+		case CONN:
+		case RESET_WAIT:
+		case RESET_CHECK:
+		case ERROR:
+			idx = type;
+			break;
+		case SETUP:
+		case RESET:
+		case D_CONN:
+			idx = type;
+			idx = (idx << 1) + lp->p_flag;
+			break;
+		case NORMAL:
+		case BUSY:
+		case REJECT:
+		case AWAIT:
+		case AWAIT_BUSY:
+		case AWAIT_REJECT:
+			validation = llc_validate_seq_nos(lp, fr);
+			if (validation > 3) 
+				type = BAD_FRAME;
+			idx = type;
+			idx = (idx << 1);
+			if (validation & 1) 
+				idx = idx +1;
+			idx = (idx << 1) + p_flag_received;
+			idx = (idx << 1) + lp->p_flag;
+		default:
+			printk("llc_proc: bad state\n");
+			return;
+	}
+	idx = (idx << 1) + pdutr_offset[lp->state];
+	lp->state = pdutr_entry[idx +1]; 
+	pc_label = pdutr_entry[idx];
+	if (pc_label != NOP)
+	{ 
+		llc_interpret_pseudo_code(lp, pc_label, skb, type);
+	}
+}
+
+
+void llc_timer_expired(llcptr lp, int t)
+{
+	int idx;		/* index in transition table	*/
+	int pc_label;       	/* action to perform, from tr tbl */
+
+	lp->timer_state[t] = TIMER_EXPIRED;
+	idx = lp->state;            /* Compute index in transition table: */
+	idx = (idx << 2) + t;
+	idx = idx << 1;
+	if (lp->retry_count >= lp->n2) 
+		idx = idx + 1;
+	idx = (idx << 1) + lp->s_flag;
+	idx = (idx << 1) + lp->p_flag;
+	idx = idx << 1;             /* 2 bytes per entry: action & newstate */
+
+	pc_label = timertr_entry[idx];
+	if (pc_label != NOP)
+	{
+		llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
+		lp->state = timertr_entry[idx +1];
+	}
+	lp->timer_state[t] = TIMER_IDLE;
+}
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov