| /**************************************************************************** |
| * 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) |
| # undef PACKET_TX_DEBUG |
| #endif |
| |
| /* ahdlc flags bit defins, for ahdlc_flags variable */ |
| |
| /* Escaped mode bit */ |
| |
| #define AHDLC_ESCAPED 0x1 |
| |
| /* Frame is ready bit */ |
| |
| #define AHDLC_RX_READY 0x2 |
| #define AHDLC_RX_ASYNC_MAP 0x4 |
| #define AHDLC_TX_ASYNC_MAP 0x8 |
| #define AHDLC_PFC 0x10 |
| #define AHDLC_ACFC 0x20 |
| |
| /**************************************************************************** |
| * 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 u16_t crcadd(u16_t crcvalue, u8_t c) |
| { |
| u16_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 = 0 | AHDLC_RX_ASYNC_MAP; |
| ctx->ahdlc_rx_count = 0; |
| ctx->ahdlc_tx_offline = 0; |
| |
| #ifdef PPP_STATISTICS |
| 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 |= AHDLC_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. |
| * |
| ****************************************************************************/ |
| |
| u8_t ahdlc_rx(struct ppp_context_s *ctx, u8_t c) |
| { |
| //static u16_t protocol; |
| |
| /* 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 & AHDLC_RX_READY) |
| { |
| /* 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 & AHDLC_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 & AHDLC_ESCAPED) |
| { |
| /* Set escaped to FALSE */ |
| |
| ctx->ahdlc_flags &= ~AHDLC_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 hae 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 |
| |
| /* Femove CRC bytes from packet */ |
| |
| ctx->ahdlc_rx_count -= 2; |
| |
| /* Lock PPP buffer */ |
| |
| ctx->ahdlc_flags &= ~AHDLC_RX_READY; |
| |
| /*upcall routine must fully process frame before return |
| * as returning signifies that buffer belongs to AHDLC again. |
| */ |
| |
| if ((c & 0x1) && (ctx->ahdlc_flags & PPP_PFC)) |
| { |
| /* Send up packet */ |
| |
| ppp_upcall(ctx, (u16_t)ctx->ahdlc_rx_buffer[0], |
| (u8_t *)&ctx->ahdlc_rx_buffer[1], |
| (u16_t)(ctx->ahdlc_rx_count - 1)); |
| } |
| else |
| { |
| /* Send up packet */ |
| |
| ppp_upcall(ctx, (u16_t)(ctx->ahdlc_rx_buffer[0] << 8 | ctx->ahdlc_rx_buffer[1]), |
| (u8_t *)&ctx->ahdlc_rx_buffer[2], (u16_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((u16_t)ahdlc_rx_buffer[0], |
| (u8_t *)&ahdlc_rx_buffer[0], (u16_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; |
| } |
| |
| /* Rry 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, u16_t protocol, u8_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 addes address, control and |
| * protocol data. |
| * |
| * Relies on local global vars : ahdlc_tx_crc, ahdlc_flags. |
| * Modifies local global vars : ahdlc_tx_crc. |
| * |
| ****************************************************************************/ |
| |
| u8_t ahdlc_tx(struct ppp_context_s *ctx, u16_t protocol, u8_t *header, |
| u8_t *buffer, u16_t headerlen, u16_t datalen) |
| { |
| u16_t i; |
| u8_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,(u8_t)(protocol >> 8)); |
| ahdlc_tx_char(ctx, protocol,(u8_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, (u8_t)(i & 0xff)); |
| ahdlc_tx_char(ctx, protocol, (u8_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; |
| } |