2017-04-30 19:39:33 +00:00
|
|
|
/* (C) 2010,2017 by Harald Welte <laforge@gnumonks.org>
|
2017-04-30 19:21:52 +00:00
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
2017-11-12 16:00:26 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*
|
2017-04-30 19:21:52 +00:00
|
|
|
* This program 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2017-05-15 15:33:02 +00:00
|
|
|
/*! \addtogroup sercomm
|
|
|
|
* @{
|
2017-06-20 02:35:06 +00:00
|
|
|
* Serial communications layer, based on HDLC.
|
|
|
|
*
|
|
|
|
* \file sercomm.c */
|
2017-05-15 15:33:02 +00:00
|
|
|
|
2017-05-15 15:16:48 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2017-04-30 19:21:52 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/msgb.h>
|
2017-05-14 19:46:08 +00:00
|
|
|
#include <osmocom/core/utils.h>
|
2017-04-30 22:19:38 +00:00
|
|
|
#include <osmocom/core/sercomm.h>
|
2017-05-14 19:46:08 +00:00
|
|
|
#include <osmocom/core/linuxlist.h>
|
2017-04-30 19:21:52 +00:00
|
|
|
|
2017-05-14 19:48:41 +00:00
|
|
|
#ifndef EMBEDDED
|
2017-04-30 22:19:13 +00:00
|
|
|
# define DEFAULT_RX_MSG_SIZE 2048
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Protect against IRQ context */
|
2017-05-15 15:16:48 +00:00
|
|
|
void sercomm_drv_lock(unsigned long __attribute__((unused)) *flags) {}
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Release protection against IRQ context */
|
2017-05-15 15:16:48 +00:00
|
|
|
void sercomm_drv_unlock(unsigned long __attribute__((unused)) *flags) {}
|
2017-04-30 19:21:52 +00:00
|
|
|
#else
|
2017-04-30 22:19:13 +00:00
|
|
|
# define DEFAULT_RX_MSG_SIZE 256
|
2017-05-15 15:16:48 +00:00
|
|
|
#endif /* EMBEDDED */
|
2017-04-30 19:21:52 +00:00
|
|
|
|
2017-05-15 15:16:48 +00:00
|
|
|
/* weak symbols to be overridden by application */
|
|
|
|
__attribute__((weak)) void sercomm_drv_start_tx(struct osmo_sercomm_inst *sercomm) {};
|
|
|
|
__attribute__((weak)) int sercomm_drv_baudrate_chg(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
2017-05-15 15:16:48 +00:00
|
|
|
return -1;
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-05-02 19:41:36 +00:00
|
|
|
#define HDLC_FLAG 0x7E
|
|
|
|
#define HDLC_ESCAPE 0x7D
|
|
|
|
|
|
|
|
#define HDLC_C_UI 0x03
|
|
|
|
#define HDLC_C_P_BIT (1 << 4)
|
|
|
|
#define HDLC_C_F_BIT (1 << 4)
|
|
|
|
|
2017-04-30 19:21:52 +00:00
|
|
|
enum rx_state {
|
|
|
|
RX_ST_WAIT_START,
|
|
|
|
RX_ST_ADDR,
|
|
|
|
RX_ST_CTRL,
|
|
|
|
RX_ST_DATA,
|
|
|
|
RX_ST_ESCAPE,
|
|
|
|
};
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Initialize an Osmocom sercomm instance
|
2017-05-15 15:33:02 +00:00
|
|
|
* \param sercomm Caller-allocated sercomm instance to be initialized
|
|
|
|
*
|
|
|
|
* This function initializes the sercomm instance, including the
|
|
|
|
* registration of the ECHO service at the ECHO DLCI
|
|
|
|
*/
|
2017-04-30 21:57:55 +00:00
|
|
|
void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
2017-04-30 19:39:33 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
|
|
|
|
INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
|
2017-04-30 19:21:52 +00:00
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.msg = NULL;
|
2017-04-30 22:19:13 +00:00
|
|
|
if (!sercomm->rx.msg_size)
|
|
|
|
sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->initialized = 1;
|
2017-04-30 19:21:52 +00:00
|
|
|
|
|
|
|
/* set up the echo dlci */
|
2017-04-30 21:57:55 +00:00
|
|
|
osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Determine if a given Osmocom sercomm instance has been initialized
|
2017-05-15 15:33:02 +00:00
|
|
|
* \param[in] sercomm Osmocom sercomm instance to be checked
|
|
|
|
* \returns 1 in case \a sercomm was previously initialized; 0 otherwise */
|
2017-04-30 21:57:55 +00:00
|
|
|
int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
2017-04-30 19:39:33 +00:00
|
|
|
return sercomm->initialized;
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! User interface for transmitting messages for a given DLCI
|
2017-05-15 15:33:02 +00:00
|
|
|
* \param[in] sercomm Osmocom sercomm instance through which to transmit
|
|
|
|
* \param[in] dlci DLCI through whcih to transmit \a msg
|
|
|
|
* \param[in] msg Message buffer to be transmitted via \a dlci on \a * sercomm
|
|
|
|
**/
|
2017-04-30 21:57:55 +00:00
|
|
|
void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
uint8_t *hdr;
|
|
|
|
|
|
|
|
/* prepend address + control octet */
|
|
|
|
hdr = msgb_push(msg, 2);
|
|
|
|
hdr[0] = dlci;
|
|
|
|
hdr[1] = HDLC_C_UI;
|
|
|
|
|
|
|
|
/* This functiion can be called from any context: FIQ, IRQ
|
|
|
|
* and supervisor context. Proper locking is important! */
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_lock(&flags);
|
2017-04-30 19:39:33 +00:00
|
|
|
msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_unlock(&flags);
|
2017-04-30 19:21:52 +00:00
|
|
|
|
|
|
|
/* tell UART that we have something to send */
|
2017-05-15 15:16:48 +00:00
|
|
|
sercomm_drv_start_tx(sercomm);
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! How deep is the Tx queue for a given DLCI?
|
2017-05-15 15:33:02 +00:00
|
|
|
* \param[n] sercomm Osmocom sercomm instance on which to operate
|
|
|
|
* \param[in] dlci DLCI whose queue depthy is to be determined
|
|
|
|
* \returns number of elements in the per-DLCI transmit queue */
|
2017-04-30 21:57:55 +00:00
|
|
|
unsigned int osmo_sercomm_tx_queue_depth(struct osmo_sercomm_inst *sercomm, uint8_t dlci)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
|
|
|
struct llist_head *le;
|
|
|
|
unsigned int num = 0;
|
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
|
2017-04-30 19:21:52 +00:00
|
|
|
num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! wait until everything has been transmitted, then grab the lock and
|
2017-05-15 15:33:02 +00:00
|
|
|
* change the baud rate as requested
|
|
|
|
* \param[in] sercomm Osmocom sercomm instance
|
|
|
|
* \param[in] bdrt New UART Baud Rate
|
|
|
|
* \returns result of the operation as provided by sercomm_drv_baudrate_chg()
|
|
|
|
*/
|
2017-05-15 15:16:48 +00:00
|
|
|
int osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
|
|
|
unsigned int i, count;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
/* count the number of pending messages */
|
|
|
|
count = 0;
|
2017-04-30 19:39:33 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
|
2017-05-15 15:16:48 +00:00
|
|
|
count += osmo_sercomm_tx_queue_depth(sercomm, i);
|
2017-04-30 19:21:52 +00:00
|
|
|
/* if we still have any in the queue, restart */
|
|
|
|
if (count == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
/* no messages in the queue, grab the lock to ensure it
|
|
|
|
* stays that way */
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_lock(&flags);
|
2017-04-30 19:39:33 +00:00
|
|
|
if (!sercomm->tx.msg && !sercomm->tx.next_char) {
|
2017-05-15 15:16:48 +00:00
|
|
|
int rc;
|
2017-04-30 19:21:52 +00:00
|
|
|
/* change speed */
|
2017-05-15 15:16:48 +00:00
|
|
|
rc = sercomm_drv_baudrate_chg(sercomm, bdrt);
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_unlock(&flags);
|
2017-05-15 15:16:48 +00:00
|
|
|
return rc;
|
|
|
|
} else
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_unlock(&flags);
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
2017-05-15 15:16:48 +00:00
|
|
|
return -1;
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! fetch one octet of to-be-transmitted serial data
|
2017-05-02 19:24:48 +00:00
|
|
|
* \param[in] sercomm Sercomm Instance from which to fetch pending data
|
|
|
|
* \param[out] ch pointer to caller-allocaed output memory
|
|
|
|
* \returns 1 in case of succss; 0 if no data available; negative on error */
|
2017-04-30 21:57:55 +00:00
|
|
|
int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/* we may be called from interrupt context, but we stiff need to lock
|
|
|
|
* because sercomm could be accessed from a FIQ context ... */
|
|
|
|
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_lock(&flags);
|
2017-04-30 19:21:52 +00:00
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
if (!sercomm->tx.msg) {
|
2017-04-30 19:21:52 +00:00
|
|
|
unsigned int i;
|
|
|
|
/* dequeue a new message from the queues */
|
2017-04-30 19:39:33 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
|
|
|
|
sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
|
|
|
|
if (sercomm->tx.msg)
|
2017-04-30 19:21:52 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-04-30 19:39:33 +00:00
|
|
|
if (sercomm->tx.msg) {
|
2017-04-30 19:21:52 +00:00
|
|
|
/* start of a new message, send start flag octet */
|
|
|
|
*ch = HDLC_FLAG;
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->tx.next_char = sercomm->tx.msg->data;
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_unlock(&flags);
|
2017-04-30 19:21:52 +00:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
/* no more data avilable */
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_unlock(&flags);
|
2017-04-30 19:21:52 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
if (sercomm->tx.state == RX_ST_ESCAPE) {
|
2017-04-30 19:21:52 +00:00
|
|
|
/* we've already transmitted the ESCAPE octet,
|
|
|
|
* we now need to transmit the escaped data */
|
2017-04-30 19:39:33 +00:00
|
|
|
*ch = *sercomm->tx.next_char++;
|
|
|
|
sercomm->tx.state = RX_ST_DATA;
|
|
|
|
} else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
|
2017-04-30 19:21:52 +00:00
|
|
|
/* last character has already been transmitted,
|
|
|
|
* send end-of-message octet */
|
|
|
|
*ch = HDLC_FLAG;
|
|
|
|
/* we've reached the end of the message buffer */
|
2017-04-30 19:39:33 +00:00
|
|
|
msgb_free(sercomm->tx.msg);
|
|
|
|
sercomm->tx.msg = NULL;
|
|
|
|
sercomm->tx.next_char = NULL;
|
2017-04-30 19:21:52 +00:00
|
|
|
/* escaping for the two control octets */
|
2017-04-30 19:39:33 +00:00
|
|
|
} else if (*sercomm->tx.next_char == HDLC_FLAG ||
|
|
|
|
*sercomm->tx.next_char == HDLC_ESCAPE ||
|
|
|
|
*sercomm->tx.next_char == 0x00) {
|
2017-04-30 19:21:52 +00:00
|
|
|
/* send an escape octet */
|
|
|
|
*ch = HDLC_ESCAPE;
|
|
|
|
/* invert bit 5 of the next octet to be sent */
|
2017-04-30 19:39:33 +00:00
|
|
|
*sercomm->tx.next_char ^= (1 << 5);
|
|
|
|
sercomm->tx.state = RX_ST_ESCAPE;
|
2017-04-30 19:21:52 +00:00
|
|
|
} else {
|
|
|
|
/* standard case, simply send next octet */
|
2017-04-30 19:39:33 +00:00
|
|
|
*ch = *sercomm->tx.next_char++;
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-05-14 19:46:08 +00:00
|
|
|
sercomm_drv_unlock(&flags);
|
2017-04-30 19:21:52 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! Register a handler for a given DLCI
|
2017-05-02 19:24:48 +00:00
|
|
|
* \param sercomm Sercomm Instance in which caller wishes to register
|
|
|
|
* \param[in] dlci Data Ling Connection Identifier to register
|
|
|
|
* \param[in] cb Callback function for \a dlci
|
|
|
|
* \returns 0 on success; negative on error */
|
2017-04-30 21:57:55 +00:00
|
|
|
int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
2017-04-30 19:39:33 +00:00
|
|
|
if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
|
2017-04-30 19:21:52 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
if (sercomm->rx.dlci_handler[dlci])
|
2017-04-30 19:21:52 +00:00
|
|
|
return -EBUSY;
|
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.dlci_handler[dlci] = cb;
|
2017-04-30 19:21:52 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dispatch an incoming message once it is completely received */
|
2017-04-30 21:57:55 +00:00
|
|
|
static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
2017-04-30 19:39:33 +00:00
|
|
|
if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
|
|
|
|
!sercomm->rx.dlci_handler[dlci]) {
|
2017-04-30 19:21:52 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
|
2017-04-30 19:21:52 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 22:17:59 +00:00
|
|
|
/*! the driver has received one byte, pass it into sercomm layer
|
2017-05-02 19:24:48 +00:00
|
|
|
* \param[in] sercomm Sercomm Instance for which a byte was received
|
|
|
|
* \param[in] ch byte that was received from line for said instance
|
|
|
|
* \returns 1 on success; 0 on unrecognized char; negative on error */
|
2017-04-30 21:57:55 +00:00
|
|
|
int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
|
2017-04-30 19:21:52 +00:00
|
|
|
{
|
|
|
|
uint8_t *ptr;
|
|
|
|
|
|
|
|
/* we are always called from interrupt context in this function,
|
|
|
|
* which means that any data structures we use need to be for
|
|
|
|
* our exclusive access */
|
2017-04-30 19:39:33 +00:00
|
|
|
if (!sercomm->rx.msg)
|
2017-04-30 22:19:13 +00:00
|
|
|
sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
|
2017-04-30 19:21:52 +00:00
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
if (msgb_tailroom(sercomm->rx.msg) == 0) {
|
2017-04-30 19:21:52 +00:00
|
|
|
//cons_puts("sercomm_drv_rx_char() overflow!\n");
|
2017-04-30 19:39:33 +00:00
|
|
|
msgb_free(sercomm->rx.msg);
|
2017-04-30 22:19:13 +00:00
|
|
|
sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.state = RX_ST_WAIT_START;
|
2017-04-30 19:21:52 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-30 19:39:33 +00:00
|
|
|
switch (sercomm->rx.state) {
|
2017-04-30 19:21:52 +00:00
|
|
|
case RX_ST_WAIT_START:
|
|
|
|
if (ch != HDLC_FLAG)
|
|
|
|
break;
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.state = RX_ST_ADDR;
|
2017-04-30 19:21:52 +00:00
|
|
|
break;
|
|
|
|
case RX_ST_ADDR:
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.dlci = ch;
|
|
|
|
sercomm->rx.state = RX_ST_CTRL;
|
2017-04-30 19:21:52 +00:00
|
|
|
break;
|
|
|
|
case RX_ST_CTRL:
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.ctrl = ch;
|
|
|
|
sercomm->rx.state = RX_ST_DATA;
|
2017-04-30 19:21:52 +00:00
|
|
|
break;
|
|
|
|
case RX_ST_DATA:
|
|
|
|
if (ch == HDLC_ESCAPE) {
|
|
|
|
/* drop the escape octet, but change state */
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.state = RX_ST_ESCAPE;
|
2017-04-30 19:21:52 +00:00
|
|
|
break;
|
|
|
|
} else if (ch == HDLC_FLAG) {
|
|
|
|
/* message is finished */
|
2017-04-30 19:39:33 +00:00
|
|
|
dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
|
2017-04-30 19:21:52 +00:00
|
|
|
/* allocate new buffer */
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.msg = NULL;
|
2017-04-30 19:21:52 +00:00
|
|
|
/* start all over again */
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.state = RX_ST_WAIT_START;
|
2017-04-30 19:21:52 +00:00
|
|
|
|
|
|
|
/* do not add the control char */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* default case: store the octet */
|
2017-04-30 19:39:33 +00:00
|
|
|
ptr = msgb_put(sercomm->rx.msg, 1);
|
2017-04-30 19:21:52 +00:00
|
|
|
*ptr = ch;
|
|
|
|
break;
|
|
|
|
case RX_ST_ESCAPE:
|
|
|
|
/* store bif-5-inverted octet in buffer */
|
|
|
|
ch ^= (1 << 5);
|
2017-04-30 19:39:33 +00:00
|
|
|
ptr = msgb_put(sercomm->rx.msg, 1);
|
2017-04-30 19:21:52 +00:00
|
|
|
*ptr = ch;
|
|
|
|
/* transition back to normal DATA state */
|
2017-04-30 19:39:33 +00:00
|
|
|
sercomm->rx.state = RX_ST_DATA;
|
2017-04-30 19:21:52 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2017-05-15 15:33:02 +00:00
|
|
|
|
|
|
|
/*! @} */
|