/// 匹配到设备回调
static void hidAppearDevice(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef deviceRef) {
if ([DeviceManger shared].isConneted) {
- // [[HidManager shared] showLog:(@"当前处于连接状态,对监听到有USB 插入 的事件不做处理")];
+ //[[HidManager shared] showLog:(@"当前处于连接状态,对监听到有USB 插入 的事件不做处理")];
return;
}
IOReturn ret = IOHIDDeviceClose(deviceRef, kIOHIDOptionsTypeNone);
if (ret != kIOReturnSuccess) {
[self showLog:(@"关闭device失败!")];
+ return;
}
// 清除引用
//检查是否已连接
- (void)checkedConected{
if (![DeviceManger shared].isConneted) {
- [HidManager shared].connectStateCallBack(true);
+ if ([HidManager shared].productType == AiMouseProductType_keyboard) {
+ [[HidManager shared] closeHid];
+ [[HidManager shared] openHid];
+ return;
+ }
+ else{
+ [HidManager shared].connectStateCallBack(true);
+ }
}
}
#pragma mark - public
- (void)openHid{
- // [self close];
if (self.hidManager == nil) {
self.deviceInfoCha = nil;
self.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
IOReturn ret = IOHIDManagerOpen(self.hidManager, 0L);
if (ret != kIOReturnSuccess) {
[self showLog:(@"打开hid失败!")];
+ return;
}else{
[self showLog:(@"打开hid成功!")];
}
IOHIDManagerRegisterDeviceRemovalCallback(self.hidManager, &hidDisAppearDevice, NULL);
}
}
-
- (BOOL)closeHid{
[self showLog:@"关闭hid"];
- // 关闭所有已连接的设备
- if (self.deviceInfoCha) {
- [self closeDevice:self.deviceInfoCha];
+
+ // 标记正在关闭,防止重复调用
+ static BOOL isClosing = NO;
+ if (isClosing) {
+ [self showLog:@"已经在关闭过程中,忽略重复请求"];
+ return NO;
+ }
+ isClosing = YES;
+
+ // 禁用回调处理
+ static BOOL callbacksDisabled = NO;
+ if (!callbacksDisabled) {
+ callbacksDisabled = YES;
+
+ // 创建一个临时队列来处理后续操作
+ dispatch_queue_t tempQueue = dispatch_queue_create("com.hidmanager.cleanup", DISPATCH_QUEUE_SERIAL);
+ dispatch_async(tempQueue, ^{
+
+ // 第一步:取消所有设备的调度并关闭
+ [self performSelector:@selector(closeAllDevices) withObject:nil afterDelay:0.1];
+
+ // 第二步:延迟后关闭HID管理器
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ [self performSelector:@selector(closeHIDManager) withObject:nil afterDelay:0.1];
+
+ // 第三步:重置状态
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ isClosing = NO;
+ callbacksDisabled = NO;
+ });
+ });
+ });
+
+ return YES;
}
+ return NO;
+}
+
+- (void)closeAllDevices {
+ [self showLog:@"开始关闭所有设备"];
+
+ // 关闭voiceRef设备
if (self.voiceRef) {
- [self closeDevice:self.voiceRef];
+ [self showLog:@"关闭voiceRef设备"];
+ IOHIDDeviceUnscheduleFromRunLoop(self.voiceRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+ IOHIDDeviceRegisterInputReportCallback(self.voiceRef, NULL, 0, NULL, NULL);
+
+ // 关闭设备前发送空报告,确保没有挂起的操作
+ IOHIDDeviceSetReport(self.voiceRef, kIOHIDReportTypeOutput, 0, NULL, 0);
+
+ IOReturn ret = IOHIDDeviceClose(self.voiceRef, kIOHIDOptionsTypeNone);
+ if (ret == kIOReturnSuccess) {
+ [self showLog:@"voiceRef设备关闭成功"];
+ self.voiceRef = nil;
+ } else {
+ [self showLog:[NSString stringWithFormat:@"voiceRef设备关闭失败,错误码:%d", ret]];
+ }
}
+ // 关闭moveRef设备
if (self.moveRef) {
- [self closeDevice:self.moveRef];
+ [self showLog:@"关闭moveRef设备"];
+ IOHIDDeviceUnscheduleFromRunLoop(self.moveRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+ IOHIDDeviceRegisterInputReportCallback(self.moveRef, NULL, 0, NULL, NULL);
+
+ // 关闭设备前发送空报告,确保没有挂起的操作
+ IOHIDDeviceSetReport(self.moveRef, kIOHIDReportTypeOutput, 0, NULL, 0);
+
+ IOReturn ret = IOHIDDeviceClose(self.moveRef, kIOHIDOptionsTypeNone);
+ if (ret == kIOReturnSuccess) {
+ [self showLog:@"moveRef设备关闭成功"];
+ self.moveRef = nil;
+ } else {
+ [self showLog:[NSString stringWithFormat:@"moveRef设备关闭失败,错误码:%d", ret]];
+ }
+ }
+
+ // 关闭deviceInfoCha设备(最后关闭,因为它是主设备)
+ if (self.deviceInfoCha) {
+ [self showLog:@"关闭deviceInfoCha设备"];
+ IOHIDDeviceUnscheduleFromRunLoop(self.deviceInfoCha, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+ IOHIDDeviceRegisterInputReportCallback(self.deviceInfoCha, NULL, 0, NULL, NULL);
+
+ // 关闭设备前发送空报告,确保没有挂起的操作
+ IOHIDDeviceSetReport(self.deviceInfoCha, kIOHIDReportTypeOutput, 0, NULL, 0);
+
+ IOReturn ret = IOHIDDeviceClose(self.deviceInfoCha, kIOHIDOptionsTypeNone);
+ if (ret == kIOReturnSuccess) {
+ [self showLog:@"deviceInfoCha设备关闭成功"];
+ self.deviceInfoCha = nil;
+ } else {
+ [self showLog:[NSString stringWithFormat:@"deviceInfoCha设备关闭失败,错误码:%d", ret]];
+ }
}
// 清空缓冲区字典
}
[self.deviceBuffers removeAllObjects];
- // 关闭HID管理器
+ [self showLog:@"所有设备关闭操作已完成"];
+}
+
+- (void)closeHIDManager {
+ [self showLog:@"开始关闭HID管理器"];
+
if (self.hidManager != nil) {
- // 取消注册回调
+ // 确保所有回调都被注销
IOHIDManagerRegisterDeviceMatchingCallback(self.hidManager, NULL, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(self.hidManager, NULL, NULL);
- IOReturn ret = IOHIDManagerClose(self.hidManager, 0);
- if (ret != kIOReturnSuccess) {
- [self showLog:[NSString stringWithFormat:@"关闭hid失败! 错误码:%d", ret]];
- }
+ // 停止HID管理器在RunLoop中的运行
+ IOHIDManagerUnscheduleFromRunLoop(self.hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
- [self showLog:@"IOHIDManager已关闭"];
- self.hidManager = nil;
+ // 运行RunLoop一小段时间,让系统处理可能的挂起操作
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
+
+ // 尝试关闭HID管理器
+ IOReturn ret = IOHIDManagerClose(self.hidManager, kIOHIDOptionsTypeNone);
+
+ if (ret == kIOReturnSuccess) {
+ [self showLog:@"IOHIDManager关闭成功"];
+ self.hidManager = nil;
+ } else {
+ [self showLog:[NSString stringWithFormat:@"IOHIDManager关闭失败,错误码:%d", ret]];
+
+ // 尝试强制释放
+ [self showLog:@"尝试强制释放HID管理器资源"];
+ CFRelease(self.hidManager);
+ self.hidManager = nil;
+ }
}
- return true;
+ [self showLog:@"HID管理器关闭操作已完成"];
+ [self openHid];
}
-
/// 发送数据给设备
-(void)writeToDevice:(NSData *)data{
if(self.deviceInfoCha == nil){
uuid = "D56CBD13-B1CD-4964-946C-9C0A140796C8"
type = "0"
version = "2.0">
+ <Breakpoints>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
+ <BreakpointContent
+ uuid = "7ADC72BB-C729-4CB1-A83F-A2B865E07EA2"
+ shouldBeEnabled = "Yes"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ breakpointStackSelectionBehavior = "1"
+ scope = "1"
+ stopOnStyle = "0">
+ </BreakpointContent>
+ </BreakpointProxy>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.SymbolicBreakpoint">
+ <BreakpointContent
+ uuid = "9DAE9373-E045-4357-AD27-9C8D84EB335F"
+ shouldBeEnabled = "Yes"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ symbolName = ""
+ moduleName = "">
+ <Locations>
+ </Locations>
+ </BreakpointContent>
+ </BreakpointProxy>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+ <BreakpointContent
+ uuid = "6E24C828-D956-49E9-8DE9-FAC25C078B6C"
+ shouldBeEnabled = "Yes"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ filePath = "AiMouseSdk/Manager/HidManager.m"
+ startingColumnNumber = "9223372036854775807"
+ endingColumnNumber = "9223372036854775807"
+ startingLineNumber = "280"
+ endingLineNumber = "280"
+ landmarkName = "hidDisAppearDevice(inContext, inResult, inSender, deviceRef)"
+ landmarkType = "9">
+ </BreakpointContent>
+ </BreakpointProxy>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+ <BreakpointContent
+ uuid = "7188EF66-0443-491E-875C-6B4DD9B0A8BF"
+ shouldBeEnabled = "Yes"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ filePath = "AiMouseSdk/Manager/HidManager.m"
+ startingColumnNumber = "9223372036854775807"
+ endingColumnNumber = "9223372036854775807"
+ startingLineNumber = "296"
+ endingLineNumber = "296"
+ landmarkName = "hidReceiveData(context, result, sender, type, reportID, report, reportLength)"
+ landmarkType = "9">
+ </BreakpointContent>
+ </BreakpointProxy>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+ <BreakpointContent
+ uuid = "569CAE7A-0458-4538-B95A-BD92FABAAEC0"
+ shouldBeEnabled = "Yes"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ filePath = "AiMouseSdk/Manager/HidManager.m"
+ startingColumnNumber = "9223372036854775807"
+ endingColumnNumber = "9223372036854775807"
+ startingLineNumber = "290"
+ endingLineNumber = "290"
+ landmarkName = "mouseData(context, result, sender, type, reportID, report, reportLength)"
+ landmarkType = "9">
+ </BreakpointContent>
+ </BreakpointProxy>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+ <BreakpointContent
+ uuid = "FD2BC354-975B-4060-8012-AA80EB63A88A"
+ shouldBeEnabled = "Yes"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ filePath = "AiMouseSdk/Manager/HidManager.m"
+ startingColumnNumber = "9223372036854775807"
+ endingColumnNumber = "9223372036854775807"
+ startingLineNumber = "524"
+ endingLineNumber = "524"
+ landmarkName = "-writeToDevice:"
+ landmarkType = "7">
+ </BreakpointContent>
+ </BreakpointProxy>
+ </Breakpoints>
</Bucket>