root/src/kits/bluetooth/LocalDevice.cpp
/*
 * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
 * Copyright 2008 Mika Lindqvist, monni1995_at_gmail.com
 * Copyright 2012 Fredrik Modéen, [firstname]@[lastname]
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include <bluetooth/bluetooth_error.h>

#include <bluetooth/HCI/btHCI_command.h>
#include <bluetooth/HCI/btHCI_event.h>

#include <bluetooth/DeviceClass.h>
#include <bluetooth/DiscoveryAgent.h>
#include <bluetooth/LocalDevice.h>
#include <bluetooth/RemoteDevice.h>

#include <bluetooth/bdaddrUtils.h>
#include <bluetoothserver_p.h>
#include <CommandManager.h>

#include <new>

#include "KitSupport.h"


namespace Bluetooth {


LocalDevice*
LocalDevice::RequestLocalDeviceID(BMessage* request)
{
        BMessage reply;
        hci_id hid;
        LocalDevice* lDevice = NULL;

        BMessenger* messenger = _RetrieveBluetoothMessenger();

        if (messenger == NULL)
                return NULL;

        if (messenger->SendMessage(request, &reply) == B_OK
                && reply.FindInt32("hci_id", &hid) == B_OK ) {

                if (hid >= 0)
                        lDevice = new (std::nothrow)LocalDevice(hid);
        }

        delete messenger;
        return lDevice;
}


#if 0
#pragma -
#endif


LocalDevice*
LocalDevice::GetLocalDevice()
{
        BMessage request(BT_MSG_ACQUIRE_LOCAL_DEVICE);

        return RequestLocalDeviceID(&request);
}


LocalDevice*
LocalDevice::GetLocalDevice(const hci_id hid)
{
        BMessage request(BT_MSG_ACQUIRE_LOCAL_DEVICE);
        request.AddInt32("hci_id", hid);

        return RequestLocalDeviceID(&request);
}


LocalDevice*
LocalDevice::GetLocalDevice(const bdaddr_t bdaddr)
{
        BMessage request(BT_MSG_ACQUIRE_LOCAL_DEVICE);
        request.AddData("bdaddr", B_ANY_TYPE, &bdaddr, sizeof(bdaddr_t));

        return RequestLocalDeviceID(&request);
}


uint32
LocalDevice::GetLocalDeviceCount()
{
        BMessenger* messenger = _RetrieveBluetoothMessenger();
        uint32 count = 0;

        if (messenger != NULL) {

                BMessage request(BT_MSG_COUNT_LOCAL_DEVICES);
                BMessage reply;

                if (messenger->SendMessage(&request, &reply) == B_OK)
                        count = reply.FindInt32("count");

                delete messenger;
        }

        return count;

}


DiscoveryAgent*
LocalDevice::GetDiscoveryAgent()
{
        // TODO: Study a singleton here
        return new (std::nothrow)DiscoveryAgent(this);
}


BString
LocalDevice::GetProperty(const char* property)
{
        return NULL;

}


status_t
LocalDevice::GetProperty(const char* property, uint32* value)
{
        if (fMessenger == NULL)
                return B_ERROR;

        BMessage request(BT_MSG_GET_PROPERTY);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddString("property", property);

        if (fMessenger->SendMessage(&request, &reply) == B_OK) {
                if (reply.FindInt32("result", (int32*)value ) == B_OK ) {
                        return B_OK;

                }
        }

        return B_ERROR;
}


int
LocalDevice::GetDiscoverable()
{
        if (fMessenger == NULL)
                return -1;

        size_t  size;
        void* command = buildReadScan(&size);
        if (command == NULL)
                return -1;

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE, command, size);
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_READ_SCAN_ENABLE));

        int8 discoverable;
        BMessage reply;
        if (fMessenger->SendMessage(&request, &reply) == B_OK
                && reply.FindInt8("scan_enable", &discoverable) == B_OK)
                return discoverable;

        return -1;
}


status_t
LocalDevice::SetDiscoverable(int mode)
{
        if (fMessenger == NULL)
                return B_ERROR;

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        size_t size;
        uint8 bt_status = BT_ERROR;

        request.AddInt32("hci_id", fHid);


        void* command = buildWriteScan(mode, &size);

        if (command == NULL) {
                return B_NO_MEMORY;
        }

        request.AddData("raw command", B_ANY_TYPE, command, size);
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_WRITE_SCAN_ENABLE));

        if (fMessenger->SendMessage(&request, &reply) == B_OK) {
                if (reply.FindUInt8("status", &bt_status ) == B_OK ) {
                        return bt_status;

                }
        }

        return B_ERROR;
}


struct authentication_t {
        uint8 param;
};


status_t
LocalDevice::SetAuthentication(bool authentication)
{
        return SingleParameterCommandRequest<struct authentication_t, uint8>
                (OGF_CONTROL_BASEBAND, OCF_WRITE_AUTH_ENABLE, authentication,
                NULL, fHid, fMessenger);
}


bdaddr_t
LocalDevice::GetBluetoothAddress()
{
        if (fMessenger == NULL)
                return bdaddrUtils::LocalAddress();

        size_t  size;
        void* command = buildReadBdAddr(&size);

        if (command == NULL)
                return bdaddrUtils::LocalAddress();

        const bdaddr_t* bdaddr;
        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;
        ssize_t ssize;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE, command, size);
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_INFORMATIONAL_PARAM,
                OCF_READ_BD_ADDR));

        if (fMessenger->SendMessage(&request, &reply) == B_OK
                && reply.FindData("bdaddr", B_ANY_TYPE, 0,
                        (const void**)&bdaddr, &ssize) == B_OK)
                        return *bdaddr;

        return bdaddrUtils::LocalAddress();
}


hci_id
LocalDevice::ID(void) const
{
        return fHid;
}


BString
LocalDevice::GetFriendlyName()
{
        if (fMessenger == NULL)
                return BString("Unknown|Messenger");

        size_t  size;
        void* command = buildReadLocalName(&size);
        if (command == NULL)
                return BString("Unknown|NoMemory");

        BString friendlyname;
        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;


        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE, command, size);
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_READ_LOCAL_NAME));

        if (fMessenger->SendMessage(&request, &reply) == B_OK
                && reply.FindString("friendlyname", &friendlyname) == B_OK)
                return friendlyname;

        return BString("Unknown|ServerFailed");
}


status_t
LocalDevice::SetFriendlyName(BString& name)
{
        uint8 bt_status = BT_ERROR;

        if (fMessenger == NULL)
                return bt_status;

        BluetoothCommand<typed_command(hci_write_local_name)>
                writeName(OGF_CONTROL_BASEBAND, OCF_WRITE_LOCAL_NAME);

        strcpy(writeName->local_name, name.String());

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                writeName.Data(), writeName.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_WRITE_LOCAL_NAME));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;
}


DeviceClass
LocalDevice::GetDeviceClass()
{

//      if (fDeviceClass.IsUnknownDeviceClass()) {

                if (fMessenger == NULL)
                        return fDeviceClass;

                size_t  size;
                void* command = buildReadClassOfDevice(&size);
                if (command == NULL)
                        return fDeviceClass;

                BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
                BMessage reply;
                const uint8* bufferRecord;
                ssize_t ssize;

                request.AddInt32("hci_id", fHid);
                request.AddData("raw command", B_ANY_TYPE, command, size);
                request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
                request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                        OCF_READ_CLASS_OF_DEV));

                if (fMessenger->SendMessage(&request, &reply) == B_OK
                        && reply.FindData("devclass", B_ANY_TYPE, 0, (const void**)&bufferRecord,
                        &ssize) == B_OK) {
                        uint8 record[3] = { bufferRecord[0], bufferRecord[1], bufferRecord[2] };
                        fDeviceClass.SetRecord(record);
                }
//      }

        return fDeviceClass;

}


status_t
LocalDevice::SetDeviceClass(DeviceClass deviceClass)
{
        uint8 bt_status = BT_ERROR;

        if (fMessenger == NULL)
                return bt_status;

        BluetoothCommand<typed_command(hci_write_dev_class)>
                setDeviceClass(OGF_CONTROL_BASEBAND, OCF_WRITE_CLASS_OF_DEV);

        setDeviceClass->dev_class[0] = deviceClass.Record() & 0xFF;
        setDeviceClass->dev_class[1] = (deviceClass.Record() & 0xFF00) >> 8;
        setDeviceClass->dev_class[2] = (deviceClass.Record() & 0xFF0000) >> 16;

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                setDeviceClass.Data(), setDeviceClass.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_WRITE_CLASS_OF_DEV));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;

}


status_t
LocalDevice::_ReadLocalVersion()
{
        uint8 bt_status = BT_ERROR;

        BluetoothCommand<> localVersion(OGF_INFORMATIONAL_PARAM,
                OCF_READ_LOCAL_VERSION);

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                localVersion.Data(), localVersion.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_INFORMATIONAL_PARAM,
                OCF_READ_LOCAL_VERSION));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;
}


status_t
LocalDevice::_ReadLocalSupportedCommands()
{
        uint8 bt_status = BT_ERROR;

        BluetoothCommand<> localSupportedCommands(OGF_INFORMATIONAL_PARAM,
                OCF_READ_LOCAL_SUPPORTED_COMMANDS);

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                localSupportedCommands.Data(), localSupportedCommands.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_INFORMATIONAL_PARAM,
                OCF_READ_LOCAL_SUPPORTED_COMMANDS));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;
}


status_t
LocalDevice::_ReadBufferSize()
{
        uint8 bt_status = BT_ERROR;

        BluetoothCommand<> BufferSize(OGF_INFORMATIONAL_PARAM,
                OCF_READ_BUFFER_SIZE);


        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                BufferSize.Data(), BufferSize.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_INFORMATIONAL_PARAM,
                OCF_READ_BUFFER_SIZE));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;
}


status_t
LocalDevice::_ReadLocalFeatures()
{
        uint8 bt_status = BT_ERROR;

        BluetoothCommand<> LocalFeatures(OGF_INFORMATIONAL_PARAM,
                OCF_READ_LOCAL_FEATURES);

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                LocalFeatures.Data(), LocalFeatures.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_INFORMATIONAL_PARAM,
                OCF_READ_LOCAL_FEATURES));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;
}


status_t
LocalDevice::_ReadLinkKeys()
{
        uint8 bt_status = BT_ERROR;

        BluetoothCommand<> LocalFeatures(OGF_CONTROL_BASEBAND,
                OCF_READ_STORED_LINK_KEY);

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE,
                LocalFeatures.Data(), LocalFeatures.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_READ_STORED_LINK_KEY));

        request.AddInt16("eventExpected",  HCI_EVENT_RETURN_LINK_KEYS);


        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;
}


struct pageTimeout_t {
        uint16 param;
};


status_t
LocalDevice::_ReadTimeouts()
{

        // Read PageTimeout
        NonParameterCommandRequest(OGF_CONTROL_BASEBAND,
                OCF_READ_PG_TIMEOUT, NULL, fHid, fMessenger);

        // Write PageTimeout
        SingleParameterCommandRequest<struct pageTimeout_t, uint16>
                (OGF_CONTROL_BASEBAND, OCF_WRITE_PG_TIMEOUT, 0x8000, NULL,
                fHid, fMessenger);

        // Write PageTimeout
        return SingleParameterCommandRequest<struct pageTimeout_t, uint16>
                (OGF_CONTROL_BASEBAND, OCF_WRITE_CA_TIMEOUT, 0x7d00, NULL,
                fHid, fMessenger);
}


status_t
LocalDevice::Reset()
{
        uint8 bt_status = BT_ERROR;

        BluetoothCommand<> Reset(OGF_CONTROL_BASEBAND, OCF_RESET);

        BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
        BMessage reply;

        request.AddInt32("hci_id", fHid);
        request.AddData("raw command", B_ANY_TYPE, Reset.Data(), Reset.Size());
        request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
        request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND,
                OCF_RESET));

        if (fMessenger->SendMessage(&request, &reply) == B_OK)
                reply.FindUInt8("status", &bt_status);

        return bt_status;

}


/*
ServiceRecord
LocalDevice::getRecord(Connection notifier) {

}

void
LocalDevice::updateRecord(ServiceRecord srvRecord) {

}
*/


LocalDevice::LocalDevice(hci_id hid)
        :
        BluetoothDevice(),
        fHid(hid)
{
        fMessenger = _RetrieveBluetoothMessenger();

        _ReadBufferSize();
        _ReadLocalFeatures();
        _ReadLocalVersion();
        _ReadLocalSupportedCommands();
        _ReadTimeouts();
        _ReadLinkKeys();

        // Uncomment this if you want your device to have a nicer default name
        // BString name("HaikuBluetooth");
        // SetFriendlyName(name);


        uint32 value;

        // HARDCODE -> move this to addons
        if (GetProperty("manufacturer", &value) == B_OK
                && value == 15) {

                // Uncomment this out if your Broadcom dongle is not working properly
                // Reset();     // Perform a reset to Broadcom buggyland

// Uncomment this out if your Broadcom dongle has a null bdaddr
//#define BT_WRITE_BDADDR_FOR_BCM2035
#ifdef BT_WRITE_BDADDR_FOR_BCM2035
#warning Writting broadcom bdaddr @ init.
                // try write bdaddr to a bcm2035 -> will be moved to an addon
                uint8 bt_status = BT_ERROR;

                BluetoothCommand<typed_command(hci_write_bcm2035_bdaddr)>
                        writeAddress(OGF_VENDOR_CMD, OCF_WRITE_BCM2035_BDADDR);

                BMessage request(BT_MSG_HANDLE_SIMPLE_REQUEST);
                BMessage reply;
                writeAddress->bdaddr.b[0] = 0x3C;
                writeAddress->bdaddr.b[1] = 0x19;
                writeAddress->bdaddr.b[2] = 0x30;
                writeAddress->bdaddr.b[3] = 0xC9;
                writeAddress->bdaddr.b[4] = 0x03;
                writeAddress->bdaddr.b[5] = 0x00;

                request.AddInt32("hci_id", fHid);
                request.AddData("raw command", B_ANY_TYPE,
                        writeAddress.Data(), writeAddress.Size());
                request.AddInt16("eventExpected",  HCI_EVENT_CMD_COMPLETE);
                request.AddInt16("opcodeExpected", PACK_OPCODE(OGF_VENDOR_CMD,
                        OCF_WRITE_BCM2035_BDADDR));

                if (fMessenger->SendMessage(&request, &reply) == B_OK)
                        reply.FindUInt8("status", &bt_status);
#endif
        }
}


LocalDevice::~LocalDevice()
{
        delete fMessenger;
}


} /* end namespace Bluetooth */