/* * These values come from Windows utility provided by Dell. If any other value * is used then BIOS silently set timeout to 0 without any error message.
*/ staticstruct quirk_entry quirk_dell_xps13_9333 = {
.needs_kbd_timeouts = true,
.kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
};
/* * Derived from information in smbios-wireless-ctl: * * cbSelect 17, Value 11 * * Return Wireless Info * cbArg1, byte0 = 0x00 * * cbRes1 Standard return codes (0, -1, -2) * cbRes2 Info bit flags: * * 0 Hardware switch supported (1) * 1 WiFi locator supported (1) * 2 WLAN supported (1) * 3 Bluetooth (BT) supported (1) * 4 WWAN supported (1) * 5 Wireless KBD supported (1) * 6 Uw b supported (1) * 7 WiGig supported (1) * 8 WLAN installed (1) * 9 BT installed (1) * 10 WWAN installed (1) * 11 Uw b installed (1) * 12 WiGig installed (1) * 13-15 Reserved (0) * 16 Hardware (HW) switch is On (1) * 17 WLAN disabled (1) * 18 BT disabled (1) * 19 WWAN disabled (1) * 20 Uw b disabled (1) * 21 WiGig disabled (1) * 20-31 Reserved (0) * * cbRes3 NVRAM size in bytes * cbRes4, byte 0 NVRAM format version number * * * Set QuickSet Radio Disable Flag * cbArg1, byte0 = 0x01 * cbArg1, byte1 * Radio ID value: * 0 Radio Status * 1 WLAN ID * 2 BT ID * 3 WWAN ID * 4 UWB ID * 5 WIGIG ID * cbArg1, byte2 Flag bits: * 0 QuickSet disables radio (1) * 1-7 Reserved (0) * * cbRes1 Standard return codes (0, -1, -2) * cbRes2 QuickSet (QS) radio disable bit map: * 0 QS disables WLAN * 1 QS disables BT * 2 QS disables WWAN * 3 QS disables UWB * 4 QS disables WIGIG * 5-31 Reserved (0) * * Wireless Switch Configuration * cbArg1, byte0 = 0x02 * * cbArg1, byte1 * Subcommand: * 0 Get config * 1 Set config * 2 Set WiFi locator enable/disable * cbArg1,byte2 * Switch settings (if byte 1==1): * 0 WLAN sw itch control (1) * 1 BT sw itch control (1) * 2 WWAN sw itch control (1) * 3 UWB sw itch control (1) * 4 WiGig sw itch control (1) * 5-7 Reserved (0) * cbArg1, byte2 Enable bits (if byte 1==2): * 0 Enable WiFi locator (1) * * cbRes1 Standard return codes (0, -1, -2) * cbRes2 QuickSet radio disable bit map: * 0 WLAN controlled by sw itch (1) * 1 BT controlled by sw itch (1) * 2 WWAN controlled by sw itch (1) * 3 UWB controlled by sw itch (1) * 4 WiGig controlled by sw itch (1) * 5-6 Reserved (0) * 7 Wireless sw itch config locked (1) * 8 WiFi locator enabled (1) * 9-14 Reserved (0) * 15 WiFi locator setting locked (1) * 16-31 Reserved (0) * * Read Local Config Data (LCD) * cbArg1, byte0 = 0x10 * cbArg1, byte1 NVRAM index low byte * cbArg1, byte2 NVRAM index high byte * cbRes1 Standard return codes (0, -1, -2) * cbRes2 4 bytes read from LCD[index] * cbRes3 4 bytes read from LCD[index+4] * cbRes4 4 bytes read from LCD[index+8] * * Write Local Config Data (LCD) * cbArg1, byte0 = 0x11 * cbArg1, byte1 NVRAM index low byte * cbArg1, byte2 NVRAM index high byte * cbArg2 4 bytes to w rite at LCD[index] * cbArg3 4 bytes to w rite at LCD[index+4] * cbArg4 4 bytes to w rite at LCD[index+8] * cbRes1 Standard return codes (0, -1, -2) * * Populate Local Config Data from NVRAM * cbArg1, byte0 = 0x12 * cbRes1 Standard return codes (0, -1, -2) * * Commit Local Config Data to NVRAM * cbArg1, byte0 = 0x13 * cbRes1 Standard return codes (0, -1, -2)
*/
staticint dell_rfkill_set(void *data, bool blocked)
{ int disable = blocked ? 1 : 0; unsignedlong radio = (unsignedlong)data; int hwswitch_bit = (unsignedlong)data - 1; struct calling_interface_buffer buffer; int hwswitch; int status; int ret;
dell_fill_request(&buffer, 0, 0, 0, 0);
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL); if (ret) return ret;
status = buffer.output[1];
dell_fill_request(&buffer, 0x2, 0, 0, 0);
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL); if (ret) return ret;
hwswitch = buffer.output[1];
/* If the hardware switch controls this radio, and the hardware
switch is disabled, always disable the radio */ if (ret == 0 && (hwswitch & BIT(hwswitch_bit)) &&
(status & BIT(0)) && !(status & BIT(16)))
disable = 1;
staticvoid dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio, int status)
{ if (status & BIT(0)) { /* Has hw-switch, sync sw_state to BIOS */ struct calling_interface_buffer buffer; int block = rfkill_blocked(rfkill);
dell_fill_request(&buffer,
1 | (radio << 8) | (block << 16), 0, 0, 0);
dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
} else { /* No hw-switch, sync BIOS state to sw_state */
rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
}
}
staticvoid dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio, int status, int hwswitch)
{ if (hwswitch & (BIT(radio - 1)))
rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
staticvoid dell_rfkill_query(struct rfkill *rfkill, void *data)
{ int radio = ((unsignedlong)data & 0xF); struct calling_interface_buffer buffer; int hwswitch; int status; int ret;
dell_fill_request(&buffer, 0, 0, 0, 0);
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
status = buffer.output[1];
staticint dell_debugfs_show(struct seq_file *s, void *data)
{ struct calling_interface_buffer buffer; int hwswitch_state; int hwswitch_ret; int status; int ret;
dell_fill_request(&buffer, 0, 0, 0, 0);
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL); if (ret) return ret;
status = buffer.output[1];
/* * rfkill support causes trouble on various models, mostly Inspirons. * So we whitelist certain series, and don't support rfkill on others.
*/
whitelisted = 0;
product = dmi_get_system_info(DMI_PRODUCT_NAME); if (product && (strncmp(product, "Latitude", 8) == 0 ||
strncmp(product, "Precision", 9) == 0))
whitelisted = 1; if (!force_rfkill && !whitelisted) return 0;
dell_fill_request(&buffer, 0, 0, 0, 0);
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
status = buffer.output[1];
/* dell wireless info smbios call is not supported */ if (ret != 0) return 0;
/* rfkill is only tested on laptops with a hwswitch */ if (!(status & BIT(0)) && !force_rfkill) return 0;
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
RFKILL_TYPE_WLAN,
&dell_rfkill_ops, (void *) 1); if (!wifi_rfkill) {
ret = -ENOMEM; goto err_wifi;
}
ret = rfkill_register(wifi_rfkill); if (ret) goto err_wifi;
}
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
&platform_device->dev,
RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2); if (!bluetooth_rfkill) {
ret = -ENOMEM; goto err_bluetooth;
}
ret = rfkill_register(bluetooth_rfkill); if (ret) goto err_bluetooth;
}
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
wwan_rfkill = rfkill_alloc("dell-wwan",
&platform_device->dev,
RFKILL_TYPE_WWAN,
&dell_rfkill_ops, (void *) 3); if (!wwan_rfkill) {
ret = -ENOMEM; goto err_wwan;
}
ret = rfkill_register(wwan_rfkill); if (ret) goto err_wwan;
}
/* * Dell Airplane Mode Switch driver (dell-rbtn) supports ACPI devices * which can receive events from HW slider switch. * * Dell SMBIOS on whitelisted models supports controlling radio devices * but does not support receiving HW button switch events. We can use * i8042 filter hook function to receive keyboard data and handle * keycode for HW button. * * So if it is possible we will use Dell Airplane Mode Switch ACPI * driver for receiving HW events and Dell SMBIOS for setting rfkill * states. If ACPI driver or device is not available we will fallback to * i8042 filter hook function. * * To prevent duplicate rfkill devices which control and do same thing, * dell-rbtn driver will automatically remove its own rfkill devices * once function dell_rbtn_notifier_register() is called.
*/
dell_rbtn_notifier_register_func =
symbol_request(dell_rbtn_notifier_register); if (dell_rbtn_notifier_register_func) {
dell_rbtn_notifier_unregister_func =
symbol_request(dell_rbtn_notifier_unregister); if (!dell_rbtn_notifier_unregister_func) {
symbol_put(dell_rbtn_notifier_register);
dell_rbtn_notifier_register_func = NULL;
}
}
if (dell_rbtn_notifier_register_func) {
ret = dell_rbtn_notifier_register_func(
&dell_laptop_rbtn_notifier);
symbol_put(dell_rbtn_notifier_register);
dell_rbtn_notifier_register_func = NULL; if (ret != 0) {
symbol_put(dell_rbtn_notifier_unregister);
dell_rbtn_notifier_unregister_func = NULL;
}
} else {
pr_info("Symbols from dell-rbtn acpi driver are not available\n");
ret = -ENODEV;
}
if (ret == 0) {
pr_info("Using dell-rbtn acpi driver for receiving events\n");
} elseif (ret != -ENODEV) {
pr_warn("Unable to register dell rbtn notifier\n"); goto err_filter;
} else {
ret = i8042_install_filter(dell_laptop_i8042_filter, NULL); if (ret) {
pr_warn("Unable to install key filter\n"); goto err_filter;
}
pr_info("Using i8042 filter function for receiving events\n");
}
return 0;
err_filter: if (wwan_rfkill)
rfkill_unregister(wwan_rfkill);
err_wwan:
rfkill_destroy(wwan_rfkill); if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
err_bluetooth:
rfkill_destroy(bluetooth_rfkill); if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
err_wifi:
rfkill_destroy(wifi_rfkill);
return ret;
}
staticvoid dell_cleanup_rfkill(void)
{ if (dell_rbtn_notifier_unregister_func) {
dell_rbtn_notifier_unregister_func(&dell_laptop_rbtn_notifier);
symbol_put(dell_rbtn_notifier_unregister);
dell_rbtn_notifier_unregister_func = NULL;
} else {
i8042_remove_filter(dell_laptop_i8042_filter);
}
cancel_delayed_work_sync(&dell_rfkill_work); if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
rfkill_destroy(wifi_rfkill);
} if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
} if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
}
/* * Derived from information in smbios-keyboard-ctl: * * cbClass 4 * cbSelect 11 * Keyboard illumination * 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 Maximum acceptable days value (0 if days not supported) * * cbArg1 0x1 = Get Current State * cbRES1 Standard return codes (0, -1, -2) * cbRES2, word0 Bitmap of current mode state * 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 * Note: Only One bit can be set * cbRES2, byte2 Currently active auto keyboard illumination triggers. * bit 0 Any keystroke * bit 1 Touchpad activity * bit 2 Pointing stick * bit 3 Any mouse * bits 4-7 Reserved for future use * cbRES2, byte3 Current Timeout on battery * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * bits 5:0 Timeout value (0-63) in sec/min/hr/day * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte * are set upon return from the [Get feature information] call. * cbRES3, byte0 Current setting of ALS value that turns the light on or off. * cbRES3, byte1 Current ALS reading * cbRES3, byte2 Current keyboard light level. * cbRES3, byte3 Current timeout on AC Power * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * Bits 5:0 Timeout value (0-63) in sec/min/hr/day * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte2 * are set upon return from the upon return from the [Get Feature information] call. * * cbArg1 0x2 = Set New State * cbRES1 Standard return codes (0, -1, -2) * cbArg2, word0 Bitmap of current mode state * 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 * Note: Only One bit can be set * cbArg2, byte2 Desired auto keyboard illumination triggers. Must remain inactive to allow * keyboard to turn off automatically. * bit 0 Any keystroke * bit 1 Touchpad activity * bit 2 Pointing stick * bit 3 Any mouse * bits 4-7 Reserved for future use * cbArg2, byte3 Desired Timeout on battery * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * bits 5:0 Timeout value (0-63) in sec/min/hr/day * cbArg3, byte0 Desired setting of ALS value that turns the light on or off. * cbArg3, byte2 Desired keyboard light level. * cbArg3, byte3 Desired Timeout on AC power * bits 7:6 Timeout units indicator: * 00b Seconds * 01b Minutes * 10b Hours * 11b Days * bits 5:0 Timeout value (0-63) in sec/min/hr/day
*/
/* * NOTE: there are three ways to set the keyboard backlight level. * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value). * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels). * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens) * * There are laptops which support only one of these methods. If we want to * support as many machines as possible we need to implement all three methods. * The first two methods use the kbd_state structure. The third uses SMBIOS * tokens. If kbd_info.levels == 0, the machine does not support setting the * keyboard backlight level via kbd_state.level.
*/
staticint kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
{ int ret;
ret = kbd_set_state(state); if (ret == 0) return 0;
/* * When setting the new state fails,try to restore the previous one. * This is needed on some machines where BIOS sets a default state when * setting a new state fails. This default state could be all off.
*/
if (kbd_set_state(old))
pr_err("Setting old previous keyboard state failed\n");
staticinlineint kbd_init_info(void)
{ struct kbd_state state; int ret; int i;
ret = kbd_get_info(&kbd_info); if (ret) return ret;
/* NOTE: Old models without KBD_LED_AC_TOKEN token supports only one * timeout value which is shared for both battery and AC power * settings. So do not try to set AC values on old models.
*/ if ((quirks && quirks->kbd_missing_ac_tag) ||
dell_smbios_find_token(KBD_LED_AC_TOKEN))
kbd_timeout_ac_supported = true;
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_mode_levels[0] is reserved, see below */ for (i = 0; i < 16; ++i) if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes))
kbd_mode_levels[1 + kbd_mode_levels_count++] = i;
/* * Find the first supported mode and assign to kbd_mode_levels[0]. * This should be 0 (off), but we cannot depend on the BIOS to * support 0.
*/ if (kbd_mode_levels_count > 0) { for (i = 0; i < 16; ++i) { if (BIT(i) & kbd_info.modes) {
kbd_mode_levels[0] = i; break;
}
}
kbd_mode_levels_count++;
}
return 0;
}
staticinlinevoid __init kbd_init_tokens(void)
{ int i;
for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) if (dell_smbios_find_token(kbd_tokens[i]))
kbd_token_bits |= BIT(i);
}
staticvoid __init kbd_init(void)
{ int ret;
if (quirks && quirks->kbd_led_not_present) return;
ret = kbd_init_info();
kbd_init_tokens();
/* * Only supports keyboard backlight when it has at least two modes.
*/ if ((ret == 0 && (kbd_info.levels != 0 || kbd_mode_levels_count >= 2))
|| kbd_get_valid_token_counts() >= 2)
kbd_led_present = true;
}
ret = sscanf(buf, "%d %c", &value, &ch); if (ret < 1) return -EINVAL; elseif (ret == 1)
ch = 's';
if (value < 0) return -EINVAL;
convert = false;
switch (ch) { case's': if (value > kbd_info.seconds)
convert = true;
unit = KBD_TIMEOUT_SECONDS; break; case'm': if (value > kbd_info.minutes)
convert = true;
unit = KBD_TIMEOUT_MINUTES; break; case'h': if (value > kbd_info.hours)
convert = true;
unit = KBD_TIMEOUT_HOURS; break; case'd': if (value > kbd_info.days)
convert = true;
unit = KBD_TIMEOUT_DAYS; break; default: return -EINVAL;
}
if (quirks && quirks->needs_kbd_timeouts)
convert = true;
if (convert) { /* Convert value from current units to seconds */ switch (unit) { case KBD_TIMEOUT_DAYS:
value *= 24;
fallthrough; case KBD_TIMEOUT_HOURS:
value *= 60;
fallthrough; case KBD_TIMEOUT_MINUTES:
value *= 60;
unit = KBD_TIMEOUT_SECONDS;
}
if (quirks && quirks->needs_kbd_timeouts) { for (i = 0; quirks->kbd_timeouts[i] != -1; i++) { if (value <= quirks->kbd_timeouts[i]) {
value = quirks->kbd_timeouts[i]; break;
}
}
}
if (value <= kbd_info.seconds && kbd_info.seconds) {
unit = KBD_TIMEOUT_SECONDS;
} elseif (value / 60 <= kbd_info.minutes && kbd_info.minutes) {
value /= 60;
unit = KBD_TIMEOUT_MINUTES;
} elseif (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) {
value /= (60 * 60);
unit = KBD_TIMEOUT_HOURS;
} elseif (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) {
value /= (60 * 60 * 24);
unit = KBD_TIMEOUT_DAYS;
} else { return -EINVAL;
}
}
ret = kbd_set_state_safe(&new_state, &state); if (ret) goto out;
ret = count;
out:
mutex_unlock(&kbd_led_mutex); return ret;
}
static ssize_t kbd_led_timeout_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct kbd_state state; int value; int ret; int len;
u8 unit;
ret = kbd_get_state(&state); if (ret) return ret;
if (kbd_timeout_ac_supported && power_supply_is_system_supplied() > 0) {
value = state.timeout_value_ac;
unit = state.timeout_unit_ac;
} else {
value = state.timeout_value;
unit = state.timeout_unit;
}
len = sprintf(buf, "%d", value);
switch (unit) { case KBD_TIMEOUT_SECONDS: return len + sprintf(buf+len, "s\n"); case KBD_TIMEOUT_MINUTES: return len + sprintf(buf+len, "m\n"); case KBD_TIMEOUT_HOURS: return len + sprintf(buf+len, "h\n"); case KBD_TIMEOUT_DAYS: return len + sprintf(buf+len, "d\n"); default: return -EINVAL;
}
if (kbd_get_max_level()) {
ret = kbd_get_state(&state); if (ret) return 0;
ret = kbd_get_level(&state); if (ret < 0) return 0; return ret;
}
if (kbd_get_valid_token_counts()) {
ret = kbd_get_first_active_token_bit(); if (ret < 0) return 0; for (num = kbd_token_bits; num != 0 && ret > 0; --ret)
num &= num - 1; /* clear the first bit set */ if (num == 0) return 0; return ffs(num) - 1;
}
pr_warn("Keyboard brightness level control not supported\n"); return 0;
}
if (kbd_get_max_level()) {
ret = kbd_get_state(&state); if (ret) goto out;
new_state = state;
ret = kbd_set_level(&new_state, value); if (ret) goto out;
ret = kbd_set_state_safe(&new_state, &state);
} elseif (kbd_get_valid_token_counts()) { for (num = kbd_token_bits; num != 0 && value > 0; --value)
num &= num - 1; /* clear the first bit set */ if (num == 0)
ret = 0; else
ret = kbd_set_token_bit(ffs(num) - 1);
} else {
pr_warn("Keyboard brightness level control not supported\n");
ret = -ENXIO;
}
staticbool dell_battery_mode_is_active(const u16 tokenid)
{ struct calling_interface_token *token; int ret;
ret = dell_battery_read(tokenid); if (ret < 0) returnfalse;
token = dell_smbios_find_token(tokenid); /* token's already verified by dell_battery_read() */
return token->value == (u16) ret;
}
/* * The rules: the minimum start charging value is 50%. The maximum * start charging value is 95%. The minimum end charging value is * 55%. The maximum end charging value is 100%. And finally, there * has to be at least a 5% difference between start & end values.
*/ #define CHARGE_START_MIN 50 #define CHARGE_START_MAX 95 #define CHARGE_END_MIN 55 #define CHARGE_END_MAX 100 #define CHARGE_MIN_DIFF 5
staticint dell_battery_set_custom_charge_start(int start)
{ struct calling_interface_buffer buffer; int end;
start = clamp(start, CHARGE_START_MIN, CHARGE_START_MAX);
end = dell_battery_read(BAT_CUSTOM_CHARGE_END); if (end < 0) return end; if ((end - start) < CHARGE_MIN_DIFF)
start = end - CHARGE_MIN_DIFF;
charge_type = power_supply_charge_types_parse(battery_supported_modes, buf); if (charge_type < 0) return charge_type;
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) { if (battery_modes[i].charge_type == charge_type) break;
} if (i == ARRAY_SIZE(battery_modes)) return -ENOENT;
err = dell_battery_set_mode(battery_modes[i].token); if (err) return err;
staticbool dell_battery_supported(struct power_supply *battery)
{ /* We currently only support the primary battery */ return strcmp(battery->desc->name, "BAT0") == 0;
}
staticint dell_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
{ /* Return 0 instead of an error to avoid being unloaded */ if (!dell_battery_supported(battery)) return 0;
if (battery_supported_modes != 0)
battery_hook_register(&dell_battery_hook);
}
staticvoid dell_battery_exit(void)
{ if (battery_supported_modes != 0)
battery_hook_unregister(&dell_battery_hook);
}
staticint __init dell_init(void)
{ struct calling_interface_buffer buffer; int max_intensity = 0; int ret;
if (!dmi_check_system(dell_device_table)) return -ENODEV;
quirks = NULL; /* find if this machine support other functions */
dmi_check_system(dell_quirks);
ret = platform_driver_register(&platform_driver); if (ret) goto fail_platform_driver;
platform_device = platform_device_alloc("dell-laptop", PLATFORM_DEVID_NONE); if (!platform_device) {
ret = -ENOMEM; goto fail_platform_device1;
}
ret = platform_device_add(platform_device); if (ret) goto fail_platform_device2;
ret = dell_setup_rfkill();
if (ret) {
pr_warn("Unable to setup rfkill\n"); goto fail_rfkill;
}
if (quirks && quirks->touchpad_led)
touchpad_led_init(&platform_device->dev);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.