Commit 91e13aa3 authored by Aaron Lu's avatar Aaron Lu Committed by Rafael J. Wysocki

ACPI: video: correct acpi_video_bus_add error processing

acpi_video_bus_get_devices() may fail due to some video output device
doesn't have the _ADR method, and in this case, the error processing
is to simply free the video structure in acpi_video_bus_add(), while
leaving those already registered video output devices in the wild,
which means for some video output device, we have already registered
a backlight interface and installed a notification handler for it.
So it can happen when user is using this system, on hotkey pressing,
the notification handler will send a keycode through a non-existing
input device, causing kernel freeze.

To solve this problem, free all those already registered video output
devices once something goes wrong in acpi_video_bus_get_devices(), so
that no wild backlight interfaces and notification handlers exist.

References: https://bugzilla.kernel.org/show_bug.cgi?id=51731
Reported-and-tested-by: <i-tek@web.de>
Signed-off-by: default avatarAaron Lu <aaron.lu@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent a6432ded
...@@ -167,7 +167,8 @@ struct acpi_video_device_flags { ...@@ -167,7 +167,8 @@ struct acpi_video_device_flags {
u8 dvi:1; u8 dvi:1;
u8 bios:1; u8 bios:1;
u8 unknown:1; u8 unknown:1;
u8 reserved:2; u8 notify:1;
u8 reserved:1;
}; };
struct acpi_video_device_cap { struct acpi_video_device_cap {
...@@ -1074,53 +1075,51 @@ acpi_video_bus_get_one_device(struct acpi_device *device, ...@@ -1074,53 +1075,51 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
struct acpi_video_device *data; struct acpi_video_device *data;
struct acpi_video_device_attrib* attribute; struct acpi_video_device_attrib* attribute;
if (!device || !video)
return -EINVAL;
status = status =
acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
if (ACPI_SUCCESS(status)) { /* Some device omits _ADR, we skip them instead of fail */
if (ACPI_FAILURE(status))
data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); return 0;
if (!data)
return -ENOMEM;
strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
device->driver_data = data;
data->device_id = device_id;
data->video = video;
data->dev = device;
attribute = acpi_video_get_device_attr(video, device_id); data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
if (!data)
return -ENOMEM;
if((attribute != NULL) && attribute->device_id_scheme) { strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
switch (attribute->display_type) { strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
case ACPI_VIDEO_DISPLAY_CRT: device->driver_data = data;
data->flags.crt = 1;
break; data->device_id = device_id;
case ACPI_VIDEO_DISPLAY_TV: data->video = video;
data->flags.tvout = 1; data->dev = device;
break;
case ACPI_VIDEO_DISPLAY_DVI: attribute = acpi_video_get_device_attr(video, device_id);
data->flags.dvi = 1;
break; if((attribute != NULL) && attribute->device_id_scheme) {
case ACPI_VIDEO_DISPLAY_LCD: switch (attribute->display_type) {
data->flags.lcd = 1; case ACPI_VIDEO_DISPLAY_CRT:
break; data->flags.crt = 1;
default: break;
data->flags.unknown = 1; case ACPI_VIDEO_DISPLAY_TV:
break; data->flags.tvout = 1;
} break;
if(attribute->bios_can_detect) case ACPI_VIDEO_DISPLAY_DVI:
data->flags.bios = 1; data->flags.dvi = 1;
} else { break;
/* Check for legacy IDs */ case ACPI_VIDEO_DISPLAY_LCD:
device_type = acpi_video_get_device_type(video, data->flags.lcd = 1;
device_id); break;
/* Ignore bits 16 and 18-20 */ default:
switch (device_type & 0xffe2ffff) { data->flags.unknown = 1;
break;
}
if(attribute->bios_can_detect)
data->flags.bios = 1;
} else {
/* Check for legacy IDs */
device_type = acpi_video_get_device_type(video, device_id);
/* Ignore bits 16 and 18-20 */
switch (device_type & 0xffe2ffff) {
case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
data->flags.crt = 1; data->flags.crt = 1;
break; break;
...@@ -1132,34 +1131,24 @@ acpi_video_bus_get_one_device(struct acpi_device *device, ...@@ -1132,34 +1131,24 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
break; break;
default: default:
data->flags.unknown = 1; data->flags.unknown = 1;
}
} }
}
acpi_video_device_bind(video, data); acpi_video_device_bind(video, data);
acpi_video_device_find_cap(data); acpi_video_device_find_cap(data);
status = acpi_install_notify_handler(device->handle,
ACPI_DEVICE_NOTIFY,
acpi_video_device_notify,
data);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX
"Error installing notify handler\n");
if(data->brightness)
kfree(data->brightness->levels);
kfree(data->brightness);
kfree(data);
return -ENODEV;
}
mutex_lock(&video->device_list_lock); status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
list_add_tail(&data->entry, &video->video_device_list); acpi_video_device_notify, data);
mutex_unlock(&video->device_list_lock); if (ACPI_FAILURE(status))
dev_err(&device->dev, "Error installing notify handler\n");
else
data->flags.notify = 1;
return 0; mutex_lock(&video->device_list_lock);
} list_add_tail(&data->entry, &video->video_device_list);
mutex_unlock(&video->device_list_lock);
return -ENOENT; return status;
} }
/* /*
...@@ -1452,9 +1441,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video, ...@@ -1452,9 +1441,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
status = acpi_video_bus_get_one_device(dev, video); status = acpi_video_bus_get_one_device(dev, video);
if (status) { if (status) {
printk(KERN_WARNING PREFIX dev_err(&dev->dev, "Can't attach device\n");
"Can't attach device\n"); break;
continue;
} }
} }
return status; return status;
...@@ -1467,13 +1455,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) ...@@ -1467,13 +1455,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
if (!device || !device->video) if (!device || !device->video)
return -ENOENT; return -ENOENT;
status = acpi_remove_notify_handler(device->dev->handle, if (device->flags.notify) {
ACPI_DEVICE_NOTIFY, status = acpi_remove_notify_handler(device->dev->handle,
acpi_video_device_notify); ACPI_DEVICE_NOTIFY, acpi_video_device_notify);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status))
printk(KERN_WARNING PREFIX dev_err(&device->dev->dev,
"Can't remove video notify handler\n"); "Can't remove video notify handler\n");
} }
if (device->backlight) { if (device->backlight) {
backlight_device_unregister(device->backlight); backlight_device_unregister(device->backlight);
device->backlight = NULL; device->backlight = NULL;
...@@ -1755,7 +1744,7 @@ static int acpi_video_bus_add(struct acpi_device *device) ...@@ -1755,7 +1744,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
error = acpi_video_bus_get_devices(video, device); error = acpi_video_bus_get_devices(video, device);
if (error) if (error)
goto err_free_video; goto err_put_video;
video->input = input = input_allocate_device(); video->input = input = input_allocate_device();
if (!input) { if (!input) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment