9a9b91c940
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
1225 lines
40 KiB
C++
1225 lines
40 KiB
C++
#ifdef _GNU_SOURCE
|
|
#undef _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/cdrom.h>
|
|
#include <linux/fiemap.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/input.h>
|
|
#include <linux/kd.h>
|
|
#include <linux/nvme_ioctl.h>
|
|
#include <linux/usb/cdc-wdm.h>
|
|
#include <linux/vt.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/in.h>
|
|
#include <scsi/sg.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <bits/ensure.h>
|
|
#include <bits/errors.hpp>
|
|
#include <bragi/helpers-frigg.hpp>
|
|
#include <frg/vector.hpp>
|
|
#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>
|
|
|
|
#define SIOCETHTOOL 0x8946
|
|
#define SIOCGSKNS 0x894C
|
|
#define TFD_IOC_SET_TICKS _IOW('T', 0, __u64)
|
|
|
|
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);
|
|
}
|
|
|
|
auto handle_siocgif =
|
|
[&arg, &request, &result](
|
|
void (*req_setup)(managarm::fs::IfreqRequest<MemoryAllocator> &req, struct ifreq *ifr),
|
|
int (*resp_parse)(managarm::fs::IfreqReply<MemoryAllocator> &resp, struct ifreq *ifr)
|
|
) -> int {
|
|
if (!arg)
|
|
return EFAULT;
|
|
|
|
auto ifr = reinterpret_cast<struct ifreq *>(arg);
|
|
|
|
managarm::posix::NetserverRequest<MemoryAllocator> token_req(getSysdepsAllocator());
|
|
managarm::fs::IfreqRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
req.set_command(request);
|
|
|
|
req_setup(req, ifr);
|
|
|
|
auto [offer, send_token_req, send_req, send_req_tail, recv_resp] = exchangeMsgsSync(
|
|
getPosixLane(),
|
|
helix_ng::offer(
|
|
helix_ng::want_lane,
|
|
helix_ng::sendBragiHeadOnly(token_req, getSysdepsAllocator()),
|
|
helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
|
|
helix_ng::recvInline()
|
|
)
|
|
);
|
|
|
|
HEL_CHECK(offer.error());
|
|
HEL_CHECK(send_token_req.error());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(send_req_tail.error());
|
|
HEL_CHECK(recv_resp.error());
|
|
|
|
auto preamble = bragi::read_preamble(recv_resp);
|
|
|
|
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
|
|
tailBuffer.resize(preamble.tail_size());
|
|
auto [recv_tail] = exchangeMsgsSync(
|
|
offer.descriptor().getHandle(),
|
|
helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
|
|
);
|
|
|
|
HEL_CHECK(recv_tail.error());
|
|
|
|
auto resp = *bragi::parse_head_tail<managarm::fs::IfreqReply>(
|
|
recv_resp, tailBuffer, getSysdepsAllocator()
|
|
);
|
|
recv_resp.reset();
|
|
|
|
int ret = resp_parse(resp, ifr);
|
|
|
|
if (result)
|
|
*result = 0;
|
|
return ret;
|
|
};
|
|
|
|
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 ENOTTY;
|
|
HEL_CHECK(send_req.error());
|
|
if (recv_resp.error() == kHelErrDismissed)
|
|
return ENOTTY;
|
|
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::SUCCESS)
|
|
return resp.error() | toErrno;
|
|
|
|
*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());
|
|
if (imbue_creds.error()) {
|
|
infoLogger(
|
|
) << "mlibc: TIOCGPGRP used on unexpected socket, returning EINVAL (FIXME)"
|
|
<< frg::endlog;
|
|
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::SUCCESS)
|
|
return resp.error() | toErrno;
|
|
*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;
|
|
}
|
|
case SIOCETHTOOL:
|
|
mlibc::infoLogger() << "\e[35mmlibc: SIOCETHTOOL is a stub" << frg::endlog;
|
|
*result = 0;
|
|
return ENOSYS;
|
|
case SIOCGSKNS:
|
|
mlibc::infoLogger() << "\e[35mmlibc: SIOCGSKNS is a stub" << frg::endlog;
|
|
*result = 0;
|
|
return ENOSYS;
|
|
case SG_IO:
|
|
mlibc::infoLogger() << "\e[35mmlibc: SG_IO is a stub" << frg::endlog;
|
|
*result = 0;
|
|
return ENOSYS;
|
|
} // end of switch()
|
|
|
|
if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGVERSION)) {
|
|
*reinterpret_cast<int *>(arg) = 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));
|
|
auto param = reinterpret_cast<struct input_id *>(arg);
|
|
|
|
managarm::fs::EvioGetIdRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
|
|
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
|
|
handle,
|
|
helix_ng::offer(
|
|
helix_ng::want_lane,
|
|
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
|
|
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
|
|
helix_ng::recvInline()
|
|
)
|
|
);
|
|
HEL_CHECK(offer.error());
|
|
auto conversation = offer.descriptor();
|
|
HEL_CHECK(send_ioctl_req.error());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(recv_resp.error());
|
|
|
|
auto resp =
|
|
*bragi::parse_head_only<managarm::fs::EvioGetIdReply>(recv_resp, getSysdepsAllocator());
|
|
recv_resp.reset();
|
|
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
|
|
|
|
param->bustype = resp.bustype();
|
|
param->vendor = resp.vendor();
|
|
param->product = resp.product();
|
|
param->version = resp.version();
|
|
|
|
*result = 0;
|
|
return 0;
|
|
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGNAME(0))) {
|
|
managarm::fs::EvioGetNameRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
|
|
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
|
|
handle,
|
|
helix_ng::offer(
|
|
helix_ng::want_lane,
|
|
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
|
|
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
|
|
helix_ng::recvInline()
|
|
)
|
|
);
|
|
HEL_CHECK(offer.error());
|
|
auto conversation = offer.descriptor();
|
|
HEL_CHECK(send_ioctl_req.error());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(recv_resp.error());
|
|
|
|
auto preamble = bragi::read_preamble(recv_resp);
|
|
__ensure(!preamble.error());
|
|
|
|
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
|
|
tailBuffer.resize(preamble.tail_size());
|
|
auto [recv_tail] = exchangeMsgsSync(
|
|
conversation.getHandle(), helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
|
|
);
|
|
|
|
HEL_CHECK(recv_tail.error());
|
|
|
|
auto resp = *bragi::parse_head_tail<managarm::fs::EvioGetNameReply>(
|
|
recv_resp, tailBuffer, getSysdepsAllocator()
|
|
);
|
|
recv_resp.reset();
|
|
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
|
|
|
|
auto chunk = frg::min(_IOC_SIZE(request), resp.name().size() + 1);
|
|
memcpy(arg, resp.name().data(), chunk);
|
|
*result = chunk;
|
|
return 0;
|
|
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGRAB)) {
|
|
mlibc::infoLogger() << "mlibc: EVIOCGRAB is a no-op" << frg::endlog;
|
|
*result = 0;
|
|
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(EVIOCGMTSLOTS(0))) {
|
|
// this ioctl is completely, utterly undocumented
|
|
// the _IOC_SIZE is a buffer size in bytes, which should be a multiple of int32_t
|
|
// bytes should be at least sizeof(int32_t) large.
|
|
// the argument (the pointer to the buffer) is an array of int32_t
|
|
// the first entry is the number of values supplied, followed by the values
|
|
// this would have been worthwhile to document ffs
|
|
|
|
// the length argument is the buffer size, in bytes
|
|
auto bytes = _IOC_SIZE(request);
|
|
// the length argument should be a multiple of int32_t
|
|
if (!bytes || bytes % sizeof(int32_t))
|
|
return EINVAL;
|
|
|
|
// the number of entries the buffer can hold
|
|
auto entries = (bytes / sizeof(int32_t)) - 1;
|
|
|
|
managarm::fs::EvioGetMultitouchSlotsRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
req.set_code(*reinterpret_cast<uint32_t *>(arg));
|
|
|
|
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
|
|
handle,
|
|
helix_ng::offer(
|
|
helix_ng::want_lane,
|
|
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
|
|
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
|
|
helix_ng::recvInline()
|
|
)
|
|
);
|
|
HEL_CHECK(offer.error());
|
|
auto conversation = offer.descriptor();
|
|
HEL_CHECK(send_ioctl_req.error());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(recv_resp.error());
|
|
|
|
auto preamble = bragi::read_preamble(recv_resp);
|
|
__ensure(!preamble.error());
|
|
|
|
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
|
|
tailBuffer.resize(preamble.tail_size());
|
|
auto [recv_tail] = exchangeMsgsSync(
|
|
conversation.getHandle(), helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
|
|
);
|
|
|
|
HEL_CHECK(recv_tail.error());
|
|
|
|
auto resp = *bragi::parse_head_tail<managarm::fs::EvioGetMultitouchSlotsReply>(
|
|
recv_resp, tailBuffer, getSysdepsAllocator()
|
|
);
|
|
recv_resp.reset();
|
|
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
|
|
|
|
auto param = reinterpret_cast<int32_t *>(arg);
|
|
|
|
for (size_t i = 0; i < resp.values_size() && i < entries; i++) {
|
|
param[i + 1] = resp.values(i);
|
|
}
|
|
|
|
param[0] = resp.values_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;
|
|
} else if (request == SIOCGIFNAME) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) { req.set_index(ifr->ifr_ifindex); },
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
strncpy(ifr->ifr_name, resp.name().data(), IFNAMSIZ);
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFCONF) {
|
|
if (!arg)
|
|
return EFAULT;
|
|
|
|
auto ifc = reinterpret_cast<struct ifconf *>(arg);
|
|
|
|
managarm::posix::NetserverRequest<MemoryAllocator> token_req(getSysdepsAllocator());
|
|
managarm::fs::IfreqRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
req.set_command(request);
|
|
|
|
auto [offer, send_token_req, send_req, send_tail, recv_resp] = exchangeMsgsSync(
|
|
getPosixLane(),
|
|
helix_ng::offer(
|
|
helix_ng::want_lane,
|
|
helix_ng::sendBragiHeadOnly(token_req, getSysdepsAllocator()),
|
|
helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
|
|
helix_ng::recvInline()
|
|
)
|
|
);
|
|
|
|
auto conversation = offer.descriptor();
|
|
|
|
HEL_CHECK(offer.error());
|
|
HEL_CHECK(send_token_req.error());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(send_tail.error());
|
|
HEL_CHECK(recv_resp.error());
|
|
|
|
auto preamble = bragi::read_preamble(recv_resp);
|
|
__ensure(!preamble.error());
|
|
|
|
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
|
|
tailBuffer.resize(preamble.tail_size());
|
|
auto [recv_tail] = exchangeMsgsSync(
|
|
conversation.getHandle(), helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
|
|
);
|
|
|
|
HEL_CHECK(recv_tail.error());
|
|
|
|
auto resp = *bragi::parse_head_tail<managarm::fs::IfconfReply>(
|
|
recv_resp, tailBuffer, getSysdepsAllocator()
|
|
);
|
|
recv_resp.reset();
|
|
|
|
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
|
|
|
|
if (ifc->ifc_buf == nullptr) {
|
|
ifc->ifc_len = int(resp.ifconf_size() * sizeof(struct ifreq));
|
|
return 0;
|
|
}
|
|
|
|
ifc->ifc_len = frg::min(int(resp.ifconf_size() * sizeof(struct ifreq)), ifc->ifc_len);
|
|
|
|
for (size_t i = 0; i < frg::min(resp.ifconf_size(), ifc->ifc_len / sizeof(struct ifreq));
|
|
++i) {
|
|
auto &conf = resp.ifconf()[i];
|
|
|
|
sockaddr_in addr{};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(conf.ip4());
|
|
|
|
ifreq *req = &ifc->ifc_req[i];
|
|
strncpy(req->ifr_name, conf.name().data(), IFNAMSIZ);
|
|
memcpy(&req->ifr_addr, &addr, sizeof(addr));
|
|
}
|
|
|
|
if (result)
|
|
*result = 0;
|
|
return 0;
|
|
} else if (request == SIOCGIFNETMASK) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
|
|
sockaddr_in addr{};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr = {htonl(resp.ip4_netmask())};
|
|
memcpy(&ifr->ifr_netmask, &addr, sizeof(addr));
|
|
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFINDEX) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
ifr->ifr_ifindex = resp.index();
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFFLAGS) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
ifr->ifr_flags = resp.flags();
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFADDR) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
|
|
sockaddr_in addr{};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr = {htonl(resp.ip4_addr())};
|
|
memcpy(&ifr->ifr_addr, &addr, sizeof(addr));
|
|
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFMTU) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
|
|
ifr->ifr_mtu = resp.mtu();
|
|
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFBRDADDR) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
|
|
sockaddr_in addr{};
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr = {htonl(resp.ip4_broadcast_addr())};
|
|
memcpy(&ifr->ifr_broadaddr, &addr, sizeof(addr));
|
|
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == SIOCGIFHWADDR) {
|
|
return handle_siocgif(
|
|
[](auto req, auto ifr) {
|
|
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
|
|
},
|
|
[](auto resp, auto ifr) {
|
|
if (resp.error() != managarm::fs::Errors::SUCCESS)
|
|
return EINVAL;
|
|
|
|
sockaddr addr{};
|
|
addr.sa_family = ARPHRD_ETHER;
|
|
memcpy(addr.sa_data, resp.mac().data(), 6);
|
|
memcpy(&ifr->ifr_hwaddr, &addr, sizeof(addr));
|
|
|
|
return 0;
|
|
}
|
|
);
|
|
} else if (request == IOCTL_WDM_MAX_COMMAND) {
|
|
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());
|
|
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();
|
|
*param = resp.size();
|
|
return 0;
|
|
} else if (request == NVME_IOCTL_ID) {
|
|
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());
|
|
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 (request == NVME_IOCTL_ADMIN_CMD) {
|
|
auto param = reinterpret_cast<struct nvme_admin_cmd *>(arg);
|
|
|
|
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
req.set_command(request);
|
|
|
|
auto [offer, send_ioctl_req, send_req, send_buffer, send_data, recv_resp, recv_data] =
|
|
exchangeMsgsSync(
|
|
handle,
|
|
helix_ng::offer(
|
|
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
|
|
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
|
|
helix_ng::sendBuffer(param, sizeof(*param)),
|
|
helix_ng::sendBuffer(reinterpret_cast<void *>(param->addr), param->data_len),
|
|
helix_ng::recvInline(),
|
|
helix_ng::recvBuffer(reinterpret_cast<void *>(param->addr), param->data_len)
|
|
)
|
|
);
|
|
|
|
HEL_CHECK(offer.error());
|
|
HEL_CHECK(send_ioctl_req.error());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(send_buffer.error());
|
|
HEL_CHECK(send_data.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 = resp.result();
|
|
param->result = resp.status();
|
|
return 0;
|
|
} else if (request == TFD_IOC_SET_TICKS) {
|
|
if (!arg)
|
|
return EFAULT;
|
|
auto param = reinterpret_cast<uint64_t *>(arg);
|
|
|
|
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
|
|
req.set_command(request);
|
|
req.set_ticks(*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());
|
|
HEL_CHECK(send_req.error());
|
|
HEL_CHECK(recv_resp.error());
|
|
|
|
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
|
|
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
|
|
return 0;
|
|
} else if (request == FICLONE || request == FICLONERANGE) {
|
|
mlibc::infoLogger() << "\e[35mmlibc: FICLONE/FICLONERANGE are no-ops" << frg::endlog;
|
|
*result = -1;
|
|
return EOPNOTSUPP;
|
|
} else if (request == FS_IOC_GETFLAGS) {
|
|
mlibc::infoLogger() << "\e[35mmlibc: FS_IOC_GETFLAGS is a no-op" << frg::endlog;
|
|
*result = 0;
|
|
return ENOSYS;
|
|
} else if (request == FS_IOC_FIEMAP) {
|
|
mlibc::infoLogger() << "\e[35mmlibc: FS_IOC_FIEMAP is a no-op" << frg::endlog;
|
|
*result = 0;
|
|
return ENOSYS;
|
|
}
|
|
|
|
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;
|
|
|
|
return ENOSYS;
|
|
}
|
|
|
|
} // namespace mlibc
|