diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index a96ddfa2b3..a4e1ae5d64 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -423,6 +423,7 @@ target_sources(rpcs3_emu PRIVATE Io/TopShotFearmaster.cpp Io/Turntable.cpp Io/usb_device.cpp + Io/usb_microphone.cpp Io/usb_vfs.cpp Io/usio.cpp Io/LogitechG27.cpp diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp index 3afa2a9e06..dffd211aed 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp @@ -14,6 +14,7 @@ #include "Emu/Cell/timers.hpp" #include "Emu/Io/usb_device.h" +#include "Emu/Io/usb_microphone.h" #include "Emu/Io/usb_vfs.h" #include "Emu/Io/Skylander.h" #include "Emu/Io/Infinity.h" @@ -185,6 +186,9 @@ private: // Music devices {0x1415, 0x0000, 0x0000, "Singstar Microphone", nullptr, nullptr}, // {0x1415, 0x0020, 0x0020, "SingStar Microphone Wireless", nullptr, nullptr}, // TODO: verifiy + // {0x12ba, 0x00f0, 0x00f0, "Bandfuse USB Guitar Adapter", nullptr, nullptr}, + // {0x28aa, 0x0001, 0x0001, "Bandfuse USB Microphone", nullptr, nullptr}, + // {0x046d, 0x0a03, 0x0a03, "Logitech Microphone", nullptr, nullptr}, {0x12BA, 0x00FF, 0x00FF, "Rocksmith Guitar Adapter", nullptr, nullptr}, {0x12BA, 0x0100, 0x0100, "Guitar Hero Guitar", nullptr, nullptr}, @@ -549,6 +553,20 @@ usb_handler_thread::usb_handler_thread() } } + switch (g_cfg.audio.microphone_type) + { + case microphone_handler::standard: + usb_devices.push_back(std::make_shared(0, get_new_location(), MicType::Logitech)); + break; + case microphone_handler::real_singstar: + case microphone_handler::singstar: + usb_devices.push_back(std::make_shared(0, get_new_location(), MicType::SingStar)); + break; + case microphone_handler::rocksmith: + usb_devices.push_back(std::make_shared(0, get_new_location(), MicType::Rocksmith)); + break; + } + for (int i = 0; i < 8; i++) // Add VFS USB mass storage devices (/dev_usbXXX) to the USB device list { const auto usb_info = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("/dev_usb%03d", i)); @@ -1128,7 +1146,7 @@ error_code sys_usbd_finalize(ppu_thread& ppu, u32 handle) // Forcefully awake all waiters while (auto cpu = lv2_obj::schedule(usbh.sq, SYS_SYNC_FIFO)) { - // Special ternimation signal value + // Special termination signal value cpu->gpr[4] = 4; cpu->gpr[5] = 0; cpu->gpr[6] = 0; @@ -1480,7 +1498,7 @@ error_code sys_usbd_transfer_data(ppu_thread& ppu, u32 handle, u32 id_pipe, vm:: case LIBUSB_REQUEST_SET_CONFIGURATION: { pipe.device->set_configuration(static_cast(+request->wValue)); - pipe.device->set_interface(0); + pipe.device->set_interface(0, 0); break; } default: break; @@ -1523,7 +1541,7 @@ error_code sys_usbd_isochronous_transfer_data(ppu_thread& ppu, u32 handle, u32 i { ppu.state += cpu_flag::wait; - sys_usbd.todo("sys_usbd_isochronous_transfer_data(handle=0x%x, id_pipe=0x%x, iso_request=*0x%x)", handle, id_pipe, iso_request); + sys_usbd.trace("sys_usbd_isochronous_transfer_data(handle=0x%x, id_pipe=0x%x, iso_request=*0x%x)", handle, id_pipe, iso_request); auto& usbh = g_fxo->get>(); @@ -1537,8 +1555,21 @@ error_code sys_usbd_isochronous_transfer_data(ppu_thread& ppu, u32 handle, u32 i const auto& pipe = usbh.get_pipe(id_pipe); auto&& [transfer_id, transfer] = usbh.get_free_transfer(); + transfer.iso_request.buf = iso_request->buf; + transfer.iso_request.start_frame = iso_request->start_frame; + transfer.iso_request.num_packets = iso_request->num_packets; + for (u32 index = 0; index < iso_request->num_packets; index++) + { + transfer.iso_request.packets[index] = iso_request->packets[index]; + } + pipe.device->isochronous_transfer(&transfer); + if (transfer.fake) + { + usbh.push_fake_transfer(&transfer); + } + // returns an identifier specific to the transfer return not_an_error(transfer_id); } @@ -1567,7 +1598,7 @@ error_code sys_usbd_get_isochronous_transfer_status(ppu_thread& ppu, u32 handle, { ppu.state += cpu_flag::wait; - sys_usbd.todo("sys_usbd_get_isochronous_transfer_status(handle=0x%x, id_transfer=0x%x, unk1=0x%x, request=*0x%x, result=*0x%x)", handle, id_transfer, unk1, request, result); + sys_usbd.trace("sys_usbd_get_isochronous_transfer_status(handle=0x%x, id_transfer=0x%x, unk1=0x%x, request=*0x%x, result=*0x%x)", handle, id_transfer, unk1, request, result); auto& usbh = g_fxo->get>(); diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.h b/rpcs3/Emu/Cell/lv2/sys_usbd.h index 76f5f0b061..45e9214e82 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.h +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.h @@ -56,7 +56,7 @@ struct UsbDeviceRequest struct UsbDeviceIsoRequest { - vm::ptr buf; + vm::bptr buf; be_t start_frame; be_t num_packets; be_t packets[8]; diff --git a/rpcs3/Emu/Io/usb_device.cpp b/rpcs3/Emu/Io/usb_device.cpp index 9291805a8c..e78b836595 100644 --- a/rpcs3/Emu/Io/usb_device.cpp +++ b/rpcs3/Emu/Io/usb_device.cpp @@ -40,9 +40,10 @@ bool usb_device::set_configuration(u8 cfg_num) return true; } -bool usb_device::set_interface(u8 int_num) +bool usb_device::set_interface(u8 int_num, u8 alt_num) { current_interface = int_num; + current_altsetting = alt_num; return true; } @@ -141,9 +142,9 @@ bool usb_device_passthrough::set_configuration(u8 cfg_num) return (libusb_set_configuration(lusb_handle, cfg_num) == LIBUSB_SUCCESS); }; -bool usb_device_passthrough::set_interface(u8 int_num) +bool usb_device_passthrough::set_interface(u8 int_num, u8 alt_num) { - usb_device::set_interface(int_num); + usb_device::set_interface(int_num, alt_num); return (libusb_claim_interface(lusb_handle, int_num) == LIBUSB_SUCCESS); } @@ -290,7 +291,7 @@ void usb_device_emulated::control_transfer(u8 bmRequestType, u8 bRequest, u16 wV case 0U /*silences warning*/ | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_INTERFACE: // 0x01 switch (bRequest) { - case LIBUSB_REQUEST_SET_INTERFACE: usb_device::set_interface(::narrow(wIndex)); break; + case LIBUSB_REQUEST_SET_INTERFACE: usb_device::set_interface(::narrow(wIndex), ::narrow(wValue)); break; default: sys_usbd.error("Unhandled control transfer(0x%02x): 0x%02x", bmRequestType, bRequest); break; } break; diff --git a/rpcs3/Emu/Io/usb_device.h b/rpcs3/Emu/Io/usb_device.h index 3d60bd36b0..8cd053efe2 100644 --- a/rpcs3/Emu/Io/usb_device.h +++ b/rpcs3/Emu/Io/usb_device.h @@ -86,6 +86,54 @@ struct UsbDeviceHID le_t wDescriptorLength; }; +struct UsbAudioInputTerminal +{ + u8 bDescriptorSubtype; + u8 bTerminalID; + le_t wTerminalType; + u8 bAssocTerminal; + u8 bNrChannels; + le_t wChannelConfig; + u8 iChannelNames; + u8 iTerminal; +}; + +struct UsbAudioOutputTerminal +{ + u8 bDescriptorSubtype; + u8 bTerminalID; + le_t wTerminalType; + u8 bAssocTerminal; + u8 bSourceID; + u8 iTerminal; +}; + +struct UsbAudioInterface +{ + u8 bDescriptorSubtype; + u8 bTerminalLink; + u8 bDelay; + le_t wFormatTag; +}; + +struct UsbAudioEndpoint +{ + u8 bEndpointAddress; + u8 bmAttributes; + le_t wMaxPacketSize; + u8 bInterval; + u8 bRefresh; + u8 bSynchAddress; +}; + +struct UsbAudioStreamingEndpoint +{ + u8 bDescriptorSubtype; + u8 bmAttributes; + u8 bLockDelayUnits; + le_t wLockDelay; +}; + struct UsbTransfer { u32 assigned_number = 0; @@ -183,7 +231,7 @@ public: virtual u32 get_configuration(u8* buf); virtual bool set_configuration(u8 cfg_num); - virtual bool set_interface(u8 int_num); + virtual bool set_interface(u8 int_num, u8 alt_num); virtual void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) = 0; virtual void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) = 0; @@ -198,6 +246,7 @@ public: protected: u8 current_config = 1; u8 current_interface = 0; + u8 current_altsetting = 0; std::array location{}; protected: @@ -214,7 +263,7 @@ public: void read_descriptors() override; u32 get_configuration(u8* buf) override; bool set_configuration(u8 cfg_num) override; - bool set_interface(u8 int_num) override; + bool set_interface(u8 int_num, u8 alt_num) override; void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; void isochronous_transfer(UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/usb_microphone.cpp b/rpcs3/Emu/Io/usb_microphone.cpp new file mode 100644 index 0000000000..743fadb66a --- /dev/null +++ b/rpcs3/Emu/Io/usb_microphone.cpp @@ -0,0 +1,690 @@ +#include "stdafx.h" +#include "Emu/system_config.h" +#include "Emu/IdManager.h" +#include "Emu/Io/usb_microphone.h" +#include "Emu/Cell/lv2/sys_usbd.h" +#include "Emu/Cell/Modules/cellMic.h" +#include "Input/pad_thread.h" + +LOG_CHANNEL(usb_mic_log); + +usb_device_mic::usb_device_mic(u32 controller_index, const std::array& location, MicType mic_type) + : usb_device_emulated(location) + , m_controller_index(controller_index) + , m_mic_type(mic_type) + , m_sample_rate(8000) + , m_volume(0, 0) +{ + switch (mic_type) + { + case MicType::SingStar: + { + device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, + UsbDeviceDescriptor { + .bcdUSB = 0x0110, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x08, + .idVendor = 0x1415, + .idProduct = 0x0000, + .bcdDevice = 0x0001, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01}); + auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, + UsbDeviceConfiguration { + .wTotalLength = 0x00b1, + .bNumInterfaces = 0x02, + .bConfigurationValue = 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0x80, + .bMaxPower = 0x2d})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + + static constexpr u8 audio_if0_header[] = { + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x01, // bDescriptorSubtype (CS_INTERFACE -> HEADER) + 0x00, 0x01, // bcdADC 1.00 + 0x28, 0x00, // wTotalLength 36 + 0x01, // binCollection 0x01 + 0x01, // baInterfaceNr 1 + }; + config0.add_node(UsbDescriptorNode(audio_if0_header[0], audio_if0_header[1], &audio_if0_header[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInputTerminal { + .bDescriptorSubtype = 0x02, // INPUT_TERMINAL + .bTerminalID = 0x01, + .wTerminalType = 0x0201, // Microphone + .bAssocTerminal = 0x02, + .bNrChannels = 0x02, + .wChannelConfig = 0x0003, + .iChannelNames = 0x00, + .iTerminal = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioOutputTerminal { + .bDescriptorSubtype = 0x03, // OUTPUT_TERMINAL + .bTerminalID = 0x02, + .wTerminalType = 0x0101, // USB Streaming + .bAssocTerminal = 0x01, + .bSourceID = 0x03, + .iTerminal = 0x00})); + + static constexpr u8 audio_if0_feature[] = { + 0x0A, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x06, // bDescriptorSubtype (CS_INTERFACE -> FEATURE_UNIT) + 0x03, // bUnitID + 0x01, // bSourceID + 0x01, // bControlSize 1 + 0x01, 0x02, // bmaControls[0] (Mute,Volume) + 0x02, 0x00, // bmaControls[1] (Volume,None) + }; + config0.add_node(UsbDescriptorNode(audio_if0_feature[0], audio_if0_feature[1], &audio_if0_feature[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x01, + .bNumEndpoints = 0x01, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInterface { + .bDescriptorSubtype = 0x01, // AS_GENERAL + .bTerminalLink = 0x02, + .bDelay = 0x01, + .wFormatTag = 0x0001})); + + static constexpr u8 audio_if1_alt1_type[] = { + 0x17, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x02, // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE) + 0x01, // bFormatType 1 + 0x01, // bNrChannels (Mono) + 0x02, // bSubFrameSize 2 + 0x10, // bBitResolution 16 + 0x05, // bSamFreqType 5 + 0x40, 0x1F, 0x00, // tSamFreq[1] 8000 Hz + 0x11, 0x2B, 0x00, // tSamFreq[2] 11025 Hz + 0x22, 0x56, 0x00, // tSamFreq[3] 22050 Hz + 0x44, 0xAC, 0x00, // tSamFreq[4] 44100 Hz + 0x80, 0xBB, 0x00, // tSamFreq[5] 48000 Hz + }; + config0.add_node(UsbDescriptorNode(audio_if1_alt1_type[0], audio_if1_alt1_type[1], &audio_if1_alt1_type[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbAudioEndpoint { + .bEndpointAddress = 0x81, + .bmAttributes = 0x05, + .wMaxPacketSize = 0x0064, + .bInterval = 0x01, + .bRefresh = 0x00, + .bSynchAddress = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT_ASI, + UsbAudioStreamingEndpoint { + .bDescriptorSubtype = 0x01, // EP_GENERAL + .bmAttributes = 0x01, // Sampling Freq Control + .bLockDelayUnits = 0x00, + .wLockDelay = 0x0000})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x02, + .bNumEndpoints = 0x01, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInterface { + .bDescriptorSubtype = 0x01, // AS_GENERAL + .bTerminalLink = 0x02, + .bDelay = 0x01, + .wFormatTag = 0x0001})); + + static constexpr u8 audio_if1_alt2_type[] = { + 0x17, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x02, // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE) + 0x01, // bFormatType 1 + 0x02, // bNrChannels (Stereo) + 0x02, // bSubFrameSize 2 + 0x10, // bBitResolution 16 + 0x05, // bSamFreqType 5 + 0x40, 0x1F, 0x00, // tSamFreq[1] 8000 Hz + 0x11, 0x2B, 0x00, // tSamFreq[2] 11025 Hz + 0x22, 0x56, 0x00, // tSamFreq[3] 22050 Hz + 0x44, 0xAC, 0x00, // tSamFreq[4] 44100 Hz + 0x80, 0xBB, 0x00, // tSamFreq[5] 48000 Hz + }; + config0.add_node(UsbDescriptorNode(audio_if1_alt2_type[0], audio_if1_alt2_type[1], &audio_if1_alt2_type[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbAudioEndpoint { + .bEndpointAddress = 0x81, + .bmAttributes = 0x05, + .wMaxPacketSize = 0x00c8, + .bInterval = 0x01, + .bRefresh = 0x00, + .bSynchAddress = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT_ASI, + UsbAudioStreamingEndpoint { + .bDescriptorSubtype = 0x01, // EP_GENERAL + .bmAttributes = 0x01, // Sampling Freq Control + .bLockDelayUnits = 0x00, + .wLockDelay = 0x0000})); + + add_string("Nam Tai E&E Products Ltd."); + add_string("USBMIC Serial# 012345678"); + break; + } + case MicType::Logitech: + { + device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, + UsbDeviceDescriptor { + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x08, + .idVendor = 0x046d, + .idProduct = 0x0a03, + .bcdDevice = 0x0102, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01}); + auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, + UsbDeviceConfiguration { + .wTotalLength = 0x0079, + .bNumInterfaces = 0x02, + .bConfigurationValue = 0x01, + .iConfiguration = 0x03, + .bmAttributes = 0x80, + .bMaxPower = 0x1e})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + + static constexpr u8 audio_if0_header[] = { + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x01, // bDescriptorSubtype (CS_INTERFACE -> HEADER) + 0x00, 0x01, // bcdADC 1.00 + 0x27, 0x00, // wTotalLength 39 + 0x01, // binCollection 0x01 + 0x01, // baInterfaceNr 1 + }; + config0.add_node(UsbDescriptorNode(audio_if0_header[0], audio_if0_header[1], &audio_if0_header[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInputTerminal { + .bDescriptorSubtype = 0x02, // INPUT_TERMINAL + .bTerminalID = 0x0d, + .wTerminalType = 0x0201, // Microphone + .bAssocTerminal = 0x00, + .bNrChannels = 0x01, + .wChannelConfig = 0x0000, + .iChannelNames = 0x00, + .iTerminal = 0x00})); + static constexpr u8 audio_if0_feature[] = { + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x06, // bDescriptorSubtype (CS_INTERFACE -> FEATURE_UNIT) + 0x02, // bUnitID + 0x0d, // bSourceID + 0x01, // bControlSize 1 + 0x03, // bmaControls[0] (Mute,Volume) + 0x00, // bmaControls[1] (None) + 0x00, // iFeature + }; + config0.add_node(UsbDescriptorNode(audio_if0_feature[0], audio_if0_feature[1], &audio_if0_feature[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioOutputTerminal { + .bDescriptorSubtype = 0x03, // OUTPUT_TERMINAL + .bTerminalID = 0x0a, + .wTerminalType = 0x0101, // USB Streaming + .bAssocTerminal = 0x00, + .bSourceID = 0x02, + .iTerminal = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x01, + .bNumEndpoints = 0x01, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInterface { + .bDescriptorSubtype = 0x01, // AS_GENERAL + .bTerminalLink = 0x0a, + .bDelay = 0x00, + .wFormatTag = 0x0001})); + + static constexpr u8 audio_if1_alt1_type[] = { + 0x17, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x02, // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE) + 0x01, // bFormatType 1 + 0x01, // bNrChannels (Mono) + 0x02, // bSubFrameSize 2 + 0x10, // bBitResolution 16 + 0x05, // bSamFreqType 5 + 0x40, 0x1F, 0x00, // tSamFreq[1] 8000 Hz + 0x11, 0x2B, 0x00, // tSamFreq[2] 11025 Hz + 0x22, 0x56, 0x00, // tSamFreq[4] 22050 Hz + 0x44, 0xAC, 0x00, // tSamFreq[6] 44100 Hz + 0x80, 0xBB, 0x00, // tSamFreq[7] 48000 Hz + }; + config0.add_node(UsbDescriptorNode(audio_if1_alt1_type[0], audio_if1_alt1_type[1], &audio_if1_alt1_type[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbAudioEndpoint { + .bEndpointAddress = 0x84, + .bmAttributes = 0x0d, + .wMaxPacketSize = 0x0060, + .bInterval = 0x01, + .bRefresh = 0x00, + .bSynchAddress = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT_ASI, + UsbAudioStreamingEndpoint { + .bDescriptorSubtype = 0x01, // EP_GENERAL + .bmAttributes = 0x01, // Sampling Freq Control + .bLockDelayUnits = 0x02, + .wLockDelay = 0x0001})); + + add_string("Logitech"); + add_string("Logitech USB Microphone"); + break; + } + case MicType::Rocksmith: + { + device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, + UsbDeviceDescriptor { + .bcdUSB = 0x0110, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x10, + .idVendor = 0x12ba, + .idProduct = 0x00ff, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01}); + auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, + UsbDeviceConfiguration { + .wTotalLength = 0x0098, + .bNumInterfaces = 0x03, + .bConfigurationValue = 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0x80, + .bMaxPower = 0x32})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + + static constexpr u8 audio_if0_header[] = { + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x01, // bDescriptorSubtype (CS_INTERFACE -> HEADER) + 0x00, 0x01, // bcdADC 1.00 + 0x27, 0x00, // wTotalLength 39 + 0x01, // binCollection 0x01 + 0x01, // baInterfaceNr 1 + }; + config0.add_node(UsbDescriptorNode(audio_if0_header[0], audio_if0_header[1], &audio_if0_header[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInputTerminal { + .bDescriptorSubtype = 0x02, // INPUT_TERMINAL + .bTerminalID = 0x02, + .wTerminalType = 0x0201, // Microphone + .bAssocTerminal = 0x00, + .bNrChannels = 0x01, + .wChannelConfig = 0x0001, + .iChannelNames = 0x00, + .iTerminal = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioOutputTerminal { + .bDescriptorSubtype = 0x03, // OUTPUT_TERMINAL + .bTerminalID = 0x07, + .wTerminalType = 0x0101, // USB Streaming + .bAssocTerminal = 0x00, + .bSourceID = 0x0a, + .iTerminal = 0x00})); + + static constexpr u8 audio_if0_feature[] = { + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x06, // bDescriptorSubtype (CS_INTERFACE -> FEATURE_UNIT) + 0x0a, // bUnitID + 0x02, // bSourceID + 0x01, // bControlSize 1 + 0x03, // bmaControls[0] (Mute,Volume) + 0x00, // bmaControls[1] (None) + 0x00, // iFeature + }; + config0.add_node(UsbDescriptorNode(audio_if0_feature[0], audio_if0_feature[1], &audio_if0_feature[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x01, + .bNumEndpoints = 0x01, + .bInterfaceClass = 0x01, + .bInterfaceSubClass = 0x02, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ACI, + UsbAudioInterface { + .bDescriptorSubtype = 0x01, // AS_GENERAL + .bTerminalLink = 0x07, + .bDelay = 0x01, + .wFormatTag = 0x0001})); + + static constexpr u8 audio_if1_alt1_type[] = { + 0x1d, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x02, // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE) + 0x01, // bFormatType 1 + 0x01, // bNrChannels (Mono) + 0x02, // bSubFrameSize 2 + 0x10, // bBitResolution 16 + 0x07, // bSamFreqType 5 + 0x40, 0x1F, 0x00, // tSamFreq[1] 8000 Hz + 0x11, 0x2B, 0x00, // tSamFreq[2] 11025 Hz + 0x80, 0x3e, 0x00, // tSamFreq[3] 16000 Hz + 0x22, 0x56, 0x00, // tSamFreq[4] 22050 Hz + 0x00, 0x7d, 0x00, // tSamFreq[5] 32000 Hz + 0x44, 0xAC, 0x00, // tSamFreq[6] 44100 Hz + 0x80, 0xBB, 0x00, // tSamFreq[7] 48000 Hz + }; + config0.add_node(UsbDescriptorNode(audio_if1_alt1_type[0], audio_if1_alt1_type[1], &audio_if1_alt1_type[2])); + + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbAudioEndpoint { + .bEndpointAddress = 0x82, + .bmAttributes = 0x0d, + .wMaxPacketSize = 0x0064, + .bInterval = 0x01, + .bRefresh = 0x00, + .bSynchAddress = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT_ASI, + UsbAudioStreamingEndpoint { + .bDescriptorSubtype = 0x01, // EP_GENERAL + .bmAttributes = 0x01, // Sampling Freq Control + .bLockDelayUnits = 0x00, + .wLockDelay = 0x0000})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, + UsbDeviceInterface { + .bInterfaceNumber = 0x02, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x01, + .bInterfaceClass = 0x03, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_HID, + UsbDeviceHID { + .bcdHID = 0x0111, + .bCountryCode = 0x00, + .bNumDescriptors = 0x01, + .bDescriptorType = 0x22, + .wDescriptorLength = 0x001a})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, + UsbDeviceEndpoint { + .bEndpointAddress = 0x87, + .bmAttributes = 0x03, + .wMaxPacketSize = 0x0010, + .bInterval = 0x01})); + + add_string("Hercules."); + add_string("Rocksmith USB Guitar Adapter"); + break; + } + } +} + +std::shared_ptr usb_device_mic::make_singstar(u32 controller_index, const std::array& location) +{ + return std::make_shared(controller_index, location, MicType::SingStar); +} + +std::shared_ptr usb_device_mic::make_logitech(u32 controller_index, const std::array& location) +{ + return std::make_shared(controller_index, location, MicType::Logitech); +} + +std::shared_ptr usb_device_mic::make_rocksmith(u32 controller_index, const std::array& location) +{ + return std::make_shared(controller_index, location, MicType::Rocksmith); +} + +u16 usb_device_mic::get_num_emu_devices() +{ + return 1; +} + +void usb_device_mic::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) +{ + transfer->fake = true; + transfer->expected_count = buf_size; + transfer->expected_result = HC_CC_NOERR; + transfer->expected_time = get_timestamp() + 1000; + + switch (bmRequestType) + { + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE: // 0x21 + switch (bRequest) + { + case SET_CUR: + { + ensure(buf_size >= 2); + const u8 ch = wValue & 0xff; + if (ch == 0) + { + m_volume[0] = (buf[1] << 8) | buf[0]; + m_volume[1] = (buf[1] << 8) | buf[0]; + usb_mic_log.notice("Set Cur Volume[%d]: 0x%04x (%d dB)", ch, m_volume[0], m_volume[0] / 256); + } + else if (ch == 1) + { + m_volume[0] = (buf[1] << 8) | buf[0]; + usb_mic_log.notice("Set Cur Volume[%d]: 0x%04x (%d dB)", ch, m_volume[0], m_volume[0] / 256); + } + else if (ch == 2) + { + m_volume[1] = (buf[1] << 8) | buf[0]; + usb_mic_log.notice("Set Cur Volume[%d]: 0x%04x (%d dB)", ch, m_volume[1], m_volume[1] / 256); + } + break; + } + default: + usb_mic_log.error("Unhandled Request: 0x%02X/0x%02X", bmRequestType, bRequest); + break; + } + break; + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_ENDPOINT: // 0x22 + switch (bRequest) + { + case SET_CUR: + ensure(buf_size >= 3); + m_sample_rate = (buf[2] << 16) | (buf[1] << 8) | buf[0]; + usb_mic_log.notice("Set Sample Rate: %d", m_sample_rate); + break; + default: + usb_mic_log.error("Unhandled Request: 0x%02X/0x%02X", bmRequestType, bRequest); + break; + } + break; + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE: // 0xa1 + switch (bRequest) + { + case GET_CUR: + { + ensure(buf_size >= 2); + const u8 ch = wValue & 0xff; + if (ch == 2) + { + buf[0] = (m_volume[1] ) & 0xff; + buf[1] = (m_volume[1] >> 8) & 0xff; + usb_mic_log.notice("Get Cur Volume[%d]: 0x%04x (%d dB)", ch, m_volume[1], m_volume[1] / 256); + } + else + { + buf[0] = (m_volume[0] ) & 0xff; + buf[1] = (m_volume[0] >> 8) & 0xff; + usb_mic_log.notice("Get Cur Volume[%d]: 0x%04x (%d dB)", ch, m_volume[0], m_volume[0] / 256); + } + break; + } + case GET_MIN: + { + ensure(buf_size >= 2); + constexpr s16 minVol = 0xff00; + buf[0] = (minVol ) & 0xff; + buf[1] = (minVol >> 8) & 0xff; + usb_mic_log.notice("Get Min Volume: 0x%04x (%d dB)", minVol, minVol / 256); + break; + } + case GET_MAX: + { + ensure(buf_size >= 2); + constexpr s16 maxVol = 0x0100; + buf[0] = (maxVol ) & 0xff; + buf[1] = (maxVol >> 8) & 0xff; + usb_mic_log.notice("Get Max Volume: 0x%04x (%d dB)", maxVol, maxVol / 256); + break; + } + default: + usb_mic_log.error("Unhandled Request: 0x%02X/0x%02X", bmRequestType, bRequest); + break; + } + break; + case 0U /*silences warning*/ | LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_ENDPOINT: // 0xa2 + switch (bRequest) + { + case GET_CUR: + ensure(buf_size >= 3); + buf[0] = (m_sample_rate ) & 0xff; + buf[1] = (m_sample_rate >> 8) & 0xff; + buf[2] = (m_sample_rate >> 16) & 0xff; + usb_mic_log.notice("Get Sample Rate: %d", m_sample_rate); + break; + default: + usb_mic_log.error("Unhandled Request: 0x%02X/0x%02X", bmRequestType, bRequest); + break; + } + break; + default: + usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer); + break; + } + + usb_mic_log.trace("control_transfer: req=0x%02X/0x%02X, val=0x%02x, idx=0x%02x, len=0x%02x, [%s]", + bmRequestType, bRequest, wValue, wIndex, wLength, fmt::buf_to_hexstring(buf, buf_size)); +} + +void usb_device_mic::isochronous_transfer(UsbTransfer* transfer) +{ + transfer->fake = true; + transfer->expected_count = 0; + transfer->expected_result = HC_CC_NOERR; + transfer->expected_time = get_timestamp() + 1000; + + const bool stereo = (m_mic_type == MicType::SingStar && current_altsetting == 2); + + auto& mic_thr = g_fxo->get(); + const std::lock_guard lock(mic_thr.mutex); + if (!mic_thr.init) + { + usb_mic_log.notice("mic init"); + mic_thr.load_config_and_init(); + mic_thr.init = 1; + } + if (!mic_thr.check_device(0)) + { + usb_mic_log.notice("mic check"); + } + microphone_device& device = ::at32(mic_thr.mic_list, 0); + if (!device.is_opened()) + { + usb_mic_log.notice("mic open"); + device.open_microphone(CELLMIC_SIGTYPE_RAW, m_sample_rate, m_sample_rate, stereo ? 2 : 1); + } + if (!device.is_started()) + { + usb_mic_log.notice("mic start"); + device.start_microphone(); + } + + u8* buf = static_cast(transfer->iso_request.buf.get_ptr()); + for (u32 index = 0; index < transfer->iso_request.num_packets; index++) + { + const u16 inlen = transfer->iso_request.packets[index] >> 4; + ensure(inlen >= (stereo ? 192 : 96)); + const u32 outlen = device.read_raw(buf, stereo ? 192 : 96); + buf += outlen; + transfer->iso_request.packets[index] = (outlen & 0xFFF) << 4; + usb_mic_log.trace(" isochronous_transfer: dev=%d, buf=0x%x, start=0x%x, pks=0x%x idx=0x%x, inlen=0x%x, outlen=0x%x", + static_cast(m_mic_type), transfer->iso_request.buf, transfer->iso_request.start_frame, transfer->iso_request.num_packets, index, inlen, outlen); + } +} diff --git a/rpcs3/Emu/Io/usb_microphone.h b/rpcs3/Emu/Io/usb_microphone.h new file mode 100644 index 0000000000..d8819a099b --- /dev/null +++ b/rpcs3/Emu/Io/usb_microphone.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Emu/Io/usb_device.h" + +enum class MicType +{ + SingStar, + Logitech, + Rocksmith, +}; + +enum +{ + SET_CUR = 0x01, + GET_CUR = 0x81, + SET_MIN = 0x02, + GET_MIN = 0x82, + SET_MAX = 0x03, + GET_MAX = 0x83, +}; + +class usb_device_mic : public usb_device_emulated +{ +public: + usb_device_mic(u32 controller_index, const std::array& location, MicType mic_type); + + static std::shared_ptr make_singstar(u32 controller_index, const std::array& location); + static std::shared_ptr make_logitech(u32 controller_index, const std::array& location); + static std::shared_ptr make_rocksmith(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; + void isochronous_transfer(UsbTransfer* transfer) override; + +private: + u32 m_controller_index; + MicType m_mic_type; + u32 m_sample_rate; + s16 m_volume[2]; +}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 7f3be85a13..15c4a26d81 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -459,6 +459,7 @@ + @@ -831,6 +832,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 7e2c398090..993d915586 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -924,6 +924,9 @@ Emu\Io + + Emu\Io + Emu\Io @@ -2121,6 +2124,9 @@ Emu\Io + + Emu\Io + Emu\Io