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 53int 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 116void 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 143void 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 176void 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 512void 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 584void 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 616