Vystem 0.2
This commit is contained in:
36
docs/shelter/devs/abstracted/ps2.md
Normal file
36
docs/shelter/devs/abstracted/ps2.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# PS2 keyboard
|
||||
|
||||
The PS2 keyboard driver is defined in `shelter/lib/include/devs/input/ps2.h` and implemented in `shelter/lib/src/devs/input/ps2.c`. This documentation only describes the public API, the one that is safe to access from external subsystems.
|
||||
|
||||
## Driver initialization
|
||||
|
||||
The PS2 is initialized using `sh_ps2_driver_init()`. The pre-requirement for this function include the keyboard input subsystem being initialized. Here is the exact procedure:
|
||||
1) Disable interrupts for safety
|
||||
2) Flush output buffer
|
||||
3) Debug log status and config byte initial value
|
||||
4) Disable keyboard and mouse PS2 ports
|
||||
5) Reflush output buffer for safety
|
||||
6) Disable both ports IRQs
|
||||
7) Perform the PS2 controller self test
|
||||
8) Enable port 1
|
||||
9) Enable scancodes translation
|
||||
10) Initialize driver internal state
|
||||
11) Enable IRQ for port 1
|
||||
12) Register interrupt handler for legacy IRQ1
|
||||
13) Enable interrupts
|
||||
14) Disable scanning
|
||||
15) Reset leds state
|
||||
16) Identify keyboard
|
||||
17) Register PS2 keyboard to the keyboard input subsystem
|
||||
18) Enable scanning
|
||||
|
||||
For the moments, commands responses handling and scancodes parsing is working but there are a few limitations:
|
||||
- since we only parsed a few ACPI tables, we are unable to detect with certainty the presence of a PS2 controller. So the system consider that the PS2 controller is available and working and the keyboard device registered to the keyboard input subsystem will always be marked as present
|
||||
- any error in the driver initialization will be interpreted as a failure of the boot process
|
||||
- for the moment, we are unable to defer the leds state update when a scancode change the NumLock/CapsLock state, so these leds won't update
|
||||
|
||||
## API
|
||||
|
||||
A few additionnal functions are provided:
|
||||
- `sh_ps2_disable_scanning()`: disable PS2 keyboard scanning
|
||||
- `sh_ps2_enable_scanning()`: enable PS2 keyboard scanning
|
||||
43
docs/shelter/devs/acpi.md
Normal file
43
docs/shelter/devs/acpi.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# ACPI Parsing
|
||||
|
||||
Shelter only support the ACPI v2 and newer ACPI specifications. That mean the XSDT pointer must exists.
|
||||
|
||||
## Root structures parsing
|
||||
|
||||
The root structures parsing is defined in `shelter/lib/include/devs/acpi.h` and implemented in `shelter/lib/src/devs/acpi.c`.
|
||||
|
||||
The `sh_acpi_parse_rsdp()` function takes the RSDP and completes a `sh_apci_ACPI_POINTERS_ROOT` structure that look like this:
|
||||
``` C
|
||||
typedef struct {
|
||||
sh_uint64 rsdt;
|
||||
sh_uint64 xsdt;
|
||||
} sh_apci_ACPI_POINTERS_ROOT;
|
||||
```
|
||||
|
||||
Then, the `sh_acpi_parse_xsdt()` function takes the XSDT pointer and parses the XSDT header. It ensures that the structure isn't corrupted, and map all the entries. At the end, it completes this structure:
|
||||
``` C
|
||||
typedef struct {
|
||||
sh_uint64 *entries;
|
||||
sh_uint64 entry_count;
|
||||
} sh_acpi_ACPI_XSDT_BODY;
|
||||
```
|
||||
|
||||
Finally, the `sh_acpi_parse_tables()` takes the `sh_acpi_ACPI_XSDT_BODY` structure and parses all the entries. It ensures that at least the first pages of each table is mapped. All the tables pointers are sorted in a structure that look like this:
|
||||
``` C
|
||||
typedef struct {
|
||||
sh_uint64 madt_physical_address;
|
||||
} sh_acpi_ACPI_TABLES;
|
||||
```
|
||||
|
||||
## Tables parsing
|
||||
|
||||
The parsing of ACPI tables is delegated to several functions, one for each tables. Here are the tables that are currently supported:
|
||||
- MADT
|
||||
|
||||
All parsing functions are defined in the files inside `shelter/lib/include/devs/acpi_tables` and in the files inside `shelter/lib/src/devs/acpi_tables`.
|
||||
|
||||
### MADT parsing
|
||||
|
||||
The MADT is parsed using the `sh_acpi_madt_parse(sh_uint64 madt_ptr)`, defined in `shelter/lib/include/devs/acpi_tables/madt.h` and implemented in `shelter/lib/src/devs/acpi_tables/madt.c`.
|
||||
|
||||
This function logs all the entries and register all LAPIC devices, IOAPIC devices and ISOs.
|
||||
142
docs/shelter/devs/devs.md
Normal file
142
docs/shelter/devs/devs.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# DevS querying
|
||||
|
||||
## Introduction
|
||||
|
||||
The Device System provides a central way to query various kernel subsystems. It is defined in `shelter/lib/include/devs/devs.h` and implemented in `shelter/lib/src/devs/devs.c`.
|
||||
|
||||
## Entry point
|
||||
|
||||
All DevS related API call goes through a standard entry point:
|
||||
``` C
|
||||
sh_devs_query(char *path,sh_devs_RESULT *result)
|
||||
```
|
||||
|
||||
The result is stored inside `result`, which is a structure like this:
|
||||
``` C
|
||||
typedef struct {
|
||||
enum sh_devs_RESULT_TYPE type;
|
||||
sh_uint64 value;
|
||||
} sh_devs_RESULT;
|
||||
```
|
||||
|
||||
There are severals returned types:
|
||||
- `SH_DEVS_VALUE`: a 64 bits value
|
||||
- `SH_DEVS_BOOL`: a boolean stored inside `value` by casting the `sh_bool` to `sh_uint64`
|
||||
- `SH_DEVS_LAPIC`: `value` should be casted to `sh_lapic_DEVICE*`, which points directly to a LAPIC device structure
|
||||
- `SH_DEVS_IOAPIC`: `value` should be casted to `sh_ioapic_DEVICE*`, which points directly to a IOAPIC device structure
|
||||
|
||||
Each endpoint has a specific syntax, but here are the common syntax elements:
|
||||
- each path starts with the `$` sign
|
||||
- each subsystem has its own prefix
|
||||
|
||||
Unless you are querying for a pointer (in this case, you can modify the value inside the pointed structure), you can't write anything in the DevS endpoints.
|
||||
|
||||
## Endpoint categories
|
||||
|
||||
All the kernel subsystems that expose datas through the DevS provide a function to parse the rest of the path without the subsystem prefix once `sh_devs_query()` dispatched the query.
|
||||
|
||||
These functions aren't documented in the documentation of these subsystems and shouldn't be called directly.
|
||||
|
||||
### LAPIC endpoints
|
||||
|
||||
Function:
|
||||
``` C
|
||||
sh_lapic_devs_query(char *sub_path,sh_devs_RESULT *result)
|
||||
```
|
||||
|
||||
Prefix: `$apic/lapic`
|
||||
|
||||
Endpoints:
|
||||
|
||||
Path with syntax | Returned type | Description
|
||||
-----------------|---------------|------------
|
||||
`$apic/lapic/by-apic-id/<id>` | `SH_DEVS_LAPIC` | Return the struct of the LAPIC device correspoding to the provided LAPIC id. **Can return `SH_NULLPTR` with a success due to sparses id**
|
||||
`$apic/lapic/by-acpi-id/<id>` | `SH_DEVS_LAPIC` | Return the struct of the LAPIC device correspoding to the provided ACPI processor id. **Can return `SH_NULLPTR` with a success due to sparses id**
|
||||
`$apic/lapic/count` | `SH_DEVS_VALUE` | Return the number of LAPIC devices registered
|
||||
`$apic/lapic/timer-frequency` | `SH_DEVS_VALUE` | Return the estimated frequency of the LAPIC timer
|
||||
`$apic/lapic/max-apic-id` | `SH_DEVS_VALUE` | Return the biggest LAPIC id among all registered LAPIC devices
|
||||
`$apic/lapic/max-acpi-id` | `SH_DEVS_VALUE` | Return the biggest ACPI processor id among all registered LAPIC devices
|
||||
`$apic/lapic/base-address` | `SH_DEVS_VALUE` | Return the physical address of the memory mapped pages for LAPICs
|
||||
`$apic/lapic/this-cpu-lapic` | `SH_DEVS_LAPIC` | Return the struct of the LAPIC device corresponding to this CPU
|
||||
|
||||
### IOAPIC endpoints
|
||||
|
||||
Function:
|
||||
``` C
|
||||
sh_ioapic_devs_query(char *sub_path,sh_devs_RESULT *result)
|
||||
```
|
||||
|
||||
Prefix: `$apic/ioapic`
|
||||
|
||||
Endpoints:
|
||||
|
||||
Path with syntax | Returned type | Description
|
||||
-----------------|---------------|------------
|
||||
`$apic/ioapic/by-ioapic-id/<id>` | `SH_DEVS_ioAPIC` | Return the struct of the IOAPIC device correspoding to the provided IOAPIC id. **Can return `SH_NULLPTR` with a success due to sparses id**
|
||||
`$apic/lapic/by-gsi/<gsi>` | `SH_DEVS_IOAPIC` | Return the struct of the IOAPIC device that manages the corresponding GSI. **Can return `SH_NULLPTR` with a success due to sparses GSI**
|
||||
`$apic/lapic/count` | `SH_DEVS_VALUE` | Return the number of IOAPIC devices registered
|
||||
`$apic/lapic/max-ioapic-id` | `SH_DEVS_VALUE` | Return the biggest IOAPIC id among all registered IOAPIC devices
|
||||
|
||||
### TSC endpoints
|
||||
|
||||
Function:
|
||||
``` C
|
||||
sh_tsc_devs_query(char *sub_path,sh_devs_RESULT *result)
|
||||
```
|
||||
|
||||
Prefix: `$tsc`
|
||||
|
||||
Endpoints:
|
||||
|
||||
Path with syntax | Returned type | Description
|
||||
-----------------|---------------|------------
|
||||
`$tsc/kernel-init-tsc` | `SH_DEVS_VALUE` | Return the TSC value saved at the start of the kernel C entry point, before AP bootstrap
|
||||
`$tsc/cpu-freq` | `SH_DEVS_VALUE` | Return the CPU frequency, either estimated or provided by CPU ID
|
||||
`$tsc/is-cpu-freq-provided` | `SH_DEVS_BOOL` | Return `SH_TRUE` if CPU frequency is provided by CPU ID, `SH_FALSE` if CPU frequency is estimated
|
||||
|
||||
### Kernel config endpoints
|
||||
|
||||
Function:
|
||||
``` C
|
||||
sh_conf_devs_query(char *sub_path,sh_devs_RESULT *result)
|
||||
```
|
||||
|
||||
Prefix: `$kernel/conf`
|
||||
|
||||
Endpoints:
|
||||
|
||||
Path with syntax | Returned type | Description
|
||||
-----------------|---------------|------------
|
||||
`$kernel/conf/log-level` | `SH_DEVS_VALUE` | Return the log level of the kernel
|
||||
`$kernel/conf/test-benchmark` | `SH_DEVS_BOOL` | Return the test-benchmark state, `SH_TRUE` if enabled
|
||||
`$kernel/conf/bench-iterations` | `SH_DEVS_VALUE` | Return the amount of iterations used for benchmarking
|
||||
`$kernel/conf/disable-serial-port` | `SH_DEVS_BOOL` | Return `SH_TRUE` if serial port is disabled, `SH_FALSE` otherwise
|
||||
`$kernel/conf/kbd-events-queue-capacity` | `SH_DEVS_VALUE` | Return the amount of events a keyboard events queue can store
|
||||
|
||||
### Memory subsystem endpoints
|
||||
|
||||
Function:
|
||||
``` C
|
||||
sh_memory_devs_query(char *sub_path,sh_devs_RESULT *result)
|
||||
```
|
||||
|
||||
Prefix: `$memory`
|
||||
|
||||
Endpoints:
|
||||
|
||||
Path with syntax | Returned type | Description
|
||||
-----------------|---------------|------------
|
||||
`$memory/free-pages` | `SH_DEVS_VALUE` | Return the amount of free pages in the Pez physical plane
|
||||
`$memory/used-pages` | `SH_DEVS_VALUE` | Return the amount of allocated pages in the Pez physical plane
|
||||
`$memory/total-pages` | `SH_DEVS_VALUE` | Return the amount of physical pages detected when parsing the memory map
|
||||
`$memory/installed-pages` | `SH_DEVS_VALUE` | Return the amount of pages detected usable when parsing the memory map. Can be slightly smaller than total pages
|
||||
|
||||
### SMP endpoints
|
||||
|
||||
Function: integrated to `sh_devs_query()`
|
||||
|
||||
Prefix: `$smp`
|
||||
|
||||
Path with syntax | Returned type | Description
|
||||
-----------------|---------------|------------
|
||||
`$smp/cpu-count` | `SH_DEVS_VALUE` | Return the number of processors
|
||||
18
docs/shelter/devs/drivers.md
Normal file
18
docs/shelter/devs/drivers.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Device drivers
|
||||
|
||||
## Overview
|
||||
|
||||
The current device drivers are scattered through the `shelter/lib/include/devs` folder. There is two kinds of devices drivers:
|
||||
- integrated: these one are used as a core part of the kernel, and aren't exposed through subsystem abstraction
|
||||
- abstracted: these one are abstracted using a specific subsystem depending on the device type that provides a standard API both for drivers and consumers
|
||||
|
||||
## Integrated drivers
|
||||
|
||||
For the moment, integrated drivers are implemented for the following devices:
|
||||
- [LAPIC devices](integrated/lapic.md)
|
||||
- [IOAPIC devices](integrated/ioapic.md)
|
||||
|
||||
## Abstracted drivers
|
||||
|
||||
For the moment, abstracted drivers are implemented for the following devices:
|
||||
- [PS2 keyboard](abstracted/ps2.md)
|
||||
13
docs/shelter/devs/index.md
Normal file
13
docs/shelter/devs/index.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Device System
|
||||
|
||||
## Introduction
|
||||
|
||||
The Device System (also called DevS) is the entire subsystem managing everything related to ACPI parsing and devices abstractions.
|
||||
|
||||
## Overview
|
||||
|
||||
The DevS is splitted in several subparts:
|
||||
- [ACPI parsing](acpi.md)
|
||||
- [Device drivers](drivers.md)
|
||||
- [Keyboard input subsystem](kbdinput.md)
|
||||
- [DevS querying](devs.md)
|
||||
35
docs/shelter/devs/integrated/ioapic.md
Normal file
35
docs/shelter/devs/integrated/ioapic.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# IOAPIC devices
|
||||
|
||||
This driver is defined inside `shelter/lib/include/devs/apic/ioapic.h` and implemented inside `shelter/lib/src/devs/apic/ioapic.c`.
|
||||
|
||||
## Driver initialization
|
||||
|
||||
During the MADT parsing, all IOAPIC entries are counted. Then, a call to `sh_ioapic_init_devs(sh_uint8 max_lapic_id)` is made to initialize the driver internal state. This call allocates an array indexed by IOAPIC id.
|
||||
|
||||
## Device initialization
|
||||
|
||||
After that, each time a IOAPIC entry is parsed, a call to `sh_ioapic_init(sh_uint32 base,sh_uint8 ioapic_id,sh_uint32 gsi_base,sh_ioapic_DEVICE *ioapic)` is made. This does three things:
|
||||
1) Identity map the memory space for the LAPIC
|
||||
2) Fill the structure for the LAPIC device
|
||||
3) Initialize the device without modifying the IOREDTBL for the moment
|
||||
|
||||
Then, a call to `sh_ioapic_bind(sh_ioapic_DEVICE *ioapic_dev)` is made, registering the LAPIC structure in the array.
|
||||
|
||||
## Basic IOAPIC management
|
||||
|
||||
For basic IOAPIC management, the following function are provided:
|
||||
- `sh_ioapic_get_dev_ioapic_id(sh_uint64 ioapic_id)`: return a `sh_ioapic_DEVICE*` pointing to the IOAPIC structure corresponding to the provided IOAPIC id
|
||||
- `sh_ioapic_get_dev_by_gsi(sh_uint32 gsi)`: return a `sh_ioapic_DEVICE*` pointing to the IOAPIC structure of the IOAPIC that manages the provided GSI
|
||||
- `sh_ioapic_read_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry)`: read a IOREDTBL entry and put its value into `entry`. If the GSI isn't managed by the provided device, raise an error
|
||||
- `sh_ioapic_write_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry)`: write and IOREDTBL entry corresponding to the provided GSI. If the GSI isn't managed by the provided device, raise an error
|
||||
|
||||
IOREDTBL read and write are thread-safe operations.
|
||||
|
||||
## Higher level operations
|
||||
|
||||
The following higher level operations are provided:
|
||||
- `sh_ioapic_mask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi)`: mask the provided GSI. If the GSI isn't managed by the provided device, raise an error
|
||||
- `sh_ioapic_unmask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi)`: unmask the provided GSI. If the GSI isn't managed by the provided device, raise an error
|
||||
- `sh_ioapic_mask_all()`: mask all the GSI for all registered IOAPIC.
|
||||
|
||||
If you are looking for an even more abstracted API, please check the GSI subsystem.
|
||||
81
docs/shelter/devs/integrated/lapic.md
Normal file
81
docs/shelter/devs/integrated/lapic.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# LAPIC devices
|
||||
|
||||
This driver is defined inside `shelter/lib/include/devs/apic/lapic.h` and implemented inside `shelter/lib/src/devs/apic/lapic.c`.
|
||||
|
||||
## Driver initialization
|
||||
|
||||
During the MADT parsing, all LAPIC entries are counted. Then, a call to `sh_lapic_init_devs(sh_uint16 max_lapic_id,sh_uint16 max_acpi_processor_id)` is made to initialize the driver internal state. This call allocates two arrays: one indexed by LAPIC id and one indexed by ACPI processors id.
|
||||
|
||||
## Device initialization
|
||||
|
||||
After that, each time a LAPIC entry is parsed, a call to `sh_lapic_init(sh_uint64 lapic_phys,sh_uint8 spurious_vector,sh_uint8 apic_id,sh_uint8 apic_processor_id,sh_uint32 flags,sh_lapic_DEVICE *lapic)` is made. This does two things:
|
||||
1) Identity map the memory space for the LAPIC
|
||||
2) Fill the structure for the LAPIC device
|
||||
|
||||
Then, a call to `sh_lapic_bind(sh_lapic_DEVICE *lapic_dev)` is made, registering the LAPIC structure in both arrays.
|
||||
|
||||
Finally, when the MADT parsing is over, a call to `sh_lapic_init_dev(sh_uint8 spurious_vector,sh_lapic_DEVICE *lapic)` initialize the LAPIC by setuping a few of its registers.
|
||||
|
||||
## Basic LAPIC management
|
||||
|
||||
For basic LAPIC management, the following functions are provided:
|
||||
- `sh_lapic_eoi(sh_lapic_DEVICE *lapic)`: send a End Of Interrupt (EOI) to the LAPIC
|
||||
- `sh_lapic_get_dev_apic_id(sh_uint64 apic_id)`: return a `sh_lapic_DEVICE*` pointing to the LAPIC structure corresponding to the provided APIC id
|
||||
- `sh_lapic_get_dev_acpi_cpu_id(sh_uint64 acpi_processor_id)`: return a `sh_lapic_DEVICE*` pointing to the LAPIC structure corresponding to the provided ACPI processor id
|
||||
- `sh_lapic_get_by_apic_id_array()`: return a `sh_lapic_DEVICE**` of the LAPIC devices pointers array, indexed by LAPIC id
|
||||
- `sh_lapic_get_max_apic_id()`: return the max LAPIC id
|
||||
- `sh_lapic_get_max_acpi_processor_id()`: return the max ACPI processor id
|
||||
- `sh_lapic_get_lapic_count()`: return the number of registered LAPICs
|
||||
- `sh_lapic_get_current_core()`: return the LAPIC id of the current core, return -1 if error happened
|
||||
|
||||
## LAPIC timer
|
||||
|
||||
To launch a one shot timer without knowing the frequency, the `sh_lapic_timer_one_shot(sh_lapic_DEVICE *lapic_dev,sh_uint32 initial_value)` can be used.
|
||||
|
||||
But the LAPIC driver provides a way to calibrate the LAPIC in order to obtain its frequency. For this, we use the following process:
|
||||
1) Launch a timer with max value
|
||||
2) Read the start TSC and current LAPIC value, iterate for 10 millions times, read the end TSC and current LAPIC value.
|
||||
3) Compute `tsc_delta` and `lapic_delta`
|
||||
4) Using the following formula, estimate the LAPIC frequency: `lapic_freq = (lapic_delta * cpu_freq) / tsc_delta`
|
||||
|
||||
This calibration process is implemented in `sh_lapic_calibrate(sh_lapic_DEVICE *lapic_dev,sh_uint64 cpu_freq)`. But the TSC frequency estimation must have been achevied before that.
|
||||
|
||||
When the LAPIC frequency is obtained, two new functions are unlocked:
|
||||
- `sh_lapic_get_frequency()`: return the estimated LAPIC frequency
|
||||
- `sh_lapic_timer_one_shot_us(sh_lapic_DEVICE *lapic_dev,sh_uint64 microseconds_count)`: launch a one shot timer by taking a value in microsecond. Compute the initial value using the estimated frequency before hand.
|
||||
|
||||
## IPI management
|
||||
|
||||
The LAPIC driver provides a helper to know if the LAPIC IPI feature is busy: `sh_lapic_ipi_is_busy(sh_lapic_DEVICE *lapic_dev)`.
|
||||
|
||||
### Sending a fixed IPI
|
||||
|
||||
The primitive to send a fixed IPI is:
|
||||
``` C
|
||||
sh_lapic_send_fixed_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint8 vector,sh_uint32 destination_mode,sh_int16 target_lapic_id);
|
||||
```
|
||||
|
||||
`lapic_dev` must be the struct of the LAPIC of the current CPU.
|
||||
|
||||
`destination_mode` can either be `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `SH_LAPIC_IPI_DESTINATION_SELF`, `SH_LAPIC_IPI_DESTINATION_ALL` or `SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF`.
|
||||
|
||||
If `destination_mode` isn't `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `target_lapic_id` must be `SH_LAPIC_IPI_NO_DESTINATION`.
|
||||
|
||||
### Sending any others IPI
|
||||
|
||||
The primitive to send any other IPI than a fixed IPI is:
|
||||
``` C
|
||||
sh_lapic_send_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint32 ipi_type,sh_uint32 destination_mode,sh_int16 target_lapic_id,sh_page_PHYSICAL_ADDRESS start_address);
|
||||
```
|
||||
|
||||
`lapic_dev` must be the struct of the LAPIC of the current CPU.
|
||||
|
||||
`ipi_type` can either be `SH_LAPIC_IPI_TYPE_INIT`, `SH_LAPIC_IPI_TYPE_STARTUP` or `SH_LAPIC_IPI_TYPE_NMI`.
|
||||
|
||||
If `ipi_type` is `SH_LAPIC_IPI_TYPE_STARTUP`, `start_address` must contain the starting address. Address constraint (compatible with 16 bits mode) are checked.
|
||||
|
||||
If `ipi_type` isn't `SH_LAPIC_IPI_TYPE_STARTUP`, `start_address` must be 0.
|
||||
|
||||
`destination_mode` can either be `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `SH_LAPIC_IPI_DESTINATION_SELF`, `SH_LAPIC_IPI_DESTINATION_ALL` or `SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF`.
|
||||
|
||||
If `destination_mode` isn't `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `target_lapic_id` must be `SH_LAPIC_IPI_NO_DESTINATION`.
|
||||
134
docs/shelter/devs/kbdinput.md
Normal file
134
docs/shelter/devs/kbdinput.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Keyboard input subsystem
|
||||
|
||||
## Introduction
|
||||
|
||||
The keyboard input subsystem is responsible for abstracting keyboard devices and keyboards events. It's defined in `shelter/lib/include/devs/input/kbd.h` and implemented in `shelter/lib/src/devs/input/kbd.c`. It's still in the early stage and isn't finished for the moment.
|
||||
|
||||
## Overview
|
||||
|
||||
The keyboard input subsystem is based on two key concepts:
|
||||
- keyboard devices: this is the standard abstraction for any keyboard-like devices
|
||||
- keyboard events: provides a standard way to interpret any events from any keyboard devices
|
||||
|
||||
The keyboard input subsystem is initialized using `sh_kbd_init_devs()`.
|
||||
|
||||
### Keyboard devices
|
||||
|
||||
### Driver side
|
||||
|
||||
The keyboard device structure look like this:
|
||||
``` C
|
||||
typedef struct {
|
||||
sh_uint8 kbd_id;
|
||||
sh_queue_KBD_EVENT *events_queue;
|
||||
sh_bool present;
|
||||
sh_bool ver_maj;
|
||||
sh_bool ver_num;
|
||||
sh_bool shift;
|
||||
sh_bool alt;
|
||||
sh_bool ctrl;
|
||||
sh_bool win;
|
||||
sh_SPIN_LOCK spinlock;
|
||||
} sh_kbd_DEVICE;
|
||||
```
|
||||
|
||||
Each keyboard device is identified with a keyboard id. When a keyboard is unregistered, the keyboard id can be recycled for a new keyboard device. There is a maximum of 256 keyboards id, and so 256 maximum registered keyboard devices at the same time.
|
||||
|
||||
The keyboard id 0 is reserved for the PS2 keyboard. It's always valid, even if there is no PS2 controller.
|
||||
|
||||
The `present` boolean defines if the keyboard is present. If `present` is `SH_FALSE`, no actions except unregistering the keyboard is allowed on the keyboard device.
|
||||
|
||||
The `present` boolean for the keyboard od keyboard id 0 indicates if the PS2 controller is available.
|
||||
|
||||
There is two primitives to register keyboards devices:
|
||||
- `sh_kbd_register_ps2(sh_bool is_present,sh_kbd_DRIVER_HANDLE **handle)`: register the PS2 keyboard, reserved for the PS2 driver
|
||||
- `sh_kbd_register(sh_bool is_present,sh_kbd_DRIVER_HANDLE **handle)`: register a new keyboard device
|
||||
|
||||
Upon registration, a `sh_kbd_DRIVER_HANDLE` struct is completed. It became non valid when the keyboard is unregistered. It is reserved for driver usage only.
|
||||
|
||||
Two primitives accessible to drivers require this driver handle:
|
||||
- `sh_kbd_set_flag(sh_kbd_DRIVER_HANDLE *handle,sh_uint8 flag,sh_bool value)`: set a flag about the keyboard corresponding to the provided driver handle
|
||||
- `sh_kbd_push_events(sh_kbd_DRIVER_HANDLE *handle,sh_kbd_EVENT *event)`: push an event in the events queue of the keyboard device corresponding to the provided driver handle
|
||||
|
||||
There is, for the moment, no primitive to unregister a keyboard.
|
||||
|
||||
### Consumer side
|
||||
|
||||
Four functions are available for consumers:
|
||||
- `sh_kbd_enumerate_devices(sh_kbd_ENUMERATION *enumeration)`: complete a `sh_kbd_ENUMERATION` structure, each bit set to 1 correspond to a valid keyboard id
|
||||
- `sh_kbd_get_handle(sh_uint8 kbd_id,sh_kbd_CONSUMER_HANDLE **handle)`: return a valid consumer handle for the provided keyboard id. It uses `sh_malloc`
|
||||
- `sh_kbd_destroy_handle(sh_kbd_CONSUMER_HANDLE **handle)`: free the provided consumer handle
|
||||
- `sh_kbd_read_flag(sh_kbd_DRIVER_HANDLE *d_handle,sh_kbd_CONSUMER_HANDLE *c_handle,sh_uint8 flag,sh_bool *value)`: read a flag about the keyboard corresponding to the provided handle. Accepts either a driver or consumer handle, but fails if both handle are provided.
|
||||
|
||||
The possible flags to read/write are: `SH_KBD_FLAG_PRESENT`, `SH_KBD_FLAG_VER_MAJ`, `SH_KBD_FLAG_VER_NUM`, `SH_KBD_FLAG_SHIFT`, `SH_KBD_FLAG_ALT`, `SH_KBD_FLAG_CTRL`, `SH_KBD_FLAG_WIN`.
|
||||
|
||||
There is, for the moment, no way to read keyboards events.
|
||||
|
||||
## Keyboard events
|
||||
|
||||
A keyboard event is represented by this structure:
|
||||
``` C
|
||||
typedef struct {
|
||||
sh_uint16 scancode;
|
||||
sh_bool pressed;
|
||||
sh_uint8 context_shift_win;
|
||||
sh_uint8 context_alt;
|
||||
sh_uint8 context_ctrl;
|
||||
sh_uint8 context_lock_status;
|
||||
sh_uint8 event_type;
|
||||
sh_uint64 timestamp;
|
||||
} sh_kbd_EVENT;
|
||||
```
|
||||
|
||||
All non-pause events contain a snapshot of keyboard context state at emission time, before the state of corresponding flag in the keyboard device changes. These fields remain valid even for `SH_EVENT_TYPE_KBD_CONTEXT`:
|
||||
- `context_shift_win`:
|
||||
- Bit 0: shift active, checked with macro `SH_KBD_EVENT_SHIFT_ACTIVE`
|
||||
- Bit 1: win active, checked with macro `SH_KBD_EVENT_WIN_ACTIVE`
|
||||
- `context_alt`:
|
||||
- Bit 0: right alt active, checked with macro `SH_KBD_EVENT_RIGHT_ACTIVE`
|
||||
- Bit 1: left alt active, checked with macro `SH_KBD_EVENT_LEFT_ACTIVE`
|
||||
- `context_ctrl`:
|
||||
- Bit 0: right control active, checked with macro `SH_KBD_EVENT_RIGHT_ACTIVE`
|
||||
- Bit 1: left control active, checked with macro `SH_KBD_EVENT_LEFT_ACTIVE`
|
||||
- `context_lock_status`:
|
||||
- Bit 0: numlock active, checked with `SH_KBD_EVENT_NUMLOCK_ACTIVE`
|
||||
- Bit 0: capslock active, checked with `SH_KBD_EVENT_CAPSLOCK_ACTIVE`
|
||||
|
||||
There is a precise way of interpreting what this structure mean. It start by checking the value of `event_type`.
|
||||
|
||||
### `SH_EVENT_TYPE_KBD_PAUSE`
|
||||
|
||||
The event is a press of the pause key. The `scancode` and `pressed` fields should be ignored.
|
||||
|
||||
### `SH_EVENT_TYPE_KBD_CONTEXT`
|
||||
|
||||
This event is a press of a modifier key. It allows to abstract modifiers keys events accross all types of keyboards.
|
||||
|
||||
The scancode must be one of the following:
|
||||
- `SH_KBD_CONTEXT_LSHIFT`
|
||||
- `SH_KBD_CONTEXT_RSHIFT`
|
||||
- `SH_KBD_CONTEXT_LCTRL`
|
||||
- `SH_KBD_CONTEXT_RCTRL`
|
||||
- `SH_KBD_CONTEXT_LALT`
|
||||
- `SH_KBD_CONTEXT_RALT`
|
||||
- `SH_KBD_CONTEXT_WIN`
|
||||
- `SH_KBD_CONTEXT_NUMLOCK`
|
||||
- `SH_KBD_CONTEXT_CAPSLOCK`
|
||||
|
||||
You need to check the `pressed` boolean to know if the key was pressed or released.
|
||||
|
||||
### `SH_EVENT_TYPE_PS2_NORMAL`
|
||||
|
||||
This event type represents a standard PS2 Set1 scancode. The scancode is put in the low 8 bits of the `scancode` fields. The high 8 bits are set to 0.
|
||||
|
||||
You need to check the `pressed` boolean to know if the key was pressed or released.
|
||||
|
||||
### `SH_EVENT_TYPE_PS2_E0`
|
||||
|
||||
This event type represents an extended PS2 Set1 scancode prefixed by `0xE0`. The scancode is put in the low 8 bits of the `scancode` fields. The high 8 bits are set to `0xE0`.
|
||||
|
||||
You need to check the `pressed` boolean to know if the key was pressed or released.
|
||||
|
||||
### Notes
|
||||
|
||||
For `SH_EVENT_TYPE_PS2_NORMAL` and `SH_EVENT_TYPE_PS2_E0` events type, the scancode, when `pressed` is `SH_FALSE`, keeps its bit 8 to 1.
|
||||
Reference in New Issue
Block a user