Browse Source

wip drivers

master
Mathieu Serandour 12 months ago
parent
commit
efbb722121
  1. 45
      kernel/drivers/dev.c
  2. 24
      kernel/drivers/dev.h
  3. 86
      kernel/drivers/driver.c
  4. 34
      kernel/drivers/driver.h
  5. 57
      kernel/drivers/nvme.c
  6. 3
      kernel/drivers/nvme.h
  7. 243
      kernel/drivers/pcie/pcie.c
  8. 92
      kernel/drivers/pcie/pcie.h
  9. 254
      kernel/drivers/pcie/scan.c
  10. 7
      kernel/drivers/pcie/scan.h
  11. 1
      kernel/drivers/ps2kb.c
  12. 342
      kernel/drivers/terminal/terminal.c
  13. 50
      kernel/drivers/terminal/terminal.h
  14. 78
      kernel/drivers/terminal/video.c
  15. 19
      kernel/drivers/terminal/video.h

45
kernel/drivers/dev.c

@ -0,0 +1,45 @@
#include <stddef.h>
#include "dev.h"
static struct dev** devices = NULL;
static unsigned n_devices = 0;
void realloc_dev(void) {
static unsigned buffsize = 0;
if(n_devices == 0)
buffsize = 0;
else if(buffsize == 0)
buffsize = n_devices;
else if(n_devices > buffsize)
buffsize *= 2;
else if(n_devices < buffsize / 2)
buffsize /= 2;
devices = realloc(devices,
buffsize * sizeof(struct dev*));
}
void register_dev(struct dev* dev) {
unsigned i = n_devices++;
realloc_dev();
devices[i] = dev;
}
void free_all_devices(void) {
for(unsigned i = 0; i < n_devices; i++) {
string_free(&devices[i]->name);
free(devices[i]);
}
n_devices = 0;
realloc_dev();
}

24
kernel/drivers/dev.h

@ -0,0 +1,24 @@
#pragma once
#include "../lib/string_t.h"
typedef struct driver driver_t;
/*
generic device:
every device should derive from this
*/
typedef struct dev {
unsigned type;
string_t name;
driver_t* driver;
} dev_t;
void register_dev(struct dev*);
// this function does not
// remove the drivers!
// it only free the memory
void free_all_devices(void);

86
kernel/drivers/driver.c

@ -0,0 +1,86 @@
#include "driver.h"
#include "dev.h"
#include "../memory/heap.h"
#include "../lib/assert.h"
#include "../lib/logging.h"
struct node {
driver_t dr;
struct node* next;
};
static struct node* head = NULL;
static unsigned n_drivers = 0;
// alloc the structure and fill with
// basic info
// and put it in the list
static driver_t* push_driver_struct(
struct dev* dev
) {
struct node* n = malloc(sizeof(struct node));
n->dr.device = dev;
n->dr.status = DRIVER_STATE_UNINITIALIZED;
// append it to the list
n->next = head;
head = n;
return &n->dr;
}
static void free_node(struct node* n) {
assert(n != NULL);
string_free(&n->dr.name);
free(n);
}
// erase the last pushed driver
static void pop_driver_struct(void) {
assert(head != NULL);
struct node* old = head;
head = head->next;
free_node(old);
}
void driver_register_and_install(
int (*install)(struct driver*),
struct dev* dev
) {
driver_t* dr = push_driver_struct(dev);
dr->install = install;
if(install(dr)) {
assert(dr->remove != NULL);
assert(dr->name.ptr != NULL);
dev->driver = dr;
log_debug("'%s' installed", dr->name);
}
else
pop_driver_struct();
}
void remove_all_drivers(void) {
// iterate through the driver list
for(struct node* node = head;
node != NULL;
)
{
log_debug("removing driver %s...",
node->dr.name.ptr);
node->dr.remove(&node->dr);
struct node* next = node->next;
free_node(node);
node = next;
}
}

34
kernel/drivers/driver.h

@ -3,7 +3,7 @@
#include <stdint.h>
#include <stddef.h>
#include <stdatomic.h>
#include "../lib/string_t.h"
typedef atomic_bool lock_t;
@ -13,28 +13,28 @@ typedef atomic_bool lock_t;
typedef struct {
void* addr;
size_t size;
void* addr;
unsigned size;
unsigned type;
} resource_t;
typedef struct {
void(* install)(driver_t*);
void(* remove) (void);
const char* name;
typedef struct driver {
// returns 0 if everything is fine
int (*install)(struct driver*);
void (*remove)(struct driver*);
string_t name;
uint32_t status;
lock_t lock;
// things like pci BARs,
// hpet registers space, ...
resource_t* iores;
// can be NULL
struct pcie_device* device;
struct dev* device;
const driver_t* parent;
const struct driver* parent;
void* data;
size_t data_len;
@ -42,6 +42,10 @@ typedef struct {
} driver_t;
void register_driver(driver_t* dr);
void driver_register_and_install(
int (*install)(struct driver*),
struct dev* dev
);
void remove_all_drivers(void);

57
kernel/drivers/nvme.c

@ -0,0 +1,57 @@
#include "nvme.h"
#include "pcie/pcie.h"
#include "driver.h"
#include "../lib/logging.h"
static void remove(driver_t* this);
struct data {
int foo;
};
int nvme_install(driver_t* this) {
if(this->device->type != DEVICE_ID_PCIE)
return 0;
this->remove = remove;
this->name = (string_t) {"NVMe driver", 0};
this->data = malloc(sizeof(struct data));
this->data_len = sizeof(struct data);
struct pcie_dev* dev = (struct pcie_dev*)this->device;
log_warn("bar0 base=%lx,type=%x,prefetchable=%x",
dev->bars[0].base,
dev->bars[0].type,
dev->bars[0].prefetchable
);
struct PCIE_config_space* cs = dev->config_space;
volatile uint64_t* bar_reg = (uint64_t*)&cs->bar[0];
//uint64_t val = *bar_reg;
*bar_reg = ~0llu;
log_warn("size %x", pcie_bar_size(dev, 0));
assert(enable_msi(dev, 50, 0, 0, 0));
int x = 0;
// installed
return 1;
}
static void remove(driver_t* this) {
free(this->data);
}

3
kernel/drivers/nvme.h

@ -1,2 +1,5 @@
#pragma once
struct driver;
int nvme_install(struct driver*);

243
kernel/drivers/pcie/pcie.c

@ -1,117 +1,200 @@
#include "pcie.h"
#include "scan.h"
#include "../driver.h"
#include "../nvme.h"
#include "../../lib/logging.h"
#include "../../lib/assert.h"
#include "../../lib/panic.h"
#include "../../memory/heap.h"
#include "../../memory/paging.h"
#include "../../memory/vmap.h"
#include "../../lib/string.h"
////__attribute__((pure))
unsigned pcie_bar_size(void* config_space,
unsigned i)
{
struct PCIE_config_space* cs = config_space;
static struct pcie_dev* devices = NULL;
static unsigned n_devices = 0;
// array of drivers
// should be the same size as the
// device array.
// device[i] corresponds to drivers[i]
// if drivers[i] = NULL, device[i] doesn't
// have an installed driver
static struct driver** drivers = NULL;
volatile uint32_t* bar_reg = (uint64_t*)
&cs->bar[i];
// at shutdown
static void pcie_shutdown(void);
// we only need the 32 bit value
// even if it is 64 bit, the bar sizes are
// inferior to 4 GB
uint32_t val32 = *bar_reg;
*bar_reg = ~0x0fllu | (val32 & 0x0f);
unsigned read = (unsigned) ~*bar_reg;
////__attribute__((pure))
static inline struct resource get_bar_resource(
struct PCIE_config_space* cs,
unsigned i)
{
volatile uint64_t* bar_reg = (uint64_t*)&cs->bar[i];
uint64_t val = *bar_reg;
*bar_reg = ~0llu;
unsigned size = (unsigned) ~*bar_reg + 1;
*bar_reg = val;
*bar_reg = val32;
return (read & ~0x0f) + 0x10;
}
// recognize compatible drivers
// and set them up
static void scan_drivers_callback(struct pcie_dev* dev) {
register_dev((struct dev *)dev);
log_info("pcie device: %s %2x:%2x:%2x.%2x, "
"%2x.%2x.%2x, rev%x",
dev->dev.name,
dev->path.domain,
dev->path.bus,
dev->path.device,
dev->path.func,
dev->info.classcode,
dev->info.subclasscode,
dev->info.progIF,
dev->info.revID
);
// 3.0: vga controller
// 3.1: xga
// 3.2: 3d
// 3.80: other
if(dev->info.classcode == 1 &&
dev->info.subclasscode == 8) {
//1.8: NVMe controller
driver_register_and_install(nvme_install, (dev_t*)dev);
}
if(dev->info.classcode == 03 &&
dev->info.subclasscode == 00) {
// vga
//setup_msi(dev, 48, 0, 0, 0);
}
// register it even if there is no driver
// to treat it
}
return (struct resource) {
.addr = (void *)val,
.size = size,
};
void pcie_init(void) {
log_debug("init pcie...");
pcie_scan(scan_drivers_callback);
}
// alloc the structure and fill with
// basic info
static struct driver* create_driver_data(
struct device* dev)
{
struct driver* dr = malloc(sizeof(struct driver));
__attribute__((pure))
static void* get_capability(struct PCIE_config_space* cs,
uint8_t capabilityID
) {
dr->dev = dev;
dr->status = DRIVER_STATE_UNINITIALIZED;
// check that the device has a
// pointer to the capabilities list
assert((cs->status & 0x10) == 0x10);
return dr;
}
// traverse the capabilities list
// see https://wiki.osdev.org/PCI#IRQ_Handling
for(uint32_t*
reg = (uint32_t*)(cs->capabilities | (uint64_t)cs);
reg != NULL;
) {
log_warn("issou %x", (*reg & 0xff));
if(capabilityID == (*reg & 0xff))
return reg;
unsigned index = (*reg>>8)&0xff;
if(index == 0)
break;
else
reg = (uint32_t*)(index | (uint64_t)cs);
// recognize compatible drivers
// and set them up
static void setup_drivers(void) {
drivers = malloc(n_devices * sizeof(void *));
for(int i = 0; i < n_devices; i++) {
struct pcie_dev* dev = &devices[i];
// only one driver is supported right now....
if(dev->info.classcode == 1) {
// mass storage drive
if(dev->info.subclasscode == 8) {
// yey! we found an nvme device!
struct driver* dr = create_driver_data(dev);
drivers[i] = dr;
if(vme_install(drivers[i])) {
dev->driver = dr;
log_info("installed driver %u",
dr->driver_name);
}
else {
dr->status = DRIVER_STATE_FAULT;
panic("couldn't install NVMe driver!!");
}
}
}
}
return NULL;
}
void pcie_init(void) {
log_debug("init pcie...");
devices = pcie_scan(&n_devices);
int enable_msi(struct pcie_dev* dev,
unsigned vector,
uint32_t processor,
uint32_t edge_trigger,
uint8_t deassert
) {
assert(dev != NULL);
struct PCIE_config_space* cs = dev->config_space;
uint32_t* cap = get_capability(cs, 0x05);
atshutdown(pcie_shutdown);
}
if(!cap) {
// no MSI support on the device
return 0;
}
// message address
cap[1] = 0xFEE00000 | (processor << 12);
cap[2] = 0; // high is reserved as NULL
static void free_device(struct pcie_dev* dev) {
assert(dev);
assert(dev->name);
// message data
free(dev->name);
*(uint16_t*)(cap+3) = (vector & 0xFF)
| (edge_trigger == 1 ? 0 : (1 << 15))
| (deassert == 1 ? 0 : (1 << 14));
// enable MSIs
((uint16_t*)cap)[1] = (((uint16_t*)cap)[1] & 0xff7e) | 1;
// keep reserved bits and capabilities
// per-vector unmask (bit 8)
// and enable (bit 0)
return 1;
}
static void pcie_shutdown(void) {
for(int i = 0; i < n_devices; i++) {
free_device(devices+i);
int enable_msix(struct pcie_dev* dev,
unsigned vector,
uint32_t processor,
uint32_t edge_trigger,
uint8_t deassert
) {
assert(dev != NULL);
struct PCIE_config_space* cs = dev->config_space;
uint32_t* restrict cap = get_capability(cs, 0x11);
if(!cap) {
// no MSI-X support on the device
return 0;
}
n_devices = 0;
free(devices);
unsigned table_reg = cap[2];
// bar index
unsigned bir = table_reg & 0x7u;
// offset in bar[bir]
unsigned table_offset = table_reg & ~0x7u;
unsigned table_size = ((cap[0] >> 16) & 0x3ff) + 1;
// bar0 ... bar5
assert(bir < 6);
//if(dev->bars[i])
//uint64_t* table = dev->bars
((uint16_t*)cap)[1] = (((uint16_t*)cap)[1] & 0x3fff) | 0x8000;
// set 'enable' bit 15
// unset 'function mask' bit 14
// keep other bits
return 1;
}

92
kernel/drivers/pcie/pcie.h

@ -4,17 +4,12 @@
#include <stddef.h>
#include "../../lib/assert.h"
#include "../driver.h"
#include "../dev.h"
void pcie_init(void);
typedef void (*driver_init_fun)(void* config_space);
typedef void (*driver_callback)(void);
struct resource {
void* addr;
size_t size;
};
// pcie devices structures
// should have this type
#define DEVICE_ID_PCIE (0x2c1e)
typedef union {
struct {
@ -40,64 +35,79 @@ struct dev_info {
uint8_t revID;
};
static_assert_equals(sizeof(pcie_path_t), 8);
typedef struct {
uint64_t base;
unsigned io: 1;
unsigned type: 2;
unsigned prefetchable: 1;
} bar_t;
struct pcie_dev {
struct resource* resources;
struct dev_info info;
const char* name;
struct dev dev;
void* config_space; // vaddr
void* config_space;
// if 64 bit bar:
// bars[2*i] contains the whole
// address along with flags
// and bars[2*i+1] is unused
bar_t bars[6];
pcie_path_t path;
struct dev_info info;
};
struct driver* driver;
static_assert_equals(sizeof(pcie_path_t), 8);
void pcie_init(void);
// return 0 if MSIs cannot be enabled
// 1 instead
int enable_msi(struct pcie_dev* dev,
unsigned vector,
uint32_t processor,
uint32_t edge_trigger,
uint8_t deassert);
// return 0 if MSIXs cannot be enabled
// 1 instead
int enable_msix(struct pcie_dev* dev,
unsigned vector,
uint32_t processor,
uint32_t edge_trigger,
uint8_t deassert);
};
struct pcie_driver {
const struct pcie_device* dev;
};
__attribute__((pure))
unsigned pcie_bar_size(void* config_space, unsigned i);
struct PCIE_config_space {
volatile uint16_t vendorID;
volatile uint16_t deviceID;
uint16_t unused0;
uint16_t unused1;
volatile uint16_t command;
volatile uint16_t status;
volatile uint8_t revID;
volatile uint8_t progIF;
volatile uint8_t subclasscode;
volatile uint8_t classcode;
uint16_t reserved3;
volatile uint16_t header_type;
volatile uint8_t header_type;
volatile uint8_t BIST;
volatile uint32_t bar[6];
volatile uint32_t cardbud_cis_ptr;
volatile uint16_t subsystemID;
volatile uint16_t subsystem_vendorID;
volatile uint16_t subsystemID;
volatile uint32_t expansion_base;
volatile uint8_t capabilities;
uint8_t reserved0[3];
uint32_t reserved1;
volatile uint8_t interrupt_line;
volatile uint8_t interrupt_pin;
uint16_t reserved5[2];
uint8_t reserved5[2];
} __packed;
/**
* PCIE drivers interfaces:
*
* provide void init(void* config_space_base)
*
* functions to call:
* register_irq(unsigned)
* unregister_irq(unsigned)
*
* int register_timer(void callback(void), unsigned period)
* void unregister_timer(int timer_id)
*
*/
static_assert_equals(sizeof(struct PCIE_config_space), 0x40);

254
kernel/drivers/pcie/scan.c

@ -6,29 +6,15 @@
#include "../../lib/assert.h"
#include "../../memory/heap.h"
#include "../../memory/paging.h"
#include "../../memory/vmap.h"
#include "../../lib/string.h"
#include "../../lib/sprintf.h"
struct PCIE_Descriptor pcie_descriptor = {0};
// array of all installed devices' functions
static struct pcie_dev* found_devices = NULL;
static size_t n_found_devices = 0;
// we construct a linked list first
// so we need a node struct
struct early_device_desc {
uint16_t bus, device, function, domain;
struct PCIE_config_space* config_space;
struct early_device_desc* next;
};
// used to iterate over devices when scanning
struct early_device_desc head_node = {.next = NULL};
struct early_device_desc* current = &head_node;
//__attribute__((pure))
static struct PCIE_config_space*
@ -43,11 +29,10 @@ static struct PCIE_config_space*
struct PCIE_busgroup* group_desc = &pcie_descriptor.array[bus_group];
return group_desc->address + (
return translate_address(group_desc->address) + (
(bus - group_desc->start_bus) << 20
| device << 15
| func << 12);
| func << 12);
}
@ -59,50 +44,133 @@ static int __attribute__((pure))
{
struct PCIE_config_space* config_space =
get_config_space_base(bus_group, bus,device,func);
//return 8086;
return config_space->vendorID;
}
static void insert(
static void new_dev(
void (*callback)(struct pcie_dev*),
unsigned bus_group,
unsigned bus,
unsigned device,
unsigned func)
{
unsigned func
) {
struct PCIE_config_space* config_space =
struct PCIE_config_space* cs =
get_config_space_base(bus_group,
bus,
device,
func);
if((config_space->header_type & 0x7f) != 0 ||
config_space->classcode == 06)
if((cs->header_type & 0x7f) != 0 ||
cs->classcode == 06)
return;
// the device is a bridge,
// we won't do anything with it anyway
// give a unique name to each device
static unsigned id = 0;
current->next = malloc(sizeof(struct early_device_desc));
current = current->next;
current->next = NULL;
// fill the new node
current->domain = bus_group;
current->bus = bus;
current->device = device;
current->function = func;
current->config_space = config_space;
// we hate mallocs.
// we definitly want to do as few mallocs
// as we possible.
// therefore we will trickely make one malloc
// call to allocate both the struct and the name
// string
// the device manager will invoke a free call
// with the device's address, so freeing
// the dev should free the name.
struct alloc {
struct pcie_dev dev;
char name[16];
// 16 should always be enough
};
struct alloc* alloc = malloc(sizeof(struct alloc));
struct pcie_dev* dev = &alloc->dev;
char* name_buf = alloc->name;
sprintf(name_buf, "pcie%u", id++);
n_found_devices++;
// fill the struct
*dev = (struct pcie_dev) {
.dev = {
.type = DEVICE_ID_PCIE,
.name = {name_buf, 0},
.driver = NULL,
},
.config_space = cs,
.info = (struct dev_info) {
.vendorID = cs->vendorID,
.deviceID = cs->deviceID,
.classcode = cs->classcode,
.subclasscode = cs->subclasscode,
.progIF = cs->progIF,
.revID = cs->revID,
},
.path = (pcie_path_t) {
.domain = bus_group,
.bus = bus,
.device = device,
.func = func,
},
};
// BARs scan
for(unsigned i = 0; i < 6;) {
// next iteration index
unsigned next_i = i+1;
uint32_t bar_reg = cs->bar[i];
uint64_t addr = bar_reg & ~0xf;
unsigned type = (bar_reg>>1) & 3;
switch(type) {
case 0: // 32bit address
break;
case 2: // 64bit address
addr |= (uint64_t)cs->bar[i+1] << 32;
next_i++; // bar[i+1] is skiped
break;
default: // illegal value for PCI 3.0
log_warn("%s: illega bar%u register",
dev->dev.name.ptr, i);
}
log_debug("BAR%u: base=%5lx, type%x",i, addr, type);
if(addr != 0) {// NULL: unused
addr = translate_address(addr);
}
dev->bars[i] = (bar_t){
.base = addr,
.io = bar_reg&1,
.type = type,
.prefetchable = (bar_reg >> 3) & 1,
};
i = next_i;
}
// our struct inherits from this one
// don't worry
callback(dev);
}
// scan a single device
// adds its functions in the linked list
// if it finds some
static void scan_device(unsigned bus_group,
static void scan_device(void (*callback)(struct pcie_dev*),
unsigned bus_group,
unsigned bus,
unsigned device)
{
@ -113,18 +181,18 @@ static void scan_device(unsigned bus_group,
bus,
device,
0);
//return;
// no device!
if(vendorID == 0xFFFF)
return;
// a device is there!
insert(bus_group,
bus,
device,
0
new_dev(callback,
bus_group,
bus,
device,
0
);
// check for additionnal functions for this
// device
for(unsigned func = 1; func < 8; func++) {
@ -135,10 +203,11 @@ static void scan_device(unsigned bus_group,
func) != vendorID)
continue;
insert(bus_group,
bus,
device,
func
new_dev(callback,
bus_group,
bus,
device,
func
);
}
}
@ -146,7 +215,7 @@ static void scan_device(unsigned bus_group,
/**
* constructs the found_devices array
*/
static void scan_devices(void) {
static void scan_devices(void (*callback)(struct pcie_dev*)) {
// first create a linked list
// as we dont know how many devices are present
@ -169,7 +238,7 @@ static void scan_devices(void) {
{
for(unsigned device = 0; device < 32; device++) {
scan_device(bus_group,bus,device);
scan_device(callback, bus_group,bus,device);
}
}
}
@ -177,68 +246,16 @@ static void scan_devices(void) {
}
// create the output array
// with the
static void create_array(void) {
// now create the final array
found_devices = malloc(
n_found_devices
* sizeof(struct pcie_dev));
// now fill it and free the devices structure
struct pcie_dev* ptr = found_devices;
unsigned i = 0;
for(struct early_device_desc* device = head_node.next;
device != NULL;
device = device->next)
{
struct PCIE_config_space* cs = device->config_space;
assert(i++ < n_found_devices);
char* name_buffer = malloc(32);
sprintf(name_buffer, "dev%u", i);
*ptr++ = (struct pcie_dev) {
.resources = NULL,
.driver = NULL,
.config_space = cs,
.name = name_buffer,
.info = (struct dev_info) {
.vendorID = cs->vendorID,
.deviceID = cs->deviceID,
.classcode = cs->classcode,
.subclasscode = cs->subclasscode,
.progIF = cs->progIF,
.revID = cs->revID,
},
.path = (pcie_path_t) {
.domain = device->domain,
.bus = device->bus,
.device = device->device,
.func = device->function,
},
};
free(device);
}
}
// identity map every page that might be a config space
static void identity_map_possible_config_spaces(void) {
// map every page that might be a config space
// to PHYSICAL
static void map_possible_config_spaces(void) {
for(unsigned i = 0; i < pcie_descriptor.size; i++) {
void* phys = pcie_descriptor.array[i].address;
// identity map the corresponding pages
// the corresponding pages
map_pages(
(uint64_t)pcie_descriptor.array[i].address, // phys
(uint64_t)pcie_descriptor.array[i].address, // virt
(uint64_t)phys, // phys
(uint64_t)translate_address(phys), // virt
256 * // busses
32 * // devices
8, // functions
@ -250,11 +267,14 @@ static void identity_map_possible_config_spaces(void) {
static void identity_unmap_possible_config_spaces(void) {
for(unsigned i = 0; i < pcie_descriptor.size; i++)
for(unsigned i = 0; i < pcie_descriptor.size; i++) {
void* phys = pcie_descriptor.array[i].address;
unmap_pages(
(uint64_t)pcie_descriptor.array[i].address,
(uint64_t)translate_address(phys), // vaddr
256 * 32 * 8
);
}
}
/**
@ -262,16 +282,8 @@ static void identity_unmap_possible_config_spaces(void) {
* we identity map the pcie configuration spaces
*
*/
struct pcie_dev* pcie_scan(unsigned* size) {
identity_map_possible_config_spaces();
scan_devices();
create_array();
identity_unmap_possible_config_spaces();
*size = n_found_devices;
return found_devices;
void pcie_scan(void (*callback)(struct pcie_dev*)) {
map_possible_config_spaces();
scan_devices(callback);
}

7
kernel/drivers/pcie/scan.h

@ -30,7 +30,12 @@ struct PCIE_Descriptor {
// defined in pcie.c
extern struct PCIE_Descriptor pcie_descriptor;
struct pcie_dev;
struct pcie_dev* pcie_scan(unsigned* size);
// invoke the argument callback function
// when a pcie device is found
// also, it maps the configuration spaces to:
// TRANSLATED_PHYSICAL_MEMORY_BEGIN + phys
void pcie_scan(void (*callback)(struct pcie_dev*));

1
kernel/drivers/ps2kb.c

@ -1,6 +1,5 @@
#include "ps2kb.h"
#include "../lib/logging.h"
#include "../lib/registers.h"
#include "../lib/sprintf.h"
#include "../int/idt.h"
#include "../int/pic.h"

342
kernel/drivers/terminal/terminal.c

@ -24,19 +24,43 @@ struct Char {
static_assert_equals(sizeof(struct Char), 8);
static void write_string(const char *string, size_t length);
static struct Char make_Char(char c);
static void print_char(const struct Char* restrict c, int line, int col);
static void flush_screen(void);
static struct Char make_Char(driver_t* this, char c);
static void print_char (driver_t* this, const struct Char* restrict c, int line, int col);
static void flush_screen (driver_t* this);
struct data {
terminal_handler_t terminal_handler;
bool need_refresh;
struct Char* char_buffer;
uint16_t ncols, nlines;
uint16_t term_nlines;
uint16_t first_line;
uint16_t cur_col, cur_line;
uint32_t current_fgcolor;
uint32_t current_bgcolor;
unsigned margin_left, margin_top;
unsigned timerID;
};
static driver_t* active_terminal = NULL;
// global functions
driver_t* get_active_terminal(void) {
return active_terminal;
}
static terminal_handler_t terminal_handler = NULL;
// need to redraw the entire terminal
static bool need_refresh = false;
static void empty_terminal_handler(const char* s, size_t l) {
static void empty_terminal_handler(driver_t* this,const char* s, size_t l) {
(void) (s + l);
(void) this;
// empty handler by default,
// make sure not to execute the address 0 :)
}
@ -48,19 +72,9 @@ static void empty_terminal_handler(const char* s, size_t l) {
static void append_string(const char *string, size_t length);
// common resource
static Image* charmap = NULL;
static struct Char* char_buffer = NULL;
static uint16_t ncols, nlines;
static uint16_t term_nlines;
static uint16_t first_line = 0;
static uint16_t cur_col, cur_line;
static uint32_t current_fgcolor = 0xa0a0a0;
static uint32_t current_bgcolor = 0;
static unsigned margin_left, margin_top;
#define UPDATE_PERIOD_MS (1000 / 60)
@ -69,124 +83,144 @@ static unsigned margin_left, margin_top;
extern uint8_t _binary_charmap_bmp;
static unsigned timerID = INVALID_TIMER_ID;
static char* stream_buffer = NULL;
//static unsigned stream_buffer_size = 0;
//static volatile unsigned stream_buffer_content_size = 0;
// should be called every UPDATE_PERIOD ms
// should execute in an IRQ
void terminal_update(void) {
void terminal_update(driver_t* this) {
(void) this;
return;
//static unsigned update_cur_line = 0;
if(need_refresh) {
need_refresh = 0;
flush_screen();
}
//if(need_refresh) {
// need_refresh = 0;
// flush_screen();
//}
// flush the buffer
}
// number of currently installed
// terminals: when it reaches 0 we have to free the charmap
void terminal_install_early(void) {
set_terminal_handler(empty_terminal_handler);
static int terminals = 0;
// log_debug("install the terminal...");
//
// assert(charmap == NULL);
// assert(char_buffer == NULL);
char terminal_install(driver_t* this) {
const Image* screenImage = getScreenImage();
struct framebuffer_dev* dev =
(struct framebuffer_dev *)this->device;
if(!dev ||
dev->dev.type != DEVICE_ID_FRAMEBUFFER ||
dev->bpp != 32
)
return false;
this->remove = terminal_remove;
this->name = (string_t){"Terminal",0};
// alloc the data
this->data = malloc(sizeof(struct data));
struct data* restrict d = this->data;
// init the data
d->first_line = 0;
// dynamicly create the terminal
// with right size
unsigned console_w = (screenImage->w * 9 ) / 10,
console_h = (screenImage->h * 95 ) / 100;
unsigned console_w = (dev->width * 9 ) / 10,
console_h = (dev->height * 95 ) / 100;
#ifdef BIGGER_FONT
ncols = console_w / TERMINAL_FONTWIDTH / 2 - 1;
term_nlines = console_h / TERMINAL_LINE_HEIGHT / 2 - 1;
#else
ncols = console_w / TERMINAL_FONTWIDTH - 1;
term_nlines = console_h / TERMINAL_LINE_HEIGHT - 1;
d->ncols = console_w / TERMINAL_FONTWIDTH - 1;
d->term_nlines = console_h / TERMINAL_LINE_HEIGHT - 1;
#endif
nlines = TERMINAL_N_PAGES * term_nlines;
d->nlines = TERMINAL_N_PAGES * d->term_nlines;
// calculate the margins
#ifdef BIGGER_FONT
margin_left = (screenImage->w - ncols * 2 * TERMINAL_FONTWIDTH ) / 2;
margin_top = 0;//(screenImage->h - term_nlines * 2 * TERMINAL_FONTHEIGHT) / 2;
d->margin_left = (dev->width - d->ncols * 2 * TERMINAL_FONTWIDTH ) / 2;
d->margin_top = 0;
#else
margin_left = (screenImage->w - ncols * TERMINAL_FONTWIDTH ) / 2;
margin_top = (screenImage->h - term_nlines * TERMINAL_FONTHEIGHT) / 2;
d->margin_left = (dev->width - d->ncols * TERMINAL_FONTWIDTH ) / 2;
d->margin_top = (dev->height - d->term_nlines * TERMINAL_FONTHEIGHT) / 2;
#endif
// allocate the terminal buffer
char_buffer = malloc(ncols * nlines * sizeof(struct Char) * 10);
stream_buffer = malloc(ncols * term_nlines);
}
d->char_buffer = malloc(d->ncols * d->nlines * sizeof(struct Char) * 10);
// finish intallation when memory
// is well initialized
void terminal_install_late(void) {
timerID = apic_create_timer(terminal_update, UPDATE_PERIOD_MS);
//d->timerID = apic_create_timer(
// (timer_callback_t)terminal_update,
// UPDATE_PERIOD_MS,
// this);
//
if(terminals++ == 0)
charmap = loadBMP_24b_1b(&_binary_charmap_bmp);
charmap = loadBMP_24b_1b(&_binary_charmap_bmp);
set_terminal_handler(write_string);
//set_terminal_handler(this, write_string);
terminal_set_colors(this, 0xfff0a0, 0x212121);
terminal_clear(this);
terminal_clear();
active_terminal = this;
return 1;
}
void terminal_remove(void) {
bmp_free(charmap);
free(char_buffer);
free(stream_buffer);
void terminal_remove(driver_t* this) {
struct data* restrict d = this->data;
// the charmap is shared amoung
// all terminals
if(--terminals == 0)
bmp_free(charmap);
free(d->char_buffer);
if(timerID != INVALID_TIMER_ID)
apic_remove_timer(timerID);
if(d->timerID != INVALID_TIMER_ID)
apic_delete_timer(d->timerID);
free(d);
//if(active_terminal == this)
// active_terminal = NULL;
}
void terminal_clear(void) {
void terminal_clear(driver_t* this) {
struct data* restrict d = this->data;
cur_col = 0;
cur_line = 0;
first_line = 0;
d->cur_col = 0;
d->cur_line = 0;
d->first_line = 0;
size_t buffer_len = nlines * ncols;
size_t buffer_len = d->nlines * d->ncols;
struct Char* ptr = d->char_buffer;
struct Char* ptr = char_buffer;
for(;buffer_len > 0; --buffer_len)
*(ptr++) = make_Char(0);
*(ptr++) = make_Char(this, 0);
flush_screen();
flush_screen(this);
}
void set_terminal_fgcolor(uint32_t c) {
current_fgcolor = c;
void terminal_set_fgcolor(driver_t* this, uint32_t c) {
struct data* restrict d = this->data;
d->current_fgcolor = c;
}
void set_terminal_bgcolor(uint32_t c) {
current_bgcolor = c;
void terminal_set_bgcolor(driver_t* this, uint32_t c) {
struct data* restrict d = this->data;
d->current_bgcolor = c;
}
/*
@ -196,96 +230,113 @@ static struct Char* get_Char_at(int l, int c) {
}
*/
static void move_buffer(int lines) {
need_refresh = true;
static void move_buffer(driver_t* this, int lines) {
struct data* restrict d = this->data;
d->need_refresh = true;
if(lines > 0) {// scroll backward
size_t bytes = ncols * lines;
size_t buff_size = nlines * ncols;
size_t bytes = d->ncols * lines;
size_t buff_size = d->nlines * d->ncols;
memmove(char_buffer, char_buffer + bytes, sizeof(struct Char)*(buff_size - bytes));
memmove(
d->char_buffer,
d->char_buffer + bytes,
sizeof(struct Char)*(buff_size - bytes)
);
// cannot touch the first one: it is already written
for(unsigned i = 1; i < bytes; i++) {
char_buffer[i+buff_size-bytes] = make_Char(0);
d->char_buffer[i+buff_size-bytes] = make_Char(this,0);
}
}
}
static void next_line(void) {
cur_col = 0;
cur_line++;
if(cur_line >= nlines) {
cur_line = nlines-4;
static void next_line(driver_t* this) {
struct data* restrict d = this->data;
d->cur_col = 0;
d->cur_line++;
if(d->cur_line >= d->nlines) {
d->cur_line = d->nlines-4;
move_buffer(4);
move_buffer(this, 4);
}
else if(cur_line >= first_line + term_nlines) {
first_line++;
need_refresh = true;
else if(d->cur_line >= d->first_line + d->term_nlines) {
d->first_line++;
d->need_refresh = true;
}
}
// create the char struct
static struct Char make_Char(char c) {
static struct Char make_Char(driver_t* this, char c) {
struct data* restrict d = this->data;
return (struct Char) {
.fg_color = current_fgcolor,
.fg_color = d->current_fgcolor,
.c = c,
.bg_color = current_bgcolor
.bg_color = d->current_bgcolor
};
}
static void emplace_normal_char(char c) {
if(cur_col >= ncols) {
next_line();
static void emplace_normal_char(driver_t* this, char c) {
struct data* restrict d = this->data;
if(d->cur_col >= d->ncols) {
next_line(this);
}
char_buffer[ncols * cur_line + cur_col] = make_Char(c);
d->char_buffer[d->ncols * d->cur_line + d->cur_col] = make_Char(this, c);
struct Char* ch = &char_buffer[ncols * cur_line + cur_col];
struct Char* ch = &d->char_buffer[d->ncols * d->cur_line + d->cur_col];
if(!need_refresh)
print_char(ch, cur_line - first_line, cur_col);
if(!d->need_refresh)
print_char(this, ch, d->cur_line - d->first_line, d->cur_col);
cur_col += 1;
d->cur_col += 1;
}
// emplace the char in the buffer, and maybe draw
static void emplace_char(char c) {
static void emplace_char(driver_t* this, char c) {
struct data* restrict d = this->data;
switch(c) {
default:
// any character
emplace_normal_char(c);
emplace_normal_char(this, c);
break;
case '\n':
{
for(unsigned i=cur_col;i < ncols; i++)
for(unsigned i=d->cur_col;i < d->ncols; i++)
{
//print_char(ch, cur_line - first_line, cur_col);
emplace_normal_char(' ');
emplace_normal_char(this, ' ');
}
}
break;
case '\t':
{
unsigned new_col = ((cur_col + TAB_SPACE) / TAB_SPACE) * TAB_SPACE;
while(cur_col < new_col)
emplace_char(' ');
unsigned new_col = ((d->cur_col + TAB_SPACE) / TAB_SPACE) * TAB_SPACE;
while(d->cur_col < new_col)
emplace_char(this, ' ');
}
break;
case '\r':
cur_col = 0;
d->cur_col = 0;
break;
}
}
static void print_char(const struct Char* restrict c, int line, int col) {
static void print_char(driver_t* this,
const struct Char* restrict c,
int line, int col) {
struct data* restrict d = this->data;
/*
Pos srcpos = {
FONTWIDTH * c_x,
@ -317,27 +368,30 @@ static void print_char(const struct Char* restrict c, int line, int col) {
};
*/
#ifdef BIGGER_FONT
blitcharX2(charmap, c->c, c->fg_color, c->bg_color,
margin_left + 2 * col * TERMINAL_FONTWIDTH,
margin_top + 2 * line * TERMINAL_LINE_HEIGHT);
blitcharX2((struct framebuffer_dev *)this->device,
charmap, c->c, c->fg_color, c->bg_color,
d->margin_left + 2 * col * TERMINAL_FONTWIDTH,
d->margin_top + 2 * line * TERMINAL_LINE_HEIGHT);
#else
blitchar(charmap, c->c, c->fg_color, c->bg_color,
margin_left + col * TERMINAL_FONTWIDTH,
margin_top + line * TERMINAL_LINE_HEIGHT);
blitchar((struct framebuffer_dev *)this->device,
charmap, c->c, c->fg_color, c->bg_color,
d->margin_left + col * TERMINAL_FONTWIDTH,
d->margin_top + line * TERMINAL_LINE_HEIGHT);
#endif
}
static void flush_screen(void) {
static void flush_screen(driver_t* this) {
struct data* d = this->data;
// begins at the terminal's first line
const struct Char* curr = char_buffer + first_line * ncols;
const struct Char* curr = d->char_buffer + d->first_line * d->ncols;
for(size_t l = 0; l < term_nlines; l++) {
for(size_t c = 0; c < ncols; c++) {
print_char(curr++, l, c);
for(size_t l = 0; l < d->term_nlines; l++) {
for(size_t c = 0; c < d->ncols; c++) {
print_char(this, curr++, l, c);
}
}
}
@ -359,34 +413,34 @@ static void append_string(const char *string, size_t length) {
}
*/
static void write_string(const char *string, size_t length) {
need_refresh = false;
void write_string(driver_t* this, const char *string, size_t length) {
struct data* restrict d = this->data;
d->need_refresh = false;
for(;length>0;--length) {
char c = *string++;