aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/src
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/src')
-rw-r--r--os/hal/src/adc.c210
-rw-r--r--os/hal/src/can.c224
-rw-r--r--os/hal/src/hal.c53
-rw-r--r--os/hal/src/mac.c178
-rw-r--r--os/hal/src/mii.c37
-rw-r--r--os/hal/src/mmc_spi.c575
-rw-r--r--os/hal/src/pal.c98
-rw-r--r--os/hal/src/serial.c201
-rw-r--r--os/hal/src/spi.c262
9 files changed, 1838 insertions, 0 deletions
diff --git a/os/hal/src/adc.c b/os/hal/src/adc.c
new file mode 100644
index 000000000..5a041f056
--- /dev/null
+++ b/os/hal/src/adc.c
@@ -0,0 +1,210 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file adc.c
+ * @brief ADC Driver code.
+ * @addtogroup ADC
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_ADC
+
+/**
+ * @brief ADC Driver initialization.
+ */
+void adcInit(void) {
+
+ adc_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p ADCDriver structure.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ */
+void adcObjectInit(ADCDriver *adcp) {
+
+ adcp->ad_state = ADC_STOP;
+ adcp->ad_config = NULL;
+ adcp->ad_callback = NULL;
+ adcp->ad_samples = NULL;
+ adcp->ad_depth = 0;
+ adcp->ad_grpp = NULL;
+ chSemInit(&adcp->ad_sem, 0);
+}
+
+/**
+ * @brief Configures and activates the ADC peripheral.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ * @param[in] config pointer to the @p ADCConfig object
+ */
+void adcStart(ADCDriver *adcp, const ADCConfig *config) {
+
+ chDbgCheck((adcp != NULL) && (config != NULL), "adcStart");
+
+ chSysLock();
+ chDbgAssert((adcp->ad_state == ADC_STOP) || (adcp->ad_state == ADC_READY),
+ "adcStart(), #1",
+ "invalid state");
+ adcp->ad_config = config;
+ adc_lld_start(adcp);
+ adcp->ad_state = ADC_READY;
+ chSysUnlock();
+}
+
+/**
+ * @brief Deactivates the ADC peripheral.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ */
+void adcStop(ADCDriver *adcp) {
+
+ chDbgCheck(adcp != NULL, "adcStop");
+
+ chSysLock();
+ chDbgAssert((adcp->ad_state == ADC_STOP) || (adcp->ad_state == ADC_READY),
+ "adcStop(), #1",
+ "invalid state");
+ adc_lld_stop(adcp);
+ adcp->ad_state = ADC_STOP;
+ chSysUnlock();
+}
+
+/**
+ * @brief Starts an ADC conversion.
+ * @details Starts a conversion operation, there are two kind of conversion
+ * modes:
+ * - <b>LINEAR</b>, this mode is activated when the @p callback
+ * parameter is set to @p NULL, in this mode the buffer is filled
+ * once and then the conversion stops automatically.
+ * - <b>CIRCULAR</b>, when a callback function is defined the
+ * conversion never stops and the buffer is filled circularly.
+ * During the conversion the callback function is invoked when
+ * the buffer is 50% filled and when the buffer is 100% filled,
+ * this way is possible to process the conversion stream in real
+ * time. This kind of conversion can only be stopped by explicitly
+ * invoking @p adcStopConversion().
+ * .
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ * @param[in] grpp pointer to a @p ADCConversionGroup 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.
+ * @param[in] callback pointer to the conversion callback function
+ * @return The operation status.
+ * @retval FALSE the conversion has been started.
+ * @retval TRUE the driver is busy, conversion not started.
+ *
+ * @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.
+ */
+bool_t adcStartConversion(ADCDriver *adcp,
+ const ADCConversionGroup *grpp,
+ adcsample_t *samples,
+ size_t depth,
+ adccallback_t callback) {
+
+ chDbgCheck((adcp != NULL) && (grpp != NULL) && (samples != NULL) &&
+ ((depth == 1) || ((depth & 1) == 0)),
+ "adcStartConversion");
+
+ chSysLock();
+ chDbgAssert((adcp->ad_state == ADC_READY) ||
+ (adcp->ad_state == ADC_RUNNING),
+ "adcStartConversion(), #1",
+ "invalid state");
+ if (adcp->ad_state == ADC_RUNNING) {
+ chSysUnlock();
+ return TRUE;
+ }
+ adcp->ad_callback = callback;
+ adcp->ad_samples = samples;
+ adcp->ad_depth = depth;
+ adcp->ad_grpp = grpp;
+ adc_lld_start_conversion(adcp);
+ adcp->ad_state = ADC_RUNNING;
+ chSysUnlock();
+ return FALSE;
+}
+
+/**
+ * @brief Stops an ongoing conversion.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ */
+void adcStopConversion(ADCDriver *adcp) {
+
+ chDbgCheck(adcp != NULL, "adcStopConversion");
+
+ chSysLock();
+ chDbgAssert((adcp->ad_state == ADC_READY) ||
+ (adcp->ad_state == ADC_RUNNING),
+ "adcStopConversion(), #1",
+ "invalid state");
+ if (adcp->ad_state == ADC_RUNNING) {
+ adc_lld_stop_conversion(adcp);
+ adcp->ad_grpp = NULL;
+ adcp->ad_state = ADC_READY;
+ chSemResetI(&adcp->ad_sem, 0);
+ chSchRescheduleS();
+ }
+ chSysUnlock();
+}
+
+/**
+ * @brief Waits for completion.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_IMMEDIATE immediate timeout.
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation result.
+ * @retval RDY_OK conversion finished (or not started).
+ * @retval RDY_TIMEOUT conversion not finished within the specified time.
+ */
+msg_t adcWaitConversion(ADCDriver *adcp, systime_t timeout) {
+
+ chSysLock();
+ chDbgAssert((adcp->ad_state == ADC_READY) ||
+ (adcp->ad_state == ADC_RUNNING),
+ "adcWaitConversion(), #1",
+ "invalid state");
+ if (adcp->ad_state == ADC_RUNNING) {
+ if (chSemWaitTimeoutS(&adcp->ad_sem, timeout) == RDY_TIMEOUT) {
+ chSysUnlock();
+ return RDY_TIMEOUT;
+ }
+ }
+ chSysUnlock();
+ return RDY_OK;
+}
+
+#endif /* CH_HAL_USE_ADC */
+
+/** @} */
diff --git a/os/hal/src/can.c b/os/hal/src/can.c
new file mode 100644
index 000000000..bb3e0d1a5
--- /dev/null
+++ b/os/hal/src/can.c
@@ -0,0 +1,224 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file CAN.c
+ * @brief CAN Driver code.
+ * @addtogroup CAN
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_CAN
+
+/**
+ * @brief CAN Driver initialization.
+ */
+void canInit(void) {
+
+ can_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p CANDriver structure.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ */
+void canObjectInit(CANDriver *canp) {
+
+ canp->can_state = CAN_STOP;
+ canp->can_config = NULL;
+ chSemInit(&canp->can_txsem, 0);
+ chSemInit(&canp->can_rxsem, 0);
+ chEvtInit(&canp->can_rxfull_event);
+ chEvtInit(&canp->can_txempty_event);
+#if CAN_USE_SLEEP_MODE
+ chEvtInit(&canp->can_sleep_event);
+ chEvtInit(&canp->can_wakeup_event);
+#endif /* CAN_USE_SLEEP_MODE */
+}
+
+/**
+ * @brief Configures and activates the CAN peripheral.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ * @param[in] config pointer to the @p CANConfig object
+ */
+void canStart(CANDriver *canp, const CANConfig *config) {
+
+ chDbgCheck((canp != NULL) && (config != NULL), "canStart");
+
+ chSysLock();
+ chDbgAssert((canp->can_state == CAN_STOP) || (canp->can_state == CAN_READY),
+ "canStart(), #1",
+ "invalid state");
+ canp->can_config = config;
+ can_lld_start(canp);
+ canp->can_state = CAN_READY;
+ chSysUnlock();
+}
+
+/**
+ * @brief Deactivates the CAN peripheral.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ */
+void canStop(CANDriver *canp) {
+
+ chDbgCheck(canp != NULL, "canStop");
+
+ chSysLock();
+ chDbgAssert((canp->can_state == CAN_STOP) || (canp->can_state == CAN_READY),
+ "canStop(), #1",
+ "invalid state");
+ can_lld_stop(canp);
+ canp->can_state = CAN_STOP;
+ chSysUnlock();
+}
+
+/**
+ * @brief Can frame transmission.
+ * @details The specified frame is queued for transmission, if the hardware
+ * queue is full then the invoking thread is queued.
+ * @note Trying to transmit while in sleep mode simply enqueues the thread.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ * @param[in] cfp pointer to the CAN frame to be transmitted
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_IMMEDIATE immediate timeout.
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation result.
+ * @retval RDY_OK the frame has been queued for transmission.
+ * @retval RDY_TIMEOUT operation not finished within the specified time.
+ * @retval RDY_RESET driver stopped while waiting.
+ */
+msg_t canTransmit(CANDriver *canp, const CANFrame *cfp, systime_t timeout) {
+ msg_t msg;
+
+ chDbgCheck((canp != NULL) && (cfp != NULL), "canTransmit");
+
+ chSysLock();
+ chDbgAssert((canp->can_state == CAN_READY) || (canp->can_state == CAN_SLEEP),
+ "canTransmit(), #1",
+ "invalid state");
+ if ((canp->can_state == CAN_SLEEP) || !can_lld_can_transmit(canp)) {
+ msg = chSemWaitTimeoutS(&canp->can_txsem, timeout);
+ if (msg != RDY_OK) {
+ chSysUnlock();
+ return msg;
+ }
+ }
+ msg = can_lld_transmit(canp, cfp);
+ chSysUnlock();
+ return msg;
+}
+
+/**
+ * @brief Can frame receive.
+ * @details The function waits until a frame is received.
+ * @note Trying to receive while in sleep mode simply enqueues the thread.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ * @param[out] cfp pointer to the buffer where the CAN frame is copied
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_IMMEDIATE immediate timeout.
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation result.
+ * @retval RDY_OK a frame has been received and placed in the buffer.
+ * @retval RDY_TIMEOUT operation not finished within the specified time.
+ * @retval RDY_RESET driver stopped while waiting.
+ */
+msg_t canReceive(CANDriver *canp, CANFrame *cfp, systime_t timeout) {
+ msg_t msg;
+
+ chDbgCheck((canp != NULL) && (cfp != NULL), "canReceive");
+
+ chSysLock();
+ chDbgAssert((canp->can_state == CAN_READY) || (canp->can_state == CAN_SLEEP),
+ "canReceive(), #1",
+ "invalid state");
+ if ((canp->can_state == CAN_SLEEP) || !can_lld_can_receive(canp)) {
+ msg = chSemWaitTimeoutS(&canp->can_rxsem, timeout);
+ if (msg != RDY_OK) {
+ chSysUnlock();
+ return msg;
+ }
+ }
+ msg = can_lld_receive(canp, cfp);
+ chSysUnlock();
+ return msg;
+}
+
+#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__)
+/**
+ * @brief Enters the sleep mode.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ */
+void canSleep(CANDriver *canp) {
+
+ chDbgCheck(canp != NULL, "canSleep");
+
+ chSysLock();
+ chDbgAssert((canp->can_state == CAN_READY) || (canp->can_state == CAN_SLEEP),
+ "canSleep(), #1",
+ "invalid state");
+ if (canp->can_state == CAN_READY) {
+ can_lld_sleep(canp);
+ canp->can_state = CAN_SLEEP;
+ chEvtBroadcastI(&canp->can_sleep_event);
+ chSchRescheduleS();
+ }
+ chSysUnlock();
+}
+
+/**
+ * @brief Enforces leaving the sleep mode.
+ * @note The sleep mode is supposed to be usually exited automatically by an
+ * hardware event.
+ *
+ * @param[in] canp pointer to the @p CANDriver object
+ */
+void canWakeup(CANDriver *canp) {
+
+ chDbgCheck(canp != NULL, "canWakeup");
+
+ chSysLock();
+ chDbgAssert((canp->can_state == CAN_READY) || (canp->can_state == CAN_SLEEP),
+ "canWakeup(), #1",
+ "invalid state");
+ if (canp->can_state == CAN_SLEEP) {
+ can_lld_wakeup(canp);
+ canp->can_state = CAN_READY;
+ chEvtBroadcastI(&canp->can_wakeup_event);
+ chSchRescheduleS();
+ }
+ chSysUnlock();
+}
+#endif /* CAN_USE_SLEEP_MODE */
+
+#endif /* CH_HAL_USE_CAN */
+
+/** @} */
diff --git a/os/hal/src/hal.c b/os/hal/src/hal.c
new file mode 100644
index 000000000..1aec1a478
--- /dev/null
+++ b/os/hal/src/hal.c
@@ -0,0 +1,53 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file hal.c
+ * @brief HAL subsystem code.
+ * @addtogroup HAL
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+void halInit(void) {
+
+#if CH_HAL_USE_PAL
+ palInit(&pal_default_config);
+#endif
+#if CH_HAL_USE_ADC
+ adcInit();
+#endif
+#if CH_HAL_USE_CAN
+ canInit();
+#endif
+#if CH_HAL_USE_MAC
+ macInit();
+#endif
+#if CH_HAL_USE_SERIAL
+ sdInit();
+#endif
+#if CH_HAL_USE_SPI
+ spiInit();
+#endif
+#if CH_HAL_USE_MMC_SPI
+ mmcInit();
+#endif
+}
diff --git a/os/hal/src/mac.c b/os/hal/src/mac.c
new file mode 100644
index 000000000..8dfb400aa
--- /dev/null
+++ b/os/hal/src/mac.c
@@ -0,0 +1,178 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mac.c
+ * @brief MAC Driver code.
+ * @addtogroup MAC
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_MAC
+
+/**
+ * @brief MAC Driver initialization.
+ */
+void macInit(void) {
+
+ mac_lld_init();
+}
+
+/**
+ * @brief Initialize the standard part of a @p MACDriver structure.
+ *
+ * @param[in] macp pointer to the @p MACDriver object
+ */
+void macObjectInit(MACDriver *macp) {
+
+ chSemInit(&macp->md_tdsem, 0);
+ chSemInit(&macp->md_rdsem, 0);
+#if CH_USE_EVENTS
+ chEvtInit(&macp->md_rdevent);
+#endif
+}
+
+/**
+ * @brief MAC address setup.
+ *
+ * @param[in] macp pointer to the @p MACDriver object
+ * @param[in] p pointer to a six bytes buffer containing the MAC address. If
+ * this parameter is set to @p NULL then a system default MAC is
+ * used.
+ *
+ * @note This function must be invoked only with the driver in the stopped
+ * state. If invoked on an active interface then it is ignored.
+ */
+void macSetAddress(MACDriver *macp, const uint8_t *p) {
+
+ mac_lld_set_address(macp, p);
+}
+
+/**
+ * @brief Allocates a transmission descriptor.
+ * @details One of the available transmission descriptors is locked and
+ * returned. If a descriptor is not currently available then the
+ * invoking thread is queued until one is freed.
+ *
+ * @param[in] macp pointer to the @p MACDriver object
+ * @param[out] tdp pointer to a @p MACTransmitDescriptor structure
+ * @param[in] time the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_IMMEDIATE immediate timeout.
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation status.
+ * @retval RDY_OK the descriptor was obtained.
+ * @retval RDY_TIMEOUT the operation timed out, descriptor not initialized.
+ */
+msg_t macWaitTransmitDescriptor(MACDriver *macp,
+ MACTransmitDescriptor *tdp,
+ systime_t time) {
+ msg_t msg;
+
+ while (((msg = max_lld_get_transmit_descriptor(macp, tdp)) != RDY_OK) &&
+ (time > 0)) {
+ chSysLock();
+ systime_t now = chTimeNow();
+ if ((msg = chSemWaitTimeoutS(&macp->md_tdsem, time)) == RDY_TIMEOUT)
+ break;
+ if (time != TIME_INFINITE)
+ time -= (chTimeNow() - now);
+ chSysUnlock();
+ }
+ return msg;
+}
+
+/**
+ * @brief Releases a transmit descriptor and starts the transmission of the
+ * enqueued data as a single frame.
+ *
+ * @param[in] tdp the pointer to the @p MACTransmitDescriptor structure
+ */
+void macReleaseTransmitDescriptor(MACTransmitDescriptor *tdp) {
+
+ mac_lld_release_transmit_descriptor(tdp);
+}
+
+/**
+ * @brief Waits for a received frame.
+ * @details Stops until a frame is received and buffered. If a frame is
+ * not immediately available then the invoking thread is queued
+ * until one is received.
+ *
+ * @param[in] macp pointer to the @p MACDriver object
+ * @param[out] rdp pointer to a @p MACReceiveDescriptor structure
+ * @param[in] time the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_IMMEDIATE immediate timeout.
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation status.
+ * @retval RDY_OK the descriptor was obtained.
+ * @retval RDY_TIMEOUT the operation timed out, descriptor not initialized.
+ */
+msg_t macWaitReceiveDescriptor(MACDriver *macp,
+ MACReceiveDescriptor *rdp,
+ systime_t time) {
+ msg_t msg;
+
+ while (((msg = max_lld_get_receive_descriptor(macp, rdp)) != RDY_OK) &&
+ (time > 0)) {
+ chSysLock();
+ systime_t now = chTimeNow();
+ if ((msg = chSemWaitTimeoutS(&macp->md_rdsem, time)) == RDY_TIMEOUT)
+ break;
+ if (time != TIME_INFINITE)
+ time -= (chTimeNow() - now);
+ chSysUnlock();
+ }
+ return msg;
+}
+
+/**
+ * @brief Releases a receive descriptor.
+ * @details The descriptor and its buffer are made available for more incoming
+ * frames.
+ *
+ * @param[in] rdp the pointer to the @p MACReceiveDescriptor structure
+ */
+void macReleaseReceiveDescriptor(MACReceiveDescriptor *rdp) {
+
+ mac_lld_release_receive_descriptor(rdp);
+}
+
+/**
+ * @brief Updates and returns the link status.
+ *
+ * @param[in] macp pointer to the @p MACDriver object
+ * @return The link status.
+ * @retval TRUE if the link is active.
+ * @retval FALSE if the link is down.
+ */
+bool_t macPollLinkStatus(MACDriver *macp) {
+
+ return mac_lld_poll_link_status(macp);
+}
+
+#endif /* CH_HAL_USE_MAC */
+
+/** @} */
diff --git a/os/hal/src/mii.c b/os/hal/src/mii.c
new file mode 100644
index 000000000..4618ecbbb
--- /dev/null
+++ b/os/hal/src/mii.c
@@ -0,0 +1,37 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mii.c
+ * @brief mii Driver code.
+ * @addtogroup MII
+ * @{
+ */
+
+#include "ch.h"
+#include "mac.h"
+#include "mii.h"
+
+/*
+ * Currently there is no code, everything is done in the header, you may
+ * omit this file from the project but this may change in future releases.
+ * The file is here because the driver's naming pattern.
+ */
+
+/** @} */
diff --git a/os/hal/src/mmc_spi.c b/os/hal/src/mmc_spi.c
new file mode 100644
index 000000000..7846da353
--- /dev/null
+++ b/os/hal/src/mmc_spi.c
@@ -0,0 +1,575 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mmc_spi.c
+ * @brief MMC over SPI driver code.
+ * @addtogroup MMC_SPI
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_MMC_SPI
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+void tmrfunc(void *p) {
+ MMCDriver *mmcp = p;
+
+ if (mmcp->mmc_cnt > 0) {
+ if (mmcp->mmc_is_inserted()) {
+ if (--mmcp->mmc_cnt == 0) {
+ mmcp->mmc_state = MMC_INSERTED;
+ chEvtBroadcastI(&mmcp->mmc_inserted_event);
+ }
+ }
+ else
+ mmcp->mmc_cnt = MMC_POLLING_INTERVAL;
+ }
+ else {
+ if (!mmcp->mmc_is_inserted()) {
+ mmcp->mmc_state = MMC_WAIT;
+ mmcp->mmc_cnt = MMC_POLLING_INTERVAL;
+ chEvtBroadcastI(&mmcp->mmc_removed_event);
+ }
+ }
+ chVTSetI(&mmcp->mmc_vt, MS2ST(MMC_POLLING_DELAY), tmrfunc, mmcp);
+}
+
+/**
+ * @brief Waits an idle condition.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ */
+static void wait(MMCDriver *mmcp) {
+ int i;
+ uint8_t buf[4];
+
+ for (i = 0; i < 16; i++) {
+ spiReceive(mmcp->mmc_spip, 1, buf);
+ if (buf[0] == 0xFF)
+ break;
+ }
+ /* Looks like it is a long wait.*/
+ while (TRUE) {
+ spiReceive(mmcp->mmc_spip, 1, buf);
+ if (buf[0] == 0xFF)
+ break;
+#ifdef MMC_NICE_WAITING
+ /* Trying to be nice with the other threads.*/
+ chThdSleep(1);
+#endif
+ }
+}
+
+/**
+ * @brief Sends a command header.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param cmd[in] the command id
+ * @param arg[in] the command argument
+ */
+static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
+ uint8_t buf[6];
+
+ /* Wait for the bus to become idle if a write operation was in progress. */
+ wait(mmcp);
+
+ buf[0] = 0x40 | cmd;
+ buf[1] = arg >> 24;
+ buf[2] = arg >> 16;
+ buf[3] = arg >> 8;
+ buf[4] = arg;
+ buf[5] = 0x95; /* Valid for CMD0 ignored by other commands. */
+ spiSend(mmcp->mmc_spip, 6, buf);
+}
+
+/**
+ * @brief Receives a single byte response.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ *
+ * @return The response as an @p uint8_t value.
+ * @retval 0xFF timed out.
+ */
+static uint8_t recvr1(MMCDriver *mmcp) {
+ int i;
+ uint8_t r1[1];
+
+ for (i = 0; i < 9; i++) {
+ spiReceive(mmcp->mmc_spip, 1, r1);
+ if (r1[0] != 0xFF)
+ return r1[0];
+ }
+ return 0xFF;
+}
+
+/**
+ * @brief Sends a command an returns a single byte response.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param cmd[in] the command id
+ * @param arg[in] the command argument
+ *
+ * @return The response as an @p uint8_t value.
+ * @retval 0xFF timed out.
+ */
+static uint8_t send_command(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
+ uint8_t r1;
+
+ spiSelect(mmcp->mmc_spip);
+ send_hdr(mmcp, cmd, arg);
+ r1 = recvr1(mmcp);
+ spiUnselect(mmcp->mmc_spip);
+ return r1;
+}
+
+/**
+ * @brief Waits that the card reaches an idle state.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ */
+static void sync(MMCDriver *mmcp) {
+ uint8_t buf[1];
+
+ spiSelect(mmcp->mmc_spip);
+ while (TRUE) {
+ spiReceive(mmcp->mmc_spip, 1, buf);
+ if (buf[0] == 0xFF)
+ break;
+#ifdef MMC_NICE_WAITING
+ chThdSleep(1); /* Trying to be nice with the other threads.*/
+#endif
+ }
+ spiUnselect(mmcp->mmc_spip);
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief MMC over SPI driver initialization.
+ */
+void mmcInit(void) {
+
+}
+
+/**
+ * @brief Initializes an instance.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[in] spip pointer to the SPI driver to be used as interface
+ * @param[in] lscfg low speed configuration for the SPI driver
+ * @param[in] hscfg high speed configuration for the SPI driver
+ * @param[in] is_protected function that returns the card write protection
+ * setting
+ * @param[in] is_inserted function that returns the card insertion sensor
+ * status
+ */
+void mmcObjectInit(MMCDriver *mmcp, SPIDriver *spip,
+ const SPIConfig *lscfg, const SPIConfig *hscfg,
+ mmcquery_t is_protected, mmcquery_t is_inserted) {
+
+ mmcp->mmc_state = MMC_STOP;
+ mmcp->mmc_config = NULL;
+ mmcp->mmc_spip = spip;
+ mmcp->mmc_lscfg = lscfg;
+ mmcp->mmc_hscfg = hscfg;
+ mmcp->mmc_is_protected = is_protected;
+ mmcp->mmc_is_inserted = is_inserted;
+ chEvtInit(&mmcp->mmc_inserted_event);
+ chEvtInit(&mmcp->mmc_removed_event);
+}
+
+/**
+ * @brief Configures and activates the MMC peripheral.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[in] config pointer to the @p MMCConfig object
+ */
+void mmcStart(MMCDriver *mmcp, const MMCConfig *config) {
+
+ chDbgCheck((mmcp != NULL) && (config != NULL), "mmcStart");
+
+ chSysLock();
+ chDbgAssert(mmcp->mmc_state == MMC_STOP, "mmcStart(), #1", "invalid state");
+ mmcp->mmc_config = config;
+ mmcp->mmc_state = MMC_WAIT;
+ mmcp->mmc_cnt = MMC_POLLING_INTERVAL;
+ chVTSetI(&mmcp->mmc_vt, MS2ST(MMC_POLLING_DELAY), tmrfunc, mmcp);
+ chSysUnlock();
+}
+
+/**
+ * @brief Disables the MMC peripheral.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ */
+void mmcStop(MMCDriver *mmcp) {
+
+ chDbgCheck(mmcp != NULL, "mmcStop");
+
+ chSysLock();
+ chDbgAssert((mmcp->mmc_state != MMC_UNINIT) &&
+ (mmcp->mmc_state != MMC_READING) &&
+ (mmcp->mmc_state != MMC_WRITING),
+ "mmcStop(), #1",
+ "invalid state");
+ if (mmcp->mmc_state != MMC_STOP) {
+ mmcp->mmc_state = MMC_STOP;
+ chVTResetI(&mmcp->mmc_vt);
+ }
+ chSysUnlock();
+ spiStop(mmcp->mmc_spip);
+}
+
+/**
+ * @brief Performs the initialization procedure on the inserted card.
+ * @details This function should be invoked when a card is inserted and
+ * brings the driver in the @p MMC_READY state where it is possible
+ * to perform read and write operations.
+ * @note It is possible to invoke this function from the insertion event
+ * handler.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful and the driver is now
+ * in the @p MMC_READY state.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcConnect(MMCDriver *mmcp) {
+ unsigned i;
+ bool_t result;
+
+ chDbgCheck(mmcp != NULL, "mmcConnect");
+
+ chDbgAssert((mmcp->mmc_state != MMC_UNINIT) &&
+ (mmcp->mmc_state != MMC_STOP),
+ "mmcConnect(), #1",
+ "invalid state");
+
+ if (mmcp->mmc_state == MMC_INSERTED) {
+ /* Slow clock mode and 128 clock pulses.*/
+ spiStart(mmcp->mmc_spip, mmcp->mmc_lscfg);
+ spiIgnore(mmcp->mmc_spip, 16);
+
+ /* SPI mode selection.*/
+ i = 0;
+ while (TRUE) {
+ if (send_command(mmcp, MMC_CMDGOIDLE, 0) == 0x01)
+ break;
+ if (++i >= MMC_CMD0_RETRY)
+ return TRUE;
+ chThdSleepMilliseconds(10);
+ }
+
+ /* Initialization. */
+ i = 0;
+ while (TRUE) {
+ uint8_t b = send_command(mmcp, MMC_CMDINIT, 0);
+ if (b == 0x00)
+ break;
+ if (b != 0x01)
+ return TRUE;
+ if (++i >= MMC_CMD1_RETRY)
+ return TRUE;
+ chThdSleepMilliseconds(10);
+ }
+
+ /* Initialization complete, full speed. */
+ spiStart(mmcp->mmc_spip, mmcp->mmc_hscfg);
+
+ /* Setting block size.*/
+ if (send_command(mmcp, MMC_CMDSETBLOCKLEN, MMC_SECTOR_SIZE) != 0x00)
+ return TRUE;
+
+ /* Transition to MMC_READY state (if not extracted).*/
+ chSysLock();
+ if (mmcp->mmc_state == MMC_INSERTED) {
+ mmcp->mmc_state = MMC_READY;
+ result = FALSE;
+ }
+ else
+ result = TRUE;
+ chSysUnlock();
+ return result;
+ }
+ if (mmcp->mmc_state == MMC_READY)
+ return FALSE;
+ /* Any other state is invalid.*/
+ return TRUE;
+}
+
+/**
+ * @brief Brings the driver in a state safe for card removal.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @return The operation status.
+ * @retval FALSE the operation was successful and the driver is now
+ * in the @p MMC_INSERTED state.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcDisconnect(MMCDriver *mmcp) {
+
+ chDbgCheck(mmcp != NULL, "mmcConnect");
+
+ chDbgAssert((mmcp->mmc_state != MMC_UNINIT) &&
+ (mmcp->mmc_state != MMC_STOP),
+ "mmcDisconnect(), #1",
+ "invalid state");
+ switch (mmcp->mmc_state) {
+ case MMC_READY:
+ /* Wait for the pending write operations to complete.*/
+ sync(mmcp);
+ chSysLock();
+ if (mmcp->mmc_state == MMC_READY)
+ mmcp->mmc_state = MMC_INSERTED;
+ chSysUnlock();
+ case MMC_INSERTED:
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+/**
+ * @brief Starts a sequential read.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[in] startblk first block to read
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
+
+ chDbgCheck(mmcp != NULL, "mmcStartSequentialRead");
+
+ chSysLock();
+ if (mmcp->mmc_state != MMC_READY) {
+ chSysUnlock();
+ return TRUE;
+ }
+ mmcp->mmc_state = MMC_READING;
+ chSysUnlock();
+
+ spiSelect(mmcp->mmc_spip);
+ send_hdr(mmcp, MMC_CMDREADMULTIPLE, startblk * MMC_SECTOR_SIZE);
+ if (recvr1(mmcp) != 0x00) {
+ spiUnselect(mmcp->mmc_spip);
+ chSysLock();
+ if (mmcp->mmc_state == MMC_READING)
+ mmcp->mmc_state = MMC_READY;
+ chSysUnlock();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * @brief Reads a block within a sequential read operation.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[out] buffer pointer to the read buffer
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
+ int i;
+
+ chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead");
+
+ chSysLock();
+ if (mmcp->mmc_state != MMC_READING) {
+ chSysUnlock();
+ return TRUE;
+ }
+ chSysUnlock();
+
+ for (i = 0; i < MMC_WAIT_DATA; i++) {
+ spiReceive(mmcp->mmc_spip, 1, buffer);
+ if (buffer[0] == 0xFE) {
+ spiReceive(mmcp->mmc_spip, MMC_SECTOR_SIZE, buffer);
+ /* CRC ignored. */
+ spiIgnore(mmcp->mmc_spip, 2);
+ return FALSE;
+ }
+ }
+ /* Timeout.*/
+ spiUnselect(mmcp->mmc_spip);
+ chSysLock();
+ if (mmcp->mmc_state == MMC_READING)
+ mmcp->mmc_state = MMC_READY;
+ chSysUnlock();
+ return TRUE;
+}
+
+/**
+ * @brief Stops a sequential read gracefully.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcStopSequentialRead(MMCDriver *mmcp) {
+ static const uint8_t stopcmd[] = {0x40 | MMC_CMDSTOP, 0, 0, 0, 0, 1, 0xFF};
+ bool_t result;
+
+ chDbgCheck(mmcp != NULL, "mmcStopSequentialRead");
+
+ chSysLock();
+ if (mmcp->mmc_state != MMC_READING) {
+ chSysUnlock();
+ return TRUE;
+ }
+ chSysUnlock();
+
+ spiSend(mmcp->mmc_spip, sizeof(stopcmd), stopcmd);
+ result = recvr1(mmcp) != 0x00;
+ spiUnselect(mmcp->mmc_spip);
+
+ chSysLock();
+ if (mmcp->mmc_state == MMC_READING)
+ mmcp->mmc_state = MMC_READY;
+ chSysUnlock();
+ return result;
+}
+
+/**
+ * @brief Starts a sequential write.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[in] startblk first block to write
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
+
+ chDbgCheck(mmcp != NULL, "mmcStartSequentialWrite");
+
+ chSysLock();
+ if (mmcp->mmc_state != MMC_READY) {
+ chSysUnlock();
+ return TRUE;
+ }
+ mmcp->mmc_state = MMC_WRITING;
+ chSysUnlock();
+
+ spiSelect(mmcp->mmc_spip);
+ send_hdr(mmcp, MMC_CMDWRITEMULTIPLE, startblk * MMC_SECTOR_SIZE);
+ if (recvr1(mmcp) != 0x00) {
+ spiUnselect(mmcp->mmc_spip);
+ chSysLock();
+ if (mmcp->mmc_state == MMC_WRITING)
+ mmcp->mmc_state = MMC_READY;
+ chSysUnlock();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * @brief Writes a block within a sequential write operation.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ * @param[out] buffer pointer to the write buffer
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) {
+ static const uint8_t start[] = {0xFF, 0xFC};
+ uint8_t b[1];
+
+ chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead");
+
+ chSysLock();
+ if (mmcp->mmc_state != MMC_WRITING) {
+ chSysUnlock();
+ return TRUE;
+ }
+ chSysUnlock();
+
+ spiSend(mmcp->mmc_spip, sizeof(start), start); /* Data prologue. */
+ spiSend(mmcp->mmc_spip, MMC_SECTOR_SIZE, buffer); /* Data. */
+ spiIgnore(mmcp->mmc_spip, 2); /* CRC ignored. */
+ spiReceive(mmcp->mmc_spip, 1, b);
+ if ((b[0] & 0x1F) == 0x05)
+ return FALSE;
+
+ /* Error.*/
+ spiUnselect(mmcp->mmc_spip);
+ chSysLock();
+ if (mmcp->mmc_state == MMC_WRITING)
+ mmcp->mmc_state = MMC_READY;
+ chSysUnlock();
+ return TRUE;
+}
+
+/**
+ * @brief Stops a sequential write gracefully.
+ *
+ * @param[in] mmcp pointer to the @p MMCDriver object
+ *
+ * @return The operation status.
+ * @retval FALSE the operation was successful.
+ * @retval TRUE the operation failed.
+ */
+bool_t mmcStopSequentialWrite(MMCDriver *mmcp) {
+ static const uint8_t stop[] = {0xFD, 0xFF};
+
+ chDbgCheck(mmcp != NULL, "mmcStopSequentialWrite");
+
+ chSysLock();
+ if (mmcp->mmc_state != MMC_WRITING) {
+ chSysUnlock();
+ return TRUE;
+ }
+ chSysUnlock();
+
+ spiSend(mmcp->mmc_spip, sizeof(stop), stop);
+ spiUnselect(mmcp->mmc_spip);
+
+ chSysLock();
+ if (mmcp->mmc_state == MMC_WRITING) {
+ mmcp->mmc_state = MMC_READY;
+ chSysUnlock();
+ return FALSE;
+ }
+ chSysUnlock();
+ return TRUE;
+}
+
+#endif /* CH_HAL_USE_MMC_SPI */
+
+/** @} */
diff --git a/os/hal/src/pal.c b/os/hal/src/pal.c
new file mode 100644
index 000000000..1f9d058d5
--- /dev/null
+++ b/os/hal/src/pal.c
@@ -0,0 +1,98 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file pal.c
+ * @brief I/O Ports Abstraction Layer code
+ * @addtogroup PAL
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_PAL
+
+/**
+ * @brief Read from an I/O bus.
+ *
+ * @param[in] bus the I/O bus, pointer to a @p IOBus structure
+ * @return The bus logical states.
+ *
+ * @note The operation is not guaranteed to be atomic on all the architectures,
+ * for atomicity and/or portability reasons you may need to enclose port
+ * I/O operations between @p chSysLock() and @p chSysUnlock().
+ * @note The function internally uses the @p palReadGroup() macro. The use of
+ * this function is preferred when you value code size, readability and
+ * error checking over speed.
+ */
+ioportmask_t palReadBus(IOBus *bus) {
+
+ chDbgCheck((bus != NULL) &&
+ (bus->bus_offset > PAL_IOPORTS_WIDTH), "palReadBus");
+
+ return palReadGroup(bus->bus_portid, bus->bus_mask, bus->bus_offset);
+}
+
+/**
+ * @brief Write to an I/O bus.
+ *
+ * @param[in] bus the I/O bus, pointer to a @p IOBus structure
+ * @param[in] bits the bits to be written on the I/O bus. Values exceeding
+ * the bus width are masked so most significant bits are lost.
+ *
+ * @note The operation is not guaranteed to be atomic on all the architectures,
+ * for atomicity and/or portability reasons you may need to enclose port
+ * I/O operations between @p chSysLock() and @p chSysUnlock().
+ * @note The default implementation is non atomic and not necessarily
+ * optimal. Low level drivers may optimize the function by using
+ * specific hardware or coding.
+ */
+void palWriteBus(IOBus *bus, ioportmask_t bits) {
+
+ chDbgCheck((bus != NULL) &&
+ (bus->bus_offset > PAL_IOPORTS_WIDTH), "palWriteBus");
+
+ palWriteGroup(bus->bus_portid, bus->bus_mask, bus->bus_offset, bits);
+}
+
+/**
+ * @brief Programs a bus with the specified mode.
+ *
+ * @param[in] bus the I/O bus, pointer to a @p IOBus structure
+ * @param[in] mode the mode
+ *
+ * @note The operation is not guaranteed to be atomic on all the architectures,
+ * for atomicity and/or portability reasons you may need to enclose port
+ * I/O operations between @p chSysLock() and @p chSysUnlock().
+ * @note The default implementation is non atomic and not necessarily
+ * optimal. Low level drivers may optimize the function by using
+ * specific hardware or coding.
+ */
+void palSetBusMode(IOBus *bus, uint_fast8_t mode) {
+
+ chDbgCheck((bus != NULL) &&
+ (bus->bus_offset > PAL_IOPORTS_WIDTH), "palSetBusMode");
+
+ palSetGroupMode(bus->bus_portid, bus->bus_mask, mode);
+}
+
+#endif /* CH_HAL_USE_PAL */
+
+/** @} */
diff --git a/os/hal/src/serial.c b/os/hal/src/serial.c
new file mode 100644
index 000000000..2944a98ab
--- /dev/null
+++ b/os/hal/src/serial.c
@@ -0,0 +1,201 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file serial.c
+ * @brief Serial Driver code.
+ * @addtogroup SERIAL
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_SERIAL
+
+/*
+ * Interface implementation, the following functions just invoke the equivalent
+ * queue-level function or macro.
+ */
+static bool_t putwouldblock(void *ip) {
+
+ return chOQIsFull(&((SerialDriver *)ip)->d2.oqueue);
+}
+
+static bool_t getwouldblock(void *ip) {
+
+ return chIQIsEmpty(&((SerialDriver *)ip)->d2.iqueue);
+}
+
+static msg_t put(void *ip, uint8_t b, systime_t timeout) {
+
+ return chOQPutTimeout(&((SerialDriver *)ip)->d2.oqueue, b, timeout);
+}
+
+static msg_t get(void *ip, systime_t timeout) {
+
+ return chIQGetTimeout(&((SerialDriver *)ip)->d2.iqueue, timeout);
+}
+
+static size_t write(void *ip, uint8_t *buffer, size_t n) {
+
+ return chOQWrite(&((SerialDriver *)ip)->d2.oqueue, buffer, n);
+}
+
+static size_t read(void *ip, uint8_t *buffer, size_t n) {
+
+ return chIQRead(&((SerialDriver *)ip)->d2.iqueue, buffer, n);
+}
+
+static const struct SerialDriverVMT vmt = {
+ {putwouldblock, getwouldblock, put, get},
+ {write, read},
+ {}
+};
+
+/**
+ * @brief Serial Driver initialization.
+ */
+void sdInit(void) {
+
+ sd_lld_init();
+}
+
+/**
+ * @brief Initializes a generic full duplex driver object.
+ * @details The HW dependent part of the initialization has to be performed
+ * outside, usually in the hardware initialization code.
+ *
+ * @param[out] sdp pointer to a @p SerialDriver structure
+ * @param[in] inotify pointer to a callback function that is invoked when
+ * some data is read from the Queue. The value can be
+ * @p NULL.
+ * @param[in] onotify pointer to a callback function that is invoked when
+ * some data is written in the Queue. The value can be
+ * @p NULL.
+ */
+void sdObjectInit(SerialDriver *sdp, qnotify_t inotify, qnotify_t onotify) {
+
+ sdp->vmt = &vmt;
+ chEvtInit(&sdp->d1.ievent);
+ chEvtInit(&sdp->d1.oevent);
+ chEvtInit(&sdp->d2.sevent);
+ sdp->d2.flags = SD_NO_ERROR;
+ chIQInit(&sdp->d2.iqueue, sdp->d2.ib, SERIAL_BUFFERS_SIZE, inotify);
+ chOQInit(&sdp->d2.oqueue, sdp->d2.ob, SERIAL_BUFFERS_SIZE, onotify);
+}
+
+/**
+ * @brief Configures and starts the driver.
+ *
+ * @param[in] sdp pointer to a @p SerialDriver object
+ * @param[in] config the architecture-dependent serial driver configuration.
+ * If this parameter is set to @p NULL then a default
+ * configuration is used.
+ */
+void sdStart(SerialDriver *sdp, const SerialDriverConfig *config) {
+
+ chSysLock();
+ sd_lld_start(sdp, config);
+ chSysUnlock();
+}
+
+/**
+ * @brief Stops the driver.
+ * @details Any thread waiting on the driver's queues will be awakened with
+ * the message @p Q_RESET.
+ *
+ * @param[in] sdp pointer to a @p SerialDrive object
+ */
+void sdStop(SerialDriver *sdp) {
+
+ chSysLock();
+ sd_lld_stop(sdp);
+ chOQResetI(&sdp->d2.oqueue);
+ chIQResetI(&sdp->d2.iqueue);
+ chSchRescheduleS();
+ chSysUnlock();
+}
+
+/**
+ * @brief Handles incoming data.
+ * @details This function must be called from the input interrupt service
+ * routine in order to enqueue incoming data and generate the
+ * related events.
+ * @param[in] sd pointer to a @p SerialDriver structure
+ * @param[in] b the byte to be written in the driver's Input Queue
+ */
+void sdIncomingDataI(SerialDriver *sd, uint8_t b) {
+
+ if (chIQPutI(&sd->d2.iqueue, b) < Q_OK)
+ sdAddFlagsI(sd, SD_OVERRUN_ERROR);
+ else
+ chEvtBroadcastI(&sd->d1.ievent);
+}
+
+/**
+ * @brief Handles outgoing data.
+ * @details Must be called from the output interrupt service routine in order
+ * to get the next byte to be transmitted.
+ *
+ * @param[in] sd pointer to a @p SerialDriver structure
+ * @return The byte value read from the driver's output queue.
+ * @retval Q_EMPTY if the queue is empty (the lower driver usually disables
+ * the interrupt source when this happens).
+ */
+msg_t sdRequestDataI(SerialDriver *sd) {
+
+ msg_t b = chOQGetI(&sd->d2.oqueue);
+ if (b < Q_OK)
+ chEvtBroadcastI(&sd->d1.oevent);
+ return b;
+}
+
+/**
+ * @brief Handles communication events/errors.
+ * @details Must be called from the I/O interrupt service routine in order to
+ * notify I/O conditions as errors, signals change etc.
+ *
+ * @param[in] sd pointer to a @p SerialDriver structure
+ * @param[in] mask condition flags to be added to the mask
+ */
+void sdAddFlagsI(SerialDriver *sd, sdflags_t mask) {
+
+ sd->d2.flags |= mask;
+ chEvtBroadcastI(&sd->d2.sevent);
+}
+
+/**
+ * @brief Returns and clears the errors mask associated to the driver.
+ *
+ * @param[in] sd pointer to a @p SerialDriver structure
+ * @return The condition flags modified since last time this function was
+ * invoked.
+ */
+sdflags_t sdGetAndClearFlags(SerialDriver *sd) {
+ sdflags_t mask;
+
+ mask = sd->d2.flags;
+ sd->d2.flags = SD_NO_ERROR;
+ return mask;
+}
+
+#endif /* CH_HAL_USE_SERIAL */
+
+/** @} */
diff --git a/os/hal/src/spi.c b/os/hal/src/spi.c
new file mode 100644
index 000000000..8b8ea6f32
--- /dev/null
+++ b/os/hal/src/spi.c
@@ -0,0 +1,262 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file spi.c
+ * @brief SPI Driver code.
+ * @addtogroup SPI
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if CH_HAL_USE_SPI
+
+/**
+ * @brief SPI Driver initialization.
+ */
+void spiInit(void) {
+
+ spi_lld_init();
+}
+
+/**
+ * @brief Initializes the standard part of a @p SPIDriver structure.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ */
+void spiObjectInit(SPIDriver *spip) {
+
+ spip->spd_state = SPI_STOP;
+#if CH_USE_MUTEXES
+ chMtxInit(&spip->spd_mutex);
+#elif CH_USE_SEMAPHORES
+ chSemInit(&spip->spd_semaphore, 1);
+#endif
+ spip->spd_config = NULL;
+}
+
+/**
+ * @brief Configures and activates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] config pointer to the @p SPIConfig object
+ */
+void spiStart(SPIDriver *spip, const SPIConfig *config) {
+
+ chDbgCheck((spip != NULL) && (config != NULL), "spiStart");
+
+ chSysLock();
+ chDbgAssert((spip->spd_state == SPI_STOP) || (spip->spd_state == SPI_READY),
+ "spiStart(), #1",
+ "invalid state");
+ spip->spd_config = config;
+ spi_lld_start(spip);
+ spip->spd_state = SPI_READY;
+ chSysUnlock();
+}
+
+/**
+ * @brief Deactivates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ */
+void spiStop(SPIDriver *spip) {
+
+ chDbgCheck(spip != NULL, "spiStop");
+
+ chSysLock();
+ chDbgAssert((spip->spd_state == SPI_STOP) || (spip->spd_state == SPI_READY),
+ "spiStop(), #1",
+ "invalid state");
+ spi_lld_stop(spip);
+ spip->spd_state = SPI_STOP;
+ chSysUnlock();
+}
+
+/**
+ * @brief Asserts the slave select signal and prepares for transfers.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ */
+void spiSelect(SPIDriver *spip) {
+
+ chDbgCheck(spip != NULL, "spiSelect");
+
+ chSysLock();
+ chDbgAssert((spip->spd_state == SPI_READY) ||
+ (spip->spd_state == SPI_ACTIVE),
+ "spiSelect(), #1",
+ "not idle");
+ spi_lld_select(spip);
+ spip->spd_state = SPI_ACTIVE;
+ chSysUnlock();
+}
+
+/**
+ * @brief Deasserts the slave select signal.
+ * @details The previously selected peripheral is unselected.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ */
+void spiUnselect(SPIDriver *spip) {
+
+ chDbgCheck(spip != NULL, "spiUnselect");
+
+ chSysLock();
+ chDbgAssert((spip->spd_state == SPI_READY) ||
+ (spip->spd_state == SPI_ACTIVE),
+ "spiUnselect(), #1",
+ "not locked");
+ spi_lld_unselect(spip);
+ spip->spd_state = SPI_READY;
+ chSysUnlock();
+}
+
+/**
+ * @brief Ignores data on the SPI bus.
+ * @details This function transmits a series of idle words on the SPI bus and
+ * ignores the received data. This function can be invoked even
+ * when a slave select signal has not been yet asserted.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be ignored
+ */
+void spiIgnore(SPIDriver *spip, size_t n) {
+
+ chDbgCheck((spip != NULL) && (n > 0), "spiIgnore");
+ chDbgAssert((spip->spd_state == SPI_READY) || (spip->spd_state == SPI_ACTIVE),
+ "spiIgnore(), #1",
+ "not active");
+
+ spi_lld_ignore(spip, n);
+}
+
+/**
+ * @brief Exchanges data on the SPI bus.
+ * @details This function performs a simultaneous transmit/receive operation.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to be exchanged
+ * @param[in] txbuf the pointer to the transmit buffer
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @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 spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf) {
+
+ 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, txbuf, rxbuf);
+}
+
+/**
+ * @brief Sends data ever the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to send
+ * @param[in] txbuf the pointer to the transmit buffer
+ *
+ * @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 spiSend(SPIDriver *spip, size_t n, const void *txbuf) {
+
+ chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL),
+ "spiSend");
+ chDbgAssert(spip->spd_state == SPI_ACTIVE,
+ "spiSend(), #1",
+ "not active");
+
+ spi_lld_send(spip, n, txbuf);
+}
+
+/**
+ * @brief Receives data from the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of words to receive
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @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 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");
+
+ spi_lld_receive(spip, n, rxbuf);
+}
+
+#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+/**
+ * @brief Gains exclusive access to the SPI bus.
+ * @details This function tries to gain ownership to the SPI bus, if the bus
+ * is already being used then the invoking thread is queued.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @note This function is only available when the @p SPI_USE_MUTUAL_EXCLUSION
+ * option is set to @p TRUE.
+ */
+void spiAcquireBus(SPIDriver *spip) {
+
+ chDbgCheck(spip != NULL, "spiAcquireBus");
+
+#if CH_USE_MUTEXES
+ chMtxLock(&spip->spd_mutex);
+#elif CH_USE_SEMAPHORES
+ chSemWait(&spip->spd_semaphore);
+#endif
+}
+
+/**
+ * @brief Releases exclusive access to the SPI bus.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @note This function is only available when the @p SPI_USE_MUTUAL_EXCLUSION
+ * option is set to @p TRUE.
+ */
+void spiReleaseBus(SPIDriver *spip) {
+
+ chDbgCheck(spip != NULL, "spiReleaseBus");
+
+#if CH_USE_MUTEXES
+ (void)spip;
+ chMtxUnlock();
+#elif CH_USE_SEMAPHORES
+ chSemSignal(&spip->spd_semaphore);
+#endif
+}
+#endif /*SPI_USE_MUTUAL_EXCLUSION */
+
+#endif /* CH_HAL_USE_SPI */
+
+/** @} */