Skip to content

Commit 6fc19d5

Browse files
committed
feat(debug): Add Fastboot debug logging, Windows USB refactor
Introduce FastbootDebug and sprinkle debug logs across FastbootUtil (RawCommand, GetVarAll) and LibUsbDevice to aid troubleshooting. Simplify FastbootCLI argument parsing, error handling, and device selection; make FastbootUtil IDisposable and ensure proper transport disposal. Refactor Windows USB stack: add CTL_CODE helper, fix P/Invoke signatures (uint sizes), rework LegacyUsbDevice and WinUSBDevice handles/IO, and rewrite WinUSBFinder to robustly enumerate and probe device interfaces. Also update project metadata in the csproj.
1 parent 79e426c commit 6fc19d5

11 files changed

Lines changed: 327 additions & 677 deletions

File tree

FastbootCLI/Program.cs

Lines changed: 34 additions & 445 deletions
Large diffs are not rendered by default.

FirmwareKit.Comm.Fastboot/Command/RawCommand.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ public partial class FastbootUtil
1010
/// </summary>
1111
public FastbootResponse RawCommand(string command)
1212
{
13+
FastbootDebug.Log("Sending command: " + command);
1314
byte[] cmdBytes = Encoding.UTF8.GetBytes(command);
1415
try
1516
{
16-
if (Transport.Write(cmdBytes, cmdBytes.Length) != cmdBytes.Length)
17+
int bytesWritten = (int)Transport.Write(cmdBytes, cmdBytes.Length);
18+
FastbootDebug.Log($"Bytes written: {bytesWritten}/{cmdBytes.Length}");
19+
if (bytesWritten != cmdBytes.Length)
1720
{
1821
return new FastbootResponse
1922
{
@@ -24,14 +27,17 @@ public FastbootResponse RawCommand(string command)
2427
}
2528
catch (Exception e)
2629
{
30+
FastbootDebug.Log("Exception during command write: " + e);
2731
return new FastbootResponse
2832
{
2933
Result = FastbootState.Fail,
3034
Response = "command write failed: " + e.Message
3135
};
3236
}
3337

38+
FastbootDebug.Log("Waiting for response...");
3439
var response = HandleResponse();
40+
FastbootDebug.Log("Response received: " + response.Response);
3541

3642
if (response.Result == FastbootState.Fail)
3743
{
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace FirmwareKit.Comm.Fastboot;
2+
3+
public static class FastbootDebug
4+
{
5+
private static bool? _debugEnabled;
6+
7+
public static bool IsEnabled
8+
{
9+
get
10+
{
11+
if (_debugEnabled == null)
12+
{
13+
_debugEnabled = Environment.GetEnvironmentVariable("FASTBOOT_DEBUG") == "1";
14+
}
15+
return _debugEnabled.Value;
16+
}
17+
set => _debugEnabled = value;
18+
}
19+
20+
public static void Log(string message)
21+
{
22+
if (IsEnabled)
23+
{
24+
Console.Error.WriteLine($"[DEBUG] {message}");
25+
}
26+
}
27+
}

FirmwareKit.Comm.Fastboot/FastbootUtil.cs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99

1010
namespace FirmwareKit.Comm.Fastboot;
1111

12-
public partial class FastbootUtil
12+
public partial class FastbootUtil : IDisposable
1313
{
14+
public void Dispose()
15+
{
16+
Transport?.Dispose();
17+
}
18+
1419
/// <summary>
1520
/// Determines whether the device is in fastbootd (userspace) mode.
1621
/// </summary>
@@ -178,24 +183,36 @@ public void NotifyReceived(FastbootState state, string? info = null, string? tex
178183
/// </summary>
179184
public Dictionary<string, string> GetVarAll()
180185
{
186+
FastbootDebug.Log("Sending command: getvar:all");
181187
_varCache.Clear();
182-
var res = RawCommand("getvar:all").ThrowIfError();
183-
var dict = new Dictionary<string, string>();
184-
foreach (var line in res.Info)
188+
try
185189
{
186-
int colonIdx = line.LastIndexOf(":");
187-
if (colonIdx > 0)
190+
var res = RawCommand("getvar:all").ThrowIfError();
191+
FastbootDebug.Log("Command response received. Parsing...");
192+
var dict = new Dictionary<string, string>();
193+
foreach (var line in res.Info)
188194
{
189-
string k = line.Substring(0, colonIdx).Trim();
190-
string v = line.Substring(colonIdx + 1).TrimStart();
191-
if (!dict.ContainsKey(k))
195+
FastbootDebug.Log("Parsing line: " + line);
196+
int colonIdx = line.LastIndexOf(":");
197+
if (colonIdx > 0)
192198
{
193-
dict[k] = v;
194-
_varCache[k] = v;
199+
string k = line.Substring(0, colonIdx).Trim();
200+
string v = line.Substring(colonIdx + 1).TrimStart();
201+
FastbootDebug.Log($"Parsed key: {k}, value: {v}");
202+
if (!dict.ContainsKey(k))
203+
{
204+
dict[k] = v;
205+
_varCache[k] = v;
206+
}
195207
}
196208
}
209+
return dict;
210+
}
211+
catch (Exception ex)
212+
{
213+
FastbootDebug.Log("Exception in GetVarAll: " + ex);
214+
throw;
197215
}
198-
return dict;
199216
}
200217

201218
/// <summary>

FirmwareKit.Comm.Fastboot/FirmwareKit.Comm.Fastboot.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
<PackageProjectUrl>https://github.com/uotan-Dev/SharpFastboot</PackageProjectUrl>
1414
<RepositoryUrl>https://github.com/uotan-Dev/SharpFastboot</RepositoryUrl>
1515
<PackageLicenseExpression>MIT</PackageLicenseExpression>
16+
<RepositoryType>git</RepositoryType>
17+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
18+
<IncludeSymbols>true</IncludeSymbols>
19+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1620
<PackageReadmeFile>README.md</PackageReadmeFile>
1721
<PackageTags>fastboot;android;firmware;flashing;usb;tcp;udp;adb</PackageTags>
18-
<Version>1.0.0</Version>
22+
<Version>1.0.1</Version>
1923
<PackageId>FirmwareKit.Comm.Fastboot</PackageId>
2024
<Product>FastbootCli</Product>
2125
</PropertyGroup>
Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,94 @@
1-
using System.ComponentModel;
1+
using System;
22
using System.Runtime.InteropServices;
3-
using System.Text;
3+
using System.ComponentModel;
44
using static FirmwareKit.Comm.Fastboot.Usb.Windows.Win32API;
55

6-
namespace FirmwareKit.Comm.Fastboot.Usb.Windows;
7-
8-
public class LegacyUsbDevice : UsbDevice
6+
namespace FirmwareKit.Comm.Fastboot.Usb.Windows
97
{
10-
public static readonly uint IoGetSerialCode = ((FILE_DEVICE_UNKNOWN) << 16) | ((FILE_READ_ACCESS) << 14) | ((16) << 2) | (METHOD_BUFFERED);
11-
public IntPtr DeviceHandle { get; private set; }
8+
public class LegacyUsbDevice : UsbDevice
9+
{
10+
public static uint IoGetSerialCode => CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS);
11+
public static uint IoGetDescriptorCode => CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS);
1212

13-
public IntPtr ReadBulkHandle { get; private set; }
13+
private IntPtr fileHandle = INVALID_HANDLE_VALUE;
1414

15-
public IntPtr WriteBulkHandle { get; private set; }
15+
public IntPtr Handle => fileHandle;
1616

17-
public override int CreateHandle()
18-
{
19-
DeviceHandle = SimpleCreateHandle(DevicePath);
20-
ReadBulkHandle = SimpleCreateHandle(DevicePath + "\\BulkRead");
21-
WriteBulkHandle = SimpleCreateHandle(DevicePath + "\\BulkWrite");
22-
if (DeviceHandle == new IntPtr(-1) ||
23-
ReadBulkHandle == new IntPtr(-1) ||
24-
WriteBulkHandle == new IntPtr(-1))
25-
return Marshal.GetLastWin32Error();
26-
GetSerialNumber();
27-
return 0;
28-
}
17+
public override int CreateHandle()
18+
{
19+
fileHandle = SimpleCreateHandle(DevicePath);
20+
if (fileHandle == INVALID_HANDLE_VALUE)
21+
return Marshal.GetLastWin32Error();
2922

30-
public override void Reset()
31-
{
32-
// Not supported in legacy driver
33-
}
23+
// 仿照谷歌原生逻辑,检查是否匹配 Fastboot 接口标准 (0xff, 0x42, 0x03)
24+
// 在 Legacy 驱动中,我们尝试探测其是否响应特定的 IOCTL
25+
if (!CheckInterface())
26+
{
27+
CloseHandle(fileHandle);
28+
fileHandle = INVALID_HANDLE_VALUE;
29+
return -1;
30+
}
3431

35-
public override int GetSerialNumber()
36-
{
37-
byte[] serial = new byte[512];
38-
int bytes_get;
39-
if (DeviceIoControl(DeviceHandle, IoGetSerialCode, Array.Empty<byte>(), 0, serial, 512, out bytes_get, IntPtr.Zero))
40-
{
41-
// The legacy driver returns the serial number as a null-terminated UTF-16 string
42-
SerialNumber = Encoding.Unicode.GetString(serial, 0, bytes_get).TrimEnd('\0');
32+
GetSerialNumber();
4333
return 0;
4434
}
45-
return Marshal.GetLastWin32Error();
46-
}
4735

48-
public override byte[] Read(int length)
49-
{
50-
uint bytesRead;
51-
byte[] data = new byte[length];
52-
if (ReadBulkHandle == IntPtr.Zero)
53-
throw new Exception("Read handle is closed.");
54-
55-
if (ReadFile(ReadBulkHandle, data, (uint)length, out bytesRead, IntPtr.Zero))
36+
private bool CheckInterface()
5637
{
57-
byte[] res = new byte[bytesRead];
58-
Array.Copy(data, res, bytesRead);
59-
return res;
38+
byte[] buffer = new byte[256];
39+
int returned;
40+
// 能够响应 IoGetSerialCode,初步认为是兼容的 Legacy 驱动
41+
return DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero);
6042
}
61-
throw new Win32Exception(Marshal.GetLastWin32Error());
62-
}
6343

64-
public override long Write(byte[] data, int length)
65-
{
66-
ulong bytesWrite = 0;
67-
if (WriteBulkHandle == IntPtr.Zero)
68-
throw new Exception("Write handle is closed.");
44+
public override int GetSerialNumber()
45+
{
46+
byte[] buffer = new byte[256];
47+
int returned;
48+
if (DeviceIoControl(fileHandle, IoGetSerialCode, null, 0, buffer, buffer.Length, out returned, IntPtr.Zero))
49+
{
50+
SerialNumber = System.Text.Encoding.Unicode.GetString(buffer, 0, returned).TrimEnd('\0');
51+
return 0;
52+
}
53+
return Marshal.GetLastWin32Error();
54+
}
6955

70-
if (WriteFile(WriteBulkHandle, data, (uint)length, out bytesWrite, IntPtr.Zero))
56+
public override byte[] Read(int length)
7157
{
72-
return (long)bytesWrite;
58+
byte[] buffer = new byte[length];
59+
uint read;
60+
if (ReadFile(fileHandle, buffer, (uint)length, out read, IntPtr.Zero))
61+
{
62+
byte[] result = new byte[read];
63+
Array.Copy(buffer, result, (int)read);
64+
return result;
65+
}
66+
throw new Win32Exception(Marshal.GetLastWin32Error());
7367
}
74-
throw new Win32Exception(Marshal.GetLastWin32Error());
75-
}
7668

77-
public override void Dispose()
78-
{
79-
if (DeviceHandle != IntPtr.Zero && DeviceHandle != new IntPtr(-1))
69+
public override long Write(byte[] data, int length)
8070
{
81-
CloseHandle(DeviceHandle);
82-
DeviceHandle = IntPtr.Zero;
71+
uint written;
72+
if (WriteFile(fileHandle, data, (uint)length, out written, IntPtr.Zero))
73+
{
74+
return written;
75+
}
76+
throw new Win32Exception(Marshal.GetLastWin32Error());
8377
}
84-
if (ReadBulkHandle != IntPtr.Zero && ReadBulkHandle != new IntPtr(-1))
78+
79+
public override void Reset()
8580
{
86-
CloseHandle(ReadBulkHandle);
87-
ReadBulkHandle = IntPtr.Zero;
81+
// Legacy 驱动通常不支持软重置
8882
}
89-
if (WriteBulkHandle != IntPtr.Zero && WriteBulkHandle != new IntPtr(-1))
83+
84+
public override void Dispose()
9085
{
91-
CloseHandle(WriteBulkHandle);
92-
WriteBulkHandle = IntPtr.Zero;
86+
if (fileHandle != INVALID_HANDLE_VALUE)
87+
{
88+
CloseHandle(fileHandle);
89+
fileHandle = INVALID_HANDLE_VALUE;
90+
}
91+
GC.SuppressFinalize(this);
9392
}
9493
}
95-
96-
9794
}

FirmwareKit.Comm.Fastboot/Usb/Windows/Win32API.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public class Win32API
1616
public static uint FILE_READ_ACCESS { get; } = 1;
1717
public static uint FILE_FLAG_OVERLAPPED { get; } = 0x40000000;
1818

19+
public static uint CTL_CODE(uint deviceType, uint function, uint method, uint access)
20+
{
21+
return (deviceType << 16) | (access << 14) | (function << 2) | method;
22+
}
23+
1924
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
2025
public static extern IntPtr CreateFileW([MarshalAs(UnmanagedType.LPWStr)] string fileName, uint access,
2126
uint shareMode, IntPtr securityAttributes,
@@ -28,11 +33,11 @@ public static extern bool DeviceIoControl(IntPtr device, uint code,
2833
byte[] outBuffer, int outBufferSize,
2934
out int bytesReturned, IntPtr overlapped);
3035

31-
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
36+
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
3237
public static extern bool CloseHandle(IntPtr handle);
3338

3439
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
35-
public static extern bool WriteFile(IntPtr hFile, byte[] buffer, uint sizeToWrite, out ulong bytesWritten, IntPtr overlapped);
40+
public static extern bool WriteFile(IntPtr hFile, byte[] buffer, uint sizeToWrite, out uint bytesWritten, IntPtr overlapped);
3641

3742
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
3843
public static extern bool ReadFile(IntPtr hFile, byte[] buffer, uint sizeToRead, out uint bytesRead, IntPtr overlapped);

FirmwareKit.Comm.Fastboot/Usb/Windows/WinUSBAPI.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ public static extern bool WinUsb_GetDescriptor(IntPtr DeviceHandle, byte Descrip
4747

4848
[DllImport("Winusb.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
4949
public static extern bool WinUsb_WritePipe(IntPtr DeviceHandle, byte pipeID, byte[] buffer,
50-
ulong bufferLen, out ulong bytesTransfered, IntPtr overlapp);
50+
uint bufferLen, out uint bytesTransfered, IntPtr overlapp);
5151

5252
[DllImport("Winusb.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
5353
public static extern bool WinUsb_ReadPipe(IntPtr DeviceHandle, byte pipeID, byte[] buffer,
54-
ulong bufferLen, out ulong bytesTransfered, IntPtr overlapp);
54+
uint bufferLen, out uint bytesTransfered, IntPtr overlapp);
5555

5656
[DllImport("Winusb.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
5757
public static extern bool WinUsb_ResetPipe(IntPtr InterfaceHandle, byte PipeID);

FirmwareKit.Comm.Fastboot/Usb/Windows/WinUSBDevice.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@ public override int CreateHandle()
4444
USBDeviceInterfaceDescriptor.bInterfaceSubClass != 0x42 ||
4545
USBDeviceInterfaceDescriptor.bInterfaceProtocol != 0x03)
4646
{
47-
// This is not a fastboot interface, skip it.
48-
return -1;
47+
// Note: Some drivers might not report official fastboot class/subclass.
48+
// If it is a known Google device (18d1:d00d), we might want to bypass this check.
49+
if (USBDeviceDescriptor.idVendor != 0x18d1 || USBDeviceDescriptor.idProduct != 0xd00d)
50+
{
51+
// This is not a fastboot interface, skip it.
52+
return -1;
53+
}
4954
}
5055

5156
for (byte endpoint = 0; endpoint < USBDeviceInterfaceDescriptor.bNumEndpoints; endpoint++)
@@ -92,6 +97,8 @@ public override int CreateHandle()
9297
return 0;
9398
}
9499

100+
public IntPtr Handle => WinUSBHandle != IntPtr.Zero ? WinUSBHandle : FileHandle;
101+
95102
public override void Reset()
96103
{
97104
if (WinUSBHandle != IntPtr.Zero)
@@ -124,8 +131,8 @@ public override int GetSerialNumber()
124131
public override byte[] Read(int length)
125132
{
126133
byte[] data = new byte[length];
127-
ulong bytesTransfered;
128-
if (WinUsb_ReadPipe(WinUSBHandle, ReadBulkID, data, (ulong)length, out bytesTransfered, IntPtr.Zero))
134+
uint bytesTransfered;
135+
if (WinUsb_ReadPipe(WinUSBHandle, ReadBulkID, data, (uint)length, out bytesTransfered, IntPtr.Zero))
129136
{
130137
byte[] realData = new byte[bytesTransfered];
131138
Array.Copy(data, realData, (int)bytesTransfered);
@@ -136,7 +143,7 @@ public override byte[] Read(int length)
136143

137144
public override long Write(byte[] data, int length)
138145
{
139-
ulong bytesWrite = 0;
146+
uint bytesWrite = 0;
140147
if (WinUSBHandle == IntPtr.Zero)
141148
throw new Exception("Device handle is closed.");
142149

0 commit comments

Comments
 (0)