From 16178e1c45a76544d34ce63db37932838353637c Mon Sep 17 00:00:00 2001 From: gdisirio Date: Sat, 28 Nov 2009 12:25:35 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1333 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/src/adc.c | 210 +++++++++++++++++++ os/hal/src/can.c | 224 ++++++++++++++++++++ os/hal/src/hal.c | 53 +++++ os/hal/src/mac.c | 178 ++++++++++++++++ os/hal/src/mii.c | 37 ++++ os/hal/src/mmc_spi.c | 575 +++++++++++++++++++++++++++++++++++++++++++++++++++ os/hal/src/pal.c | 98 +++++++++ os/hal/src/serial.c | 201 ++++++++++++++++++ os/hal/src/spi.c | 262 +++++++++++++++++++++++ 9 files changed, 1838 insertions(+) create mode 100644 os/hal/src/adc.c create mode 100644 os/hal/src/can.c create mode 100644 os/hal/src/hal.c create mode 100644 os/hal/src/mac.c create mode 100644 os/hal/src/mii.c create mode 100644 os/hal/src/mmc_spi.c create mode 100644 os/hal/src/pal.c create mode 100644 os/hal/src/serial.c create mode 100644 os/hal/src/spi.c (limited to 'os/hal/src') 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 . +*/ + +/** + * @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: + * - LINEAR, 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. + * - CIRCULAR, 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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 . +*/ + +/** + * @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 */ + +/** @} */ -- cgit v1.2.3