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/slab.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
llc_data_request(llcptr lp,struct sk_buff * skb)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
disconnect_request(llcptr lp)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
connect_request(llcptr lp)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 incoming 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
llc_interpret_pseudo_code(llcptr lp,int pc_label,struct sk_buff * skb,char type)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 9:
199 if ((type != I_CMD) || (fr->i_hdr.i_pflag == 0))
200 break;
201 case 1:
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 2:
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 3:
224 lp->llc_mode = MODE_ABM;
225 lp->llc_callbacks|=LLC_CONN_CONFIRM;
226 break;
227 case 4:
228 skb_pull(skb, 4);
229 lp->inc_skb=skb;
230 lp->llc_callbacks|=LLC_DATA_INDIC;
231 break;
232 case 5:
233 lp->llc_mode = MODE_ADM;
234 lp->llc_callbacks|=LLC_DISC_INDICATION;
235 break;
236 case 70:
237 lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
238 break;
239 case 71:
240 lp->llc_callbacks|=LLC_RESET_INDIC_REM;
241 break;
242 case 7:
243 lp->llc_callbacks|=LLC_RST_CONFIRM;
244 break;
245 case 66:
246 lp->llc_callbacks|=LLC_FRMR_RECV;
247 break;
248 case 67:
249 lp->llc_callbacks|=LLC_FRMR_SENT;
250 break;
251 case 68:
252 lp->llc_callbacks|=LLC_REMOTE_BUSY;
253 break;
254 case 69:
255 lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
256 break;
257 case 11:
258 llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
259 break;
260 case 12:
261 llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
262 break;
263 case 13:
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 14:
271 llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
272 break;
273 case 15:
274 llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
275 5, (char *) &lp->frmr_info_fld);
276 break;
277 case 16:
278 llc_sendipdu(lp, I_CMD, 1, skb);
279 break;
280 case 17:
281 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
282 break;
283 case 18:
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 19:
291 llc_sendipdu(lp, I_CMD, 0, skb);
292 break;
293 case 20:
294 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
295 break;
296 case 21:
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 22:
304 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
305 break;
306 case 23:
307 llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
308 break;
309 case 24:
310 llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
311 break;
312 case 25:
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 26:
319 llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
320 break;
321 case 27:
322 llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
323 break;
324 case 28:
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 29:
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 30:
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 31:
349 llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
350 break;
351 case 32:
352 llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
353 break;
354 case 33:
355 llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
356 break;
357 case 34:
358 llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
359 break;
360 case 35:
361 llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
362 break;
363 case 36:
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 37:
370 llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
371 lp->f_flag = 0;
372 break;
373 case 38:
374 llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
375 break;
376 case 39:
377 lp->s_flag = 0;
378 break;
379 case 40:
380 lp->s_flag = 1;
381 break;
382 case 41:
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 44:
393 if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
394 llc_start_timer(lp, ACK_TIMER);
395 break;
396 case 42:
397 llc_start_timer(lp, ACK_TIMER);
398 break;
399 case 43:
400 llc_start_timer(lp, REJ_TIMER);
401 break;
402 case 45:
403 llc_stop_timer(lp, ACK_TIMER);
404 break;
405 case 46:
406 llc_stop_timer(lp, ACK_TIMER);
407 lp->p_flag = 0;
408 break;
409 case 10:
410 if (lp->data_flag == 2)
411 llc_stop_timer(lp, REJ_TIMER);
412 break;
413 case 47:
414 llc_stop_timer(lp, REJ_TIMER);
415 break;
416 case 48:
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 49:
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 50:
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 51:
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 52:
455 lp->data_flag = 2;
456 break;
457 case 53:
458 lp->data_flag = 0;
459 break;
460 case 54:
461 lp->data_flag = 1;
462 break;
463 case 55:
464 if (lp->data_flag == 0)
465 lp->data_flag = 1;
466 break;
467 case 56:
468 lp->p_flag = 0;
469 break;
470 case 57:
471 lp->p_flag = lp->f_flag;
472 break;
473 case 58:
474 lp->remote_busy = 0;
475 break;
476 case 59:
477 lp->retry_count = 0;
478 break;
479 case 60:
480 lp->retry_count++;
481 break;
482 case 61:
483 lp->vr = 0;
484 break;
485 case 62:
486 lp->vr++;
487 break;
488 case 63:
489 lp->vs = 0;
490 break;
491 case 64:
492 lp->vs = fr->i_hdr.nr;
493 break;
494 case 65:
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
llc_process_otype2_frame(llcptr lp,struct sk_buff * skb,char type)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 != 0)
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
llc_timer_expired(llcptr lp,int t)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 != 0)
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
616