Add IRC library

This commit is contained in:
GaryOderNichts 2022-11-25 22:14:21 +01:00 committed by fincs
parent 9b3b9cb9b7
commit 7d9fa9e416
5 changed files with 647 additions and 1 deletions

View File

@ -27,12 +27,14 @@ SOURCES := cafe \
libraries/wutdefaultheap \
libraries/libwhb/src \
libraries/libgfd/src \
libraries/libirc/src \
libraries/nn_erreula \
libraries/nn_swkbd
DATA := data
INCLUDES := include \
libraries/libwhb/include \
libraries/libgfd/include \
libraries/libirc/include
#---------------------------------------------------------------------------------
# options for code generation
@ -101,7 +103,11 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
all: lib/libwut.a lib/libwutd.a
dist-bin: all
@tar --exclude=*~ -cjf wut-$(VERSION).tar.bz2 include lib share -C libraries/libwhb include -C ../libgfd include
@tar --exclude=*~ -cjf wut-$(VERSION).tar.bz2 \
include lib share \
-C libraries/libwhb include \
-C ../libgfd include \
-C ../libirc include
dist-src:
@tar --exclude=*~ -cjf wut-src-$(VERSION).tar.bz2 cafe include libraries share Makefile

View File

@ -0,0 +1,46 @@
#pragma once
#include <nsysccr/irda.h>
/**
* \defgroup irc_cdc
* \ingroup irc
* Low-Level InfraRed Connection
*
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
int32_t
__CCRCDCIRCConnect(int32_t drcIndex,
uint8_t *result,
uint16_t timeout,
CCRCDCIrdaBitrate bitrate,
uint32_t receiveSize,
uint8_t targetId,
CCRCDCIrdaConnectionType type);
int32_t
__CCRCDCIRCSend(int32_t drcIndex,
uint8_t *result,
uint32_t size,
uint32_t receiveSize,
void *data);
int32_t
__CCRCDCIRCReceive(int32_t drcIndex,
uint8_t *result,
uint16_t *receivedSize,
void *data);
int32_t
__CCRCDCIRCDisconnect(int32_t drcIndex,
uint8_t *result);
#ifdef __cplusplus
}
#endif
/** @} */

View File

@ -0,0 +1,178 @@
#pragma once
#include <wut.h>
#include <vpad/input.h>
#include <nsysccr/irda.h>
/**
* \defgroup irc_irc
* \ingroup irc
* High-Level InfraRed Connection
*
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
//! No error
IRC_RESULT_SUCCESS = 0,
//! The amount of data received from the other device does not match the requested size
IRC_RESULT_INVALID_RECEIVE_SIZE = 3,
//! The received packet is invalid
IRC_RESULT_INVALID_PACKET = 5,
//! No data was available to receive
IRC_RESULT_NO_DATA = 6,
//! Tried to connect while a connection was already established
IRC_RESULT_ALREADY_CONNECTED = 11,
//! IR is not available (Is the DRC TV menu open?)
IRC_IR_UNAVAILABLE = 12,
//! IRC is not initialized
IRC_RESULT_UNINITIALIZED = 13,
//! No connection
IRC_RESULT_NOT_CONNECTED = 13,
//! __CCRCDCIRCConnect failed
IRC_RESULT_CONNECT_FAILED = 15,
//! __CCRCDCIRCReceive failed
IRC_RESULT_RECEIVE_FAILED = 16,
//! __CCRCDCIRCSend failed
IRC_RESULT_SEND_FAILED = 17,
//! __CCRCDCIRCDisconnect failed
IRC_RESULT_DISCONNECT_FAILED = 18,
} IRCResult;
typedef void (*IRCConnectCallback)();
typedef void (*IRCReceiveCallback)(void *data, uint16_t size, IRCResult result);
/**
* Initializes the IRC library.
*
* \param channel
* The device to use for IR.
*
* \param targetId
* The targetId of the other device.
*
* \return
* \c TRUE on success.
*/
BOOL
IRCInit(VPADChan channel,
uint8_t targetId);
/**
* Starts an IR connection.
*
* \param channel
* The device to use for IR.
*
* \param timeout
* Timeout in ms for the connection.
*
* \param type
* The type of the connection.
*
* \param bitrate
* The bitrate of the connection.
* See \link CCRCDCIrdaBitrateEnum \endlink.
*
* \param receiveSize
* Amount of data which should be received from the other device.
*
* \param callback
* Callback which will be called once the connection is established (can be \c NULL).
*
* \return
* \c IRC_RESULT_SUCCESS on success.
*/
IRCResult
IRCConnect(VPADChan channel,
uint16_t timeout,
CCRCDCIrdaConnectionType type,
CCRCDCIrdaBitrate bitrate,
uint32_t receiveSize,
IRCConnectCallback callback);
/**
* Process the internal state.
* Should be called periodically.
*
* \param channel
* The device to use for IR.
*
* \return
* \c IRC_RESULT_SUCCESS on success.
*/
IRCResult
IRCProc(VPADChan channel);
/**
* Sends data over an IR connection.
*
* \param channel
* The device to use for IR.
*
* \param data
* The data which should be sent.
*
* \param dataSize
* The size of the data which should be sent.
*
* \param receiveSize
* Amount of data which should be received from the other device.
*
* \return
* \c IRC_RESULT_SUCCESS on success.
*/
IRCResult
IRCSend(VPADChan channel,
void *data,
uint32_t dataSize,
uint32_t receiveSize);
/**
* Checks if a connection is established.
*
* \param channel
* The device to use for IR.
*
* \return
* \c TRUE if connected.
*/
BOOL
IRCIsConnect(VPADChan channel);
/**
* Terminates the connection.
*
* \param channel
* The device to use for IR.
*
* \return
* \c IRC_RESULT_SUCCESS on success.
*/
IRCResult
IRCDisconnect(VPADChan channel);
/**
* Sets a callback for receiving data.
*
* \param channel
* The device to use for IR.
*
* \param receiveCallback
* The receive callback which should be set.
*
* \return
* The previously set callback.
*/
IRCReceiveCallback
IRCSetReceiveCallback(VPADChan channel,
IRCReceiveCallback receiveCallback);
#ifdef __cplusplus
}
#endif
/** @} */

150
libraries/libirc/src/cdc.c Normal file
View File

@ -0,0 +1,150 @@
#include "irc/cdc.h"
#include <nsysccr/irda.h>
#include <vpad/input.h>
#include <string.h>
static uint8_t sendBuffer[CCR_CDC_IRDA_DATA_TRANSFER_SIZE] __attribute__((aligned(0x40)));
static uint8_t replyBuffer[CCR_CDC_IRDA_DATA_TRANSFER_SIZE] __attribute__((aligned(0x40)));
int32_t
__CCRCDCIRCConnect(int32_t drcIndex,
uint8_t *result,
uint16_t timeout,
CCRCDCIrdaBitrate bitrate,
uint32_t receiveSize,
uint8_t targetId,
CCRCDCIrdaConnectionType type)
{
CCRCDCIrdaConnectRequest *request = (CCRCDCIrdaConnectRequest *) sendBuffer;
CCRCDCIrdaConnectReply *reply = (CCRCDCIrdaConnectReply *) replyBuffer;
if (receiveSize < 0x3e) {
request->receiveSize = receiveSize + sizeof(CCRCDCIrdaSmallPacketHeader) + 1; // +1 for crc8
} else {
request->receiveSize = receiveSize + sizeof(CCRCDCIrdaLargePacketHeader) + 1; // +1 for crc8
}
reply->result = 0;
request->command = CCR_IRDA_COMMAND_CONNECT;
request->timeout = timeout;
request->bitrate = bitrate;
request->targetId = targetId;
request->type = type;
int32_t ret = CCRCDCPerIrdaControl(CCR_CDC_DESTINATION_DRC0 + drcIndex,
request, sizeof(CCRCDCIrdaConnectRequest),
reply, sizeof(CCRCDCIrdaConnectReply));
if (ret == 0) {
*result = reply->result;
}
return ret;
}
int32_t
__CCRCDCIRCSend(int32_t drcIndex,
uint8_t *result,
uint32_t size,
uint32_t receiveSize,
void *data)
{
CCRCDCIrdaSendRequest *request = (CCRCDCIrdaSendRequest *) sendBuffer;
CCRCDCIrdaSendReply *reply = (CCRCDCIrdaSendReply *) replyBuffer;
uint16_t totalReceiveSize;
if (receiveSize < 0x3e) {
totalReceiveSize = receiveSize + sizeof(CCRCDCIrdaSmallPacketHeader) + 1; // +1 for crc8
} else {
totalReceiveSize = receiveSize + sizeof(CCRCDCIrdaLargePacketHeader) + 1; // +1 for crc8
}
void *packetData;
uint32_t packetSize;
if (size < 0x3e) {
CCRCDCIrdaSmallPacketHeader *header = (CCRCDCIrdaSmallPacketHeader *) request->data;
header->sessionId = 0;
header->receiveSize = totalReceiveSize;
header->magic = 0xa5;
header->unk = 0;
header->isLarge = 0;
header->dataSize = size + 2; // +2 for requestSize
packetData = header + 1;
packetSize = size + sizeof(CCRCDCIrdaSmallPacketHeader) + 1; // +1 for crc8
} else {
CCRCDCIrdaLargePacketHeader *header = (CCRCDCIrdaLargePacketHeader *) request->data;
header->sessionId = 0;
header->receiveSize = totalReceiveSize;
header->magic = 0xa5;
header->unk = 0;
header->isLarge = 1;
header->dataSize = size + 2; // +2 for requestSize
packetData = header + 1;
packetSize = size + sizeof(CCRCDCIrdaLargePacketHeader) + 1; // +1 for crc8
}
// Copy the data into the packet
memcpy(packetData, data, size);
// Fill the rest of the buffer with 0
uint32_t spaceLeft = CCR_CDC_IRDA_DATA_TRANSFER_SIZE - sizeof(CCRCDCIrdaSendRequest) - packetSize;
if (spaceLeft > 0) {
memset((uint8_t *) packetData + size, 0, spaceLeft);
}
request->command = CCR_IRDA_COMMAND_SEND;
request->size = packetSize;
reply->result = 0;
int32_t ret = CCRCDCPerIrdaControl(CCR_CDC_DESTINATION_DRC0 + drcIndex,
request, CCR_CDC_IRDA_DATA_TRANSFER_SIZE,
reply, sizeof(CCRCDCIrdaSendReply));
if (ret == 0) {
*result = reply->result;
}
return ret;
}
int32_t
__CCRCDCIRCReceive(int32_t drcIndex,
uint8_t *result,
uint16_t *receivedSize,
void *data)
{
CCRCDCIrdaReceiveRequest *request = (CCRCDCIrdaReceiveRequest *) sendBuffer;
CCRCDCIrdaReceiveReply *reply = (CCRCDCIrdaReceiveReply *) data;
request->command = CCR_IRDA_COMMAND_RECEIVE;
memset(data, 0, CCR_CDC_IRDA_DATA_TRANSFER_SIZE);
int32_t ret = CCRCDCPerIrdaControl(CCR_CDC_DESTINATION_DRC0 + drcIndex,
request, sizeof(CCRCDCIrdaReceiveRequest),
reply, CCR_CDC_IRDA_DATA_TRANSFER_SIZE);
if (ret == 0) {
*result = reply->result;
*receivedSize = reply->size + 1; // +1 for crc8
}
VPADBASEClearIRCEvent((VPADChan) drcIndex);
return ret;
}
int32_t
__CCRCDCIRCDisconnect(int32_t drcIndex,
uint8_t *result)
{
CCRCDCIrdaDisconnectRequest *request = (CCRCDCIrdaDisconnectRequest *) sendBuffer;
CCRCDCIrdaDisconnectReply *reply = (CCRCDCIrdaDisconnectReply *) replyBuffer;
reply->result = 0;
request->command = CCR_IRDA_COMMAND_DISCONNECT;
int32_t ret = CCRCDCPerIrdaControl(CCR_CDC_DESTINATION_DRC0 + drcIndex,
request, sizeof(CCRCDCIrdaDisconnectRequest),
reply, sizeof(CCRCDCIrdaDisconnectReply));
if (ret == 0) {
*result = reply->result;
}
return ret;
}

266
libraries/libirc/src/irc.c Normal file
View File

@ -0,0 +1,266 @@
#include "irc/irc.h"
#include "irc/cdc.h"
#include <vpad/input.h>
typedef enum {
IRC_STATE_UNINITIALIZED,
IRC_STATE_DISCONNECTED,
IRC_STATE_WAITING,
IRC_STATE_CONNECTED,
} IRCState;
static BOOL isInitialized = FALSE;
static IRCState state = IRC_STATE_UNINITIALIZED;
static CCRCDCIrdaConnectionType connectionType;
static uint8_t targetId;
static uint8_t sessionId;
static IRCReceiveCallback receiveCallback;
static IRCConnectCallback connectCallback;
static uint8_t receiveBuffer[CCR_CDC_IRDA_DATA_TRANSFER_SIZE] __attribute__((aligned(0x40)));
BOOL
IRCInit(VPADChan channel,
uint8_t _targetId)
{
if (!VPADBASEIsInit()) {
return FALSE;
}
if (isInitialized) {
return FALSE;
}
isInitialized = TRUE;
state = IRC_STATE_DISCONNECTED;
connectionType = CCR_IRDA_CONNECTION_WAIT;
targetId = _targetId;
receiveCallback = NULL;
connectCallback = NULL;
return TRUE;
}
IRCResult
IRCConnect(VPADChan channel,
uint16_t timeout,
CCRCDCIrdaConnectionType type,
CCRCDCIrdaBitrate bitrate,
uint32_t receiveSize,
IRCConnectCallback callback)
{
if (state == IRC_STATE_UNINITIALIZED) {
return IRC_RESULT_UNINITIALIZED;
}
// Can't connected if we already have an established connection
if (state == IRC_STATE_CONNECTED) {
return IRC_RESULT_ALREADY_CONNECTED;
}
// IR is blocked if the TV menu is opened
if (VPADGetTVMenuStatus(channel)) {
return IRC_IR_UNAVAILABLE;
}
connectionType = type;
uint8_t result;
if (__CCRCDCIRCConnect(channel,
&result,
timeout,
bitrate,
receiveSize,
targetId,
type) != 0) {
return IRC_RESULT_CONNECT_FAILED;
}
if (result != 0) {
return result;
}
/* This causes a race between the connected bit changing in IRCStatus
and IRCProc changing the state back to disconnected.
Let's not do this. */
//if (connectionType == CCR_IRDA_CONNECTION_ANY) {
// // If the type is any, we don't need to wait for a connection
// state = IRC_STATE_CONNECTED;
//} else {
state = IRC_STATE_WAITING;
connectCallback = callback;
//}
return IRC_RESULT_SUCCESS;
}
IRCResult
IRCProc(VPADChan channel)
{
if (!isInitialized) {
return IRC_RESULT_UNINITIALIZED;
}
uint32_t status = VPADBASEGetIRCStatus(channel);
if (status & VPAD_IRC_STATUS_FLAG_CONNECTED) {
// If we were waiting on a connection, we're now connected
if (state == IRC_STATE_WAITING) {
if (connectCallback) {
connectCallback();
}
state = IRC_STATE_CONNECTED;
return IRC_RESULT_SUCCESS;
}
} else {
// If we were connected, the connection is now broken
if (state == IRC_STATE_CONNECTED) {
state = IRC_STATE_DISCONNECTED;
}
}
if (!(status & VPAD_IRC_STATUS_FLAG_HAS_DATA)) {
// IR is blocked if the TV menu is opened
if (VPADGetTVMenuStatus(channel)) {
state = IRC_STATE_DISCONNECTED;
return IRC_IR_UNAVAILABLE;
}
return IRC_RESULT_SUCCESS;
}
// If we're here, there is pending data which can be received
uint8_t result;
uint16_t receivedSize;
if (__CCRCDCIRCReceive(channel,
&result,
&receivedSize,
receiveBuffer) != 0) {
return IRC_RESULT_RECEIVE_FAILED;
}
if (state == IRC_STATE_UNINITIALIZED) {
return IRC_RESULT_SUCCESS;
}
if (state == IRC_STATE_DISCONNECTED) {
if (receiveCallback) {
receiveCallback(NULL, 0, result);
}
return result;
}
// If we're not connected yet, but are already receiving data, terminate connection
if (state == IRC_STATE_WAITING) {
if (receiveCallback) {
receiveCallback(NULL, 0, result);
}
state = IRC_STATE_DISCONNECTED;
return result;
}
if (!receiveCallback) {
return IRC_RESULT_SUCCESS;
}
if (result != 0) {
receiveCallback(NULL, 0, result);
return result;
}
CCRCDCIrdaSmallPacketHeader *smallHeader = (CCRCDCIrdaSmallPacketHeader *) (receiveBuffer + sizeof(CCRCDCIrdaReceiveReply));
CCRCDCIrdaLargePacketHeader *largeHeader = (CCRCDCIrdaLargePacketHeader *) (receiveBuffer + sizeof(CCRCDCIrdaReceiveReply));
sessionId = smallHeader->sessionId;
void *packetData;
uint16_t packetSize;
if (smallHeader->isLarge) {
packetData = largeHeader + 1;
packetSize = largeHeader->dataSize;
} else {
packetData = smallHeader + 1;
packetSize = smallHeader->dataSize;
}
// Don't count the request size
packetSize -= 2;
if (packetSize > 0x1fe) {
receiveCallback(NULL, 0, IRC_RESULT_INVALID_PACKET);
return IRC_RESULT_INVALID_PACKET;
}
receiveCallback(packetData, packetSize, IRC_RESULT_SUCCESS);
return IRC_RESULT_SUCCESS;
}
IRCResult
IRCSend(VPADChan channel,
void *data,
uint32_t dataSize,
uint32_t receiveSize)
{
if (!isInitialized) {
return IRC_RESULT_UNINITIALIZED;
}
// IR is blocked if the TV menu is opened
if (VPADGetTVMenuStatus(channel)) {
return IRC_IR_UNAVAILABLE;
}
if (state != IRC_STATE_CONNECTED) {
return IRC_RESULT_NOT_CONNECTED;
}
uint8_t result;
if (__CCRCDCIRCSend(channel,
&result,
dataSize,
receiveSize,
data) != 0) {
return IRC_RESULT_SEND_FAILED;
}
return result;
}
BOOL
IRCIsConnect(VPADChan channel)
{
return state == IRC_STATE_CONNECTED;
}
IRCResult
IRCDisconnect(VPADChan channel)
{
if (!isInitialized) {
return IRC_RESULT_UNINITIALIZED;
}
// IR is blocked if the TV menu is opened
if (VPADGetTVMenuStatus(channel)) {
return IRC_IR_UNAVAILABLE;
}
uint8_t result;
if (__CCRCDCIRCDisconnect(channel,
&result) != 0) {
return IRC_RESULT_DISCONNECT_FAILED;
}
state = IRC_STATE_DISCONNECTED;
return result;
}
IRCReceiveCallback
IRCSetReceiveCallback(VPADChan channel,
IRCReceiveCallback _receiveCallback)
{
IRCReceiveCallback prev = receiveCallback;
receiveCallback = _receiveCallback;
return prev;
}