blob: 1798acec3dd8a229184fc8ae7ce6289720bcead0 [file] [log] [blame]
Harald Weltecc95f4b2017-04-30 21:39:33 +02001/* (C) 2010,2017 by Harald Welte <laforge@gnumonks.org>
Harald Weltec68af6a2017-04-30 21:21:52 +02002 *
3 * All Rights Reserved
4 *
Harald Weltee08da972017-11-13 01:00:26 +09005 * SPDX-License-Identifier: GPL-2.0+
6 *
Harald Weltec68af6a2017-04-30 21:21:52 +02007 * 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 *
Harald Weltec68af6a2017-04-30 21:21:52 +020017 */
18
Harald Welte77117132017-05-15 17:33:02 +020019/*! \addtogroup sercomm
20 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020021 * Serial communications layer, based on HDLC.
22 *
23 * \file sercomm.c */
Harald Welte77117132017-05-15 17:33:02 +020024
Harald Welte799bef52017-05-15 17:16:48 +020025#include "config.h"
26
Harald Weltec68af6a2017-04-30 21:21:52 +020027#include <stdint.h>
28#include <stdio.h>
29#include <errno.h>
30
31#include <osmocom/core/msgb.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020032#include <osmocom/core/utils.h>
Harald Welte1a97e2c2017-05-01 00:19:38 +020033#include <osmocom/core/sercomm.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020034#include <osmocom/core/linuxlist.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020035
Harald Welte7af6d222017-05-14 21:48:41 +020036#ifndef EMBEDDED
Harald Weltef6adcd72017-05-01 00:19:13 +020037# define DEFAULT_RX_MSG_SIZE 2048
Neels Hofmeyr87e45502017-06-20 00:17:59 +020038/*! Protect against IRQ context */
Harald Welte799bef52017-05-15 17:16:48 +020039void sercomm_drv_lock(unsigned long __attribute__((unused)) *flags) {}
Neels Hofmeyr87e45502017-06-20 00:17:59 +020040/*! Release protection against IRQ context */
Harald Welte799bef52017-05-15 17:16:48 +020041void sercomm_drv_unlock(unsigned long __attribute__((unused)) *flags) {}
Harald Weltec68af6a2017-04-30 21:21:52 +020042#else
Harald Weltef6adcd72017-05-01 00:19:13 +020043# define DEFAULT_RX_MSG_SIZE 256
Harald Welte799bef52017-05-15 17:16:48 +020044#endif /* EMBEDDED */
Harald Weltec68af6a2017-04-30 21:21:52 +020045
Harald Welte799bef52017-05-15 17:16:48 +020046/* weak symbols to be overridden by application */
47__attribute__((weak)) void sercomm_drv_start_tx(struct osmo_sercomm_inst *sercomm) {};
48__attribute__((weak)) int sercomm_drv_baudrate_chg(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +020049{
Harald Welte799bef52017-05-15 17:16:48 +020050 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +020051}
52
Harald Welte8a4eb832017-05-02 21:41:36 +020053#define HDLC_FLAG 0x7E
54#define HDLC_ESCAPE 0x7D
55
56#define HDLC_C_UI 0x03
57#define HDLC_C_P_BIT (1 << 4)
58#define HDLC_C_F_BIT (1 << 4)
59
Harald Weltec68af6a2017-04-30 21:21:52 +020060enum rx_state {
61 RX_ST_WAIT_START,
62 RX_ST_ADDR,
63 RX_ST_CTRL,
64 RX_ST_DATA,
65 RX_ST_ESCAPE,
66};
67
Neels Hofmeyr87e45502017-06-20 00:17:59 +020068/*! Initialize an Osmocom sercomm instance
Harald Welte77117132017-05-15 17:33:02 +020069 * \param sercomm Caller-allocated sercomm instance to be initialized
70 *
71 * This function initializes the sercomm instance, including the
72 * registration of the ECHO service at the ECHO DLCI
73 */
Harald Welte13588362017-04-30 23:57:55 +020074void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020075{
76 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020077 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
78 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020079
Harald Weltecc95f4b2017-04-30 21:39:33 +020080 sercomm->rx.msg = NULL;
Harald Weltef6adcd72017-05-01 00:19:13 +020081 if (!sercomm->rx.msg_size)
82 sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
Harald Weltecc95f4b2017-04-30 21:39:33 +020083 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020084
85 /* set up the echo dlci */
Harald Welte13588362017-04-30 23:57:55 +020086 osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020087}
88
Neels Hofmeyr87e45502017-06-20 00:17:59 +020089/*! Determine if a given Osmocom sercomm instance has been initialized
Harald Welte77117132017-05-15 17:33:02 +020090 * \param[in] sercomm Osmocom sercomm instance to be checked
91 * \returns 1 in case \a sercomm was previously initialized; 0 otherwise */
Harald Welte13588362017-04-30 23:57:55 +020092int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020093{
Harald Weltecc95f4b2017-04-30 21:39:33 +020094 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +020095}
96
Neels Hofmeyr87e45502017-06-20 00:17:59 +020097/*! User interface for transmitting messages for a given DLCI
Harald Welte77117132017-05-15 17:33:02 +020098 * \param[in] sercomm Osmocom sercomm instance through which to transmit
99 * \param[in] dlci DLCI through whcih to transmit \a msg
100 * \param[in] msg Message buffer to be transmitted via \a dlci on \a * sercomm
101 **/
Harald Welte13588362017-04-30 23:57:55 +0200102void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200103{
104 unsigned long flags;
105 uint8_t *hdr;
106
107 /* prepend address + control octet */
108 hdr = msgb_push(msg, 2);
109 hdr[0] = dlci;
110 hdr[1] = HDLC_C_UI;
111
112 /* This functiion can be called from any context: FIQ, IRQ
113 * and supervisor context. Proper locking is important! */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200114 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200115 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200116 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200117
Harald Weltec68af6a2017-04-30 21:21:52 +0200118 /* tell UART that we have something to send */
Harald Welte799bef52017-05-15 17:16:48 +0200119 sercomm_drv_start_tx(sercomm);
Harald Weltec68af6a2017-04-30 21:21:52 +0200120}
121
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200122/*! How deep is the Tx queue for a given DLCI?
Harald Welte77117132017-05-15 17:33:02 +0200123 * \param[n] sercomm Osmocom sercomm instance on which to operate
124 * \param[in] dlci DLCI whose queue depthy is to be determined
125 * \returns number of elements in the per-DLCI transmit queue */
Harald Welte13588362017-04-30 23:57:55 +0200126unsigned int osmo_sercomm_tx_queue_depth(struct osmo_sercomm_inst *sercomm, uint8_t dlci)
Harald Weltec68af6a2017-04-30 21:21:52 +0200127{
128 struct llist_head *le;
129 unsigned int num = 0;
130
Harald Weltecc95f4b2017-04-30 21:39:33 +0200131 llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200132 num++;
133 }
134
135 return num;
136}
137
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200138/*! wait until everything has been transmitted, then grab the lock and
Harald Welte77117132017-05-15 17:33:02 +0200139 * change the baud rate as requested
140 * \param[in] sercomm Osmocom sercomm instance
141 * \param[in] bdrt New UART Baud Rate
142 * \returns result of the operation as provided by sercomm_drv_baudrate_chg()
143 */
Harald Welte799bef52017-05-15 17:16:48 +0200144int osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200145{
146 unsigned int i, count;
147 unsigned long flags;
148
149 while (1) {
150 /* count the number of pending messages */
151 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200152 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Welte799bef52017-05-15 17:16:48 +0200153 count += osmo_sercomm_tx_queue_depth(sercomm, i);
Harald Weltec68af6a2017-04-30 21:21:52 +0200154 /* if we still have any in the queue, restart */
155 if (count == 0)
156 break;
157 }
158
159 while (1) {
160 /* no messages in the queue, grab the lock to ensure it
161 * stays that way */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200162 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200163 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Welte799bef52017-05-15 17:16:48 +0200164 int rc;
Harald Weltec68af6a2017-04-30 21:21:52 +0200165 /* change speed */
Harald Welte799bef52017-05-15 17:16:48 +0200166 rc = sercomm_drv_baudrate_chg(sercomm, bdrt);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200167 sercomm_drv_unlock(&flags);
Harald Welte799bef52017-05-15 17:16:48 +0200168 return rc;
169 } else
Harald Weltedf8e89f2017-05-14 21:46:08 +0200170 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200171 }
Harald Welte799bef52017-05-15 17:16:48 +0200172 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +0200173}
Harald Weltec68af6a2017-04-30 21:21:52 +0200174
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200175/*! fetch one octet of to-be-transmitted serial data
Harald Welteea3d3ba2017-05-02 21:24:48 +0200176 * \param[in] sercomm Sercomm Instance from which to fetch pending data
177 * \param[out] ch pointer to caller-allocaed output memory
178 * \returns 1 in case of succss; 0 if no data available; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200179int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200180{
181 unsigned long flags;
182
183 /* we may be called from interrupt context, but we stiff need to lock
184 * because sercomm could be accessed from a FIQ context ... */
185
Harald Weltedf8e89f2017-05-14 21:46:08 +0200186 sercomm_drv_lock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200187
Harald Weltecc95f4b2017-04-30 21:39:33 +0200188 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200189 unsigned int i;
190 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200191 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
192 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
193 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200194 break;
195 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200196 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200197 /* start of a new message, send start flag octet */
198 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200199 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltedf8e89f2017-05-14 21:46:08 +0200200 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200201 return 1;
202 } else {
203 /* no more data avilable */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200204 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200205 return 0;
206 }
207 }
208
Harald Weltecc95f4b2017-04-30 21:39:33 +0200209 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200210 /* we've already transmitted the ESCAPE octet,
211 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200212 *ch = *sercomm->tx.next_char++;
213 sercomm->tx.state = RX_ST_DATA;
214 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200215 /* last character has already been transmitted,
216 * send end-of-message octet */
217 *ch = HDLC_FLAG;
218 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200219 msgb_free(sercomm->tx.msg);
220 sercomm->tx.msg = NULL;
221 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200222 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200223 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
224 *sercomm->tx.next_char == HDLC_ESCAPE ||
225 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200226 /* send an escape octet */
227 *ch = HDLC_ESCAPE;
228 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200229 *sercomm->tx.next_char ^= (1 << 5);
230 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200231 } else {
232 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200233 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200234 }
235
Harald Weltedf8e89f2017-05-14 21:46:08 +0200236 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200237 return 1;
238}
239
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200240/*! Register a handler for a given DLCI
Harald Welteea3d3ba2017-05-02 21:24:48 +0200241 * \param sercomm Sercomm Instance in which caller wishes to register
242 * \param[in] dlci Data Ling Connection Identifier to register
243 * \param[in] cb Callback function for \a dlci
244 * \returns 0 on success; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200245int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200246{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200247 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200248 return -EINVAL;
249
Harald Weltecc95f4b2017-04-30 21:39:33 +0200250 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200251 return -EBUSY;
252
Harald Weltecc95f4b2017-04-30 21:39:33 +0200253 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200254 return 0;
255}
256
257/* dispatch an incoming message once it is completely received */
Harald Welte13588362017-04-30 23:57:55 +0200258static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200259{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200260 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
261 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200262 msgb_free(msg);
263 return;
264 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200265 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200266}
267
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200268/*! the driver has received one byte, pass it into sercomm layer
Harald Welteea3d3ba2017-05-02 21:24:48 +0200269 * \param[in] sercomm Sercomm Instance for which a byte was received
270 * \param[in] ch byte that was received from line for said instance
271 * \returns 1 on success; 0 on unrecognized char; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200272int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200273{
274 uint8_t *ptr;
275
276 /* we are always called from interrupt context in this function,
277 * which means that any data structures we use need to be for
278 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200279 if (!sercomm->rx.msg)
Harald Weltef6adcd72017-05-01 00:19:13 +0200280 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltec68af6a2017-04-30 21:21:52 +0200281
Harald Weltecc95f4b2017-04-30 21:39:33 +0200282 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200283 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200284 msgb_free(sercomm->rx.msg);
Harald Weltef6adcd72017-05-01 00:19:13 +0200285 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200286 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200287 return 0;
288 }
289
Harald Weltecc95f4b2017-04-30 21:39:33 +0200290 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200291 case RX_ST_WAIT_START:
292 if (ch != HDLC_FLAG)
293 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200294 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200295 break;
296 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200297 sercomm->rx.dlci = ch;
298 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200299 break;
300 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200301 sercomm->rx.ctrl = ch;
302 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200303 break;
304 case RX_ST_DATA:
305 if (ch == HDLC_ESCAPE) {
306 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200307 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200308 break;
309 } else if (ch == HDLC_FLAG) {
310 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200311 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200312 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200313 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200314 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200315 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200316
317 /* do not add the control char */
318 break;
319 }
320 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200321 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200322 *ptr = ch;
323 break;
324 case RX_ST_ESCAPE:
325 /* store bif-5-inverted octet in buffer */
326 ch ^= (1 << 5);
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 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200330 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200331 break;
332 }
333
334 return 1;
335}
Harald Welte77117132017-05-15 17:33:02 +0200336
337/*! @} */