nuttx-apps/netutils/pppd/ahdlc.c

450 lines
13 KiB
C

/****************************************************************************
* apps/netutils/pppd/ahdlc.c
* Ahdlc receive and transmit processor for PPP engine.
*
* Version: 0.1 Original Version Jan 11, 1998
* Copyright (C) 1998, Mycal Labs www.mycal.com
* Copyright (c) 2003, Mike Johnson, Mycal Labs, www.mycal.net
* All rights reserved.
*
* 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 Mike Johnson/Mycal Labs
* www.mycal.net.
* 4. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "ppp_conf.h"
#include "ppp.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if PPP_DEBUG
# define DEBUG1(x) debug_printf x
# define PACKET_TX_DEBUG 1
#else
# define DEBUG1(x)
# define PACKET_TX_DEBUG 0
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Simple and fast CRC16 routine for embedded processors.
*
* Just slightly slower than the table lookup method but consumes
* almost no space. Much faster and smaller than the loop and
* shift method that is widely used in the embedded space.
* Can be optimized even more in .ASM
*
* data = (crcvalue ^ inputchar) & 0xff;
* data = (data ^ (data << 4)) & 0xff;
* crc = (crc >> 8) ^ ((data << 8) ^ (data <<3) ^ (data >> 4))
*
****************************************************************************/
static uint16_t crcadd(uint16_t crcvalue, uint8_t c)
{
uint16_t b;
b = (crcvalue ^ c) & 0xFF;
b = (b ^ (b << 4)) & 0xFF;
b = (b << 8) ^ (b << 3) ^ (b >> 4);
return ((crcvalue >> 8) ^ b);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* ahdlc_init(buffer, buffersize) - this initializes the ahdlc engine to
* allow for rx frames.
*
****************************************************************************/
void ahdlc_init(struct ppp_context_s *ctx)
{
ctx->ahdlc_flags = PPP_RX_ASYNC_MAP;
ctx->ahdlc_rx_count = 0;
ctx->ahdlc_tx_offline = 0;
#ifdef PPP_STATISTICS
ctx->ahdlc_crc_error = 0;
ctx->ahdlc_rx_tobig_error = 0;
#endif
}
/****************************************************************************
* ahdlc_rx_ready() - resets the ahdlc engine to the beginning of frame
* state.
*
****************************************************************************/
void ahdlc_rx_ready(struct ppp_context_s *ctx)
{
ctx->ahdlc_rx_count = 0;
ctx->ahdlc_rx_crc = 0xffff;
ctx->ahdlc_flags |= PPP_RX_READY;
}
/****************************************************************************
* ahdlc receive function - This routine processes incoming bytes and tries
* to build a PPP frame.
*
* Two possible reasons that ahdlc_rx will not process characters:
* o Buffer is locked - in this case ahdlc_rx returns 1, char
* sending routing should retry.
*
****************************************************************************/
uint8_t ahdlc_rx(FAR struct ppp_context_s *ctx, uint8_t c)
{
/* Check to see if PPP packet is useable, we should have hardware flow
* control set, but if host ignores it and sends us a char when the PPP
* Receive packet is in use, discard the character.
*/
if ((ctx->ahdlc_flags & PPP_RX_READY) != 0)
{
/* Check to see if character is less than 0x20 hex we really should set
* AHDLC_RX_ASYNC_MAP on by default and only turn it off when it is
* negotiated off to handle some buggy stacks.
*/
if ((c < 0x20) && ((ctx->ahdlc_flags & PPP_RX_ASYNC_MAP) == 0))
{
/* Discard character */
DEBUG1(("Discard because char is < 0x20 hex and asysnc map is 0\n"));
return 0;
}
/* Are we in escaped mode? */
if ((ctx->ahdlc_flags & PPP_ESCAPED) != 0)
{
/* Set escaped to FALSE */
ctx->ahdlc_flags &= ~PPP_ESCAPED;
/* If value is 0x7e then silently discard and reset receive packet */
if (c == 0x7e)
{
ahdlc_rx_ready(ctx);
return 0;
}
/* Incoming char = itself xor 20 */
c = c ^ 0x20;
}
else if (c == 0x7e)
{
/* Handle frame end */
if (ctx->ahdlc_rx_crc == CRC_GOOD_VALUE)
{
DEBUG1(("\nReceiving packet with good crc value, len %d\n",
ctx->ahdlc_rx_count));
/* we have a good packet, turn off CTS until we are done with this
* packet
*/
/* CTS_OFF(); */
#if PPP_STATISTICS
/* Update statistics */
++ctx->ppp_rx_frame_count;
#endif
/* Remove CRC bytes from packet */
ctx->ahdlc_rx_count -= 2;
/* Lock PPP buffer */
ctx->ahdlc_flags &= ~PPP_RX_READY;
/* upcall routine must fully process frame before return as
* returning signifies that buffer belongs to AHDLC again.
*/
if ((ctx->ahdlc_rx_buffer[0] & 0x1) != 0 &&
(ctx->ahdlc_flags & PPP_PFC) != 0)
{
/* Send up packet */
ppp_upcall(ctx, (uint16_t)ctx->ahdlc_rx_buffer[0],
(FAR uint8_t *) & ctx->ahdlc_rx_buffer[1],
(uint16_t)(ctx->ahdlc_rx_count - 1));
}
else
{
/* Send up packet */
ppp_upcall(ctx,
(uint16_t)(ctx->ahdlc_rx_buffer[0] << 8 | ctx->
ahdlc_rx_buffer[1]),
(FAR uint8_t *) & ctx->ahdlc_rx_buffer[2],
(uint16_t)(ctx->ahdlc_rx_count - 2));
}
ctx->ahdlc_tx_offline = 0; /* The remote side is alive */
ahdlc_rx_ready(ctx);
return 0;
}
else if (ctx->ahdlc_rx_count > 3)
{
DEBUG1(("\nReceiving packet with bad crc value, was 0x%04x len %d\n",
ctx->ahdlc_rx_crc, ctx->ahdlc_rx_count));
#ifdef PPP_STATISTICS
++ctx->ahdlc_crc_error;
#endif
/* Shouldn't we dump the packet and not pass it up? */
/* ppp_upcall((uint16_t)ahdlc_rx_buffer[0], (FAR uint8_t
* *)&ahdlc_rx_buffer[0], (uint16_t)(ahdlc_rx_count+2));
* dump_ppp_packet(&ahdlc_rx_buffer[0],ahdlc_rx_count);
*/
}
ahdlc_rx_ready(ctx);
return 0;
}
else if (c == 0x7d)
{
/* Handle escaped chars */
ctx->ahdlc_flags |= PPP_ESCAPED;
return 0;
}
/* Try to store char if not too big */
if (ctx->ahdlc_rx_count >= PPP_RX_BUFFER_SIZE)
{
#ifdef PPP_STATISTICS
++ctx->ahdlc_rx_tobig_error;
#endif
ahdlc_rx_ready(ctx);
}
else
{
/* Add CRC in */
ctx->ahdlc_rx_crc = crcadd(ctx->ahdlc_rx_crc, c);
/* Do auto ACFC, if packet len is zero discard 0xff and 0x03 */
if (ctx->ahdlc_rx_count == 0)
{
if ((c == 0xff) || (c == 0x03))
{
return 0;
}
}
/* Store char */
ctx->ahdlc_rx_buffer[ctx->ahdlc_rx_count++] = c;
}
}
else
{
/* we are busy and didn't process the character. */
DEBUG1(("Busy/not active\n"));
return 1;
}
return 0;
}
/****************************************************************************
* ahdlc_tx_char(char) - write a character to the serial device,
* escape if necessary.
*
* Relies on local global vars : ahdlc_tx_crc, ahdlc_flags.
* Modifies local global vars : ahdlc_tx_crc.
*
****************************************************************************/
void ahdlc_tx_char(struct ppp_context_s *ctx, uint16_t protocol, uint8_t c)
{
/* Add in crc */
ctx->ahdlc_tx_crc = crcadd(ctx->ahdlc_tx_crc, c);
/* See if we need to escape char, we always escape 0x7d and 0x7e, in the case
* of char < 0x20 we only support async map of default or none, so escape if
* ASYNC map is not set. We may want to modify this to support a bitmap set
* ASYNC map.
*/
if ((c == 0x7d) || (c == 0x7e) || ((c < 0x20) && ((protocol == LCP) ||
(ctx->
ahdlc_flags &
PPP_TX_ASYNC_MAP) == 0)))
{
/* Send escape char and xor byte by 0x20 */
ppp_arch_putchar(ctx, 0x7d);
c ^= 0x20;
}
ppp_arch_putchar(ctx, c);
}
/****************************************************************************
* ahdlc_tx(protocol,buffer,len) - Transmit a PPP frame.
*
* Buffer contains protocol data, ahdlc_tx adds address, control and
* protocol data.
*
* Relies on local global vars : ahdlc_tx_crc, ahdlc_flags.
* Modifies local global vars : ahdlc_tx_crc.
*
****************************************************************************/
uint8_t ahdlc_tx(struct ppp_context_s *ctx, uint16_t protocol,
FAR uint8_t * header, FAR uint8_t * buffer, uint16_t headerlen,
uint16_t datalen)
{
uint16_t i;
uint8_t c;
DEBUG1(("\nAHDLC_TX - transmit frame, protocol 0x%04x, length %d offline %d\n",
protocol, datalen + headerlen, ctx->ahdlc_tx_offline));
if (AHDLC_TX_OFFLINE && (ctx->ahdlc_tx_offline++ > AHDLC_TX_OFFLINE))
{
ctx->ahdlc_tx_offline = 0;
DEBUG1(("\nAHDLC_TX to many outstanding TX packets => ppp_reconnect()\n"));
ppp_reconnect(ctx);
return 0;
}
#if PACKET_TX_DEBUG
DEBUG1(("\n"));
for (i = 0; i < headerlen; ++i)
{
DEBUG1(("0x%02x ", header[i]));
}
for (i = 0; i < datalen; ++i)
{
DEBUG1(("0x%02x ", buffer[i]));
}
DEBUG1(("\n\n"));
#endif
/* Check to see that physical layer is up, we can assume is some cases */
/* Write leading 0x7e */
ppp_arch_putchar(ctx, 0x7e);
/* Set initial CRC value */
ctx->ahdlc_tx_crc = 0xffff;
/* send HDLC control and address if not disabled or of LCP frame type */
/* if ((0==(ahdlc_flags & PPP_ACFC)) || ((0xc0==buffer[0]) &&
* (0x21==buffer[1])))
*/
if ((0 == (ctx->ahdlc_flags & PPP_ACFC)) || (protocol == LCP))
{
ahdlc_tx_char(ctx, protocol, 0xff);
ahdlc_tx_char(ctx, protocol, 0x03);
}
/* Write Protocol */
ahdlc_tx_char(ctx, protocol, (uint8_t)(protocol >> 8));
ahdlc_tx_char(ctx, protocol, (uint8_t)(protocol & 0xff));
/* Write header if it exists */
for (i = 0; i < headerlen; ++i)
{
/* Get next byte from buffer */
c = header[i];
/* Write it... */
ahdlc_tx_char(ctx, protocol, c);
}
/* Write frame bytes */
for (i = 0; i < datalen; ++i)
{
/* Get next byte from buffer */
c = buffer[i];
/* Write it... */
ahdlc_tx_char(ctx, protocol, c);
}
/* Send crc, lsb then msb */
i = ctx->ahdlc_tx_crc ^ 0xffff;
ahdlc_tx_char(ctx, protocol, (uint8_t)(i & 0xff));
ahdlc_tx_char(ctx, protocol, (uint8_t)((i >> 8) & 0xff));
/* Write trailing 0x7e, probably not needed but it doesn't hurt */
ppp_arch_putchar(ctx, 0x7e);
#if PPP_STATISTICS
/* Update statistics */
++ctx->ppp_tx_frame_count;
#endif
return 0;
}