Files
nt-lite/code/kernel/hal/hal_serial.c
T

79 lines
2.3 KiB
C

// HAL serial output endpoint. This file owns polling COM1 writes and subscribes serial to HAL text streams.
#include <stdint.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/hal/hal_output.h>
#define HAL_COM1_DATA_PORT 0x03F8u
#define HAL_COM1_STATUS_PORT 0x03FDu
#define HAL_COM1_TX_READY 0x20u
static volatile long gSerialWriteLock;
// Waits until COM1 can accept one byte.
static void HalSerialWaitForTransmit(void) {
while ((__inbyte(HAL_COM1_STATUS_PORT) & HAL_COM1_TX_READY) == 0)
_mm_pause();
}
// Writes one byte to COM1.
static void HalSerialWriteByte(uint8_t value) {
HalSerialWaitForTransmit();
__outbyte(HAL_COM1_DATA_PORT, value);
}
// Acquires the normal serial writer lock.
static void HalSerialAcquireWriteLock(void) {
while (_InterlockedCompareExchange(&gSerialWriteLock, 1, 0) != 0)
_mm_pause();
}
// Releases the normal serial writer lock.
static void HalSerialReleaseWriteLock(void) {
_InterlockedExchange(&gSerialWriteLock, 0);
}
// Writes one output record through polling COM1.
static void HalSerialWriteRecord(void* context, const HAL_OUTPUT_RECORD* record) {
(void)context;
if (!record || !record->Buffer)
return;
bool emergency = (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) != 0;
uintptr_t interruptFlags = 0;
if (!emergency) {
interruptFlags = HalDisableInterrupts();
HalSerialAcquireWriteLock();
}
for (size_t i = 0; i < record->Length; i++) {
char c = record->Buffer[i];
if (c == '\n')
HalSerialWriteByte('\r');
HalSerialWriteByte((uint8_t)c);
}
if (!emergency) {
HalSerialReleaseWriteLock();
HalRestoreInterrupts(interruptFlags);
}
}
// Registers the polling serial endpoint.
void HalSerialInitialize(HAL_OUTPUT_ROUTER* output) {
if (!output || !output->RegisterEndpoint)
return;
HAL_OUTPUT_ENDPOINT endpoint;
endpoint.Name = "serial0";
endpoint.StreamMask = HAL_OUTPUT_STREAM_ALL;
endpoint.Capabilities = HAL_OUTPUT_ENDPOINT_PANIC_SAFE;
endpoint.Write = HalSerialWriteRecord;
endpoint.Context = 0;
uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID;
if (KSUCCESS(output->RegisterEndpoint(&endpoint, &endpointId)))
output->SerialEndpointId = endpointId;
}