mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-15 00:16:48 +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 "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;
|
||||
|
|
|
@ -23,4 +23,5 @@
|
|||
#define DEBUG(...)
|
||||
#endif
|
||||
|
||||
void debugLogInit(void);
|
||||
int debugLog(const char *fmt, ...);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
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