893387b2c5
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
450 lines
13 KiB
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 since 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
|
|
* 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;
|
|
}
|