* Copyright (C) Dirk Husemann, Computer Science Department IV,
* University of Erlangen-Nuremberg, Germany, 1990, 1991, 1992
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* Dirk Husemann and the Computer Science Department (IV) of
* the University of Erlangen-Nuremberg, Germany.
* %sccs.include.redist.c%
* @(#)llc_subr.c 8.1 (Berkeley) %G%
#include <sys/socketvar.h>
#include <netccitt/dll.h>
#include <netccitt/llc_var.h>
* Frame names for diagnostic messages
char *frame_names
[] = { "INFO", "RR", "RNR", "REJ", "DM", "SABME", "DISC",
"UA", "FRMR", "UI", "XID", "TEST", "ILLEGAL", "TIMER", "N2xT1"};
int llc_tracelevel
= LLCTR_URGENT
;
* Values for accessing various bitfields
struct bitslice llc_bitslice
[] = {
* We keep the link control blocks on a doubly linked list -
* primarily for checking in llc_time()
struct llccb_q llccb_q
= { &llccb_q
, &llccb_q
};
* Flag for signalling wether route tree for AF_LINK has been
int af_link_rts_init_done
= 0;
* Functions dealing with struct sockaddr_dl */
/* Compare sdl_a w/ sdl_b */
sdl_cmp(struct sockaddr_dl
*sdl_a
, struct sockaddr_dl
*sdl_b
)
if (LLADDRLEN(sdl_a
) != LLADDRLEN(sdl_b
))
return(bcmp((caddr_t
) sdl_a
->sdl_data
, (caddr_t
) sdl_b
->sdl_data
,
/* Copy sdl_f to sdl_t */
sdl_copy(struct sockaddr_dl
*sdl_f
, struct sockaddr_dl
*sdl_t
)
bcopy((caddr_t
) sdl_f
, (caddr_t
) sdl_t
, sdl_f
->sdl_len
);
/* Swap sdl_a w/ sdl_b */
sdl_swapaddr(struct sockaddr_dl
*sdl_a
, struct sockaddr_dl
*sdl_b
)
struct sockaddr_dl sdl_tmp
;
sdl_copy(sdl_a
, &sdl_tmp
);
sdl_copy(&sdl_tmp
, sdl_b
);
/* Fetch the sdl of the associated if */
sdl_getaddrif(struct ifnet
*ifp
)
register struct ifaddr
*ifa
;
for(ifa
= ifp
->if_addrlist
; ifa
; ifa
= ifa
->ifa_next
)
if (ifa
->ifa_addr
->sa_family
== AF_LINK
)
return((struct sockaddr_dl
*)(ifa
->ifa_addr
));
return((struct sockaddr_dl
*)0);
/* Check addr of interface with the one given */
sdl_checkaddrif(struct ifnet
*ifp
, struct sockaddr_dl
*sdl_c
)
register struct ifaddr
*ifa
;
for(ifa
= ifp
->if_addrlist
; ifa
; ifa
= ifa
->ifa_next
)
if ((ifa
->ifa_addr
->sa_family
== AF_LINK
) &&
!sdl_cmp((struct sockaddr_dl
*)(ifa
->ifa_addr
), sdl_c
))
/* Build an sdl from MAC addr, DLSAP addr, and interface */
sdl_setaddrif(struct ifnet
*ifp
, u_char
*mac_addr
, u_char dlsap_addr
,
u_char mac_len
, struct sockaddr_dl
*sdl_to
)
register struct sockaddr_dl
*sdl_tmp
;
if ((sdl_tmp
= sdl_getaddrif(ifp
)) ) {
sdl_copy(sdl_tmp
, sdl_to
);
bcopy((caddr_t
) mac_addr
, (caddr_t
) LLADDR(sdl_to
), mac_len
);
*(LLADDR(sdl_to
)+mac_len
) = dlsap_addr
;
sdl_to
->sdl_alen
= mac_len
+1;
/* Fill out the sdl header aggregate */
sdl_sethdrif(struct ifnet
*ifp
, u_char
*mac_src
, u_char dlsap_src
, u_char
*mac_dst
,
u_char dlsap_dst
, u_char mac_len
, struct sdl_hdr
*sdlhdr_to
)
if ( !sdl_setaddrif(ifp
, mac_src
, dlsap_src
, mac_len
,
&sdlhdr_to
->sdlhdr_src
) ||
!sdl_setaddrif(ifp
, mac_dst
, dlsap_dst
, mac_len
,
&sdlhdr_to
->sdlhdr_dst
) )
static struct sockaddr_dl sap_saddr
;
static struct sockaddr_dl sap_sgate
= {
sizeof(struct sockaddr_dl
), /* _len */
* Set sapinfo for SAP address, llcconfig, af, and interface
llc_setsapinfo(struct ifnet
*ifp
, u_char af
, u_char sap
, struct dllconfig
*llconf
)
struct sockaddr_dl
*ifdl_addr
;
struct rtentry
*sirt
= (struct rtentry
*)0;
struct npaidbentry
*sapinfo
;
int size
= sizeof(struct npaidbentry
);
* We rely/assume that only STREAM protocols will make use of
* connection oriented LLC2. If this will one day not be the
* case this will obviously fail.
pp
= pffindtype (af
, SOCK_STREAM
);
if (pp
== 0 || pp
->pr_input
== 0 || pp
->pr_ctlinput
== 0) {
printf("network level protosw error");
* We need a way to jot down the LLC2 configuration for
* a certain LSAP address. To do this we enter
ifdl_addr
= sdl_getaddrif(ifp
);
sdl_copy(ifdl_addr
, &sap_saddr
);
sdl_copy(ifdl_addr
, &sap_sgate
);
saploc
= LLSAPLOC(&sap_saddr
, ifp
);
sap_saddr
.sdl_data
[saploc
] = sap
;
rtrequest(RTM_ADD
, (struct sockaddr
*)&sap_saddr
,
(struct sockaddr
*)&sap_sgate
, 0, 0, &sirt
);
/* Plug in config information in rt->rt_llinfo */
sirt
->rt_llinfo
= malloc(size
, M_PCB
, M_WAITOK
);
sapinfo
= (struct npaidbentry
*) sirt
->rt_llinfo
;
bzero ((caddr_t
)sapinfo
, size
);
* For the time being we support LLC CLASS II here
sapinfo
->si_class
= LLC_CLASS_II
;
sapinfo
->si_window
= llconf
->dllcfg_window
;
sapinfo
->si_trace
= llconf
->dllcfg_trace
;
sapinfo
->si_input
= pp
->pr_input
;
sapinfo
->si_ctlinput
= (caddr_t (*)())pp
->pr_ctlinput
;
* Get sapinfo for SAP address and interface
llc_getsapinfo(u_char sap
, struct ifnet
*ifp
)
struct sockaddr_dl
*ifdl_addr
;
struct sockaddr_dl si_addr
;
ifdl_addr
= sdl_getaddrif(ifp
);
sdl_copy(ifdl_addr
, &si_addr
);
saploc
= LLSAPLOC(&si_addr
, ifp
);
si_addr
.sdl_data
[saploc
] = sap
;
if ((sirt
= rtalloc1((struct sockaddr
*)&si_addr
, 0)))
return((struct npaidbentry
*)sirt
->rt_llinfo
);
* llc_seq2slot() --- We only allocate enough memory to hold the window. This
* introduces the necessity to keep track of two ``pointers''
* o llcl_freeslot the next free slot to be used
* this one advances modulo llcl_window
* o llcl_projvs the V(S) associated with the next frame
* to be set via llcl_freeslot
* this one advances modulo LLC_MAX_SEQUENCE
* A new frame is inserted at llcl_output_buffers[llcl_freeslot], after
* which both llcl_freeslot and llcl_projvs are incremented.
* The slot sl(sn) for any given sequence number sn is given by
* sl(sn) = (llcl_freeslot + llcl_window - 1 - (llcl_projvs +
* LLC_MAX_SEQUENCE- sn) % LLC_MAX_SEQUENCE) %
* i.e. we first calculate the number of frames we need to ``go back''
* from the current one (really the next one, but that doesn't matter as
* llcl_projvs is likewise of by plus one) and subtract that from the
* pointer to the most recently taken frame (llcl_freeslot - 1).
llc_seq2slot(struct llc_linkcb
*linkp
, short seqn
)
sn
= (linkp
->llcl_freeslot
+ linkp
->llcl_window
-
(linkp
->llcl_projvs
+ LLC_MAX_SEQUENCE
- seqn
) %
LLC_MAX_SEQUENCE
) % linkp
->llcl_window
;
* LLC2 link state handler
* There is in most cases one function per LLC2 state. The LLC2 standard
* ISO 8802-2 allows in some cases for ambiguities, i.e. we have the choice
* to do one thing or the other. Right now I have just chosen one but have also
* indicated the spot by "multiple possibilities". One could make the behavior
* in those cases configurable, allowing the superuser to enter a profile word
* (32/64 bits, whatever is needed) that would suit her needs [I quite like
* that idea, perhaps I'll get around to it].
* [Preceeding each state handler function is the description as taken from
* ISO 8802-2, section 7.9.2.1]
* ADM --- The connection component is in the asynchronous disconnected mode.
* It can accept an SABME PDU from a remote LLC SSAP or, at the request
* of the service access point user, can initiate an SABME PDU
* transmission to a remote LLC DSAP, to establish a data link
* connection. It also responds to a DISC command PDU and to any
* command PDU with the P bit set to ``1''.
llc_state_ADM(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
llc_send(linkp
, LLCFT_SABME
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_SETFLAG(linkp
, S
, 0);
LLC_NEWSTATE(linkp
, SETUP
);
case LLCFT_SABME
+ LLC_CMD
:
* ISO 8802-2, table 7-1, ADM state says to set
* the P flag, yet this will cause an SABME [P] to be
* answered with an UA only, not an UA [F], all
* other `disconnected' states set the F flag, so ...
LLC_SETFLAG(linkp
, F
, pollfinal
);
LLC_NEWSTATE(linkp
, CONN
);
action
= LLC_CONNECT_INDICATION
;
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, pollfinal
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1)
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, 1);
/* remain in ADM state */
* CONN --- The local connection component has received an SABME PDU from a
* remote LLC SSAP, and it is waiting for the local user to accept or
llc_state_CONN(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
case NL_CONNECT_RESPONSE
:
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, LLC_GETFLAG(linkp
, F
));
LLC_SETFLAG(linkp
, P
, 0);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
case NL_DISCONNECT_REQUEST
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, LLC_GETFLAG(linkp
, F
));
LLC_NEWSTATE(linkp
, ADM
);
case LLCFT_SABME
+ LLC_CMD
:
LLC_SETFLAG(linkp
, F
, pollfinal
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
/* all other frames effect nothing here */
* RESET_WAIT --- The local connection component is waiting for the local user
* to indicate a RESET_REQUEST or a DISCONNECT_REQUEST.
llc_state_RESET_WAIT(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
if (LLC_GETFLAG(linkp
, S
) == 0) {
llc_send(linkp
, LLCFT_SABME
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, RESET
);
llc_send(linkp
, LLCFT_UA
, LLC_RSP
,
LLC_SETFLAG(linkp
, P
, 0);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_RESET_CONFIRM
;
case NL_DISCONNECT_REQUEST
:
if (LLC_GETFLAG(linkp
, S
) == 0) {
llc_send(linkp
, LLCFT_DISC
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, D_CONN
);
llc_send(linkp
, LLCFT_DM
, LLC_RSP
,
LLC_NEWSTATE(linkp
, ADM
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLCFT_SABME
+ LLC_CMD
:
LLC_SETFLAG(linkp
, S
, 1);
LLC_SETFLAG(linkp
, F
, pollfinal
);
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, pollfinal
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
* RESET_CHECK --- The local connection component is waiting for the local user
* to accept or refuse a remote reset request.
llc_state_RESET_CHECK(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, LLC_GETFLAG(linkp
, F
));
LLC_SETFLAG(linkp
, P
, 0);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
case NL_DISCONNECT_REQUEST
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, LLC_GETFLAG(linkp
, F
));
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLCFT_SABME
+ LLC_CMD
:
LLC_SETFLAG(linkp
, F
, pollfinal
);
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, pollfinal
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
* SETUP --- The connection component has transmitted an SABME command PDU to a
* remote LLC DSAP and is waiting for a reply.
llc_state_SETUP(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
case LLCFT_SABME
+ LLC_CMD
:
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, pollfinal
);
LLC_SETFLAG(linkp
, S
, 1);
if (LLC_GETFLAG(linkp
, P
) == pollfinal
) {
LLC_STOP_ACK_TIMER(linkp
);
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_CONNECT_CONFIRM
;
case LLC_ACK_TIMER_EXPIRED
:
if (LLC_GETFLAG(linkp
, S
) == 1) {
LLC_SETFLAG(linkp
, P
, 0);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0),
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_CONNECT_CONFIRM
;
} else if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_SABME
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, pollfinal
);
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
* RESET --- As a result of a service access point user request or the receipt
* of a FRMR response PDU, the local connection component has sent an
* SABME command PDU to the remote LLC DSAP to reset the data link
* connection and is waiting for a reply.
llc_state_RESET(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
case LLCFT_SABME
+ LLC_CMD
:
LLC_SETFLAG(linkp
, S
, 1);
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, pollfinal
);
if (LLC_GETFLAG(linkp
, P
) == pollfinal
) {
LLC_STOP_ACK_TIMER(linkp
);
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_RESET_CONFIRM
;
case LLC_ACK_TIMER_EXPIRED
:
if (LLC_GETFLAG(linkp
, S
) == 1) {
LLC_SETFLAG(linkp
, P
, 0);
LLC_SETFLAG(linkp
, REMOTE_BUSY
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_RESET_CONFIRM
;
} else if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_SABME
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, pollfinal
);
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
* D_CONN --- At the request of the service access point user, the local LLC
* has sent a DISC command PDU to the remote LLC DSAP and is waiting
llc_state_D_CONN(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
case LLCFT_SABME
+ LLC_CMD
:
llc_send(linkp
, LLCFT_DM
, LLC_RSP
, pollfinal
);
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
if (LLC_GETFLAG(linkp
, P
) == pollfinal
) {
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, pollfinal
);
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
case LLC_ACK_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_DISC
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_START_ACK_TIMER(linkp
);
} else LLC_NEWSTATE(linkp
, ADM
);
* ERROR --- The local connection component has detected an error in a received
* PDU and has sent a FRMR response PDU. It is waiting for a reply from
* the remote connection component.
llc_state_ERROR(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
case LLCFT_SABME
+ LLC_CMD
:
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, RESET_CHECK
);
action
= LLC_RESET_INDICATION_REMOTE
;
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, pollfinal
);
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
LLC_STOP_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLCFT_FRMR
+ LLC_RSP
:
LLC_STOP_ACK_TIMER(linkp
);
LLC_SETFLAG(linkp
, S
, 0);
LLC_NEWSTATE(linkp
, RESET_WAIT
);
action
= LLC_FRMR_RECEIVED
;
case LLC_ACK_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_FRMR
, LLC_RSP
, 0);
LLC_START_ACK_TIMER(linkp
);
LLC_SETFLAG(linkp
, S
, 0);
LLC_NEWSTATE(linkp
, RESET_WAIT
);
action
= LLC_RESET_INDICATION_LOCAL
;
llc_send(linkp
, LLCFT_FRMR
, LLC_RSP
, pollfinal
);
LLC_START_ACK_TIMER(linkp
);
* NORMAL, BUSY, REJECT, AWAIT, AWAIT_BUSY, and AWAIT_REJECT all share
* a common core state handler.
llc_state_NBRAcore(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
switch(frame_kind
+ cmdrsp
) {
case NL_DISCONNECT_REQUEST
:
llc_send(linkp
, LLCFT_DISC
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, D_CONN
);
llc_send(linkp
, LLCFT_SABME
, LLC_CMD
, pollfinal
);
LLC_SETFLAG(linkp
, P
, pollfinal
);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_START_ACK_TIMER(linkp
);
LLC_SETFLAG(linkp
, S
, 0);
LLC_NEWSTATE(linkp
, RESET
);
case LLCFT_SABME
+ LLC_CMD
:
LLC_SETFLAG(linkp
, F
, pollfinal
);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_NEWSTATE(linkp
, RESET_CHECK
);
action
= LLC_RESET_INDICATION_REMOTE
;
case LLCFT_DISC
+ LLC_CMD
:
llc_send(linkp
, LLCFT_UA
, LLC_RSP
, pollfinal
);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLCFT_FRMR
+ LLC_RSP
:
LLC_STOP_ALL_TIMERS(linkp
);
LLC_SETFLAG(linkp
, S
, 0);
LLC_NEWSTATE(linkp
, RESET_WAIT
);
action
= LLC_FRMR_RECEIVED
;
LLC_STOP_ALL_TIMERS(linkp
);
LLC_NEWSTATE(linkp
, ADM
);
action
= LLC_DISCONNECT_INDICATION
;
case LLC_INVALID_NR
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_CMD
:
LLC_SETFRMR(linkp
, frame
, cmdrsp
,
(frame_kind
== LLC_INVALID_NR
? LLC_FRMR_Z
:
(LLC_FRMR_V
| LLC_FRMR_W
)));
llc_send(linkp
, LLCFT_FRMR
, LLC_RSP
, pollfinal
);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ERROR
);
case LLC_INVALID_NR
+ LLC_RSP
:
case LLC_INVALID_NS
+ LLC_RSP
:
case LLC_INVALID_NR
: frmrcause
= LLC_FRMR_Z
; break;
case LLC_INVALID_NS
: frmrcause
= LLC_FRMR_V
| LLC_FRMR_W
; break;
default: frmrcause
= LLC_FRMR_W
;
LLC_SETFRMR(linkp
, frame
, cmdrsp
, frmrcause
);
llc_send(linkp
, LLCFT_FRMR
, LLC_RSP
, 0);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ERROR
);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1 &&
LLC_GETFLAG(linkp
, P
) == 0) {
LLC_SETFRMR(linkp
, frame
, cmdrsp
, LLC_FRMR_W
);
LLC_STOP_ALL_TIMERS(linkp
);
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, ERROR
);
case LLC_P_TIMER_EXPIRED
:
case LLC_ACK_TIMER_EXPIRED
:
case LLC_REJ_TIMER_EXPIRED
:
case LLC_BUSY_TIMER_EXPIRED
:
if (linkp
->llcl_retry
>= llc_n2
) {
LLC_STOP_ALL_TIMERS(linkp
);
LLC_SETFLAG(linkp
, S
, 0);
LLC_NEWSTATE(linkp
, RESET_WAIT
);
action
= LLC_RESET_INDICATION_LOCAL
;
* NORMAL --- A data link connection exists between the local LLC service access
* point and the remote LLC service access point. Sending and
* reception of information and supervisory PDUs can be performed.
llc_state_NORMAL(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
int action
= LLC_PASSITON
;
switch(frame_kind
+ cmdrsp
) {
if (LLC_GETFLAG(linkp
, REMOTE_BUSY
) == 0) {
if (LLC_GETFLAG(linkp
, P
) == 0) {
/* multiple possibilities */
llc_send(linkp
, LLCFT_INFO
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
if (LLC_TIMERXPIRED(linkp
, ACK
) != LLC_TIMER_RUNNING
)
LLC_START_ACK_TIMER(linkp
);
/* multiple possibilities */
llc_send(linkp
, LLCFT_INFO
, LLC_CMD
, 0);
if (LLC_TIMERXPIRED(linkp
, ACK
) != LLC_TIMER_RUNNING
)
LLC_START_ACK_TIMER(linkp
);
case LLC_LOCAL_BUSY_DETECTED
:
if (LLC_GETFLAG(linkp
, P
) == 0) {
/* multiple possibilities --- action-wise */
/* multiple possibilities --- CMD/RSP-wise */
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_START_P_TIMER(linkp
);
LLC_SETFLAG(linkp
, DATA
, 0);
LLC_NEWSTATE(linkp
, BUSY
);
/* multiple possibilities --- CMD/RSP-wise */
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_SETFLAG(linkp
, DATA
, 0);
LLC_NEWSTATE(linkp
, BUSY
);
case LLC_INVALID_NS
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_REJ
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
} else if (pollfinal
== 0 && p
== 1) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
} else if ((pollfinal
== 0 && p
== 0) ||
(pollfinal
== 1 && p
== 1 && cmdrsp
== LLC_RSP
)) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_START_P_TIMER(linkp
);
LLC_START_REJ_TIMER(linkp
);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, REJECT
);
case LLCFT_INFO
+ LLC_CMD
:
case LLCFT_INFO
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
action
= LLC_DATA_INDICATION
;
} else if (pollfinal
== 0 && p
== 1) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
action
= LLC_DATA_INDICATION
;
} else if ((pollfinal
== 0 && p
== 0 && cmdrsp
== LLC_CMD
) ||
(pollfinal
== p
&& cmdrsp
== LLC_RSP
)) {
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_SENDACKNOWLEDGE(linkp
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1)
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
action
= LLC_DATA_INDICATION
;
case LLCFT_RR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if ((pollfinal
== 0) ||
(cmdrsp
== LLC_RSP
&& pollfinal
== 1 && p
== 1)) {
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case LLCFT_RNR
+ LLC_CMD
:
case LLCFT_RNR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
} else if ((pollfinal
== 0) ||
(cmdrsp
== LLC_RSP
&& pollfinal
== 1 && p
== 1)) {
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
case LLCFT_REJ
+ LLC_CMD
:
case LLCFT_REJ
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_RSP
, 1);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 0 && p
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if ((pollfinal
== 0 && p
== 0 && cmdrsp
== LLC_CMD
) ||
(pollfinal
== p
&& cmdrsp
== LLC_RSP
)) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_START_P_TIMER(linkp
);
llc_resend(linkp
, LLC_CMD
, 1);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case NL_INITIATE_PF_CYCLE
:
if (LLC_GETFLAG(linkp
, P
) == 0) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
case LLC_P_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT
);
case LLC_ACK_TIMER_EXPIRED
:
case LLC_BUSY_TIMER_EXPIRED
:
if ((LLC_GETFLAG(linkp
, P
) == 0)
&& (linkp
->llcl_retry
< llc_n2
)) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT
);
if (action
== LLC_PASSITON
)
action
= llc_state_NBRAcore(linkp
, frame
, frame_kind
,
* BUSY --- A data link connection exists between the local LLC service access
* point and the remote LLC service access point. I PDUs may be sent.
* Local conditions make it likely that the information feld of
* received I PDUs will be ignored. Supervisory PDUs may be both sent
llc_state_BUSY(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
int action
= LLC_PASSITON
;
switch(frame_kind
+ cmdrsp
) {
if (LLC_GETFLAG(linkp
, REMOTE_BUSY
) == 0)
if (LLC_GETFLAG(linkp
, P
) == 0) {
llc_send(linkp
, LLCFT_INFO
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
if (LLC_TIMERXPIRED(linkp
, ACK
) != LLC_TIMER_RUNNING
)
LLC_START_ACK_TIMER(linkp
);
llc_send(linkp
, LLCFT_INFO
, LLC_CMD
, 0);
if (LLC_TIMERXPIRED(linkp
, ACK
) != LLC_TIMER_RUNNING
)
LLC_START_ACK_TIMER(linkp
);
case LLC_LOCAL_BUSY_CLEARED
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int df
= LLC_GETFLAG(linkp
, DATA
);
/* multiple possibilities */
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 1);
LLC_START_REJ_TIMER(linkp
);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 0);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
/* multiple possibilities */
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, NORMAL
);
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 0);
LLC_NEWSTATE(linkp
, NORMAL
);
/* multiple possibilities */
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 0);
LLC_NEWSTATE(linkp
, REJECT
);
case LLC_INVALID_NS
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (LLC_GETFLAG(linkp
, DATA
) == 0)
LLC_SETFLAG(linkp
, DATA
, 1);
} else if ((cmdrsp
== LLC_CMD
&& pollfinal
== 0 && p
== 0) ||
(cmdrsp
== LLC_RSP
&& pollfinal
== p
)) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (LLC_GETFLAG(linkp
, DATA
) == 0)
LLC_SETFLAG(linkp
, DATA
, 1);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 0 && p
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (LLC_GETFLAG(linkp
, DATA
) == 0)
LLC_SETFLAG(linkp
, DATA
, 1);
case LLCFT_INFO
+ LLC_CMD
:
case LLCFT_INFO
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (LLC_GETFLAG(linkp
, DATA
) == 2)
LLC_STOP_REJ_TIMER(linkp
);
LLC_SETFLAG(linkp
, DATA
, 0);
action
= LLC_DATA_INDICATION
;
} else if ((cmdrsp
== LLC_CMD
&& pollfinal
== 0 && p
== 0) ||
(cmdrsp
== LLC_RSP
&& pollfinal
== p
)) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (LLC_GETFLAG(linkp
, DATA
) == 2)
LLC_STOP_REJ_TIMER(linkp
);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1)
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
action
= LLC_DATA_INDICATION
;
} else if (pollfinal
== 0 && p
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (LLC_GETFLAG(linkp
, DATA
) == 2)
LLC_STOP_REJ_TIMER(linkp
);
LLC_SETFLAG(linkp
, DATA
, 0);
action
= LLC_DATA_INDICATION
;
case LLCFT_RNR
+ LLC_CMD
:
case LLCFT_RNR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (frame_kind
== LLCFT_RR
) {
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
= 0 ||
(cmdrsp
== LLC_RSP
&& pollfinal
== 1)) {
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (frame_kind
== LLCFT_RR
) {
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
case LLCFT_REJ
+ LLC_CMD
:
case LLCFT_REJ
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if ((cmdrsp
== LLC_CMD
&& pollfinal
== 0 && p
== 0) ||
(cmdrsp
== LLC_RSP
&& pollfinal
== p
)) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 0 && p
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case NL_INITIATE_PF_CYCLE
:
if (LLC_GETFLAG(linkp
, P
) == 0) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
case LLC_P_TIMER_EXPIRED
:
/* multiple possibilities */
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT_BUSY
);
case LLC_ACK_TIMER_EXPIRED
:
case LLC_BUSY_TIMER_EXPIRED
:
if (LLC_GETFLAG(linkp
, P
) == 0 && linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT_BUSY
);
case LLC_REJ_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
)
if (LLC_GETFLAG(linkp
, P
) == 0) {
/* multiple possibilities */
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_SETFLAG(linkp
, DATA
, 1);
LLC_NEWSTATE(linkp
, AWAIT_BUSY
);
LLC_SETFLAG(linkp
, DATA
, 1);
LLC_NEWSTATE(linkp
, BUSY
);
if (action
== LLC_PASSITON
)
action
= llc_state_NBRAcore(linkp
, frame
, frame_kind
,
* REJECT --- A data link connection exists between the local LLC service
* access point and the remote LLC service access point. The local
* connection component has requested that the remote connection
* component resend a specific I PDU that the local connection
* componnent has detected as being out of sequence. Both I PDUs and
* supervisory PDUs may be sent and received.
llc_state_REJECT(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
int action
= LLC_PASSITON
;
switch(frame_kind
+ cmdrsp
) {
if (LLC_GETFLAG(linkp
, P
) == 0) {
llc_send(linkp
, LLCFT_INFO
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
if (LLC_TIMERXPIRED(linkp
, ACK
) != LLC_TIMER_RUNNING
)
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
llc_send(linkp
, LLCFT_INFO
, LLC_CMD
, 0);
if (LLC_TIMERXPIRED(linkp
, ACK
) != LLC_TIMER_RUNNING
)
LLC_START_ACK_TIMER(linkp
);
LLC_NEWSTATE(linkp
, REJECT
);
case NL_LOCAL_BUSY_DETECTED
:
if (LLC_GETFLAG(linkp
, P
) == 0) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_SETFLAG(linkp
, DATA
, 2);
LLC_NEWSTATE(linkp
, BUSY
);
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_SETFLAG(linkp
, DATA
, 2);
LLC_NEWSTATE(linkp
, BUSY
);
case LLC_INVALID_NS
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
} else if (pollfinal
== 0 ||
(cmdrsp
== LLC_RSP
&& pollfinal
== 1 && p
== 1)) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case LLCFT_INFO
+ LLC_CMD
:
case LLCFT_INFO
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_STOP_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_DATA_INDICATION
;
} else if ((cmdrsp
= LLC_RSP
&& pollfinal
== p
) ||
(cmdrsp
== LLC_CMD
&& pollfinal
== 0 && p
== 0)) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
if (cmdrsp
== LLC_RSP
&& pollfinal
== 1)
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_STOP_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_DATA_INDICATION
;
} else if (pollfinal
== 0 && p
== 1) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_CMD
, 0);
LLC_STOP_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_DATA_INDICATION
;
case LLCFT_RR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_SENDACKNOWLEDGE(linkp
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 0 ||
(cmdrsp
== LLC_RSP
&& pollfinal
== 1 && p
== 1)) {
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case LLCFT_RNR
+ LLC_CMD
:
case LLCFT_RNR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 0 ||
(cmdrsp
== LLC_RSP
&& pollfinal
== 1 && p
== 1)) {
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
case LLCFT_REJ
+ LLC_CMD
:
case LLCFT_REJ
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_RSP
, 1);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if ((cmdrsp
== LLC_CMD
&& pollfinal
== 0 && p
== 0) ||
(cmdrsp
== LLC_RSP
&& pollfinal
== p
)) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_UPDATE_P_FLAG(linkp
, cmdrsp
, pollfinal
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 0 && p
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case NL_INITIATE_PF_CYCLE
:
if (LLC_GETFLAG(linkp
, P
) == 0) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
case LLC_REJ_TIMER_EXPIRED
:
if (LLC_GETFLAG(linkp
, P
) == 0 && linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_START_REJ_TIMER(linkp
);
case LLC_P_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT_REJECT
);
case LLC_ACK_TIMER_EXPIRED
:
case LLC_BUSY_TIMER_EXPIRED
:
if (LLC_GETFLAG(linkp
, P
) == 0 && linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_START_REJ_TIMER(linkp
);
* I cannot locate the description of RESET_V(S)
* in ISO 8802-2, table 7-1, state REJECT, last event,
* and assume they meant to set V(S) to 0 ...
linkp
->llcl_vs
= 0; /* XXX */
LLC_NEWSTATE(linkp
, AWAIT_REJECT
);
if (action
== LLC_PASSITON
)
action
= llc_state_NBRAcore(linkp
, frame
, frame_kind
,
* AWAIT --- A data link connection exists between the local LLC service access
* point and the remote LLC service access point. The local LLC is
* performing a timer recovery operation and has sent a command PDU
* with the P bit set to ``1'', and is awaiting an acknowledgement
* from the remote LLC. I PDUs may be received but not sent.
* Supervisory PDUs may be both sent and received.
llc_state_AWAIT(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
int action
= LLC_PASSITON
;
switch(frame_kind
+ cmdrsp
) {
case LLC_LOCAL_BUSY_DETECTED
:
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_SETFLAG(linkp
, DATA
, 0);
LLC_NEWSTATE(linkp
, AWAIT_BUSY
);
case LLC_INVALID_NS
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_REJ
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT_REJECT
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_START_REJ_TIMER(linkp
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, REJECT
);
} else if (pollfinal
== 0) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT_REJECT
);
case LLCFT_INFO
+ LLC_RSP
:
case LLCFT_INFO
+ LLC_CMD
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
action
= LLC_DATA_INDICATION
;
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_DATA_INDICATION
;
} else if (pollfinal
== 0) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
action
= LLC_DATA_INDICATION
;
case LLCFT_REJ
+ LLC_CMD
:
case LLCFT_REJ
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, NORMAL
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case LLCFT_RNR
+ LLC_CMD
:
case LLCFT_RNR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (pollfinal
== 1 && cmdrsp
== LLC_CMD
) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
} else if (pollfinal
== 1 && cmdrsp
== LLC_RSP
) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, NORMAL
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
case LLC_P_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
if (action
== LLC_PASSITON
)
action
= llc_state_NBRAcore(linkp
, frame
, frame_kind
,
* AWAIT_BUSY --- A data link connection exists between the local LLC service
* access point and the remote LLC service access point. The
* local LLC is performing a timer recovery operation and has
* sent a command PDU with the P bit set to ``1'', and is
* awaiting an acknowledgement from the remote LLC. I PDUs may
* not be sent. Local conditions make it likely that the
* information feld of receoved I PDUs will be ignored.
* Supervisory PDUs may be both sent and received.
llc_state_AWAIT_BUSY(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
int action
= LLC_PASSITON
;
switch(frame_kind
+ cmdrsp
) {
case LLC_LOCAL_BUSY_CLEARED
:
switch (LLC_GETFLAG(linkp
, DATA
)) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 0);
LLC_START_REJ_TIMER(linkp
);
LLC_NEWSTATE(linkp
, AWAIT_REJECT
);
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 0);
LLC_NEWSTATE(linkp
, AWAIT
);
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 0);
LLC_NEWSTATE(linkp
, AWAIT_REJECT
);
case LLC_INVALID_NS
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SETFLAG(linkp
, DATA
, 1);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SETFLAG(linkp
, DATA
, 1);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_NEWSTATE(linkp
, BUSY
);
} else if (pollfinal
== 0) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SETFLAG(linkp
, DATA
, 1);
case LLCFT_INFO
+ LLC_CMD
:
case LLCFT_INFO
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SETFLAG(linkp
, DATA
, 0);
action
= LLC_DATA_INDICATION
;
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SETFLAG(linkp
, DATA
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_NEWSTATE(linkp
, BUSY
);
action
= LLC_DATA_INDICATION
;
} else if (pollfinal
== 0) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SETFLAG(linkp
, DATA
, 0);
action
= LLC_DATA_INDICATION
;
case LLCFT_REJ
+ LLC_CMD
:
case LLCFT_REJ
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, BUSY
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case LLCFT_RNR
+ LLC_CMD
:
case LLCFT_RNR
+ LLC_RSP
: {
register int p
= LLC_GETFLAG(linkp
, P
);
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RNR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, BUSY
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
case LLC_P_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
if (action
== LLC_PASSITON
)
action
= llc_state_NBRAcore(linkp
, frame
, frame_kind
,
* AWAIT_REJECT --- A data link connection exists between the local LLC service
* access point and the remote LLC service access point. The
* local connection component has requested that the remote
* connection component re-transmit a specific I PDU that the
* local connection component has detected as being out of
* sequence. Before the local LLC entered this state it was
* performing a timer recovery operation and had sent a
* command PDU with the P bit set to ``1'', and is still
* awaiting an acknowledgment from the remote LLC. I PDUs may
* be received but not transmitted. Supervisory PDUs may be
* both transmitted and received.
llc_state_AWAIT_REJECT(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
int action
= LLC_PASSITON
;
switch(frame_kind
+ cmdrsp
) {
case LLC_LOCAL_BUSY_DETECTED
:
llc_send(linkp
, LLCFT_RNR
, LLC_CMD
, 0);
LLC_SETFLAG(linkp
, DATA
, 2);
LLC_NEWSTATE(linkp
, AWAIT_BUSY
);
case LLC_INVALID_NS
+ LLC_CMD
:
case LLC_INVALID_NS
+ LLC_RSP
: {
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, REJECT
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
case LLCFT_INFO
+ LLC_CMD
:
case LLCFT_INFO
+ LLC_RSP
: {
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_STOP_REJ_TIMER(linkp
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_NEWSTATE(linkp
, AWAIT
);
action
= LLC_DATA_INDICATION
;
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_STOP_REJ_TIMER(linkp
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 0);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, NORMAL
);
action
= LLC_DATA_INDICATION
;
} else if (pollfinal
== 0) {
llc_send(linkp
, LLCFT_RR
, LLC_CMD
, 0);
LLC_STOP_REJ_TIMER(linkp
);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_NEWSTATE(linkp
, AWAIT
);
action
= LLC_DATA_INDICATION
;
case LLCFT_REJ
+ LLC_CMD
:
case LLCFT_REJ
+ LLC_RSP
: {
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
llc_resend(linkp
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, REJECT
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_CLEAR_REMOTE_BUSY(linkp
, action
);
case LLCFT_RNR
+ LLC_CMD
:
case LLCFT_RNR
+ LLC_RSP
: {
register int nr
= LLCGBITS(frame
->llc_control_ext
, s_nr
);
if (cmdrsp
== LLC_CMD
&& pollfinal
== 1) {
llc_send(linkp
, LLCFT_RR
, LLC_RSP
, 1);
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
} else if (cmdrsp
== LLC_RSP
&& pollfinal
== 1) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
LLC_NEWSTATE(linkp
, REJECT
);
} else if (pollfinal
== 0) {
LLC_UPDATE_NR_RECEIVED(linkp
, nr
);
LLC_SET_REMOTE_BUSY(linkp
, action
);
case LLC_P_TIMER_EXPIRED
:
if (linkp
->llcl_retry
< llc_n2
) {
llc_send(linkp
, LLCFT_REJ
, LLC_CMD
, 1);
LLC_START_P_TIMER(linkp
);
if (action
== LLC_PASSITON
)
action
= llc_state_NBRAcore(linkp
, frame
, frame_kind
,
* llc_statehandler() --- Wrapper for llc_state_*() functions.
* Deals with action codes and checks for
llc_statehandler(struct llc_linkcb
*linkp
, struct llc
*frame
, int frame_kind
,
int cmdrsp
, int pollfinal
)
* To check for ``zombie'' links each time llc_statehandler() gets called
* the AGE timer of linkp is reset. If it expires llc_timer() will
* take care of the link --- i.e. kill it 8=)
LLC_STARTTIMER(linkp
, AGE
);
* Now call the current statehandler function.
action
= (*linkp
->llcl_statehandler
)(linkp
, frame
, frame_kind
,
case LLC_CONNECT_INDICATION
: {
LLC_TRACE(linkp
, LLCTR_INTERESTING
, "CONNECT INDICATION");
(*linkp
->llcl_sapinfo
->si_ctlinput
)
(struct sockaddr
*) &linkp
->llcl_addr
, (caddr_t
) linkp
);
if (linkp
->llcl_nlnext
== 0)
naction
= NL_DISCONNECT_REQUEST
;
else naction
= NL_CONNECT_RESPONSE
;
action
= (*linkp
->llcl_statehandler
)(linkp
, frame
, naction
, 0, 0);
goto once_more_and_again
;
case LLC_CONNECT_CONFIRM
:
/* llc_resend(linkp, LLC_CMD, 0); */
case LLC_DISCONNECT_INDICATION
:
LLC_TRACE(linkp
, LLCTR_INTERESTING
, "DISCONNECT INDICATION");
(*linkp
->llcl_sapinfo
->si_ctlinput
)
(PRC_DISCONNECT_INDICATION
,
(struct sockaddr
*) &linkp
->llcl_addr
, linkp
->llcl_nlnext
);
/* internally visible only */
case LLC_RESET_INDICATION_LOCAL
:
* not much we can do here, the state machine either makes it or
case LLC_RESET_INDICATION_REMOTE
:
LLC_TRACE(linkp
, LLCTR_SHOULDKNOW
, "RESET INDICATION (REMOTE)");
action
= (*linkp
->llcl_statehandler
)(linkp
, frame
,
NL_RESET_RESPONSE
, 0, 0);
goto once_more_and_again
;
LLC_TRACE(linkp
, LLCTR_URGENT
, "FRMR SENT");
LLC_TRACE(linkp
, LLCTR_URGEN
, "FRMR RECEIVED");
action
= (*linkp
->llcl_statehandler
)(linkp
, frame
,
goto once_more_and_again
;
LLC_TRACE(linkp
, LLCTR_SHOULDKNOW
, "REMOTE BUSY");
case LLC_REMOTE_NOT_BUSY
:
LLC_TRACE(linkp
, LLCTR_SHOULDKNOW
, "REMOTE BUSY CLEARED");
* try to get queued frames out
* Only LLC_DATA_INDICATION is for the time being
* passed up to the network layer entity.
* The remaining action codes are for the time
* being visible internally only.
* However, this can/may be changed if necessary.
* The INIT call. This routine is called once after the system is booted.
llcintrq
.ifq_maxlen
= IFQ_MAXLEN
;
* In case of a link reset we need to shuffle the frames queued inside the
llc_resetwindow(struct llc_linkcb
*linkp
)
register struct mbuf
*mptr
= (struct mbuf
*) 0;
register struct mbuf
*anchor
= (struct mbuf
*)0;
/* Pick up all queued frames and collect them in a linked mbuf list */
if (linkp
->llcl_slotsfree
!= linkp
->llcl_window
) {
i
= llc_seq2slot(linkp
, linkp
->llcl_nr_received
);
anchor
= mptr
= linkp
->llcl_output_buffers
[i
];
for (; i
!= linkp
->llcl_freeslot
;
i
= llc_seq2slot(linkp
, i
+1)) {
if (linkp
->llcl_output_buffers
[i
]) {
mptr
->m_nextpkt
= linkp
->llcl_output_buffers
[i
];
} else panic("LLC2 window broken");
mptr
->m_nextpkt
= (struct mbuf
*) 0;
/* Now --- plug 'em in again */
if (anchor
!= (struct mbuf
*)0) {
for (i
= 0, mptr
= anchor
; mptr
!= (struct mbuf
*) 0; i
++) {
linkp
->llcl_output_buffers
[i
] = mptr
;
linkp
->llcl_output_buffers
[i
]->m_nextpkt
= (struct mbuf
*)0;
linkp
->llcl_freeslot
= i
;
} else linkp
->llcl_freeslot
= 0;
/* We're resetting the link, the next frame to be acknowledged is 0 */
linkp
->llcl_nr_received
= 0;
/* set distance between LLC2 sequence number and the top of window to 0 */
linkp
->llcl_projvs
= linkp
->llcl_freeslot
;
* llc_newlink() --- We allocate enough memory to contain a link control block
* and initialize it properly. We don't intiate the actual setup
llc_newlink(struct sockaddr_dl
*dst
, struct ifnet
*ifp
, struct rtentry
*nlrt
,
caddr_t nlnext
, struct rtentry
*llrt
)
struct llc_linkcb
*nlinkp
;
u_char sap
= LLSAPADDR(dst
);
/* allocate memory for link control block */
MALLOC(nlinkp
, struct llc_linkcb
*, sizeof(struct llc_linkcb
),
bzero((caddr_t
)nlinkp
, sizeof(struct llc_linkcb
));
sdl_copy(dst
, &nlinkp
->llcl_addr
);
/* hold on to the network layer route entry */
nlinkp
->llcl_nlrt
= nlrt
;
/* likewise the network layer control block */
nlinkp
->llcl_nlnext
= nlnext
;
/* jot down the link layer route entry */
nlinkp
->llcl_llrt
= llrt
;
nlinkp
->llcl_writeqh
= nlinkp
->llcl_writeqt
= NULL
;
/* setup initial state handler function */
nlinkp
->llcl_statehandler
= llc_state_ADM
;
/* hold on to interface pointer */
/* get service access point information */
nlinkp
->llcl_sapinfo
= llc_getsapinfo(sap
, ifp
);
/* get window size from SAP info block */
if ((llcwindow
= nlinkp
->llcl_sapinfo
->si_window
) == 0)
llcwindow
= LLC_MAX_WINDOW
;
/* allocate memory for window buffer */
MALLOC(nlinkp
->llcl_output_buffers
, struct mbuf
**,
llcwindow
*sizeof(struct mbuf
*), M_PCB
, M_DONTWAIT
);
if (nlinkp
->llcl_output_buffers
== 0) {
bzero((caddr_t
)nlinkp
->llcl_output_buffers
,
llcwindow
*sizeof(struct mbuf
*));
/* set window size & slotsfree */
nlinkp
->llcl_slotsfree
= nlinkp
->llcl_window
= llcwindow
;
/* enter into linked listed of link control blocks */
insque(nlinkp
, &llccb_q
);
* llc_dellink() --- farewell to link control block
llc_dellink(struct llc_linkcb
*linkp
)
register struct npaidbentry
*sapinfo
= linkp
->llcl_sapinfo
;
/* notify upper layer of imminent death */
if (linkp
->llcl_nlnext
&& sapinfo
->si_ctlinput
)
(PRC_DISCONNECT_INDICATION
,
(struct sockaddr
*)&linkp
->llcl_addr
, linkp
->llcl_nlnext
);
((struct npaidbentry
*)(linkp
->llcl_llrt
->rt_llinfo
))->np_link
= (struct llc_linkcb
*) 0;
/* leave link control block queue */
/* drop queued packets */
for (m
= linkp
->llcl_writeqh
; m
;) {
/* drop packets in the window */
for(i
= 0; i
< linkp
->llcl_window
; i
++)
if (linkp
->llcl_output_buffers
[i
])
m_freem(linkp
->llcl_output_buffers
[i
]);
/* return the window space */
FREE((caddr_t
)linkp
->llcl_output_buffers
, M_PCB
);
/* return the control block space --- now it's gone ... */
FREE((caddr_t
)linkp
, M_PCB
);
llc_decode(struct llc
* frame
, struct llc_linkcb
* linkp
)
register int ft
= LLC_BAD_PDU
;
if ((frame
->llc_control
& 01) == 0) {
} else switch (frame
->llc_control
) {
case LLC_UI_P
: ft
= LLC_UI
; break;
case LLC_DM_P
: ft
=LLCFT_DM
; break;
case LLC_DISC_P
: ft
= LLCFT_DISC
; break;
case LLC_UA_P
: ft
= LLCFT_UA
; break;
case LLC_SABME_P
: ft
= LLCFT_SABME
; break;
case LLC_FRMR_P
: ft
= LLCFT_FRMR
; break;
case LLC_XID_P
: ft
= LLCFT_XID
; break;
case LLC_TEST_P
: ft
= LLCFT_TEST
; break;
case LLC_RR
: ft
= LLCFT_RR
; break;
case LLC_RNR
: ft
= LLCFT_RNR
; break;
case LLC_REJ
: ft
= LLCFT_REJ
; break;
if (LLCGBITS(frame
->llc_control
, i_ns
) != linkp
->llcl_vr
) {
/* fall thru --- yeeeeeee */
if (LLC_NR_VALID(linkp
, LLCGBITS(frame
->llc_control_ext
,
* llc_anytimersup() --- Checks if at least one timer is still up and running.
llc_anytimersup(struct llc_linkcb
* linkp
)
if (linkp
->llcl_timers
[i
] > 0)
* llc_link_dump() - dump link info
#define SAL(s) ((struct sockaddr_dl *)&(s)->llcl_addr)
#define CHECK(l, s) if (LLC_STATEEQ(l, s)) return #s
char *timer_names
[] = {"ACK", "P", "BUSY", "REJ", "AGE"};
llc_getstatename(struct llc_linkcb
*linkp
)
CHECK(linkp
, RESET_WAIT
);
CHECK(linkp
, RESET_CHECK
);
CHECK(linkp
, AWAIT_BUSY
);
CHECK(linkp
, AWAIT_REJECT
);
llc_link_dump(struct llc_linkcb
* linkp
, const char *message
)
printf("if %s%d\n", linkp
->llcl_if
->if_name
, linkp
->llcl_if
->if_unit
);
printf(">> %s <<\n", message
);
for (i
= 0; i
< (SAL(linkp
)->sdl_alen
)-2; i
++)
printf("%x:", (char)*(LLADDR(SAL(linkp
))+i
) & 0xff);
printf("%x,", (char)*(LLADDR(SAL(linkp
))+i
) & 0xff);
printf("%x\n", (char)*(LLADDR(SAL(linkp
))+i
+1) & 0xff);
/* print state we're in and timers */
printf("state %s, ", llc_getstatename(linkp
));
for (i
= LLC_ACK_SHIFT
; i
< LLC_AGE_SHIFT
; i
++)
printf("%s-%c %d/", timer_names
[i
],
(linkp
->llcl_timerflags
& (1<<i
) ? 'R' : 'S'),
printf("%s-%c %d\n", timer_names
[i
], (linkp
->llcl_timerflags
& (1<<i
) ?
'R' : 'S'), linkp
->llcl_timers
[i
]);
printf("flags P %d/F %d/S %d/DATA %d/REMOTE_BUSY %d\n",
LLC_GETFLAG(linkp
, P
), LLC_GETFLAG(linkp
, S
),
LLC_GETFLAG(linkp
, DATA
), LLC_GETFLAG(linkp
, REMOTE_BUSY
));
/* print send and receive state variables, ack, and window */
printf("V(R) %d/V(S) %d/N(R) received %d/window %d/freeslot %d\n",
linkp
->llcl_vs
, linkp
->llcl_vr
, linkp
->llcl_nr_received
,
linkp
->llcl_window
, linkp
->llcl_freeslot
);
/* further expansions can follow here */
llc_trace(struct llc_linkcb
*linkp
, int level
, const char *message
)
if (linkp
->llcl_sapinfo
->si_trace
&& level
> llc_tracelevel
)
llc_link_dump(linkp
, message
);