Merge commit '01fd5cb3f0da802ca82d3b6aef46d2a3d8e6c15c'
[osmocom-bb.git] / src / target / firmware / comm / sercomm.c
1 /* Serial communications layer, based on HDLC */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
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 <osmocore/msgb.h>
28
29 #ifdef HOST_BUILD
30 #ifndef ARRAY_SIZE
31 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
32 #endif
33 #include <sercomm.h>
34 #define local_irq_save(x)
35 #define local_fiq_disable()
36 #define local_irq_restore(x)
37
38 #else
39 #include <debug.h>
40 #include <osmocore/linuxlist.h>
41 #include <asm/system.h>
42
43 #include <comm/sercomm.h>
44 #include <calypso/uart.h>
45 #endif
46
47 #define SERCOMM_RX_MSG_SIZE     256
48
49 enum rx_state {
50         RX_ST_WAIT_START,
51         RX_ST_ADDR,
52         RX_ST_CTRL,
53         RX_ST_DATA,
54         RX_ST_ESCAPE,
55 };
56
57 static struct {
58         int initialized;
59
60         /* transmit side */
61         struct {
62                 struct llist_head dlci_queues[_SC_DLCI_MAX];
63                 struct msgb *msg;
64                 uint8_t *next_char;
65         } tx;
66
67         /* receive side */
68         struct {
69                 dlci_cb_t dlci_handler[_SC_DLCI_MAX];
70                 struct msgb *msg;
71                 enum rx_state state;
72                 uint8_t dlci;
73                 uint8_t ctrl;
74         } rx;
75         
76 } sercomm;
77
78 void sercomm_init(void)
79 {
80         unsigned int i;
81         for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
82                 INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]);
83
84         sercomm.rx.msg = NULL;
85         sercomm.initialized = 1;
86 }
87
88 int sercomm_initialized(void)
89 {
90         return sercomm.initialized;
91 }
92
93 /* user interface for transmitting messages for a given DLCI */
94 void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
95 {
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! */
106         local_irq_save(flags);
107         local_fiq_disable();
108         msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
109         local_irq_restore(flags);
110
111 #ifndef HOST_BUILD
112         /* tell UART that we have something to send */
113         uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1);
114 #endif
115 }
116
117 /* how deep is the Tx queue for a given DLCI */
118 unsigned int sercomm_tx_queue_depth(uint8_t dlci)
119 {
120         struct llist_head *le;
121         unsigned int num = 0;
122
123         llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
124                 num++;
125         }
126
127         return num;
128 }
129
130 /* fetch one octet of to-be-transmitted serial data */
131 int sercomm_drv_pull(uint8_t *ch)
132 {
133         /* we are always called from interrupt context in this function,
134          * which means that any data structures we use need to be for
135          * our exclusive access */
136         if (!sercomm.tx.msg) {
137                 unsigned int i;
138                 /* dequeue a new message from the queues */
139                 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) {
140                         sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]);
141                         if (sercomm.tx.msg)
142                                 break;
143                 }
144                 if (sercomm.tx.msg) {
145                         /* start of a new message, send start flag octet */
146                         *ch = HDLC_FLAG;
147                         sercomm.tx.next_char = sercomm.tx.msg->data;
148                         return 1;
149                 } else {
150                         /* no more data avilable */
151                         return 0;
152                 }
153         }
154
155         /* escaping for the two control octets */
156         if (*sercomm.tx.next_char == HDLC_FLAG ||
157             *sercomm.tx.next_char == HDLC_ESCAPE) {
158                 /* send an escape octet */
159                 *ch = HDLC_ESCAPE;
160                 /* invert bit 5 of the next octet to be sent */
161                 *sercomm.tx.next_char ^= (1 << 5);
162         } else if (sercomm.tx.next_char == sercomm.tx.msg->tail) {
163                 /* last character has already been transmitted,
164                  * send end-of-message octet */
165                 *ch = HDLC_FLAG;
166                 /* we've reached the end of the message buffer */
167                 msgb_free(sercomm.tx.msg);
168                 sercomm.tx.msg = NULL;
169                 sercomm.tx.next_char = NULL;
170         } else {
171                 /* standard case, simply send next octet */
172                 *ch = *sercomm.tx.next_char++;
173         }
174         return 1;
175 }
176
177 /* register a handler for a given DLCI */
178 int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
179 {
180         if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
181                 return -EINVAL;
182
183         if (sercomm.rx.dlci_handler[dlci])
184                 return -EBUSY;
185
186         sercomm.rx.dlci_handler[dlci] = cb;
187         return 0;
188 }
189
190 /* dispatch an incomnig message once it is completely received */
191 static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
192 {
193         if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) ||
194             !sercomm.rx.dlci_handler[dlci]) {
195                 msgb_free(msg);
196                 return;
197         }
198         sercomm.rx.dlci_handler[dlci](dlci, msg);
199 }
200
201 /* the driver has received one byte, pass it into sercomm layer */
202 int sercomm_drv_rx_char(uint8_t ch)
203 {
204         uint8_t *ptr;
205
206         /* we are always called from interrupt context in this function,
207          * which means that any data structures we use need to be for
208          * our exclusive access */
209         if (!sercomm.rx.msg)
210                 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
211
212         if (msgb_tailroom(sercomm.rx.msg) == 0) {
213                 //cons_puts("sercomm_drv_rx_char() overflow!\n");
214                 msgb_free(sercomm.rx.msg);
215                 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
216                 sercomm.rx.state = RX_ST_WAIT_START;
217                 return 0;
218         }
219
220         switch (sercomm.rx.state) {
221         case RX_ST_WAIT_START:
222                 if (ch != HDLC_FLAG)
223                         break;
224                 sercomm.rx.state = RX_ST_ADDR;
225                 break;
226         case RX_ST_ADDR:
227                 sercomm.rx.dlci = ch;
228                 sercomm.rx.state = RX_ST_CTRL;
229                 break;
230         case RX_ST_CTRL:
231                 sercomm.rx.ctrl = ch;
232                 sercomm.rx.state = RX_ST_DATA;
233                 break;
234         case RX_ST_DATA:
235                 if (ch == HDLC_ESCAPE) {
236                         /* drop the escape octet, but change state */
237                         sercomm.rx.state = RX_ST_ESCAPE;
238                         break;
239                 } else if (ch == HDLC_FLAG) {
240                         /* message is finished */
241                         dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
242                         /* allocate new buffer */
243                         sercomm.rx.msg = NULL;
244                         /* start all over again */
245                         sercomm.rx.state = RX_ST_WAIT_START;
246
247                         /* do not add the control char */
248                         break;
249                 }
250                 /* default case: store the octet */
251                 ptr = msgb_put(sercomm.rx.msg, 1);
252                 *ptr = ch;
253                 break;
254         case RX_ST_ESCAPE:
255                 /* store bif-5-inverted octet in buffer */
256                 ch ^= (1 << 5);
257                 ptr = msgb_put(sercomm.rx.msg, 1);
258                 *ptr = ch;
259                 /* transition back to nromal DATA state */
260                 sercomm.rx.state = RX_ST_DATA;
261                 break;
262         }
263
264         return 1;
265 }