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-2024 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.4.0
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  //Default task parameters
52  settings->task = OS_TASK_DEFAULT_PARAMS;
55 
56  //The Modbus/TCP server is not bound to any interface
57  settings->interface = NULL;
58 
59  //Modbus/TCP port number
60  settings->port = MODBUS_TCP_PORT;
61  //Default unit identifier
62  settings->unitId = MODBUS_DEFAULT_UNIT_ID;
63  //Idle connection timeout
64  settings->timeout = MODBUS_SERVER_TIMEOUT;
65 
66  //TCP connection open callback function
67  settings->openCallback = NULL;
68  //TCP connection close callback function
69  settings->closeCallback = NULL;
70 
71 #if (MODBUS_SERVER_TLS_SUPPORT == ENABLED)
72  //TLS initialization callback function
73  settings->tlsInitCallback = NULL;
74 #endif
75 
76  //Lock Modbus table callback function
77  settings->lockCallback = NULL;
78  //Unlock Modbus table callback function
79  settings->unlockCallback = NULL;
80  //Get coil state callback function
81  settings->readCoilCallback = NULL;
82  //Get discrete input state callback function
83  settings->readDiscreteInputCallback = NULL;
84  //Set coil state callback function
85  settings->writeCoilCallback = NULL;
86  //Get register value callback function
87  settings->readRegCallback = NULL;
88  //Get holding register value callback function
89  settings->readHoldingRegCallback = NULL;
90  //Get input register value callback function
91  settings->readInputRegCallback = NULL;
92  //Set register value callback function
93  settings->writeRegCallback = NULL;
94  //PDU processing callback
95  settings->processPduCallback = NULL;
96  //Tick callback function
97  settings->tickCallback = NULL;
98 }
99 
100 
101 /**
102  * @brief Initialize Modbus/TCP server context
103  * @param[in] context Pointer to the Modbus/TCP server context
104  * @param[in] settings Modbus/TCP server specific settings
105  * @return Error code
106  **/
107 
109  const ModbusServerSettings *settings)
110 {
111  error_t error;
112 
113  //Debug message
114  TRACE_INFO("Initializing Modbus/TCP server...\r\n");
115 
116  //Ensure the parameters are valid
117  if(context == NULL || settings == NULL)
119 
120  //Clear Modbus/TCP server context
121  osMemset(context, 0, sizeof(ModbusServerContext));
122 
123  //Initialize task parameters
124  context->taskParams = settings->task;
125  context->taskId = OS_INVALID_TASK_ID;
126 
127  //Save user settings
128  context->settings = *settings;
129 
130  //Initialize status code
131  error = NO_ERROR;
132 
133  //Create an event object to poll the state of sockets
134  if(!osCreateEvent(&context->event))
135  {
136  //Failed to create event
137  error = ERROR_OUT_OF_RESOURCES;
138  }
139 
140 #if (MODBUS_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
141  //Check status code
142  if(!error)
143  {
144  //Initialize ticket encryption context
145  error = tlsInitTicketContext(&context->tlsTicketContext);
146  }
147 #endif
148 
149  //Any error to report?
150  if(error)
151  {
152  //Clean up side effects
153  modbusServerDeinit(context);
154  }
155 
156  //Return status code
157  return error;
158 }
159 
160 
161 /**
162  * @brief Start Modbus/TCP server
163  * @param[in] context Pointer to the Modbus/TCP server context
164  * @return Error code
165  **/
166 
168 {
169  error_t error;
170 
171  //Make sure the Modbus/TCP server context is valid
172  if(context == NULL)
174 
175  //Debug message
176  TRACE_INFO("Starting Modbus/TCP server...\r\n");
177 
178  //Make sure the Modbus/TCP server is not already running
179  if(context->running)
180  return ERROR_ALREADY_RUNNING;
181 
182  //Start of exception handling block
183  do
184  {
185  //Open a TCP socket
187  //Failed to open socket?
188  if(context->socket == NULL)
189  {
190  //Report an error
191  error = ERROR_OPEN_FAILED;
192  break;
193  }
194 
195  //Force the socket to operate in non-blocking mode
196  error = socketSetTimeout(context->socket, 0);
197  //Any error to report?
198  if(error)
199  break;
200 
201  //Associate the socket with the relevant interface
202  error = socketBindToInterface(context->socket,
203  context->settings.interface);
204  //Any error to report?
205  if(error)
206  break;
207 
208  //The Modbus/TCP server listens for connection requests on port 502
209  error = socketBind(context->socket, &IP_ADDR_ANY, context->settings.port);
210  //Any error to report?
211  if(error)
212  break;
213 
214  //Place socket in listening state
215  error = socketListen(context->socket, 0);
216  //Any error to report?
217  if(error)
218  break;
219 
220  //Start the Modbus/TCP server
221  context->stop = FALSE;
222  context->running = TRUE;
223 
224  //Create a task
225  context->taskId = osCreateTask("Modbus/TCP Server",
226  (OsTaskCode) modbusServerTask, context, &context->taskParams);
227 
228  //Failed to create task?
229  if(context->taskId == OS_INVALID_TASK_ID)
230  {
231  //Report an error
232  error = ERROR_OUT_OF_RESOURCES;
233  break;
234  }
235 
236  //End of exception handling block
237  } while(0);
238 
239  //Any error to report?
240  if(error)
241  {
242  //Clean up side effects
243  context->running = FALSE;
244 
245  //Close listening socket
246  socketClose(context->socket);
247  context->socket = NULL;
248  }
249 
250  //Return status code
251  return error;
252 }
253 
254 
255 /**
256  * @brief Stop Modbus/TCP server
257  * @param[in] context Pointer to the Modbus/TCP server context
258  * @return Error code
259  **/
260 
262 {
263  uint_t i;
264 
265  //Make sure the Modbus/TCP server context is valid
266  if(context == NULL)
268 
269  //Debug message
270  TRACE_INFO("Stopping Modbus/TCP server...\r\n");
271 
272  //Check whether the Modbus/TCP server is running
273  if(context->running)
274  {
275  //Stop the Modbus/TCP server
276  context->stop = TRUE;
277  //Send a signal to the task to abort any blocking operation
278  osSetEvent(&context->event);
279 
280  //Wait for the task to terminate
281  while(context->running)
282  {
283  osDelayTask(1);
284  }
285 
286  //Loop through the connection table
287  for(i = 0; i < MODBUS_SERVER_MAX_CONNECTIONS; i++)
288  {
289  //Check the state of the current connection
290  if(context->connection[i].state != MODBUS_CONNECTION_STATE_CLOSED)
291  {
292  //Close client connection
293  modbusServerCloseConnection(&context->connection[i]);
294  }
295  }
296 
297  //Close listening socket
298  socketClose(context->socket);
299  context->socket = NULL;
300  }
301 
302  //Successful processing
303  return NO_ERROR;
304 }
305 
306 
307 /**
308  * @brief Modbus/TCP server task
309  * @param[in] context Pointer to the Modbus/TCP server context
310  **/
311 
313 {
314  error_t error;
315  uint_t i;
316  systime_t timeout;
317  ModbusClientConnection *connection;
319 
320 #if (NET_RTOS_SUPPORT == ENABLED)
321  //Task prologue
322  osEnterTask();
323 
324  //Process events
325  while(1)
326  {
327 #endif
328  //Set polling timeout
329  timeout = MODBUS_SERVER_TICK_INTERVAL;
330 
331  //Clear event descriptor set
332  osMemset(eventDesc, 0, sizeof(eventDesc));
333 
334  //Specify the events the application is interested in
335  for(i = 0; i < MODBUS_SERVER_MAX_CONNECTIONS; i++)
336  {
337  //Point to the structure describing the current connection
338  connection = &context->connection[i];
339 
340  //Loop through active connections only
341  if(connection->state != MODBUS_CONNECTION_STATE_CLOSED)
342  {
343  //Register connection events
344  modbusServerRegisterConnectionEvents(connection, &eventDesc[i]);
345 
346  //Check whether the socket is ready for I/O operation
347  if(eventDesc[i].eventFlags != 0)
348  {
349  //No need to poll the underlying socket for incoming traffic
350  timeout = 0;
351  }
352  }
353  }
354 
355  //The Modbus/TCP server listens for connection requests on port 502
356  eventDesc[i].socket = context->socket;
357  eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
358 
359  //Wait for one of the set of sockets to become ready to perform I/O
360  error = socketPoll(eventDesc, MODBUS_SERVER_MAX_CONNECTIONS + 1,
361  &context->event, timeout);
362 
363  //Check status code
364  if(error == NO_ERROR || error == ERROR_TIMEOUT ||
365  error == ERROR_WAIT_CANCELED)
366  {
367  //Stop request?
368  if(context->stop)
369  {
370  //Stop Modbus/TCP server operation
371  context->running = FALSE;
372  //Task epilogue
373  osExitTask();
374  //Kill ourselves
376  }
377 
378  //Event-driven processing
379  for(i = 0; i < MODBUS_SERVER_MAX_CONNECTIONS; i++)
380  {
381  //Point to the structure describing the current connection
382  connection = &context->connection[i];
383 
384  //Loop through active connections only
385  if(connection->state != MODBUS_CONNECTION_STATE_CLOSED)
386  {
387  //Check whether the socket is ready to perform I/O
388  if(eventDesc[i].eventFlags != 0)
389  {
390  //Connection event handler
392  }
393  }
394  }
395 
396  //Any connection request received on port 502?
397  if(eventDesc[i].eventFlags != 0)
398  {
399  //Accept connection request
401  }
402  }
403 
404  //Handle periodic operations
405  modbusServerTick(context);
406 
407 #if (NET_RTOS_SUPPORT == ENABLED)
408  }
409 #endif
410 }
411 
412 
413 /**
414  * @brief Release Modbus/TCP server context
415  * @param[in] context Pointer to the Modbus/TCP server context
416  **/
417 
419 {
420  //Make sure the Modbus/TCP server context is valid
421  if(context != NULL)
422  {
423  //Free previously allocated resources
424  osDeleteEvent(&context->event);
425 
426 #if (MODBUS_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
427  //Release ticket encryption context
428  tlsFreeTicketContext(&context->tlsTicketContext);
429 #endif
430 
431  //Clear Modbus/TCP server context
432  osMemset(context, 0, sizeof(ModbusServerContext));
433  }
434 }
435 
436 #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:87
#define MODBUS_SERVER_STACK_SIZE
Definition: modbus_server.h:61
#define ModbusServerContext
#define MODBUS_SERVER_TIMEOUT
Definition: modbus_server.h:80
@ MODBUS_CONNECTION_STATE_CLOSED
#define MODBUS_SERVER_PRIORITY
Definition: modbus_server.h:68
#define ModbusClientConnection
#define MODBUS_SERVER_MAX_CONNECTIONS
Definition: modbus_server.h:73
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:135
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
void osDeleteEvent(OsEvent *event)
Delete an event object.
const OsTaskParameters OS_TASK_DEFAULT_PARAMS
void osDelayTask(systime_t delay)
Delay routine.
OsTaskId osCreateTask(const char_t *name, OsTaskCode taskCode, void *arg, const OsTaskParameters *params)
Create a task.
void osDeleteTask(OsTaskId taskId)
Delete a task.
bool_t osCreateEvent(OsEvent *event)
Create an event object.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void(* OsTaskCode)(void *arg)
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:778
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:1592
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:875
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:125
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:148
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:1517
@ 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.
OsTaskParameters task
Task parameters.
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:398
uint_t eventMask
Requested events.
Definition: socket.h:400
Socket * socket
Handle to a socket to monitor.
Definition: socket.h:399
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