Re: [PATCH v2] platform: x86: dell-laptop: Add support for keyboard backlight

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, Nov 23, 2014 at 03:50:45PM +0100, Pali Rohár wrote:
> This patch adds support for configuring keyboard backlight settings on supported
> Dell laptops. It exports kernel leds interface and uses Dell SMBIOS tokens or
> keyboard class interface.
> 
> With this patch it is possible to set:
> * keyboard backlight level
> * timeout after which will be backlight automatically turned off
> * input activity triggers (keyboard, touchpad, mouse) which enable backlight
> * ambient light settings
> 
> Settings are exported via sysfs:
> /sys/class/leds/dell::kbd_backlight/
> 
> Code is based on newly released documentation by Dell in libsmbios project.
> 

Hi Pali,

I would really like to see this broken up. Possibly adding levels and timeouts
as separate patches for example. It is quite difficult to review this all at
once in a reasonable amount of time.

During this review I caught a few more CodingStyle violations, and raised some
questions about the timeout and levels mechanisms.

> @@ -62,6 +71,10 @@ struct calling_interface_structure {
>  
>  struct quirk_entry {
>  	u8 touchpad_led;
> +
> +	/* Ordered list of timeouts expressed in seconds.
> +	 * The list must end with -1 */

Despite other instances in this file, block comments are documented in
CodingStyle as:

/*
 * Comment text.
 */

The old ones should be cleaned up eventually, but new ones need to be done
according to CodingStyle. Please correct throughout.

> +	int kbd_timeouts[];
>  };
>  
>  static struct quirk_entry *quirks;
> @@ -76,6 +89,10 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
>  	return 1;
>  }
>  
> +static struct quirk_entry quirk_dell_xps13_9333 = {
> +	.kbd_timeouts = { 0, 5, 15, 60, 5*60, 15*60, -1 },

Where did these values come from? Were they documented in the libsmbios project?
Can you provide a URL to that? These really should be described by the firmware,
but if they aren't, nothing we can do about it.

> @@ -790,6 +842,964 @@ static void touchpad_led_exit(void)
>  	led_classdev_unregister(&touchpad_led);
>  }
>  
> +/* Derived from information in smbios-keyboard-ctl:

See block comment above.

> +
> + cbClass 4
> + cbSelect 11
> + Keyboar illumination

Keyboard

> + cbArg1 determines the function to be performed
> +
> + cbArg1 0x0 = Get Feature Information
> +  cbRES1         Standard return codes (0, -1, -2)
> +  cbRES2, word0  Bitmap of user-selectable modes
> +     bit 0     Always off (All systems)
> +     bit 1     Always on (Travis ATG, Siberia)
> +     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
> +     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
> +     bit 4     Auto: Input-activity-based On; input-activity based Off
> +     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
> +     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
> +     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
> +     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
> +     bits 9-15 Reserved for future use
> +  cbRES2, byte2  Reserved for future use
> +  cbRES2, byte3  Keyboard illumination type
> +     0         Reserved
> +     1         Tasklight
> +     2         Backlight
> +     3-255     Reserved for future use
> +  cbRES3, byte0  Supported auto keyboard illumination trigger bitmap.
> +     bit 0     Any keystroke
> +     bit 1     Touchpad activity
> +     bit 2     Pointing stick
> +     bit 3     Any mouse
> +     bits 4-7  Reserved for future use
> +  cbRES3, byte1  Supported timeout unit bitmap
> +     bit 0     Seconds
> +     bit 1     Minutes
> +     bit 2     Hours
> +     bit 3     Days
> +     bits 4-7  Reserved for future use
> +  cbRES3, byte2  Number of keyboard light brightness levels
> +  cbRES4, byte0  Maximum acceptable seconds value (0 if seconds not supported).
> +  cbRES4, byte1  Maximum acceptable minutes value (0 if minutes not supported).
> +  cbRES4, byte2  Maximum acceptable hours value (0 if hours not supported).
> +  cbRES4, byte3  Maxiomum acceptable days value (0 if days not supported)

Maximum             ^

This interface appears to define all possible values for the timeout between
cbRES3[1] and cbRES4[*]. Why is the kbd_timeouts[] array a quirk with fixed
values? It seems it could indeed be dynamically determined.

> +
> +struct kbd_info {
> +	u16 modes;
> +	u8 type;
> +	u8 triggers;
> +	u8 levels;
> +	u8 seconds;
> +	u8 minutes;
> +	u8 hours;
> +	u8 days;
> +};
> +
> +
> +static u8 kbd_mode_levels[16];
> +static int kbd_mode_levels_count;

I'm confused by these. How are they different from kbd_info.levels?

We seem to check the latter first and fall back to these if that is 0.... why?

> +static int kbd_get_info(struct kbd_info *info)
> +{
> +	u8 units;
> +	int ret;
> +
> +	get_buffer();
> +
> +	buffer->input[0] = 0x0;
> +	dell_send_request(buffer, 4, 11);
> +	ret = buffer->output[0];
> +
> +	if (ret) {
> +		ret = dell_smi_error(ret);
> +		goto out;
> +	}
> +
> +	info->modes = buffer->output[1] & 0xFFFF;
> +	info->type = (buffer->output[1] >> 24) & 0xFF;
> +	info->triggers = buffer->output[2] & 0xFF;
> +	units = (buffer->output[2] >> 8) & 0xFF;
> +	info->levels = (buffer->output[2] >> 16) & 0xFF;
> +
> +	if (units & BIT(0))
> +		info->seconds = (buffer->output[3] >> 0) & 0xFF;
> +	if (units & BIT(1))
> +		info->minutes = (buffer->output[3] >> 8) & 0xFF;
> +	if (units & BIT(2))
> +		info->hours = (buffer->output[3] >> 16) & 0xFF;
> +	if (units & BIT(3))
> +		info->days = (buffer->output[3] >> 24) & 0xFF;
> +
> +out:

Please indent gotos by one space. This improves diffs context by not treating the goto label
like a function.

> +	release_buffer();
> +	return ret;
> +}
> +
> +static unsigned int kbd_get_max_level(void)
> +{
> +	if (kbd_info.levels != 0)
> +		return kbd_info.levels;
> +	if (kbd_mode_levels_count > 0)
> +		return kbd_mode_levels_count - 1;

Here for example. In what scenario does this happen?

> +	return 0;
> +}
> +



> +static inline int kbd_init_info(void)
> +{
> +	struct kbd_state state;
> +	int ret;
> +	int i;
> +
> +	ret = kbd_get_info(&kbd_info);
> +	if (ret)
> +		return ret;
> +
> +	kbd_get_state(&state);
> +
> +	/* NOTE: timeout value is stored in 6 bits so max value is 63 */
> +	if (kbd_info.seconds > 63)
> +		kbd_info.seconds = 63;
> +	if (kbd_info.minutes > 63)
> +		kbd_info.minutes = 63;
> +	if (kbd_info.hours > 63)
> +		kbd_info.hours = 63;
> +	if (kbd_info.days > 63)
> +		kbd_info.days = 63;
> +
> +	/* NOTE: On tested machines ON mode did not work and caused
> +	 *       problems (turned backlight off) so do not use it
> +	 */
> +	kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON);
> +
> +	kbd_previous_level = kbd_get_level(&state);
> +	kbd_previous_mode_bit = state.mode_bit;
> +
> +	if (kbd_previous_level == 0 && kbd_get_max_level() != 0)
> +		kbd_previous_level = 1;
> +
> +	if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) {
> +		kbd_previous_mode_bit =
> +			ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF));
> +		if (kbd_previous_mode_bit != 0)
> +			kbd_previous_mode_bit--;
> +	}
> +
> +	if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) |
> +			      BIT(KBD_MODE_BIT_TRIGGER_ALS)))
> +		kbd_als_supported = true;
> +
> +	if (kbd_info.modes & (
> +	    BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) |
> +	    BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) |
> +	    BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100)
> +	   ))
> +		kbd_triggers_supported = true;
> +
> +	for (i = 0; i < 16; ++i)

Doesn't this only apply to bits 5-8? Why not just loop through those?

for (i = KBD_MODE_BIT_TRIGGER_25; i <= KBD_MODE_BIT_TRIGGER_100)

The level_mode_bit check becomes unecessary.

> +		if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes))
> +			kbd_mode_levels[1+kbd_mode_levels_count++] = i;

One space around operators like + please.

What is kbd_mode_levels[0] reserved for? The current level? Isn't that what
kbd_state.level is for?

> +
> +	if (kbd_mode_levels_count > 0) {
> +		for (i = 0; i < 16; ++i) {

If 0-4 don't apply here, why loop through them? Should we just set levels[0] to
0 if it isn't one of 5-8?

If bits 9-16 are reserved, it seems it would be best to avoid checking for them
as they might return a false positive, and we'd be setting kbd_mode_levels to an
undefined value.

> +			if (BIT(i) & kbd_info.modes) {
> +				kbd_mode_levels[0] = i;
> +				break;
> +			}
> +		}
> +		kbd_mode_levels_count++;
> +	}
> +
> +	return 0;
-- 
Darren Hart
Intel Open Source Technology Center
--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux