1 /* Serial communications layer, based on HDLC */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
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.
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.
29 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
31 #include <osmocom/msgb.h>
36 #include <linuxlist.h>
38 #include <comm/msgb.h>
39 #include <comm/sercomm.h>
40 #include <calypso/uart.h>
43 #define SERCOMM_RX_MSG_SIZE 256
58 struct llist_head dlci_queues[_SC_DLCI_MAX];
65 dlci_cb_t dlci_handler[_SC_DLCI_MAX];
74 void sercomm_init(void)
77 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
78 INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]);
80 sercomm.rx.msg = NULL;
81 sercomm.initialized = 1;
84 int sercomm_initialized(void)
86 return sercomm.initialized;
89 /* user interface for transmitting messages for a given DLCI */
90 void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
94 /* prepend address + control octet */
95 hdr = msgb_push(msg, 2);
98 msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
101 /* tell UART that we have something to send */
102 uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1);
106 /* how deep is the Tx queue for a given DLCI */
107 unsigned int sercomm_tx_queue_depth(uint8_t dlci)
109 struct llist_head *le;
110 unsigned int num = 0;
112 llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
119 /* fetch one octet of to-be-transmitted serial data */
120 int sercomm_drv_pull(uint8_t *ch)
122 if (!sercomm.tx.msg) {
124 /* dequeue a new message from the queues */
125 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) {
126 sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]);
130 if (sercomm.tx.msg) {
131 /* start of a new message, send start flag octet */
133 sercomm.tx.next_char = sercomm.tx.msg->data;
136 /* no more data avilable */
141 /* escaping for the two control octets */
142 if (*sercomm.tx.next_char == HDLC_FLAG ||
143 *sercomm.tx.next_char == HDLC_ESCAPE) {
144 /* send an escape octet */
146 /* invert bit 5 of the next octet to be sent */
147 *sercomm.tx.next_char ^= (1 << 5);
148 } else if (sercomm.tx.next_char == sercomm.tx.msg->tail) {
149 /* last character has already been transmitted,
150 * send end-of-message octet */
152 /* we've reached the end of the message buffer */
153 msgb_free(sercomm.tx.msg);
154 sercomm.tx.msg = NULL;
155 sercomm.tx.next_char = NULL;
157 /* standard case, simply send next octet */
158 *ch = *sercomm.tx.next_char++;
163 /* register a handler for a given DLCI */
164 int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
166 if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
169 if (sercomm.rx.dlci_handler[dlci])
172 sercomm.rx.dlci_handler[dlci] = cb;
176 /* dispatch an incomnig message once it is completely received */
177 static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
179 if (sercomm.rx.dlci_handler[dlci])
180 sercomm.rx.dlci_handler[dlci](dlci, msg);
185 /* the driver has received one byte, pass it into sercomm layer */
186 int sercomm_drv_rx_char(uint8_t ch)
191 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
193 if (msgb_tailroom(sercomm.rx.msg) == 0) {
194 msgb_free(sercomm.rx.msg);
195 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
196 sercomm.rx.state = RX_ST_WAIT_START;
200 switch (sercomm.rx.state) {
201 case RX_ST_WAIT_START:
204 sercomm.rx.state = RX_ST_ADDR;
207 sercomm.rx.dlci = ch;
208 sercomm.rx.state = RX_ST_CTRL;
211 sercomm.rx.ctrl = ch;
212 sercomm.rx.state = RX_ST_DATA;
215 if (ch == HDLC_ESCAPE) {
216 /* drop the escape octet, but change state */
217 sercomm.rx.state = RX_ST_ESCAPE;
219 } else if (ch == HDLC_FLAG) {
220 /* message is finished */
221 dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
222 /* allocate new buffer */
223 sercomm.rx.msg = NULL;
224 /* start all over again */
225 sercomm.rx.state = RX_ST_WAIT_START;
227 /* do not add the control char */
230 /* default case: store the octet */
231 ptr = msgb_put(sercomm.rx.msg, 1);
235 /* store bif-5-inverted octet in buffer */
237 ptr = msgb_put(sercomm.rx.msg, 1);
239 /* transition back to nromal DATA state */
240 sercomm.rx.state = RX_ST_DATA;