blob: 111565f26f85d58769d9a152ff4f9221bed76f3c [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 Welte1a97e2c2017-05-01 00:19:38 +020028#include <osmocom/core/sercomm.h>
Harald Weltec68af6a2017-04-30 21:21:52 +020029
30#ifdef HOST_BUILD
31
Harald Weltef6adcd72017-05-01 00:19:13 +020032# define DEFAULT_RX_MSG_SIZE 2048
Harald Weltec68af6a2017-04-30 21:21:52 +020033# ifndef ARRAY_SIZE
34# define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
35# endif
Harald Weltec68af6a2017-04-30 21:21:52 +020036
37static inline void sercomm_lock(unsigned long __attribute__((unused)) *flags) {}
38static inline void sercomm_unlock(unsigned long __attribute__((unused)) *flags) {}
39
40#else
41
Harald Weltef6adcd72017-05-01 00:19:13 +020042# define DEFAULT_RX_MSG_SIZE 256
Harald Weltec68af6a2017-04-30 21:21:52 +020043# include <debug.h>
44# include <osmocom/core/linuxlist.h>
45# include <asm/system.h>
46
47static inline void sercomm_lock(unsigned long *flags)
48{
49 local_firq_save(*flags);
50}
51
52static inline void sercomm_unlock(unsigned long *flags)
53{
54 local_irq_restore(*flags);
55}
56
Harald Weltec68af6a2017-04-30 21:21:52 +020057# include <uart.h>
58
59#endif
60
Harald Weltec68af6a2017-04-30 21:21:52 +020061enum rx_state {
62 RX_ST_WAIT_START,
63 RX_ST_ADDR,
64 RX_ST_CTRL,
65 RX_ST_DATA,
66 RX_ST_ESCAPE,
67};
68
Harald Weltec68af6a2017-04-30 21:21:52 +020069
70#ifndef HOST_BUILD
Harald Welte13588362017-04-30 23:57:55 +020071void osmo_sercomm_bind_uart(struct osmo_sercomm_inst *sercomm, int uart)
Harald Weltec68af6a2017-04-30 21:21:52 +020072{
Harald Weltecc95f4b2017-04-30 21:39:33 +020073 sercomm->uart_id = uart;
Harald Weltec68af6a2017-04-30 21:21:52 +020074}
75
Harald Welte13588362017-04-30 23:57:55 +020076int osmo_sercomm_get_uart(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020077{
Harald Weltecc95f4b2017-04-30 21:39:33 +020078 return sercomm->uart_id;
Harald Weltec68af6a2017-04-30 21:21:52 +020079}
80#endif
81
Harald Welte13588362017-04-30 23:57:55 +020082void osmo_sercomm_init(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020083{
84 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020085 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
86 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020087
Harald Weltecc95f4b2017-04-30 21:39:33 +020088 sercomm->rx.msg = NULL;
Harald Weltef6adcd72017-05-01 00:19:13 +020089 if (!sercomm->rx.msg_size)
90 sercomm->rx.msg_size = DEFAULT_RX_MSG_SIZE;
Harald Weltecc95f4b2017-04-30 21:39:33 +020091 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020092
93 /* set up the echo dlci */
Harald Welte13588362017-04-30 23:57:55 +020094 osmo_sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &osmo_sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020095}
96
Harald Welte13588362017-04-30 23:57:55 +020097int osmo_sercomm_initialized(struct osmo_sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020098{
Harald Weltecc95f4b2017-04-30 21:39:33 +020099 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +0200100}
101
102/* user interface for transmitting messages for a given DLCI */
Harald Welte13588362017-04-30 23:57:55 +0200103void osmo_sercomm_sendmsg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200104{
105 unsigned long flags;
106 uint8_t *hdr;
107
108 /* prepend address + control octet */
109 hdr = msgb_push(msg, 2);
110 hdr[0] = dlci;
111 hdr[1] = HDLC_C_UI;
112
113 /* This functiion can be called from any context: FIQ, IRQ
114 * and supervisor context. Proper locking is important! */
115 sercomm_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200116 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200117 sercomm_unlock(&flags);
118
119#ifndef HOST_BUILD
120 /* tell UART that we have something to send */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200121 uart_irq_enable(sercomm->uart_id, UART_IRQ_TX_EMPTY, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200122#endif
123}
124
125/* how deep is the Tx queue for a given DLCI */
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
138#ifndef HOST_BUILD
139/* wait until everything has been transmitted, then grab the lock and
140 * change the baud rate as requested */
Harald Welte13588362017-04-30 23:57:55 +0200141void osmo_sercomm_change_speed(struct osmo_sercomm_inst *sercomm, enum uart_baudrate bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200142{
143 unsigned int i, count;
144 unsigned long flags;
145
146 while (1) {
147 /* count the number of pending messages */
148 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200149 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Weltec68af6a2017-04-30 21:21:52 +0200150 count += sercomm_tx_queue_depth(i);
151 /* if we still have any in the queue, restart */
152 if (count == 0)
153 break;
154 }
155
156 while (1) {
157 /* no messages in the queue, grab the lock to ensure it
158 * stays that way */
159 sercomm_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200160 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200161 /* change speed */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200162 uart_baudrate(sercomm->uart_id, bdrt);
Harald Weltec68af6a2017-04-30 21:21:52 +0200163 sercomm_unlock(&flags);
164 break;
165 }
166 sercomm_unlock(&flags);
167 }
168}
169#endif
170
171/* fetch one octet of to-be-transmitted serial data */
Harald Welte13588362017-04-30 23:57:55 +0200172int osmo_sercomm_drv_pull(struct osmo_sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200173{
174 unsigned long flags;
175
176 /* we may be called from interrupt context, but we stiff need to lock
177 * because sercomm could be accessed from a FIQ context ... */
178
179 sercomm_lock(&flags);
180
Harald Weltecc95f4b2017-04-30 21:39:33 +0200181 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200182 unsigned int i;
183 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200184 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
185 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
186 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200187 break;
188 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200189 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200190 /* start of a new message, send start flag octet */
191 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200192 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltec68af6a2017-04-30 21:21:52 +0200193 sercomm_unlock(&flags);
194 return 1;
195 } else {
196 /* no more data avilable */
197 sercomm_unlock(&flags);
198 return 0;
199 }
200 }
201
Harald Weltecc95f4b2017-04-30 21:39:33 +0200202 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200203 /* we've already transmitted the ESCAPE octet,
204 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200205 *ch = *sercomm->tx.next_char++;
206 sercomm->tx.state = RX_ST_DATA;
207 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200208 /* last character has already been transmitted,
209 * send end-of-message octet */
210 *ch = HDLC_FLAG;
211 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200212 msgb_free(sercomm->tx.msg);
213 sercomm->tx.msg = NULL;
214 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200215 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200216 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
217 *sercomm->tx.next_char == HDLC_ESCAPE ||
218 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200219 /* send an escape octet */
220 *ch = HDLC_ESCAPE;
221 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200222 *sercomm->tx.next_char ^= (1 << 5);
223 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200224 } else {
225 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200226 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200227 }
228
229 sercomm_unlock(&flags);
230 return 1;
231}
232
233/* register a handler for a given DLCI */
Harald Welte13588362017-04-30 23:57:55 +0200234int osmo_sercomm_register_rx_cb(struct osmo_sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200235{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200236 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200237 return -EINVAL;
238
Harald Weltecc95f4b2017-04-30 21:39:33 +0200239 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200240 return -EBUSY;
241
Harald Weltecc95f4b2017-04-30 21:39:33 +0200242 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200243 return 0;
244}
245
246/* dispatch an incoming message once it is completely received */
Harald Welte13588362017-04-30 23:57:55 +0200247static void dispatch_rx_msg(struct osmo_sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200248{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200249 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
250 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200251 msgb_free(msg);
252 return;
253 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200254 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200255}
256
257/* the driver has received one byte, pass it into sercomm layer */
Harald Welte13588362017-04-30 23:57:55 +0200258int osmo_sercomm_drv_rx_char(struct osmo_sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200259{
260 uint8_t *ptr;
261
262 /* we are always called from interrupt context in this function,
263 * which means that any data structures we use need to be for
264 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200265 if (!sercomm->rx.msg)
Harald Weltef6adcd72017-05-01 00:19:13 +0200266 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltec68af6a2017-04-30 21:21:52 +0200267
Harald Weltecc95f4b2017-04-30 21:39:33 +0200268 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200269 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200270 msgb_free(sercomm->rx.msg);
Harald Weltef6adcd72017-05-01 00:19:13 +0200271 sercomm->rx.msg = osmo_sercomm_alloc_msgb(sercomm->rx.msg_size);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200272 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200273 return 0;
274 }
275
Harald Weltecc95f4b2017-04-30 21:39:33 +0200276 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200277 case RX_ST_WAIT_START:
278 if (ch != HDLC_FLAG)
279 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200280 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200281 break;
282 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200283 sercomm->rx.dlci = ch;
284 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200285 break;
286 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200287 sercomm->rx.ctrl = ch;
288 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200289 break;
290 case RX_ST_DATA:
291 if (ch == HDLC_ESCAPE) {
292 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200293 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200294 break;
295 } else if (ch == HDLC_FLAG) {
296 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200297 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200298 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200299 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200300 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200301 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200302
303 /* do not add the control char */
304 break;
305 }
306 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200307 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200308 *ptr = ch;
309 break;
310 case RX_ST_ESCAPE:
311 /* store bif-5-inverted octet in buffer */
312 ch ^= (1 << 5);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200313 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200314 *ptr = ch;
315 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200316 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200317 break;
318 }
319
320 return 1;
321}