diff --git a/os/hal/include/dac.h b/os/hal/include/dac.h
new file mode 100644
index 000000000..92caa1be7
--- /dev/null
+++ b/os/hal/include/dac.h
@@ -0,0 +1,324 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT 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 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT 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, see .
+*/
+
+/**
+ * @file dac.h
+ * @brief DAC Driver macros and structures.
+ *
+ * @addtogroup DAC
+ * @{
+ */
+
+#ifndef _DAC_H_
+#define _DAC_H_
+
+#if HAL_USE_DAC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name DAC configuration options
+ * @{
+ */
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if DAC_USE_MUTUAL_EXCLUSION && !CH_USE_MUTEXES && !CH_USE_SEMAPHORES
+#error "DAC_USE_MUTUAL_EXCLUSION requires CH_USE_MUTEXES and/or CH_USE_SEMAPHORES"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Driver state machine possible states.
+ */
+typedef enum {
+ DAC_UNINIT = 0, /**< Not initialized. */
+ DAC_STOP = 1, /**< Stopped. */
+ DAC_READY = 2, /**< Ready. */
+ DAC_ACTIVE = 3, /**< Exchanging data. */
+ DAC_COMPLETE = 4, /**< Asynchronous operation complete. */
+ DAC_ERROR = 5 /**< Error. */
+} dacstate_t;
+
+#include "dac_lld.h"
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @name Low Level driver helper macros
+ * @{
+ */
+#if DAC_USE_WAIT || defined(__DOXYGEN__)
+/**
+ * @brief Waits for operation completion.
+ * @details This function waits for the driver to complete the current
+ * operation.
+ * @pre An operation must be running while the function is invoked.
+ * @note No more than one thread can wait on a DAC driver using
+ * this function.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_wait_s(dacp) { \
+ chDbgAssert((dacp)->thread == NULL, \
+ "_dac_wait_s(), #1", "already waiting"); \
+ (dacp)->thread = chThdSelf(); \
+ chSchGoSleepS(THD_STATE_SUSPENDED); \
+}
+/**
+ * @brief Resumes a thread waiting for a conversion completion.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_reset_i(dacp) { \
+ if ((dacp)->thread != NULL) { \
+ Thread *tp = (dacp)->thread; \
+ (dacp)->thread = NULL; \
+ tp->p_u.rdymsg = RDY_RESET; \
+ chSchReadyI(tp); \
+ } \
+}
+
+/**
+ * @brief Resumes a thread waiting for a conversion completion.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_reset_s(dacp) { \
+ if ((dacp)->thread != NULL) { \
+ Thread *tp = (dacp)->thread; \
+ (dacp)->thread = NULL; \
+ chSchWakeupS(tp, RDY_RESET); \
+ } \
+}
+
+/**
+ * @brief Wakes up the waiting thread.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_wakeup_isr(dacp) { \
+ chSysLockFromIsr(); \
+ if ((dacp)->thread != NULL) { \
+ Thread *tp; \
+ tp = (dacp)->thread; \
+ (dacp)->thread = NULL; \
+ tp->p_u.rdymsg = RDY_OK; \
+ chSchReadyI(tp); \
+ } \
+ chSysUnlockFromIsr(); \
+}
+
+/**
+ * @brief Wakes up the waiting thread with a timeout message.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_timeout_isr(dacp) { \
+ chSysLockFromIsr(); \
+ if ((dacp)->thread != NULL) { \
+ Thread *tp; \
+ tp = (dacp)->thread; \
+ (dacp)->thread = NULL; \
+ tp->p_u.rdymsg = RDY_TIMEOUT; \
+ chSchReadyI(tp); \
+ } \
+ chSysUnlockFromIsr(); \
+}
+
+#else /* !DAC_USE_WAIT */
+#define _dac_wait_s(dacp)
+#define _dac_reset_i(dacp)
+#define _dac_reset_s(dacp)
+#define _dac_wakeup_isr(dacp)
+#define _dac_timeout_isr(dacp)
+#endif /* !DAC_USE_WAIT */
+
+/**
+ * @brief Common ISR code, half buffer event.
+ * @details This code handles the portable part of the ISR code:
+ * - Callback invocation.
+ * .
+ * @note This macro is meant to be used in the low level drivers
+ * implementation only.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_isr_half_code(dacp) { \
+ if ((dacp)->grpp->end_cb != NULL) { \
+ (dacp)->grpp->end_cb(dacp, (dacp)->samples, (dacp)->depth / 2); \
+ } \
+}
+
+/**
+ * @brief Common ISR code, full buffer event.
+ * @details This code handles the portable part of the ISR code:
+ * - Callback invocation.
+ * - Waiting thread wakeup, if any.
+ * - Driver state transitions.
+ * .
+ * @note This macro is meant to be used in the low level drivers
+ * implementation only.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @notapi
+ */
+#define _dac_isr_full_code(dacp) { \
+ if ((dacp)->grpp->circular) { \
+ /* Callback handling.*/ \
+ if ((dacp)->grpp->end_cb != NULL) { \
+ if ((dacp)->depth > 1) { \
+ /* Invokes the callback passing the 2nd half of the buffer.*/ \
+ size_t half = (dacp)->depth / 2; \
+ size_t half_index = half * (dacp)->grpp->num_channels; \
+ (dacp)->grpp->end_cb(dacp, (dacp)->samples + half_index, half); \
+ } \
+ else { \
+ /* Invokes the callback passing the whole buffer.*/ \
+ (dacp)->grpp->end_cb(dacp, (dacp)->samples, (dacp)->depth); \
+ } \
+ } \
+ } \
+ else { \
+ /* End conversion.*/ \
+ dac_lld_stop_conversion(dacp); \
+ if ((dacp)->grpp->end_cb != NULL) { \
+ (dacp)->state = DAC_COMPLETE; \
+ if ((dacp)->depth > 1) { \
+ /* Invokes the callback passing the 2nd half of the buffer.*/ \
+ size_t half = (dacp)->depth / 2; \
+ size_t half_index = half * (dacp)->grpp->num_channels; \
+ (dacp)->grpp->end_cb(dacp, (dacp)->samples + half_index, half); \
+ } \
+ else { \
+ /* Invokes the callback passing the whole buffer.*/ \
+ (dacp)->grpp->end_cb(dacp, (dacp)->samples, (dacp)->depth); \
+ } \
+ if ((dacp)->state == DAC_COMPLETE) { \
+ (dacp)->state = DAC_READY; \
+ (dacp)->grpp = NULL; \
+ } \
+ } \
+ else { \
+ (dacp)->state = DAC_READY; \
+ (dacp)->grpp = NULL; \
+ } \
+ _dac_wakeup_isr(dacp); \
+ } \
+}
+
+/**
+ * @brief Common ISR code, error event.
+ * @details This code handles the portable part of the ISR code:
+ * - Callback invocation.
+ * - Waiting thread timeout signaling, if any.
+ * - Driver state transitions.
+ * .
+ * @note This macro is meant to be used in the low level drivers
+ * implementation only.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ * @param[in] err platform dependent error code
+ *
+ * @notapi
+ */
+#define _dac_isr_error_code(dacp, err) { \
+ dac_lld_stop_conversion(dacp); \
+ if ((dacp)->grpp->error_cb != NULL) { \
+ (dacp)->state = DAC_ERROR; \
+ (dacp)->grpp->error_cb(dacp, err); \
+ if ((dacp)->state == DAC_ERROR) \
+ (dacp)->state = DAC_READY; \
+ } \
+ (dacp)->grpp = NULL; \
+ _dac_timeout_isr(dacp); \
+}
+/** @} */
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void dacInit(void);
+ void dacObjectInit(DACDriver *dacp);
+ void dacStart(DACDriver *dacp, const DACConfig *config);
+ void dacStop(DACDriver *dacp);
+ void dacStartSend(DACDriver *dacp);
+#if DAC_USE_WAIT
+ void dacSend(DACDriver *dacp);
+#endif /* DAC_USE_WAIT */
+#if DAC_USE_MUTUAL_EXCLUSION
+ void dacAcquireBus(DACDriver *dacp);
+ void dacReleaseBus(DACDriver *dacp);
+#endif /* DAC_USE_MUTUAL_EXCLUSION */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_DAC */
+
+#endif /* _DAC_H_ */
+
+/** @} */
diff --git a/os/hal/include/spi.h b/os/hal/include/spi.h
index f9988631b..5e0fa9e53 100644
--- a/os/hal/include/spi.h
+++ b/os/hal/include/spi.h
@@ -244,13 +244,14 @@ typedef enum {
* @notapi
*/
#define _spi_wakeup_isr(spip) { \
+ chSysLockFromIsr(); \
if ((spip)->thread != NULL) { \
Thread *tp = (spip)->thread; \
(spip)->thread = NULL; \
- chSysLockFromIsr(); \
+ tp->p_u.rdymsg = RDY_OK; \
chSchReadyI(tp); \
- chSysUnlockFromIsr(); \
} \
+ chSysUnlockFromIsr(); \
}
#else /* !SPI_USE_WAIT */
#define _spi_wait_s(spip)
diff --git a/os/hal/src/dac.c b/os/hal/src/dac.c
new file mode 100644
index 000000000..b4a659791
--- /dev/null
+++ b/os/hal/src/dac.c
@@ -0,0 +1,286 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT 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 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT 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, see .
+*/
+
+/**
+ * @file dac.c
+ * @brief DAC Driver code.
+ *
+ * @addtogroup DAC
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if HAL_USE_DAC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief DAC Driver initialization.
+ * @note This function is implicitly invoked by @p halInit(), there is
+ * no need to explicitly initialize the driver.
+ *
+ * @init
+ */
+void dacInit(void) {
+
+ dac_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p DACDriver structure.
+ *
+ * @param[out] dacp pointer to the @p DACDriver object
+ *
+ * @init
+ */
+void dacObjectInit(DACDriver *dacp) {
+
+ dacp->state = DAC_STOP;
+ dacp->config = NULL;
+#if DAC_USE_WAIT
+ dacp->thread = NULL;
+#endif /* DAC_USE_WAIT */
+#if DAC_USE_MUTUAL_EXCLUSION
+#if CH_USE_MUTEXES
+ chMtxInit(&dacp->mutex);
+#else
+ chSemInit(&dacp->semaphore, 1);
+#endif
+#endif /* DAC_USE_MUTUAL_EXCLUSION */
+#if defined(DAC_DRIVER_EXT_INIT_HOOK)
+ DAC_DRIVER_EXT_INIT_HOOK(dacp);
+#endif
+}
+
+/**
+ * @brief Configures and activates the DAC peripheral.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ * @param[in] config pointer to the @p DACConfig object
+ *
+ * @api
+ */
+void dacStart(DACDriver *dacp, const DACConfig *config) {
+
+ chDbgCheck((dacp != NULL) && (config != NULL), "dacStart");
+
+ chSysLock();
+ chDbgAssert((dacp->state == DAC_STOP) || (dacp->state == DAC_READY),
+ "dacStart(), #1", "invalid state");
+ dacp->config = config;
+ dac_lld_start(dacp);
+ dacp->state = DAC_READY;
+ chSysUnlock();
+}
+
+/**
+ * @brief Deactivates the DAC peripheral.
+ * @note Deactivating the peripheral also enforces a release of the slave
+ * select line.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @api
+ */
+void dacStop(DACDriver *dacp) {
+
+ chDbgCheck(dacp != NULL, "dacStop");
+
+ chSysLock();
+ chDbgAssert((dacp->state == DAC_STOP) || (dacp->state == DAC_READY),
+ "dacStop(), #1", "invalid state");
+ dac_lld_stop(dacp);
+ dacp->state = DAC_STOP;
+ chSysUnlock();
+}
+
+/**
+ * @brief Starts a DAC conversion.
+ * @details Starts an asynchronous conversion operation.
+ * @note The buffer is organized as a matrix of M*N elements where M is the
+ * channels number configured into the conversion group and N is the
+ * buffer depth. The samples are sequentially written into the buffer
+ * with no gaps.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ * @param[in] grpp pointer to a @p DACConversionGroup object
+ * @param[in] samples pointer to the samples buffer
+ * @param[in] depth buffer depth (matrix rows number). The buffer depth
+ * must be one or an even number.
+ *
+ * @api
+ */
+void dacStartConversion(DACDriver *dacp,
+ const DACConversionGroup *grpp,
+ const dacsample_t *samples,
+ size_t depth) {
+
+ chSysLock();
+ dacStartConversionI(dacp, grpp, samples, depth);
+ chSysUnlock();
+}
+
+/**
+ * @brief Starts a DAC conversion.
+ * @details Starts an asynchronous conversion operation.
+ * @post The callbacks associated to the conversion group will be invoked
+ * on buffer fill and error events.
+ * @note The buffer is organized as a matrix of M*N elements where M is the
+ * channels number configured into the conversion group and N is the
+ * buffer depth. The samples are sequentially written into the buffer
+ * with no gaps.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ * @param[in] grpp pointer to a @p DACConversionGroup object
+ * @param[in] samples pointer to the samples buffer
+ * @param[in] depth buffer depth (matrix rows number). The buffer depth
+ * must be one or an even number.
+ *
+ * @iclass
+ */
+void dacStartConversionI(DACDriver *dacp,
+ const DACConversionGroup *grpp,
+ const dacsample_t *samples,
+ size_t depth) {
+
+ chDbgCheckClassI();
+ chDbgCheck((dacp != NULL) && (grpp != NULL) && (samples != NULL) &&
+ ((depth == 1) || ((depth & 1) == 0)),
+ "dacStartConversionI");
+ chDbgAssert((dacp->state == DAC_READY) ||
+ (dacp->state == DAC_COMPLETE) ||
+ (dacp->state == DAC_ERROR),
+ "dacStartConversionI(), #1", "not ready");
+
+ dacp->samples = samples;
+ dacp->depth = depth;
+ dacp->grpp = grpp;
+ dacp->state = DAC_ACTIVE;
+ dac_lld_start_conversion(dacp);
+}
+
+#if DAC_USE_WAIT || defined(__DOXYGEN__)
+/**
+ * @brief Performs a DAC conversion.
+ * @details Performs a synchronous conversion operation.
+ * @note The buffer is organized as a matrix of M*N elements where M is the
+ * channels number configured into the conversion group and N is the
+ * buffer depth. The samples are sequentially written into the buffer
+ * with no gaps.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ * @param[in] grpp pointer to a @p DACConversionGroup object
+ * @param[out] samples pointer to the samples buffer
+ * @param[in] depth buffer depth (matrix rows number). The buffer depth
+ * must be one or an even number.
+ * @return The operation result.
+ * @retval RDY_OK Conversion finished.
+ * @retval RDY_RESET The conversion has been stopped using
+ * @p acdStopConversion() or @p acdStopConversionI(),
+ * the result buffer may contain incorrect data.
+ * @retval RDY_TIMEOUT The conversion has been stopped because an hardware
+ * error.
+ *
+ * @api
+ */
+msg_t dacConvert(DACDriver *dacp,
+ const DACConversionGroup *grpp,
+ const dacsample_t *samples,
+ size_t depth) {
+ msg_t msg;
+
+ chSysLock();
+ chDbgAssert(dacp->thread == NULL, "dacConvert(), #1", "already waiting");
+ dacStartConversionI(dacp, grpp, samples, depth);
+ _dac_wait_s(dacp);
+ msg = chThdSelf()->p_u.rdymsg;
+ chSysUnlock();
+ return msg;
+}
+#endif /* DAC_USE_WAIT */
+
+#if DAC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+/**
+ * @brief Gains exclusive access to the DAC bus.
+ * @details This function tries to gain ownership to the DAC bus, if the bus
+ * is already being used then the invoking thread is queued.
+ * @pre In order to use this function the option @p DAC_USE_MUTUAL_EXCLUSION
+ * must be enabled.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @api
+ */
+void dacAcquireBus(DACDriver *dacp) {
+
+ chDbgCheck(dacp != NULL, "dacAcquireBus");
+
+#if CH_USE_MUTEXES
+ chMtxLock(&dacp->mutex);
+#elif CH_USE_SEMAPHORES
+ chSemWait(&dacp->semaphore);
+#endif
+}
+
+/**
+ * @brief Releases exclusive access to the DAC bus.
+ * @pre In order to use this function the option @p DAC_USE_MUTUAL_EXCLUSION
+ * must be enabled.
+ *
+ * @param[in] dacp pointer to the @p DACDriver object
+ *
+ * @api
+ */
+void dacReleaseBus(DACDriver *dacp) {
+
+ chDbgCheck(dacp != NULL, "dacReleaseBus");
+
+#if CH_USE_MUTEXES
+ (void)dacp;
+ chMtxUnlock();
+#elif CH_USE_SEMAPHORES
+ chSemSignal(&dacp->semaphore);
+#endif
+}
+#endif /* DAC_USE_MUTUAL_EXCLUSION */
+
+#endif /* HAL_USE_DAC */
+
+/** @} */