Atmosphere/thermosphere/src/transport_interface.c
2021-02-19 21:52:12 +00:00

303 lines
9.2 KiB
C

/*
* 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);
ENSURE(!transportInterfaceListIsEmpty(&g_transportInterfaceFreeList));
TransportInterface *iface;
FOREACH_LINK (link, &g_transportInterfaceList) {
iface = transportInterfaceGetLinkParent(link);
ENSURE(iface->type != type || iface->id != id);
}
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)
{
// Allow interrupts to be taken here if the caller allows it
recursiveSpinlockLock(&iface->lock);
// Get the lock, prevent the interrupt from being pending if there's incoming data
u64 flags = maskIrq();
switch (iface->type) {
case TRANSPORT_INTERFACE_TYPE_UART: {
UartDevice dev = (UartDevice)iface->id;
uartSetInterruptStatus(dev, DIRECTION_READ, false);
break;
}
default:
break;
}
restoreInterruptFlags(flags);
}
void transportInterfaceRelease(TransportInterface *iface)
{
u64 flags = maskIrq();
// See transportInterfaceAcquire
switch (iface->type) {
case TRANSPORT_INTERFACE_TYPE_UART: {
UartDevice dev = (UartDevice)iface->id;
uartSetInterruptStatus(dev, DIRECTION_READ, true);
break;
}
default:
break;
}
recursiveSpinlockUnlockRestoreIrq(&iface->lock, flags);
}
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);
ENSURE(iface != NULL);
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);
}
maskIrq(); // Avoid stack overflow, as the line will be asserted once more
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;
}
}