Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions src/kernel/include/drivers/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,60 @@ enum SERIAL_PORT {
SERIAL_PORT_COM8 = 0x4E8,
};

/**
* @brief Setup the serial port for 38400 baud, 8 bits, no parity, one stop bit.
*
* The chip is tested in loopback mode before being put into normal operation.
*
* @param port serial port base address
* @return 0 for success, 1 if the chip is faulty
*/
int serial_init(uint16_t port);

char serial_read(uint16_t port);
void serial_write_str(uint16_t port, const char * str);
void serial_write(uint16_t port, const char * str, size_t count);
/**
* @brief Read a single character, blocking until one is received.
*
* @param port serial port base address
* @return character read
*/
char serial_read_char(uint16_t port);

/**
* @brief Read count characters into buff, blocking until they are received.
*
* @param port serial port base address
* @param buff buffer to read into
* @param count number of characters to read
* @return number of characters read (0 for failure)
*/
size_t serial_read(uint16_t port, char * buff, size_t count);

/**
* @brief Write a single character, blocking until it can be transmitted.
*
* @param port serial port base address
* @param c character to write
* @return number of characters written (0 for failure)
*/
size_t serial_write_char(uint16_t port, char c);

/**
* @brief Write a null terminated string.
*
* @param port serial port base address
* @param str characters to write
* @return number of characters written (0 for failure)
*/
size_t serial_write_str(uint16_t port, const char * str);

/**
* @brief Write count characters from str.
*
* @param port serial port base address
* @param str characters to write
* @param count number of characters to write
* @return number of characters written (0 for failure)
*/
size_t serial_write(uint16_t port, const char * str, size_t count);

#endif // DRIVERS_SERIAL_H
3 changes: 2 additions & 1 deletion src/kernel/include/kernel/device/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
#define KERNEL_DEVICE_SERIAL_H

#include <stddef.h>
#include <stdint.h>

#include "kernel/io/device.h"

io_device_t * io_device_serial_open();
io_device_t * io_device_serial_open(uint16_t port);

int io_device_serial_write_raw(int handle, const char * buff, size_t size, size_t pos);

Expand Down
53 changes: 38 additions & 15 deletions src/kernel/src/drivers/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

// TODO might need to disable interrupts while interacting with ports?

static int serial_received(uint16_t port);
static int is_transmit_empty(uint16_t port);

int serial_init(uint16_t port) {
port_byte_out(port + 1, 0x00); // Disable all interrupts
port_byte_out(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
Expand All @@ -30,37 +33,57 @@ int serial_init(uint16_t port) {
return 0;
}

static int serial_received(uint16_t port) {
return port_byte_in(port + 5) & 1;
}

char serial_read(uint16_t port) {
char serial_read_char(uint16_t port) {
while (serial_received(port) == 0);

return port_byte_in(port);
}

static int is_transmit_empty(uint16_t port) {
return port_byte_in(port + 5) & 0x20;
size_t serial_read(uint16_t port, char * buff, size_t count) {
if (!buff) {
return 0;
}

for (size_t i = 0; i < count; i++) {
buff[i] = serial_read_char(port);
}

return count;
}

size_t serial_write_char(uint16_t port, char c) {
while (is_transmit_empty(port) == 0);

port_byte_out(port, c);

return 1;
}

void serial_write_str(uint16_t port, const char * str) {
size_t serial_write_str(uint16_t port, const char * str) {
if (!str) {
return;
return 0;
}

size_t count = kstrlen(str);
serial_write(port, str, count);
return serial_write(port, str, count);
}

void serial_write(uint16_t port, const char * str, size_t count) {
size_t serial_write(uint16_t port, const char * str, size_t count) {
if (!str) {
return;
return 0;
}

while (is_transmit_empty(port) == 0);

for (size_t i = 0; i < count; i++) {
port_byte_out(port, *str++);
serial_write_char(port, str[i]);
}

return count;
Comment thread
automas-dev marked this conversation as resolved.
}

static int serial_received(uint16_t port) {
return port_byte_in(port + 5) & 1;
}

static int is_transmit_empty(uint16_t port) {
return port_byte_in(port + 5) & 0x20;
}
73 changes: 55 additions & 18 deletions src/kernel/src/kernel/device/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,74 @@
#include "kernel/memory.h"
#include "libc/string.h"

// WARNING the serial device backs kernel logging, so do not log in this file or
// you will get an infinite loop.

typedef struct _serial_device {
uint16_t port;
} serial_device_t;

static int _serial_close(void * device_data);
static size_t _serial_read(void * device_data, char * buff, size_t size, size_t pos);
static size_t _serial_write(void * device_data, const char * buff, size_t size, size_t pos);

io_device_t * io_device_serial_open() {
io_device_t * dev = kmalloc(sizeof(io_device_t));
if (dev) {
kmemset(dev, 0, sizeof(io_device_t));
io_device_t * io_device_serial_open(uint16_t port) {
serial_device_t * serial = kmalloc(sizeof(serial_device_t));
if (!serial) {
return 0;
}

dev->flags = IO_DEVICE_FLAG_WRITE;
serial->port = port;

dev->close_fn = _serial_close;
dev->write_fn = _serial_write;
io_device_t * dev = kmalloc(sizeof(io_device_t));
if (!dev) {
kfree(serial);
return 0;
}

kmemset(dev, 0, sizeof(io_device_t));

dev->flags = IO_DEVICE_FLAG_READ | IO_DEVICE_FLAG_WRITE;

dev->close_fn = _serial_close;
dev->read_fn = _serial_read;
dev->write_fn = _serial_write;

dev->device_data = serial;

return dev;
}

// handle and pos not used
int io_device_serial_write_raw(int handle, const char * buff, size_t size, size_t pos) {
return serial_write(SERIAL_PORT_COM1, buff, size);
}

static int _serial_close(void * device_data) {
// Nothing to free
if (!device_data) {
return 1;
}

kfree(device_data);
return 0;
}

// device_data and pos not used
static size_t _serial_write(void * device_data, const char * buff, size_t size, size_t pos) {
// TODO get a return value from driver
serial_write(SERIAL_PORT_COM1, buff, size);
return size;
// pos not used
static size_t _serial_read(void * device_data, char * buff, size_t size, size_t pos) {
if (!device_data || !buff) {
return 0;
}

serial_device_t * serial = device_data;
return serial_read(serial->port, buff, size);
Comment thread
automas-dev marked this conversation as resolved.
}

// handle and pos not used
int io_device_serial_write_raw(int handle, const char * buff, size_t size, size_t pos) {
// TODO get a return value from driver
serial_write(SERIAL_PORT_COM1, buff, size);
return size;
// pos not used
static size_t _serial_write(void * device_data, const char * buff, size_t size, size_t pos) {
if (!device_data || !buff) {
return 0;
}

serial_device_t * serial = device_data;
return serial_write(serial->port, buff, size);
}