diff --git a/TODO.txt b/TODO.txt index 200d184d1..18f8d5fe1 100644 --- a/TODO.txt +++ b/TODO.txt @@ -28,9 +28,10 @@ NEXT: - DONE: Bluetooth is on iff at least one client did request it. delay off by 10 seconds - DONE: add enum POWER_EVENTS {POWER_WILL_SLEEP, POWER_WILL_WAKE_UP); - DONE: add register_for_power_management_notifictations( void (*callback)(POWER_EVENTS event)); to bt_control.h - - move core foundation power event code to bt_control_iphone.m - - add sleep() to bt_control.h (assumption: no off -> sleep transition) - + - DONE: move core foundation power event code to bt_control_iphone.m + - DONE: add sleep() to bt_control.h (assumption: no off -> sleep transition) + - call control->sleep() instead of control->off() + - clean up components - consolidate ios code in port_ios.m (bt_control_iphone.m, platform_iphone.m) - consolidate cocoa/corefoundation code in port_corefoundation.c (remote_device_db) diff --git a/src/bt_control_iphone.m b/src/bt_control_iphone.m index efc56719e..a190d77e0 100644 --- a/src/bt_control_iphone.m +++ b/src/bt_control_iphone.m @@ -72,15 +72,57 @@ #define IOKIT #include #include + +// costants +#define sys_iokit err_system(0x38) +#define sub_iokit_common err_sub(0) + +#define iokit_common_msg(message) (UInt32)(sys_iokit|sub_iokit_common|message) + +#define kIOMessageCanDevicePowerOff iokit_common_msg(0x200) +#define kIOMessageDeviceWillPowerOff iokit_common_msg(0x210) +#define kIOMessageDeviceWillNotPowerOff iokit_common_msg(0x220) +#define kIOMessageDeviceHasPoweredOn iokit_common_msg(0x230) +#define kIOMessageCanSystemPowerOff iokit_common_msg(0x240) +#define kIOMessageSystemWillPowerOff iokit_common_msg(0x250) +#define kIOMessageSystemWillNotPowerOff iokit_common_msg(0x260) +#define kIOMessageCanSystemSleep iokit_common_msg(0x270) +#define kIOMessageSystemWillSleep iokit_common_msg(0x280) +#define kIOMessageSystemWillNotSleep iokit_common_msg(0x290) +#define kIOMessageSystemHasPoweredOn iokit_common_msg(0x300) +#define kIOMessageSystemWillRestart iokit_common_msg(0x310) +#define kIOMessageSystemWillPowerOn iokit_common_msg(0x320) + +// types +typedef io_object_t io_connect_t; +typedef io_object_t io_service_t; +typedef kern_return_t IOReturn; +typedef struct IONotificationPort * IONotificationPortRef; + +// prototypes kern_return_t IOMasterPort( mach_port_t bootstrapPort, mach_port_t * masterPort ); CFMutableDictionaryRef IOServiceNameMatching(const char * name ); CFTypeRef IORegistryEntrySearchCFProperty(mach_port_t entry, const io_name_t plane, CFStringRef key, CFAllocatorRef allocator, UInt32 options ); mach_port_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching ); kern_return_t IOObjectRelease(mach_port_t object); + +typedef void (*IOServiceInterestCallback)(void * refcon, io_service_t service, uint32_t messageType, void * messageArgument); +io_connect_t IORegisterForSystemPower (void * refcon, IONotificationPortRef * thePortRef, + IOServiceInterestCallback callback, io_object_t * notifier ); +IOReturn IODeregisterForSystemPower (io_object_t *notifier); +CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify ); +IOReturn IOAllowPowerChange ( io_connect_t kernelPort, long notificationID ); +IOReturn IOCancelPowerChange ( io_connect_t kernelPort, long notificationID ); + +// local globals +static io_object_t notifier; +static io_connect_t root_port = 0; // a reference to the Root Power Domain IOService + #endif #endif + int iphone_system_bt_enabled(){ return SBA_getBluetoothEnabled(); } @@ -93,6 +135,8 @@ void iphone_system_bt_set_enabled(int enabled) #endif +static void (*power_notification_callback)(POWER_NOTIFICATION_t event) = NULL; + #define BUFF_LEN 80 static char buffer[BUFF_LEN+1]; @@ -504,6 +548,103 @@ static int iphone_sleep(void *config){ return iphone_off(config); } +#ifdef IOKIT +static void MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument ) { + printf( "messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)messageArgument); + switch ( messageType ) { + case kIOMessageCanSystemSleep: + /* Idle sleep is about to kick in. This message will not be sent for forced sleep. + Applications have a chance to prevent sleep by calling IOCancelPowerChange. + Most applications should not prevent idle sleep. + + Power Management waits up to 30 seconds for you to either allow or deny idle sleep. + If you don't acknowledge this power change by calling either IOAllowPowerChange + or IOCancelPowerChange, the system will wait 30 seconds then go to sleep. + */ + + // Uncomment to cancel idle sleep + // IOCancelPowerChange( root_port, (long)messageArgument ); + // we will allow idle sleep + IOAllowPowerChange( root_port, (long)messageArgument ); + break; + + case kIOMessageSystemWillSleep: + /* The system WILL go to sleep. If you do not call IOAllowPowerChange or + IOCancelPowerChange to acknowledge this message, sleep will be + delayed by 30 seconds. + + NOTE: If you call IOCancelPowerChange to deny sleep it returns kIOReturnSuccess, + however the system WILL still go to sleep. + */ + + if (power_notification_callback){ + (*power_notification_callback)(POWER_WILL_SLEEP); + } + + // don't allow power change to get the 30 second delay + // IOAllowPowerChange( root_port, (long)messageArgument ); + + break; + + case kIOMessageSystemWillPowerOn: + + // System has started the wake up process... + if (power_notification_callback){ + (*power_notification_callback)(POWER_WILL_WAKE_UP); + } + break; + + case kIOMessageSystemHasPoweredOn: + //System has finished waking up... + break; + + default: + break; + + } +} + +/** handle IOKIT power notifications - http://developer.apple.com/library/mac/#qa/qa2004/qa1340.html */ +void iphone_register_for_power_notifications(void (*cb)(POWER_NOTIFICATION_t event)){ + static IONotificationPortRef notifyPortRef = NULL; // notification port allocated by IORegisterForSystemPower + static io_object_t notifierObject = 0; // notifier object, used to deregister later + + if (cb) { + if (!root_port) { + // register to receive system sleep notifications + root_port = IORegisterForSystemPower(NULL, ¬ifyPortRef, MySleepCallBack, ¬ifierObject); + if (!root_port) { + printf("IORegisterForSystemPower failed\n"); + } + + // add the notification port to the application runloop + CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes); + } + power_notification_callback = cb; + } else { + if (root_port) { + // we no longer want sleep notifications: + + // remove the sleep notification port from the application runloop + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes); + + // deregister for system sleep notifications + IODeregisterForSystemPower(¬ifierObject); + + // IORegisterForSystemPower implicitly opens the Root Power Domain IOService + // so we close it here + IOServiceClose(root_port); + + // destroy the notification port allocated by IORegisterForSystemPower + IONotificationPortDestroy(notifyPortRef); + + root_port = 0; + } + power_notification_callback = NULL; + } +} +#endif + // single instance bt_control_t bt_control_iphone = { iphone_on, @@ -512,5 +653,9 @@ bt_control_t bt_control_iphone = { iphone_valid, iphone_name, NULL, // custom init sequence +#ifdef IOKIT + iphone_register_for_power_notifications +#else NULL // register_for_power_notifications +#endif }; diff --git a/src/daemon.c b/src/daemon.c index aac0110a9..ebed4ad2c 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -73,7 +73,6 @@ #define DAEMON_NO_ACTIVE_CLIENT_TIMEOUT 10000 -#define HANDLE_POWER_NOTIFICATIONS static hci_transport_t * transport; @@ -94,125 +93,6 @@ static void active_clients_add(connection_t *connection); static void active_clients_start_power_off_timer(); static void active_clients_stop_power_off_timer(); -#ifdef HANDLE_POWER_NOTIFICATIONS - -#include - -// minimal IOKit -#include -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_2_0 -// compile issue fix with 4.2 headers -#undef NSEC_PER_USEC -#undef USEC_PER_SEC -#undef NSEC_PER_SEC -// end of fix -#include -#define IOKIT -#include - -// costants -#define sys_iokit err_system(0x38) -#define sub_iokit_common err_sub(0) - -#define iokit_common_msg(message) (UInt32)(sys_iokit|sub_iokit_common|message) - -#define kIOMessageCanDevicePowerOff iokit_common_msg(0x200) -#define kIOMessageDeviceWillPowerOff iokit_common_msg(0x210) -#define kIOMessageDeviceWillNotPowerOff iokit_common_msg(0x220) -#define kIOMessageDeviceHasPoweredOn iokit_common_msg(0x230) -#define kIOMessageCanSystemPowerOff iokit_common_msg(0x240) -#define kIOMessageSystemWillPowerOff iokit_common_msg(0x250) -#define kIOMessageSystemWillNotPowerOff iokit_common_msg(0x260) -#define kIOMessageCanSystemSleep iokit_common_msg(0x270) -#define kIOMessageSystemWillSleep iokit_common_msg(0x280) -#define kIOMessageSystemWillNotSleep iokit_common_msg(0x290) -#define kIOMessageSystemHasPoweredOn iokit_common_msg(0x300) -#define kIOMessageSystemWillRestart iokit_common_msg(0x310) -#define kIOMessageSystemWillPowerOn iokit_common_msg(0x320) - -// types -typedef io_object_t io_connect_t; -typedef io_object_t io_service_t; -typedef kern_return_t IOReturn; - -typedef struct IONotificationPort * IONotificationPortRef; -typedef void -(*IOServiceInterestCallback)( - void * refcon, - io_service_t service, - uint32_t messageType, - void * messageArgument ); -io_connect_t IORegisterForSystemPower (void * refcon, - IONotificationPortRef * thePortRef, - IOServiceInterestCallback callback, - io_object_t * notifier ); -CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify ); -IOReturn IOAllowPowerChange ( io_connect_t kernelPort, long notificationID ); -IOReturn IOCancelPowerChange ( io_connect_t kernelPort, long notificationID ); - -io_connect_t root_port; -io_object_t notifier; -io_connect_t root_port; // a reference to the Root Power Domain IOService - -void MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument ) { - printf( "messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)messageArgument); - switch ( messageType ) { - case kIOMessageCanSystemSleep: - /* Idle sleep is about to kick in. This message will not be sent for forced sleep. - Applications have a chance to prevent sleep by calling IOCancelPowerChange. - Most applications should not prevent idle sleep. - - Power Management waits up to 30 seconds for you to either allow or deny idle sleep. - If you don't acknowledge this power change by calling either IOAllowPowerChange - or IOCancelPowerChange, the system will wait 30 seconds then go to sleep. - */ - - // Uncomment to cancel idle sleep - // IOCancelPowerChange( root_port, (long)messageArgument ); - // we will allow idle sleep - IOAllowPowerChange( root_port, (long)messageArgument ); - break; - - case kIOMessageSystemWillSleep: - /* The system WILL go to sleep. If you do not call IOAllowPowerChange or - IOCancelPowerChange to acknowledge this message, sleep will be - delayed by 30 seconds. - - NOTE: If you call IOCancelPowerChange to deny sleep it returns kIOReturnSuccess, - however the system WILL still go to sleep. - */ - - // let's sleep - power_management_sleep = 1; - hci_power_control(HCI_POWER_SLEEP); - - // power control only starts falling asleep, count on the 30 second delay - // IOAllowPowerChange( root_port, (long)messageArgument ); - break; - - case kIOMessageSystemWillPowerOn: - - //System has started the wake up process... - - // assume that all clients use Bluetooth -> if connection, start Bluetooth - power_management_sleep = 0; - if (!active_clients_empty()) { - hci_power_control(HCI_POWER_ON); - } - break; - - case kIOMessageSystemHasPoweredOn: - //System has finished waking up... - break; - - default: - break; - - } -} -#endif -#endif - static void dummy_bluetooth_status_handler(BLUETOOTH_STATE state){ printf("Bluetooth status: %u\n", state); }; @@ -422,6 +302,25 @@ static void daemon_packet_handler(void * connection, uint8_t packet_type, uint16 } } +static void power_notification_callback(POWER_NOTIFICATION_t notification){ + switch (notification) { + case POWER_WILL_SLEEP: + // let's sleep + power_management_sleep = 1; + hci_power_control(HCI_POWER_SLEEP); + break; + case POWER_WILL_WAKE_UP: + // assume that all clients use Bluetooth -> if connection, start Bluetooth + power_management_sleep = 0; + if (!active_clients_empty()) { + hci_power_control(HCI_POWER_ON); + } + break; + default: + break; + } +} + static void daemon_sigint_handler(int param){ printf(" <= SIGINT received, shutting down..\n"); hci_power_control( HCI_POWER_OFF); @@ -529,11 +428,15 @@ int main (int argc, char * const * argv){ run_loop_init(RUN_LOOP_POSIX); #endif + // init power management notifications + if (control->register_for_power_notifications){ + control->register_for_power_notifications(power_notification_callback); + } + // use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER); hci_dump_set_max_packets(1000); - // hci_dump_open(NULL, HCI_DUMP_STDOUT); - + // init HCI hci_init(transport, &config, control, remote_device_db); @@ -559,24 +462,7 @@ int main (int argc, char * const * argv){ } #endif socket_connection_register_packet_callback(daemon_client_handler); - -#ifdef HANDLE_POWER_NOTIFICATIONS - IONotificationPortRef notifyPortRef; // notification port allocated by IORegisterForSystemPower - io_object_t notifierObject; // notifier object, used to deregister later - void* refCon = NULL; // this parameter is passed to the callback - - // register to receive system sleep notifications - root_port = IORegisterForSystemPower( refCon, ¬ifyPortRef, MySleepCallBack, ¬ifierObject ); - if (!root_port) - { - printf("IORegisterForSystemPower failed\n"); - return 1; - } - - // add the notification port to the application runloop - CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes); -#endif - + // go! run_loop_execute(); return 0;