Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion entry/src/main/ets/components/test/UsbControllerTestView.ets
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import { usbManager } from '@kit.BasicServicesKit';
import { UsbDriverService, UsbDriverListener, ControllerType, AbstractController, ButtonFlags } from '../../service/usbdriver';
import { HidDdkController } from '../../service/usbdriver/HidDdkController';
import { GamepadManager } from '../../service/input/GamepadManager';
import { AXIS_MAX } from '../../service/input/GamepadTypes';
import { AppColors } from '../../common/Theme';
Expand Down Expand Up @@ -56,6 +57,7 @@ class DeviceInfo {
vid: number = 0;
pid: number = 0;
type: number = 0;
transport: string = '';
}

// USB 驱动监听器实现类
Expand Down Expand Up @@ -127,13 +129,15 @@ export struct UsbControllerTestView {
@State logMessages: string[] = [];
@State pollRate: number = 0;
@State pollCount: number = 0;
@State hidChannelInfo: string = '';

private usbDriverService: UsbDriverService | null = null;
private listener: TestViewDriverListener | null = null;
private pollCountInternal: number = 0;
private lastPollStatTime: number = 0;
private pollRateTimer: number = -1;
private uiRefreshTimer: number = -1;
private hidStatsTimer: number = -1;
// 内部缓冲区:USB 轮询回调(高频,125-1000Hz)写入此变量,
// 由 UI 刷新定时器同步到 @State 以正确触发 UI 重渲染
private internalLastInputState: ControllerInputState | null = null;
Expand All @@ -143,13 +147,44 @@ export struct UsbControllerTestView {
this.checkUsbDevices();
this.startPollRateTimer();
this.startUiRefreshTimer();
this.startHidStatsTimer();
}

aboutToDisappear(): void {
this.stopUiRefreshTimer();
this.stopPollRateTimer();
this.stopHidStatsTimer();
this.stopDriver();
}

// 1 秒刷新 HID DDK 通道状态(iface/描述符/报文率/最近错误)
private startHidStatsTimer(): void {
this.hidStatsTimer = setInterval(() => {
try {
const controllers = UsbDriverService.getInstance().getControllers();
const hidController = controllers.find(c => c instanceof HidDdkController);
if (hidController instanceof HidDdkController) {
const stats = hidController.getReaderStats();
this.hidChannelInfo =
`接口 ${stats.iface} · 描述符 ${stats.descriptorLength}B · ` +
`${stats.reportsPerSec.toFixed(1)} Hz · 累计 ${stats.totalReports} 报文` +
(stats.lastError !== 0 ? ` · 错误 ${stats.lastError}` : '') +
(stats.running ? '' : ' · 已停止');
} else {
this.hidChannelInfo = '';
}
} catch (err) {
this.hidChannelInfo = '';
}
}, 1000);
}

private stopHidStatsTimer(): void {
if (this.hidStatsTimer !== -1) {
clearInterval(this.hidStatsTimer);
this.hidStatsTimer = -1;
}
}

private startPollRateTimer(): void {
this.lastPollStatTime = Date.now();
Expand Down Expand Up @@ -228,6 +263,7 @@ export struct UsbControllerTestView {
deviceInfo.vid = controller.getVendorId();
deviceInfo.pid = controller.getProductId();
deviceInfo.type = controller.getControllerType();
deviceInfo.transport = controller.getTransportInfo();
this.connectedDevices = [...this.connectedDevices, deviceInfo];
}

Expand Down Expand Up @@ -486,7 +522,7 @@ export struct UsbControllerTestView {
.fontSize(13)
.fontWeight(FontWeight.Medium)
.fontColor(AppColors.TextPrimary)
Text(`${this.getControllerTypeName(device.type)} · VID:0x${device.vid.toString(16).toUpperCase()} PID:0x${device.pid.toString(16).toUpperCase()}`)
Text(`${this.getControllerTypeName(device.type)}${device.transport !== '' ? ` · ${device.transport}` : ''} · VID:0x${device.vid.toString(16).toUpperCase()} PID:0x${device.pid.toString(16).toUpperCase()}`)
.fontSize(10)
.fontColor(AppColors.TextTertiary)
}
Expand All @@ -506,6 +542,29 @@ export struct UsbControllerTestView {
.borderRadius(12)
.border({ width: 1, color: AppColors.CardBorder })

// ═══ HID DDK 通道状态卡(实验通道工作时显示) ═══
if (this.hidChannelInfo !== '') {
Row({ space: 8 }) {
Text('HID-DDK')
.fontSize(10)
.fontWeight(FontWeight.Bold)
.fontColor(AppColors.Success)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor('#1A4CAF50')
.borderRadius(4)
Text(this.hidChannelInfo)
.fontSize(11)
.fontColor(AppColors.TextSecondary)
.fontFamily('monospace')
.layoutWeight(1)
}
.width('100%')
.padding({ left: 12, right: 12, top: 8, bottom: 8 })
.backgroundColor(AppColors.CardBackground)
.borderRadius(8)
.border({ width: 1, color: AppColors.CardBorder })
}

// ═══ 输入可视化卡片 ═══
if (this.hasInput) {
Column({ space: 10 }) {
Expand Down
6 changes: 6 additions & 0 deletions entry/src/main/ets/entryability/EntryAbility.ets
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { AppStateService } from '../service/AppStateService';
import { UiSettings } from '../service/SettingsService';
import { RcpSessionPool } from '../utils/RcpSessionPool';
import { NetworkErrorClassifierSelfCheck } from '../utils/NetworkErrorClassifierSelfCheck';
import { HidDdkProbe } from '../service/usbdriver/HidDdkProbe';

const TAG = 'EntryAbility';
const DOMAIN = 0x0000;
Expand Down Expand Up @@ -45,6 +46,11 @@ export default class EntryAbility extends UIAbility {
// 注册应用前后台状态变化监听
this.registerAppStateCallback();

// HID DDK 主进程可用性自检(无 USB 设备时用哑 deviceId 验证 Init 权限)
HidDdkProbe.startupCheck().catch(() => {
// 自检只打日志,失败无需处理
});

// 网络契约自检(纯函数,零开销,仅日志)
NetworkErrorClassifierSelfCheck.run();
}
Expand Down
17 changes: 17 additions & 0 deletions entry/src/main/ets/pages/SettingsPageV2.ets
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ struct SettingsPageV2 {
@State usbDriverEnabled: boolean = false; // 默认关闭 USB 手柄驱动
@State forceUsbDriverOnly: boolean = false; // 强制纯 USB 驱动模式
@State ddkHighSpeedPolling: boolean = false; // USB 驱动高速轮询
@State hidDdkInputChannel: boolean = false; // HID DDK 输入通道(实验)

// 输入 - 屏幕控制器
@State enableOnscreenControls: boolean = false;
Expand Down Expand Up @@ -563,6 +564,7 @@ struct SettingsPageV2 {
this.usbDriverEnabled = await this.loadBoolean(SettingsKeys.USB_DRIVER_ENABLED, false);
this.forceUsbDriverOnly = await this.loadBoolean(SettingsKeys.FORCE_USB_DRIVER_ONLY, false);
this.ddkHighSpeedPolling = await this.loadBoolean(SettingsKeys.DDK_HIGH_SPEED_POLLING, false);
this.hidDdkInputChannel = await this.loadBoolean(SettingsKeys.HID_DDK_INPUT_CHANNEL, false);

// 输入 - 屏幕控制器
this.enableOnscreenControls = await this.loadBoolean(SettingsKeys.ENABLE_ONSCREEN_CONTROLS, false);
Expand Down Expand Up @@ -1727,6 +1729,21 @@ struct SettingsPageV2 {
});
}
},
{
title: 'HID DDK 输入通道(实验)',
subtitle: '通过内核 HID 接口直读手柄原始报文,免 USB 接口声明与内核驱动重绑定。需配合"强制 USB 驱动接管输入"使用,不可用时自动回退',
type: 'toggle',
value: this.hidDdkInputChannel,
visible: this.usbDriverEnabled,
action: () => {
this.hidDdkInputChannel = !this.hidDdkInputChannel;
this.saveSetting(SettingsKeys.HID_DDK_INPUT_CHANNEL, this.hidDdkInputChannel);
ToastQueue.show({
message: '下次串流时生效',
duration: 2500
});
}
},
{
title: '手柄测试',
subtitle: '测试 USB 和蓝牙/系统手柄',
Expand Down
3 changes: 3 additions & 0 deletions entry/src/main/ets/service/SettingsService.ets
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class SettingsKeys {
static readonly USB_DRIVER_ENABLED: string = 'settings_usb_driver_enabled'; // 默认启用 USB 手柄驱动
static readonly FORCE_USB_DRIVER_ONLY: string = 'settings_force_usb_driver_only'; // 强制纯 USB 驱动模式(禁用 GCK)
static readonly DDK_HIGH_SPEED_POLLING: string = 'settings_ddk_high_speed_polling'; // USB 驱动高速轮询(DDK)
static readonly HID_DDK_INPUT_CHANNEL: string = 'settings_hid_ddk_input_channel'; // HID DDK 输入通道(实验,hidraw 直读)

// 输入 - 屏幕控制器
static readonly ENABLE_ONSCREEN_CONTROLS: string = 'settings_enable_onscreen_controls';
Expand Down Expand Up @@ -209,6 +210,7 @@ export interface InputSettings {
usbDriverEnabled: boolean; // 默认启用 USB 手柄驱动
forceUsbDriverOnly: boolean; // 强制纯 USB 驱动模式
ddkHighSpeedPolling: boolean; // USB 驱动高速轮询(DDK)
hidDdkInputChannel: boolean; // HID DDK 输入通道(实验)

// 体感助手
gyroAssistEnabled: boolean;
Expand Down Expand Up @@ -795,6 +797,7 @@ export class SettingsService {
usbDriverEnabled: await this.getBoolean(SettingsKeys.USB_DRIVER_ENABLED, false),
forceUsbDriverOnly: await this.getBoolean(SettingsKeys.FORCE_USB_DRIVER_ONLY, false),
ddkHighSpeedPolling: await this.getBoolean(SettingsKeys.DDK_HIGH_SPEED_POLLING, false),
hidDdkInputChannel: await this.getBoolean(SettingsKeys.HID_DDK_INPUT_CHANNEL, false),

// 体感助手
gyroAssistEnabled: await this.getBoolean(SettingsKeys.GYRO_ASSIST_ENABLED, false),
Expand Down
5 changes: 5 additions & 0 deletions entry/src/main/ets/service/usbdriver/AbstractController.ets
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,11 @@ export abstract class AbstractController {
}
}

/** 传输通道标识(供测试页/诊断展示),子类可覆写 */
getTransportInfo(): string {
return 'USB';
}

/**
* 启动控制器
* @returns 是否启动成功
Expand Down
Loading
Loading