2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
27 #define UART1_BASE 0x3f8
28 #define UART2_BASE 0x2f8
43 /* interrupt enable register bits */
49 /* fifo control register bits */
50 #define FIFO_ENABLE 0x01
51 #define FIFO_RECV_CLEAR 0x02
52 #define FIFO_SEND_CLEAR 0x04
54 #define FIFO_TRIG_4 0x40
55 #define FIFO_TRIG_8 0x80
56 #define FIFO_TRIG_14 0xc0
58 /* interrupt id register bits */
59 #define IID_PENDING 0x01
63 #define IID_FIFO_EN 0xc0
65 #define IID_SOURCE 0xe
71 #define IID_STATUS 0x6
73 /* line control register bits */
74 #define LCTL_BITS_8 0x03
75 #define LCTL_STOP_2 0x04
76 #define LCTL_DLAB 0x80
77 #define LCTL_8N1 LCTL_BITS_8
78 #define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2)
80 /* modem control register bits */
83 #define MCTL_OUT1 0x04
84 #define MCTL_OUT2 0x08
85 #define MCTL_LOOP 0x10
87 /* line status register bits */
89 #define LST_ERR_OVER 0x02
90 #define LST_ERR_PARITY 0x04
91 #define LST_ERR_FRAME 0x08
92 #define LST_ERR_BRK 0x10
93 #define LST_TREG_EMPTY 0x20
94 #define LST_TIDLE 0x40
95 #define LST_ERROR 0x80
97 /* modem status register bits */
98 #define MST_DELTA_CTS 0x01
99 #define MST_DELTA_DSR 0x02
100 #define MST_TERI 0x04
101 #define MST_DELTA_DCD 0x08
104 #define MST_RING 0x40
107 /* interrupt controller stuff */
108 #define PIC1_CMD_PORT 0x20
109 #define PIC1_DATA_PORT 0x21
110 #define PIC2_CMD_PORT 0xa0
111 #define PIC2_DATA_PORT 0xa1
112 #define OCW2_EOI 0x20
114 #define COM_FMT_8N1 LCTL_8N1
115 #define COM_FMT_8N2 LCTL_8N2
117 #define INBUF_SIZE 16
118 #define BNEXT(x) (((x) + 1) & (INBUF_SIZE - 1))
119 #define BEMPTY(b) (b##_ridx == b##_widx)
126 char inbuf[INBUF_SIZE];
127 int inbuf_ridx, inbuf_widx;
131 static int have_recv(int base);
132 static void recv_intr();
134 static struct serial_port ports[2];
137 static int uart_base[] = {UART1_BASE, UART2_BASE};
138 static int uart_irq[] = {UART1_IRQ, UART2_IRQ};
140 int ser_open(int pidx, int baud, unsigned int mode)
142 unsigned short div = 115200 / baud;
143 int i, fd, base, intr;
146 if(pidx < 0 || pidx > 1) {
147 printf("ser_open: invalid serial port: %d\n", pidx);
151 for(i=0; i<num_open; i++) {
152 if(ports[i].base == uart_base[pidx]) {
153 /* the port is already open, return the same fd and increment ref */
160 memset(ports + fd, 0, sizeof ports[pidx]);
162 base = uart_base[pidx];
163 intr = uart_irq[pidx];
171 interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr);
173 outp(base + UART_LCTL, LCTL_DLAB);
174 outp(base + UART_DIVLO, div & 0xff);
175 outp(base + UART_DIVHI, (div >> 8) & 0xff);
176 outp(base + UART_LCTL, fmt); /* fmt should be LCTL_8N1, LCTL_8N2 etc */
177 outp(base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR);
178 outp(base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2);
179 outp(base + UART_INTR, INTR_RECV);
181 ports[fd].base = base;
182 ports[fd].intr = intr;
183 ports[fd].blocking = 1;
188 void ser_close(int fd)
190 if(!ports[fd].ref) return;
192 if(--ports[fd].ref == 0) {
193 outp(ports[fd].base + UART_INTR, 0);
194 outp(ports[fd].base + UART_MCTL, 0);
199 int ser_block(int fd)
201 ports[fd].blocking = 1;
205 int ser_nonblock(int fd)
207 ports[fd].blocking = 0;
211 int ser_pending(int fd)
213 return !BEMPTY(ports[fd].inbuf);
216 /* if msec < 0: wait for ever */
217 int ser_wait(int fd, long msec)
220 while(!(res = ser_pending(fd))) {
226 static int can_send(int fd)
228 int base = ports[fd].base;
229 return inp(base + UART_LSTAT) & LST_TREG_EMPTY;
232 void ser_putc(int fd, char c)
234 int base = ports[fd].base;
240 while(!can_send(fd));
241 /*while((inp(base + UART_MSTAT) & MST_CTS) == 0);*/
242 outp(base + UART_DATA, c);
247 struct serial_port *p = ports + fd;
251 while(!(have = ser_pending(fd)));
253 have = ser_pending(fd);
257 c = p->inbuf[p->inbuf_ridx];
258 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
263 int ser_write(int fd, const char *buf, int count)
267 ser_putc(fd, *buf++);
272 int ser_read(int fd, char *buf, int count)
275 while(n < count && (c = ser_getc(fd)) != -1) {
282 char *ser_getline(int fd, char *buf, int bsz)
286 int i, rd, size, offs;
288 size = sizeof linebuf - widx - 1;
289 while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
296 for(i=0; i<widx; i++) {
297 if(linebuf[i] == '\r' || linebuf[i] == '\n') {
298 size = i >= bsz ? bsz - 1 : i;
299 memcpy(buf, linebuf, size);
303 memmove(linebuf, linebuf + offs, widx - offs);
311 static int have_recv(int base)
313 unsigned short stat = inp(base + UART_LSTAT);
314 if(stat & LST_ERROR) {
315 panic("serial receive error\n");
317 return stat & LST_DRDY;
320 static void recv_intr()
325 int base = uart_base[i];
326 struct serial_port *p = ports + i;
328 while(((idreg = inp(base + UART_IID)) & IID_PENDING) == 0) {
329 while(have_recv(base)) {
330 c = inp(base + UART_DATA);
332 #ifdef ENABLE_GDB_STUB
333 if(c == 3 && i == GDB_SERIAL_PORT) {
339 p->inbuf[p->inbuf_widx] = c;
340 p->inbuf_widx = BNEXT(p->inbuf_widx);
342 if(p->inbuf_widx == p->inbuf_ridx) {
343 /* we overflowed, drop the oldest */
344 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
351 #ifdef ENABLE_GDB_STUB
352 void putDebugChar(int c)
354 ser_putc(GDB_SERIAL_PORT, c);
357 int getDebugChar(void)
359 return ser_getc(GDB_SERIAL_PORT);