summaryrefslogtreecommitdiff
path: root/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/sysdeps/managarm/generic/ioctl.cpp
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/sysdeps/managarm/generic/ioctl.cpp')
-rw-r--r--lib/mlibc/sysdeps/managarm/generic/ioctl.cpp708
1 files changed, 708 insertions, 0 deletions
diff --git a/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp b/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp
new file mode 100644
index 0000000..384c09e
--- /dev/null
+++ b/lib/mlibc/sysdeps/managarm/generic/ioctl.cpp
@@ -0,0 +1,708 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/cdrom.h>
+#include <linux/input.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <sys/ioctl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/all-sysdeps.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-pipe.hpp>
+
+#include <fs.frigg_bragi.hpp>
+#include <posix.frigg_bragi.hpp>
+
+namespace mlibc {
+
+static constexpr bool logIoctls = false;
+
+int ioctl_drm(int fd, unsigned long request, void *arg, int *result, HelHandle handle);
+
+int sys_ioctl(int fd, unsigned long request, void *arg, int *result) {
+ if(logIoctls)
+ mlibc::infoLogger() << "mlibc: ioctl with"
+ << " type: 0x" << frg::hex_fmt(_IOC_TYPE(request))
+ << ", number: 0x" << frg::hex_fmt(_IOC_NR(request))
+ << " (raw request: " << frg::hex_fmt(request) << ")"
+ << " on fd " << fd << frg::endlog;
+
+ SignalGuard sguard;
+ auto handle = getHandleForFd(fd);
+ if(!handle)
+ return EBADF;
+
+ if(_IOC_TYPE(request) == 'd') {
+ return ioctl_drm(fd, request, arg, result, handle);
+ }
+
+ managarm::fs::IoctlRequest<MemoryAllocator> ioctl_req(getSysdepsAllocator());
+
+ switch(request) {
+ case FIONBIO: {
+ auto mode = reinterpret_cast<int *>(arg);
+ int flags = fcntl(fd, F_GETFL, 0);
+ if(*mode) {
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ }else{
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ return 0;
+ }
+ case FIONREAD: {
+ auto argp = reinterpret_cast<int *>(arg);
+
+ auto handle = getHandleForFd(fd);
+ if (!handle)
+ return EBADF;
+
+ if(!argp)
+ return EINVAL;
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(FIONREAD);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_CONNECTED) {
+ return ENOTCONN;
+ } else {
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *argp = resp.fionread_count();
+
+ return 0;
+ }
+ }
+ case FIOCLEX: {
+ managarm::posix::IoctlFioclexRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_fd(fd);
+
+ auto [offer, sendReq, recvResp] = exchangeMsgsSync(
+ getPosixLane(),
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(sendReq.error());
+ if(recvResp.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(recvResp.error());
+
+ managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recvResp.data(), recvResp.length());
+ __ensure(resp.error() == managarm::posix::Errors::SUCCESS);
+ return 0;
+ }
+ case TCGETS: {
+ auto param = reinterpret_cast<struct termios *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_attrs] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(param, sizeof(struct termios))
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_attrs.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ __ensure(recv_attrs.actualLength() == sizeof(struct termios));
+ *result = resp.result();
+ return 0;
+ }
+ case TCSETS: {
+ auto param = reinterpret_cast<struct termios *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, send_attrs, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(param, sizeof(struct termios)),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(send_attrs.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ if(result)
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCSCTTY: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline())
+ );
+
+ HEL_CHECK(offer.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_ioctl_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }else if(resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
+ return EPERM;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGWINSZ: {
+ auto param = reinterpret_cast<struct winsize *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::ILLEGAL_OPERATION_TARGET)
+ return EINVAL;
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ param->ws_col = resp.pts_width();
+ param->ws_row = resp.pts_height();
+ param->ws_xpixel = resp.pts_pixel_width();
+ param->ws_ypixel = resp.pts_pixel_height();
+ return 0;
+ }
+ case TIOCSWINSZ: {
+ auto param = reinterpret_cast<const struct winsize *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_pts_width(param->ws_col);
+ req.set_pts_height(param->ws_row);
+ req.set_pts_pixel_width(param->ws_xpixel);
+ req.set_pts_pixel_height(param->ws_ypixel);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGPTN: {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline())
+ );
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *param = resp.pts_index();
+ if(result)
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGPGRP: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error())
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
+ return ENOTTY;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ *static_cast<int *>(arg) = resp.pid();
+ return 0;
+ }
+ case TIOCSPGRP: {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_pgid(*param);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
+ return EPERM;
+ } else if(resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
+ return EINVAL;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ case TIOCGSID: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::imbueCredentials(),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ if(send_ioctl_req.error())
+ return EINVAL;
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error())
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ if(imbue_creds.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(imbue_creds.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
+ return ENOTTY;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ *static_cast<int *>(arg) = resp.pid();
+ return 0;
+ }
+ case CDROM_GET_CAPABILITY: {
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+
+ frg::string<MemoryAllocator> ser(getSysdepsAllocator());
+ req.SerializeToString(&ser);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] =
+ exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBuffer(ser.data(), ser.size()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ if(send_ioctl_req.error())
+ return EINVAL;
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error())
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ if(resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
+ return ENOTTY;
+ }
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }
+ } // end of switch()
+
+
+ if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGVERSION)) {
+ *reinterpret_cast<int *>(arg) = 0x010001; // should be EV_VERSION
+ *result = 0;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGID)) {
+ memset(arg, 0, sizeof(struct input_id));
+ *result = 0;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGNAME(0))) {
+ const char *s = "Managarm generic evdev";
+ auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
+ memcpy(arg, s, chunk);
+ *result = chunk;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGPHYS(0))) {
+ // Returns the sysfs path of the device.
+ const char *s = "input0";
+ auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
+ memcpy(arg, s, chunk);
+ *result = chunk;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGUNIQ(0))) {
+ // Returns a unique ID for the device.
+ const char *s = "0";
+ auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
+ memcpy(arg, s, chunk);
+ *result = chunk;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGPROP(0))) {
+ // Returns a bitmask of properties of the device.
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGKEY(0))) {
+ // Returns the current key state.
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGLED(0))) {
+ // Returns the current LED state.
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCGSW(0))) {
+ auto size = _IOC_SIZE(request);
+ memset(arg, 0, size);
+ *result = size;
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) >= _IOC_NR(EVIOCGBIT(0, 0))
+ && _IOC_NR(request) <= _IOC_NR(EVIOCGBIT(EV_MAX, 0))) {
+ // Returns a bitmask of capabilities of the device.
+ // If type is zero, return a mask of supported types.
+ // As EV_SYN is zero, this implies that it is impossible
+ // to get the mask of supported synthetic events.
+ auto type = _IOC_NR(request) - _IOC_NR(EVIOCGBIT(0, 0));
+ if(!type) {
+ // TODO: Check with the Linux ABI if we have to do this.
+ memset(arg, 0, _IOC_SIZE(request));
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(EVIOCGBIT(0, 0));
+ req.set_size(_IOC_SIZE(request));
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(arg, _IOC_SIZE(request)))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_data.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = recv_data.actualLength();
+ return 0;
+ }else{
+ // TODO: Check with the Linux ABI if we have to do this.
+ memset(arg, 0, _IOC_SIZE(request));
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(EVIOCGBIT(1, 0));
+ req.set_input_type(type);
+ req.set_size(_IOC_SIZE(request));
+
+ auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
+ handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline(),
+ helix_ng::recvBuffer(arg, _IOC_SIZE(request)))
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+ HEL_CHECK(recv_data.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = recv_data.actualLength();
+ return 0;
+ }
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) == _IOC_NR(EVIOCSCLOCKID)) {
+ auto param = reinterpret_cast<int *>(arg);
+
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(request);
+ req.set_input_clock(*param);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+ *result = resp.result();
+ return 0;
+ }else if(_IOC_TYPE(request) == 'E'
+ && _IOC_NR(request) >= _IOC_NR(EVIOCGABS(0))
+ && _IOC_NR(request) <= _IOC_NR(EVIOCGABS(ABS_MAX))) {
+ auto param = reinterpret_cast<struct input_absinfo *>(arg);
+
+ auto type = _IOC_NR(request) - _IOC_NR(EVIOCGABS(0));
+ managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
+ req.set_command(EVIOCGABS(0));
+ req.set_input_type(type);
+
+ auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(handle,
+ helix_ng::offer(
+ helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
+ helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
+ helix_ng::recvInline()
+ )
+ );
+
+ HEL_CHECK(offer.error());
+ HEL_CHECK(send_ioctl_req.error());
+ if(send_req.error() == kHelErrDismissed)
+ return EINVAL;
+ HEL_CHECK(send_req.error());
+ HEL_CHECK(recv_resp.error());
+
+ managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
+ resp.ParseFromArray(recv_resp.data(), recv_resp.length());
+ __ensure(resp.error() == managarm::fs::Errors::SUCCESS);
+
+ param->value = resp.input_value();
+ param->minimum = resp.input_min();
+ param->maximum = resp.input_max();
+ param->fuzz = resp.input_fuzz();
+ param->flat = resp.input_flat();
+ param->resolution = resp.input_resolution();
+
+ *result = resp.result();
+ return 0;
+ }else if(request == KDSETMODE) {
+ auto param = reinterpret_cast<unsigned int *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: KD_SETMODE(" << frg::hex_fmt(param) << ") is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == KDGETMODE) {
+ auto param = reinterpret_cast<unsigned int *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: KD_GETMODE is a no-op" << frg::endlog;
+ *param = 0;
+
+ *result = 0;
+ return 0;
+ }else if(request == KDSKBMODE) {
+ auto param = reinterpret_cast<long>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: KD_SKBMODE(" << frg::hex_fmt(param) << ") is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == VT_SETMODE) {
+ // auto param = reinterpret_cast<struct vt_mode *>(arg);
+ mlibc::infoLogger() << "\e[35mmlibc: VT_SETMODE is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == VT_GETSTATE) {
+ auto param = reinterpret_cast<struct vt_stat *>(arg);
+
+ param->v_active = 0;
+ param->v_signal = 0;
+ param->v_state = 0;
+
+ mlibc::infoLogger() << "\e[35mmlibc: VT_GETSTATE is a no-op" << frg::endlog;
+
+ *result = 0;
+ return 0;
+ }else if(request == VT_ACTIVATE || request == VT_WAITACTIVE) {
+ mlibc::infoLogger() << "\e[35mmlibc: VT_ACTIVATE/VT_WAITACTIVE are no-ops" << frg::endlog;
+ *result = 0;
+ return 0;
+ }else if(request == TIOCSPTLCK) {
+ mlibc::infoLogger() << "\e[35mmlibc: TIOCSPTLCK is a no-op" << frg::endlog;
+ if(result)
+ *result = 0;
+ return 0;
+ }
+
+ mlibc::infoLogger() << "mlibc: Unexpected ioctl with"
+ << " type: 0x" << frg::hex_fmt(_IOC_TYPE(request))
+ << ", number: 0x" << frg::hex_fmt(_IOC_NR(request))
+ << " (raw request: " << frg::hex_fmt(request) << ")" << frg::endlog;
+ __ensure(!"Illegal ioctl request");
+ __builtin_unreachable();
+}
+
+} //namespace mlibc