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.
27 #include <osmocom/core/msgb.h>
31 # define SERCOMM_RX_MSG_SIZE 2048
33 # define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
37 static inline void sercomm_lock(unsigned long __attribute__((unused)) *flags) {}
38 static inline void sercomm_unlock(unsigned long __attribute__((unused)) *flags) {}
42 # define SERCOMM_RX_MSG_SIZE 256
44 # include <osmocom/core/linuxlist.h>
45 # include <asm/system.h>
47 static inline void sercomm_lock(unsigned long *flags)
49 local_firq_save(*flags);
52 static inline void sercomm_unlock(unsigned long *flags)
54 local_irq_restore(*flags);
57 # include <comm/sercomm.h>
76 struct llist_head dlci_queues[_SC_DLCI_MAX];
84 dlci_cb_t dlci_handler[_SC_DLCI_MAX];
93 void sercomm_init(void)
96 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
97 INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]);
99 sercomm.rx.msg = NULL;
100 sercomm.initialized = 1;
102 /* set up the echo dlci */
103 sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg);
106 int sercomm_initialized(void)
108 return sercomm.initialized;
111 /* user interface for transmitting messages for a given DLCI */
112 void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
117 /* prepend address + control octet */
118 hdr = msgb_push(msg, 2);
122 /* This functiion can be called from any context: FIQ, IRQ
123 * and supervisor context. Proper locking is important! */
124 sercomm_lock(&flags);
125 msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
126 sercomm_unlock(&flags);
129 /* tell UART that we have something to send */
130 uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1);
134 /* how deep is the Tx queue for a given DLCI */
135 unsigned int sercomm_tx_queue_depth(uint8_t dlci)
137 struct llist_head *le;
138 unsigned int num = 0;
140 llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
147 /* fetch one octet of to-be-transmitted serial data */
148 int sercomm_drv_pull(uint8_t *ch)
152 /* we may be called from interrupt context, but we stiff need to lock
153 * because sercomm could be accessed from a FIQ context ... */
155 sercomm_lock(&flags);
157 if (!sercomm.tx.msg) {
159 /* dequeue a new message from the queues */
160 for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) {
161 sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]);
165 if (sercomm.tx.msg) {
166 /* start of a new message, send start flag octet */
168 sercomm.tx.next_char = sercomm.tx.msg->data;
169 sercomm_unlock(&flags);
172 /* no more data avilable */
173 sercomm_unlock(&flags);
178 if (sercomm.tx.state == RX_ST_ESCAPE) {
179 /* we've already transmitted the ESCAPE octet,
180 * we now need to transmit the escaped data */
181 *ch = *sercomm.tx.next_char++;
182 sercomm.tx.state = RX_ST_DATA;
183 } else if (sercomm.tx.next_char >= sercomm.tx.msg->tail) {
184 /* last character has already been transmitted,
185 * send end-of-message octet */
187 /* we've reached the end of the message buffer */
188 msgb_free(sercomm.tx.msg);
189 sercomm.tx.msg = NULL;
190 sercomm.tx.next_char = NULL;
191 /* escaping for the two control octets */
192 } else if (*sercomm.tx.next_char == HDLC_FLAG ||
193 *sercomm.tx.next_char == HDLC_ESCAPE ||
194 *sercomm.tx.next_char == 0x00) {
195 /* send an escape octet */
197 /* invert bit 5 of the next octet to be sent */
198 *sercomm.tx.next_char ^= (1 << 5);
199 sercomm.tx.state = RX_ST_ESCAPE;
201 /* standard case, simply send next octet */
202 *ch = *sercomm.tx.next_char++;
205 sercomm_unlock(&flags);
209 /* register a handler for a given DLCI */
210 int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
212 if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
215 if (sercomm.rx.dlci_handler[dlci])
218 sercomm.rx.dlci_handler[dlci] = cb;
222 /* dispatch an incoming message once it is completely received */
223 static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
225 if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) ||
226 !sercomm.rx.dlci_handler[dlci]) {
230 sercomm.rx.dlci_handler[dlci](dlci, msg);
233 /* the driver has received one byte, pass it into sercomm layer */
234 int sercomm_drv_rx_char(uint8_t ch)
238 /* we are always called from interrupt context in this function,
239 * which means that any data structures we use need to be for
240 * our exclusive access */
242 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
244 if (msgb_tailroom(sercomm.rx.msg) == 0) {
245 //cons_puts("sercomm_drv_rx_char() overflow!\n");
246 msgb_free(sercomm.rx.msg);
247 sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
248 sercomm.rx.state = RX_ST_WAIT_START;
252 switch (sercomm.rx.state) {
253 case RX_ST_WAIT_START:
256 sercomm.rx.state = RX_ST_ADDR;
259 sercomm.rx.dlci = ch;
260 sercomm.rx.state = RX_ST_CTRL;
263 sercomm.rx.ctrl = ch;
264 sercomm.rx.state = RX_ST_DATA;
267 if (ch == HDLC_ESCAPE) {
268 /* drop the escape octet, but change state */
269 sercomm.rx.state = RX_ST_ESCAPE;
271 } else if (ch == HDLC_FLAG) {
272 /* message is finished */
273 dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
274 /* allocate new buffer */
275 sercomm.rx.msg = NULL;
276 /* start all over again */
277 sercomm.rx.state = RX_ST_WAIT_START;
279 /* do not add the control char */
282 /* default case: store the octet */
283 ptr = msgb_put(sercomm.rx.msg, 1);
287 /* store bif-5-inverted octet in buffer */
289 ptr = msgb_put(sercomm.rx.msg, 1);
291 /* transition back to normal DATA state */
292 sercomm.rx.state = RX_ST_DATA;