diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/802/cl2llc.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/802/cl2llc.c')
-rw-r--r-- | net/802/cl2llc.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/net/802/cl2llc.c b/net/802/cl2llc.c new file mode 100644 index 000000000..5e1d12837 --- /dev/null +++ b/net/802/cl2llc.c @@ -0,0 +1,615 @@ +/* + * 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 + * Changed callbacks + * + * 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: + } + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + 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); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may be invalid after the callback + */ + } +} + + +/* + * 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); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may be invalid after the callback + */ + } +} + + +/* + * 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 9: + if ((type != I_CMD) || (fr->i_hdr.i_pflag =0)) + break; + case 1: + 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 2: + lp->state = NORMAL; /* needed to eliminate connect_response() */ + lp->llc_mode = MODE_ABM; + lp->llc_callbacks|=LLC_CONN_INDICATION; + break; + case 3: + lp->llc_mode = MODE_ABM; + lp->llc_callbacks|=LLC_CONN_CONFIRM; + break; + case 4: + skb_pull(skb, 4); + lp->inc_skb=skb; + lp->llc_callbacks|=LLC_DATA_INDIC; + break; + case 5: + lp->llc_mode = MODE_ADM; + lp->llc_callbacks|=LLC_DISC_INDICATION; + break; + case 70: + lp->llc_callbacks|=LLC_RESET_INDIC_LOC; + break; + case 71: + lp->llc_callbacks|=LLC_RESET_INDIC_REM; + break; + case 7: + lp->llc_callbacks|=LLC_RST_CONFIRM; + break; + case 66: + lp->llc_callbacks|=LLC_FRMR_RECV; + break; + case 67: + lp->llc_callbacks|=LLC_FRMR_SENT; + break; + case 68: + lp->llc_callbacks|=LLC_REMOTE_BUSY; + break; + case 69: + lp->llc_callbacks|=LLC_REMOTE_NOTBUSY; + break; + case 11: + llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL); + break; + case 12: + llc_sendpdu(lp, DM_RSP, 0, 0, NULL); + break; + case 13: + 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 14: + llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld); + break; + case 15: + llc_sendpdu(lp, FRMR_RSP, lp->p_flag, + 5, (char *) &lp->frmr_info_fld); + break; + case 16: + llc_sendipdu(lp, I_CMD, 1, skb); + break; + case 17: + resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1); + break; + case 18: + 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 19: + llc_sendipdu(lp, I_CMD, 0, skb); + break; + case 20: + resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0); + break; + case 21: + 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 22: + resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1); + break; + case 23: + llc_sendpdu(lp, REJ_CMD, 1, 0, NULL); + break; + case 24: + llc_sendpdu(lp, REJ_RSP, 1, 0, NULL); + break; + case 25: + if (IS_RSP(fr)) + llc_sendpdu(lp, REJ_CMD, 0, 0, NULL); + else + llc_sendpdu(lp, REJ_RSP, 0, 0, NULL); + break; + case 26: + llc_sendpdu(lp, RNR_CMD, 1, 0, NULL); + break; + case 27: + llc_sendpdu(lp, RNR_RSP, 1, 0, NULL); + break; + case 28: + if (IS_RSP(fr)) + llc_sendpdu(lp, RNR_CMD, 0, 0, NULL); + else + llc_sendpdu(lp, RNR_RSP, 0, 0, NULL); + break; + case 29: + if (lp->remote_busy == 0) + { + lp->remote_busy = 1; + llc_start_timer(lp, BUSY_TIMER); + lp->llc_callbacks|=LLC_REMOTE_BUSY; + } + else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE) + { + llc_start_timer(lp, BUSY_TIMER); + } + break; + case 30: + if (IS_RSP(fr)) + llc_sendpdu(lp, RNR_CMD, 0, 0, NULL); + else + llc_sendpdu(lp, RNR_RSP, 0, 0, NULL); + break; + case 31: + llc_sendpdu(lp, RR_CMD, 1, 0, NULL); + break; + case 32: + llc_sendpdu(lp, RR_CMD, 1, 0, NULL); + break; + case 33: + llc_sendpdu(lp, RR_RSP, 1, 0, NULL); + break; + case 34: + llc_sendpdu(lp, RR_RSP, 1, 0, NULL); + break; + case 35: + llc_sendpdu(lp, RR_RSP, 0, 0, NULL); + break; + case 36: + if (IS_RSP(fr)) + llc_sendpdu(lp, RR_CMD, 0, 0, NULL); + else + llc_sendpdu(lp, RR_RSP, 0, 0, NULL); + break; + case 37: + llc_sendpdu(lp, SABME_CMD, 0, 0, NULL); + lp->f_flag = 0; + break; + case 38: + llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL); + break; + case 39: + lp->s_flag = 0; + break; + case 40: + lp->s_flag = 1; + break; + case 41: + 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 44: + if (lp->timer_state[ACK_TIMER] == TIMER_IDLE) + llc_start_timer(lp, ACK_TIMER); + break; + case 42: + llc_start_timer(lp, ACK_TIMER); + break; + case 43: + llc_start_timer(lp, REJ_TIMER); + break; + case 45: + llc_stop_timer(lp, ACK_TIMER); + break; + case 46: + llc_stop_timer(lp, ACK_TIMER); + lp->p_flag = 0; + break; + case 10: + if (lp->data_flag == 2) + llc_stop_timer(lp, REJ_TIMER); + break; + case 47: + llc_stop_timer(lp, REJ_TIMER); + break; + case 48: + 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 49: + llc_stop_timer(lp, P_TIMER); + llc_stop_timer(lp, REJ_TIMER); + llc_stop_timer(lp, BUSY_TIMER); + break; + case 50: + 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 (skb_peek(&lp->rtq) != NULL) + { + /* + * Re-transmit queue not empty + */ + llc_start_timer(lp, ACK_TIMER); + } + } + break; + case 51: + 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 52: + lp->data_flag = 2; + break; + case 53: + lp->data_flag = 0; + break; + case 54: + lp->data_flag = 1; + break; + case 55: + if (lp->data_flag == 0) + lp->data_flag = 1; + break; + case 56: + lp->p_flag = 0; + break; + case 57: + lp->p_flag = lp->f_flag; + break; + case 58: + lp->remote_busy = 0; + break; + case 59: + lp->retry_count = 0; + break; + case 60: + lp->retry_count++; + break; + case 61: + lp->vr = 0; + break; + case 62: + lp->vr++; + break; + case 63: + lp->vs = 0; + break; + case 64: + lp->vs = fr->i_hdr.nr; + break; + case 65: + 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 != 0) + { + llc_interpret_pseudo_code(lp, pc_label, skb, type); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may no longer be valid after this point. Be + * careful what is added! + */ + } +} + + +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 != 0) + { + llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME); + lp->state = timertr_entry[idx +1]; + } + lp->timer_state[t] = TIMER_IDLE; + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * And lp may have vanished in the event callback + */ +} + |