modbus_server.c
Go to the documentation of this file.
1 /**
2  * @file modbus_server.c
3  * @brief Modbus/TCP server
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2023 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.3.2
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL MODBUS_TRACE_LEVEL
33 
34 //Dependencies
35 #include "modbus/modbus_server.h"
38 #include "debug.h"
39 
40 //Check TCP/IP stack configuration
41 #if (MODBUS_SERVER_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Initialize settings with default values
46  * @param[out] settings Structure that contains Modbus/TCP server settings
47  **/
48 
50 {
51  //The Modbus/TCP server is not bound to any interface
52  settings->interface = NULL;
53 
54  //Modbus/TCP port number
55  settings->port = MODBUS_TCP_PORT;
56  //Default unit identifier
57  settings->unitId = MODBUS_DEFAULT_UNIT_ID;
58  //Idle connection timeout
59  settings->timeout = MODBUS_SERVER_TIMEOUT;
60 
61  //TCP connection open callback function
62  settings->openCallback = NULL;
63  //TCP connection close callback function
64  settings->closeCallback = NULL;
65 
66 #if (MODBUS_SERVER_TLS_SUPPORT == ENABLED)
67  //TLS initialization callback function
68  settings->tlsInitCallback = NULL;
69 #endif
70 
71  //Lock Modbus table callback function
72  settings->lockCallback = NULL;
73  //Unlock Modbus table callback function
74  settings->unlockCallback = NULL;
75  //Get coil state callback function
76  settings->readCoilCallback = NULL;
77  //Get discrete input state callback function
78  settings->readDiscreteInputCallback = NULL;
79  //Set coil state callback function
80  settings->writeCoilCallback = NULL;
81  //Get register value callback function
82  settings->readRegCallback = NULL;
83  //Get holding register value callback function
84  settings->readHoldingRegCallback = NULL;
85  //Get input register value callback function
86  settings->readInputRegCallback = NULL;
87  //Set register value callback function
88  settings->writeRegCallback = NULL;
89  //PDU processing callback
90  settings->processPduCallback = NULL;
91  //Tick callback function
92  settings->tickCallback = NULL;
93 }
94 
95 
96 /**
97  * @brief Initialize Modbus/TCP server context
98  * @param[in] context Pointer to the Modbus/TCP server context
99  * @param[in] settings Modbus/TCP server specific settings
100  * @return Error code
101  **/
102 
104  const ModbusServerSettings *settings)
105 {
106  error_t error;
107 
108  //Debug message
109  TRACE_INFO("Initializing Modbus/TCP server...\r\n");
110 
111  //Ensure the parameters are valid
112  if(context == NULL || settings == NULL)
114 
115  //Clear Modbus/TCP server context
116  osMemset(context, 0, sizeof(ModbusServerContext));
117 
118  //Save user settings
119  context->settings = *settings;
120 
121  //Initialize status code
122  error = NO_ERROR;
123 
124  //Create an event object to poll the state of sockets
125  if(!osCreateEvent(&context->event))
126  {
127  //Failed to create event
128  error = ERROR_OUT_OF_RESOURCES;
129  }
130 
131 #if (MODBUS_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
132  //Check status code
133  if(!error)
134  {
135  //Initialize ticket encryption context
136  error = tlsInitTicketContext(&context->tlsTicketContext);
137  }
138 #endif
139 
140  //Any error to report?
141  if(error)
142  {
143  //Clean up side effects
144  modbusServerDeinit(context);
145  }
146 
147  //Return status code
148  return error;
149 }
150 
151 
152 /**
153  * @brief Start Modbus/TCP server
154  * @param[in] context Pointer to the Modbus/TCP server context
155  * @return Error code
156  **/
157 
159 {
160  error_t error;
161 
162  //Make sure the Modbus/TCP server context is valid
163  if(context == NULL)
165 
166  //Debug message
167  TRACE_INFO("Starting Modbus/TCP server...\r\n");
168 
169  //Make sure the Modbus/TCP server is not already running
170  if(context->running)
171  return ERROR_ALREADY_RUNNING;
172 
173  //Start of exception handling block
174  do
175  {
176  //Open a TCP socket
178  //Failed to open socket?
179  if(context->socket == NULL)
180  {
181  //Report an error
182  error = ERROR_OPEN_FAILED;
183  break;
184  }
185 
186  //Force the socket to operate in non-blocking mode
187  error = socketSetTimeout(context->socket, 0);
188  //Any error to report?
189  if(error)
190  break;
191 
192  //Associate the socket with the relevant interface
193  error = socketBindToInterface(context->socket,
194  context->settings.interface);
195  //Any error to report?
196  if(error)
197  break;
198 
199  //The Modbus/TCP server listens for connection requests on port 502
200  error = socketBind(context->socket, &IP_ADDR_ANY, context->settings.port);
201  //Any error to report?
202  if(error)
203  break;
204 
205  //Place socket in listening state
206  error = socketListen(context->socket, 0);
207  //Any error to report?
208  if(error)
209  break;
210 
211  //Start the Modbus/TCP server
212  context->stop = FALSE;
213  context->running = TRUE;
214 
215 #if (OS_STATIC_TASK_SUPPORT == ENABLED)
216  //Create a task using statically allocated memory
217  context->taskId = osCreateStaticTask("Modbus/TCP Server",
218  (OsTaskCode) modbusServerTask, context, &context->taskTcb,
219  context->taskStack, MODBUS_SERVER_STACK_SIZE, MODBUS_SERVER_PRIORITY);
220 #else
221  //Create a task
222  context->taskId = osCreateTask("Modbus/TCP Server",
225 #endif
226 
227  //Failed to create task?
228  if(context->taskId == OS_INVALID_TASK_ID)
229  {
230  //Report an error
231  error = ERROR_OUT_OF_RESOURCES;
232  break;
233  }
234 
235  //End of exception handling block
236  } while(0);
237 
238  //Any error to report?
239  if(error)
240  {
241  //Clean up side effects
242  context->running = FALSE;
243 
244  //Close listening socket
245  socketClose(context->socket);
246  context->socket = NULL;
247  }
248 
249  //Return status code
250  return error;
251 }
252 
253 
254 /**
255  * @brief Stop Modbus/TCP server
256  * @param[in] context Pointer to the Modbus/TCP server context
257  * @return Error code
258  **/
259 
261 {
262  uint_t i;
263 
264  //Make sure the Modbus/TCP server context is valid
265  if(context == NULL)
267 
268  //Debug message
269  TRACE_INFO("Stopping Modbus/TCP server...\r\n");
270 
271  //Check whether the Modbus/TCP server is running
272  if(context->running)
273  {
274  //Stop the Modbus/TCP server
275  context->stop = TRUE;
276  //Send a signal to the task to abort any blocking operation
277  osSetEvent(&context->event);
278 
279  //Wait for the task to terminate
280  while(context->running)
281  {
282  osDelayTask(1);
283  }
284 
285  //Loop through the connection table
286  for(i = 0; i < MODBUS_SERVER_MAX_CONNECTIONS; i++)
287  {
288  //Check the state of the current connection
289  if(context->connection[i].state != MODBUS_CONNECTION_STATE_CLOSED)
290  {
291  //Close client connection
292  modbusServerCloseConnection(&context->connection[i]);
293  }
294  }
295 
296  //Close listening socket
297  socketClose(context->socket);
298  context->socket = NULL;
299  }
300 
301  //Successful processing
302  return NO_ERROR;
303 }
304 
305 
306 /**
307  * @brief Modbus/TCP server task
308  * @param[in] context Pointer to the Modbus/TCP server context
309  **/
310 
312 {
313  error_t error;
314  uint_t i;
315  systime_t timeout;
316  ModbusClientConnection *connection;
318 
319 #if (NET_RTOS_SUPPORT == ENABLED)
320  //Task prologue
321  osEnterTask();
322 
323  //Process events
324  while(1)
325  {
326 #endif
327  //Set polling timeout
328  timeout = MODBUS_SERVER_TICK_INTERVAL;
329 
330  //Clear event descriptor set
331  osMemset(eventDesc, 0, sizeof(eventDesc));
332 
333  //Specify the events the application is interested in
334  for(i = 0; i < MODBUS_SERVER_MAX_CONNECTIONS; i++)
335  {
336  //Point to the structure describing the current connection
337  connection = &context->connection[i];
338 
339  //Loop through active connections only
340  if(connection->state != MODBUS_CONNECTION_STATE_CLOSED)
341  {
342  //Register connection events
343  modbusServerRegisterConnectionEvents(connection, &eventDesc[i]);
344 
345  //Check whether the socket is ready for I/O operation
346  if(eventDesc[i].eventFlags != 0)
347  {
348  //No need to poll the underlying socket for incoming traffic
349  timeout = 0;
350  }
351  }
352  }
353 
354  //The Modbus/TCP server listens for connection requests on port 502
355  eventDesc[i].socket = context->socket;
356  eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
357 
358  //Wait for one of the set of sockets to become ready to perform I/O
359  error = socketPoll(eventDesc, MODBUS_SERVER_MAX_CONNECTIONS + 1,
360  &context->event, timeout);
361 
362  //Check status code
363  if(error == NO_ERROR || error == ERROR_TIMEOUT ||
364  error == ERROR_WAIT_CANCELED)
365  {
366  //Stop request?
367  if(context->stop)
368  {
369  //Stop Modbus/TCP server operation
370  context->running = FALSE;
371  //Task epilogue
372  osExitTask();
373  //Kill ourselves
375  }
376 
377  //Event-driven processing
378  for(i = 0; i < MODBUS_SERVER_MAX_CONNECTIONS; i++)
379  {
380  //Point to the structure describing the current connection
381  connection = &context->connection[i];
382 
383  //Loop through active connections only
384  if(connection->state != MODBUS_CONNECTION_STATE_CLOSED)
385  {
386  //Check whether the socket is ready to perform I/O
387  if(eventDesc[i].eventFlags != 0)
388  {
389  //Connection event handler
391  }
392  }
393  }
394 
395  //Any connection request received on port 502?
396  if(eventDesc[i].eventFlags != 0)
397  {
398  //Accept connection request
400  }
401  }
402 
403  //Handle periodic operations
404  modbusServerTick(context);
405 
406 #if (NET_RTOS_SUPPORT == ENABLED)
407  }
408 #endif
409 }
410 
411 
412 /**
413  * @brief Release Modbus/TCP server context
414  * @param[in] context Pointer to the Modbus/TCP server context
415  **/
416 
418 {
419  //Make sure the Modbus/TCP server context is valid
420  if(context != NULL)
421  {
422  //Free previously allocated resources
423  osDeleteEvent(&context->event);
424 
425 #if (MODBUS_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
426  //Release ticket encryption context
427  tlsFreeTicketContext(&context->tlsTicketContext);
428 #endif
429 
430  //Clear Modbus/TCP server context
431  osMemset(context, 0, sizeof(ModbusServerContext));
432  }
433 }
434 
435 #endif
unsigned int uint_t
Definition: compiler_port.h:50
Debugging facilities.
#define TRACE_INFO(...)
Definition: debug.h:95
error_t
Error codes.
Definition: error.h:43
@ ERROR_WAIT_CANCELED
Definition: error.h:73
@ ERROR_ALREADY_RUNNING
Definition: error.h:292
@ ERROR_TIMEOUT
Definition: error.h:95
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
@ ERROR_OPEN_FAILED
Definition: error.h:75
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
const IpAddr IP_ADDR_ANY
Definition: ip.c:51
#define MODBUS_TCP_PORT
Definition: modbus_common.h:38
#define MODBUS_DEFAULT_UNIT_ID
Definition: modbus_common.h:45
void modbusServerDeinit(ModbusServerContext *context)
Release Modbus/TCP server context.
error_t modbusServerInit(ModbusServerContext *context, const ModbusServerSettings *settings)
Initialize Modbus/TCP server context.
void modbusServerTask(ModbusServerContext *context)
Modbus/TCP server task.
void modbusServerGetDefaultSettings(ModbusServerSettings *settings)
Initialize settings with default values.
Definition: modbus_server.c:49
error_t modbusServerStop(ModbusServerContext *context)
Stop Modbus/TCP server.
error_t modbusServerStart(ModbusServerContext *context)
Start Modbus/TCP server.
Modbus/TCP server.
#define MODBUS_SERVER_TICK_INTERVAL
Definition: modbus_server.h:80
#define MODBUS_SERVER_STACK_SIZE
Definition: modbus_server.h:54
#define ModbusServerContext
#define MODBUS_SERVER_TIMEOUT
Definition: modbus_server.h:73
@ MODBUS_CONNECTION_STATE_CLOSED
#define MODBUS_SERVER_PRIORITY
Definition: modbus_server.h:61
#define ModbusClientConnection
#define MODBUS_SERVER_MAX_CONNECTIONS
Definition: modbus_server.h:66
void modbusServerRegisterConnectionEvents(ModbusClientConnection *connection, SocketEventDesc *eventDesc)
Register connection events.
void modbusServerTick(ModbusServerContext *context)
Handle periodic operations.
void modbusServerProcessConnectionEvents(ModbusClientConnection *connection)
Connection event handler.
Helper functions for Modbus/TCP server.
void modbusServerAcceptConnection(ModbusServerContext *context)
Accept connection request.
void modbusServerCloseConnection(ModbusClientConnection *connection)
Close network connection.
Transport protocol abstraction layer.
#define socketBindToInterface
Definition: net_legacy.h:193
#define osMemset(p, value, length)
Definition: os_port.h:137
#define TRUE
Definition: os_port.h:52
#define FALSE
Definition: os_port.h:48
void osDeleteEvent(OsEvent *event)
Delete an event object.
void osDelayTask(systime_t delay)
Delay routine.
void osDeleteTask(OsTaskId taskId)
Delete a task.
bool_t osCreateEvent(OsEvent *event)
Create an event object.
OsTaskId osCreateStaticTask(const char_t *name, OsTaskCode taskCode, void *param, OsTaskTcb *tcb, OsStackType *stack, size_t stackSize, int_t priority)
Create a task with statically allocated memory.
OsTaskId osCreateTask(const char_t *name, OsTaskCode taskCode, void *param, size_t stackSize, int_t priority)
Create a task.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void(* OsTaskCode)(void *param)
Task routine.
#define osEnterTask()
#define OS_SELF_TASK_ID
#define OS_INVALID_TASK_ID
uint32_t systime_t
System time.
#define osExitTask()
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:777
error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout)
Wait for one of a set of sockets to become ready to perform I/O.
Definition: socket.c:1591
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:874
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:124
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:147
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:1516
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:100
@ SOCKET_TYPE_STREAM
Definition: socket.h:85
@ SOCKET_EVENT_RX_READY
Definition: socket.h:169
Modbus/TCP server settings.
ModbusServerTlsInitCallback tlsInitCallback
TLS initialization callback function.
ModbusServerLockCallback lockCallback
Lock Modbus table callback function.
ModbusServerWriteCoilCallback writeCoilCallback
Set coil state callback function.
uint8_t unitId
Unit identifier.
ModbusServerOpenCallback openCallback
TCP connection open callback function.
ModbusServerReadCoilCallback readDiscreteInputCallback
Get discrete input state callback function.
ModbusServerUnlockCallback unlockCallback
Unlock Modbus table callback function.
ModbusServerCloseCallback closeCallback
TCP connection close callback function.
uint16_t port
Modbus/TCP port number.
ModbusServerTickCallback tickCallback
Tick callback function.
ModbusServerReadRegCallback readRegCallback
Get register value callback function.
ModbusServerReadRegCallback readHoldingRegCallback
Get holding register value callback function.
ModbusServerReadCoilCallback readCoilCallback
Get coil state callback function.
systime_t timeout
Idle connection timeout.
ModbusServerProcessPduCallback processPduCallback
PDU processing callback function.
ModbusServerReadRegCallback readInputRegCallback
Get input register value callback function.
NetInterface * interface
Underlying network interface.
ModbusServerWriteRegCallback writeRegCallback
Set register value callback function.
Structure describing socket events.
Definition: socket.h:395
uint_t eventMask
Requested events.
Definition: socket.h:397
Socket * socket
Handle to a socket to monitor.
Definition: socket.h:396
error_t tlsInitTicketContext(TlsTicketContext *ticketContext)
Initialize ticket encryption context.
Definition: tls_ticket.c:49
void tlsFreeTicketContext(TlsTicketContext *ticketContext)
Properly dispose ticket encryption context.
Definition: tls_ticket.c:448