// The front SD slot IPCCommandResult CWII_IPC_HLE_Device_sdio_slot0::IOCtl(u32 _CommandAddress) { u32 Cmd = Memory::Read_U32(_CommandAddress + 0xC); u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); // As a safety precaution we fill the out buffer with zeros to avoid // returning nonsense values Memory::Memset(BufferOut, 0, BufferOutSize); u32 ReturnValue = 0; switch (Cmd) { case IOCTL_WRITEHCR: { u32 reg = Memory::Read_U32(BufferIn); u32 val = Memory::Read_U32(BufferIn + 16); DEBUG_LOG(WII_IPC_SD, "IOCTL_WRITEHCR 0x%08x - 0x%08x", reg, val); if (reg >= 0x200) { DEBUG_LOG(WII_IPC_SD, "IOCTL_WRITEHCR out of range"); break; } if ((reg == HCR_CLOCKCONTROL) && (val & 1)) { // Clock is set to oscillate, enable bit 1 to say it's stable m_Registers[reg] = val | 2; } else if ((reg == HCR_SOFTWARERESET) && val) { // When a reset is specified, the register gets cleared m_Registers[reg] = 0; } else { // Default to just storing the new value m_Registers[reg] = val; } } break; case IOCTL_READHCR: { u32 reg = Memory::Read_U32(BufferIn); if (reg >= 0x200) { DEBUG_LOG(WII_IPC_SD, "IOCTL_READHCR out of range"); break; } u32 val = m_Registers[reg]; DEBUG_LOG(WII_IPC_SD, "IOCTL_READHCR 0x%08x - 0x%08x", reg, val); // Just reading the register Memory::Write_U32(val, BufferOut); } break; case IOCTL_RESETCARD: DEBUG_LOG(WII_IPC_SD, "IOCTL_RESETCARD"); if (m_Card) m_Status |= CARD_INITIALIZED; // Returns 16bit RCA and 16bit 0s (meaning success) Memory::Write_U32(0x9f620000, BufferOut); break; case IOCTL_SETCLK: { DEBUG_LOG(WII_IPC_SD, "IOCTL_SETCLK"); // libogc only sets it to 1 and makes sure the return isn't negative... // one half of the sdclk divisor: a power of two or zero. u32 clock = Memory::Read_U32(BufferIn); if (clock != 1) INFO_LOG(WII_IPC_SD, "Setting to %i, interesting", clock); } break; case IOCTL_SENDCMD: INFO_LOG(WII_IPC_SD, "IOCTL_SENDCMD %x IPC:%08x", Memory::Read_U32(BufferIn), _CommandAddress); ReturnValue = ExecuteCommand(BufferIn, BufferInSize, 0, 0, BufferOut, BufferOutSize); break; case IOCTL_GETSTATUS: if (SConfig::GetInstance().m_WiiSDCard) m_Status |= CARD_INSERTED; else m_Status = CARD_NOT_EXIST; INFO_LOG(WII_IPC_SD, "IOCTL_GETSTATUS. Replying that SD card is %s%s", (m_Status & CARD_INSERTED) ? "inserted" : "not present", (m_Status & CARD_INITIALIZED) ? " and initialized" : ""); Memory::Write_U32(m_Status, BufferOut); break; case IOCTL_GETOCR: DEBUG_LOG(WII_IPC_SD, "IOCTL_GETOCR"); Memory::Write_U32(0x80ff8000, BufferOut); break; default: ERROR_LOG(WII_IPC_SD, "Unknown SD IOCtl command (0x%08x)", Cmd); break; } // INFO_LOG(WII_IPC_SD, "InBuffer"); // DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_SD); // INFO_LOG(WII_IPC_SD, "OutBuffer"); // DumpCommands(BufferOut, BufferOutSize/4, LogTypes::WII_IPC_SD); if (ReturnValue == RET_EVENT_REGISTER) { // async m_event.addr = _CommandAddress; Memory::Write_U32(0, _CommandAddress + 0x4); // Check if the condition is already true EventNotify(); return GetNoReply(); } else if (ReturnValue == RET_EVENT_UNREGISTER) { // release returns 0 // unknown sd int // technically we do it out of order, oh well EnqueueReply(m_event.addr, EVENT_INVALID); m_event.addr = 0; m_event.type = EVENT_NONE; Memory::Write_U32(0, _CommandAddress + 0x4); return GetDefaultReply(); } else { Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); return GetDefaultReply(); } }
IPCCommandResult CWII_IPC_HLE_Device_usb_ven::IOCtl(u32 _CommandAddress) { IPCCommandResult Reply = GetNoReply(); u32 Command = Memory::Read_U32(_CommandAddress + 0x0c); u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1c); INFO_LOG(OSHLE, "%s - IOCtl: %x", GetDeviceName().c_str(), Command); INFO_LOG(OSHLE, "%x:%x %x:%x", BufferIn, BufferInSize, BufferOut, BufferOutSize); switch (Command) { case USBV5_IOCTL_GETVERSION: Memory::Write_U32(0x50001, BufferOut); Reply = GetDefaultReply(); break; case USBV5_IOCTL_GETDEVICECHANGE: { // sent on change static bool firstcall = true; if (firstcall) { Reply = GetDefaultReply(); firstcall = false; } // num devices Memory::Write_U32(0, _CommandAddress + 4); return Reply; } break; case USBV5_IOCTL_ATTACHFINISH: Reply = GetDefaultReply(); break; case USBV5_IOCTL_SUSPEND_RESUME: DEBUG_LOG(OSHLE, "Device: %i Resumed: %i", Memory::Read_U32(BufferIn), Memory::Read_U32(BufferIn + 4)); Reply = GetDefaultReply(); break; case USBV5_IOCTL_GETDEVPARAMS: { s32 device = Memory::Read_U32(BufferIn); u32 unk = Memory::Read_U32(BufferIn + 4); DEBUG_LOG(OSHLE, "USBV5_IOCTL_GETDEVPARAMS device: %i unk: %i", device, unk); Memory::Write_U32(0, BufferOut); Reply = GetDefaultReply(); } break; default: DEBUG_LOG(OSHLE, "%x:%x %x:%x", BufferIn, BufferInSize, BufferOut, BufferOutSize); break; } Memory::Write_U32(0, _CommandAddress + 4); return Reply; }
IPCCommandResult CWII_IPC_HLE_Device_hid::IOCtl(u32 _CommandAddress) { if (Core::g_want_determinism) { Memory::Write_U32(-1, _CommandAddress + 0x4); return GetDefaultReply(); } u32 Parameter = Memory::Read_U32(_CommandAddress + 0xC); u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); u32 ReturnValue = 0; switch (Parameter) { case IOCTL_HID_GET_ATTACHED: { DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Get Attached) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", BufferIn, BufferInSize, BufferOut, BufferOutSize); deviceCommandAddress = _CommandAddress; return GetNoReply(); } case IOCTL_HID_OPEN: { DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Open) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", BufferIn, BufferInSize, BufferOut, BufferOutSize); //hid version, apparently ReturnValue = 0x40001; break; } case IOCTL_HID_SET_SUSPEND: { DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Set Suspend) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", BufferIn, BufferInSize, BufferOut, BufferOutSize); // not actually implemented in IOS ReturnValue = 0; break; } case IOCTL_HID_CANCEL_INTERRUPT: { DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Cancel Interrupt) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", BufferIn, BufferInSize, BufferOut, BufferOutSize); ReturnValue = 0; break; } case IOCTL_HID_CONTROL: { /* ERROR CODES: -4 Can't find device specified */ u32 dev_num = Memory::Read_U32(BufferIn + 0x10); u8 bmRequestType = Memory::Read_U8(BufferIn + 0x14); u8 bRequest = Memory::Read_U8(BufferIn + 0x15); u16 wValue = Memory::Read_U16(BufferIn + 0x16); u16 wIndex = Memory::Read_U16(BufferIn + 0x18); u16 wLength = Memory::Read_U16(BufferIn + 0x1A); u32 data = Memory::Read_U32(BufferIn + 0x1C); ReturnValue = HIDERR_NO_DEVICE_FOUND; libusb_device_handle * dev_handle = GetDeviceByDevNum(dev_num); if (dev_handle == nullptr) { DEBUG_LOG(WII_IPC_HID, "Could not find handle: %X", dev_num); break; } struct libusb_transfer *transfer = libusb_alloc_transfer(0); transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; u8 * buffer = (u8*)malloc(wLength + LIBUSB_CONTROL_SETUP_SIZE); libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, wIndex, wLength); Memory::CopyFromEmu(buffer + LIBUSB_CONTROL_SETUP_SIZE, data, wLength); libusb_fill_control_transfer(transfer, dev_handle, buffer, handleUsbUpdates, (void*)(size_t)_CommandAddress, /* no timeout */ 0); libusb_submit_transfer(transfer); //DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Control)(%02X, %02X) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", // bmRequestType, bRequest, BufferIn, BufferInSize, BufferOut, BufferOutSize); // It's the async way! return GetNoReply(); } case IOCTL_HID_INTERRUPT_OUT: case IOCTL_HID_INTERRUPT_IN: { u32 dev_num = Memory::Read_U32(BufferIn + 0x10); u32 endpoint = Memory::Read_U32(BufferIn + 0x14); u32 length = Memory::Read_U32(BufferIn + 0x18); u32 data = Memory::Read_U32(BufferIn + 0x1C); ReturnValue = HIDERR_NO_DEVICE_FOUND; libusb_device_handle * dev_handle = GetDeviceByDevNum(dev_num); if (dev_handle == nullptr) { DEBUG_LOG(WII_IPC_HID, "Could not find handle: %X", dev_num); break; } struct libusb_transfer *transfer = libusb_alloc_transfer(0); transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; libusb_fill_interrupt_transfer(transfer, dev_handle, endpoint, Memory::GetPointer(data), length, handleUsbUpdates, (void*)(size_t)_CommandAddress, 0); libusb_submit_transfer(transfer); //DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Interrupt %s)(%d,%d,%X) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", // Parameter == IOCTL_HID_INTERRUPT_IN ? "In" : "Out", endpoint, length, data, BufferIn, BufferInSize, BufferOut, BufferOutSize); // It's the async way! return GetNoReply(); } case IOCTL_HID_SHUTDOWN: { std::lock_guard<std::mutex> lk(m_device_list_reply_mutex); if (deviceCommandAddress != 0) { Memory::Write_U32(0xFFFFFFFF, Memory::Read_U32(deviceCommandAddress + 0x18)); // The original hardware overwrites the command type with the async reply type. Memory::Write_U32(IPC_REP_ASYNC, deviceCommandAddress); // IOS also seems to write back the command that was responded to in the FD field. Memory::Write_U32(IPC_CMD_IOCTL, deviceCommandAddress + 8); // Return value Memory::Write_U32(-1, deviceCommandAddress + 4); WII_IPC_HLE_Interface::EnqueueReply(deviceCommandAddress); deviceCommandAddress = 0; } DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(Shutdown) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", BufferIn, BufferInSize, BufferOut, BufferOutSize); break; } default: { DEBUG_LOG(WII_IPC_HID, "HID::IOCtl(0x%x) (BufferIn: (%08x, %i), BufferOut: (%08x, %i)", Parameter, BufferIn, BufferInSize, BufferOut, BufferOutSize); break; } } Memory::Write_U32(ReturnValue, _CommandAddress + 4); return GetDefaultReply(); }
IPCCommandResult NetIPTop::IOCtl(const IOCtlRequest& request) { if (Core::g_want_determinism) { return GetDefaultReply(IPC_EACCES); } s32 return_value = 0; switch (request.request) { case IOCTL_SO_STARTUP: { request.Log(GetDeviceName(), LogTypes::IOS_WC24); break; } case IOCTL_SO_SOCKET: { u32 af = Memory::Read_U32(request.buffer_in); u32 type = Memory::Read_U32(request.buffer_in + 4); u32 prot = Memory::Read_U32(request.buffer_in + 8); WiiSockMan& sm = WiiSockMan::GetInstance(); return_value = sm.NewSocket(af, type, prot); INFO_LOG(IOS_NET, "IOCTL_SO_SOCKET " "Socket: %08x (%d,%d,%d), BufferIn: (%08x, %i), BufferOut: (%08x, %i)", return_value, af, type, prot, request.buffer_in, request.buffer_in_size, request.buffer_out, request.buffer_out_size); break; } case IOCTL_SO_ICMPSOCKET: { u32 pf = Memory::Read_U32(request.buffer_in); WiiSockMan& sm = WiiSockMan::GetInstance(); return_value = sm.NewSocket(pf, SOCK_RAW, IPPROTO_ICMP); INFO_LOG(IOS_NET, "IOCTL_SO_ICMPSOCKET(%x) %d", pf, return_value); break; } case IOCTL_SO_CLOSE: case IOCTL_SO_ICMPCLOSE: { u32 fd = Memory::Read_U32(request.buffer_in); WiiSockMan& sm = WiiSockMan::GetInstance(); return_value = sm.DeleteSocket(fd); INFO_LOG(IOS_NET, "%s(%x) %x", request.request == IOCTL_SO_ICMPCLOSE ? "IOCTL_SO_ICMPCLOSE" : "IOCTL_SO_CLOSE", fd, return_value); break; } case IOCTL_SO_ACCEPT: case IOCTL_SO_BIND: case IOCTL_SO_CONNECT: case IOCTL_SO_FCNTL: { u32 fd = Memory::Read_U32(request.buffer_in); WiiSockMan& sm = WiiSockMan::GetInstance(); sm.DoSock(fd, request, static_cast<NET_IOCTL>(request.request)); return GetNoReply(); } ///////////////////////////////////////////////////////////// // TODO: Tidy all below // ///////////////////////////////////////////////////////////// case IOCTL_SO_SHUTDOWN: { request.Log(GetDeviceName(), LogTypes::IOS_WC24); u32 fd = Memory::Read_U32(request.buffer_in); u32 how = Memory::Read_U32(request.buffer_in + 4); int ret = shutdown(fd, how); return_value = WiiSockMan::GetNetErrorCode(ret, "SO_SHUTDOWN", false); break; } case IOCTL_SO_LISTEN: { u32 fd = Memory::Read_U32(request.buffer_in); u32 BACKLOG = Memory::Read_U32(request.buffer_in + 0x04); u32 ret = listen(fd, BACKLOG); return_value = WiiSockMan::GetNetErrorCode(ret, "SO_LISTEN", false); request.Log(GetDeviceName(), LogTypes::IOS_WC24); break; } case IOCTL_SO_GETSOCKOPT: { u32 fd = Memory::Read_U32(request.buffer_out); u32 level = Memory::Read_U32(request.buffer_out + 4); u32 optname = Memory::Read_U32(request.buffer_out + 8); request.Log(GetDeviceName(), LogTypes::IOS_WC24); // Do the level/optname translation int nat_level = -1, nat_optname = -1; for (auto& map : opt_level_mapping) if (level == map[1]) nat_level = map[0]; for (auto& map : opt_name_mapping) if (optname == map[1]) nat_optname = map[0]; u8 optval[20]; u32 optlen = 4; int ret = getsockopt(fd, nat_level, nat_optname, (char*)&optval, (socklen_t*)&optlen); return_value = WiiSockMan::GetNetErrorCode(ret, "SO_GETSOCKOPT", false); Memory::Write_U32(optlen, request.buffer_out + 0xC); Memory::CopyToEmu(request.buffer_out + 0x10, optval, optlen); if (optname == SO_ERROR) { s32 last_error = WiiSockMan::GetInstance().GetLastNetError(); Memory::Write_U32(sizeof(s32), request.buffer_out + 0xC); Memory::Write_U32(last_error, request.buffer_out + 0x10); } break; } case IOCTL_SO_SETSOCKOPT: { u32 fd = Memory::Read_U32(request.buffer_in); u32 level = Memory::Read_U32(request.buffer_in + 4); u32 optname = Memory::Read_U32(request.buffer_in + 8); u32 optlen = Memory::Read_U32(request.buffer_in + 0xc); u8 optval[20]; optlen = std::min(optlen, (u32)sizeof(optval)); Memory::CopyFromEmu(optval, request.buffer_in + 0x10, optlen); INFO_LOG(IOS_NET, "IOCTL_SO_SETSOCKOPT(%08x, %08x, %08x, %08x) " "BufferIn: (%08x, %i), BufferOut: (%08x, %i)" "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx " "%02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx", fd, level, optname, optlen, request.buffer_in, request.buffer_in_size, request.buffer_out, request.buffer_out_size, optval[0], optval[1], optval[2], optval[3], optval[4], optval[5], optval[6], optval[7], optval[8], optval[9], optval[10], optval[11], optval[12], optval[13], optval[14], optval[15], optval[16], optval[17], optval[18], optval[19]); // TODO: bug booto about this, 0x2005 most likely timeout related, default value on Wii is , // 0x2001 is most likely tcpnodelay if (level == 6 && (optname == 0x2005 || optname == 0x2001)) { return_value = 0; break; } // Do the level/optname translation int nat_level = -1, nat_optname = -1; for (auto& map : opt_level_mapping) if (level == map[1]) nat_level = map[0]; for (auto& map : opt_name_mapping) if (optname == map[1]) nat_optname = map[0]; if (nat_level == -1 || nat_optname == -1) { INFO_LOG(IOS_NET, "SO_SETSOCKOPT: unknown level %d or optname %d", level, optname); // Default to the given level/optname. They match on Windows... nat_level = level; nat_optname = optname; } int ret = setsockopt(fd, nat_level, nat_optname, (char*)optval, optlen); return_value = WiiSockMan::GetNetErrorCode(ret, "SO_SETSOCKOPT", false); break; } case IOCTL_SO_GETSOCKNAME: { u32 fd = Memory::Read_U32(request.buffer_in); request.Log(GetDeviceName(), LogTypes::IOS_WC24); sockaddr sa; socklen_t sa_len; sa_len = sizeof(sa); int ret = getsockname(fd, &sa, &sa_len); if (request.buffer_out_size < 2 + sizeof(sa.sa_data)) WARN_LOG(IOS_NET, "IOCTL_SO_GETSOCKNAME output buffer is too small. Truncating"); if (request.buffer_out_size > 0) Memory::Write_U8(request.buffer_out_size, request.buffer_out); if (request.buffer_out_size > 1) Memory::Write_U8(sa.sa_family & 0xFF, request.buffer_out + 1); if (request.buffer_out_size > 2) Memory::CopyToEmu(request.buffer_out + 2, &sa.sa_data, std::min<size_t>(sizeof(sa.sa_data), request.buffer_out_size - 2)); return_value = ret; break; } case IOCTL_SO_GETPEERNAME: { u32 fd = Memory::Read_U32(request.buffer_in); sockaddr sa; socklen_t sa_len; sa_len = sizeof(sa); int ret = getpeername(fd, &sa, &sa_len); if (request.buffer_out_size < 2 + sizeof(sa.sa_data)) WARN_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME output buffer is too small. Truncating"); if (request.buffer_out_size > 0) Memory::Write_U8(request.buffer_out_size, request.buffer_out); if (request.buffer_out_size > 1) Memory::Write_U8(AF_INET, request.buffer_out + 1); if (request.buffer_out_size > 2) Memory::CopyToEmu(request.buffer_out + 2, &sa.sa_data, std::min<size_t>(sizeof(sa.sa_data), request.buffer_out_size - 2)); INFO_LOG(IOS_NET, "IOCTL_SO_GETPEERNAME(%x)", fd); return_value = ret; break; } case IOCTL_SO_GETHOSTID: { request.Log(GetDeviceName(), LogTypes::IOS_WC24); #ifdef _WIN32 DWORD forwardTableSize, ipTableSize, result; DWORD ifIndex = -1; std::unique_ptr<MIB_IPFORWARDTABLE> forwardTable; std::unique_ptr<MIB_IPADDRTABLE> ipTable; forwardTableSize = 0; if (GetIpForwardTable(nullptr, &forwardTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { forwardTable = std::unique_ptr<MIB_IPFORWARDTABLE>((PMIB_IPFORWARDTABLE) operator new(forwardTableSize)); } ipTableSize = 0; if (GetIpAddrTable(nullptr, &ipTableSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { ipTable = std::unique_ptr<MIB_IPADDRTABLE>((PMIB_IPADDRTABLE) operator new(ipTableSize)); } // find the interface IP used for the default route and use that result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); while (result == NO_ERROR || result == ERROR_MORE_DATA) // can return ERROR_MORE_DATA on XP even after the first call { for (DWORD i = 0; i < forwardTable->dwNumEntries; ++i) { if (forwardTable->table[i].dwForwardDest == 0) { ifIndex = forwardTable->table[i].dwForwardIfIndex; break; } } if (result == NO_ERROR || ifIndex != -1) break; result = GetIpForwardTable(forwardTable.get(), &forwardTableSize, FALSE); } if (ifIndex != -1 && GetIpAddrTable(ipTable.get(), &ipTableSize, FALSE) == NO_ERROR) { for (DWORD i = 0; i < ipTable->dwNumEntries; ++i) { if (ipTable->table[i].dwIndex == ifIndex) { return_value = Common::swap32(ipTable->table[i].dwAddr); break; } } } #endif // default placeholder, in case of failure if (return_value == 0) return_value = 192 << 24 | 168 << 16 | 1 << 8 | 150; break; } case IOCTL_SO_INETATON: { std::string hostname = Memory::GetString(request.buffer_in); struct hostent* remoteHost = gethostbyname(hostname.c_str()); if (remoteHost == nullptr || remoteHost->h_addr_list == nullptr || remoteHost->h_addr_list[0] == nullptr) { INFO_LOG(IOS_NET, "IOCTL_SO_INETATON = -1 " "%s, BufferIn: (%08x, %i), BufferOut: (%08x, %i), IP Found: None", hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out, request.buffer_out_size); return_value = 0; } else { Memory::Write_U32(Common::swap32(*(u32*)remoteHost->h_addr_list[0]), request.buffer_out); INFO_LOG(IOS_NET, "IOCTL_SO_INETATON = 0 " "%s, BufferIn: (%08x, %i), BufferOut: (%08x, %i), IP Found: %08X", hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out, request.buffer_out_size, Common::swap32(*(u32*)remoteHost->h_addr_list[0])); return_value = 1; } break; } case IOCTL_SO_INETPTON: { std::string address = Memory::GetString(request.buffer_in); INFO_LOG(IOS_NET, "IOCTL_SO_INETPTON " "(Translating: %s)", address.c_str()); return_value = inet_pton(address.c_str(), Memory::GetPointer(request.buffer_out + 4)); break; } case IOCTL_SO_INETNTOP: { // u32 af = Memory::Read_U32(BufferIn); // u32 validAddress = Memory::Read_U32(request.buffer_in + 4); // u32 src = Memory::Read_U32(request.buffer_in + 8); char ip_s[16]; sprintf(ip_s, "%i.%i.%i.%i", Memory::Read_U8(request.buffer_in + 8), Memory::Read_U8(request.buffer_in + 8 + 1), Memory::Read_U8(request.buffer_in + 8 + 2), Memory::Read_U8(request.buffer_in + 8 + 3)); INFO_LOG(IOS_NET, "IOCTL_SO_INETNTOP %s", ip_s); Memory::CopyToEmu(request.buffer_out, (u8*)ip_s, strlen(ip_s)); break; } case IOCTL_SO_POLL: { // Map Wii/native poll events types struct { int native; int wii; } mapping[] = { {POLLRDNORM, 0x0001}, {POLLRDBAND, 0x0002}, {POLLPRI, 0x0004}, {POLLWRNORM, 0x0008}, {POLLWRBAND, 0x0010}, {POLLERR, 0x0020}, {POLLHUP, 0x0040}, {POLLNVAL, 0x0080}, }; u32 unknown = Memory::Read_U32(request.buffer_in); u32 timeout = Memory::Read_U32(request.buffer_in + 4); int nfds = request.buffer_out_size / 0xc; if (nfds == 0) ERROR_LOG(IOS_NET, "Hidden POLL"); std::vector<pollfd_t> ufds(nfds); for (int i = 0; i < nfds; ++i) { ufds[i].fd = Memory::Read_U32(request.buffer_out + 0xc * i); // fd int events = Memory::Read_U32(request.buffer_out + 0xc * i + 4); // events ufds[i].revents = Memory::Read_U32(request.buffer_out + 0xc * i + 8); // revents // Translate Wii to native events int unhandled_events = events; ufds[i].events = 0; for (auto& map : mapping) { if (events & map.wii) ufds[i].events |= map.native; unhandled_events &= ~map.wii; } DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL(%d) " "Sock: %08x, Unknown: %08x, Events: %08x, " "NativeEvents: %08x", i, ufds[i].fd, unknown, events, ufds[i].events); // Do not pass return-only events to the native poll ufds[i].events &= ~(POLLERR | POLLHUP | POLLNVAL | UNSUPPORTED_WSAPOLL); if (unhandled_events) ERROR_LOG(IOS_NET, "SO_POLL: unhandled Wii event types: %04x", unhandled_events); } int ret = poll(ufds.data(), nfds, timeout); ret = WiiSockMan::GetNetErrorCode(ret, "SO_POLL", false); for (int i = 0; i < nfds; ++i) { // Translate native to Wii events int revents = 0; for (auto& map : mapping) { if (ufds[i].revents & map.native) revents |= map.wii; } // No need to change fd or events as they are input only. // Memory::Write_U32(ufds[i].fd, request.buffer_out + 0xc*i); //fd // Memory::Write_U32(events, request.buffer_out + 0xc*i + 4); //events Memory::Write_U32(revents, request.buffer_out + 0xc * i + 8); // revents DEBUG_LOG(IOS_NET, "IOCTL_SO_POLL socket %d wevents %08X events %08X revents %08X", i, revents, ufds[i].events, ufds[i].revents); } return_value = ret; break; } case IOCTL_SO_GETHOSTBYNAME: { if (request.buffer_out_size != 0x460) { ERROR_LOG(IOS_NET, "Bad buffer size for IOCTL_SO_GETHOSTBYNAME"); return_value = -1; break; } std::string hostname = Memory::GetString(request.buffer_in); hostent* remoteHost = gethostbyname(hostname.c_str()); INFO_LOG(IOS_NET, "IOCTL_SO_GETHOSTBYNAME " "Address: %s, BufferIn: (%08x, %i), BufferOut: (%08x, %i)", hostname.c_str(), request.buffer_in, request.buffer_in_size, request.buffer_out, request.buffer_out_size); if (remoteHost) { for (int i = 0; remoteHost->h_aliases[i]; ++i) { DEBUG_LOG(IOS_NET, "alias%i:%s", i, remoteHost->h_aliases[i]); } for (int i = 0; remoteHost->h_addr_list[i]; ++i) { u32 ip = Common::swap32(*(u32*)(remoteHost->h_addr_list[i])); std::string ip_s = StringFromFormat("%i.%i.%i.%i", ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); DEBUG_LOG(IOS_NET, "addr%i:%s", i, ip_s.c_str()); } // Host name; located immediately after struct static const u32 GETHOSTBYNAME_STRUCT_SIZE = 0x10; static const u32 GETHOSTBYNAME_IP_LIST_OFFSET = 0x110; // Limit host name length to avoid buffer overflow. u32 name_length = (u32)strlen(remoteHost->h_name) + 1; if (name_length > (GETHOSTBYNAME_IP_LIST_OFFSET - GETHOSTBYNAME_STRUCT_SIZE)) { ERROR_LOG(IOS_NET, "Hostname too long in IOCTL_SO_GETHOSTBYNAME"); return_value = -1; break; } Memory::CopyToEmu(request.buffer_out + GETHOSTBYNAME_STRUCT_SIZE, remoteHost->h_name, name_length); Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_STRUCT_SIZE, request.buffer_out); // IP address list; located at offset 0x110. u32 num_ip_addr = 0; while (remoteHost->h_addr_list[num_ip_addr]) num_ip_addr++; // Limit number of IP addresses to avoid buffer overflow. // (0x460 - 0x340) / sizeof(pointer) == 72 static const u32 GETHOSTBYNAME_MAX_ADDRESSES = 71; num_ip_addr = std::min(num_ip_addr, GETHOSTBYNAME_MAX_ADDRESSES); for (u32 i = 0; i < num_ip_addr; ++i) { u32 addr = request.buffer_out + GETHOSTBYNAME_IP_LIST_OFFSET + i * 4; Memory::Write_U32_Swap(*(u32*)(remoteHost->h_addr_list[i]), addr); } // List of pointers to IP addresses; located at offset 0x340. // This must be exact: PPC code to convert the struct hardcodes // this offset. static const u32 GETHOSTBYNAME_IP_PTR_LIST_OFFSET = 0x340; Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET, request.buffer_out + 12); for (u32 i = 0; i < num_ip_addr; ++i) { u32 addr = request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + i * 4; Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_LIST_OFFSET + i * 4, addr); } Memory::Write_U32(0, request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + num_ip_addr * 4); // Aliases - empty. (Hardware doesn't return anything.) Memory::Write_U32(request.buffer_out + GETHOSTBYNAME_IP_PTR_LIST_OFFSET + num_ip_addr * 4, request.buffer_out + 4); // Returned struct must be ipv4. _assert_msg_(IOS_NET, remoteHost->h_addrtype == AF_INET && remoteHost->h_length == sizeof(u32), "returned host info is not IPv4"); Memory::Write_U16(AF_INET, request.buffer_out + 8); Memory::Write_U16(sizeof(u32), request.buffer_out + 10); return_value = 0; } else { return_value = -1; } break; } case IOCTL_SO_ICMPCANCEL: ERROR_LOG(IOS_NET, "IOCTL_SO_ICMPCANCEL"); default: request.DumpUnknown(GetDeviceName(), LogTypes::IOS_NET); }
IPCCommandResult CWII_IPC_HLE_Device_usb_oh0_46d_a03::IOCtlV(u32 CommandAddress) { IPCCommandResult SendReply = GetNoReply(); SIOCtlVBuffer CommandBuffer(CommandAddress); switch (CommandBuffer.Parameter) { case USBV0_IOCTL_CTRLMSG: { USBSetupPacket setup_packet; setup_packet.bmRequestType = *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address); setup_packet.bRequest = *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[1].m_Address); setup_packet.wValue = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[2].m_Address); setup_packet.wIndex = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[3].m_Address); setup_packet.wLength = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[4].m_Address); const u32 payload_addr = CommandBuffer.PayloadBuffer[0].m_Address; #define DIR_TO_DEV 0 #define DIR_TO_HOST 1 #define TYPE_STANDARD 0 #define TYPE_CLASS 1 #define TYPE_VENDOR 2 #define RECP_DEV 0 #define RECP_INT 1 #define RECP_ENDP 2 #define USBHDR(dir, type, recipient, request) \ ((((dir << 7) | (type << 5) | recipient) << 8) | request) switch (((u16)setup_packet.bmRequestType << 8) | setup_packet.bRequest) { case USBHDR(DIR_TO_HOST, TYPE_STANDARD, RECP_DEV, 6): // GET_DESCRIPTOR switch (setup_packet.wValue >> 8) { // CONFIGURATION case 2: { const usb_configurationdesc config = { 9, 2, 121, 2, 1, 3, 0x80, 30 }; if (setup_packet.wLength == 9) { memcpy(Memory::GetPointer(payload_addr), &config, setup_packet.wLength); } else { #define LE24(x) (x & 0xff), ((x >> 8) & 0xff), (x >> 16) #pragma pack(push, 1) struct { usb_configurationdesc config; usb_interfacedesc int0; struct audiocontrol_hdr { u8 bLength; u8 bDescriptorType; u8 bDescriptorSubtype; }; struct { audiocontrol_hdr hdr; u16 bcdADC; u16 wTotalLength; u8 bInCollection; u8 baInterfaceNr; } audiocontrol_header; struct { audiocontrol_hdr hdr; u8 bTerminalID; u16 wTerminalType; u8 bAssocTerminal; u8 bNrChannels; u16 wChannelConfig; u8 iChannelNames; u8 iTerminal; } audiocontrol_input_terminal; struct { audiocontrol_hdr hdr; u8 bUnitID; u8 bSourceID; u8 bControlSize; u8 bmaControls0; u8 bmaControls1; u8 iFeature; } audiocontrol_feature_unit; struct { audiocontrol_hdr hdr; u8 bTerminalID; u16 wTerminalType; u8 bAssocTerminal; u8 bSourceID; u8 iTerminal; } audiocontrol_output_terminal; usb_interfacedesc int1; usb_interfacedesc int2; struct { audiocontrol_hdr hdr; u8 bTerminalLink; u8 bDelay; u16 wFormatTag; } audiocontrol_as_general; struct { audiocontrol_hdr hdr; u8 bFormatType; u8 bNrChannels; u8 bSubframeSize; u8 bBitResolution; u8 bSamFreqType; u8 tSamFreq[3 * 5]; } audiocontrol_format_type; usb_endpointdesc endp; struct { audiocontrol_hdr hdr; u8 bmAttributes; u8 bLockDelayUnits; u16 wLockDelay; } audiocontrol_ep_general; } const fullconfig = { config, { 9, 4, 0, 0, 0, 1, 1, 0, 0 }, { 9, 36, 1, 0x100, 39, 1, 1 }, { 12, 36, 2, 13, 0x201, 0, 1, 0, 0, 0 }, { 9, 36, 6, 2, 13, 1, 3, 0 }, { 9, 36, 3, 10, 0x101, 0, 2, 0 }, { 9, 4, 1, 0, 0, 1, 2, 0, 0 }, { 9, 4, 1, 1, 1, 1, 2, 0, 0 }, { 7, 36, 1, 10, 0, 1 }, { 23, 36, 2, 1, 1, 2, 16, 5, LE24(8000), LE24(11025), LE24(22050), LE24(44100), LE24(48000) }, { 9, 5, 0x84, 13, 0x60, 1, 0, 0 }, { 7, 37, 1, 1, 2, 1 } }; #pragma pack(pop) #undef LE24 memcpy(Memory::GetPointer(payload_addr), &fullconfig, setup_packet.wLength); } Memory::Write_U32(sizeof(USBSetupPacket) + setup_packet.wLength, CommandAddress + 4); return GetDefaultReply(); } break; default: goto outerdefault; } break; case USBHDR(DIR_TO_HOST, TYPE_CLASS, RECP_INT, 0x82): case USBHDR(DIR_TO_HOST, TYPE_CLASS, RECP_INT, 0x83): if (setup_packet.bRequest & 1) Memory::Write_U16(0x7fff, payload_addr); else Memory::Write_U16(0x8000, payload_addr); break; case USBHDR(DIR_TO_DEV, TYPE_CLASS, RECP_ENDP, 1): { u32 freq = *(u32*)Memory::GetPointer(payload_addr) & 0xffffff; WARN_LOG(OSHLE, "set freq: %x", freq); } break; case USBHDR(DIR_TO_DEV, TYPE_STANDARD, RECP_INT, 11): break; outerdefault: default: WARN_LOG(OSHLE, "UNK %02x %02x %04x %04x", setup_packet.bmRequestType, setup_packet.bRequest, setup_packet.wValue, setup_packet.wLength); break; } // command finished, send a reply to command WII_IPC_HLE_Interface::EnqueueReply(CommandBuffer.m_Address); } break; case USBV0_IOCTL_ISOMSG: { // endp 81 = mic -> console // endp 03 = console -> mic u8 endpoint = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); u16 length = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address); u8 num_packets = Memory::Read_U8(CommandBuffer.InBuffer[2].m_Address); u16 *packet_sizes = (u16*)Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address); u8 *packets = Memory::GetPointer(CommandBuffer.PayloadBuffer[1].m_Address); u16 packet_len = Common::swap16(packet_sizes[0]); WARN_LOG(OSHLE, "%i to endpoint %02x", packet_len, endpoint); /* for (int i = 0; i < num_packets; i++) { u16 packet_len = Common::swap16(packet_sizes[i]); WARN_LOG(OSHLE, "packet %i [%i] to endpoint %02x", i, packet_len, endpoint); WARN_LOG(OSHLE, "%s", ArrayToString(packets, packet_len, 16).c_str()); packets += packet_len; } */ if (endpoint == AUDIO_IN) for (u16 *sample = (u16*)packets; sample != (u16*)(packets + length); sample++) *sample = 0; // TODO actual responses should obey some kinda timey thing SendReply = GetDefaultReply(); } break; default: WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str()); WARN_LOG(OSHLE, " Parameter: 0x%x", CommandBuffer.Parameter); WARN_LOG(OSHLE, " NumberIn: 0x%08x", CommandBuffer.NumberInBuffer); WARN_LOG(OSHLE, " NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer); WARN_LOG(OSHLE, " BufferVector: 0x%08x", CommandBuffer.BufferVector); //DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer, LogTypes::OSHLE, LogTypes::LWARNING); break; } Memory::Write_U32(0, CommandAddress + 4); return SendReply; }
IPCCommandResult CWII_IPC_HLE_Device_usb_oh0_57e_308::IOCtlV(u32 CommandAddress) { IPCCommandResult SendReply = GetNoReply(); SIOCtlVBuffer CommandBuffer(CommandAddress); switch (CommandBuffer.Parameter) { case USBV0_IOCTL_CTRLMSG: { USBSetupPacket setup_packet; setup_packet.bmRequestType = *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address); setup_packet.bRequest = *( u8*)Memory::GetPointer(CommandBuffer.InBuffer[1].m_Address); setup_packet.wValue = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[2].m_Address); setup_packet.wIndex = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[3].m_Address); setup_packet.wLength = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[4].m_Address); const u32 payload_addr = CommandBuffer.PayloadBuffer[0].m_Address; static bool initialized = false; #define DIR_TO_DEV 0 #define DIR_TO_HOST 1 #define TYPE_STANDARD 0 #define TYPE_VENDOR 2 #define RECP_DEV 0 #define RECP_INT 1 #define RECP_ENDP 2 #define USBHDR(dir, type, recipient, request) \ ((((dir << 7) | (type << 5) | recipient) << 8) | request) switch (((u16)setup_packet.bmRequestType << 8) | setup_packet.bRequest) { case USBHDR(DIR_TO_DEV, TYPE_STANDARD, RECP_INT, 11): _dbg_assert_(OSHLE, setup_packet.wValue == 1); break; case USBHDR(DIR_TO_HOST, TYPE_STANDARD, RECP_INT, 10): Memory::Write_U8(1, payload_addr); break; case USBHDR(DIR_TO_HOST, TYPE_VENDOR, RECP_INT, 6): if (!initialized) Memory::Write_U8(0, payload_addr), initialized = true; else Memory::Write_U8(1, payload_addr); break; case USBHDR(DIR_TO_DEV, TYPE_VENDOR, RECP_INT, 1): SetRegister(payload_addr); break; case USBHDR(DIR_TO_HOST, TYPE_VENDOR, RECP_INT, 2): GetRegister(payload_addr); break; case USBHDR(DIR_TO_DEV, TYPE_VENDOR, RECP_INT, 0): initialized = false; break; default: WARN_LOG(OSHLE, "UNK %02x %02x %04x %04x", setup_packet.bmRequestType, setup_packet.bRequest, setup_packet.wValue, setup_packet.wLength); break; } // command finished, send a reply to command WII_IPC_HLE_Interface::EnqueueReply(CommandBuffer.m_Address); } break; case USBV0_IOCTL_BLKMSG: { u8 Command = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); switch (Command) { // used for sending firmware case DATA_OUT: { u16 len = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address); WARN_LOG(OSHLE, "SEND DATA %x %x %x", len, CommandBuffer.PayloadBuffer[0].m_Address, CommandBuffer.PayloadBuffer[0].m_Size); SendReply = GetDefaultReply(); } break; default: WARN_LOG(OSHLE, "UNK BLKMSG %i", Command); break; } } break; case USBV0_IOCTL_ISOMSG: { // endp 81 = mic -> console // endp 03 = console -> mic u8 endpoint = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); u16 length = Memory::Read_U16(CommandBuffer.InBuffer[1].m_Address); u8 num_packets = Memory::Read_U8(CommandBuffer.InBuffer[2].m_Address); u16 *packet_sizes = (u16*)Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address); u8 *packets = Memory::GetPointer(CommandBuffer.PayloadBuffer[1].m_Address); /* for (int i = 0; i < num_packets; i++) { u16 packet_len = Common::swap16(packet_sizes[i]); WARN_LOG(OSHLE, "packet %i [%i] to endpoint %02x", i, packet_len, endpoint); WARN_LOG(OSHLE, "%s", ArrayToString(packets, packet_len, 16).c_str()); packets += packet_len; } */ if (endpoint == AUDIO_IN) for (u16 *sample = (u16*)packets; sample != (u16*)(packets + length); sample++) *sample = 0x8000; // TODO actual responses should obey some kinda timey thing SendReply = GetDefaultReply(); } break; default: WARN_LOG(OSHLE, "%s - IOCtlV:", GetDeviceName().c_str()); WARN_LOG(OSHLE, " Parameter: 0x%x", CommandBuffer.Parameter); WARN_LOG(OSHLE, " NumberIn: 0x%08x", CommandBuffer.NumberInBuffer); WARN_LOG(OSHLE, " NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer); WARN_LOG(OSHLE, " BufferVector: 0x%08x", CommandBuffer.BufferVector); //DumpAsync(CommandBuffer.BufferVector, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer, LogTypes::OSHLE, LogTypes::LWARNING); break; } Memory::Write_U32(0, CommandAddress + 4); return SendReply; }
IPCCommandResult CWII_IPC_HLE_Device_usb_ven::IOCtl(u32 CommandAddress) { IPCCommandResult SendReply = GetNoReply(); u32 Command = Memory::Read_U32(CommandAddress + 0x0c); u32 BufferIn = Memory::Read_U32(CommandAddress + 0x10); u32 BufferInSize = Memory::Read_U32(CommandAddress + 0x14); u32 BufferOut = Memory::Read_U32(CommandAddress + 0x18); u32 BufferOutSize = Memory::Read_U32(CommandAddress + 0x1c); WARN_LOG(OSHLE, "%s - IOCtl:%x", GetDeviceName().c_str(), Command); WARN_LOG(OSHLE, "%x:%x %x:%x", BufferIn, BufferInSize, BufferOut, BufferOutSize); switch (Command) { case USBV5_IOCTL_GETVERSION: Memory::Write_U32(0x50001, BufferOut); SendReply = GetDefaultReply(); break; case USBV5_IOCTL_GETDEVICECHANGE: { // fd Memory::Write_U32(0xcd000030, BufferOut); // vid, pid Memory::Write_U32(0x046d0a03, BufferOut + 4); // token //Memory::Write_U32(0, BufferOut + 8); // sent on change static bool firstcall = true; if (firstcall) SendReply = GetDefaultReply(), firstcall = false; // num devices Memory::Write_U32(1, CommandAddress + 4); return SendReply; } break; case USBV5_IOCTL_ATTACHFINISH: SendReply = GetDefaultReply(); break; case USBV5_IOCTL_SUSPEND_RESUME: WARN_LOG(OSHLE, "device:%i resumed:%i", Memory::Read_U32(BufferIn), Memory::Read_U32(BufferIn + 4)); SendReply = GetDefaultReply(); break; case USBV5_IOCTL_GETDEVPARAMS: { s32 device = Memory::Read_U32(BufferIn); u32 unk = Memory::Read_U32(BufferIn + 4); WARN_LOG(OSHLE, "USBV5_IOCTL_GETDEVPARAMS device:%i unk:%i", device, unk); Memory::Write_U32(0, BufferOut); SendReply = GetDefaultReply(); } break; default: //WARN_LOG(OSHLE, "%x:%x %x:%x", BufferIn, BufferInSize, BufferOut, BufferOutSize); break; } Memory::Write_U32(0, CommandAddress + 4); return SendReply; }