/*
 * Copyright (C) 2009-2012 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.
 * 4. Any redistribution, use, or modification is done solely for
 *    personal benefit and not for any commercial purpose or for
 *    monetary gain.
 *
 * 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.
 *
 * Please inquire about commercial licensing options at btstack@ringwald.ch
 *
 */

/*
 *  bt_control_iphone.c
 *
 *  control Bluetooth module using BlueTool
 *
 *  Created by Matthias Ringwald on 5/19/09.
 *  PowerManagement implementation by Jens David, DG1KJD on 20110801.
 *
 *  Bluetooth Toggle by BigBoss
 */

#include "btstack-config.h"

#include "bt_control_iphone.h"
#include "hci_transport.h"
#include "hci.h"
#include "debug.h"

#include <errno.h>
#include <fcntl.h>        // open
#include <stdlib.h>       // system, random, srandom
#include <stdio.h>        // sscanf, printf
#include <string.h>       // strcpy, strcat, strncmp
#include <sys/utsname.h>  // uname
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

#include "../platforms/ios/SpringBoardAccess/SpringBoardAccess.h"

// minimal IOKit
#include <Availability.h>
#include <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>

// compile issue fix
#undef NSEC_PER_USEC
#undef USEC_PER_SEC
#undef NSEC_PER_SEC
// end of fix

#include <mach/mach.h>
#define IOKIT
#include <device/device_types.h>

// 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_connect_t  root_port = 0; // a reference to the Root Power Domain IOService
static int power_notification_pipe_fds[2];
static data_source_t power_notification_ds;

static void (*power_notification_callback)(POWER_NOTIFICATION_t event) = NULL;

#define BUFF_LEN 80
static char buffer[BUFF_LEN+1];

// local mac address and default transport speed from IORegistry
static bd_addr_t local_mac_address;
static uint32_t  transport_speed;

static int power_management_active = 0;
static char *os3xBlueTool = "BlueTool";
static char *os4xBlueTool = "/usr/local/bin/BlueToolH4";

static NSTimeInterval lastPowerOffTime = 0.0;

static NSTimeInterval getTime(void){
    NSDate * now = [[NSDate alloc] init];
    double result = [now timeIntervalSince1970];
    [now release];
    return result;
}

/**
 * check OS version for >= 4.0
 */
static int iphone_os_at_least_40(void){
    return kCFCoreFoundationVersionNumber >= 550.32;
}

/**
 * check OS version for >= 6.0
 */
static int iphone_os_at_least_60(void){
    return kCFCoreFoundationVersionNumber >= 788.0;
}

/** 
 * get machine name
 */
static struct utsname unmae_info;
static char *get_machine_name(void){
	uname(&unmae_info);
	return unmae_info.machine;
}

/**
 * on iPhone/iPod touch
 */
static int iphone_valid(void *config){
	char * machine = get_machine_name();
	if (!strncmp("iPod1", machine, strlen("iPod1"))) return 0;     // 1st gen touch no BT
    return 1;
}

static const char * iphone_name(void *config){
    return get_machine_name();
}

// Get BD_ADDR from IORegistry
static void ioregistry_get_info(void){
    mach_port_t mp;
    IOMasterPort(MACH_PORT_NULL,&mp);
    CFMutableDictionaryRef bt_matching = IOServiceNameMatching("bluetooth");
    mach_port_t bt_service = IOServiceGetMatchingService(mp, bt_matching);

    // local-mac-address
    CFTypeRef local_mac_address_ref = IORegistryEntrySearchCFProperty(bt_service,"IODeviceTree",CFSTR("local-mac-address"), kCFAllocatorDefault, 1);
    CFDataGetBytes(local_mac_address_ref,CFRangeMake(0,CFDataGetLength(local_mac_address_ref)),local_mac_address); // buffer needs to be unsigned char

    // transport-speed
    CFTypeRef transport_speed_ref = IORegistryEntrySearchCFProperty(bt_service,"IODeviceTree",CFSTR("transport-speed"), kCFAllocatorDefault, 1);
    int transport_speed_len = CFDataGetLength(transport_speed_ref);
    CFDataGetBytes(transport_speed_ref,CFRangeMake(0,transport_speed_len), (uint8_t*) &transport_speed); // buffer needs to be unsigned char

    IOObjectRelease(bt_service);
    
    // dump info
    log_info("local-mac-address: %s\n", bd_addr_to_str(local_mac_address));
    log_info("transport-speed:   %u\n", transport_speed);
}

static int iphone_has_csr(void){
    // construct script path from device name
    char *machine = get_machine_name();
	if (strncmp(machine, "iPhone1,", strlen("iPhone1,")) == 0) {
        return 1;
	}
    return 0;
}

static void iphone_write_string(int fd, char *string){
    int len = strlen(string);
    write(fd, string, len);
}

static void iphone_csr_set_pskey(int fd, int key, int value){
    int len = sprintf(buffer, "csr -p 0x%04x=0x%04x\n", key, value);
    write(fd, buffer, len);
}

static void iphone_csr_set_bd_addr(int fd){
    int len = sprintf(buffer,"\ncsr -p 0x0001=0x00%.2x,0x%.2x%.2x,0x00%.2x,0x%.2x%.2x\n",
            local_mac_address[3], local_mac_address[4], local_mac_address[5], local_mac_address[2], local_mac_address[0], local_mac_address[1]);
    write(fd, buffer, len);
}

static void iphone_csr_set_baud(int fd, int baud){
    // calculate baud rate (assume rate is multiply of 100)
    uint32_t baud_key = (4096 * (baud/100) + 4999) / 10000;
    iphone_csr_set_pskey(fd, 0x01be, baud_key);
}

static void iphone_bcm_set_bd_addr(int fd){
    int len = sprintf(buffer, "bcm -a %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_address[0], local_mac_address[1],
            local_mac_address[2], local_mac_address[3], local_mac_address[4], local_mac_address[5]);
    write(fd, buffer, len);
}

static void iphone_bcm_set_baud(int fd, int baud){
    int len = sprintf(buffer, "bcm -b %u\n",  baud);
    write(fd, buffer, len);
}

// OS 3.x
static int iphone_write_initscript (int output, int baudrate){
        
    // construct script path from device name
    strcpy(buffer, "/etc/bluetool/");
    char *machine = get_machine_name();
    strcat(buffer, machine);
    if (iphone_has_csr()){
		strcat(buffer, ".init.script");
    } else {
		strcat(buffer, ".boot.script");
    }
        
    // open script
    int input = open(buffer, O_RDONLY);
    
    int pos = 0;
    int mirror = 0;
    int store = 1;
    while (1){
        int chars = read(input, &buffer[pos], 1);
        
        // end-of-line
        if (chars == 0 || buffer[pos]=='\n' || buffer[pos]== '\r'){
            if (store) {
                // stored characters
                write(output, buffer, pos+chars);
            }
            if (mirror) {
                write(output, "\n", 1);
            }
            pos = 0;
            mirror = 0;
            store = 1;
            if (chars) {
                continue;
            } else {
                break;
            }
        }
        
        // mirror
        if (mirror){
            write(output, &buffer[pos], 1);
        }
        
        // store
        if (store) {
            pos++;
        }
        			
		// iPhone - set BD_ADDR after "csr -i"
		if (store == 1 && pos == 6 && strncmp(buffer, "csr -i", 6) == 0) {
			store = 0;
			write(output, buffer, pos); // "csr -i"
            iphone_csr_set_bd_addr(output);
		}
		
		// iPod2,1
		// check for "bcm -X" cmds
		if (store == 1 && pos == 6){
 			if (strncmp(buffer, "bcm -", 5) == 0) {
				store  = 0;
				switch (buffer[5]){
					case 'a': // BT Address
                        iphone_bcm_set_bd_addr(output);
						mirror = 0;
						break;
					case 'b': // baud rate command OS 2.x
                    case 'B': // baud rate command OS 3.x
                        iphone_bcm_set_baud(output, baudrate);
						mirror = 0;
						break;
					case 's': // sleep mode - replace with "wake" command?
						iphone_write_string(output, "wake on\n");
						mirror = 0;
						break;
					default: // other "bcm -X" command
						write(output, buffer, pos);
						mirror = 1;
				}
			}
		}
        
        // iPhone1,1 & iPhone 2,1: OS 3.x
        // check for "csr -B" and "csr -T"
        if (store == 1 && pos == 6){
			if (strncmp(buffer, "csr -", 5) == 0) {
                switch(buffer[5]){
                    case 'T':   // Transport Mode
                        store  = 0;
                        break;
                    case 'B':   // Baud rate
                        iphone_csr_set_baud(output, baudrate);
                        store  = 0;
                        break;
                    default:    // wait for full command
                        break;
                }
            }
        }
                
		// iPhone1,1 & iPhone 2,1: OS 2.x
        // check for "csr -p 0x1234=0x5678" (20)
        if (store == 1 && pos == 20) {
            int pskey, value;
            store = 0;
            if (sscanf(buffer, "csr -p 0x%x=0x%x", &pskey, &value) == 2){
                switch (pskey) {
                    case 0x01f9:    // UART MODE
                    case 0x01c1:    // Configure H5 mode
                        mirror = 0;
                        break;
                    case 0x01be:    // PSKEY_UART_BAUD_RATE
                        iphone_csr_set_baud(output, baudrate);
                        mirror = 0;
                        break;
                    default:
                        // anything else: dump buffer and start forwarding
                        write(output, buffer, pos);
                        mirror = 1;
                        break;
                }
            } else {
                write(output, buffer, pos);
                mirror = 1;
            }
        }
    }
    // close input
    close(input);

#ifdef USE_POWERMANAGEMENT
    if (iphone_has_csr()) {
        /* CSR BT module: deactivated since it didn't work on iPhone 3G, 3.1.3
           the first few packets didn't get received when iPhone is sleeping.
           That's quite possible since Apple uses H5, but BTstack uses H4
           H5's packet retransmission should take care of that */
        // iphone_write_string(output, "msleep 50\n");
        // iphone_write_string(output, "csr -p 0x01ca=0x0031\n");
        // iphone_write_string(output, "msleep 50\n");
        // iphone_write_string(output, "csr -p 0x01c7=0x0001,0x01f4,0x0005,0x0020\n");
    } else {
        /* BCM BT module, deactivated since untested for now */
        // iphone_write_string(output, "bcm -s 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x01\n");
        // iphone_write_string(output, "msleep 50\n");
    }
#endif

    return 0;
}

// OS 4.x and higher
static void iphone_write_configscript(int fd, int baudrate){

    // check for os version >= 6.0
    int os6x = iphone_os_at_least_60();

    iphone_write_string(fd, "device -D -S\n");

    if (os6x){
        iphone_write_string(fd, "wake on\n");
    }

    if (iphone_has_csr()) {
        iphone_csr_set_bd_addr(fd);
        if (baudrate) {
            iphone_csr_set_baud(fd, baudrate);
        }
        iphone_write_string(fd, "csr -r\n");
#ifdef USE_POWERMANAGEMENT
        /* CSR BT module: deactivated since untested, but it most likely won't work
           see comments in 3.x init sequence above */
        // iphone_write_string(fd, "msleep 50\n");
        // iphone_write_string(fd, "csr -p 0x01ca=0x0031\n");
        // iphone_write_string(fd, "msleep 50\n");
        // iphone_write_string(fd, "csr -p 0x01c7=0x0001,0x01f4,0x0005,0x0020\n");
#endif
    } else {
        iphone_bcm_set_bd_addr(fd);
        if (baudrate) {
            iphone_bcm_set_baud(fd, baudrate);
            iphone_write_string(fd, "msleep 200\n");
        }
#ifdef USE_POWERMANAGEMENT
        // power management only active on 4.x with BCM (iPhone 3GS and higher, all iPads, iPod touch 3G and higher)
        iphone_write_string(fd, "bcm -s 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x01\n");
        iphone_write_string(fd, "msleep 50\n");
#endif
    }

    if (os6x){
        iphone_write_string(fd, "wake off\n");
    }

    iphone_write_string(fd, "quit\n");
}

static int iphone_on (void *transport_config){
    // hci_uart_config->baudrate_init == 0, if using native speed    
    
    log_info("iphone_on: entered\n");

    int err = 0;

    hci_uart_config_t * hci_uart_config = (hci_uart_config_t*) transport_config;

    // get local-mac-addr and transport-speed from IORegistry 
	ioregistry_get_info();

#ifdef USE_RANDOM_BD_ADDR
    // While developing an app that emulates a Bluetooth HID keyboard, we've learnt
    // that in (some versions/driver combinations of) Windows XP, information about
    // a device with incorrect SDP descriptions are stored forever.
    // 
    // To continue development, this option was added that picks a random BD_ADDR
    // on start to trick windows in giving us a fresh start on each try.
    //
    // Use with caution!
    
    srandom(time(NULL));
    bt_store_32(local_mac_address, 0, random());
    bt_store_16(local_mac_address, 4, random());
#endif

    if (iphone_system_bt_enabled()){
        perror("iphone_on: System Bluetooth enabled!");
        return 1;
    }
    
    // assert min 10 seconds time between power off and power on
    if (iphone_os_at_least_60()) {
        NSTimeInterval timeSinceLastPowerOff = getTime() - lastPowerOffTime;
        if (timeSinceLastPowerOff < 10.0 ){
            int delay = 10 - timeSinceLastPowerOff;
            log_info("iphone_on: delay init for %u seconds", delay);
            sleep(delay);
        }
    } 

    // unload BTServer and BlueTool
    log_info("iphone_on: unload BTServer\n");
    err = system ("launchctl unload /System/Library/LaunchDaemons/com.apple.BTServer.plist");
        
    if (iphone_os_at_least_60()) {
        err = system ("launchctl unload /System/Library/LaunchDaemons/com.apple.BTServer.le.plist");
        err = system ("launchctl unload /System/Library/LaunchDaemons/com.apple.BlueTool.plist");
    }
    
    // check for os version >= 4.0
    int os4x = iphone_os_at_least_40();
    
    // Use patched BlueTool on iOS 4.0 with CSR chipset
    char * bluetool = os3xBlueTool;
    if (iphone_has_csr() && os4x) {
        bluetool = os4xBlueTool;
    }
    
    // quick test if Bluetooth UART can be opened - and try to start cleanly
    int fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd > 0)  {
        // everything's fine
        close(fd);
    } else {
        // no way!
        log_error( "bt_control.c:iphone_on(): Failed to open '%s', trying killall %s\n", hci_uart_config->device_name, bluetool);
        system("killall -9 BlueToolH4");
        system("killall -9 BlueTool");
        sleep(3); 
        
        // try again
        fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
        if (fd > 0){
            close(fd);
        } else {
            log_error( "bt_control.c:iphone_on(): Failed to open '%s' again, trying killall BTServer and killall %s\n", hci_uart_config->device_name, bluetool);
            system("killall -9 BTServer");
            system("killall -9 BlueToolH4");
            system("killall -9 BlueTool");
            sleep(3); 
        }
    }
    
    // basic config on 4.0+
    if (os4x) {
        sprintf(buffer, "%s -F boot", bluetool);
        system(buffer);
        sprintf(buffer, "%s -F init", bluetool);
        system(buffer);
    }
    
    // advanced config - set bd addr, use custom baud rate, enable deep sleep
    FILE * outputFile = popen(bluetool, "r+");
    setvbuf(outputFile, NULL, _IONBF, 0);
    int output = fileno(outputFile);
    
    if (os4x) {
        // 4.x - send custom config
        iphone_write_configscript(output, hci_uart_config->baudrate_init);
    } else {
        // 3.x - modify original script on the fly
        iphone_write_initscript(output, hci_uart_config->baudrate_init);
    }
    
    // log output
    fflush(outputFile);
    int singlechar = 0;
    char linebuf[80];
    int pos = 0;
    while (singlechar != EOF) {
        singlechar   = fgetc(outputFile);
        linebuf[pos] = singlechar;
        if (singlechar != EOF && singlechar != '\n' && singlechar != '\r' && pos < 78) {
            pos++;
            continue;
        };
        linebuf[pos] = 0;
        if (pos > 0) {
            log_info("%s", linebuf);
        }
        pos = 0;
    };
    err = pclose(outputFile);

#ifdef USE_POWERMANAGEMENT
    power_management_active = bt_control_iphone_power_management_supported();

    // if baud == 0, we're using system default: set in transport config
    if (hci_uart_config->baudrate_init == 0) {
        hci_uart_config->baudrate_init = transport_speed;
    }
#endif
    
    // if we sleep for about 3 seconds, we miss a strage packet... but we don't care
    // sleep(3); 

    return err;
}

static int iphone_off (void *config){
	
    // power off (all models)
    log_info("iphone_off: turn off using BlueTool\n");
    
    // power off (all models < IOS 6.0)
    if (iphone_os_at_least_60()) {
        log_info("Skipping BlueTool invocation to turn off module power since IOS >= 6.0");
    } else {
        log_info("iphone_off: turn off using BlueTool\n");
        system ("echo \"power off\nquit\" | BlueTool");
    }

    // reload BlueTool
    if (iphone_os_at_least_60()) {
        log_info("iphone_off: reload BlueTool\n");
        system ("launchctl load /System/Library/LaunchDaemons/com.apple.BlueTool.plist");
        system ("launchctl load /System/Library/LaunchDaemons/com.apple.BTServer.le.plist");
        log_info("iphone_off: done\n");
    }

    // reload BTServer
    log_info("iphone_off: reload BTServer\n");
    system ("launchctl load /System/Library/LaunchDaemons/com.apple.BTServer.plist");
    log_info("iphone_off: done\n");

    lastPowerOffTime = getTime();

    return 0;
}

static int iphone_sleep(void *config){

    // will sleep by itself
    if (power_management_active) return 0;
    
    // put Bluetooth into deep sleep
    system ("echo \"wake off\nquit\" | BlueTool");
    return 0;
}

static int iphone_wake(void *config){

    // will wake by itself
    if (power_management_active) return 0;
    
    // wake up Bluetooth module
    system ("echo \"wake on\nquit\" | BlueTool");
    return 0;
}

static void MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument ) {
    
    char data;
    log_info( "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. 
			 */
			
            // notify main thread
            data = POWER_WILL_SLEEP;
            write(power_notification_pipe_fds[1], &data, 1);

            // don't allow power change, even when power management is active
            // BTstack needs to disable discovery mode during sleep to save power

            // if (!power_management_active) break;
            // IOAllowPowerChange( root_port, (long)messageArgument );
            
            break;
			
        case kIOMessageSystemWillPowerOn:
            
            data = POWER_WILL_WAKE_UP;
            write(power_notification_pipe_fds[1], &data, 1);
            break;
			
        case kIOMessageSystemHasPoweredOn:
            //System has finished waking up...
			break;
			
        default:
            break;
			
    }
}

static int  power_notification_process(struct data_source *ds) {

    if (!power_notification_callback) return -1;

    // get token
    char token;
    int bytes_read = read(power_notification_pipe_fds[0], &token, 1);
    if (bytes_read != 1) return -1;
        
    log_info("power_notification_process: %u\n", token);

    power_notification_callback( (POWER_NOTIFICATION_t) token );    
    
    return 0;
}

/**
 * assumption: called only once, cb != null
 */
void iphone_register_for_power_notifications(void (*cb)(POWER_NOTIFICATION_t event)){

    // handle IOKIT power notifications - http://developer.apple.com/library/mac/#qa/qa2004/qa1340.html
    IONotificationPortRef  notifyPortRef =  NULL; // notification port allocated by IORegisterForSystemPower
    io_object_t            notifierObject = 0;    // notifier object, used to deregister later
    root_port = IORegisterForSystemPower(NULL, &notifyPortRef, MySleepCallBack, &notifierObject);
    if (!root_port) {
        log_info("IORegisterForSystemPower failed\n");
        return;
    }
    
    // add the notification port to this thread's runloop
    CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
    
    // 
    power_notification_callback = cb;
    
 	// create pipe to deliver power notification to main run loop
	pipe(power_notification_pipe_fds);

    // set up data source handler
    power_notification_ds.fd =      power_notification_pipe_fds[0];
    power_notification_ds.process = power_notification_process;
    run_loop_add_data_source(&power_notification_ds);  
}

int bt_control_iphone_power_management_supported(void){
    // only supported on Broadcom chipsets with iOS 4.0+
    if ( iphone_has_csr()) return 0;
    if (!iphone_os_at_least_40()) return 0;
    return 1;
}

// direct access
int bt_control_iphone_power_management_enabled(void){
    return power_management_active;
}

// single instance
bt_control_t bt_control_iphone = {
    .on     = iphone_on,
    .off    = iphone_off,
    .sleep  = iphone_sleep,
    .wake   = iphone_wake,
    .valid  = iphone_valid,
    .name   = iphone_name,
    .register_for_power_notifications = iphone_register_for_power_notifications
};

int iphone_system_bt_enabled(void){
    return SBA_getBluetoothEnabled();
}

void iphone_system_bt_set_enabled(int enabled)
{
    SBA_setBluetoothEnabled(enabled);
    sleep(2); // give change a chance
}

int iphone_system_has_csr(void){
    return iphone_has_csr();
}