* Copyright (c) University of British Columbia, 1984
* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* the Laboratory for Computation Vision and the Computer Science Department
* of the University of British Columbia.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* From: @(#)hd_input.c 7.7 (Berkeley) 5/29/91
* $Id: hd_input.c,v 1.2 1993/09/09 23:20:21 rgrimes Exp $
frame_reject (struct hdcb
*hdp
, int rejectcode
, struct Hdlc_iframe
*frame
);
rej_routine (struct hdcb
*hdp
, int rejnr
);
free_iframes (struct hdcb
*hdp
, int *nr
, int finalbit
);
* This routine is called when the HDLC physical device has
* completed reading a frame.
register struct hdcb
*hdp
;
register struct ifnet
*ifp
;
extern struct ifqueue pkintrq
;
static struct ifnet
*lastifp
;
static struct hdcb
*lasthdp
;
IF_DEQUEUE (&hdintrq
, m
);
if (m
->m_len
< HDHEADERLN
) {
printf ("hdintr: packet too short (len=%d)\n",
if ((m
->m_flags
& M_PKTHDR
) == 0)
* look up the appropriate hdlc control block
for (hdp
= hdcbhead
; hdp
; hdp
= hdp
->hd_next
)
printf ("hdintr: unknown interface %x\n", ifp
);
/* Process_rxframe returns FALSE if the frame was NOT queued
for the next higher layers. */
if (process_rxframe (hdp
, m
) == FALSE
)
process_rxframe (hdp
, fbuf
)
register struct hdcb
*hdp
;
register struct mbuf
*fbuf
;
register int queued
= FALSE
, frametype
, pf
;
register struct Hdlc_frame
*frame
;
frame
= mtod (fbuf
, struct Hdlc_frame
*);
pf
= ((struct Hdlc_iframe
*) frame
) -> pf
;
hd_trace (hdp
, RX
, frame
);
if (frame
-> address
!= ADDRESS_A
&& frame
-> address
!= ADDRESS_B
)
switch ((frametype
= hd_decode (hdp
, frame
)) + hdp
->hd_state
) {
* Link now closed. Leave timer running
* so hd_timer() can periodically check the
* status of interface driver flag bit IFF_UP.
hdp
->hd_state
= DISCONNECTED
;
* This is a non-standard state change needed for DCEs
* that do dynamic link selection. We can't go into the
* usual "SEND DM" state because a DM is a SARM in LAP.
hd_writeinternal (hdp
, SABM
, POLLOFF
);
hdp
->hd_state
= SABM_SENT
;
hd_writeinternal (hdp
, UA
, pf
);
hd_message (hdp
, "Link level operational");
/* Notify the packet level - to send RESTART. */
(void) pk_ctlinput (PRC_LINKUP
, hdp
->hd_pkp
);
/* Got a SABM collision. Acknowledge the remote's SABM
via UA but still wait for UA. */
hd_writeinternal (hdp
, UA
, pf
);
/* Request to reset the link from the remote. */
hd_message (hdp
, "Link reset");
hd_writeinternal (hdp
, UA
, pf
);
(void) pk_ctlinput (PRC_LINKRESET
, hdp
->hd_pkp
);
hd_writeinternal (hdp
, UA
, pf
);
hd_message (hdp
, "DM received: link down");
(void) pk_ctlinput (PRC_LINKDOWN
, hdp
->hd_pkp
);
hd_writeinternal (hdp
, SABM
, pf
);
hdp
->hd_state
= SABM_SENT
;
/* Note: This is a non-standard state change. */
hd_writeinternal (hdp
, UA
, pf
);
hd_writeinternal (hdp
, SABM
, POLLOFF
);
hdp
->hd_state
= SABM_SENT
;
hd_writeinternal (hdp
, DM
, pf
);
hd_message (hdp
, "DISC received: link down");
(void) pk_ctlinput (PRC_LINKDOWN
, hdp
->hd_pkp
);
hd_writeinternal (hdp
, UA
, pf
);
hd_message (hdp
, "UA received: link down");
(void) pk_ctlinput (PRC_LINKDOWN
, hdp
->hd_pkp
);
hd_writeinternal (hdp
, DM
, pf
);
hd_writeinternal (hdp
, SABM
, pf
);
hdp
->hd_state
= SABM_SENT
;
hd_writeinternal (hdp
, DM
, pf
);
hd_message (hdp
, "FRMR received: link down");
(void) pk_ctlinput (PRC_LINKDOWN
, hdp
->hd_pkp
);
hd_writeinternal (hdp
, SABM
, pf
);
process_sframe (hdp
, (struct Hdlc_sframe
*)frame
, frametype
);
queued
= process_iframe (hdp
, fbuf
, (struct Hdlc_iframe
*)frame
);
hd_writeinternal (hdp
, DM
, POLLON
);
hd_writeinternal (hdp
, FRMR
, POLLOFF
);
case ILLEGAL
+ SABM_SENT
:
hd_writeinternal (hdp
, DM
, POLLOFF
);
hd_message (hdp
, "Unknown frame received: link down");
(void) pk_ctlinput (PRC_LINKDOWN
, hdp
->hd_pkp
);
case ILLEGAL
+ WAIT_SABM
:
hd_writeinternal (hdp
, FRMR
, POLLOFF
);
hdp
->hd_state
= WAIT_SABM
;
process_iframe (hdp
, fbuf
, frame
)
register struct hdcb
*hdp
;
register struct Hdlc_iframe
*frame
;
register int nr
= frame
-> nr
,
register int queued
= FALSE
;
* Validate the iframe's N(R) value. It's N(R) value must be in
* sync with our V(S) value and our "last received nr".
if (valid_nr (hdp
, nr
, FALSE
) == FALSE
) {
frame_reject (hdp
, Z
, frame
);
* This section tests the IFRAME for proper sequence. That is, it's
* sequence number N(S) MUST be equal to V(S).
if (pf
|| (hdp
->hd_condition
& REJ_CONDITION
) == 0) {
hdp
->hd_condition
|= REJ_CONDITION
;
* Flush the transmit queue. This is ugly but we
* have no choice. A reject response must be
* immediately sent to the DCE. Failure to do so
* may result in another out of sequence iframe
* arriving (and thus sending another reject)
* before the first reject is transmitted. This
* will cause the DCE to receive two or more
* rejects back to back, which must never happen.
hd_writeinternal (hdp
, REJ
, pf
);
hdp
->hd_condition
&= ~REJ_CONDITION
;
* This section finally tests the IFRAME's sequence number against
* the window size (K) and the sequence number of the last frame
* we have acknowledged. If the IFRAME is completely correct then
* it is queued for the packet level.
if (ns
!= (hdp
-> hd_lasttxnr
+ hdp
-> hd_xcp
-> xc_lwsize
) % MODULUS
) {
hdp
-> hd_vr
= (hdp
-> hd_vr
+ 1) % MODULUS
;
/* Must generate a RR or RNR with final bit on. */
hd_writeinternal (hdp
, RR
, POLLON
);
* Hopefully we can piggyback the RR, if not we will generate
* a RR when T3 timer expires.
if (hdp
-> hd_rrtimer
== 0)
/* Forward iframe to packet level of X.25. */
fbuf
-> m_data
+= HDHEADERLN
;
fbuf
-> m_len
-= HDHEADERLN
;
fbuf
-> m_pkthdr
.len
-= HDHEADERLN
;
fbuf
-> m_pkthdr
.rcvif
= (struct ifnet
*)hdp
-> hd_pkp
;
fbuf
->m_act
= 0; /* probably not necessary */
for (m
= fbuf
; m
-> m_next
; m
= m
-> m_next
)
m
-> m_act
= (struct mbuf
*) 0;
m
-> m_act
= (struct mbuf
*) 1;
* Here if the remote station has transmitted more iframes then
* the number which have been acknowledged plus K.
frame_reject (hdp
, W
, frame
);
* This routine is used to determine if a value (the middle parameter)
* is between two other values. The low value is the first parameter
* the high value is the last parameter. The routine checks the middle
* value to see if it is within the range of the first and last values.
* The reason we need this routine is the values are modulo some base
* hence a simple test for greater or less than is not sufficient.
range_check (rear
, value
, front
)
register bool result
= FALSE
;
result
= (rear
<= value
) && (value
<= front
);
result
= (rear
<= value
) || (value
<= front
);
* This routine handles all the frame reject conditions which can
* arise as a result of secondary processing. The frame reject
* condition Y (frame length error) are handled elsewhere.
frame_reject (hdp
, rejectcode
, frame
)
struct Hdlc_iframe
*frame
;
register struct Frmr_frame
*frmr
= &hd_frmr
;
frmr
-> frmr_control
= ((struct Hdlc_frame
*) frame
) -> control
;
frmr
-> frmr_ns
= frame
-> ns
;
frmr
-> frmr_nr
= frame
-> nr
;
frmr
-> frmr_w
= frmr
-> frmr_x
= frmr
-> frmr_y
=
frmr
-> frmr_z
= 1;/* invalid N(R). */
frmr
-> frmr_y
= 1;/* iframe length error. */
frmr
-> frmr_x
= 1;/* invalid information field. */
frmr
-> frmr_w
= 1;/* invalid N(S). */
hd_writeinternal (hdp
, FRMR
, POLLOFF
);
hdp
->hd_state
= WAIT_SABM
;
* This procedure is invoked when ever we receive a supervisor
* frame such as RR, RNR and REJ. All processing for these
process_sframe (hdp
, frame
, frametype
)
register struct hdcb
*hdp
;
register struct Hdlc_sframe
*frame
;
register int nr
= frame
-> nr
, pf
= frame
-> pf
, pollbit
= 0;
if (valid_nr (hdp
, nr
, pf
) == TRUE
) {
hdp
->hd_condition
&= ~REMOTE_RNR_CONDITION
;
hdp
->hd_condition
|= REMOTE_RNR_CONDITION
;
hdp
->hd_condition
&= ~REMOTE_RNR_CONDITION
;
hdp
->hd_condition
&= ~TIMER_RECOVERY_CONDITION
;
if (frametype
== RR
&& hdp
->hd_lastrxnr
== hdp
->hd_vs
&& hdp
->hd_timer
== 0 && hdp
->hd_txq
.head
== 0)
hd_writeinternal(hdp
, RR
, pf
);
/* If any iframes have been queued because of the
timer condition, transmit then now. */
if (hdp
->hd_condition
& REMOTE_RNR_CONDITION
) {
/* Remote is busy or timer condition, so only
if (hdp
->hd_vs
!= hdp
->hd_retxqi
)
hd_send_iframe (hdp
, hdp
->hd_retxq
[hdp
->hd_vs
], pollbit
);
else /* Flush the retransmit list first. */
while (hdp
->hd_vs
!= hdp
->hd_retxqi
)
hd_send_iframe (hdp
, hdp
->hd_retxq
[hdp
->hd_vs
], POLLOFF
);
frame_reject (hdp
, Z
, (struct Hdlc_iframe
*)frame
); /* Invalid N(R). */
* This routine tests the validity of the N(R) which we have received.
* If it is ok, then all the iframes which it acknowledges (if any)
valid_nr (hdp
, nr
, finalbit
)
register struct hdcb
*hdp
;
/* Make sure it really does acknowledge something. */
if (hdp
->hd_lastrxnr
== nr
)
* This section validates the frame's N(R) value. It's N(R) value
* must be in syncronization with our V(S) value and our "last
* received nr" variable. If it is correct then we are able to send
* more IFRAME's, else frame reject condition is entered.
if (range_check (hdp
->hd_lastrxnr
, nr
, hdp
->hd_vs
) == FALSE
) {
if ((hdp
->hd_condition
& TIMER_RECOVERY_CONDITION
) &&
range_check (hdp
->hd_vs
, nr
, hdp
->hd_xx
) == TRUE
)
* If we get to here, we do have a valid frame but it might be out
* of sequence. However, we should still accept the receive state
* number N(R) since it has already passed our previous test and it
* does acknowledge frames which we are sending.
free_iframes (hdp
, &nr
, finalbit
);/* Free all acknowledged iframes */
* This routine determines how many iframes need to be retransmitted.
* It then resets the Send State Variable V(S) to accomplish this.
register struct hdcb
*hdp
;
* Flush the output queue. Any iframes queued for
* transmission will be out of sequence.
* Determine how many frames should be re-transmitted. In the case
* of a normal REJ this should be 1 to K. In the case of a timer
* recovery REJ (ie. a REJ with the Final Bit on) this could be 0.
if (hdp
->hd_condition
& TIMER_RECOVERY_CONDITION
)
anchor
= (anchor
- rejnr
+ 8) % MODULUS
;
/* There is at least one iframe to retransmit. */
while (hdp
->hd_vs
!= hdp
->hd_retxqi
)
hd_send_iframe (hdp
, hdp
->hd_retxq
[hdp
->hd_vs
], POLLOFF
);
* This routine frees iframes from the retransmit queue. It is called
* when a previously written iframe is acknowledged.
free_iframes (hdp
, nr
, finalbit
)
register struct hdcb
*hdp
;
* We need to do the following because of a funny quirk in the
* protocol. This case occures when in Timer recovery condition
* we get a N(R) which acknowledges all the outstanding iframes
* but with the Final Bit off. In this case we need to save the last
* iframe for possible retransmission even though it has already been
if ((hdp
->hd_condition
& TIMER_RECOVERY_CONDITION
) && *nr
== hdp
->hd_xx
&& finalbit
== 0) {
*nr
= (*nr
- 1 + 8) % MODULUS
;
/* printf ("QUIRK\n"); */
k
= (*nr
- hdp
->hd_lastrxnr
+ 8) % MODULUS
;
/* Loop here freeing all acknowledged iframes. */
for (i
= 0; i
< k
; ++i
) {
m_freem (hdp
->hd_retxq
[hdp
->hd_lastrxnr
]);
hdp
->hd_retxq
[hdp
->hd_lastrxnr
] = 0;
hdp
->hd_lastrxnr
= (hdp
->hd_lastrxnr
+ 1) % MODULUS
;