It's kinda necessary to clean up the mess, so there goes another tool whose purpose is to check if at least some commands are supported by all LG wheels. The tool tells you what effect is expected to be generated. It can also send a fully custom command when run like "-c AABBCCDDEEFFGGHH".
#include <getopt.h>
#include <libusb-1.0/libusb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VID_LOGITECH 0x046D
#define ARRAY_SIZE(arr) sizeof(arr)/sizeof(arr[0])
typedef struct {
unsigned char cmd[8];
char desc[64];
} ff_cmd;
static const unsigned int device_ids[] = {0xC294, /* DF */
0xC298, /* DFP */
0xC299, /* G25 */
0xC29A, /* DFGT */
0xC29B /* G27 */
};
static struct option long_options[] = {
{"custom", required_argument, 0, 'c'},
{0, 0, 0, 0}
};
/* Standard commands that should be accepted by all
* Logitech wheels */
static ff_cmd std_commands[] = {
//Test autocentering
{ {0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Reset effects"},
{ {0xFE, 0x0D, 0x07, 0x07, 0xFF, 0x00, 0x00, 0x00}, "Full autocentering"},
{ {0xFE, 0x0D, 0x03, 0x03, 0x80, 0x00, 0x00, 0x00}, "Medium autocentering"},
{ {0xFE, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "No autocentering"},
//Test constant force
{ {0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Reset effects"},
{ {0x11, 0x08, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00}, "Full left constant force"},
{ {0x11, 0x08, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00}, "Full right constant force"},
{ {0x11, 0x08, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00}, "No constant force"},
//Remove autocentering and constant force
{ {0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Reset effects"},
{ {0xFE, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "No autocentering"},
//Test friction
{ {0x21, 0x02, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00}, "Clockwise resistance"},
{ {0x21, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00}, "Counter-clockwise resistance"},
{ {0x21, 0x02, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00}, "Resistance both ways"},
{ {0x21, 0x02, 0x0F, 0x01, 0x0F, 0x01, 0x00, 0x00}, "'Assistance' both ways"},
{ {0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Reset friction"}
};
/* Autocentering commands for Formula EX */
static ff_cmd fex_ac_commands[] = {
//Test autocentering
{ {0xFE, 0x03, 0x07, 0x07, 0xFF, 0x00, 0x00, 0x00}, "Full autocentering"},
{ {0xFE, 0x03, 0x03, 0x03, 0x80, 0x00, 0x00, 0x00}, "Medium autocentering"},
{ {0xFE, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "No autocentering"},
};
libusb_device_handle* find_wheel()
{
libusb_device_handle* hDev = NULL;
for(int i = 0; i < ARRAY_SIZE(device_ids); i++) {
hDev = libusb_open_device_with_vid_pid(NULL, VID_LOGITECH, device_ids[i]);
if(hDev != 0) {
printf("%s %X\n", "Found wheel - PID ", device_ids[i]);
break;
}
}
return hDev;
}
int send_custom_command(unsigned char* cmd, libusb_device_handle* hDev)
{
//Get access to the wheel
int stat;
stat = libusb_detach_kernel_driver(hDev, 0);
if (stat < 0) {
fprintf(stderr, "Cannot detach kernel driver\n");
return -1;
}
stat = libusb_claim_interface(hDev, 0);
if (stat < 0) {
fprintf(stderr, "Cannot claim interface\n");
return -1;
}
//Send command
int transferred = 0;
stat = libusb_interrupt_transfer(hDev, 1, cmd, sizeof(cmd),
&transferred, 3000);
printf("Bytes sent: %d\n", transferred);
if (stat < 0) {
fprintf(stderr, "Error sending USB command");
return -1;
}
//Release interface and hand control over to kernel
stat = libusb_release_interface(hDev, 0);
if (stat < 0) {
fprintf(stderr, "Cannot release interface\n");
return -1;
}
stat = libusb_attach_kernel_driver(hDev, 0);
if (stat < 0) {
fprintf(stderr, "Cannot reattach kernel driver\n");
return -1;
}
return 0;
}
int send_command(unsigned char* cmd, libusb_device_handle* hDev)
{
int transferred = 0;
int stat = libusb_interrupt_transfer(hDev, 1, cmd, sizeof(cmd),
&transferred, 3000);
if (stat < 0) {
fprintf(stderr, "Error sending USB command");
return -1;
}
return 0;
}
int main(int argc, char** argv)
{
char* custom_cmd_s = NULL;
libusb_init(NULL);
libusb_device_handle* hDev = find_wheel();
if(hDev == NULL) {
printf("No wheel found\n");
libusb_exit(NULL);
return -1;
}
while(optind < argc) {
int idx = -1;
int result = getopt_long(argc, argv, "c:",
long_options, &idx);
switch(result) {
case 'c':
custom_cmd_s = calloc(sizeof(char), 16);
strcpy(custom_cmd_s, optarg);
//Create command
char temp[3];
unsigned char byte;
unsigned char custom_cmd[8];
memset(custom_cmd, 0, sizeof(custom_cmd));
temp[2] = '\0';
int i = 0;
while(i < 8) {
memcpy(temp, custom_cmd_s+(i*2), 2);
if(strcmp(temp, "") == 0)
break;
sscanf(temp, "%X", &byte);
custom_cmd[i++] = byte;
}
for(int j = 0; j < 8; j++) {
printf("%02X ", custom_cmd[j]);
}
printf("\nPress ENTER to send command\n");
getchar();
send_custom_command(custom_cmd, hDev);
free(custom_cmd_s);
libusb_exit(NULL);
return 0;
}
}
//Get access to the wheel
int stat;
stat = libusb_detach_kernel_driver(hDev, 0);
if (stat < 0)
fprintf(stderr, "Cannot detach kernel driver\n");
stat = libusb_claim_interface(hDev, 0);
if (stat < 0)
fprintf(stderr, "Cannot claim interface\n");
//Send standard commands
for(int i = 0; i < ARRAY_SIZE(std_commands); i++) {
send_command(std_commands[i].cmd, hDev);
printf("%s\n%s", std_commands[i].desc, "Press ENTER to continue\n");
getchar();
}
printf("Test autocentering for Formula EX? (Y/N)\n");
char c = getchar();
if(c == 'y' || c == 'Y') {
//Send Formula EX AC commands
for(int i = 0; i < ARRAY_SIZE(fex_ac_commands); i++) {
send_command(std_commands[i].cmd, hDev);
printf("%s\n%s", fex_ac_commands[i].desc, "Press ENTER to continue\n");
getchar();
}
}
//Release interface and hand control over to kernel
stat = libusb_release_interface(hDev, 0);
if (stat < 0)
fprintf(stderr, "Cannot release interface\n");
stat = libusb_attach_kernel_driver(hDev, 0);
if (stat < 0)
fprintf(stderr, "Cannot reattach kernel driver\n");
libusb_exit(NULL);
return 0;
}
I'm posting from my iPod which won't allow me to attach anything (sorry guys). Source can be compiled like this "gcc -std=gnu99 -lusb-1.0 src.c -o check_cmds"