Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hp_accel: do not call ACPI from invalid context

The LED on HP notebooks is connected through ACPI. That unfortunately
means that it needs to be delayed by using schedule_work() to avoid
calling the ACPI interpreter from an invalid context.

[akpm@linux-foundation.org: use flush_work() rather than sort-of reimplementing it]
Signed-off-by: Pavel Machek <pavel@suse.cz>
Cc: Éric Piel <eric.piel@tremplin-utc.net>
Cc: Len Brown <lenb@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Pavel Machek and committed by
Linus Torvalds
9e1c9d86 219beb29

+49 -19
+49 -19
drivers/hwmon/hp_accel.c
··· 3 3 * 4 4 * Copyright (C) 2007-2008 Yan Burman 5 5 * Copyright (C) 2008 Eric Piel 6 - * Copyright (C) 2008 Pavel Machek 6 + * Copyright (C) 2008-2009 Pavel Machek 7 7 * 8 8 * This program is free software; you can redistribute it and/or modify 9 9 * it under the terms of the GNU General Public License as published by ··· 44 44 #define DRIVER_NAME "lis3lv02d" 45 45 #define ACPI_MDPS_CLASS "accelerometer" 46 46 47 + /* Delayed LEDs infrastructure ------------------------------------ */ 48 + 49 + /* Special LED class that can defer work */ 50 + struct delayed_led_classdev { 51 + struct led_classdev led_classdev; 52 + struct work_struct work; 53 + enum led_brightness new_brightness; 54 + 55 + unsigned int led; /* For driver */ 56 + void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); 57 + }; 58 + 59 + static inline void delayed_set_status_worker(struct work_struct *work) 60 + { 61 + struct delayed_led_classdev *data = 62 + container_of(work, struct delayed_led_classdev, work); 63 + 64 + data->set_brightness(data, data->new_brightness); 65 + } 66 + 67 + static inline void delayed_sysfs_set(struct led_classdev *led_cdev, 68 + enum led_brightness brightness) 69 + { 70 + struct delayed_led_classdev *data = container_of(led_cdev, 71 + struct delayed_led_classdev, led_classdev); 72 + data->new_brightness = brightness; 73 + schedule_work(&data->work); 74 + } 75 + 76 + /* HP-specific accelerometer driver ------------------------------------ */ 47 77 48 78 /* For automatic insertion of the module */ 49 79 static struct acpi_device_id lis3lv02d_device_ids[] = { ··· 185 155 */ 186 156 }; 187 157 188 - static acpi_status hpled_acpi_write(acpi_handle handle, int reg) 158 + static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) 189 159 { 160 + acpi_handle handle = adev.device->handle; 190 161 unsigned long long ret; /* Not used when writing */ 191 162 union acpi_object in_obj[1]; 192 163 struct acpi_object_list args = { 1, in_obj }; 193 164 194 165 in_obj[0].type = ACPI_TYPE_INTEGER; 195 - in_obj[0].integer.value = reg; 166 + in_obj[0].integer.value = !!value; 196 167 197 - return acpi_evaluate_integer(handle, "ALED", &args, &ret); 168 + acpi_evaluate_integer(handle, "ALED", &args, &ret); 198 169 } 199 170 200 - static void hpled_set(struct led_classdev *led_cdev, 201 - enum led_brightness value) 202 - { 203 - hpled_acpi_write(adev.device->handle, !!value); 204 - } 205 - 206 - static struct led_classdev hpled_led = { 207 - .name = "hp:red:hddprotection", 208 - .default_trigger = "none", 209 - .brightness_set = hpled_set, 171 + static struct delayed_led_classdev hpled_led = { 172 + .led_classdev = { 173 + .name = "hp::hddprotect", 174 + .default_trigger = "none", 175 + .brightness_set = delayed_sysfs_set, 176 + .flags = LED_CORE_SUSPENDRESUME, 177 + }, 178 + .set_brightness = hpled_set, 210 179 }; 211 180 212 181 static int lis3lv02d_add(struct acpi_device *device) ··· 237 208 adev.ac = lis3lv02d_axis_normal; 238 209 } 239 210 240 - ret = led_classdev_register(NULL, &hpled_led); 211 + INIT_WORK(&hpled_led.work, delayed_set_status_worker); 212 + ret = led_classdev_register(NULL, &hpled_led.led_classdev); 241 213 if (ret) 242 214 return ret; 243 215 244 216 ret = lis3lv02d_init_device(&adev); 245 217 if (ret) { 246 - led_classdev_unregister(&hpled_led); 218 + flush_work(&hpled_led.work); 219 + led_classdev_unregister(&hpled_led.led_classdev); 247 220 return ret; 248 221 } 249 222 ··· 260 229 lis3lv02d_joystick_disable(); 261 230 lis3lv02d_poweroff(device->handle); 262 231 263 - led_classdev_unregister(&hpled_led); 232 + flush_work(&hpled_led.work); 233 + led_classdev_unregister(&hpled_led.led_classdev); 264 234 265 235 return lis3lv02d_remove_fs(); 266 236 } ··· 272 240 { 273 241 /* make sure the device is off when we suspend */ 274 242 lis3lv02d_poweroff(device->handle); 275 - led_classdev_suspend(&hpled_led); 276 243 return 0; 277 244 } 278 245 ··· 284 253 else 285 254 lis3lv02d_poweroff(device->handle); 286 255 mutex_unlock(&adev.lock); 287 - led_classdev_resume(&hpled_led); 288 256 return 0; 289 257 } 290 258 #else