From 388c245ce40e65a4f7a635f76a05518779b14c15 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Sun, 12 Jan 2020 01:59:26 +0000 Subject: [PATCH] thermosphere: add TransportInterface abstraction layer --- thermosphere/src/debug_log.c | 13 +- thermosphere/src/debug_log.h | 1 + thermosphere/src/irq.c | 116 +++++++--- thermosphere/src/irq.h | 26 +-- thermosphere/src/main.c | 11 +- thermosphere/src/platform/qemu/uart.c | 19 +- thermosphere/src/platform/qemu/uart.h | 2 + thermosphere/src/platform/tegra/uart.c | 18 ++ thermosphere/src/platform/tegra/uart.h | 2 + thermosphere/src/transport_interface.c | 301 +++++++++++++++++++++++++ thermosphere/src/transport_interface.h | 56 +++++ 11 files changed, 499 insertions(+), 66 deletions(-) create mode 100644 thermosphere/src/transport_interface.c create mode 100644 thermosphere/src/transport_interface.h 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);