main kernel startup, libc, console tty, asmops, build flags fixes
[bootcensus] / src / serial.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "serial.h"
4 #include "asmops.h"
5
6 #define UART1_BASE      0x3f8
7 #define UART2_BASE      0x2f8
8 #define UART1_IRQ       4
9 #define UART2_IRQ       3
10
11 #define UART_DATA       0
12 #define UART_INTR       1
13 #define UART_DIVLO      0
14 #define UART_DIVHI      1
15 #define UART_FIFO       2
16 #define UART_IID        2
17 #define UART_LCTL       3
18 #define UART_MCTL       4
19 #define UART_LSTAT      5
20 #define UART_MSTAT      6
21
22 /* interrupt enable register bits */
23 #define INTR_RECV       1
24 #define INTR_SEND       2
25 #define INTR_LSTAT      4
26 #define INTR_DELTA      8
27
28 /* fifo control register bits */
29 #define FIFO_ENABLE             0x01
30 #define FIFO_RECV_CLEAR 0x02
31 #define FIFO_SEND_CLEAR 0x04
32 #define FIFO_DMA                0x08
33 #define FIFO_TRIG_4             0x40
34 #define FIFO_TRIG_8             0x80
35 #define FIFO_TRIG_14    0xc0
36
37 /* interrupt id register bits */
38 #define IID_PENDING             0x01
39 #define IID_ID0                 0x02
40 #define IID_ID1                 0x04
41 #define IID_ID2                 0x08
42 #define IID_FIFO_EN             0xc0
43
44 #define IID_SOURCE              0xe
45
46 #define IID_DELTA               0
47 #define IID_SEND                0x2
48 #define IID_RECV                0x4
49 #define IID_FIFO                0xc
50 #define IID_STATUS              0x6
51
52 /* line control register bits */
53 #define LCTL_BITS_8     0x03
54 #define LCTL_STOP_2     0x04
55 #define LCTL_DLAB       0x80
56 #define LCTL_8N1        LCTL_BITS_8
57 #define LCTL_8N2        (LCTL_BITS_8 | LCTL_STOP_2)
58
59 /* modem control register bits */
60 #define MCTL_DTR        0x01
61 #define MCTL_RTS        0x02
62 #define MCTL_OUT1       0x04
63 #define MCTL_OUT2       0x08
64 #define MCTL_LOOP       0x10
65
66 /* line status register bits */
67 #define LST_DRDY                0x01
68 #define LST_ERR_OVER    0x02
69 #define LST_ERR_PARITY  0x04
70 #define LST_ERR_FRAME   0x08
71 #define LST_ERR_BRK             0x10
72 #define LST_TREG_EMPTY  0x20
73 #define LST_TIDLE               0x40
74 #define LST_ERROR               0x80
75
76 /* modem status register bits */
77 #define MST_DELTA_CTS   0x01
78 #define MST_DELTA_DSR   0x02
79 #define MST_TERI                0x04
80 #define MST_DELTA_DCD   0x08
81 #define MST_CTS                 0x10
82 #define MST_DSR                 0x20
83 #define MST_RING                0x40
84 #define MST_DCD                 0x80
85
86 /* interrupt controller stuff */
87 #define PIC1_CMD_PORT   0x20
88 #define PIC1_DATA_PORT  0x21
89 #define PIC2_CMD_PORT   0xa0
90 #define PIC2_DATA_PORT  0xa1
91 #define OCW2_EOI                0x20
92
93 #define COM_FMT_8N1             LCTL_8N1
94 #define COM_FMT_8N2             LCTL_8N2
95
96 struct serial_port {
97         int base, intr;
98         int blocking;
99
100         char inbuf[256];
101         int inbuf_ridx, inbuf_widx;
102 };
103
104 #define BNEXT(x)        (((x) + 1) & 0xff)
105 #define BEMPTY(b)       (b##_ridx == b##_widx)
106
107 /*
108 static int have_recv(int base);
109 static void recv_intr(void);*/
110
111 static struct serial_port ports[2];
112 static int num_open;
113
114 static int uart_base[] = {UART1_BASE, UART2_BASE};
115 /*static int uart_irq[] = {UART1_IRQ, UART2_IRQ};*/
116
117 int ser_open(int pidx, int baud, unsigned int mode)
118 {
119         unsigned short div = 115200 / baud;
120         int base; /*intr*/
121         unsigned int fmt;
122         /*int prev_if;*/
123
124         if(pidx < 0 || pidx > 1) {
125                 printf("ser_open: invalid serial port: %d\n", pidx);
126                 return -1;
127         }
128
129         if(ports[pidx].base) {
130                 printf("ser_open: port %d already open!\n", pidx);
131                 return -1;
132         }
133         memset(ports + pidx, 0, sizeof ports[pidx]);
134
135         base = uart_base[pidx];
136         /*intr = uart_irq[pidx] | 8;*/
137
138         if(mode & SER_8N2) {
139                 fmt = COM_FMT_8N2;
140         } else {
141                 fmt = COM_FMT_8N1;
142         }
143
144         /*prev_if = disable_intr();*/
145         /* TODO set interrupt handler */
146         /* unmask the appropriate interrupt */
147         /*outb(inb(PIC1_DATA_PORT) & ~(1 << uart_irq[pidx]), PIC1_DATA_PORT);*/
148
149         outb(LCTL_DLAB, base + UART_LCTL);
150         outb(div & 0xff, base + UART_DIVLO);
151         outb((div >> 8) & 0xff, base + UART_DIVHI);
152         outb(fmt, base + UART_LCTL);    /* fmt should be LCTL_8N1, LCTL_8N2 etc */
153         outb(FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR, base + UART_FIFO);
154         outb(MCTL_DTR | MCTL_RTS | MCTL_OUT2, base + UART_MCTL);
155         /*outb(INTR_RECV, base + UART_INTR);
156
157         restore_intr(prev_if);*/
158
159         ports[pidx].base = base;
160         /*ports[pidx].intr = intr;*/
161         ports[pidx].blocking = 1;
162         ++num_open;
163         return pidx;
164 }
165
166 void ser_close(int fd)
167 {
168         if(--num_open == 0) {
169                 /*int prev_if = disable_intr();*/
170                 /*outb(0, ports[fd].base + UART_INTR);*/
171                 outb(0, ports[fd].base + UART_MCTL);
172                 /*restore_intr(prev_if);*/
173         }
174
175         ports[fd].base = 0;
176 }
177
178 int ser_block(int fd)
179 {
180         ports[fd].blocking = 1;
181         return 0;
182 }
183
184 int ser_nonblock(int fd)
185 {
186         ports[fd].blocking = 0;
187         return 0;
188 }
189
190 int ser_pending(int fd)
191 {
192         return !BEMPTY(ports[fd].inbuf);
193 }
194
195 /* if msec < 0: wait for ever */
196 int ser_wait(int fd, long msec)
197 {
198         int res;
199         while(!(res = ser_pending(fd))) {
200                 /* TODO timeout */
201         }
202         return res;
203 }
204
205 static int can_send(int fd)
206 {
207         int base = ports[fd].base;
208         return inb(base + UART_LSTAT) & LST_TREG_EMPTY;
209 }
210
211 void ser_putc(int fd, char c)
212 {
213         int base = ports[fd].base;
214         while(!can_send(fd));
215         while((inb(base + UART_MSTAT) & MST_CTS) == 0);
216         outb(c, base + UART_DATA);
217 }
218
219 int ser_getc(int fd)
220 {
221         struct serial_port *p = ports + fd;
222         int have, c = -1;
223
224         if(p->blocking) {
225                 while(!(have = ser_pending(fd)));
226         } else {
227                 have = ser_pending(fd);
228         }
229
230         if(have) {
231                 c = p->inbuf[p->inbuf_ridx];
232                 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
233         }
234         return c;
235 }
236
237 int ser_write(int fd, const char *buf, int count)
238 {
239         int n = count;
240         while(n--) {
241                 ser_putc(fd, *buf++);
242         }
243         return count;
244 }
245
246 int ser_read(int fd, char *buf, int count)
247 {
248         int c, n = 0;
249         while(n < count && (c = ser_getc(fd)) != -1) {
250                 *buf++ = c;
251                 ++n;
252         }
253         return n;
254 }
255
256 char *ser_getline(int fd, char *buf, int bsz)
257 {
258         static char linebuf[512];
259         static int widx;
260         int i, rd, size, offs;
261
262         size = sizeof linebuf - widx;
263         while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
264                 widx += rd;
265                 size -= rd;
266         }
267
268         linebuf[widx] = 0;
269
270         for(i=0; i<widx; i++) {
271                 if(linebuf[i] == '\r' || linebuf[i] == '\n') {
272                         size = i >= bsz ? bsz - 1 : i;
273                         memcpy(buf, linebuf, size);
274                         buf[size] = 0;
275
276                         offs = i + 1;
277                         memmove(linebuf, linebuf + offs, widx - offs);
278                         widx -= offs;
279                         return buf;
280                 }
281         }
282         return 0;
283 }
284
285 #if 0
286 static int have_recv(int base)
287 {
288         unsigned short stat = inb(base + UART_LSTAT);
289         if(stat & LST_ERROR) {
290                 printf("serial receive error\n");
291                 panic();
292         }
293         return stat & LST_DRDY;
294 }
295
296 static void __interrupt __far recv_intr()
297 {
298         int i, idreg, c;
299
300         for(i=0; i<2; i++) {
301                 int base = uart_base[i];
302                 struct serial_port *p = ports + i;
303
304                 while(((idreg = inb(base + UART_IID)) & IID_PENDING) == 0) {
305                         while(have_recv(base)) {
306                                 c = inb(base + UART_DATA);
307
308                                 p->inbuf[p->inbuf_widx] = inb(base + UART_DATA);
309                                 p->inbuf_widx = BNEXT(p->inbuf_widx);
310
311                                 if(p->inbuf_widx == p->inbuf_ridx) {
312                                         /* we overflowed, drop the oldest */
313                                         p->inbuf_ridx = BNEXT(p->inbuf_ridx);
314                                 }
315                         }
316                 }
317         }
318
319         outb(OCW2_EOI, PIC1_CMD_PORT);
320 }
321 #endif