int acpi_video_get_levels()

in acpi_video.c [825:932]


int acpi_video_get_levels(struct acpi_device *device,
			  struct acpi_video_device_brightness **dev_br,
			  int *pmax_level)
{
	union acpi_object *obj = NULL;
	int i, max_level = 0, count = 0, level_ac_battery = 0;
	union acpi_object *o;
	struct acpi_video_device_brightness *br = NULL;
	int result = 0;
	u32 value;

	if (ACPI_FAILURE(acpi_video_device_lcd_query_levels(device->handle, &obj))) {
		acpi_handle_debug(device->handle,
				  "Could not query available LCD brightness level\n");
		result = -ENODEV;
		goto out;
	}

	if (obj->package.count < ACPI_VIDEO_FIRST_LEVEL) {
		result = -EINVAL;
		goto out;
	}

	br = kzalloc(sizeof(*br), GFP_KERNEL);
	if (!br) {
		result = -ENOMEM;
		goto out;
	}

	/*
	 * Note that we have to reserve 2 extra items (ACPI_VIDEO_FIRST_LEVEL),
	 * in order to account for buggy BIOS which don't export the first two
	 * special levels (see below)
	 */
	br->levels = kmalloc_array(obj->package.count + ACPI_VIDEO_FIRST_LEVEL,
				   sizeof(*br->levels),
				   GFP_KERNEL);
	if (!br->levels) {
		result = -ENOMEM;
		goto out_free;
	}

	for (i = 0; i < obj->package.count; i++) {
		o = (union acpi_object *)&obj->package.elements[i];
		if (o->type != ACPI_TYPE_INTEGER) {
			acpi_handle_info(device->handle, "Invalid data\n");
			continue;
		}
		value = (u32) o->integer.value;
		/* Skip duplicate entries */
		if (count > ACPI_VIDEO_FIRST_LEVEL
		    && br->levels[count - 1] == value)
			continue;

		br->levels[count] = value;

		if (br->levels[count] > max_level)
			max_level = br->levels[count];
		count++;
	}

	/*
	 * some buggy BIOS don't export the levels
	 * when machine is on AC/Battery in _BCL package.
	 * In this case, the first two elements in _BCL packages
	 * are also supported brightness levels that OS should take care of.
	 */
	for (i = ACPI_VIDEO_FIRST_LEVEL; i < count; i++) {
		if (br->levels[i] == br->levels[ACPI_VIDEO_AC_LEVEL])
			level_ac_battery++;
		if (br->levels[i] == br->levels[ACPI_VIDEO_BATTERY_LEVEL])
			level_ac_battery++;
	}

	if (level_ac_battery < ACPI_VIDEO_FIRST_LEVEL) {
		level_ac_battery = ACPI_VIDEO_FIRST_LEVEL - level_ac_battery;
		br->flags._BCL_no_ac_battery_levels = 1;
		for (i = (count - 1 + level_ac_battery);
		     i >= ACPI_VIDEO_FIRST_LEVEL; i--)
			br->levels[i] = br->levels[i - level_ac_battery];
		count += level_ac_battery;
	} else if (level_ac_battery > ACPI_VIDEO_FIRST_LEVEL)
		acpi_handle_info(device->handle,
				 "Too many duplicates in _BCL package");

	/* Check if the _BCL package is in a reversed order */
	if (max_level == br->levels[ACPI_VIDEO_FIRST_LEVEL]) {
		br->flags._BCL_reversed = 1;
		sort(&br->levels[ACPI_VIDEO_FIRST_LEVEL],
		     count - ACPI_VIDEO_FIRST_LEVEL,
		     sizeof(br->levels[ACPI_VIDEO_FIRST_LEVEL]),
		     acpi_video_cmp_level, NULL);
	} else if (max_level != br->levels[count - 1])
		acpi_handle_info(device->handle,
				 "Found unordered _BCL package");

	br->count = count;
	*dev_br = br;
	if (pmax_level)
		*pmax_level = max_level;

out:
	kfree(obj);
	return result;
out_free:
	kfree(br);
	goto out;
}