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.
6 * Class 2 llc algorithm.
7 * Pseudocode interpreter, transition table lookup,
8 * data_request & indicate primitives...
10 * Code for initialization, termination, registration and
13 * Copyright Tim Alpaerts,
14 * <Tim_Alpaerts@toyota-motor-europe.com>
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.
22 * Alan Cox : Chainsawed into Linux format
23 * Modified to use llc_ names
26 * This file must be processed by sed before it can be compiled.
29 #include <linux/types.h>
30 #include <linux/kernel.h>
31 #include <linux/malloc.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>
39 #include "pseudo/pseudocode.h"
40 #include "transit/pdutr.h"
41 #include "transit/timertr.h"
42 #include <net/llc_frame.h>
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.
53 int llc_data_request(llcptr lp
, struct sk_buff
*skb
)
55 if (skb_headroom(skb
) < (lp
->dev
->hard_header_len
+4)){
56 printk("cl2llc: data_request() not enough headroom in skb\n");
62 if ((lp
->state
!= NORMAL
) && (lp
->state
!= BUSY
) && (lp
->state
!= REJECT
))
64 printk("cl2llc: data_request() while no llc connection\n");
69 { /* if the remote llc is BUSY, */
70 ADD_TO_ATQ(skb
); /* save skb in the await transmit queue */
76 * Else proceed with xmit
83 llc_interpret_pseudo_code(lp
, NORMAL2
, skb
, NO_FRAME
);
85 llc_interpret_pseudo_code(lp
, NORMAL1
, skb
, NO_FRAME
);
89 llc_interpret_pseudo_code(lp
, BUSY2
, skb
, NO_FRAME
);
91 llc_interpret_pseudo_code(lp
, BUSY1
, skb
, NO_FRAME
);
95 llc_interpret_pseudo_code(lp
, REJECT2
, skb
, NO_FRAME
);
97 llc_interpret_pseudo_code(lp
, REJECT1
, skb
, NO_FRAME
);
101 if(lp
->llc_callbacks
)
113 * Disconnect_request() requests that the llc to terminate a connection
116 void disconnect_request(llcptr lp
)
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
))
126 llc_interpret_pseudo_code(lp
, SH1
, NULL
, NO_FRAME
);
127 if(lp
->llc_callbacks
)
133 * lp may be invalid after the callback
140 * Connect_request() requests that the llc to start a connection
143 void connect_request(llcptr lp
)
145 if (lp
->state
== ADM
)
148 llc_interpret_pseudo_code(lp
, ADM1
, NULL
, NO_FRAME
);
149 if(lp
->llc_callbacks
)
155 * lp may be invalid after the callback
162 * Interpret_pseudo_code() executes the actions in the connection component
163 * state transition table. Table 4 in document on p88.
165 * If this function is called to handle an incomming pdu, skb will point
166 * to the buffer with the pdu and type will contain the decoded pdu type.
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.
172 * If called because a timer expired no skb is passed, and there is no
176 void llc_interpret_pseudo_code(llcptr lp
, int pc_label
, struct sk_buff
*skb
,
179 short int pc
; /* program counter in pseudo code array */
180 char p_flag_received
;
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
;
188 fr
= (frameptr
) skb
->data
;
193 pc
= pseudo_code_idx
[pc_label
];
194 while(pseudo_code
[pc
])
196 switch(pseudo_code
[pc
])
199 if ((type
!= I_CMD
) || (fr
->i_hdr
.i_pflag
== 0))
203 llc_stop_timer(lp
, BUSY_TIMER
);
204 if ((lp
->state
== NORMAL
) ||
205 (lp
->state
== REJECT
) ||
208 skb2
= llc_pull_from_atq(lp
);
210 llc_start_timer(lp
, ACK_TIMER
);
213 llc_sendipdu( lp
, I_CMD
, 0, skb2
);
214 skb2
= llc_pull_from_atq(lp
);
219 lp
->state
= NORMAL
; /* needed to eliminate connect_response() */
220 lp
->llc_mode
= MODE_ABM
;
221 lp
->llc_callbacks
|=LLC_CONN_INDICATION
;
224 lp
->llc_mode
= MODE_ABM
;
225 lp
->llc_callbacks
|=LLC_CONN_CONFIRM
;
230 lp
->llc_callbacks
|=LLC_DATA_INDIC
;
233 lp
->llc_mode
= MODE_ADM
;
234 lp
->llc_callbacks
|=LLC_DISC_INDICATION
;
237 lp
->llc_callbacks
|=LLC_RESET_INDIC_LOC
;
240 lp
->llc_callbacks
|=LLC_RESET_INDIC_REM
;
243 lp
->llc_callbacks
|=LLC_RST_CONFIRM
;
246 lp
->llc_callbacks
|=LLC_FRMR_RECV
;
249 lp
->llc_callbacks
|=LLC_FRMR_SENT
;
252 lp
->llc_callbacks
|=LLC_REMOTE_BUSY
;
255 lp
->llc_callbacks
|=LLC_REMOTE_NOTBUSY
;
258 llc_sendpdu(lp
, DISC_CMD
, lp
->f_flag
, 0, NULL
);
261 llc_sendpdu(lp
, DM_RSP
, 0, 0, NULL
);
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
);
271 llc_sendpdu(lp
, FRMR_RSP
, 0, 5, (char *) &lp
->frmr_info_fld
);
274 llc_sendpdu(lp
, FRMR_RSP
, lp
->p_flag
,
275 5, (char *) &lp
->frmr_info_fld
);
278 llc_sendipdu(lp
, I_CMD
, 1, skb
);
281 resend_count
= llc_resend_ipdu(lp
, fr
->i_hdr
.nr
, I_CMD
, 1);
284 resend_count
= llc_resend_ipdu(lp
, fr
->i_hdr
.nr
, I_CMD
, 1);
285 if (resend_count
== 0)
287 llc_sendpdu(lp
, RR_CMD
, 1, 0, NULL
);
291 llc_sendipdu(lp
, I_CMD
, 0, skb
);
294 resend_count
= llc_resend_ipdu(lp
, fr
->i_hdr
.nr
, I_CMD
, 0);
297 resend_count
= llc_resend_ipdu(lp
, fr
->i_hdr
.nr
, I_CMD
, 0);
298 if (resend_count
== 0)
300 llc_sendpdu(lp
, RR_CMD
, 0, 0, NULL
);
304 resend_count
= llc_resend_ipdu(lp
, fr
->i_hdr
.nr
, I_RSP
, 1);
307 llc_sendpdu(lp
, REJ_CMD
, 1, 0, NULL
);
310 llc_sendpdu(lp
, REJ_RSP
, 1, 0, NULL
);
314 llc_sendpdu(lp
, REJ_CMD
, 0, 0, NULL
);
316 llc_sendpdu(lp
, REJ_RSP
, 0, 0, NULL
);
319 llc_sendpdu(lp
, RNR_CMD
, 1, 0, NULL
);
322 llc_sendpdu(lp
, RNR_RSP
, 1, 0, NULL
);
326 llc_sendpdu(lp
, RNR_CMD
, 0, 0, NULL
);
328 llc_sendpdu(lp
, RNR_RSP
, 0, 0, NULL
);
331 if (lp
->remote_busy
== 0)
334 llc_start_timer(lp
, BUSY_TIMER
);
335 lp
->llc_callbacks
|=LLC_REMOTE_BUSY
;
337 else if (lp
->timer_state
[BUSY_TIMER
] == TIMER_IDLE
)
339 llc_start_timer(lp
, BUSY_TIMER
);
344 llc_sendpdu(lp
, RNR_CMD
, 0, 0, NULL
);
346 llc_sendpdu(lp
, RNR_RSP
, 0, 0, NULL
);
349 llc_sendpdu(lp
, RR_CMD
, 1, 0, NULL
);
352 llc_sendpdu(lp
, RR_CMD
, 1, 0, NULL
);
355 llc_sendpdu(lp
, RR_RSP
, 1, 0, NULL
);
358 llc_sendpdu(lp
, RR_RSP
, 1, 0, NULL
);
361 llc_sendpdu(lp
, RR_RSP
, 0, 0, NULL
);
365 llc_sendpdu(lp
, RR_CMD
, 0, 0, NULL
);
367 llc_sendpdu(lp
, RR_RSP
, 0, 0, NULL
);
370 llc_sendpdu(lp
, SABME_CMD
, 0, 0, NULL
);
374 llc_sendpdu(lp
, UA_RSP
, lp
->f_flag
, 0, NULL
);
383 if(lp
->timer_state
[P_TIMER
] == TIMER_RUNNING
)
384 llc_stop_timer(lp
, P_TIMER
);
385 llc_start_timer(lp
, P_TIMER
);
393 if (lp
->timer_state
[ACK_TIMER
] == TIMER_IDLE
)
394 llc_start_timer(lp
, ACK_TIMER
);
397 llc_start_timer(lp
, ACK_TIMER
);
400 llc_start_timer(lp
, REJ_TIMER
);
403 llc_stop_timer(lp
, ACK_TIMER
);
406 llc_stop_timer(lp
, ACK_TIMER
);
410 if (lp
->data_flag
== 2)
411 llc_stop_timer(lp
, REJ_TIMER
);
414 llc_stop_timer(lp
, REJ_TIMER
);
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
);
423 llc_stop_timer(lp
, P_TIMER
);
424 llc_stop_timer(lp
, REJ_TIMER
);
425 llc_stop_timer(lp
, BUSY_TIMER
);
428 ack_count
= llc_free_acknowledged_skbs(lp
,
429 (unsigned char) fr
->s_hdr
.nr
);
433 llc_stop_timer(lp
, ACK_TIMER
);
434 if (skb_peek(&lp
->rtq
) != NULL
)
437 * Re-transmit queue not empty
439 llc_start_timer(lp
, ACK_TIMER
);
445 p_flag_received
= fr
->u_hdr
.u_pflag
;
447 p_flag_received
= fr
->i_hdr
.i_pflag
;
448 if ((fr
->pdu_hdr
.ssap
& 0x01) && (p_flag_received
))
451 llc_stop_timer(lp
, P_TIMER
);
464 if (lp
->data_flag
== 0)
471 lp
->p_flag
= lp
->f_flag
;
492 lp
->vs
= fr
->i_hdr
.nr
;
496 lp
->f_flag
= fr
->u_hdr
.u_pflag
;
498 lp
->f_flag
= fr
->i_hdr
.i_pflag
;
508 * Process_otype2_frame will handle incoming frames
509 * for 802.2 Type 2 Procedure.
512 void llc_process_otype2_frame(llcptr lp
, struct sk_buff
*skb
, char type
)
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 */
520 fr
= (frameptr
) skb
->data
;
523 p_flag_received
= fr
->u_hdr
.u_pflag
;
525 p_flag_received
= fr
->i_hdr
.i_pflag
;
529 /* Compute index in transition table: */
532 idx
= (idx
<< 1) + p_flag_received
;
544 idx
= (idx
<< 1) + lp
->p_flag
;
552 validation
= llc_validate_seq_nos(lp
, fr
);
559 idx
= (idx
<< 1) + p_flag_received
;
560 idx
= (idx
<< 1) + lp
->p_flag
;
562 printk("llc_proc: bad state\n");
565 idx
= (idx
<< 1) + pdutr_offset
[lp
->state
];
566 lp
->state
= pdutr_entry
[idx
+1];
567 pc_label
= pdutr_entry
[idx
];
570 llc_interpret_pseudo_code(lp
, pc_label
, skb
, type
);
571 if(lp
->llc_callbacks
)
577 * lp may no longer be valid after this point. Be
578 * careful what is added!
584 void llc_timer_expired(llcptr lp
, int t
)
586 int idx
; /* index in transition table */
587 int pc_label
; /* action to perform, from tr tbl */
589 lp
->timer_state
[t
] = TIMER_EXPIRED
;
590 idx
= lp
->state
; /* Compute index in transition table: */
591 idx
= (idx
<< 2) + t
;
593 if (lp
->retry_count
>= lp
->n2
)
595 idx
= (idx
<< 1) + lp
->s_flag
;
596 idx
= (idx
<< 1) + lp
->p_flag
;
597 idx
= idx
<< 1; /* 2 bytes per entry: action & newstate */
599 pc_label
= timertr_entry
[idx
];
602 llc_interpret_pseudo_code(lp
, pc_label
, NULL
, NO_FRAME
);
603 lp
->state
= timertr_entry
[idx
+1];
605 lp
->timer_state
[t
] = TIMER_IDLE
;
606 if(lp
->llc_callbacks
)
612 * And lp may have vanished in the event callback