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/>.
23 #define UART1_BASE 0x3f8
24 #define UART2_BASE 0x2f8
39 /* interrupt enable register bits */
45 /* fifo control register bits */
46 #define FIFO_ENABLE 0x01
47 #define FIFO_RECV_CLEAR 0x02
48 #define FIFO_SEND_CLEAR 0x04
50 #define FIFO_TRIG_4 0x40
51 #define FIFO_TRIG_8 0x80
52 #define FIFO_TRIG_14 0xc0
54 /* interrupt id register bits */
55 #define IID_PENDING 0x01
59 #define IID_FIFO_EN 0xc0
61 #define IID_SOURCE 0xe
67 #define IID_STATUS 0x6
69 /* line control register bits */
70 #define LCTL_BITS_8 0x03
71 #define LCTL_STOP_2 0x04
72 #define LCTL_DLAB 0x80
73 #define LCTL_8N1 LCTL_BITS_8
74 #define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2)
76 /* modem control register bits */
79 #define MCTL_OUT1 0x04
80 #define MCTL_OUT2 0x08
81 #define MCTL_LOOP 0x10
83 /* line status register bits */
85 #define LST_ERR_OVER 0x02
86 #define LST_ERR_PARITY 0x04
87 #define LST_ERR_FRAME 0x08
88 #define LST_ERR_BRK 0x10
89 #define LST_TREG_EMPTY 0x20
90 #define LST_TIDLE 0x40
91 #define LST_ERROR 0x80
93 /* modem status register bits */
94 #define MST_DELTA_CTS 0x01
95 #define MST_DELTA_DSR 0x02
97 #define MST_DELTA_DCD 0x08
100 #define MST_RING 0x40
103 /* interrupt controller stuff */
104 #define PIC1_CMD_PORT 0x20
105 #define PIC1_DATA_PORT 0x21
106 #define PIC2_CMD_PORT 0xa0
107 #define PIC2_DATA_PORT 0xa1
108 #define OCW2_EOI 0x20
110 #define COM_FMT_8N1 LCTL_8N1
111 #define COM_FMT_8N2 LCTL_8N2
118 int inbuf_ridx, inbuf_widx;
121 #define BNEXT(x) (((x) + 1) & 0xff)
122 #define BEMPTY(b) (b##_ridx == b##_widx)
125 static int have_recv(int base);
126 static void recv_intr(void);*/
128 static struct serial_port ports[2];
131 static int uart_base[] = {UART1_BASE, UART2_BASE};
132 /*static int uart_irq[] = {UART1_IRQ, UART2_IRQ};*/
134 int ser_open(int pidx, int baud, unsigned int mode)
136 unsigned short div = 115200 / baud;
141 if(pidx < 0 || pidx > 1) {
142 printf("ser_open: invalid serial port: %d\n", pidx);
146 if(ports[pidx].base) {
147 printf("ser_open: port %d already open!\n", pidx);
150 memset(ports + pidx, 0, sizeof ports[pidx]);
152 base = uart_base[pidx];
153 /*intr = uart_irq[pidx] | 8;*/
161 /*prev_if = disable_intr();*/
162 /* TODO set interrupt handler */
163 /* unmask the appropriate interrupt */
164 /*outb(inb(PIC1_DATA_PORT) & ~(1 << uart_irq[pidx]), PIC1_DATA_PORT);*/
166 outb(LCTL_DLAB, base + UART_LCTL);
167 outb(div & 0xff, base + UART_DIVLO);
168 outb((div >> 8) & 0xff, base + UART_DIVHI);
169 outb(fmt, base + UART_LCTL); /* fmt should be LCTL_8N1, LCTL_8N2 etc */
170 outb(FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR, base + UART_FIFO);
171 outb(MCTL_DTR | MCTL_RTS | MCTL_OUT2, base + UART_MCTL);
172 /*outb(INTR_RECV, base + UART_INTR);
174 restore_intr(prev_if);*/
176 ports[pidx].base = base;
177 /*ports[pidx].intr = intr;*/
178 ports[pidx].blocking = 1;
183 void ser_close(int fd)
185 if(--num_open == 0) {
186 /*int prev_if = disable_intr();*/
187 /*outb(0, ports[fd].base + UART_INTR);*/
188 outb(0, ports[fd].base + UART_MCTL);
189 /*restore_intr(prev_if);*/
195 int ser_block(int fd)
197 ports[fd].blocking = 1;
201 int ser_nonblock(int fd)
203 ports[fd].blocking = 0;
207 int ser_pending(int fd)
209 return !BEMPTY(ports[fd].inbuf);
212 /* if msec < 0: wait for ever */
213 int ser_wait(int fd, long msec)
216 while(!(res = ser_pending(fd))) {
222 static int can_send(int fd)
224 int base = ports[fd].base;
225 return inb(base + UART_LSTAT) & LST_TREG_EMPTY;
228 void ser_putc(int fd, char c)
230 int base = ports[fd].base;
236 while(!can_send(fd));
237 while((inb(base + UART_MSTAT) & MST_CTS) == 0);
238 outb(c, base + UART_DATA);
243 struct serial_port *p = ports + fd;
247 while(!(have = ser_pending(fd)));
249 have = ser_pending(fd);
253 c = p->inbuf[p->inbuf_ridx];
254 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
259 int ser_write(int fd, const char *buf, int count)
263 ser_putc(fd, *buf++);
268 int ser_read(int fd, char *buf, int count)
271 while(n < count && (c = ser_getc(fd)) != -1) {
278 char *ser_getline(int fd, char *buf, int bsz)
280 static char linebuf[512];
282 int i, rd, size, offs;
284 size = sizeof linebuf - widx;
285 while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
292 for(i=0; i<widx; i++) {
293 if(linebuf[i] == '\r' || linebuf[i] == '\n') {
294 size = i >= bsz ? bsz - 1 : i;
295 memcpy(buf, linebuf, size);
299 memmove(linebuf, linebuf + offs, widx - offs);
308 static int have_recv(int base)
310 unsigned short stat = inb(base + UART_LSTAT);
311 if(stat & LST_ERROR) {
312 printf("serial receive error\n");
315 return stat & LST_DRDY;
318 static void __interrupt __far recv_intr()
323 int base = uart_base[i];
324 struct serial_port *p = ports + i;
326 while(((idreg = inb(base + UART_IID)) & IID_PENDING) == 0) {
327 while(have_recv(base)) {
328 c = inb(base + UART_DATA);
330 p->inbuf[p->inbuf_widx] = inb(base + UART_DATA);
331 p->inbuf_widx = BNEXT(p->inbuf_widx);
333 if(p->inbuf_widx == p->inbuf_ridx) {
334 /* we overflowed, drop the oldest */
335 p->inbuf_ridx = BNEXT(p->inbuf_ridx);
341 outb(OCW2_EOI, PIC1_CMD_PORT);