#include <unistd.h>
#include <ddi.h>
#include <libarch/ddi.h>
#include <stdio.h>
#include "isa.h"
#include "serial.h"
#define NAME "serial"
#define REG_COUNT 7
#define COM1 0x3F8
#define COM2 0x2F8
static ioport8_t *com1 = 0, *com2 = 0;
static void serial_init_port(ioport8_t *port);
static void serial_write_8(ioport8_t *port, uint8_t c);
static bool is_transmit_empty(ioport8_t *port);
static uint8_t serial_read_8(ioport8_t *port);
static bool serial_received(ioport8_t *port);
static void serial_probe(bridge_to_isa_t *parent);
static void * serial_probe_port(void *phys_addr);
static isa_drv_ops_t serial_isa_ops = {
.probe = serial_probe
};
static isa_drv_t serial_isa_drv = {
.name = NAME,
.ops = &serial_isa_ops
};
int serial_init()
{
// TODO: register this driver by generic isa bus driver
isa_register_driver(&serial_isa_drv);
return 1;
}
static bool serial_received(ioport8_t *port)
{
return (pio_read_8(port + 5) & 1) != 0;
}
static uint8_t serial_read_8(ioport8_t *port)
{
while (!serial_received(port))
;
uint8_t c = pio_read_8(port);
return c;
}
static bool is_transmit_empty(ioport8_t *port)
{
return (pio_read_8(port + 5) & 0x20) != 0;
}
static void serial_write_8(ioport8_t *port, uint8_t c)
{
while (!is_transmit_empty(port))
;
pio_write_8(port, c);
}
static void serial_init_port(ioport8_t *port)
{
pio_write_8(port + 1, 0x00); // Disable all interrupts
pio_write_8(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
pio_write_8(port + 0, 0x60); // Set divisor to 96 (lo byte) 1200 baud
pio_write_8(port + 1, 0x00); // (hi byte)
pio_write_8(port + 3, 0x07); // 8 bits, no parity, two stop bits
pio_write_8(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
pio_write_8(port + 4, 0x0B); // IRQs enabled, RTS/DSR set
}
static void serial_probe(bridge_to_isa_t *parent)
{
printf(NAME
" driver: probe()\n");
printf(NAME
" driver: probing com1 \n");
if (com1 = (ioport8_t *)serial_probe_port(parent->ops->absolutize(COM1))) {
printf(NAME
" driver: initializing com1 \n");
serial_init_port(com1);
} else {
printf(NAME
" driver: com1 is not present \n");
}
printf(NAME
" driver: probing com2 \n");
if (com2 = (ioport8_t *)serial_probe_port(parent->ops->absolutize(COM2))) {
printf(NAME
" driver: initializing com2 \n");
serial_init_port(com2);
}
else {
printf(NAME
" driver: com2 is not present \n");
}
}
// returns virtual address of the serial port, if the serial port is present at this physical address, NULL otherwise
static void * serial_probe_port(void *phys_addr)
{
ioport8_t *port_addr = NULL;
if (pio_enable(phys_addr, REG_COUNT, (void **)(&port_addr))) { // Gain control over port's registers.
printf(NAME
": Error - cannot gain the port %lx.\n", phys_addr
);
return NULL;
}
uint8_t olddata;
olddata = pio_read_8(port_addr + 4);
pio_write_8(port_addr + 4, 0x10);
if ((pio_read_8(port_addr + 6) & 0xf0)) {
return NULL;
}
pio_write_8(port_addr + 4, 0x1f);
if ((pio_read_8(port_addr + 6) & 0xf0) != 0xf0) {
return 0;
}
pio_write_8(port_addr + 4, olddata);
return port_addr;
}