Add software
This commit is contained in:
350
Software/portapack-mayhem/firmware/chibios/os/hal/src/adc.c
Normal file
350
Software/portapack-mayhem/firmware/chibios/os/hal/src/adc.c
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file adc.c
|
||||
* @brief ADC Driver code.
|
||||
*
|
||||
* @addtogroup ADC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_ADC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief ADC Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void adcInit(void) {
|
||||
|
||||
adc_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p ADCDriver structure.
|
||||
*
|
||||
* @param[out] adcp pointer to the @p ADCDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void adcObjectInit(ADCDriver *adcp) {
|
||||
|
||||
adcp->state = ADC_STOP;
|
||||
adcp->config = NULL;
|
||||
adcp->samples = NULL;
|
||||
adcp->depth = 0;
|
||||
adcp->grpp = NULL;
|
||||
#if ADC_USE_WAIT
|
||||
adcp->thread = NULL;
|
||||
#endif /* ADC_USE_WAIT */
|
||||
#if ADC_USE_MUTUAL_EXCLUSION
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxInit(&adcp->mutex);
|
||||
#else
|
||||
chSemInit(&adcp->semaphore, 1);
|
||||
#endif
|
||||
#endif /* ADC_USE_MUTUAL_EXCLUSION */
|
||||
#if defined(ADC_DRIVER_EXT_INIT_HOOK)
|
||||
ADC_DRIVER_EXT_INIT_HOOK(adcp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @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. Depending on
|
||||
* the implementation the value can be @p NULL.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void adcStart(ADCDriver *adcp, const ADCConfig *config) {
|
||||
|
||||
chDbgCheck(adcp != NULL, "adcStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((adcp->state == ADC_STOP) || (adcp->state == ADC_READY),
|
||||
"adcStart(), #1", "invalid state");
|
||||
adcp->config = config;
|
||||
adc_lld_start(adcp);
|
||||
adcp->state = ADC_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the ADC peripheral.
|
||||
*
|
||||
* @param[in] adcp pointer to the @p ADCDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void adcStop(ADCDriver *adcp) {
|
||||
|
||||
chDbgCheck(adcp != NULL, "adcStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((adcp->state == ADC_STOP) || (adcp->state == ADC_READY),
|
||||
"adcStop(), #1", "invalid state");
|
||||
adc_lld_stop(adcp);
|
||||
adcp->state = ADC_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts an ADC conversion.
|
||||
* @details Starts an asynchronous conversion operation.
|
||||
* @note The buffer is organized as a matrix of M*N elements where M is the
|
||||
* channels number configured into the conversion group and N is the
|
||||
* buffer depth. The samples are sequentially written into the buffer
|
||||
* with no gaps.
|
||||
*
|
||||
* @param[in] 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.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void adcStartConversion(ADCDriver *adcp,
|
||||
const ADCConversionGroup *grpp,
|
||||
adcsample_t *samples,
|
||||
size_t depth) {
|
||||
|
||||
chSysLock();
|
||||
adcStartConversionI(adcp, grpp, samples, depth);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts an ADC conversion.
|
||||
* @details Starts an asynchronous conversion operation.
|
||||
* @post The callbacks associated to the conversion group will be invoked
|
||||
* on buffer fill and error events.
|
||||
* @note The buffer is organized as a matrix of M*N elements where M is the
|
||||
* channels number configured into the conversion group and N is the
|
||||
* buffer depth. The samples are sequentially written into the buffer
|
||||
* with no gaps.
|
||||
*
|
||||
* @param[in] 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.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void adcStartConversionI(ADCDriver *adcp,
|
||||
const ADCConversionGroup *grpp,
|
||||
adcsample_t *samples,
|
||||
size_t depth) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck((adcp != NULL) && (grpp != NULL) && (samples != NULL) &&
|
||||
((depth == 1) || ((depth & 1) == 0)),
|
||||
"adcStartConversionI");
|
||||
chDbgAssert((adcp->state == ADC_READY) ||
|
||||
(adcp->state == ADC_COMPLETE) ||
|
||||
(adcp->state == ADC_ERROR),
|
||||
"adcStartConversionI(), #1", "not ready");
|
||||
|
||||
adcp->samples = samples;
|
||||
adcp->depth = depth;
|
||||
adcp->grpp = grpp;
|
||||
adcp->state = ADC_ACTIVE;
|
||||
adc_lld_start_conversion(adcp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops an ongoing conversion.
|
||||
* @details This function stops the currently ongoing conversion and returns
|
||||
* the driver in the @p ADC_READY state. If there was no conversion
|
||||
* being processed then the function does nothing.
|
||||
*
|
||||
* @param[in] adcp pointer to the @p ADCDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void adcStopConversion(ADCDriver *adcp) {
|
||||
|
||||
chDbgCheck(adcp != NULL, "adcStopConversion");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((adcp->state == ADC_READY) ||
|
||||
(adcp->state == ADC_ACTIVE),
|
||||
"adcStopConversion(), #1", "invalid state");
|
||||
if (adcp->state != ADC_READY) {
|
||||
adc_lld_stop_conversion(adcp);
|
||||
adcp->grpp = NULL;
|
||||
adcp->state = ADC_READY;
|
||||
_adc_reset_s(adcp);
|
||||
}
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops an ongoing conversion.
|
||||
* @details This function stops the currently ongoing conversion and returns
|
||||
* the driver in the @p ADC_READY state. If there was no conversion
|
||||
* being processed then the function does nothing.
|
||||
*
|
||||
* @param[in] adcp pointer to the @p ADCDriver object
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void adcStopConversionI(ADCDriver *adcp) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(adcp != NULL, "adcStopConversionI");
|
||||
chDbgAssert((adcp->state == ADC_READY) ||
|
||||
(adcp->state == ADC_ACTIVE) ||
|
||||
(adcp->state == ADC_COMPLETE),
|
||||
"adcStopConversionI(), #1", "invalid state");
|
||||
|
||||
if (adcp->state != ADC_READY) {
|
||||
adc_lld_stop_conversion(adcp);
|
||||
adcp->grpp = NULL;
|
||||
adcp->state = ADC_READY;
|
||||
_adc_reset_i(adcp);
|
||||
}
|
||||
}
|
||||
|
||||
#if ADC_USE_WAIT || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Performs an ADC conversion.
|
||||
* @details Performs a synchronous conversion operation.
|
||||
* @note The buffer is organized as a matrix of M*N elements where M is the
|
||||
* channels number configured into the conversion group and N is the
|
||||
* buffer depth. The samples are sequentially written into the buffer
|
||||
* with no gaps.
|
||||
*
|
||||
* @param[in] 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.
|
||||
* @return The operation result.
|
||||
* @retval RDY_OK Conversion finished.
|
||||
* @retval RDY_RESET The conversion has been stopped using
|
||||
* @p acdStopConversion() or @p acdStopConversionI(),
|
||||
* the result buffer may contain incorrect data.
|
||||
* @retval RDY_TIMEOUT The conversion has been stopped because an hardware
|
||||
* error.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t adcConvert(ADCDriver *adcp,
|
||||
const ADCConversionGroup *grpp,
|
||||
adcsample_t *samples,
|
||||
size_t depth) {
|
||||
msg_t msg;
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(adcp->thread == NULL, "adcConvert(), #1", "already waiting");
|
||||
adcStartConversionI(adcp, grpp, samples, depth);
|
||||
adcp->thread = chThdSelf();
|
||||
chSchGoSleepS(THD_STATE_SUSPENDED);
|
||||
msg = chThdSelf()->p_u.rdymsg;
|
||||
chSysUnlock();
|
||||
return msg;
|
||||
}
|
||||
#endif /* ADC_USE_WAIT */
|
||||
|
||||
#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Gains exclusive access to the ADC peripheral.
|
||||
* @details This function tries to gain ownership to the ADC bus, if the bus
|
||||
* is already being used then the invoking thread is queued.
|
||||
* @pre In order to use this function the option
|
||||
* @p ADC_USE_MUTUAL_EXCLUSION must be enabled.
|
||||
*
|
||||
* @param[in] adcp pointer to the @p ADCDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void adcAcquireBus(ADCDriver *adcp) {
|
||||
|
||||
chDbgCheck(adcp != NULL, "adcAcquireBus");
|
||||
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxLock(&adcp->mutex);
|
||||
#elif CH_USE_SEMAPHORES
|
||||
chSemWait(&adcp->semaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases exclusive access to the ADC peripheral.
|
||||
* @pre In order to use this function the option
|
||||
* @p ADC_USE_MUTUAL_EXCLUSION must be enabled.
|
||||
*
|
||||
* @param[in] adcp pointer to the @p ADCDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void adcReleaseBus(ADCDriver *adcp) {
|
||||
|
||||
chDbgCheck(adcp != NULL, "adcReleaseBus");
|
||||
|
||||
#if CH_USE_MUTEXES
|
||||
(void)adcp;
|
||||
chMtxUnlock();
|
||||
#elif CH_USE_SEMAPHORES
|
||||
chSemSignal(&adcp->semaphore);
|
||||
#endif
|
||||
}
|
||||
#endif /* ADC_USE_MUTUAL_EXCLUSION */
|
||||
|
||||
#endif /* HAL_USE_ADC */
|
||||
|
||||
/** @} */
|
292
Software/portapack-mayhem/firmware/chibios/os/hal/src/can.c
Normal file
292
Software/portapack-mayhem/firmware/chibios/os/hal/src/can.c
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file can.c
|
||||
* @brief CAN Driver code.
|
||||
*
|
||||
* @addtogroup CAN
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_CAN || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief CAN Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void canInit(void) {
|
||||
|
||||
can_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p CANDriver structure.
|
||||
*
|
||||
* @param[out] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void canObjectInit(CANDriver *canp) {
|
||||
|
||||
canp->state = CAN_STOP;
|
||||
canp->config = NULL;
|
||||
chSemInit(&canp->txsem, 0);
|
||||
chSemInit(&canp->rxsem, 0);
|
||||
chEvtInit(&canp->rxfull_event);
|
||||
chEvtInit(&canp->txempty_event);
|
||||
chEvtInit(&canp->error_event);
|
||||
#if CAN_USE_SLEEP_MODE
|
||||
chEvtInit(&canp->sleep_event);
|
||||
chEvtInit(&canp->wakeup_event);
|
||||
#endif /* CAN_USE_SLEEP_MODE */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the CAN peripheral.
|
||||
* @note Activating the CAN bus can be a slow operation this this function
|
||||
* is not atomic, it waits internally for the initialization to
|
||||
* complete.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
* @param[in] config pointer to the @p CANConfig object. Depending on
|
||||
* the implementation the value can be @p NULL.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void canStart(CANDriver *canp, const CANConfig *config) {
|
||||
|
||||
chDbgCheck(canp != NULL, "canStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((canp->state == CAN_STOP) ||
|
||||
(canp->state == CAN_STARTING) ||
|
||||
(canp->state == CAN_READY),
|
||||
"canStart(), #1", "invalid state");
|
||||
while (canp->state == CAN_STARTING)
|
||||
chThdSleepS(1);
|
||||
if (canp->state == CAN_STOP) {
|
||||
canp->config = config;
|
||||
can_lld_start(canp);
|
||||
canp->state = CAN_READY;
|
||||
}
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the CAN peripheral.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void canStop(CANDriver *canp) {
|
||||
|
||||
chDbgCheck(canp != NULL, "canStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((canp->state == CAN_STOP) || (canp->state == CAN_READY),
|
||||
"canStop(), #1", "invalid state");
|
||||
can_lld_stop(canp);
|
||||
canp->state = CAN_STOP;
|
||||
chSemResetI(&canp->rxsem, 0);
|
||||
chSemResetI(&canp->txsem, 0);
|
||||
chSchRescheduleS();
|
||||
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] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
|
||||
* @param[in] ctfp 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 The operation has timed out.
|
||||
* @retval RDY_RESET The driver has been stopped while waiting.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t canTransmit(CANDriver *canp,
|
||||
canmbx_t mailbox,
|
||||
const CANTxFrame *ctfp,
|
||||
systime_t timeout) {
|
||||
|
||||
chDbgCheck((canp != NULL) && (ctfp != NULL) && (mailbox <= CAN_TX_MAILBOXES),
|
||||
"canTransmit");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
|
||||
"canTransmit(), #1", "invalid state");
|
||||
while ((canp->state == CAN_SLEEP) || !can_lld_is_tx_empty(canp, mailbox)) {
|
||||
msg_t msg = chSemWaitTimeoutS(&canp->txsem, timeout);
|
||||
if (msg != RDY_OK) {
|
||||
chSysUnlock();
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
can_lld_transmit(canp, mailbox, ctfp);
|
||||
chSysUnlock();
|
||||
return RDY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
|
||||
* @param[out] crfp 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 (useful in an
|
||||
* event driven scenario where a thread never blocks
|
||||
* for I/O).
|
||||
* - @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 The operation has timed out.
|
||||
* @retval RDY_RESET The driver has been stopped while waiting.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t canReceive(CANDriver *canp,
|
||||
canmbx_t mailbox,
|
||||
CANRxFrame *crfp,
|
||||
systime_t timeout) {
|
||||
|
||||
chDbgCheck((canp != NULL) && (crfp != NULL) && (mailbox < CAN_RX_MAILBOXES),
|
||||
"canReceive");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
|
||||
"canReceive(), #1", "invalid state");
|
||||
while ((canp->state == CAN_SLEEP) || !can_lld_is_rx_nonempty(canp, mailbox)) {
|
||||
msg_t msg = chSemWaitTimeoutS(&canp->rxsem, timeout);
|
||||
if (msg != RDY_OK) {
|
||||
chSysUnlock();
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
can_lld_receive(canp, mailbox, crfp);
|
||||
chSysUnlock();
|
||||
return RDY_OK;
|
||||
}
|
||||
|
||||
#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Enters the sleep mode.
|
||||
* @details This function puts the CAN driver in sleep mode and broadcasts
|
||||
* the @p sleep_event event source.
|
||||
* @pre In order to use this function the option @p CAN_USE_SLEEP_MODE must
|
||||
* be enabled and the @p CAN_SUPPORTS_SLEEP mode must be supported
|
||||
* by the low level driver.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void canSleep(CANDriver *canp) {
|
||||
|
||||
chDbgCheck(canp != NULL, "canSleep");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
|
||||
"canSleep(), #1", "invalid state");
|
||||
if (canp->state == CAN_READY) {
|
||||
can_lld_sleep(canp);
|
||||
canp->state = CAN_SLEEP;
|
||||
chEvtBroadcastI(&canp->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->state == CAN_READY) || (canp->state == CAN_SLEEP),
|
||||
"canWakeup(), #1", "invalid state");
|
||||
if (canp->state == CAN_SLEEP) {
|
||||
can_lld_wakeup(canp);
|
||||
canp->state = CAN_READY;
|
||||
chEvtBroadcastI(&canp->wakeup_event);
|
||||
chSchRescheduleS();
|
||||
}
|
||||
chSysUnlock();
|
||||
}
|
||||
#endif /* CAN_USE_SLEEP_MODE */
|
||||
|
||||
#endif /* HAL_USE_CAN */
|
||||
|
||||
/** @} */
|
214
Software/portapack-mayhem/firmware/chibios/os/hal/src/ext.c
Normal file
214
Software/portapack-mayhem/firmware/chibios/os/hal/src/ext.c
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ext.c
|
||||
* @brief EXT Driver code.
|
||||
*
|
||||
* @addtogroup EXT
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_EXT || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief EXT Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void extInit(void) {
|
||||
|
||||
ext_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p EXTDriver structure.
|
||||
*
|
||||
* @param[out] extp pointer to the @p EXTDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void extObjectInit(EXTDriver *extp) {
|
||||
|
||||
extp->state = EXT_STOP;
|
||||
extp->config = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the EXT peripheral.
|
||||
* @post After activation all EXT channels are in the disabled state,
|
||||
* use @p extChannelEnable() in order to activate them.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
* @param[in] config pointer to the @p EXTConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void extStart(EXTDriver *extp, const EXTConfig *config) {
|
||||
|
||||
chDbgCheck((extp != NULL) && (config != NULL), "extStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((extp->state == EXT_STOP) || (extp->state == EXT_ACTIVE),
|
||||
"extStart(), #1", "invalid state");
|
||||
extp->config = config;
|
||||
ext_lld_start(extp);
|
||||
extp->state = EXT_ACTIVE;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the EXT peripheral.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void extStop(EXTDriver *extp) {
|
||||
|
||||
chDbgCheck(extp != NULL, "extStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((extp->state == EXT_STOP) || (extp->state == EXT_ACTIVE),
|
||||
"extStop(), #1", "invalid state");
|
||||
ext_lld_stop(extp);
|
||||
extp->state = EXT_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables an EXT channel.
|
||||
* @pre The channel must not be in @p EXT_CH_MODE_DISABLED mode.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
* @param[in] channel channel to be enabled
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void extChannelEnable(EXTDriver *extp, expchannel_t channel) {
|
||||
|
||||
chDbgCheck((extp != NULL) && (channel < EXT_MAX_CHANNELS),
|
||||
"extChannelEnable");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((extp->state == EXT_ACTIVE) &&
|
||||
((extp->config->channels[channel].mode &
|
||||
EXT_CH_MODE_EDGES_MASK) != EXT_CH_MODE_DISABLED),
|
||||
"extChannelEnable(), #1", "invalid state");
|
||||
extChannelEnableI(extp, channel);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables an EXT channel.
|
||||
* @pre The channel must not be in @p EXT_CH_MODE_DISABLED mode.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
* @param[in] channel channel to be disabled
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void extChannelDisable(EXTDriver *extp, expchannel_t channel) {
|
||||
|
||||
chDbgCheck((extp != NULL) && (channel < EXT_MAX_CHANNELS),
|
||||
"extChannelDisable");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((extp->state == EXT_ACTIVE) &&
|
||||
((extp->config->channels[channel].mode &
|
||||
EXT_CH_MODE_EDGES_MASK) != EXT_CH_MODE_DISABLED),
|
||||
"extChannelDisable(), #1", "invalid state");
|
||||
extChannelDisableI(extp, channel);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Changes the operation mode of a channel.
|
||||
* @note This function attempts to write over the current configuration
|
||||
* structure that must have been not declared constant. This
|
||||
* violates the @p const qualifier in @p extStart() but it is
|
||||
* intentional.
|
||||
* @note This function cannot be used if the configuration structure is
|
||||
* declared @p const.
|
||||
* @note The effect of this function on constant configuration structures
|
||||
* is not defined.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
* @param[in] channel channel to be changed
|
||||
* @param[in] extcp new configuration for the channel
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void extSetChannelModeI(EXTDriver *extp,
|
||||
expchannel_t channel,
|
||||
const EXTChannelConfig *extcp) {
|
||||
EXTChannelConfig *oldcp;
|
||||
|
||||
chDbgCheck((extp != NULL) && (channel < EXT_MAX_CHANNELS) &&
|
||||
(extcp != NULL), "extSetChannelModeI");
|
||||
|
||||
chDbgAssert(extp->state == EXT_ACTIVE,
|
||||
"extSetChannelModeI(), #1", "invalid state");
|
||||
|
||||
/* Note that here the access is enforced as non-const, known access
|
||||
violation.*/
|
||||
oldcp = (EXTChannelConfig *)&extp->config->channels[channel];
|
||||
|
||||
/* Overwiting the old channels configuration then the channel is reconfigured
|
||||
by the low level driver.*/
|
||||
*oldcp = *extcp;
|
||||
ext_lld_channel_enable(extp, channel);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_EXT */
|
||||
|
||||
/** @} */
|
275
Software/portapack-mayhem/firmware/chibios/os/hal/src/gpt.c
Normal file
275
Software/portapack-mayhem/firmware/chibios/os/hal/src/gpt.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file gpt.c
|
||||
* @brief GPT Driver code.
|
||||
*
|
||||
* @addtogroup GPT
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_GPT || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief GPT Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void gptInit(void) {
|
||||
|
||||
gpt_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p GPTDriver structure.
|
||||
*
|
||||
* @param[out] gptp pointer to the @p GPTDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void gptObjectInit(GPTDriver *gptp) {
|
||||
|
||||
gptp->state = GPT_STOP;
|
||||
gptp->config = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the GPT peripheral.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
* @param[in] config pointer to the @p GPTConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStart(GPTDriver *gptp, const GPTConfig *config) {
|
||||
|
||||
chDbgCheck((gptp != NULL) && (config != NULL), "gptStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((gptp->state == GPT_STOP) || (gptp->state == GPT_READY),
|
||||
"gptStart(), #1", "invalid state");
|
||||
gptp->config = config;
|
||||
gpt_lld_start(gptp);
|
||||
gptp->state = GPT_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the GPT peripheral.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStop(GPTDriver *gptp) {
|
||||
|
||||
chDbgCheck(gptp != NULL, "gptStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((gptp->state == GPT_STOP) || (gptp->state == GPT_READY),
|
||||
"gptStop(), #1", "invalid state");
|
||||
gpt_lld_stop(gptp);
|
||||
gptp->state = GPT_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Changes the interval of GPT peripheral.
|
||||
* @details This function changes the interval of a running GPT unit.
|
||||
* @pre The GPT unit must have been activated using @p gptStart().
|
||||
* @pre The GPT unit must have been running in continuous mode using
|
||||
* @p gptStartContinuous().
|
||||
* @post The GPT unit interval is changed to the new value.
|
||||
*
|
||||
* @param[in] gptp pointer to a @p GPTDriver object
|
||||
* @param[in] interval new cycle time in timer ticks
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptChangeInterval(GPTDriver *gptp, gptcnt_t interval) {
|
||||
|
||||
chDbgCheck(gptp != NULL, "gptChangeInterval");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(gptp->state == GPT_CONTINUOUS,
|
||||
"gptChangeInterval(), #1", "invalid state");
|
||||
gptChangeIntervalI(gptp, interval);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the timer in continuous mode.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
* @param[in] interval period in ticks
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStartContinuous(GPTDriver *gptp, gptcnt_t interval) {
|
||||
|
||||
chSysLock();
|
||||
gptStartContinuousI(gptp, interval);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the timer in continuous mode.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
* @param[in] interval period in ticks
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void gptStartContinuousI(GPTDriver *gptp, gptcnt_t interval) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(gptp != NULL, "gptStartContinuousI");
|
||||
chDbgAssert(gptp->state == GPT_READY,
|
||||
"gptStartContinuousI(), #1", "invalid state");
|
||||
|
||||
gptp->state = GPT_CONTINUOUS;
|
||||
gpt_lld_start_timer(gptp, interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the timer in one shot mode.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
* @param[in] interval time interval in ticks
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStartOneShot(GPTDriver *gptp, gptcnt_t interval) {
|
||||
|
||||
chSysLock();
|
||||
gptStartOneShotI(gptp, interval);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the timer in one shot mode.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
* @param[in] interval time interval in ticks
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStartOneShotI(GPTDriver *gptp, gptcnt_t interval) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(gptp != NULL, "gptStartOneShotI");
|
||||
chDbgAssert(gptp->state == GPT_READY,
|
||||
"gptStartOneShotI(), #1", "invalid state");
|
||||
|
||||
gptp->state = GPT_ONESHOT;
|
||||
gpt_lld_start_timer(gptp, interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the timer.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStopTimer(GPTDriver *gptp) {
|
||||
|
||||
chSysLock();
|
||||
gptStopTimerI(gptp);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the timer.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptStopTimerI(GPTDriver *gptp) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(gptp != NULL, "gptStopTimerI");
|
||||
chDbgAssert((gptp->state == GPT_READY) || (gptp->state == GPT_CONTINUOUS) ||
|
||||
(gptp->state == GPT_ONESHOT),
|
||||
"gptStopTimerI(), #1", "invalid state");
|
||||
|
||||
gptp->state = GPT_READY;
|
||||
gpt_lld_stop_timer(gptp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the timer in one shot mode and waits for completion.
|
||||
* @details This function specifically polls the timer waiting for completion
|
||||
* in order to not have extra delays caused by interrupt servicing,
|
||||
* this function is only recommended for short delays.
|
||||
* @note The configured callback is not invoked when using this function.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
* @param[in] interval time interval in ticks
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void gptPolledDelay(GPTDriver *gptp, gptcnt_t interval) {
|
||||
|
||||
chDbgAssert(gptp->state == GPT_READY,
|
||||
"gptPolledDelay(), #1", "invalid state");
|
||||
|
||||
gptp->state = GPT_ONESHOT;
|
||||
gpt_lld_polled_delay(gptp, interval);
|
||||
gptp->state = GPT_READY;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_GPT */
|
||||
|
||||
/** @} */
|
201
Software/portapack-mayhem/firmware/chibios/os/hal/src/hal.c
Normal file
201
Software/portapack-mayhem/firmware/chibios/os/hal/src/hal.c
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file hal.c
|
||||
* @brief HAL subsystem code.
|
||||
*
|
||||
* @addtogroup HAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief HAL initialization.
|
||||
* @details This function invokes the low level initialization code then
|
||||
* initializes all the drivers enabled in the HAL. Finally the
|
||||
* board-specific initialization is performed by invoking
|
||||
* @p boardInit() (usually defined in @p board.c).
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void halInit(void) {
|
||||
|
||||
hal_lld_init();
|
||||
|
||||
#if HAL_USE_TM || defined(__DOXYGEN__)
|
||||
tmInit();
|
||||
#endif
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
palInit(&pal_default_config);
|
||||
#endif
|
||||
#if HAL_USE_ADC || defined(__DOXYGEN__)
|
||||
adcInit();
|
||||
#endif
|
||||
#if HAL_USE_CAN || defined(__DOXYGEN__)
|
||||
canInit();
|
||||
#endif
|
||||
#if HAL_USE_EXT || defined(__DOXYGEN__)
|
||||
extInit();
|
||||
#endif
|
||||
#if HAL_USE_GPT || defined(__DOXYGEN__)
|
||||
gptInit();
|
||||
#endif
|
||||
#if HAL_USE_I2C || defined(__DOXYGEN__)
|
||||
i2cInit();
|
||||
#endif
|
||||
#if HAL_USE_ICU || defined(__DOXYGEN__)
|
||||
icuInit();
|
||||
#endif
|
||||
#if HAL_USE_MAC || defined(__DOXYGEN__)
|
||||
macInit();
|
||||
#endif
|
||||
#if HAL_USE_PWM || defined(__DOXYGEN__)
|
||||
pwmInit();
|
||||
#endif
|
||||
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
|
||||
sdInit();
|
||||
#endif
|
||||
#if HAL_USE_SDC || defined(__DOXYGEN__)
|
||||
sdcInit();
|
||||
#endif
|
||||
#if HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
spiInit();
|
||||
#endif
|
||||
#if HAL_USE_UART || defined(__DOXYGEN__)
|
||||
uartInit();
|
||||
#endif
|
||||
#if HAL_USE_USB || defined(__DOXYGEN__)
|
||||
usbInit();
|
||||
#endif
|
||||
#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)
|
||||
mmcInit();
|
||||
#endif
|
||||
#if HAL_USE_SERIAL_USB || defined(__DOXYGEN__)
|
||||
sduInit();
|
||||
#endif
|
||||
#if HAL_USE_RTC || defined(__DOXYGEN__)
|
||||
rtcInit();
|
||||
#endif
|
||||
/* Board specific initialization.*/
|
||||
boardInit();
|
||||
}
|
||||
|
||||
#if HAL_IMPLEMENTS_COUNTERS || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Realtime window test.
|
||||
* @details This function verifies if the current realtime counter value
|
||||
* lies within the specified range or not. The test takes care
|
||||
* of the realtime counter wrapping to zero on overflow.
|
||||
* @note When start==end then the function returns always true because the
|
||||
* whole time range is specified.
|
||||
* @note This is an optional service that could not be implemented in
|
||||
* all HAL implementations.
|
||||
* @note This function can be called from any context.
|
||||
*
|
||||
* @par Example 1
|
||||
* Example of a guarded loop using the realtime counter. The loop implements
|
||||
* a timeout after one second.
|
||||
* @code
|
||||
* halrtcnt_t start = halGetCounterValue();
|
||||
* halrtcnt_t timeout = start + S2RTT(1);
|
||||
* while (my_condition) {
|
||||
* if (!halIsCounterWithin(start, timeout)
|
||||
* return TIMEOUT;
|
||||
* // Do something.
|
||||
* }
|
||||
* // Continue.
|
||||
* @endcode
|
||||
*
|
||||
* @par Example 2
|
||||
* Example of a loop that lasts exactly 50 microseconds.
|
||||
* @code
|
||||
* halrtcnt_t start = halGetCounterValue();
|
||||
* halrtcnt_t timeout = start + US2RTT(50);
|
||||
* while (halIsCounterWithin(start, timeout)) {
|
||||
* // Do something.
|
||||
* }
|
||||
* // Continue.
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] start the start of the time window (inclusive)
|
||||
* @param[in] end the end of the time window (non inclusive)
|
||||
* @retval TRUE current time within the specified time window.
|
||||
* @retval FALSE current time not within the specified time window.
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
bool_t halIsCounterWithin(halrtcnt_t start, halrtcnt_t end) {
|
||||
halrtcnt_t now = halGetCounterValue();
|
||||
|
||||
return end > start ? (now >= start) && (now < end) :
|
||||
(now >= start) || (now < end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Polled delay.
|
||||
* @note The real delays is always few cycles in excess of the specified
|
||||
* value.
|
||||
* @note This is an optional service that could not be implemented in
|
||||
* all HAL implementations.
|
||||
* @note This function can be called from any context.
|
||||
*
|
||||
* @param[in] ticks number of ticks
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void halPolledDelay(halrtcnt_t ticks) {
|
||||
halrtcnt_t start = halGetCounterValue();
|
||||
halrtcnt_t timeout = start + (ticks);
|
||||
while (halIsCounterWithin(start, timeout))
|
||||
;
|
||||
}
|
||||
#endif /* HAL_IMPLEMENTS_COUNTERS */
|
||||
|
||||
/** @} */
|
310
Software/portapack-mayhem/firmware/chibios/os/hal/src/i2c.c
Normal file
310
Software/portapack-mayhem/firmware/chibios/os/hal/src/i2c.c
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file i2c.c
|
||||
* @brief I2C Driver code.
|
||||
*
|
||||
* @addtogroup I2C
|
||||
* @{
|
||||
*/
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_I2C || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief I2C Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void i2cInit(void) {
|
||||
i2c_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p I2CDriver structure.
|
||||
*
|
||||
* @param[out] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void i2cObjectInit(I2CDriver *i2cp) {
|
||||
|
||||
i2cp->state = I2C_STOP;
|
||||
i2cp->config = NULL;
|
||||
|
||||
#if I2C_USE_MUTUAL_EXCLUSION
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxInit(&i2cp->mutex);
|
||||
#else
|
||||
chSemInit(&i2cp->semaphore, 1);
|
||||
#endif /* CH_USE_MUTEXES */
|
||||
#endif /* I2C_USE_MUTUAL_EXCLUSION */
|
||||
|
||||
#if defined(I2C_DRIVER_EXT_INIT_HOOK)
|
||||
I2C_DRIVER_EXT_INIT_HOOK(i2cp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the I2C peripheral.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] config pointer to the @p I2CConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void i2cStart(I2CDriver *i2cp, const I2CConfig *config) {
|
||||
|
||||
chDbgCheck((i2cp != NULL) && (config != NULL), "i2cStart");
|
||||
chDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) ||
|
||||
(i2cp->state == I2C_LOCKED),
|
||||
"i2cStart(), #1",
|
||||
"invalid state");
|
||||
|
||||
chSysLock();
|
||||
i2cp->config = config;
|
||||
i2c_lld_start(i2cp);
|
||||
i2cp->state = I2C_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the I2C peripheral.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void i2cStop(I2CDriver *i2cp) {
|
||||
|
||||
chDbgCheck(i2cp != NULL, "i2cStop");
|
||||
chDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) ||
|
||||
(i2cp->state == I2C_LOCKED),
|
||||
"i2cStop(), #1",
|
||||
"invalid state");
|
||||
|
||||
chSysLock();
|
||||
i2c_lld_stop(i2cp);
|
||||
i2cp->state = I2C_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the errors mask associated to the previous operation.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @return The errors mask.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
i2cflags_t i2cGetErrors(I2CDriver *i2cp) {
|
||||
|
||||
chDbgCheck(i2cp != NULL, "i2cGetErrors");
|
||||
|
||||
return i2c_lld_get_errors(i2cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data via the I2C bus.
|
||||
* @details Function designed to realize "read-through-write" transfer
|
||||
* paradigm. If you want transmit data without any further read,
|
||||
* than set @b rxbytes field to 0.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] addr slave device address (7 bits) without R/W bit
|
||||
* @param[in] txbuf pointer to transmit buffer
|
||||
* @param[in] txbytes number of bytes to be transmitted
|
||||
* @param[out] rxbuf pointer to receive buffer
|
||||
* @param[in] rxbytes number of bytes to be received, set it to 0 if
|
||||
* you want transmit only
|
||||
* @param[in] timeout the number of ticks before the operation timeouts,
|
||||
* the following special values are allowed:
|
||||
* - @a TIME_INFINITE no timeout.
|
||||
* .
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK if the function succeeded.
|
||||
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
|
||||
* be retrieved using @p i2cGetErrors().
|
||||
* @retval RDY_TIMEOUT if a timeout occurred before operation end.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t i2cMasterTransmitTimeout(I2CDriver *i2cp,
|
||||
i2caddr_t addr,
|
||||
const uint8_t *txbuf,
|
||||
size_t txbytes,
|
||||
uint8_t *rxbuf,
|
||||
size_t rxbytes,
|
||||
systime_t timeout) {
|
||||
msg_t rdymsg;
|
||||
|
||||
chDbgCheck((i2cp != NULL) && (addr != 0) &&
|
||||
(txbytes > 0) && (txbuf != NULL) &&
|
||||
((rxbytes == 0) || ((rxbytes > 0) && (rxbuf != NULL))) &&
|
||||
(timeout != TIME_IMMEDIATE),
|
||||
"i2cMasterTransmitTimeout");
|
||||
|
||||
chDbgAssert(i2cp->state == I2C_READY,
|
||||
"i2cMasterTransmitTimeout(), #1", "not ready");
|
||||
|
||||
chSysLock();
|
||||
i2cp->errors = I2CD_NO_ERROR;
|
||||
i2cp->state = I2C_ACTIVE_TX;
|
||||
rdymsg = i2c_lld_master_transmit_timeout(i2cp, addr, txbuf, txbytes,
|
||||
rxbuf, rxbytes, timeout);
|
||||
if (rdymsg == RDY_TIMEOUT)
|
||||
i2cp->state = I2C_LOCKED;
|
||||
else
|
||||
i2cp->state = I2C_READY;
|
||||
chSysUnlock();
|
||||
return rdymsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data from the I2C bus.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] addr slave device address (7 bits) without R/W bit
|
||||
* @param[out] rxbuf pointer to receive buffer
|
||||
* @param[in] rxbytes number of bytes to be received
|
||||
* @param[in] timeout the number of ticks before the operation timeouts,
|
||||
* the following special values are allowed:
|
||||
* - @a TIME_INFINITE no timeout.
|
||||
* .
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK if the function succeeded.
|
||||
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
|
||||
* be retrieved using @p i2cGetErrors().
|
||||
* @retval RDY_TIMEOUT if a timeout occurred before operation end.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t i2cMasterReceiveTimeout(I2CDriver *i2cp,
|
||||
i2caddr_t addr,
|
||||
uint8_t *rxbuf,
|
||||
size_t rxbytes,
|
||||
systime_t timeout){
|
||||
|
||||
msg_t rdymsg;
|
||||
|
||||
chDbgCheck((i2cp != NULL) && (addr != 0) &&
|
||||
(rxbytes > 0) && (rxbuf != NULL) &&
|
||||
(timeout != TIME_IMMEDIATE),
|
||||
"i2cMasterReceiveTimeout");
|
||||
|
||||
chDbgAssert(i2cp->state == I2C_READY,
|
||||
"i2cMasterReceive(), #1", "not ready");
|
||||
|
||||
chSysLock();
|
||||
i2cp->errors = I2CD_NO_ERROR;
|
||||
i2cp->state = I2C_ACTIVE_RX;
|
||||
rdymsg = i2c_lld_master_receive_timeout(i2cp, addr, rxbuf, rxbytes, timeout);
|
||||
if (rdymsg == RDY_TIMEOUT)
|
||||
i2cp->state = I2C_LOCKED;
|
||||
else
|
||||
i2cp->state = I2C_READY;
|
||||
chSysUnlock();
|
||||
return rdymsg;
|
||||
}
|
||||
|
||||
#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Gains exclusive access to the I2C bus.
|
||||
* @details This function tries to gain ownership to the I2C bus, if the bus
|
||||
* is already being used then the invoking thread is queued.
|
||||
* @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION
|
||||
* must be enabled.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void i2cAcquireBus(I2CDriver *i2cp) {
|
||||
|
||||
chDbgCheck(i2cp != NULL, "i2cAcquireBus");
|
||||
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxLock(&i2cp->mutex);
|
||||
#elif CH_USE_SEMAPHORES
|
||||
chSemWait(&i2cp->semaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases exclusive access to the I2C bus.
|
||||
* @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION
|
||||
* must be enabled.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void i2cReleaseBus(I2CDriver *i2cp) {
|
||||
|
||||
chDbgCheck(i2cp != NULL, "i2cReleaseBus");
|
||||
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxUnlock();
|
||||
#elif CH_USE_SEMAPHORES
|
||||
chSemSignal(&i2cp->semaphore);
|
||||
#endif
|
||||
}
|
||||
#endif /* I2C_USE_MUTUAL_EXCLUSION */
|
||||
|
||||
#endif /* HAL_USE_I2C */
|
||||
|
||||
/** @} */
|
166
Software/portapack-mayhem/firmware/chibios/os/hal/src/icu.c
Normal file
166
Software/portapack-mayhem/firmware/chibios/os/hal/src/icu.c
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file icu.c
|
||||
* @brief ICU Driver code.
|
||||
*
|
||||
* @addtogroup ICU
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_ICU || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief ICU Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void icuInit(void) {
|
||||
|
||||
icu_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p ICUDriver structure.
|
||||
*
|
||||
* @param[out] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void icuObjectInit(ICUDriver *icup) {
|
||||
|
||||
icup->state = ICU_STOP;
|
||||
icup->config = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the ICU peripheral.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
* @param[in] config pointer to the @p ICUConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void icuStart(ICUDriver *icup, const ICUConfig *config) {
|
||||
|
||||
chDbgCheck((icup != NULL) && (config != NULL), "icuStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((icup->state == ICU_STOP) || (icup->state == ICU_READY),
|
||||
"icuStart(), #1", "invalid state");
|
||||
icup->config = config;
|
||||
icu_lld_start(icup);
|
||||
icup->state = ICU_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the ICU peripheral.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void icuStop(ICUDriver *icup) {
|
||||
|
||||
chDbgCheck(icup != NULL, "icuStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((icup->state == ICU_STOP) || (icup->state == ICU_READY),
|
||||
"icuStop(), #1", "invalid state");
|
||||
icu_lld_stop(icup);
|
||||
icup->state = ICU_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables the input capture.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void icuEnable(ICUDriver *icup) {
|
||||
|
||||
chDbgCheck(icup != NULL, "icuEnable");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(icup->state == ICU_READY, "icuEnable(), #1", "invalid state");
|
||||
icu_lld_enable(icup);
|
||||
icup->state = ICU_WAITING;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables the input capture.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void icuDisable(ICUDriver *icup) {
|
||||
|
||||
chDbgCheck(icup != NULL, "icuDisable");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((icup->state == ICU_READY) || (icup->state == ICU_WAITING) ||
|
||||
(icup->state == ICU_ACTIVE) || (icup->state == ICU_IDLE),
|
||||
"icuDisable(), #1", "invalid state");
|
||||
icu_lld_disable(icup);
|
||||
icup->state = ICU_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_ICU */
|
||||
|
||||
/** @} */
|
279
Software/portapack-mayhem/firmware/chibios/os/hal/src/mac.c
Normal file
279
Software/portapack-mayhem/firmware/chibios/os/hal/src/mac.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mac.c
|
||||
* @brief MAC Driver code.
|
||||
*
|
||||
* @addtogroup MAC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_MAC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if MAC_USE_ZERO_COPY && !MAC_SUPPORTS_ZERO_COPY
|
||||
#error "MAC_USE_ZERO_COPY not supported by this implementation"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief MAC Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void macInit(void) {
|
||||
|
||||
mac_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the standard part of a @p MACDriver structure.
|
||||
*
|
||||
* @param[out] macp pointer to the @p MACDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void macObjectInit(MACDriver *macp) {
|
||||
|
||||
macp->state = MAC_STOP;
|
||||
macp->config = NULL;
|
||||
chSemInit(&macp->tdsem, 0);
|
||||
chSemInit(&macp->rdsem, 0);
|
||||
#if MAC_USE_EVENTS
|
||||
chEvtInit(&macp->rdevent);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the MAC peripheral.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
* @param[in] config pointer to the @p MACConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void macStart(MACDriver *macp, const MACConfig *config) {
|
||||
|
||||
chDbgCheck((macp != NULL) && (config != NULL), "macStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(macp->state == MAC_STOP,
|
||||
"macStart(), #1", "invalid state");
|
||||
macp->config = config;
|
||||
mac_lld_start(macp);
|
||||
macp->state = MAC_ACTIVE;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the MAC peripheral.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void macStop(MACDriver *macp) {
|
||||
|
||||
chDbgCheck(macp != NULL, "macStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((macp->state == MAC_STOP) || (macp->state == MAC_ACTIVE),
|
||||
"macStop(), #1", "invalid state");
|
||||
mac_lld_stop(macp);
|
||||
macp->state = MAC_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t macWaitTransmitDescriptor(MACDriver *macp,
|
||||
MACTransmitDescriptor *tdp,
|
||||
systime_t time) {
|
||||
msg_t msg;
|
||||
systime_t now;
|
||||
|
||||
chDbgCheck((macp != NULL) && (tdp != NULL), "macWaitTransmitDescriptor");
|
||||
chDbgAssert(macp->state == MAC_ACTIVE, "macWaitTransmitDescriptor(), #1",
|
||||
"not active");
|
||||
|
||||
while (((msg = mac_lld_get_transmit_descriptor(macp, tdp)) != RDY_OK) &&
|
||||
(time > 0)) {
|
||||
chSysLock();
|
||||
now = chTimeNow();
|
||||
if ((msg = chSemWaitTimeoutS(&macp->tdsem, time)) == RDY_TIMEOUT) {
|
||||
chSysUnlock();
|
||||
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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void macReleaseTransmitDescriptor(MACTransmitDescriptor *tdp) {
|
||||
|
||||
chDbgCheck((tdp != NULL), "macReleaseTransmitDescriptor");
|
||||
|
||||
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.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
msg_t macWaitReceiveDescriptor(MACDriver *macp,
|
||||
MACReceiveDescriptor *rdp,
|
||||
systime_t time) {
|
||||
msg_t msg;
|
||||
systime_t now;
|
||||
|
||||
chDbgCheck((macp != NULL) && (rdp != NULL), "macWaitReceiveDescriptor");
|
||||
chDbgAssert(macp->state == MAC_ACTIVE, "macWaitReceiveDescriptor(), #1",
|
||||
"not active");
|
||||
|
||||
while (((msg = mac_lld_get_receive_descriptor(macp, rdp)) != RDY_OK) &&
|
||||
(time > 0)) {
|
||||
chSysLock();
|
||||
now = chTimeNow();
|
||||
if ((msg = chSemWaitTimeoutS(&macp->rdsem, time)) == RDY_TIMEOUT) {
|
||||
chSysUnlock();
|
||||
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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void macReleaseReceiveDescriptor(MACReceiveDescriptor *rdp) {
|
||||
|
||||
chDbgCheck((rdp != NULL), "macReleaseReceiveDescriptor");
|
||||
|
||||
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.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t macPollLinkStatus(MACDriver *macp) {
|
||||
|
||||
chDbgCheck((macp != NULL), "macPollLinkStatus");
|
||||
chDbgAssert(macp->state == MAC_ACTIVE, "macPollLinkStatus(), #1",
|
||||
"not active");
|
||||
|
||||
return mac_lld_poll_link_status(macp);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_MAC */
|
||||
|
||||
/** @} */
|
886
Software/portapack-mayhem/firmware/chibios/os/hal/src/mmc_spi.c
Normal file
886
Software/portapack-mayhem/firmware/chibios/os/hal/src/mmc_spi.c
Normal file
@ -0,0 +1,886 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
/*
|
||||
Parts of this file have been contributed by Matthias Blaicher.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mmc_spi.c
|
||||
* @brief MMC over SPI driver code.
|
||||
*
|
||||
* @addtogroup MMC_SPI
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/* Forward declarations required by mmc_vmt.*/
|
||||
static bool_t mmc_read(void *instance, uint32_t startblk,
|
||||
uint8_t *buffer, uint32_t n);
|
||||
static bool_t mmc_write(void *instance, uint32_t startblk,
|
||||
const uint8_t *buffer, uint32_t n);
|
||||
|
||||
/**
|
||||
* @brief Virtual methods table.
|
||||
*/
|
||||
static const struct MMCDriverVMT mmc_vmt = {
|
||||
(bool_t (*)(void *))mmc_lld_is_card_inserted,
|
||||
(bool_t (*)(void *))mmc_lld_is_write_protected,
|
||||
(bool_t (*)(void *))mmcConnect,
|
||||
(bool_t (*)(void *))mmcDisconnect,
|
||||
mmc_read,
|
||||
mmc_write,
|
||||
(bool_t (*)(void *))mmcSync,
|
||||
(bool_t (*)(void *, BlockDeviceInfo *))mmcGetInfo
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
|
||||
*/
|
||||
static const uint8_t crc7_lookup_table[256] = {
|
||||
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53,
|
||||
0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
|
||||
0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29,
|
||||
0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
|
||||
0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78,
|
||||
0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
|
||||
0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66,
|
||||
0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
|
||||
0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05,
|
||||
0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
|
||||
0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a,
|
||||
0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
|
||||
0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b,
|
||||
0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
|
||||
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71,
|
||||
0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
|
||||
0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76,
|
||||
0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
|
||||
0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c,
|
||||
0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
|
||||
0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d,
|
||||
0x62, 0x6b, 0x70, 0x79
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static bool_t mmc_read(void *instance, uint32_t startblk,
|
||||
uint8_t *buffer, uint32_t n) {
|
||||
|
||||
if (mmcStartSequentialRead((MMCDriver *)instance, startblk))
|
||||
return CH_FAILED;
|
||||
while (n > 0) {
|
||||
if (mmcSequentialRead((MMCDriver *)instance, buffer))
|
||||
return CH_FAILED;
|
||||
buffer += MMCSD_BLOCK_SIZE;
|
||||
n--;
|
||||
}
|
||||
if (mmcStopSequentialRead((MMCDriver *)instance))
|
||||
return CH_FAILED;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
static bool_t mmc_write(void *instance, uint32_t startblk,
|
||||
const uint8_t *buffer, uint32_t n) {
|
||||
|
||||
if (mmcStartSequentialWrite((MMCDriver *)instance, startblk))
|
||||
return CH_FAILED;
|
||||
while (n > 0) {
|
||||
if (mmcSequentialWrite((MMCDriver *)instance, buffer))
|
||||
return CH_FAILED;
|
||||
buffer += MMCSD_BLOCK_SIZE;
|
||||
n--;
|
||||
}
|
||||
if (mmcStopSequentialWrite((MMCDriver *)instance))
|
||||
return CH_FAILED;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate the MMC standard CRC-7 based on a lookup table.
|
||||
*
|
||||
* @param[in] crc start value for CRC
|
||||
* @param[in] buffer pointer to data buffer
|
||||
* @param[in] len length of data
|
||||
* @return Calculated CRC
|
||||
*/
|
||||
static uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len) {
|
||||
|
||||
while (len--)
|
||||
crc = crc7_lookup_table[(crc << 1) ^ (*buffer++)];
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits an idle condition.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void wait(MMCDriver *mmcp) {
|
||||
int i;
|
||||
uint8_t buf[4];
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
spiReceive(mmcp->config->spip, 1, buf);
|
||||
if (buf[0] == 0xFF)
|
||||
return;
|
||||
}
|
||||
/* Looks like it is a long wait.*/
|
||||
while (TRUE) {
|
||||
spiReceive(mmcp->config->spip, 1, buf);
|
||||
if (buf[0] == 0xFF)
|
||||
break;
|
||||
#if 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[in] cmd the command id
|
||||
* @param[in] arg the command argument
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
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;
|
||||
/* Calculate CRC for command header, shift to right position, add stop bit.*/
|
||||
buf[5] = ((crc7(0, buf, 5) & 0x7F) << 1) | 0x01;
|
||||
|
||||
spiSend(mmcp->config->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.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static uint8_t recvr1(MMCDriver *mmcp) {
|
||||
int i;
|
||||
uint8_t r1[1];
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
spiReceive(mmcp->config->spip, 1, r1);
|
||||
if (r1[0] != 0xFF)
|
||||
return r1[0];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives a three byte response.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
* @param[out] buffer pointer to four bytes wide buffer
|
||||
* @return First response byte as an @p uint8_t value.
|
||||
* @retval 0xFF timed out.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static uint8_t recvr3(MMCDriver *mmcp, uint8_t* buffer) {
|
||||
uint8_t r1;
|
||||
|
||||
r1 = recvr1(mmcp);
|
||||
spiReceive(mmcp->config->spip, 4, buffer);
|
||||
|
||||
return r1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a command an returns a single byte response.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
* @param[in] cmd the command id
|
||||
* @param[in] arg the command argument
|
||||
* @return The response as an @p uint8_t value.
|
||||
* @retval 0xFF timed out.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static uint8_t send_command_R1(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
|
||||
uint8_t r1;
|
||||
|
||||
spiSelect(mmcp->config->spip);
|
||||
send_hdr(mmcp, cmd, arg);
|
||||
r1 = recvr1(mmcp);
|
||||
spiUnselect(mmcp->config->spip);
|
||||
return r1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a command which returns a five bytes response (R3).
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
* @param[in] cmd the command id
|
||||
* @param[in] arg the command argument
|
||||
* @param[out] response pointer to four bytes wide uint8_t buffer
|
||||
* @return The first byte of the response (R1) as an @p
|
||||
* uint8_t value.
|
||||
* @retval 0xFF timed out.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static uint8_t send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg,
|
||||
uint8_t *response) {
|
||||
uint8_t r1;
|
||||
|
||||
spiSelect(mmcp->config->spip);
|
||||
send_hdr(mmcp, cmd, arg);
|
||||
r1 = recvr3(mmcp, response);
|
||||
spiUnselect(mmcp->config->spip);
|
||||
return r1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the CSD.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
* @param[out] csd pointer to the CSD buffer
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static bool_t read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4]) {
|
||||
unsigned i;
|
||||
uint8_t *bp, buf[16];
|
||||
|
||||
spiSelect(mmcp->config->spip);
|
||||
send_hdr(mmcp, cmd, 0);
|
||||
if (recvr1(mmcp) != 0x00) {
|
||||
spiUnselect(mmcp->config->spip);
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/* Wait for data availability.*/
|
||||
for (i = 0; i < MMC_WAIT_DATA; i++) {
|
||||
spiReceive(mmcp->config->spip, 1, buf);
|
||||
if (buf[0] == 0xFE) {
|
||||
uint32_t *wp;
|
||||
|
||||
spiReceive(mmcp->config->spip, 16, buf);
|
||||
bp = buf;
|
||||
for (wp = &cxd[3]; wp >= cxd; wp--) {
|
||||
*wp = ((uint32_t)bp[0] << 24) | ((uint32_t)bp[1] << 16) |
|
||||
((uint32_t)bp[2] << 8) | (uint32_t)bp[3];
|
||||
bp += 4;
|
||||
}
|
||||
|
||||
/* CRC ignored then end of transaction. */
|
||||
spiIgnore(mmcp->config->spip, 2);
|
||||
spiUnselect(mmcp->config->spip);
|
||||
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
}
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits that the card reaches an idle state.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void sync(MMCDriver *mmcp) {
|
||||
uint8_t buf[1];
|
||||
|
||||
spiSelect(mmcp->config->spip);
|
||||
while (TRUE) {
|
||||
spiReceive(mmcp->config->spip, 1, buf);
|
||||
if (buf[0] == 0xFF)
|
||||
break;
|
||||
#if MMC_NICE_WAITING
|
||||
chThdSleep(1); /* Trying to be nice with the other threads.*/
|
||||
#endif
|
||||
}
|
||||
spiUnselect(mmcp->config->spip);
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief MMC over SPI driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void mmcInit(void) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes an instance.
|
||||
*
|
||||
* @param[out] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void mmcObjectInit(MMCDriver *mmcp) {
|
||||
|
||||
mmcp->vmt = &mmc_vmt;
|
||||
mmcp->state = BLK_STOP;
|
||||
mmcp->config = NULL;
|
||||
mmcp->block_addresses = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void mmcStart(MMCDriver *mmcp, const MMCConfig *config) {
|
||||
|
||||
chDbgCheck((mmcp != NULL) && (config != NULL), "mmcStart");
|
||||
chDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
|
||||
"mmcStart(), #1", "invalid state");
|
||||
|
||||
mmcp->config = config;
|
||||
mmcp->state = BLK_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables the MMC peripheral.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void mmcStop(MMCDriver *mmcp) {
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcStop");
|
||||
chDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
|
||||
"mmcStop(), #1", "invalid state");
|
||||
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_STOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CH_SUCCESS the operation succeeded and the driver is now
|
||||
* in the @p MMC_READY state.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcConnect(MMCDriver *mmcp) {
|
||||
unsigned i;
|
||||
uint8_t r3[4];
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcConnect");
|
||||
|
||||
chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
|
||||
"mmcConnect(), #1", "invalid state");
|
||||
|
||||
/* Connection procedure in progress.*/
|
||||
mmcp->state = BLK_CONNECTING;
|
||||
mmcp->block_addresses = FALSE;
|
||||
|
||||
/* Slow clock mode and 128 clock pulses.*/
|
||||
spiStart(mmcp->config->spip, mmcp->config->lscfg);
|
||||
spiIgnore(mmcp->config->spip, 16);
|
||||
|
||||
/* SPI mode selection.*/
|
||||
i = 0;
|
||||
while (TRUE) {
|
||||
if (send_command_R1(mmcp, MMCSD_CMD_GO_IDLE_STATE, 0) == 0x01)
|
||||
break;
|
||||
if (++i >= MMC_CMD0_RETRY)
|
||||
goto failed;
|
||||
chThdSleepMilliseconds(10);
|
||||
}
|
||||
|
||||
/* Try to detect if this is a high capacity card and switch to block
|
||||
addresses if possible.
|
||||
This method is based on "How to support SDC Ver2 and high capacity cards"
|
||||
by ElmChan.*/
|
||||
if (send_command_R3(mmcp, MMCSD_CMD_SEND_IF_COND,
|
||||
MMCSD_CMD8_PATTERN, r3) != 0x05) {
|
||||
|
||||
/* Switch to SDHC mode.*/
|
||||
i = 0;
|
||||
while (TRUE) {
|
||||
if ((send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0) == 0x01) &&
|
||||
(send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND,
|
||||
0x400001aa, r3) == 0x00))
|
||||
break;
|
||||
|
||||
if (++i >= MMC_ACMD41_RETRY)
|
||||
goto failed;
|
||||
chThdSleepMilliseconds(10);
|
||||
}
|
||||
|
||||
/* Execute dedicated read on OCR register */
|
||||
send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, r3);
|
||||
|
||||
/* Check if CCS is set in response. Card operates in block mode if set.*/
|
||||
if (r3[0] & 0x40)
|
||||
mmcp->block_addresses = TRUE;
|
||||
}
|
||||
|
||||
/* Initialization.*/
|
||||
i = 0;
|
||||
while (TRUE) {
|
||||
uint8_t b = send_command_R1(mmcp, MMCSD_CMD_INIT, 0);
|
||||
if (b == 0x00)
|
||||
break;
|
||||
if (b != 0x01)
|
||||
goto failed;
|
||||
if (++i >= MMC_CMD1_RETRY)
|
||||
goto failed;
|
||||
chThdSleepMilliseconds(10);
|
||||
}
|
||||
|
||||
/* Initialization complete, full speed.*/
|
||||
spiStart(mmcp->config->spip, mmcp->config->hscfg);
|
||||
|
||||
/* Setting block size.*/
|
||||
if (send_command_R1(mmcp, MMCSD_CMD_SET_BLOCKLEN,
|
||||
MMCSD_BLOCK_SIZE) != 0x00)
|
||||
goto failed;
|
||||
|
||||
/* Determine capacity.*/
|
||||
if (read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd))
|
||||
goto failed;
|
||||
mmcp->capacity = mmcsdGetCapacity(mmcp->csd);
|
||||
if (mmcp->capacity == 0)
|
||||
goto failed;
|
||||
|
||||
if (read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid))
|
||||
goto failed;
|
||||
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
|
||||
/* Connection failed, state reset to BLK_ACTIVE.*/
|
||||
failed:
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_ACTIVE;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CH_SUCCESS the operation succeeded and the driver is now
|
||||
* in the @p MMC_INSERTED state.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcDisconnect(MMCDriver *mmcp) {
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcDisconnect");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
|
||||
"mmcDisconnect(), #1", "invalid state");
|
||||
if (mmcp->state == BLK_ACTIVE) {
|
||||
chSysUnlock();
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
mmcp->state = BLK_DISCONNECTING;
|
||||
chSysUnlock();
|
||||
|
||||
/* Wait for the pending write operations to complete.*/
|
||||
spiStart(mmcp->config->spip, mmcp->config->hscfg);
|
||||
sync(mmcp);
|
||||
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_ACTIVE;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcStartSequentialRead");
|
||||
chDbgAssert(mmcp->state == BLK_READY,
|
||||
"mmcStartSequentialRead(), #1", "invalid state");
|
||||
|
||||
/* Read operation in progress.*/
|
||||
mmcp->state = BLK_READING;
|
||||
|
||||
/* (Re)starting the SPI in case it has been reprogrammed externally, it can
|
||||
happen if the SPI bus is shared among multiple peripherals.*/
|
||||
spiStart(mmcp->config->spip, mmcp->config->hscfg);
|
||||
spiSelect(mmcp->config->spip);
|
||||
|
||||
if (mmcp->block_addresses)
|
||||
send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk);
|
||||
else
|
||||
send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk * MMCSD_BLOCK_SIZE);
|
||||
|
||||
if (recvr1(mmcp) != 0x00) {
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_FAILED;
|
||||
}
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
|
||||
int i;
|
||||
|
||||
chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead");
|
||||
|
||||
if (mmcp->state != BLK_READING)
|
||||
return CH_FAILED;
|
||||
|
||||
for (i = 0; i < MMC_WAIT_DATA; i++) {
|
||||
spiReceive(mmcp->config->spip, 1, buffer);
|
||||
if (buffer[0] == 0xFE) {
|
||||
spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);
|
||||
/* CRC ignored. */
|
||||
spiIgnore(mmcp->config->spip, 2);
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
}
|
||||
/* Timeout.*/
|
||||
spiUnselect(mmcp->config->spip);
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops a sequential read gracefully.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcStopSequentialRead(MMCDriver *mmcp) {
|
||||
static const uint8_t stopcmd[] = {0x40 | MMCSD_CMD_STOP_TRANSMISSION,
|
||||
0, 0, 0, 0, 1, 0xFF};
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcStopSequentialRead");
|
||||
|
||||
if (mmcp->state != BLK_READING)
|
||||
return CH_FAILED;
|
||||
|
||||
spiSend(mmcp->config->spip, sizeof(stopcmd), stopcmd);
|
||||
/* result = recvr1(mmcp) != 0x00;*/
|
||||
/* Note, ignored r1 response, it can be not zero, unknown issue.*/
|
||||
(void) recvr1(mmcp);
|
||||
|
||||
/* Read operation finished.*/
|
||||
spiUnselect(mmcp->config->spip);
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcStartSequentialWrite");
|
||||
chDbgAssert(mmcp->state == BLK_READY,
|
||||
"mmcStartSequentialWrite(), #1", "invalid state");
|
||||
|
||||
/* Write operation in progress.*/
|
||||
mmcp->state = BLK_WRITING;
|
||||
|
||||
spiStart(mmcp->config->spip, mmcp->config->hscfg);
|
||||
spiSelect(mmcp->config->spip);
|
||||
if (mmcp->block_addresses)
|
||||
send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, startblk);
|
||||
else
|
||||
send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK,
|
||||
startblk * MMCSD_BLOCK_SIZE);
|
||||
|
||||
if (recvr1(mmcp) != 0x00) {
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_FAILED;
|
||||
}
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
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), "mmcSequentialWrite");
|
||||
|
||||
if (mmcp->state != BLK_WRITING)
|
||||
return CH_FAILED;
|
||||
|
||||
spiSend(mmcp->config->spip, sizeof(start), start); /* Data prologue. */
|
||||
spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data. */
|
||||
spiIgnore(mmcp->config->spip, 2); /* CRC ignored. */
|
||||
spiReceive(mmcp->config->spip, 1, b);
|
||||
if ((b[0] & 0x1F) == 0x05) {
|
||||
wait(mmcp);
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/* Error.*/
|
||||
spiUnselect(mmcp->config->spip);
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops a sequential write gracefully.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcStopSequentialWrite(MMCDriver *mmcp) {
|
||||
static const uint8_t stop[] = {0xFD, 0xFF};
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcStopSequentialWrite");
|
||||
|
||||
if (mmcp->state != BLK_WRITING)
|
||||
return CH_FAILED;
|
||||
|
||||
spiSend(mmcp->config->spip, sizeof(stop), stop);
|
||||
spiUnselect(mmcp->config->spip);
|
||||
|
||||
/* Write operation finished.*/
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for card idle condition.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcSync(MMCDriver *mmcp) {
|
||||
|
||||
chDbgCheck(mmcp != NULL, "mmcSync");
|
||||
|
||||
if (mmcp->state != BLK_READY)
|
||||
return CH_FAILED;
|
||||
|
||||
/* Synchronization operation in progress.*/
|
||||
mmcp->state = BLK_SYNCING;
|
||||
|
||||
spiStart(mmcp->config->spip, mmcp->config->hscfg);
|
||||
sync(mmcp);
|
||||
|
||||
/* Synchronization operation finished.*/
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the media info.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
* @param[out] bdip pointer to a @p BlockDeviceInfo structure
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcGetInfo(MMCDriver *mmcp, BlockDeviceInfo *bdip) {
|
||||
|
||||
chDbgCheck((mmcp != NULL) && (bdip != NULL), "mmcGetInfo");
|
||||
|
||||
if (mmcp->state != BLK_READY)
|
||||
return CH_FAILED;
|
||||
|
||||
bdip->blk_num = mmcp->capacity;
|
||||
bdip->blk_size = MMCSD_BLOCK_SIZE;
|
||||
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases blocks.
|
||||
*
|
||||
* @param[in] mmcp pointer to the @p MMCDriver object
|
||||
* @param[in] startblk starting block number
|
||||
* @param[in] endblk ending block number
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk) {
|
||||
|
||||
chDbgCheck((mmcp != NULL), "mmcErase");
|
||||
|
||||
/* Erase operation in progress.*/
|
||||
mmcp->state = BLK_WRITING;
|
||||
|
||||
/* Handling command differences between HC and normal cards.*/
|
||||
if (!mmcp->block_addresses) {
|
||||
startblk *= MMCSD_BLOCK_SIZE;
|
||||
endblk *= MMCSD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_START, startblk))
|
||||
goto failed;
|
||||
|
||||
if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_END, endblk))
|
||||
goto failed;
|
||||
|
||||
if (send_command_R1(mmcp, MMCSD_CMD_ERASE, 0))
|
||||
goto failed;
|
||||
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
|
||||
/* Command failed, state reset to BLK_ACTIVE.*/
|
||||
failed:
|
||||
spiStop(mmcp->config->spip);
|
||||
mmcp->state = BLK_READY;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_MMC_SPI */
|
||||
|
||||
/** @} */
|
121
Software/portapack-mayhem/firmware/chibios/os/hal/src/mmcsd.c
Normal file
121
Software/portapack-mayhem/firmware/chibios/os/hal/src/mmcsd.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mmcsd.c
|
||||
* @brief MMC/SD cards common code.
|
||||
*
|
||||
* @addtogroup MMCSD
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_MMC_SPI || HAL_USE_SDC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Gets a bit field from a words array.
|
||||
* @note The bit zero is the LSb of the first word.
|
||||
*
|
||||
* @param[in] data pointer to the words array
|
||||
* @param[in] end bit offset of the last bit of the field, inclusive
|
||||
* @param[in] start bit offset of the first bit of the field, inclusive
|
||||
*
|
||||
* @return The bits field value, left aligned.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static uint32_t mmcsd_get_slice(uint32_t *data, uint32_t end, uint32_t start) {
|
||||
unsigned startidx, endidx, startoff;
|
||||
uint32_t endmask;
|
||||
|
||||
chDbgCheck((end >= start) && ((end - start) < 32), "mmcsd_get_slice");
|
||||
|
||||
startidx = start / 32;
|
||||
startoff = start % 32;
|
||||
endidx = end / 32;
|
||||
endmask = (1 << ((end % 32) + 1)) - 1;
|
||||
|
||||
/* One or two pieces?*/
|
||||
if (startidx < endidx)
|
||||
return (data[startidx] >> startoff) | /* Two pieces case. */
|
||||
((data[endidx] & endmask) << (32 - startoff));
|
||||
return (data[startidx] & endmask) >> startoff; /* One piece case. */
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Extract card capacity from a CSD.
|
||||
* @details The capacity is returned as number of available blocks.
|
||||
*
|
||||
* @param[in] csd the CSD record
|
||||
*
|
||||
* @return The card capacity.
|
||||
* @retval 0 CSD format error
|
||||
*/
|
||||
uint32_t mmcsdGetCapacity(uint32_t csd[4]) {
|
||||
|
||||
switch (csd[3] >> 30) {
|
||||
uint32_t a, b, c;
|
||||
case 0:
|
||||
/* CSD version 1.0 */
|
||||
a = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_SLICE);
|
||||
b = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_MULT_SLICE);
|
||||
c = mmcsd_get_slice(csd, MMCSD_CSD_10_READ_BL_LEN_SLICE);
|
||||
return (a + 1) << (b + 2) << (c - 9); /* 2^9 == MMCSD_BLOCK_SIZE. */
|
||||
case 1:
|
||||
/* CSD version 2.0.*/
|
||||
return 1024 * (mmcsd_get_slice(csd, MMCSD_CSD_20_C_SIZE_SLICE) + 1);
|
||||
default:
|
||||
/* Reserved value detected.*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_MMC_SPI || HAL_USE_SDC */
|
||||
|
||||
/** @} */
|
137
Software/portapack-mayhem/firmware/chibios/os/hal/src/pal.c
Normal file
137
Software/portapack-mayhem/firmware/chibios/os/hal/src/pal.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pal.c
|
||||
* @brief I/O Ports Abstraction Layer code.
|
||||
*
|
||||
* @addtogroup PAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Read from an I/O bus.
|
||||
* @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.
|
||||
* @note The function can be called from any context.
|
||||
*
|
||||
* @param[in] bus the I/O bus, pointer to a @p IOBus structure
|
||||
* @return The bus logical states.
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
ioportmask_t palReadBus(IOBus *bus) {
|
||||
|
||||
chDbgCheck((bus != NULL) && (bus->offset < PAL_IOPORTS_WIDTH),
|
||||
"palReadBus");
|
||||
|
||||
return palReadGroup(bus->portid, bus->mask, bus->offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write to an I/O bus.
|
||||
* @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.
|
||||
* @note The function can be called from any context.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void palWriteBus(IOBus *bus, ioportmask_t bits) {
|
||||
|
||||
chDbgCheck((bus != NULL) && (bus->offset < PAL_IOPORTS_WIDTH),
|
||||
"palWriteBus");
|
||||
|
||||
palWriteGroup(bus->portid, bus->mask, bus->offset, bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Programs a bus with the specified 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.
|
||||
* @note The function can be called from any context.
|
||||
*
|
||||
* @param[in] bus the I/O bus, pointer to a @p IOBus structure
|
||||
* @param[in] mode the mode
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void palSetBusMode(IOBus *bus, iomode_t mode) {
|
||||
|
||||
chDbgCheck((bus != NULL) && (bus->offset < PAL_IOPORTS_WIDTH),
|
||||
"palSetBusMode");
|
||||
|
||||
palSetGroupMode(bus->portid, bus->mask, bus->offset, mode);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_PAL */
|
||||
|
||||
/** @} */
|
214
Software/portapack-mayhem/firmware/chibios/os/hal/src/pwm.c
Normal file
214
Software/portapack-mayhem/firmware/chibios/os/hal/src/pwm.c
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pwm.c
|
||||
* @brief PWM Driver code.
|
||||
*
|
||||
* @addtogroup PWM
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_PWM || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief PWM Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void pwmInit(void) {
|
||||
|
||||
pwm_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p PWMDriver structure.
|
||||
*
|
||||
* @param[out] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void pwmObjectInit(PWMDriver *pwmp) {
|
||||
|
||||
pwmp->state = PWM_STOP;
|
||||
pwmp->config = NULL;
|
||||
#if defined(PWM_DRIVER_EXT_INIT_HOOK)
|
||||
PWM_DRIVER_EXT_INIT_HOOK(pwmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the PWM peripheral.
|
||||
* @note Starting a driver that is already in the @p PWM_READY state
|
||||
* disables all the active channels.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] config pointer to a @p PWMConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pwmStart(PWMDriver *pwmp, const PWMConfig *config) {
|
||||
|
||||
chDbgCheck((pwmp != NULL) && (config != NULL), "pwmStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((pwmp->state == PWM_STOP) || (pwmp->state == PWM_READY),
|
||||
"pwmStart(), #1", "invalid state");
|
||||
pwmp->config = config;
|
||||
pwmp->period = config->period;
|
||||
pwm_lld_start(pwmp);
|
||||
pwmp->state = PWM_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the PWM peripheral.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pwmStop(PWMDriver *pwmp) {
|
||||
|
||||
chDbgCheck(pwmp != NULL, "pwmStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((pwmp->state == PWM_STOP) || (pwmp->state == PWM_READY),
|
||||
"pwmStop(), #1", "invalid state");
|
||||
pwm_lld_stop(pwmp);
|
||||
pwmp->state = PWM_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Changes the period the PWM peripheral.
|
||||
* @details This function changes the period of a PWM unit that has already
|
||||
* been activated using @p pwmStart().
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The PWM unit period is changed to the new value.
|
||||
* @note If a period is specified that is shorter than the pulse width
|
||||
* programmed in one of the channels then the behavior is not
|
||||
* guaranteed.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] period new cycle time in ticks
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pwmChangePeriod(PWMDriver *pwmp, pwmcnt_t period) {
|
||||
|
||||
chDbgCheck(pwmp != NULL, "pwmChangePeriod");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(pwmp->state == PWM_READY,
|
||||
"pwmChangePeriod(), #1", "invalid state");
|
||||
pwmChangePeriodI(pwmp, period);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables a PWM channel.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The channel is active using the specified configuration.
|
||||
* @note Depending on the hardware implementation this function has
|
||||
* effect starting on the next cycle (recommended implementation)
|
||||
* or immediately (fallback implementation).
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
|
||||
* @param[in] width PWM pulse width as clock pulses number
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pwmEnableChannel(PWMDriver *pwmp,
|
||||
pwmchannel_t channel,
|
||||
pwmcnt_t width) {
|
||||
|
||||
chDbgCheck((pwmp != NULL) && (channel < PWM_CHANNELS),
|
||||
"pwmEnableChannel");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(pwmp->state == PWM_READY,
|
||||
"pwmEnableChannel(), #1", "not ready");
|
||||
pwm_lld_enable_channel(pwmp, channel, width);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables a PWM channel.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The channel is disabled and its output line returned to the
|
||||
* idle state.
|
||||
* @note Depending on the hardware implementation this function has
|
||||
* effect starting on the next cycle (recommended implementation)
|
||||
* or immediately (fallback implementation).
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pwmDisableChannel(PWMDriver *pwmp, pwmchannel_t channel) {
|
||||
|
||||
chDbgCheck((pwmp != NULL) && (channel < PWM_CHANNELS),
|
||||
"pwmEnableChannel");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(pwmp->state == PWM_READY,
|
||||
"pwmDisableChannel(), #1", "not ready");
|
||||
pwm_lld_disable_channel(pwmp, channel);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_PWM */
|
||||
|
||||
/** @} */
|
193
Software/portapack-mayhem/firmware/chibios/os/hal/src/rtc.c
Normal file
193
Software/portapack-mayhem/firmware/chibios/os/hal/src/rtc.c
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file rtc.c
|
||||
* @brief RTC Driver code.
|
||||
*
|
||||
* @addtogroup RTC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_RTC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief RTC Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void rtcInit(void) {
|
||||
|
||||
rtc_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set current time.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] timespec pointer to a @p RTCTime structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtcSetTime(RTCDriver *rtcp, const RTCTime *timespec) {
|
||||
|
||||
chDbgCheck((rtcp != NULL) && (timespec != NULL), "rtcSetTime");
|
||||
|
||||
chSysLock();
|
||||
rtcSetTimeI(rtcp, timespec);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current time.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[out] timespec pointer to a @p RTCTime structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtcGetTime(RTCDriver *rtcp, RTCTime *timespec) {
|
||||
|
||||
chDbgCheck((rtcp != NULL) && (timespec != NULL), "rtcGetTime");
|
||||
|
||||
chSysLock();
|
||||
rtcGetTimeI(rtcp, timespec);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
#if (RTC_ALARMS > 0) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Set alarm time.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] alarm alarm identifier
|
||||
* @param[in] alarmspec pointer to a @p RTCAlarm structure or @p NULL
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtcSetAlarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
const RTCAlarm *alarmspec) {
|
||||
|
||||
chDbgCheck((rtcp != NULL) && (alarm < RTC_ALARMS), "rtcSetAlarm");
|
||||
|
||||
chSysLock();
|
||||
rtcSetAlarmI(rtcp, alarm, alarmspec);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current alarm.
|
||||
* @note If an alarm has not been set then the returned alarm specification
|
||||
* is not meaningful.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] alarm alarm identifier
|
||||
* @param[out] alarmspec pointer to a @p RTCAlarm structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtcGetAlarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
RTCAlarm *alarmspec) {
|
||||
|
||||
chDbgCheck((rtcp != NULL) && (alarm < RTC_ALARMS) && (alarmspec != NULL),
|
||||
"rtcGetAlarm");
|
||||
|
||||
chSysLock();
|
||||
rtcGetAlarmI(rtcp, alarm, alarmspec);
|
||||
chSysUnlock();
|
||||
}
|
||||
#endif /* RTC_ALARMS > 0 */
|
||||
|
||||
#if RTC_SUPPORTS_CALLBACKS || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Enables or disables RTC callbacks.
|
||||
* @details This function enables or disables the callback, use a @p NULL
|
||||
* pointer in order to disable it.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] callback callback function pointer or @p NULL
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtcSetCallback(RTCDriver *rtcp, rtccb_t callback) {
|
||||
|
||||
chDbgCheck((rtcp != NULL), "rtcSetCallback");
|
||||
|
||||
chSysLock();
|
||||
rtcSetCallbackI(rtcp, callback);
|
||||
chSysUnlock();
|
||||
}
|
||||
#endif /* RTC_SUPPORTS_CALLBACKS */
|
||||
|
||||
/**
|
||||
* @brief Get current time in format suitable for usage in FatFS.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @return FAT time value.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
uint32_t rtcGetTimeFat(RTCDriver *rtcp) {
|
||||
|
||||
chDbgCheck((rtcp != NULL), "rtcSetTime");
|
||||
return rtc_lld_get_time_fat(rtcp);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_RTC */
|
||||
|
||||
/** @} */
|
587
Software/portapack-mayhem/firmware/chibios/os/hal/src/sdc.c
Normal file
587
Software/portapack-mayhem/firmware/chibios/os/hal/src/sdc.c
Normal file
@ -0,0 +1,587 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sdc.c
|
||||
* @brief SDC Driver code.
|
||||
*
|
||||
* @addtogroup SDC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SDC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Virtual methods table.
|
||||
*/
|
||||
static const struct SDCDriverVMT sdc_vmt = {
|
||||
(bool_t (*)(void *))sdc_lld_is_card_inserted,
|
||||
(bool_t (*)(void *))sdc_lld_is_write_protected,
|
||||
(bool_t (*)(void *))sdcConnect,
|
||||
(bool_t (*)(void *))sdcDisconnect,
|
||||
(bool_t (*)(void *, uint32_t, uint8_t *, uint32_t))sdcRead,
|
||||
(bool_t (*)(void *, uint32_t, const uint8_t *, uint32_t))sdcWrite,
|
||||
(bool_t (*)(void *))sdcSync,
|
||||
(bool_t (*)(void *, BlockDeviceInfo *))sdcGetInfo
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Wait for the card to complete pending operations.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t _sdc_wait_for_transfer_state(SDCDriver *sdcp) {
|
||||
uint32_t resp[1];
|
||||
|
||||
while (TRUE) {
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_STATUS,
|
||||
sdcp->rca, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
return CH_FAILED;
|
||||
switch (MMCSD_R1_STS(resp[0])) {
|
||||
case MMCSD_STS_TRAN:
|
||||
return CH_SUCCESS;
|
||||
case MMCSD_STS_DATA:
|
||||
case MMCSD_STS_RCV:
|
||||
case MMCSD_STS_PRG:
|
||||
#if SDC_NICE_WAITING
|
||||
chThdSleepMilliseconds(1);
|
||||
#endif
|
||||
continue;
|
||||
default:
|
||||
/* The card should have been initialized so any other state is not
|
||||
valid and is reported as an error.*/
|
||||
return CH_FAILED;
|
||||
}
|
||||
}
|
||||
/* If something going too wrong.*/
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief SDC Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void sdcInit(void) {
|
||||
|
||||
sdc_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p SDCDriver structure.
|
||||
*
|
||||
* @param[out] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void sdcObjectInit(SDCDriver *sdcp) {
|
||||
|
||||
sdcp->vmt = &sdc_vmt;
|
||||
sdcp->state = BLK_STOP;
|
||||
sdcp->errors = SDC_NO_ERROR;
|
||||
sdcp->config = NULL;
|
||||
sdcp->capacity = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the SDC peripheral.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] config pointer to the @p SDCConfig object, can be @p NULL if
|
||||
* the driver supports a default configuration or
|
||||
* requires no configuration
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void sdcStart(SDCDriver *sdcp, const SDCConfig *config) {
|
||||
|
||||
chDbgCheck(sdcp != NULL, "sdcStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE),
|
||||
"sdcStart(), #1", "invalid state");
|
||||
sdcp->config = config;
|
||||
sdc_lld_start(sdcp);
|
||||
sdcp->state = BLK_ACTIVE;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the SDC peripheral.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void sdcStop(SDCDriver *sdcp) {
|
||||
|
||||
chDbgCheck(sdcp != NULL, "sdcStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE),
|
||||
"sdcStop(), #1", "invalid state");
|
||||
sdc_lld_stop(sdcp);
|
||||
sdcp->state = BLK_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 BLK_READY state where it is possible
|
||||
* to perform read and write operations.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcConnect(SDCDriver *sdcp) {
|
||||
uint32_t resp[1];
|
||||
|
||||
chDbgCheck(sdcp != NULL, "sdcConnect");
|
||||
chDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY),
|
||||
"mmcConnect(), #1", "invalid state");
|
||||
|
||||
/* Connection procedure in progress.*/
|
||||
sdcp->state = BLK_CONNECTING;
|
||||
|
||||
/* Card clock initialization.*/
|
||||
sdc_lld_start_clk(sdcp);
|
||||
|
||||
/* Enforces the initial card state.*/
|
||||
sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0);
|
||||
|
||||
/* V2.0 cards detection.*/
|
||||
if (!sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_IF_COND,
|
||||
MMCSD_CMD8_PATTERN, resp)) {
|
||||
sdcp->cardmode = SDC_MODE_CARDTYPE_SDV20;
|
||||
/* Voltage verification.*/
|
||||
if (((resp[0] >> 8) & 0xF) != 1)
|
||||
goto failed;
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
}
|
||||
else {
|
||||
#if SDC_MMC_SUPPORT
|
||||
/* MMC or SD V1.1 detection.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
sdcp->cardmode = SDC_MODE_CARDTYPE_MMC;
|
||||
else
|
||||
#endif /* SDC_MMC_SUPPORT */
|
||||
{
|
||||
sdcp->cardmode = SDC_MODE_CARDTYPE_SDV11;
|
||||
|
||||
/* Reset error flag illegal command.*/
|
||||
sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#if SDC_MMC_SUPPORT
|
||||
if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_MMC) {
|
||||
/* TODO: MMC initialization.*/
|
||||
goto failed;
|
||||
}
|
||||
else
|
||||
#endif /* SDC_MMC_SUPPORT */
|
||||
{
|
||||
unsigned i;
|
||||
uint32_t ocr;
|
||||
|
||||
/* SD initialization.*/
|
||||
if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_SDV20)
|
||||
ocr = 0xC0100000;
|
||||
else
|
||||
ocr = 0x80100000;
|
||||
|
||||
/* SD-type initialization. */
|
||||
i = 0;
|
||||
while (TRUE) {
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_APP_OP_COND, ocr, resp))
|
||||
goto failed;
|
||||
if ((resp[0] & 0x80000000) != 0) {
|
||||
if (resp[0] & 0x40000000)
|
||||
sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY;
|
||||
break;
|
||||
}
|
||||
if (++i >= SDC_INIT_RETRY)
|
||||
goto failed;
|
||||
chThdSleepMilliseconds(10);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reads CID.*/
|
||||
if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_ALL_SEND_CID, 0, sdcp->cid))
|
||||
goto failed;
|
||||
|
||||
/* Asks for the RCA.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_RELATIVE_ADDR,
|
||||
0, &sdcp->rca))
|
||||
goto failed;
|
||||
|
||||
/* Reads CSD.*/
|
||||
if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_SEND_CSD,
|
||||
sdcp->rca, sdcp->csd))
|
||||
goto failed;
|
||||
|
||||
/* Switches to high speed.*/
|
||||
sdc_lld_set_data_clk(sdcp);
|
||||
|
||||
/* Selects the card for operations.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEL_DESEL_CARD,
|
||||
sdcp->rca, resp))
|
||||
goto failed;
|
||||
|
||||
/* Block length fixed at 512 bytes.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BLOCKLEN,
|
||||
MMCSD_BLOCK_SIZE, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
|
||||
/* Switches to wide bus mode.*/
|
||||
switch (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) {
|
||||
case SDC_MODE_CARDTYPE_SDV11:
|
||||
case SDC_MODE_CARDTYPE_SDV20:
|
||||
sdc_lld_set_bus_mode(sdcp, SDC_MODE_4BIT);
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, sdcp->rca, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BUS_WIDTH, 2, resp) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine capacity.*/
|
||||
sdcp->capacity = mmcsdGetCapacity(sdcp->csd);
|
||||
if (sdcp->capacity == 0)
|
||||
goto failed;
|
||||
|
||||
/* Initialization complete.*/
|
||||
sdcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
|
||||
/* Connection failed, state reset to BLK_ACTIVE.*/
|
||||
failed:
|
||||
sdc_lld_stop_clk(sdcp);
|
||||
sdcp->state = BLK_ACTIVE;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Brings the driver in a state safe for card removal.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcDisconnect(SDCDriver *sdcp) {
|
||||
|
||||
chDbgCheck(sdcp != NULL, "sdcDisconnect");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY),
|
||||
"sdcDisconnect(), #1", "invalid state");
|
||||
if (sdcp->state == BLK_ACTIVE) {
|
||||
chSysUnlock();
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
sdcp->state = BLK_DISCONNECTING;
|
||||
chSysUnlock();
|
||||
|
||||
/* Waits for eventual pending operations completion.*/
|
||||
if (_sdc_wait_for_transfer_state(sdcp)) {
|
||||
sdc_lld_stop_clk(sdcp);
|
||||
sdcp->state = BLK_ACTIVE;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/* Card clock stopped.*/
|
||||
sdc_lld_stop_clk(sdcp);
|
||||
sdcp->state = BLK_ACTIVE;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads one or more blocks.
|
||||
* @pre The driver must be in the @p BLK_READY state after a successful
|
||||
* sdcConnect() invocation.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to read
|
||||
* @param[out] buf pointer to the read buffer
|
||||
* @param[in] n number of blocks to read
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcRead(SDCDriver *sdcp, uint32_t startblk,
|
||||
uint8_t *buf, uint32_t n) {
|
||||
bool_t status;
|
||||
|
||||
chDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0), "sdcRead");
|
||||
chDbgAssert(sdcp->state == BLK_READY, "sdcRead(), #1", "invalid state");
|
||||
|
||||
if ((startblk + n - 1) > sdcp->capacity){
|
||||
sdcp->errors |= SDC_OVERFLOW_ERROR;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/* Read operation in progress.*/
|
||||
sdcp->state = BLK_READING;
|
||||
|
||||
status = sdc_lld_read(sdcp, startblk, buf, n);
|
||||
|
||||
/* Read operation finished.*/
|
||||
sdcp->state = BLK_READY;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes one or more blocks.
|
||||
* @pre The driver must be in the @p BLK_READY state after a successful
|
||||
* sdcConnect() invocation.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to write
|
||||
* @param[out] buf pointer to the write buffer
|
||||
* @param[in] n number of blocks to write
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcWrite(SDCDriver *sdcp, uint32_t startblk,
|
||||
const uint8_t *buf, uint32_t n) {
|
||||
bool_t status;
|
||||
|
||||
chDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0), "sdcWrite");
|
||||
chDbgAssert(sdcp->state == BLK_READY, "sdcWrite(), #1", "invalid state");
|
||||
|
||||
if ((startblk + n - 1) > sdcp->capacity){
|
||||
sdcp->errors |= SDC_OVERFLOW_ERROR;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/* Write operation in progress.*/
|
||||
sdcp->state = BLK_WRITING;
|
||||
|
||||
status = sdc_lld_write(sdcp, startblk, buf, n);
|
||||
|
||||
/* Write operation finished.*/
|
||||
sdcp->state = BLK_READY;
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the errors mask associated to the previous operation.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @return The errors mask.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
sdcflags_t sdcGetAndClearErrors(SDCDriver *sdcp) {
|
||||
sdcflags_t flags;
|
||||
|
||||
chDbgCheck(sdcp != NULL, "sdcGetAndClearErrors");
|
||||
chDbgAssert(sdcp->state == BLK_READY,
|
||||
"sdcGetAndClearErrors(), #1", "invalid state");
|
||||
|
||||
chSysLock();
|
||||
flags = sdcp->errors;
|
||||
sdcp->errors = SDC_NO_ERROR;
|
||||
chSysUnlock();
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for card idle condition.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcSync(SDCDriver *sdcp) {
|
||||
bool_t result;
|
||||
|
||||
chDbgCheck(sdcp != NULL, "sdcSync");
|
||||
|
||||
if (sdcp->state != BLK_READY)
|
||||
return CH_FAILED;
|
||||
|
||||
/* Synchronization operation in progress.*/
|
||||
sdcp->state = BLK_SYNCING;
|
||||
|
||||
result = sdc_lld_sync(sdcp);
|
||||
|
||||
/* Synchronization operation finished.*/
|
||||
sdcp->state = BLK_READY;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the media info.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[out] bdip pointer to a @p BlockDeviceInfo structure
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcGetInfo(SDCDriver *sdcp, BlockDeviceInfo *bdip) {
|
||||
|
||||
chDbgCheck((sdcp != NULL) && (bdip != NULL), "sdcGetInfo");
|
||||
|
||||
if (sdcp->state != BLK_READY)
|
||||
return CH_FAILED;
|
||||
|
||||
bdip->blk_num = sdcp->capacity;
|
||||
bdip->blk_size = MMCSD_BLOCK_SIZE;
|
||||
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Erases the supplied blocks.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk starting block number
|
||||
* @param[in] endblk ending block number
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdcErase(SDCDriver *sdcp, uint32_t startblk, uint32_t endblk) {
|
||||
uint32_t resp[1];
|
||||
|
||||
chDbgCheck((sdcp != NULL), "sdcErase");
|
||||
chDbgAssert(sdcp->state == BLK_READY, "sdcErase(), #1", "invalid state");
|
||||
|
||||
/* Erase operation in progress.*/
|
||||
sdcp->state = BLK_WRITING;
|
||||
|
||||
/* Handling command differences between HC and normal cards.*/
|
||||
if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) {
|
||||
startblk *= MMCSD_BLOCK_SIZE;
|
||||
endblk *= MMCSD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
_sdc_wait_for_transfer_state(sdcp);
|
||||
|
||||
if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_START,
|
||||
startblk, resp) != CH_SUCCESS) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
|
||||
if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_END,
|
||||
endblk, resp) != CH_SUCCESS) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
|
||||
if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE,
|
||||
0, resp) != CH_SUCCESS) ||
|
||||
MMCSD_R1_ERROR(resp[0]))
|
||||
goto failed;
|
||||
|
||||
/* Quick sleep to allow it to transition to programming or receiving state */
|
||||
/* TODO: ??????????????????????????? */
|
||||
|
||||
/* Wait for it to return to transfer state to indicate it has finished erasing */
|
||||
_sdc_wait_for_transfer_state(sdcp);
|
||||
|
||||
sdcp->state = BLK_READY;
|
||||
return CH_SUCCESS;
|
||||
|
||||
failed:
|
||||
sdcp->state = BLK_READY;
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SDC */
|
||||
|
||||
/** @} */
|
253
Software/portapack-mayhem/firmware/chibios/os/hal/src/serial.c
Normal file
253
Software/portapack-mayhem/firmware/chibios/os/hal/src/serial.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file serial.c
|
||||
* @brief Serial Driver code.
|
||||
*
|
||||
* @addtogroup SERIAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* Interface implementation, the following functions just invoke the equivalent
|
||||
* queue-level function or macro.
|
||||
*/
|
||||
|
||||
static size_t write(void *ip, const uint8_t *bp, size_t n) {
|
||||
|
||||
return chOQWriteTimeout(&((SerialDriver *)ip)->oqueue, bp,
|
||||
n, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static size_t read(void *ip, uint8_t *bp, size_t n) {
|
||||
|
||||
return chIQReadTimeout(&((SerialDriver *)ip)->iqueue, bp,
|
||||
n, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static msg_t put(void *ip, uint8_t b) {
|
||||
|
||||
return chOQPutTimeout(&((SerialDriver *)ip)->oqueue, b, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static msg_t get(void *ip) {
|
||||
|
||||
return chIQGetTimeout(&((SerialDriver *)ip)->iqueue, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static msg_t putt(void *ip, uint8_t b, systime_t timeout) {
|
||||
|
||||
return chOQPutTimeout(&((SerialDriver *)ip)->oqueue, b, timeout);
|
||||
}
|
||||
|
||||
static msg_t gett(void *ip, systime_t timeout) {
|
||||
|
||||
return chIQGetTimeout(&((SerialDriver *)ip)->iqueue, timeout);
|
||||
}
|
||||
|
||||
static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t time) {
|
||||
|
||||
return chOQWriteTimeout(&((SerialDriver *)ip)->oqueue, bp, n, time);
|
||||
}
|
||||
|
||||
static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t time) {
|
||||
|
||||
return chIQReadTimeout(&((SerialDriver *)ip)->iqueue, bp, n, time);
|
||||
}
|
||||
|
||||
static const struct SerialDriverVMT vmt = {
|
||||
write, read, put, get,
|
||||
putt, gett, writet, readt
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Serial Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void sdObjectInit(SerialDriver *sdp, qnotify_t inotify, qnotify_t onotify) {
|
||||
|
||||
sdp->vmt = &vmt;
|
||||
chEvtInit(&sdp->event);
|
||||
sdp->state = SD_STOP;
|
||||
chIQInit(&sdp->iqueue, sdp->ib, SERIAL_BUFFERS_SIZE, inotify, sdp);
|
||||
chOQInit(&sdp->oqueue, sdp->ob, SERIAL_BUFFERS_SIZE, onotify, sdp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void sdStart(SerialDriver *sdp, const SerialConfig *config) {
|
||||
|
||||
chDbgCheck(sdp != NULL, "sdStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((sdp->state == SD_STOP) || (sdp->state == SD_READY),
|
||||
"sdStart(), #1",
|
||||
"invalid state");
|
||||
sd_lld_start(sdp, config);
|
||||
sdp->state = SD_READY;
|
||||
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 SerialDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void sdStop(SerialDriver *sdp) {
|
||||
|
||||
chDbgCheck(sdp != NULL, "sdStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((sdp->state == SD_STOP) || (sdp->state == SD_READY),
|
||||
"sdStop(), #1",
|
||||
"invalid state");
|
||||
sd_lld_stop(sdp);
|
||||
sdp->state = SD_STOP;
|
||||
chOQResetI(&sdp->oqueue);
|
||||
chIQResetI(&sdp->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.
|
||||
* @note The incoming data event is only generated when the input queue
|
||||
* becomes non-empty.
|
||||
* @note In order to gain some performance it is suggested to not use
|
||||
* this function directly but copy this code directly into the
|
||||
* interrupt service routine.
|
||||
*
|
||||
* @param[in] sdp pointer to a @p SerialDriver structure
|
||||
* @param[in] b the byte to be written in the driver's Input Queue
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void sdIncomingDataI(SerialDriver *sdp, uint8_t b) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(sdp != NULL, "sdIncomingDataI");
|
||||
|
||||
if (chIQIsEmptyI(&sdp->iqueue))
|
||||
chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE);
|
||||
if (chIQPutI(&sdp->iqueue, b) < Q_OK)
|
||||
chnAddFlagsI(sdp, SD_OVERRUN_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles outgoing data.
|
||||
* @details Must be called from the output interrupt service routine in order
|
||||
* to get the next byte to be transmitted.
|
||||
* @note In order to gain some performance it is suggested to not use
|
||||
* this function directly but copy this code directly into the
|
||||
* interrupt service routine.
|
||||
*
|
||||
* @param[in] sdp 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).
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
msg_t sdRequestDataI(SerialDriver *sdp) {
|
||||
msg_t b;
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(sdp != NULL, "sdRequestDataI");
|
||||
|
||||
b = chOQGetI(&sdp->oqueue);
|
||||
if (b < Q_OK)
|
||||
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
|
||||
return b;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SERIAL */
|
||||
|
||||
/** @} */
|
@ -0,0 +1,421 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file serial_usb.c
|
||||
* @brief Serial over USB Driver code.
|
||||
*
|
||||
* @addtogroup SERIAL_USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SERIAL_USB || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* Current Line Coding.
|
||||
*/
|
||||
static cdc_linecoding_t linecoding = {
|
||||
{0x00, 0x96, 0x00, 0x00}, /* 38400. */
|
||||
LC_STOP_1, LC_PARITY_NONE, 8
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* Interface implementation.
|
||||
*/
|
||||
|
||||
static size_t write(void *ip, const uint8_t *bp, size_t n) {
|
||||
|
||||
return chOQWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp,
|
||||
n, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static size_t read(void *ip, uint8_t *bp, size_t n) {
|
||||
|
||||
return chIQReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp,
|
||||
n, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static msg_t put(void *ip, uint8_t b) {
|
||||
|
||||
return chOQPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static msg_t get(void *ip) {
|
||||
|
||||
return chIQGetTimeout(&((SerialUSBDriver *)ip)->iqueue, TIME_INFINITE);
|
||||
}
|
||||
|
||||
static msg_t putt(void *ip, uint8_t b, systime_t timeout) {
|
||||
|
||||
return chOQPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, timeout);
|
||||
}
|
||||
|
||||
static msg_t gett(void *ip, systime_t timeout) {
|
||||
|
||||
return chIQGetTimeout(&((SerialUSBDriver *)ip)->iqueue, timeout);
|
||||
}
|
||||
|
||||
static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t time) {
|
||||
|
||||
return chOQWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp, n, time);
|
||||
}
|
||||
|
||||
static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t time) {
|
||||
|
||||
return chIQReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp, n, time);
|
||||
}
|
||||
|
||||
static const struct SerialUSBDriverVMT vmt = {
|
||||
write, read, put, get,
|
||||
putt, gett, writet, readt
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Notification of data removed from the input queue.
|
||||
*/
|
||||
static void inotify(GenericQueue *qp) {
|
||||
size_t n, maxsize;
|
||||
SerialUSBDriver *sdup = chQGetLink(qp);
|
||||
|
||||
/* If the USB driver is not in the appropriate state then transactions
|
||||
must not be started.*/
|
||||
if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) ||
|
||||
(sdup->state != SDU_READY))
|
||||
return;
|
||||
|
||||
/* If there is in the queue enough space to hold at least one packet and
|
||||
a transaction is not yet started then a new transaction is started for
|
||||
the available space.*/
|
||||
maxsize = sdup->config->usbp->epc[sdup->config->bulk_out]->out_maxsize;
|
||||
if (!usbGetReceiveStatusI(sdup->config->usbp, sdup->config->bulk_out) &&
|
||||
((n = chIQGetEmptyI(&sdup->iqueue)) >= maxsize)) {
|
||||
chSysUnlock();
|
||||
|
||||
n = (n / maxsize) * maxsize;
|
||||
usbPrepareQueuedReceive(sdup->config->usbp,
|
||||
sdup->config->bulk_out,
|
||||
&sdup->iqueue, n);
|
||||
|
||||
chSysLock();
|
||||
usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Notification of data inserted into the output queue.
|
||||
*/
|
||||
static void onotify(GenericQueue *qp) {
|
||||
size_t n;
|
||||
SerialUSBDriver *sdup = chQGetLink(qp);
|
||||
|
||||
/* If the USB driver is not in the appropriate state then transactions
|
||||
must not be started.*/
|
||||
if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) ||
|
||||
(sdup->state != SDU_READY))
|
||||
return;
|
||||
|
||||
/* If there is not an ongoing transaction and the output queue contains
|
||||
data then a new transaction is started.*/
|
||||
if (!usbGetTransmitStatusI(sdup->config->usbp, sdup->config->bulk_in) &&
|
||||
((n = chOQGetFullI(&sdup->oqueue)) > 0)) {
|
||||
chSysUnlock();
|
||||
|
||||
usbPrepareQueuedTransmit(sdup->config->usbp,
|
||||
sdup->config->bulk_in,
|
||||
&sdup->oqueue, n);
|
||||
|
||||
chSysLock();
|
||||
usbStartTransmitI(sdup->config->usbp, sdup->config->bulk_in);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Serial Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void sduInit(void) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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] sdup pointer to a @p SerialUSBDriver structure
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void sduObjectInit(SerialUSBDriver *sdup) {
|
||||
|
||||
sdup->vmt = &vmt;
|
||||
chEvtInit(&sdup->event);
|
||||
sdup->state = SDU_STOP;
|
||||
chIQInit(&sdup->iqueue, sdup->ib, SERIAL_USB_BUFFERS_SIZE, inotify, sdup);
|
||||
chOQInit(&sdup->oqueue, sdup->ob, SERIAL_USB_BUFFERS_SIZE, onotify, sdup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and starts the driver.
|
||||
*
|
||||
* @param[in] sdup pointer to a @p SerialUSBDriver object
|
||||
* @param[in] config the serial over USB driver configuration
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void sduStart(SerialUSBDriver *sdup, const SerialUSBConfig *config) {
|
||||
USBDriver *usbp = config->usbp;
|
||||
|
||||
chDbgCheck(sdup != NULL, "sduStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((sdup->state == SDU_STOP) || (sdup->state == SDU_READY),
|
||||
"sduStart(), #1",
|
||||
"invalid state");
|
||||
usbp->in_params[config->bulk_in - 1] = sdup;
|
||||
usbp->out_params[config->bulk_out - 1] = sdup;
|
||||
usbp->in_params[config->int_in - 1] = sdup;
|
||||
sdup->config = config;
|
||||
sdup->state = SDU_READY;
|
||||
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] sdup pointer to a @p SerialUSBDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void sduStop(SerialUSBDriver *sdup) {
|
||||
USBDriver *usbp = sdup->config->usbp;
|
||||
|
||||
chDbgCheck(sdup != NULL, "sdStop");
|
||||
|
||||
chSysLock();
|
||||
|
||||
chDbgAssert((sdup->state == SDU_STOP) || (sdup->state == SDU_READY),
|
||||
"sduStop(), #1",
|
||||
"invalid state");
|
||||
|
||||
/* Driver in stopped state.*/
|
||||
usbp->in_params[sdup->config->bulk_in - 1] = NULL;
|
||||
usbp->out_params[sdup->config->bulk_out - 1] = NULL;
|
||||
usbp->in_params[sdup->config->int_in - 1] = NULL;
|
||||
sdup->state = SDU_STOP;
|
||||
|
||||
/* Queues reset in order to signal the driver stop to the application.*/
|
||||
chnAddFlagsI(sdup, CHN_DISCONNECTED);
|
||||
chIQResetI(&sdup->iqueue);
|
||||
chOQResetI(&sdup->oqueue);
|
||||
chSchRescheduleS();
|
||||
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB device configured handler.
|
||||
*
|
||||
* @param[in] sdup pointer to a @p SerialUSBDriver object
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void sduConfigureHookI(SerialUSBDriver *sdup) {
|
||||
USBDriver *usbp = sdup->config->usbp;
|
||||
|
||||
chIQResetI(&sdup->iqueue);
|
||||
chOQResetI(&sdup->oqueue);
|
||||
chnAddFlagsI(sdup, CHN_CONNECTED);
|
||||
|
||||
/* Starts the first OUT transaction immediately.*/
|
||||
usbPrepareQueuedReceive(usbp, sdup->config->bulk_out, &sdup->iqueue,
|
||||
usbp->epc[sdup->config->bulk_out]->out_maxsize);
|
||||
usbStartReceiveI(usbp, sdup->config->bulk_out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default requests hook.
|
||||
* @details Applications wanting to use the Serial over USB driver can use
|
||||
* this function as requests hook in the USB configuration.
|
||||
* The following requests are emulated:
|
||||
* - CDC_GET_LINE_CODING.
|
||||
* - CDC_SET_LINE_CODING.
|
||||
* - CDC_SET_CONTROL_LINE_STATE.
|
||||
* .
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @return The hook status.
|
||||
* @retval TRUE Message handled internally.
|
||||
* @retval FALSE Message not handled.
|
||||
*/
|
||||
bool_t sduRequestsHook(USBDriver *usbp) {
|
||||
|
||||
if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) {
|
||||
switch (usbp->setup[1]) {
|
||||
case CDC_GET_LINE_CODING:
|
||||
usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
|
||||
return TRUE;
|
||||
case CDC_SET_LINE_CODING:
|
||||
usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
|
||||
return TRUE;
|
||||
case CDC_SET_CONTROL_LINE_STATE:
|
||||
/* Nothing to do, there are no control lines.*/
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default data transmitted callback.
|
||||
* @details The application must use this function as callback for the IN
|
||||
* data endpoint.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*/
|
||||
void sduDataTransmitted(USBDriver *usbp, usbep_t ep) {
|
||||
size_t n;
|
||||
SerialUSBDriver *sdup = usbp->in_params[ep - 1];
|
||||
|
||||
if (sdup == NULL)
|
||||
return;
|
||||
|
||||
chSysLockFromIsr();
|
||||
chnAddFlagsI(sdup, CHN_OUTPUT_EMPTY);
|
||||
|
||||
if ((n = chOQGetFullI(&sdup->oqueue)) > 0) {
|
||||
/* The endpoint cannot be busy, we are in the context of the callback,
|
||||
so it is safe to transmit without a check.*/
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
usbPrepareQueuedTransmit(usbp, ep, &sdup->oqueue, n);
|
||||
|
||||
chSysLockFromIsr();
|
||||
usbStartTransmitI(usbp, ep);
|
||||
}
|
||||
else if ((usbp->epc[ep]->in_state->txsize > 0) &&
|
||||
!(usbp->epc[ep]->in_state->txsize &
|
||||
(usbp->epc[ep]->in_maxsize - 1))) {
|
||||
/* Transmit zero sized packet in case the last one has maximum allowed
|
||||
size. Otherwise the recipient may expect more data coming soon and
|
||||
not return buffered data to app. See section 5.8.3 Bulk Transfer
|
||||
Packet Size Constraints of the USB Specification document.*/
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
usbPrepareQueuedTransmit(usbp, ep, &sdup->oqueue, 0);
|
||||
|
||||
chSysLockFromIsr();
|
||||
usbStartTransmitI(usbp, ep);
|
||||
}
|
||||
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default data received callback.
|
||||
* @details The application must use this function as callback for the OUT
|
||||
* data endpoint.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*/
|
||||
void sduDataReceived(USBDriver *usbp, usbep_t ep) {
|
||||
size_t n, maxsize;
|
||||
SerialUSBDriver *sdup = usbp->out_params[ep - 1];
|
||||
|
||||
if (sdup == NULL)
|
||||
return;
|
||||
|
||||
chSysLockFromIsr();
|
||||
chnAddFlagsI(sdup, CHN_INPUT_AVAILABLE);
|
||||
|
||||
/* Writes to the input queue can only happen when there is enough space
|
||||
to hold at least one packet.*/
|
||||
maxsize = usbp->epc[ep]->out_maxsize;
|
||||
if ((n = chIQGetEmptyI(&sdup->iqueue)) >= maxsize) {
|
||||
/* The endpoint cannot be busy, we are in the context of the callback,
|
||||
so a packet is in the buffer for sure.*/
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
n = (n / maxsize) * maxsize;
|
||||
usbPrepareQueuedReceive(usbp, ep, &sdup->iqueue, n);
|
||||
|
||||
chSysLockFromIsr();
|
||||
usbStartReceiveI(usbp, ep);
|
||||
}
|
||||
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default data received callback.
|
||||
* @details The application must use this function as callback for the IN
|
||||
* interrupt endpoint.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*/
|
||||
void sduInterruptTransmitted(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
(void)usbp;
|
||||
(void)ep;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SERIAL */
|
||||
|
||||
/** @} */
|
447
Software/portapack-mayhem/firmware/chibios/os/hal/src/spi.c
Normal file
447
Software/portapack-mayhem/firmware/chibios/os/hal/src/spi.c
Normal file
@ -0,0 +1,447 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file spi.c
|
||||
* @brief SPI Driver code.
|
||||
*
|
||||
* @addtogroup SPI
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief SPI Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void spiInit(void) {
|
||||
|
||||
spi_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p SPIDriver structure.
|
||||
*
|
||||
* @param[out] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void spiObjectInit(SPIDriver *spip) {
|
||||
|
||||
spip->state = SPI_STOP;
|
||||
spip->config = NULL;
|
||||
#if SPI_USE_WAIT
|
||||
spip->thread = NULL;
|
||||
#endif /* SPI_USE_WAIT */
|
||||
#if SPI_USE_MUTUAL_EXCLUSION
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxInit(&spip->mutex);
|
||||
#else
|
||||
chSemInit(&spip->semaphore, 1);
|
||||
#endif
|
||||
#endif /* SPI_USE_MUTUAL_EXCLUSION */
|
||||
#if defined(SPI_DRIVER_EXT_INIT_HOOK)
|
||||
SPI_DRIVER_EXT_INIT_HOOK(spip);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiStart(SPIDriver *spip, const SPIConfig *config) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (config != NULL), "spiStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
|
||||
"spiStart(), #1", "invalid state");
|
||||
spip->config = config;
|
||||
spi_lld_start(spip);
|
||||
spip->state = SPI_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the SPI peripheral.
|
||||
* @note Deactivating the peripheral also enforces a release of the slave
|
||||
* select line.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiStop(SPIDriver *spip) {
|
||||
|
||||
chDbgCheck(spip != NULL, "spiStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
|
||||
"spiStop(), #1", "invalid state");
|
||||
spi_lld_unselect(spip);
|
||||
spi_lld_stop(spip);
|
||||
spip->state = SPI_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asserts the slave select signal and prepares for transfers.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiSelect(SPIDriver *spip) {
|
||||
|
||||
chDbgCheck(spip != NULL, "spiSelect");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiSelect(), #1", "not ready");
|
||||
spiSelectI(spip);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deasserts the slave select signal.
|
||||
* @details The previously selected peripheral is unselected.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiUnselect(SPIDriver *spip) {
|
||||
|
||||
chDbgCheck(spip != NULL, "spiUnselect");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiUnselect(), #1", "not ready");
|
||||
spiUnselectI(spip);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ignores data on the SPI bus.
|
||||
* @details This asynchronous function starts the transmission of a series of
|
||||
* idle words on the SPI bus and ignores the received data.
|
||||
* @pre A slave must have been selected using @p spiSelect() or
|
||||
* @p spiSelectI().
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to be ignored
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiStartIgnore(SPIDriver *spip, size_t n) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0), "spiStartIgnore");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiStartIgnore(), #1", "not ready");
|
||||
spiStartIgnoreI(spip, n);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges data on the SPI bus.
|
||||
* @details This asynchronous function starts a simultaneous transmit/receive
|
||||
* operation.
|
||||
* @pre A slave must have been selected using @p spiSelect() or
|
||||
* @p spiSelectI().
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiStartExchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL),
|
||||
"spiStartExchange");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiStartExchange(), #1", "not ready");
|
||||
spiStartExchangeI(spip, n, txbuf, rxbuf);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data over the SPI bus.
|
||||
* @details This asynchronous function starts a transmit operation.
|
||||
* @pre A slave must have been selected using @p spiSelect() or
|
||||
* @p spiSelectI().
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL),
|
||||
"spiStartSend");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiStartSend(), #1", "not ready");
|
||||
spiStartSendI(spip, n, txbuf);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data from the SPI bus.
|
||||
* @details This asynchronous function starts a receive operation.
|
||||
* @pre A slave must have been selected using @p spiSelect() or
|
||||
* @p spiSelectI().
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL),
|
||||
"spiStartReceive");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiStartReceive(), #1", "not ready");
|
||||
spiStartReceiveI(spip, n, rxbuf);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
#if SPI_USE_WAIT || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Ignores data on the SPI bus.
|
||||
* @details This synchronous function performs the transmission of a series of
|
||||
* idle words on the SPI bus and ignores the received data.
|
||||
* @pre In order to use this function the option @p SPI_USE_WAIT must be
|
||||
* enabled.
|
||||
* @pre In order to use this function the driver must have been configured
|
||||
* without callbacks (@p end_cb = @p NULL).
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to be ignored
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiIgnore(SPIDriver *spip, size_t n) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0), "spiIgnoreWait");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiIgnore(), #1", "not ready");
|
||||
chDbgAssert(spip->config->end_cb == NULL, "spiIgnore(), #2", "has callback");
|
||||
spiStartIgnoreI(spip, n);
|
||||
_spi_wait_s(spip);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges data on the SPI bus.
|
||||
* @details This synchronous function performs a simultaneous transmit/receive
|
||||
* operation.
|
||||
* @pre In order to use this function the option @p SPI_USE_WAIT must be
|
||||
* enabled.
|
||||
* @pre In order to use this function the driver must have been configured
|
||||
* without callbacks (@p end_cb = @p NULL).
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiExchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL),
|
||||
"spiExchange");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiExchange(), #1", "not ready");
|
||||
chDbgAssert(spip->config->end_cb == NULL,
|
||||
"spiExchange(), #2", "has callback");
|
||||
spiStartExchangeI(spip, n, txbuf, rxbuf);
|
||||
_spi_wait_s(spip);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data over the SPI bus.
|
||||
* @details This synchronous function performs a transmit operation.
|
||||
* @pre In order to use this function the option @p SPI_USE_WAIT must be
|
||||
* enabled.
|
||||
* @pre In order to use this function the driver must have been configured
|
||||
* without callbacks (@p end_cb = @p NULL).
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiSend(SPIDriver *spip, size_t n, const void *txbuf) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL), "spiSend");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiSend(), #1", "not ready");
|
||||
chDbgAssert(spip->config->end_cb == NULL, "spiSend(), #2", "has callback");
|
||||
spiStartSendI(spip, n, txbuf);
|
||||
_spi_wait_s(spip);
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data from the SPI bus.
|
||||
* @details This synchronous function performs a receive operation.
|
||||
* @pre In order to use this function the option @p SPI_USE_WAIT must be
|
||||
* enabled.
|
||||
* @pre In order to use this function the driver must have been configured
|
||||
* without callbacks (@p end_cb = @p NULL).
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf) {
|
||||
|
||||
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL),
|
||||
"spiReceive");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(spip->state == SPI_READY, "spiReceive(), #1", "not ready");
|
||||
chDbgAssert(spip->config->end_cb == NULL,
|
||||
"spiReceive(), #2", "has callback");
|
||||
spiStartReceiveI(spip, n, rxbuf);
|
||||
_spi_wait_s(spip);
|
||||
chSysUnlock();
|
||||
}
|
||||
#endif /* SPI_USE_WAIT */
|
||||
|
||||
#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.
|
||||
* @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
|
||||
* must be enabled.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiAcquireBus(SPIDriver *spip) {
|
||||
|
||||
chDbgCheck(spip != NULL, "spiAcquireBus");
|
||||
|
||||
#if CH_USE_MUTEXES
|
||||
chMtxLock(&spip->mutex);
|
||||
#elif CH_USE_SEMAPHORES
|
||||
chSemWait(&spip->semaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases exclusive access to the SPI bus.
|
||||
* @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
|
||||
* must be enabled.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void spiReleaseBus(SPIDriver *spip) {
|
||||
|
||||
chDbgCheck(spip != NULL, "spiReleaseBus");
|
||||
|
||||
#if CH_USE_MUTEXES
|
||||
(void)spip;
|
||||
chMtxUnlock();
|
||||
#elif CH_USE_SEMAPHORES
|
||||
chSemSignal(&spip->semaphore);
|
||||
#endif
|
||||
}
|
||||
#endif /* SPI_USE_MUTUAL_EXCLUSION */
|
||||
|
||||
#endif /* HAL_USE_SPI */
|
||||
|
||||
/** @} */
|
135
Software/portapack-mayhem/firmware/chibios/os/hal/src/tm.c
Normal file
135
Software/portapack-mayhem/firmware/chibios/os/hal/src/tm.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tm.c
|
||||
* @brief Time Measurement driver code.
|
||||
*
|
||||
* @addtogroup TM
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_TM || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Subsystem calibration value.
|
||||
*/
|
||||
static halrtcnt_t measurement_offset;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Starts a measurement.
|
||||
*
|
||||
* @param[in,out] tmp pointer to a @p TimeMeasurement structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void tm_start(TimeMeasurement *tmp) {
|
||||
|
||||
tmp->last = halGetCounterValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops a measurement.
|
||||
*
|
||||
* @param[in,out] tmp pointer to a @p TimeMeasurement structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void tm_stop(TimeMeasurement *tmp) {
|
||||
|
||||
halrtcnt_t now = halGetCounterValue();
|
||||
tmp->last = now - tmp->last - measurement_offset;
|
||||
if (tmp->last > tmp->worst)
|
||||
tmp->worst = tmp->last;
|
||||
else if (tmp->last < tmp->best)
|
||||
tmp->best = tmp->last;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Initializes the Time Measurement unit.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void tmInit(void) {
|
||||
TimeMeasurement tm;
|
||||
|
||||
/* Time Measurement subsystem calibration, it does a null measurement
|
||||
and calculates the call overhead which is subtracted to real
|
||||
measurements.*/
|
||||
measurement_offset = 0;
|
||||
tmObjectInit(&tm);
|
||||
tmStartMeasurement(&tm);
|
||||
tmStopMeasurement(&tm);
|
||||
measurement_offset = tm.last;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes a @p TimeMeasurement object.
|
||||
*
|
||||
* @param[out] tmp pointer to a @p TimeMeasurement structure
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void tmObjectInit(TimeMeasurement *tmp) {
|
||||
|
||||
tmp->start = tm_start;
|
||||
tmp->stop = tm_stop;
|
||||
tmp->last = (halrtcnt_t)0;
|
||||
tmp->worst = (halrtcnt_t)0;
|
||||
tmp->best = (halrtcnt_t)-1;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_TM */
|
||||
|
||||
/** @} */
|
360
Software/portapack-mayhem/firmware/chibios/os/hal/src/uart.c
Normal file
360
Software/portapack-mayhem/firmware/chibios/os/hal/src/uart.c
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file uart.c
|
||||
* @brief UART Driver code.
|
||||
*
|
||||
* @addtogroup UART
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_UART || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief UART Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void uartInit(void) {
|
||||
|
||||
uart_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p UARTDriver structure.
|
||||
*
|
||||
* @param[out] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void uartObjectInit(UARTDriver *uartp) {
|
||||
|
||||
uartp->state = UART_STOP;
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
uartp->config = NULL;
|
||||
/* Optional, user-defined initializer.*/
|
||||
#if defined(UART_DRIVER_EXT_INIT_HOOK)
|
||||
UART_DRIVER_EXT_INIT_HOOK(uartp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the UART peripheral.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] config pointer to the @p UARTConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void uartStart(UARTDriver *uartp, const UARTConfig *config) {
|
||||
|
||||
chDbgCheck((uartp != NULL) && (config != NULL), "uartStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((uartp->state == UART_STOP) || (uartp->state == UART_READY),
|
||||
"uartStart(), #1", "invalid state");
|
||||
|
||||
uartp->config = config;
|
||||
uart_lld_start(uartp);
|
||||
uartp->state = UART_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the UART peripheral.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void uartStop(UARTDriver *uartp) {
|
||||
|
||||
chDbgCheck(uartp != NULL, "uartStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((uartp->state == UART_STOP) || (uartp->state == UART_READY),
|
||||
"uartStop(), #1", "invalid state");
|
||||
|
||||
uart_lld_stop(uartp);
|
||||
uartp->state = UART_STOP;
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a transmission on the UART peripheral.
|
||||
* @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.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void uartStartSend(UARTDriver *uartp, size_t n, const void *txbuf) {
|
||||
|
||||
chDbgCheck((uartp != NULL) && (n > 0) && (txbuf != NULL),
|
||||
"uartStartSend");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(uartp->state == UART_READY,
|
||||
"uartStartSend(), #1", "is active");
|
||||
chDbgAssert(uartp->txstate != UART_TX_ACTIVE,
|
||||
"uartStartSend(), #2", "tx active");
|
||||
|
||||
uart_lld_start_send(uartp, n, txbuf);
|
||||
uartp->txstate = UART_TX_ACTIVE;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a transmission on the UART peripheral.
|
||||
* @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.
|
||||
* @note This function has to be invoked from a lock zone.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void uartStartSendI(UARTDriver *uartp, size_t n, const void *txbuf) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck((uartp != NULL) && (n > 0) && (txbuf != NULL),
|
||||
"uartStartSendI");
|
||||
chDbgAssert(uartp->state == UART_READY,
|
||||
"uartStartSendI(), #1", "is active");
|
||||
chDbgAssert(uartp->txstate != UART_TX_ACTIVE,
|
||||
"uartStartSendI(), #2", "tx active");
|
||||
|
||||
uart_lld_start_send(uartp, n, txbuf);
|
||||
uartp->txstate = UART_TX_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing transmission.
|
||||
* @note Stopping a transmission also suppresses the transmission callbacks.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not transmitted by the
|
||||
* stopped transmit operation.
|
||||
* @retval 0 There was no transmit operation in progress.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
size_t uartStopSend(UARTDriver *uartp) {
|
||||
size_t n;
|
||||
|
||||
chDbgCheck(uartp != NULL, "uartStopSend");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(uartp->state == UART_READY, "uartStopSend(), #1", "not active");
|
||||
|
||||
if (uartp->txstate == UART_TX_ACTIVE) {
|
||||
n = uart_lld_stop_send(uartp);
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
}
|
||||
else
|
||||
n = 0;
|
||||
chSysUnlock();
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing transmission.
|
||||
* @note Stopping a transmission also suppresses the transmission callbacks.
|
||||
* @note This function has to be invoked from a lock zone.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not transmitted by the
|
||||
* stopped transmit operation.
|
||||
* @retval 0 There was no transmit operation in progress.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
size_t uartStopSendI(UARTDriver *uartp) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(uartp != NULL, "uartStopSendI");
|
||||
chDbgAssert(uartp->state == UART_READY, "uartStopSendI(), #1", "not active");
|
||||
|
||||
if (uartp->txstate == UART_TX_ACTIVE) {
|
||||
size_t n = uart_lld_stop_send(uartp);
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
return n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a receive operation on the UART peripheral.
|
||||
* @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.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[in] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void uartStartReceive(UARTDriver *uartp, size_t n, void *rxbuf) {
|
||||
|
||||
chDbgCheck((uartp != NULL) && (n > 0) && (rxbuf != NULL),
|
||||
"uartStartReceive");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(uartp->state == UART_READY,
|
||||
"uartStartReceive(), #1", "is active");
|
||||
chDbgAssert(uartp->rxstate != UART_RX_ACTIVE,
|
||||
"uartStartReceive(), #2", "rx active");
|
||||
|
||||
uart_lld_start_receive(uartp, n, rxbuf);
|
||||
uartp->rxstate = UART_RX_ACTIVE;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a receive operation on the UART peripheral.
|
||||
* @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.
|
||||
* @note This function has to be invoked from a lock zone.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void uartStartReceiveI(UARTDriver *uartp, size_t n, void *rxbuf) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck((uartp != NULL) && (n > 0) && (rxbuf != NULL),
|
||||
"uartStartReceiveI");
|
||||
chDbgAssert(uartp->state == UART_READY,
|
||||
"uartStartReceiveI(), #1", "is active");
|
||||
chDbgAssert(uartp->rxstate != UART_RX_ACTIVE,
|
||||
"uartStartReceiveI(), #2", "rx active");
|
||||
|
||||
uart_lld_start_receive(uartp, n, rxbuf);
|
||||
uartp->rxstate = UART_RX_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing receive operation.
|
||||
* @note Stopping a receive operation also suppresses the receive callbacks.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not received by the
|
||||
* stopped receive operation.
|
||||
* @retval 0 There was no receive operation in progress.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
size_t uartStopReceive(UARTDriver *uartp) {
|
||||
size_t n;
|
||||
|
||||
chDbgCheck(uartp != NULL, "uartStopReceive");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(uartp->state == UART_READY,
|
||||
"uartStopReceive(), #1", "not active");
|
||||
|
||||
if (uartp->rxstate == UART_RX_ACTIVE) {
|
||||
n = uart_lld_stop_receive(uartp);
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
}
|
||||
else
|
||||
n = 0;
|
||||
chSysUnlock();
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing receive operation.
|
||||
* @note Stopping a receive operation also suppresses the receive callbacks.
|
||||
* @note This function has to be invoked from a lock zone.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not received by the
|
||||
* stopped receive operation.
|
||||
* @retval 0 There was no receive operation in progress.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
size_t uartStopReceiveI(UARTDriver *uartp) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(uartp != NULL, "uartStopReceiveI");
|
||||
chDbgAssert(uartp->state == UART_READY,
|
||||
"uartStopReceiveI(), #1", "not active");
|
||||
|
||||
if (uartp->rxstate == UART_RX_ACTIVE) {
|
||||
size_t n = uart_lld_stop_receive(uartp);
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
return n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_UART */
|
||||
|
||||
/** @} */
|
810
Software/portapack-mayhem/firmware/chibios/os/hal/src/usb.c
Normal file
810
Software/portapack-mayhem/firmware/chibios/os/hal/src/usb.c
Normal file
@ -0,0 +1,810 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
|
||||
2011,2012,2013 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/>.
|
||||
|
||||
---
|
||||
|
||||
A special exception to the GPL can be applied should you wish to distribute
|
||||
a combined work that includes ChibiOS/RT, without being obliged to provide
|
||||
the source code for any proprietary components. See the file exception.txt
|
||||
for full details of how and when the exception can be applied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file usb.c
|
||||
* @brief USB Driver code.
|
||||
*
|
||||
* @addtogroup USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "usb.h"
|
||||
|
||||
#if HAL_USE_USB || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static const uint8_t zero_status[] = {0x00, 0x00};
|
||||
static const uint8_t active_status[] ={0x00, 0x00};
|
||||
static const uint8_t halted_status[] = {0x01, 0x00};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief SET ADDRESS transaction callback.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*/
|
||||
static void set_address(USBDriver *usbp) {
|
||||
|
||||
usbp->address = usbp->setup[2];
|
||||
usb_lld_set_address(usbp);
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_ADDRESS);
|
||||
usbp->state = USB_SELECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Standard requests handler.
|
||||
* @details This is the standard requests default handler, most standard
|
||||
* requests are handled here, the user can override the standard
|
||||
* handling using the @p requests_hook_cb hook in the
|
||||
* @p USBConfig structure.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @return The request handling exit code.
|
||||
* @retval FALSE Request not recognized by the handler or error.
|
||||
* @retval TRUE Request handled.
|
||||
*/
|
||||
static bool_t default_handler(USBDriver *usbp) {
|
||||
const USBDescriptor *dp;
|
||||
|
||||
/* Decoding the request.*/
|
||||
switch (((usbp->setup[0] & (USB_RTYPE_RECIPIENT_MASK |
|
||||
USB_RTYPE_TYPE_MASK)) |
|
||||
(usbp->setup[1] << 8))) {
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_GET_STATUS << 8):
|
||||
/* Just returns the current status word.*/
|
||||
usbSetupTransfer(usbp, (uint8_t *)&usbp->status, 2, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_CLEAR_FEATURE << 8):
|
||||
/* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
|
||||
number is handled as an error.*/
|
||||
if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
|
||||
usbp->status &= ~2;
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_FEATURE << 8):
|
||||
/* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
|
||||
number is handled as an error.*/
|
||||
if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
|
||||
usbp->status |= 2;
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_ADDRESS << 8):
|
||||
/* The SET_ADDRESS handling can be performed here or postponed after
|
||||
the status packed depending on the USB_SET_ADDRESS_MODE low
|
||||
driver setting.*/
|
||||
#if USB_SET_ADDRESS_MODE == USB_EARLY_SET_ADDRESS
|
||||
if ((usbp->setup[0] == USB_RTYPE_RECIPIENT_DEVICE) &&
|
||||
(usbp->setup[1] == USB_REQ_SET_ADDRESS))
|
||||
set_address(usbp);
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
#else
|
||||
usbSetupTransfer(usbp, NULL, 0, set_address);
|
||||
#endif
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_GET_DESCRIPTOR << 8):
|
||||
/* Handling descriptor requests from the host.*/
|
||||
dp = usbp->config->get_descriptor_cb(
|
||||
usbp, usbp->setup[3], usbp->setup[2],
|
||||
usbFetchWord(&usbp->setup[4]));
|
||||
if (dp == NULL)
|
||||
return FALSE;
|
||||
usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_GET_CONFIGURATION << 8):
|
||||
/* Returning the last selected configuration.*/
|
||||
usbSetupTransfer(usbp, &usbp->configuration, 1, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_CONFIGURATION << 8):
|
||||
/* Handling configuration selection from the host.*/
|
||||
usbp->configuration = usbp->setup[2];
|
||||
if (usbp->configuration == 0)
|
||||
usbp->state = USB_SELECTED;
|
||||
else
|
||||
usbp->state = USB_ACTIVE;
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_CONFIGURED);
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_GET_STATUS << 8):
|
||||
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_SYNCH_FRAME << 8):
|
||||
/* Just sending two zero bytes, the application can change the behavior
|
||||
using a hook..*/
|
||||
usbSetupTransfer(usbp, (uint8_t *)zero_status, 2, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_GET_STATUS << 8):
|
||||
/* Sending the EP status.*/
|
||||
if (usbp->setup[4] & 0x80) {
|
||||
switch (usb_lld_get_status_in(usbp, usbp->setup[4] & 0x0F)) {
|
||||
case EP_STATUS_STALLED:
|
||||
usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
|
||||
return TRUE;
|
||||
case EP_STATUS_ACTIVE:
|
||||
usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (usb_lld_get_status_out(usbp, usbp->setup[4] & 0x0F)) {
|
||||
case EP_STATUS_STALLED:
|
||||
usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
|
||||
return TRUE;
|
||||
case EP_STATUS_ACTIVE:
|
||||
usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_CLEAR_FEATURE << 8):
|
||||
/* Only ENDPOINT_HALT is handled as feature.*/
|
||||
if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT)
|
||||
return FALSE;
|
||||
/* Clearing the EP status, not valid for EP0, it is ignored in that case.*/
|
||||
if ((usbp->setup[4] & 0x0F) > 0) {
|
||||
if (usbp->setup[4] & 0x80)
|
||||
usb_lld_clear_in(usbp, usbp->setup[4] & 0x0F);
|
||||
else
|
||||
usb_lld_clear_out(usbp, usbp->setup[4] & 0x0F);
|
||||
}
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_SET_FEATURE << 8):
|
||||
/* Only ENDPOINT_HALT is handled as feature.*/
|
||||
if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT)
|
||||
return FALSE;
|
||||
/* Stalling the EP, not valid for EP0, it is ignored in that case.*/
|
||||
if ((usbp->setup[4] & 0x0F) > 0) {
|
||||
if (usbp->setup[4] & 0x80)
|
||||
usb_lld_stall_in(usbp, usbp->setup[4] & 0x0F);
|
||||
else
|
||||
usb_lld_stall_out(usbp, usbp->setup[4] & 0x0F);
|
||||
}
|
||||
usbSetupTransfer(usbp, NULL, 0, NULL);
|
||||
return TRUE;
|
||||
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_DESCRIPTOR << 8):
|
||||
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_CLEAR_FEATURE << 8):
|
||||
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_SET_FEATURE << 8):
|
||||
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_GET_INTERFACE << 8):
|
||||
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_SET_INTERFACE << 8):
|
||||
/* All the above requests are not handled here, if you need them then
|
||||
use the hook mechanism and provide handling.*/
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief USB Driver initialization.
|
||||
* @note This function is implicitly invoked by @p halInit(), there is
|
||||
* no need to explicitly initialize the driver.
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void usbInit(void) {
|
||||
|
||||
usb_lld_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the standard part of a @p USBDriver structure.
|
||||
*
|
||||
* @param[out] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @init
|
||||
*/
|
||||
void usbObjectInit(USBDriver *usbp) {
|
||||
unsigned i;
|
||||
|
||||
usbp->state = USB_STOP;
|
||||
usbp->config = NULL;
|
||||
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
||||
usbp->in_params[i] = NULL;
|
||||
usbp->out_params[i] = NULL;
|
||||
}
|
||||
usbp->transmitting = 0;
|
||||
usbp->receiving = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the USB peripheral.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] config pointer to the @p USBConfig object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void usbStart(USBDriver *usbp, const USBConfig *config) {
|
||||
unsigned i;
|
||||
|
||||
chDbgCheck((usbp != NULL) && (config != NULL), "usbStart");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((usbp->state == USB_STOP) || (usbp->state == USB_READY),
|
||||
"usbStart(), #1", "invalid state");
|
||||
usbp->config = config;
|
||||
for (i = 0; i <= USB_MAX_ENDPOINTS; i++)
|
||||
usbp->epc[i] = NULL;
|
||||
usb_lld_start(usbp);
|
||||
usbp->state = USB_READY;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the USB peripheral.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void usbStop(USBDriver *usbp) {
|
||||
|
||||
chDbgCheck(usbp != NULL, "usbStop");
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert((usbp->state == USB_STOP) || (usbp->state == USB_READY) ||
|
||||
(usbp->state == USB_SELECTED) || (usbp->state == USB_ACTIVE),
|
||||
"usbStop(), #1", "invalid state");
|
||||
usb_lld_stop(usbp);
|
||||
usbp->state = USB_STOP;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables an endpoint.
|
||||
* @details This function enables an endpoint, both IN and/or OUT directions
|
||||
* depending on the configuration structure.
|
||||
* @note This function must be invoked in response of a SET_CONFIGURATION
|
||||
* or SET_INTERFACE message.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
* @param[in] epcp the endpoint configuration
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void usbInitEndpointI(USBDriver *usbp, usbep_t ep,
|
||||
const USBEndpointConfig *epcp) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck((usbp != NULL) && (epcp != NULL), "usbInitEndpointI");
|
||||
chDbgAssert(usbp->state == USB_ACTIVE,
|
||||
"usbEnableEndpointI(), #1", "invalid state");
|
||||
chDbgAssert(usbp->epc[ep] == NULL,
|
||||
"usbEnableEndpointI(), #2", "already initialized");
|
||||
|
||||
/* Logically enabling the endpoint in the USBDriver structure.*/
|
||||
if (epcp->in_state != NULL)
|
||||
memset(epcp->in_state, 0, sizeof(USBInEndpointState));
|
||||
if (epcp->out_state != NULL)
|
||||
memset(epcp->out_state, 0, sizeof(USBOutEndpointState));
|
||||
|
||||
usbp->epc[ep] = epcp;
|
||||
|
||||
/* Low level endpoint activation.*/
|
||||
usb_lld_init_endpoint(usbp, ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables all the active endpoints.
|
||||
* @details This function disables all the active endpoints except the
|
||||
* endpoint zero.
|
||||
* @note This function must be invoked in response of a SET_CONFIGURATION
|
||||
* message with configuration number zero.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
void usbDisableEndpointsI(USBDriver *usbp) {
|
||||
unsigned i;
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(usbp != NULL, "usbDisableEndpointsI");
|
||||
chDbgAssert(usbp->state == USB_SELECTED,
|
||||
"usbDisableEndpointsI(), #1", "invalid state");
|
||||
|
||||
usbp->transmitting &= ~1;
|
||||
usbp->receiving &= ~1;
|
||||
for (i = 1; i <= USB_MAX_ENDPOINTS; i++)
|
||||
usbp->epc[i] = NULL;
|
||||
|
||||
/* Low level endpoints deactivation.*/
|
||||
usb_lld_disable_endpoints(usbp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares for a receive transaction on an OUT endpoint.
|
||||
* @post The endpoint is ready for @p usbStartReceiveI().
|
||||
* @note This function can be called both in ISR and thread context.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
* @param[out] buf buffer where to copy the received data
|
||||
* @param[in] n transaction size
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void usbPrepareReceive(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n) {
|
||||
USBOutEndpointState *osp = usbp->epc[ep]->out_state;
|
||||
|
||||
osp->rxqueued = FALSE;
|
||||
osp->mode.linear.rxbuf = buf;
|
||||
osp->rxsize = n;
|
||||
osp->rxcnt = 0;
|
||||
|
||||
usb_lld_prepare_receive(usbp, ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares for a transmit transaction on an IN endpoint.
|
||||
* @post The endpoint is ready for @p usbStartTransmitI().
|
||||
* @note This function can be called both in ISR and thread context.
|
||||
* @note The queue must contain at least the amount of data specified
|
||||
* as transaction size.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
* @param[in] buf buffer where to fetch the data to be transmitted
|
||||
* @param[in] n transaction size
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void usbPrepareTransmit(USBDriver *usbp, usbep_t ep,
|
||||
const uint8_t *buf, size_t n) {
|
||||
USBInEndpointState *isp = usbp->epc[ep]->in_state;
|
||||
|
||||
isp->txqueued = FALSE;
|
||||
isp->mode.linear.txbuf = buf;
|
||||
isp->txsize = n;
|
||||
isp->txcnt = 0;
|
||||
|
||||
usb_lld_prepare_transmit(usbp, ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares for a receive transaction on an OUT endpoint.
|
||||
* @post The endpoint is ready for @p usbStartReceiveI().
|
||||
* @note This function can be called both in ISR and thread context.
|
||||
* @note The queue must have enough free space to accommodate the
|
||||
* specified transaction size rounded to the next packet size
|
||||
* boundary. For example if the transaction size is 1 and the
|
||||
* packet size is 64 then the queue must have space for at least
|
||||
* 64 bytes.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
* @param[in] iqp input queue to be filled with incoming data
|
||||
* @param[in] n transaction size
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void usbPrepareQueuedReceive(USBDriver *usbp, usbep_t ep,
|
||||
InputQueue *iqp, size_t n) {
|
||||
USBOutEndpointState *osp = usbp->epc[ep]->out_state;
|
||||
|
||||
osp->rxqueued = TRUE;
|
||||
osp->mode.queue.rxqueue = iqp;
|
||||
osp->rxsize = n;
|
||||
osp->rxcnt = 0;
|
||||
|
||||
usb_lld_prepare_receive(usbp, ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares for a transmit transaction on an IN endpoint.
|
||||
* @post The endpoint is ready for @p usbStartTransmitI().
|
||||
* @note This function can be called both in ISR and thread context.
|
||||
* @note The transmit transaction size is equal to the data contained
|
||||
* in the queue.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
* @param[in] oqp output queue to be fetched for outgoing data
|
||||
* @param[in] n transaction size
|
||||
*
|
||||
* @special
|
||||
*/
|
||||
void usbPrepareQueuedTransmit(USBDriver *usbp, usbep_t ep,
|
||||
OutputQueue *oqp, size_t n) {
|
||||
USBInEndpointState *isp = usbp->epc[ep]->in_state;
|
||||
|
||||
isp->txqueued = TRUE;
|
||||
isp->mode.queue.txqueue = oqp;
|
||||
isp->txsize = n;
|
||||
isp->txcnt = 0;
|
||||
|
||||
usb_lld_prepare_transmit(usbp, ep);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a receive transaction on an OUT endpoint.
|
||||
* @post The endpoint callback is invoked when the transfer has been
|
||||
* completed.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval FALSE Operation started successfully.
|
||||
* @retval TRUE Endpoint busy, operation not started.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
bool_t usbStartReceiveI(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(usbp != NULL, "usbStartReceiveI");
|
||||
|
||||
if (usbGetReceiveStatusI(usbp, ep))
|
||||
return TRUE;
|
||||
|
||||
usbp->receiving |= (1 << ep);
|
||||
usb_lld_start_out(usbp, ep);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a transmit transaction on an IN endpoint.
|
||||
* @post The endpoint callback is invoked when the transfer has been
|
||||
* completed.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval FALSE Operation started successfully.
|
||||
* @retval TRUE Endpoint busy, operation not started.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
bool_t usbStartTransmitI(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(usbp != NULL, "usbStartTransmitI");
|
||||
|
||||
if (usbGetTransmitStatusI(usbp, ep))
|
||||
return TRUE;
|
||||
|
||||
usbp->transmitting |= (1 << ep);
|
||||
usb_lld_start_in(usbp, ep);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stalls an OUT endpoint.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval FALSE Endpoint stalled.
|
||||
* @retval TRUE Endpoint busy, not stalled.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
bool_t usbStallReceiveI(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(usbp != NULL, "usbStallReceiveI");
|
||||
|
||||
if (usbGetReceiveStatusI(usbp, ep))
|
||||
return TRUE;
|
||||
|
||||
usb_lld_stall_out(usbp, ep);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stalls an IN endpoint.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval FALSE Endpoint stalled.
|
||||
* @retval TRUE Endpoint busy, not stalled.
|
||||
*
|
||||
* @iclass
|
||||
*/
|
||||
bool_t usbStallTransmitI(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
chDbgCheckClassI();
|
||||
chDbgCheck(usbp != NULL, "usbStallTransmitI");
|
||||
|
||||
if (usbGetTransmitStatusI(usbp, ep))
|
||||
return TRUE;
|
||||
|
||||
usb_lld_stall_in(usbp, ep);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB reset routine.
|
||||
* @details This function must be invoked when an USB bus reset condition is
|
||||
* detected.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void _usb_reset(USBDriver *usbp) {
|
||||
unsigned i;
|
||||
|
||||
usbp->state = USB_READY;
|
||||
usbp->status = 0;
|
||||
usbp->address = 0;
|
||||
usbp->configuration = 0;
|
||||
usbp->transmitting = 0;
|
||||
usbp->receiving = 0;
|
||||
|
||||
/* Invalidates all endpoints into the USBDriver structure.*/
|
||||
for (i = 0; i <= USB_MAX_ENDPOINTS; i++)
|
||||
usbp->epc[i] = NULL;
|
||||
|
||||
/* EP0 state machine initialization.*/
|
||||
usbp->ep0state = USB_EP0_WAITING_SETUP;
|
||||
|
||||
/* Low level reset.*/
|
||||
usb_lld_reset(usbp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default EP0 SETUP callback.
|
||||
* @details This function is used by the low level driver as default handler
|
||||
* for EP0 SETUP events.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number, always zero
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void _usb_ep0setup(USBDriver *usbp, usbep_t ep) {
|
||||
size_t max;
|
||||
|
||||
usbp->ep0state = USB_EP0_WAITING_SETUP;
|
||||
usbReadSetup(usbp, ep, usbp->setup);
|
||||
|
||||
/* First verify if the application has an handler installed for this
|
||||
request.*/
|
||||
if (!(usbp->config->requests_hook_cb) ||
|
||||
!(usbp->config->requests_hook_cb(usbp))) {
|
||||
/* Invoking the default handler, if this fails then stalls the
|
||||
endpoint zero as error.*/
|
||||
if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) != USB_RTYPE_TYPE_STD) ||
|
||||
!default_handler(usbp)) {
|
||||
/* Error response, the state machine goes into an error state, the low
|
||||
level layer will have to reset it to USB_EP0_WAITING_SETUP after
|
||||
receiving a SETUP packet.*/
|
||||
usb_lld_stall_in(usbp, 0);
|
||||
usb_lld_stall_out(usbp, 0);
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
|
||||
usbp->ep0state = USB_EP0_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if (USB_SET_ADDRESS_ACK_HANDLING == USB_SET_ADDRESS_ACK_HW)
|
||||
if (usbp->setup[1] == USB_REQ_SET_ADDRESS) {
|
||||
/* Zero-length packet sent by hardware */
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* Transfer preparation. The request handler must have populated
|
||||
correctly the fields ep0next, ep0n and ep0endcb using the macro
|
||||
usbSetupTransfer().*/
|
||||
max = usbFetchWord(&usbp->setup[6]);
|
||||
/* The transfer size cannot exceed the specified amount.*/
|
||||
if (usbp->ep0n > max)
|
||||
usbp->ep0n = max;
|
||||
if ((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) {
|
||||
/* IN phase.*/
|
||||
if (usbp->ep0n > 0) {
|
||||
/* Starts the transmit phase.*/
|
||||
usbp->ep0state = USB_EP0_TX;
|
||||
usbPrepareTransmit(usbp, 0, usbp->ep0next, usbp->ep0n);
|
||||
chSysLockFromIsr();
|
||||
usbStartTransmitI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
else {
|
||||
/* No transmission phase, directly receiving the zero sized status
|
||||
packet.*/
|
||||
usbp->ep0state = USB_EP0_WAITING_STS;
|
||||
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
|
||||
usbPrepareReceive(usbp, 0, NULL, 0);
|
||||
chSysLockFromIsr();
|
||||
usbStartReceiveI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
#else
|
||||
usb_lld_end_setup(usbp, ep);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* OUT phase.*/
|
||||
if (usbp->ep0n > 0) {
|
||||
/* Starts the receive phase.*/
|
||||
usbp->ep0state = USB_EP0_RX;
|
||||
usbPrepareReceive(usbp, 0, usbp->ep0next, usbp->ep0n);
|
||||
chSysLockFromIsr();
|
||||
usbStartReceiveI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
else {
|
||||
/* No receive phase, directly sending the zero sized status
|
||||
packet.*/
|
||||
usbp->ep0state = USB_EP0_SENDING_STS;
|
||||
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
|
||||
usbPrepareTransmit(usbp, 0, NULL, 0);
|
||||
chSysLockFromIsr();
|
||||
usbStartTransmitI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
#else
|
||||
usb_lld_end_setup(usbp, ep);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default EP0 IN callback.
|
||||
* @details This function is used by the low level driver as default handler
|
||||
* for EP0 IN events.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number, always zero
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void _usb_ep0in(USBDriver *usbp, usbep_t ep) {
|
||||
size_t max;
|
||||
|
||||
(void)ep;
|
||||
switch (usbp->ep0state) {
|
||||
case USB_EP0_TX:
|
||||
max = usbFetchWord(&usbp->setup[6]);
|
||||
/* If the transmitted size is less than the requested size and it is a
|
||||
multiple of the maximum packet size then a zero size packet must be
|
||||
transmitted.*/
|
||||
if ((usbp->ep0n < max) && ((usbp->ep0n % usbp->epc[0]->in_maxsize) == 0)) {
|
||||
usbPrepareTransmit(usbp, 0, NULL, 0);
|
||||
chSysLockFromIsr();
|
||||
usbStartTransmitI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
usbp->ep0state = USB_EP0_WAITING_TX0;
|
||||
return;
|
||||
}
|
||||
/* Falls into, it is intentional.*/
|
||||
case USB_EP0_WAITING_TX0:
|
||||
/* Transmit phase over, receiving the zero sized status packet.*/
|
||||
usbp->ep0state = USB_EP0_WAITING_STS;
|
||||
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
|
||||
usbPrepareReceive(usbp, 0, NULL, 0);
|
||||
chSysLockFromIsr();
|
||||
usbStartReceiveI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
#else
|
||||
usb_lld_end_setup(usbp, ep);
|
||||
#endif
|
||||
return;
|
||||
case USB_EP0_SENDING_STS:
|
||||
/* Status packet sent, invoking the callback if defined.*/
|
||||
if (usbp->ep0endcb != NULL)
|
||||
usbp->ep0endcb(usbp);
|
||||
usbp->ep0state = USB_EP0_WAITING_SETUP;
|
||||
return;
|
||||
default:
|
||||
;
|
||||
}
|
||||
/* Error response, the state machine goes into an error state, the low
|
||||
level layer will have to reset it to USB_EP0_WAITING_SETUP after
|
||||
receiving a SETUP packet.*/
|
||||
usb_lld_stall_in(usbp, 0);
|
||||
usb_lld_stall_out(usbp, 0);
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
|
||||
usbp->ep0state = USB_EP0_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default EP0 OUT callback.
|
||||
* @details This function is used by the low level driver as default handler
|
||||
* for EP0 OUT events.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number, always zero
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void _usb_ep0out(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
(void)ep;
|
||||
switch (usbp->ep0state) {
|
||||
case USB_EP0_RX:
|
||||
/* Receive phase over, sending the zero sized status packet.*/
|
||||
usbp->ep0state = USB_EP0_SENDING_STS;
|
||||
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
|
||||
usbPrepareTransmit(usbp, 0, NULL, 0);
|
||||
chSysLockFromIsr();
|
||||
usbStartTransmitI(usbp, 0);
|
||||
chSysUnlockFromIsr();
|
||||
#else
|
||||
usb_lld_end_setup(usbp, ep);
|
||||
#endif
|
||||
return;
|
||||
case USB_EP0_WAITING_STS:
|
||||
/* Status packet received, it must be zero sized, invoking the callback
|
||||
if defined.*/
|
||||
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
|
||||
if (usbGetReceiveTransactionSizeI(usbp, 0) != 0)
|
||||
break;
|
||||
#endif
|
||||
if (usbp->ep0endcb != NULL)
|
||||
usbp->ep0endcb(usbp);
|
||||
usbp->ep0state = USB_EP0_WAITING_SETUP;
|
||||
return;
|
||||
default:
|
||||
;
|
||||
}
|
||||
/* Error response, the state machine goes into an error state, the low
|
||||
level layer will have to reset it to USB_EP0_WAITING_SETUP after
|
||||
receiving a SETUP packet.*/
|
||||
usb_lld_stall_in(usbp, 0);
|
||||
usb_lld_stall_out(usbp, 0);
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
|
||||
usbp->ep0state = USB_EP0_ERROR;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_USB */
|
||||
|
||||
/** @} */
|
Reference in New Issue
Block a user