diff options
author | Aaron Lu <aaron.lu@intel.com> | 2013-04-24 22:47:49 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-04-25 06:53:19 -0400 |
commit | 91e13aa37023437c260c85a3f17308052bbfbfa2 (patch) | |
tree | c867075f3975ca0d3611e99c5be6f102da55f326 /drivers/acpi/video.c | |
parent | a6432ded299726f123b93d0132fead200551535c (diff) |
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: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/video.c')
-rw-r--r-- | drivers/acpi/video.c | 143 |
1 files changed, 66 insertions, 77 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index ed192e5d82b6..c3932d0876e0 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c | |||
@@ -167,7 +167,8 @@ struct acpi_video_device_flags { | |||
167 | u8 dvi:1; | 167 | u8 dvi:1; |
168 | u8 bios:1; | 168 | u8 bios:1; |
169 | u8 unknown:1; | 169 | u8 unknown:1; |
170 | u8 reserved:2; | 170 | u8 notify:1; |
171 | u8 reserved:1; | ||
171 | }; | 172 | }; |
172 | 173 | ||
173 | struct acpi_video_device_cap { | 174 | struct acpi_video_device_cap { |
@@ -1074,53 +1075,51 @@ acpi_video_bus_get_one_device(struct acpi_device *device, | |||
1074 | struct acpi_video_device *data; | 1075 | struct acpi_video_device *data; |
1075 | struct acpi_video_device_attrib* attribute; | 1076 | struct acpi_video_device_attrib* attribute; |
1076 | 1077 | ||
1077 | if (!device || !video) | ||
1078 | return -EINVAL; | ||
1079 | |||
1080 | status = | 1078 | status = |
1081 | acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); | 1079 | acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); |
1082 | if (ACPI_SUCCESS(status)) { | 1080 | /* Some device omits _ADR, we skip them instead of fail */ |
1083 | 1081 | if (ACPI_FAILURE(status)) | |
1084 | data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); | 1082 | return 0; |
1085 | if (!data) | ||
1086 | return -ENOMEM; | ||
1087 | |||
1088 | strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); | ||
1089 | strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); | ||
1090 | device->driver_data = data; | ||
1091 | |||
1092 | data->device_id = device_id; | ||
1093 | data->video = video; | ||
1094 | data->dev = device; | ||
1095 | 1083 | ||
1096 | attribute = acpi_video_get_device_attr(video, device_id); | 1084 | data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); |
1085 | if (!data) | ||
1086 | return -ENOMEM; | ||
1097 | 1087 | ||
1098 | if((attribute != NULL) && attribute->device_id_scheme) { | 1088 | strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); |
1099 | switch (attribute->display_type) { | 1089 | strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); |
1100 | case ACPI_VIDEO_DISPLAY_CRT: | 1090 | device->driver_data = data; |
1101 | data->flags.crt = 1; | 1091 | |
1102 | break; | 1092 | data->device_id = device_id; |
1103 | case ACPI_VIDEO_DISPLAY_TV: | 1093 | data->video = video; |
1104 | data->flags.tvout = 1; | 1094 | data->dev = device; |
1105 | break; | 1095 | |
1106 | case ACPI_VIDEO_DISPLAY_DVI: | 1096 | attribute = acpi_video_get_device_attr(video, device_id); |
1107 | data->flags.dvi = 1; | 1097 | |
1108 | break; | 1098 | if((attribute != NULL) && attribute->device_id_scheme) { |
1109 | case ACPI_VIDEO_DISPLAY_LCD: | 1099 | switch (attribute->display_type) { |
1110 | data->flags.lcd = 1; | 1100 | case ACPI_VIDEO_DISPLAY_CRT: |
1111 | break; | 1101 | data->flags.crt = 1; |
1112 | default: | 1102 | break; |
1113 | data->flags.unknown = 1; | 1103 | case ACPI_VIDEO_DISPLAY_TV: |
1114 | break; | 1104 | data->flags.tvout = 1; |
1115 | } | 1105 | break; |
1116 | if(attribute->bios_can_detect) | 1106 | case ACPI_VIDEO_DISPLAY_DVI: |
1117 | data->flags.bios = 1; | 1107 | data->flags.dvi = 1; |
1118 | } else { | 1108 | break; |
1119 | /* Check for legacy IDs */ | 1109 | case ACPI_VIDEO_DISPLAY_LCD: |
1120 | device_type = acpi_video_get_device_type(video, | 1110 | data->flags.lcd = 1; |
1121 | device_id); | 1111 | break; |
1122 | /* Ignore bits 16 and 18-20 */ | 1112 | default: |
1123 | switch (device_type & 0xffe2ffff) { | 1113 | data->flags.unknown = 1; |
1114 | break; | ||
1115 | } | ||
1116 | if(attribute->bios_can_detect) | ||
1117 | data->flags.bios = 1; | ||
1118 | } else { | ||
1119 | /* Check for legacy IDs */ | ||
1120 | device_type = acpi_video_get_device_type(video, device_id); | ||
1121 | /* Ignore bits 16 and 18-20 */ | ||
1122 | switch (device_type & 0xffe2ffff) { | ||
1124 | case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: | 1123 | case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: |
1125 | data->flags.crt = 1; | 1124 | data->flags.crt = 1; |
1126 | break; | 1125 | break; |
@@ -1132,34 +1131,24 @@ acpi_video_bus_get_one_device(struct acpi_device *device, | |||
1132 | break; | 1131 | break; |
1133 | default: | 1132 | default: |
1134 | data->flags.unknown = 1; | 1133 | data->flags.unknown = 1; |
1135 | } | ||
1136 | } | 1134 | } |
1135 | } | ||
1137 | 1136 | ||
1138 | acpi_video_device_bind(video, data); | 1137 | acpi_video_device_bind(video, data); |
1139 | acpi_video_device_find_cap(data); | 1138 | acpi_video_device_find_cap(data); |
1140 | |||
1141 | status = acpi_install_notify_handler(device->handle, | ||
1142 | ACPI_DEVICE_NOTIFY, | ||
1143 | acpi_video_device_notify, | ||
1144 | data); | ||
1145 | if (ACPI_FAILURE(status)) { | ||
1146 | printk(KERN_ERR PREFIX | ||
1147 | "Error installing notify handler\n"); | ||
1148 | if(data->brightness) | ||
1149 | kfree(data->brightness->levels); | ||
1150 | kfree(data->brightness); | ||
1151 | kfree(data); | ||
1152 | return -ENODEV; | ||
1153 | } | ||
1154 | 1139 | ||
1155 | mutex_lock(&video->device_list_lock); | 1140 | status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, |
1156 | list_add_tail(&data->entry, &video->video_device_list); | 1141 | acpi_video_device_notify, data); |
1157 | mutex_unlock(&video->device_list_lock); | 1142 | if (ACPI_FAILURE(status)) |
1143 | dev_err(&device->dev, "Error installing notify handler\n"); | ||
1144 | else | ||
1145 | data->flags.notify = 1; | ||
1158 | 1146 | ||
1159 | return 0; | 1147 | mutex_lock(&video->device_list_lock); |
1160 | } | 1148 | list_add_tail(&data->entry, &video->video_device_list); |
1149 | mutex_unlock(&video->device_list_lock); | ||
1161 | 1150 | ||
1162 | return -ENOENT; | 1151 | return status; |
1163 | } | 1152 | } |
1164 | 1153 | ||
1165 | /* | 1154 | /* |
@@ -1452,9 +1441,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video, | |||
1452 | 1441 | ||
1453 | status = acpi_video_bus_get_one_device(dev, video); | 1442 | status = acpi_video_bus_get_one_device(dev, video); |
1454 | if (status) { | 1443 | if (status) { |
1455 | printk(KERN_WARNING PREFIX | 1444 | dev_err(&dev->dev, "Can't attach device\n"); |
1456 | "Can't attach device\n"); | 1445 | break; |
1457 | continue; | ||
1458 | } | 1446 | } |
1459 | } | 1447 | } |
1460 | return status; | 1448 | return status; |
@@ -1467,13 +1455,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) | |||
1467 | if (!device || !device->video) | 1455 | if (!device || !device->video) |
1468 | return -ENOENT; | 1456 | return -ENOENT; |
1469 | 1457 | ||
1470 | status = acpi_remove_notify_handler(device->dev->handle, | 1458 | if (device->flags.notify) { |
1471 | ACPI_DEVICE_NOTIFY, | 1459 | status = acpi_remove_notify_handler(device->dev->handle, |
1472 | acpi_video_device_notify); | 1460 | ACPI_DEVICE_NOTIFY, acpi_video_device_notify); |
1473 | if (ACPI_FAILURE(status)) { | 1461 | if (ACPI_FAILURE(status)) |
1474 | printk(KERN_WARNING PREFIX | 1462 | dev_err(&device->dev->dev, |
1475 | "Can't remove video notify handler\n"); | 1463 | "Can't remove video notify handler\n"); |
1476 | } | 1464 | } |
1465 | |||
1477 | if (device->backlight) { | 1466 | if (device->backlight) { |
1478 | backlight_device_unregister(device->backlight); | 1467 | backlight_device_unregister(device->backlight); |
1479 | device->backlight = NULL; | 1468 | device->backlight = NULL; |
@@ -1755,7 +1744,7 @@ static int acpi_video_bus_add(struct acpi_device *device) | |||
1755 | 1744 | ||
1756 | error = acpi_video_bus_get_devices(video, device); | 1745 | error = acpi_video_bus_get_devices(video, device); |
1757 | if (error) | 1746 | if (error) |
1758 | goto err_free_video; | 1747 | goto err_put_video; |
1759 | 1748 | ||
1760 | video->input = input = input_allocate_device(); | 1749 | video->input = input = input_allocate_device(); |
1761 | if (!input) { | 1750 | if (!input) { |