blob: 07b1aa08d001d941a27267ce4432b9c7345e73a7 [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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
Harald Welte77117132017-05-15 17:33:02 +020021/*! \addtogroup sercomm
22 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020023 * Serial communications layer, based on HDLC.
24 *
25 * \file sercomm.c */
Harald Welte77117132017-05-15 17:33:02 +020026
Harald Welte799bef52017-05-15 17:16:48 +020027#include "config.h"
28
Harald Weltec68af6a2017-04-30 21:21:52 +020029#include <stdint.h>
30#include <stdio.h>
31#include <errno.h>
32
33#include <osmocom/core/msgb.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020034#include <osmocom/core/utils.h>
Harald Welte1a97e2c2017-05-01 00:19:38 +020035#include <osmocom/core/sercomm.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020036#include <osmocom/core/linuxlist.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020037
Harald Welte7af6d222017-05-14 21:48:41 +020038#ifndef EMBEDDED
Harald Weltef6adcd72017-05-01 00:19:13 +020039# define DEFAULT_RX_MSG_SIZE 2048
Neels Hofmeyr87e45502017-06-20 00:17:59 +020040/*! Protect against IRQ context */
Harald Welte799bef52017-05-15 17:16:48 +020041void sercomm_drv_lock(unsigned long __attribute__((unused)) *flags) {}
Neels Hofmeyr87e45502017-06-20 00:17:59 +020042/*! Release protection against IRQ context */
Harald Welte799bef52017-05-15 17:16:48 +020043void sercomm_drv_unlock(unsigned long __attribute__((unused)) *flags) {}
Harald Weltec68af6a2017-04-30 21:21:52 +020044#else
Harald Weltef6adcd72017-05-01 00:19:13 +020045# define DEFAULT_RX_MSG_SIZE 256
Harald Welte799bef52017-05-15 17:16:48 +020046#endif /* EMBEDDED */
Harald Weltec68af6a2017-04-30 21:21:52 +020047
Harald Welte799bef52017-05-15 17:16:48 +020048/* weak symbols to be overridden by application */
49__attribute__((weak)) void sercomm_drv_start_tx(struct osmo_sercomm_inst *sercomm) {};
50__attribute__((weak)) int sercomm_drv_baudrate_chg(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +020051{
Harald Welte799bef52017-05-15 17:16:48 +020052 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +020053}
54
Harald Welte8a4eb832017-05-02 21:41:36 +020055#define HDLC_FLAG 0x7E
56#define HDLC_ESCAPE 0x7D
57
58#define HDLC_C_UI 0x03
59#define HDLC_C_P_BIT (1 << 4)
60#define HDLC_C_F_BIT (1 << 4)
61
Harald Weltec68af6a2017-04-30 21:21:52 +020062enum rx_state {
63 RX_ST_WAIT_START,
64 RX_ST_ADDR,
65 RX_ST_CTRL,
66 RX_ST_DATA,
67 RX_ST_ESCAPE,
68};
69
Neels Hofmeyr87e45502017-06-20 00:17:59 +020070/*! Initialize an Osmocom sercomm instance
Harald Welte77117132017-05-15 17:33:02 +020071 * \param sercomm Caller-allocated sercomm instance to be initialized
72 *
73 * This function initializes the sercomm instance, including the
74 * registration of the ECHO service at the ECHO DLCI
75 */
Harald Welte13588362017-04-30 23:57:55 +020076void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020077{
78 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020079 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
80 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020081
Harald Weltecc95f4b2017-04-30 21:39:33 +020082 sercomm->rx.msg = NULL;
Harald Weltef6adcd72017-05-01 00:19:13 +020083 if (!sercomm->rx.msg_size)
84 sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
Harald Weltecc95f4b2017-04-30 21:39:33 +020085 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020086
87 /* set up the echo dlci */
Harald Welte13588362017-04-30 23:57:55 +020088 osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020089}
90
Neels Hofmeyr87e45502017-06-20 00:17:59 +020091/*! Determine if a given Osmocom sercomm instance has been initialized
Harald Welte77117132017-05-15 17:33:02 +020092 * \param[in] sercomm Osmocom sercomm instance to be checked
93 * \returns 1 in case \a sercomm was previously initialized; 0 otherwise */
Harald Welte13588362017-04-30 23:57:55 +020094int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020095{
Harald Weltecc95f4b2017-04-30 21:39:33 +020096 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +020097}
98
Neels Hofmeyr87e45502017-06-20 00:17:59 +020099/*! User interface for transmitting messages for a given DLCI
Harald Welte77117132017-05-15 17:33:02 +0200100 * \param[in] sercomm Osmocom sercomm instance through which to transmit
101 * \param[in] dlci DLCI through whcih to transmit \a msg
102 * \param[in] msg Message buffer to be transmitted via \a dlci on \a * sercomm
103 **/
Harald Welte13588362017-04-30 23:57:55 +0200104void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200105{
106 unsigned long flags;
107 uint8_t *hdr;
108
109 /* prepend address + control octet */
110 hdr = msgb_push(msg, 2);
111 hdr[0] = dlci;
112 hdr[1] = HDLC_C_UI;
113
114 /* This functiion can be called from any context: FIQ, IRQ
115 * and supervisor context. Proper locking is important! */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200116 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200117 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200118 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200119
Harald Weltec68af6a2017-04-30 21:21:52 +0200120 /* tell UART that we have something to send */
Harald Welte799bef52017-05-15 17:16:48 +0200121 sercomm_drv_start_tx(sercomm);
Harald Weltec68af6a2017-04-30 21:21:52 +0200122}
123
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200124/*! How deep is the Tx queue for a given DLCI?
Harald Welte77117132017-05-15 17:33:02 +0200125 * \param[n] sercomm Osmocom sercomm instance on which to operate
126 * \param[in] dlci DLCI whose queue depthy is to be determined
127 * \returns number of elements in the per-DLCI transmit queue */
Harald Welte13588362017-04-30 23:57:55 +0200128unsigned int osmo_sercomm_tx_queue_depth(struct osmo_sercomm_inst *sercomm, uint8_t dlci)
Harald Weltec68af6a2017-04-30 21:21:52 +0200129{
130 struct llist_head *le;
131 unsigned int num = 0;
132
Harald Weltecc95f4b2017-04-30 21:39:33 +0200133 llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200134 num++;
135 }
136
137 return num;
138}
139
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200140/*! wait until everything has been transmitted, then grab the lock and
Harald Welte77117132017-05-15 17:33:02 +0200141 * change the baud rate as requested
142 * \param[in] sercomm Osmocom sercomm instance
143 * \param[in] bdrt New UART Baud Rate
144 * \returns result of the operation as provided by sercomm_drv_baudrate_chg()
145 */
Harald Welte799bef52017-05-15 17:16:48 +0200146int osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, uint32_t bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200147{
148 unsigned int i, count;
149 unsigned long flags;
150
151 while (1) {
152 /* count the number of pending messages */
153 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200154 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Welte799bef52017-05-15 17:16:48 +0200155 count += osmo_sercomm_tx_queue_depth(sercomm, i);
Harald Weltec68af6a2017-04-30 21:21:52 +0200156 /* if we still have any in the queue, restart */
157 if (count == 0)
158 break;
159 }
160
161 while (1) {
162 /* no messages in the queue, grab the lock to ensure it
163 * stays that way */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200164 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200165 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Welte799bef52017-05-15 17:16:48 +0200166 int rc;
Harald Weltec68af6a2017-04-30 21:21:52 +0200167 /* change speed */
Harald Welte799bef52017-05-15 17:16:48 +0200168 rc = sercomm_drv_baudrate_chg(sercomm, bdrt);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200169 sercomm_drv_unlock(&flags);
Harald Welte799bef52017-05-15 17:16:48 +0200170 return rc;
171 } else
Harald Weltedf8e89f2017-05-14 21:46:08 +0200172 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200173 }
Harald Welte799bef52017-05-15 17:16:48 +0200174 return -1;
Harald Weltec68af6a2017-04-30 21:21:52 +0200175}
Harald Weltec68af6a2017-04-30 21:21:52 +0200176
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200177/*! fetch one octet of to-be-transmitted serial data
Harald Welteea3d3ba2017-05-02 21:24:48 +0200178 * \param[in] sercomm Sercomm Instance from which to fetch pending data
179 * \param[out] ch pointer to caller-allocaed output memory
180 * \returns 1 in case of succss; 0 if no data available; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200181int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200182{
183 unsigned long flags;
184
185 /* we may be called from interrupt context, but we stiff need to lock
186 * because sercomm could be accessed from a FIQ context ... */
187
Harald Weltedf8e89f2017-05-14 21:46:08 +0200188 sercomm_drv_lock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200189
Harald Weltecc95f4b2017-04-30 21:39:33 +0200190 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200191 unsigned int i;
192 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200193 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
194 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
195 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200196 break;
197 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200198 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200199 /* start of a new message, send start flag octet */
200 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200201 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltedf8e89f2017-05-14 21:46:08 +0200202 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200203 return 1;
204 } else {
205 /* no more data avilable */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200206 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200207 return 0;
208 }
209 }
210
Harald Weltecc95f4b2017-04-30 21:39:33 +0200211 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200212 /* we've already transmitted the ESCAPE octet,
213 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200214 *ch = *sercomm->tx.next_char++;
215 sercomm->tx.state = RX_ST_DATA;
216 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200217 /* last character has already been transmitted,
218 * send end-of-message octet */
219 *ch = HDLC_FLAG;
220 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200221 msgb_free(sercomm->tx.msg);
222 sercomm->tx.msg = NULL;
223 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200224 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200225 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
226 *sercomm->tx.next_char == HDLC_ESCAPE ||
227 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200228 /* send an escape octet */
229 *ch = HDLC_ESCAPE;
230 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200231 *sercomm->tx.next_char ^= (1 << 5);
232 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200233 } else {
234 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200235 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200236 }
237
Harald Weltedf8e89f2017-05-14 21:46:08 +0200238 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200239 return 1;
240}
241
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200242/*! Register a handler for a given DLCI
Harald Welteea3d3ba2017-05-02 21:24:48 +0200243 * \param sercomm Sercomm Instance in which caller wishes to register
244 * \param[in] dlci Data Ling Connection Identifier to register
245 * \param[in] cb Callback function for \a dlci
246 * \returns 0 on success; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200247int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200248{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200249 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200250 return -EINVAL;
251
Harald Weltecc95f4b2017-04-30 21:39:33 +0200252 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200253 return -EBUSY;
254
Harald Weltecc95f4b2017-04-30 21:39:33 +0200255 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200256 return 0;
257}
258
259/* dispatch an incoming message once it is completely received */
Harald Welte13588362017-04-30 23:57:55 +0200260static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200261{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200262 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
263 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200264 msgb_free(msg);
265 return;
266 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200267 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200268}
269
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200270/*! the driver has received one byte, pass it into sercomm layer
Harald Welteea3d3ba2017-05-02 21:24:48 +0200271 * \param[in] sercomm Sercomm Instance for which a byte was received
272 * \param[in] ch byte that was received from line for said instance
273 * \returns 1 on success; 0 on unrecognized char; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200274int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200275{
276 uint8_t *ptr;
277
278 /* we are always called from interrupt context in this function,
279 * which means that any data structures we use need to be for
280 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200281 if (!sercomm->rx.msg)
Harald Weltef6adcd72017-05-01 00:19:13 +0200282 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltec68af6a2017-04-30 21:21:52 +0200283
Harald Weltecc95f4b2017-04-30 21:39:33 +0200284 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200285 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200286 msgb_free(sercomm->rx.msg);
Harald Weltef6adcd72017-05-01 00:19:13 +0200287 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200288 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200289 return 0;
290 }
291
Harald Weltecc95f4b2017-04-30 21:39:33 +0200292 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200293 case RX_ST_WAIT_START:
294 if (ch != HDLC_FLAG)
295 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200296 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200297 break;
298 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200299 sercomm->rx.dlci = ch;
300 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200301 break;
302 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200303 sercomm->rx.ctrl = ch;
304 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200305 break;
306 case RX_ST_DATA:
307 if (ch == HDLC_ESCAPE) {
308 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200309 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200310 break;
311 } else if (ch == HDLC_FLAG) {
312 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200313 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200314 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200315 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200316 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200317 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200318
319 /* do not add the control char */
320 break;
321 }
322 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200323 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200324 *ptr = ch;
325 break;
326 case RX_ST_ESCAPE:
327 /* store bif-5-inverted octet in buffer */
328 ch ^= (1 << 5);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200329 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200330 *ptr = ch;
331 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200332 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200333 break;
334 }
335
336 return 1;
337}
Harald Welte77117132017-05-15 17:33:02 +0200338
339/*! @} */