BigOS/kernel/serialport.c
2023-01-01 22:40:12 +08:00

150 lines
3.5 KiB
C

#include "serialport.h"
#include "x86.h"
#include "tty.h"
#include "const.h"
#include "type.h"
#include "assert.h"
#include "stdio.h"
#include "memman.h"
void put_irq_handler(int irq, irq_handler handler);
void enable_irq(int irq);
static void serial_buf_push(NTTY* tty, u32 key);
void serial_handler(int irq) {
volatile char ch = read_serial();
// kprintf("[%d]", ch);
serial_buf_push(get_tty(2), ch);
}
void serial_tty_init_i(NTTY* tty) {
serial_buf* sp = (serial_buf*)tty->input_buf;
sp->buf = (void*)K_PHY2LIN(do_kmalloc(SERIAL_BUF_SIZE));
kprintf("%p", sp->buf);
sp->head = sp->tail = sp->readable = sp->len = 0;
outb(PORT + 1, 0x01);
put_irq_handler(RS232_IRQ, serial_handler);
enable_irq(RS232_IRQ);
}
void serial_tty_init_o(NTTY* tty) {
// do nothing
}
#define NEXTKEY(x) NEXT(x, SERIAL_BUF_SIZE)
#define LASTKEY(x) LAST(x, SERIAL_BUF_SIZE)
static void serial_buf_push(NTTY* tty, u32 key) {
serial_buf* sp = (serial_buf*)tty->input_buf;
u8* buf = sp->buf;
switch (key)
{
case '\r':
case '\n': // escape ENTER
key = '\n';
buf[sp->tail] = '\n';
sp->len++;
sp->tail = NEXTKEY(sp->tail);
sp->readable = CYCLE_SUB(sp->head, sp->tail, SERIAL_BUF_SIZE);
serial_tty_rcevbuf(tty, '\n');
break;
case '\b': case 127:
key = '\b';
if (sp->len > sp->readable)
{
sp->len--;
sp->tail = LASTKEY(sp->tail);
serial_tty_rcevbuf(tty, '\b');
serial_tty_rcevbuf(tty, ' ');
serial_tty_rcevbuf(tty, '\b');
}
break;
default:
if ((key & 0xff) == 0)
return;
if (sp->len == SERIAL_BUF_SIZE - 1)
return;
buf[sp->tail] = (u8)(key & 0xff);
sp->len++;
sp->tail = NEXTKEY(sp->tail);
serial_tty_rcevbuf(tty, key & 0xff);
break;
}
}
int serial_tty_read(NTTY* tty, char* buf, int nr) {
assert(tty->input_buf);
serial_buf *sp = (serial_buf *)tty->input_buf;
int i = 0;
while (1)
{
if (sp->readable)
{
disable_int();
break;
}
}
assert(sp->buf);
u8 *ibuf = sp->buf;
for (; i < nr && i < sp->readable; ++i)
{
*buf ++ = ibuf[sp->head];
sp->head = NEXTKEY(sp->head);
}
sp->readable -= i;
sp->len -= i;
enable_int();
return i;
}
void serial_tty_write(NTTY* tty, char ch) {
write_serial(ch);
}
void serial_tty_rcevbuf(NTTY* tty, char ch) {
serial_tty_write(tty, ch);
}
int init_serial() {
outb(PORT + 1, 0x00); // Disable all interrupts
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
outb(PORT + 1, 0x00); // (hi byte)
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
// Check if serial is faulty (i.e: not same byte as sent)
if(inb(PORT + 0) != 0xAE) {
assert(0);
return 1;
}
// If serial is not faulty set it in normal operation mode
// (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
outb(PORT + 4, 0x0f);
return 0;
}
static inline int is_transmit_empty() {
return inb(PORT + 5) & 0x20;
}
static inline int serial_received() {
return inb(PORT + 5) & 1;
}
char read_serial() {
while (serial_received() == 0);
return inb(PORT);
}
void write_serial(char a) {
while (is_transmit_empty() == 0);
outb(PORT,a);
}