gpiolib: improve overlap check of range of gpio
authorBamvor Jian Zhang <bamvor.zhangjian@linaro.org>
Mon, 16 Nov 2015 05:02:46 +0000 (13:02 +0800)
committerLinus Walleij <linus.walleij@linaro.org>
Thu, 19 Nov 2015 08:24:40 +0000 (09:24 +0100)
There are limitations for the current checker:
1.  Could not check the overlap if the new gpiochip is the secondly
    gpiochip.
2.  Could not check the overlap if the new gpiochip is overlap
    with the left of gpiochip. E.g. if we insert [c, d] between
    [a,b] and [e, f], and e >= c + d, it will successful even if
    c < a + b.
3.  Allow overlap of base of different gpiochip.

This patch fix these issues by checking the overlap of both right and
left gpiochip in the same loop statement.

Signed-off-by: Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
[Tweaked to remove unnecessary ret variable]
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpiolib.c

index 44c8d83527489814894a8539e7631b887f88d166..eed70c36e8ac6eb266eefe85a1b72d1a0e02ca4c 100644 (file)
@@ -182,7 +182,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
 
 /*
  * Add a new chip to the global chips list, keeping the list of chips sorted
- * by base order.
+ * by range(means [base, base + ngpio - 1]) order.
  *
  * Return -EBUSY if the new chip overlaps with some other chip's integer
  * space.
@@ -190,31 +190,48 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
 static int gpiochip_add_to_list(struct gpio_chip *chip)
 {
        struct list_head *pos;
-       struct gpio_chip *_chip;
-       int err = 0;
+       struct gpio_chip *iterator;
+       struct gpio_chip *previous = NULL;
 
-       /* find where to insert our chip */
-       list_for_each(pos, &gpio_chips) {
-               _chip = list_entry(pos, struct gpio_chip, list);
-               /* shall we insert before _chip? */
-               if (_chip->base >= chip->base + chip->ngpio)
-                       break;
+       if (list_empty(&gpio_chips)) {
+               pos = gpio_chips.next;
+               goto found;
        }
 
-       /* are we stepping on the chip right before? */
-       if (pos != &gpio_chips && pos->prev != &gpio_chips) {
-               _chip = list_entry(pos->prev, struct gpio_chip, list);
-               if (_chip->base + _chip->ngpio > chip->base) {
-                       dev_err(chip->parent,
-                               "GPIO integer space overlap, cannot add chip\n");
-                       err = -EBUSY;
+       list_for_each(pos, &gpio_chips) {
+               iterator = list_entry(pos, struct gpio_chip, list);
+               if (iterator->base >= chip->base + chip->ngpio) {
+                       /*
+                        * Iterator is the first GPIO chip so there is no
+                        * previous one
+                        */
+                       if (previous == NULL) {
+                               goto found;
+                       } else {
+                               /*
+                                * We found a valid range(means
+                                * [base, base + ngpio - 1]) between previous
+                                * and iterator chip.
+                                */
+                               if (previous->base + previous->ngpio
+                                               <= chip->base)
+                                       goto found;
+                       }
                }
+               previous = iterator;
        }
 
-       if (!err)
-               list_add_tail(&chip->list, pos);
+       /* We are beyond the last chip in the list */
+       if (iterator->base + iterator->ngpio <= chip->base)
+               goto found;
 
-       return err;
+       dev_err(chip->parent,
+              "GPIO integer space overlap, cannot add chip\n");
+       return -EBUSY;
+
+found:
+       list_add_tail(&chip->list, pos);
+       return 0;
 }
 
 /**