int gpiod_set_array_value_complex()

in gpiolib.c [2865:2972]


int gpiod_set_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 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);

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

		gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
				       value_bitmap);

		i = find_first_zero_bit(array_info->set_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 count = 0;

		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);

		do {
			struct gpio_desc *desc = desc_array[i];
			int hwgpio = gpio_chip_hwgpio(desc);
			int value = test_bit(i, value_bitmap);

			/*
			 * Pins applicable for fast input but not for
			 * fast output processing may have been already
			 * inverted inside the fast path, skip them.
			 */
			if (!raw && !(array_info &&
			    test_bit(i, array_info->invert_mask)) &&
			    test_bit(FLAG_ACTIVE_LOW, &desc->flags))
				value = !value;
			trace_gpio_value(desc_to_gpio(desc), 0, value);
			/*
			 * collect all normal outputs belonging to the same chip
			 * open drain and open source outputs are set individually
			 */
			if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) && !raw) {
				gpio_set_open_drain_value_commit(desc, value);
			} else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags) && !raw) {
				gpio_set_open_source_value_commit(desc, value);
			} else {
				__set_bit(hwgpio, mask);
				__assign_bit(hwgpio, bits, value);
				count++;
			}
			i++;

			if (array_info)
				i = find_next_zero_bit(array_info->set_mask,
						       array_size, i);
		} while ((i < array_size) &&
			 (desc_array[i]->gdev->chip == gc));
		/* push collected bits to outputs */
		if (count != 0)
			gpio_chip_set_multiple(gc, mask, bits);

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