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