HIDRemoteDeviceAACPServer-macOS.m   [plain text]

//
// HIDAccesoryServer.m
// HIDAccesoryServer
//
// Created by yg on 12/19/17.
// Copyright © 2017 apple. All rights reserved.
//

#import "HIDRemoteDeviceAACPServer.h"

#import <IOBluetooth/IOBluetooth.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothDevicePriv.h>
#import <IOBluetooth/objc/IOBluetoothAudioManager.h>
#import <IOBluetooth/IOBluetoothDaemonNSXPCClient.h>
#import "HIDRemoteSimpleProtocol.h"
#import "RemoteHIDPrivate.h"
#import <IOKit/hid/IOHIDKeys.h>

static uint16_t generation;

@interface IOBluetoothDevice (RemoteHID)

-(NSString *) description;

@end

@implementation IOBluetoothDevice (RemoteHID)

-(NSString *) description
{
#if !RC_HIDE_B288
return [NSString stringWithFormat:@"<IOBluetoothDevicename:%@ address:%@ h1:%d>", self.name, self.addressString, self.isH1];
#else
return [NSString stringWithFormat:@"<IOBluetoothDevicename:%@ address:%@>", self.name, self.addressString];
#endif
}

@end

@interface HIDRemoteDeviceAACPServer () <IOBluetoothDaemonNSXPCDelegate>
{
dispatch_queue_t _queue;
}

@property IOBluetoothDaemonNSXPCClient * client;

@end

@implementation HIDRemoteDeviceAACPServer

-(nullable instancetype) initWithQueue:(dispatch_queue_t) queue
{
self = [super initWithQueue:queue ];
if (!self) {
return self;
}

_queue = dispatch_queue_create("com.apple.hidrc.bluetooth", DISPATCH_QUEUE_SERIAL);

self.client = [[IOBluetoothDaemonNSXPCClient alloc] initWithDelegate:self];
if (!self.client) {
return nil;
}

return self;
}

-(void) activate
{
dispatch_async(_queue, ^{
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(audioDeviceCreatedHandler:) name:IOBluetoothAACPDeviceConnected object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disconnectBTDevicesHandler:) name:kIOBluetoothDeviceNotificationNameDisconnected object:nil];

[self connectBTDevices];

[super activate];
});
}

-(void) cancel
{
dispatch_async(_queue, ^{
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:IOBluetoothAudioDeviceCreated object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kIOBluetoothDeviceNotificationNameDisconnected object:nil];

[self.devices enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj __unused, BOOL * _Nonnull stop __unused) {
[self removeBTDevice:key];
}];

[super cancel];
});
}

-(void) audioDeviceCreatedHandler:(NSNotification *) notification
{
IOBluetoothDevice * device = [IOBluetoothDevice deviceWithAddressString:(NSString *)[notification object]];
dispatch_async(self.queue, ^{
[self connectBTDevice:device];
});
}

-(void) connectBTDevices
{
for (IOBluetoothDevice * device in [IOBluetoothDevice connectedDevices]) {
[self connectBTDevice:device];
}
}

-(void) connectBTDevice:(IOBluetoothDevice *)device
{
os_log_info (RemoteHIDLog (), "Connect device:%@", device);
#if !RC_HIDE_B288
if (device.isH1) {
if (!self.devices[device]) {
[self addBTDevice:device];
}
}
#endif
}

-(void) disconnectBTDevicesHandler:(NSNotification *) notification
{
os_log_info (RemoteHIDLog (), "disconnectBTDevicesHandler:%@", notification);

IOBluetoothDevice * device = (IOBluetoothDevice *)[notification object];
dispatch_async(self.queue, ^{
[self removeBTDevice:device];
});
}

-(void) addBTDevice:(IOBluetoothDevice *) device
{
IOReturn status;

os_log (RemoteHIDLog (), "HID AACP device connect:%@", device);

[self connectEndpoint:device];

size_t packetLength = sizeof(HIDTransportHeader) + sizeof(HIDDeviceControl);
uint8_t packet[packetLength];

HIDTransportHeader * header = (HIDTransportHeader *)&packet[0];
header->generation = ++generation;

HIDDeviceControl * devicePacket = (HIDDeviceControl *)&packet[sizeof(HIDTransportHeader)];
devicePacket->header.packetType = HIDPacketTypeDeviceConnect;
devicePacket->header.length = sizeof(HIDDeviceControl);

// sort crash protection send message to other side to notify we are ready to handle events
status = [self sendMessageBTDevice:device data:(const uint8_t *) &packet[0] size:packetLength];
require_action_quiet(status == kIOReturnSuccess, exit, os_log_error (RemoteHIDLog (), "addDevice device:%p result:%d", device, status));

exit:

return;
}

-(IOReturn) sendMessageBTDevice:(IOBluetoothDevice *) device data:(const uint8_t *) data size:(size_t) size
{
IOReturn status = kIOReturnSuccess;

NSData * message = [NSData dataWithBytes:data length:size];

os_log_debug (RemoteHIDLogPackets (), "[%p] send packet:%{RemoteHID:packet}.*P", device, (int)size , data);

[self.client sendAACPCustomData:HID_AACP_MESSAGE_TYPE withData:message forDevice:device.addressString];

return status;
}

-(void) removeBTDevice:(IOBluetoothDevice *) device
{
os_log (RemoteHIDLog (), "HID AACP device disconnect:%@", device);

[self disconnectEndpoint:device];
}

- (void)aacpCustomDataEvent:(BTAccessoryCustomMessageType)type withData:(NSData *)data forDevice:(NSString *)deviceAddress;
{
uint64_t timestamp = mach_absolute_time ();

os_log_debug (RemoteHIDLog (), "aacpCustomDataEvent address:%@ type:0x%x size:%lu", deviceAddress, type, (unsigned long)data.length);

if (type != HID_AACP_MESSAGE_TYPE) {
return;
}

dispatch_sync(_queue, ^{
__block IOBluetoothDevice * endpoint = nil;

[self.devices enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj __unused, BOOL * _Nonnull stop ) {
IOBluetoothDevice * device = (IOBluetoothDevice *) key;
if ([deviceAddress isEqualToString:device.addressString]) {
endpoint = device;
*stop = YES;
}
}];
if (endpoint) {
os_log_debug (RemoteHIDLogPackets (), "[%p] receive packet client:0x%x timestamp:%lld packet:%{RemoteHID:packet}.*P", endpoint, type, timestamp, (int)data.length, data.bytes);

[self endpointMessageHandler:(id) endpoint data:(uint8_t*) data.bytes size:(size_t)data.length];
}
});
}

-(IOReturn) remoteDeviceSetReport:(HIDRemoteDevice *) device
type:(IOHIDReportType) type
reportID:(__unused uint8_t)reportID
report:(uint8_t *) report
reportLength:(NSUInteger) reportLength
{
IOReturn status = kIOReturnSuccess;
HIDDeviceReport * devicePacket;
HIDTransportHeader * header;
NSMutableData * data = [[NSMutableData alloc] initWithLength:reportLength + sizeof(HIDDeviceReport) + sizeof(HIDTransportHeader)];

os_log_debug (RemoteHIDLog (), "remoteDeviceSetReport deviceID:0x%llx type:%d reportLength:%d", device.deviceID, type, (int)reportLength);

devicePacket = (HIDDeviceReport *) ((uint8_t *)data.bytes + sizeof(HIDTransportHeader));
header = (HIDTransportHeader *) ((uint8_t *)data.bytes);

memcpy(devicePacket->data, report, reportLength);
devicePacket->header.deviceID = (uint32_t) device.deviceID;
devicePacket->header.packetType = HIDPacketTypeSetReport;
devicePacket->reportType = type;
devicePacket->header.length = (uint32_t)reportLength + sizeof(HIDDeviceReport);
header->generation = ++generation;

IOBluetoothDevice * btDevice = (IOBluetoothDevice *) device.endpoint;
status = [self sendMessageBTDevice:btDevice data:(const uint8_t *) data.bytes size:data.length];
require_action_quiet(status == kIOReturnSuccess, exit, status = kIOReturnError; os_log_error (RemoteHIDLog (), "SetReport device:%@ status:0x%x", device, status));

exit:
return status;
}

-(BOOL) createRemoteDevice:(id) endpoint deviceID:(uint64_t) deviceID property:(NSMutableDictionary *) property
{
IOBluetoothDevice * btDevice = (IOBluetoothDevice *) endpoint;

property[@kIOHIDTransportKey] = @kIOHIDTransportBTAACPValue;
property[@kIOHIDRequestTimeoutKey] = @(kRemoteHIDDeviceTimeout * USEC_PER_SEC);
property[@"BT_ADDR"] = btDevice.addressString;

return [super createRemoteDevice:endpoint deviceID:deviceID property:property];
}

-(IOReturn) remoteDeviceGetReport:(HIDRemoteDevice *) device
type:(IOHIDReportType) type
reportID:(uint8_t) reportID
report:(__unused uint8_t *) report
reportLength:(__unused NSUInteger *) reportLength
{
NSMutableData * data = [[NSMutableData alloc] initWithLength:sizeof(HIDDeviceReport) + sizeof(HIDTransportHeader) + 1];
IOReturn status = kIOReturnSuccess;
HIDDeviceReport * devicePacket;
HIDTransportHeader * header;

devicePacket = (HIDDeviceReport *) ((uint8_t *)data.bytes + sizeof(HIDTransportHeader));
header = (HIDTransportHeader *) ((uint8_t *)data.bytes);

devicePacket->header.deviceID = (uint32_t) device.deviceID;
devicePacket->header.packetType = HIDPacketTypeGetReport;
devicePacket->reportType = type;
devicePacket->header.length = 1 + sizeof(HIDDeviceReport);
devicePacket->data[0] = reportID;
header->generation = ++generation;

IOBluetoothDevice * btDevice = (IOBluetoothDevice *) device.endpoint;
status = [self sendMessageBTDevice:btDevice data:(const uint8_t *) data.bytes size:data.length];
require_action_quiet(status == kIOReturnSuccess, exit, status = kIOReturnError; os_log_error (RemoteHIDLog (), "GetReport device:%@ status:0x%x", btDevice, status));

exit:

return status;
}

@end