Tom Martins / Mbed OS Nucleo_RS485_Modbus

Fork of Modbus by Cam Marshall

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