Add 'src/shared/libosmocore/' from commit '3cae0398eaef6045de883849a236c38d1767cb41'
[osmocom-bb.git] / src / target / firmware / lib / console.c
1 /* Ringbuffer based serial console layer, imported from OpenPCD */
2
3 /* (C) 2006-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 <string.h>
25 #include <console.h>
26 #include <calypso/uart.h>
27
28 #include <asm/system.h>
29
30 struct cons {
31         char buf[CONS_RB_SIZE];
32         char *next_inbyte;
33         char *next_outbyte;
34         int initialized;
35 };
36 static struct cons cons;
37
38 void cons_init(void)
39 {
40         memset(cons.buf, 0, sizeof(cons.buf));
41         cons.next_inbyte = &cons.buf[0];
42         cons.next_outbyte = &cons.buf[0];
43         cons.initialized = 1;
44 }
45
46 /* determine how many bytes are left in the ringbuffer without overwriting
47    bytes that haven't been written to the console yet */
48 static int __cons_rb_space(void)
49 {
50         if (cons.next_inbyte == cons.next_outbyte)
51                 return sizeof(cons.buf)-1;
52         else if (cons.next_outbyte > cons.next_inbyte)
53                 return (cons.next_outbyte - cons.next_inbyte) -1;
54         else
55                 return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte);
56 }
57
58 /* pull one char out of debug ring buffer */
59 static int cons_rb_pull(char *ret)
60 {
61         unsigned long flags;
62
63         local_irq_save(flags);
64
65         if (cons.next_outbyte == cons.next_inbyte) {
66                 local_irq_restore(flags);
67                 return -1;
68         }
69
70         *ret = *cons.next_outbyte;
71
72         cons.next_outbyte++;
73         if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) {
74                 cons.next_outbyte = &cons.buf[0];
75         }
76 #if 0
77          else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) {
78                 cons.next_outbyte -= sizeof(cons.buf);
79         }
80 #endif
81
82         local_irq_restore(flags);
83
84         return 0;
85 }
86
87 /* returns if everything was flushed (1) or if there's more to flush (0) */
88 static void __rb_flush_wait(void)
89 {
90         char ch;
91         while (cons_rb_pull(&ch) >= 0)
92                 uart_putchar_wait(CONS_UART_NR, ch);
93 }
94
95 /* returns if everything was flushed (1) or if there's more to flush (0) */
96 static int __rb_flush(void)
97 {
98         while (!uart_tx_busy(CONS_UART_NR)) {
99                 char ch;
100                 if (cons_rb_pull(&ch) < 0) {
101                         /* no more data to write, disable interest in Tx FIFO interrupts */
102                         return 1;
103                 }
104                 uart_putchar_nb(CONS_UART_NR, ch);
105         }
106
107         /* if we reach here, UART Tx FIFO is busy again */
108         return 0;
109 }
110
111 /* flush pending data from debug ring buffer to serial port */
112 int cons_rb_flush(void)
113 {
114         return __rb_flush();
115 }
116
117 static void cons_memcpy(char *pos, const char *data, int len)
118 {
119 #if 0
120         /* Somehow our memcpy is broken !?! */
121         memcpy(pos, data, len);
122 #else
123         int i;
124         for (i = 0; i < len; i++)
125                 *pos++ = *data++;
126 #endif
127 }
128
129 /* Append bytes to ring buffer, not more than we have left! */
130 static void __cons_rb_append(const char *data, int len)
131 {
132         if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) {
133                 int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte;
134                 /* copy the first part before we wrap */
135                 cons_memcpy(cons.next_inbyte, data, before_tail);
136                 data += before_tail;
137                 len -= before_tail;
138                 /* reset the buffer */
139                 cons.next_inbyte = &cons.buf[0];
140         }
141         cons_memcpy(cons.next_inbyte, data, len);
142         cons.next_inbyte += len;
143 }
144
145 /* append bytes to the ringbuffer, do one wrap */
146 int cons_rb_append(const char *data, int len)
147 {
148         unsigned long flags;
149         int bytes_left;
150         const char *data_cur;
151
152         /* we will never be able to write more than the console buffer */
153         if (len > (int) sizeof(cons.buf))
154                 len = sizeof(cons.buf);
155
156         local_irq_save(flags);
157
158         bytes_left = __cons_rb_space();
159         data_cur = data;
160
161         if (len > bytes_left) {
162                 /* append what we can */
163                 __cons_rb_append(data_cur, bytes_left);
164                 /* busy-wait for all characters to be transmitted */
165                 __rb_flush_wait();
166                 /* fill it with the remaining bytes */
167                 len -= bytes_left;
168                 data_cur += bytes_left;
169         }
170         __cons_rb_append(data_cur, len);
171
172         /* we want to get Tx FIFO interrupts */
173         uart_irq_enable(CONS_UART_NR, UART_IRQ_TX_EMPTY, 1);
174
175         local_irq_restore(flags);
176
177         return len;
178 }
179
180 int cons_puts(const char *s)
181 {
182         if (cons.initialized) {
183                 return cons_rb_append(s, strlen(s));
184         } else {
185                 /* if the console is not active yet, we need to fall back */
186                 int i = strlen(s);
187                 while (i--)
188                         uart_putchar_wait(CONS_UART_NR, *s++);
189                 return i;
190         }
191 }
192
193 int cons_putchar(char c)
194 {
195         if (cons.initialized)
196                 return cons_rb_append(&c, 1);
197         else {
198                 /* if the console is not active yet, we need to fall back */
199                 uart_putchar_wait(CONS_UART_NR, c);
200                 return 0;
201         }
202 }