/*
|
/*
|
* NET An implementation of the IEEE 802.2 LLC protocol for the
|
* NET An implementation of the IEEE 802.2 LLC protocol for the
|
* LINUX operating system. LLC is implemented as a set of
|
* LINUX operating system. LLC is implemented as a set of
|
* state machines and callbacks for higher networking layers.
|
* state machines and callbacks for higher networking layers.
|
*
|
*
|
* Class 2 llc algorithm.
|
* Class 2 llc algorithm.
|
* Pseudocode interpreter, transition table lookup,
|
* Pseudocode interpreter, transition table lookup,
|
* data_request & indicate primitives...
|
* data_request & indicate primitives...
|
*
|
*
|
* Code for initialization, termination, registration and
|
* Code for initialization, termination, registration and
|
* MAC layer glue.
|
* MAC layer glue.
|
*
|
*
|
* Copyright Tim Alpaerts,
|
* Copyright Tim Alpaerts,
|
* <Tim_Alpaerts@toyota-motor-europe.com>
|
* <Tim_Alpaerts@toyota-motor-europe.com>
|
*
|
*
|
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
* as published by the Free Software Foundation; either version
|
* as published by the Free Software Foundation; either version
|
* 2 of the License, or (at your option) any later version.
|
* 2 of the License, or (at your option) any later version.
|
*
|
*
|
* Changes
|
* Changes
|
* Alan Cox : Chainsawed into Linux format
|
* Alan Cox : Chainsawed into Linux format
|
* Modified to use llc_ names
|
* Modified to use llc_ names
|
* Changed callbacks
|
* Changed callbacks
|
*
|
*
|
* This file must be processed by sed before it can be compiled.
|
* This file must be processed by sed before it can be compiled.
|
*/
|
*/
|
|
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/slab.h>
|
#include <linux/slab.h>
|
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
#include <net/p8022.h>
|
#include <net/p8022.h>
|
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
|
|
#include "pseudo/pseudocode.h"
|
#include "pseudo/pseudocode.h"
|
#include "transit/pdutr.h"
|
#include "transit/pdutr.h"
|
#include "transit/timertr.h"
|
#include "transit/timertr.h"
|
#include <net/llc_frame.h>
|
#include <net/llc_frame.h>
|
#include <net/llc.h>
|
#include <net/llc.h>
|
|
|
/*
|
/*
|
* Data_request() is called by the client to present a data unit
|
* Data_request() is called by the client to present a data unit
|
* to the llc for transmission.
|
* to the llc for transmission.
|
* In the future this function should also check if the transmit window
|
* 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
|
* allows the sending of another pdu, and if not put the skb on the atq
|
* for deferred sending.
|
* for deferred sending.
|
*/
|
*/
|
|
|
int llc_data_request(llcptr lp, struct sk_buff *skb)
|
int llc_data_request(llcptr lp, struct sk_buff *skb)
|
{
|
{
|
if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
|
if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
|
printk("cl2llc: data_request() not enough headroom in skb\n");
|
printk("cl2llc: data_request() not enough headroom in skb\n");
|
return -1;
|
return -1;
|
};
|
};
|
|
|
skb_push(skb, 4);
|
skb_push(skb, 4);
|
|
|
if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
|
if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
|
{
|
{
|
printk("cl2llc: data_request() while no llc connection\n");
|
printk("cl2llc: data_request() while no llc connection\n");
|
return -1;
|
return -1;
|
}
|
}
|
|
|
if (lp->remote_busy)
|
if (lp->remote_busy)
|
{ /* if the remote llc is BUSY, */
|
{ /* if the remote llc is BUSY, */
|
ADD_TO_ATQ(skb); /* save skb in the await transmit queue */
|
ADD_TO_ATQ(skb); /* save skb in the await transmit queue */
|
return 0;
|
return 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
/*
|
/*
|
* Else proceed with xmit
|
* Else proceed with xmit
|
*/
|
*/
|
|
|
switch(lp->state)
|
switch(lp->state)
|
{
|
{
|
case NORMAL:
|
case NORMAL:
|
if(lp->p_flag)
|
if(lp->p_flag)
|
llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
|
llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
|
else
|
else
|
llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
|
llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
|
break;
|
break;
|
case BUSY:
|
case BUSY:
|
if (lp->p_flag)
|
if (lp->p_flag)
|
llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
|
llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
|
else
|
else
|
llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
|
llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
|
break;
|
break;
|
case REJECT:
|
case REJECT:
|
if (lp->p_flag)
|
if (lp->p_flag)
|
llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
|
llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
|
else
|
else
|
llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
|
llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
|
break;
|
break;
|
default:;
|
default:;
|
}
|
}
|
if(lp->llc_callbacks)
|
if(lp->llc_callbacks)
|
{
|
{
|
lp->llc_event(lp);
|
lp->llc_event(lp);
|
lp->llc_callbacks=0;
|
lp->llc_callbacks=0;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
|
|
|
|
/*
|
/*
|
* Disconnect_request() requests that the llc to terminate a connection
|
* Disconnect_request() requests that the llc to terminate a connection
|
*/
|
*/
|
|
|
void disconnect_request(llcptr lp)
|
void disconnect_request(llcptr lp)
|
{
|
{
|
if ((lp->state == NORMAL) ||
|
if ((lp->state == NORMAL) ||
|
(lp->state == BUSY) ||
|
(lp->state == BUSY) ||
|
(lp->state == REJECT) ||
|
(lp->state == REJECT) ||
|
(lp->state == AWAIT) ||
|
(lp->state == AWAIT) ||
|
(lp->state == AWAIT_BUSY) ||
|
(lp->state == AWAIT_BUSY) ||
|
(lp->state == AWAIT_REJECT))
|
(lp->state == AWAIT_REJECT))
|
{
|
{
|
lp->state = D_CONN;
|
lp->state = D_CONN;
|
llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
|
llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
|
if(lp->llc_callbacks)
|
if(lp->llc_callbacks)
|
{
|
{
|
lp->llc_event(lp);
|
lp->llc_event(lp);
|
lp->llc_callbacks=0;
|
lp->llc_callbacks=0;
|
}
|
}
|
/*
|
/*
|
* lp may be invalid after the callback
|
* lp may be invalid after the callback
|
*/
|
*/
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Connect_request() requests that the llc to start a connection
|
* Connect_request() requests that the llc to start a connection
|
*/
|
*/
|
|
|
void connect_request(llcptr lp)
|
void connect_request(llcptr lp)
|
{
|
{
|
if (lp->state == ADM)
|
if (lp->state == ADM)
|
{
|
{
|
lp->state = SETUP;
|
lp->state = SETUP;
|
llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
|
llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
|
if(lp->llc_callbacks)
|
if(lp->llc_callbacks)
|
{
|
{
|
lp->llc_event(lp);
|
lp->llc_event(lp);
|
lp->llc_callbacks=0;
|
lp->llc_callbacks=0;
|
}
|
}
|
/*
|
/*
|
* lp may be invalid after the callback
|
* lp may be invalid after the callback
|
*/
|
*/
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Interpret_pseudo_code() executes the actions in the connection component
|
* Interpret_pseudo_code() executes the actions in the connection component
|
* state transition table. Table 4 in document on p88.
|
* state transition table. Table 4 in document on p88.
|
*
|
*
|
* If this function is called to handle an incoming pdu, skb will point
|
* If this function is called to handle an incoming pdu, skb will point
|
* to the buffer with the pdu and type will contain the decoded pdu type.
|
* 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
|
* 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
|
* the llc client to hold the information unit to be transmitted, there is
|
* no valid type in this case.
|
* no valid type in this case.
|
*
|
*
|
* If called because a timer expired no skb is passed, and there is no
|
* If called because a timer expired no skb is passed, and there is no
|
* type.
|
* type.
|
*/
|
*/
|
|
|
void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb,
|
void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb,
|
char type)
|
char type)
|
{
|
{
|
short int pc; /* program counter in pseudo code array */
|
short int pc; /* program counter in pseudo code array */
|
char p_flag_received;
|
char p_flag_received;
|
frameptr fr;
|
frameptr fr;
|
int resend_count; /* number of pdus resend by llc_resend_ipdu() */
|
int resend_count; /* number of pdus resend by llc_resend_ipdu() */
|
int ack_count; /* number of pdus acknowledged */
|
int ack_count; /* number of pdus acknowledged */
|
struct sk_buff *skb2;
|
struct sk_buff *skb2;
|
|
|
if (skb != NULL)
|
if (skb != NULL)
|
{
|
{
|
fr = (frameptr) skb->data;
|
fr = (frameptr) skb->data;
|
}
|
}
|
else
|
else
|
fr = NULL;
|
fr = NULL;
|
|
|
pc = pseudo_code_idx[pc_label];
|
pc = pseudo_code_idx[pc_label];
|
while(pseudo_code[pc])
|
while(pseudo_code[pc])
|
{
|
{
|
switch(pseudo_code[pc])
|
switch(pseudo_code[pc])
|
{
|
{
|
case 9:
|
case 9:
|
if ((type != I_CMD) || (fr->i_hdr.i_pflag == 0))
|
if ((type != I_CMD) || (fr->i_hdr.i_pflag == 0))
|
break;
|
break;
|
case 1:
|
case 1:
|
lp->remote_busy = 0;
|
lp->remote_busy = 0;
|
llc_stop_timer(lp, BUSY_TIMER);
|
llc_stop_timer(lp, BUSY_TIMER);
|
if ((lp->state == NORMAL) ||
|
if ((lp->state == NORMAL) ||
|
(lp->state == REJECT) ||
|
(lp->state == REJECT) ||
|
(lp->state == BUSY))
|
(lp->state == BUSY))
|
{
|
{
|
skb2 = llc_pull_from_atq(lp);
|
skb2 = llc_pull_from_atq(lp);
|
if (skb2 != NULL)
|
if (skb2 != NULL)
|
llc_start_timer(lp, ACK_TIMER);
|
llc_start_timer(lp, ACK_TIMER);
|
while (skb2 != NULL)
|
while (skb2 != NULL)
|
{
|
{
|
llc_sendipdu( lp, I_CMD, 0, skb2);
|
llc_sendipdu( lp, I_CMD, 0, skb2);
|
skb2 = llc_pull_from_atq(lp);
|
skb2 = llc_pull_from_atq(lp);
|
}
|
}
|
}
|
}
|
break;
|
break;
|
case 2:
|
case 2:
|
lp->state = NORMAL; /* needed to eliminate connect_response() */
|
lp->state = NORMAL; /* needed to eliminate connect_response() */
|
lp->llc_mode = MODE_ABM;
|
lp->llc_mode = MODE_ABM;
|
lp->llc_callbacks|=LLC_CONN_INDICATION;
|
lp->llc_callbacks|=LLC_CONN_INDICATION;
|
break;
|
break;
|
case 3:
|
case 3:
|
lp->llc_mode = MODE_ABM;
|
lp->llc_mode = MODE_ABM;
|
lp->llc_callbacks|=LLC_CONN_CONFIRM;
|
lp->llc_callbacks|=LLC_CONN_CONFIRM;
|
break;
|
break;
|
case 4:
|
case 4:
|
skb_pull(skb, 4);
|
skb_pull(skb, 4);
|
lp->inc_skb=skb;
|
lp->inc_skb=skb;
|
lp->llc_callbacks|=LLC_DATA_INDIC;
|
lp->llc_callbacks|=LLC_DATA_INDIC;
|
break;
|
break;
|
case 5:
|
case 5:
|
lp->llc_mode = MODE_ADM;
|
lp->llc_mode = MODE_ADM;
|
lp->llc_callbacks|=LLC_DISC_INDICATION;
|
lp->llc_callbacks|=LLC_DISC_INDICATION;
|
break;
|
break;
|
case 70:
|
case 70:
|
lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
|
lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
|
break;
|
break;
|
case 71:
|
case 71:
|
lp->llc_callbacks|=LLC_RESET_INDIC_REM;
|
lp->llc_callbacks|=LLC_RESET_INDIC_REM;
|
break;
|
break;
|
case 7:
|
case 7:
|
lp->llc_callbacks|=LLC_RST_CONFIRM;
|
lp->llc_callbacks|=LLC_RST_CONFIRM;
|
break;
|
break;
|
case 66:
|
case 66:
|
lp->llc_callbacks|=LLC_FRMR_RECV;
|
lp->llc_callbacks|=LLC_FRMR_RECV;
|
break;
|
break;
|
case 67:
|
case 67:
|
lp->llc_callbacks|=LLC_FRMR_SENT;
|
lp->llc_callbacks|=LLC_FRMR_SENT;
|
break;
|
break;
|
case 68:
|
case 68:
|
lp->llc_callbacks|=LLC_REMOTE_BUSY;
|
lp->llc_callbacks|=LLC_REMOTE_BUSY;
|
break;
|
break;
|
case 69:
|
case 69:
|
lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
|
lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
|
break;
|
break;
|
case 11:
|
case 11:
|
llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
|
llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
|
break;
|
break;
|
case 12:
|
case 12:
|
llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
|
llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
|
break;
|
break;
|
case 13:
|
case 13:
|
lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
|
lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
|
lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
|
lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
|
lp->frmr_info_fld.vs = lp->vs;
|
lp->frmr_info_fld.vs = lp->vs;
|
lp->frmr_info_fld.vr_cr = lp->vr;
|
lp->frmr_info_fld.vr_cr = lp->vr;
|
llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
|
llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
|
break;
|
break;
|
case 14:
|
case 14:
|
llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
|
llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
|
break;
|
break;
|
case 15:
|
case 15:
|
llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
|
llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
|
5, (char *) &lp->frmr_info_fld);
|
5, (char *) &lp->frmr_info_fld);
|
break;
|
break;
|
case 16:
|
case 16:
|
llc_sendipdu(lp, I_CMD, 1, skb);
|
llc_sendipdu(lp, I_CMD, 1, skb);
|
break;
|
break;
|
case 17:
|
case 17:
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
|
break;
|
break;
|
case 18:
|
case 18:
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
|
if (resend_count == 0)
|
if (resend_count == 0)
|
{
|
{
|
llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
|
llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
|
}
|
}
|
break;
|
break;
|
case 19:
|
case 19:
|
llc_sendipdu(lp, I_CMD, 0, skb);
|
llc_sendipdu(lp, I_CMD, 0, skb);
|
break;
|
break;
|
case 20:
|
case 20:
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
|
break;
|
break;
|
case 21:
|
case 21:
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
|
if (resend_count == 0)
|
if (resend_count == 0)
|
{
|
{
|
llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
|
llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
|
}
|
}
|
break;
|
break;
|
case 22:
|
case 22:
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
|
resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
|
break;
|
break;
|
case 23:
|
case 23:
|
llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
|
llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
|
break;
|
break;
|
case 24:
|
case 24:
|
llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
|
llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
|
break;
|
break;
|
case 25:
|
case 25:
|
if (IS_RSP(fr))
|
if (IS_RSP(fr))
|
llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
|
llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
|
else
|
else
|
llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
|
llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
|
break;
|
break;
|
case 26:
|
case 26:
|
llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
|
llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
|
break;
|
break;
|
case 27:
|
case 27:
|
llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
|
llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
|
break;
|
break;
|
case 28:
|
case 28:
|
if (IS_RSP(fr))
|
if (IS_RSP(fr))
|
llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
|
llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
|
else
|
else
|
llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
|
llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
|
break;
|
break;
|
case 29:
|
case 29:
|
if (lp->remote_busy == 0)
|
if (lp->remote_busy == 0)
|
{
|
{
|
lp->remote_busy = 1;
|
lp->remote_busy = 1;
|
llc_start_timer(lp, BUSY_TIMER);
|
llc_start_timer(lp, BUSY_TIMER);
|
lp->llc_callbacks|=LLC_REMOTE_BUSY;
|
lp->llc_callbacks|=LLC_REMOTE_BUSY;
|
}
|
}
|
else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
|
else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
|
{
|
{
|
llc_start_timer(lp, BUSY_TIMER);
|
llc_start_timer(lp, BUSY_TIMER);
|
}
|
}
|
break;
|
break;
|
case 30:
|
case 30:
|
if (IS_RSP(fr))
|
if (IS_RSP(fr))
|
llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
|
llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
|
else
|
else
|
llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
|
llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
|
break;
|
break;
|
case 31:
|
case 31:
|
llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
|
llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
|
break;
|
break;
|
case 32:
|
case 32:
|
llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
|
llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
|
break;
|
break;
|
case 33:
|
case 33:
|
llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
|
llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
|
break;
|
break;
|
case 34:
|
case 34:
|
llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
|
llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
|
break;
|
break;
|
case 35:
|
case 35:
|
llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
|
llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
|
break;
|
break;
|
case 36:
|
case 36:
|
if (IS_RSP(fr))
|
if (IS_RSP(fr))
|
llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
|
llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
|
else
|
else
|
llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
|
llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
|
break;
|
break;
|
case 37:
|
case 37:
|
llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
|
llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
|
lp->f_flag = 0;
|
lp->f_flag = 0;
|
break;
|
break;
|
case 38:
|
case 38:
|
llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
|
llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
|
break;
|
break;
|
case 39:
|
case 39:
|
lp->s_flag = 0;
|
lp->s_flag = 0;
|
break;
|
break;
|
case 40:
|
case 40:
|
lp->s_flag = 1;
|
lp->s_flag = 1;
|
break;
|
break;
|
case 41:
|
case 41:
|
if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
|
if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
|
llc_stop_timer(lp, P_TIMER);
|
llc_stop_timer(lp, P_TIMER);
|
llc_start_timer(lp, P_TIMER);
|
llc_start_timer(lp, P_TIMER);
|
if (lp->p_flag == 0)
|
if (lp->p_flag == 0)
|
{
|
{
|
lp->retry_count = 0;
|
lp->retry_count = 0;
|
lp->p_flag = 1;
|
lp->p_flag = 1;
|
}
|
}
|
break;
|
break;
|
case 44:
|
case 44:
|
if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
|
if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
|
llc_start_timer(lp, ACK_TIMER);
|
llc_start_timer(lp, ACK_TIMER);
|
break;
|
break;
|
case 42:
|
case 42:
|
llc_start_timer(lp, ACK_TIMER);
|
llc_start_timer(lp, ACK_TIMER);
|
break;
|
break;
|
case 43:
|
case 43:
|
llc_start_timer(lp, REJ_TIMER);
|
llc_start_timer(lp, REJ_TIMER);
|
break;
|
break;
|
case 45:
|
case 45:
|
llc_stop_timer(lp, ACK_TIMER);
|
llc_stop_timer(lp, ACK_TIMER);
|
break;
|
break;
|
case 46:
|
case 46:
|
llc_stop_timer(lp, ACK_TIMER);
|
llc_stop_timer(lp, ACK_TIMER);
|
lp->p_flag = 0;
|
lp->p_flag = 0;
|
break;
|
break;
|
case 10:
|
case 10:
|
if (lp->data_flag == 2)
|
if (lp->data_flag == 2)
|
llc_stop_timer(lp, REJ_TIMER);
|
llc_stop_timer(lp, REJ_TIMER);
|
break;
|
break;
|
case 47:
|
case 47:
|
llc_stop_timer(lp, REJ_TIMER);
|
llc_stop_timer(lp, REJ_TIMER);
|
break;
|
break;
|
case 48:
|
case 48:
|
llc_stop_timer(lp, ACK_TIMER);
|
llc_stop_timer(lp, ACK_TIMER);
|
llc_stop_timer(lp, P_TIMER);
|
llc_stop_timer(lp, P_TIMER);
|
llc_stop_timer(lp, REJ_TIMER);
|
llc_stop_timer(lp, REJ_TIMER);
|
llc_stop_timer(lp, BUSY_TIMER);
|
llc_stop_timer(lp, BUSY_TIMER);
|
break;
|
break;
|
case 49:
|
case 49:
|
llc_stop_timer(lp, P_TIMER);
|
llc_stop_timer(lp, P_TIMER);
|
llc_stop_timer(lp, REJ_TIMER);
|
llc_stop_timer(lp, REJ_TIMER);
|
llc_stop_timer(lp, BUSY_TIMER);
|
llc_stop_timer(lp, BUSY_TIMER);
|
break;
|
break;
|
case 50:
|
case 50:
|
ack_count = llc_free_acknowledged_skbs(lp,
|
ack_count = llc_free_acknowledged_skbs(lp,
|
(unsigned char) fr->s_hdr.nr);
|
(unsigned char) fr->s_hdr.nr);
|
if (ack_count > 0)
|
if (ack_count > 0)
|
{
|
{
|
lp->retry_count = 0;
|
lp->retry_count = 0;
|
llc_stop_timer(lp, ACK_TIMER);
|
llc_stop_timer(lp, ACK_TIMER);
|
if (skb_peek(&lp->rtq) != NULL)
|
if (skb_peek(&lp->rtq) != NULL)
|
{
|
{
|
/*
|
/*
|
* Re-transmit queue not empty
|
* Re-transmit queue not empty
|
*/
|
*/
|
llc_start_timer(lp, ACK_TIMER);
|
llc_start_timer(lp, ACK_TIMER);
|
}
|
}
|
}
|
}
|
break;
|
break;
|
case 51:
|
case 51:
|
if (IS_UFRAME(fr))
|
if (IS_UFRAME(fr))
|
p_flag_received = fr->u_hdr.u_pflag;
|
p_flag_received = fr->u_hdr.u_pflag;
|
else
|
else
|
p_flag_received = fr->i_hdr.i_pflag;
|
p_flag_received = fr->i_hdr.i_pflag;
|
if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
|
if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
|
{
|
{
|
lp->p_flag = 0;
|
lp->p_flag = 0;
|
llc_stop_timer(lp, P_TIMER);
|
llc_stop_timer(lp, P_TIMER);
|
}
|
}
|
break;
|
break;
|
case 52:
|
case 52:
|
lp->data_flag = 2;
|
lp->data_flag = 2;
|
break;
|
break;
|
case 53:
|
case 53:
|
lp->data_flag = 0;
|
lp->data_flag = 0;
|
break;
|
break;
|
case 54:
|
case 54:
|
lp->data_flag = 1;
|
lp->data_flag = 1;
|
break;
|
break;
|
case 55:
|
case 55:
|
if (lp->data_flag == 0)
|
if (lp->data_flag == 0)
|
lp->data_flag = 1;
|
lp->data_flag = 1;
|
break;
|
break;
|
case 56:
|
case 56:
|
lp->p_flag = 0;
|
lp->p_flag = 0;
|
break;
|
break;
|
case 57:
|
case 57:
|
lp->p_flag = lp->f_flag;
|
lp->p_flag = lp->f_flag;
|
break;
|
break;
|
case 58:
|
case 58:
|
lp->remote_busy = 0;
|
lp->remote_busy = 0;
|
break;
|
break;
|
case 59:
|
case 59:
|
lp->retry_count = 0;
|
lp->retry_count = 0;
|
break;
|
break;
|
case 60:
|
case 60:
|
lp->retry_count++;
|
lp->retry_count++;
|
break;
|
break;
|
case 61:
|
case 61:
|
lp->vr = 0;
|
lp->vr = 0;
|
break;
|
break;
|
case 62:
|
case 62:
|
lp->vr++;
|
lp->vr++;
|
break;
|
break;
|
case 63:
|
case 63:
|
lp->vs = 0;
|
lp->vs = 0;
|
break;
|
break;
|
case 64:
|
case 64:
|
lp->vs = fr->i_hdr.nr;
|
lp->vs = fr->i_hdr.nr;
|
break;
|
break;
|
case 65:
|
case 65:
|
if (IS_UFRAME(fr))
|
if (IS_UFRAME(fr))
|
lp->f_flag = fr->u_hdr.u_pflag;
|
lp->f_flag = fr->u_hdr.u_pflag;
|
else
|
else
|
lp->f_flag = fr->i_hdr.i_pflag;
|
lp->f_flag = fr->i_hdr.i_pflag;
|
break;
|
break;
|
default:;
|
default:;
|
}
|
}
|
pc++;
|
pc++;
|
}
|
}
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Process_otype2_frame will handle incoming frames
|
* Process_otype2_frame will handle incoming frames
|
* for 802.2 Type 2 Procedure.
|
* for 802.2 Type 2 Procedure.
|
*/
|
*/
|
|
|
void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
|
void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
|
{
|
{
|
int idx; /* index in transition table */
|
int idx; /* index in transition table */
|
int pc_label; /* action to perform, from tr tbl */
|
int pc_label; /* action to perform, from tr tbl */
|
int validation; /* result of validate_seq_nos */
|
int validation; /* result of validate_seq_nos */
|
int p_flag_received; /* p_flag in received frame */
|
int p_flag_received; /* p_flag in received frame */
|
frameptr fr;
|
frameptr fr;
|
|
|
fr = (frameptr) skb->data;
|
fr = (frameptr) skb->data;
|
|
|
if (IS_UFRAME(fr))
|
if (IS_UFRAME(fr))
|
p_flag_received = fr->u_hdr.u_pflag;
|
p_flag_received = fr->u_hdr.u_pflag;
|
else
|
else
|
p_flag_received = fr->i_hdr.i_pflag;
|
p_flag_received = fr->i_hdr.i_pflag;
|
|
|
switch(lp->state)
|
switch(lp->state)
|
{
|
{
|
/* Compute index in transition table: */
|
/* Compute index in transition table: */
|
case ADM:
|
case ADM:
|
idx = type;
|
idx = type;
|
idx = (idx << 1) + p_flag_received;
|
idx = (idx << 1) + p_flag_received;
|
break;
|
break;
|
case CONN:
|
case CONN:
|
case RESET_WAIT:
|
case RESET_WAIT:
|
case RESET_CHECK:
|
case RESET_CHECK:
|
case ERROR:
|
case ERROR:
|
idx = type;
|
idx = type;
|
break;
|
break;
|
case SETUP:
|
case SETUP:
|
case RESET:
|
case RESET:
|
case D_CONN:
|
case D_CONN:
|
idx = type;
|
idx = type;
|
idx = (idx << 1) + lp->p_flag;
|
idx = (idx << 1) + lp->p_flag;
|
break;
|
break;
|
case NORMAL:
|
case NORMAL:
|
case BUSY:
|
case BUSY:
|
case REJECT:
|
case REJECT:
|
case AWAIT:
|
case AWAIT:
|
case AWAIT_BUSY:
|
case AWAIT_BUSY:
|
case AWAIT_REJECT:
|
case AWAIT_REJECT:
|
validation = llc_validate_seq_nos(lp, fr);
|
validation = llc_validate_seq_nos(lp, fr);
|
if (validation > 3)
|
if (validation > 3)
|
type = BAD_FRAME;
|
type = BAD_FRAME;
|
idx = type;
|
idx = type;
|
idx = (idx << 1);
|
idx = (idx << 1);
|
if (validation & 1)
|
if (validation & 1)
|
idx = idx +1;
|
idx = idx +1;
|
idx = (idx << 1) + p_flag_received;
|
idx = (idx << 1) + p_flag_received;
|
idx = (idx << 1) + lp->p_flag;
|
idx = (idx << 1) + lp->p_flag;
|
default:
|
default:
|
printk("llc_proc: bad state\n");
|
printk("llc_proc: bad state\n");
|
return;
|
return;
|
}
|
}
|
idx = (idx << 1) + pdutr_offset[lp->state];
|
idx = (idx << 1) + pdutr_offset[lp->state];
|
lp->state = pdutr_entry[idx +1];
|
lp->state = pdutr_entry[idx +1];
|
pc_label = pdutr_entry[idx];
|
pc_label = pdutr_entry[idx];
|
if (pc_label != 0)
|
if (pc_label != 0)
|
{
|
{
|
llc_interpret_pseudo_code(lp, pc_label, skb, type);
|
llc_interpret_pseudo_code(lp, pc_label, skb, type);
|
if(lp->llc_callbacks)
|
if(lp->llc_callbacks)
|
{
|
{
|
lp->llc_event(lp);
|
lp->llc_event(lp);
|
lp->llc_callbacks=0;
|
lp->llc_callbacks=0;
|
}
|
}
|
/*
|
/*
|
* lp may no longer be valid after this point. Be
|
* lp may no longer be valid after this point. Be
|
* careful what is added!
|
* careful what is added!
|
*/
|
*/
|
}
|
}
|
}
|
}
|
|
|
|
|
void llc_timer_expired(llcptr lp, int t)
|
void llc_timer_expired(llcptr lp, int t)
|
{
|
{
|
int idx; /* index in transition table */
|
int idx; /* index in transition table */
|
int pc_label; /* action to perform, from tr tbl */
|
int pc_label; /* action to perform, from tr tbl */
|
|
|
lp->timer_state[t] = TIMER_EXPIRED;
|
lp->timer_state[t] = TIMER_EXPIRED;
|
idx = lp->state; /* Compute index in transition table: */
|
idx = lp->state; /* Compute index in transition table: */
|
idx = (idx << 2) + t;
|
idx = (idx << 2) + t;
|
idx = idx << 1;
|
idx = idx << 1;
|
if (lp->retry_count >= lp->n2)
|
if (lp->retry_count >= lp->n2)
|
idx = idx + 1;
|
idx = idx + 1;
|
idx = (idx << 1) + lp->s_flag;
|
idx = (idx << 1) + lp->s_flag;
|
idx = (idx << 1) + lp->p_flag;
|
idx = (idx << 1) + lp->p_flag;
|
idx = idx << 1; /* 2 bytes per entry: action & newstate */
|
idx = idx << 1; /* 2 bytes per entry: action & newstate */
|
|
|
pc_label = timertr_entry[idx];
|
pc_label = timertr_entry[idx];
|
if (pc_label != 0)
|
if (pc_label != 0)
|
{
|
{
|
llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
|
llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
|
lp->state = timertr_entry[idx +1];
|
lp->state = timertr_entry[idx +1];
|
}
|
}
|
lp->timer_state[t] = TIMER_IDLE;
|
lp->timer_state[t] = TIMER_IDLE;
|
if(lp->llc_callbacks)
|
if(lp->llc_callbacks)
|
{
|
{
|
lp->llc_event(lp);
|
lp->llc_event(lp);
|
lp->llc_callbacks=0;
|
lp->llc_callbacks=0;
|
}
|
}
|
/*
|
/*
|
* And lp may have vanished in the event callback
|
* And lp may have vanished in the event callback
|
*/
|
*/
|
}
|
}
|
|
|
|
|