USB_HIDv5: Submit interrupt transfers to the correct endpoint

Unlike VEN, the endpoint is determined by the value at 8-12.
If it's non-zero, HID submits the request to the interrupt OUT
endpoint. Otherwise, the request is submitted to the IN endpoint.

This commit changes HIDv5 to keep track of endpoints (like IOS does)
and use them when submitting interrupt transfers.
This commit is contained in:
Léo Lam 2017-11-03 11:17:12 +01:00
parent ff52333b14
commit ac3b866083
6 changed files with 62 additions and 20 deletions

View File

@ -174,23 +174,6 @@ IPCCommandResult USBV5ResourceManager::SuspendResume(USBV5Device& device,
return GetDefaultReply(IPC_SUCCESS);
}
s32 USBV5ResourceManager::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
{
switch (ioctlv.request)
{
case USB::IOCTLV_USBV5_CTRLMSG:
return device.SubmitTransfer(std::make_unique<USB::V5CtrlMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_INTRMSG:
return device.SubmitTransfer(std::make_unique<USB::V5IntrMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_BULKMSG:
return device.SubmitTransfer(std::make_unique<USB::V5BulkMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_ISOMSG:
return device.SubmitTransfer(std::make_unique<USB::V5IsoMessage>(m_ios, ioctlv));
default:
return IPC_EINVAL;
}
}
IPCCommandResult USBV5ResourceManager::HandleDeviceIOCtl(const IOCtlRequest& request,
Handler handler)
{

View File

@ -86,7 +86,6 @@ protected:
IPCCommandResult SetAlternateSetting(USBV5Device& device, const IOCtlRequest& request);
IPCCommandResult Shutdown(const IOCtlRequest& request);
IPCCommandResult SuspendResume(USBV5Device& device, const IOCtlRequest& request);
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& request);
using Handler = std::function<IPCCommandResult(USBV5Device&)>;
IPCCommandResult HandleDeviceIOCtl(const IOCtlRequest& request, Handler handler);

View File

@ -71,13 +71,40 @@ IPCCommandResult USB_HIDv5::IOCtlV(const IOCtlVRequest& request)
auto host_device = GetDeviceById(device->host_id);
host_device->Attach(device->interface_number);
return HandleTransfer(host_device, request.request,
[&, this]() { return SubmitTransfer(*host_device, request); });
[&, this]() { return SubmitTransfer(*device, *host_device, request); });
}
default:
return GetDefaultReply(IPC_EINVAL);
}
}
s32 USB_HIDv5::SubmitTransfer(USBV5Device& device, USB::Device& host_device,
const IOCtlVRequest& ioctlv)
{
switch (ioctlv.request)
{
case USB::IOCTLV_USBV5_CTRLMSG:
return host_device.SubmitTransfer(std::make_unique<USB::V5CtrlMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_INTRMSG:
{
auto message = std::make_unique<USB::V5IntrMessage>(m_ios, ioctlv);
// Unlike VEN, the endpoint is determined by the value at 8-12.
// If it's non-zero, HID submits the request to the interrupt OUT endpoint.
// Otherwise, the request is submitted to the IN endpoint.
AdditionalDeviceData* data = &m_additional_device_data[&device - m_usbv5_devices.data()];
if (Memory::Read_U32(ioctlv.in_vectors[0].address + 8) != 0)
message->endpoint = data->interrupt_out_endpoint;
else
message->endpoint = data->interrupt_in_endpoint;
return host_device.SubmitTransfer(std::move(message));
}
default:
return IPC_EINVAL;
}
}
IPCCommandResult USB_HIDv5::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request)
{
// FIXME: Unlike VEN, there are 3 valid values for the endpoint,
@ -128,7 +155,15 @@ IPCCommandResult USB_HIDv5::GetDeviceInfo(USBV5Device& device, const IOCtlReques
constexpr u8 ENDPOINT_IN = 0x80;
if (endpoint.bmAttributes == ENDPOINT_INTERRUPT)
{
const u32 offset = (endpoint.bEndpointAddress & ENDPOINT_IN) != 0 ? 80 : 88;
const bool is_in_endpoint = (endpoint.bEndpointAddress & ENDPOINT_IN) != 0;
AdditionalDeviceData* data = &m_additional_device_data[&device - m_usbv5_devices.data()];
if (is_in_endpoint)
data->interrupt_in_endpoint = endpoint.bEndpointAddress;
else
data->interrupt_out_endpoint = endpoint.bEndpointAddress;
const u32 offset = is_in_endpoint ? 80 : 88;
endpoint.Swap();
Memory::CopyToEmu(request.buffer_out + offset, &endpoint, sizeof(endpoint));
}

View File

@ -27,9 +27,16 @@ public:
private:
IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request);
IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request);
s32 SubmitTransfer(USBV5Device& device, USB::Device& host_device, const IOCtlVRequest& ioctlv);
bool ShouldAddDevice(const USB::Device& device) const override;
bool HasInterfaceNumberInIDs() const override { return true; }
struct AdditionalDeviceData
{
u8 interrupt_in_endpoint = 0;
u8 interrupt_out_endpoint = 0;
};
std::array<AdditionalDeviceData, 32> m_additional_device_data{};
};
} // namespace Device
} // namespace HLE

View File

@ -95,6 +95,23 @@ IPCCommandResult USB_VEN::IOCtlV(const IOCtlVRequest& request)
}
}
s32 USB_VEN::SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv)
{
switch (ioctlv.request)
{
case USB::IOCTLV_USBV5_CTRLMSG:
return device.SubmitTransfer(std::make_unique<USB::V5CtrlMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_INTRMSG:
return device.SubmitTransfer(std::make_unique<USB::V5IntrMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_BULKMSG:
return device.SubmitTransfer(std::make_unique<USB::V5BulkMessage>(m_ios, ioctlv));
case USB::IOCTLV_USBV5_ISOMSG:
return device.SubmitTransfer(std::make_unique<USB::V5IsoMessage>(m_ios, ioctlv));
default:
return IPC_EINVAL;
}
}
IPCCommandResult USB_VEN::CancelEndpoint(USBV5Device& device, const IOCtlRequest& request)
{
const u8 endpoint = static_cast<u8>(Memory::Read_U32(request.buffer_in + 8));

View File

@ -29,6 +29,7 @@ private:
IPCCommandResult CancelEndpoint(USBV5Device& device, const IOCtlRequest& request);
IPCCommandResult GetDeviceInfo(USBV5Device& device, const IOCtlRequest& request);
s32 SubmitTransfer(USB::Device& device, const IOCtlVRequest& ioctlv);
bool HasInterfaceNumberInIDs() const override { return false; }
};
} // namespace Device