blob: 409efba3a5632ebb99712c7725e31174625a4f27 [file] [log] [blame]
Harald Weltec68af6a2017-04-30 21:21:52 +02001/* Serial communications layer, based on HDLC */
2
Harald Weltecc95f4b2017-04-30 21:39:33 +02003/* (C) 2010,2017 by Harald Welte <laforge@gnumonks.org>
Harald Weltec68af6a2017-04-30 21:21:52 +02004 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
Harald Welte799bef52017-05-15 17:16:48 +020023#include "config.h"
24
Harald Weltec68af6a2017-04-30 21:21:52 +020025#include <stdint.h>
26#include <stdio.h>
27#include <errno.h>
28
29#include <osmocom/core/msgb.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020030#include <osmocom/core/utils.h>
Harald Welte1a97e2c2017-05-01 00:19:38 +020031#include <osmocom/core/sercomm.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020032#include <osmocom/core/linuxlist.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020033
Harald Welte7af6d222017-05-14 21:48:41 +020034#ifndef EMBEDDED
Harald Weltef6adcd72017-05-01 00:19:13 +020035# define DEFAULT_RX_MSG_SIZE 2048
Harald Welte799bef52017-05-15 17:16:48 +020036void sercomm_drv_lock(unsigned long __attribute__((unused)) *flags) {}
37void sercomm_drv_unlock(unsigned long __attribute__((unused)) *flags) {}
Harald Weltec68af6a2017-04-30 21:21:52 +020038#else
Harald Weltef6adcd72017-05-01 00:19:13 +020039# define DEFAULT_RX_MSG_SIZE 256
Harald Welte799bef52017-05-15 17:16:48 +020040#endif /* EMBEDDED */
Harald Weltec68af6a2017-04-30 21:21:52 +020041
Harald Welte799bef52017-05-15 17:16:48 +020042/* weak symbols to be overridden by application */
43__attribute__((weak)) void sercomm_drv_start_tx(struct osmo_sercomm_inst *sercomm) {};
44__attribute__((weak)) int sercomm_drv_baudrate_chg(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +020045{
Harald Welte799bef52017-05-15 17:16:48 +020046 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +020047}
48
Harald Welte8a4eb832017-05-02 21:41:36 +020049#define HDLC_FLAG 0x7E
50#define HDLC_ESCAPE 0x7D
51
52#define HDLC_C_UI 0x03
53#define HDLC_C_P_BIT (1 << 4)
54#define HDLC_C_F_BIT (1 << 4)
55
Harald Weltec68af6a2017-04-30 21:21:52 +020056enum rx_state {
57 RX_ST_WAIT_START,
58 RX_ST_ADDR,
59 RX_ST_CTRL,
60 RX_ST_DATA,
61 RX_ST_ESCAPE,
62};
63
Harald Welte13588362017-04-30 23:57:55 +020064void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020065{
66 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020067 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
68 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020069
Harald Weltecc95f4b2017-04-30 21:39:33 +020070 sercomm->rx.msg = NULL;
Harald Weltef6adcd72017-05-01 00:19:13 +020071 if (!sercomm->rx.msg_size)
72 sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
Harald Weltecc95f4b2017-04-30 21:39:33 +020073 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020074
75 /* set up the echo dlci */
Harald Welte13588362017-04-30 23:57:55 +020076 osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020077}
78
Harald Welte13588362017-04-30 23:57:55 +020079int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020080{
Harald Weltecc95f4b2017-04-30 21:39:33 +020081 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +020082}
83
84/* user interface for transmitting messages for a given DLCI */
Harald Welte13588362017-04-30 23:57:55 +020085void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +020086{
87 unsigned long flags;
88 uint8_t *hdr;
89
90 /* prepend address + control octet */
91 hdr = msgb_push(msg, 2);
92 hdr[0] = dlci;
93 hdr[1] = HDLC_C_UI;
94
95 /* This functiion can be called from any context: FIQ, IRQ
96 * and supervisor context. Proper locking is important! */
Harald Weltedf8e89f2017-05-14 21:46:08 +020097 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +020098 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltedf8e89f2017-05-14 21:46:08 +020099 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200100
Harald Weltec68af6a2017-04-30 21:21:52 +0200101 /* tell UART that we have something to send */
Harald Welte799bef52017-05-15 17:16:48 +0200102 sercomm_drv_start_tx(sercomm);
Harald Weltec68af6a2017-04-30 21:21:52 +0200103}
104
105/* how deep is the Tx queue for a given DLCI */
Harald Welte13588362017-04-30 23:57:55 +0200106unsigned int osmo_sercomm_tx_queue_depth(struct osmo_sercomm_inst *sercomm, uint8_t dlci)
Harald Weltec68af6a2017-04-30 21:21:52 +0200107{
108 struct llist_head *le;
109 unsigned int num = 0;
110
Harald Weltecc95f4b2017-04-30 21:39:33 +0200111 llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200112 num++;
113 }
114
115 return num;
116}
117
Harald Weltec68af6a2017-04-30 21:21:52 +0200118/* wait until everything has been transmitted, then grab the lock and
119 * change the baud rate as requested */
Harald Welte799bef52017-05-15 17:16:48 +0200120int osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200121{
122 unsigned int i, count;
123 unsigned long flags;
124
125 while (1) {
126 /* count the number of pending messages */
127 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200128 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Welte799bef52017-05-15 17:16:48 +0200129 count += osmo_sercomm_tx_queue_depth(sercomm, i);
Harald Weltec68af6a2017-04-30 21:21:52 +0200130 /* if we still have any in the queue, restart */
131 if (count == 0)
132 break;
133 }
134
135 while (1) {
136 /* no messages in the queue, grab the lock to ensure it
137 * stays that way */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200138 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200139 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Welte799bef52017-05-15 17:16:48 +0200140 int rc;
Harald Weltec68af6a2017-04-30 21:21:52 +0200141 /* change speed */
Harald Welte799bef52017-05-15 17:16:48 +0200142 rc = sercomm_drv_baudrate_chg(sercomm, bdrt);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200143 sercomm_drv_unlock(&flags);
Harald Welte799bef52017-05-15 17:16:48 +0200144 return rc;
145 } else
Harald Weltedf8e89f2017-05-14 21:46:08 +0200146 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200147 }
Harald Welte799bef52017-05-15 17:16:48 +0200148 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +0200149}
Harald Weltec68af6a2017-04-30 21:21:52 +0200150
Harald Welteea3d3ba2017-05-02 21:24:48 +0200151/*! \brief fetch one octet of to-be-transmitted serial data
152 * \param[in] sercomm Sercomm Instance from which to fetch pending data
153 * \param[out] ch pointer to caller-allocaed output memory
154 * \returns 1 in case of succss; 0 if no data available; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200155int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200156{
157 unsigned long flags;
158
159 /* we may be called from interrupt context, but we stiff need to lock
160 * because sercomm could be accessed from a FIQ context ... */
161
Harald Weltedf8e89f2017-05-14 21:46:08 +0200162 sercomm_drv_lock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200163
Harald Weltecc95f4b2017-04-30 21:39:33 +0200164 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200165 unsigned int i;
166 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200167 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
168 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
169 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200170 break;
171 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200172 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200173 /* start of a new message, send start flag octet */
174 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200175 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltedf8e89f2017-05-14 21:46:08 +0200176 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200177 return 1;
178 } else {
179 /* no more data avilable */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200180 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200181 return 0;
182 }
183 }
184
Harald Weltecc95f4b2017-04-30 21:39:33 +0200185 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200186 /* we've already transmitted the ESCAPE octet,
187 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200188 *ch = *sercomm->tx.next_char++;
189 sercomm->tx.state = RX_ST_DATA;
190 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200191 /* last character has already been transmitted,
192 * send end-of-message octet */
193 *ch = HDLC_FLAG;
194 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200195 msgb_free(sercomm->tx.msg);
196 sercomm->tx.msg = NULL;
197 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200198 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200199 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
200 *sercomm->tx.next_char == HDLC_ESCAPE ||
201 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200202 /* send an escape octet */
203 *ch = HDLC_ESCAPE;
204 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200205 *sercomm->tx.next_char ^= (1 << 5);
206 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200207 } else {
208 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200209 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200210 }
211
Harald Weltedf8e89f2017-05-14 21:46:08 +0200212 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200213 return 1;
214}
215
Harald Welteea3d3ba2017-05-02 21:24:48 +0200216/*! \brief Register a handler for a given DLCI
217 * \param sercomm Sercomm Instance in which caller wishes to register
218 * \param[in] dlci Data Ling Connection Identifier to register
219 * \param[in] cb Callback function for \a dlci
220 * \returns 0 on success; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200221int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200222{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200223 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200224 return -EINVAL;
225
Harald Weltecc95f4b2017-04-30 21:39:33 +0200226 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200227 return -EBUSY;
228
Harald Weltecc95f4b2017-04-30 21:39:33 +0200229 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200230 return 0;
231}
232
233/* dispatch an incoming message once it is completely received */
Harald Welte13588362017-04-30 23:57:55 +0200234static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200235{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200236 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
237 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200238 msgb_free(msg);
239 return;
240 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200241 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200242}
243
Harald Welteea3d3ba2017-05-02 21:24:48 +0200244/*! \brief the driver has received one byte, pass it into sercomm layer
245 * \param[in] sercomm Sercomm Instance for which a byte was received
246 * \param[in] ch byte that was received from line for said instance
247 * \returns 1 on success; 0 on unrecognized char; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200248int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200249{
250 uint8_t *ptr;
251
252 /* we are always called from interrupt context in this function,
253 * which means that any data structures we use need to be for
254 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200255 if (!sercomm->rx.msg)
Harald Weltef6adcd72017-05-01 00:19:13 +0200256 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltec68af6a2017-04-30 21:21:52 +0200257
Harald Weltecc95f4b2017-04-30 21:39:33 +0200258 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200259 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200260 msgb_free(sercomm->rx.msg);
Harald Weltef6adcd72017-05-01 00:19:13 +0200261 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200262 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200263 return 0;
264 }
265
Harald Weltecc95f4b2017-04-30 21:39:33 +0200266 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200267 case RX_ST_WAIT_START:
268 if (ch != HDLC_FLAG)
269 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200270 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200271 break;
272 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200273 sercomm->rx.dlci = ch;
274 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200275 break;
276 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200277 sercomm->rx.ctrl = ch;
278 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200279 break;
280 case RX_ST_DATA:
281 if (ch == HDLC_ESCAPE) {
282 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200283 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200284 break;
285 } else if (ch == HDLC_FLAG) {
286 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200287 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200288 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200289 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200290 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200291 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200292
293 /* do not add the control char */
294 break;
295 }
296 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200297 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200298 *ptr = ch;
299 break;
300 case RX_ST_ESCAPE:
301 /* store bif-5-inverted octet in buffer */
302 ch ^= (1 << 5);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200303 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200304 *ptr = ch;
305 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200306 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200307 break;
308 }
309
310 return 1;
311}