1
0
Fork 0
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:
TuxSH 2020-01-12 01:59:26 +00:00
parent 1086c0612c
commit 388c245ce4
11 changed files with 499 additions and 66 deletions

View file

@ -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;

View file

@ -23,4 +23,5 @@
#define DEBUG(...)
#endif
void debugLogInit(void);
int debugLog(const char *fmt, ...);

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View 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;
}
}

View 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);