libnx/nx/source/services/uart.c
2020-09-12 18:53:30 -04:00

259 lines
10 KiB
C

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