USB: Added support for G27 compatibility modes

- Driving Force
- Driving Force Pro
- Driving Force GT
- G25
This commit is contained in:
Florin9doi 2025-12-26 22:46:21 +02:00
parent 77aa5d4bbf
commit f66647c833
8 changed files with 648 additions and 201 deletions

View File

@ -1057,6 +1057,27 @@ void connect_usb_controller(u8 index, input::product_type type)
}
}
void reconnect_usb(u32 assigned_number)
{
auto usbh = g_fxo->try_get<named_thread<usb_handler_thread>>();
if (!usbh)
{
return;
}
std::lock_guard lock(usbh->mutex);
for (auto& [nr, pair] : usbh->handled_devices)
{
auto& [internal_dev, dev] = pair;
if (nr == assigned_number)
{
usbh->disconnect_usb_device(dev, false);
usbh->connect_usb_device(dev, false);
break;
}
}
}
void handle_hotplug_event(bool connected)
{
if (auto usbh = g_fxo->try_get<named_thread<usb_handler_thread>>())

View File

@ -89,4 +89,5 @@ error_code sys_usbd_register_extra_ldd(ppu_thread& ppu, u32 handle, vm::cptr<cha
error_code sys_usbd_unregister_extra_ldd(ppu_thread& ppu, u32 handle, vm::cptr<char> s_product, u16 slen_product);
void connect_usb_controller(u8 index, input::product_type);
void reconnect_usb(u32 assigned_number);
void handle_hotplug_event(bool connected);

View File

@ -17,7 +17,254 @@
#include "Input/pad_thread.h"
#include "Input/sdl_instance.h"
LOG_CHANNEL(logitech_g27_log, "LOGIG27");
LOG_CHANNEL(logitech_g27_log, "logitech_g27");
#pragma pack(push, 1)
struct DF_data
{
u8 square : 1;
u8 cross : 1;
u8 circle : 1;
u8 triangle : 1;
u8 l1 : 1; // Left_paddle
u8 r1 : 1; // Right_paddle
u8 l2 : 1;
u8 r2 : 1;
u8 select : 1; // Share
u8 start : 1; // Options
u8 l3 : 1;
u8 r3 : 1;
u8 ps : 1;
u8 : 3;
u8 dpad; // 00=N, 01=NE, 02=E, 03=SW, 04=S, 05=SW, 06=W, 07=NW, 08=IDLE
u8 steering; // 00=Left, ff=Right
u8 brake_throttle; // 00=ThrottlePressed, 7f=BothReleased/BothPressed, ff=BrakePressed
u8 const1;
u8 const2;
u32 : 32;
u32 : 32;
u16 : 16;
u8 brake; // 00=Released, ff=Pressed
u8 throttle; // 00=Released, ff=Pressed
u32 : 32;
u32 : 32;
};
struct DFP_data
{
u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
u16 cross: 1;
u16 square : 1;
u8 circle : 1;
u8 triangle : 1;
u8 r1 : 1; // Right_paddle
u8 l1 : 1; // Left_paddle
u8 r2 : 1;
u8 l2 : 1;
u8 select : 1; // Share
u8 start : 1; // Options
u8 r3 : 1;
u8 l3 : 1;
u8 r3_2 : 1;
u8 l3_2 : 1;
u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
u8 brake_throttle; // 00=ThrottlePressed, 7f=BothReleased/BothPressed, ff=BrakePressed
u8 throttle; // 00=Pressed, ff=Released
u8 brake; // 00=Pressed, ff=Released
u8 pedals_attached : 1;
u8 powered : 1;
u8 : 1;
u8 self_check_done : 1;
u8 set1 : 1; // always set
u8 : 3;
};
struct DFGT_data
{
u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
u8 cross: 1;
u8 square : 1;
u8 circle : 1;
u8 triangle : 1;
u8 r1 : 1; // Right_paddle
u8 l1 : 1; // Left_paddle
u8 r2 : 1;
u8 l2 : 1;
u8 select : 1; // Share
u8 start : 1; // Options
u8 r3 : 1;
u8 l3 : 1;
u8 : 2;
u8 dial_center : 1;
u8 plus : 1;
u8 dial_cw : 1;
u8 dial_ccw : 1;
u8 minus : 1;
u8 : 1;
u8 ps : 1;
u8 pedals_attached : 1;
u8 powered : 1;
u8 self_check_done : 1;
u8 set1 : 1;
u8 : 1;
u8 set2 : 1;
u8 : 1;
u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
u16 : 2;
u8 throttle; // 00=Pressed, ff=Released
u8 brake; // 00=Pressed, ff=Released
};
struct G25_data
{
u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
u8 cross: 1;
u8 square : 1;
u8 circle : 1;
u8 triangle : 1;
u8 r1 : 1; // Right_paddle
u8 l1 : 1; // Left_paddle
u8 r2 : 1; // + dial_center
u8 l2 : 1;
u8 select : 1; // Share
u8 start : 1; // Options
u8 r3 : 1; // + dial_cw + plus
u8 l3 : 1; // + dial_ccw + minus
u8 gear1 : 1;
u8 gear2 : 1;
u8 gear3 : 1;
u8 gear4 : 1;
u8 gear5 : 1;
u8 gear6 : 1;
u8 gearR : 1;
u8 : 1;
u16 pedals_detached : 1;
u16 powered : 1;
u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
u8 throttle; // 00=Pressed, ff=Released
u8 brake; // 00=Pressed, ff=Released
u8 clutch; // 00=Pressed, ff=Released
u8 shifter_x; // 30=left(1,2), 7a=middle(3,4), b2=right(5,6)
u8 shifter_y; // 32=bottom(2,4,6), b7=top(1,3,5)
u8 shifter_attached : 1;
u8 set1 : 1;
u8 : 1;
u8 shifter_pressed : 1;
u8 : 4;
};
struct G27_data
{
u8 dpad : 4; // 0=N, 1=NE, 2=E, 3=SW, 4=S, 5=SW, 6=W, 7=NW, 8=IDLE
u8 cross: 1;
u8 square : 1;
u8 circle : 1;
u8 triangle : 1;
u8 r1 : 1; // Right_paddle
u8 l1 : 1; // Left_paddle
u8 r2 : 1;
u8 l2 : 1;
u8 select : 1; // Share
u8 start : 1; // Options
u8 r3 : 1; // + dial_center
u8 l3 : 1;
u8 gear1 : 1;
u8 gear2 : 1;
u8 gear3 : 1;
u8 gear4 : 1;
u8 gear5 : 1;
u8 gear6 : 1;
u8 dial_cw : 1;
u8 dial_ccw : 1;
u16 plus : 1;
u16 minus : 1;
u16 steering : 14; // 0000=Left, 1fff=Mid, 3fff=Right
u8 throttle; // 00=Pressed, ff=Released
u8 brake; // 00=Pressed, ff=Released
u8 clutch; // 00=Pressed, ff=Released
u8 shifter_x; // 30=left(1,2), 7a=middle(3,4), b2=right(5,6)
u8 shifter_y; // 32=bottom(2,4,6), b7=top(1,3,5)
u8 gearR : 1;
u8 pedals_detached : 1;
u8 powered : 1;
u8 shifter_attached : 1;
u8 set1 : 1;
u8 : 1;
u8 shifter_pressed : 1;
u8 range : 1;
};
#pragma pack(pop)
static const std::map<logitech_personality,
std::pair<UsbDeviceDescriptor, std::array<u8, 0x29>>> s_logitech_personality = {
{
logitech_personality::driving_force,
{
UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC294, 0x1350, 0x01, 0x02, 0x00, 0x01},
{0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x09, 0x21, 0x00, 0x01, 0x21, 0x01, 0x22, 0x9D, 0x00, 0x07, 0x05, 0x81, 0x03, 0x40,
0x00, 0x0A, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x0A}
}
},
{
logitech_personality::driving_force_pro,
{
UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC298, 0x1350, 0x01, 0x02, 0x00, 0x01},
{0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x09, 0x21, 0x00, 0x01, 0x21, 0x01, 0x22, 0x61, 0x00, 0x07, 0x05, 0x81, 0x03, 0x08,
0x00, 0x0A, 0x07, 0x05, 0x01, 0x03, 0x08, 0x00, 0x0A}
}
},
{
logitech_personality::g25,
{
UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC299, 0x1350, 0x01, 0x02, 0x00, 0x01},
{0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x21, 0x01, 0x22, 0x6F, 0x00, 0x07, 0x05, 0x81, 0x03, 0x10,
0x00, 0x02, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x02}
}
},
{
logitech_personality::driving_force_gt,
{
UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC29A, 0x1350, 0x00, 0x02, 0x00, 0x01},
{0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x00, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
0x00, 0xFE, 0x09, 0x21, 0x11, 0x01, 0x21, 0x01, 0x22, 0x73, 0x00, 0x07, 0x05, 0x81, 0x03, 0x10,
0x00, 0x02, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x02}
}
},
{
logitech_personality::g27,
{
UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 0x10, 0x046D, 0xC29B, 0x1350, 0x01, 0x02, 0x00, 0x01},
{0x09, 0x02, 0x29, 0x00, 0x01, 0x01, 0x04, 0x80, 0x31, 0x09, 0x04, 0x00, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x09, 0x21, 0x11, 0x01, 0x21, 0x01, 0x22, 0x85, 0x00, 0x07, 0x05, 0x81, 0x03, 0x10,
0x00, 0x02, 0x07, 0x05, 0x01, 0x03, 0x10, 0x00, 0x02}
}
}
};
// ref: https://github.com/libsdl-org/SDL/issues/7941, need to use SDL_HAPTIC_STEERING_AXIS for some windows drivers
static const SDL_HapticDirection STEERING_DIRECTION =
@ -26,20 +273,31 @@ static const SDL_HapticDirection STEERING_DIRECTION =
.dir = {0, 0, 0}
};
usb_device_logitech_g27::usb_device_logitech_g27(u32 controller_index, const std::array<u8, 7>& location)
: usb_device_emulated(location), m_controller_index(controller_index)
void usb_device_logitech_g27::set_personality(logitech_personality personality, bool reconnect)
{
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0, 0, 0, 16, 0x046d, 0xc29b, 0x1350, 1, 2, 0, 1});
m_personality = personality;
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, ::at32(s_logitech_personality, personality).first);
// parse the raw response like with passthrough device
static constexpr u8 raw_config[] = {0x9, 0x2, 0x29, 0x0, 0x1, 0x1, 0x4, 0x80, 0x31, 0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x9, 0x21, 0x11, 0x1, 0x21, 0x1, 0x22, 0x85, 0x0, 0x7, 0x5, 0x81, 0x3, 0x10, 0x0, 0x2, 0x7, 0x5, 0x1, 0x3, 0x10, 0x0, 0x2};
const u8* raw_config = ::at32(s_logitech_personality, personality).second.data();
auto& conf = device.add_node(UsbDescriptorNode(raw_config[0], raw_config[1], &raw_config[2]));
for (unsigned int index = raw_config[0]; index < sizeof(raw_config);)
for (unsigned int index = raw_config[0]; index < raw_config[2];)
{
conf.add_node(UsbDescriptorNode(raw_config[index], raw_config[index + 1], &raw_config[index + 2]));
index += raw_config[index];
}
if (reconnect)
{
reconnect_usb(assigned_number);
}
}
usb_device_logitech_g27::usb_device_logitech_g27(u32 controller_index, const std::array<u8, 7>& location)
: usb_device_emulated(location), m_controller_index(controller_index)
{
set_personality(logitech_personality::driving_force);
m_default_spring_effect.type = SDL_HAPTIC_SPRING;
m_default_spring_effect.condition.direction = STEERING_DIRECTION;
m_default_spring_effect.condition.length = SDL_HAPTIC_INFINITY;
@ -63,7 +321,14 @@ usb_device_logitech_g27::usb_device_logitech_g27(u32 controller_index, const std
while (thread_ctrl::state() != thread_state::aborting)
{
sdl_refresh();
thread_ctrl::wait_for(5'000'000);
thread_ctrl::wait_for(1'000'000);
std::unique_lock lock(g_cfg_logitech_g27.m_mutex);
if (logitech_personality::invalid != m_next_personality && m_personality != m_next_personality)
{
set_personality(m_next_personality, true);
m_next_personality = logitech_personality::invalid;
}
}
});
}
@ -115,7 +380,7 @@ u16 usb_device_logitech_g27::get_num_emu_devices()
void usb_device_logitech_g27::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
{
logitech_g27_log.todo("control transfer bmRequestType %02x, bRequest %02x, wValue %04x, wIndex %04x, wLength %04x, %s", bmRequestType, bRequest, wValue, wIndex, wLength, fmt::buf_to_hexstring(buf, buf_size));
logitech_g27_log.notice("control transfer bmRequestType %02x, bRequest %02x, wValue %04x, wIndex %04x, wLength %04x, %s", bmRequestType, bRequest, wValue, wIndex, wLength, fmt::buf_to_hexstring(buf, buf_size));
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
}
@ -194,9 +459,11 @@ static inline logitech_g27_sdl_mapping get_runtime_mapping()
convert_mapping(cfg.dial_clockwise, mapping.dial_clockwise);
convert_mapping(cfg.dial_anticlockwise, mapping.dial_anticlockwise);
convert_mapping(cfg.dial_center, mapping.dial_center);
convert_mapping(cfg.select, mapping.select);
convert_mapping(cfg.pause, mapping.pause);
convert_mapping(cfg.start, mapping.start);
convert_mapping(cfg.ps, mapping.ps);
convert_mapping(cfg.shifter_1, mapping.shifter_1);
convert_mapping(cfg.shifter_2, mapping.shifter_2);
@ -205,7 +472,6 @@ static inline logitech_g27_sdl_mapping get_runtime_mapping()
convert_mapping(cfg.shifter_5, mapping.shifter_5);
convert_mapping(cfg.shifter_6, mapping.shifter_6);
convert_mapping(cfg.shifter_r, mapping.shifter_r);
convert_mapping(cfg.shifter_press, mapping.shifter_press);
return mapping;
}
@ -467,6 +733,50 @@ static u8 hat_components_to_logitech_g27_hat(bool up, bool down, bool left, bool
return sdl_hat_to_logitech_g27_hat(sdl_hat);
}
static std::pair<u8, u8> shifter_to_coord_xy(bool shifter_1, bool shifter_2, bool shifter_3, bool shifter_4,
bool shifter_5, bool shifter_6, bool shifter_r)
{
// rough analog values recorded in https://github.com/RPCS3/rpcs3/pull/17199#issuecomment-2883934412
constexpr u8 coord_center = 0x80;
constexpr u8 coord_top = 0xb7;
constexpr u8 coord_bottom = 0x32;
constexpr u8 coord_left = 0x30;
constexpr u8 coord_right = 0xb3;
constexpr u8 coord_right_reverse = 0xaa;
if (shifter_1)
{
return {coord_left, coord_top};
}
else if (shifter_2)
{
return {coord_left, coord_bottom};
}
else if (shifter_3)
{
return {coord_center, coord_top};
}
else if (shifter_4)
{
return {coord_center, coord_bottom};
}
else if (shifter_5)
{
return {coord_right, coord_top};
}
else if (shifter_6)
{
return {coord_right, coord_bottom};
}
else if (shifter_r)
{
return {coord_right_reverse, coord_bottom};
}
else
{
return {coord_center, coord_center};
}
}
static bool fetch_sdl_as_button(SDL_Joystick* joystick, const sdl_mapping& mapping)
{
switch (mapping.type)
@ -646,6 +956,220 @@ static inline void set_bit(u8* buf, int bit_num, bool set)
buf[byte_num] = buf[byte_num] & (~mask);
}
void usb_device_logitech_g27::transfer_df(u32 buf_size, u8* buf, UsbTransfer* transfer)
{
DF_data data{};
ensure(buf_size >= sizeof(data));
transfer->expected_count = sizeof(data);
const std::lock_guard lock(m_sdl_handles_mutex);
data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
data.dpad = hat_components_to_logitech_g27_hat(
sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
);
data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering) >> 6;
data.brake_throttle = 0x7f;
data.const1 = 0x7f;
data.const2 = 0x7f;
data.brake = 0xff - sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
data.throttle = 0xff - sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
std::memcpy(buf, &data, sizeof(data));
}
void usb_device_logitech_g27::transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer)
{
DFP_data data{};
ensure(buf_size >= sizeof(data));
transfer->expected_count = sizeof(data);
const std::lock_guard lock(m_sdl_handles_mutex);
data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
data.r3 = data.r3_2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
data.l3 = data.l3_2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
data.dpad = hat_components_to_logitech_g27_hat(
sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
);
data.brake_throttle = 0x7f;
data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
data.pedals_attached = 1;
data.powered = 1;
data.self_check_done = 1;
data.set1 = 1;
std::memcpy(buf, &data, sizeof(data));
}
void usb_device_logitech_g27::transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer)
{
DFGT_data data{};
ensure(buf_size >= sizeof(data));
transfer->expected_count = sizeof(data);
const std::lock_guard lock(m_sdl_handles_mutex);
data.dpad = hat_components_to_logitech_g27_hat(
sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
);
data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
data.dial_center = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_center);
data.plus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.plus);
data.dial_cw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_clockwise);
data.dial_ccw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_anticlockwise);
data.minus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.minus);
data.ps = sdl_to_logitech_g27_button(m_joysticks, m_mapping.ps);
data.pedals_attached = 1;
data.powered = 1;
data.self_check_done = 1;
data.set1 = 1;
data.set2 = 1;
data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
std::memcpy(buf, &data, sizeof(data));
}
void usb_device_logitech_g27::transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer)
{
G25_data data{};
ensure(buf_size >= sizeof(data));
transfer->expected_count = sizeof(data);
const std::lock_guard lock(m_sdl_handles_mutex);
data.dpad = hat_components_to_logitech_g27_hat(
sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
);
data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
data.gear1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_1);
data.gear2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_2);
data.gear3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_3);
data.gear4 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_4);
data.gear5 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_5);
data.gear6 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_6);
data.gearR = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_r);
data.pedals_detached = 0;
data.powered = 1;
data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
data.clutch = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.clutch);
auto [shifter_x, shifter_y] = shifter_to_coord_xy(data.gear1, data.gear2,
data.gear3, data.gear4, data.gear5, data.gear6, data.gearR);
data.shifter_x = shifter_x;
data.shifter_y = shifter_y;
data.shifter_attached = 1;
data.set1 = 1;
data.shifter_pressed = data.gearR;
std::memcpy(buf, &data, sizeof(data));
}
void usb_device_logitech_g27::transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer)
{
G27_data data{};
ensure(buf_size >= sizeof(data));
transfer->expected_count = sizeof(data);
const std::lock_guard lock(m_sdl_handles_mutex);
data.dpad = hat_components_to_logitech_g27_hat(
sdl_to_logitech_g27_button(m_joysticks, m_mapping.up),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.down),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.left),
sdl_to_logitech_g27_button(m_joysticks, m_mapping.right)
);
data.cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
data.square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
data.circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
data.triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
data.r1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
data.l1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
data.r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
data.l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
data.select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
data.start = sdl_to_logitech_g27_button(m_joysticks, m_mapping.start);
data.r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
data.l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
data.gear1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_1);
data.gear2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_2);
data.gear3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_3);
data.gear4 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_4);
data.gear5 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_5);
data.gear6 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_6);
const bool shifter_r = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_r);
data.dial_cw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_clockwise);
data.dial_ccw = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_anticlockwise);
data.plus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.plus);
data.minus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.minus);
data.steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
data.throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
data.brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
data.clutch = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.clutch);
auto [shifter_x, shifter_y] = shifter_to_coord_xy(data.gear1, data.gear2,
data.gear3, data.gear4, data.gear5, data.gear6, shifter_r);
data.shifter_x = shifter_x;
data.shifter_y = shifter_y;
data.gearR = shifter_r;
data.pedals_detached = 0;
data.powered = 1;
data.shifter_attached = 1;
data.set1 = 1;
data.shifter_pressed = shifter_r;
data.range = (m_wheel_range > 360);
std::memcpy(buf, &data, sizeof(data));
}
void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
{
transfer->fake = true;
@ -655,168 +1179,31 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
if (endpoint & (1 << 7))
{
if (buf_size < 11)
{
logitech_g27_log.error("Not populating input buffer with a buffer of the size of %u", buf_size);
return;
}
ensure(buf_size >= 11);
memset(buf, 0, buf_size);
transfer->expected_count = 11;
sdl_instance::get_instance().pump_events();
// Fetch input states from SDL
m_sdl_handles_mutex.lock();
const u16 steering = sdl_to_logitech_g27_steering(m_joysticks, m_mapping.steering);
const u8 throttle = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.throttle);
const u8 brake = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.brake);
const u8 clutch = sdl_to_logitech_g27_pedal(m_joysticks, m_mapping.clutch);
const bool shift_up = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_up);
const bool shift_down = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shift_down);
const bool up = sdl_to_logitech_g27_button(m_joysticks, m_mapping.up);
const bool down = sdl_to_logitech_g27_button(m_joysticks, m_mapping.down);
const bool left = sdl_to_logitech_g27_button(m_joysticks, m_mapping.left);
const bool right = sdl_to_logitech_g27_button(m_joysticks, m_mapping.right);
const bool triangle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.triangle);
const bool cross = sdl_to_logitech_g27_button(m_joysticks, m_mapping.cross);
const bool square = sdl_to_logitech_g27_button(m_joysticks, m_mapping.square);
const bool circle = sdl_to_logitech_g27_button(m_joysticks, m_mapping.circle);
const bool l2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l2);
const bool l3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.l3);
const bool r2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r2);
const bool r3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.r3);
const bool plus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.plus);
const bool minus = sdl_to_logitech_g27_button(m_joysticks, m_mapping.minus);
const bool dial_clockwise = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_clockwise);
const bool dial_anticlockwise = sdl_to_logitech_g27_button(m_joysticks, m_mapping.dial_anticlockwise);
const bool select = sdl_to_logitech_g27_button(m_joysticks, m_mapping.select);
const bool pause = sdl_to_logitech_g27_button(m_joysticks, m_mapping.pause);
const bool shifter_1 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_1);
const bool shifter_2 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_2);
const bool shifter_3 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_3);
const bool shifter_4 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_4);
const bool shifter_5 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_5);
const bool shifter_6 = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_6);
const bool shifter_r = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_r);
const bool shifter_press = sdl_to_logitech_g27_button(m_joysticks, m_mapping.shifter_press);
m_sdl_handles_mutex.unlock();
// populate buffer
buf[0] = hat_components_to_logitech_g27_hat(up, down, left, right);
set_bit(buf, 8, shift_up);
set_bit(buf, 9, shift_down);
set_bit(buf, 7, triangle);
set_bit(buf, 4, cross);
set_bit(buf, 5, square);
set_bit(buf, 6, circle);
set_bit(buf, 11, l2);
set_bit(buf, 15, l3);
set_bit(buf, 10, r2);
set_bit(buf, 14, r3);
set_bit(buf, 22, dial_clockwise);
set_bit(buf, 23, dial_anticlockwise);
set_bit(buf, 24, plus);
set_bit(buf, 25, minus);
set_bit(buf, 12, select);
set_bit(buf, 13, pause);
set_bit(buf, 16, shifter_1);
set_bit(buf, 17, shifter_2);
set_bit(buf, 18, shifter_3);
set_bit(buf, 19, shifter_4);
set_bit(buf, 20, shifter_5);
set_bit(buf, 21, shifter_6);
set_bit(buf, 80, shifter_r);
// calibrated, unsure
set_bit(buf, 82, true);
// shifter connected
set_bit(buf, 83, true);
/*
* shifter pressed/down bit
* mechanical references:
* - G29 shifter mechanical explanation https://youtu.be/d7qCn3o8K98?t=1124
* - same mechanism on the G27 https://youtu.be/rdjejtIfkVA?t=760
* - same mechanism on the G25 https://youtu.be/eCyt_4luwF0?t=130
* on healthy G29/G27/G25 shifters, shifter is mechnically kept pressed in reverse, the bit should be set
* the shifter_press mapping alone captures instead a shifter press without going into reverse, ie. neutral press, just in case there are games using it for input
*/
set_bit(buf, 86, shifter_press | shifter_r);
buf[3] = (steering << 2) | buf[3];
buf[4] = steering >> 6;
buf[5] = throttle;
buf[6] = brake;
buf[7] = clutch;
// rough analog values recorded in https://github.com/RPCS3/rpcs3/pull/17199#issuecomment-2883934412
// buf[8] shifter x
// buf[9] shifter y
constexpr u8 shifter_coord_center = 0x80;
constexpr u8 shifter_coord_top = 0xb7;
constexpr u8 shifter_coord_bottom = 0x32;
constexpr u8 shifter_coord_left = 0x30;
constexpr u8 shifter_coord_right = 0xb3;
constexpr u8 shifter_coord_right_reverse = 0xaa;
if (shifter_1)
switch (m_personality)
{
buf[8] = shifter_coord_left;
buf[9] = shifter_coord_top;
}
else if (shifter_2)
{
buf[8] = shifter_coord_left;
buf[9] = shifter_coord_bottom;
}
else if (shifter_3)
{
buf[8] = shifter_coord_center;
buf[9] = shifter_coord_top;
}
else if (shifter_4)
{
buf[8] = shifter_coord_center;
buf[9] = shifter_coord_bottom;
}
else if (shifter_5)
{
buf[8] = shifter_coord_right;
buf[9] = shifter_coord_top;
}
else if (shifter_6)
{
buf[8] = shifter_coord_right;
buf[9] = shifter_coord_bottom;
}
else if (shifter_r)
{
buf[8] = shifter_coord_right_reverse;
buf[9] = shifter_coord_bottom;
}
else
{
buf[8] = shifter_coord_center;
buf[9] = shifter_coord_center;
case logitech_personality::driving_force:
transfer_df(buf_size, buf, transfer);
break;
case logitech_personality::driving_force_pro:
transfer_dfp(buf_size, buf, transfer);
break;
case logitech_personality::g25:
transfer_g25(buf_size, buf, transfer);
break;
case logitech_personality::driving_force_gt:
transfer_dfgt(buf_size, buf, transfer);
break;
case logitech_personality::g27:
transfer_g27(buf_size, buf, transfer);
break;
case logitech_personality::invalid:
fmt::throw_exception("unreachable");
}
buf[10] = buf[10] | (m_wheel_range > 360 ? 0x90 : 0x10);
// logitech_g27_log.error("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10]);
// logitech_g27_log.error("dev=%d, ep in : %s", static_cast<u8>(m_personality), fmt::buf_to_hexstring(buf, buf_size));
return;
}
@ -831,8 +1218,7 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
transfer->expected_count = buf_size;
// logitech_g27_log.error("%02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
// printf("%02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
// logitech_g27_log.error("ep out : %s", fmt::buf_to_hexstring(buf, buf_size));
// TODO maybe force clipping from cfg
@ -843,9 +1229,37 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
switch (buf[1])
{
case 0x01:
case 0x09:
case 0x10:
{
// Change to DFP
logitech_g27_log.error("Drive Force Pro mode switch command ignored");
// Change device mode
u8 cmd = buf[1];
u8 arg = buf[2];
if (buf[8] == 0xf8) // we have 2 commands back to back
{
cmd = buf[9];
arg = buf[10];
}
m_next_personality = logitech_personality::invalid;
if (cmd == 0x09 && arg == 0x04)
m_next_personality = logitech_personality::g27;
else if (cmd == 0x09 && arg == 0x03)
m_next_personality = logitech_personality::driving_force_gt;
else if ((cmd == 0x09 && arg == 0x02) || cmd == 0x10)
m_next_personality = logitech_personality::g25;
else if ((cmd == 0x09 && arg == 0x01) || cmd == 0x01)
m_next_personality = logitech_personality::driving_force_pro;
else if (cmd == 0x09 && arg == 0x00)
m_next_personality = logitech_personality::driving_force;
logitech_g27_log.notice("Change device mode : buf=[%s], cmd=%x, arg=%x -> new_pers=%x(%s)",
fmt::buf_to_hexstring(buf, buf_size), cmd, arg, static_cast<u8>(m_next_personality),
m_next_personality == logitech_personality::g27 ? "G27"
: m_next_personality == logitech_personality::driving_force_gt ? "Driving Force GT"
: m_next_personality == logitech_personality::g25 ? "G25"
: m_next_personality == logitech_personality::driving_force_pro ? "Driving Force Pro"
: m_next_personality == logitech_personality::driving_force ? "Driving Force" : "Invalid");
break;
}
case 0x02:
@ -862,24 +1276,12 @@ void usb_device_logitech_g27::interrupt_transfer(u32 buf_size, u8* buf, u32 endp
m_wheel_range = 900;
break;
}
case 0x09:
{
// Change device mode
logitech_g27_log.error("Change device mode to %d %s detaching command ignored", buf[2], buf[3] ? "with" : "without");
break;
}
case 0x0a:
{
// Revert indentity
logitech_g27_log.error("Revert device identity after reset %s command ignored", buf[2] ? "enable" : "disable");
break;
}
case 0x10:
{
// Switch to G25 with detach
logitech_g27_log.error("Switch to G25 with detach command ignored");
break;
}
case 0x11:
{
// Switch to G25 without detach

View File

@ -16,6 +16,16 @@
#include <map>
#include <vector>
enum class logitech_personality
{
driving_force,
driving_force_pro,
g25,
driving_force_gt,
g27,
invalid,
};
enum class logitech_g27_ffb_state
{
inactive,
@ -83,9 +93,11 @@ struct logitech_g27_sdl_mapping
sdl_mapping dial_clockwise {};
sdl_mapping dial_anticlockwise {};
sdl_mapping dial_center {};
sdl_mapping select {};
sdl_mapping pause {};
sdl_mapping start {};
sdl_mapping ps {};
sdl_mapping shifter_1 {};
sdl_mapping shifter_2 {};
@ -94,7 +106,6 @@ struct logitech_g27_sdl_mapping
sdl_mapping shifter_5 {};
sdl_mapping shifter_6 {};
sdl_mapping shifter_r {};
sdl_mapping shifter_press {};
};
class usb_device_logitech_g27 : public usb_device_emulated
@ -112,9 +123,17 @@ public:
private:
void sdl_refresh();
void set_personality(logitech_personality personality, bool reconnect = false);
void transfer_df(u32 buf_size, u8* buf, UsbTransfer* transfer);
void transfer_dfp(u32 buf_size, u8* buf, UsbTransfer* transfer);
void transfer_dfgt(u32 buf_size, u8* buf, UsbTransfer* transfer);
void transfer_g25(u32 buf_size, u8* buf, UsbTransfer* transfer);
void transfer_g27(u32 buf_size, u8* buf, UsbTransfer* transfer);
u32 m_controller_index = 0;
logitech_personality m_personality = logitech_personality::invalid;
logitech_personality m_next_personality = logitech_personality::invalid;
logitech_g27_sdl_mapping m_mapping {};
bool m_reverse_effects = false;

View File

@ -96,9 +96,11 @@ public:
emulated_logitech_g27_mapping dial_clockwise{this, "dial_clockwise", 0, sdl_mapping_type::button, 21, hat_component::none, false};
emulated_logitech_g27_mapping dial_anticlockwise{this, "dial_anticlockwise", 0, sdl_mapping_type::button, 22, hat_component::none, false};
emulated_logitech_g27_mapping dial_center{this, "dial_center", 0, sdl_mapping_type::button, 23, hat_component::none, false};
emulated_logitech_g27_mapping select{this, "select", 0, sdl_mapping_type::button, 8, hat_component::none, false};
emulated_logitech_g27_mapping pause{this, "pause", 0, sdl_mapping_type::button, 9, hat_component::none, false};
emulated_logitech_g27_mapping start{this, "pause", 0, sdl_mapping_type::button, 9, hat_component::none, false};
emulated_logitech_g27_mapping ps{this, "ps", 0, sdl_mapping_type::button, 24, hat_component::none, false};
emulated_logitech_g27_mapping shifter_1{this, "shifter_1", 0, sdl_mapping_type::button, 3, hat_component::none, false};
emulated_logitech_g27_mapping shifter_2{this, "shifter_2", 0, sdl_mapping_type::button, 0, hat_component::none, false};
@ -107,7 +109,6 @@ public:
emulated_logitech_g27_mapping shifter_5{this, "shifter_5", 0, sdl_mapping_type::hat, 0, hat_component::up, false};
emulated_logitech_g27_mapping shifter_6{this, "shifter_6", 0, sdl_mapping_type::hat, 0, hat_component::down, false};
emulated_logitech_g27_mapping shifter_r{this, "shifter_r", 0, sdl_mapping_type::hat, 0, hat_component::left, false};
emulated_logitech_g27_mapping shifter_press{this, "shifter_press", 0, sdl_mapping_type::hat, 0, hat_component::right, false};
cfg::_bool reverse_effects{this, "reverse_effects", false};
cfg::uint<0, 0xFFFFFFFFFFFFFFFF> ffb_device_type_id{this, "ffb_device_type_id", 0};

View File

@ -219,7 +219,7 @@ void usb_device_topshotelite::control_transfer(u8 bmRequestType, u8 bRequest, u1
extern bool is_input_allowed();
static void set_sensor_pos(struct TopShotElite_data* ts, s32 led_lx, s32 led_ly, s32 led_rx, s32 led_ry, s32 detect_l, s32 detect_r)
static void set_sensor_pos(TopShotElite_data* ts, s32 led_lx, s32 led_ly, s32 led_rx, s32 led_ry, s32 detect_l, s32 detect_r)
{
ts->led_lx_hi = led_lx >> 2;
ts->led_lx_lo = led_lx & 0x3;
@ -250,7 +250,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en
transfer->expected_result = HC_CC_NOERR;
transfer->expected_time = get_timestamp() + 4000;
struct TopShotElite_data ts{};
TopShotElite_data ts{};
ts.dpad = Dpad_None;
ts.stick_lx = ts.stick_ly = ts.stick_rx = ts.stick_ry = 0x7f;
if (m_mode)

View File

@ -247,7 +247,7 @@ static int get_heartrate_sensor_value(u8 heartrate)
return sensor_data[heartrate - 30];
}
static void set_sensor_pos(struct TopShotFearmaster_data* ts, s32 led_lx, s32 led_ly, s32 led_rx, s32 led_ry, s32 detect_l, s32 detect_r)
static void set_sensor_pos(TopShotFearmaster_data* ts, s32 led_lx, s32 led_ly, s32 led_rx, s32 led_ry, s32 detect_l, s32 detect_r)
{
ts->led_lx_hi = led_lx >> 2;
ts->led_lx_lo = led_lx & 0x3;
@ -278,7 +278,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32
transfer->expected_result = HC_CC_NOERR;
transfer->expected_time = get_timestamp() + 4000;
struct TopShotFearmaster_data ts{};
TopShotFearmaster_data ts{};
ts.dpad = Dpad_None;
ts.stick_lx = ts.stick_ly = ts.stick_rx = ts.stick_ry = 0x7f;
if (m_mode)

View File

@ -18,7 +18,7 @@
#include <QSlider>
#include <QComboBox>
LOG_CHANNEL(logitech_g27_cfg_log, "LOGIG27");
LOG_CHANNEL(logitech_g27_cfg_log, "logitech_g27");
static const QString DEFAULT_STATUS = " ";
@ -57,9 +57,11 @@ enum class mapping_device
DIAL_CLOCKWISE,
DIAL_ANTICLOCKWISE,
DIAL_CENTER,
SELECT,
PAUSE,
START,
PS,
SHIFTER_1,
SHIFTER_2,
@ -68,7 +70,6 @@ enum class mapping_device
SHIFTER_5,
SHIFTER_6,
SHIFTER_R,
SHIFTER_PRESS,
// Enum count
COUNT
@ -97,12 +98,14 @@ QString device_name(mapping_device dev)
case mapping_device::L3: return QObject::tr("L3");
case mapping_device::R2: return QObject::tr("R2");
case mapping_device::R3: return QObject::tr("R3");
case mapping_device::PLUS: return QObject::tr("L4");
case mapping_device::MINUS: return QObject::tr("L5");
case mapping_device::DIAL_CLOCKWISE: return QObject::tr("R4");
case mapping_device::DIAL_ANTICLOCKWISE: return QObject::tr("R5");
case mapping_device::PLUS: return QObject::tr("L4\nPlus");
case mapping_device::MINUS: return QObject::tr("L5\nMinus");
case mapping_device::DIAL_CLOCKWISE: return QObject::tr("R4\nDial CW");
case mapping_device::DIAL_ANTICLOCKWISE: return QObject::tr("R5\nDial CCW");
case mapping_device::DIAL_CENTER: return QObject::tr("Dial Center");
case mapping_device::SELECT: return QObject::tr("Select");
case mapping_device::PAUSE: return QObject::tr("Pause");
case mapping_device::START: return QObject::tr("Start");
case mapping_device::PS: return QObject::tr("PS");
case mapping_device::SHIFTER_1: return QObject::tr("Gear 1");
case mapping_device::SHIFTER_2: return QObject::tr("Gear 2");
case mapping_device::SHIFTER_3: return QObject::tr("Gear 3");
@ -110,7 +113,6 @@ QString device_name(mapping_device dev)
case mapping_device::SHIFTER_5: return QObject::tr("Gear 5");
case mapping_device::SHIFTER_6: return QObject::tr("Gear 6");
case mapping_device::SHIFTER_R: return QObject::tr("Gear R");
case mapping_device::SHIFTER_PRESS: return QObject::tr("Shifter press");
case mapping_device::COUNT: return "";
}
return "";
@ -144,8 +146,10 @@ emulated_logitech_g27_mapping& device_cfg(mapping_device dev)
case mapping_device::MINUS: return cfg.minus;
case mapping_device::DIAL_CLOCKWISE: return cfg.dial_clockwise;
case mapping_device::DIAL_ANTICLOCKWISE: return cfg.dial_anticlockwise;
case mapping_device::DIAL_CENTER: return cfg.dial_center;
case mapping_device::SELECT: return cfg.select;
case mapping_device::PAUSE: return cfg.pause;
case mapping_device::START: return cfg.start;
case mapping_device::PS: return cfg.ps;
case mapping_device::SHIFTER_1: return cfg.shifter_1;
case mapping_device::SHIFTER_2: return cfg.shifter_2;
case mapping_device::SHIFTER_3: return cfg.shifter_3;
@ -153,7 +157,6 @@ emulated_logitech_g27_mapping& device_cfg(mapping_device dev)
case mapping_device::SHIFTER_5: return cfg.shifter_5;
case mapping_device::SHIFTER_6: return cfg.shifter_6;
case mapping_device::SHIFTER_R: return cfg.shifter_r;
case mapping_device::SHIFTER_PRESS: return cfg.shifter_press;
default: fmt::throw_exception("Unexpected mapping_device %d", static_cast<int>(dev));
}
}