aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorPali Rohár <pali.rohar@gmail.com>2014-11-11 14:21:22 -0500
committerDarren Hart <dvhart@linux.intel.com>2014-11-19 12:25:31 -0500
commit83fc44c32ad8b8bb359d6d0e93071b6b23e831d5 (patch)
tree0bf9aa3be5077e99d8634a137114cf762326ba5b /drivers/platform
parentfb1d97a2c7a6b833043003e933dc1a67dd242d5a (diff)
dell-wmi: Update code for processing WMI events
The WMI buffer can contain multiple events. First value in buffer is length of event followed by data of specified length. After that is next length and next data. When length is zero then there is no more events in bufffer. This patch adds support for processing all events in buffer (not only first) and parse more event types (not only hotkey events). Because of variable length of events sometimes BIOS fills more hotkeys (or other values) into single WMI event. In this case this patch also processes these multiple hotkeys (and not only first one). Some event types are just ignored because kernel is not interested in them (e.g. NIC Link status, battery unplug, ...). This patch is based on DSDT table from Dell Latitude E6440. Code should be backward compatible so will process other events of old types same as before this patch. This patch also fixes a problem with unknown WMI event messages being written to the log. Now all known events are parsed and those which are not interesting to the kernel are dropped without an unknown WMI event message. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Tested-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/dell-wmi.c159
1 files changed, 128 insertions, 31 deletions
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 25721bf20092..e2b6a642b3c5 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -145,57 +145,154 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
145 145
146static struct input_dev *dell_wmi_input_dev; 146static struct input_dev *dell_wmi_input_dev;
147 147
148static void dell_wmi_process_key(int reported_key)
149{
150 const struct key_entry *key;
151
152 key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
153 reported_key);
154 if (!key) {
155 pr_info("Unknown key %x pressed\n", reported_key);
156 return;
157 }
158
159 pr_debug("Key %x pressed\n", reported_key);
160
161 /* Don't report brightness notifications that will also come via ACPI */
162 if ((key->keycode == KEY_BRIGHTNESSUP ||
163 key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
164 return;
165
166 sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
167}
168
148static void dell_wmi_notify(u32 value, void *context) 169static void dell_wmi_notify(u32 value, void *context)
149{ 170{
150 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 171 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
151 union acpi_object *obj; 172 union acpi_object *obj;
152 acpi_status status; 173 acpi_status status;
174 acpi_size buffer_size;
175 u16 *buffer_entry, *buffer_end;
176 int len, i;
153 177
154 status = wmi_get_event_data(value, &response); 178 status = wmi_get_event_data(value, &response);
155 if (status != AE_OK) { 179 if (status != AE_OK) {
156 pr_info("bad event status 0x%x\n", status); 180 pr_warn("bad event status 0x%x\n", status);
157 return; 181 return;
158 } 182 }
159 183
160 obj = (union acpi_object *)response.pointer; 184 obj = (union acpi_object *)response.pointer;
185 if (!obj) {
186 pr_warn("no response\n");
187 return;
188 }
161 189
162 if (obj && obj->type == ACPI_TYPE_BUFFER) { 190 if (obj->type != ACPI_TYPE_BUFFER) {
163 const struct key_entry *key; 191 pr_warn("bad response type %x\n", obj->type);
164 int reported_key; 192 kfree(obj);
165 u16 *buffer_entry = (u16 *)obj->buffer.pointer; 193 return;
166 int buffer_size = obj->buffer.length/2; 194 }
167 195
168 if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) { 196 pr_debug("Received WMI event (%*ph)\n",
169 pr_info("Received unknown WMI event (0x%x)\n", 197 obj->buffer.length, obj->buffer.pointer);
170 buffer_entry[1]);
171 kfree(obj);
172 return;
173 }
174 198
175 if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0)) 199 buffer_entry = (u16 *)obj->buffer.pointer;
176 reported_key = (int)buffer_entry[2]; 200 buffer_size = obj->buffer.length/2;
201
202 if (!dell_new_hk_type) {
203 if (buffer_size >= 3 && buffer_entry[1] == 0x0)
204 dell_wmi_process_key(buffer_entry[2]);
177 else if (buffer_size >= 2) 205 else if (buffer_size >= 2)
178 reported_key = (int)buffer_entry[1] & 0xffff; 206 dell_wmi_process_key(buffer_entry[1]);
179 else { 207 else
180 pr_info("Received unknown WMI event\n"); 208 pr_info("Received unknown WMI event\n");
181 kfree(obj); 209 kfree(obj);
182 return; 210 return;
211 }
212
213 buffer_end = buffer_entry + buffer_size;
214
215 while (buffer_entry < buffer_end) {
216
217 len = buffer_entry[0];
218 if (len == 0)
219 break;
220
221 len++;
222
223 if (buffer_entry + len > buffer_end) {
224 pr_warn("Invalid length of WMI event\n");
225 break;
183 } 226 }
184 227
185 key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, 228 pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
186 reported_key); 229
187 if (!key) { 230 switch (buffer_entry[1]) {
188 pr_info("Unknown key %x pressed\n", reported_key); 231 case 0x00:
189 } else if ((key->keycode == KEY_BRIGHTNESSUP || 232 for (i = 2; i < len; ++i) {
190 key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { 233 switch (buffer_entry[i]) {
191 /* Don't report brightness notifications that will also 234 case 0xe043:
192 * come via ACPI */ 235 /* NIC Link is Up */
193 ; 236 pr_debug("NIC Link is Up\n");
194 } else { 237 break;
195 sparse_keymap_report_entry(dell_wmi_input_dev, key, 238 case 0xe044:
196 1, true); 239 /* NIC Link is Down */
240 pr_debug("NIC Link is Down\n");
241 break;
242 case 0xe045:
243 /* Unknown event but defined in DSDT */
244 default:
245 /* Unknown event */
246 pr_info("Unknown WMI event type 0x00: "
247 "0x%x\n", (int)buffer_entry[i]);
248 break;
249 }
250 }
251 break;
252 case 0x10:
253 /* Keys pressed */
254 for (i = 2; i < len; ++i)
255 dell_wmi_process_key(buffer_entry[i]);
256 break;
257 case 0x11:
258 for (i = 2; i < len; ++i) {
259 switch (buffer_entry[i]) {
260 case 0xfff0:
261 /* Battery unplugged */
262 pr_debug("Battery unplugged\n");
263 break;
264 case 0xfff1:
265 /* Battery inserted */
266 pr_debug("Battery inserted\n");
267 break;
268 case 0x01e1:
269 case 0x02ea:
270 case 0x02eb:
271 case 0x02ec:
272 case 0x02f6:
273 /* Keyboard backlight level changed */
274 pr_debug("Keyboard backlight level "
275 "changed\n");
276 break;
277 default:
278 /* Unknown event */
279 pr_info("Unknown WMI event type 0x11: "
280 "0x%x\n", (int)buffer_entry[i]);
281 break;
282 }
283 }
284 break;
285 default:
286 /* Unknown event */
287 pr_info("Unknown WMI event type 0x%x\n",
288 (int)buffer_entry[1]);
289 break;
197 } 290 }
291
292 buffer_entry += len;
293
198 } 294 }
295
199 kfree(obj); 296 kfree(obj);
200} 297}
201 298