Как записать данные на устройство gip (контроллер Xbox Series)?C#

Место общения программистов C#
Ответить
Anonymous
 Как записать данные на устройство gip (контроллер Xbox Series)?

Сообщение Anonymous »

Я работаю над программой на C#, которая будет отправлять пакет отключения питания на мой контроллер серии Xbox, чтобы его выключить.
В 2024 году Microsoft выпустила документацию GIP, и, насколько я понимаю, Xbox One и Series работают с GIP.
Я запускаю компьютер с Win10 и использую контроллер Xbox Series с беспроводным ключом Microsoft. Из этого поста я узнал, что сначала мне нужно получить дескриптор интерфейса XBOXGIP:

Использование интерфейса GIP начинается с получения дескриптора
интерфейса через путь устройства \.\XboxGIP

HANDLE hFile = CreateFileW(L"\\\\.\\XboxGIP", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);

Затем я прочитал контроллер с помощью ReadFile и смог получить данные, которые приходят на ПК при запуске контроллера. Я получил 3 разных сообщения:
  • Сообщение с метаданными:
Received 315 bytes:
7E ED 82 C6 8B DA 00 00 04 20 00 00 27 01 00 00
00 00 00 00 5E 04 12 0B 10 00 01 00 00 00 00 00
00 00 00 00 00 00 23 01 CD 00 16 00 1B 00 1C 00
26 00 2F 00 4C 00 00 00 00 00 00 00 00 00 01 05
00 17 00 00 09 01 02 03 04 06 07 0C 0D 1E 08 01
04 05 06 0A 0C 0D 1E 01 1A 00 57 69 6E 64 6F 77
73 2E 58 62 6F 78 2E 49 6E 70 75 74 2E 47 61 6D
65 70 61 64 08 56 FF 76 97 FD 9B 81 45 AD 45 B6
45 BB A5 26 D6 2C 40 2E 08 DF 07 E1 45 A5 AB A3
12 7A F1 97 B5 E7 1F F3 B8 86 73 E9 40 A9 F8 2F
21 26 3A CF B7 FE D2 DD EC 87 D3 94 42 BD 96 1A
71 2E 3D C7 7D 6B E5 F2 87 BB C3 B1 49 82 65 FF
FF F3 77 99 EE 1E 9B AD 34 AD 36 B5 4F 8A C7 17
23 4C 9F 54 6F 77 CE 34 7A E2 7D C6 45 8C A4 00
42 C0 8B D9 4A C0 C8 96 EA 16 B2 8B 44 BE 80 7E
5D EB 06 98 E2 03 17 00 20 2C 00 01 00 10 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 17 00 09
3C 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 17 00 1E 40 00 01 00 22 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00
  • Приветственное сообщение:
Received 53 bytes:
7E ED 82 C6 8B DA 00 00 02 20 00 00 21 00 00 00
00 00 00 00 7E ED 82 C6 8B DA 00 00 5E 04 12 0B
05 00 17 00 06 00 00 00 08 04 01 00 01 00 01 00
00 00 00 00 00
  • И сообщения о состоянии устройства, которые приходили примерно каждые 500 мс:
Received 24 bytes:
7E ED 82 C6 8B DA 00 00 03 20 00 00 04 00 00 00
00 00 00 00 8B 00 00 58

Каждое сообщение выше начинается с 7E ED 82 C6 8B DA 00 00, который является уникальным идентификатором моего устройства.
Мне удалось расшифровать эти сообщения с помощью документации и чата GPT. Они придерживаются документации, но я не могу понять, как отправить команду обратно контроллеру. Затем я попробовал записать на устройство с помощью WriteFile. Вот как должно выглядеть установленное состояние устройства. Я пробовал две вещи:
  • Отправить только пакет с помощью команды:
powerOffCommand[0] = 0x05; // Command ID
powerOffCommand[1] = 0x20; // Flags
powerOffCommand[2] = 0x01; // Sequence number
powerOffCommand[3] = 0x01; // payload length
powerOffCommand[4] = 0x04; // payload. Power Off command.

В этом случае я получил сообщение об ошибке, сообщающее, что устройство не подключено, хотя я считываю с него данные.
  • Отправка пакета с идентификатором устройства и тем же командным пакетом:
powerOffCommand[0] = 0x7E;
powerOffCommand[1] = 0xED;
powerOffCommand[2] = 0x82;
powerOffCommand[3] = 0xC6;
powerOffCommand[4] = 0x8B;
powerOffCommand[5] = 0xDA;
powerOffCommand[6] = 0x00;
powerOffCommand[7] = 0x00;

powerOffCommand[8] = 0x05; // Command ID
powerOffCommand[9] = 0x20; // Flags
powerOffCommand[10] = 0x01; // Sequence number
powerOffCommand[11] = 0x01; // payload length
powerOffCommand[12] = 0x04; // payload. Power Off command.

В этом случае я получаю сообщение о том, что параметр неверен, что намекает на то, что оно обнаружило устройство, но по какой-то причине отправляемый мной пакет его не удовлетворяет. Изменение любого из байтов идентификатора приводит к ошибке «Устройство не подключено».
Я ни в коем случае не владею C#. Это всего лишь домашний проект, поэтому прошу помощи в отправке команды контроллеру. Приведенный ниже код был написан с помощью ChatGPT и моих скудных знаний программирования на C# и Javascript.
На что следует обратить внимание:
  • Возможно, что-то не так с порядковым номером. Я пробовал отправлять разные байты, такие как 0x01, 0x02, 0x03 и некоторые другие случайные, но это ничего не изменило.
  • Возможно, информация об интерфейсе xboxgip устарела, и мне следует написать прямо на контроллер, но когда я пытаюсь это сделать, я получаю ошибку «Отказано в доступе». Я посмотрел, какие процессы заблокировали устройство, но не смог понять, как завершить dwm.exe таким образом, чтобы не сломать графический интерфейс моего рабочего стола, а также освободить контроллер. Я не знаю, как запретить контроллеру отправлять входные данные в ОС для управления меню в Win10.
  • Также я нашел эту таблицу, но не могу ее расшифровать. Здесь хост отправляет команду выключения, но когда я делаю то же самое, он выдает ошибку. Но байт 0xD2 выглядит интересно. Я не понимаю, для чего это нужно. -- Я узнал, что это USB-пакеты, передаваемые между хостом и устройством. Данные GIP запаковываются в USB-пакет и отправляются на контроллер.
    Рисунок 4-16: USB-трассировка для установки состояния нисходящего устройства: выключено
    Изображение
  • Согласно документации, все идентификаторы устройств должен начинаться с 0x00, 0x00, 0xFF, 0xFB. Но вот идентификатор моего устройства: 7E ED 82 C6 8B DA 00 00. У него нет 0xFF, 0xFB.
Все устройства GIP ДОЛЖНЫ иметь уникальный 64-битный идентификатор основного устройства,
из которого четыре старших байта — 0x00, 0x00, 0xFF, 0xFB.
Оставшиеся байты ДОЛЖНЫ быть случайными числами, определяемыми при загрузке
устройства GIP.

Вот полный код:
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

public class XboxGipController
{
// Constants
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint OPEN_EXISTING = 3;
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

// Define the specific IOCTL code
private const uint GIP_ADD_REENUMERATE_CALLER_CONTEXT = 0x40001CD0;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern SafeFileHandle CreateFileW(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(
SafeFileHandle hFile,
byte[] lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped
);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteFile(
SafeFileHandle hFile,
byte[] lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped
);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);

private static void ProcessGipMessage(byte[] data, int length)
{
Console.WriteLine($"Received {length} bytes:");
for (int i = 0; i < length; i++)
{
Console.Write($"{data:X2} ");
if ((i + 1) % 16 == 0)
Console.WriteLine();
}
Console.WriteLine();
}

public static void ReenumerateGipControllers()
{
SafeFileHandle? hFile = null;

try
{
// Open the GIP device interface
hFile = CreateFileW(
@"\\.\XboxGIP",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero
);

if (hFile.IsInvalid)
{
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}

// Send the re-enumeration command
uint bytesReturned;
bool success = DeviceIoControl(
hFile,
GIP_ADD_REENUMERATE_CALLER_CONTEXT,
IntPtr.Zero,
0,
IntPtr.Zero,
0,
out bytesReturned,
IntPtr.Zero
);

if (!success)
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

Console.WriteLine("GIP controller re-enumeration triggered successfully");

byte[] powerOffCommand = new byte[64];
// Xbox controller ID
powerOffCommand[0] = 0x7E;
powerOffCommand[1] = 0xED;
powerOffCommand[2] = 0x82;
powerOffCommand[3] = 0xC6;
powerOffCommand[4] = 0x8B;
powerOffCommand[5] = 0xDA;
powerOffCommand[6] = 0x00;
powerOffCommand[7] = 0x00;

powerOffCommand[8] = 0x05; // Command ID
powerOffCommand[9] = 0x20; // Flags
powerOffCommand[10] = 0x01; // Sequence number
powerOffCommand[11] = 0x01; // payload length
powerOffCommand[12] = 0x04; // payload

// Send the power off command
// Comment the next 15 lines to skip writing and check how ReadFile works.
// Otherwise the code will throw.
uint bytesWritten;
bool successWrite = WriteFile(
hFile,
powerOffCommand,
(uint)powerOffCommand.Length,
out bytesWritten,
IntPtr.Zero
);

if (!successWrite)
{
ProcessGipMessage(powerOffCommand, powerOffCommand.Length);
throw new Win32Exception(Marshal.GetLastWin32Error());
}

byte[] buffer = new byte[1000];
uint bytesRead;
while (true)
{
bool successRead = ReadFile(
hFile, // Handle to the GIP device
buffer, // Buffer to receive data
(uint)buffer.Length, // Buffer size
out bytesRead, // Number of bytes actually read
IntPtr.Zero
);

if (!successRead)
{
int error = Marshal.GetLastWin32Error();
if (error == 259)
break;

throw new Win32Exception(error);
}

if (bytesRead > 0)
{
ProcessGipMessage(buffer, (int)bytesRead);
}
else
{
System.Threading.Thread.Sleep(10);
}
}
}
finally
{
hFile?.Close();
}
}

// Usage example
public static void Main()
{
try
{
ReenumerateGipControllers();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}


Подробнее здесь: https://stackoverflow.com/questions/797 ... controller
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»