int gpiod_get_array_value_complex()

in gpiolib.c [2568:2679]


int gpiod_get_array_value_complex(bool raw, bool can_sleep,
				  unsigned int array_size,
				  struct gpio_desc **desc_array,
				  struct gpio_array *array_info,
				  unsigned long *value_bitmap)
{
	int ret, i = 0;

	/*
	 * Validate array_info against desc_array and its size.
	 * It should immediately follow desc_array if both
	 * have been obtained from the same gpiod_get_array() call.
	 */
	if (array_info && array_info->desc == desc_array &&
	    array_size <= array_info->size &&
	    (void *)array_info == desc_array + array_info->size) {
		if (!can_sleep)
			WARN_ON(array_info->chip->can_sleep);

		ret = gpio_chip_get_multiple(array_info->chip,
					     array_info->get_mask,
					     value_bitmap);
		if (ret)
			return ret;

		if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
			bitmap_xor(value_bitmap, value_bitmap,
				   array_info->invert_mask, array_size);

		i = find_first_zero_bit(array_info->get_mask, array_size);
		if (i == array_size)
			return 0;
	} else {
		array_info = NULL;
	}

	while (i < array_size) {
		struct gpio_chip *gc = desc_array[i]->gdev->chip;
		DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
		DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
		unsigned long *mask, *bits;
		int first, j;

		if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
			mask = fastpath_mask;
			bits = fastpath_bits;
		} else {
			gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;

			mask = bitmap_alloc(gc->ngpio, flags);
			if (!mask)
				return -ENOMEM;

			bits = bitmap_alloc(gc->ngpio, flags);
			if (!bits) {
				bitmap_free(mask);
				return -ENOMEM;
			}
		}

		bitmap_zero(mask, gc->ngpio);

		if (!can_sleep)
			WARN_ON(gc->can_sleep);

		/* collect all inputs belonging to the same chip */
		first = i;
		do {
			const struct gpio_desc *desc = desc_array[i];
			int hwgpio = gpio_chip_hwgpio(desc);

			__set_bit(hwgpio, mask);
			i++;

			if (array_info)
				i = find_next_zero_bit(array_info->get_mask,
						       array_size, i);
		} while ((i < array_size) &&
			 (desc_array[i]->gdev->chip == gc));

		ret = gpio_chip_get_multiple(gc, mask, bits);
		if (ret) {
			if (mask != fastpath_mask)
				bitmap_free(mask);
			if (bits != fastpath_bits)
				bitmap_free(bits);
			return ret;
		}

		for (j = first; j < i; ) {
			const struct gpio_desc *desc = desc_array[j];
			int hwgpio = gpio_chip_hwgpio(desc);
			int value = test_bit(hwgpio, bits);

			if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
				value = !value;
			__assign_bit(j, value_bitmap, value);
			trace_gpio_value(desc_to_gpio(desc), 1, value);
			j++;

			if (array_info)
				j = find_next_zero_bit(array_info->get_mask, i,
						       j);
		}

		if (mask != fastpath_mask)
			bitmap_free(mask);
		if (bits != fastpath_bits)
			bitmap_free(bits);
	}
	return 0;
}