blob: 4fd979a16afc3442a203863b7d25d6cbfe029b2e [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>
28
29#ifdef HOST_BUILD
30
31# define SERCOMM_RX_MSG_SIZE 2048
32# ifndef ARRAY_SIZE
33# define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
34# endif
35# include <sercomm.h>
36
37static inline void sercomm_lock(unsigned long __attribute__((unused)) *flags) {}
38static inline void sercomm_unlock(unsigned long __attribute__((unused)) *flags) {}
39
40#else
41
42# define SERCOMM_RX_MSG_SIZE 256
43# 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
57# include <comm/sercomm.h>
58# include <uart.h>
59
60#endif
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
Harald Weltec68af6a2017-04-30 21:21:52 +020070
71#ifndef HOST_BUILD
Harald Weltecc95f4b2017-04-30 21:39:33 +020072void sercomm_bind_uart(struct sercomm_inst *sercomm, int uart)
Harald Weltec68af6a2017-04-30 21:21:52 +020073{
Harald Weltecc95f4b2017-04-30 21:39:33 +020074 sercomm->uart_id = uart;
Harald Weltec68af6a2017-04-30 21:21:52 +020075}
76
Harald Weltecc95f4b2017-04-30 21:39:33 +020077int sercomm_get_uart(struct sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020078{
Harald Weltecc95f4b2017-04-30 21:39:33 +020079 return sercomm->uart_id;
Harald Weltec68af6a2017-04-30 21:21:52 +020080}
81#endif
82
Harald Weltecc95f4b2017-04-30 21:39:33 +020083void sercomm_init(struct sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020084{
85 unsigned int i;
Harald Weltecc95f4b2017-04-30 21:39:33 +020086 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
87 INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
Harald Weltec68af6a2017-04-30 21:21:52 +020088
Harald Weltecc95f4b2017-04-30 21:39:33 +020089 sercomm->rx.msg = NULL;
90 sercomm->initialized = 1;
Harald Weltec68af6a2017-04-30 21:21:52 +020091
92 /* set up the echo dlci */
Harald Weltecc95f4b2017-04-30 21:39:33 +020093 sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &sercomm_sendmsg);
Harald Weltec68af6a2017-04-30 21:21:52 +020094}
95
Harald Weltecc95f4b2017-04-30 21:39:33 +020096int sercomm_initialized(struct sercomm_inst *sercomm)
Harald Weltec68af6a2017-04-30 21:21:52 +020097{
Harald Weltecc95f4b2017-04-30 21:39:33 +020098 return sercomm->initialized;
Harald Weltec68af6a2017-04-30 21:21:52 +020099}
100
101/* user interface for transmitting messages for a given DLCI */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200102void sercomm_sendmsg(struct 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! */
114 sercomm_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200115 msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200116 sercomm_unlock(&flags);
117
118#ifndef HOST_BUILD
119 /* tell UART that we have something to send */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200120 uart_irq_enable(sercomm->uart_id, UART_IRQ_TX_EMPTY, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200121#endif
122}
123
124/* how deep is the Tx queue for a given DLCI */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200125unsigned int sercomm_tx_queue_depth(struct sercomm_inst *sercomm, uint8_t dlci)
Harald Weltec68af6a2017-04-30 21:21:52 +0200126{
127 struct llist_head *le;
128 unsigned int num = 0;
129
Harald Weltecc95f4b2017-04-30 21:39:33 +0200130 llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200131 num++;
132 }
133
134 return num;
135}
136
137#ifndef HOST_BUILD
138/* wait until everything has been transmitted, then grab the lock and
139 * change the baud rate as requested */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200140void sercomm_change_speed(struct sercomm_inst *sercomm, enum uart_baudrate bdrt)
Harald Weltec68af6a2017-04-30 21:21:52 +0200141{
142 unsigned int i, count;
143 unsigned long flags;
144
145 while (1) {
146 /* count the number of pending messages */
147 count = 0;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200148 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
Harald Weltec68af6a2017-04-30 21:21:52 +0200149 count += sercomm_tx_queue_depth(i);
150 /* if we still have any in the queue, restart */
151 if (count == 0)
152 break;
153 }
154
155 while (1) {
156 /* no messages in the queue, grab the lock to ensure it
157 * stays that way */
158 sercomm_lock(&flags);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200159 if (!sercomm->tx.msg && !sercomm->tx.next_char) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200160 /* change speed */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200161 uart_baudrate(sercomm->uart_id, bdrt);
Harald Weltec68af6a2017-04-30 21:21:52 +0200162 sercomm_unlock(&flags);
163 break;
164 }
165 sercomm_unlock(&flags);
166 }
167}
168#endif
169
170/* fetch one octet of to-be-transmitted serial data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200171int sercomm_drv_pull(struct sercomm_inst *sercomm, uint8_t *ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200172{
173 unsigned long flags;
174
175 /* we may be called from interrupt context, but we stiff need to lock
176 * because sercomm could be accessed from a FIQ context ... */
177
178 sercomm_lock(&flags);
179
Harald Weltecc95f4b2017-04-30 21:39:33 +0200180 if (!sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200181 unsigned int i;
182 /* dequeue a new message from the queues */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200183 for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
184 sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
185 if (sercomm->tx.msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200186 break;
187 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200188 if (sercomm->tx.msg) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200189 /* start of a new message, send start flag octet */
190 *ch = HDLC_FLAG;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200191 sercomm->tx.next_char = sercomm->tx.msg->data;
Harald Weltec68af6a2017-04-30 21:21:52 +0200192 sercomm_unlock(&flags);
193 return 1;
194 } else {
195 /* no more data avilable */
196 sercomm_unlock(&flags);
197 return 0;
198 }
199 }
200
Harald Weltecc95f4b2017-04-30 21:39:33 +0200201 if (sercomm->tx.state == RX_ST_ESCAPE) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200202 /* we've already transmitted the ESCAPE octet,
203 * we now need to transmit the escaped data */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200204 *ch = *sercomm->tx.next_char++;
205 sercomm->tx.state = RX_ST_DATA;
206 } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200207 /* last character has already been transmitted,
208 * send end-of-message octet */
209 *ch = HDLC_FLAG;
210 /* we've reached the end of the message buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200211 msgb_free(sercomm->tx.msg);
212 sercomm->tx.msg = NULL;
213 sercomm->tx.next_char = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200214 /* escaping for the two control octets */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200215 } else if (*sercomm->tx.next_char == HDLC_FLAG ||
216 *sercomm->tx.next_char == HDLC_ESCAPE ||
217 *sercomm->tx.next_char == 0x00) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200218 /* send an escape octet */
219 *ch = HDLC_ESCAPE;
220 /* invert bit 5 of the next octet to be sent */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200221 *sercomm->tx.next_char ^= (1 << 5);
222 sercomm->tx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200223 } else {
224 /* standard case, simply send next octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200225 *ch = *sercomm->tx.next_char++;
Harald Weltec68af6a2017-04-30 21:21:52 +0200226 }
227
228 sercomm_unlock(&flags);
229 return 1;
230}
231
232/* register a handler for a given DLCI */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200233int sercomm_register_rx_cb(struct sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
Harald Weltec68af6a2017-04-30 21:21:52 +0200234{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200235 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
Harald Weltec68af6a2017-04-30 21:21:52 +0200236 return -EINVAL;
237
Harald Weltecc95f4b2017-04-30 21:39:33 +0200238 if (sercomm->rx.dlci_handler[dlci])
Harald Weltec68af6a2017-04-30 21:21:52 +0200239 return -EBUSY;
240
Harald Weltecc95f4b2017-04-30 21:39:33 +0200241 sercomm->rx.dlci_handler[dlci] = cb;
Harald Weltec68af6a2017-04-30 21:21:52 +0200242 return 0;
243}
244
245/* dispatch an incoming message once it is completely received */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200246static void dispatch_rx_msg(struct sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
Harald Weltec68af6a2017-04-30 21:21:52 +0200247{
Harald Weltecc95f4b2017-04-30 21:39:33 +0200248 if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
249 !sercomm->rx.dlci_handler[dlci]) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200250 msgb_free(msg);
251 return;
252 }
Harald Weltecc95f4b2017-04-30 21:39:33 +0200253 sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200254}
255
256/* the driver has received one byte, pass it into sercomm layer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200257int sercomm_drv_rx_char(struct sercomm_inst *sercomm, uint8_t ch)
Harald Weltec68af6a2017-04-30 21:21:52 +0200258{
259 uint8_t *ptr;
260
261 /* we are always called from interrupt context in this function,
262 * which means that any data structures we use need to be for
263 * our exclusive access */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200264 if (!sercomm->rx.msg)
265 sercomm->rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
Harald Weltec68af6a2017-04-30 21:21:52 +0200266
Harald Weltecc95f4b2017-04-30 21:39:33 +0200267 if (msgb_tailroom(sercomm->rx.msg) == 0) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200268 //cons_puts("sercomm_drv_rx_char() overflow!\n");
Harald Weltecc95f4b2017-04-30 21:39:33 +0200269 msgb_free(sercomm->rx.msg);
270 sercomm->rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
271 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200272 return 0;
273 }
274
Harald Weltecc95f4b2017-04-30 21:39:33 +0200275 switch (sercomm->rx.state) {
Harald Weltec68af6a2017-04-30 21:21:52 +0200276 case RX_ST_WAIT_START:
277 if (ch != HDLC_FLAG)
278 break;
Harald Weltecc95f4b2017-04-30 21:39:33 +0200279 sercomm->rx.state = RX_ST_ADDR;
Harald Weltec68af6a2017-04-30 21:21:52 +0200280 break;
281 case RX_ST_ADDR:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200282 sercomm->rx.dlci = ch;
283 sercomm->rx.state = RX_ST_CTRL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200284 break;
285 case RX_ST_CTRL:
Harald Weltecc95f4b2017-04-30 21:39:33 +0200286 sercomm->rx.ctrl = ch;
287 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200288 break;
289 case RX_ST_DATA:
290 if (ch == HDLC_ESCAPE) {
291 /* drop the escape octet, but change state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200292 sercomm->rx.state = RX_ST_ESCAPE;
Harald Weltec68af6a2017-04-30 21:21:52 +0200293 break;
294 } else if (ch == HDLC_FLAG) {
295 /* message is finished */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200296 dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
Harald Weltec68af6a2017-04-30 21:21:52 +0200297 /* allocate new buffer */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200298 sercomm->rx.msg = NULL;
Harald Weltec68af6a2017-04-30 21:21:52 +0200299 /* start all over again */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200300 sercomm->rx.state = RX_ST_WAIT_START;
Harald Weltec68af6a2017-04-30 21:21:52 +0200301
302 /* do not add the control char */
303 break;
304 }
305 /* default case: store the octet */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200306 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200307 *ptr = ch;
308 break;
309 case RX_ST_ESCAPE:
310 /* store bif-5-inverted octet in buffer */
311 ch ^= (1 << 5);
Harald Weltecc95f4b2017-04-30 21:39:33 +0200312 ptr = msgb_put(sercomm->rx.msg, 1);
Harald Weltec68af6a2017-04-30 21:21:52 +0200313 *ptr = ch;
314 /* transition back to normal DATA state */
Harald Weltecc95f4b2017-04-30 21:39:33 +0200315 sercomm->rx.state = RX_ST_DATA;
Harald Weltec68af6a2017-04-30 21:21:52 +0200316 break;
317 }
318
319 return 1;
320}