Modbus RTU/ASCII/TCP with lwip TCP working partial, but with errors (retransmitions)

Dependencies:   EthernetNetIf mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers porttcp.cpp Source File

porttcp.cpp

00001 /*
00002  * FreeModbus Libary: lwIP Port
00003  * Copyright (C) 2006 Christian Walter <wolti@sil.at>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2.1 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  *
00019  * File: $Id: porttcp.c,v 1.1 2006/08/30 23:18:07 wolti Exp $
00020  */
00021 #include "mbed.h"                   // Cam
00022 
00023 extern DigitalOut RXLed;
00024 extern DigitalOut TXLed;
00025 extern Timeout RXTimeout;
00026 extern Timeout TXTimeout;
00027 
00028 void RXTimeoutFunc(void);
00029 void TXTimeoutFunc(void);
00030 
00031 /* ----------------------- System includes ----------------------------------*/
00032 #include <stdio.h>
00033 #include <string.h>
00034 
00035 #include "port.h"
00036 
00037 /* ----------------------- lwIP includes ------------------------------------*/
00038 #include "lwip/api.h"
00039 #include "lwip/tcp.h"
00040 
00041 /* ----------------------- Modbus includes ----------------------------------*/
00042 #include "mb.h"
00043 #include "mbport.h"
00044 
00045 /* ----------------------- MBAP Header --------------------------------------*/
00046 #define MB_TCP_UID          6
00047 #define MB_TCP_LEN          4
00048 #define MB_TCP_FUNC         7
00049 
00050 /* ----------------------- Defines  -----------------------------------------*/
00051 #define MB_TCP_DEFAULT_PORT 502 /* TCP listening port. */
00052 #define MB_TCP_BUF_SIZE     ( 256 + 7 ) /* Must hold a complete Modbus TCP frame. */
00053 
00054 /* ----------------------- Prototypes ---------------------------------------*/
00055 void            vMBPortEventClose( void );
00056 #ifdef MB_TCP_DEBUG
00057 void            vMBPortLog( eMBPortLogLevel eLevel, const CHAR * szModule,
00058                             const CHAR * szFmt, ... );
00059 #endif
00060 
00061 /* ----------------------- Static variables ---------------------------------*/
00062 static struct tcp_pcb *pxPCBListen;
00063 static struct tcp_pcb *pxPCBClient;
00064 
00065 static UCHAR    aucTCPBuf[MB_TCP_BUF_SIZE];
00066 static USHORT   usTCPBufPos;
00067 
00068 /* ----------------------- Static functions ---------------------------------*/
00069 static err_t    prvxMBTCPPortAccept( void *pvArg, struct tcp_pcb *pxPCB, err_t xErr );
00070 static err_t    prvxMBTCPPortReceive( void *pvArg, struct tcp_pcb *pxPCB, struct pbuf *p,
00071                                       err_t xErr );
00072 static void     prvvMBTCPPortError( void *pvArg, err_t xErr );
00073 
00074 /* ----------------------- Begin implementation -----------------------------*/
00075 BOOL
00076 xMBTCPPortInit( USHORT usTCPPort )
00077 {
00078     struct tcp_pcb *pxPCBListenNew, *pxPCBListenOld;
00079     BOOL            bOkay = FALSE;
00080     USHORT          usPort;
00081 
00082     if( usTCPPort == 0 )
00083     {
00084         usPort = MB_TCP_DEFAULT_PORT;
00085     }
00086     else
00087     {
00088         usPort = ( USHORT ) usTCPPort;
00089     }
00090 
00091     if( ( pxPCBListenNew = pxPCBListenOld = tcp_new(  ) ) == NULL )
00092     {
00093         /* Can't create TCP socket. */
00094         bOkay = FALSE;
00095     }
00096     else if( tcp_bind( pxPCBListenNew, IP_ADDR_ANY, ( u16_t ) usPort ) != ERR_OK )
00097     {
00098         /* Bind failed - Maybe illegal port value or in use. */
00099         ( void )tcp_close( pxPCBListenOld );
00100         bOkay = FALSE;
00101     }
00102     else if( ( pxPCBListenNew = tcp_listen( pxPCBListenNew ) ) == NULL )
00103     {
00104         ( void )tcp_close( pxPCBListenOld );
00105         bOkay = FALSE;
00106     }
00107     else
00108     {
00109         /* Register callback function for new clients. */
00110         tcp_accept( pxPCBListenNew, prvxMBTCPPortAccept );
00111 
00112         /* Everything okay. Set global variable. */
00113         pxPCBListen = pxPCBListenNew;
00114 
00115 #ifdef MB_TCP_DEBUG
00116         vMBPortLog( MB_LOG_DEBUG, "MBTCP-ACCEPT", "Protocol stack ready.\r\n" );
00117 #endif
00118     }
00119     bOkay = TRUE;
00120     return bOkay;
00121 }
00122 
00123 void
00124 prvvMBPortReleaseClient( struct tcp_pcb *pxPCB )
00125 {
00126     if( pxPCB != NULL )
00127     {
00128         if( tcp_close( pxPCB ) != ERR_OK )
00129         {
00130             tcp_abort( pxPCB );
00131         }
00132         if( pxPCB == pxPCBClient )
00133         {
00134 #ifdef MB_TCP_DEBUG
00135             vMBPortLog( MB_LOG_DEBUG, "MBTCP-CLOSE", "Closed connection to %d.%d.%d.%d.\r\n",
00136                         ip4_addr1( &( pxPCB->remote_ip ) ),
00137                         ip4_addr2( &( pxPCB->remote_ip ) ),
00138                         ip4_addr3( &( pxPCB->remote_ip ) ), ip4_addr4( &( pxPCB->remote_ip ) ) );
00139 #endif
00140             pxPCBClient = NULL;
00141         }
00142         if( pxPCB == pxPCBListen )
00143         {
00144             pxPCBListen = NULL;
00145         }
00146     }
00147 }
00148 void
00149 vMBTCPPortClose(  )
00150 {
00151     /* Shutdown any open client sockets. */
00152     prvvMBPortReleaseClient( pxPCBClient );
00153 
00154     /* Shutdown or listening socket. */
00155     prvvMBPortReleaseClient( pxPCBListen );
00156 
00157     /* Release resources for the event queue. */
00158 //    vMBPortEventClose(  );
00159 }
00160 
00161 void
00162 vMBTCPPortDisable( void )
00163 {
00164     prvvMBPortReleaseClient( pxPCBClient );
00165 }
00166 
00167 err_t
00168 prvxMBTCPPortAccept( void *pvArg, struct tcp_pcb *pxPCB, err_t xErr )
00169 {
00170     err_t           error;
00171 
00172     if( xErr != ERR_OK )
00173     {
00174         return xErr;
00175     }
00176 
00177     /* We can handle only one client. */
00178     if( pxPCBClient == NULL )
00179     {
00180         /* Register the client. */
00181         pxPCBClient = pxPCB;
00182 
00183         /* Set up the receive function prvxMBTCPPortReceive( ) to be called when data
00184          * arrives.
00185          */
00186         tcp_recv( pxPCB, prvxMBTCPPortReceive );
00187 
00188         /* Register error handler. */
00189         tcp_err( pxPCB, prvvMBTCPPortError );
00190 
00191         /* Set callback argument later used in the error handler. */
00192         tcp_arg( pxPCB, pxPCB );
00193 
00194         /* Reset the buffers and state variables. */
00195         usTCPBufPos = 0;
00196 
00197 #ifdef MB_TCP_DEBUG
00198         vMBPortLog( MB_LOG_DEBUG, "MBTCP-ACCEPT", "Accepted new client %d.%d.%d.%d\r\n",
00199                     ip4_addr1( &( pxPCB->remote_ip ) ),
00200                     ip4_addr2( &( pxPCB->remote_ip ) ),
00201                     ip4_addr3( &( pxPCB->remote_ip ) ), ip4_addr4( &( pxPCB->remote_ip ) ) );
00202 #endif
00203 
00204         error = ERR_OK;
00205     }
00206     else
00207     {
00208         prvvMBPortReleaseClient( pxPCB );
00209         error = ERR_OK;
00210     }
00211     return error;
00212 }
00213 
00214 /* Called in case of an unrecoverable error. In any case we drop the client
00215  * connection. */
00216 void
00217 prvvMBTCPPortError( void *pvArg, err_t xErr )
00218 {
00219     struct tcp_pcb *pxPCB = (tcp_pcb*)&pvArg;
00220 
00221     if( pxPCB != NULL )
00222     {
00223 #ifdef MB_TCP_DEBUG
00224         vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR", "Error with client connection! Droping it.\r\n" );
00225 #endif
00226         prvvMBPortReleaseClient( pxPCB );
00227     }
00228 }
00229 
00230 err_t
00231 prvxMBTCPPortReceive( void *pvArg, struct tcp_pcb *pxPCB, struct pbuf *p, err_t xErr )
00232 {
00233     USHORT          usLength;
00234 
00235     err_t           error;
00236 
00237     if( xErr != ERR_OK )
00238     {
00239         return xErr;
00240     }
00241 
00242     /* If pbuf is NULL then remote end has closed connection. */
00243     if( p == NULL )
00244     {
00245         prvvMBPortReleaseClient( pxPCB );
00246         return ERR_OK;
00247     }
00248 
00249     /* Acknowledge that we have received the data bytes. */
00250 //    tcp_recved( pxPCB, p->len );
00251 
00252     RXLed = 1;
00253     RXTimeout.attach(RXTimeoutFunc, 0.020);
00254 
00255     /* Check for internal buffer overflow. In case of an error drop the
00256      * client. */
00257     if( ( usTCPBufPos + p->len ) >= MB_TCP_BUF_SIZE )
00258     {
00259         prvvMBPortReleaseClient( pxPCB );
00260         error = ERR_OK;
00261     }
00262     else
00263     {
00264         memcpy( &aucTCPBuf[usTCPBufPos], p->payload, p->len );
00265         usTCPBufPos += p->len;
00266 
00267         /* If we have received the MBAP header we can analyze it and calculate
00268          * the number of bytes left to complete the current request. If complete
00269          * notify the protocol stack.
00270          */
00271         if( usTCPBufPos >= MB_TCP_FUNC )
00272         {
00273             /* Length is a byte count of Modbus PDU (function code + data) and the
00274              * unit identifier. */
00275             usLength = aucTCPBuf[MB_TCP_LEN] << 8U;
00276             usLength |= aucTCPBuf[MB_TCP_LEN + 1];
00277 
00278             /* Is the frame already complete. */
00279             if( usTCPBufPos < ( MB_TCP_UID + usLength ) )
00280             {
00281             }
00282             else if( usTCPBufPos == ( MB_TCP_UID + usLength ) )
00283             {
00284 #ifdef MB_TCP_DEBUG
00285                 prvvMBTCPLogFrame( "MBTCP-RECV", &aucTCPBuf[0], usTCPBufPos );
00286 #endif
00287                 ( void )xMBPortEventPost( EV_FRAME_RECEIVED );
00288             }
00289             else
00290             {
00291 #ifdef MB_TCP_DEBUG
00292                 vMBPortLog( MB_LOG_DEBUG, "MBTCP-ERROR",
00293                             "Received to many bytes! Droping client.\r\n" );
00294 #endif
00295                 /* This should not happen. We can't deal with such a client and
00296                  * drop the connection for security reasons.
00297                  */
00298                 prvvMBPortReleaseClient( pxPCB );
00299             }
00300         }
00301     }
00302     pbuf_free( p );
00303     return error;
00304 }
00305 
00306 BOOL
00307 xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
00308 {
00309     *ppucMBTCPFrame = &aucTCPBuf[0];
00310     *usTCPLength = usTCPBufPos;
00311 
00312     /* Reset the buffer. */
00313     usTCPBufPos = 0;
00314     return TRUE;
00315 }
00316 
00317 BOOL
00318 xMBTCPPortSendResponse( const UCHAR * pucMBTCPFrame, USHORT usTCPLength )
00319 {
00320     BOOL            bFrameSent = FALSE;
00321 
00322     if( pxPCBClient )
00323     {
00324         /* Make sure we can send the packet. */
00325         assert( tcp_sndbuf( pxPCBClient ) >= usTCPLength );
00326 
00327 //        if( tcp_write( pxPCBClient, pucMBTCPFrame, ( u16_t ) usTCPLength, NETCONN_COPY ) == ERR_OK )
00328         if( tcp_write( pxPCBClient, pucMBTCPFrame, ( u16_t ) usTCPLength, 1 ) == ERR_OK )
00329         {
00330 #ifdef MB_TCP_DEBUG
00331             prvvMBTCPLogFrame( "MBTCP-SENT", &aucTCPBuf[0], usTCPLength );
00332 #endif
00333             /* Make sure data gets sent immediately. */
00334             ( void )tcp_output( pxPCBClient );
00335             bFrameSent = TRUE;
00336 
00337             TXLed = 1;
00338             TXTimeout.attach(TXTimeoutFunc, 0.020);
00339        }
00340         else
00341         {
00342             /* Drop the connection in case of an write error. */
00343             prvvMBPortReleaseClient( pxPCBClient );
00344         }
00345     }
00346     return bFrameSent;
00347 }