From: Szymon Janc <szymon.janc@tieto.com>
To: Lukasz Rymanowski <lukasz.rymanowski@tieto.com>
Cc: linux-bluetooth@vger.kernel.org
Subject: Re: [PATCH v2 3/4] android/gatt: Add GATT Connect
Date: Sun, 16 Mar 2014 18:32:25 +0100 [thread overview]
Message-ID: <1484281.rtieurC29v@leonov> (raw)
In-Reply-To: <1394804979-7270-4-git-send-email-lukasz.rymanowski@tieto.com>
Hi,
On Friday 14 of March 2014 14:49:38 Lukasz Rymanowski wrote:
> This patch introduce connect LE device functionality.
>
> There is gatt_device representing remote le device. Each gatt device
> has a list own list of clients as it is possible that more apps
> would like to use same remote device.
>
> Possible connect scenarios:
>
> 1. There is no ACL connection to device:
> Then new dev is put on conn_wait_queue and le scan is enabled.
> Once device is found we do connect it.
>
> Once device is connected then device is moved form conn_wait_queue to
> conn_list and success event is sent to client(s) with conn_id
>
> 2. Device is already connected:
> Then we update client list, reply with success and do send connect event.
>
> 3. For unregisterd clients or uknown conn_id, failed response is sent.
> ---
> android/Android.mk | 4 +
> android/Makefile.am | 3 +
> android/gatt.c | 475
> +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 463
> insertions(+), 19 deletions(-)
>
> diff --git a/android/Android.mk b/android/Android.mk
> index 34e21ea..0e0932d 100644
> --- a/android/Android.mk
> +++ b/android/Android.mk
> @@ -60,9 +60,13 @@ LOCAL_SRC_FILES := \
> bluez/lib/sdp.c \
> bluez/lib/bluetooth.c \
> bluez/lib/hci.c \
> + bluez/lib/uuid.c \
> bluez/btio/btio.c \
> bluez/src/sdp-client.c \
> bluez/profiles/network/bnep.c \
> + bluez/attrib/gattrib.c \
> + bluez/attrib/gatt.c \
> + bluez/attrib/att.c
>
> LOCAL_C_INCLUDES := \
> $(call include-path-for, glib) \
> diff --git a/android/Makefile.am b/android/Makefile.am
> index e7de560..1d0747e 100644
> --- a/android/Makefile.am
> +++ b/android/Makefile.am
> @@ -42,6 +42,9 @@ android_bluetoothd_SOURCES = android/main.c \
> android/handsfree.h android/handsfree.c \
> android/gatt.h android/gatt.c \
> android/health.h android/health.c \
> + attrib/att.c attrib/att.h \
> + attrib/gatt.c attrib/gatt.h \
> + attrib/gattrib.c attrib/gattrib.h \
> btio/btio.h btio/btio.c \
> src/sdp-client.h src/sdp-client.c \
> profiles/network/bnep.h profiles/network/bnep.c
> diff --git a/android/gatt.c b/android/gatt.c
> index 38f7c1b..9afdf4b 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -29,10 +29,13 @@
> #include <stdlib.h>
> #include <stdint.h>
> #include <glib.h>
> +#include <errno.h>
> +#include <sys/socket.h>
>
> #include "ipc.h"
> #include "ipc-common.h"
> #include "lib/sdp.h"
> +#include "lib/uuid.h"
> #include "bluetooth.h"
> #include "gatt.h"
> #include "src/log.h"
> @@ -40,16 +43,38 @@
> #include "utils.h"
> #include "src/shared/util.h"
> #include "src/shared/queue.h"
> +#include "attrib/gattrib.h"
> +#include "attrib/att.h"
> +#include "attrib/gatt.h"
> +#include "btio/btio.h"
>
> struct gatt_client {
> int32_t id;
> uint8_t uuid[16];
> };
>
> +struct gatt_device {
> + bdaddr_t bdaddr;
> + uint8_t bdaddr_type;
> +
> + struct queue *clients;
> +
> + bool connect_ready;
> + int32_t conn_id;
> +
> + GAttrib *attrib;
> + GIOChannel *att_io;
> +
> + guint watch_id;
> +};
> +
> static struct ipc *hal_ipc = NULL;
> static bdaddr_t adapter_addr;
> +
> static struct queue *gatt_clients = NULL;
> static struct queue *scan_clients = NULL;
> +static struct queue *conn_list = NULL; /* Connected devices */
> +static struct queue *conn_wait_queue = NULL; /* Devices waiting for connect
> */
>
> static bool match_client_by_uuid(const void *data, const void *user_data)
> {
> @@ -72,29 +97,25 @@ static bool match_by_value(const void *data, const void
> *user_data) return data == user_data;
> }
>
> -static void le_device_found_handler(bdaddr_t *addr, uint8_t addr_type, int
> rssi, - uint16_t eir_len, const void *eir)
> +static bool match_dev_by_bdaddr(const void *data, const void *user_data)
> {
> - uint8_t buf[IPC_MTU];
> - struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
> - char bda[18];
> + const struct gatt_device *dev = data;
> + const bdaddr_t *addr = user_data;
>
> - if (queue_isempty(scan_clients))
> - return;
> -
> - ba2str(addr, bda);
> - DBG("gatt: LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi,
> - eir ? true : false);
> + return !bacmp(&dev->bdaddr, addr);
> +}
>
> - bdaddr2android(addr, ev->bda);
> - ev->rssi = rssi;
> - ev->len = eir_len;
> +static bool match_dev_connect_ready(const void *data, const void
> *user_data) +{
> + const struct gatt_device *dev = data;
>
> - memcpy(ev->adv_data, eir, ev->len);
> + return dev->connect_ready;
> +}
>
> - ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
> - HAL_EV_GATT_CLIENT_SCAN_RESULT,
> - sizeof(ev) + eir_len, ev);
> +static void destroy_device(struct gatt_device *dev)
> +{
> + queue_destroy(dev->clients, NULL);
> + free(dev);
> }
>
> static void handle_client_register(const void *buf, uint16_t len)
> @@ -172,6 +193,253 @@ failed:
> HAL_OP_GATT_CLIENT_UNREGISTER, status);
> }
>
> +static void connection_cleanup(struct gatt_device *device)
> +{
> + if (device->watch_id) {
> + g_source_remove(device->watch_id);
> + device->watch_id = 0;
> + }
> +
> + if (device->att_io) {
> + g_io_channel_shutdown(device->att_io, FALSE, NULL);
> + g_io_channel_unref(device->att_io);
> + device->att_io = NULL;
> + }
> +
> + if (device->attrib) {
> + GAttrib *attrib = device->attrib;
> + device->attrib = NULL;
> + g_attrib_cancel_all(attrib);
> + g_attrib_unref(attrib);
> + }
> +}
> +
> +static void send_disconnect_notify(void *data, void *user_data)
> +{
> + struct hal_ev_gatt_client_disconnect ev;
> + struct gatt_device *dev = user_data;
> +
> + ev.client_if = PTR_TO_INT(data);
> + ev.conn_id = dev->conn_id;
> + ev.status = HAL_STATUS_SUCCESS;
> + bdaddr2android(&dev->bdaddr, &ev.bda);
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
> + HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
> +}
This should have proper types as parameters and if needed dedicated wrapper
callback for list iteration should be added.
> +
> +static bool is_device_wating_for_connect(const bdaddr_t *addr, uint8_t
> addr_type) +{
> + struct gatt_device *dev;
> +
> + DBG("");
> +
> + dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, (void *)addr);
No need cast to (void *) here.
> + if (!dev)
> + return false;
> +
> + dev->bdaddr_type = addr_type;
> +
> + /* Mark that this device is ready for connect.
> + * Need it because will continue with connect after scan is stopped
> + */
> + dev->connect_ready = true;
> +
> + return true;
> +}
> +
> +static void bt_le_discovery_stop_cb(void);
If this is really needed I'd prefer to have this before any function
definitions at file's top.
> +
> +static void le_device_found_handler(bdaddr_t *addr, uint8_t addr_type,
> + int rssi, uint16_t eir_len,
> + const void *eir)
> +{
> + uint8_t buf[IPC_MTU];
> + struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
> + char bda[18];
> +
> + if (queue_isempty(scan_clients))
> + goto connect;
> +
> + ba2str(addr, bda);
> + DBG("gatt: LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi,
> + !!eir);
> +
> + bdaddr2android(addr, ev->bda);
> + ev->rssi = rssi;
> + ev->len = eir_len;
> +
> + memcpy(ev->adv_data, eir, ev->len);
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
> + HAL_EV_GATT_CLIENT_SCAN_RESULT,
> + sizeof(ev) + eir_len, ev);
Use ev->len here instead of eir_len.
> +
> +connect:
> + if (!is_device_wating_for_connect(addr, addr_type))
> + return;
> +
> + /* We are ok to perform connect now. Stop discovery
> + * and once it is stopped continue with creating ACL
> + */
> + bt_le_discovery_stop(bt_le_discovery_stop_cb);
> +}
> +
> +static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
> + gpointer user_data)
> +{
> + bdaddr_t *addr = user_data;
> + struct gatt_device *dev;
> + int sock, err = 0;
> + socklen_t len;
> +
> + sock = g_io_channel_unix_get_fd(io);
> + len = sizeof(err);
> + getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len);
Should check if getsockopt() succeed. Also this watch should probably check
for G_IO_ERR and G_IO_NVAL as well.
> +
> + DBG("%s (%d)", strerror(err), err);
> +
> + dev = queue_remove_if(conn_list, match_dev_by_bdaddr, addr);
> + connection_cleanup(dev);
> +
> + /* Keep scanning/re-connection active if disconnection reason
> + * is connection timeout, remote user terminated connection or local
> + * initiated disconnection.
> + */
> + if (err == ETIMEDOUT || err == ECONNRESET || err == ECONNABORTED) {
> + if (!queue_push_tail(conn_wait_queue, dev)) {
> + error("gatt: Cannot push data");
> + } else {
> + bt_le_discovery_start(le_device_found_handler);
> + return FALSE;
> + }
> + }
> +
> + queue_foreach(dev->clients, send_disconnect_notify, dev);
> + destroy_device(dev);
> +
> + return FALSE;
> +}
> +
> +static void send_client_connect_notify(void *data, void *user_data)
> +{
> + struct hal_ev_gatt_client_connect *ev = user_data;
> + int32_t id = PTR_TO_INT(data);
> +
> + ev->client_if = id;
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
> + HAL_EV_GATT_CLIENT_CONNECT, sizeof(*ev), ev);
> +
> +}
> +static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
> +{
> + bdaddr_t *addr = user_data;
> + struct gatt_device *dev;
> + struct hal_ev_gatt_client_connect ev;
> + GAttrib *attrib;
> + static uint32_t conn_id = 0;
> + uint8_t status;
> +
> + /* Take device form conn waiting queue */
typo: form->from
> + dev = queue_remove_if(conn_wait_queue, match_dev_by_bdaddr, addr);
> + if (!dev) {
> + error("gatt: Device not on the connect wait queue!?");
> + g_io_channel_shutdown(io, TRUE, NULL);
> + return;
> + }
> +
> + g_io_channel_unref(dev->att_io);
> + dev->att_io = NULL;
> +
> + /* Set address and client id in the event */
> + bdaddr2android(&dev->bdaddr, &ev.bda);
> +
> + if (gerr) {
> + error("gatt: connection failed %s", gerr->message);
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + attrib = g_attrib_new(io);
> + if (!attrib) {
> + error("gatt: unable to create new GAttrib instance");
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + dev->attrib = attrib;
> + dev->watch_id = g_io_add_watch(io, G_IO_HUP, disconnected_cb, dev);
> + dev->conn_id = ++conn_id;
> +
> + /* Move gatt device from connect queue to conn_list */
> + if (!queue_push_tail(conn_list, dev)) {
> + error("gatt: Cannot push dev on conn_list");
> + connection_cleanup(dev);
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + status = HAL_STATUS_SUCCESS;
> + goto reply;
> +
> +reply:
> + ev.conn_id = dev ? conn_id : 0;
I would do dev ? dev->conn_id : 0 for clarity.
> + ev.status = status;
> +
> + queue_foreach(dev->clients, send_client_connect_notify, &ev);
> +
> + /* If connection did not succeed, destroy device */
> + if (status)
> + destroy_device(dev);
> +}
> +
> +static int connect_le(struct gatt_device *dev)
> +{
> + BtIOSecLevel sec_level;
> + GIOChannel *io;
> + GError *gerr = NULL;
> + char addr[18];
> +
> + ba2str(&dev->bdaddr, addr);
> +
> + /* There is one connection attempt going on */
> + if (dev->att_io) {
> + info("gatt: connection to dev %s is ongoing", addr);
> + return -EALREADY;
> + }
> +
> + DBG("Connection attempt to: %s", addr);
> +
> + /*TODO: If we are bonded then we should use higier sec level */
> + sec_level = BT_IO_SEC_LOW;
> +
> + /*
> + * This connection will help us catch any PDUs that comes before
> + * pairing finishes
> + */
> + io = bt_io_connect(connect_cb, dev, NULL, &gerr,
> + BT_IO_OPT_SOURCE_BDADDR,
> + &adapter_addr,
> + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
> + BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
> + BT_IO_OPT_DEST_TYPE, dev->bdaddr_type,
> + BT_IO_OPT_CID, ATT_CID,
> + BT_IO_OPT_SEC_LEVEL, sec_level,
> + BT_IO_OPT_INVALID);
> + if (!io) {
> + error("gatt: Failed bt_io_connect(%s): %s", addr,
> + gerr->message);
> + g_error_free(gerr);
> + return -EIO;
> + }
> +
> + /* Keep this, so we can cancel the connection */
> + dev->att_io = io;
> +
> + return 0;
> +}
> +
> static void handle_client_scan(const void *buf, uint16_t len)
> {
> const struct hal_cmd_gatt_client_scan *cmd = buf;
> @@ -229,12 +497,166 @@ reply:
> status);
> }
>
> +static int connect_next_dev(void)
> +{
> + struct gatt_device *dev;
> +
> + DBG("");
> +
> + if (queue_isempty(conn_wait_queue))
> + return 0;
> +
> + /* Discovery has been stopped because there is connection waiting */
> + dev = queue_find(conn_wait_queue, match_dev_connect_ready, NULL);
> + if (!dev)
> + /* Lets try again. */
> + return -1;
> +
> + dev->connect_ready = false;
> +
> + return connect_le(dev);
> +}
> +
> +static void bt_le_discovery_stop_cb(void)
> +{
> + DBG("");
> +
> + /* Check now if there is any device ready to connect*/
> + if (connect_next_dev() < 0)
> + bt_le_discovery_start(le_device_found_handler);
> +}
> +
> +static struct gatt_device *find_device(bdaddr_t *addr)
> +{
> + struct gatt_device *dev;
> +
> + dev = queue_find(conn_list, match_dev_by_bdaddr, addr);
> + if (dev)
> + return dev;
> +
> + dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, addr);
> + if (dev)
> + return dev;
> +
> + return NULL;
> +}
> +
> static void handle_client_connect(const void *buf, uint16_t len)
> {
> + const struct hal_cmd_gatt_client_connect *cmd = buf;
> + struct gatt_device *dev = NULL;
> + void *l;
> + bdaddr_t addr;
> + uint8_t status;
> + bool send_notify = false;
> +
> DBG("");
>
> + /* Check if client is registered */
> + l = queue_find(gatt_clients, match_client_by_id,
> + INT_TO_PTR(cmd->client_if));
> + if (!l) {
> + error("gatt: Client id %d not found", cmd->client_if);
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + android2bdaddr(&cmd->bdaddr, &addr);
> +
> + /* We do support many clients for one device connection so lets check
> + * If device is connected or in connecting state just update list of
> + * clients
> + */
> + dev = find_device(&addr);
> + if (dev) {
> +
> + status = HAL_STATUS_SUCCESS;
Set status before goto only.
> +
> + /* Remeber to send dummy notification event if we area
> + * connected
> + */
> + if (dev->conn_id)
> + send_notify = true;
> +
> + if (queue_find(dev->clients, match_by_value,
> + INT_TO_PTR(cmd->client_if)))
> + goto reply;
> +
> + /* Store another client */
> + if (!queue_push_tail(dev->clients,
> + INT_TO_PTR(cmd->client_if))) {
> + error("gatt: Cannot push client on gatt device list");
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + goto reply;
> + }
> +
> + /* Lets create new gatt device and put it on conn_wait_queue.
> + * Once it is connected we move it to conn_list
> + */
> + dev = new0(struct gatt_device, 1);
> + if (!dev) {
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + memcpy(&dev->bdaddr, &addr, sizeof(bdaddr_t));
> +
> + /* Create queue to keep list of clients for given device*/
> + dev->clients = queue_new();
> + if (!dev->clients) {
> + error("gatt: Cannot create client queue");
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + /* Update client list of device */
> + if (!queue_push_tail(dev->clients, INT_TO_PTR(cmd->client_if))) {
> + error("gatt: Cannot push client on the client queue!?");
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + /* Start le scan if not started */
> + if (queue_isempty(scan_clients)) {
> + if (!bt_le_discovery_start(le_device_found_handler)) {
> + error("gatt: Could not start scan");
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> + }
> +
> + if (!queue_push_tail(conn_wait_queue, dev)) {
> + error("gatt: Cannot push device on conn_wait_queue");
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + status = HAL_STATUS_SUCCESS;
> + goto reply;
> +
This goto is not needed :)
> +reply:
> ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
> - HAL_STATUS_FAILED);
> + status);
> +
> + /* Send dummy notification since ACL is already up*/
> + if (send_notify) {
> + struct hal_ev_gatt_client_connect ev;
> +
> + ev.conn_id = dev->conn_id;
> + ev.status = HAL_STATUS_SUCCESS;
> + ev.client_if = cmd->client_if;
> + bdaddr2android(&addr, &ev.bda);
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
> + HAL_EV_GATT_CLIENT_CONNECT,
> + sizeof(ev), &ev);
> + }
> +
You are already dereferencing dev in if above.
> + if (status && dev)
> + destroy_device(dev);
> }
>
> static void handle_client_disconnect(const void *buf, uint16_t len)
> @@ -610,6 +1032,18 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t
> *addr)
>
> hal_ipc = ipc;
>
> + conn_list = queue_new();
> + if (!conn_list) {
> + error("gatt: Can not create conn queue");
> + return false;
> + }
> +
> + conn_wait_queue = queue_new();
> + if (!conn_wait_queue) {
> + error("gatt: Can not create conn queue");
> + return false;
> + }
> +
> ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
> G_N_ELEMENTS(cmd_handlers));
>
> @@ -637,4 +1071,7 @@ void bt_gatt_unregister(void)
>
> ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
> hal_ipc = NULL;
> +
> + queue_destroy(conn_list, NULL);
> + queue_destroy(conn_wait_queue, NULL);
Full those lists here for completeness.
> }
--
BR
Szymon Janc
next prev parent reply other threads:[~2014-03-16 17:32 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-03-14 13:49 [PATCH v2 0/4] android:Add initial support for GATT Client Lukasz Rymanowski
2014-03-14 13:49 ` [PATCH v2 1/4] android/bluetooth: Add GATT notifications on LE discovery Lukasz Rymanowski
2014-03-16 16:26 ` Szymon Janc
2014-03-16 22:32 ` Lukasz Rymanowski
2014-03-14 13:49 ` [PATCH v2 2/4] android/gatt: Use Core profile for LE scan Lukasz Rymanowski
2014-03-16 16:26 ` Szymon Janc
2014-03-16 22:33 ` Lukasz Rymanowski
2014-03-14 13:49 ` [PATCH v2 3/4] android/gatt: Add GATT Connect Lukasz Rymanowski
2014-03-16 17:32 ` Szymon Janc [this message]
2014-03-16 22:39 ` Lukasz Rymanowski
2014-03-14 13:49 ` [PATCH v2 4/4] android/gatt: Add disconnect GATT device Lukasz Rymanowski
2014-03-16 17:40 ` Szymon Janc
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1484281.rtieurC29v@leonov \
--to=szymon.janc@tieto.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=lukasz.rymanowski@tieto.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox