greybus: loopback: sort list of connections for masking purposes
authorBryan O'Donoghue <bryan.odonoghue@linaro.org>
Mon, 14 Sep 2015 09:48:46 +0000 (10:48 +0100)
committerGreg Kroah-Hartman <gregkh@google.com>
Tue, 15 Sep 2015 04:38:12 +0000 (21:38 -0700)
In user-space we specify a list of connections as a bit-mask with the
assumption that a list such is indexed as indicated below.

end0:3:3:1:1 = 1
end0:3:3:2:3 = 2
end0:3:3:3:4 = 4

Current code assigns bitmask ids based on the order of discovery, however
user-space has no idea what the order of discovery is. This patch sorts the
linked list of connections associated with the loopback driver and assigns
a bit-id based on the sorted list - not the order of discovery. This
change therefore enforces the end-users idea that end0:3:3:1:1 above is
always denoted by bit 1 - even if from the AP's perspective it was the last
entry discovered.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Reviewed-by: Patrick Titiano <ptitiano@baylibre.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/loopback.c

index ba65457f9f705fd246560438b6955299c49447c4..a62e122c2bccc8b3da23e7c31ce4a7d0e40d7edb 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/fs.h>
 #include <linux/kfifo.h>
 #include <linux/debugfs.h>
+#include <linux/list_sort.h>
 
 #include <asm/div64.h>
 
@@ -843,6 +844,50 @@ static const struct file_operations gb_loopback_debugfs_dev_latency_ops = {
        .release        = single_release,
 };
 
+static int gb_loopback_bus_id_compare(void *priv, struct list_head *lha,
+                                     struct list_head *lhb)
+{
+       struct gb_loopback *a = list_entry(lha, struct gb_loopback, entry);
+       struct gb_loopback *b = list_entry(lhb, struct gb_loopback, entry);
+       struct gb_connection *ca = a->connection;
+       struct gb_connection *cb = b->connection;
+
+       if (ca->bundle->intf->module->module_id <
+           cb->bundle->intf->module->module_id)
+               return -1;
+       if (cb->bundle->intf->module->module_id <
+           ca->bundle->intf->module->module_id)
+               return 1;
+       if (ca->bundle->intf->interface_id < cb->bundle->intf->interface_id)
+               return -1;
+       if (cb->bundle->intf->interface_id < ca->bundle->intf->interface_id)
+               return 1;
+       if (ca->bundle->id < cb->bundle->id)
+               return -1;
+       if (cb->bundle->id < ca->bundle->id)
+               return 1;
+       if (ca->intf_cport_id < cb->intf_cport_id)
+               return -1;
+       else if (cb->intf_cport_id < ca->intf_cport_id)
+               return 1;
+
+       return 0;
+}
+
+static void gb_loopback_insert_id(struct gb_loopback *gb)
+{
+       struct gb_loopback *gb_list;
+       u32 new_lbid = 0;
+
+       /* perform an insertion sort */
+       list_add_tail(&gb->entry, &gb_dev.list);
+       list_sort(NULL, &gb_dev.list, gb_loopback_bus_id_compare);
+       list_for_each_entry(gb_list, &gb_dev.list, entry) {
+               gb_list->lbid = 1 << new_lbid;
+               new_lbid++;
+       }
+}
+
 #define DEBUGFS_NAMELEN 32
 
 static int gb_loopback_connection_init(struct gb_connection *connection)
@@ -908,14 +953,13 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
 
        /* Fork worker thread */
        mutex_init(&gb->mutex);
-       gb->lbid = 1 << gb_dev.count;
        gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback");
        if (IS_ERR(gb->task)) {
                retval = PTR_ERR(gb->task);
                goto out_kfifo1;
        }
 
-       list_add_tail(&gb->entry, &gb_dev.list);
+       gb_loopback_insert_id(gb);
        gb_dev.count++;
        mutex_unlock(&gb_dev.mutex);
        return 0;