mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-01-03 11:11:14 +00:00
thermosphere: add TransportInterface abstraction layer
This commit is contained in:
parent
1086c0612c
commit
388c245ce4
11 changed files with 499 additions and 66 deletions
|
@ -19,11 +19,22 @@
|
||||||
#include "platform/uart.h"
|
#include "platform/uart.h"
|
||||||
#include "semihosting.h"
|
#include "semihosting.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "transport_interface.h"
|
||||||
|
#include "platform/uart.h"
|
||||||
|
|
||||||
#ifndef DLOG_USE_SEMIHOSTING_WRITE0
|
#ifndef DLOG_USE_SEMIHOSTING_WRITE0
|
||||||
#define DLOG_USE_SEMIHOSTING_WRITE0 1
|
#define DLOG_USE_SEMIHOSTING_WRITE0 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static TransportInterface *g_debugLogTransportInterface;
|
||||||
|
|
||||||
|
void debugLogInit(void)
|
||||||
|
{
|
||||||
|
if (!DLOG_USE_SEMIHOSTING_WRITE0) {
|
||||||
|
transportInterfaceCreate(TRANSPORT_INTERFACE_TYPE_UART, DEFAULT_UART, DEFAULT_UART_FLAGS, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: UNSAFE!
|
// NOTE: UNSAFE!
|
||||||
int debugLog(const char *fmt, ...)
|
int debugLog(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -37,7 +48,7 @@ int debugLog(const char *fmt, ...)
|
||||||
if (DLOG_USE_SEMIHOSTING_WRITE0 && semihosting_connection_supported()) {
|
if (DLOG_USE_SEMIHOSTING_WRITE0 && semihosting_connection_supported()) {
|
||||||
semihosting_write_string(buf);
|
semihosting_write_string(buf);
|
||||||
} else {
|
} else {
|
||||||
uartWriteData(DEFAULT_UART, buf, (size_t)res);
|
transportInterfaceWriteData(g_debugLogTransportInterface, buf, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -23,4 +23,5 @@
|
||||||
#define DEBUG(...)
|
#define DEBUG(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void debugLogInit(void);
|
||||||
int debugLog(const char *fmt, ...);
|
int debugLog(const char *fmt, ...);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "debug_log.h"
|
#include "debug_log.h"
|
||||||
#include "vgic.h"
|
#include "vgic.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
#include "transport_interface.h"
|
||||||
|
|
||||||
IrqManager g_irqManager = {0};
|
IrqManager g_irqManager = {0};
|
||||||
|
|
||||||
|
@ -98,40 +99,6 @@ static void initGic(void)
|
||||||
currentCoreCtx->gicInterfaceMask = gicd->itargetsr[0];
|
currentCoreCtx->gicInterfaceMask = gicd->itargetsr[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
|
|
||||||
{
|
|
||||||
volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
|
|
||||||
gicd->icenabler[id / 32] = BIT(id % 32);
|
|
||||||
|
|
||||||
if (id >= 32) {
|
|
||||||
u32 cfgr = gicd->icfgr[id / 16];
|
|
||||||
cfgr &= ~(3 << IRQ_CFGR_SHIFT(id));
|
|
||||||
cfgr |= (!isLevelSensitive ? 3 : 1) << IRQ_CFGR_SHIFT(id);
|
|
||||||
gicd->icfgr[id / 16] = cfgr;
|
|
||||||
gicd->itargetsr[id] |= currentCoreCtx->gicInterfaceMask;
|
|
||||||
}
|
|
||||||
gicd->icpendr[id / 32] = BIT(id % 32);
|
|
||||||
gicd->ipriorityr[id] = (prio << g_irqManager.priorityShift) & 0xFF;
|
|
||||||
gicd->isenabler[id / 32] = BIT(id % 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
void initIrq(void)
|
|
||||||
{
|
|
||||||
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
|
|
||||||
|
|
||||||
initGic();
|
|
||||||
vgicInit();
|
|
||||||
|
|
||||||
// Configure the interrupts we use here
|
|
||||||
for (u32 i = 0; i < ThermosphereSgi_Max; i++) {
|
|
||||||
configureInterrupt(i, IRQ_PRIORITY_HOST, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
configureInterrupt(GIC_IRQID_MAINTENANCE, IRQ_PRIORITY_HOST, true);
|
|
||||||
|
|
||||||
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame)
|
static inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame)
|
||||||
{
|
{
|
||||||
// Evaluate if the timer really has expired in the PoV of the guest kernel. If not, reschedule (add missed time delta) it & exit early
|
// Evaluate if the timer really has expired in the PoV of the guest kernel. If not, reschedule (add missed time delta) it & exit early
|
||||||
|
@ -148,6 +115,75 @@ static inline bool checkRescheduleEmulatedPtimer(ExceptionStackFrame *frame)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void doConfigureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
|
||||||
|
{
|
||||||
|
volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
|
||||||
|
gicd->icenabler[id / 32] = BIT(id % 32);
|
||||||
|
|
||||||
|
if (id >= 32) {
|
||||||
|
u32 cfgr = gicd->icfgr[id / 16];
|
||||||
|
cfgr &= ~(3 << IRQ_CFGR_SHIFT(id));
|
||||||
|
cfgr |= (!isLevelSensitive ? 3 : 1) << IRQ_CFGR_SHIFT(id);
|
||||||
|
gicd->icfgr[id / 16] = cfgr;
|
||||||
|
gicd->itargetsr[id] = 0xFF; // all cpu interfaces
|
||||||
|
}
|
||||||
|
gicd->icpendr[id / 32] = BIT(id % 32);
|
||||||
|
gicd->ipriorityr[id] = (prio << g_irqManager.priorityShift) & 0xFF;
|
||||||
|
gicd->isenabler[id / 32] = BIT(id % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initIrq(void)
|
||||||
|
{
|
||||||
|
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
|
||||||
|
|
||||||
|
initGic();
|
||||||
|
vgicInit();
|
||||||
|
|
||||||
|
// Configure the interrupts we use here
|
||||||
|
for (u32 i = 0; i < ThermosphereSgi_Max; i++) {
|
||||||
|
doConfigureInterrupt(i, IRQ_PRIORITY_HOST, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
doConfigureInterrupt(GIC_IRQID_MAINTENANCE, IRQ_PRIORITY_HOST, true);
|
||||||
|
|
||||||
|
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
|
||||||
|
{
|
||||||
|
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
|
||||||
|
doConfigureInterrupt(id, prio, isLevelSensitive);
|
||||||
|
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irqSetAffinity(u16 id, u8 affinity)
|
||||||
|
{
|
||||||
|
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
|
||||||
|
g_irqManager.gic.gicd->itargetsr[id] = affinity;
|
||||||
|
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool irqIsGuest(u16 id)
|
||||||
|
{
|
||||||
|
if (id >= 32 + g_irqManager.numSharedInterrupts) {
|
||||||
|
DEBUG("vgic: %u not supported by physical distributor\n", (u32)id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
ret = ret && id != GIC_IRQID_MAINTENANCE;
|
||||||
|
ret = ret && id != GIC_IRQID_NS_PHYS_HYP_TIMER;
|
||||||
|
|
||||||
|
// If the following interrupts don't exist, that's fine, they're defined as GIC_IRQID_SPURIOUS in that case
|
||||||
|
// (for which the function isn't called, anyway)
|
||||||
|
ret = ret && id != GIC_IRQID_NS_VIRT_HYP_TIMER;
|
||||||
|
ret = ret && id != GIC_IRQID_SEC_PHYS_HYP_TIMER;
|
||||||
|
ret = ret && id != GIC_IRQID_SEC_VIRT_HYP_TIMER;
|
||||||
|
|
||||||
|
ret = ret && transportInterfaceFindByIrqId(id) == NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
||||||
{
|
{
|
||||||
(void)isLowerEl;
|
(void)isLowerEl;
|
||||||
|
@ -159,7 +195,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
||||||
u32 irqId = iar & 0x3FF;
|
u32 irqId = iar & 0x3FF;
|
||||||
u32 srcCore = (iar >> 10) & 7;
|
u32 srcCore = (iar >> 10) & 7;
|
||||||
|
|
||||||
DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId);
|
//DEBUG("EL2 [core %d]: Received irq %x\n", (int)currentCoreCtx->coreId, irqId);
|
||||||
|
|
||||||
if (irqId == GIC_IRQID_SPURIOUS) {
|
if (irqId == GIC_IRQID_SPURIOUS) {
|
||||||
// Spurious interrupt received
|
// Spurious interrupt received
|
||||||
|
@ -192,10 +228,12 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL;
|
||||||
|
|
||||||
// Priority drop
|
// Priority drop
|
||||||
gicc->eoir = iar;
|
gicc->eoir = iar;
|
||||||
|
|
||||||
isGuestInterrupt = isGuestInterrupt && irqIsGuest(irqId);
|
isGuestInterrupt = isGuestInterrupt && transportIface == NULL && irqIsGuest(irqId);
|
||||||
|
|
||||||
recursiveSpinlockLock(&g_irqManager.lock);
|
recursiveSpinlockLock(&g_irqManager.lock);
|
||||||
|
|
||||||
|
@ -213,4 +251,10 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
|
||||||
vgicUpdateState();
|
vgicUpdateState();
|
||||||
|
|
||||||
recursiveSpinlockUnlock(&g_irqManager.lock);
|
recursiveSpinlockUnlock(&g_irqManager.lock);
|
||||||
|
|
||||||
|
// Bottom half part
|
||||||
|
if (transportIface != NULL) {
|
||||||
|
unmaskIrq();
|
||||||
|
transportInterfaceIrqHandlerBottomHalf(transportIface);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "platform/interrupt_config.h"
|
#include "platform/interrupt_config.h"
|
||||||
#include "platform/uart.h"
|
|
||||||
|
|
||||||
#define IRQ_PRIORITY_HOST 0
|
#define IRQ_PRIORITY_HOST 0
|
||||||
#define IRQ_PRIORITY_GUEST 1
|
#define IRQ_PRIORITY_GUEST 1
|
||||||
|
@ -49,8 +48,10 @@ typedef enum ThermosphereSgi {
|
||||||
extern IrqManager g_irqManager;
|
extern IrqManager g_irqManager;
|
||||||
|
|
||||||
void initIrq(void);
|
void initIrq(void);
|
||||||
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32);
|
|
||||||
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive);
|
void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive);
|
||||||
|
bool irqIsGuest(u16 id);
|
||||||
|
void irqSetAffinity(u16 id, u8 affinityMask);
|
||||||
|
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32);
|
||||||
|
|
||||||
static inline void generateSgiForAllOthers(ThermosphereSgi id)
|
static inline void generateSgiForAllOthers(ThermosphereSgi id)
|
||||||
{
|
{
|
||||||
|
@ -71,24 +72,3 @@ static inline void generateSgiForAll(ThermosphereSgi id)
|
||||||
{
|
{
|
||||||
generateSgiForList(id, MASK(g_irqManager.numCpuInterfaces));
|
generateSgiForList(id, MASK(g_irqManager.numCpuInterfaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool irqIsGuest(u16 id)
|
|
||||||
{
|
|
||||||
if (id >= 32 + g_irqManager.numSharedInterrupts) {
|
|
||||||
DEBUG("vgic: %u not supported by physical distributor\n", (u32)id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = id != GIC_IRQID_MAINTENANCE && id != GIC_IRQID_NS_PHYS_HYP_TIMER;
|
|
||||||
ret = ret && id != uartGetIrqId(DEFAULT_UART); // FIXME
|
|
||||||
#if GIC_IRQID_NS_VIRT_HYP_TIMER != GIC_IRQID_SPURIOUS
|
|
||||||
ret = ret && id != GIC_IRQID_NS_VIRT_HYP_TIMER;
|
|
||||||
#endif
|
|
||||||
#if GIC_IRQID_SEC_PHYS_HYP_TIMER != GIC_IRQID_SPURIOUS
|
|
||||||
ret = ret && id != GIC_IRQID_SEC_PHYS_HYP_TIMER;
|
|
||||||
#endif
|
|
||||||
#if GIC_IRQID_SEC_VIRT_HYP_TIMER != GIC_IRQID_SPURIOUS
|
|
||||||
ret = ret && id != GIC_IRQID_SEC_VIRT_HYP_TIMER;
|
|
||||||
#endif
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "watchpoints.h"
|
#include "watchpoints.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
|
#include "transport_interface.h"
|
||||||
|
|
||||||
extern const u8 __start__[];
|
extern const u8 __start__[];
|
||||||
|
|
||||||
|
@ -39,18 +40,18 @@ static void loadKernelViaSemihosting(void)
|
||||||
|
|
||||||
void thermosphereMain(ExceptionStackFrame *frame)
|
void thermosphereMain(ExceptionStackFrame *frame)
|
||||||
{
|
{
|
||||||
enableTraps();
|
|
||||||
enableBreakpointsAndWatchpoints();
|
|
||||||
timerInit();
|
|
||||||
initIrq();
|
initIrq();
|
||||||
|
|
||||||
if (currentCoreCtx->isBootCore) {
|
if (currentCoreCtx->isBootCore) {
|
||||||
uartInit(DEFAULT_UART, BAUD_115200, 0);
|
transportInterfaceInitLayer();
|
||||||
uartSetInterruptStatus(DEFAULT_UART, DIRECTION_READ, true);
|
debugLogInit();
|
||||||
|
|
||||||
DEBUG("EL2: core %u reached main first!\n", currentCoreCtx->coreId);
|
DEBUG("EL2: core %u reached main first!\n", currentCoreCtx->coreId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableTraps();
|
||||||
|
enableBreakpointsAndWatchpoints();
|
||||||
|
timerInit();
|
||||||
initBreakpoints();
|
initBreakpoints();
|
||||||
initWatchpoints();
|
initWatchpoints();
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,6 @@ void uartInit(UartDevice dev, u32 baudRate, u32 flags)
|
||||||
|
|
||||||
// Enable tx, rx, and uart overall
|
// Enable tx, rx, and uart overall
|
||||||
uart->cr = PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN;
|
uart->cr = PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN;
|
||||||
//uart->imsc = PL011_RTI | PL011_RXI | PL011_RXI;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void uartWriteData(UartDevice dev, const void *buffer, size_t size)
|
void uartWriteData(UartDevice dev, const void *buffer, size_t size)
|
||||||
|
@ -129,6 +128,24 @@ size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t uartReadDataUntil(UartDevice dev, char *buffer, size_t maxSize, char delimiter)
|
||||||
|
{
|
||||||
|
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < maxSize; i++) {
|
||||||
|
while (uart->fr & PL011_UARTFR_RXFE);
|
||||||
|
buffer[i] = uart->dr;
|
||||||
|
++count;
|
||||||
|
if (buffer[i] == delimiter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
ReadWriteDirection uartGetInterruptDirection(UartDevice dev)
|
ReadWriteDirection uartGetInterruptDirection(UartDevice dev)
|
||||||
{
|
{
|
||||||
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
|
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
|
||||||
|
|
|
@ -132,6 +132,8 @@ void uartInit(UartDevice dev, u32 baudRate, u32 flags);
|
||||||
void uartWriteData(UartDevice dev, const void *buffer, size_t size);
|
void uartWriteData(UartDevice dev, const void *buffer, size_t size);
|
||||||
void uartReadData(UartDevice dev, void *buffer, size_t size);
|
void uartReadData(UartDevice dev, void *buffer, size_t size);
|
||||||
size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize);
|
size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize);
|
||||||
|
size_t uartReadDataUntil(UartDevice dev, char *buffer, size_t maxSize, char delimiter);
|
||||||
|
|
||||||
ReadWriteDirection uartGetInterruptDirection(UartDevice dev);
|
ReadWriteDirection uartGetInterruptDirection(UartDevice dev);
|
||||||
void uartSetInterruptStatus(UartDevice dev, ReadWriteDirection direction, bool enable);
|
void uartSetInterruptStatus(UartDevice dev, ReadWriteDirection direction, bool enable);
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,24 @@ size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t uartReadDataUntil(UartDevice dev, char *buffer, size_t maxSize, char delimiter)
|
||||||
|
{
|
||||||
|
volatile tegra_uart_t *uart = uartGetRegisters(dev);
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < maxSize && (uart->lsr & UART_LSR_RDR); i++) {
|
||||||
|
while (!(uart->lsr & UART_LSR_RDR)) // Wait until it's possible to receive data.
|
||||||
|
|
||||||
|
buffer[i] = uart->rbr;
|
||||||
|
++count;
|
||||||
|
if (buffer[i] == delimiter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
ReadWriteDirection uartGetInterruptDirection(UartDevice dev)
|
ReadWriteDirection uartGetInterruptDirection(UartDevice dev)
|
||||||
{
|
{
|
||||||
volatile tegra_uart_t *uart = uartGetRegisters(dev);
|
volatile tegra_uart_t *uart = uartGetRegisters(dev);
|
||||||
|
|
|
@ -195,6 +195,8 @@ void uartInit(UartDevice dev, u32 baud, u32 flags);
|
||||||
void uartWriteData(UartDevice dev, const void *buffer, size_t size);
|
void uartWriteData(UartDevice dev, const void *buffer, size_t size);
|
||||||
void uartReadData(UartDevice dev, void *buffer, size_t size);
|
void uartReadData(UartDevice dev, void *buffer, size_t size);
|
||||||
size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize);
|
size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize);
|
||||||
|
size_t uartReadDataUntil(UartDevice dev, char *buffer, size_t maxSize, char delimiter);
|
||||||
|
|
||||||
ReadWriteDirection uartGetInterruptDirection(UartDevice dev);
|
ReadWriteDirection uartGetInterruptDirection(UartDevice dev);
|
||||||
void uartSetInterruptStatus(UartDevice dev, ReadWriteDirection direction, bool enable);
|
void uartSetInterruptStatus(UartDevice dev, ReadWriteDirection direction, bool enable);
|
||||||
|
|
||||||
|
|
301
thermosphere/src/transport_interface.c
Normal file
301
thermosphere/src/transport_interface.c
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "transport_interface.h"
|
||||||
|
#include "platform/uart.h"
|
||||||
|
#include "core_ctx.h"
|
||||||
|
#include "irq.h"
|
||||||
|
|
||||||
|
#define FOREACH_LINK(link, list)\
|
||||||
|
for (TransportInterfaceLink *link = transportInterfaceListGetFirstLink(list); link != transportInterfaceListGetEndLink(list); link = link->next)
|
||||||
|
|
||||||
|
typedef struct TransportInterfaceLink {
|
||||||
|
struct TransportInterfaceLink *prev, *next;
|
||||||
|
} TransportInterfaceLink;
|
||||||
|
|
||||||
|
struct TransportInterface {
|
||||||
|
TransportInterfaceLink link;
|
||||||
|
TransportInterfaceReceiveDataCallback receiveDataCallback;
|
||||||
|
TransportInterfaceProcessDataCallback processDataCallback;
|
||||||
|
void *ctx;
|
||||||
|
RecursiveSpinlock lock;
|
||||||
|
u32 id;
|
||||||
|
u32 flags;
|
||||||
|
TransportInterfaceType type;
|
||||||
|
// ignored ReadWriteDirection interruptDirection;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct TransportInterfaceList {
|
||||||
|
TransportInterfaceLink link;
|
||||||
|
} TransportInterfaceList;
|
||||||
|
|
||||||
|
static RecursiveSpinlock g_transportInterfaceLayerLock;
|
||||||
|
static TransportInterface g_transportInterfaceStorage[MAX_TRANSPORT_INTERFACES];
|
||||||
|
|
||||||
|
static TransportInterfaceList g_transportInterfaceList, g_transportInterfaceFreeList;
|
||||||
|
|
||||||
|
static inline TransportInterfaceLink *transportInterfaceListGetLastLink(TransportInterfaceList *list)
|
||||||
|
{
|
||||||
|
return list->link.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TransportInterfaceLink *transportInterfaceListGetFirstLink(TransportInterfaceList *list)
|
||||||
|
{
|
||||||
|
return list->link.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TransportInterfaceLink *transportInterfaceListGetEndLink(TransportInterfaceList *list)
|
||||||
|
{
|
||||||
|
return &list->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool transportInterfaceListIsEmpty(TransportInterfaceList *list)
|
||||||
|
{
|
||||||
|
return transportInterfaceListGetFirstLink(list) == transportInterfaceListGetEndLink(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TransportInterface *transportInterfaceGetLinkParent(TransportInterfaceLink *link)
|
||||||
|
{
|
||||||
|
// Static aliasing rules say the first member of a struct has the same address as the struct itself
|
||||||
|
return (TransportInterface *)link;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void transportInterfaceListInit(TransportInterfaceList *list)
|
||||||
|
{
|
||||||
|
list->link.prev = transportInterfaceListGetEndLink(list);
|
||||||
|
list->link.next = transportInterfaceListGetEndLink(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void transportInterfaceListInsertAfter(TransportInterfaceLink *pos, TransportInterfaceLink *nd)
|
||||||
|
{
|
||||||
|
// if pos is last & list is empty, ->next writes to first, etc.
|
||||||
|
pos->next->prev = nd;
|
||||||
|
nd->prev = pos;
|
||||||
|
nd->next = pos->next;
|
||||||
|
pos->next = nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void transportInterfaceListInsertBefore(TransportInterfaceLink *pos, TransportInterfaceLink *nd)
|
||||||
|
{
|
||||||
|
pos->prev->next = nd;
|
||||||
|
nd->prev = pos->prev;
|
||||||
|
nd->next = pos;
|
||||||
|
pos->prev = nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void transportInterfaceListRemove(const TransportInterfaceLink *nd)
|
||||||
|
{
|
||||||
|
nd->prev->next = nd->next;
|
||||||
|
nd->next->prev = nd->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceInitLayer(void)
|
||||||
|
{
|
||||||
|
transportInterfaceListInit(&g_transportInterfaceList);
|
||||||
|
transportInterfaceListInit(&g_transportInterfaceFreeList);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < MAX_TRANSPORT_INTERFACES; i++) {
|
||||||
|
transportInterfaceListInsertAfter(transportInterfaceListGetLastLink(&g_transportInterfaceFreeList), &g_transportInterfaceStorage[i].link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportInterface *transportInterfaceCreate(
|
||||||
|
TransportInterfaceType type,
|
||||||
|
u32 id,
|
||||||
|
u32 flags,
|
||||||
|
TransportInterfaceReceiveDataCallback receiveDataCallback,
|
||||||
|
TransportInterfaceProcessDataCallback processDataCallback,
|
||||||
|
void *ctx
|
||||||
|
)
|
||||||
|
{
|
||||||
|
u64 irqFlags = recursiveSpinlockLockMaskIrq(&g_transportInterfaceLayerLock);
|
||||||
|
if (transportInterfaceListIsEmpty(&g_transportInterfaceFreeList)) {
|
||||||
|
PANIC("transportInterfaceCreateAndInit: resource exhaustion\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportInterface *iface;
|
||||||
|
|
||||||
|
FOREACH_LINK (link, &g_transportInterfaceList) {
|
||||||
|
iface = transportInterfaceGetLinkParent(link);
|
||||||
|
if (iface->type == type && iface->id == id) {
|
||||||
|
PANIC("transportInterfaceCreateAndInit: device already registered\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iface = transportInterfaceGetLinkParent(transportInterfaceListGetFirstLink(&g_transportInterfaceFreeList));
|
||||||
|
|
||||||
|
iface->type = type;
|
||||||
|
iface->id = id;
|
||||||
|
iface->receiveDataCallback = receiveDataCallback;
|
||||||
|
iface->processDataCallback = processDataCallback;
|
||||||
|
iface->ctx = ctx;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)id;
|
||||||
|
uartInit(dev, BAUD_115200, flags);
|
||||||
|
uartSetInterruptStatus(id, DIRECTION_READ, iface->receiveDataCallback != NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
transportInterfaceListRemove(&iface->link);
|
||||||
|
transportInterfaceListInsertAfter(transportInterfaceListGetLastLink(&g_transportInterfaceList), &iface->link);
|
||||||
|
recursiveSpinlockUnlockRestoreIrq(&g_transportInterfaceLayerLock, irqFlags);
|
||||||
|
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceAcquire(TransportInterface *iface)
|
||||||
|
{
|
||||||
|
// Get the lock, prevent the interrupt from being pending if there's incoming data
|
||||||
|
recursiveSpinlockLock(&iface->lock);
|
||||||
|
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
uartSetInterruptStatus(dev, DIRECTION_READ, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceRelease(TransportInterface *iface)
|
||||||
|
{
|
||||||
|
// See transportInterfaceAcquire
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
uartSetInterruptStatus(dev, DIRECTION_READ, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
recursiveSpinlockUnlock(&iface->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportInterface *transportInterfaceFindByIrqId(u16 irqId)
|
||||||
|
{
|
||||||
|
u64 irqFlags = recursiveSpinlockLockMaskIrq(&g_transportInterfaceLayerLock);
|
||||||
|
|
||||||
|
TransportInterface *ret = NULL;
|
||||||
|
FOREACH_LINK (link, &g_transportInterfaceList) {
|
||||||
|
TransportInterface *iface = transportInterfaceGetLinkParent(link);
|
||||||
|
if (iface->type == TRANSPORT_INTERFACE_TYPE_UART && uartGetIrqId((UartDevice)iface->id) == irqId) {
|
||||||
|
ret = iface;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recursiveSpinlockUnlockRestoreIrq(&g_transportInterfaceLayerLock, irqFlags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceSetInterruptAffinity(TransportInterface *iface, u8 affinity)
|
||||||
|
{
|
||||||
|
u64 irqFlags = recursiveSpinlockLockMaskIrq(&iface->lock);
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
irqSetAffinity(uartGetIrqId(dev), affinity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
recursiveSpinlockUnlockRestoreIrq(&iface->lock, irqFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportInterface *transportInterfaceIrqHandlerTopHalf(u16 irqId)
|
||||||
|
{
|
||||||
|
TransportInterface *iface = transportInterfaceFindByIrqId(irqId);
|
||||||
|
if (iface == NULL) {
|
||||||
|
PANIC("transportInterfaceLayerIrqHandlerTop: irq id %x not found!\n", irqId);
|
||||||
|
}
|
||||||
|
|
||||||
|
transportInterfaceAcquire(iface);
|
||||||
|
|
||||||
|
// Interrupt should have gone from active-and-pending to only active (on the GIC)
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceIrqHandlerBottomHalf(TransportInterface *iface)
|
||||||
|
{
|
||||||
|
size_t sz = iface->receiveDataCallback(iface, iface->ctx);
|
||||||
|
if (sz > 0 && iface->processDataCallback != NULL) {
|
||||||
|
iface->processDataCallback(iface, iface->ctx, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
transportInterfaceRelease(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceWriteData(TransportInterface *iface, const void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
uartWriteData(dev, buffer, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportInterfaceReadData(TransportInterface *iface, void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
uartReadData(dev, buffer, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t transportInterfaceReadDataMax(TransportInterface *iface, void *buffer, size_t maxSize)
|
||||||
|
{
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
return uartReadDataMax(dev, buffer, maxSize);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t transportInterfaceReadDataUntil(TransportInterface *iface, char *buffer, size_t maxSize, char delimiter)
|
||||||
|
{
|
||||||
|
switch (iface->type) {
|
||||||
|
case TRANSPORT_INTERFACE_TYPE_UART: {
|
||||||
|
UartDevice dev = (UartDevice)iface->id;
|
||||||
|
return uartReadDataUntil(dev, buffer, maxSize, delimiter);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
56
thermosphere/src/transport_interface.h
Normal file
56
thermosphere/src/transport_interface.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
|
||||||
|
#define MAX_TRANSPORT_INTERFACES 4
|
||||||
|
|
||||||
|
typedef enum TransportInterfaceType {
|
||||||
|
TRANSPORT_INTERFACE_TYPE_UART = 0,
|
||||||
|
|
||||||
|
TRANSPORT_INTERFACE_TYPE_MAX,
|
||||||
|
} TransportInterfaceType;
|
||||||
|
|
||||||
|
struct TransportInterface;
|
||||||
|
typedef struct TransportInterface TransportInterface;
|
||||||
|
typedef size_t (*TransportInterfaceReceiveDataCallback)(TransportInterface *iface, void *ctx);
|
||||||
|
typedef void (*TransportInterfaceProcessDataCallback)(TransportInterface *iface, void *ctx, size_t dataSize);
|
||||||
|
|
||||||
|
void transportInterfaceInitLayer(void);
|
||||||
|
TransportInterface *transportInterfaceCreate(
|
||||||
|
TransportInterfaceType type,
|
||||||
|
u32 id,
|
||||||
|
u32 flags,
|
||||||
|
TransportInterfaceReceiveDataCallback receiveDataCallback,
|
||||||
|
TransportInterfaceProcessDataCallback processDataCallback,
|
||||||
|
void *ctx
|
||||||
|
);
|
||||||
|
void transportInterfaceAcquire(TransportInterface *iface);
|
||||||
|
void transportInterfaceRelease(TransportInterface *iface);
|
||||||
|
|
||||||
|
TransportInterface *transportInterfaceFindByIrqId(u16 irqId);
|
||||||
|
void transportInterfaceSetInterruptAffinity(TransportInterface *iface, u8 affinity);
|
||||||
|
|
||||||
|
TransportInterface *transportInterfaceIrqHandlerTopHalf(u16 irqId);
|
||||||
|
void transportInterfaceIrqHandlerBottomHalf(TransportInterface *iface);
|
||||||
|
|
||||||
|
void transportInterfaceWriteData(TransportInterface *iface, const void *buffer, size_t size);
|
||||||
|
void transportInterfaceReadData(TransportInterface *iface, void *buffer, size_t size);
|
||||||
|
size_t transportInterfaceReadDataMax(TransportInterface *iface, void *buffer, size_t maxSize);
|
||||||
|
size_t transportInterfaceReadDataUntil(TransportInterface *iface, char *buffer, size_t maxSize, char delimiter);
|
Loading…
Reference in a new issue