USB: Usbd support for microphones

This commit is contained in:
Florin9doi 2026-01-09 14:31:09 +02:00 committed by Megamouse
parent cf6aaaa74f
commit d9edf98d55
9 changed files with 831 additions and 11 deletions

View file

@ -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

View file

@ -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<usb_device_mic>(0, get_new_location(), MicType::Logitech));
break;
case microphone_handler::real_singstar:
case microphone_handler::singstar:
usb_devices.push_back(std::make_shared<usb_device_mic>(0, get_new_location(), MicType::SingStar));
break;
case microphone_handler::rocksmith:
usb_devices.push_back(std::make_shared<usb_device_mic>(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<ppu_thread>(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<u8>(+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<named_thread<usb_handler_thread>>();
@ -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<named_thread<usb_handler_thread>>();

View file

@ -56,7 +56,7 @@ struct UsbDeviceRequest
struct UsbDeviceIsoRequest
{
vm::ptr<void> buf;
vm::bptr<void> buf;
be_t<u32> start_frame;
be_t<u32> num_packets;
be_t<u16> packets[8];

View file

@ -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<u8>(wIndex)); break;
case LIBUSB_REQUEST_SET_INTERFACE: usb_device::set_interface(::narrow<u8>(wIndex), ::narrow<u8>(wValue)); break;
default: sys_usbd.error("Unhandled control transfer(0x%02x): 0x%02x", bmRequestType, bRequest); break;
}
break;

View file

@ -86,6 +86,54 @@ struct UsbDeviceHID
le_t<u16, 1> wDescriptorLength;
};
struct UsbAudioInputTerminal
{
u8 bDescriptorSubtype;
u8 bTerminalID;
le_t<u16, 1> wTerminalType;
u8 bAssocTerminal;
u8 bNrChannels;
le_t<u16, 1> wChannelConfig;
u8 iChannelNames;
u8 iTerminal;
};
struct UsbAudioOutputTerminal
{
u8 bDescriptorSubtype;
u8 bTerminalID;
le_t<u16, 1> wTerminalType;
u8 bAssocTerminal;
u8 bSourceID;
u8 iTerminal;
};
struct UsbAudioInterface
{
u8 bDescriptorSubtype;
u8 bTerminalLink;
u8 bDelay;
le_t<u16, 1> wFormatTag;
};
struct UsbAudioEndpoint
{
u8 bEndpointAddress;
u8 bmAttributes;
le_t<u16, 1> wMaxPacketSize;
u8 bInterval;
u8 bRefresh;
u8 bSynchAddress;
};
struct UsbAudioStreamingEndpoint
{
u8 bDescriptorSubtype;
u8 bmAttributes;
u8 bLockDelayUnits;
le_t<u16, 1> 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<u8, 7> 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;

View file

@ -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<u8, 7>& 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> usb_device_mic::make_singstar(u32 controller_index, const std::array<u8, 7>& location)
{
return std::make_shared<usb_device_mic>(controller_index, location, MicType::SingStar);
}
std::shared_ptr<usb_device> usb_device_mic::make_logitech(u32 controller_index, const std::array<u8, 7>& location)
{
return std::make_shared<usb_device_mic>(controller_index, location, MicType::Logitech);
}
std::shared_ptr<usb_device> usb_device_mic::make_rocksmith(u32 controller_index, const std::array<u8, 7>& location)
{
return std::make_shared<usb_device_mic>(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<mic_thread>();
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<u8*>(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<u8>(m_mic_type), transfer->iso_request.buf, transfer->iso_request.start_frame, transfer->iso_request.num_packets, index, inlen, outlen);
}
}

View file

@ -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<u8, 7>& location, MicType mic_type);
static std::shared_ptr<usb_device> make_singstar(u32 controller_index, const std::array<u8, 7>& location);
static std::shared_ptr<usb_device> make_logitech(u32 controller_index, const std::array<u8, 7>& location);
static std::shared_ptr<usb_device> make_rocksmith(u32 controller_index, const std::array<u8, 7>& 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];
};

View file

@ -459,6 +459,7 @@
<ClCompile Include="Emu\Io\Skylander.cpp" />
<ClCompile Include="Emu\Io\KamenRider.cpp" />
<ClCompile Include="Emu\Io\usb_device.cpp" />
<ClCompile Include="Emu\Io\usb_microphone.cpp" />
<ClCompile Include="Emu\Io\usb_vfs.cpp" />
<ClCompile Include="Emu\RSX\Capture\rsx_capture.cpp" />
<ClCompile Include="Emu\RSX\Capture\rsx_replay.cpp" />
@ -831,6 +832,7 @@
<ClInclude Include="Emu\Io\Skylander.h" />
<ClInclude Include="Emu\Io\KamenRider.h" />
<ClInclude Include="Emu\Io\usb_device.h" />
<ClInclude Include="Emu\Io\usb_microphone.h" />
<ClInclude Include="Emu\Io\usb_vfs.h" />
<ClInclude Include="Emu\IPC.h" />
<ClInclude Include="Emu\Audio\AudioDumper.h" />

View file

@ -924,6 +924,9 @@
<ClCompile Include="Emu\Io\usb_device.cpp">
<Filter>Emu\Io</Filter>
</ClCompile>
<ClCompile Include="Emu\Io\usb_microphone.cpp">
<Filter>Emu\Io</Filter>
</ClCompile>
<ClCompile Include="Emu\Io\usb_vfs.cpp">
<Filter>Emu\Io</Filter>
</ClCompile>
@ -2121,6 +2124,9 @@
<ClInclude Include="Emu\Io\usb_device.h">
<Filter>Emu\Io</Filter>
</ClInclude>
<ClInclude Include="Emu\Io\usb_microphone.h">
<Filter>Emu\Io</Filter>
</ClInclude>
<ClInclude Include="Emu\Io\usb_vfs.h">
<Filter>Emu\Io</Filter>
</ClInclude>