1 /* 2 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. 3 * Copyright (c) 2006 Christian Walter <[email protected]> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * File: $Id: mb.c,v 1.27 2007/02/18 23:45:41 wolti Exp $ 29 */ 30 31 /* ----------------------- System includes ----------------------------------*/ 32 #include "stdlib.h" 33 #include "string.h" 34 35 /* ----------------------- Platform includes --------------------------------*/ 36 #include "port.h" 37 38 /* ----------------------- Modbus includes ----------------------------------*/ 39 40 #include "mb.h" 41 #include "mbconfig.h" 42 #include "mbframe.h" 43 #include "mbproto.h" 44 #include "mbfunc.h" 45 46 #include "mbport.h" 47 #if MB_SLAVE_RTU_ENABLED == 1 48 #include "mbrtu.h" 49 #endif 50 #if MB_SLAVE_ASCII_ENABLED == 1 51 #include "mbascii.h" 52 #endif 53 #if MB_SLAVE_TCP_ENABLED == 1 54 #include "mbtcp.h" 55 #endif 56 57 #ifndef MB_PORT_HAS_CLOSE 58 #define MB_PORT_HAS_CLOSE 0 59 #endif 60 61 /* ----------------------- Static variables ---------------------------------*/ 62 63 static UCHAR ucMBAddress; 64 static eMBMode eMBCurrentMode; 65 66 static enum 67 { 68 STATE_ENABLED, 69 STATE_DISABLED, 70 STATE_NOT_INITIALIZED 71 } eMBState = STATE_NOT_INITIALIZED; 72 73 /* Functions pointer which are initialized in eMBInit( ). Depending on the 74 * mode (RTU or ASCII) the are set to the correct implementations. 75 * Using for Modbus Slave 76 */ 77 static peMBFrameSend peMBFrameSendCur; 78 static pvMBFrameStart pvMBFrameStartCur; 79 static pvMBFrameStop pvMBFrameStopCur; 80 static peMBFrameReceive peMBFrameReceiveCur; 81 static pvMBFrameClose pvMBFrameCloseCur; 82 83 /* Callback functions required by the porting layer. They are called when 84 * an external event has happend which includes a timeout or the reception 85 * or transmission of a character. 86 * Using for Modbus Slave 87 */ 88 BOOL( *pxMBFrameCBByteReceived ) ( void ); 89 BOOL( *pxMBFrameCBTransmitterEmpty ) ( void ); 90 BOOL( *pxMBPortCBTimerExpired ) ( void ); 91 92 BOOL( *pxMBFrameCBReceiveFSMCur ) ( void ); 93 BOOL( *pxMBFrameCBTransmitFSMCur ) ( void ); 94 95 /* An array of Modbus functions handlers which associates Modbus function 96 * codes with implementing functions. 97 */ 98 static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = { 99 #if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0 100 {MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID}, 101 #endif 102 #if MB_FUNC_READ_INPUT_ENABLED > 0 103 {MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister}, 104 #endif 105 #if MB_FUNC_READ_HOLDING_ENABLED > 0 106 {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister}, 107 #endif 108 #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0 109 {MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister}, 110 #endif 111 #if MB_FUNC_WRITE_HOLDING_ENABLED > 0 112 {MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister}, 113 #endif 114 #if MB_FUNC_READWRITE_HOLDING_ENABLED > 0 115 {MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister}, 116 #endif 117 #if MB_FUNC_READ_COILS_ENABLED > 0 118 {MB_FUNC_READ_COILS, eMBFuncReadCoils}, 119 #endif 120 #if MB_FUNC_WRITE_COIL_ENABLED > 0 121 {MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil}, 122 #endif 123 #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 124 {MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils}, 125 #endif 126 #if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0 127 {MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs}, 128 #endif 129 }; 130 131 /* ----------------------- Start implementation -----------------------------*/ 132 eMBErrorCode 133 eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) 134 { 135 eMBErrorCode eStatus = MB_ENOERR; 136 137 /* check preconditions */ 138 if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) || 139 ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ) 140 { 141 eStatus = MB_EINVAL; 142 } 143 else 144 { 145 ucMBAddress = ucSlaveAddress; 146 147 switch ( eMode ) 148 { 149 #if MB_SLAVE_RTU_ENABLED > 0 150 case MB_RTU: 151 pvMBFrameStartCur = eMBRTUStart; 152 pvMBFrameStopCur = eMBRTUStop; 153 peMBFrameSendCur = eMBRTUSend; 154 peMBFrameReceiveCur = eMBRTUReceive; 155 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; 156 pxMBFrameCBByteReceived = xMBRTUReceiveFSM; 157 pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; 158 pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; 159 160 eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity ); 161 break; 162 #endif 163 #if MB_SLAVE_ASCII_ENABLED > 0 164 case MB_ASCII: 165 pvMBFrameStartCur = eMBASCIIStart; 166 pvMBFrameStopCur = eMBASCIIStop; 167 peMBFrameSendCur = eMBASCIISend; 168 peMBFrameReceiveCur = eMBASCIIReceive; 169 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; 170 pxMBFrameCBByteReceived = xMBASCIIReceiveFSM; 171 pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM; 172 pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired; 173 174 eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity ); 175 break; 176 #endif 177 default: 178 eStatus = MB_EINVAL; 179 break; 180 } 181 182 if( eStatus == MB_ENOERR ) 183 { 184 if( !xMBPortEventInit( ) ) 185 { 186 /* port dependent event module initalization failed. */ 187 eStatus = MB_EPORTERR; 188 } 189 else 190 { 191 eMBCurrentMode = eMode; 192 eMBState = STATE_DISABLED; 193 } 194 } 195 } 196 return eStatus; 197 } 198 199 #if MB_SLAVE_TCP_ENABLED > 0 200 eMBErrorCode 201 eMBTCPInit( USHORT ucTCPPort ) 202 { 203 eMBErrorCode eStatus = MB_ENOERR; 204 205 if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) 206 { 207 eMBState = STATE_DISABLED; 208 } 209 else if( !xMBPortEventInit( ) ) 210 { 211 /* Port dependent event module initalization failed. */ 212 eStatus = MB_EPORTERR; 213 } 214 else 215 { 216 pvMBFrameStartCur = eMBTCPStart; 217 pvMBFrameStopCur = eMBTCPStop; 218 peMBFrameReceiveCur = eMBTCPReceive; 219 peMBFrameSendCur = eMBTCPSend; 220 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL; 221 ucMBAddress = MB_TCP_PSEUDO_ADDRESS; 222 eMBCurrentMode = MB_TCP; 223 eMBState = STATE_DISABLED; 224 } 225 return eStatus; 226 } 227 #endif 228 229 eMBErrorCode 230 eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler ) 231 { 232 int i; 233 eMBErrorCode eStatus; 234 235 if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) ) 236 { 237 ENTER_CRITICAL_SECTION( ); 238 if( pxHandler != NULL ) 239 { 240 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) 241 { 242 if( ( xFuncHandlers[i].pxHandler == NULL ) || 243 ( xFuncHandlers[i].pxHandler == pxHandler ) ) 244 { 245 xFuncHandlers[i].ucFunctionCode = ucFunctionCode; 246 xFuncHandlers[i].pxHandler = pxHandler; 247 break; 248 } 249 } 250 eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES; 251 } 252 else 253 { 254 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) 255 { 256 if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) 257 { 258 xFuncHandlers[i].ucFunctionCode = 0; 259 xFuncHandlers[i].pxHandler = NULL; 260 break; 261 } 262 } 263 /* Remove can't fail. */ 264 eStatus = MB_ENOERR; 265 } 266 EXIT_CRITICAL_SECTION( ); 267 } 268 else 269 { 270 eStatus = MB_EINVAL; 271 } 272 return eStatus; 273 } 274 275 276 eMBErrorCode 277 eMBClose( void ) 278 { 279 eMBErrorCode eStatus = MB_ENOERR; 280 281 if( eMBState == STATE_DISABLED ) 282 { 283 if( pvMBFrameCloseCur != NULL ) 284 { 285 pvMBFrameCloseCur( ); 286 } 287 } 288 else 289 { 290 eStatus = MB_EILLSTATE; 291 } 292 return eStatus; 293 } 294 295 296 eMBErrorCode 297 eMBEnable( void ) 298 { 299 eMBErrorCode eStatus = MB_ENOERR; 300 301 if( eMBState == STATE_DISABLED ) 302 { 303 /* Activate the protocol stack. */ 304 pvMBFrameStartCur( ); 305 eMBState = STATE_ENABLED; 306 } 307 else 308 { 309 eStatus = MB_EILLSTATE; 310 } 311 return eStatus; 312 } 313 314 eMBErrorCode 315 eMBDisable( void ) 316 { 317 eMBErrorCode eStatus; 318 319 if( eMBState == STATE_ENABLED ) 320 { 321 pvMBFrameStopCur( ); 322 eMBState = STATE_DISABLED; 323 eStatus = MB_ENOERR; 324 } 325 else if( eMBState == STATE_DISABLED ) 326 { 327 eStatus = MB_ENOERR; 328 } 329 else 330 { 331 eStatus = MB_EILLSTATE; 332 } 333 return eStatus; 334 } 335 336 eMBErrorCode eMBPoll( void ) 337 { 338 static UCHAR *ucMBFrame; 339 static UCHAR ucRcvAddress; 340 static UCHAR ucFunctionCode; 341 static USHORT usLength; 342 static eMBException eException; 343 344 int i; 345 eMBErrorCode eStatus = MB_ENOERR; 346 eMBEventType eEvent; 347 348 /* Check if the protocol stack is ready. */ 349 if( eMBState != STATE_ENABLED ) 350 { 351 return MB_EILLSTATE; 352 } 353 354 /* Check if there is a event available. If not return control to caller. 355 * Otherwise we will handle the event. */ 356 if( xMBPortEventGet( &eEvent ) == TRUE ) 357 { 358 switch ( eEvent ) 359 { 360 case EV_READY: 361 break; 362 363 case EV_FRAME_RECEIVED: 364 eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); 365 if( eStatus == MB_ENOERR ) 366 { 367 /* Check if the frame is for us. If not ignore the frame. */ 368 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) 369 { 370 ( void )xMBPortEventPost( EV_EXECUTE ); 371 } 372 } 373 break; 374 375 case EV_EXECUTE: 376 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; 377 eException = MB_EX_ILLEGAL_FUNCTION; 378 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) 379 { 380 /* No more function handlers registered. Abort. */ 381 if( xFuncHandlers[i].ucFunctionCode == 0 ) 382 { 383 break; 384 } 385 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) 386 { 387 eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); 388 break; 389 } 390 } 391 392 /* If the request was not sent to the broadcast address we 393 * return a reply. */ 394 if( ucRcvAddress != MB_ADDRESS_BROADCAST ) 395 { 396 if( eException != MB_EX_NONE ) 397 { 398 /* An exception occured. Build an error frame. */ 399 usLength = 0; 400 ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); 401 ucMBFrame[usLength++] = eException; 402 } 403 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); 404 } 405 break; 406 407 case EV_FRAME_SENT: 408 break; 409 } 410 } 411 return MB_ENOERR; 412 } 413