trxcon/l1sched: clarify TDMA Fn (mod 26) maps
[osmocom-bb.git] / src / target / firmware / comm / sercomm.c
blob360dafea5b359a4bc889d9d78c182803d6974d63
1 /* Serial communications layer, based on HDLC */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
5 * All Rights Reserved
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.
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.
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <errno.h>
23 #include <osmocom/core/msgb.h>
25 #ifdef HOST_BUILD
27 # define SERCOMM_RX_MSG_SIZE 2048
28 # ifndef ARRAY_SIZE
29 # define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
30 # endif
31 # include <sercomm.h>
33 static inline void sercomm_lock(unsigned long __attribute__((unused)) *flags) {}
34 static inline void sercomm_unlock(unsigned long __attribute__((unused)) *flags) {}
36 #else
38 # define SERCOMM_RX_MSG_SIZE 256
39 # include <debug.h>
40 # include <osmocom/core/linuxlist.h>
41 # include <asm/system.h>
43 static inline void sercomm_lock(unsigned long *flags)
45 local_firq_save(*flags);
48 static inline void sercomm_unlock(unsigned long *flags)
50 local_irq_restore(*flags);
53 # include <comm/sercomm.h>
54 # include <uart.h>
56 #endif
59 enum rx_state {
60 RX_ST_WAIT_START,
61 RX_ST_ADDR,
62 RX_ST_CTRL,
63 RX_ST_DATA,
64 RX_ST_ESCAPE,
67 static struct {
68 int initialized;
69 int uart_id;
71 /* transmit side */
72 struct {
73 struct llist_head dlci_queues[_SC_DLCI_MAX];
74 struct msgb *msg;
75 enum rx_state state;
76 uint8_t *next_char;
77 } tx;
79 /* receive side */
80 struct {
81 dlci_cb_t dlci_handler[_SC_DLCI_MAX];
82 struct msgb *msg;
83 enum rx_state state;
84 uint8_t dlci;
85 uint8_t ctrl;
86 } rx;
88 } sercomm;
90 #ifndef HOST_BUILD
91 void sercomm_bind_uart(int uart)
93 sercomm.uart_id = uart;
96 int sercomm_get_uart(void)
98 return sercomm.uart_id;
100 #endif
102 void sercomm_init(void)
104 unsigned int i;
105 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
106 INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]);
108 sercomm.rx.msg = NULL;
109 sercomm.initialized = 1;
111 /* set up the echo dlci */
112 sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg);
115 int sercomm_initialized(void)
117 return sercomm.initialized;
120 /* user interface for transmitting messages for a given DLCI */
121 void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
123 unsigned long flags;
124 uint8_t *hdr;
126 /* prepend address + control octet */
127 hdr = msgb_push(msg, 2);
128 hdr[0] = dlci;
129 hdr[1] = HDLC_C_UI;
131 /* This functiion can be called from any context: FIQ, IRQ
132 * and supervisor context. Proper locking is important! */
133 sercomm_lock(&flags);
134 msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
135 sercomm_unlock(&flags);
137 #ifndef HOST_BUILD
138 /* tell UART that we have something to send */
139 uart_irq_enable(sercomm.uart_id, UART_IRQ_TX_EMPTY, 1);
140 #endif
143 /* how deep is the Tx queue for a given DLCI */
144 unsigned int sercomm_tx_queue_depth(uint8_t dlci)
146 struct llist_head *le;
147 unsigned int num = 0;
149 llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
150 num++;
153 return num;
156 /* fetch one octet of to-be-transmitted serial data */
157 int sercomm_drv_pull(uint8_t *ch)
159 unsigned long flags;
161 /* we may be called from interrupt context, but we stiff need to lock
162 * because sercomm could be accessed from a FIQ context ... */
164 sercomm_lock(&flags);
166 if (!sercomm.tx.msg) {
167 unsigned int i;
168 /* dequeue a new message from the queues */
169 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) {
170 sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]);
171 if (sercomm.tx.msg)
172 break;
174 if (sercomm.tx.msg) {
175 /* start of a new message, send start flag octet */
176 *ch = HDLC_FLAG;
177 sercomm.tx.next_char = sercomm.tx.msg->data;
178 sercomm_unlock(&flags);
179 return 1;
180 } else {
181 /* no more data available */
182 sercomm_unlock(&flags);
183 return 0;
187 if (sercomm.tx.state == RX_ST_ESCAPE) {
188 /* we've already transmitted the ESCAPE octet,
189 * we now need to transmit the escaped data */
190 *ch = *sercomm.tx.next_char++;
191 sercomm.tx.state = RX_ST_DATA;
192 } else if (sercomm.tx.next_char >= sercomm.tx.msg->tail) {
193 /* last character has already been transmitted,
194 * send end-of-message octet */
195 *ch = HDLC_FLAG;
196 /* we've reached the end of the message buffer */
197 msgb_free(sercomm.tx.msg);
198 sercomm.tx.msg = NULL;
199 sercomm.tx.next_char = NULL;
200 /* escaping for the two control octets */
201 } else if (*sercomm.tx.next_char == HDLC_FLAG ||
202 *sercomm.tx.next_char == HDLC_ESCAPE ||
203 *sercomm.tx.next_char == 0x00) {
204 /* send an escape octet */
205 *ch = HDLC_ESCAPE;
206 /* invert bit 5 of the next octet to be sent */
207 *sercomm.tx.next_char ^= (1 << 5);
208 sercomm.tx.state = RX_ST_ESCAPE;
209 } else {
210 /* standard case, simply send next octet */
211 *ch = *sercomm.tx.next_char++;
214 sercomm_unlock(&flags);
215 return 1;
218 /* register a handler for a given DLCI */
219 int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
221 if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
222 return -EINVAL;
224 if (sercomm.rx.dlci_handler[dlci])
225 return -EBUSY;
227 sercomm.rx.dlci_handler[dlci] = cb;
228 return 0;
231 /* dispatch an incoming message once it is completely received */
232 static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
234 if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) ||
235 !sercomm.rx.dlci_handler[dlci]) {
236 msgb_free(msg);
237 return;
239 sercomm.rx.dlci_handler[dlci](dlci, msg);
242 /* the driver has received one byte, pass it into sercomm layer */
243 int sercomm_drv_rx_char(uint8_t ch)
245 uint8_t *ptr;
247 /* we are always called from interrupt context in this function,
248 * which means that any data structures we use need to be for
249 * our exclusive access */
250 if (!sercomm.rx.msg)
251 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
253 if (msgb_tailroom(sercomm.rx.msg) == 0) {
254 //cons_puts("sercomm_drv_rx_char() overflow!\n");
255 msgb_free(sercomm.rx.msg);
256 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
257 sercomm.rx.state = RX_ST_WAIT_START;
258 return 0;
261 switch (sercomm.rx.state) {
262 case RX_ST_WAIT_START:
263 if (ch != HDLC_FLAG)
264 break;
265 sercomm.rx.state = RX_ST_ADDR;
266 break;
267 case RX_ST_ADDR:
268 sercomm.rx.dlci = ch;
269 sercomm.rx.state = RX_ST_CTRL;
270 break;
271 case RX_ST_CTRL:
272 sercomm.rx.ctrl = ch;
273 sercomm.rx.state = RX_ST_DATA;
274 break;
275 case RX_ST_DATA:
276 if (ch == HDLC_ESCAPE) {
277 /* drop the escape octet, but change state */
278 sercomm.rx.state = RX_ST_ESCAPE;
279 break;
280 } else if (ch == HDLC_FLAG) {
281 /* message is finished */
282 dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
283 /* allocate new buffer */
284 sercomm.rx.msg = NULL;
285 /* start all over again */
286 sercomm.rx.state = RX_ST_WAIT_START;
288 /* do not add the control char */
289 break;
291 /* default case: store the octet */
292 ptr = msgb_put(sercomm.rx.msg, 1);
293 *ptr = ch;
294 break;
295 case RX_ST_ESCAPE:
296 /* store bif-5-inverted octet in buffer */
297 ch ^= (1 << 5);
298 ptr = msgb_put(sercomm.rx.msg, 1);
299 *ptr = ch;
300 /* transition back to normal DATA state */
301 sercomm.rx.state = RX_ST_DATA;
302 break;
305 return 1;