ChibiOS/os/hal/lib/complex/buffered_sio/hal_buffered_sio.c

252 lines
7.4 KiB
C

/*
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file hal_buffered_sio.c
* @brief Buffered SIO Driver code.
*
* @addtogroup HAL_BUFFERED_SIO
* @{
*/
#include "hal.h"
#if (HAL_USE_SIO == TRUE) || defined(__DOXYGEN__)
#include "hal_buffered_sio.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static void __bsio_push_data(BufferedSIODriver *bsiop) {
while (!sioIsTXFullX(bsiop->siop)) {
msg_t msg;
msg = oqGetI(&bsiop->oqueue);
if (msg < MSG_OK) {
chnAddFlagsI((BufferedSerial *)bsiop, CHN_OUTPUT_EMPTY);
return;
}
sioPutX(bsiop->siop, (uint_fast16_t)msg);
}
}
static void __bsio_pop_data(BufferedSIODriver *bsiop) {
/* RX FIFO needs to be fully emptied or SIO will not generate more RX FIFO
events.*/
while (!sioIsRXEmptyX(bsiop->siop)) {
bsIncomingDataI((BufferedSerial *)bsiop, sioGetX(bsiop->siop));
}
}
static void __bsio_default_cb(SIODriver *siop) {
BufferedSIODriver *bsiop = (BufferedSIODriver *)siop->arg;
sioevents_t events;
osalSysLockFromISR();
/* Posting the non-data SIO events as channel event flags, the masks are
made to match.*/
events = sioGetAndClearEventsI(siop);
chnAddFlagsI(bsiop, (eventflags_t)(events & ~SIO_EV_ALL_DATA));
/* RX FIFO event.*/
if ((events & SIO_EV_RXNOTEMPY) != (sioevents_t)0) {
__bsio_pop_data(bsiop);
}
/* TX FIFO event.*/
if ((events & SIO_EV_TXNOTFULL) != (sioevents_t)0) {
__bsio_push_data(bsiop);
}
osalSysUnlockFromISR();
}
static void __bsio_onotify(io_queue_t *qp) {
__bsio_push_data((BufferedSIODriver *)qp->q_link);
}
/*
* Interface implementation.
*/
static size_t __bsio_write(void *ip, const uint8_t *bp, size_t n) {
return __buffered_serial_write_impl(ip, bp, n);
}
static size_t __bsio_read(void *ip, uint8_t *bp, size_t n) {
return __buffered_serial_read_impl(ip, bp, n);
}
static msg_t __bsio_put(void *ip, uint8_t b) {
return __buffered_serial_put_impl(ip, b);
}
static msg_t __bsio_get(void *ip) {
return __buffered_serial_get_impl(ip);
}
static msg_t __bsio_putt(void *ip, uint8_t b, sysinterval_t timeout) {
return __buffered_serial_put_timeout_impl(ip, b, timeout);
}
static msg_t __bsio_gett(void *ip, sysinterval_t timeout) {
return __buffered_serial_get_timeout_impl(ip, timeout);
}
static size_t __bsio_writet(void *ip, const uint8_t *bp, size_t n,
sysinterval_t timeout) {
return __buffered_serial_write_timeout_impl(ip, bp, n, timeout);
}
static size_t __bsio_readt(void *ip, uint8_t *bp, size_t n,
sysinterval_t timeout) {
return __buffered_serial_read_timeout_impl(ip, bp, n, timeout);
}
static msg_t __bsio_ctl(void *ip, unsigned int operation, void *arg) {
return sioControlX(((BufferedSIODriver *)ip)->siop, operation, arg);
}
static const struct BufferedSIODriverVMT vmt = {
(size_t)0,
__bsio_write, __bsio_read, __bsio_put, __bsio_get,
__bsio_putt, __bsio_gett, __bsio_writet, __bsio_readt,
__bsio_ctl
};
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Initializes a generic serial driver object.
* @details The HW dependent part of the initialization has to be performed
* outside, usually in the hardware initialization code.
*
* @param[out] bsiop pointer to a @p BufferedSIODriver structure
* @param[in] siop pointer to the @p SIODriver object
* @param[in] ib pointer to the input buffer
* @param[in] ibsize size of the input buffer
* @param[in] ob pointer to the output buffer
* @param[in] obsize size of the output buffer
*
* @init
*/
void bsioObjectInit(BufferedSIODriver *bsiop, SIODriver *siop,
uint8_t *ib, size_t ibsize,
uint8_t *ob, size_t obsize) {
__buffered_serial_objinit_impl((void *)bsiop, (const void *)&vmt,
ib, ibsize, NULL, NULL,
ob, obsize, __bsio_onotify, (void *)bsiop);
bsiop->siop = siop;
siop->arg = (void *)bsiop;
}
/**
* @brief Configures and starts the driver.
*
* @param[out] bsiop pointer to a @p BufferedSIODriver structure
* @param[in] config the architecture-dependent serial driver configuration.
* If this parameter is set to @p NULL then a default
* configuration is used.
* @return The operation status.
*
* @api
*/
msg_t bsioStart(BufferedSIODriver *bsiop, const BufferedSIOConfig *config) {
msg_t msg;
osalDbgCheck(bsiop != NULL);
osalDbgAssert((bsiop->state == BS_STOP) || (bsiop->state == BS_READY),
"invalid state");
msg = sioStart(bsiop->siop, config);
if (msg == HAL_RET_SUCCESS) {
osalSysLock();
sioSetCallbackX(bsiop->siop, &__bsio_default_cb);
sioWriteEnableFlagsI(bsiop->siop, SIO_FL_ALL);
bsiop->state = BS_READY;
osalSysUnlock();
}
else {
bsiop->state = BS_STOP;
}
return msg;
}
/**
* @brief Stops the driver.
* @details Any thread waiting on the driver's queues will be awakened with
* the message @p MSG_RESET.
*
* @param[out] bsiop pointer to a @p BufferedSIODriver structure
*
* @api
*/
void bsioStop(BufferedSIODriver *bsiop) {
osalDbgCheck(bsiop != NULL);
osalDbgAssert((bsiop->state == BS_STOP) || (bsiop->state == BS_READY),
"invalid state");
/* Stopping undelying SIO driver.*/
sioStop(bsiop->siop);
bsiop->state = BS_STOP;
osalSysLock();
oqResetI(&bsiop->oqueue); /* TODO should go in the upper class.*/
iqResetI(&bsiop->iqueue);
osalOsRescheduleS();
osalSysUnlock();
}
#endif /* HAL_USE_SIO == TRUE */
/** @} */