From: Tyszkowski Jakub <jakub.tyszkowski@tieto.com>
To: Andrzej Kaczmarek <andrzej.kaczmarek@tieto.com>
Cc: linux-bluetooth <linux-bluetooth@vger.kernel.org>
Subject: Re: [PATCH] android/gatt: Refactor client connection handling
Date: Thu, 24 Apr 2014 12:57:49 +0200 [thread overview]
Message-ID: <5358EE2D.5090807@tieto.com> (raw)
In-Reply-To: <CAF3PWx1E4DscULb_y-_c_xzGwuywKppX7+UxJ290r4Ekto0bHA@mail.gmail.com>
Hi, Andrzej
On 04/23/2014 07:35 PM, Andrzej Kaczmarek wrote:
> Hi Jakub,
>
> On 23 April 2014 14:26, Jakub Tyszkowski <jakub.tyszkowski@tieto.com> wrote:
>>
>> Multiple, connection specific device lists were replaced with one,
>> while devices store their connection state instead. Client list in each
>> device were replaced with single, global list, storing (connection_id,
>> client*, device*) tuples. This seams to be more natural and easier to
>> perform actions which are mostly triggered by specific clients on
>> specific device or connection.
>>
>> Connection id previously assigned to device now properly identifies
>> client<->device and not the physical adapter<->device connection.
>> ---
>> android/gatt.c | 957 +++++++++++++++++++++++++++++----------------------------
>> 1 file changed, 495 insertions(+), 462 deletions(-)
>>
>> diff --git a/android/gatt.c b/android/gatt.c
>> index 8a6bc12..55f4f97 100644
>> --- a/android/gatt.c
>> +++ b/android/gatt.c
>> @@ -54,6 +54,20 @@
>> #define GATT_SUCCESS 0x00000000
>> #define GATT_FAILURE 0x00000101
>>
>> +typedef enum {
>> + DEVICE_DISCONNECTED = 0,
>> + DEVICE_CONNECTION_PENDING,
>> + DEVICE_CONNECTABLE,
>> + DEVICE_CONNECTED,
>> +} gatt_device_t;
>
> I'd call it device_state.
>
Ups, I've renamed it by accident. Will fix that.
>> +static char *device_str[] = {
>> + "DISCONNECTED",
>> + "CONNECTION PENDING",
>> + "CONNECTABLE",
>> + "CONNECTED",
>> +};
>
> device_state_str here and make it const.
>
Renamed by accident as above. I'll make it const
>> struct gatt_client {
>> int32_t id;
>> uint8_t uuid[16];
>> @@ -98,8 +112,7 @@ struct service {
>> struct notification_data {
>> struct hal_gatt_srvc_id service;
>> struct hal_gatt_gatt_id ch;
>> - struct gatt_client *client;
>> - struct gatt_device *dev;
>> + struct connection_record *connection;
>> guint notif_id;
>> guint ind_id;
>> int ref;
>> @@ -109,16 +122,21 @@ struct gatt_device {
>> bdaddr_t bdaddr;
>> uint8_t bdaddr_type;
>>
>> - struct queue *clients;
>> -
>> - bool connect_ready;
>> - int32_t conn_id;
>> + gatt_device_t state;
>>
>> GAttrib *attrib;
>> GIOChannel *att_io;
>> struct queue *services;
>>
>> guint watch_id;
>> +
>> + uint32_t connection_ref_count;
>
> Why not just e.g. 'conn_ref'? You won't have loooooong lines later.
>
Yeap. Why not :)
>> +};
>> +
>> +struct connection_record {
>> + struct gatt_device *device;
>> + struct gatt_client *client;
>> + int32_t id;
>> };
>
> connection should be enough (you actually use it like this in function
> names anyway)
>
OK.
>> static struct ipc *hal_ipc = NULL;
>> @@ -127,9 +145,8 @@ static bool scanning = false;
>>
>> static struct queue *gatt_clients = NULL;
>> static struct queue *gatt_servers = NULL;
>> -static struct queue *conn_list = NULL; /* Connected devices */
>> -static struct queue *conn_wait_queue = NULL; /* Devs waiting to connect */
>> -static struct queue *disc_dev_list = NULL; /* Disconnected devices */
>> +static struct queue *gatt_devices = NULL;
>
> And this one I'd call just devices unless there's other list of
> non-gatt devices to be added.
>
Just wanted to keep the gatt_clients, gatt_server convention, but yes,
this could be changed.
>> +static struct queue *client_connections = NULL;
>>
>> static struct queue *listen_clients = NULL;
>>
>> @@ -276,26 +293,76 @@ static bool match_dev_by_bdaddr(const void *data, const void *user_data)
>> return !bacmp(&dev->bdaddr, addr);
>> }
>>
>> -static bool match_dev_connect_ready(const void *data, const void *user_data)
>> +static bool match_dev_by_state(const void *data, const void *user_data)
>
> You have other functions called e.g.
> 'match_connection_by_device_and_client' so would be good to have this
> consistent - either dev there or device here.
>
I'd go with "device", like "find_device..." functions.
>> {
>> const struct gatt_device *dev = data;
>>
>> - return dev->connect_ready;
>> + if (dev->state != PTR_TO_UINT(user_data))
>> + return false;
>> +
>> + return true;
>> }
>>
>> -static bool match_dev_by_conn_id(const void *data, const void *user_data)
>> +static bool match_connection_by_id(const void *data, const void *user_data)
>> {
>> - const struct gatt_device *dev = data;
>> - const int32_t conn_id = PTR_TO_INT(user_data);
>> + const struct connection_record *record = data;
>
> Same here, later in code you use 'conn' or 'connection' as local
> variable for this so as long as it's possible, let's make it
> consistent. I'd make it 'connection' for function arguments and 'conn'
> for local variables to avoid conflicts.
>
Sure.
>> + const int32_t id = PTR_TO_INT(user_data);
>>
>> - return dev->conn_id == conn_id;
>> + return record->id == id;
>> }
>>
>> -static bool match_dev_without_client(const void *data, const void *user_data)
>> +static bool match_connection_by_device_and_client(const void *data,
>> + const void *user_data)
>> {
>> - const struct gatt_device *dev = data;
>> + const struct connection_record *record = data;
>> + const struct connection_record *match = user_data;
>>
>> - return queue_isempty(dev->clients);
>> + if (record->device == match->device)
>> + return record->client == match->client;
>> +
>> + return false;
>
> return (record->device == match->device && record->client == match->client);
>
OK.
>> +}
>> +
>> +static struct connection_record *find_connection_by_id(int32_t conn_id)
>> +{
>> + return queue_find(client_connections, match_connection_by_id,
>> + INT_TO_PTR(conn_id));
>> +}
>> +
>> +static bool match_connection_by_device(const void *data, const void *user_data)
>> +{
>> + const struct connection_record *record = data;
>> + const struct gatt_device *dev = user_data;
>> +
>> + return record->device == dev;
>> +}
>> +
>> +static bool match_connection_by_client(const void *data, const void *user_data)
>> +{
>> + const struct connection_record *record = data;
>> + const struct gatt_client *client = user_data;
>> +
>> + return record->client == client;
>> +}
>> +
>> +static struct gatt_device *find_device(struct gatt_device *dev)
>> +{
>> + return queue_find(gatt_devices, match_by_value, dev);
>> +}
>> +
>> +static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
>> +{
>> + return queue_find(gatt_devices, match_dev_by_bdaddr, (void *)addr);
>> +}
>> +
>> +static struct gatt_device *find_device_by_state(uint32_t state)
>> +{
>> + struct gatt_device *dev;
>> +
>> + dev = queue_find(gatt_devices, match_dev_by_state,
>> + UINT_TO_PTR(state));
>> +
>> + return dev;
>
> You have other functions above which return directly from queue_find,
> why this one can't?
>
Well... it should. I'll fix it.
>> }
>>
>> static bool match_srvc_by_element_id(const void *data, const void *user_data)
>> @@ -355,7 +422,7 @@ static bool match_notification(const void *a, const void *b)
>> const struct notification_data *a1 = a;
>> const struct notification_data *b1 = b;
>>
>> - if (bacmp(&a1->dev->bdaddr, &b1->dev->bdaddr))
>> + if (a1->connection != b1->connection)
>> return false;
>>
>> if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch)))
>> @@ -381,11 +448,13 @@ static bool match_char_by_element_id(const void *data, const void *user_data)
>> static void destroy_notification(void *data)
>> {
>> struct notification_data *notification = data;
>> + struct gatt_client *client;
>>
>> if (--notification->ref)
>> return;
>>
>> - queue_remove_if(notification->client->notifications, match_notification,
>> + client = notification->connection->client;
>> + queue_remove_if(client->notifications, match_notification,
>> notification);
>> free(notification);
>> }
>> @@ -393,14 +462,31 @@ static void destroy_notification(void *data)
>> static void unregister_notification(void *data)
>> {
>> struct notification_data *notification = data;
>> + struct gatt_device *dev = notification->connection->device;
>>
>> - if (notification->notif_id)
>> - g_attrib_unregister(notification->dev->attrib,
>> - notification->notif_id);
>> + /* No device means it was already disconnected and client cleanup was
>> + * triggered afterwards, but once client unregisters, device stays if
>> + * used by others. Then just unregister single handle.
>> + */
>> + if (!queue_find(gatt_devices, match_by_value, dev))
>> + return;
>> +
>> + if (notification->notif_id && dev)
>> + g_attrib_unregister(dev->attrib, notification->notif_id);
>> +
>> + if (notification->ind_id && dev)
>> + g_attrib_unregister(dev->attrib, notification->ind_id);
>> +}
>> +
>> +static void device_set_state(struct gatt_device *dev, uint32_t state)
>> +{
>> + char bda[18];
>> +
>> + ba2str(&dev->bdaddr, bda);
>> + DBG("gatt: Device %s state changed %s -> %s", bda,
>> + device_str[dev->state], device_str[state]);
>>
>> - if (notification->ind_id)
>> - g_attrib_unregister(notification->dev->attrib,
>> - notification->ind_id);
>> + dev->state = state;
>> }
>>
>> static void connection_cleanup(struct gatt_device *device)
>> @@ -422,21 +508,18 @@ static void connection_cleanup(struct gatt_device *device)
>> g_attrib_cancel_all(attrib);
>> g_attrib_unref(attrib);
>> }
>> -}
>> -
>> -static void destroy_device(void *data)
>> -{
>> - struct gatt_device *dev = data;
>>
>> - if (!dev)
>> - return;
>> -
>> - if (dev->conn_id)
>> - connection_cleanup(dev);
>> + /* If device was in connection_pending or connectable state we
>> + * search device list if we should stop the scan.
>> + */
>> + if (device->state == DEVICE_CONNECTION_PENDING ||
>> + device->state == DEVICE_CONNECTABLE) {
>> + if (!find_device_by_state(DEVICE_CONNECTION_PENDING) &&
>> + !find_device_by_state(DEVICE_CONNECTABLE) && !scanning)
>
> Check bool first - no need to iterate list twice in case scanning==false anyway.
> And perhaps there should be some helper to check if there's device in
> either state so we don't need to iterate list twice - this can be done
> in single pass.
>
I'll add helper function for that.
>> + bt_le_discovery_stop(NULL);
>> + }
>>
>> - queue_destroy(dev->clients, NULL);
>> - queue_destroy(dev->services, destroy_service);
>> - free(dev);
>> + device_set_state(device, DEVICE_DISCONNECTED);
>> }
>>
>> static void destroy_gatt_client(void *data)
>> @@ -531,123 +614,89 @@ failed:
>> status);
>> }
>>
>> -static void send_client_disconnect_notify(int32_t id, struct gatt_device *dev,
>> - int32_t status)
>> +static void send_client_connection_notify(struct connection_record *conn,
>> + int32_t gatt_event,
>> + int32_t status)
>> {
>> struct hal_ev_gatt_client_disconnect ev;
>>
>> - ev.client_if = id;
>> - ev.conn_id = dev->conn_id;
>> + ev.client_if = conn->client->id;
>> + ev.conn_id = conn->id;
>> ev.status = status;
>> - bdaddr2android(&dev->bdaddr, &ev.bda);
>>
>> - ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
>> - HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
>> -}
>> + bdaddr2android(&conn->device->bdaddr, &ev.bda);
>>
>> -static void send_client_connect_notify(int32_t id, struct gatt_device *dev,
>> - int32_t status)
>> -{
>> - struct hal_ev_gatt_client_connect ev;
>> -
>> - ev.client_if = id;
>> - ev.status = status;
>> - bdaddr2android(&dev->bdaddr, &ev.bda);
>> - ev.conn_id = status == GATT_SUCCESS ? dev->conn_id : 0;
>> -
>> - ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
>> - HAL_EV_GATT_CLIENT_CONNECT, sizeof(ev), &ev);
>> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, gatt_event, sizeof(ev),
>> + &ev);
>> }
>>
>> -static void remove_client_from_device_list(void *data, void *user_data)
>> +static void send_client_disconnect_notify(struct connection_record *conn,
>> + int32_t status)
>> {
>> - struct gatt_device *dev = data;
>> -
>> - queue_remove_if(dev->clients, match_by_value, user_data);
>> + send_client_connection_notify(conn, HAL_EV_GATT_CLIENT_DISCONNECT,
>> + status);
>> }
>>
>> -static void put_device_on_disc_list(struct gatt_device *dev)
>> +static void send_client_connect_notify(struct connection_record *conn,
>> + int32_t status)
>> {
>> - dev->conn_id = 0;
>> - queue_remove_all(dev->clients, NULL, NULL, NULL);
>> - queue_push_tail(disc_dev_list, dev);
>> + send_client_connection_notify(conn, HAL_EV_GATT_CLIENT_CONNECT, status);
>> }
>>
>> -static void cleanup_conn_list(int32_t id)
>> +static void disconnect_notify_by_dev(void *data, void *user_data)
>> {
>> - struct gatt_device *dev;
>> -
>> - /* Find device without client */
>> - dev = queue_remove_if(conn_list, match_dev_without_client, NULL);
>> - while (dev) {
>> - /* Device is connected, lets disconnect and notify client */
>> - connection_cleanup(dev);
>> - send_client_disconnect_notify(id, dev, GATT_SUCCESS);
>> + struct connection_record *record = data;
>> + struct gatt_device *dev = user_data;
>>
>> - /* Put device on disconnected device list */
>> - put_device_on_disc_list(dev);
>> - dev = queue_remove_if(conn_list, match_dev_without_client,
>> - NULL);
>> - };
>> + if (dev == record->device) {
>
> if (dev != record->device)
> return;
>
Got it.
>> + if (dev->state == DEVICE_CONNECTED)
>> + send_client_disconnect_notify(record, GATT_SUCCESS);
>> + else if (dev->state == DEVICE_CONNECTION_PENDING ||
>> + dev->state == DEVICE_CONNECTABLE)
>> + send_client_connect_notify(record, GATT_FAILURE);
>> + }
>> }
>>
>> -static void cleanup_conn_wait_queue(int32_t id)
>> +static void destroy_connection(void *data)
>> {
>> - struct gatt_device *dev;
>> + struct connection_record *conn = data;
>>
>> - /* Find device without client */
>> - dev = queue_remove_if(conn_wait_queue, match_dev_without_client, NULL);
>> - while (dev) {
>> - send_client_connect_notify(id, dev, GATT_FAILURE);
>> + if (!queue_find(gatt_devices, match_by_value, conn->device))
>> + goto cleanup;
>>
>> - /* Put device on disconnected device list */
>> - put_device_on_disc_list(dev);
>> - dev = queue_remove_if(conn_wait_queue,
>> - match_dev_without_client, NULL);
>> - };
>> + if (conn->device->connection_ref_count > 0)
>> + conn->device->connection_ref_count--;
>
> As long as reference counting works fine you do not need to check >0
> since it should not be possible to have connection for device without
> +1 reference.
> This actually hides potential issue in reference counting.
>
That's true. Will change that.
>> - /* Stop scan we had for connecting devices */
>> - if (queue_isempty(conn_wait_queue) && !scanning)
>> - bt_le_discovery_stop(NULL);
>> + if (conn->device->connection_ref_count == 0)
>> + connection_cleanup(conn->device);
>> +
>> +cleanup:
>> + free(conn);
>> }
>>
>> -static void remove_client_from_devices(int32_t id)
>> +static void device_disconnect_clients(struct gatt_device *dev)
>> {
>> - DBG("");
>> + /* Notify disconnection to all clients */
>> + queue_foreach(client_connections, disconnect_notify_by_dev, dev);
>>
>> - queue_foreach(conn_list, remove_client_from_device_list,
>> - INT_TO_PTR(id));
>> - cleanup_conn_list(id);
>> -
>> - queue_foreach(conn_wait_queue, remove_client_from_device_list,
>> - INT_TO_PTR(id));
>> - cleanup_conn_wait_queue(id);
>> + /* Remove all clients by given device's */
>> + queue_remove_all(client_connections, match_connection_by_device, dev,
>> + destroy_connection);
>> }
>>
>> -static void handle_client_unregister(const void *buf, uint16_t len)
>> +static void destroy_device(void *data)
>> {
>> - const struct hal_cmd_gatt_client_unregister *cmd = buf;
>> - uint8_t status;
>> - struct gatt_client *cl;
>> + struct gatt_device *dev = data;
>>
>> - DBG("");
>> + if (!dev)
>> + return;
>>
>> - cl = queue_remove_if(gatt_clients, match_client_by_id,
>> - INT_TO_PTR(cmd->client_if));
>> - if (!cl) {
>> - error("gatt: client_if=%d not found", cmd->client_if);
>> - status = HAL_STATUS_FAILED;
>> - } else {
>> - /* Check if there is any connect request or connected device for this
>> - * client. If so, remove this client from those lists.
>> - */
>> - remove_client_from_devices(cl->id);
>> - destroy_gatt_client(cl);
>> - status = HAL_STATUS_SUCCESS;
>> - }
>> + if (dev->state != DEVICE_DISCONNECTED)
>> + device_disconnect_clients(dev);
>>
>> - ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
>> - HAL_OP_GATT_CLIENT_UNREGISTER, status);
>> + queue_destroy(dev->services, destroy_service);
>> +
>> + free(dev);
>> }
>>
>> static void send_client_primary_notify(void *data, void *user_data)
>> @@ -670,17 +719,18 @@ static void send_client_primary_notify(void *data, void *user_data)
>> sizeof(ev), &ev);
>> }
>>
>> -static void send_client_all_primary(int32_t status, struct queue *services,
>> - int32_t conn_id)
>> +static void send_client_all_primary(struct connection_record *conn,
>> + int32_t status)
>> {
>> struct hal_ev_gatt_client_search_complete ev;
>>
>> if (!status)
>> - queue_foreach(services, send_client_primary_notify,
>> - INT_TO_PTR(conn_id));
>> + queue_foreach(conn->device->services,
>> + send_client_primary_notify,
>> + INT_TO_PTR(conn->id));
>>
>> ev.status = status;
>> - ev.conn_id = conn_id;
>> + ev.conn_id = conn->id;
>> ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
>> HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
>>
>> @@ -735,7 +785,7 @@ static struct service *create_service(uint8_t id, bool primary, char *uuid,
>>
>> static void primary_cb(uint8_t status, GSList *services, void *user_data)
>> {
>> - struct gatt_device *dev = user_data;
>> + struct connection_record *conn = user_data;
>> GSList *l;
>> int32_t gatt_status;
>> uint8_t instance_id;
>> @@ -755,7 +805,7 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
>> goto done;
>> }
>>
>> - if (!queue_isempty(dev->services)) {
>> + if (!queue_isempty(conn->device->services)) {
>> info("gatt: Services already cached");
>> gatt_status = GATT_SUCCESS;
>> goto done;
>> @@ -774,7 +824,7 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
>> if (!p)
>> continue;
>>
>> - if (!queue_push_tail(dev->services, p)) {
>> + if (!queue_push_tail(conn->device->services, p)) {
>> error("gatt: Cannot push primary service to the list");
>> free(p);
>> continue;
>> @@ -787,36 +837,7 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
>> gatt_status = GATT_SUCCESS;
>>
>> done:
>> - send_client_all_primary(gatt_status, dev->services, dev->conn_id);
>> -}
>> -
>> -static void client_disconnect_notify(void *data, void *user_data)
>> -{
>> - struct gatt_device *dev = user_data;
>> - int32_t id = PTR_TO_INT(data);
>> -
>> - send_client_disconnect_notify(id, dev, GATT_SUCCESS);
>> -}
>> -
>> -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);
>> - 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;
>> + send_client_all_primary(conn, gatt_status);
>> }
>>
>> static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
>> @@ -826,6 +847,7 @@ static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
>> {
>> uint8_t buf[IPC_MTU];
>> struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
>> + struct gatt_device *dev;
>> char bda[18];
>>
>> if (!scanning || !discoverable)
>> @@ -845,9 +867,13 @@ static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
>> sizeof(*ev) + ev->len, ev);
>>
>> connect:
>> - if (!is_device_wating_for_connect(addr, addr_type))
>> + dev = find_device_by_addr(addr);
>> + if (!dev || (dev->state != DEVICE_CONNECTION_PENDING))
>> return;
>>
>> + device_set_state(dev, DEVICE_CONNECTABLE);
>> + dev->bdaddr_type = addr_type;
>> +
>> /* We are ok to perform connect now. Stop discovery
>> * and once it is stopped continue with creating ACL
>> */
>> @@ -861,45 +887,36 @@ static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
>> int sock, err = 0;
>> socklen_t len;
>>
>> - queue_remove(conn_list, dev);
>> -
>> sock = g_io_channel_unix_get_fd(io);
>> len = sizeof(err);
>> if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
>> DBG("%s (%d)", strerror(err), err);
>>
>> - connection_cleanup(dev);
>> -
>> - queue_foreach(dev->clients, client_disconnect_notify, dev);
>> -
>> - /* Reset conn_id and put on disconnected list. */
>> - put_device_on_disc_list(dev);
>> + device_disconnect_clients(dev);
>>
>> return FALSE;
>> }
>>
>> -struct connect_data {
>> - struct gatt_device *dev;
>> - int32_t status;
>> -};
>> -
>> static void send_client_connect_notifications(void *data, void *user_data)
>> {
>> - int32_t id = PTR_TO_INT(data);
>> - struct connect_data *c = user_data;
>> + struct connection_record *rec = data;
>> + uint32_t status = PTR_TO_UINT(user_data);
>>
>> - send_client_connect_notify(id, c->dev, c->status);
>> + send_client_connect_notify(rec, status);
>> }
>>
>> static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
>> {
>> struct gatt_device *dev = user_data;
>> - struct connect_data data;
>> + uint32_t status;
>> GAttrib *attrib;
>> - static uint32_t conn_id = 0;
>>
>> - /* Take device from conn waiting queue */
>> - if (!queue_remove(conn_wait_queue, dev)) {
>> + /* Look for device if connection request is valid.
>> + * TODO: Is this even possible when we have dev->att_io check
>> + * in connect_le()?
>> + */
>> + dev = find_device(dev);
>> + if (!dev || dev->state != DEVICE_CONNECTABLE) {
>> error("gatt: Device not on the connect wait queue!?");
>> g_io_channel_shutdown(io, TRUE, NULL);
>> return;
>> @@ -910,38 +927,31 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
>>
>> if (gerr) {
>> error("gatt: connection failed %s", gerr->message);
>> - data.status = GATT_FAILURE;
>> + status = GATT_FAILURE;
>> goto reply;
>> }
>>
>> attrib = g_attrib_new(io);
>> if (!attrib) {
>> error("gatt: unable to create new GAttrib instance");
>> - data.status = GATT_FAILURE;
>> + status = GATT_FAILURE;
>> goto reply;
>> }
>>
>> dev->attrib = attrib;
>> dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
>> 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);
>> - data.status = GATT_FAILURE;
>> - goto reply;
>> - }
>> + device_set_state(dev, DEVICE_CONNECTED);
>>
>> - data.status = GATT_SUCCESS;
>> + status = GATT_SUCCESS;
>>
>> reply:
>> - data.dev = dev;
>> - queue_foreach(dev->clients, send_client_connect_notifications, &data);
>> + queue_foreach(client_connections, send_client_connect_notifications,
>> + UINT_TO_PTR(status));
>>
>> /* If connection did not succeed, destroy device */
>> - if (data.status)
>> + if (status)
>> destroy_device(dev);
>>
>> /* Check if we should restart scan */
>> @@ -1050,16 +1060,9 @@ static int connect_next_dev(void)
>>
>> 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);
>> + dev = find_device_by_state(DEVICE_CONNECTABLE);
>> if (!dev)
>> - /* Lets try again. */
>> - return -1;
>> -
>> - dev->connect_ready = false;
>> + return -ENODEV;
>>
>> return connect_le(dev);
>
> There should be separate CONNECTING state for device which is
> currently pending connection (actual one, not
> scanning-to-be-connected). Once there's connection request, we should
> not start scanning if there's device pending connection, just leave it
> in CONNECTION_PENDING state and then restart scanning in connect_cb in
> case there's any device in such state. This covers scenario where
> device was advertising and just stopped before we try to make actual
> connection - for consecutive connection request we'll try to start
> scanning but it won't work since there's connection request still
> pending.
Ok, It sounds reasonable to have such state.
>
>> }
>> @@ -1073,22 +1076,6 @@ static void bt_le_discovery_stop_cb(void)
>> 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;
>> -
>> - return queue_find(conn_wait_queue, match_dev_by_bdaddr, addr);
>> -}
>> -
>> -static struct gatt_device *find_device_by_conn_id(int32_t conn_id)
>> -{
>> - return queue_find(conn_list, match_dev_by_conn_id, INT_TO_PTR(conn_id));
>> -}
>> -
>> static struct gatt_device *create_device(bdaddr_t *addr)
>> {
>> struct gatt_device *dev;
>> @@ -1099,96 +1086,202 @@ static struct gatt_device *create_device(bdaddr_t *addr)
>>
>> bacpy(&dev->bdaddr, addr);
>>
>> - dev->clients = queue_new();
>> dev->services = queue_new();
>> -
>> - if (!dev->clients || !dev->services) {
>> + if (!dev->services) {
>> error("gatt: Failed to allocate memory for client");
>> +
>
> Avoid unrelated changes, i.e. whitespace.
>
OK.
>> destroy_device(dev);
>> return NULL;
>> }
>>
>> + if (!queue_push_head(gatt_devices, dev)) {
>> + error("gatt: Cannot push device to queue");
>> +
>> + return NULL;
>
> Need to destroy device here.
>
Thanks, missed that one.
>> + }
>> +
>> return dev;
>> }
>>
>> -static void handle_client_connect(const void *buf, uint16_t len)
>> +static struct connection_record *create_connection(struct gatt_device *device,
>> + struct gatt_client *client)
>> {
>> - const struct hal_cmd_gatt_client_connect *cmd = buf;
>> - struct gatt_device *dev = NULL;
>> - void *l;
>> - bdaddr_t addr;
>> + struct connection_record *new_conn;
>> + struct connection_record *last;
>> +
>> + /* Check if already connected */
>> + new_conn = new0(struct connection_record, 1);
>> + if (!new_conn)
>> + return NULL;
>> +
>> + new_conn->client = client;
>> + new_conn->device = device;
>> +
>> + /* Make connection id unique to connection record
>> + * (client, device) pair.
>> + */
>> + last = queue_peek_head(client_connections);
>> + if (last)
>> + new_conn->id = last->id + 1;
>> + else
>> + new_conn->id = 1;
>
> You can just make static which will increment id for each connection
> and avoid this unnecessary logic here.
>
This was a "sort of" id reusage, but yes, we have plenty of those in
int32_t range. I'll change that.
>> +
>> + if (!queue_push_head(client_connections, new_conn)) {
>> + error("gatt: Cannot push client on the client queue!?");
>> +
>> + free(new_conn);
>> + return NULL;
>> + }
>> +
>> + new_conn->device->connection_ref_count++;
>> +
>> + return new_conn;
>> +}
>> +
>> +static void trigger_disconnection(struct connection_record *conn)
>> +{
>> + /* Notify client */
>> + if (queue_remove(client_connections, conn))
>> + send_client_disconnect_notify(conn, GATT_SUCCESS);
>> +
>> + destroy_connection(conn);
>> +}
>> +
>> +static void client_disconnect_devices(struct gatt_client *client)
>> +{
>> + struct connection_record *conn;
>> +
>> + /* find every connection for client record and trigger disconnect */
>> + while ((conn = queue_remove_if(client_connections,
>> + match_connection_by_client, client)))
>
> Make it without assignment in condition.
>
OK.
>> + trigger_disconnection(conn);
>> +}
>> +
>> +static bool trigger_connection(struct connection_record *conn)
>> +{
>> + switch (conn->device->state) {
>> + case DEVICE_DISCONNECTED:
>> + device_set_state(conn->device, DEVICE_CONNECTION_PENDING);
>> + break;
>> + case DEVICE_CONNECTED:
>> + send_client_connect_notify(conn, GATT_SUCCESS);
>> + break;
>> + case DEVICE_CONNECTION_PENDING:
>> + default:
>> + break;
>> + }
>> +
>> + /* after state change trigger discovering */
>> + if (!scanning && (conn->device->state == DEVICE_CONNECTION_PENDING))
>> + if (!bt_le_discovery_start(le_device_found_handler)) {
>> + error("gatt: Could not start scan");
>> +
>> + return false;
>> + }
>> +
>> + return true;
>> +}
>> +
>> +static void handle_client_unregister(const void *buf, uint16_t len)
>> +{
>> + const struct hal_cmd_gatt_client_unregister *cmd = buf;
>> uint8_t status;
>> - bool send_notify = false;
>> + struct gatt_client *cl;
>>
>> DBG("");
>>
>> - /* Check if client is registered */
>> - l = find_client_by_id(cmd->client_if);
>> - if (!l) {
>> - error("gatt: Client id %d not found", cmd->client_if);
>> + cl = queue_remove_if(gatt_clients, match_client_by_id,
>> + INT_TO_PTR(cmd->client_if));
>> + if (!cl) {
>> + error("gatt: client_if=%d not found", cmd->client_if);
>> status = HAL_STATUS_FAILED;
>> - goto reply;
>> + } else {
>> + /* Check if there is any connect request or connected device
>> + * for this client. If so, remove this client from those lists.
>> + */
>> + client_disconnect_devices(cl);
>> + destroy_gatt_client(cl);
>> + status = HAL_STATUS_SUCCESS;
>> }
>>
>> - android2bdaddr(&cmd->bdaddr, &addr);
>> + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
>> + HAL_OP_GATT_CLIENT_UNREGISTER, status);
>> +}
>>
>> - /* 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) {
>> - /* Remeber to send dummy notification event if we area
>> - * connected
>> - */
>> - if (dev->conn_id)
>> - send_notify = true;
>> +static struct connection_record *find_connection_by_dev_client_id(
>
> It's by addr and client_id.
>
Yeap, I'll fix that.
>
> BR,
> Andrzej
>
Thanks, I'll resend v2 with all those issues fixed.
BR,
Jakub
prev parent reply other threads:[~2014-04-24 10:57 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-04-23 12:26 [PATCH] android/gatt: Client connection handling refactor Jakub Tyszkowski
2014-04-23 12:26 ` [PATCH] android/gatt: Refactor client connection handling Jakub Tyszkowski
2014-04-23 17:35 ` Andrzej Kaczmarek
2014-04-24 10:57 ` Tyszkowski Jakub [this message]
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=5358EE2D.5090807@tieto.com \
--to=jakub.tyszkowski@tieto.com \
--cc=andrzej.kaczmarek@tieto.com \
--cc=linux-bluetooth@vger.kernel.org \
/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;
as well as URLs for NNTP newsgroup(s).