| 1 | /* |
| 2 | * Copyright (c) University of British Columbia, 1984 |
| 3 | * Copyright (c) 1990 The Regents of the University of California. |
| 4 | * All rights reserved. |
| 5 | * |
| 6 | * This code is derived from software contributed to Berkeley by |
| 7 | * the Laboratory for Computation Vision and the Computer Science Department |
| 8 | * of the University of British Columbia. |
| 9 | * |
| 10 | * %sccs.include.redist.c% |
| 11 | * |
| 12 | * @(#)hd_input.c 7.4 (Berkeley) %G% |
| 13 | */ |
| 14 | |
| 15 | #include "param.h" |
| 16 | #include "systm.h" |
| 17 | #include "mbuf.h" |
| 18 | #include "domain.h" |
| 19 | #include "socket.h" |
| 20 | #include "protosw.h" |
| 21 | #include "errno.h" |
| 22 | #include "time.h" |
| 23 | #include "kernel.h" |
| 24 | |
| 25 | #include "../net/if.h" |
| 26 | |
| 27 | #include "hdlc.h" |
| 28 | #include "hd_var.h" |
| 29 | #include "x25.h" |
| 30 | |
| 31 | /* |
| 32 | * HDLC INPUT INTERFACE |
| 33 | * |
| 34 | * This routine is called when the HDLC physical device has |
| 35 | * completed reading a frame. |
| 36 | */ |
| 37 | |
| 38 | hdintr () |
| 39 | { |
| 40 | register struct mbuf *m; |
| 41 | register struct hdcb *hdp; |
| 42 | register struct ifnet *ifp; |
| 43 | register int s; |
| 44 | static struct ifnet *lastifp; |
| 45 | static struct hdcb *lasthdp; |
| 46 | |
| 47 | for (;;) { |
| 48 | s = splimp (); |
| 49 | IF_DEQUEUEIF(&hdintrq, m, ifp); |
| 50 | splx (s); |
| 51 | if (m == 0) |
| 52 | break; |
| 53 | if (m->m_len < HDHEADERLN) { |
| 54 | printf ("hdintr: packet too short (len=%d)\n", |
| 55 | m->m_len); |
| 56 | m_freem (m); |
| 57 | continue; |
| 58 | } |
| 59 | |
| 60 | /* |
| 61 | * look up the appropriate hdlc control block |
| 62 | */ |
| 63 | |
| 64 | if (ifp == lastifp) |
| 65 | hdp = lasthdp; |
| 66 | else { |
| 67 | for (hdp = hdcbhead; hdp; hdp = hdp->hd_next) |
| 68 | if (hdp->hd_ifp == ifp) |
| 69 | break; |
| 70 | if (hdp == 0) { |
| 71 | printf ("hdintr: unknown interface %x\n", ifp); |
| 72 | m_freem (m); |
| 73 | continue; |
| 74 | } |
| 75 | lastifp = ifp; |
| 76 | lasthdp = hdp; |
| 77 | } |
| 78 | |
| 79 | /* Process_rxframe returns FALSE if the frame was NOT queued |
| 80 | for the next higher layers. */ |
| 81 | if (process_rxframe (hdp, m) == FALSE) |
| 82 | m_freem (m); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | process_rxframe (hdp, fbuf) |
| 87 | register struct hdcb *hdp; |
| 88 | register struct mbuf *fbuf; |
| 89 | { |
| 90 | register int queued = FALSE, frametype, pf; |
| 91 | register struct Hdlc_frame *frame; |
| 92 | |
| 93 | frame = mtod (fbuf, struct Hdlc_frame *); |
| 94 | pf = ((struct Hdlc_iframe *) frame) -> pf; |
| 95 | |
| 96 | hd_trace (hdp, RX, frame); |
| 97 | if (frame -> address != ADDRESS_A && frame -> address != ADDRESS_B) |
| 98 | return (queued); |
| 99 | |
| 100 | switch ((frametype = hd_decode (hdp, frame)) + hdp->hd_state) { |
| 101 | case DM + DISC_SENT: |
| 102 | case UA + DISC_SENT: |
| 103 | /* |
| 104 | * Link now closed. Leave timer running |
| 105 | * so hd_timer() can periodically check the |
| 106 | * status of interface driver flag bit IFF_UP. |
| 107 | */ |
| 108 | hdp->hd_state = DISCONNECTED; |
| 109 | break; |
| 110 | |
| 111 | case DM + INIT: |
| 112 | case UA + INIT: |
| 113 | /* |
| 114 | * This is a non-standard state change needed for DCEs |
| 115 | * that do dynamic link selection. We can't go into the |
| 116 | * usual "SEND DM" state because a DM is a SARM in LAP. |
| 117 | */ |
| 118 | hd_writeinternal (hdp, SABM, POLLOFF); |
| 119 | hdp->hd_state = SABM_SENT; |
| 120 | SET_TIMER (hdp); |
| 121 | break; |
| 122 | |
| 123 | case SABM + DM_SENT: |
| 124 | case SABM + WAIT_SABM: |
| 125 | hd_writeinternal (hdp, UA, pf); |
| 126 | case UA + SABM_SENT: |
| 127 | case UA + WAIT_UA: |
| 128 | KILL_TIMER (hdp); |
| 129 | hd_initvars (hdp); |
| 130 | hdp->hd_state = ABM; |
| 131 | hd_message (hdp, "Link level operational"); |
| 132 | /* Notify the packet level - to send RESTART. */ |
| 133 | (void) pk_ctlinput (PRC_LINKUP, hdp->hd_xcp); |
| 134 | break; |
| 135 | |
| 136 | case SABM + SABM_SENT: |
| 137 | /* Got a SABM collision. Acknowledge the remote's SABM |
| 138 | via UA but still wait for UA. */ |
| 139 | hd_writeinternal (hdp, UA, pf); |
| 140 | break; |
| 141 | |
| 142 | case SABM + ABM: |
| 143 | /* Request to reset the link from the remote. */ |
| 144 | KILL_TIMER (hdp); |
| 145 | hd_message (hdp, "Link reset"); |
| 146 | #ifdef HDLCDEBUG |
| 147 | hd_dumptrace (hdp); |
| 148 | #endif |
| 149 | hd_flush (hdp->hd_ifp); |
| 150 | hd_writeinternal (hdp, UA, pf); |
| 151 | hd_initvars (hdp); |
| 152 | (void) pk_ctlinput (PRC_LINKRESET, hdp->hd_xcp); |
| 153 | hdp->hd_resets++; |
| 154 | break; |
| 155 | |
| 156 | case SABM + WAIT_UA: |
| 157 | hd_writeinternal (hdp, UA, pf); |
| 158 | break; |
| 159 | |
| 160 | case DM + ABM: |
| 161 | hd_message (hdp, "DM received: link down"); |
| 162 | #ifdef HDLCDEBUG |
| 163 | hd_dumptrace (hdp); |
| 164 | #endif |
| 165 | (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp); |
| 166 | hd_flush (hdp->hd_ifp); |
| 167 | case DM + DM_SENT: |
| 168 | case DM + WAIT_SABM: |
| 169 | case DM + WAIT_UA: |
| 170 | hd_writeinternal (hdp, SABM, pf); |
| 171 | hdp->hd_state = SABM_SENT; |
| 172 | SET_TIMER (hdp); |
| 173 | break; |
| 174 | |
| 175 | case DISC + INIT: |
| 176 | case DISC + DM_SENT: |
| 177 | case DISC + SABM_SENT: |
| 178 | /* Note: This is a non-standard state change. */ |
| 179 | hd_writeinternal (hdp, UA, pf); |
| 180 | hd_writeinternal (hdp, SABM, POLLOFF); |
| 181 | hdp->hd_state = SABM_SENT; |
| 182 | SET_TIMER (hdp); |
| 183 | break; |
| 184 | |
| 185 | case DISC + WAIT_UA: |
| 186 | hd_writeinternal (hdp, DM, pf); |
| 187 | SET_TIMER (hdp); |
| 188 | hdp->hd_state = DM_SENT; |
| 189 | break; |
| 190 | |
| 191 | case DISC + ABM: |
| 192 | hd_message (hdp, "DISC received: link down"); |
| 193 | (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp); |
| 194 | case DISC + WAIT_SABM: |
| 195 | hd_writeinternal (hdp, UA, pf); |
| 196 | hdp->hd_state = DM_SENT; |
| 197 | SET_TIMER (hdp); |
| 198 | break; |
| 199 | |
| 200 | case UA + ABM: |
| 201 | hd_message (hdp, "UA received: link down"); |
| 202 | (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp); |
| 203 | case UA + WAIT_SABM: |
| 204 | hd_writeinternal (hdp, DM, pf); |
| 205 | hdp->hd_state = DM_SENT; |
| 206 | SET_TIMER (hdp); |
| 207 | break; |
| 208 | |
| 209 | case FRMR + DM_SENT: |
| 210 | hd_writeinternal (hdp, SABM, pf); |
| 211 | hdp->hd_state = SABM_SENT; |
| 212 | SET_TIMER (hdp); |
| 213 | break; |
| 214 | |
| 215 | case FRMR + WAIT_SABM: |
| 216 | hd_writeinternal (hdp, DM, pf); |
| 217 | hdp->hd_state = DM_SENT; |
| 218 | SET_TIMER (hdp); |
| 219 | break; |
| 220 | |
| 221 | case FRMR + ABM: |
| 222 | hd_message (hdp, "FRMR received: link down"); |
| 223 | (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp); |
| 224 | #ifdef HDLCDEBUG |
| 225 | hd_dumptrace (hdp); |
| 226 | #endif |
| 227 | hd_flush (hdp->hd_ifp); |
| 228 | hd_writeinternal (hdp, SABM, pf); |
| 229 | hdp->hd_state = WAIT_UA; |
| 230 | SET_TIMER (hdp); |
| 231 | break; |
| 232 | |
| 233 | case RR + ABM: |
| 234 | case RNR + ABM: |
| 235 | case REJ + ABM: |
| 236 | process_sframe (hdp, (struct Hdlc_sframe *)frame, frametype); |
| 237 | break; |
| 238 | |
| 239 | case IFRAME + ABM: |
| 240 | queued = process_iframe (hdp, fbuf, (struct Hdlc_iframe *)frame); |
| 241 | break; |
| 242 | |
| 243 | case IFRAME + SABM_SENT: |
| 244 | case RR + SABM_SENT: |
| 245 | case RNR + SABM_SENT: |
| 246 | case REJ + SABM_SENT: |
| 247 | hd_writeinternal (hdp, DM, POLLON); |
| 248 | hdp->hd_state = DM_SENT; |
| 249 | SET_TIMER (hdp); |
| 250 | break; |
| 251 | |
| 252 | case IFRAME + WAIT_SABM: |
| 253 | case RR + WAIT_SABM: |
| 254 | case RNR + WAIT_SABM: |
| 255 | case REJ + WAIT_SABM: |
| 256 | hd_writeinternal (hdp, FRMR, POLLOFF); |
| 257 | SET_TIMER (hdp); |
| 258 | break; |
| 259 | |
| 260 | case ILLEGAL + SABM_SENT: |
| 261 | hdp->hd_unknown++; |
| 262 | hd_writeinternal (hdp, DM, POLLOFF); |
| 263 | hdp->hd_state = DM_SENT; |
| 264 | SET_TIMER (hdp); |
| 265 | break; |
| 266 | |
| 267 | case ILLEGAL + ABM: |
| 268 | hd_message (hdp, "Unknown frame received: link down"); |
| 269 | (void) pk_ctlinput (PRC_LINKDOWN, hdp->hd_xcp); |
| 270 | case ILLEGAL + WAIT_SABM: |
| 271 | hdp->hd_unknown++; |
| 272 | #ifdef HDLCDEBUG |
| 273 | hd_dumptrace (hdp); |
| 274 | #endif |
| 275 | hd_writeinternal (hdp, FRMR, POLLOFF); |
| 276 | hdp->hd_state = WAIT_SABM; |
| 277 | SET_TIMER (hdp); |
| 278 | break; |
| 279 | } |
| 280 | |
| 281 | return (queued); |
| 282 | } |
| 283 | |
| 284 | process_iframe (hdp, fbuf, frame) |
| 285 | register struct hdcb *hdp; |
| 286 | struct mbuf *fbuf; |
| 287 | register struct Hdlc_iframe *frame; |
| 288 | { |
| 289 | register int nr = frame -> nr, |
| 290 | ns = frame -> ns, |
| 291 | pf = frame -> pf; |
| 292 | register int queued = FALSE; |
| 293 | |
| 294 | /* |
| 295 | * Validate the iframe's N(R) value. It's N(R) value must be in |
| 296 | * sync with our V(S) value and our "last received nr". |
| 297 | */ |
| 298 | |
| 299 | if (valid_nr (hdp, nr, FALSE) == FALSE) { |
| 300 | frame_reject (hdp, Z, frame); |
| 301 | return (queued); |
| 302 | } |
| 303 | |
| 304 | |
| 305 | /* |
| 306 | * This section tests the IFRAME for proper sequence. That is, it's |
| 307 | * sequence number N(S) MUST be equal to V(S). |
| 308 | */ |
| 309 | |
| 310 | if (ns != hdp->hd_vr) { |
| 311 | hdp->hd_invalid_ns++; |
| 312 | if (pf || (hdp->hd_condition & REJ_CONDITION) == 0) { |
| 313 | hdp->hd_condition |= REJ_CONDITION; |
| 314 | /* |
| 315 | * Flush the transmit queue. This is ugly but we |
| 316 | * have no choice. A reject response must be |
| 317 | * immediately sent to the DCE. Failure to do so |
| 318 | * may result in another out of sequence iframe |
| 319 | * arriving (and thus sending another reject) |
| 320 | * before the first reject is transmitted. This |
| 321 | * will cause the DCE to receive two or more |
| 322 | * rejects back to back, which must never happen. |
| 323 | */ |
| 324 | hd_flush (hdp->hd_ifp); |
| 325 | hd_writeinternal (hdp, REJ, pf); |
| 326 | } |
| 327 | return (queued); |
| 328 | } |
| 329 | hdp->hd_condition &= ~REJ_CONDITION; |
| 330 | |
| 331 | /* |
| 332 | * This section finally tests the IFRAME's sequence number against |
| 333 | * the window size (K) and the sequence number of the last frame |
| 334 | * we have acknowledged. If the IFRAME is completely correct then |
| 335 | * it is queued for the packet level. |
| 336 | */ |
| 337 | |
| 338 | if (ns != (hdp->hd_lasttxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) { |
| 339 | hdp->hd_vr = (hdp->hd_vr + 1) % MODULUS; |
| 340 | if (pf == 1) { |
| 341 | /* Must generate a RR or RNR with final bit on. */ |
| 342 | hd_writeinternal (hdp, RR, POLLON); |
| 343 | } else |
| 344 | /* |
| 345 | * Hopefully we can piggyback the RR, if not we will generate |
| 346 | * a RR when T3 timer expires. |
| 347 | */ |
| 348 | if (hdp -> hd_rrtimer == 0) |
| 349 | hdp->hd_rrtimer = hd_t3; |
| 350 | |
| 351 | /* Forward iframe to packet level of X.25. */ |
| 352 | fbuf -> m_data += HDHEADERLN; |
| 353 | fbuf -> m_len -= HDHEADERLN; |
| 354 | #ifdef BSD4_3 |
| 355 | fbuf->m_act = 0; /* probably not necessary */ |
| 356 | #else |
| 357 | { |
| 358 | register struct mbuf *m; |
| 359 | |
| 360 | for (m = fbuf; m -> m_next; m = m -> m_next) |
| 361 | m -> m_act = (struct mbuf *) 0; |
| 362 | m -> m_act = (struct mbuf *) 1; |
| 363 | } |
| 364 | #endif |
| 365 | pk_input (fbuf, hdp->hd_xcp); |
| 366 | queued = TRUE; |
| 367 | hd_start (hdp); |
| 368 | } else { |
| 369 | /* |
| 370 | * Here if the remote station has transmitted more iframes then |
| 371 | * the number which have been acknowledged plus K. |
| 372 | */ |
| 373 | hdp->hd_invalid_ns++; |
| 374 | frame_reject (hdp, W, frame); |
| 375 | } |
| 376 | return (queued); |
| 377 | } |
| 378 | |
| 379 | /* |
| 380 | * This routine is used to determine if a value (the middle parameter) |
| 381 | * is between two other values. The low value is the first parameter |
| 382 | * the high value is the last parameter. The routine checks the middle |
| 383 | * value to see if it is within the range of the first and last values. |
| 384 | * The reason we need this routine is the values are modulo some base |
| 385 | * hence a simple test for greater or less than is not sufficient. |
| 386 | */ |
| 387 | |
| 388 | bool |
| 389 | range_check (rear, value, front) |
| 390 | int rear, |
| 391 | value, |
| 392 | front; |
| 393 | { |
| 394 | register bool result = FALSE; |
| 395 | |
| 396 | if (front > rear) |
| 397 | result = (rear <= value) && (value <= front); |
| 398 | else |
| 399 | result = (rear <= value) || (value <= front); |
| 400 | |
| 401 | return (result); |
| 402 | } |
| 403 | |
| 404 | /* |
| 405 | * This routine handles all the frame reject conditions which can |
| 406 | * arise as a result of secondary processing. The frame reject |
| 407 | * condition Y (frame length error) are handled elsewhere. |
| 408 | */ |
| 409 | |
| 410 | static |
| 411 | frame_reject (hdp, rejectcode, frame) |
| 412 | struct hdcb *hdp; |
| 413 | struct Hdlc_iframe *frame; |
| 414 | { |
| 415 | register struct Frmr_frame *frmr = &hd_frmr; |
| 416 | |
| 417 | frmr -> frmr_control = ((struct Hdlc_frame *) frame) -> control; |
| 418 | |
| 419 | frmr -> frmr_ns = frame -> ns; |
| 420 | frmr -> frmr_f1_0 = 0; |
| 421 | frmr -> frmr_nr = frame -> nr; |
| 422 | frmr -> frmr_f2_0 = 0; |
| 423 | |
| 424 | frmr -> frmr_0000 = 0; |
| 425 | frmr -> frmr_w = frmr -> frmr_x = frmr -> frmr_y = |
| 426 | frmr -> frmr_z = 0; |
| 427 | switch (rejectcode) { |
| 428 | case Z: |
| 429 | frmr -> frmr_z = 1;/* invalid N(R). */ |
| 430 | break; |
| 431 | |
| 432 | case Y: |
| 433 | frmr -> frmr_y = 1;/* iframe length error. */ |
| 434 | break; |
| 435 | |
| 436 | case X: |
| 437 | frmr -> frmr_x = 1;/* invalid information field. */ |
| 438 | frmr -> frmr_w = 1; |
| 439 | break; |
| 440 | |
| 441 | case W: |
| 442 | frmr -> frmr_w = 1;/* invalid N(S). */ |
| 443 | } |
| 444 | |
| 445 | hd_writeinternal (hdp, FRMR, POLLOFF); |
| 446 | |
| 447 | hdp->hd_state = WAIT_SABM; |
| 448 | SET_TIMER (hdp); |
| 449 | } |
| 450 | |
| 451 | /* |
| 452 | * This procedure is invoked when ever we receive a supervisor |
| 453 | * frame such as RR, RNR and REJ. All processing for these |
| 454 | * frames is done here. |
| 455 | */ |
| 456 | |
| 457 | process_sframe (hdp, frame, frametype) |
| 458 | register struct hdcb *hdp; |
| 459 | register struct Hdlc_sframe *frame; |
| 460 | int frametype; |
| 461 | { |
| 462 | register int nr = frame -> nr, pf = frame -> pf, pollbit = 0; |
| 463 | |
| 464 | if (valid_nr (hdp, nr, pf) == TRUE) { |
| 465 | switch (frametype) { |
| 466 | case RR: |
| 467 | hdp->hd_condition &= ~REMOTE_RNR_CONDITION; |
| 468 | break; |
| 469 | |
| 470 | case RNR: |
| 471 | hdp->hd_condition |= REMOTE_RNR_CONDITION; |
| 472 | hdp->hd_retxcnt = 0; |
| 473 | break; |
| 474 | |
| 475 | case REJ: |
| 476 | hdp->hd_condition &= ~REMOTE_RNR_CONDITION; |
| 477 | rej_routine (hdp, nr); |
| 478 | } |
| 479 | |
| 480 | if (pf == 1) { |
| 481 | hdp->hd_retxcnt = 0; |
| 482 | hdp->hd_condition &= ~TIMER_RECOVERY_CONDITION; |
| 483 | |
| 484 | /* If any iframes have been queued because of the |
| 485 | timer condition, transmit then now. */ |
| 486 | if (hdp->hd_condition & REMOTE_RNR_CONDITION) { |
| 487 | /* Remote is busy or timer condition, so only |
| 488 | send one. */ |
| 489 | if (hdp->hd_vs != hdp->hd_retxqi) |
| 490 | hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], pollbit); |
| 491 | } |
| 492 | else /* Flush the retransmit list first. */ |
| 493 | while (hdp->hd_vs != hdp->hd_retxqi) |
| 494 | hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF); |
| 495 | } |
| 496 | |
| 497 | hd_start (hdp); |
| 498 | } else |
| 499 | frame_reject (hdp, Z, (struct Hdlc_iframe *)frame); /* Invalid N(R). */ |
| 500 | } |
| 501 | |
| 502 | /* |
| 503 | * This routine tests the validity of the N(R) which we have received. |
| 504 | * If it is ok, then all the iframes which it acknowledges (if any) |
| 505 | * will be freed. |
| 506 | */ |
| 507 | |
| 508 | bool |
| 509 | valid_nr (hdp, nr, finalbit) |
| 510 | register struct hdcb *hdp; |
| 511 | register int finalbit; |
| 512 | { |
| 513 | /* Make sure it really does acknowledge something. */ |
| 514 | if (hdp->hd_lastrxnr == nr) |
| 515 | return (TRUE); |
| 516 | |
| 517 | /* |
| 518 | * This section validates the frame's N(R) value. It's N(R) value |
| 519 | * must be in syncronization with our V(S) value and our "last |
| 520 | * received nr" variable. If it is correct then we are able to send |
| 521 | * more IFRAME's, else frame reject condition is entered. |
| 522 | */ |
| 523 | |
| 524 | if (range_check (hdp->hd_lastrxnr, nr, hdp->hd_vs) == FALSE) { |
| 525 | if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && |
| 526 | range_check (hdp->hd_vs, nr, hdp->hd_xx) == TRUE) |
| 527 | hdp->hd_vs = nr; |
| 528 | |
| 529 | else { |
| 530 | hdp->hd_invalid_nr++; |
| 531 | return (FALSE); |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | /* |
| 536 | * If we get to here, we do have a valid frame but it might be out |
| 537 | * of sequence. However, we should still accept the receive state |
| 538 | * number N(R) since it has already passed our previous test and it |
| 539 | * does acknowledge frames which we are sending. |
| 540 | */ |
| 541 | |
| 542 | KILL_TIMER (hdp); |
| 543 | free_iframes (hdp, &nr, finalbit);/* Free all acknowledged iframes */ |
| 544 | if (nr != hdp->hd_vs) |
| 545 | SET_TIMER (hdp); |
| 546 | |
| 547 | return (TRUE); |
| 548 | } |
| 549 | |
| 550 | /* |
| 551 | * This routine determines how many iframes need to be retransmitted. |
| 552 | * It then resets the Send State Variable V(S) to accomplish this. |
| 553 | */ |
| 554 | |
| 555 | static |
| 556 | rej_routine (hdp, rejnr) |
| 557 | register struct hdcb *hdp; |
| 558 | register int rejnr; |
| 559 | { |
| 560 | register int anchor; |
| 561 | |
| 562 | /* |
| 563 | * Flush the output queue. Any iframes queued for |
| 564 | * transmission will be out of sequence. |
| 565 | */ |
| 566 | |
| 567 | hd_flush (hdp->hd_ifp); |
| 568 | |
| 569 | /* |
| 570 | * Determine how many frames should be re-transmitted. In the case |
| 571 | * of a normal REJ this should be 1 to K. In the case of a timer |
| 572 | * recovery REJ (ie. a REJ with the Final Bit on) this could be 0. |
| 573 | */ |
| 574 | |
| 575 | anchor = hdp->hd_vs; |
| 576 | if (hdp->hd_condition & TIMER_RECOVERY_CONDITION) |
| 577 | anchor = hdp->hd_xx; |
| 578 | |
| 579 | anchor = (anchor - rejnr + 8) % MODULUS; |
| 580 | |
| 581 | if (anchor > 0) { |
| 582 | |
| 583 | /* There is at least one iframe to retransmit. */ |
| 584 | KILL_TIMER (hdp); |
| 585 | hdp->hd_vs = rejnr; |
| 586 | |
| 587 | while (hdp->hd_vs != hdp->hd_retxqi) |
| 588 | hd_send_iframe (hdp, hdp->hd_retxq[hdp->hd_vs], POLLOFF); |
| 589 | |
| 590 | } |
| 591 | hd_start (hdp); |
| 592 | } |
| 593 | |
| 594 | /* |
| 595 | * This routine frees iframes from the retransmit queue. It is called |
| 596 | * when a previously written iframe is acknowledged. |
| 597 | */ |
| 598 | |
| 599 | static |
| 600 | free_iframes (hdp, nr, finalbit) |
| 601 | register struct hdcb *hdp; |
| 602 | int *nr; |
| 603 | register int finalbit; |
| 604 | |
| 605 | { |
| 606 | register int i, k; |
| 607 | |
| 608 | /* |
| 609 | * We need to do the following because of a funny quirk in the |
| 610 | * protocol. This case occures when in Timer recovery condition |
| 611 | * we get a N(R) which acknowledges all the outstanding iframes |
| 612 | * but with the Final Bit off. In this case we need to save the last |
| 613 | * iframe for possible retransmission even though it has already been |
| 614 | * acknowledged! |
| 615 | */ |
| 616 | |
| 617 | if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && *nr == hdp->hd_xx && finalbit == 0) { |
| 618 | *nr = (*nr - 1 + 8) % MODULUS; |
| 619 | /* printf ("QUIRK\n"); */ |
| 620 | } |
| 621 | |
| 622 | k = (*nr - hdp->hd_lastrxnr + 8) % MODULUS; |
| 623 | |
| 624 | /* Loop here freeing all acknowledged iframes. */ |
| 625 | for (i = 0; i < k; ++i) { |
| 626 | m_freem (hdp->hd_retxq[hdp->hd_lastrxnr]); |
| 627 | hdp->hd_retxq[hdp->hd_lastrxnr] = 0; |
| 628 | hdp->hd_lastrxnr = (hdp->hd_lastrxnr + 1) % MODULUS; |
| 629 | } |
| 630 | |
| 631 | } |