blob: f3241c4e2814e410dce14248359d95e032ab856f [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
23#include <stdint.h>
24#include <stdio.h>
25#include <errno.h>
26
27#include <osmocom/core/msgb.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020028#include <osmocom/core/utils.h>
Harald Welte1a97e2c2017-05-01 00:19:38 +020029#include <osmocom/core/sercomm.h>
Harald Weltedf8e89f2017-05-14 21:46:08 +020030#include <osmocom/core/linuxlist.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020031
Harald Welte7af6d222017-05-14 21:48:41 +020032#ifndef EMBEDDED
Harald Weltec68af6a2017-04-30 21:21:52 +020033
Harald Weltef6adcd72017-05-01 00:19:13 +020034# define DEFAULT_RX_MSG_SIZE 2048
Harald Weltedf8e89f2017-05-14 21:46:08 +020035static inline void sercomm_drv_lock(unsigned long __attribute__((unused)) *flags) {}
36static inline void sercomm_drv_unlock(unsigned long __attribute__((unused)) *flags) {}
Harald Weltec68af6a2017-04-30 21:21:52 +020037
38#else
39
Harald Weltef6adcd72017-05-01 00:19:13 +020040# define DEFAULT_RX_MSG_SIZE 256
Harald Weltec68af6a2017-04-30 21:21:52 +020041# include <debug.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020042# include <asm/system.h>
43
Harald Weltedf8e89f2017-05-14 21:46:08 +020044static inline void sercomm_drv_lock(unsigned long *flags)
Harald Weltec68af6a2017-04-30 21:21:52 +020045{
46 local_firq_save(*flags);
47}
48
Harald Weltedf8e89f2017-05-14 21:46:08 +020049static inline void sercomm_drv_unlock(unsigned long *flags)
Harald Weltec68af6a2017-04-30 21:21:52 +020050{
51 local_irq_restore(*flags);
52}
53
Harald Weltec68af6a2017-04-30 21:21:52 +020054# include <uart.h>
55
56#endif
57
Harald Welte8a4eb832017-05-02 21:41:36 +020058#define HDLC_FLAG 0x7E
59#define HDLC_ESCAPE 0x7D
60
61#define HDLC_C_UI 0x03
62#define HDLC_C_P_BIT (1 << 4)
63#define HDLC_C_F_BIT (1 << 4)
64
Harald Weltec68af6a2017-04-30 21:21:52 +020065enum rx_state {
66 RX_ST_WAIT_START,
67 RX_ST_ADDR,
68 RX_ST_CTRL,
69 RX_ST_DATA,
70 RX_ST_ESCAPE,
71};
72
Harald Welte13588362017-04-30 23:57:55 +020073void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020074{
75 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020076 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
77 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020078
Harald Weltecc95f4b2017-04-30 21:39:33 +020079 sercomm->rx.msg = NULL;
Harald Weltef6adcd72017-05-01 00:19:13 +020080 if (!sercomm->rx.msg_size)
81 sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
Harald Weltecc95f4b2017-04-30 21:39:33 +020082 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020083
84 /* set up the echo dlci */
Harald Welte13588362017-04-30 23:57:55 +020085 osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020086}
87
Harald Welte13588362017-04-30 23:57:55 +020088int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020089{
Harald Weltecc95f4b2017-04-30 21:39:33 +020090 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +020091}
92
93/* user interface for transmitting messages for a given DLCI */
Harald Welte13588362017-04-30 23:57:55 +020094void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +020095{
96 unsigned long flags;
97 uint8_t *hdr;
98
99 /* prepend address + control octet */
100 hdr = msgb_push(msg, 2);
101 hdr[0] = dlci;
102 hdr[1] = HDLC_C_UI;
103
104 /* This functiion can be called from any context: FIQ, IRQ
105 * and supervisor context. Proper locking is important! */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200106 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200107 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200108 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200109
Harald Welte7af6d222017-05-14 21:48:41 +0200110#ifdef EMBEDDED
Harald Weltec68af6a2017-04-30 21:21:52 +0200111 /* tell UART that we have something to send */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200112 uart_irq_enable(sercomm->uart_id, UART_IRQ_TX_EMPTY, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200113#endif
114}
115
116/* how deep is the Tx queue for a given DLCI */
Harald Welte13588362017-04-30 23:57:55 +0200117unsigned int osmo_sercomm_tx_queue_depth(struct osmo_sercomm_inst *sercomm, uint8_t dlci)
Harald Weltec68af6a2017-04-30 21:21:52 +0200118{
119 struct llist_head *le;
120 unsigned int num = 0;
121
Harald Weltecc95f4b2017-04-30 21:39:33 +0200122 llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200123 num++;
124 }
125
126 return num;
127}
128
Harald Welte7af6d222017-05-14 21:48:41 +0200129#ifdef EMBEDDED
Harald Weltec68af6a2017-04-30 21:21:52 +0200130/* wait until everything has been transmitted, then grab the lock and
131 * change the baud rate as requested */
Harald Welte13588362017-04-30 23:57:55 +0200132void osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, enum uart_baudrate bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200133{
134 unsigned int i, count;
135 unsigned long flags;
136
137 while (1) {
138 /* count the number of pending messages */
139 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200140 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Weltec68af6a2017-04-30 21:21:52 +0200141 count += sercomm_tx_queue_depth(i);
142 /* if we still have any in the queue, restart */
143 if (count == 0)
144 break;
145 }
146
147 while (1) {
148 /* no messages in the queue, grab the lock to ensure it
149 * stays that way */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200150 sercomm_drv_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200151 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200152 /* change speed */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200153 uart_baudrate(sercomm->uart_id, bdrt);
Harald Weltedf8e89f2017-05-14 21:46:08 +0200154 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200155 break;
156 }
Harald Weltedf8e89f2017-05-14 21:46:08 +0200157 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200158 }
159}
160#endif
161
Harald Welteea3d3ba2017-05-02 21:24:48 +0200162/*! \brief fetch one octet of to-be-transmitted serial data
163 * \param[in] sercomm Sercomm Instance from which to fetch pending data
164 * \param[out] ch pointer to caller-allocaed output memory
165 * \returns 1 in case of succss; 0 if no data available; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200166int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200167{
168 unsigned long flags;
169
170 /* we may be called from interrupt context, but we stiff need to lock
171 * because sercomm could be accessed from a FIQ context ... */
172
Harald Weltedf8e89f2017-05-14 21:46:08 +0200173 sercomm_drv_lock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200174
Harald Weltecc95f4b2017-04-30 21:39:33 +0200175 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200176 unsigned int i;
177 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200178 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
179 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
180 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200181 break;
182 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200183 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200184 /* start of a new message, send start flag octet */
185 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200186 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltedf8e89f2017-05-14 21:46:08 +0200187 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200188 return 1;
189 } else {
190 /* no more data avilable */
Harald Weltedf8e89f2017-05-14 21:46:08 +0200191 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200192 return 0;
193 }
194 }
195
Harald Weltecc95f4b2017-04-30 21:39:33 +0200196 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200197 /* we've already transmitted the ESCAPE octet,
198 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200199 *ch = *sercomm->tx.next_char++;
200 sercomm->tx.state = RX_ST_DATA;
201 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200202 /* last character has already been transmitted,
203 * send end-of-message octet */
204 *ch = HDLC_FLAG;
205 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200206 msgb_free(sercomm->tx.msg);
207 sercomm->tx.msg = NULL;
208 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200209 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200210 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
211 *sercomm->tx.next_char == HDLC_ESCAPE ||
212 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200213 /* send an escape octet */
214 *ch = HDLC_ESCAPE;
215 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200216 *sercomm->tx.next_char ^= (1 << 5);
217 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200218 } else {
219 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200220 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200221 }
222
Harald Weltedf8e89f2017-05-14 21:46:08 +0200223 sercomm_drv_unlock(&flags);
Harald Weltec68af6a2017-04-30 21:21:52 +0200224 return 1;
225}
226
Harald Welteea3d3ba2017-05-02 21:24:48 +0200227/*! \brief Register a handler for a given DLCI
228 * \param sercomm Sercomm Instance in which caller wishes to register
229 * \param[in] dlci Data Ling Connection Identifier to register
230 * \param[in] cb Callback function for \a dlci
231 * \returns 0 on success; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200232int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200233{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200234 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200235 return -EINVAL;
236
Harald Weltecc95f4b2017-04-30 21:39:33 +0200237 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200238 return -EBUSY;
239
Harald Weltecc95f4b2017-04-30 21:39:33 +0200240 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200241 return 0;
242}
243
244/* dispatch an incoming message once it is completely received */
Harald Welte13588362017-04-30 23:57:55 +0200245static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200246{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200247 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
248 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200249 msgb_free(msg);
250 return;
251 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200252 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200253}
254
Harald Welteea3d3ba2017-05-02 21:24:48 +0200255/*! \brief the driver has received one byte, pass it into sercomm layer
256 * \param[in] sercomm Sercomm Instance for which a byte was received
257 * \param[in] ch byte that was received from line for said instance
258 * \returns 1 on success; 0 on unrecognized char; negative on error */
Harald Welte13588362017-04-30 23:57:55 +0200259int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200260{
261 uint8_t *ptr;
262
263 /* we are always called from interrupt context in this function,
264 * which means that any data structures we use need to be for
265 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200266 if (!sercomm->rx.msg)
Harald Weltef6adcd72017-05-01 00:19:13 +0200267 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltec68af6a2017-04-30 21:21:52 +0200268
Harald Weltecc95f4b2017-04-30 21:39:33 +0200269 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200270 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200271 msgb_free(sercomm->rx.msg);
Harald Weltef6adcd72017-05-01 00:19:13 +0200272 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200273 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200274 return 0;
275 }
276
Harald Weltecc95f4b2017-04-30 21:39:33 +0200277 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200278 case RX_ST_WAIT_START:
279 if (ch != HDLC_FLAG)
280 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200281 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200282 break;
283 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200284 sercomm->rx.dlci = ch;
285 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200286 break;
287 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200288 sercomm->rx.ctrl = ch;
289 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200290 break;
291 case RX_ST_DATA:
292 if (ch == HDLC_ESCAPE) {
293 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200294 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200295 break;
296 } else if (ch == HDLC_FLAG) {
297 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200298 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200299 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200300 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200301 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200302 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200303
304 /* do not add the control char */
305 break;
306 }
307 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200308 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200309 *ptr = ch;
310 break;
311 case RX_ST_ESCAPE:
312 /* store bif-5-inverted octet in buffer */
313 ch ^= (1 << 5);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200314 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200315 *ptr = ch;
316 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200317 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200318 break;
319 }
320
321 return 1;
322}