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.
23 #include <osmocom/core/msgb.h>
27 # define SERCOMM_RX_MSG_SIZE 2048
29 # define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
33 static inline void sercomm_lock(unsigned long __attribute__((unused
)) *flags
) {}
34 static inline void sercomm_unlock(unsigned long __attribute__((unused
)) *flags
) {}
38 # define SERCOMM_RX_MSG_SIZE 256
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>
73 struct llist_head dlci_queues
[_SC_DLCI_MAX
];
81 dlci_cb_t dlci_handler
[_SC_DLCI_MAX
];
91 void sercomm_bind_uart(int uart
)
93 sercomm
.uart_id
= uart
;
96 int sercomm_get_uart(void)
98 return sercomm
.uart_id
;
102 void sercomm_init(void)
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
)
126 /* prepend address + control octet */
127 hdr
= msgb_push(msg
, 2);
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
);
138 /* tell UART that we have something to send */
139 uart_irq_enable(sercomm
.uart_id
, UART_IRQ_TX_EMPTY
, 1);
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
]) {
156 /* fetch one octet of to-be-transmitted serial data */
157 int sercomm_drv_pull(uint8_t *ch
)
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
) {
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
]);
174 if (sercomm
.tx
.msg
) {
175 /* start of a new message, send start flag octet */
177 sercomm
.tx
.next_char
= sercomm
.tx
.msg
->data
;
178 sercomm_unlock(&flags
);
181 /* no more data available */
182 sercomm_unlock(&flags
);
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 */
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 */
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
;
210 /* standard case, simply send next octet */
211 *ch
= *sercomm
.tx
.next_char
++;
214 sercomm_unlock(&flags
);
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
))
224 if (sercomm
.rx
.dlci_handler
[dlci
])
227 sercomm
.rx
.dlci_handler
[dlci
] = cb
;
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
]) {
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
)
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 */
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
;
261 switch (sercomm
.rx
.state
) {
262 case RX_ST_WAIT_START
:
265 sercomm
.rx
.state
= RX_ST_ADDR
;
268 sercomm
.rx
.dlci
= ch
;
269 sercomm
.rx
.state
= RX_ST_CTRL
;
272 sercomm
.rx
.ctrl
= ch
;
273 sercomm
.rx
.state
= RX_ST_DATA
;
276 if (ch
== HDLC_ESCAPE
) {
277 /* drop the escape octet, but change state */
278 sercomm
.rx
.state
= RX_ST_ESCAPE
;
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 */
291 /* default case: store the octet */
292 ptr
= msgb_put(sercomm
.rx
.msg
, 1);
296 /* store bif-5-inverted octet in buffer */
298 ptr
= msgb_put(sercomm
.rx
.msg
, 1);
300 /* transition back to normal DATA state */
301 sercomm
.rx
.state
= RX_ST_DATA
;