aboutsummaryrefslogtreecommitdiffstats
path: root/os
diff options
context:
space:
mode:
authorgdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2009-10-26 16:02:29 +0000
committergdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2009-10-26 16:02:29 +0000
commita1db002da5d4bd3fdc0c7fcc0f2aa466de803d4f (patch)
treee9ff84603a24140f7afc46b6735ed120b75fb2e1 /os
parent34f29ab36c7d465d397304811945a226234b9db6 (diff)
downloadChibiOS-a1db002da5d4bd3fdc0c7fcc0f2aa466de803d4f.tar.gz
ChibiOS-a1db002da5d4bd3fdc0c7fcc0f2aa466de803d4f.tar.bz2
ChibiOS-a1db002da5d4bd3fdc0c7fcc0f2aa466de803d4f.zip
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1256 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os')
-rw-r--r--os/io/platforms/STM32/spi_lld.c210
-rw-r--r--os/io/platforms/STM32/spi_lld.h14
-rw-r--r--os/io/spi.c63
-rw-r--r--os/io/spi.h4
-rw-r--r--os/io/templates/spi_lld.c56
-rw-r--r--os/io/templates/spi_lld.h6
6 files changed, 292 insertions, 61 deletions
diff --git a/os/io/platforms/STM32/spi_lld.c b/os/io/platforms/STM32/spi_lld.c
index 3ee0abba6..d0a84ddf7 100644
--- a/os/io/platforms/STM32/spi_lld.c
+++ b/os/io/platforms/STM32/spi_lld.c
@@ -47,24 +47,116 @@ static uint16_t dummytx;
/* Low Level Driver local functions. */
/*===========================================================================*/
+static void spi_stop(SPIDriver *spip, msg_t msg) {
+
+ /* Stops RX and TX DMA channels.*/
+ spip->spd_dmarx->CCR = 0;
+ spip->spd_dmatx->CCR = 0;
+
+ /* Stops SPI operations.*/
+ spip->spd_spi->CR1 &= ~SPI_CR1_SPE;
+
+ chSchReadyI(spip->spd_thread)->p_msg = msg;
+}
+
+static void dma_start(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+ uint32_t ccr;
+
+ /* Common DMA setup.*/
+ ccr = spip->spd_dmaprio;
+ if ((spip->spd_config->spc_cr1 & SPI_CR1_DFF) != 0)
+ ccr |= DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0; /* 16 bits transfer.*/
+
+ /* RX DMA setup.*/
+ spip->spd_dmarx->CMAR = (uint32_t)rxbuf;
+ spip->spd_dmarx->CNDTR = (uint32_t)n;
+ spip->spd_dmarx->CCR |= ccr;
+
+ /* TX DMA setup.*/
+ spip->spd_dmatx->CMAR = (uint32_t)txbuf;
+ spip->spd_dmatx->CNDTR = (uint32_t)n;
+ spip->spd_dmatx->CCR |= ccr;
+}
+
+static msg_t spi_start_wait(SPIDriver *spip) {
+ msg_t msg;
+
+ chSysLock();
+ spip->spd_spi->CR1 |= SPI_CR1_SPE; /* SPI enable.*/
+ spip->spd_thread = currp;
+ chSchGoSleepS(PRSUSPENDED); /* Wait for completion event.*/
+ spip->spd_thread = NULL;
+ msg = currp->p_rdymsg;
+ chSysUnlock();
+ return msg;
+}
+
/*===========================================================================*/
/* Low Level Driver interrupt handlers. */
/*===========================================================================*/
#if USE_STM32_SPI1 || defined(__DOXYGEN__)
+/**
+ * @brief SPI1 RX DMA interrupt handler (channel 2).
+ */
CH_IRQ_HANDLER(Vector70) {
CH_IRQ_PROLOGUE();
+ if ((DMA1->ISR & DMA_ISR_TCIF2) != 0)
+ spi_stop(&SPID1, RDY_OK);
+ else
+ spi_stop(&SPID1, RDY_RESET);
+ DMA1->IFCR |= DMA_IFCR_CGIF2 | DMA_IFCR_CTCIF2 |
+ DMA_IFCR_CHTIF2 | DMA_IFCR_CTEIF2;
+
+ CH_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief SPI1 TX DMA interrupt handler (channel 3).
+ */
+CH_IRQ_HANDLER(Vector74) {
+
+ CH_IRQ_PROLOGUE();
+
+ spi_stop(&SPID1, RDY_RESET);
+ DMA1->IFCR |= DMA_IFCR_CGIF3 | DMA_IFCR_CTCIF3 |
+ DMA_IFCR_CHTIF3 | DMA_IFCR_CTEIF3;
+
CH_IRQ_EPILOGUE();
}
#endif
#if USE_STM32_SPI2 || defined(__DOXYGEN__)
+/**
+ * @brief SPI2 RX DMA interrupt handler (channel 4).
+ */
CH_IRQ_HANDLER(Vector78) {
CH_IRQ_PROLOGUE();
+ if ((DMA1->ISR & DMA_ISR_TCIF2) != 0)
+ spi_stop(&SPID2, RDY_OK);
+ else
+ spi_stop(&SPID2, RDY_RESET);
+ DMA2->IFCR |= DMA_IFCR_CGIF4 | DMA_IFCR_CTCIF4 |
+ DMA_IFCR_CHTIF4 | DMA_IFCR_CTEIF4;
+
+ CH_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief SPI2 TX DMA interrupt handler (channel 5).
+ */
+CH_IRQ_HANDLER(Vector7C) {
+
+ CH_IRQ_PROLOGUE();
+
+ spi_stop(&SPID2, RDY_RESET);
+ DMA2->IFCR |= DMA_IFCR_CGIF5 | DMA_IFCR_CTCIF5 |
+ DMA_IFCR_CHTIF5 | DMA_IFCR_CTEIF5;
+
CH_IRQ_EPILOGUE();
}
#endif
@@ -78,10 +170,11 @@ CH_IRQ_HANDLER(Vector78) {
*/
void spi_lld_init(void) {
- dummyrx = dummytx = 0xFFFF;
+ dummytx = 0xFFFF;
#if USE_STM32_SPI1
spiObjectInit(&SPID1);
+ SPID1.spd_thread = NULL;
SPID1.spd_spi = SPI1;
SPID1.spd_dmarx = DMA1_Channel2;
SPID1.spd_dmatx = DMA1_Channel3;
@@ -92,6 +185,7 @@ void spi_lld_init(void) {
#if USE_STM32_SPI2
spiObjectInit(&SPID2);
+ SPID2.spd_thread = NULL;
SPID2.spd_spi = SPI2;
SPID2.spd_dmarx = DMA1_Channel4;
SPID2.spd_dmatx = DMA1_Channel5;
@@ -115,6 +209,20 @@ void spi_lld_setup(SPIDriver *spip) {
/* DMA setup.*/
spip->spd_dmarx->CPAR = (uint32_t)&spip->spd_spi->DR;
spip->spd_dmatx->CPAR = (uint32_t)&spip->spd_spi->DR;
+
+ /*
+ * If specified in the configuration then emits a pulses train on
+ * the SPI clock line without asserting any slave.
+ */
+ if (spip->spd_config->spc_initcnt > 0) {
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, (size_t)spip->spd_config->spc_initcnt,
+ &dummyrx, &dummytx);
+ (void) spi_start_wait(spip);
+ }
}
/**
@@ -143,48 +251,70 @@ void spi_lld_unselect(SPIDriver *spip) {
* @details This function performs a simultaneous transmit/receive operation.
*
* @param[in] spip pointer to the @p SPIDriver object
- * @param n number of words to be exchanged
- * @param rxbuf the pointer to the receive buffer, if @p NULL is specified then
- * the input data is discarded.
- * Note that the buffer is organized as an uint8_t array for
- * data sizes below or equal to 8 bits else it is organized as
- * an uint16_t array.
- * @param txbuf the pointer to the transmit buffer, if @p NULL is specified all
- * ones are transmitted.
- * Note that the buffer is organized as an uint8_t array for
- * data sizes below or equal to 8 bits else it is organized as
- * an uint16_t array.
+ * @param n number of words to exchange
+ * @param rxbuf the pointer to the receive buffer
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
*/
-void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
- uint32_t baseccr, ccr;
-
- /* Common DMA setup.*/
- baseccr = spip->spd_dmaprio;
- if ((spip->spd_config->spc_cr1 & SPI_CR1_DFF) != 0)
- baseccr |= DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0; /* 16 bits transfer.*/
-
- /* RX DMA setup.*/
- ccr = baseccr | DMA_CCR1_TCIE | DMA_CCR1_TEIE | DMA_CCR1_EN;
- if (rxbuf == NULL)
- rxbuf = &dummyrx;
- else
- ccr |= DMA_CCR1_MINC;
- spip->spd_dmarx->CMAR = (uint32_t)rxbuf;
- spip->spd_dmarx->CNDTR = (uint32_t)n;
- spip->spd_dmarx->CCR = ccr;
+msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, n, rxbuf, txbuf);
+ return spi_start_wait(spip);
+}
- /* TX DMA setup.*/
- ccr = baseccr | DMA_CCR1_DIR | DMA_CCR1_TEIE | DMA_CCR1_EN;
- if (txbuf == NULL)
- txbuf = &dummytx;
- else
- ccr |= DMA_CCR1_PINC;
- spip->spd_dmatx->CMAR = (uint32_t)txbuf;
- spip->spd_dmatx->CNDTR = (uint32_t)n;
- spip->spd_dmatx->CCR = ccr;
+/**
+ * @brief Sends data ever the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to send
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ */
+msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf) {
+
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, n, &dummyrx, txbuf);
+ return spi_start_wait(spip);
+}
- /* SPI enable.*/
- spip->spd_spi->CR1 != SPI_CR1_SPE;
+/**
+ * @brief Receives data from the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to receive
+ * @param rxbuf the pointer to the receive buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ */
+msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
+
+ spip->spd_dmarx->CCR = DMA_CCR1_TCIE | DMA_CCR1_MINC |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ spip->spd_dmatx->CCR = DMA_CCR1_DIR |
+ DMA_CCR1_TEIE | DMA_CCR1_EN;
+ dma_start(spip, n, rxbuf, &dummytx);
+ return spi_start_wait(spip);
}
/** @} */
diff --git a/os/io/platforms/STM32/spi_lld.h b/os/io/platforms/STM32/spi_lld.h
index 5e5c26106..5775b67db 100644
--- a/os/io/platforms/STM32/spi_lld.h
+++ b/os/io/platforms/STM32/spi_lld.h
@@ -58,7 +58,7 @@
#endif
/**
- * @brief SPI1 DMA priority (0..3).
+ * @brief SPI1 DMA priority (0..3|lowest..highest).
* @note The priority level is used for both the TX and RX DMA channels but
* because of the channels ordering the RX channel has always priority
* over the TX channel.
@@ -68,7 +68,7 @@
#endif
/**
- * @brief SPI2 DMA priority (0..3).
+ * @brief SPI2 DMA priority (0..3|lowest..highest).
* @note The priority level is used for both the TX and RX DMA channels but
* because of the channels ordering the RX channel has always priority
* over the TX channel.
@@ -97,7 +97,7 @@ typedef struct {
/**
* @brief Clock pulses to be generated after initialization.
*/
- cnt_t spc_clkpulses;
+ cnt_t spc_initcnt;
/* End of the mandatory fields.*/
/**
* @brief The chip select line port.
@@ -137,6 +137,10 @@ typedef struct {
const SPIConfig *spd_config;
/* End of the mandatory fields.*/
/**
+ * @brief Thread waiting for I/O completion.
+ */
+ Thread *spd_thread;
+ /**
* @brief Pointer to the SPIx registers block.
*/
SPI_TypeDef *spd_spi;
@@ -174,7 +178,9 @@ extern "C" {
void spi_lld_setup(SPIDriver *spip);
void spi_lld_select(SPIDriver *spip);
void spi_lld_unselect(SPIDriver *spip);
- void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf);
+ msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf);
+ msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf);
+ msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
#ifdef __cplusplus
}
#endif
diff --git a/os/io/spi.c b/os/io/spi.c
index 7c08fe413..be34c469d 100644
--- a/os/io/spi.c
+++ b/os/io/spi.c
@@ -59,6 +59,7 @@ void spiObjectInit(SPIDriver *spip) {
*/
void spiSetup(SPIDriver *spip, const SPIConfig *config) {
+ chDbgCheck((spip != NULL) && (config != NULL), "spiSetup");
chDbgAssert(spip->spd_state == SPI_IDLE,
"spiSetup(), #1",
"not idle");
@@ -74,6 +75,8 @@ void spiSetup(SPIDriver *spip, const SPIConfig *config) {
*/
void spiSelect(SPIDriver *spip) {
+ chDbgCheck(spip != NULL, "spiSelect");
+
chSysLock();
chDbgAssert(spip->spd_state == SPI_IDLE,
@@ -93,6 +96,8 @@ void spiSelect(SPIDriver *spip) {
*/
void spiUnselect(SPIDriver *spip) {
+ chDbgCheck(spip != NULL, "spiUnselect");
+
chSysLock();
chDbgAssert(spip->spd_state == SPI_ACTIVE,
@@ -121,13 +126,63 @@ void spiUnselect(SPIDriver *spip) {
* data sizes below or equal to 8 bits else it is organized as
* an uint16_t array.
*/
-void spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+msg_t spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+ chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL),
+ "spiExchange");
chDbgAssert(spip->spd_state == SPI_ACTIVE,
"spiExchange(), #1",
"not active");
- spi_lld_exchange(spip, n, rxbuf, txbuf);
+ return spi_lld_exchange(spip, n, rxbuf, txbuf);
+}
+
+/**
+ * @brief Sends data ever the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to send
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ */
+msg_t spiSend(SPIDriver *spip, size_t n, void *txbuf) {
+
+ chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL),
+ "spiSend");
+ chDbgAssert(spip->spd_state == SPI_ACTIVE,
+ "spiSend(), #1",
+ "not active");
+
+ return spi_lld_send(spip, n, txbuf);
+}
+
+/**
+ * @brief Receives data from the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to receive
+ * @param rxbuf the pointer to the receive buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ */
+msg_t spiReceive(SPIDriver *spip, size_t n, void *rxbuf) {
+
+ chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL),
+ "spiReceive");
+ chDbgAssert(spip->spd_state == SPI_ACTIVE,
+ "spiReceive(), #1",
+ "not active");
+
+ return spi_lld_receive(spip, n, rxbuf);
}
#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
@@ -143,6 +198,8 @@ void spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
*/
void spiAcquireBus(SPIDriver *spip) {
+ chDbgCheck(spip != NULL, "spiAcquireBus");
+
#if CH_USE_MUTEXES
chMtxLock(&spip->spd_mutex);
#elif CH_USE_SEMAPHORES
@@ -160,6 +217,8 @@ void spiAcquireBus(SPIDriver *spip) {
*/
void spiReleaseBus(SPIDriver *spip) {
+ chDbgCheck(spip != NULL, "spiReleaseBus");
+
#if CH_USE_MUTEXES
(void)spip;
chMtxUnlock();
diff --git a/os/io/spi.h b/os/io/spi.h
index 54409fe76..816e9f6d4 100644
--- a/os/io/spi.h
+++ b/os/io/spi.h
@@ -48,7 +48,9 @@ extern "C" {
void spiSetup(SPIDriver *spip, const SPIConfig *config);
void spiSelect(SPIDriver *spip);
void spiUnselect(SPIDriver *spip);
- void spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf);
+ msg_t spiExchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf);
+ msg_t spiSend(SPIDriver *spip, size_t n, void *txbuf);
+ msg_t spiReceive(SPIDriver *spip, size_t n, void *rxbuf);
#if SPI_USE_MUTUAL_EXCLUSION
void spiAcquireBus(SPIDriver *spip);
void spiReleaseBus(SPIDriver *spip);
diff --git a/os/io/templates/spi_lld.c b/os/io/templates/spi_lld.c
index ca938fe7b..e31dffc4b 100644
--- a/os/io/templates/spi_lld.c
+++ b/os/io/templates/spi_lld.c
@@ -79,19 +79,51 @@ void spi_lld_unselect(SPIDriver *spip) {
* @details This function performs a simultaneous transmit/receive operation.
*
* @param[in] spip pointer to the @p SPIDriver object
- * @param n number of words to be exchanged
- * @param rxbuf the pointer to the receive buffer, if @p NULL is specified then
- * the input data is discarded.
- * Note that the buffer is organized as an uint8_t array for
- * data sizes below or equal to 8 bits else it is organized as
- * an uint16_t array.
- * @param txbuf the pointer to the transmit buffer, if @p NULL is specified all
- * ones are transmitted.
- * Note that the buffer is organized as an uint8_t array for
- * data sizes below or equal to 8 bits else it is organized as
- * an uint16_t array.
+ * @param n number of words to exchange
+ * @param rxbuf the pointer to the receive buffer
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ */
+msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+
+}
+
+/**
+ * @brief Sends data ever the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to send
+ * @param txbuf the pointer to the transmit buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
+ */
+msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf) {
+
+}
+
+/**
+ * @brief Receives data from the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param n number of words to receive
+ * @param rxbuf the pointer to the receive buffer
+ * @return The operation status is returned.
+ * @retval RDY_OK operation complete.
+ * @retval RDY_RESET hardware failure.
+ *
+ * @note The buffers are organized as uint8_t arrays for data sizes below or
+ * equal to 8 bits else it is organized as uint16_t arrays.
*/
-void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf) {
+msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
}
diff --git a/os/io/templates/spi_lld.h b/os/io/templates/spi_lld.h
index 37f14781e..a8479c008 100644
--- a/os/io/templates/spi_lld.h
+++ b/os/io/templates/spi_lld.h
@@ -62,7 +62,7 @@ typedef struct {
/**
* @brief Clock pulses to be generated after initialization.
*/
- cnt_t spc_clkpulses;
+ cnt_t spc_initcnt;
/* End of the mandatory fields.*/
} SPIConfig;
@@ -102,7 +102,9 @@ extern "C" {
void spi_lld_setup(SPIDriver *spip);
void spi_lld_select(SPIDriver *spip);
void spi_lld_unselect(SPIDriver *spip);
- void spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf);
+ msg_t spi_lld_exchange(SPIDriver *spip, size_t n, void *rxbuf, void *txbuf);
+ msg_t spi_lld_send(SPIDriver *spip, size_t n, void *txbuf);
+ msg_t spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
#ifdef __cplusplus
}
#endif