1 /*
2  * SPDX-FileCopyrightText: 2013 Armink
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
7  */
8 /*
9  * FreeModbus Libary: ESP32 Port
10  * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in the
19  *   documentation and/or other materials provided with the distribution.
20  * 3. The name of the author may not be used to endorse or promote products
21  *   derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * File: $Id: portevent.c v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
35  */
36 
37 /* ----------------------- Modbus includes ----------------------------------*/
38 #include "mb_m.h"
39 #include "mbport.h"
40 #include "mbconfig.h"
41 
42 #include "freertos/FreeRTOS.h"
43 #include "freertos/task.h"
44 #include "freertos/event_groups.h"
45 #include "port.h"
46 #include "mbport.h"
47 #include "freertos/semphr.h"
48 #include "port_serial_master.h"
49 
50 #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
51 /* ----------------------- Defines ------------------------------------------*/
52 // Event bit mask for xMBMasterPortEventGet()
53 #define MB_EVENT_POLL_MASK  (EventBits_t)( EV_MASTER_READY | \
54                                             EV_MASTER_FRAME_RECEIVED | \
55                                             EV_MASTER_EXECUTE | \
56                                             EV_MASTER_FRAME_SENT | \
57                                             EV_MASTER_FRAME_TRANSMIT | \
58                                             EV_MASTER_ERROR_PROCESS )
59 
60 // Event bit mask for eMBMasterWaitRequestFinish()
61 #define MB_EVENT_REQ_MASK   (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
62                                             EV_MASTER_ERROR_RESPOND_TIMEOUT | \
63                                             EV_MASTER_ERROR_RECEIVE_DATA | \
64                                             EV_MASTER_ERROR_EXECUTE_FUNCTION )
65 
66 #define MB_EVENT_RESOURCE   (EventBits_t)( 0x0080 )
67 
68 /* ----------------------- Variables ----------------------------------------*/
69 static EventGroupHandle_t xResourceMasterHdl;
70 static EventGroupHandle_t xEventGroupMasterHdl;
71 static EventGroupHandle_t xEventGroupMasterConfirmHdl;
72 
73 /* ----------------------- Start implementation -----------------------------*/
74 
75 BOOL
xMBMasterPortEventInit(void)76 xMBMasterPortEventInit( void )
77 {
78     xEventGroupMasterHdl = xEventGroupCreate();
79     xEventGroupMasterConfirmHdl = xEventGroupCreate();
80     MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
81                     FALSE, "mb stack event group creation error.");
82     return TRUE;
83 }
84 
85 BOOL MB_PORT_ISR_ATTR
xMBMasterPortEventPost(eMBMasterEventType eEvent)86 xMBMasterPortEventPost( eMBMasterEventType eEvent )
87 {
88     BOOL bStatus = FALSE;
89     eMBMasterEventType eTempEvent = eEvent;
90 
91     if( (BOOL)xPortInIsrContext() == TRUE )
92     {
93         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
94         BaseType_t xResult = xEventGroupSetBitsFromISR( xEventGroupMasterHdl,
95                                                         (EventBits_t) eTempEvent,
96                                                         &xHigherPriorityTaskWoken );
97         // Was the message posted successfully?
98         if( xResult == pdPASS ) {
99             // If xHigherPriorityTaskWoken is now set to pdTRUE
100             // then a context switch should be requested.
101             if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR();
102             bStatus = TRUE;
103         } else {
104             bStatus = FALSE;
105         }
106     }
107     else
108     {
109         // Set event bits if the function is called from task
110         // The return result is not checked here because
111         // It might be that event bit was cleared automatically as a
112         // task that was waiting for the bit was removed from the Blocked state.
113         (void) xEventGroupSetBits( xEventGroupMasterHdl, (EventBits_t)eTempEvent );
114         bStatus = TRUE;
115     }
116     return bStatus;
117 }
118 
119 eMBMasterEventType
xMBMasterPortFsmWaitConfirmation(eMBMasterEventType eEventMask,ULONG ulTimeout)120 xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout)
121 {
122     EventBits_t uxBits;
123     uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl,  // The event group being tested.
124                                     eEventMask,                 // The bits within the event group to wait for.
125                                     pdFALSE,                    // Keep masked bits.
126                                     pdFALSE,                    // Don't wait for both bits, either bit will do.
127                                     ulTimeout);                 // Wait timeout for either bit to be set.
128     if (ulTimeout && uxBits) {
129         // Clear confirmation events that where set in the mask
130         xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
131     }
132     return (eMBMasterEventType)(uxBits & eEventMask);
133 }
134 
135 BOOL
xMBMasterPortEventGet(eMBMasterEventType * eEvent)136 xMBMasterPortEventGet( eMBMasterEventType* eEvent )
137 {
138     EventBits_t uxBits;
139     BOOL    xEventHappened = FALSE;
140     uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
141                                     MB_EVENT_POLL_MASK, // The bits within the event group to wait for.
142                                     pdTRUE,             // Masked bits should be cleared before returning.
143                                     pdFALSE,            // Don't wait for both bits, either bit will do.
144                                     portMAX_DELAY);     // Wait forever for either bit to be set.
145     // Check if poll event is correct
146     if (MB_PORT_CHECK_EVENT(uxBits, MB_EVENT_POLL_MASK)) {
147         *eEvent = (eMBMasterEventType)(uxBits & MB_EVENT_POLL_MASK);
148         // Set event bits in confirmation group (for synchronization with port task)
149         xEventGroupSetBits( xEventGroupMasterConfirmHdl, *eEvent );
150         xEventHappened = TRUE;
151     } else {
152         ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event triggered = %d.", __func__, uxBits);
153         *eEvent = (eMBMasterEventType)uxBits;
154         xEventHappened = FALSE;
155     }
156     return xEventHappened;
157 }
158 
159 // This function is initialize the OS resource for modbus master.
vMBMasterOsResInit(void)160 void vMBMasterOsResInit( void )
161 {
162     xResourceMasterHdl = xEventGroupCreate();
163     xEventGroupSetBits(xResourceMasterHdl, MB_EVENT_RESOURCE);
164     MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "Resource create error.");
165 }
166 
167 /**
168  * This function is take Mobus Master running resource.
169  * Note:The resource is define by Operating System.
170  *
171  * @param lTimeOut the waiting time.
172  *
173  * @return resource take result
174  */
xMBMasterRunResTake(LONG lTimeOut)175 BOOL xMBMasterRunResTake( LONG lTimeOut )
176 {
177     EventBits_t uxBits;
178     uxBits = xEventGroupWaitBits( xResourceMasterHdl,   // The event group being tested.
179                                     MB_EVENT_RESOURCE,  // The bits within the event group to wait for.
180                                     pdTRUE,             // Masked bits should be cleared before returning.
181                                     pdFALSE,            // Don't wait for both bits, either bit will do.
182                                     lTimeOut);          // Resource wait timeout.
183     MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), FALSE , "Take resource failure.");
184     ESP_LOGD(MB_PORT_TAG,"%s:Take resource (%x) (%lu ticks).", __func__, uxBits,  lTimeOut);
185     return TRUE;
186 }
187 
188 /**
189  * This function is release Modbus Master running resource.
190  * Note:The resource is define by Operating System.If you not use OS this function can be empty.
191  */
vMBMasterRunResRelease(void)192 void vMBMasterRunResRelease( void )
193 {
194     EventBits_t uxBits = xEventGroupSetBits( xResourceMasterHdl, MB_EVENT_RESOURCE );
195     MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), ; , "Resource release failure.");
196     ESP_LOGD(MB_PORT_TAG,"%s: Release resource (%x).", __func__, uxBits);
197 }
198 
199 /**
200  * This is modbus master respond timeout error process callback function.
201  * @note There functions will block modbus master poll while execute OS waiting.
202  *
203  * @param ucDestAddress destination salve address
204  * @param pucPDUData PDU buffer data
205  * @param ucPDULength PDU buffer length
206  *
207  */
vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress,const UCHAR * pucPDUData,USHORT ucPDULength)208 void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
209 {
210     BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RESPOND_TIMEOUT);
211     MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RESPOND_TIMEOUT' failed!", __func__);
212     ESP_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
213 }
214 
215 /**
216  * This is modbus master receive data error process callback function.
217  * @note There functions will block modbus master poll while execute OS waiting.
218  *
219  * @param ucDestAddress destination salve address
220  * @param pucPDUData PDU buffer data
221  * @param ucPDULength PDU buffer length
222  */
vMBMasterErrorCBReceiveData(UCHAR ucDestAddress,const UCHAR * pucPDUData,USHORT ucPDULength)223 void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
224 {
225     BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RECEIVE_DATA);
226     MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RECEIVE_DATA' failed!", __func__);
227     ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data timeout failure.", __func__);
228     ESP_LOG_BUFFER_HEX_LEVEL("Err rcv buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
229 }
230 
231 /**
232  * This is modbus master execute function error process callback function.
233  * @note There functions will block modbus master poll while execute OS waiting.
234  * So,for real-time of system.Do not execute too much waiting process.
235  *
236  * @param ucDestAddress destination salve address
237  * @param pucPDUData PDU buffer data
238  * @param ucPDULength PDU buffer length
239  *
240  */
vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress,const UCHAR * pucPDUData,USHORT ucPDULength)241 void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
242 {
243     BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_EXECUTE_FUNCTION);
244     MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_EXECUTE_FUNCTION' failed!", __func__);
245     ESP_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
246     ESP_LOG_BUFFER_HEX_LEVEL("Exec func buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
247 }
248 
249 /**
250  * This is modbus master request process success callback function.
251  * @note There functions will block modbus master poll while execute OS waiting.
252  * So,for real-time of system. Do not execute too much waiting process.
253  */
vMBMasterCBRequestSuccess(void)254 void vMBMasterCBRequestSuccess( void ) {
255      /**
256      * @note This code is use OS's event mechanism for modbus master protocol stack.
257      * If you don't use OS, you can change it.
258      */
259     BOOL ret = xMBMasterPortEventPost(EV_MASTER_PROCESS_SUCCESS);
260     MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_PROCESS_SUCCESS' failed!", __func__);
261     ESP_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
262 }
263 
264 /**
265  * This function is wait for modbus master request finish and return result.
266  * Waiting result include request process success, request respond timeout,
267  * receive data error and execute function error.You can use the above callback function.
268  * @note If you are use OS, you can use OS's event mechanism. Otherwise you have to run
269  * much user custom delay for waiting.
270  *
271  * @return request error code
272  */
eMBMasterWaitRequestFinish(void)273 eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
274     eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
275     eMBMasterEventType xRecvedEvent;
276 
277     EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
278                                                 MB_EVENT_REQ_MASK,  // The bits within the event group to wait for.
279                                                 pdTRUE,             // Masked bits should be cleared before returning.
280                                                 pdFALSE,            // Don't wait for both bits, either bit will do.
281                                                 portMAX_DELAY );    // Wait forever for either bit to be set.
282     xRecvedEvent = (eMBMasterEventType)(uxBits);
283     if (xRecvedEvent) {
284         ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, xRecvedEvent);
285         if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
286             // if we wait for certain event bits but get from poll subset
287             ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, xRecvedEvent);
288         }
289         xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
290         if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
291             eErrStatus = MB_MRE_NO_ERR;
292         } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT)) {
293             eErrStatus = MB_MRE_TIMEDOUT;
294         } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RECEIVE_DATA)) {
295             eErrStatus = MB_MRE_REV_DATA;
296         } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_EXECUTE_FUNCTION)) {
297             eErrStatus = MB_MRE_EXE_FUN;
298         }
299     } else {
300         ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, uxBits);
301         // https://github.com/espressif/esp-idf/issues/5275
302         // if a no event is received, that means vMBMasterPortEventClose()
303         // has been closed, so event group has been deleted by FreeRTOS, which
304         // triggers the send of 0 value to the event group to unlock this task
305         // waiting on it. For this patch, handles it as a time out without assert.
306         eErrStatus = MB_MRE_TIMEDOUT;
307     }
308     return eErrStatus;
309 }
310 
vMBMasterPortEventClose(void)311 void vMBMasterPortEventClose(void)
312 {
313     vEventGroupDelete(xEventGroupMasterHdl);
314     vEventGroupDelete(xEventGroupMasterConfirmHdl);
315     vEventGroupDelete(xResourceMasterHdl);
316 }
317 
318 #endif
319