btstack/port/ios/PrefsBundle/BluetoothController.m
2016-03-28 13:15:21 +02:00

311 lines
9.8 KiB
Objective-C

/*
* Copyright (C) 2011 by Matthias Ringwald
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#import "BluetoothController.h"
#include "btstack_run_loop.h"
#include "btstack_run_loop_corefoundation.h"
#include "btstack_event.h"
#pragma mark callback handler
static void btstackStoppedCallback(CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo) {
[[BluetoothController sharedInstance] connectionBroke];
}
static void bt_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t* packet, uint16_t size) {
if (packet_type != HCI_EVENT_PACKET) return;
[[BluetoothController sharedInstance] eventPacketHandler:packet withSize:size];
}
static BluetoothController* sharedInstance = nil;
@implementation BluetoothController
@synthesize listener;
+(BluetoothController*) sharedInstance{
if (!sharedInstance) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
-(void) resetState{
// initial state
isConnected = NO;
system_bluetooth = NO;
hci_state = HCI_STATE_OFF;
state = kIdle;
targetType = BluetoothTypeNone;
}
-(id)init{
self = [super init];
listener = nil;
// register for BTstack restart notficiations
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), // the notification center to use
NULL, // an arbitrary observer-identifier
btstackStoppedCallback, // callback to call when a notification is posted
CFSTR("ch.ringwald.btstack.stopped"), // notification name
NULL, // optional object identifier to filter notifications
CFNotificationSuspensionBehaviorDrop); // suspension behavior
// set up libBTstack
btstack_run_loop_init(btstack_run_loop_corefoundation_get_instance());
bt_register_packet_handler(bt_packet_handler);
return self;
}
-(BOOL)open{
if (!isConnected) {
[self resetState];
int err = bt_open();
if (err){
NSLog(@"Cannot connect to BTdaemon!");
return NO;
}
// NSLog(@"Connected to BTdaemon!");
isConnected = YES;
}
// get status
bt_send_cmd(&btstack_get_state);
return YES;
}
-(void)connectionBroke {
// NSLog(@"BTstack stopped");
[self resetState];
[self open];
[listener bluetoothStateChanged];
}
-(void)close{
if (isConnected) {
// NSLog(@"Disconnected from BTdaemon!");
bt_close();
}
[self resetState];
}
-(BOOL)isConnected{
return isConnected;
}
-(BluetoothType_t)targetType{
if (isActive) {
return targetType;
}
return [self bluetoothType];
}
-(BluetoothType_t) bluetoothType {
if (hci_state != HCI_STATE_OFF) {
return BluetoothTypeBTstack;
}
if (system_bluetooth) {
return BluetoothTypeApple;
}
return BluetoothTypeNone;
}
-(BOOL)isActive{
return isActive;
}
-(BOOL)canChange{
if (isActive) return NO;
if (!isConnected) return NO;
return YES;
}
-(void)bluetoothStateChanged {
BluetoothType_t type = [self bluetoothType];
if (state != kIdle && type != targetType) {
return;
}
// NSLog(@"bluetoothStateChanged %u", type);
state = kIdle;
isActive = NO;
[listener bluetoothStateChanged];
}
-(void)eventPacketHandler:(uint8_t*) packet withSize:(uint16_t) size {
// NSLog(@"bt_packet_handler event: %u, state %u", packet[0], state);
// update state
switch(hci_event_packet_get_type(packet)){
case BTSTACK_EVENT_STATE:
hci_state = packet[2];
// NSLog(@"new BTSTACK_EVENT_STATE %u", hci_state);
break;
case DAEMON_EVENT_SYSTEM_BLUETOOTH_ENABLED:
system_bluetooth = packet[2];
// NSLog(@"new DAEMON_EVENT_SYSTEM_BLUETOOTH_ENABLED %u", system_bluetooth);
break;
default:
break;
}
switch(state){
case kIdle:
if (hci_event_packet_get_type(packet) == BTSTACK_EVENT_STATE) {
if (hci_state == HCI_STATE_OFF) {
bt_send_cmd(&btstack_get_system_bluetooth_enabled);
} else {
system_bluetooth = 0;
}
}
break;
case kW4SystemOffToEnableBTstack:
if (hci_event_packet_get_type(packet) == DAEMON_EVENT_SYSTEM_BLUETOOTH_ENABLED) {
if (system_bluetooth == 0){
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON);
state = kIdle;
}
}
break;
case kW4BTstackOffToEnableSystem:
if (hci_event_packet_get_type(packet) == BTSTACK_EVENT_STATE) {
if (hci_state == HCI_STATE_OFF) {
// NSLog(@"Sending set system bluetooth enable A");
bt_send_cmd(&btstack_set_system_bluetooth_enabled, 1);
}
}
if (hci_event_packet_get_type(packet) == DAEMON_EVENT_SYSTEM_BLUETOOTH_ENABLED) {
if (system_bluetooth == 0){
// NSLog(@"Sending set system bluetooth enable B");
bt_send_cmd(&btstack_set_system_bluetooth_enabled, 1);
} else {
// NSLog(@"Sending set system bluetooth enable DONE");
state = kIdle;
}
}
break;
case kW4Transition:
break;
}
[self bluetoothStateChanged];
}
-(void)requestType:(BluetoothType_t)bluetoothType{
// NSLog(@"bluetoothChangeRequested: old %u, new %u", [self bluetoothType], bluetoothType);
// ignore taps during transition
if (state != kIdle) {
return;
};
switch ([self bluetoothType]){
case BluetoothTypeNone:
switch (bluetoothType) {
case BluetoothTypeNone:
break;
case BluetoothTypeApple:
state = kW4BTstackOffToEnableSystem; // hack: turning on iOS after BTstack fails, this will make it retry
bt_send_cmd(&btstack_set_system_bluetooth_enabled, 1);
targetType = BluetoothTypeApple;
isActive = YES;
break;
case BluetoothTypeBTstack:
state = kW4Transition;
bt_send_cmd(&btstack_set_bluetooth_enabled, 1);
targetType = BluetoothTypeBTstack;
isActive = YES;
break;
}
break;
case BluetoothTypeApple:
switch (bluetoothType) {
case BluetoothTypeNone:
state = kW4Transition;
bt_send_cmd(&btstack_set_system_bluetooth_enabled, 0);
targetType = BluetoothTypeNone;
isActive = YES;
break;
case BluetoothTypeApple:
break;
case BluetoothTypeBTstack:
state = kW4SystemOffToEnableBTstack;
bt_send_cmd(&btstack_set_system_bluetooth_enabled, 0);
targetType = BluetoothTypeBTstack;
isActive = YES;
break;
}
break;
case BluetoothTypeBTstack:
switch (bluetoothType) {
case BluetoothTypeNone:
state = kW4Transition;
bt_send_cmd(&btstack_set_bluetooth_enabled, 0);
targetType = BluetoothTypeNone;
isActive = YES;
break;
case BluetoothTypeApple:
state = kW4BTstackOffToEnableSystem;
bt_send_cmd(&btstack_set_bluetooth_enabled, 0);
targetType = BluetoothTypeApple;
isActive = YES;
break;
case BluetoothTypeBTstack:
break;
}
break;
}
}
@end