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/>.
26 #define UART1_BASE 0x3f8
27 #define UART2_BASE 0x2f8
42 /* interrupt enable register bits */
48 /* fifo control register bits */
49 #define FIFO_ENABLE 0x01
50 #define FIFO_RECV_CLEAR 0x02
51 #define FIFO_SEND_CLEAR 0x04
53 #define FIFO_TRIG_4 0x40
54 #define FIFO_TRIG_8 0x80
55 #define FIFO_TRIG_14 0xc0
57 /* interrupt id register bits */
58 #define IID_PENDING 0x01
62 #define IID_FIFO_EN 0xc0
64 #define IID_SOURCE 0xe
70 #define IID_STATUS 0x6
72 /* line control register bits */
73 #define LCTL_BITS_8 0x03
74 #define LCTL_STOP_2 0x04
75 #define LCTL_DLAB 0x80
76 #define LCTL_8N1 LCTL_BITS_8
77 #define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2)
79 /* modem control register bits */
82 #define MCTL_OUT1 0x04
83 #define MCTL_OUT2 0x08
84 #define MCTL_LOOP 0x10
86 /* line status register bits */
88 #define LST_ERR_OVER 0x02
89 #define LST_ERR_PARITY 0x04
90 #define LST_ERR_FRAME 0x08
91 #define LST_ERR_BRK 0x10
92 #define LST_TREG_EMPTY 0x20
93 #define LST_TIDLE 0x40
94 #define LST_ERROR 0x80
96 /* modem status register bits */
97 #define MST_DELTA_CTS 0x01
98 #define MST_DELTA_DSR 0x02
100 #define MST_DELTA_DCD 0x08
103 #define MST_RING 0x40
106 /* interrupt controller stuff */
107 #define PIC1_CMD_PORT 0x20
108 #define PIC1_DATA_PORT 0x21
109 #define PIC2_CMD_PORT 0xa0
110 #define PIC2_DATA_PORT 0xa1
111 #define OCW2_EOI 0x20
113 #define COM_FMT_8N1 LCTL_8N1
114 #define COM_FMT_8N2 LCTL_8N2
121 int inbuf_ridx, inbuf_widx;
124 #define BNEXT(x) (((x) + 1) & 0xff)
125 #define BEMPTY(b) (b##_ridx == b##_widx)
127 static int have_recv(int base);
128 static void recv_intr();
130 static struct serial_port ports[2];
133 static int uart_base[] = {UART1_BASE, UART2_BASE};
134 static int uart_irq[] = {UART1_IRQ, UART2_IRQ};
136 int ser_open(int pidx, int baud, unsigned int mode)
138 unsigned short div = 115200 / baud;
142 if(pidx < 0 || pidx > 1) {
143 printf("ser_open: invalid serial port: %d\n", pidx);
147 if(ports[pidx].base) {
148 printf("ser_open: port %d already open!\n", pidx);
151 memset(ports + pidx, 0, sizeof ports[pidx]);
153 base = uart_base[pidx];
154 intr = uart_irq[pidx];
162 interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr);
164 outb(LCTL_DLAB, base + UART_LCTL);
165 outb(div & 0xff, base + UART_DIVLO);
166 outb((div >> 8) & 0xff, base + UART_DIVHI);
167 outb(fmt, base + UART_LCTL); /* fmt should be LCTL_8N1, LCTL_8N2 etc */
168 outb(FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR, base + UART_FIFO);
169 outb(MCTL_DTR | MCTL_RTS | MCTL_OUT2, base + UART_MCTL);
170 outb(INTR_RECV, base + UART_INTR);
172 ports[pidx].base = base;
173 ports[pidx].intr = intr;
174 ports[pidx].blocking = 1;
179 void ser_close(int fd)
181 if(--num_open == 0) {
182 outb(0, ports[fd].base + UART_INTR);
183 outb(0, ports[fd].base + UART_MCTL);
189 int ser_block(int fd)
191 ports[fd].blocking = 1;
195 int ser_nonblock(int fd)
197 ports[fd].blocking = 0;
201 int ser_pending(int fd)
203 return !BEMPTY(ports[fd].inbuf);
206 /* if msec < 0: wait for ever */
207 int ser_wait(int fd, long msec)
210 while(!(res = ser_pending(fd))) {
216 static int can_send(int fd)
218 int base = ports[fd].base;
219 return inb(base + UART_LSTAT) & LST_TREG_EMPTY;
222 void ser_putc(int fd, char c)
224 int base = ports[fd].base;
230 while(!can_send(fd));
231 /*while((inb(base + UART_MSTAT) & MST_CTS) == 0);*/
232 outb(c, base + UART_DATA);
237 struct serial_port *p = ports + fd;
241 while(!(have = ser_pending(fd)));
243 have = ser_pending(fd);
247 c = p->inbuf[p->inbuf_ridx];
248 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
253 int ser_write(int fd, const char *buf, int count)
257 ser_putc(fd, *buf++);
262 int ser_read(int fd, char *buf, int count)
265 while(n < count && (c = ser_getc(fd)) != -1) {
272 char *ser_getline(int fd, char *buf, int bsz)
274 static char linebuf[512];
276 int i, rd, size, offs;
278 size = sizeof linebuf - widx;
279 while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
286 for(i=0; i<widx; i++) {
287 if(linebuf[i] == '\r' || linebuf[i] == '\n') {
288 size = i >= bsz ? bsz - 1 : i;
289 memcpy(buf, linebuf, size);
293 memmove(linebuf, linebuf + offs, widx - offs);
301 static int have_recv(int base)
303 unsigned short stat = inb(base + UART_LSTAT);
304 if(stat & LST_ERROR) {
305 panic("serial receive error\n");
307 return stat & LST_DRDY;
310 static void recv_intr()
315 int base = uart_base[i];
316 struct serial_port *p = ports + i;
318 while(((idreg = inb(base + UART_IID)) & IID_PENDING) == 0) {
319 while(have_recv(base)) {
320 c = inb(base + UART_DATA);
322 #ifdef ENABLE_GDB_STUB
323 if(c == 3 && i == GDB_SERIAL_PORT) {
329 p->inbuf[p->inbuf_widx] = c;
330 p->inbuf_widx = BNEXT(p->inbuf_widx);
332 if(p->inbuf_widx == p->inbuf_ridx) {
333 /* we overflowed, drop the oldest */
334 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
341 #ifdef ENABLE_GDB_STUB
342 void putDebugChar(int c)
344 ser_putc(GDB_SERIAL_PORT, c);
347 int getDebugChar(void)
349 return ser_getc(GDB_SERIAL_PORT);