| #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); |
| } |
| |
| |
| |
| |
| |