aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGabriele Mazzotta <gabriele.mzt@gmail.com>2015-02-19 05:58:29 -0500
committerDarren Hart <dvhart@linux.intel.com>2015-04-07 23:57:32 -0400
commit6cff8d60aa0aba5583ecda09984dbcb2f24cc28d (patch)
tree654725b0acb42475010569b20a95175807c7b7d2
parent9659bee43bf010d4c7037fee9c023124f144c6ca (diff)
platform: x86: dell-laptop: Add support for keyboard backlight
This patch adds the support for the configuration of the keyboard backlight on supported Dell laptops. With this patch it is possible to set: * keyboard backlight level * timeout after which the backlight will be automatically turned off * input activity triggers (keyboard, touchpad, mouse) that enable the backlight * ambient light settings The settings are exposed via /sys/class/leds/dell::kbd_backlight/ The code is based on the newly released documentation by Dell in the libsmbios project. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Gabriele Mazzotta <gabriele.mzt@gmail.com> Cc: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
-rw-r--r--Documentation/ABI/testing/sysfs-platform-dell-laptop69
-rw-r--r--drivers/platform/x86/dell-laptop.c1089
2 files changed, 1152 insertions, 6 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-dell-laptop b/Documentation/ABI/testing/sysfs-platform-dell-laptop
new file mode 100644
index 000000000000..8c6a0b8e1131
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-dell-laptop
@@ -0,0 +1,69 @@
1What: /sys/class/leds/dell::kbd_backlight/als_enabled
2Date: December 2014
3KernelVersion: 3.19
4Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
5 Pali Rohár <pali.rohar@gmail.com>
6Description:
7 This file allows to control the automatic keyboard
8 illumination mode on some systems that have an ambient
9 light sensor. Write 1 to this file to enable the auto
10 mode, 0 to disable it.
11
12What: /sys/class/leds/dell::kbd_backlight/als_setting
13Date: December 2014
14KernelVersion: 3.19
15Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
16 Pali Rohár <pali.rohar@gmail.com>
17Description:
18 This file allows to specifiy the on/off threshold value,
19 as reported by the ambient light sensor.
20
21What: /sys/class/leds/dell::kbd_backlight/start_triggers
22Date: December 2014
23KernelVersion: 3.19
24Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
25 Pali Rohár <pali.rohar@gmail.com>
26Description:
27 This file allows to control the input triggers that
28 turn on the keyboard backlight illumination that is
29 disabled because of inactivity.
30 Read the file to see the triggers available. The ones
31 enabled are preceded by '+', those disabled by '-'.
32
33 To enable a trigger, write its name preceded by '+' to
34 this file. To disable a trigger, write its name preceded
35 by '-' instead.
36
37 For example, to enable the keyboard as trigger run:
38 echo +keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
39 To disable it:
40 echo -keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers
41
42 Note that not all the available triggers can be configured.
43
44What: /sys/class/leds/dell::kbd_backlight/stop_timeout
45Date: December 2014
46KernelVersion: 3.19
47Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>,
48 Pali Rohár <pali.rohar@gmail.com>
49Description:
50 This file allows to specify the interval after which the
51 keyboard illumination is disabled because of inactivity.
52 The timeouts are expressed in seconds, minutes, hours and
53 days, for which the symbols are 's', 'm', 'h' and 'd'
54 respectively.
55
56 To configure the timeout, write to this file a value along
57 with any the above units. If no unit is specified, the value
58 is assumed to be expressed in seconds.
59
60 For example, to set the timeout to 10 minutes run:
61 echo 10m > /sys/class/leds/dell::kbd_backlight/stop_timeout
62
63 Note that when this file is read, the returned value might be
64 expressed in a different unit than the one used when the timeout
65 was set.
66
67 Also note that only some timeouts are supported and that
68 some systems might fall back to a specific timeout in case
69 an invalid timeout is written to this file.
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 3d21efe11d7b..d688d806a8a5 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -2,9 +2,11 @@
2 * Driver for Dell laptop extras 2 * Driver for Dell laptop extras
3 * 3 *
4 * Copyright (c) Red Hat <mjg@redhat.com> 4 * Copyright (c) Red Hat <mjg@redhat.com>
5 * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
6 * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
5 * 7 *
6 * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell 8 * Based on documentation in the libsmbios package:
7 * Inc. 9 * Copyright (C) 2005-2014 Dell Inc.
8 * 10 *
9 * This program is free software; you can redistribute it and/or modify 11 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as 12 * it under the terms of the GNU General Public License version 2 as
@@ -32,6 +34,13 @@
32#include "../../firmware/dcdbas.h" 34#include "../../firmware/dcdbas.h"
33 35
34#define BRIGHTNESS_TOKEN 0x7d 36#define BRIGHTNESS_TOKEN 0x7d
37#define KBD_LED_OFF_TOKEN 0x01E1
38#define KBD_LED_ON_TOKEN 0x01E2
39#define KBD_LED_AUTO_TOKEN 0x01E3
40#define KBD_LED_AUTO_25_TOKEN 0x02EA
41#define KBD_LED_AUTO_50_TOKEN 0x02EB
42#define KBD_LED_AUTO_75_TOKEN 0x02EC
43#define KBD_LED_AUTO_100_TOKEN 0x02F6
35 44
36/* This structure will be modified by the firmware when we enter 45/* This structure will be modified by the firmware when we enter
37 * system management mode, hence the volatiles */ 46 * system management mode, hence the volatiles */
@@ -62,6 +71,13 @@ struct calling_interface_structure {
62 71
63struct quirk_entry { 72struct quirk_entry {
64 u8 touchpad_led; 73 u8 touchpad_led;
74
75 int needs_kbd_timeouts;
76 /*
77 * Ordered list of timeouts expressed in seconds.
78 * The list must end with -1
79 */
80 int kbd_timeouts[];
65}; 81};
66 82
67static struct quirk_entry *quirks; 83static struct quirk_entry *quirks;
@@ -76,6 +92,15 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
76 return 1; 92 return 1;
77} 93}
78 94
95/*
96 * These values come from Windows utility provided by Dell. If any other value
97 * is used then BIOS silently set timeout to 0 without any error message.
98 */
99static struct quirk_entry quirk_dell_xps13_9333 = {
100 .needs_kbd_timeouts = 1,
101 .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
102};
103
79static int da_command_address; 104static int da_command_address;
80static int da_command_code; 105static int da_command_code;
81static int da_num_tokens; 106static int da_num_tokens;
@@ -267,6 +292,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
267 }, 292 },
268 .driver_data = &quirk_dell_vostro_v130, 293 .driver_data = &quirk_dell_vostro_v130,
269 }, 294 },
295 {
296 .callback = dmi_matched,
297 .ident = "Dell XPS13 9333",
298 .matches = {
299 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
300 DMI_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
301 },
302 .driver_data = &quirk_dell_xps13_9333,
303 },
270 { } 304 { }
271}; 305};
272 306
@@ -331,17 +365,29 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
331 } 365 }
332} 366}
333 367
334static int find_token_location(int tokenid) 368static int find_token_id(int tokenid)
335{ 369{
336 int i; 370 int i;
371
337 for (i = 0; i < da_num_tokens; i++) { 372 for (i = 0; i < da_num_tokens; i++) {
338 if (da_tokens[i].tokenID == tokenid) 373 if (da_tokens[i].tokenID == tokenid)
339 return da_tokens[i].location; 374 return i;
340 } 375 }
341 376
342 return -1; 377 return -1;
343} 378}
344 379
380static int find_token_location(int tokenid)
381{
382 int id;
383
384 id = find_token_id(tokenid);
385 if (id == -1)
386 return -1;
387
388 return da_tokens[id].location;
389}
390
345static struct calling_interface_buffer * 391static struct calling_interface_buffer *
346dell_send_request(struct calling_interface_buffer *buffer, int class, 392dell_send_request(struct calling_interface_buffer *buffer, int class,
347 int select) 393 int select)
@@ -362,6 +408,20 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
362 return buffer; 408 return buffer;
363} 409}
364 410
411static inline int dell_smi_error(int value)
412{
413 switch (value) {
414 case 0: /* Completed successfully */
415 return 0;
416 case -1: /* Completed with error */
417 return -EIO;
418 case -2: /* Function not supported */
419 return -ENXIO;
420 default: /* Unknown error */
421 return -EINVAL;
422 }
423}
424
365/* Derived from information in DellWirelessCtl.cpp: 425/* Derived from information in DellWirelessCtl.cpp:
366 Class 17, select 11 is radio control. It returns an array of 32-bit values. 426 Class 17, select 11 is radio control. It returns an array of 32-bit values.
367 427
@@ -716,7 +776,7 @@ static int dell_send_intensity(struct backlight_device *bd)
716 else 776 else
717 dell_send_request(buffer, 1, 1); 777 dell_send_request(buffer, 1, 1);
718 778
719out: 779 out:
720 release_buffer(); 780 release_buffer();
721 return ret; 781 return ret;
722} 782}
@@ -740,7 +800,7 @@ static int dell_get_intensity(struct backlight_device *bd)
740 800
741 ret = buffer->output[1]; 801 ret = buffer->output[1];
742 802
743out: 803 out:
744 release_buffer(); 804 release_buffer();
745 return ret; 805 return ret;
746} 806}
@@ -789,6 +849,1018 @@ static void touchpad_led_exit(void)
789 led_classdev_unregister(&touchpad_led); 849 led_classdev_unregister(&touchpad_led);
790} 850}
791 851
852/*
853 * Derived from information in smbios-keyboard-ctl:
854 *
855 * cbClass 4
856 * cbSelect 11
857 * Keyboard illumination
858 * cbArg1 determines the function to be performed
859 *
860 * cbArg1 0x0 = Get Feature Information
861 * cbRES1 Standard return codes (0, -1, -2)
862 * cbRES2, word0 Bitmap of user-selectable modes
863 * bit 0 Always off (All systems)
864 * bit 1 Always on (Travis ATG, Siberia)
865 * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG)
866 * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off
867 * bit 4 Auto: Input-activity-based On; input-activity based Off
868 * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off
869 * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off
870 * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off
871 * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off
872 * bits 9-15 Reserved for future use
873 * cbRES2, byte2 Reserved for future use
874 * cbRES2, byte3 Keyboard illumination type
875 * 0 Reserved
876 * 1 Tasklight
877 * 2 Backlight
878 * 3-255 Reserved for future use
879 * cbRES3, byte0 Supported auto keyboard illumination trigger bitmap.
880 * bit 0 Any keystroke
881 * bit 1 Touchpad activity
882 * bit 2 Pointing stick
883 * bit 3 Any mouse
884 * bits 4-7 Reserved for future use
885 * cbRES3, byte1 Supported timeout unit bitmap
886 * bit 0 Seconds
887 * bit 1 Minutes
888 * bit 2 Hours
889 * bit 3 Days
890 * bits 4-7 Reserved for future use
891 * cbRES3, byte2 Number of keyboard light brightness levels
892 * cbRES4, byte0 Maximum acceptable seconds value (0 if seconds not supported).
893 * cbRES4, byte1 Maximum acceptable minutes value (0 if minutes not supported).
894 * cbRES4, byte2 Maximum acceptable hours value (0 if hours not supported).
895 * cbRES4, byte3 Maximum acceptable days value (0 if days not supported)
896 *
897 * cbArg1 0x1 = Get Current State
898 * cbRES1 Standard return codes (0, -1, -2)
899 * cbRES2, word0 Bitmap of current mode state
900 * bit 0 Always off (All systems)
901 * bit 1 Always on (Travis ATG, Siberia)
902 * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG)
903 * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off
904 * bit 4 Auto: Input-activity-based On; input-activity based Off
905 * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off
906 * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off
907 * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off
908 * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off
909 * bits 9-15 Reserved for future use
910 * Note: Only One bit can be set
911 * cbRES2, byte2 Currently active auto keyboard illumination triggers.
912 * bit 0 Any keystroke
913 * bit 1 Touchpad activity
914 * bit 2 Pointing stick
915 * bit 3 Any mouse
916 * bits 4-7 Reserved for future use
917 * cbRES2, byte3 Current Timeout
918 * bits 7:6 Timeout units indicator:
919 * 00b Seconds
920 * 01b Minutes
921 * 10b Hours
922 * 11b Days
923 * bits 5:0 Timeout value (0-63) in sec/min/hr/day
924 * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte
925 * are set upon return from the [Get feature information] call.
926 * cbRES3, byte0 Current setting of ALS value that turns the light on or off.
927 * cbRES3, byte1 Current ALS reading
928 * cbRES3, byte2 Current keyboard light level.
929 *
930 * cbArg1 0x2 = Set New State
931 * cbRES1 Standard return codes (0, -1, -2)
932 * cbArg2, word0 Bitmap of current mode state
933 * bit 0 Always off (All systems)
934 * bit 1 Always on (Travis ATG, Siberia)
935 * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG)
936 * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off
937 * bit 4 Auto: Input-activity-based On; input-activity based Off
938 * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off
939 * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off
940 * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off
941 * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off
942 * bits 9-15 Reserved for future use
943 * Note: Only One bit can be set
944 * cbArg2, byte2 Desired auto keyboard illumination triggers. Must remain inactive to allow
945 * keyboard to turn off automatically.
946 * bit 0 Any keystroke
947 * bit 1 Touchpad activity
948 * bit 2 Pointing stick
949 * bit 3 Any mouse
950 * bits 4-7 Reserved for future use
951 * cbArg2, byte3 Desired Timeout
952 * bits 7:6 Timeout units indicator:
953 * 00b Seconds
954 * 01b Minutes
955 * 10b Hours
956 * 11b Days
957 * bits 5:0 Timeout value (0-63) in sec/min/hr/day
958 * cbArg3, byte0 Desired setting of ALS value that turns the light on or off.
959 * cbArg3, byte2 Desired keyboard light level.
960 */
961
962
963enum kbd_timeout_unit {
964 KBD_TIMEOUT_SECONDS = 0,
965 KBD_TIMEOUT_MINUTES,
966 KBD_TIMEOUT_HOURS,
967 KBD_TIMEOUT_DAYS,
968};
969
970enum kbd_mode_bit {
971 KBD_MODE_BIT_OFF = 0,
972 KBD_MODE_BIT_ON,
973 KBD_MODE_BIT_ALS,
974 KBD_MODE_BIT_TRIGGER_ALS,
975 KBD_MODE_BIT_TRIGGER,
976 KBD_MODE_BIT_TRIGGER_25,
977 KBD_MODE_BIT_TRIGGER_50,
978 KBD_MODE_BIT_TRIGGER_75,
979 KBD_MODE_BIT_TRIGGER_100,
980};
981
982#define kbd_is_als_mode_bit(bit) \
983 ((bit) == KBD_MODE_BIT_ALS || (bit) == KBD_MODE_BIT_TRIGGER_ALS)
984#define kbd_is_trigger_mode_bit(bit) \
985 ((bit) >= KBD_MODE_BIT_TRIGGER_ALS && (bit) <= KBD_MODE_BIT_TRIGGER_100)
986#define kbd_is_level_mode_bit(bit) \
987 ((bit) >= KBD_MODE_BIT_TRIGGER_25 && (bit) <= KBD_MODE_BIT_TRIGGER_100)
988
989struct kbd_info {
990 u16 modes;
991 u8 type;
992 u8 triggers;
993 u8 levels;
994 u8 seconds;
995 u8 minutes;
996 u8 hours;
997 u8 days;
998};
999
1000struct kbd_state {
1001 u8 mode_bit;
1002 u8 triggers;
1003 u8 timeout_value;
1004 u8 timeout_unit;
1005 u8 als_setting;
1006 u8 als_value;
1007 u8 level;
1008};
1009
1010static const int kbd_tokens[] = {
1011 KBD_LED_OFF_TOKEN,
1012 KBD_LED_AUTO_25_TOKEN,
1013 KBD_LED_AUTO_50_TOKEN,
1014 KBD_LED_AUTO_75_TOKEN,
1015 KBD_LED_AUTO_100_TOKEN,
1016 KBD_LED_ON_TOKEN,
1017};
1018
1019static u16 kbd_token_bits;
1020
1021static struct kbd_info kbd_info;
1022static bool kbd_als_supported;
1023static bool kbd_triggers_supported;
1024
1025static u8 kbd_mode_levels[16];
1026static int kbd_mode_levels_count;
1027
1028static u8 kbd_previous_level;
1029static u8 kbd_previous_mode_bit;
1030
1031static bool kbd_led_present;
1032
1033/*
1034 * NOTE: there are three ways to set the keyboard backlight level.
1035 * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value).
1036 * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels).
1037 * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens)
1038 *
1039 * There are laptops which support only one of these methods. If we want to
1040 * support as many machines as possible we need to implement all three methods.
1041 * The first two methods use the kbd_state structure. The third uses SMBIOS
1042 * tokens. If kbd_info.levels == 0, the machine does not support setting the
1043 * keyboard backlight level via kbd_state.level.
1044 */
1045
1046static int kbd_get_info(struct kbd_info *info)
1047{
1048 u8 units;
1049 int ret;
1050
1051 get_buffer();
1052
1053 buffer->input[0] = 0x0;
1054 dell_send_request(buffer, 4, 11);
1055 ret = buffer->output[0];
1056
1057 if (ret) {
1058 ret = dell_smi_error(ret);
1059 goto out;
1060 }
1061
1062 info->modes = buffer->output[1] & 0xFFFF;
1063 info->type = (buffer->output[1] >> 24) & 0xFF;
1064 info->triggers = buffer->output[2] & 0xFF;
1065 units = (buffer->output[2] >> 8) & 0xFF;
1066 info->levels = (buffer->output[2] >> 16) & 0xFF;
1067
1068 if (units & BIT(0))
1069 info->seconds = (buffer->output[3] >> 0) & 0xFF;
1070 if (units & BIT(1))
1071 info->minutes = (buffer->output[3] >> 8) & 0xFF;
1072 if (units & BIT(2))
1073 info->hours = (buffer->output[3] >> 16) & 0xFF;
1074 if (units & BIT(3))
1075 info->days = (buffer->output[3] >> 24) & 0xFF;
1076
1077 out:
1078 release_buffer();
1079 return ret;
1080}
1081
1082static unsigned int kbd_get_max_level(void)
1083{
1084 if (kbd_info.levels != 0)
1085 return kbd_info.levels;
1086 if (kbd_mode_levels_count > 0)
1087 return kbd_mode_levels_count - 1;
1088 return 0;
1089}
1090
1091static int kbd_get_level(struct kbd_state *state)
1092{
1093 int i;
1094
1095 if (kbd_info.levels != 0)
1096 return state->level;
1097
1098 if (kbd_mode_levels_count > 0) {
1099 for (i = 0; i < kbd_mode_levels_count; ++i)
1100 if (kbd_mode_levels[i] == state->mode_bit)
1101 return i;
1102 return 0;
1103 }
1104
1105 return -EINVAL;
1106}
1107
1108static int kbd_set_level(struct kbd_state *state, u8 level)
1109{
1110 if (kbd_info.levels != 0) {
1111 if (level != 0)
1112 kbd_previous_level = level;
1113 if (state->level == level)
1114 return 0;
1115 state->level = level;
1116 if (level != 0 && state->mode_bit == KBD_MODE_BIT_OFF)
1117 state->mode_bit = kbd_previous_mode_bit;
1118 else if (level == 0 && state->mode_bit != KBD_MODE_BIT_OFF) {
1119 kbd_previous_mode_bit = state->mode_bit;
1120 state->mode_bit = KBD_MODE_BIT_OFF;
1121 }
1122 return 0;
1123 }
1124
1125 if (kbd_mode_levels_count > 0 && level < kbd_mode_levels_count) {
1126 if (level != 0)
1127 kbd_previous_level = level;
1128 state->mode_bit = kbd_mode_levels[level];
1129 return 0;
1130 }
1131
1132 return -EINVAL;
1133}
1134
1135static int kbd_get_state(struct kbd_state *state)
1136{
1137 int ret;
1138
1139 get_buffer();
1140
1141 buffer->input[0] = 0x1;
1142 dell_send_request(buffer, 4, 11);
1143 ret = buffer->output[0];
1144
1145 if (ret) {
1146 ret = dell_smi_error(ret);
1147 goto out;
1148 }
1149
1150 state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
1151 if (state->mode_bit != 0)
1152 state->mode_bit--;
1153
1154 state->triggers = (buffer->output[1] >> 16) & 0xFF;
1155 state->timeout_value = (buffer->output[1] >> 24) & 0x3F;
1156 state->timeout_unit = (buffer->output[1] >> 30) & 0x3;
1157 state->als_setting = buffer->output[2] & 0xFF;
1158 state->als_value = (buffer->output[2] >> 8) & 0xFF;
1159 state->level = (buffer->output[2] >> 16) & 0xFF;
1160
1161 out:
1162 release_buffer();
1163 return ret;
1164}
1165
1166static int kbd_set_state(struct kbd_state *state)
1167{
1168 int ret;
1169
1170 get_buffer();
1171 buffer->input[0] = 0x2;
1172 buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
1173 buffer->input[1] |= (state->triggers & 0xFF) << 16;
1174 buffer->input[1] |= (state->timeout_value & 0x3F) << 24;
1175 buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
1176 buffer->input[2] = state->als_setting & 0xFF;
1177 buffer->input[2] |= (state->level & 0xFF) << 16;
1178 dell_send_request(buffer, 4, 11);
1179 ret = buffer->output[0];
1180 release_buffer();
1181
1182 return dell_smi_error(ret);
1183}
1184
1185static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
1186{
1187 int ret;
1188
1189 ret = kbd_set_state(state);
1190 if (ret == 0)
1191 return 0;
1192
1193 /*
1194 * When setting the new state fails,try to restore the previous one.
1195 * This is needed on some machines where BIOS sets a default state when
1196 * setting a new state fails. This default state could be all off.
1197 */
1198
1199 if (kbd_set_state(old))
1200 pr_err("Setting old previous keyboard state failed\n");
1201
1202 return ret;
1203}
1204
1205static int kbd_set_token_bit(u8 bit)
1206{
1207 int id;
1208 int ret;
1209
1210 if (bit >= ARRAY_SIZE(kbd_tokens))
1211 return -EINVAL;
1212
1213 id = find_token_id(kbd_tokens[bit]);
1214 if (id == -1)
1215 return -EINVAL;
1216
1217 get_buffer();
1218 buffer->input[0] = da_tokens[id].location;
1219 buffer->input[1] = da_tokens[id].value;
1220 dell_send_request(buffer, 1, 0);
1221 ret = buffer->output[0];
1222 release_buffer();
1223
1224 return dell_smi_error(ret);
1225}
1226
1227static int kbd_get_token_bit(u8 bit)
1228{
1229 int id;
1230 int ret;
1231 int val;
1232
1233 if (bit >= ARRAY_SIZE(kbd_tokens))
1234 return -EINVAL;
1235
1236 id = find_token_id(kbd_tokens[bit]);
1237 if (id == -1)
1238 return -EINVAL;
1239
1240 get_buffer();
1241 buffer->input[0] = da_tokens[id].location;
1242 dell_send_request(buffer, 0, 0);
1243 ret = buffer->output[0];
1244 val = buffer->output[1];
1245 release_buffer();
1246
1247 if (ret)
1248 return dell_smi_error(ret);
1249
1250 return (val == da_tokens[id].value);
1251}
1252
1253static int kbd_get_first_active_token_bit(void)
1254{
1255 int i;
1256 int ret;
1257
1258 for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) {
1259 ret = kbd_get_token_bit(i);
1260 if (ret == 1)
1261 return i;
1262 }
1263
1264 return ret;
1265}
1266
1267static int kbd_get_valid_token_counts(void)
1268{
1269 return hweight16(kbd_token_bits);
1270}
1271
1272static inline int kbd_init_info(void)
1273{
1274 struct kbd_state state;
1275 int ret;
1276 int i;
1277
1278 ret = kbd_get_info(&kbd_info);
1279 if (ret)
1280 return ret;
1281
1282 kbd_get_state(&state);
1283
1284 /* NOTE: timeout value is stored in 6 bits so max value is 63 */
1285 if (kbd_info.seconds > 63)
1286 kbd_info.seconds = 63;
1287 if (kbd_info.minutes > 63)
1288 kbd_info.minutes = 63;
1289 if (kbd_info.hours > 63)
1290 kbd_info.hours = 63;
1291 if (kbd_info.days > 63)
1292 kbd_info.days = 63;
1293
1294 /* NOTE: On tested machines ON mode did not work and caused
1295 * problems (turned backlight off) so do not use it
1296 */
1297 kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON);
1298
1299 kbd_previous_level = kbd_get_level(&state);
1300 kbd_previous_mode_bit = state.mode_bit;
1301
1302 if (kbd_previous_level == 0 && kbd_get_max_level() != 0)
1303 kbd_previous_level = 1;
1304
1305 if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) {
1306 kbd_previous_mode_bit =
1307 ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF));
1308 if (kbd_previous_mode_bit != 0)
1309 kbd_previous_mode_bit--;
1310 }
1311
1312 if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) |
1313 BIT(KBD_MODE_BIT_TRIGGER_ALS)))
1314 kbd_als_supported = true;
1315
1316 if (kbd_info.modes & (
1317 BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) |
1318 BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) |
1319 BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100)
1320 ))
1321 kbd_triggers_supported = true;
1322
1323 /* kbd_mode_levels[0] is reserved, see below */
1324 for (i = 0; i < 16; ++i)
1325 if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes))
1326 kbd_mode_levels[1 + kbd_mode_levels_count++] = i;
1327
1328 /*
1329 * Find the first supported mode and assign to kbd_mode_levels[0].
1330 * This should be 0 (off), but we cannot depend on the BIOS to
1331 * support 0.
1332 */
1333 if (kbd_mode_levels_count > 0) {
1334 for (i = 0; i < 16; ++i) {
1335 if (BIT(i) & kbd_info.modes) {
1336 kbd_mode_levels[0] = i;
1337 break;
1338 }
1339 }
1340 kbd_mode_levels_count++;
1341 }
1342
1343 return 0;
1344
1345}
1346
1347static inline void kbd_init_tokens(void)
1348{
1349 int i;
1350
1351 for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
1352 if (find_token_id(kbd_tokens[i]) != -1)
1353 kbd_token_bits |= BIT(i);
1354}
1355
1356static void kbd_init(void)
1357{
1358 int ret;
1359
1360 ret = kbd_init_info();
1361 kbd_init_tokens();
1362
1363 if (kbd_token_bits != 0 || ret == 0)
1364 kbd_led_present = true;
1365}
1366
1367static ssize_t kbd_led_timeout_store(struct device *dev,
1368 struct device_attribute *attr,
1369 const char *buf, size_t count)
1370{
1371 struct kbd_state new_state;
1372 struct kbd_state state;
1373 bool convert;
1374 int value;
1375 int ret;
1376 char ch;
1377 u8 unit;
1378 int i;
1379
1380 ret = sscanf(buf, "%d %c", &value, &ch);
1381 if (ret < 1)
1382 return -EINVAL;
1383 else if (ret == 1)
1384 ch = 's';
1385
1386 if (value < 0)
1387 return -EINVAL;
1388
1389 convert = false;
1390
1391 switch (ch) {
1392 case 's':
1393 if (value > kbd_info.seconds)
1394 convert = true;
1395 unit = KBD_TIMEOUT_SECONDS;
1396 break;
1397 case 'm':
1398 if (value > kbd_info.minutes)
1399 convert = true;
1400 unit = KBD_TIMEOUT_MINUTES;
1401 break;
1402 case 'h':
1403 if (value > kbd_info.hours)
1404 convert = true;
1405 unit = KBD_TIMEOUT_HOURS;
1406 break;
1407 case 'd':
1408 if (value > kbd_info.days)
1409 convert = true;
1410 unit = KBD_TIMEOUT_DAYS;
1411 break;
1412 default:
1413 return -EINVAL;
1414 }
1415
1416 if (quirks && quirks->needs_kbd_timeouts)
1417 convert = true;
1418
1419 if (convert) {
1420 /* Convert value from current units to seconds */
1421 switch (unit) {
1422 case KBD_TIMEOUT_DAYS:
1423 value *= 24;
1424 case KBD_TIMEOUT_HOURS:
1425 value *= 60;
1426 case KBD_TIMEOUT_MINUTES:
1427 value *= 60;
1428 unit = KBD_TIMEOUT_SECONDS;
1429 }
1430
1431 if (quirks && quirks->needs_kbd_timeouts) {
1432 for (i = 0; quirks->kbd_timeouts[i] != -1; i++) {
1433 if (value <= quirks->kbd_timeouts[i]) {
1434 value = quirks->kbd_timeouts[i];
1435 break;
1436 }
1437 }
1438 }
1439
1440 if (value <= kbd_info.seconds && kbd_info.seconds) {
1441 unit = KBD_TIMEOUT_SECONDS;
1442 } else if (value / 60 <= kbd_info.minutes && kbd_info.minutes) {
1443 value /= 60;
1444 unit = KBD_TIMEOUT_MINUTES;
1445 } else if (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) {
1446 value /= (60 * 60);
1447 unit = KBD_TIMEOUT_HOURS;
1448 } else if (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) {
1449 value /= (60 * 60 * 24);
1450 unit = KBD_TIMEOUT_DAYS;
1451 } else {
1452 return -EINVAL;
1453 }
1454 }
1455
1456 ret = kbd_get_state(&state);
1457 if (ret)
1458 return ret;
1459
1460 new_state = state;
1461 new_state.timeout_value = value;
1462 new_state.timeout_unit = unit;
1463
1464 ret = kbd_set_state_safe(&new_state, &state);
1465 if (ret)
1466 return ret;
1467
1468 return count;
1469}
1470
1471static ssize_t kbd_led_timeout_show(struct device *dev,
1472 struct device_attribute *attr, char *buf)
1473{
1474 struct kbd_state state;
1475 int ret;
1476 int len;
1477
1478 ret = kbd_get_state(&state);
1479 if (ret)
1480 return ret;
1481
1482 len = sprintf(buf, "%d", state.timeout_value);
1483
1484 switch (state.timeout_unit) {
1485 case KBD_TIMEOUT_SECONDS:
1486 return len + sprintf(buf+len, "s\n");
1487 case KBD_TIMEOUT_MINUTES:
1488 return len + sprintf(buf+len, "m\n");
1489 case KBD_TIMEOUT_HOURS:
1490 return len + sprintf(buf+len, "h\n");
1491 case KBD_TIMEOUT_DAYS:
1492 return len + sprintf(buf+len, "d\n");
1493 default:
1494 return -EINVAL;
1495 }
1496
1497 return len;
1498}
1499
1500static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR,
1501 kbd_led_timeout_show, kbd_led_timeout_store);
1502
1503static const char * const kbd_led_triggers[] = {
1504 "keyboard",
1505 "touchpad",
1506 /*"trackstick"*/ NULL, /* NOTE: trackstick is just alias for touchpad */
1507 "mouse",
1508};
1509
1510static ssize_t kbd_led_triggers_store(struct device *dev,
1511 struct device_attribute *attr,
1512 const char *buf, size_t count)
1513{
1514 struct kbd_state new_state;
1515 struct kbd_state state;
1516 bool triggers_enabled = false;
1517 int trigger_bit = -1;
1518 char trigger[21];
1519 int i, ret;
1520
1521 ret = sscanf(buf, "%20s", trigger);
1522 if (ret != 1)
1523 return -EINVAL;
1524
1525 if (trigger[0] != '+' && trigger[0] != '-')
1526 return -EINVAL;
1527
1528 ret = kbd_get_state(&state);
1529 if (ret)
1530 return ret;
1531
1532 if (kbd_triggers_supported)
1533 triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
1534
1535 if (kbd_triggers_supported) {
1536 for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
1537 if (!(kbd_info.triggers & BIT(i)))
1538 continue;
1539 if (!kbd_led_triggers[i])
1540 continue;
1541 if (strcmp(trigger+1, kbd_led_triggers[i]) != 0)
1542 continue;
1543 if (trigger[0] == '+' &&
1544 triggers_enabled && (state.triggers & BIT(i)))
1545 return count;
1546 if (trigger[0] == '-' &&
1547 (!triggers_enabled || !(state.triggers & BIT(i))))
1548 return count;
1549 trigger_bit = i;
1550 break;
1551 }
1552 }
1553
1554 if (trigger_bit != -1) {
1555 new_state = state;
1556 if (trigger[0] == '+')
1557 new_state.triggers |= BIT(trigger_bit);
1558 else {
1559 new_state.triggers &= ~BIT(trigger_bit);
1560 /* NOTE: trackstick bit (2) must be disabled when
1561 * disabling touchpad bit (1), otherwise touchpad
1562 * bit (1) will not be disabled */
1563 if (trigger_bit == 1)
1564 new_state.triggers &= ~BIT(2);
1565 }
1566 if ((kbd_info.triggers & new_state.triggers) !=
1567 new_state.triggers)
1568 return -EINVAL;
1569 if (new_state.triggers && !triggers_enabled) {
1570 new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
1571 kbd_set_level(&new_state, kbd_previous_level);
1572 } else if (new_state.triggers == 0) {
1573 kbd_set_level(&new_state, 0);
1574 }
1575 if (!(kbd_info.modes & BIT(new_state.mode_bit)))
1576 return -EINVAL;
1577 ret = kbd_set_state_safe(&new_state, &state);
1578 if (ret)
1579 return ret;
1580 if (new_state.mode_bit != KBD_MODE_BIT_OFF)
1581 kbd_previous_mode_bit = new_state.mode_bit;
1582 return count;
1583 }
1584
1585 return -EINVAL;
1586}
1587
1588static ssize_t kbd_led_triggers_show(struct device *dev,
1589 struct device_attribute *attr, char *buf)
1590{
1591 struct kbd_state state;
1592 bool triggers_enabled;
1593 int level, i, ret;
1594 int len = 0;
1595
1596 ret = kbd_get_state(&state);
1597 if (ret)
1598 return ret;
1599
1600 len = 0;
1601
1602 if (kbd_triggers_supported) {
1603 triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
1604 level = kbd_get_level(&state);
1605 for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
1606 if (!(kbd_info.triggers & BIT(i)))
1607 continue;
1608 if (!kbd_led_triggers[i])
1609 continue;
1610 if ((triggers_enabled || level <= 0) &&
1611 (state.triggers & BIT(i)))
1612 buf[len++] = '+';
1613 else
1614 buf[len++] = '-';
1615 len += sprintf(buf+len, "%s ", kbd_led_triggers[i]);
1616 }
1617 }
1618
1619 if (len)
1620 buf[len - 1] = '\n';
1621
1622 return len;
1623}
1624
1625static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR,
1626 kbd_led_triggers_show, kbd_led_triggers_store);
1627
1628static ssize_t kbd_led_als_enabled_store(struct device *dev,
1629 struct device_attribute *attr,
1630 const char *buf, size_t count)
1631{
1632 struct kbd_state new_state;
1633 struct kbd_state state;
1634 bool triggers_enabled = false;
1635 int enable;
1636 int ret;
1637
1638 ret = kstrtoint(buf, 0, &enable);
1639 if (ret)
1640 return ret;
1641
1642 ret = kbd_get_state(&state);
1643 if (ret)
1644 return ret;
1645
1646 if (enable == kbd_is_als_mode_bit(state.mode_bit))
1647 return count;
1648
1649 new_state = state;
1650
1651 if (kbd_triggers_supported)
1652 triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
1653
1654 if (enable) {
1655 if (triggers_enabled)
1656 new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS;
1657 else
1658 new_state.mode_bit = KBD_MODE_BIT_ALS;
1659 } else {
1660 if (triggers_enabled) {
1661 new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
1662 kbd_set_level(&new_state, kbd_previous_level);
1663 } else {
1664 new_state.mode_bit = KBD_MODE_BIT_ON;
1665 }
1666 }
1667 if (!(kbd_info.modes & BIT(new_state.mode_bit)))
1668 return -EINVAL;
1669
1670 ret = kbd_set_state_safe(&new_state, &state);
1671 if (ret)
1672 return ret;
1673 kbd_previous_mode_bit = new_state.mode_bit;
1674
1675 return count;
1676}
1677
1678static ssize_t kbd_led_als_enabled_show(struct device *dev,
1679 struct device_attribute *attr,
1680 char *buf)
1681{
1682 struct kbd_state state;
1683 bool enabled = false;
1684 int ret;
1685
1686 ret = kbd_get_state(&state);
1687 if (ret)
1688 return ret;
1689 enabled = kbd_is_als_mode_bit(state.mode_bit);
1690
1691 return sprintf(buf, "%d\n", enabled ? 1 : 0);
1692}
1693
1694static DEVICE_ATTR(als_enabled, S_IRUGO | S_IWUSR,
1695 kbd_led_als_enabled_show, kbd_led_als_enabled_store);
1696
1697static ssize_t kbd_led_als_setting_store(struct device *dev,
1698 struct device_attribute *attr,
1699 const char *buf, size_t count)
1700{
1701 struct kbd_state state;
1702 struct kbd_state new_state;
1703 u8 setting;
1704 int ret;
1705
1706 ret = kstrtou8(buf, 10, &setting);
1707 if (ret)
1708 return ret;
1709
1710 ret = kbd_get_state(&state);
1711 if (ret)
1712 return ret;
1713
1714 new_state = state;
1715 new_state.als_setting = setting;
1716
1717 ret = kbd_set_state_safe(&new_state, &state);
1718 if (ret)
1719 return ret;
1720
1721 return count;
1722}
1723
1724static ssize_t kbd_led_als_setting_show(struct device *dev,
1725 struct device_attribute *attr,
1726 char *buf)
1727{
1728 struct kbd_state state;
1729 int ret;
1730
1731 ret = kbd_get_state(&state);
1732 if (ret)
1733 return ret;
1734
1735 return sprintf(buf, "%d\n", state.als_setting);
1736}
1737
1738static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR,
1739 kbd_led_als_setting_show, kbd_led_als_setting_store);
1740
1741static struct attribute *kbd_led_attrs[] = {
1742 &dev_attr_stop_timeout.attr,
1743 &dev_attr_start_triggers.attr,
1744 NULL,
1745};
1746
1747static const struct attribute_group kbd_led_group = {
1748 .attrs = kbd_led_attrs,
1749};
1750
1751static struct attribute *kbd_led_als_attrs[] = {
1752 &dev_attr_als_enabled.attr,
1753 &dev_attr_als_setting.attr,
1754 NULL,
1755};
1756
1757static const struct attribute_group kbd_led_als_group = {
1758 .attrs = kbd_led_als_attrs,
1759};
1760
1761static const struct attribute_group *kbd_led_groups[] = {
1762 &kbd_led_group,
1763 &kbd_led_als_group,
1764 NULL,
1765};
1766
1767static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev)
1768{
1769 int ret;
1770 u16 num;
1771 struct kbd_state state;
1772
1773 if (kbd_get_max_level()) {
1774 ret = kbd_get_state(&state);
1775 if (ret)
1776 return 0;
1777 ret = kbd_get_level(&state);
1778 if (ret < 0)
1779 return 0;
1780 return ret;
1781 }
1782
1783 if (kbd_get_valid_token_counts()) {
1784 ret = kbd_get_first_active_token_bit();
1785 if (ret < 0)
1786 return 0;
1787 for (num = kbd_token_bits; num != 0 && ret > 0; --ret)
1788 num &= num - 1; /* clear the first bit set */
1789 if (num == 0)
1790 return 0;
1791 return ffs(num) - 1;
1792 }
1793
1794 pr_warn("Keyboard brightness level control not supported\n");
1795 return 0;
1796}
1797
1798static void kbd_led_level_set(struct led_classdev *led_cdev,
1799 enum led_brightness value)
1800{
1801 struct kbd_state state;
1802 struct kbd_state new_state;
1803 u16 num;
1804
1805 if (kbd_get_max_level()) {
1806 if (kbd_get_state(&state))
1807 return;
1808 new_state = state;
1809 if (kbd_set_level(&new_state, value))
1810 return;
1811 kbd_set_state_safe(&new_state, &state);
1812 return;
1813 }
1814
1815 if (kbd_get_valid_token_counts()) {
1816 for (num = kbd_token_bits; num != 0 && value > 0; --value)
1817 num &= num - 1; /* clear the first bit set */
1818 if (num == 0)
1819 return;
1820 kbd_set_token_bit(ffs(num) - 1);
1821 return;
1822 }
1823
1824 pr_warn("Keyboard brightness level control not supported\n");
1825}
1826
1827static struct led_classdev kbd_led = {
1828 .name = "dell::kbd_backlight",
1829 .brightness_set = kbd_led_level_set,
1830 .brightness_get = kbd_led_level_get,
1831 .groups = kbd_led_groups,
1832};
1833
1834static int __init kbd_led_init(struct device *dev)
1835{
1836 kbd_init();
1837 if (!kbd_led_present)
1838 return -ENODEV;
1839 if (!kbd_als_supported)
1840 kbd_led_groups[1] = NULL;
1841 kbd_led.max_brightness = kbd_get_max_level();
1842 if (!kbd_led.max_brightness) {
1843 kbd_led.max_brightness = kbd_get_valid_token_counts();
1844 if (kbd_led.max_brightness)
1845 kbd_led.max_brightness--;
1846 }
1847 return led_classdev_register(dev, &kbd_led);
1848}
1849
1850static void brightness_set_exit(struct led_classdev *led_cdev,
1851 enum led_brightness value)
1852{
1853 /* Don't change backlight level on exit */
1854};
1855
1856static void kbd_led_exit(void)
1857{
1858 if (!kbd_led_present)
1859 return;
1860 kbd_led.brightness_set = brightness_set_exit;
1861 led_classdev_unregister(&kbd_led);
1862}
1863
792static int __init dell_init(void) 1864static int __init dell_init(void)
793{ 1865{
794 int max_intensity = 0; 1866 int max_intensity = 0;
@@ -841,6 +1913,8 @@ static int __init dell_init(void)
841 if (quirks && quirks->touchpad_led) 1913 if (quirks && quirks->touchpad_led)
842 touchpad_led_init(&platform_device->dev); 1914 touchpad_led_init(&platform_device->dev);
843 1915
1916 kbd_led_init(&platform_device->dev);
1917
844 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); 1918 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
845 if (dell_laptop_dir != NULL) 1919 if (dell_laptop_dir != NULL)
846 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, 1920 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
@@ -908,6 +1982,7 @@ static void __exit dell_exit(void)
908 debugfs_remove_recursive(dell_laptop_dir); 1982 debugfs_remove_recursive(dell_laptop_dir);
909 if (quirks && quirks->touchpad_led) 1983 if (quirks && quirks->touchpad_led)
910 touchpad_led_exit(); 1984 touchpad_led_exit();
1985 kbd_led_exit();
911 i8042_remove_filter(dell_laptop_i8042_filter); 1986 i8042_remove_filter(dell_laptop_i8042_filter);
912 cancel_delayed_work_sync(&dell_rfkill_work); 1987 cancel_delayed_work_sync(&dell_rfkill_work);
913 backlight_device_unregister(dell_backlight_device); 1988 backlight_device_unregister(dell_backlight_device);
@@ -924,5 +1999,7 @@ module_init(dell_init);
924module_exit(dell_exit); 1999module_exit(dell_exit);
925 2000
926MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 2001MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
2002MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
2003MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
927MODULE_DESCRIPTION("Dell laptop driver"); 2004MODULE_DESCRIPTION("Dell laptop driver");
928MODULE_LICENSE("GPL"); 2005MODULE_LICENSE("GPL");