Added uart.

This commit is contained in:
yellows8 2020-09-12 18:53:30 -04:00
parent ab59dfb5bb
commit f01fb21da5
No known key found for this signature in database
GPG Key ID: 0AF90DA3F1E60E43
3 changed files with 502 additions and 0 deletions

View File

@ -60,6 +60,7 @@ extern "C" {
#include "switch/services/lbl.h"
#include "switch/services/i2c.h"
#include "switch/services/gpio.h"
#include "switch/services/uart.h"
#include "switch/services/bpc.h"
#include "switch/services/pcv.h"
#include "switch/services/clkrst.h"

View File

@ -0,0 +1,243 @@
/**
* @file uart.h
* @brief UART service IPC wrapper.
* @author yellows8
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../kernel/event.h"
#include "../sf/service.h"
/// UartPort
typedef enum {
UartPort_Bluetooth = 1, ///< Bluetooth
UartPort_JoyConR = 2, ///< Joy-Con(R)
UartPort_JoyConL = 3, ///< Joy-Con(L)
UartPort_MCU = 4, ///< MCU
} UartPort;
/// UartPortForDev
typedef enum {
UartPortForDev_JoyConR = 1, ///< Joy-Con(R)
UartPortForDev_JoyConL = 2, ///< Joy-Con(L)
UartPortForDev_Bluetooth = 3, ///< Bluetooth
} UartPortForDev;
/// FlowControlMode
typedef enum {
UartFlowControlMode_None = 0, ///< None
UartFlowControlMode_Hardware = 1, ///< Hardware
} UartFlowControlMode;
/// PortEventType
typedef enum {
UartPortEventType_SendBufferEmpty = 0, ///< SendBufferEmpty
UartPortEventType_SendBufferReady = 1, ///< SendBufferReady
UartPortEventType_ReceiveBufferReady = 2, ///< ReceiveBufferReady
UartPortEventType_ReceiveEnd = 3, ///< ReceiveEnd
} UartPortEventType;
/// PortSession
typedef struct {
Service s; ///< IPortSession
} UartPortSession;
/// Initialize uart.
Result uartInitialize(void);
/// Exit uart.
void uartExit(void);
/// Gets the Service object for the actual uart service session.
Service* uartGetServiceSession(void);
/**
* @brief HasPort
* @param[in] port \ref UartPort
* @param[out] out Output success flag.
*/
Result uartHasPort(UartPort port, bool *out);
/**
* @brief HasPortForDev
* @param[in] port \ref UartPortForDev
* @param[out] out Output success flag.
*/
Result uartHasPortForDev(UartPortForDev port, bool *out);
/**
* @brief IsSupportedBaudRate
* @param[in] port \ref UartPort
* @param[in] baud_rate BaudRate
* @param[out] out Output success flag.
*/
Result uartIsSupportedBaudRate(UartPort port, u32 baud_rate, bool *out);
/**
* @brief IsSupportedBaudRateForDev
* @param[in] port \ref UartPortForDev
* @param[in] baud_rate BaudRate
* @param[out] out Output success flag.
*/
Result uartIsSupportedBaudRateForDev(UartPortForDev port, u32 baud_rate, bool *out);
/**
* @brief IsSupportedFlowControlMode
* @param[in] port \ref UartPort
* @param[in] flow_control_mode \ref UartFlowControlMode
* @param[out] out Output success flag.
*/
Result uartIsSupportedFlowControlMode(UartPort port, UartFlowControlMode flow_control_mode, bool *out);
/**
* @brief IsSupportedFlowControlModeForDev
* @param[in] port \ref UartPortForDev
* @param[in] flow_control_mode \ref UartFlowControlMode
* @param[out] out Output success flag.
*/
Result uartIsSupportedFlowControlModeForDev(UartPortForDev port, UartFlowControlMode flow_control_mode, bool *out);
/**
* @brief Creates an \ref UartPortSession.
* @note Use \ref uartPortSessionOpenPort or \ref uartPortSessionOpenPortForDev before using any other cmds.
* @param[out] s \ref UartPortSession
*/
Result uartCreatePortSession(UartPortSession *s);
/**
* @brief IsSupportedPortEvent
* @param[in] port \ref UartPort
* @param[in] port_event_type \ref UartPortEventType
* @param[out] out Output success flag.
*/
Result uartIsSupportedPortEvent(UartPort port, UartPortEventType port_event_type, bool *out);
/**
* @brief IsSupportedPortEventForDev
* @param[in] port \ref UartPortForDev
* @param[in] port_event_type \ref UartPortEventType
* @param[out] out Output success flag.
*/
Result uartIsSupportedPortEventForDev(UartPortForDev port, UartPortEventType port_event_type, bool *out);
/**
* @brief IsSupportedDeviceVariation
* @note Only available on [7.0.0+].
* @param[in] port \ref UartPort
* @param[in] device_variation DeviceVariation
* @param[out] out Output success flag.
*/
Result uartIsSupportedDeviceVariation(UartPort port, u32 device_variation, bool *out);
/**
* @brief IsSupportedDeviceVariationForDev
* @note Only available on [7.0.0+].
* @param[in] port \ref UartPortForDev
* @param[in] device_variation DeviceVariation
* @param[out] out Output success flag.
*/
Result uartIsSupportedDeviceVariationForDev(UartPortForDev port, u32 device_variation, bool *out);
///@name IPortSession
///@{
/**
* @brief Close an \ref UartPortSession.
* @param s \ref UartPortSession
*/
void uartPortSessionClose(UartPortSession* s);
/**
* @brief OpenPort
* @note This is not usable when the specified \ref UartPort is already being used.
* @param s \ref UartPortSession
* @param[out] out Output success flag.
* @param[in] port \ref UartPort
* @param[in] baud_rate BaudRate
* @param[in] flow_control_mode \ref UartFlowControlMode
* @param[in] device_variation [7.0.0+] DeviceVariation
* @param[in] is_invert_tx [6.0.0+] IsInvertTx
* @param[in] is_invert_rx [6.0.0+] IsInvertRx
* @param[in] is_invert_rts [6.0.0+] IsInvertRts
* @param[in] is_invert_cts [6.0.0+] IsInvertCts
* @param[in] send_buffer Send buffer, must be 0x1000-byte aligned.
* @param[in] send_buffer_length Send buffer size, must be 0x1000-byte aligned.
* @param[in] receive_buffer Receive buffer, must be 0x1000-byte aligned.
* @param[in] receive_buffer_length Receive buffer size, must be 0x1000-byte aligned.
*/
Result uartPortSessionOpenPort(UartPortSession* s, bool *out, UartPort port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, void* send_buffer, u64 send_buffer_length, void* receive_buffer, u64 receive_buffer_length);
/**
* @brief OpenPortForDev
* @note See the notes for \ref uartPortSessionOpenPort.
* @param s \ref UartPortSession
* @param[out] out Output success flag.
* @param[in] port \ref UartPortForDev
* @param[in] baud_rate BaudRate
* @param[in] flow_control_mode \ref UartFlowControlMode
* @param[in] device_variation [7.0.0+] DeviceVariation
* @param[in] is_invert_tx [6.0.0+] IsInvertTx
* @param[in] is_invert_rx [6.0.0+] IsInvertRx
* @param[in] is_invert_rts [6.0.0+] IsInvertRts
* @param[in] is_invert_cts [6.0.0+] IsInvertCts
* @param[in] send_buffer Send buffer, must be 0x1000-byte aligned.
* @param[in] send_buffer_length Send buffer size, must be 0x1000-byte aligned.
* @param[in] receive_buffer Receive buffer, must be 0x1000-byte aligned.
* @param[in] receive_buffer_length Receive buffer size, must be 0x1000-byte aligned.
*/
Result uartPortSessionOpenPortForDev(UartPortSession* s, bool *out, UartPortForDev port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, void* send_buffer, u64 send_buffer_length, void* receive_buffer, u64 receive_buffer_length);
/**
* @brief GetWritableLength
* @param s \ref UartPortSession
* @param[out] out Output WritableLength.
*/
Result uartPortSessionGetWritableLength(UartPortSession* s, u64 *out);
/**
* @brief Send
* @param s \ref UartPortSession
* @param[in] in_data Input data buffer.
* @param[in] size Input data buffer size.
* @param[out] out Output size.
*/
Result uartPortSessionSend(UartPortSession* s, const void* in_data, size_t size, u64 *out);
/**
* @brief GetReadableLength
* @param s \ref UartPortSession
* @param[out] out Output ReadableLength.
*/
Result uartPortSessionGetReadableLength(UartPortSession* s, u64 *out);
/**
* @brief Receive
* @param s \ref UartPortSession
* @param[out] out_data Output data buffer.
* @param[in] size Output data buffer size.
* @param[out] out Output size.
*/
Result uartPortSessionReceive(UartPortSession* s, void* out_data, size_t size, u64 *out);
/**
* @brief BindPortEvent
* @note The Event must be closed by the user after using \ref uartPortSessionUnbindPortEvent.
* @param s \ref UartPortSession
* @param[in] port_event_type \ref UartPortEventType
* @param[in] threshold Threshold
* @param[out] out Output success flag.
* @param[out] out_event Output Event with autoclear=false.
*/
Result uartPortSessionBindPortEvent(UartPortSession* s, UartPortEventType port_event_type, s64 threshold, bool *out, Event *out_event);
/**
* @brief UnbindPortEvent
* @param s \ref UartPortSession
* @param[in] port_event_type \ref UartPortEventType
* @param[out] out Output success flag.
*/
Result uartPortSessionUnbindPortEvent(UartPortSession* s, UartPortEventType port_event_type, bool *out);
///@}

258
nx/source/services/uart.c Normal file
View File

@ -0,0 +1,258 @@
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <string.h>
#include "service_guard.h"
#include "runtime/hosversion.h"
#include "kernel/tmem.h"
#include "services/uart.h"
static Service g_uartSrv;
NX_GENERATE_SERVICE_GUARD(uart);
Result _uartInitialize(void) {
return smGetService(&g_uartSrv, "uart");
}
void _uartCleanup(void) {
serviceClose(&g_uartSrv);
}
Service* uartGetServiceSession(void) {
return &g_uartSrv;
}
static Result _uartCmdNoInOutU64(Service* srv, u64 *out, u32 cmd_id) {
return serviceDispatchOut(srv, cmd_id, *out);
}
static Result _uartCmdInU32OutBool(Service* srv, u32 inval, bool *out, u32 cmd_id) {
u8 tmp=0;
Result rc = serviceDispatchInOut(srv, cmd_id, inval, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
static Result _uartCmdInTwoU32sOutBool(Service* srv, u32 inval0, u32 inval1, bool *out, u32 cmd_id) {
const struct {
u32 inval0;
u32 inval1;
} in = { inval0, inval1 };
u8 tmp=0;
Result rc = serviceDispatchInOut(srv, cmd_id, in, tmp);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
Result uartHasPort(UartPort port, bool *out) {
return _uartCmdInU32OutBool(&g_uartSrv, port, out, 0);
}
Result uartHasPortForDev(UartPortForDev port, bool *out) {
return _uartCmdInU32OutBool(&g_uartSrv, port, out, 1);
}
Result uartIsSupportedBaudRate(UartPort port, u32 baud_rate, bool *out) {
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, baud_rate, out, 2);
}
Result uartIsSupportedBaudRateForDev(UartPortForDev port, u32 baud_rate, bool *out) {
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, baud_rate, out, 3);
}
Result uartIsSupportedFlowControlMode(UartPort port, UartFlowControlMode flow_control_mode, bool *out) {
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, flow_control_mode, out, 4);
}
Result uartIsSupportedFlowControlModeForDev(UartPortForDev port, UartFlowControlMode flow_control_mode, bool *out) {
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, flow_control_mode, out, 5);
}
Result uartCreatePortSession(UartPortSession *s) {
return serviceDispatch(&g_uartSrv, 6,
.out_num_objects = 1,
.out_objects = &s->s,
);
}
Result uartIsSupportedPortEvent(UartPort port, UartPortEventType port_event_type, bool *out) {
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, port_event_type, out, 7);
}
Result uartIsSupportedPortEventForDev(UartPortForDev port, UartPortEventType port_event_type, bool *out) {
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, port_event_type, out, 8);
}
Result uartIsSupportedDeviceVariation(UartPort port, u32 device_variation, bool *out) {
if (hosversionBefore(7,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, device_variation, out, 9);
}
Result uartIsSupportedDeviceVariationForDev(UartPortForDev port, u32 device_variation, bool *out) {
if (hosversionBefore(7,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, device_variation, out, 10);
}
// IPortSession
void uartPortSessionClose(UartPortSession* s) {
serviceClose(&s->s);
}
/// pre-6.0.0
static Result _uartPortSessionOpenPort_v1(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length, u32 cmd_id) {
const struct {
u32 port;
u32 baud_rate;
u32 flow_control_mode;
u32 pad;
u64 send_buffer_length;
u64 receive_buffer_length;
} in = { port, baud_rate, flow_control_mode, 0, send_buffer_length, receive_buffer_length };
u8 tmp=0;
Result rc = serviceDispatchInOut(&s->s, cmd_id, in, tmp,
.in_num_handles = 2,
.in_handles = { send_handle, receive_handle },
);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
/// 6.x
static Result _uartPortSessionOpenPort_v6(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length, u32 cmd_id) {
const struct {
u8 is_invert_tx;
u8 is_invert_rx;
u8 is_invert_rts;
u8 is_invert_cts;
u32 port;
u32 baud_rate;
u32 flow_control_mode;
u64 send_buffer_length;
u64 receive_buffer_length;
} in = { is_invert_tx!=0, is_invert_rx!=0, is_invert_rts!=0, is_invert_cts!=0, port, baud_rate, flow_control_mode, send_buffer_length, receive_buffer_length };
u8 tmp=0;
Result rc = serviceDispatchInOut(&s->s, cmd_id, in, tmp,
.in_num_handles = 2,
.in_handles = { send_handle, receive_handle },
);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
/// [7.0.0+]
static Result _uartPortSessionOpenPort_v7(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length, u32 cmd_id) {
const struct {
u8 is_invert_tx;
u8 is_invert_rx;
u8 is_invert_rts;
u8 is_invert_cts;
u32 port;
u32 baud_rate;
u32 flow_control_mode;
u32 device_variation;
u32 pad;
u64 send_buffer_length;
u64 receive_buffer_length;
} in = { is_invert_tx!=0, is_invert_rx!=0, is_invert_rts!=0, is_invert_cts!=0, port, baud_rate, flow_control_mode, device_variation, 0, send_buffer_length, receive_buffer_length };
u8 tmp=0;
Result rc = serviceDispatchInOut(&s->s, cmd_id, in, tmp,
.in_num_handles = 2,
.in_handles = { send_handle, receive_handle },
);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
return rc;
}
static Result _uartPortSessionOpenPort(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length, u32 cmd_id) {
if (hosversionBefore(6,0,0))
return _uartPortSessionOpenPort_v1(s, out, port, baud_rate, flow_control_mode, send_handle, receive_handle, send_buffer_length, receive_buffer_length, cmd_id);
else if (hosversionBefore(7,0,0)) // 6.x
return _uartPortSessionOpenPort_v6(s, out, port, baud_rate, flow_control_mode, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length, cmd_id);
else // [7.0.0+]
return _uartPortSessionOpenPort_v7(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length, cmd_id);
}
Result uartPortSessionOpenPort(UartPortSession* s, bool *out, UartPort port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, void* send_buffer, u64 send_buffer_length, void* receive_buffer, u64 receive_buffer_length) {
Result rc=0;
TransferMemory send_tmem={0};
TransferMemory receive_tmem={0};
rc = tmemCreateFromMemory(&send_tmem, send_buffer, send_buffer_length, Perm_R | Perm_W);
if (R_SUCCEEDED(rc)) rc = tmemCreateFromMemory(&receive_tmem, receive_buffer, send_buffer_length, Perm_R | Perm_W);
if (R_SUCCEEDED(rc)) rc = _uartPortSessionOpenPort(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_tmem.handle, receive_tmem.handle, send_buffer_length, receive_buffer_length, 0);
tmemClose(&send_tmem);
tmemClose(&receive_tmem);
return rc;
}
Result uartPortSessionOpenPortForDev(UartPortSession* s, bool *out, UartPortForDev port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, void* send_buffer, u64 send_buffer_length, void* receive_buffer, u64 receive_buffer_length) {
Result rc=0;
TransferMemory send_tmem={0};
TransferMemory receive_tmem={0};
rc = tmemCreateFromMemory(&send_tmem, send_buffer, send_buffer_length, Perm_R | Perm_W);
if (R_SUCCEEDED(rc)) rc = tmemCreateFromMemory(&receive_tmem, receive_buffer, send_buffer_length, Perm_R | Perm_W);
if (R_SUCCEEDED(rc)) rc = _uartPortSessionOpenPort(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_tmem.handle, receive_tmem.handle, send_buffer_length, receive_buffer_length, 1);
tmemClose(&send_tmem);
tmemClose(&receive_tmem);
return rc;
}
Result uartPortSessionGetWritableLength(UartPortSession* s, u64 *out) {
return _uartCmdNoInOutU64(&s->s, out, 2);
}
Result uartPortSessionSend(UartPortSession* s, const void* in_data, size_t size, u64 *out_size) {
return serviceDispatchOut(&s->s, 3, *out_size,
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
.buffers = { { in_data, size } },
);
}
Result uartPortSessionGetReadableLength(UartPortSession* s, u64 *out) {
return _uartCmdNoInOutU64(&s->s, out, 4);
}
Result uartPortSessionReceive(UartPortSession* s, void* out_data, size_t size, u64 *out_size) {
return serviceDispatchOut(&s->s, 5, *out_size,
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = { { out_data, size } },
);
}
Result uartPortSessionBindPortEvent(UartPortSession* s, UartPortEventType port_event_type, s64 threshold, bool *out, Event *out_event) {
const struct {
u32 port_event_type;
u32 pad;
s64 threshold;
} in = { port_event_type, 0, threshold };
u8 tmp=0;
Handle tmp_handle=0;
Result rc = serviceDispatchInOut(&s->s, 6, in, tmp,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = &tmp_handle,
);
if (R_SUCCEEDED(rc) && out) *out = tmp & 1;
if (R_SUCCEEDED(rc) && out_event) eventLoadRemote(out_event, tmp_handle, false);
return rc;
}
Result uartPortSessionUnbindPortEvent(UartPortSession* s, UartPortEventType port_event_type, bool *out) {
return _uartCmdInU32OutBool(&s->s, port_event_type, out, 7);
}