blob: 33d48531751c6bdd3e7efe5843c07765001efb8a [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 Welte77117132017-05-15 17:33:02 +020023/*! \addtogroup sercomm
24 * @{
Harald Welte96e2a002017-06-12 21:44:18 +020025 * \brief Serial communications layer, based on HDLC
Harald Welte77117132017-05-15 17:33:02 +020026 */
27
28/*! \file sercomm.c
29 */
30
Harald Welte799bef52017-05-15 17:16:48 +020031#include "config.h"
32
Harald Weltec68af6a2017-04-30 21:21:52 +020033#include <stdint.h>
34#include <stdio.h>
35#include <errno.h>
36
37#include <osmocom/core/msgb.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020038#include <osmocom/core/utils.h>
Harald Welte1a97e2c2017-05-01 00:19:38 +020039#include <osmocom/core/sercomm.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020040#include <osmocom/core/linuxlist.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020041
Harald Welte7af6d222017-05-14 21:48:41 +020042#ifndef EMBEDDED
Harald Weltef6adcd72017-05-01 00:19:13 +020043# define DEFAULT_RX_MSG_SIZE 2048
Harald Welte77117132017-05-15 17:33:02 +020044/*! \brief Protect against IRQ context */
Harald Welte799bef52017-05-15 17:16:48 +020045void sercomm_drv_lock(unsigned long __attribute__((unused)) *flags) {}
Harald Welte77117132017-05-15 17:33:02 +020046/*! \brief Release protection against IRQ context */
Harald Welte799bef52017-05-15 17:16:48 +020047void sercomm_drv_unlock(unsigned long __attribute__((unused)) *flags) {}
Harald Weltec68af6a2017-04-30 21:21:52 +020048#else
Harald Weltef6adcd72017-05-01 00:19:13 +020049# define DEFAULT_RX_MSG_SIZE 256
Harald Welte799bef52017-05-15 17:16:48 +020050#endif /* EMBEDDED */
Harald Weltec68af6a2017-04-30 21:21:52 +020051
Harald Welte799bef52017-05-15 17:16:48 +020052/* weak symbols to be overridden by application */
53__attribute__((weak)) void sercomm_drv_start_tx(struct osmo_sercomm_inst *sercomm) {};
54__attribute__((weak)) int sercomm_drv_baudrate_chg(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +020055{
Harald Welte799bef52017-05-15 17:16:48 +020056 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +020057}
58
Harald Welte8a4eb832017-05-02 21:41:36 +020059#define HDLC_FLAG 0x7E
60#define HDLC_ESCAPE 0x7D
61
62#define HDLC_C_UI 0x03
63#define HDLC_C_P_BIT (1 << 4)
64#define HDLC_C_F_BIT (1 << 4)
65
Harald Weltec68af6a2017-04-30 21:21:52 +020066enum rx_state {
67 RX_ST_WAIT_START,
68 RX_ST_ADDR,
69 RX_ST_CTRL,
70 RX_ST_DATA,
71 RX_ST_ESCAPE,
72};
73
Harald Welte77117132017-05-15 17:33:02 +020074/*! \brief Initialize an Osmocom sercomm instance
75 * \param sercomm Caller-allocated sercomm instance to be initialized
76 *
77 * This function initializes the sercomm instance, including the
78 * registration of the ECHO service at the ECHO DLCI
79 */
Harald Welte13588362017-04-30 23:57:55 +020080void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020081{
82 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020083 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
84 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020085
Harald Weltecc95f4b2017-04-30 21:39:33 +020086 sercomm->rx.msg = NULL;
Harald Weltef6adcd72017-05-01 00:19:13 +020087 if (!sercomm->rx.msg_size)
88 sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
Harald Weltecc95f4b2017-04-30 21:39:33 +020089 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020090
91 /* set up the echo dlci */
Harald Welte13588362017-04-30 23:57:55 +020092 osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020093}
94
Harald Welte77117132017-05-15 17:33:02 +020095/*! \brief Determine if a given Osmocom sercomm instance has been initialized
96 * \param[in] sercomm Osmocom sercomm instance to be checked
97 * \returns 1 in case \a sercomm was previously initialized; 0 otherwise */
Harald Welte13588362017-04-30 23:57:55 +020098int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020099{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200100 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +0200101}
102
Harald Welte77117132017-05-15 17:33:02 +0200103/*! \brief User interface for transmitting messages for a given DLCI
104 * \param[in] sercomm Osmocom sercomm instance through which to transmit
105 * \param[in] dlci DLCI through whcih to transmit \a msg
106 * \param[in] msg Message buffer to be transmitted via \a dlci on \a * sercomm
107 **/
Harald Welte13588362017-04-30 23:57:55 +0200108void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200109{
110 unsigned long flags;
111 uint8_t *hdr;
112
113 /* prepend address + control octet */
114 hdr = msgb_push(msg, 2);
115 hdr[0] = dlci;
116 hdr[1] = HDLC_C_UI;
117
118 /* This functiion can be called from any context: FIQ, IRQ
119 * and supervisor context. Proper locking is important! */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200120 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200121 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200122 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200123
Harald Weltec68af6a2017-04-30 21:21:52 +0200124 /* tell UART that we have something to send */
Harald Welte799bef52017-05-15 17:16:48 +0200125 sercomm_drv_start_tx(sercomm);
Harald Weltec68af6a2017-04-30 21:21:52 +0200126}
127
Harald Welte77117132017-05-15 17:33:02 +0200128/*! \brief How deep is the Tx queue for a given DLCI?
129 * \param[n] sercomm Osmocom sercomm instance on which to operate
130 * \param[in] dlci DLCI whose queue depthy is to be determined
131 * \returns number of elements in the per-DLCI transmit queue */
Harald Welte13588362017-04-30 23:57:55 +0200132unsigned int osmo_sercomm_tx_queue_depth(struct osmo_sercomm_inst *sercomm, uint8_t dlci)
Harald Weltec68af6a2017-04-30 21:21:52 +0200133{
134 struct llist_head *le;
135 unsigned int num = 0;
136
Harald Weltecc95f4b2017-04-30 21:39:33 +0200137 llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200138 num++;
139 }
140
141 return num;
142}
143
Harald Welte77117132017-05-15 17:33:02 +0200144/*! \brief wait until everything has been transmitted, then grab the lock and
145 * change the baud rate as requested
146 * \param[in] sercomm Osmocom sercomm instance
147 * \param[in] bdrt New UART Baud Rate
148 * \returns result of the operation as provided by sercomm_drv_baudrate_chg()
149 */
Harald Welte799bef52017-05-15 17:16:48 +0200150int osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200151{
152 unsigned int i, count;
153 unsigned long flags;
154
155 while (1) {
156 /* count the number of pending messages */
157 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200158 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Welte799bef52017-05-15 17:16:48 +0200159 count += osmo_sercomm_tx_queue_depth(sercomm, i);
Harald Weltec68af6a2017-04-30 21:21:52 +0200160 /* if we still have any in the queue, restart */
161 if (count == 0)
162 break;
163 }
164
165 while (1) {
166 /* no messages in the queue, grab the lock to ensure it
167 * stays that way */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200168 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200169 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Welte799bef52017-05-15 17:16:48 +0200170 int rc;
Harald Weltec68af6a2017-04-30 21:21:52 +0200171 /* change speed */
Harald Welte799bef52017-05-15 17:16:48 +0200172 rc = sercomm_drv_baudrate_chg(sercomm, bdrt);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200173 sercomm_drv_unlock(&flags);
Harald Welte799bef52017-05-15 17:16:48 +0200174 return rc;
175 } else
Harald Weltedf8e89f2017-05-14 21:46:08 +0200176 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200177 }
Harald Welte799bef52017-05-15 17:16:48 +0200178 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +0200179}
Harald Weltec68af6a2017-04-30 21:21:52 +0200180
Harald Welteea3d3ba2017-05-02 21:24:48 +0200181/*! \brief fetch one octet of to-be-transmitted serial data
182 * \param[in] sercomm Sercomm Instance from which to fetch pending data
183 * \param[out] ch pointer to caller-allocaed output memory
184 * \returns 1 in case of succss; 0 if no data available; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200185int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200186{
187 unsigned long flags;
188
189 /* we may be called from interrupt context, but we stiff need to lock
190 * because sercomm could be accessed from a FIQ context ... */
191
Harald Weltedf8e89f2017-05-14 21:46:08 +0200192 sercomm_drv_lock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200193
Harald Weltecc95f4b2017-04-30 21:39:33 +0200194 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200195 unsigned int i;
196 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200197 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
198 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
199 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200200 break;
201 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200202 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200203 /* start of a new message, send start flag octet */
204 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200205 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltedf8e89f2017-05-14 21:46:08 +0200206 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200207 return 1;
208 } else {
209 /* no more data avilable */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200210 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200211 return 0;
212 }
213 }
214
Harald Weltecc95f4b2017-04-30 21:39:33 +0200215 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200216 /* we've already transmitted the ESCAPE octet,
217 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200218 *ch = *sercomm->tx.next_char++;
219 sercomm->tx.state = RX_ST_DATA;
220 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200221 /* last character has already been transmitted,
222 * send end-of-message octet */
223 *ch = HDLC_FLAG;
224 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200225 msgb_free(sercomm->tx.msg);
226 sercomm->tx.msg = NULL;
227 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200228 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200229 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
230 *sercomm->tx.next_char == HDLC_ESCAPE ||
231 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200232 /* send an escape octet */
233 *ch = HDLC_ESCAPE;
234 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200235 *sercomm->tx.next_char ^= (1 << 5);
236 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200237 } else {
238 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200239 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200240 }
241
Harald Weltedf8e89f2017-05-14 21:46:08 +0200242 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200243 return 1;
244}
245
Harald Welteea3d3ba2017-05-02 21:24:48 +0200246/*! \brief Register a handler for a given DLCI
247 * \param sercomm Sercomm Instance in which caller wishes to register
248 * \param[in] dlci Data Ling Connection Identifier to register
249 * \param[in] cb Callback function for \a dlci
250 * \returns 0 on success; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200251int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200252{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200253 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200254 return -EINVAL;
255
Harald Weltecc95f4b2017-04-30 21:39:33 +0200256 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200257 return -EBUSY;
258
Harald Weltecc95f4b2017-04-30 21:39:33 +0200259 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200260 return 0;
261}
262
263/* dispatch an incoming message once it is completely received */
Harald Welte13588362017-04-30 23:57:55 +0200264static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200265{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200266 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
267 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200268 msgb_free(msg);
269 return;
270 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200271 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200272}
273
Harald Welteea3d3ba2017-05-02 21:24:48 +0200274/*! \brief the driver has received one byte, pass it into sercomm layer
275 * \param[in] sercomm Sercomm Instance for which a byte was received
276 * \param[in] ch byte that was received from line for said instance
277 * \returns 1 on success; 0 on unrecognized char; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200278int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200279{
280 uint8_t *ptr;
281
282 /* we are always called from interrupt context in this function,
283 * which means that any data structures we use need to be for
284 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200285 if (!sercomm->rx.msg)
Harald Weltef6adcd72017-05-01 00:19:13 +0200286 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltec68af6a2017-04-30 21:21:52 +0200287
Harald Weltecc95f4b2017-04-30 21:39:33 +0200288 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200289 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200290 msgb_free(sercomm->rx.msg);
Harald Weltef6adcd72017-05-01 00:19:13 +0200291 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200292 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200293 return 0;
294 }
295
Harald Weltecc95f4b2017-04-30 21:39:33 +0200296 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200297 case RX_ST_WAIT_START:
298 if (ch != HDLC_FLAG)
299 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200300 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200301 break;
302 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200303 sercomm->rx.dlci = ch;
304 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200305 break;
306 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200307 sercomm->rx.ctrl = ch;
308 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200309 break;
310 case RX_ST_DATA:
311 if (ch == HDLC_ESCAPE) {
312 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200313 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200314 break;
315 } else if (ch == HDLC_FLAG) {
316 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200317 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200318 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200319 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200320 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200321 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200322
323 /* do not add the control char */
324 break;
325 }
326 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200327 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200328 *ptr = ch;
329 break;
330 case RX_ST_ESCAPE:
331 /* store bif-5-inverted octet in buffer */
332 ch ^= (1 << 5);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200333 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200334 *ptr = ch;
335 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200336 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200337 break;
338 }
339
340 return 1;
341}
Harald Welte77117132017-05-15 17:33:02 +0200342
343/*! @} */