xref: /nrf52832-nimble/rt-thread/components/net/freemodbus/modbus/mb.c (revision 104654410c56c573564690304ae786df310c91fc)
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
eMBInit(eMBMode eMode,UCHAR ucSlaveAddress,UCHAR ucPort,ULONG ulBaudRate,eMBParity eParity)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
eMBTCPInit(USHORT ucTCPPort)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
eMBRegisterCB(UCHAR ucFunctionCode,pxMBFunctionHandler pxHandler)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
eMBClose(void)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
eMBEnable(void)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
eMBDisable(void)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 
eMBPoll(void)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