blob: 7a36e2cbb5b5d027dd3e38dd88efb0ca8ef8ef32 [file] [log] [blame]
#include <stdlib.h>
#include <string.h>
#include "aapiSocket.h"
#include "aapiGatt.h"
#include "workQueue.h"
#include "persist.h"
#include "rfcomm.h"
#include "config.h"
#include "timer.h"
#include "l2cap.h"
#include "aapi.h"
#include "hci.h"
#include "log.h"
#include "sdp.h"
#include "bt.h"
#include "mt.h"
/* a device will never hove more than this many properties */
#define BT_MAX_PROPS 32
struct aapiWorkItem {
uint8_t type;
union {
struct {
bool on;
} state;
struct properties {
bt_status_t status;
bt_bdaddr_t bd_addr;
int num_properties;
bt_property_t properties[];
} properties;
struct {
bt_bdaddr_t remote_bd_addr;
bt_bdname_t bd_name;
uint32_t cod;
} pinReq;
struct {
bt_bdaddr_t remote_bd_addr;
bt_bdname_t bd_name;
uint32_t cod;
bt_ssp_variant_t pairing_variant;
uint32_t pass_key;
} sspReq;
struct {
bt_status_t status;
bt_bdaddr_t remote_bd_addr;
uint32_t state;
} stateCbk;
struct {
uint16_t opcode;
uint8_t len;
uint8_t buf[];
} hciEvt;
struct {
bt_status_t status;
uint16_t num_packets;
} leTestEvt;
};
};
#define WT_ADAPTER_STATE_CHG 0
#define WT_ADAPTER_PROPS 1
#define WT_REMOTE_PROPS 2
#define WT_DEV_DISCOVERED 3
#define WT_DISC_STATE_CHG 4
#define WT_PIN_REQ 5
#define WT_SSP_REQ 6
#define WT_BOND_STATE_CHG 7
#define WT_ACL_STATE_CHG 8
#define WT_DUT_EVT 9
#define WT_LE_TEST_EVT 10
struct aapiBondedDevicesEnumData {
bt_bdaddr_t *devs;
uint32_t num;
};
/* our state */
static pthread_t mWorker;
static struct workQueue *mWorkQ;
static pthread_mutex_t mStateLock = PTHREAD_MUTEX_INITIALIZER;
static bool mStackUp = false;
/* properties java can get/set */
//TODO: mutex around these!
static bt_bdaddr_t mLocalMac = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}};
static bt_bdname_t mLocalName;
static uint32_t mDevCls = 0xCC0704; //wristwatch (0x20C is smartphone)
static bt_scan_mode_t mScanMode = BT_SCAN_MODE_CONNECTABLE;
static bt_device_type_t mDevType = BT_DEVICE_DEVTYPE_DUAL;
static uint32_t mDiscoveryTimeout = 60 ;/* in seconds */
/* fwd decls */
static void aapiSendDeviceInfo(const struct bt_addr *peer);
/*
* FUNCTION: aapiIsStackUp
* USE: Find out if stack is up
* PARAMS: none
* RETURN: the answer
* NOTES:
*/
bool aapiIsStackUp(void)
{
bool up;
pthread_mutex_lock(&mStateLock);
up = mStackUp;
pthread_mutex_unlock(&mStateLock);
logd("aapiIsStackUp()->%s\n", up ? "true" : "false");
return up;
}
/*
* FUNCTION: aapiHciLogEnable
* USE: Enable/disable HCI logging
* PARAMS: enable - on or off?
* RETURN: bt_status_t
* NOTES:
*/
static int aapiHciLogEnable(uint8_t enable)
{
logd("AAPI: hci log enable(%u)\n", enable);
//TODO
return 0;
}
/*
* FUNCTION: aapiLeTestMode
* USE: Send an LE test command
* PARAMS: opcode - the opcode
* buf - the command params
* len - length of said params
* RETURN: bt_status_t
* NOTES: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End commands only
* Does not require DUT mode enabled (?)
*/
static int aapiLeTestMode(uint16_t opcode, uint8_t *buf, uint8_t len)
{
logd("AAPI: le test mode (0x%04X, %ub)\n", opcode, len);
//TODO
return 0;
}
/*
* FUNCTION: aapiDutSend
* USE: Send any command to the controller
* PARAMS: opcode - the opcode
* buf - the command params
* len - length of said params
* RETURN: bt_status_t
* NOTES: intended only for vendor test commands, only valid after DUT mode enabled by .dut_mode_configure
*/
static int aapiDutSend(uint16_t opcode, uint8_t *buf, uint8_t len)
{
logd("AAPI: DUT send (0x%04X, %ub)\n", opcode, len);
//TODO
return 0;
}
/*
* FUNCTION: aapiDutEnable
* USE: Enable/disable DUT mode (used for debugging/testing)
* PARAMS: enable - on or off?
* RETURN: bt_status_t
* NOTES:
*/
static int aapiDutEnable(uint8_t enable)
{
logd("AAPI: dut enable(%u)\n", enable);
//TODO
return 0;
}
/*
* FUNCTION: aapiSspReply
* USE: SSP pairing
* PARAMS: bd_addr - the peer adress
* variant- pairing type being used
* accept - pairing was cancelled by user
* passkey - entered code
* RETURN: bt_status_t
* NOTES:
*/
static int aapiSspReply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, uint8_t accept, uint32_t passkey)
{
struct bt_addr addr;
logd("AAPI: ssp reply\n");
memcpy(addr.addr, bd_addr->address, sizeof(addr.addr));
addr.type = BT_ADDR_TYPE_EDR; /* XXX: this is a bug in android's api, but we must live with it (for now) */
switch (variant) {
case BT_SSP_VARIANT_PASSKEY_CONFIRMATION:
hciSecUserCbkSspUserConfirmationReq(&addr, accept);
break;
case BT_SSP_VARIANT_PASSKEY_ENTRY:
hciSecUserCbkSspUserPasskeyReq(&addr, accept ? passkey : HCI_SSP_NUMERIC_INVALID);
break;
case BT_SSP_VARIANT_CONSENT:
case BT_SSP_VARIANT_PASSKEY_NOTIFICATION:
logw("unexpected pair type %u\n", variant);
break;
}
return 0;
}
/*
* FUNCTION: aapiPinReply
* USE: Legacy pairing
* PARAMS: bd_addr - the peer adress
* accept - pairing was cancelled by user
* pin_len - length of entered pin
* pin_code - entered code
* RETURN: bt_status_t
* NOTES:
*/
static int aapiPinReply(const bt_bdaddr_t *bd_addr, uint8_t accept, uint8_t pin_len, bt_pin_code_t *pin_code)
{
struct bt_addr addr;
logd("AAPI: pin reply(%ub)\n", pin_len);
memcpy(addr.addr, bd_addr->address, sizeof(addr.addr));
addr.type = BT_ADDR_TYPE_EDR; /* XXX: this is a bug in android's api, but we must live with it (for now) */
hciSecUserCbkPinCodeReq(&addr, accept ? pin_code->pin : NULL, accept ? pin_len : 0);
return 0;
}
/*
* FUNCTION: aapiBondCreate
* USE: Start a pairing process
* PARAMS: bd_addr - the peer adress
* transport - the transport to use (BT_TRANSPORT_*)
* RETURN: bt_status_t
* NOTES:
*/
static int aapiBondCreate(const bt_bdaddr_t *bd_addr, int transport)
{
logd("AAPI: bond create\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiBondRemove
* USE: Delete pairing info
* PARAMS: bd_addr - the peer adress
* RETURN: bt_status_t
* NOTES:
*/
static int aapiBondRemove(const bt_bdaddr_t *bd_addr)
{
logd("AAPI: bond remove\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiBondCancel
* USE: Cancel a pairing process
* PARAMS: bd_addr - the peer adress
* RETURN: bt_status_t
* NOTES:
*/
static int aapiBondCancel(const bt_bdaddr_t *bd_addr)
{
logd("AAPI: bond cancel\n");
//TODO: java will call this to cancel SSP. instead of sending us an SSP reply. this means we need to keep a list of all pending pairings here and send a relpy to hci from here...
return 0;
}
/*
* FUNCTION: aapiDiscoveryStart
* USE: Start a discovery
* PARAMS: NONE
* RETURN: bt_status_t
* NOTES:
*/
static int aapiDiscoveryStart(void)
{
logd("AAPI: discovery start\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiDiscoveryStop
* USE: Cancel an ongoing discovery
* PARAMS: NONE
* RETURN: bt_status_t
* NOTES:
*/
static int aapiDiscoveryStop(void)
{
logd("AAPI: discovery stop\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiGetRemoteServices
* USE: Find serives at the peer
* PARAMS: remote_addr - the device
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetRemoteServices(bt_bdaddr_t *remote_addr)
{
logd("AAPI: get remote services\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiGetRemoteServiceRecord
* USE: Find and retrieve a service record from a peer
* PARAMS: remote_addr - the device
* uuid - the UUID to search for
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetRemoteServiceRecord(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid)
{
logd("AAPI: get remote service record\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiGetRemoteDevProperties
* USE: Get all of a remote device's properties
* PARAMS: remote_addr - the device
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetRemoteDevProperties(bt_bdaddr_t *remote_addr)
{
logd("AAPI: get remote dev properties\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiGetRemoteDevProperty
* USE: Get a remote device's property
* PARAMS: remote_addr - the device
* type - the property to get
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetRemoteDevProperty(bt_bdaddr_t *remote_addr, bt_property_type_t type)
{
logd("AAPI: get remote dev prop\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiSetRemoteDevProperty
* USE: Set a remote device's property
* PARAMS: remote_addr - the device
* property - the property to set
* RETURN: bt_status_t
* NOTES:
*/
static int aapiSetRemoteDevProperty(bt_bdaddr_t *remote_addr, const bt_property_t *property)
{
logd("AAPI: set remote dev prop\n");
//TODO
return 0;
}
/*
* FUNCTION: aapiGetallBondedDevices
* USE: Call back to get all bonded devices. We determine it is bonded by
* looking at the keys we have for it
* PARAMS: cbkData - *(struct aapiBondedDevicesEnumData) - enumeration state
* addr - is the peer address
* name - device name if known
* nameLen - length of the name
* devCls - the device class
* haveKeys - bitmaskof keys we have
* wantedKey - unused, NULL
* RETURN: true to keep enumerating
* NOTES:
*/
static bool aapiGetAllBondedDevicesEnumF(void *cbkData, const struct bt_addr *addr, const void *name, uint32_t nameLen, uint32_t devCls, uint32_t haveKeys, const uint8_t *wantedKey)
{
struct aapiBondedDevicesEnumData *data = (struct aapiBondedDevicesEnumData*)cbkData;
bt_bdaddr_t *devs;
//TODO: which LE key to use to tell we're bnoded?
if (!(haveKeys & ((1 << KEY_TYPE_MITM_PROTECTED) | (1 << KEY_TYPE_MITM_UNPROTECTED))))
return true;
/* since android only uses the 6-byte mac, we may get collisions between EDR and LE devices. Nothing we can do */
devs = realloc(data->devs, sizeof(bt_bdaddr_t[data->num + 1]));
if (!devs) {
logw("Ending bonded device enumeration early - out of memory\n");
return false;
}
memcpy(&devs[data->num].address, addr->addr, sizeof(devs[data->num].address));
data->num++;
data->devs = devs;
return true;
}
/*
* FUNCTION: aapiGetSomeAdapterProperties
* USE: Get all or one of Bluetooth Adapter properties
* PARAMS: statToSend - what status value to send
* typeP - points to prop typ ewe want, or NULL for all
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetSomeAdapterProperties(bt_status_t statToSend, const bt_property_type_t *typeP)
{
struct aapiBondedDevicesEnumData bondedDevsEnumData = {.devs = NULL, .num = 0};
static bt_uuid_t uuids[] = {{{0}}}; /* TODO */
bt_property_t props[BT_MAX_PROPS];
unsigned numProps = 0, numUuids = 0, numBondedDevs = 0;
if (!typeP || *typeP == BT_PROPERTY_BDNAME) {
mLocalName.name[persistGetDeviceName(mLocalName.name)] = 0;
props[numProps].type = BT_PROPERTY_BDNAME;
props[numProps].len = strnlen((const char*)mLocalName.name, sizeof(mLocalName.name));
props[numProps].val = &mLocalName;
numProps++;
}
if (!typeP || *typeP == BT_PROPERTY_BDADDR) {
props[numProps].type = BT_PROPERTY_BDADDR;
props[numProps].len = sizeof(mLocalMac);
props[numProps].val = &mLocalMac;
numProps++;
}
if (!typeP || *typeP == BT_PROPERTY_UUIDS) {
props[numProps].type = BT_PROPERTY_UUIDS;
props[numProps].len = sizeof(bt_uuid_t[numUuids]);
props[numProps].val = &uuids;
numProps++;
}
if (!typeP || *typeP == BT_PROPERTY_CLASS_OF_DEVICE) {
props[numProps].type = BT_PROPERTY_CLASS_OF_DEVICE;
props[numProps].len = sizeof(mDevCls);
props[numProps].val = &mDevCls;
numProps++;
}
if (!typeP || *typeP == BT_PROPERTY_TYPE_OF_DEVICE) {
props[numProps].type = BT_PROPERTY_TYPE_OF_DEVICE;
props[numProps].len = sizeof(mDevType);
props[numProps].val = &mDevType;
numProps++;
}
/* TODO: BT_PROPERTY_SERVICE_RECORD here ? */
if (!typeP || *typeP == BT_PROPERTY_ADAPTER_SCAN_MODE) {
props[numProps].type = BT_PROPERTY_ADAPTER_SCAN_MODE;
props[numProps].len = sizeof(mScanMode);
props[numProps].val = &mScanMode;
numProps++;
}
if (!typeP || *typeP == BT_PROPERTY_ADAPTER_BONDED_DEVICES) {
persistEnumKnownDevs(aapiGetAllBondedDevicesEnumF, &bondedDevsEnumData, NULL);
props[numProps].type = BT_PROPERTY_ADAPTER_BONDED_DEVICES;
props[numProps].len = sizeof(bt_bdaddr_t[bondedDevsEnumData.num]);
props[numProps].val = bondedDevsEnumData.devs;
numProps++;
}
if (!typeP || *typeP == BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
mDiscoveryTimeout = persistGetDiscoveryLength();
props[numProps].type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT;
props[numProps].len = sizeof(mDiscoveryTimeout);
props[numProps].val = &mDiscoveryTimeout;
numProps++;
}
aapiAdapterProperties(statToSend, numProps, props);
if (bondedDevsEnumData.devs)
free(bondedDevsEnumData.devs);
return BT_STATUS_SUCCESS;
}
/*
* FUNCTION: aapiGetAdapterProperty
* USE: Get a Bluetooth Adapter property
* PARAMS: type - the property to get
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetAdapterProperty(bt_property_type_t type)
{
logd("AAPI: get adapter property(%u)\n", type);
return aapiGetSomeAdapterProperties(BT_STATUS_SUCCESS, &type);
}
/*
* FUNCTION: aapiGetAdapterProperties
* USE: Get all Bluetooth Adapter properties at init
* PARAMS: NONE
* RETURN: bt_status_t
* NOTES:
*/
static int aapiGetAdapterProperties(void)
{
logd("AAPI: get adapter properties\n");
return aapiGetSomeAdapterProperties(BT_STATUS_SUCCESS, NULL);
}
/*
* FUNCTION: aapiSetPropOpDoneCbk
* USE: Done calback for "set property" calls to hci
* PARAMS: cbkData - in our case the property we tried to set
* status - the status from the chip (0 = good, else bad)
* RETURN: NONE
* NOTES: calls back to java with success and new property value
*/
static void aapiSetPropOpDoneCbk(void *cbkData, uint8_t status)
{
bt_property_type_t prop = (bt_property_type_t)cbkData;
logd("Prop %u set with status %u\n", prop, status);
aapiGetSomeAdapterProperties(status ? BT_STATUS_FAIL : BT_STATUS_SUCCESS, &prop);
}
/*
* FUNCTION: aapiSetAdapterProperty
* USE: Set a Bluetooth Adapter property
* PARAMS: property - the property to set
* RETURN: bt_status_t
* NOTES:
*/
static int aapiSetAdapterProperty(const bt_property_t *property)
{
uint32_t len;
logd("AAPI: set adapter prop (%u)\n", property->type);
switch (property->type) {
case BT_PROPERTY_BDNAME:
memset(&mLocalName, 0, sizeof(mLocalName));
len = property->len;
if (len > sizeof(mLocalName)) {
len = sizeof(mLocalName);
logw("Name truncated\n");
}
memcpy(&mLocalName, property->val, len);
if (!persistSetDeviceName(mLocalName.name, len))
logw("Failed to save BT name\n");
if (hciSetLocalName((const char*)mLocalName.name, aapiSetPropOpDoneCbk, (void*)BT_PROPERTY_BDNAME))
break;
loge("Set name up failed\n");
return BT_STATUS_FAIL;
case BT_PROPERTY_ADAPTER_SCAN_MODE:
if (property->len != sizeof(mScanMode)) {
loge("refusing set scan mode with strange len (%u != %u)\n", property->len, sizeof(mScanMode));
return BT_STATUS_FAIL;
}
memcpy(&mScanMode, property->val, sizeof(mScanMode));
bool discoverable = mScanMode == BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
bool connectible = mScanMode != BT_SCAN_MODE_NONE;
if (hciSetDiscoverableConnectable(&discoverable, &connectible, aapiSetPropOpDoneCbk, (void*)BT_PROPERTY_ADAPTER_SCAN_MODE))
break;
loge("Set discoverable/connectible up failed\n");
return BT_STATUS_FAIL;
case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
if (property->len != sizeof(mDiscoveryTimeout)) {
loge("refusing set discovery timeout with strange len (%u != %u)\n", property->len, sizeof(mDiscoveryTimeout));
return BT_STATUS_FAIL;
}
memcpy(&mDiscoveryTimeout, property->val, sizeof(mDiscoveryTimeout));
if (!persistSetDiscoveryLength(mDiscoveryTimeout))
logw("Failed to save discovery timeout\n");
return aapiGetSomeAdapterProperties(BT_STATUS_SUCCESS, &property->type);
default:
loge("Refusing to set unknown local property %u\n", property->type);
return BT_STATUS_FAIL;
}
return BT_STATUS_SUCCESS;
}
/*
* FUNCTION: aapiEnableOpDoneCbk
* USE: Done calback for a step in the init process
* PARAMS: cbkData - in our case the step number
* status - the status from the chip (0 = good, else bad)
* RETURN: NONE
* NOTES: calls next step or calls java to say we're done (success or fail is conveyed too)
*/
static void aapiEnableOpDoneCbk(void *cbkData, uint8_t status)
{
unsigned long step = (unsigned long)cbkData;
if (status) {
loge("Enable failed on step %u with status %u\n", step, status);
aapiAdapterStateChanged(false);
}
logd("aapi init step %u done\n", step);
switch (step) {
case 0:
if (hciSetDeviceClass(mDevCls, aapiEnableOpDoneCbk, (void*)(step + 1)))
return;
break;
case 1:;
bool discoverable = mScanMode == BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
bool connectible = mScanMode != BT_SCAN_MODE_NONE;
if (hciSetDiscoverableConnectable(&discoverable, &connectible, aapiEnableOpDoneCbk, (void*)(step + 1)))
return;
break;
case 2:
aapiAdapterStateChanged(true);
return;
default:
loge("Unknown init step %u\n", step);
aapiAdapterStateChanged(false);
return;
}
loge("Error after step %u\n", step);
aapiAdapterStateChanged(false);
}
/*
* FUNCTION: aapiEnable
* USE: Enable BT
* PARAMS: NONE
* RETURN: bt_status_t
* NOTES:
*/
static int aapiEnable(void)
{
int ret;
logd("AAPI: enable\n");
pthread_mutex_lock(&mStateLock);
if (mStackUp) {
loge("stack requested to come up while already up\n");
goto fail_up;
}
mLocalName.name[persistGetDeviceName(mLocalName.name)] = 0;
mDiscoveryTimeout = persistGetDiscoveryLength();
if (!timersInit()) {
loge("timers failed\n");
goto fail_timers;
}
if (!hciUp(mLocalMac.address, HCI_DISP_CAP_DISP_YES_NO)) {
loge("HCI up failed\n");
goto fail_hci;
}
hciGetLocalAddress(mLocalMac.address);
if (hciInfoAclBufSizeEdr(NULL, NULL) && hciInfoAclBufSizeLe(NULL, NULL))
mDevType = BT_DEVICE_DEVTYPE_DUAL;
else if (hciInfoAclBufSizeLe(NULL, NULL))
mDevType = BT_DEVICE_DEVTYPE_BLE;
else if (hciInfoAclBufSizeEdr(NULL, NULL))
mDevType = BT_DEVICE_DEVTYPE_BREDR;
else {
mDevType = 0;
loge("device type indeterminate\n");
}
ret = l2cInit();
if (ret) {
loge("L2C up failed\n");
goto fail_l2c;
}
if (!sdpInit()) {
loge("SDP up failed\n");
goto fail_sdp;
}
if (!rfcInit()) {
loge("RFC up failed\n");
goto fail_rfc;
}
if (!hciSetLocalName((const char*)mLocalName.name, aapiEnableOpDoneCbk, (void*)0)) {
loge("Set name up failed\n");
goto fail_name;
}
/* more enable here if needed */
aapiAdapterStateChanged(true);
aapiGetAdapterProperties(); /* contrary to api, this must be here */
mStackUp = true;
aapiSocketNotifStackState(true);
aapiGattNotifStackState(true);
pthread_mutex_unlock(&mStateLock);
return BT_STATUS_SUCCESS;
fail_name:
rfcDeinit();
fail_rfc:
sdpDeinit();
fail_sdp:
l2cDeinit();
fail_l2c:
hciDown();
fail_hci:
timersDeinit();
fail_timers:
fail_up:
mStackUp = false;
aapiSocketNotifStackState(false);
aapiGattNotifStackState(false);
persistStore();
pthread_mutex_unlock(&mStateLock);
return BT_STATUS_FAIL;
}
/*
* FUNCTION: aapiDisable
* USE: Disable BT
* PARAMS: NONE
* RETURN: bt_status_t
* NOTES:
*/
static int aapiDisable(void)
{
logd("AAPI: disable\n");
pthread_mutex_lock(&mStateLock);
if (!mStackUp)
loge("Cannot turn stack off while off\n");
else {
mStackUp = false;
aapiSocketNotifStackState(false);
aapiGattNotifStackState(false);
rfcDeinit();
sdpDeinit();
l2cDeinit();
hciDown();
timersDeinit();
}
persistStore();
pthread_mutex_unlock(&mStateLock);
return 0;
}
/*
* FUNCTION: aapiGetProfileIface
* USE: Get a pointer to a particular profile's interface
* PARAMS: profile_id - the profile
* RETURN: bt_status_t
* NOTES:
*/
static const void* aapiGetProfileIface(const char *profile_id)
{
logd("AAPI: aapiGetProfileIface('%s')\n", profile_id);
// if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID))
// return &pHandsfree;
if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID))
return aapiGetProfileIfaceSockets();
// if (!strcmp(profile_id, BT_PROFILE_PAN_ID))
// return &pPan;
// if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
// return &pAdvAudio;
// if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID))
// return &pHidHost;
// if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID))
// return &pHealth;
// if (!strcmp(profile_id, BT_PROFILE_GATT_ID))
// return aapiGetProfileIfaceGatt();
// if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID))
// return &pAvrcp;
logw("Asked for unknown profile '%s'\n", profile_id);
return NULL;
}
/*
* FUNCTION: aapiWorker
* USE: BT worker thread for callbacks to java
* PARAMS: callbacks - the callbacks to the higher layer
* RETURN: unused
* NOTES:
*/
static void* btWorker(void *callbacks)
{
bt_callbacks_t *cbks = (bt_callbacks_t*)callbacks;
struct aapiWorkItem *wi;
int status;
pthread_setname_np(pthread_self(), "aapi_worker");
cbks->thread_evt_cb(ASSOCIATE_JVM);
while(1) {
status = workQueueGet(mWorkQ, (void**)&wi);
if (status)
break;
logd("callback type %u\n", wi->type);
switch (wi->type) {
case WT_ADAPTER_STATE_CHG:
cbks->adapter_state_changed_cb(wi->state.on ? BT_STATE_ON : BT_STATE_OFF);
break;
case WT_ADAPTER_PROPS:
cbks->adapter_properties_cb(wi->properties.status, wi->properties.num_properties, wi->properties.properties);
break;
case WT_REMOTE_PROPS:
cbks->remote_device_properties_cb(wi->properties.status, &wi->properties.bd_addr, wi->properties.num_properties, wi->properties.properties);
break;
case WT_DEV_DISCOVERED:
cbks->device_found_cb(wi->properties.num_properties, wi->properties.properties);
break;
case WT_DISC_STATE_CHG:
cbks->discovery_state_changed_cb(wi->state.on ? BT_DISCOVERY_STARTED : BT_DISCOVERY_STOPPED);
break;
case WT_PIN_REQ:
cbks->pin_request_cb(&wi->pinReq.remote_bd_addr, &wi->pinReq.bd_name, wi->pinReq.cod, false);
break;
case WT_SSP_REQ:
cbks->ssp_request_cb(&wi->sspReq.remote_bd_addr,&wi->sspReq.bd_name, wi->sspReq.cod, wi->sspReq.pairing_variant, wi->sspReq.pass_key);
break;
case WT_BOND_STATE_CHG:
cbks->bond_state_changed_cb(wi->stateCbk.status, &wi->stateCbk.remote_bd_addr, wi->stateCbk.state);
break;
case WT_ACL_STATE_CHG:
cbks->acl_state_changed_cb(wi->stateCbk.status, &wi->stateCbk.remote_bd_addr, wi->stateCbk.state);
break;
case WT_DUT_EVT:
cbks->dut_mode_recv_cb(wi->hciEvt.opcode, wi->hciEvt.buf, wi->hciEvt.len);
break;
case WT_LE_TEST_EVT:
cbks->le_test_mode_cb(wi->leTestEvt.status, wi->leTestEvt.num_packets);
break;
default:
logw("unknown work item type %u\n", wi->type);
break;
}
logd("callback type %u done\n", wi->type);
free(wi);
}
cbks->thread_evt_cb(DISASSOCIATE_JVM);
logd("aapi thread exiting\n");
return NULL;
}
/*
* FUNCTION: aapiWorkQueueFreeItem
* USE: Delete work items from the workQ on workQ deletion
* PARAMS: wi - the work item to free
* RETURN: NONE
* NOTES:
*/
static void btWorkQueueFreeItem(void *wi)
{
free(wi);
}
/*
* FUNCTION: aapiInit
* USE: Init the stack (but not the chip), get callbacks
* PARAMS: callbacks - the callbacks to our user
* RETURN: bt_status_t
* NOTES: only stack-level init here (aka none)
*/
static int aapiInit(bt_callbacks_t* callbacks)
{
static bt_callbacks_t cbks;
memcpy(&cbks, callbacks, sizeof(cbks));
persistLoad();
mWorkQ = workQueueAlloc(AAPI_NUM_OUTSTANDING_WORK_ITEMS);
if (!mWorkQ)
return BT_STATUS_NOMEM;
if (pthread_create(&mWorker, NULL, btWorker, &cbks)) {
workQueueFree(mWorkQ, btWorkQueueFreeItem);
return BT_STATUS_FAIL;
}
return 0;
}
/*
* FUNCTION: aapiCleanup
* USE: Cleanup the stack and prepare for unloading
* PARAMS: NONE
* RETURN: NONE
* NOTES:
*/
static void aapiCleanup(void)
{
workQueueWakeAll(mWorkQ, 1);
pthread_join(mWorker, NULL);
workQueueFree(mWorkQ, btWorkQueueFreeItem);
}
/*
* FUNCTION: aapiSetOsCallouts
* USE: Gives the stack callbacks into the OS for alarms and wakelocks
* PARAMS: callouts - the struct for alarm & wakelock access
* RETURN: BT_STATUS_*
* NOTES:
*/
static int aapiSetOsCallouts(bt_os_callouts_t *callouts)
{
//XXX: TODO: use this eventually
return BT_STATUS_SUCCESS;
}
/*
* FUNCTION: aapiReadEnergyInfo
* USE: Who knows???
* PARAMS: NONE
* RETURN: BT_STATUS_SUCCESS or BT_STATUS_NOT_READY
* NOTES: "Success indicates that the VSC command was sent to controller"
*/
static int aapiReadEnergyInfo()
{
//TODO: WTF is this???
return BT_STATUS_SUCCESS;
}
/*
* FUNCTION: aapiGetBtIface
* USE: Return the bt_interface_t struct full of ways to call us
* PARAMS: NONE
* RETURN: the bt_interface_t struct
* NOTES: no init here
*/
static const bt_interface_t* aapiGetBtIface(void)
{
static const bt_interface_t iface = {
.size = sizeof(bt_interface_t),
.init = aapiInit,
.cleanup = aapiCleanup,
.enable = aapiEnable,
.disable = aapiDisable,
.get_adapter_properties = aapiGetAdapterProperties,
.get_adapter_property = aapiGetAdapterProperty,
.set_adapter_property = aapiSetAdapterProperty,
.get_remote_device_properties = aapiGetRemoteDevProperties,
.get_remote_device_property = aapiGetRemoteDevProperty,
.set_remote_device_property = aapiSetRemoteDevProperty,
.get_remote_service_record = aapiGetRemoteServiceRecord,
.get_remote_services = aapiGetRemoteServices,
.start_discovery = aapiDiscoveryStart,
.cancel_discovery = aapiDiscoveryStop,
.create_bond = aapiBondCreate,
.remove_bond = aapiBondRemove,
.cancel_bond = aapiBondCancel,
.pin_reply = aapiPinReply,
.ssp_reply = aapiSspReply,
.get_profile_interface = aapiGetProfileIface,
.dut_mode_configure = aapiDutEnable,
.dut_mode_send = aapiDutSend,
.le_test_mode = aapiLeTestMode,
.config_hci_snoop_log = aapiHciLogEnable,
.set_os_callouts = aapiSetOsCallouts,
.read_energy_info = aapiReadEnergyInfo,
};
return &iface;
}
/*
* FUNCTION: aapiClose
* USE: Module close function
* PARAMS: device - our bluetooth_device_t
* RETURN: 0 on success
* NOTES: only module-level cleanup here (aka: none)
*/
static int aapiClose(struct hw_device_t* device)
{
bluetooth_device_t *btd = (bluetooth_device_t*)device;
//TODO: cleanup, if any
return 0;
}
/*
* FUNCTION: aapiOpen
* USE: Module open function
* PARAMS: module - the module object
* name - the module name
* abstractionP - we store our bluetooth_device_t there
* RETURN: 0 on success
* NOTES: only module-level init here (aka none)
*/
static int aapiOpen(const struct hw_module_t* module, char const* name, struct hw_device_t** abstractionP)
{
bluetooth_device_t *btd = (bluetooth_device_t*)calloc(1, sizeof(bluetooth_device_t));
btd->common.tag = HARDWARE_DEVICE_TAG;
btd->common.version = 0;
btd->common.module = (struct hw_module_t*)module; /* casting const away is bad, but sadly such is our API */
btd->common.close = aapiClose;
btd->get_bluetooth_interface = aapiGetBtIface;
*abstractionP = (struct hw_device_t*)btd;
return 0;
}
/* these are needed for android module loader to open us */
static struct hw_module_methods_t mAapiMethods = {
.open = aapiOpen,
};
extern hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = 1,
.hal_api_version = 0,
.id = BT_HARDWARE_MODULE_ID,
.name = "NewBlue stack",
.author = "dmitrygr@",
.methods = &mAapiMethods,
};
/*
* FUNCTION: aapiWorkItemAlloc
* USE: Allocate a work item
* PARAMS: type - work item type
* extraSz - how many extra bytes to allocate
* RETURN: the pointer on success, else NULL
* NOTES: will print an error msg on error
*/
static struct aapiWorkItem* aapiWorkItemAlloc(uint8_t type, uint32_t extraSz)
{
struct aapiWorkItem *wi = calloc(1, sizeof(struct aapiWorkItem) + extraSz);
if (wi)
wi->type = type;
else
loge("Failed to allocate work item type %u (+%ub)\n", type, extraSz);
return wi;
}
/*
* FUNCTION: aapiWorkItemEnqueue
* USE: Enqueue a work item
* PARAMS: wi - the work item
* RETURN: NONE
* NOTES: either enqueues or frees it. an error msg will be printed if needed
*/
static void aapiWorkItemEnqueue(struct aapiWorkItem *wi)
{
if (workQueuePut(mWorkQ, wi))
return;
loge("Failed to enqueue work item type %u\n", wi->type);
btWorkQueueFreeItem(wi);
}
/*
* FUNCTION: aapiAdapterStateChanged
* USE: Notify android about BT adapter going up or coming down
* PARAMS: on - up or down?
* RETURN: NONE
* NOTES:
*/
void aapiAdapterStateChanged(bool on)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_ADAPTER_STATE_CHG, 0);
if (!wi)
return;
wi->state.on = on;
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiWorkItemAllocWithProperties
* USE: allocate a work item with a deep copy of all properties
* PARAMS: type - the work item type
* num_properties - how many properties we're sending
* properties - the properties to send (a copy is made)
* RETURN: the work item or NULL on error
* NOTES:
*/
static struct aapiWorkItem* aapiWorkItemAllocWithProperties(uint8_t type, int num_properties, const bt_property_t *properties)
{
struct aapiWorkItem* wi;
uint32_t len = 0;
bt_property_t *dstProps;
uint8_t *dstDatas;
int i;
for (i = 0; i < num_properties; i++)
len += sizeof(bt_property_t) + properties[i].len;
wi = aapiWorkItemAlloc(type, len);
if (!wi)
return NULL;
dstProps = wi->properties.properties;
dstDatas = (uint8_t*)(dstProps + num_properties);
for (i = 0; i < num_properties; i++) {
dstProps[i].type = properties[i].type;
dstProps[i].len = properties[i].len;
dstProps[i].val = dstDatas;
memcpy(dstDatas, properties[i].val, properties[i].len);
dstDatas += properties[i].len;
}
wi->properties.num_properties = num_properties;
return wi;
}
/*
* FUNCTION: aapiAdapterProperties
* USE: Notify android about BT adapter properties
* PARAMS: status - the status to send
* num_properties - how many properties we're sending
* properties - the properties to send (a copy is made)
* RETURN: NONE
* NOTES:
*/
void aapiAdapterProperties(bt_status_t status, int num_properties, const bt_property_t *properties)
{
struct aapiWorkItem* wi = aapiWorkItemAllocWithProperties(WT_ADAPTER_PROPS, num_properties, properties);
if (!wi)
return;
wi->properties.status = status;
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiRemoteDevProperties
* USE: Notify android about remote device properties
* PARAMS: status - the status to send
* bd_addr - address of said device
* num_properties - how many properties we're sending
* properties - the properties to send (a copy is made)
* RETURN: NONE
* NOTES:
*/
void aapiRemoteDevProperties(bt_status_t status, const bt_bdaddr_t *bd_addr, int num_properties, const bt_property_t *properties)
{
struct aapiWorkItem* wi = aapiWorkItemAllocWithProperties(WT_REMOTE_PROPS, num_properties, properties);
if (!wi)
return;
wi->properties.status = status;
memcpy(&wi->properties.bd_addr, bd_addr, sizeof(wi->properties.bd_addr));
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiDevDiscoveredCbk
* USE: Notify android about a discovered device
* PARAMS: num_properties - how many properties we're sending
* properties - the properties to send (a copy is made)
* RETURN: NONE
* NOTES:
*/
void aapiDevDiscoveredCbk(int num_properties, const bt_property_t *properties)
{
struct aapiWorkItem* wi = aapiWorkItemAllocWithProperties(WT_DEV_DISCOVERED, num_properties, properties);
if (!wi)
return;
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiDiscoveryStateChanged
* USE: Notify android about BT adapter starting or stopping discovery
* PARAMS: on - start or stop?
* RETURN: NONE
* NOTES:
*/
void aapiDiscoveryStateChanged(bool on)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_DISC_STATE_CHG, 0);
if (!wi)
return;
wi->state.on = on;
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiPinReqCbk
* USE: Notify android about a need for PIN entry UI
* PARAMS: peer - remote device address (a copy is made)
* RETURN: NONE
* NOTES:
*/
void aapiPinReqCbk(const struct bt_addr *peer)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_PIN_REQ, 0);
bool ret;
if (!wi)
return;
ret = persistGetKnownDev(peer, wi->pinReq.bd_name.name, NULL, &wi->pinReq.cod);
if (!ret)
logw("SSP-requesting device not know\n");
memcpy(&wi->pinReq.remote_bd_addr.address, peer->addr, sizeof(wi->pinReq.remote_bd_addr.address));
aapiSendDeviceInfo(peer);
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiSspReqCbk
* USE: Notify android about a need for SSP pairing UI
* PARAMS: peer - remote device address (a copy is made)
* pairing_variant - pairing typ requested
* pass_key - the passkey to display, if relevant
* RETURN: NONE
* NOTES:
*/
void aapiSspReqCbk(const struct bt_addr *peer, bt_ssp_variant_t pairing_variant, uint32_t pass_key)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_SSP_REQ, 0);
bool ret;
if (!wi)
return;
ret = persistGetKnownDev(peer, wi->sspReq.bd_name.name, NULL, &wi->sspReq.cod);
if (!ret)
logw("SSP-requesting device not know\n");
wi->sspReq.pairing_variant = pairing_variant;
wi->sspReq.pass_key = pass_key;
memcpy(&wi->sspReq.remote_bd_addr.address, peer->addr, sizeof(wi->sspReq.remote_bd_addr.address));
aapiSendDeviceInfo(peer);
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiBondStateChangedCbk
* USE: Notify android about bonding changes
* PARAMS: peer - remote device address (a copy is made)
* state - AAPI_BOND_STATE_*
* RETURN: NONE
* NOTES:
*/
void aapiBondStateChangedCbk(const struct bt_addr *peer, uint8_t state)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_BOND_STATE_CHG, 0);
if (!wi)
return;
switch (state) {
case AAPI_BOND_STATE_FAILED:
wi->stateCbk.status = AAPI_BOND_STATE_FAILED;
wi->stateCbk.state = BT_BOND_STATE_NONE;
break;
case AAPI_BOND_STATE_INPROGRESS:
wi->stateCbk.status = AAPI_BOND_STATE_SUCCESS;
wi->stateCbk.state = BT_BOND_STATE_BONDING;
break;
case AAPI_BOND_STATE_SUCCESS:
wi->stateCbk.status = AAPI_BOND_STATE_SUCCESS;
wi->stateCbk.state = BT_BOND_STATE_BONDED;
break;
}
memcpy(&wi->stateCbk.remote_bd_addr.address, peer->addr, sizeof(wi->stateCbk.remote_bd_addr.address));
aapiSendDeviceInfo(peer);
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiAclStateChanged
* USE: Notify android about an ACL link being established
* PARAMS: status - the atatus
* peer - remote device address (a copy is made)
* up - was link brought up or did it come down
* RETURN: NONE
* NOTES:
*/
void aapiAclStateChanged(bt_status_t status, const struct bt_addr *peer, bool up)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_ACL_STATE_CHG, 0);
if (!wi)
return;
wi->stateCbk.status = status;
wi->stateCbk.state = up ? BT_ACL_STATE_CONNECTED : BT_ACL_STATE_DISCONNECTED;
memcpy(&wi->stateCbk.remote_bd_addr.address, peer->addr, sizeof(wi->stateCbk.remote_bd_addr.address));
aapiSendDeviceInfo(peer);
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiDutModeEventCbk
* USE: Notify android about an event in response to a DUT-mode command
* PARAMS: opcode - the command opcode
* buf - the reply buffer (a copy is made)
* len - length of said buffer
* RETURN: NONE
* NOTES:
*/
void aapiDutModeEventCbk(uint16_t opcode, const uint8_t *buf, uint8_t len)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_DUT_EVT, len);
if (!wi)
return;
wi->hciEvt.opcode = opcode;
wi->hciEvt.len = len;
memcpy(&wi->hciEvt.buf, buf, len);
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiLeTestModeEventCbk
* USE: Notify android about an event in response to a LE test mode commands
* PARAMS: status - result of command
* num_packets - num_packets field from command complete event, if command was "end test"
* RETURN: NONE
* NOTES:
*/
void aapiLeTestModeEventCbk(bt_status_t status, uint16_t num_packets)
{
struct aapiWorkItem* wi = aapiWorkItemAlloc(WT_LE_TEST_EVT, 0);
if (!wi)
return;
wi->leTestEvt.status = status;
wi->leTestEvt.num_packets = num_packets;
aapiWorkItemEnqueue(wi);
}
/*
* FUNCTION: aapiSendDeviceInfo
* USE: Notify android about a device we know about
* PARAMS: peer - the address of the device
* RETURN: NONE
* NOTES:
*/
static void aapiSendDeviceInfo(const struct bt_addr *peer)
{
bt_property_t props[BT_MAX_PROPS];
bt_device_type_t devType;
unsigned numProps = 0;
bt_bdname_t name;
uint32_t nameLen;
bt_bdaddr_t addr;
uint32_t devCls;
if (!persistGetKnownDev(peer, name.name, &nameLen, &devCls)) {
logi("Device unknown to aapiSendDeviceInfo()\n");
return;
}
memcpy(addr.address, peer->addr, sizeof(addr.address));
//TODO: collapse LE & EDR devices into one if really the same device
devType = BT_ADDR_IS_EDR(*peer) ? BT_DEVICE_DEVTYPE_BREDR : BT_DEVICE_DEVTYPE_BLE;
if (nameLen) {
name.name[nameLen] = 0;
props[numProps].type = BT_PROPERTY_BDNAME;
props[numProps].len = nameLen;
props[numProps].val = &name;
numProps++;
}
if (devCls) {
props[numProps].type = BT_PROPERTY_CLASS_OF_DEVICE;
props[numProps].len = sizeof(mDevCls);
props[numProps].val = &mDevCls;
numProps++;
}
logd("%u props for device sent\n", numProps);
aapiRemoteDevProperties(BT_STATUS_SUCCESS, &addr, numProps, props);
}