#define VINE_GATT_DESC_UUID "fa87c0d0-afac-11de-8a39-0800200c9a66"
#define BT_ADDRESS_LEN 17
+#define BT_GATT_ATT_DATA_LEN_MAX 512
typedef enum {
VINE_GATT_ROLE_UNKNOWN = 0,
size_t len;
//size_t accumulating;
bool last; // true, single message or last fragment of a message.
+ vine_gatt_s *gatt;
} gatt_data_s;
typedef struct {
} role;
VineQueue<gatt_data_s *> *recv_buffer;
+ VineQueue<gatt_data_s *> *write_buffer;
+ bool ready_to_write;
char *remote_address;
void *user; // vine_data_path_h
};
static map<int, struct vine_gatt_s *> g_eventfds;
static vine_gatt_s *_create_gatt(void);
+static int __write_to_client(vine_gatt_s *gatt);
+static int __write_to_server(vine_gatt_s *gatt);
static vine_data_path_error __convert_bt_error_to_data_path_error(int error)
{
}
}
-static gatt_data_s *__create_gatt_data(const char *buf, size_t len, bool last)
+static gatt_data_s *__create_gatt_data(vine_gatt_s *gatt, const char *buf, size_t len, bool last)
{
gatt_data_s *gd = (gatt_data_s *)calloc(1, sizeof(gatt_data_s));
RET_VAL_IF(!gd, NULL, "Out of memory.");
gd->buf = data;
gd->len = len;
gd->last = last;
+ gd->gatt = gatt;
return gd;
}
if (data->buf)
free(data->buf);
+ data->buf = NULL;
+ data->gatt = NULL;
free(data);
}
{
RET_IF(user_data == NULL, "user_data is NULL");
- VINE_LOGI("Got write value requestment from %s.", remote_address);
+ VINE_LOGI("Got write value request from %s.", remote_address);
vine_gatt_s *gatt = (vine_gatt_s *)user_data;
vine_gatt_s *client_gatt = __find_client_gatt(gatt, remote_address);
RET_IF(client_gatt == NULL, "Cannot find a client[%s].", remote_address);
// TODO: Need to handle splited data using offset.
- gatt_data_s *recv_data = __create_gatt_data(value, len, true);
+ gatt_data_s *recv_data = __create_gatt_data(gatt, value, len, true);
RET_IF(recv_data == NULL, "recv_data is NULL");
client_gatt->recv_buffer->push(recv_data);
g_callbacks.received_cb(len, client_gatt->user);
}
+static void __notify_write_event(vine_gatt_s *gatt)
+{
+ g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_OP, gatt->eventfd, POLLOUT, gatt->user);
+
+ uint64_t v = 1;
+ if (write(gatt->eventfd, &v, sizeof(v)) == -1)
+ VINE_LOGE("Write error(%d)", errno);
+}
+
void __gatt_server_noti_state_changed_cb(bool notify, bt_gatt_server_h server,
bt_gatt_h gatt_handle, void *user_data)
{
RET_IF(user_data == NULL, "user_data is NULL.");
vine_gatt_s *gatt = (vine_gatt_s *)user_data;
- if (notify) {
- g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_OP, gatt->eventfd, POLLOUT, gatt->user);
+ auto iter = gatt->role.server->client_list->begin();
+ if (iter == gatt->role.server->client_list->end()) {
+ VINE_LOGE("Cannot find accepted client.");
+ return;
+ }
- uint64_t v = 1;
- if (write(gatt->eventfd, &v, sizeof(v)) == -1)
- VINE_LOGE("Write error(%d)", errno);
+ vine_gatt_s *accepted_client = iter->second;
+ if (!accepted_client) {
+ VINE_LOGE("Cannot find accepted client.");
+ return;
}
+
+ gatt->ready_to_write = notify;
+ accepted_client->ready_to_write = notify;
+
VINE_LOGI("Noti state changed. notify[%d] characteristic[%p]", notify, gatt_handle);
+ if (notify) {
+ // TODO: This will be changed if BT API supports a remote address here.
+ __notify_write_event(accepted_client);
+ }
}
void __gatt_server_noti_sent_cb(int result,
VINE_LOGI("Send data to %s is %s. server[%p] completed[%d]",
remote_address, result == 0 ? "success" : "failed", server, completed);
- vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+ gatt_data_s *data = (gatt_data_s *)user_data;
+ if (!data->gatt)
+ return;
+
+ vine_gatt_s *gatt = data->gatt;
g_callbacks.written_cb(1, gatt->user); // bytes will be ignored.
+ __destroy_gatt_data(data);
+
+ gatt->ready_to_write = true;
+ __notify_write_event(gatt);
}
// Client Callbacks
+static void __gatt_client_mtu_changed_cb(bt_gatt_client_h client,
+ const bt_gatt_client_att_mtu_info_s *mtu_info, void *user_data)
+{
+ VINE_LOGI("MTU is changed. %u", mtu_info->mtu);
+}
+
static void __gatt_client_service_changed_cb(bt_gatt_client_h client,
bt_gatt_client_service_change_type_e type, const char *uuid, void *user_data)
{
VINE_LOGI("gatt[%p] completed to send data. result[%d]", gatt_handle, result);
- vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+ gatt_data_s *data = (gatt_data_s *)user_data;
+ if (!data->gatt)
+ return;
+
+ vine_gatt_s *gatt = data->gatt;
+
g_callbacks.written_cb(1, gatt->user); // bytes will be ignored.
+ __destroy_gatt_data(data);
+
+ gatt->ready_to_write = true;
+ __notify_write_event(gatt);
}
void __gatt_client_characteristic_value_changed_cb(bt_gatt_h characteristic,
char *value, int len, void *user_data)
{
+ VINE_LOGD("+");
RET_IF(user_data == NULL, "user_data is NULL");
vine_gatt_s *gatt = (vine_gatt_s *)user_data;
- gatt_data_s *recv_data = __create_gatt_data(value, len, true);
+ gatt_data_s *recv_data =__create_gatt_data(gatt, value, len, true);
RET_IF(recv_data == NULL, "recv_data is NULL");
gatt->recv_buffer->push(recv_data);
if (ret != BT_ERROR_NONE || !client)
return __convert_bt_error_to_data_path_error(ret);
+ ret = bt_gatt_client_set_att_mtu_changed_cb(client, __gatt_client_mtu_changed_cb, gatt);
+ if (ret != BT_ERROR_NONE)
+ goto ERR;
+
+ ret = bt_gatt_client_request_att_mtu_change(client, BT_GATT_ATT_DATA_LEN_MAX);
+ if (ret != BT_ERROR_NONE)
+ goto ERR;
+
ret = bt_gatt_client_set_service_changed_cb(client, __gatt_client_service_changed_cb, gatt);
if (ret != BT_ERROR_NONE)
goto ERR;
RET_IF(accepted_gatt == NULL, "Failed to create accepted gatt.");
gatt->role.server->client_list->insert(std::make_pair(string(address), accepted_gatt));
- __invoke_accepted_cb(accepted_gatt, accepted_gatt->user);
VINE_LOGI("%s is connected.", address);
+ __invoke_accepted_cb(accepted_gatt, accepted_gatt->user);
}
static void __handle_disconnected_client(vine_gatt_s *gatt, const char *address)
bt_gatt_disconnect(remote_address);
result = -1;
}
+ gatt->ready_to_write = true;
__invoke_connected_cb(result, gatt->user);
}
}
vine_gatt_s *gatt = (vine_gatt_s *)calloc(1, sizeof(vine_gatt_s));
RET_VAL_IF(gatt == NULL, NULL, "Out of memory");
+ int fd = eventfd(0, 0);
+ if (fd < 0) {
+ VINE_LOGE("cannot get fd for handling event. errno: %d", errno);
+ free(gatt);
+ return NULL;
+ }
+
if (bt_gatt_set_connection_state_changed_cb(__gatt_connection_state_changed_cb,
(void *)gatt) != BT_ERROR_NONE) {
VINE_LOGE("Failed to register GATT connection state changed callback");
return NULL;
}
- gatt->eventfd = eventfd(0, 0);
+ gatt->eventfd = fd;
+ g_eventfds[gatt->eventfd] = gatt;
gatt->recv_buffer = new VineQueue<gatt_data_s *>;
+ gatt->write_buffer = new VineQueue<gatt_data_s *>;
return gatt;
}
char initial_value[1] = {'0'}; // TODO: will be changed.
int permissions = BT_GATT_PERMISSION_READ | BT_GATT_PERMISSION_WRITE;
int properties = BT_GATT_PROPERTY_BROADCAST | BT_GATT_PROPERTY_READ |
- BT_GATT_PROPERTY_WRITE | BT_GATT_PROPERTY_NOTIFY;
+ BT_GATT_PROPERTY_WRITE
+ | BT_GATT_PROPERTY_INDICATE;
if (bt_gatt_characteristic_create(VINE_GATT_CHAR_UUID,
permissions, properties,
return descriptor;
}
-static int _gatt_write_to_server(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+static gatt_data_s *__get_pending_write_data(vine_gatt_s *gatt)
{
- int ret = bt_gatt_set_value(gatt->characteristic, (const char *)buf, len);
+ if (gatt->write_buffer->empty()) {
+ VINE_LOGI("gatt[%p] write_buffer is empty.", gatt);
+ return NULL;
+ }
+
+ gatt_data_s *data = NULL;
+
+ do {
+ data = gatt->write_buffer->front();
+ gatt->write_buffer->pop();
+ } while(data == nullptr); // TODO: handle infinite loop.
+
+ return data;
+}
+
+static int __write_to_server(vine_gatt_s *gatt)
+{
+ RET_VAL_IF(!gatt->ready_to_write, VINE_DATA_PATH_ERROR_NONE,
+ "Cannot write yet. The request will be operated later.");
+
+ gatt_data_s *data = __get_pending_write_data(gatt);
+ RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_NO_DATA, "There is no pending data.");
+
+ int ret = bt_gatt_set_value(gatt->characteristic, (const char *)data->buf, data->len);
RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
"bt_gatt_set_value() failed.");
ret = bt_gatt_client_write_value(gatt->characteristic,
- __gatt_client_write_complete_cb, (void *)gatt);
+ __gatt_client_write_complete_cb, (void *)data);
RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
"bt_gatt_client_write_value() failed.");
+ gatt->ready_to_write = false;
+
VINE_LOGI("Succeed to send data to server");
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __push_write_data(vine_gatt_s *gatt, char *buf, size_t len)
+{
+
+ gatt_data_s *data = __create_gatt_data(gatt, buf, len, true);
+ RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_OUT_OF_MEMORY, "out of memory.");
+
+ gatt->write_buffer->push(data);
+ VINE_LOGI("gatt[%p] data[%p]", gatt, data);
return VINE_DATA_PATH_ERROR_NONE;
}
-static int _gatt_write_to_client(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+static int _gatt_write_to_server(vine_gatt_s *gatt, unsigned char *buf, size_t len)
{
- int ret = bt_gatt_set_value(gatt->characteristic, (const char *)buf, len);
+ int ret = __push_write_data(gatt, (char *)buf, len);
+ if (ret != VINE_DATA_PATH_ERROR_NONE)
+ return ret;
+
+ ret = __write_to_server(gatt);
+ if (ret != VINE_DATA_PATH_ERROR_NONE)
+ return ret;
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __write_to_client(vine_gatt_s *gatt)
+{
+ RET_VAL_IF(!gatt->ready_to_write, VINE_DATA_PATH_ERROR_NONE,
+ "Cannot write yet. The request will be operated later.");
+
+ gatt_data_s *data = __get_pending_write_data(gatt);
+ RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_NO_DATA, "There is no pending data.");
+
+ int ret = bt_gatt_set_value(gatt->characteristic, (const char *)data->buf, data->len);
RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
"bt_gatt_set_value() failed.");
ret = bt_gatt_server_notify_characteristic_changed_value(gatt->characteristic,
- __gatt_server_noti_sent_cb, gatt->remote_address, (void *)gatt);
+ __gatt_server_noti_sent_cb, gatt->remote_address, (void *)data);
RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
"bt_gatt_server_notify_characteristic_changed_value() failed.");
- VINE_LOGI("Succeed to send data to %s", gatt->remote_address ? gatt->remote_address : "all");
+ gatt->ready_to_write = false;
+
+ VINE_LOGI("Succeed to send data to %s",
+ gatt->remote_address ? gatt->remote_address : "all");
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int _gatt_write_to_client(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+{
+ int ret = __push_write_data(gatt, (char *)buf, len);
+ if (ret != VINE_DATA_PATH_ERROR_NONE)
+ return ret;
+
+ ret = __write_to_client(gatt);
+ if (ret != VINE_DATA_PATH_ERROR_NONE)
+ return ret;
return VINE_DATA_PATH_ERROR_NONE;
}
// TODO: handle pending write events.
vine_gatt_s *gatt = it->second;
VINE_LOGI("found gatt[%p]", gatt);
+
+ if (gatt->type == VINE_GATT_ROLE_SERVER
+ || (gatt->type == VINE_GATT_ROLE_CLIENT
+ && gatt->role.client && gatt->role.client->is_accepted))
+ __write_to_client(gatt);
+ else if (gatt->type == VINE_GATT_ROLE_CLIENT)
+ __write_to_server(gatt);
}
int gatt_create(vine_dp_plugin_h *handle, void *plugin_data, void *user)
{
RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
RET_VAL_IF(buf == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "buf is NULL");
- RET_VAL_IF(len <= 0, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "len is less than 0");
+ RET_VAL_IF(len <= 0 || len >= BT_GATT_ATT_DATA_LEN_MAX,
+ VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "invalid length.");
vine_gatt_s *gatt = (vine_gatt_s *)handle;
int ret;