1 /*
2  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
3  * Copyright (c) 2006 Christian Walter <wolti@sil.at>
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.28 2010/06/06 13:54:40 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 #include "mb.h"
40 #include "mbconfig.h"
41 #include "mbframe.h"
42 #include "mbproto.h"
43 #include "mbfunc.h"
44 #include "mbport.h"
45 
46 #if MB_SLAVE_RTU_ENABLED
47 #include "mbrtu.h"
48 #endif
49 #if MB_SLAVE_ASCII_ENABLED
50 #include "mbascii.h"
51 #endif
52 #if MB_TCP_ENABLED
53 #include "mbtcp.h"
54 #endif
55 
56 #ifndef MB_PORT_HAS_CLOSE
57 #define MB_PORT_HAS_CLOSE 1
58 #endif
59 
60 /* ----------------------- Static variables ---------------------------------*/
61 
62 static UCHAR    ucMBAddress;
63 static eMBMode  eMBCurrentMode;
64 
65 volatile UCHAR ucMbSlaveBuf[MB_SERIAL_BUF_SIZE];
66 
67 static enum
68 {
69     STATE_ENABLED,
70     STATE_DISABLED,
71     STATE_NOT_INITIALIZED
72 } eMBState = STATE_NOT_INITIALIZED;
73 
74 /* Functions pointer which are initialized in eMBInit( ). Depending on the
75  * mode (RTU or ASCII) the are set to the correct implementations.
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  */
87 BOOL( *pxMBFrameCBByteReceived ) ( void );
88 BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
89 BOOL( *pxMBPortCBTimerExpired ) ( void );
90 BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
91 BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );
92 
93 /* An array of Modbus functions handlers which associates Modbus function
94  * codes with implementing functions.
95  */
96 static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
97 #if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
98     {MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
99 #endif
100 #if MB_FUNC_READ_INPUT_ENABLED > 0
101     {MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister},
102 #endif
103 #if MB_FUNC_READ_HOLDING_ENABLED > 0
104     {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
105 #endif
106 #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
107     {MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister},
108 #endif
109 #if MB_FUNC_WRITE_HOLDING_ENABLED > 0
110     {MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister},
111 #endif
112 #if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
113     {MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister},
114 #endif
115 #if MB_FUNC_READ_COILS_ENABLED > 0
116     {MB_FUNC_READ_COILS, eMBFuncReadCoils},
117 #endif
118 #if MB_FUNC_WRITE_COIL_ENABLED > 0
119     {MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil},
120 #endif
121 #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
122     {MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils},
123 #endif
124 #if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
125     {MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs},
126 #endif
127 };
128 
129 /* ----------------------- Start implementation -----------------------------*/
130 eMBErrorCode
eMBInit(eMBMode eMode,UCHAR ucSlaveAddress,UCHAR ucPort,ULONG ulBaudRate,eMBParity eParity)131 eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
132 {
133     eMBErrorCode    eStatus = MB_ENOERR;
134 
135     /* check preconditions */
136     if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
137         ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) )
138     {
139         eStatus = MB_EINVAL;
140     }
141     else
142     {
143         ucMBAddress = ucSlaveAddress;
144 
145         switch ( eMode )
146         {
147 #if MB_SLAVE_RTU_ENABLED > 0
148         case MB_RTU:
149             pvMBFrameStartCur = eMBRTUStart;
150             pvMBFrameStopCur = eMBRTUStop;
151             peMBFrameSendCur = eMBRTUSend;
152             peMBFrameReceiveCur = eMBRTUReceive;
153             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
154             pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
155             pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
156             pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
157 
158             eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
159             break;
160 #endif
161 #if MB_SLAVE_ASCII_ENABLED > 0
162         case MB_ASCII:
163             pvMBFrameStartCur = eMBASCIIStart;
164             pvMBFrameStopCur = eMBASCIIStop;
165             peMBFrameSendCur = eMBASCIISend;
166             peMBFrameReceiveCur = eMBASCIIReceive;
167             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
168             pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
169             pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
170             pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
171 
172             eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
173             break;
174 #endif
175         default:
176             eStatus = MB_EINVAL;
177         }
178 
179         if( eStatus == MB_ENOERR )
180         {
181             if( !xMBPortEventInit(  ) )
182             {
183                 /* port dependent event module initalization failed. */
184                 eStatus = MB_EPORTERR;
185             }
186             else
187             {
188                 eMBCurrentMode = eMode;
189                 eMBState = STATE_DISABLED;
190             }
191         }
192     }
193     return eStatus;
194 }
195 
196 #if MB_TCP_ENABLED > 0
197 eMBErrorCode
eMBTCPInit(USHORT ucTCPPort)198 eMBTCPInit( USHORT ucTCPPort )
199 {
200     eMBErrorCode    eStatus = MB_ENOERR;
201 
202     if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR )
203     {
204         eMBState = STATE_DISABLED;
205     }
206     else if( !xMBPortEventInit(  ) )
207     {
208         /* Port dependent event module initalization failed. */
209         eStatus = MB_EPORTERR;
210     }
211     else
212     {
213         pvMBFrameStartCur = eMBTCPStart;
214         pvMBFrameStopCur = eMBTCPStop;
215         peMBFrameReceiveCur = eMBTCPReceive;
216         peMBFrameSendCur = eMBTCPSend;
217         pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL;
218         ucMBAddress = MB_TCP_PSEUDO_ADDRESS;
219         eMBCurrentMode = MB_TCP;
220         eMBState = STATE_DISABLED;
221     }
222     return eStatus;
223 }
224 #endif
225 
226 eMBErrorCode
eMBRegisterCB(UCHAR ucFunctionCode,pxMBFunctionHandler pxHandler)227 eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler )
228 {
229     int             i;
230     eMBErrorCode    eStatus;
231 
232     if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= MB_FUNC_CODE_MAX ) )
233     {
234         ENTER_CRITICAL_SECTION(  );
235         if( pxHandler != NULL )
236         {
237             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
238             {
239                 if( ( xFuncHandlers[i].pxHandler == NULL ) ||
240                     ( xFuncHandlers[i].pxHandler == pxHandler ) )
241                 {
242                     xFuncHandlers[i].ucFunctionCode = ucFunctionCode;
243                     xFuncHandlers[i].pxHandler = pxHandler;
244                     break;
245                 }
246             }
247             eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES;
248         }
249         else
250         {
251             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
252             {
253                 if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
254                 {
255                     xFuncHandlers[i].ucFunctionCode = 0;
256                     xFuncHandlers[i].pxHandler = NULL;
257                     break;
258                 }
259             }
260             /* Remove can't fail. */
261             eStatus = MB_ENOERR;
262         }
263         EXIT_CRITICAL_SECTION(  );
264     }
265     else
266     {
267         eStatus = MB_EINVAL;
268     }
269     return eStatus;
270 }
271 
272 
273 eMBErrorCode
eMBClose(void)274 eMBClose( void )
275 {
276     eMBErrorCode    eStatus = MB_ENOERR;
277 
278     if( eMBState == STATE_DISABLED )
279     {
280         if( pvMBFrameCloseCur != NULL )
281         {
282             pvMBFrameCloseCur(  );
283         }
284     }
285     else
286     {
287         eStatus = MB_EILLSTATE;
288     }
289     return eStatus;
290 }
291 
292 eMBErrorCode
eMBEnable(void)293 eMBEnable( void )
294 {
295     eMBErrorCode    eStatus = MB_ENOERR;
296 
297     if( eMBState == STATE_DISABLED )
298     {
299         /* Activate the protocol stack. */
300         pvMBFrameStartCur(  );
301         eMBState = STATE_ENABLED;
302     }
303     else
304     {
305         eStatus = MB_EILLSTATE;
306     }
307     return eStatus;
308 }
309 
310 eMBErrorCode
eMBDisable(void)311 eMBDisable( void )
312 {
313     eMBErrorCode    eStatus;
314 
315     if( eMBState == STATE_ENABLED )
316     {
317         pvMBFrameStopCur(  );
318         eMBState = STATE_DISABLED;
319         eStatus = MB_ENOERR;
320     }
321     else if( eMBState == STATE_DISABLED )
322     {
323         eStatus = MB_ENOERR;
324     }
325     else
326     {
327         eStatus = MB_EILLSTATE;
328     }
329     return eStatus;
330 }
331 
332 eMBErrorCode
eMBPoll(void)333 eMBPoll( void )
334 {
335     static UCHAR    *ucMBFrame = NULL;
336     static UCHAR    ucRcvAddress;
337     static UCHAR    ucFunctionCode;
338     static USHORT   usLength;
339     static eMBException eException;
340 
341     int             i;
342     eMBErrorCode    eStatus = MB_ENOERR;
343     eMBEventType    eEvent;
344 
345     /* Check if the protocol stack is ready. */
346     if( eMBState != STATE_ENABLED )
347     {
348         return MB_EILLSTATE;
349     }
350 
351     /* Check if there is a event available. If not return control to caller.
352      * Otherwise we will handle the event. */
353     if( xMBPortEventGet( &eEvent ) == TRUE )
354     {
355         switch ( eEvent )
356         {
357         case EV_READY:
358             ESP_LOGD(MB_PORT_TAG, "%s:EV_READY", __func__);
359             break;
360 
361         case EV_FRAME_RECEIVED:
362             ESP_LOGD(MB_PORT_TAG, "EV_FRAME_RECEIVED");
363             eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
364             if( eStatus == MB_ENOERR )
365             {
366                 /* Check if the frame is for us. If not ignore the frame. */
367                 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
368                 {
369                     ( void )xMBPortEventPost( EV_EXECUTE );
370                     ESP_LOG_BUFFER_HEX_LEVEL(MB_PORT_TAG, &ucMBFrame[MB_PDU_FUNC_OFF], usLength, ESP_LOG_DEBUG);
371                 }
372             }
373             break;
374 
375         case EV_EXECUTE:
376             if ( !ucMBFrame ) {
377                 return MB_EILLSTATE;
378             }
379             ESP_LOGD(MB_PORT_TAG, "%s:EV_EXECUTE", __func__);
380             ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
381             eException = MB_EX_ILLEGAL_FUNCTION;
382             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
383             {
384                 /* No more function handlers registered. Abort. */
385                 if( xFuncHandlers[i].ucFunctionCode == 0 )
386                 {
387                     break;
388                 }
389                 if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
390                 {
391                     eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
392                     break;
393                 }
394             }
395 
396             /* If the request was not sent to the broadcast address we
397              * return a reply. */
398             if( ucRcvAddress != MB_ADDRESS_BROADCAST )
399             {
400                 if( eException != MB_EX_NONE )
401                 {
402                     /* An exception occurred. Build an error frame. */
403                     usLength = 0;
404                     ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
405                     ucMBFrame[usLength++] = eException;
406                 }
407                 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
408                 {
409                     vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
410                 }
411                 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
412             }
413             break;
414 
415         case EV_FRAME_TRANSMIT:
416             ESP_LOGD(MB_PORT_TAG, "%s:EV_FRAME_TRANSMIT", __func__);
417             break;
418 
419         case EV_FRAME_SENT:
420             ESP_LOGD(MB_PORT_TAG, "%s:EV_FRAME_SENT", __func__);
421             break;
422 
423         default:
424             break;
425         }
426     }
427     return eStatus;
428 }
429