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