NetBSD/sys/netccitt/llc_output.c

335 lines
9.3 KiB
C

/* $NetBSD: llc_output.c,v 1.7 2001/11/13 00:12:57 lukem Exp $ */
/*
* Copyright (c) 1990, 1991, 1992
* Dirk Husemann, Computer Science Department IV,
* University of Erlangen-Nuremberg, Germany.
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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
* SUCH DAMAGE.
*
* @(#)llc_output.c 8.1 (Berkeley) 6/10/93
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: llc_output.c,v 1.7 2001/11/13 00:12:57 lukem Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_llc.h>
#include <net/route.h>
#include <netccitt/dll.h>
#include <netccitt/llc_var.h>
#include <machine/stdarg.h>
/*
* llc_output() --- called by an upper layer (network layer) entity whenever
* there is an INFO frame to be transmitted. We enqueue the
* info frame and call llc_start() to do the actual sending.
*/
int
#if __STDC__
llc_output(struct mbuf *m, ...)
#else
llc_output(m, va_alist)
struct mbuf *m;
va_dcl
#endif
{
struct llc_linkcb *linkp;
int i = splnet();
va_list ap;
va_start(ap, m);
linkp = va_arg(ap, struct llc_linkcb *);
va_end(ap);
LLC_ENQUEUE(linkp, m);
llc_start(linkp);
splx(i);
return 0;
}
/*
* llc_start() --- We try to subsequently dequeue all the frames available and
* send them out.
*/
void
llc_start(linkp)
struct llc_linkcb *linkp;
{
struct mbuf *m;
while ((LLC_STATEEQ(linkp,NORMAL) || LLC_STATEEQ(linkp,BUSY) ||
LLC_STATEEQ(linkp,REJECT)) &&
(linkp->llcl_slotsfree > 0) &&
(LLC_GETFLAG(linkp,REMOTE_BUSY) == 0)) {
LLC_DEQUEUE(linkp,m);
if (m == NULL)
break;
LLC_SETFRAME(linkp, m);
(void) llc_statehandler(linkp, NULL, NL_DATA_REQUEST, 0, 0);
}
}
/*
* llc_send() --- Handles single frames. If dealing with INFO frames we need to
* prepend the LLC header, otherwise we just allocate an mbuf.
* In both cases the actual send is done by llc_rawsend().
*/
void
llc_send(linkp, frame_kind, cmdrsp, pollfinal)
struct llc_linkcb *linkp;
int frame_kind;
int cmdrsp;
int pollfinal;
{
struct mbuf *m = (struct mbuf *) 0;
struct llc *frame;
if (frame_kind == LLCFT_INFO)
m = linkp->llcl_output_buffers[llc_seq2slot(linkp,
linkp->llcl_vs)];
LLC_GETHDR(frame, m);
/* pass it on to llc_rawsend() */
llc_rawsend(linkp, m, frame, frame_kind, linkp->llcl_vs, cmdrsp, pollfinal);
if (frame_kind == LLCFT_INFO)
LLC_INC(linkp->llcl_vs);
}
/*
* llc_resend() --- llc_resend() retransmits all unacknowledged INFO frames.
*/
void
llc_resend(linkp, cmdrsp, pollfinal)
struct llc_linkcb *linkp;
int cmdrsp;
int pollfinal;
{
struct llc *frame;
struct mbuf *m;
int slot;
if (linkp->llcl_slotsfree < linkp->llcl_window)
/* assert lock between nr_received & V(S) */
if (linkp->llcl_nr_received != linkp->llcl_vs)
panic("llc: V(S) != N(R) received\n");
for (slot = llc_seq2slot(linkp, linkp->llcl_vs);
slot != linkp->llcl_freeslot;
LLC_INC(linkp->llcl_vs),
slot = llc_seq2slot(linkp, linkp->llcl_vs)) {
m = linkp->llcl_output_buffers[slot];
LLC_GETHDR(frame, m);
llc_rawsend(linkp, m, frame, LLCFT_INFO, linkp->llcl_vs,
cmdrsp, pollfinal);
pollfinal = 0;
}
}
/*
* llc_rawsend() --- constructs an LLC frame and sends it out via the
* associated interface of the link control block.
*
* We need to make sure that outgoing frames have the correct length,
* in particular the 4 byte ones (RR, RNR, REJ) as LLC_GETHDR() will
* set the mbuf len to 3 as default len for non INFO frames ...
*
* Frame kind Length (w/o MAC header, {D,S}SAP incl.)
* --------------------------------------------------------------
* DISC, SABME, UA, DM 3 bytes ({D,S}SAP + CONTROL)
* RR, RNR, REJ 4 bytes ({D,S}SAP + CONTROL0 + CONTROL1)
* XID 6 bytes ({D,S}SAP + CONTROL0 + FI,CLASS,WINDOW)
* FRMR 7 bytes ({D,S}SAP + CONTROL0 + REJ CONTROL,V(S),V(R),CAUSE)
* INFO 4 -- MTU
* UI, TEST 3 -- MTU
*
*/
#define LLC_SETLEN(m, l) (m)->m_pkthdr.len = (m)->m_len = (l)
void
llc_rawsend(linkp, m, frame, frame_kind, vs, cmdrsp, pollfinal)
struct llc_linkcb *linkp;
struct mbuf *m;
struct llc *frame;
int frame_kind;
int vs;
int cmdrsp;
int pollfinal;
{
short adjust = LLC_UFRAMELEN;
struct ifnet *ifp;
switch (frame_kind) {
/* supervisory and information frames */
case LLCFT_INFO:
frame->llc_control = LLC_INFO;
LLCSBITS(frame->llc_control, i_ns, vs);
LLCSBITS(frame->llc_control_ext, i_nr, linkp->llcl_vr);
adjust = LLC_ISFRAMELEN;
break;
case LLCFT_RR:
frame->llc_control = LLC_RR;
LLC_SETLEN(m, LLC_ISFRAMELEN);
LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr);
adjust = LLC_ISFRAMELEN;
break;
case LLCFT_RNR:
frame->llc_control = LLC_RNR;
LLC_SETLEN(m, LLC_ISFRAMELEN);
LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr);
adjust = LLC_ISFRAMELEN;
break;
case LLCFT_REJ:
frame->llc_control = LLC_REJ;
LLC_SETLEN(m, LLC_ISFRAMELEN);
LLCSBITS(frame->llc_control_ext, s_nr, linkp->llcl_vr);
adjust = LLC_ISFRAMELEN;
break;
/* unnumbered frames */
case LLCFT_DM:
frame->llc_control = LLC_DM;
break;
case LLCFT_SABME:
frame->llc_control = LLC_SABME;
break;
case LLCFT_DISC:
frame->llc_control = LLC_DISC;
break;
case LLCFT_UA:
frame->llc_control = LLC_UA;
break;
case LLCFT_UI:
frame->llc_control = LLC_UI;
break;
case LLCFT_FRMR:
frame->llc_control = LLC_FRMR;
/* get more space --- FRMR frame are longer then usual */
LLC_SETLEN(m, LLC_FRMRLEN);
bcopy((caddr_t) & linkp->llcl_frmrinfo,
(caddr_t) & frame->llc_frmrinfo,
sizeof(struct frmrinfo));
break;
default:
/*
* We don't send {XID, TEST} frames
*/
if (m)
m_freem(m);
return;
}
/*
* Fill in DSAP/SSAP
*/
frame->llc_dsap = frame->llc_ssap = LLSAPADDR(&linkp->llcl_addr);
frame->llc_ssap |= cmdrsp;
/*
* Check for delayed action pending. ISO 8802-2, 7.9.2 (5)
* and ISO 8802-2, 7.9.2.3 (32), (34), (36) pertain to this
* piece of code --- hopefully we got it right here (i.e.
* in the spirit of (32), (34), and (36) ...
*/
switch (frame_kind) {
case LLCFT_RR:
case LLCFT_RNR:
case LLCFT_REJ:
case LLCFT_INFO:
switch (LLC_GETFLAG(linkp,DACTION)) {
case LLC_DACKCMD:
case LLC_DACKRSP:
LLC_STOPTIMER(linkp,DACTION);
break;
case LLC_DACKCMDPOLL:
if (cmdrsp == LLC_CMD) {
pollfinal = 1;
LLC_STOPTIMER(linkp,DACTION);
}
break;
case LLC_DACKRSPFINAL:
if (cmdrsp == LLC_RSP) {
pollfinal = 1;
LLC_STOPTIMER(linkp,DACTION);
}
break;
}
break;
}
if (adjust == LLC_UFRAMELEN)
LLCSBITS(frame->llc_control,u_pf,pollfinal);
else
LLCSBITS(frame->llc_control_ext,s_pf,pollfinal);
/*
* Get interface to send frame onto
*/
ifp = linkp->llcl_if;
if (frame_kind == LLCFT_INFO) {
/*
* send out a copy of the frame, retain the original
*/
(*ifp->if_output) (ifp, m_copy(m, 0, (int) M_COPYALL),
rt_key(linkp->llcl_nlrt),
linkp->llcl_nlrt);
/*
* Account for the LLC header and let it ``disappear''
* as the raw info frame payload is what we hold in
* the output_buffers of the link.
*/
m_adj(m, LLC_ISFRAMELEN);
} else
(*ifp->if_output) (ifp, m,
rt_key(linkp->llcl_nlrt),
linkp->llcl_nlrt);
}