#include "BluetoothGatt.hpp"
#include "CharacteristicsEditor.hpp"
#include "TypedArrayDataModel.hpp"
#include "Util.hpp"
#include <QVariant>
#include <QMap>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
using namespace bb::cascades;
#define BT_CONTROLLER_EVENT 1
#define BT_GATT_CONNECT_EVENT 2
#define BT_GATT_DISCONNECT_EVENT 3
#define BT_GATT_NOTIFICATION_EVENT 4
typedef struct {
int event;
char *bt_addr;
char *event_data;
} btController_t;
typedef struct {
char *bdaddr;
char *service;
int instance;
int err;
uint16_t connInt;
uint16_t latency;
uint16_t superTimeout;
} btGatt_t;
typedef struct {
int instance;
uint16_t handle;
uint8_t *val;
uint16_t len;
} btNotification_t;
BluetoothGatt *s_globalBluetoothGatt = 0;
bool processBluetoothEvent(const int event, void *data)
{
if (s_globalBluetoothGatt) {
s_globalBluetoothGatt->processBluetoothEvent(event, data);
return true;
}
return false;
}
void bt_controller_cb(const int event, const char *bt_addr, const char *event_data)
{
btController_t *ctrl = (btController_t *)calloc(1, sizeof(btController_t));
if (!ctrl)
return;
ctrl->event = event;
ctrl->bt_addr = strdup(bt_addr);
ctrl->event_data = strdup(event_data);
if ((0 == ctrl->bt_addr) || (!processBluetoothEvent(BT_CONTROLLER_EVENT, (void*)ctrl))) {
free(ctrl->bt_addr);
free(ctrl->event_data);
free(ctrl);
}
}
void gatt_service_connected_cb(const char *bdaddr, const char *service, int instance, int err, uint16_t connInt, uint16_t latency, uint16_t superTimeout, void *userData)
{
if ((0 == bdaddr) || (0 == service) || (0 == userData))
return;
btGatt_t *gatt = (btGatt_t *)calloc(1, sizeof(btGatt_t));
if (!gatt)
return;
gatt->bdaddr = strdup(bdaddr);
gatt->service = strdup(service);
gatt->instance = instance;
gatt->err = err;
gatt->connInt = connInt;
gatt->latency = latency;
gatt->superTimeout = superTimeout;
if ((0 == gatt->bdaddr) || (0 == gatt->service) || (!processBluetoothEvent(BT_GATT_CONNECT_EVENT, (void*) gatt))) {
free(gatt->bdaddr);
free(gatt->service);
free(gatt);
}
}
void gatt_service_disconnected_cb(const char *bdaddr, const char *service, int instance, int reason, void *userData)
{
if ((0 == bdaddr) || (0 == service) || (0 == userData))
return;
btGatt_t *gatt = (btGatt_t *) calloc(1, sizeof(btGatt_t));
if (!gatt)
return;
gatt->bdaddr = strdup(bdaddr);
gatt->service = strdup(service);
gatt->instance = instance;
gatt->err = reason;
if ((0 == gatt->bdaddr) || (0 == gatt->service) || (!processBluetoothEvent(BT_GATT_DISCONNECT_EVENT, (void*) gatt))) {
free(gatt->bdaddr);
free(gatt->service);
free(gatt);
}
}
static void notifications_cb(int instance, uint16_t handle, const uint8_t *val, uint16_t len, void*)
{
qDebug() << "Notification callback";
if ( 0 == val )
return;
btNotification_t *notify = (btNotification_t *)calloc(1, sizeof(btNotification_t));
if (0 == notify)
return;
notify->instance = instance;
notify->handle = handle;
notify->val = (uint8_t*)calloc(len, sizeof(uint8_t));
if (0 == notify->val) {
free(notify);
return;
}
memcpy(notify->val, val, len);
notify->len = len;
if ((0 == notify->val) || (!processBluetoothEvent(BT_GATT_NOTIFICATION_EVENT, (void*) notify))) {
free(notify->val);
free(notify);
}
}
BluetoothGatt::BluetoothGatt(QObject *parent)
: QObject(parent)
, m_devices(new TypedArrayDataModel())
, m_services(new TypedArrayDataModel())
, m_characteristics(new TypedArrayDataModel())
, m_characteristicList(0)
, m_characteristicStruct(0)
, m_editor(new CharacteristicsEditor(this))
{
s_globalBluetoothGatt = this;
qmlRegisterType<CharacteristicsEditor>();
m_devices->setParent(this);
m_services->setParent(this);
m_characteristics->setParent(this);
if (bt_device_init(bt_controller_cb) != EOK) {
qWarning("Unable to initialize bluetooth device");
}
m_gatt_cb.connected = gatt_service_connected_cb;
m_gatt_cb.disconnected = gatt_service_disconnected_cb;
m_gatt_cb.updated = NULL;
if (bt_gatt_init(&m_gatt_cb) != EOK) {
qWarning() << "GATT initialization failure" << errno;
}
if (bt_le_init(0) != EOK) {
qWarning() << "LE initialization failure " << errno;
}
bool ok = connect(this, SIGNAL(bluetoothEvent(const int, void*) ), this, SLOT(handleBluetoothEvent(const int, void *)));
Q_ASSERT(ok);
ok = connect(m_editor, SIGNAL(characteristicNotificationsEnabledChanged(bool)), this, SLOT(handleCharacteristicNotificationsEnabledChanged(bool)));
Q_ASSERT(ok);
refreshDevices();
}
BluetoothGatt::~BluetoothGatt()
{
bt_device_deinit();
bt_gatt_deinit();
bt_le_deinit();
s_globalBluetoothGatt = 0;
}
void BluetoothGatt::processBluetoothEvent(const int event, void *data)
{
emit bluetoothEvent(event, data);
}
void BluetoothGatt::handleBluetoothEvent(const int event, void *data)
{
switch (event) {
case BT_CONTROLLER_EVENT:
if (data) {
btController_t *ctrl = (btController_t*) data;
switch (ctrl->event) {
case BT_EVT_RADIO_SHUTDOWN:
case BT_EVT_RADIO_INIT:
break;
case BT_EVT_ACCESS_CHANGED:
break;
case BT_EVT_DEVICE_ADDED:
qDebug() << "Device added:" << ctrl->bt_addr;
break;
case BT_EVT_LE_DEVICE_CONNECTED:
qDebug() << "LE device connected:" << ctrl->bt_addr;
break;
case BT_EVT_LE_DEVICE_DISCONNECTED:
qDebug() << "LE device disconnected:" << ctrl->bt_addr;
break;
case BT_EVT_LE_NAME_UPDATED:
qDebug() << "LE device name updated:" << ctrl->bt_addr;
break;
default:
qWarning() << "Unknown event" << ctrl->event << "/" << ctrl->bt_addr;
break;
}
free(ctrl->bt_addr);
free(ctrl->event_data);
free(ctrl);
}
break;
case BT_GATT_CONNECT_EVENT:
if (data) {
btGatt_t *gatt = (btGatt_t*)data;
gattServiceConnected(gatt->bdaddr, gatt->service, gatt->instance, gatt->err, gatt->connInt, gatt->latency, gatt->superTimeout);
free(gatt->bdaddr);
free(gatt->service);
free(gatt);
}
break;
case BT_GATT_DISCONNECT_EVENT:
if (data) {
btGatt_t *gatt = (btGatt_t*)data;
gattServiceDisconnected(gatt->bdaddr, gatt->service, gatt->instance, gatt->err);
free(gatt->bdaddr);
free(gatt->service);
free(gatt);
}
break;
case BT_GATT_NOTIFICATION_EVENT:
if (data) {
btNotification_t *notify = (btNotification_t*)data;
gattNotification(notify->instance, notify->handle, notify->val, notify->len);
free(notify->val);
free(notify);
}
break;
default:
break;
}
}
void BluetoothGatt::handleCharacteristicNotificationsEnabledChanged(bool enabled)
{
bt_gatt_enable_notify(m_gattInstance, m_characteristicStruct, enabled);
}
void BluetoothGatt::disconnectServices()
{
for (int i = 0; i < m_services->size(); i++) {
const QVariantMap service = m_services->value(i).toMap();
const int rc = bt_gatt_disconnect_service(activeDevice().toAscii().constData(), service["uuid"].toString().toAscii().constData());
gattServiceDisconnected(activeDevice(), service["uuid"].toString(), 0, rc);
}
}
void BluetoothGatt::resetEditor()
{
m_characteristicStruct = 0;
}
void BluetoothGatt::resetCharacteristicsList()
{
if (m_characteristicList) {
free(m_characteristicList);
m_characteristicList = 0;
}
}
void BluetoothGatt::refreshDevices()
{
m_devices->clear();
bt_remote_device_t **remote_device_array;
char tempbuff[128];
bt_remote_device_t *next_remote_device = 0;
char **services_array = 0;
remote_device_array = bt_disc_retrieve_devices(BT_DISCOVERY_ALL, 0);
if (remote_device_array) {
for (int i = 0; 0 != (next_remote_device = remote_device_array[i]); ++i) {
services_array = bt_rdev_get_services_gatt( next_remote_device );
if (services_array ) {
bt_rdev_free_services( services_array );
QVariantMap map;
map["type"] = "item";
bt_rdev_get_friendly_name(next_remote_device, tempbuff, 128);
map["name"] = tempbuff;
bt_rdev_get_addr(next_remote_device, tempbuff);
map["address"] = tempbuff;
map["paired"] = true;
m_devices->append(map);
}
}
bt_rdev_free_array(remote_device_array);
}
}
void BluetoothGatt::viewServices(int which)
{
if (which < 0 || which >= m_devices->size())
return;
const QVariantMap device = m_devices->value(which).toMap();
if (device.isEmpty())
return;
setActiveDevice(device["address"].toString());
setActiveDeviceName(device["name"].toString());
m_services->clear();
bt_remote_device_t *remote_device = bt_rdev_get_device(device["address"].toString().toAscii());
if (!remote_device)
return;
int device_type = 0;
char **services_array = 0;
device_type = bt_rdev_get_type(remote_device);
if (device_type == BT_DEVICE_TYPE_LE_PUBLIC || device_type == BT_DEVICE_TYPE_LE_PRIVATE) {
if ((services_array = bt_rdev_get_services_gatt(remote_device))) {
int i;
bt_gatt_conn_parm_t conParm;
for (i = 0; 0 != services_array[i]; i++) {
QVariantMap map;
if (strncasecmp(services_array[i], "0x", 2) == 0 ){
map["uuid"] = &(services_array[i][2]);
} else {
map["uuid"] = services_array[i];
}
int got_svc_name=0;
if ( strcmp(services_array[i],"0xF000AA00-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI IR Temperature Service";
got_svc_name = 1;
}
if ( strcmp(services_array[i],"0xF000AA10-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI Accelerometer Service";
got_svc_name = 1;
}
if ( strcmp(services_array[i],"0xF000AA20-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI Humidity Service";
got_svc_name = 1;
}
if ( strcmp(services_array[i],"0xF000AA30-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI Magnetometer Service";
got_svc_name = 1;
}
if ( strcmp(services_array[i],"0xF000AA40-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI Barometer Service";
got_svc_name = 1;
}
if ( strcmp(services_array[i],"0xF000AA50-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI Gyroscope Service";
got_svc_name = 1;
}
if ( strcmp(services_array[i],"0xF000AA60-0451-4000-B000-000000000000") == 0) {
map["name"] = "TI Test Service";
got_svc_name = 1;
}
if (got_svc_name == 0) {
map["name"] = Util::parse_service_uuid( services_array[i] );
}
map["connected"] = false;
m_services->append(map);
conParm.minConn = 0x30;
conParm.maxConn = 0x50;
conParm.latency = 0;
conParm.superTimeout = 50;
qDebug() << "Connecting service" << QString(services_array[i]);
if (bt_gatt_connect_service(activeDevice().toAscii().constData(), services_array[i], 0, &conParm, this) < 0) {
setErrorMessage(QString("GATT connect service request failed: %1 (%2)").arg(errno).arg(strerror(errno)));
}
}
bt_rdev_free_services(services_array);
}
}
bt_rdev_free(remote_device);
}
void BluetoothGatt::viewCharacteristics(int row)
{
if (row < 0 || row >= m_services->size())
return;
const QVariantMap service = m_services->value(row).toMap();
m_characteristics->clear();
if (!service["connected"].toBool()) {
setErrorMessage(QString("%1: service not connected: %2").arg(activeDeviceName()).arg(service["uuid"].toString()));
return;
}
setActiveService(service["uuid"].toString());
m_gattInstance = service["instance"].toInt();
if (m_characteristicList) {
free(m_characteristicList);
m_characteristicList = 0;
}
m_characteristicListSize = bt_gatt_characteristics_count(m_gattInstance);
if (m_characteristicListSize == -1) {
qWarning() << "GATT characteristics count failed:" << errno << "(" << strerror(errno) << ")";
bt_gatt_disconnect_instance(m_gattInstance);
return;
}
if (m_characteristicListSize == 0) {
setErrorMessage("GATT Characteristic count returned 0");
bt_gatt_disconnect_instance(m_gattInstance);
return;
}
m_characteristicList = (bt_gatt_characteristic_t*)malloc(m_characteristicListSize * sizeof(bt_gatt_characteristic_t));
if (!m_characteristicList) {
setErrorMessage("GATT characteristics: Not enough memory");
bt_gatt_disconnect_instance(m_gattInstance);
return;
}
int number = 0;
do {
number = bt_gatt_characteristics(m_gattInstance, m_characteristicList, m_characteristicListSize);
} while ((number == -1) && (errno== EBUSY));
m_characteristicListSize = number;
if (m_characteristicListSize == -1) {
setErrorMessage(QString("GATT characteristics failed: %1 (%2)").arg(errno).arg(strerror(errno)));
bt_gatt_disconnect_instance(m_gattInstance);
return;
}
qDebug() << "GATT characteristics: Retreived" << m_characteristicListSize << "successfully";
for (int i = 0; i < m_characteristicListSize; i++) {
QVariantMap map;
map["name"] = Util::parse_characteristic_uuid(m_characteristicList[i].uuid);
map["uuid"] = m_characteristicList[i].uuid;
qDebug() << "Properties" << m_characteristicList[i].properties;
m_characteristics->append(map);
}
bt_gatt_reg_notifications(m_gattInstance, notifications_cb);
}
void BluetoothGatt::gattServiceConnected(const QString&, const QString &_serviceUuid, int instance, int err, uint16_t, uint16_t, uint16_t)
{
if (err != EOK) {
setErrorMessage(QString("GATT service connection failed %1 (%2)").arg(err).arg(strerror(err)));
return;
}
QString serviceUuid(_serviceUuid);
if (serviceUuid.startsWith("0x"))
serviceUuid = serviceUuid.mid(2);
for (int i = 0; i < m_services->size(); i++) {
QVariantMap service = m_services->value(i).toMap();
if (serviceUuid == service["uuid"].toString()) {
service["connected"] = true;
service["instance"] = instance;
m_services->replace(i, service);
qDebug() << "GATT service connected:" << serviceUuid;
break;
}
}
}
void BluetoothGatt::gattServiceDisconnected(const QString&, const QString &_serviceUuid, int, int err )
{
if (err != EOK) {
setErrorMessage(QString("GATT service disconnection failed %1 (%2)").arg(err).arg(strerror(err)));
return;
}
QString serviceUuid(_serviceUuid);
if (serviceUuid.startsWith("0x"))
serviceUuid = serviceUuid.mid(2);
for (int i = 0; i < m_services->size(); i++) {
QVariantMap service = m_services->value(i).toMap();
if (serviceUuid == service["uuid"].toString()) {
service["connected"] = false;
m_services->replace(i, service);
qDebug() << "GATT service disconnected:" << serviceUuid;
break;
}
}
}
void BluetoothGatt::gattNotification(int instance, uint16_t handle, const uint8_t *val, uint16_t len)
{
if (instance == m_gattInstance && m_characteristicStruct && handle == m_characteristicStruct->value_handle) {
qDebug("Bluetooth Gatt Sample:\tCharacteristic notification: val @%lx, len %d.\n", (long)val, len);
m_editor->setCharacteristicValue(val, len);
} else {
qDebug("Bluetooth Gatt Sample:\tNon-characteristic notification: instance %d, handle %d, val @%lx, len %d.\n", instance, handle, (long)val, len);
}
}
void BluetoothGatt::viewCharacteristicsEditor(int row)
{
if (row < 0 || row >= m_characteristicListSize)
return;
m_characteristicStruct = &(m_characteristicList[row]);
m_editor->setGattInstance(m_gattInstance);
m_editor->setCharacteristic(m_characteristicStruct);
}
QString BluetoothGatt::activeDevice() const
{
return m_activeDevice;
}
void BluetoothGatt::setActiveDevice(const QString &device)
{
if (m_activeDevice != device) {
m_activeDevice = device;
emit activeDeviceChanged();
}
}
QString BluetoothGatt::activeDeviceName() const
{
return m_activeDeviceName;
}
void BluetoothGatt::setActiveDeviceName(const QString &deviceName)
{
if (m_activeDeviceName != deviceName) {
m_activeDeviceName = deviceName;
}
}
QString BluetoothGatt::activeService() const
{
return m_activeService;
}
void BluetoothGatt::setActiveService(const QString &service)
{
if (m_activeService != service) {
m_activeService = service;
emit activeServiceChanged();
}
}
QString BluetoothGatt::activeCharacteristic() const
{
return m_activeCharacteristic;
}
void BluetoothGatt::setActiveCharacteristic(const QString &characteristic)
{
if (m_activeCharacteristic != characteristic) {
m_activeCharacteristic = characteristic;
emit activeCharacteristicChanged();
}
}
bb::cascades::DataModel* BluetoothGatt::devicesModel() const
{
return m_devices;
}
bb::cascades::DataModel* BluetoothGatt::servicesModel() const
{
return m_services;
}
bb::cascades::DataModel* BluetoothGatt::characteristicsModel() const
{
return m_characteristics;
}
CharacteristicsEditor* BluetoothGatt::editor() const
{
return m_editor;
}
QString BluetoothGatt::errorMessage() const
{
return m_errorMessage;
}
void BluetoothGatt::setErrorMessage(const QString &errorMessage)
{
m_errorMessage = errorMessage;
emit errorMessageChanged();
}