Issue 2 move folders and use deep path include file names to prevent collisions (#4)
* moving folders and files and adjust server demo build * Fix Makefile for apps/server on Linux * fix unit test source file folders * fix datetime convert UTC functions. Add Code::Blocks project for datetime testing * added some ignore extensions * disable parallel make option * fix build for abort, dcc, and epics apps * fix build for dcc, epics, error, and getevent apps. * Fixed building of all apps * fix the ipv4 to ipv6 router app build * Change indent style from Google to Webkit * make pretty to re-format style * removed common Makefile since we already had one and two was too many * remove scripts from root folder that are no longer maintained or used * remove mercurial EOL and ignore files for git repo * remove .vscodeconfig files from repo * tweak clang-format style * clang-format src and apps with tweaked style * added clang-tidy to fix readability if braces in src * result of make tidy for src and apps * fix clang-tidy mangling * Added code::blocks project for BACnet server simulation * added code::blocks linux project for WhoIs app * update text files for EOL * fix EOL in some files * fixed make win32 apps for older gcc * Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler. * created codeblocks project for apps/epics for Windows * fixing ports/xplained to work with new data structure. * fix ports/xplained example for Atmel Studio compile * fix ports/stm32f10x example for gcc Makefile compile * fix ports/stm32f10x example for IAR EWARM compile * fix ports/xplained timer callback * fix ports/bdk_atxx_mspt build with subdirs * fix ports/bdk_atxx_mspt build with subdirs * updated git ignore for IAR build artifacts * updated gitignore for non-tracked files and folders * fixed bdk-atxx4-mstp port for Rowley Crossworks project file * fixed bdk-atxx4-mstp port for GCC AVR Makefile * fixed atmega168 port for IAR AVR and GCC AVR Makefile * fixed at91sam7s port for IAR ARM and GCC ARM Makefile * removed unmaintainable DOS, RTOS32, and atmega8 ports. Updated rx62n (untested). * changed arm7 to uip port
This commit is contained in:
@@ -0,0 +1,400 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
Corrections by Ferran Arumi, 2007, Barcelona, Spain
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/bacaddr.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
|
||||
/** @file tsm.c BACnet Transaction State Machine operations */
|
||||
/* FIXME: modify basic service handlers to use TSM rather than this buffer! */
|
||||
uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 };
|
||||
|
||||
#if (MAX_TSM_TRANSACTIONS)
|
||||
/* Really only needed for segmented messages */
|
||||
/* and a little for sending confirmed messages */
|
||||
/* If we are only a server and only initiate broadcasts, */
|
||||
/* then we don't need a TSM layer. */
|
||||
|
||||
/* FIXME: not coded for segmentation */
|
||||
|
||||
/* declare space for the TSM transactions, and set it up in the init. */
|
||||
/* table rules: an Invoke ID = 0 is an unused spot in the table */
|
||||
static BACNET_TSM_DATA TSM_List[MAX_TSM_TRANSACTIONS];
|
||||
|
||||
/* invoke ID for incrementing between subsequent calls. */
|
||||
static uint8_t Current_Invoke_ID = 1;
|
||||
|
||||
static tsm_timeout_function Timeout_Function;
|
||||
|
||||
void tsm_set_timeout_handler(tsm_timeout_function pFunction)
|
||||
{
|
||||
Timeout_Function = pFunction;
|
||||
}
|
||||
|
||||
/* returns MAX_TSM_TRANSACTIONS if not found */
|
||||
static uint8_t tsm_find_invokeID_index(uint8_t invokeID)
|
||||
{
|
||||
unsigned i = 0; /* counter */
|
||||
uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */
|
||||
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
|
||||
if (TSM_List[i].InvokeID == invokeID) {
|
||||
index = (uint8_t)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static uint8_t tsm_find_first_free_index(void)
|
||||
{
|
||||
unsigned i = 0; /* counter */
|
||||
uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */
|
||||
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
|
||||
if (TSM_List[i].InvokeID == 0) {
|
||||
index = (uint8_t)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
bool tsm_transaction_available(void)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned i = 0; /* counter */
|
||||
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
|
||||
if (TSM_List[i].InvokeID == 0) {
|
||||
/* one is available! */
|
||||
status = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t tsm_transaction_idle_count(void)
|
||||
{
|
||||
uint8_t count = 0; /* return value */
|
||||
unsigned i = 0; /* counter */
|
||||
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
|
||||
if ((TSM_List[i].InvokeID == 0) &&
|
||||
(TSM_List[i].state == TSM_STATE_IDLE)) {
|
||||
/* one is available! */
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* sets the invokeID */
|
||||
|
||||
void tsm_invokeID_set(uint8_t invokeID)
|
||||
{
|
||||
if (invokeID == 0) {
|
||||
invokeID = 1;
|
||||
}
|
||||
Current_Invoke_ID = invokeID;
|
||||
}
|
||||
|
||||
/* gets the next free invokeID,
|
||||
and reserves a spot in the table
|
||||
returns 0 if none are available */
|
||||
uint8_t tsm_next_free_invokeID(void)
|
||||
{
|
||||
uint8_t index = 0;
|
||||
uint8_t invokeID = 0;
|
||||
bool found = false;
|
||||
|
||||
/* is there even space available? */
|
||||
if (tsm_transaction_available()) {
|
||||
while (!found) {
|
||||
index = tsm_find_invokeID_index(Current_Invoke_ID);
|
||||
if (index == MAX_TSM_TRANSACTIONS) {
|
||||
/* Not found, so this invokeID is not used */
|
||||
found = true;
|
||||
/* set this id into the table */
|
||||
index = tsm_find_first_free_index();
|
||||
if (index != MAX_TSM_TRANSACTIONS) {
|
||||
TSM_List[index].InvokeID = invokeID = Current_Invoke_ID;
|
||||
TSM_List[index].state = TSM_STATE_IDLE;
|
||||
TSM_List[index].RequestTimer = apdu_timeout();
|
||||
/* update for the next call or check */
|
||||
Current_Invoke_ID++;
|
||||
/* skip zero - we treat that internally as invalid or no
|
||||
* free */
|
||||
if (Current_Invoke_ID == 0) {
|
||||
Current_Invoke_ID = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* found! This invokeID is already used */
|
||||
/* try next one */
|
||||
Current_Invoke_ID++;
|
||||
/* skip zero - we treat that internally as invalid or no free */
|
||||
if (Current_Invoke_ID == 0) {
|
||||
Current_Invoke_ID = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return invokeID;
|
||||
}
|
||||
|
||||
void tsm_set_confirmed_unsegmented_transaction(uint8_t invokeID,
|
||||
BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *ndpu_data,
|
||||
uint8_t *apdu,
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
uint16_t j = 0;
|
||||
uint8_t index;
|
||||
|
||||
if (invokeID) {
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
if (index < MAX_TSM_TRANSACTIONS) {
|
||||
/* SendConfirmedUnsegmented */
|
||||
TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION;
|
||||
TSM_List[index].RetryCount = 0;
|
||||
/* start the timer */
|
||||
TSM_List[index].RequestTimer = apdu_timeout();
|
||||
/* copy the data */
|
||||
for (j = 0; j < apdu_len; j++) {
|
||||
TSM_List[index].apdu[j] = apdu[j];
|
||||
}
|
||||
TSM_List[index].apdu_len = apdu_len;
|
||||
npdu_copy_data(&TSM_List[index].npdu_data, ndpu_data);
|
||||
bacnet_address_copy(&TSM_List[index].dest, dest);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* used to retrieve the transaction payload */
|
||||
/* if we wanted to find out what we sent (i.e. when we get an ack) */
|
||||
bool tsm_get_transaction_pdu(uint8_t invokeID,
|
||||
BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *ndpu_data,
|
||||
uint8_t *apdu,
|
||||
uint16_t *apdu_len)
|
||||
{
|
||||
uint16_t j = 0;
|
||||
uint8_t index;
|
||||
bool found = false;
|
||||
|
||||
if (invokeID) {
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
/* how much checking is needed? state? dest match? just invokeID? */
|
||||
if (index < MAX_TSM_TRANSACTIONS) {
|
||||
/* FIXME: we may want to free the transaction so it doesn't timeout
|
||||
*/
|
||||
/* retrieve the transaction */
|
||||
/* FIXME: bounds check the pdu_len? */
|
||||
*apdu_len = (uint16_t)TSM_List[index].apdu_len;
|
||||
for (j = 0; j < *apdu_len; j++) {
|
||||
apdu[j] = TSM_List[index].apdu[j];
|
||||
}
|
||||
npdu_copy_data(ndpu_data, &TSM_List[index].npdu_data);
|
||||
bacnet_address_copy(dest, &TSM_List[index].dest);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/* called once a millisecond or slower */
|
||||
void tsm_timer_milliseconds(uint16_t milliseconds)
|
||||
{
|
||||
unsigned i = 0; /* counter */
|
||||
|
||||
for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) {
|
||||
if (TSM_List[i].state == TSM_STATE_AWAIT_CONFIRMATION) {
|
||||
if (TSM_List[i].RequestTimer > milliseconds) {
|
||||
TSM_List[i].RequestTimer -= milliseconds;
|
||||
} else {
|
||||
TSM_List[i].RequestTimer = 0;
|
||||
}
|
||||
/* AWAIT_CONFIRMATION */
|
||||
if (TSM_List[i].RequestTimer == 0) {
|
||||
if (TSM_List[i].RetryCount < apdu_retries()) {
|
||||
TSM_List[i].RequestTimer = apdu_timeout();
|
||||
TSM_List[i].RetryCount++;
|
||||
datalink_send_pdu(&TSM_List[i].dest, &TSM_List[i].npdu_data,
|
||||
&TSM_List[i].apdu[0], TSM_List[i].apdu_len);
|
||||
} else {
|
||||
/* note: the invoke id has not been cleared yet
|
||||
and this indicates a failed message:
|
||||
IDLE and a valid invoke id */
|
||||
TSM_List[i].state = TSM_STATE_IDLE;
|
||||
if (TSM_List[i].InvokeID != 0) {
|
||||
if (Timeout_Function) {
|
||||
Timeout_Function(TSM_List[i].InvokeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* frees the invokeID and sets its state to IDLE */
|
||||
void tsm_free_invoke_id(uint8_t invokeID)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
if (index < MAX_TSM_TRANSACTIONS) {
|
||||
TSM_List[index].state = TSM_STATE_IDLE;
|
||||
TSM_List[index].InvokeID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if the invoke ID has been made free by the Transaction State Machine.
|
||||
* @param invokeID [in] The invokeID to be checked, normally of last message
|
||||
* sent.
|
||||
* @return True if it is free (done with), False if still pending in the TSM.
|
||||
*/
|
||||
bool tsm_invoke_id_free(uint8_t invokeID)
|
||||
{
|
||||
bool status = true;
|
||||
uint8_t index;
|
||||
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
if (index < MAX_TSM_TRANSACTIONS) {
|
||||
status = false;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/** See if we failed get a confirmation for the message associated
|
||||
* with this invoke ID.
|
||||
* @param invokeID [in] The invokeID to be checked, normally of last message
|
||||
* sent.
|
||||
* @return True if already failed, False if done or segmented or still waiting
|
||||
* for a confirmation.
|
||||
*/
|
||||
bool tsm_invoke_id_failed(uint8_t invokeID)
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t index;
|
||||
|
||||
index = tsm_find_invokeID_index(invokeID);
|
||||
if (index < MAX_TSM_TRANSACTIONS) {
|
||||
/* a valid invoke ID and the state is IDLE is a
|
||||
message that failed to confirm */
|
||||
if (TSM_List[index].state == TSM_STATE_IDLE) {
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
/* flag to send an I-Am */
|
||||
bool I_Am_Request = true;
|
||||
|
||||
/* dummy function stubs */
|
||||
int datalink_send_pdu(BACNET_ADDRESS *dest,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *pdu,
|
||||
unsigned pdu_len)
|
||||
{
|
||||
(void)dest;
|
||||
(void)npdu_data;
|
||||
(void)pdu;
|
||||
(void)pdu_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dummy function stubs */
|
||||
void datalink_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||
{
|
||||
(void)dest;
|
||||
}
|
||||
|
||||
void testTSM(Test *pTest)
|
||||
{
|
||||
/* FIXME: add some unit testing... */
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_TSM
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet TSM", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testTSM);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_TSM */
|
||||
#endif /* TEST */
|
||||
#endif /* MAX_TSM_TRANSACTIONS */
|
||||
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef TSM_H
|
||||
#define TSM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
/* note: TSM functionality is optional - only needed if we are
|
||||
doing client requests */
|
||||
|
||||
/* FIXME: modify basic service handlers to use TSM rather than this buffer! */
|
||||
extern uint8_t Handler_Transmit_Buffer[MAX_PDU];
|
||||
|
||||
#if (!MAX_TSM_TRANSACTIONS)
|
||||
#define tsm_free_invoke_id(x) (void)x;
|
||||
#else
|
||||
typedef enum {
|
||||
TSM_STATE_IDLE,
|
||||
TSM_STATE_AWAIT_CONFIRMATION,
|
||||
TSM_STATE_AWAIT_RESPONSE,
|
||||
TSM_STATE_SEGMENTED_REQUEST,
|
||||
TSM_STATE_SEGMENTED_CONFIRMATION
|
||||
} BACNET_TSM_STATE;
|
||||
|
||||
/* 5.4.1 Variables And Parameters */
|
||||
/* The following variables are defined for each instance of */
|
||||
/* Transaction State Machine: */
|
||||
typedef struct BACnet_TSM_Data {
|
||||
/* used to count APDU retries */
|
||||
uint8_t RetryCount;
|
||||
/* used to count segment retries */
|
||||
/*uint8_t SegmentRetryCount; */
|
||||
/* used to control APDU retries and the acceptance of server replies */
|
||||
/*bool SentAllSegments; */
|
||||
/* stores the sequence number of the last segment received in order */
|
||||
/*uint8_t LastSequenceNumber; */
|
||||
/* stores the sequence number of the first segment of */
|
||||
/* a sequence of segments that fill a window */
|
||||
/*uint8_t InitialSequenceNumber; */
|
||||
/* stores the current window size */
|
||||
/*uint8_t ActualWindowSize; */
|
||||
/* stores the window size proposed by the segment sender */
|
||||
/*uint8_t ProposedWindowSize; */
|
||||
/* used to perform timeout on PDU segments */
|
||||
/*uint8_t SegmentTimer; */
|
||||
/* used to perform timeout on Confirmed Requests */
|
||||
/* in milliseconds */
|
||||
uint16_t RequestTimer;
|
||||
/* unique id */
|
||||
uint8_t InvokeID;
|
||||
/* state that the TSM is in */
|
||||
BACNET_TSM_STATE state;
|
||||
/* the address we sent it to */
|
||||
BACNET_ADDRESS dest;
|
||||
/* the network layer info */
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
/* copy of the APDU, should we need to send it again */
|
||||
uint8_t apdu[MAX_PDU];
|
||||
unsigned apdu_len;
|
||||
} BACNET_TSM_DATA;
|
||||
|
||||
typedef void (
|
||||
*tsm_timeout_function) (
|
||||
uint8_t invoke_id);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void tsm_set_timeout_handler(
|
||||
tsm_timeout_function pFunction);
|
||||
|
||||
bool tsm_transaction_available(
|
||||
void);
|
||||
uint8_t tsm_transaction_idle_count(
|
||||
void);
|
||||
void tsm_timer_milliseconds(
|
||||
uint16_t milliseconds);
|
||||
/* free the invoke ID when the reply comes back */
|
||||
void tsm_free_invoke_id(
|
||||
uint8_t invokeID);
|
||||
/* use these in tandem */
|
||||
uint8_t tsm_next_free_invokeID(
|
||||
void);
|
||||
void tsm_invokeID_set(
|
||||
uint8_t invokeID);
|
||||
/* returns the same invoke ID that was given */
|
||||
void tsm_set_confirmed_unsegmented_transaction(
|
||||
uint8_t invokeID,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * ndpu_data,
|
||||
uint8_t * apdu,
|
||||
uint16_t apdu_len);
|
||||
/* returns true if transaction is found */
|
||||
bool tsm_get_transaction_pdu(
|
||||
uint8_t invokeID,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * ndpu_data,
|
||||
uint8_t * apdu,
|
||||
uint16_t * apdu_len);
|
||||
|
||||
bool tsm_invoke_id_free(
|
||||
uint8_t invokeID);
|
||||
bool tsm_invoke_id_failed(
|
||||
uint8_t invokeID);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
/* define out any functions necessary for compile */
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user