mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 06:20:21 +00:00
Furi: count ISR time. Cli: show ISR time in top. (#3751)
* Furi: count ISR time. Cli: show ISR time in top. * hal: interrupt: macros for interrupt accounting; split FURI_ALWAYS_STATIC_INLINE -> FURI_ALWAYS_INLINE static Co-authored-by: hedger <hedger@nanode.su> Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
parent
7879876ba1
commit
cf0cc4fa8d
9 changed files with 81 additions and 19 deletions
|
@ -399,8 +399,9 @@ static void cli_command_top(Cli* cli, FuriString* args, void* context) {
|
||||||
|
|
||||||
uint32_t uptime = tick / furi_kernel_get_tick_frequency();
|
uint32_t uptime = tick / furi_kernel_get_tick_frequency();
|
||||||
printf(
|
printf(
|
||||||
"Threads: %zu, Uptime: %luh%lum%lus\r\n",
|
"Threads: %zu, ISR Time: %0.2f%%, Uptime: %luh%lum%lus\r\n",
|
||||||
furi_thread_list_size(thread_list),
|
furi_thread_list_size(thread_list),
|
||||||
|
(double)furi_thread_list_get_isr_time(thread_list),
|
||||||
uptime / 60 / 60,
|
uptime / 60 / 60,
|
||||||
uptime / 60 % 60,
|
uptime / 60 % 60,
|
||||||
uptime % 60);
|
uptime % 60);
|
||||||
|
|
|
@ -29,8 +29,8 @@ extern "C" {
|
||||||
#define FURI_PACKED __attribute__((packed))
|
#define FURI_PACKED __attribute__((packed))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FURI_ALWAYS_STATIC_INLINE
|
#ifndef FURI_ALWAYS_INLINE
|
||||||
#define FURI_ALWAYS_STATIC_INLINE __attribute__((always_inline)) static inline
|
#define FURI_ALWAYS_INLINE __attribute__((always_inline)) inline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FURI_IS_IRQ_MASKED
|
#ifndef FURI_IS_IRQ_MASKED
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "thread_list.h"
|
#include "thread_list.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
|
||||||
|
#include <furi_hal_interrupt.h>
|
||||||
|
|
||||||
#include <m-array.h>
|
#include <m-array.h>
|
||||||
#include <m-dict.h>
|
#include <m-dict.h>
|
||||||
|
|
||||||
|
@ -23,6 +25,8 @@ struct FuriThreadList {
|
||||||
FuriThreadListItemDict_t search;
|
FuriThreadListItemDict_t search;
|
||||||
uint32_t runtime_previous;
|
uint32_t runtime_previous;
|
||||||
uint32_t runtime_current;
|
uint32_t runtime_current;
|
||||||
|
uint32_t isr_previous;
|
||||||
|
uint32_t isr_current;
|
||||||
};
|
};
|
||||||
|
|
||||||
FuriThreadList* furi_thread_list_alloc(void) {
|
FuriThreadList* furi_thread_list_alloc(void) {
|
||||||
|
@ -85,7 +89,10 @@ void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32
|
||||||
instance->runtime_previous = instance->runtime_current;
|
instance->runtime_previous = instance->runtime_current;
|
||||||
instance->runtime_current = runtime;
|
instance->runtime_current = runtime;
|
||||||
|
|
||||||
uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;
|
instance->isr_previous = instance->isr_current;
|
||||||
|
instance->isr_current = furi_hal_interrupt_get_time_in_isr_total();
|
||||||
|
|
||||||
|
const uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;
|
||||||
|
|
||||||
FuriThreadListItemArray_it_t it;
|
FuriThreadListItemArray_it_t it;
|
||||||
FuriThreadListItemArray_it(it, instance->items);
|
FuriThreadListItemArray_it(it, instance->items);
|
||||||
|
@ -108,3 +115,10 @@ void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float furi_thread_list_get_isr_time(FuriThreadList* instance) {
|
||||||
|
const uint32_t runtime_counter = instance->runtime_current - instance->runtime_previous;
|
||||||
|
const uint32_t isr_counter = instance->isr_current - instance->isr_previous;
|
||||||
|
|
||||||
|
return (float)isr_counter / (float)runtime_counter;
|
||||||
|
}
|
|
@ -76,6 +76,14 @@ FuriThreadListItem* furi_thread_list_get_or_insert(FuriThreadList* instance, Fur
|
||||||
*/
|
*/
|
||||||
void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick);
|
void furi_thread_list_process(FuriThreadList* instance, uint32_t runtime, uint32_t tick);
|
||||||
|
|
||||||
|
/** Get percent of time spent in ISR
|
||||||
|
*
|
||||||
|
* @param instance The instance
|
||||||
|
*
|
||||||
|
* @return percent of time spent in ISR
|
||||||
|
*/
|
||||||
|
float furi_thread_list_get_isr_time(FuriThreadList* instance);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,68.0,,
|
Version,+,68.1,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
|
@ -1288,6 +1288,7 @@ Function,+,furi_hal_info_get_api_version,void,"uint16_t*, uint16_t*"
|
||||||
Function,-,furi_hal_init,void,
|
Function,-,furi_hal_init,void,
|
||||||
Function,-,furi_hal_init_early,void,
|
Function,-,furi_hal_init_early,void,
|
||||||
Function,+,furi_hal_interrupt_get_name,const char*,uint8_t
|
Function,+,furi_hal_interrupt_get_name,const char*,uint8_t
|
||||||
|
Function,+,furi_hal_interrupt_get_time_in_isr_total,uint32_t,
|
||||||
Function,-,furi_hal_interrupt_init,void,
|
Function,-,furi_hal_interrupt_init,void,
|
||||||
Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*"
|
Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*"
|
||||||
Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*"
|
Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*"
|
||||||
|
@ -1633,6 +1634,7 @@ Function,+,furi_thread_join,_Bool,FuriThread*
|
||||||
Function,+,furi_thread_list_alloc,FuriThreadList*,
|
Function,+,furi_thread_list_alloc,FuriThreadList*,
|
||||||
Function,+,furi_thread_list_free,void,FuriThreadList*
|
Function,+,furi_thread_list_free,void,FuriThreadList*
|
||||||
Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t"
|
Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t"
|
||||||
|
Function,+,furi_thread_list_get_isr_time,float,FuriThreadList*
|
||||||
Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*"
|
Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*"
|
||||||
Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t"
|
Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t"
|
||||||
Function,+,furi_thread_list_size,size_t,FuriThreadList*
|
Function,+,furi_thread_list_size,size_t,FuriThreadList*
|
||||||
|
|
|
|
@ -1,5 +1,5 @@
|
||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,68.0,,
|
Version,+,68.1,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||||
|
@ -1413,6 +1413,7 @@ Function,+,furi_hal_infrared_set_tx_output,void,FuriHalInfraredTxPin
|
||||||
Function,-,furi_hal_init,void,
|
Function,-,furi_hal_init,void,
|
||||||
Function,-,furi_hal_init_early,void,
|
Function,-,furi_hal_init_early,void,
|
||||||
Function,+,furi_hal_interrupt_get_name,const char*,uint8_t
|
Function,+,furi_hal_interrupt_get_name,const char*,uint8_t
|
||||||
|
Function,+,furi_hal_interrupt_get_time_in_isr_total,uint32_t,
|
||||||
Function,-,furi_hal_interrupt_init,void,
|
Function,-,furi_hal_interrupt_init,void,
|
||||||
Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*"
|
Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*"
|
||||||
Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*"
|
Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, FuriHalInterruptPriority, FuriHalInterruptISR, void*"
|
||||||
|
@ -1847,6 +1848,7 @@ Function,+,furi_thread_join,_Bool,FuriThread*
|
||||||
Function,+,furi_thread_list_alloc,FuriThreadList*,
|
Function,+,furi_thread_list_alloc,FuriThreadList*,
|
||||||
Function,+,furi_thread_list_free,void,FuriThreadList*
|
Function,+,furi_thread_list_free,void,FuriThreadList*
|
||||||
Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t"
|
Function,+,furi_thread_list_get_at,FuriThreadListItem*,"FuriThreadList*, size_t"
|
||||||
|
Function,+,furi_thread_list_get_isr_time,float,FuriThreadList*
|
||||||
Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*"
|
Function,+,furi_thread_list_get_or_insert,FuriThreadListItem*,"FuriThreadList*, FuriThread*"
|
||||||
Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t"
|
Function,+,furi_thread_list_process,void,"FuriThreadList*, uint32_t, uint32_t"
|
||||||
Function,+,furi_thread_list_size,size_t,FuriThreadList*
|
Function,+,furi_thread_list_size,size_t,FuriThreadList*
|
||||||
|
|
|
|
@ -249,7 +249,7 @@ void furi_hal_gpio_remove_int_callback(const GpioPin* gpio) {
|
||||||
FURI_CRITICAL_EXIT();
|
FURI_CRITICAL_EXIT();
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void furi_hal_gpio_int_call(uint16_t pin_num) {
|
FURI_ALWAYS_INLINE static void furi_hal_gpio_int_call(uint16_t pin_num) {
|
||||||
if(gpio_interrupt[pin_num].callback) {
|
if(gpio_interrupt[pin_num].callback) {
|
||||||
gpio_interrupt[pin_num].callback(gpio_interrupt[pin_num].context);
|
gpio_interrupt[pin_num].callback(gpio_interrupt[pin_num].context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,22 @@
|
||||||
|
|
||||||
#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 5)
|
#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 5)
|
||||||
|
|
||||||
|
#define FURI_HAL_INTERRUPT_ACCOUNT_START() const uint32_t _isr_start = DWT->CYCCNT;
|
||||||
|
#define FURI_HAL_INTERRUPT_ACCOUNT_END() \
|
||||||
|
const uint32_t _time_in_isr = DWT->CYCCNT - _isr_start; \
|
||||||
|
furi_hal_interrupt.counter_time_in_isr_total += _time_in_isr;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FuriHalInterruptISR isr;
|
FuriHalInterruptISR isr;
|
||||||
void* context;
|
void* context;
|
||||||
} FuriHalInterruptISRPair;
|
} FuriHalInterruptISRPair;
|
||||||
|
|
||||||
FuriHalInterruptISRPair furi_hal_interrupt_isr[FuriHalInterruptIdMax] = {0};
|
typedef struct {
|
||||||
|
FuriHalInterruptISRPair isr[FuriHalInterruptIdMax];
|
||||||
|
uint32_t counter_time_in_isr_total;
|
||||||
|
} FuriHalIterrupt;
|
||||||
|
|
||||||
|
static FuriHalIterrupt furi_hal_interrupt = {};
|
||||||
|
|
||||||
const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
|
const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
|
||||||
// TIM1, TIM16, TIM17
|
// TIM1, TIM16, TIM17
|
||||||
|
@ -67,12 +77,16 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
|
||||||
[FuriHalInterruptIdLpUart1] = LPUART1_IRQn,
|
[FuriHalInterruptIdLpUart1] = LPUART1_IRQn,
|
||||||
};
|
};
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_call(FuriHalInterruptId index) {
|
FURI_ALWAYS_INLINE static void furi_hal_interrupt_call(FuriHalInterruptId index) {
|
||||||
furi_check(furi_hal_interrupt_isr[index].isr);
|
const FuriHalInterruptISRPair* isr_descr = &furi_hal_interrupt.isr[index];
|
||||||
furi_hal_interrupt_isr[index].isr(furi_hal_interrupt_isr[index].context);
|
furi_check(isr_descr->isr);
|
||||||
|
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_START();
|
||||||
|
isr_descr->isr(isr_descr->context);
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void
|
FURI_ALWAYS_INLINE static void
|
||||||
furi_hal_interrupt_enable(FuriHalInterruptId index, uint16_t priority) {
|
furi_hal_interrupt_enable(FuriHalInterruptId index, uint16_t priority) {
|
||||||
NVIC_SetPriority(
|
NVIC_SetPriority(
|
||||||
furi_hal_interrupt_irqn[index],
|
furi_hal_interrupt_irqn[index],
|
||||||
|
@ -80,19 +94,19 @@ FURI_ALWAYS_STATIC_INLINE void
|
||||||
NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]);
|
NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_clear_pending(FuriHalInterruptId index) {
|
FURI_ALWAYS_INLINE static void furi_hal_interrupt_clear_pending(FuriHalInterruptId index) {
|
||||||
NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]);
|
NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_get_pending(FuriHalInterruptId index) {
|
FURI_ALWAYS_INLINE static void furi_hal_interrupt_get_pending(FuriHalInterruptId index) {
|
||||||
NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]);
|
NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_set_pending(FuriHalInterruptId index) {
|
FURI_ALWAYS_INLINE static void furi_hal_interrupt_set_pending(FuriHalInterruptId index) {
|
||||||
NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]);
|
NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FURI_ALWAYS_STATIC_INLINE void furi_hal_interrupt_disable(FuriHalInterruptId index) {
|
FURI_ALWAYS_INLINE static void furi_hal_interrupt_disable(FuriHalInterruptId index) {
|
||||||
NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]);
|
NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,17 +151,18 @@ void furi_hal_interrupt_set_isr_ex(
|
||||||
|
|
||||||
uint16_t real_priority = FURI_HAL_INTERRUPT_DEFAULT_PRIORITY - priority;
|
uint16_t real_priority = FURI_HAL_INTERRUPT_DEFAULT_PRIORITY - priority;
|
||||||
|
|
||||||
|
FuriHalInterruptISRPair* isr_descr = &furi_hal_interrupt.isr[index];
|
||||||
if(isr) {
|
if(isr) {
|
||||||
// Pre ISR set
|
// Pre ISR set
|
||||||
furi_check(furi_hal_interrupt_isr[index].isr == NULL);
|
furi_check(isr_descr->isr == NULL);
|
||||||
} else {
|
} else {
|
||||||
// Pre ISR clear
|
// Pre ISR clear
|
||||||
furi_hal_interrupt_disable(index);
|
furi_hal_interrupt_disable(index);
|
||||||
furi_hal_interrupt_clear_pending(index);
|
furi_hal_interrupt_clear_pending(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_hal_interrupt_isr[index].isr = isr;
|
isr_descr->isr = isr;
|
||||||
furi_hal_interrupt_isr[index].context = context;
|
isr_descr->context = context;
|
||||||
__DMB();
|
__DMB();
|
||||||
|
|
||||||
if(isr) {
|
if(isr) {
|
||||||
|
@ -304,27 +319,37 @@ extern void HW_IPCC_Tx_Handler(void);
|
||||||
extern void HW_IPCC_Rx_Handler(void);
|
extern void HW_IPCC_Rx_Handler(void);
|
||||||
|
|
||||||
void SysTick_Handler(void) {
|
void SysTick_Handler(void) {
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_START();
|
||||||
furi_hal_os_tick();
|
furi_hal_os_tick();
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
void USB_LP_IRQHandler(void) {
|
void USB_LP_IRQHandler(void) {
|
||||||
#ifndef FURI_RAM_EXEC
|
#ifndef FURI_RAM_EXEC
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_START();
|
||||||
usbd_poll(&udev);
|
usbd_poll(&udev);
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_END();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void USB_HP_IRQHandler(void) { //-V524
|
void USB_HP_IRQHandler(void) { //-V524
|
||||||
#ifndef FURI_RAM_EXEC
|
#ifndef FURI_RAM_EXEC
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_START();
|
||||||
usbd_poll(&udev);
|
usbd_poll(&udev);
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_END();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPCC_C1_TX_IRQHandler(void) {
|
void IPCC_C1_TX_IRQHandler(void) {
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_START();
|
||||||
HW_IPCC_Tx_Handler();
|
HW_IPCC_Tx_Handler();
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPCC_C1_RX_IRQHandler(void) {
|
void IPCC_C1_RX_IRQHandler(void) {
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_START();
|
||||||
HW_IPCC_Rx_Handler();
|
HW_IPCC_Rx_Handler();
|
||||||
|
FURI_HAL_INTERRUPT_ACCOUNT_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FPU_IRQHandler(void) {
|
void FPU_IRQHandler(void) {
|
||||||
|
@ -499,3 +524,7 @@ const char* furi_hal_interrupt_get_name(uint8_t exception_number) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t furi_hal_interrupt_get_time_in_isr_total(void) {
|
||||||
|
return furi_hal_interrupt.counter_time_in_isr_total;
|
||||||
|
}
|
|
@ -118,6 +118,12 @@ void furi_hal_interrupt_set_isr_ex(
|
||||||
*/
|
*/
|
||||||
const char* furi_hal_interrupt_get_name(uint8_t exception_number);
|
const char* furi_hal_interrupt_get_name(uint8_t exception_number);
|
||||||
|
|
||||||
|
/** Get total time(in CPU clocks) spent in ISR
|
||||||
|
*
|
||||||
|
* @return total time in CPU clocks
|
||||||
|
*/
|
||||||
|
uint32_t furi_hal_interrupt_get_time_in_isr_total(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue