aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
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