diff --git a/thermosphere/src/debug_log.c b/thermosphere/src/debug_log.c
index 34fbff61f..e8eb6e497 100644
--- a/thermosphere/src/debug_log.c
+++ b/thermosphere/src/debug_log.c
@@ -19,11 +19,22 @@
#include "platform/uart.h"
#include "semihosting.h"
#include "utils.h"
+#include "transport_interface.h"
+#include "platform/uart.h"
#ifndef DLOG_USE_SEMIHOSTING_WRITE0
#define DLOG_USE_SEMIHOSTING_WRITE0 1
#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!
int debugLog(const char *fmt, ...)
{
@@ -37,7 +48,7 @@ int debugLog(const char *fmt, ...)
if (DLOG_USE_SEMIHOSTING_WRITE0 && semihosting_connection_supported()) {
semihosting_write_string(buf);
} else {
- uartWriteData(DEFAULT_UART, buf, (size_t)res);
+ transportInterfaceWriteData(g_debugLogTransportInterface, buf, res);
}
return res;
diff --git a/thermosphere/src/debug_log.h b/thermosphere/src/debug_log.h
index d8c46df85..8076d4f16 100644
--- a/thermosphere/src/debug_log.h
+++ b/thermosphere/src/debug_log.h
@@ -23,4 +23,5 @@
#define DEBUG(...)
#endif
+void debugLogInit(void);
int debugLog(const char *fmt, ...);
diff --git a/thermosphere/src/irq.c b/thermosphere/src/irq.c
index e8385ce7e..87ed2b9b0 100644
--- a/thermosphere/src/irq.c
+++ b/thermosphere/src/irq.c
@@ -19,6 +19,7 @@
#include "debug_log.h"
#include "vgic.h"
#include "timer.h"
+#include "transport_interface.h"
IrqManager g_irqManager = {0};
@@ -98,40 +99,6 @@ static void initGic(void)
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)
{
// 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;
}
+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)isLowerEl;
@@ -159,7 +195,7 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
u32 irqId = iar & 0x3FF;
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) {
// Spurious interrupt received
@@ -192,10 +228,12 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
break;
}
+ TransportInterface *transportIface = irqId >= 32 ? transportInterfaceIrqHandlerTopHalf(irqId) : NULL;
+
// Priority drop
gicc->eoir = iar;
- isGuestInterrupt = isGuestInterrupt && irqIsGuest(irqId);
+ isGuestInterrupt = isGuestInterrupt && transportIface == NULL && irqIsGuest(irqId);
recursiveSpinlockLock(&g_irqManager.lock);
@@ -213,4 +251,10 @@ void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
vgicUpdateState();
recursiveSpinlockUnlock(&g_irqManager.lock);
+
+ // Bottom half part
+ if (transportIface != NULL) {
+ unmaskIrq();
+ transportInterfaceIrqHandlerBottomHalf(transportIface);
+ }
}
diff --git a/thermosphere/src/irq.h b/thermosphere/src/irq.h
index 4f91fdf2a..6ca212a9e 100644
--- a/thermosphere/src/irq.h
+++ b/thermosphere/src/irq.h
@@ -21,7 +21,6 @@
#include "exceptions.h"
#include "utils.h"
#include "platform/interrupt_config.h"
-#include "platform/uart.h"
#define IRQ_PRIORITY_HOST 0
#define IRQ_PRIORITY_GUEST 1
@@ -49,8 +48,10 @@ typedef enum ThermosphereSgi {
extern IrqManager g_irqManager;
void initIrq(void);
-void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32);
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)
{
@@ -71,24 +72,3 @@ static inline void generateSgiForAll(ThermosphereSgi id)
{
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;
-}
diff --git a/thermosphere/src/main.c b/thermosphere/src/main.c
index 00375154e..dd957a04a 100644
--- a/thermosphere/src/main.c
+++ b/thermosphere/src/main.c
@@ -13,6 +13,7 @@
#include "watchpoints.h"
#include "timer.h"
#include "irq.h"
+#include "transport_interface.h"
extern const u8 __start__[];
@@ -39,18 +40,18 @@ static void loadKernelViaSemihosting(void)
void thermosphereMain(ExceptionStackFrame *frame)
{
- enableTraps();
- enableBreakpointsAndWatchpoints();
- timerInit();
initIrq();
if (currentCoreCtx->isBootCore) {
- uartInit(DEFAULT_UART, BAUD_115200, 0);
- uartSetInterruptStatus(DEFAULT_UART, DIRECTION_READ, true);
+ transportInterfaceInitLayer();
+ debugLogInit();
DEBUG("EL2: core %u reached main first!\n", currentCoreCtx->coreId);
}
+ enableTraps();
+ enableBreakpointsAndWatchpoints();
+ timerInit();
initBreakpoints();
initWatchpoints();
diff --git a/thermosphere/src/platform/qemu/uart.c b/thermosphere/src/platform/qemu/uart.c
index 66fa09021..7734ca7b7 100644
--- a/thermosphere/src/platform/qemu/uart.c
+++ b/thermosphere/src/platform/qemu/uart.c
@@ -87,7 +87,6 @@ void uartInit(UartDevice dev, u32 baudRate, u32 flags)
// Enable tx, rx, and uart overall
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)
@@ -129,6 +128,24 @@ size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize)
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)
{
volatile PL011UartRegisters *uart = uartGetRegisters(dev);
diff --git a/thermosphere/src/platform/qemu/uart.h b/thermosphere/src/platform/qemu/uart.h
index 523dea480..d266ff6ce 100644
--- a/thermosphere/src/platform/qemu/uart.h
+++ b/thermosphere/src/platform/qemu/uart.h
@@ -132,6 +132,8 @@ void uartInit(UartDevice dev, u32 baudRate, u32 flags);
void uartWriteData(UartDevice dev, const 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 uartReadDataUntil(UartDevice dev, char *buffer, size_t maxSize, char delimiter);
+
ReadWriteDirection uartGetInterruptDirection(UartDevice dev);
void uartSetInterruptStatus(UartDevice dev, ReadWriteDirection direction, bool enable);
diff --git a/thermosphere/src/platform/tegra/uart.c b/thermosphere/src/platform/tegra/uart.c
index 7664b2069..c6274f046 100644
--- a/thermosphere/src/platform/tegra/uart.c
+++ b/thermosphere/src/platform/tegra/uart.c
@@ -192,6 +192,24 @@ size_t uartReadDataMax(UartDevice dev, void *buffer, size_t maxSize)
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)
{
volatile tegra_uart_t *uart = uartGetRegisters(dev);
diff --git a/thermosphere/src/platform/tegra/uart.h b/thermosphere/src/platform/tegra/uart.h
index 70782224e..b2aeffc48 100644
--- a/thermosphere/src/platform/tegra/uart.h
+++ b/thermosphere/src/platform/tegra/uart.h
@@ -195,6 +195,8 @@ void uartInit(UartDevice dev, u32 baud, u32 flags);
void uartWriteData(UartDevice dev, const 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 uartReadDataUntil(UartDevice dev, char *buffer, size_t maxSize, char delimiter);
+
ReadWriteDirection uartGetInterruptDirection(UartDevice dev);
void uartSetInterruptStatus(UartDevice dev, ReadWriteDirection direction, bool enable);
diff --git a/thermosphere/src/transport_interface.c b/thermosphere/src/transport_interface.c
new file mode 100644
index 000000000..755949679
--- /dev/null
+++ b/thermosphere/src/transport_interface.c
@@ -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 .
+ */
+
+#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;
+ }
+}
diff --git a/thermosphere/src/transport_interface.h b/thermosphere/src/transport_interface.h
new file mode 100644
index 000000000..b9f6d1e25
--- /dev/null
+++ b/thermosphere/src/transport_interface.h
@@ -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 .
+ */
+
+#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);