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/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>
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
53 int 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
116 void 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
143 void 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 incomming 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
176 void 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
512 void 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
584 void 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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.