/* www.mycal.com *--------------------------------------------------------------------------- * 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. * * This file is part of the Mycal Modified uIP TCP/IP stack. * * $Id: ahdlc.c,v 1.1 2006/06/17 22:48:09 adamdunkels Exp $ * */ /* */ /* include files */ /* */ #include "net/uip.h" #include "ppp.h" #if 0 #define DEBUG1(x) #else #include #define DEBUG1(x) debug_printf x #endif #define PACKET_TX_DEBUG 1 /*--------------------------------------------------------------------------- * 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 Local Globals * 10 bytes - standard * - with counters ---------------------------------------------------------------------------*/ /* running tx CRC */ u16_t ahdlc_tx_crc; /* running rx CRC */ u16_t ahdlc_rx_crc; /* number of rx bytes processed, cur frame */ u16_t ahdlc_rx_count; /* ahdlc state flags, see above */ u8_t ahdlc_flags; u8_t ahdlc_tx_offline; /* * The following can be optimized out */ u8_t *ahdlc_rx_buffer; /* What to do here? +++ */ u16_t ahdlc_max_rx_buffer_size; /* * Optional statistics counters. */ #ifdef AHDLC_COUNTERS u8_t ahdlc_rx_tobig_error; #endif /*---------------------------------------------------------------------------*/ /* 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); } /*---------------------------------------------------------------------------*/ /* ahdlc_init(buffer, buffersize) - this initializes the ahdlc engine to * allow for rx frames. */ /*---------------------------------------------------------------------------*/ void ahdlc_init(u8_t *buffer, u16_t maxrxbuffersize) { ahdlc_flags = 0 | AHDLC_RX_ASYNC_MAP; ahdlc_rx_buffer = buffer; ahdlc_max_rx_buffer_size = maxrxbuffersize; /* ahdlc_async_map = 0; */ #ifdef AHDLC_COUNTERS ahdlc_rx_tobig_error = 0; ahdl_tx_offline = 0; #endif } /*---------------------------------------------------------------------------*/ /* ahdlc_rx_ready() - resets the ahdlc engine to the beginning of frame * state. */ /*---------------------------------------------------------------------------*/ void ahdlc_rx_ready(void) { ahdlc_rx_count = 0; ahdlc_rx_crc = 0xffff; 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(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(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) && ((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(ahdlc_flags & AHDLC_ESCAPED) { /* set escaped to FALSE */ ahdlc_flags &= ~AHDLC_ESCAPED; /* if value is 0x7e then silently discard and reset receive packet */ if(c == 0x7e) { ahdlc_rx_ready(); return 0; } /* incomming char = itself xor 20 */ c = c ^ 0x20; } else if(c == 0x7e) { /* handle frame end */ if(ahdlc_rx_crc == CRC_GOOD_VALUE) { DEBUG1(("\nReceiving packet with good crc value, len %d\n",ahdlc_rx_count)); /* we hae a good packet, turn off CTS until we are done with this packet */ /*CTS_OFF();*/ /* remove CRC bytes from packet */ ahdlc_rx_count -= 2; /* lock PPP buffer */ 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) && (ahdlc_flags & PPP_PFC)) { /* Send up packet */ ppp_upcall((u16_t)ahdlc_rx_buffer[0], (u8_t *)&ahdlc_rx_buffer[1], (u16_t)(ahdlc_rx_count - 1)); } else { /* Send up packet */ ppp_upcall((u16_t)(ahdlc_rx_buffer[0] << 8 | ahdlc_rx_buffer[1]), (u8_t *)&ahdlc_rx_buffer[2], (u16_t)(ahdlc_rx_count - 2)); } ahdlc_tx_offline = 0; // The remote side is alive ahdlc_rx_ready(); return 0; } else if(ahdlc_rx_count > 3) { DEBUG1(("\nReceiving packet with bad crc value, was 0x%04x len %d\n",ahdlc_rx_crc, ahdlc_rx_count)); #ifdef AHDLC_COUNTERS ++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(); return 0; } else if(c == 0x7d) { /* handle escaped chars*/ ahdlc_flags |= PPP_ESCAPED; return 0; } /* try to store char if not to big */ if(ahdlc_rx_count >= ahdlc_max_rx_buffer_size /*PPP_RX_BUFFER_SIZE*/) { #ifdef AHDLC_COUNTERS ++ahdlc_rx_tobig_error; #endif ahdlc_rx_ready(); } else { /* Add CRC in */ ahdlc_rx_crc = crcadd(ahdlc_rx_crc, c); /* do auto ACFC, if packet len is zero discard 0xff and 0x03 */ if(ahdlc_rx_count == 0) { if((c == 0xff) || (c == 0x03)) return 0; } /* Store char */ ppp_rx_buffer[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(u16_t protocol, u8_t c) { /* add in crc */ ahdlc_tx_crc = crcadd(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) || (ahdlc_flags & PPP_TX_ASYNC_MAP) == 0))) { /* send escape char and xor byte by 0x20 */ ppp_arch_putchar(0x7d); c ^= 0x20; } ppp_arch_putchar(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(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,ahdlc_tx_offline)); if (AHDLC_TX_OFFLINE && (ahdlc_tx_offline++ > AHDLC_TX_OFFLINE)) { ahdlc_tx_offline = 0; DEBUG1(("\nAHDLC_TX to many outstanding TX packets => ppp_reconnect()\n")); ppp_reconnect(); 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(0x7e); /* set initial CRC value */ 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 == (ahdlc_flags & PPP_ACFC)) || (protocol == LCP)) { ahdlc_tx_char(protocol, 0xff); ahdlc_tx_char(protocol, 0x03); } /* Write Protocol */ ahdlc_tx_char(protocol,(u8_t)(protocol >> 8)); ahdlc_tx_char(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(protocol, c); } /* Write frame bytes */ for(i = 0; i < datalen; ++i) { /* Get next byte from buffer */ c = buffer[i]; /* Write it...*/ ahdlc_tx_char(protocol, c); } /* send crc, lsb then msb */ i = ahdlc_tx_crc ^ 0xffff; ahdlc_tx_char(protocol, (u8_t)(i & 0xff)); ahdlc_tx_char(protocol, (u8_t)((i >> 8) & 0xff)); /* write trailing 0x7e, probably not needed but it doesn't hurt*/ ppp_arch_putchar(0x7e); return 0; } /*---------------------------------------------------------------------------*/