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