Commit 76e0134f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (32 commits)
  ACPI: i2c-scmi: don't use acpi_device_uid()
  ACPI: simplify building device HID/CID list
  ACPI: remove acpi_device_uid() and related stuff
  ACPI: remove acpi_device.flags.hardware_id
  ACPI: remove acpi_device.flags.compatible_ids
  ACPI: maintain a single list of _HID and _CID IDs
  ACPI: make sure every acpi_device has an ID
  ACPI: use acpi_device_hid() when possible
  ACPI: fix synthetic HID for \_SB_
  ACPI: handle re-enumeration, when acpi_devices might already exist
  ACPI: factor out device type and status checking
  ACPI: add acpi_bus_get_status_handle()
  ACPI: use acpi_walk_namespace() to enumerate devices
  ACPI: identify device tree root by null parent pointer, not ACPI_BUS_TYPE
  ACPI: enumerate namespace before adding functional fixed hardware devices
  ACPI: convert acpi_bus_scan() to operate on an acpi_handle
  ACPI: add acpi_bus_get_parent() and remove "parent" arguments
  ACPI: remove unnecessary argument checking
  ACPI: remove redundant "type" arguments
  ACPI: remove acpi_device_set_context() "type" argument
  ...
parents bfebb140 e96c9284
...@@ -199,18 +199,22 @@ kind to allow it (and it often doesn't!). ...@@ -199,18 +199,22 @@ kind to allow it (and it often doesn't!).
Not all bits in the mask can be modified. Not all bits that can be Not all bits in the mask can be modified. Not all bits that can be
modified do anything. Not all hot keys can be individually controlled modified do anything. Not all hot keys can be individually controlled
by the mask. Some models do not support the mask at all, and in those by the mask. Some models do not support the mask at all. The behaviour
models, hot keys cannot be controlled individually. The behaviour of of the mask is, therefore, highly dependent on the ThinkPad model.
the mask is, therefore, highly dependent on the ThinkPad model.
The driver will filter out any unmasked hotkeys, so even if the firmware
doesn't allow disabling an specific hotkey, the driver will not report
events for unmasked hotkeys.
Note that unmasking some keys prevents their default behavior. For Note that unmasking some keys prevents their default behavior. For
example, if Fn+F5 is unmasked, that key will no longer enable/disable example, if Fn+F5 is unmasked, that key will no longer enable/disable
Bluetooth by itself. Bluetooth by itself in firmware.
Note also that not all Fn key combinations are supported through ACPI. Note also that not all Fn key combinations are supported through ACPI
For example, on the X40, the brightness, volume and "Access IBM" buttons depending on the ThinkPad model and firmware version. On those
do not generate ACPI events even with this driver. They *can* be used ThinkPads, it is still possible to support some extra hotkeys by
through the "ThinkPad Buttons" utility, see http://www.nongnu.org/tpb/ polling the "CMOS NVRAM" at least 10 times per second. The driver
attempts to enables this functionality automatically when required.
procfs notes: procfs notes:
...@@ -255,18 +259,11 @@ sysfs notes: ...@@ -255,18 +259,11 @@ sysfs notes:
1: does nothing 1: does nothing
hotkey_mask: hotkey_mask:
bit mask to enable driver-handling (and depending on bit mask to enable reporting (and depending on
the firmware, ACPI event generation) for each hot key the firmware, ACPI event generation) for each hot key
(see above). Returns the current status of the hot keys (see above). Returns the current status of the hot keys
mask, and allows one to modify it. mask, and allows one to modify it.
Note: when NVRAM polling is active, the firmware mask
will be different from the value returned by
hotkey_mask. The driver will retain enabled bits for
hotkeys that are under NVRAM polling even if the
firmware refuses them, and will not set these bits on
the firmware hot key mask.
hotkey_all_mask: hotkey_all_mask:
bit mask that should enable event reporting for all bit mask that should enable event reporting for all
supported hot keys, when echoed to hotkey_mask above. supported hot keys, when echoed to hotkey_mask above.
...@@ -279,7 +276,8 @@ sysfs notes: ...@@ -279,7 +276,8 @@ sysfs notes:
bit mask that should enable event reporting for all bit mask that should enable event reporting for all
supported hot keys, except those which are always supported hot keys, except those which are always
handled by the firmware anyway. Echo it to handled by the firmware anyway. Echo it to
hotkey_mask above, to use. hotkey_mask above, to use. This is the default mask
used by the driver.
hotkey_source_mask: hotkey_source_mask:
bit mask that selects which hot keys will the driver bit mask that selects which hot keys will the driver
...@@ -287,9 +285,10 @@ sysfs notes: ...@@ -287,9 +285,10 @@ sysfs notes:
based on the capabilities reported by the ACPI firmware, based on the capabilities reported by the ACPI firmware,
but it can be overridden at runtime. but it can be overridden at runtime.
Hot keys whose bits are set in both hotkey_source_mask Hot keys whose bits are set in hotkey_source_mask are
and also on hotkey_mask are polled for in NVRAM. Only a polled for in NVRAM, and reported as hotkey events if
few hot keys are available through CMOS NVRAM polling. enabled in hotkey_mask. Only a few hot keys are
available through CMOS NVRAM polling.
Warning: when in NVRAM mode, the volume up/down/mute Warning: when in NVRAM mode, the volume up/down/mute
keys are synthesized according to changes in the mixer, keys are synthesized according to changes in the mixer,
...@@ -525,6 +524,7 @@ compatibility purposes when hotkey_report_mode is set to 1. ...@@ -525,6 +524,7 @@ compatibility purposes when hotkey_report_mode is set to 1.
0x2305 System is waking up from suspend to eject bay 0x2305 System is waking up from suspend to eject bay
0x2404 System is waking up from hibernation to undock 0x2404 System is waking up from hibernation to undock
0x2405 System is waking up from hibernation to eject bay 0x2405 System is waking up from hibernation to eject bay
0x5010 Brightness level changed/control event
The above events are never propagated by the driver. The above events are never propagated by the driver.
...@@ -532,7 +532,6 @@ The above events are never propagated by the driver. ...@@ -532,7 +532,6 @@ The above events are never propagated by the driver.
0x4003 Undocked (see 0x2x04), can sleep again 0x4003 Undocked (see 0x2x04), can sleep again
0x500B Tablet pen inserted into its storage bay 0x500B Tablet pen inserted into its storage bay
0x500C Tablet pen removed from its storage bay 0x500C Tablet pen removed from its storage bay
0x5010 Brightness level changed (newer Lenovo BIOSes)
The above events are propagated by the driver. The above events are propagated by the driver.
...@@ -621,6 +620,8 @@ For Lenovo models *with* ACPI backlight control: ...@@ -621,6 +620,8 @@ For Lenovo models *with* ACPI backlight control:
2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi, 2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi,
and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN. Process and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN. Process
these keys on userspace somehow (e.g. by calling xbacklight). these keys on userspace somehow (e.g. by calling xbacklight).
The driver will do this automatically if it detects that ACPI video
has been disabled.
Bluetooth Bluetooth
...@@ -1459,3 +1460,8 @@ Sysfs interface changelog: ...@@ -1459,3 +1460,8 @@ Sysfs interface changelog:
0x020400: Marker for 16 LEDs support. Also, LEDs that are known 0x020400: Marker for 16 LEDs support. Also, LEDs that are known
to not exist in a given model are not registered with to not exist in a given model are not registered with
the LED sysfs class anymore. the LED sysfs class anymore.
0x020500: Updated hotkey driver, hotkey_mask is always available
and it is always able to disable hot keys. Very old
thinkpads are properly supported. hotkey_bios_mask
is deprecated and marked for removal.
...@@ -94,36 +94,33 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) ...@@ -94,36 +94,33 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
EXPORT_SYMBOL(acpi_bus_get_device); EXPORT_SYMBOL(acpi_bus_get_device);
int acpi_bus_get_status(struct acpi_device *device) acpi_status acpi_bus_get_status_handle(acpi_handle handle,
unsigned long long *sta)
{ {
acpi_status status = AE_OK; acpi_status status;
unsigned long long sta = 0;
if (!device) status = acpi_evaluate_integer(handle, "_STA", NULL, sta);
return -EINVAL; if (ACPI_SUCCESS(status))
return AE_OK;
/* if (status == AE_NOT_FOUND) {
* Evaluate _STA if present. *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
*/ ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING;
if (device->flags.dynamic_status) { return AE_OK;
status =
acpi_evaluate_integer(device->handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status))
return -ENODEV;
STRUCT_TO_INT(device->status) = (int)sta;
} }
return status;
}
/* int acpi_bus_get_status(struct acpi_device *device)
* According to ACPI spec some device can be present and functional {
* even if the parent is not present but functional. acpi_status status;
* In such conditions the child device should not inherit the status unsigned long long sta;
* from the parent.
*/ status = acpi_bus_get_status_handle(device->handle, &sta);
else if (ACPI_FAILURE(status))
STRUCT_TO_INT(device->status) = return -ENODEV;
ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; STRUCT_TO_INT(device->status) = (int) sta;
if (device->status.functional && !device->status.present) { if (device->status.functional && !device->status.present) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: " ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: "
...@@ -135,10 +132,8 @@ int acpi_bus_get_status(struct acpi_device *device) ...@@ -135,10 +132,8 @@ int acpi_bus_get_status(struct acpi_device *device)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n",
device->pnp.bus_id, device->pnp.bus_id,
(u32) STRUCT_TO_INT(device->status))); (u32) STRUCT_TO_INT(device->status)));
return 0; return 0;
} }
EXPORT_SYMBOL(acpi_bus_get_status); EXPORT_SYMBOL(acpi_bus_get_status);
void acpi_bus_private_data_handler(acpi_handle handle, void acpi_bus_private_data_handler(acpi_handle handle,
......
...@@ -22,6 +22,8 @@ extern struct acpi_device *acpi_root; ...@@ -22,6 +22,8 @@ extern struct acpi_device *acpi_root;
#define ACPI_BUS_HID "LNXSYBUS" #define ACPI_BUS_HID "LNXSYBUS"
#define ACPI_BUS_DEVICE_NAME "System Bus" #define ACPI_BUS_DEVICE_NAME "System Bus"
#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_device_list);
static LIST_HEAD(acpi_bus_id_list); static LIST_HEAD(acpi_bus_id_list);
DEFINE_MUTEX(acpi_device_lock); DEFINE_MUTEX(acpi_device_lock);
...@@ -43,40 +45,19 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, ...@@ -43,40 +45,19 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
{ {
int len; int len;
int count; int count;
struct acpi_hardware_id *id;
if (!acpi_dev->flags.hardware_id && !acpi_dev->flags.compatible_ids)
return -ENODEV;
len = snprintf(modalias, size, "acpi:"); len = snprintf(modalias, size, "acpi:");
size -= len; size -= len;
if (acpi_dev->flags.hardware_id) { list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
count = snprintf(&modalias[len], size, "%s:", count = snprintf(&modalias[len], size, "%s:", id->id);
acpi_dev->pnp.hardware_id);
if (count < 0 || count >= size) if (count < 0 || count >= size)
return -EINVAL; return -EINVAL;
len += count; len += count;
size -= count; size -= count;
} }
if (acpi_dev->flags.compatible_ids) {
struct acpica_device_id_list *cid_list;
int i;
cid_list = acpi_dev->pnp.cid_list;
for (i = 0; i < cid_list->count; i++) {
count = snprintf(&modalias[len], size, "%s:",
cid_list->ids[i].string);
if (count < 0 || count >= size) {
printk(KERN_ERR PREFIX "%s cid[%i] exceeds event buffer size",
acpi_dev->pnp.device_name, i);
break;
}
len += count;
size -= count;
}
}
modalias[len] = '\0'; modalias[len] = '\0';
return len; return len;
} }
...@@ -183,7 +164,7 @@ static ssize_t ...@@ -183,7 +164,7 @@ static ssize_t
acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_device *acpi_dev = to_acpi_device(dev);
return sprintf(buf, "%s\n", acpi_dev->pnp.hardware_id); return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev));
} }
static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL);
...@@ -219,17 +200,13 @@ static int acpi_device_setup_files(struct acpi_device *dev) ...@@ -219,17 +200,13 @@ static int acpi_device_setup_files(struct acpi_device *dev)
goto end; goto end;
} }
if (dev->flags.hardware_id) { result = device_create_file(&dev->dev, &dev_attr_hid);
result = device_create_file(&dev->dev, &dev_attr_hid); if (result)
if (result) goto end;
goto end;
}
if (dev->flags.hardware_id || dev->flags.compatible_ids) { result = device_create_file(&dev->dev, &dev_attr_modalias);
result = device_create_file(&dev->dev, &dev_attr_modalias); if (result)
if (result) goto end;
goto end;
}
/* /*
* If device has _EJ0, 'eject' file is created that is used to trigger * If device has _EJ0, 'eject' file is created that is used to trigger
...@@ -255,11 +232,8 @@ static void acpi_device_remove_files(struct acpi_device *dev) ...@@ -255,11 +232,8 @@ static void acpi_device_remove_files(struct acpi_device *dev)
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
device_remove_file(&dev->dev, &dev_attr_eject); device_remove_file(&dev->dev, &dev_attr_eject);
if (dev->flags.hardware_id || dev->flags.compatible_ids) device_remove_file(&dev->dev, &dev_attr_modalias);
device_remove_file(&dev->dev, &dev_attr_modalias); device_remove_file(&dev->dev, &dev_attr_hid);
if (dev->flags.hardware_id)
device_remove_file(&dev->dev, &dev_attr_hid);
if (dev->handle) if (dev->handle)
device_remove_file(&dev->dev, &dev_attr_path); device_remove_file(&dev->dev, &dev_attr_path);
} }
...@@ -271,6 +245,7 @@ int acpi_match_device_ids(struct acpi_device *device, ...@@ -271,6 +245,7 @@ int acpi_match_device_ids(struct acpi_device *device,
const struct acpi_device_id *ids) const struct acpi_device_id *ids)
{ {
const struct acpi_device_id *id; const struct acpi_device_id *id;
struct acpi_hardware_id *hwid;
/* /*
* If the device is not present, it is unnecessary to load device * If the device is not present, it is unnecessary to load device
...@@ -279,40 +254,30 @@ int acpi_match_device_ids(struct acpi_device *device, ...@@ -279,40 +254,30 @@ int acpi_match_device_ids(struct acpi_device *device,
if (!device->status.present) if (!device->status.present)
return -ENODEV; return -ENODEV;
if (device->flags.hardware_id) { for (id = ids; id->id[0]; id++)
for (id = ids; id->id[0]; id++) { list_for_each_entry(hwid, &device->pnp.ids, list)
if (!strcmp((char*)id->id, device->pnp.hardware_id)) if (!strcmp((char *) id->id, hwid->id))
return 0; return 0;
}
}
if (device->flags.compatible_ids) {
struct acpica_device_id_list *cid_list = device->pnp.cid_list;
int i;
for (id = ids; id->id[0]; id++) {
/* compare multiple _CID entries against driver ids */
for (i = 0; i < cid_list->count; i++) {
if (!strcmp((char*)id->id,
cid_list->ids[i].string))
return 0;
}
}
}
return -ENOENT; return -ENOENT;
} }
EXPORT_SYMBOL(acpi_match_device_ids); EXPORT_SYMBOL(acpi_match_device_ids);
static void acpi_free_ids(struct acpi_device *device)
{
struct acpi_hardware_id *id, *tmp;
list_for_each_entry_safe(id, tmp, &device->pnp.ids, list) {
kfree(id->id);
kfree(id);
}
}
static void acpi_device_release(struct device *dev) static void acpi_device_release(struct device *dev)
{ {
struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_device *acpi_dev = to_acpi_device(dev);
kfree(acpi_dev->pnp.cid_list); acpi_free_ids(acpi_dev);
if (acpi_dev->flags.hardware_id)
kfree(acpi_dev->pnp.hardware_id);
if (acpi_dev->flags.unique_id)
kfree(acpi_dev->pnp.unique_id);
kfree(acpi_dev); kfree(acpi_dev);
} }
...@@ -378,15 +343,13 @@ static acpi_status acpi_device_notify_fixed(void *data) ...@@ -378,15 +343,13 @@ static acpi_status acpi_device_notify_fixed(void *data)
static int acpi_device_install_notify_handler(struct acpi_device *device) static int acpi_device_install_notify_handler(struct acpi_device *device)
{ {
acpi_status status; acpi_status status;
char *hid;
hid = acpi_device_hid(device); if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
if (!strcmp(hid, ACPI_BUTTON_HID_POWERF))
status = status =
acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
acpi_device_notify_fixed, acpi_device_notify_fixed,
device); device);
else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
status = status =
acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
acpi_device_notify_fixed, acpi_device_notify_fixed,
...@@ -404,10 +367,10 @@ static int acpi_device_install_notify_handler(struct acpi_device *device) ...@@ -404,10 +367,10 @@ static int acpi_device_install_notify_handler(struct acpi_device *device)
static void acpi_device_remove_notify_handler(struct acpi_device *device) static void acpi_device_remove_notify_handler(struct acpi_device *device)
{ {
if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
acpi_device_notify_fixed); acpi_device_notify_fixed);
else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
acpi_device_notify_fixed); acpi_device_notify_fixed);
else else
...@@ -474,12 +437,12 @@ struct bus_type acpi_bus_type = { ...@@ -474,12 +437,12 @@ struct bus_type acpi_bus_type = {
.uevent = acpi_device_uevent, .uevent = acpi_device_uevent,
}; };
static int acpi_device_register(struct acpi_device *device, static int acpi_device_register(struct acpi_device *device)
struct acpi_device *parent)
{ {
int result; int result;
struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id;
int found = 0; int found = 0;
/* /*
* Linkage * Linkage
* ------- * -------
...@@ -501,8 +464,9 @@ static int acpi_device_register(struct acpi_device *device, ...@@ -501,8 +464,9 @@ static int acpi_device_register(struct acpi_device *device,
* If failed, create one and link it into acpi_bus_id_list * If failed, create one and link it into acpi_bus_id_list
*/ */
list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) {
if(!strcmp(acpi_device_bus_id->bus_id, device->flags.hardware_id? device->pnp.hardware_id : "device")) { if (!strcmp(acpi_device_bus_id->bus_id,
acpi_device_bus_id->instance_no ++; acpi_device_hid(device))) {
acpi_device_bus_id->instance_no++;
found = 1; found = 1;
kfree(new_bus_id); kfree(new_bus_id);
break; break;
...@@ -510,7 +474,7 @@ static int acpi_device_register(struct acpi_device *device, ...@@ -510,7 +474,7 @@ static int acpi_device_register(struct acpi_device *device,
} }
if (!found) { if (!found) {
acpi_device_bus_id = new_bus_id; acpi_device_bus_id = new_bus_id;
strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device"); strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device));
acpi_device_bus_id->instance_no = 0; acpi_device_bus_id->instance_no = 0;
list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
} }
...@@ -524,7 +488,7 @@ static int acpi_device_register(struct acpi_device *device, ...@@ -524,7 +488,7 @@ static int acpi_device_register(struct acpi_device *device,
mutex_unlock(&acpi_device_lock); mutex_unlock(&acpi_device_lock);
if (device->parent) if (device->parent)
device->dev.parent = &parent->dev; device->dev.parent = &device->parent->dev;
device->dev.bus = &acpi_bus_type; device->dev.bus = &acpi_bus_type;
device->dev.release = &acpi_device_release; device->dev.release = &acpi_device_release;
result = device_register(&device->dev); result = device_register(&device->dev);
...@@ -664,6 +628,33 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver); ...@@ -664,6 +628,33 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver);
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Device Enumeration Device Enumeration
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
static struct acpi_device *acpi_bus_get_parent(acpi_handle handle)
{
acpi_status status;
int ret;
struct acpi_device *device;
/*
* Fixed hardware devices do not appear in the namespace and do not
* have handles, but we fabricate acpi_devices for them, so we have
* to deal with them specially.
*/
if (handle == NULL)
return acpi_root;
do {
status = acpi_get_parent(handle, &handle);
if (status == AE_NULL_ENTRY)
return NULL;
if (ACPI_FAILURE(status))
return acpi_root;
ret = acpi_bus_get_device(handle, &device);
if (ret == 0)
return device;
} while (1);
}
acpi_status acpi_status
acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd)
{ {
...@@ -876,11 +867,6 @@ static int acpi_bus_get_flags(struct acpi_device *device) ...@@ -876,11 +867,6 @@ static int acpi_bus_get_flags(struct acpi_device *device)
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
device->flags.dynamic_status = 1; device->flags.dynamic_status = 1;
/* Presence of _CID indicates 'compatible_ids' */
status = acpi_get_handle(device->handle, "_CID", &temp);
if (ACPI_SUCCESS(status))
device->flags.compatible_ids = 1;
/* Presence of _RMV indicates 'removable' */ /* Presence of _RMV indicates 'removable' */
status = acpi_get_handle(device->handle, "_RMV", &temp); status = acpi_get_handle(device->handle, "_RMV", &temp);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
...@@ -918,8 +904,7 @@ static int acpi_bus_get_flags(struct acpi_device *device) ...@@ -918,8 +904,7 @@ static int acpi_bus_get_flags(struct acpi_device *device)
return 0; return 0;
} }
static void acpi_device_get_busid(struct acpi_device *device, static void acpi_device_get_busid(struct acpi_device *device)
acpi_handle handle, int type)
{ {
char bus_id[5] = { '?', 0 }; char bus_id[5] = { '?', 0 };
struct acpi_buffer buffer = { sizeof(bus_id), bus_id }; struct acpi_buffer buffer = { sizeof(bus_id), bus_id };
...@@ -931,10 +916,12 @@ static void acpi_device_get_busid(struct acpi_device *device, ...@@ -931,10 +916,12 @@ static void acpi_device_get_busid(struct acpi_device *device,
* The device's Bus ID is simply the object name. * The device's Bus ID is simply the object name.
* TBD: Shouldn't this value be unique (within the ACPI namespace)? * TBD: Shouldn't this value be unique (within the ACPI namespace)?
*/ */
switch (type) { if (ACPI_IS_ROOT_DEVICE(device)) {
case ACPI_BUS_TYPE_SYSTEM:
strcpy(device->pnp.bus_id, "ACPI"); strcpy(device->pnp.bus_id, "ACPI");
break; return;
}
switch (device->device_type) {
case ACPI_BUS_TYPE_POWER_BUTTON: case ACPI_BUS_TYPE_POWER_BUTTON:
strcpy(device->pnp.bus_id, "PWRF"); strcpy(device->pnp.bus_id, "PWRF");
break; break;
...@@ -942,7 +929,7 @@ static void acpi_device_get_busid(struct acpi_device *device, ...@@ -942,7 +929,7 @@ static void acpi_device_get_busid(struct acpi_device *device,
strcpy(device->pnp.bus_id, "SLPF"); strcpy(device->pnp.bus_id, "SLPF");
break; break;
default: default:
acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer);
/* Clean up trailing underscores (if any) */ /* Clean up trailing underscores (if any) */
for (i = 3; i > 1; i--) { for (i = 3; i > 1; i--) {
if (bus_id[i] == '_') if (bus_id[i] == '_')
...@@ -1000,204 +987,132 @@ static int acpi_dock_match(struct acpi_device *device) ...@@ -1000,204 +987,132 @@ static int acpi_dock_match(struct acpi_device *device)
return acpi_get_handle(device->handle, "_DCK", &tmp); return acpi_get_handle(device->handle, "_DCK", &tmp);
} }
static struct acpica_device_id_list* char *acpi_device_hid(struct acpi_device *device)
acpi_add_cid(
struct acpi_device_info *info,
struct acpica_device_id *new_cid)
{ {
struct acpica_device_id_list *cid; struct acpi_hardware_id *hid;
char *next_id_string;
acpi_size cid_length;
acpi_size new_cid_length;
u32 i;
/* Allocate new CID list with room for the new CID */
if (!new_cid)
new_cid_length = info->compatible_id_list.list_size;
else if (info->compatible_id_list.list_size)
new_cid_length = info->compatible_id_list.list_size +
new_cid->length + sizeof(struct acpica_device_id);
else
new_cid_length = sizeof(struct acpica_device_id_list) + new_cid->length;
cid = ACPI_ALLOCATE_ZEROED(new_cid_length);
if (!cid) {
return NULL;
}
cid->list_size = new_cid_length;
cid->count = info->compatible_id_list.count;
if (new_cid)
cid->count++;
next_id_string = (char *) cid->ids + (cid->count * sizeof(struct acpica_device_id));
/* Copy all existing CIDs */
for (i = 0; i < info->compatible_id_list.count; i++) {
cid_length = info->compatible_id_list.ids[i].length;
cid->ids[i].string = next_id_string;
cid->ids[i].length = cid_length;
ACPI_MEMCPY(next_id_string, info->compatible_id_list.ids[i].string, hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list);
cid_length); return hid->id;
}
next_id_string += cid_length; EXPORT_SYMBOL(acpi_device_hid);
}
/* Append the new CID */ static void acpi_add_id(struct acpi_device *device, const char *dev_id)
{
struct acpi_hardware_id *id;
if (new_cid) { id = kmalloc(sizeof(*id), GFP_KERNEL);
cid->ids[i].string = next_id_string; if (!id)
cid->ids[i].length = new_cid->length; return;
ACPI_MEMCPY(next_id_string, new_cid->string, new_cid->length); id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL);
if (!id->id) {
kfree(id);
return;
} }
return cid; strcpy(id->id, dev_id);
list_add_tail(&id->list, &device->pnp.ids);
} }
static void acpi_device_set_id(struct acpi_device *device, static void acpi_device_set_id(struct acpi_device *device)
struct acpi_device *parent, acpi_handle handle,
int type)
{ {
struct acpi_device_info *info = NULL;
char *hid = NULL;
char *uid = NULL;
struct acpica_device_id_list *cid_list = NULL;
char *cid_add = NULL;
acpi_status status; acpi_status status;
struct acpi_device_info *info;
struct acpica_device_id_list *cid_list;
int i;
switch (type) { switch (device->device_type) {
case ACPI_BUS_TYPE_DEVICE: case ACPI_BUS_TYPE_DEVICE:
status = acpi_get_object_info(handle, &info); if (ACPI_IS_ROOT_DEVICE(device)) {
acpi_add_id(device, ACPI_SYSTEM_HID);
break;
} else if (ACPI_IS_ROOT_DEVICE(device->parent)) {
/* \_SB_, the only root-level namespace device */
acpi_add_id(device, ACPI_BUS_HID);
strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME);
strcpy(device->pnp.device_class, ACPI_BUS_CLASS);
break;
}
status = acpi_get_object_info(device->handle, &info);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__); printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__);
return; return;
} }
if (info->valid & ACPI_VALID_HID) if (info->valid & ACPI_VALID_HID)
hid = info->hardware_id.string; acpi_add_id(device, info->hardware_id.string);
if (info->valid & ACPI_VALID_UID) if (info->valid & ACPI_VALID_CID) {
uid = info->unique_id.string;
if (info->valid & ACPI_VALID_CID)
cid_list = &info->compatible_id_list; cid_list = &info->compatible_id_list;
for (i = 0; i < cid_list->count; i++)
acpi_add_id(device, cid_list->ids[i].string);
}
if (info->valid & ACPI_VALID_ADR) { if (info->valid & ACPI_VALID_ADR) {
device->pnp.bus_address = info->address; device->pnp.bus_address = info->address;
device->flags.bus_address = 1; device->flags.bus_address = 1;
} }
/* If we have a video/bay/dock device, add our selfdefined /*
HID to the CID list. Like that the video/bay/dock drivers * Some devices don't reliably have _HIDs & _CIDs, so add
will get autoloaded and the device might still match * synthetic HIDs to make sure drivers can find them.
against another driver. */
*/
if (acpi_is_video_device(device)) if (acpi_is_video_device(device))
cid_add = ACPI_VIDEO_HID; acpi_add_id(device, ACPI_VIDEO_HID);
else if (ACPI_SUCCESS(acpi_bay_match(device))) else if (ACPI_SUCCESS(acpi_bay_match(device)))
cid_add = ACPI_BAY_HID; acpi_add_id(device, ACPI_BAY_HID);
else if (ACPI_SUCCESS(acpi_dock_match(device))) else if (ACPI_SUCCESS(acpi_dock_match(device)))
cid_add = ACPI_DOCK_HID; acpi_add_id(device, ACPI_DOCK_HID);
break; break;
case ACPI_BUS_TYPE_POWER: case ACPI_BUS_TYPE_POWER:
hid = ACPI_POWER_HID; acpi_add_id(device, ACPI_POWER_HID);
break; break;
case ACPI_BUS_TYPE_PROCESSOR: case ACPI_BUS_TYPE_PROCESSOR:
hid = ACPI_PROCESSOR_OBJECT_HID; acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID);
break;
case ACPI_BUS_TYPE_SYSTEM:
hid = ACPI_SYSTEM_HID;
break; break;
case ACPI_BUS_TYPE_THERMAL: case ACPI_BUS_TYPE_THERMAL:
hid = ACPI_THERMAL_HID; acpi_add_id(device, ACPI_THERMAL_HID);
break; break;
case ACPI_BUS_TYPE_POWER_BUTTON: case ACPI_BUS_TYPE_POWER_BUTTON:
hid = ACPI_BUTTON_HID_POWERF; acpi_add_id(device, ACPI_BUTTON_HID_POWERF);
break; break;
case ACPI_BUS_TYPE_SLEEP_BUTTON: case ACPI_BUS_TYPE_SLEEP_BUTTON:
hid = ACPI_BUTTON_HID_SLEEPF; acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF);
break; break;
} }
/* /*
* \_SB * We build acpi_devices for some objects that don't have _HID or _CID,
* ---- * e.g., PCI bridges and slots. Drivers can't bind to these objects,
* Fix for the system root bus device -- the only root-level device. * but we do use them indirectly by traversing the acpi_device tree.
* This generic ID isn't useful for driver binding, but it provides
* the useful property that "every acpi_device has an ID."
*/ */
if (((acpi_handle)parent == ACPI_ROOT_OBJECT) && (type == ACPI_BUS_TYPE_DEVICE)) { if (list_empty(&device->pnp.ids))
hid = ACPI_BUS_HID; acpi_add_id(device, "device");
strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME);
strcpy(device->pnp.device_class, ACPI_BUS_CLASS);
}
if (hid) {
device->pnp.hardware_id = ACPI_ALLOCATE_ZEROED(strlen (hid) + 1);
if (device->pnp.hardware_id) {
strcpy(device->pnp.hardware_id, hid);
device->flags.hardware_id = 1;
}
}
if (!device->flags.hardware_id)
device->pnp.hardware_id = "";
if (uid) {
device->pnp.unique_id = ACPI_ALLOCATE_ZEROED(strlen (uid) + 1);
if (device->pnp.unique_id) {
strcpy(device->pnp.unique_id, uid);
device->flags.unique_id = 1;
}
}
if (!device->flags.unique_id)
device->pnp.unique_id = "";
if (cid_list || cid_add) {
struct acpica_device_id_list *list;
if (cid_add) {
struct acpica_device_id cid;
cid.length = strlen (cid_add) + 1;
cid.string = cid_add;
list = acpi_add_cid(info, &cid);
} else {
list = acpi_add_cid(info, NULL);
}
if (list) {
device->pnp.cid_list = list;
if (cid_add)
device->flags.compatible_ids = 1;
}
}
kfree(info);
} }
static int acpi_device_set_context(struct acpi_device *device, int type) static int acpi_device_set_context(struct acpi_device *device)
{ {
acpi_status status = AE_OK; acpi_status status;
int result = 0;
/* /*
* Context * Context
* ------- * -------
* Attach this 'struct acpi_device' to the ACPI object. This makes * Attach this 'struct acpi_device' to the ACPI object. This makes
* resolutions from handle->device very efficient. Note that we need * resolutions from handle->device very efficient. Fixed hardware
* to be careful with fixed-feature devices as they all attach to the * devices have no handles, so we skip them.
* root object.
*/ */
if (type != ACPI_BUS_TYPE_POWER_BUTTON && if (!device->handle)
type != ACPI_BUS_TYPE_SLEEP_BUTTON) { return 0;
status = acpi_attach_data(device->handle,
acpi_bus_data_handler, device);
if (ACPI_FAILURE(status)) { status = acpi_attach_data(device->handle,
printk(KERN_ERR PREFIX "Error attaching device data\n"); acpi_bus_data_handler, device);
result = -ENODEV; if (ACPI_SUCCESS(status))
} return 0;
}
return result; printk(KERN_ERR PREFIX "Error attaching device data\n");
return -ENODEV;
} }
static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) static int acpi_bus_remove(struct acpi_device *dev, int rmdevice)
...@@ -1223,17 +1138,14 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) ...@@ -1223,17 +1138,14 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice)
return 0; return 0;
} }
static int static int acpi_add_single_object(struct acpi_device **child,
acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type,
struct acpi_device *parent, acpi_handle handle, int type, unsigned long long sta,
struct acpi_bus_ops *ops) struct acpi_bus_ops *ops)
{ {
int result = 0; int result;
struct acpi_device *device = NULL; struct acpi_device *device;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
if (!child)
return -EINVAL;
device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
if (!device) { if (!device) {
...@@ -1241,75 +1153,31 @@ acpi_add_single_object(struct acpi_device **child, ...@@ -1241,75 +1153,31 @@ acpi_add_single_object(struct acpi_device **child,
return -ENOMEM; return -ENOMEM;
} }
INIT_LIST_HEAD(&device->pnp.ids);
device->device_type = type;
device->handle = handle; device->handle = handle;
device->parent = parent; device->parent = acpi_bus_get_parent(handle);
device->bus_ops = *ops; /* workround for not call .start */ device->bus_ops = *ops; /* workround for not call .start */
STRUCT_TO_INT(device->status) = sta;
acpi_device_get_busid(device);
acpi_device_get_busid(device, handle, type);
/* /*
* Flags * Flags
* ----- * -----
* Get prior to calling acpi_bus_get_status() so we know whether * Note that we only look for object handles -- cannot evaluate objects
* or not _STA is present. Note that we only look for object * until we know the device is present and properly initialized.
* handles -- cannot evaluate objects until we know the device is
* present and properly initialized.
*/ */
result = acpi_bus_get_flags(device); result = acpi_bus_get_flags(device);
if (result) if (result)
goto end; goto end;
/*
* Status
* ------
* See if the device is present. We always assume that non-Device
* and non-Processor objects (e.g. thermal zones, power resources,
* etc.) are present, functioning, etc. (at least when parent object
* is present). Note that _STA has a different meaning for some
* objects (e.g. power resources) so we need to be careful how we use
* it.
*/
switch (type) {
case ACPI_BUS_TYPE_PROCESSOR:
case ACPI_BUS_TYPE_DEVICE:
result = acpi_bus_get_status(device);
if (ACPI_FAILURE(result)) {
result = -ENODEV;
goto end;
}
/*
* When the device is neither present nor functional, the
* device should not be added to Linux ACPI device tree.
* When the status of the device is not present but functinal,
* it should be added to Linux ACPI tree. For example : bay
* device , dock device.
* In such conditions it is unncessary to check whether it is
* bay device or dock device.
*/
if (!device->status.present && !device->status.functional) {
result = -ENODEV;
goto end;
}
break;
default:
STRUCT_TO_INT(device->status) =
ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING;
break;
}
/* /*
* Initialize Device * Initialize Device
* ----------------- * -----------------
* TBD: Synch with Core's enumeration/initialization process. * TBD: Synch with Core's enumeration/initialization process.
*/ */
acpi_device_set_id(device);
/*
* Hardware ID, Unique ID, & Bus Address
* -------------------------------------
*/
acpi_device_set_id(device, parent, handle, type);
/* /*
* Power Management * Power Management
...@@ -1341,10 +1209,10 @@ acpi_add_single_object(struct acpi_device **child, ...@@ -1341,10 +1209,10 @@ acpi_add_single_object(struct acpi_device **child,
goto end; goto end;
} }
if ((result = acpi_device_set_context(device, type))) if ((result = acpi_device_set_context(device)))
goto end; goto end;
result = acpi_device_register(device, parent); result = acpi_device_register(device);
/* /*
* Bind _ADR-Based Devices when hot add * Bind _ADR-Based Devices when hot add
...@@ -1355,128 +1223,122 @@ acpi_add_single_object(struct acpi_device **child, ...@@ -1355,128 +1223,122 @@ acpi_add_single_object(struct acpi_device **child,
} }
end: end:
if (!result) if (!result) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Adding %s [%s] parent %s\n", dev_name(&device->dev),
(char *) buffer.pointer,
device->parent ? dev_name(&device->parent->dev) :
"(null)"));
kfree(buffer.pointer);
*child = device; *child = device;
else } else
acpi_device_release(&device->dev); acpi_device_release(&device->dev);
return result; return result;
} }
static int acpi_bus_scan(struct acpi_device *start, struct acpi_bus_ops *ops) #define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)
static int acpi_bus_type_and_status(acpi_handle handle, int *type,
unsigned long long *sta)
{ {
acpi_status status = AE_OK; acpi_status status;
struct acpi_device *parent = NULL; acpi_object_type acpi_type;
struct acpi_device *child = NULL;
acpi_handle phandle = NULL;
acpi_handle chandle = NULL;
acpi_object_type type = 0;
u32 level = 1;
status = acpi_get_type(handle, &acpi_type);
if (ACPI_FAILURE(status))
return -ENODEV;
if (!start) switch (acpi_type) {
return -EINVAL; case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */
case ACPI_TYPE_DEVICE:
*type = ACPI_BUS_TYPE_DEVICE;
status = acpi_bus_get_status_handle(handle, sta);
if (ACPI_FAILURE(status))
return -ENODEV;
break;
case ACPI_TYPE_PROCESSOR:
*type = ACPI_BUS_TYPE_PROCESSOR;
status = acpi_bus_get_status_handle(handle, sta);
if (ACPI_FAILURE(status))
return -ENODEV;
break;
case ACPI_TYPE_THERMAL:
*type = ACPI_BUS_TYPE_THERMAL;
*sta = ACPI_STA_DEFAULT;
break;
case ACPI_TYPE_POWER:
*type = ACPI_BUS_TYPE_POWER;
*sta = ACPI_STA_DEFAULT;
break;
default:
return -ENODEV;
}
parent = start; return 0;
phandle = start->handle; }
/* static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
* Parse through the ACPI namespace, identify all 'devices', and void *context, void **return_value)
* create a new 'struct acpi_device' for each. {
*/ struct acpi_bus_ops *ops = context;
while ((level > 0) && parent) { int type;
unsigned long long sta;
struct acpi_device *device;
acpi_status status;
int result;
status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, result = acpi_bus_type_and_status(handle, &type, &sta);
chandle, &chandle); if (result)
return AE_OK;
/* if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
* If this scope is exhausted then move our way back up. !(sta & ACPI_STA_DEVICE_FUNCTIONING))
*/ return AE_CTRL_DEPTH;
if (ACPI_FAILURE(status)) {
level--;
chandle = phandle;
acpi_get_parent(phandle, &phandle);
if (parent->parent)
parent = parent->parent;
continue;
}
status = acpi_get_type(chandle, &type); /*
if (ACPI_FAILURE(status)) * We may already have an acpi_device from a previous enumeration. If
continue; * so, we needn't add it again, but we may still have to start it.
*/
device = NULL;
acpi_bus_get_device(handle, &device);
if (ops->acpi_op_add && !device)
acpi_add_single_object(&device, handle, type, sta, ops);
/* if (!device)
* If this is a scope object then parse it (depth-first). return AE_CTRL_DEPTH;
*/
if (type == ACPI_TYPE_LOCAL_SCOPE) {
level++;
phandle = chandle;
chandle = NULL;
continue;
}
/* if (ops->acpi_op_start && !(ops->acpi_op_add)) {
* We're only interested in objects that we consider 'devices'. status = acpi_start_single_object(device);
*/ if (ACPI_FAILURE(status))
switch (type) { return AE_CTRL_DEPTH;
case ACPI_TYPE_DEVICE: }
type = ACPI_BUS_TYPE_DEVICE;
break;
case ACPI_TYPE_PROCESSOR:
type = ACPI_BUS_TYPE_PROCESSOR;
break;
case ACPI_TYPE_THERMAL:
type = ACPI_BUS_TYPE_THERMAL;
break;
case ACPI_TYPE_POWER:
type = ACPI_BUS_TYPE_POWER;
break;
default:
continue;
}
if (ops->acpi_op_add) if (!*return_value)
status = acpi_add_single_object(&child, parent, *return_value = device;
chandle, type, ops); return AE_OK;
else }
status = acpi_bus_get_device(chandle, &child);
if (ACPI_FAILURE(status)) static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
continue; struct acpi_device **child)
{
acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
void *device = NULL;
if (ops->acpi_op_start && !(ops->acpi_op_add)) { acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
status = acpi_start_single_object(child); printk(KERN_INFO PREFIX "Enumerating devices from [%s]\n",
if (ACPI_FAILURE(status)) (char *) buffer.pointer);
continue;
}
/* status = acpi_bus_check_add(handle, 0, ops, &device);
* If the device is present, enabled, and functioning then if (ACPI_SUCCESS(status))
* parse its scope (depth-first). Note that we need to acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
* represent absent devices to facilitate PnP notifications acpi_bus_check_add, ops, &device);
* -- but only the subtree head (not all of its children,
* which will be enumerated when the parent is inserted).
*
* TBD: Need notifications and other detection mechanisms
* in place before we can fully implement this.
*/
/*
* When the device is not present but functional, it is also
* necessary to scan the children of this device.
*/
if (child->status.present || (!child->status.present &&
child->status.functional)) {
status = acpi_get_next_object(ACPI_TYPE_ANY, chandle,
NULL, NULL);
if (ACPI_SUCCESS(status)) {
level++;
phandle = chandle;
chandle = NULL;
parent = child;
}
}
}
if (child)
*child = device;
return 0; return 0;
} }
...@@ -1484,36 +1346,25 @@ int ...@@ -1484,36 +1346,25 @@ int
acpi_bus_add(struct acpi_device **child, acpi_bus_add(struct acpi_device **child,
struct acpi_device *parent, acpi_handle handle, int type) struct acpi_device *parent, acpi_handle handle, int type)
{ {
int result;
struct acpi_bus_ops ops; struct acpi_bus_ops ops;
memset(&ops, 0, sizeof(ops)); memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1; ops.acpi_op_add = 1;
result = acpi_add_single_object(child, parent, handle, type, &ops); acpi_bus_scan(handle, &ops, child);
if (!result) return 0;
result = acpi_bus_scan(*child, &ops);
return result;
} }
EXPORT_SYMBOL(acpi_bus_add); EXPORT_SYMBOL(acpi_bus_add);
int acpi_bus_start(struct acpi_device *device) int acpi_bus_start(struct acpi_device *device)
{ {
int result;
struct acpi_bus_ops ops; struct acpi_bus_ops ops;
memset(&ops, 0, sizeof(ops));
ops.acpi_op_start = 1;
if (!device) acpi_bus_scan(device->handle, &ops, NULL);
return -EINVAL; return 0;
result = acpi_start_single_object(device);
if (!result) {
memset(&ops, 0, sizeof(ops));
ops.acpi_op_start = 1;
result = acpi_bus_scan(device, &ops);
}
return result;
} }
EXPORT_SYMBOL(acpi_bus_start); EXPORT_SYMBOL(acpi_bus_start);
...@@ -1572,15 +1423,12 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice) ...@@ -1572,15 +1423,12 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice)
} }
EXPORT_SYMBOL_GPL(acpi_bus_trim); EXPORT_SYMBOL_GPL(acpi_bus_trim);
static int acpi_bus_scan_fixed(struct acpi_device *root) static int acpi_bus_scan_fixed(void)
{ {
int result = 0; int result = 0;
struct acpi_device *device = NULL; struct acpi_device *device = NULL;
struct acpi_bus_ops ops; struct acpi_bus_ops ops;
if (!root)
return -ENODEV;
memset(&ops, 0, sizeof(ops)); memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1; ops.acpi_op_add = 1;
ops.acpi_op_start = 1; ops.acpi_op_start = 1;
...@@ -1589,16 +1437,16 @@ static int acpi_bus_scan_fixed(struct acpi_device *root) ...@@ -1589,16 +1437,16 @@ static int acpi_bus_scan_fixed(struct acpi_device *root)
* Enumerate all fixed-feature devices. * Enumerate all fixed-feature devices.
*/ */
if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) {
result = acpi_add_single_object(&device, acpi_root, result = acpi_add_single_object(&device, NULL,
NULL,
ACPI_BUS_TYPE_POWER_BUTTON, ACPI_BUS_TYPE_POWER_BUTTON,
ACPI_STA_DEFAULT,
&ops); &ops);
} }
if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
result = acpi_add_single_object(&device, acpi_root, result = acpi_add_single_object(&device, NULL,
NULL,
ACPI_BUS_TYPE_SLEEP_BUTTON, ACPI_BUS_TYPE_SLEEP_BUTTON,
ACPI_STA_DEFAULT,
&ops); &ops);
} }
...@@ -1620,25 +1468,16 @@ int __init acpi_scan_init(void) ...@@ -1620,25 +1468,16 @@ int __init acpi_scan_init(void)
printk(KERN_ERR PREFIX "Could not register bus type\n"); printk(KERN_ERR PREFIX "Could not register bus type\n");
} }
/*
* Create the root device in the bus's device tree
*/
result = acpi_add_single_object(&acpi_root, NULL, ACPI_ROOT_OBJECT,
ACPI_BUS_TYPE_SYSTEM, &ops);
if (result)
goto Done;
/* /*
* Enumerate devices in the ACPI namespace. * Enumerate devices in the ACPI namespace.
*/ */
result = acpi_bus_scan_fixed(acpi_root); result = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root);
if (!result) if (!result)
result = acpi_bus_scan(acpi_root, &ops); result = acpi_bus_scan_fixed();
if (result) if (result)
acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
Done:
return result; return result;
} }
...@@ -369,9 +369,8 @@ static int acpi_smbus_cmi_add(struct acpi_device *device) ...@@ -369,9 +369,8 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
goto err; goto err;
snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name), snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
"SMBus CMI adapter %s (%s)", "SMBus CMI adapter %s",
acpi_device_name(device), acpi_device_name(device));
acpi_device_uid(device));
smbus_cmi->adapter.owner = THIS_MODULE; smbus_cmi->adapter.owner = THIS_MODULE;
smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm; smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
smbus_cmi->adapter.algo_data = smbus_cmi; smbus_cmi->adapter.algo_data = smbus_cmi;
......
...@@ -406,7 +406,6 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle, ...@@ -406,7 +406,6 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
__func__, status); __func__, status);
return retval; return retval;
} }
info->hardware_id.string[sizeof(info->hardware_id.length) - 1] = '\0';
if (info->current_status && (info->valid & ACPI_VALID_HID) && if (info->current_status && (info->valid & ACPI_VALID_HID) &&
(!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) || (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
*/ */
#define TPACPI_VERSION "0.23" #define TPACPI_VERSION "0.23"
#define TPACPI_SYSFS_VERSION 0x020400 #define TPACPI_SYSFS_VERSION 0x020500
/* /*
* Changelog: * Changelog:
...@@ -145,6 +145,51 @@ enum { ...@@ -145,6 +145,51 @@ enum {
TP_ACPI_WGSV_STATE_UWBPWR = 0x0020, /* UWB radio enabled */ TP_ACPI_WGSV_STATE_UWBPWR = 0x0020, /* UWB radio enabled */
}; };
/* HKEY events */
enum tpacpi_hkey_event_t {
/* Hotkey-related */
TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */
TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */
TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */
TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */
TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */
TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */
/* Reasons for waking up from S3/S4 */
TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */
TP_HKEY_EV_WKUP_S4_UNDOCK = 0x2404, /* undock requested, S4 */
TP_HKEY_EV_WKUP_S3_BAYEJ = 0x2305, /* bay ejection req, S3 */
TP_HKEY_EV_WKUP_S4_BAYEJ = 0x2405, /* bay ejection req, S4 */
TP_HKEY_EV_WKUP_S3_BATLOW = 0x2313, /* battery empty, S3 */
TP_HKEY_EV_WKUP_S4_BATLOW = 0x2413, /* battery empty, S4 */
/* Auto-sleep after eject request */
TP_HKEY_EV_BAYEJ_ACK = 0x3003, /* bay ejection complete */
TP_HKEY_EV_UNDOCK_ACK = 0x4003, /* undock complete */
/* Misc bay events */
TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */
/* User-interface events */
TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */
TP_HKEY_EV_LID_OPEN = 0x5002, /* laptop lid opened */
TP_HKEY_EV_TABLET_TABLET = 0x5009, /* tablet swivel up */
TP_HKEY_EV_TABLET_NOTEBOOK = 0x500a, /* tablet swivel down */
TP_HKEY_EV_PEN_INSERTED = 0x500b, /* tablet pen inserted */
TP_HKEY_EV_PEN_REMOVED = 0x500c, /* tablet pen removed */
TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */
/* Thermal events */
TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */
TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */
TP_HKEY_EV_ALARM_SENSOR_HOT = 0x6021, /* sensor too hot */
TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */
/* Misc */
TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */
};
/**************************************************************************** /****************************************************************************
* Main driver * Main driver
*/ */
...@@ -1848,6 +1893,27 @@ static struct ibm_struct thinkpad_acpi_driver_data = { ...@@ -1848,6 +1893,27 @@ static struct ibm_struct thinkpad_acpi_driver_data = {
* Hotkey subdriver * Hotkey subdriver
*/ */
/*
* ThinkPad firmware event model
*
* The ThinkPad firmware has two main event interfaces: normal ACPI
* notifications (which follow the ACPI standard), and a private event
* interface.
*
* The private event interface also issues events for the hotkeys. As
* the driver gained features, the event handling code ended up being
* built around the hotkey subdriver. This will need to be refactored
* to a more formal event API eventually.
*
* Some "hotkeys" are actually supposed to be used as event reports,
* such as "brightness has changed", "volume has changed", depending on
* the ThinkPad model and how the firmware is operating.
*
* Unlike other classes, hotkey-class events have mask/unmask control on
* non-ancient firmware. However, how it behaves changes a lot with the
* firmware model and version.
*/
enum { /* hot key scan codes (derived from ACPI DSDT) */ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_FNF1 = 0, TP_ACPI_HOTKEYSCAN_FNF1 = 0,
TP_ACPI_HOTKEYSCAN_FNF2, TP_ACPI_HOTKEYSCAN_FNF2,
...@@ -1875,7 +1941,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ ...@@ -1875,7 +1941,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_THINKPAD, TP_ACPI_HOTKEYSCAN_THINKPAD,
}; };
enum { /* Keys available through NVRAM polling */ enum { /* Keys/events available through NVRAM polling */
TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U, TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U,
TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U, TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U,
}; };
...@@ -1930,8 +1996,11 @@ static struct task_struct *tpacpi_hotkey_task; ...@@ -1930,8 +1996,11 @@ static struct task_struct *tpacpi_hotkey_task;
static struct mutex hotkey_thread_mutex; static struct mutex hotkey_thread_mutex;
/* /*
* Acquire mutex to write poller control variables. * Acquire mutex to write poller control variables as an
* Increment hotkey_config_change when changing them. * atomic block.
*
* Increment hotkey_config_change when changing them if you
* want the kthread to forget old state.
* *
* See HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END * See HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END
*/ */
...@@ -1942,6 +2011,11 @@ static unsigned int hotkey_config_change; ...@@ -1942,6 +2011,11 @@ static unsigned int hotkey_config_change;
* hotkey poller control variables * hotkey poller control variables
* *
* Must be atomic or readers will also need to acquire mutex * Must be atomic or readers will also need to acquire mutex
*
* HOTKEY_CONFIG_CRITICAL_START/HOTKEY_CONFIG_CRITICAL_END
* should be used only when the changes need to be taken as
* a block, OR when one needs to force the kthread to forget
* old state.
*/ */
static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */ static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */
static unsigned int hotkey_poll_freq = 10; /* Hz */ static unsigned int hotkey_poll_freq = 10; /* Hz */
...@@ -1972,10 +2046,12 @@ static enum { /* Reasons for waking up */ ...@@ -1972,10 +2046,12 @@ static enum { /* Reasons for waking up */
static int hotkey_autosleep_ack; static int hotkey_autosleep_ack;
static u32 hotkey_orig_mask; static u32 hotkey_orig_mask; /* events the BIOS had enabled */
static u32 hotkey_all_mask; static u32 hotkey_all_mask; /* all events supported in fw */
static u32 hotkey_reserved_mask; static u32 hotkey_reserved_mask; /* events better left disabled */
static u32 hotkey_mask; static u32 hotkey_driver_mask; /* events needed by the driver */
static u32 hotkey_user_mask; /* events visible to userspace */
static u32 hotkey_acpi_mask; /* events enabled in firmware */
static unsigned int hotkey_report_mode; static unsigned int hotkey_report_mode;
...@@ -1983,6 +2059,9 @@ static u16 *hotkey_keycode_map; ...@@ -1983,6 +2059,9 @@ static u16 *hotkey_keycode_map;
static struct attribute_set *hotkey_dev_attributes; static struct attribute_set *hotkey_dev_attributes;
static void tpacpi_driver_event(const unsigned int hkey_event);
static void hotkey_driver_event(const unsigned int scancode);
/* HKEY.MHKG() return bits */ /* HKEY.MHKG() return bits */
#define TP_HOTKEY_TABLET_MASK (1 << 3) #define TP_HOTKEY_TABLET_MASK (1 << 3)
...@@ -2017,24 +2096,53 @@ static int hotkey_get_tablet_mode(int *status) ...@@ -2017,24 +2096,53 @@ static int hotkey_get_tablet_mode(int *status)
} }
/* /*
* Reads current event mask from firmware, and updates
* hotkey_acpi_mask accordingly. Also resets any bits
* from hotkey_user_mask that are unavailable to be
* delivered (shadow requirement of the userspace ABI).
*
* Call with hotkey_mutex held * Call with hotkey_mutex held
*/ */
static int hotkey_mask_get(void) static int hotkey_mask_get(void)
{ {
u32 m = 0;
if (tp_features.hotkey_mask) { if (tp_features.hotkey_mask) {
u32 m = 0;
if (!acpi_evalf(hkey_handle, &m, "DHKN", "d")) if (!acpi_evalf(hkey_handle, &m, "DHKN", "d"))
return -EIO; return -EIO;
hotkey_acpi_mask = m;
} else {
/* no mask support doesn't mean no event support... */
hotkey_acpi_mask = hotkey_all_mask;
} }
HOTKEY_CONFIG_CRITICAL_START
hotkey_mask = m | (hotkey_source_mask & hotkey_mask); /* sync userspace-visible mask */
HOTKEY_CONFIG_CRITICAL_END hotkey_user_mask &= (hotkey_acpi_mask | hotkey_source_mask);
return 0; return 0;
} }
void static hotkey_mask_warn_incomplete_mask(void)
{
/* log only what the user can fix... */
const u32 wantedmask = hotkey_driver_mask &
~(hotkey_acpi_mask | hotkey_source_mask) &
(hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK);
if (wantedmask)
printk(TPACPI_NOTICE
"required events 0x%08x not enabled!\n",
wantedmask);
}
/* /*
* Set the firmware mask when supported
*
* Also calls hotkey_mask_get to update hotkey_acpi_mask.
*
* NOTE: does not set bits in hotkey_user_mask, but may reset them.
*
* Call with hotkey_mutex held * Call with hotkey_mutex held
*/ */
static int hotkey_mask_set(u32 mask) static int hotkey_mask_set(u32 mask)
...@@ -2042,66 +2150,98 @@ static int hotkey_mask_set(u32 mask) ...@@ -2042,66 +2150,98 @@ static int hotkey_mask_set(u32 mask)
int i; int i;
int rc = 0; int rc = 0;
if (tp_features.hotkey_mask) { const u32 fwmask = mask & ~hotkey_source_mask;
if (!tp_warned.hotkey_mask_ff &&
(mask == 0xffff || mask == 0xffffff ||
mask == 0xffffffff)) {
tp_warned.hotkey_mask_ff = 1;
printk(TPACPI_NOTICE
"setting the hotkey mask to 0x%08x is likely "
"not the best way to go about it\n", mask);
printk(TPACPI_NOTICE
"please consider using the driver defaults, "
"and refer to up-to-date thinkpad-acpi "
"documentation\n");
}
HOTKEY_CONFIG_CRITICAL_START if (tp_features.hotkey_mask) {
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++) {
u32 m = 1 << i;
/* enable in firmware mask only keys not in NVRAM
* mode, but enable the key in the cached hotkey_mask
* regardless of mode, or the key will end up
* disabled by hotkey_mask_get() */
if (!acpi_evalf(hkey_handle, if (!acpi_evalf(hkey_handle,
NULL, "MHKM", "vdd", i + 1, NULL, "MHKM", "vdd", i + 1,
!!((mask & ~hotkey_source_mask) & m))) { !!(mask & (1 << i)))) {
rc = -EIO; rc = -EIO;
break; break;
} else {
hotkey_mask = (hotkey_mask & ~m) | (mask & m);
} }
} }
HOTKEY_CONFIG_CRITICAL_END }
/* hotkey_mask_get must be called unconditionally below */ /*
if (!hotkey_mask_get() && !rc && * We *must* make an inconditional call to hotkey_mask_get to
(hotkey_mask & ~hotkey_source_mask) != * refresh hotkey_acpi_mask and update hotkey_user_mask
(mask & ~hotkey_source_mask)) { *
printk(TPACPI_NOTICE * Take the opportunity to also log when we cannot _enable_
"requested hot key mask 0x%08x, but " * a given event.
"firmware forced it to 0x%08x\n", */
mask, hotkey_mask); if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) {
} printk(TPACPI_NOTICE
} else { "asked for hotkey mask 0x%08x, but "
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL "firmware forced it to 0x%08x\n",
HOTKEY_CONFIG_CRITICAL_START fwmask, hotkey_acpi_mask);
hotkey_mask = mask & hotkey_source_mask;
HOTKEY_CONFIG_CRITICAL_END
hotkey_mask_get();
if (hotkey_mask != mask) {
printk(TPACPI_NOTICE
"requested hot key mask 0x%08x, "
"forced to 0x%08x (NVRAM poll mask is "
"0x%08x): no firmware mask support\n",
mask, hotkey_mask, hotkey_source_mask);
}
#else
hotkey_mask_get();
rc = -ENXIO;
#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
} }
hotkey_mask_warn_incomplete_mask();
return rc;
}
/*
* Sets hotkey_user_mask and tries to set the firmware mask
*
* Call with hotkey_mutex held
*/
static int hotkey_user_mask_set(const u32 mask)
{
int rc;
/* Give people a chance to notice they are doing something that
* is bound to go boom on their users sooner or later */
if (!tp_warned.hotkey_mask_ff &&
(mask == 0xffff || mask == 0xffffff ||
mask == 0xffffffff)) {
tp_warned.hotkey_mask_ff = 1;
printk(TPACPI_NOTICE
"setting the hotkey mask to 0x%08x is likely "
"not the best way to go about it\n", mask);
printk(TPACPI_NOTICE
"please consider using the driver defaults, "
"and refer to up-to-date thinkpad-acpi "
"documentation\n");
}
/* Try to enable what the user asked for, plus whatever we need.
* this syncs everything but won't enable bits in hotkey_user_mask */
rc = hotkey_mask_set((mask | hotkey_driver_mask) & ~hotkey_source_mask);
/* Enable the available bits in hotkey_user_mask */
hotkey_user_mask = mask & (hotkey_acpi_mask | hotkey_source_mask);
return rc;
}
/*
* Sets the driver hotkey mask.
*
* Can be called even if the hotkey subdriver is inactive
*/
static int tpacpi_hotkey_driver_mask_set(const u32 mask)
{
int rc;
/* Do the right thing if hotkey_init has not been called yet */
if (!tp_features.hotkey) {
hotkey_driver_mask = mask;
return 0;
}
mutex_lock(&hotkey_mutex);
HOTKEY_CONFIG_CRITICAL_START
hotkey_driver_mask = mask;
hotkey_source_mask |= (mask & ~hotkey_all_mask);
HOTKEY_CONFIG_CRITICAL_END
rc = hotkey_mask_set((hotkey_acpi_mask | hotkey_driver_mask) &
~hotkey_source_mask);
mutex_unlock(&hotkey_mutex);
return rc; return rc;
} }
...@@ -2137,11 +2277,10 @@ static void tpacpi_input_send_tabletsw(void) ...@@ -2137,11 +2277,10 @@ static void tpacpi_input_send_tabletsw(void)
} }
} }
static void tpacpi_input_send_key(unsigned int scancode) /* Do NOT call without validating scancode first */
static void tpacpi_input_send_key(const unsigned int scancode)
{ {
unsigned int keycode; const unsigned int keycode = hotkey_keycode_map[scancode];
keycode = hotkey_keycode_map[scancode];
if (keycode != KEY_RESERVED) { if (keycode != KEY_RESERVED) {
mutex_lock(&tpacpi_inputdev_send_mutex); mutex_lock(&tpacpi_inputdev_send_mutex);
...@@ -2162,19 +2301,28 @@ static void tpacpi_input_send_key(unsigned int scancode) ...@@ -2162,19 +2301,28 @@ static void tpacpi_input_send_key(unsigned int scancode)
} }
} }
/* Do NOT call without validating scancode first */
static void tpacpi_input_send_key_masked(const unsigned int scancode)
{
hotkey_driver_event(scancode);
if (hotkey_user_mask & (1 << scancode))
tpacpi_input_send_key(scancode);
}
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
/* Do NOT call without validating scancode first */
static void tpacpi_hotkey_send_key(unsigned int scancode) static void tpacpi_hotkey_send_key(unsigned int scancode)
{ {
tpacpi_input_send_key(scancode); tpacpi_input_send_key_masked(scancode);
if (hotkey_report_mode < 2) { if (hotkey_report_mode < 2) {
acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device, acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
0x80, 0x1001 + scancode); 0x80, TP_HKEY_EV_HOTKEY_BASE + scancode);
} }
} }
static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m) static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
{ {
u8 d; u8 d;
...@@ -2210,21 +2358,24 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m) ...@@ -2210,21 +2358,24 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m)
} }
} }
static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
struct tp_nvram_state *newn,
const u32 event_mask)
{
#define TPACPI_COMPARE_KEY(__scancode, __member) \ #define TPACPI_COMPARE_KEY(__scancode, __member) \
do { \ do { \
if ((mask & (1 << __scancode)) && \ if ((event_mask & (1 << __scancode)) && \
oldn->__member != newn->__member) \ oldn->__member != newn->__member) \
tpacpi_hotkey_send_key(__scancode); \ tpacpi_hotkey_send_key(__scancode); \
} while (0) } while (0)
#define TPACPI_MAY_SEND_KEY(__scancode) \ #define TPACPI_MAY_SEND_KEY(__scancode) \
do { if (mask & (1 << __scancode)) \ do { \
tpacpi_hotkey_send_key(__scancode); } while (0) if (event_mask & (1 << __scancode)) \
tpacpi_hotkey_send_key(__scancode); \
} while (0)
static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
struct tp_nvram_state *newn,
u32 mask)
{
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
...@@ -2270,15 +2421,22 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, ...@@ -2270,15 +2421,22 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
} }
} }
} }
}
#undef TPACPI_COMPARE_KEY #undef TPACPI_COMPARE_KEY
#undef TPACPI_MAY_SEND_KEY #undef TPACPI_MAY_SEND_KEY
}
/*
* Polling driver
*
* We track all events in hotkey_source_mask all the time, since
* most of them are edge-based. We only issue those requested by
* hotkey_user_mask or hotkey_driver_mask, though.
*/
static int hotkey_kthread(void *data) static int hotkey_kthread(void *data)
{ {
struct tp_nvram_state s[2]; struct tp_nvram_state s[2];
u32 mask; u32 poll_mask, event_mask;
unsigned int si, so; unsigned int si, so;
unsigned long t; unsigned long t;
unsigned int change_detector, must_reset; unsigned int change_detector, must_reset;
...@@ -2298,10 +2456,12 @@ static int hotkey_kthread(void *data) ...@@ -2298,10 +2456,12 @@ static int hotkey_kthread(void *data)
/* Initial state for compares */ /* Initial state for compares */
mutex_lock(&hotkey_thread_data_mutex); mutex_lock(&hotkey_thread_data_mutex);
change_detector = hotkey_config_change; change_detector = hotkey_config_change;
mask = hotkey_source_mask & hotkey_mask; poll_mask = hotkey_source_mask;
event_mask = hotkey_source_mask &
(hotkey_driver_mask | hotkey_user_mask);
poll_freq = hotkey_poll_freq; poll_freq = hotkey_poll_freq;
mutex_unlock(&hotkey_thread_data_mutex); mutex_unlock(&hotkey_thread_data_mutex);
hotkey_read_nvram(&s[so], mask); hotkey_read_nvram(&s[so], poll_mask);
while (!kthread_should_stop()) { while (!kthread_should_stop()) {
if (t == 0) { if (t == 0) {
...@@ -2324,15 +2484,17 @@ static int hotkey_kthread(void *data) ...@@ -2324,15 +2484,17 @@ static int hotkey_kthread(void *data)
t = 0; t = 0;
change_detector = hotkey_config_change; change_detector = hotkey_config_change;
} }
mask = hotkey_source_mask & hotkey_mask; poll_mask = hotkey_source_mask;
event_mask = hotkey_source_mask &
(hotkey_driver_mask | hotkey_user_mask);
poll_freq = hotkey_poll_freq; poll_freq = hotkey_poll_freq;
mutex_unlock(&hotkey_thread_data_mutex); mutex_unlock(&hotkey_thread_data_mutex);
if (likely(mask)) { if (likely(poll_mask)) {
hotkey_read_nvram(&s[si], mask); hotkey_read_nvram(&s[si], poll_mask);
if (likely(si != so)) { if (likely(si != so)) {
hotkey_compare_and_issue_event(&s[so], &s[si], hotkey_compare_and_issue_event(&s[so], &s[si],
mask); event_mask);
} }
} }
...@@ -2364,10 +2526,12 @@ static void hotkey_poll_stop_sync(void) ...@@ -2364,10 +2526,12 @@ static void hotkey_poll_stop_sync(void)
/* call with hotkey_mutex held */ /* call with hotkey_mutex held */
static void hotkey_poll_setup(bool may_warn) static void hotkey_poll_setup(bool may_warn)
{ {
u32 hotkeys_to_poll = hotkey_source_mask & hotkey_mask; const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask;
const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask;
if (hotkeys_to_poll != 0 && hotkey_poll_freq > 0 && if (hotkey_poll_freq > 0 &&
(tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { (poll_driver_mask ||
(poll_user_mask && tpacpi_inputdev->users > 0))) {
if (!tpacpi_hotkey_task) { if (!tpacpi_hotkey_task) {
tpacpi_hotkey_task = kthread_run(hotkey_kthread, tpacpi_hotkey_task = kthread_run(hotkey_kthread,
NULL, TPACPI_NVRAM_KTHREAD_NAME); NULL, TPACPI_NVRAM_KTHREAD_NAME);
...@@ -2380,12 +2544,13 @@ static void hotkey_poll_setup(bool may_warn) ...@@ -2380,12 +2544,13 @@ static void hotkey_poll_setup(bool may_warn)
} }
} else { } else {
hotkey_poll_stop_sync(); hotkey_poll_stop_sync();
if (may_warn && hotkeys_to_poll != 0 && if (may_warn && (poll_driver_mask || poll_user_mask) &&
hotkey_poll_freq == 0) { hotkey_poll_freq == 0) {
printk(TPACPI_NOTICE printk(TPACPI_NOTICE
"hot keys 0x%08x require polling, " "hot keys 0x%08x and/or events 0x%08x "
"which is currently disabled\n", "require polling, which is currently "
hotkeys_to_poll); "disabled\n",
poll_user_mask, poll_driver_mask);
} }
} }
} }
...@@ -2403,9 +2568,7 @@ static void hotkey_poll_set_freq(unsigned int freq) ...@@ -2403,9 +2568,7 @@ static void hotkey_poll_set_freq(unsigned int freq)
if (!freq) if (!freq)
hotkey_poll_stop_sync(); hotkey_poll_stop_sync();
HOTKEY_CONFIG_CRITICAL_START
hotkey_poll_freq = freq; hotkey_poll_freq = freq;
HOTKEY_CONFIG_CRITICAL_END
} }
#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */ #else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
...@@ -2440,7 +2603,8 @@ static int hotkey_inputdev_open(struct input_dev *dev) ...@@ -2440,7 +2603,8 @@ static int hotkey_inputdev_open(struct input_dev *dev)
static void hotkey_inputdev_close(struct input_dev *dev) static void hotkey_inputdev_close(struct input_dev *dev)
{ {
/* disable hotkey polling when possible */ /* disable hotkey polling when possible */
if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING) if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING &&
!(hotkey_source_mask & hotkey_driver_mask))
hotkey_poll_setup_safe(false); hotkey_poll_setup_safe(false);
} }
...@@ -2488,15 +2652,7 @@ static ssize_t hotkey_mask_show(struct device *dev, ...@@ -2488,15 +2652,7 @@ static ssize_t hotkey_mask_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
int res; return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_user_mask);
if (mutex_lock_killable(&hotkey_mutex))
return -ERESTARTSYS;
res = hotkey_mask_get();
mutex_unlock(&hotkey_mutex);
return (res)?
res : snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_mask);
} }
static ssize_t hotkey_mask_store(struct device *dev, static ssize_t hotkey_mask_store(struct device *dev,
...@@ -2512,7 +2668,7 @@ static ssize_t hotkey_mask_store(struct device *dev, ...@@ -2512,7 +2668,7 @@ static ssize_t hotkey_mask_store(struct device *dev,
if (mutex_lock_killable(&hotkey_mutex)) if (mutex_lock_killable(&hotkey_mutex))
return -ERESTARTSYS; return -ERESTARTSYS;
res = hotkey_mask_set(t); res = hotkey_user_mask_set(t);
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
hotkey_poll_setup(true); hotkey_poll_setup(true);
...@@ -2594,6 +2750,8 @@ static ssize_t hotkey_source_mask_store(struct device *dev, ...@@ -2594,6 +2750,8 @@ static ssize_t hotkey_source_mask_store(struct device *dev,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned long t; unsigned long t;
u32 r_ev;
int rc;
if (parse_strtoul(buf, 0xffffffffUL, &t) || if (parse_strtoul(buf, 0xffffffffUL, &t) ||
((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0))
...@@ -2606,14 +2764,28 @@ static ssize_t hotkey_source_mask_store(struct device *dev, ...@@ -2606,14 +2764,28 @@ static ssize_t hotkey_source_mask_store(struct device *dev,
hotkey_source_mask = t; hotkey_source_mask = t;
HOTKEY_CONFIG_CRITICAL_END HOTKEY_CONFIG_CRITICAL_END
rc = hotkey_mask_set((hotkey_user_mask | hotkey_driver_mask) &
~hotkey_source_mask);
hotkey_poll_setup(true); hotkey_poll_setup(true);
hotkey_mask_set(hotkey_mask);
/* check if events needed by the driver got disabled */
r_ev = hotkey_driver_mask & ~(hotkey_acpi_mask & hotkey_all_mask)
& ~hotkey_source_mask & TPACPI_HKEY_NVRAM_KNOWN_MASK;
mutex_unlock(&hotkey_mutex); mutex_unlock(&hotkey_mutex);
if (rc < 0)
printk(TPACPI_ERR "hotkey_source_mask: failed to update the"
"firmware event mask!\n");
if (r_ev)
printk(TPACPI_NOTICE "hotkey_source_mask: "
"some important events were disabled: "
"0x%04x\n", r_ev);
tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t);
return count; return (rc < 0) ? rc : count;
} }
static struct device_attribute dev_attr_hotkey_source_mask = static struct device_attribute dev_attr_hotkey_source_mask =
...@@ -2731,9 +2903,8 @@ static struct device_attribute dev_attr_hotkey_wakeup_reason = ...@@ -2731,9 +2903,8 @@ static struct device_attribute dev_attr_hotkey_wakeup_reason =
static void hotkey_wakeup_reason_notify_change(void) static void hotkey_wakeup_reason_notify_change(void)
{ {
if (tp_features.hotkey_mask) sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "wakeup_reason");
"wakeup_reason");
} }
/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */ /* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
...@@ -2750,9 +2921,8 @@ static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete = ...@@ -2750,9 +2921,8 @@ static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete =
static void hotkey_wakeup_hotunplug_complete_notify_change(void) static void hotkey_wakeup_hotunplug_complete_notify_change(void)
{ {
if (tp_features.hotkey_mask) sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "wakeup_hotunplug_complete");
"wakeup_hotunplug_complete");
} }
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
...@@ -2760,27 +2930,19 @@ static void hotkey_wakeup_hotunplug_complete_notify_change(void) ...@@ -2760,27 +2930,19 @@ static void hotkey_wakeup_hotunplug_complete_notify_change(void)
static struct attribute *hotkey_attributes[] __initdata = { static struct attribute *hotkey_attributes[] __initdata = {
&dev_attr_hotkey_enable.attr, &dev_attr_hotkey_enable.attr,
&dev_attr_hotkey_bios_enabled.attr, &dev_attr_hotkey_bios_enabled.attr,
&dev_attr_hotkey_bios_mask.attr,
&dev_attr_hotkey_report_mode.attr, &dev_attr_hotkey_report_mode.attr,
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL &dev_attr_hotkey_wakeup_reason.attr,
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
&dev_attr_hotkey_mask.attr, &dev_attr_hotkey_mask.attr,
&dev_attr_hotkey_all_mask.attr, &dev_attr_hotkey_all_mask.attr,
&dev_attr_hotkey_recommended_mask.attr, &dev_attr_hotkey_recommended_mask.attr,
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
&dev_attr_hotkey_source_mask.attr, &dev_attr_hotkey_source_mask.attr,
&dev_attr_hotkey_poll_freq.attr, &dev_attr_hotkey_poll_freq.attr,
#endif #endif
}; };
static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_bios_mask.attr,
#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
&dev_attr_hotkey_mask.attr,
&dev_attr_hotkey_all_mask.attr,
&dev_attr_hotkey_recommended_mask.attr,
#endif
&dev_attr_hotkey_wakeup_reason.attr,
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
};
/* /*
* Sync both the hw and sw blocking state of all switches * Sync both the hw and sw blocking state of all switches
*/ */
...@@ -2843,16 +3005,16 @@ static void hotkey_exit(void) ...@@ -2843,16 +3005,16 @@ static void hotkey_exit(void)
kfree(hotkey_keycode_map); kfree(hotkey_keycode_map);
if (tp_features.hotkey) { dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original HKEY status and mask\n");
"restoring original hot key mask\n"); /* yes, there is a bitwise or below, we want the
/* no short-circuit boolean operator below! */ * functions to be called even if one of them fail */
if ((hotkey_mask_set(hotkey_orig_mask) | if (((tp_features.hotkey_mask &&
hotkey_status_set(false)) != 0) hotkey_mask_set(hotkey_orig_mask)) |
printk(TPACPI_ERR hotkey_status_set(false)) != 0)
"failed to restore hot key mask " printk(TPACPI_ERR
"to BIOS defaults\n"); "failed to restore hot key mask "
} "to BIOS defaults\n");
} }
static void __init hotkey_unmap(const unsigned int scancode) static void __init hotkey_unmap(const unsigned int scancode)
...@@ -2864,6 +3026,35 @@ static void __init hotkey_unmap(const unsigned int scancode) ...@@ -2864,6 +3026,35 @@ static void __init hotkey_unmap(const unsigned int scancode)
} }
} }
/*
* HKEY quirks:
* TPACPI_HK_Q_INIMASK: Supports FN+F3,FN+F4,FN+F12
*/
#define TPACPI_HK_Q_INIMASK 0x0001
static const struct tpacpi_quirk tpacpi_hotkey_qtable[] __initconst = {
TPACPI_Q_IBM('I', 'H', TPACPI_HK_Q_INIMASK), /* 600E */
TPACPI_Q_IBM('I', 'N', TPACPI_HK_Q_INIMASK), /* 600E */
TPACPI_Q_IBM('I', 'D', TPACPI_HK_Q_INIMASK), /* 770, 770E, 770ED */
TPACPI_Q_IBM('I', 'W', TPACPI_HK_Q_INIMASK), /* A20m */
TPACPI_Q_IBM('I', 'V', TPACPI_HK_Q_INIMASK), /* A20p */
TPACPI_Q_IBM('1', '0', TPACPI_HK_Q_INIMASK), /* A21e, A22e */
TPACPI_Q_IBM('K', 'U', TPACPI_HK_Q_INIMASK), /* A21e */
TPACPI_Q_IBM('K', 'X', TPACPI_HK_Q_INIMASK), /* A21m, A22m */
TPACPI_Q_IBM('K', 'Y', TPACPI_HK_Q_INIMASK), /* A21p, A22p */
TPACPI_Q_IBM('1', 'B', TPACPI_HK_Q_INIMASK), /* A22e */
TPACPI_Q_IBM('1', '3', TPACPI_HK_Q_INIMASK), /* A22m */
TPACPI_Q_IBM('1', 'E', TPACPI_HK_Q_INIMASK), /* A30/p (0) */
TPACPI_Q_IBM('1', 'C', TPACPI_HK_Q_INIMASK), /* R30 */
TPACPI_Q_IBM('1', 'F', TPACPI_HK_Q_INIMASK), /* R31 */
TPACPI_Q_IBM('I', 'Y', TPACPI_HK_Q_INIMASK), /* T20 */
TPACPI_Q_IBM('K', 'Z', TPACPI_HK_Q_INIMASK), /* T21 */
TPACPI_Q_IBM('1', '6', TPACPI_HK_Q_INIMASK), /* T22 */
TPACPI_Q_IBM('I', 'Z', TPACPI_HK_Q_INIMASK), /* X20, X21 */
TPACPI_Q_IBM('1', 'D', TPACPI_HK_Q_INIMASK), /* X22, X23, X24 */
};
static int __init hotkey_init(struct ibm_init_struct *iibm) static int __init hotkey_init(struct ibm_init_struct *iibm)
{ {
/* Requirements for changing the default keymaps: /* Requirements for changing the default keymaps:
...@@ -2906,9 +3097,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -2906,9 +3097,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_UNKNOWN, /* 0x0D: FN+INSERT */ KEY_UNKNOWN, /* 0x0D: FN+INSERT */
KEY_UNKNOWN, /* 0x0E: FN+DELETE */ KEY_UNKNOWN, /* 0x0E: FN+DELETE */
/* brightness: firmware always reacts to them, unless /* brightness: firmware always reacts to them */
* X.org did some tricks in the radeon BIOS scratch
* registers of *some* models */
KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
KEY_RESERVED, /* 0x10: FN+END (brightness down) */ KEY_RESERVED, /* 0x10: FN+END (brightness down) */
...@@ -2983,6 +3172,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -2983,6 +3172,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
int status; int status;
int hkeyv; int hkeyv;
unsigned long quirks;
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"initializing hotkey subdriver\n"); "initializing hotkey subdriver\n");
...@@ -3008,9 +3199,16 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3008,9 +3199,16 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if (!tp_features.hotkey) if (!tp_features.hotkey)
return 1; return 1;
quirks = tpacpi_check_quirks(tpacpi_hotkey_qtable,
ARRAY_SIZE(tpacpi_hotkey_qtable));
tpacpi_disable_brightness_delay(); tpacpi_disable_brightness_delay();
hotkey_dev_attributes = create_attr_set(13, NULL); /* MUST have enough space for all attributes to be added to
* hotkey_dev_attributes */
hotkey_dev_attributes = create_attr_set(
ARRAY_SIZE(hotkey_attributes) + 2,
NULL);
if (!hotkey_dev_attributes) if (!hotkey_dev_attributes)
return -ENOMEM; return -ENOMEM;
res = add_many_to_attr_set(hotkey_dev_attributes, res = add_many_to_attr_set(hotkey_dev_attributes,
...@@ -3019,7 +3217,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3019,7 +3217,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if (res) if (res)
goto err_exit; goto err_exit;
/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, /* mask not supported on 600e/x, 770e, 770x, A21e, A2xm/p,
A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
for HKEY interface version 0x100 */ for HKEY interface version 0x100 */
if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
...@@ -3033,10 +3231,22 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3033,10 +3231,22 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
* MHKV 0x100 in A31, R40, R40e, * MHKV 0x100 in A31, R40, R40e,
* T4x, X31, and later * T4x, X31, and later
*/ */
tp_features.hotkey_mask = 1;
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"firmware HKEY interface version: 0x%x\n", "firmware HKEY interface version: 0x%x\n",
hkeyv); hkeyv);
/* Paranoia check AND init hotkey_all_mask */
if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
"MHKA", "qd")) {
printk(TPACPI_ERR
"missing MHKA handler, "
"please report this to %s\n",
TPACPI_MAIL);
/* Fallback: pre-init for FN+F3,F4,F12 */
hotkey_all_mask = 0x080cU;
} else {
tp_features.hotkey_mask = 1;
}
} }
} }
...@@ -3044,32 +3254,23 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3044,32 +3254,23 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
"hotkey masks are %s\n", "hotkey masks are %s\n",
str_supported(tp_features.hotkey_mask)); str_supported(tp_features.hotkey_mask));
if (tp_features.hotkey_mask) { /* Init hotkey_all_mask if not initialized yet */
if (!acpi_evalf(hkey_handle, &hotkey_all_mask, if (!tp_features.hotkey_mask && !hotkey_all_mask &&
"MHKA", "qd")) { (quirks & TPACPI_HK_Q_INIMASK))
printk(TPACPI_ERR hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */
"missing MHKA handler, "
"please report this to %s\n",
TPACPI_MAIL);
/* FN+F12, FN+F4, FN+F3 */
hotkey_all_mask = 0x080cU;
}
}
/* hotkey_source_mask *must* be zero for /* Init hotkey_acpi_mask and hotkey_orig_mask */
* the first hotkey_mask_get */
if (tp_features.hotkey_mask) { if (tp_features.hotkey_mask) {
/* hotkey_source_mask *must* be zero for
* the first hotkey_mask_get to return hotkey_orig_mask */
res = hotkey_mask_get(); res = hotkey_mask_get();
if (res) if (res)
goto err_exit; goto err_exit;
hotkey_orig_mask = hotkey_mask; hotkey_orig_mask = hotkey_acpi_mask;
res = add_many_to_attr_set( } else {
hotkey_dev_attributes, hotkey_orig_mask = hotkey_all_mask;
hotkey_mask_attributes, hotkey_acpi_mask = hotkey_all_mask;
ARRAY_SIZE(hotkey_mask_attributes));
if (res)
goto err_exit;
} }
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
...@@ -3183,14 +3384,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3183,14 +3384,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
} }
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
if (tp_features.hotkey_mask) { hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK & ~hotkey_all_mask
& ~hotkey_all_mask & ~hotkey_reserved_mask;
& ~hotkey_reserved_mask;
} else {
hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
& ~hotkey_reserved_mask;
}
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"hotkey source mask 0x%08x, polling freq %u\n", "hotkey source mask 0x%08x, polling freq %u\n",
...@@ -3204,13 +3400,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3204,13 +3400,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
hotkey_exit(); hotkey_exit();
return res; return res;
} }
res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask) res = hotkey_mask_set(((hotkey_all_mask & ~hotkey_reserved_mask)
& ~hotkey_reserved_mask) | hotkey_driver_mask)
| hotkey_orig_mask); & ~hotkey_source_mask);
if (res < 0 && res != -ENXIO) { if (res < 0 && res != -ENXIO) {
hotkey_exit(); hotkey_exit();
return res; return res;
} }
hotkey_user_mask = (hotkey_acpi_mask | hotkey_source_mask)
& ~hotkey_reserved_mask;
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"initial masks: user=0x%08x, fw=0x%08x, poll=0x%08x\n",
hotkey_user_mask, hotkey_acpi_mask, hotkey_source_mask);
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"legacy ibm/hotkey event reporting over procfs %s\n", "legacy ibm/hotkey event reporting over procfs %s\n",
...@@ -3245,7 +3446,7 @@ static bool hotkey_notify_hotkey(const u32 hkey, ...@@ -3245,7 +3446,7 @@ static bool hotkey_notify_hotkey(const u32 hkey,
if (scancode > 0 && scancode < 0x21) { if (scancode > 0 && scancode < 0x21) {
scancode--; scancode--;
if (!(hotkey_source_mask & (1 << scancode))) { if (!(hotkey_source_mask & (1 << scancode))) {
tpacpi_input_send_key(scancode); tpacpi_input_send_key_masked(scancode);
*send_acpi_ev = false; *send_acpi_ev = false;
} else { } else {
*ignore_acpi_ev = true; *ignore_acpi_ev = true;
...@@ -3264,20 +3465,20 @@ static bool hotkey_notify_wakeup(const u32 hkey, ...@@ -3264,20 +3465,20 @@ static bool hotkey_notify_wakeup(const u32 hkey,
*ignore_acpi_ev = false; *ignore_acpi_ev = false;
switch (hkey) { switch (hkey) {
case 0x2304: /* suspend, undock */ case TP_HKEY_EV_WKUP_S3_UNDOCK: /* suspend, undock */
case 0x2404: /* hibernation, undock */ case TP_HKEY_EV_WKUP_S4_UNDOCK: /* hibernation, undock */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
*ignore_acpi_ev = true; *ignore_acpi_ev = true;
break; break;
case 0x2305: /* suspend, bay eject */ case TP_HKEY_EV_WKUP_S3_BAYEJ: /* suspend, bay eject */
case 0x2405: /* hibernation, bay eject */ case TP_HKEY_EV_WKUP_S4_BAYEJ: /* hibernation, bay eject */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
*ignore_acpi_ev = true; *ignore_acpi_ev = true;
break; break;
case 0x2313: /* Battery on critical low level (S3) */ case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */
case 0x2413: /* Battery on critical low level (S4) */ case TP_HKEY_EV_WKUP_S4_BATLOW: /* Battery on critical low level/S4 */
printk(TPACPI_ALERT printk(TPACPI_ALERT
"EMERGENCY WAKEUP: battery almost empty\n"); "EMERGENCY WAKEUP: battery almost empty\n");
/* how to auto-heal: */ /* how to auto-heal: */
...@@ -3307,21 +3508,21 @@ static bool hotkey_notify_usrevent(const u32 hkey, ...@@ -3307,21 +3508,21 @@ static bool hotkey_notify_usrevent(const u32 hkey,
*ignore_acpi_ev = false; *ignore_acpi_ev = false;
switch (hkey) { switch (hkey) {
case 0x5010: /* Lenovo new BIOS: brightness changed */ case TP_HKEY_EV_PEN_INSERTED: /* X61t: tablet pen inserted into bay */
case 0x500b: /* X61t: tablet pen inserted into bay */ case TP_HKEY_EV_PEN_REMOVED: /* X61t: tablet pen removed from bay */
case 0x500c: /* X61t: tablet pen removed from bay */
return true; return true;
case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ case TP_HKEY_EV_TABLET_TABLET: /* X41t-X61t: tablet mode */
case 0x500a: /* X41t-X61t: swivel down (normal mode) */ case TP_HKEY_EV_TABLET_NOTEBOOK: /* X41t-X61t: normal mode */
tpacpi_input_send_tabletsw(); tpacpi_input_send_tabletsw();
hotkey_tablet_mode_notify_change(); hotkey_tablet_mode_notify_change();
*send_acpi_ev = false; *send_acpi_ev = false;
return true; return true;
case 0x5001: case TP_HKEY_EV_LID_CLOSE: /* Lid closed */
case 0x5002: case TP_HKEY_EV_LID_OPEN: /* Lid opened */
/* LID switch events. Do not propagate */ case TP_HKEY_EV_BRGHT_CHANGED: /* brightness changed */
/* do not propagate these events */
*ignore_acpi_ev = true; *ignore_acpi_ev = true;
return true; return true;
...@@ -3339,30 +3540,30 @@ static bool hotkey_notify_thermal(const u32 hkey, ...@@ -3339,30 +3540,30 @@ static bool hotkey_notify_thermal(const u32 hkey,
*ignore_acpi_ev = false; *ignore_acpi_ev = false;
switch (hkey) { switch (hkey) {
case 0x6011: case TP_HKEY_EV_ALARM_BAT_HOT:
printk(TPACPI_CRIT printk(TPACPI_CRIT
"THERMAL ALARM: battery is too hot!\n"); "THERMAL ALARM: battery is too hot!\n");
/* recommended action: warn user through gui */ /* recommended action: warn user through gui */
return true; return true;
case 0x6012: case TP_HKEY_EV_ALARM_BAT_XHOT:
printk(TPACPI_ALERT printk(TPACPI_ALERT
"THERMAL EMERGENCY: battery is extremely hot!\n"); "THERMAL EMERGENCY: battery is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */ /* recommended action: immediate sleep/hibernate */
return true; return true;
case 0x6021: case TP_HKEY_EV_ALARM_SENSOR_HOT:
printk(TPACPI_CRIT printk(TPACPI_CRIT
"THERMAL ALARM: " "THERMAL ALARM: "
"a sensor reports something is too hot!\n"); "a sensor reports something is too hot!\n");
/* recommended action: warn user through gui, that */ /* recommended action: warn user through gui, that */
/* some internal component is too hot */ /* some internal component is too hot */
return true; return true;
case 0x6022: case TP_HKEY_EV_ALARM_SENSOR_XHOT:
printk(TPACPI_ALERT printk(TPACPI_ALERT
"THERMAL EMERGENCY: " "THERMAL EMERGENCY: "
"a sensor reports something is extremely hot!\n"); "a sensor reports something is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */ /* recommended action: immediate sleep/hibernate */
return true; return true;
case 0x6030: case TP_HKEY_EV_THM_TABLE_CHANGED:
printk(TPACPI_INFO printk(TPACPI_INFO
"EC reports that Thermal Table has changed\n"); "EC reports that Thermal Table has changed\n");
/* recommended action: do nothing, we don't have /* recommended action: do nothing, we don't have
...@@ -3420,7 +3621,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) ...@@ -3420,7 +3621,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
break; break;
case 3: case 3:
/* 0x3000-0x3FFF: bay-related wakeups */ /* 0x3000-0x3FFF: bay-related wakeups */
if (hkey == 0x3003) { if (hkey == TP_HKEY_EV_BAYEJ_ACK) {
hotkey_autosleep_ack = 1; hotkey_autosleep_ack = 1;
printk(TPACPI_INFO printk(TPACPI_INFO
"bay ejected\n"); "bay ejected\n");
...@@ -3432,7 +3633,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) ...@@ -3432,7 +3633,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
break; break;
case 4: case 4:
/* 0x4000-0x4FFF: dock-related wakeups */ /* 0x4000-0x4FFF: dock-related wakeups */
if (hkey == 0x4003) { if (hkey == TP_HKEY_EV_UNDOCK_ACK) {
hotkey_autosleep_ack = 1; hotkey_autosleep_ack = 1;
printk(TPACPI_INFO printk(TPACPI_INFO
"undocked\n"); "undocked\n");
...@@ -3454,7 +3655,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) ...@@ -3454,7 +3655,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
break; break;
case 7: case 7:
/* 0x7000-0x7FFF: misc */ /* 0x7000-0x7FFF: misc */
if (tp_features.hotkey_wlsw && hkey == 0x7000) { if (tp_features.hotkey_wlsw &&
hkey == TP_HKEY_EV_RFKILL_CHANGED) {
tpacpi_send_radiosw_update(); tpacpi_send_radiosw_update();
send_acpi_ev = 0; send_acpi_ev = 0;
known_ev = true; known_ev = true;
...@@ -3500,10 +3702,12 @@ static void hotkey_resume(void) ...@@ -3500,10 +3702,12 @@ static void hotkey_resume(void)
{ {
tpacpi_disable_brightness_delay(); tpacpi_disable_brightness_delay();
if (hotkey_mask_get()) if (hotkey_status_set(true) < 0 ||
hotkey_mask_set(hotkey_acpi_mask) < 0)
printk(TPACPI_ERR printk(TPACPI_ERR
"error while trying to read hot key mask " "error while attempting to reset the event "
"from firmware\n"); "firmware interface\n");
tpacpi_send_radiosw_update(); tpacpi_send_radiosw_update();
hotkey_tablet_mode_notify_change(); hotkey_tablet_mode_notify_change();
hotkey_wakeup_reason_notify_change(); hotkey_wakeup_reason_notify_change();
...@@ -3532,8 +3736,8 @@ static int hotkey_read(char *p) ...@@ -3532,8 +3736,8 @@ static int hotkey_read(char *p)
return res; return res;
len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
if (tp_features.hotkey_mask) { if (hotkey_all_mask) {
len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_mask); len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask);
len += sprintf(p + len, len += sprintf(p + len,
"commands:\tenable, disable, reset, <mask>\n"); "commands:\tenable, disable, reset, <mask>\n");
} else { } else {
...@@ -3570,7 +3774,7 @@ static int hotkey_write(char *buf) ...@@ -3570,7 +3774,7 @@ static int hotkey_write(char *buf)
if (mutex_lock_killable(&hotkey_mutex)) if (mutex_lock_killable(&hotkey_mutex))
return -ERESTARTSYS; return -ERESTARTSYS;
mask = hotkey_mask; mask = hotkey_user_mask;
res = 0; res = 0;
while ((cmd = next_cmd(&buf))) { while ((cmd = next_cmd(&buf))) {
...@@ -3592,12 +3796,11 @@ static int hotkey_write(char *buf) ...@@ -3592,12 +3796,11 @@ static int hotkey_write(char *buf)
} }
} }
if (!res) if (!res) {
tpacpi_disclose_usertask("procfs hotkey", tpacpi_disclose_usertask("procfs hotkey",
"set mask to 0x%08x\n", mask); "set mask to 0x%08x\n", mask);
res = hotkey_user_mask_set(mask);
if (!res && mask != hotkey_mask) }
res = hotkey_mask_set(mask);
errexit: errexit:
mutex_unlock(&hotkey_mutex); mutex_unlock(&hotkey_mutex);
...@@ -6010,8 +6213,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ...@@ -6010,8 +6213,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
&ibm_backlight_data); &ibm_backlight_data);
if (IS_ERR(ibm_backlight_device)) { if (IS_ERR(ibm_backlight_device)) {
int rc = PTR_ERR(ibm_backlight_device);
ibm_backlight_device = NULL;
printk(TPACPI_ERR "Could not register backlight device\n"); printk(TPACPI_ERR "Could not register backlight device\n");
return PTR_ERR(ibm_backlight_device); return rc;
} }
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
"brightness is supported\n"); "brightness is supported\n");
...@@ -7499,6 +7704,21 @@ static struct ibm_struct fan_driver_data = { ...@@ -7499,6 +7704,21 @@ static struct ibm_struct fan_driver_data = {
**************************************************************************** ****************************************************************************
****************************************************************************/ ****************************************************************************/
/*
* HKEY event callout for other subdrivers go here
* (yes, it is ugly, but it is quick, safe, and gets the job done
*/
static void tpacpi_driver_event(const unsigned int hkey_event)
{
}
static void hotkey_driver_event(const unsigned int scancode)
{
tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode);
}
/* sysfs name ---------------------------------------------------------- */ /* sysfs name ---------------------------------------------------------- */
static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev, static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
......
...@@ -153,6 +153,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device) ...@@ -153,6 +153,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
acpi_handle temp = NULL; acpi_handle temp = NULL;
acpi_status status; acpi_status status;
struct pnp_dev *dev; struct pnp_dev *dev;
struct acpi_hardware_id *id;
/* /*
* If a PnPacpi device is not present , the device * If a PnPacpi device is not present , the device
...@@ -193,15 +194,12 @@ static int __init pnpacpi_add_device(struct acpi_device *device) ...@@ -193,15 +194,12 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
if (dev->capabilities & PNP_CONFIGURABLE) if (dev->capabilities & PNP_CONFIGURABLE)
pnpacpi_parse_resource_option_data(dev); pnpacpi_parse_resource_option_data(dev);
if (device->flags.compatible_ids) { list_for_each_entry(id, &device->pnp.ids, list) {
struct acpica_device_id_list *cid_list = device->pnp.cid_list; if (!strcmp(id->id, acpi_device_hid(device)))
int i; continue;
if (!ispnpidacpi(id->id))
for (i = 0; i < cid_list->count; i++) { continue;
if (!ispnpidacpi(cid_list->ids[i].string)) pnp_add_id(dev, id->id);
continue;
pnp_add_id(dev, cid_list->ids[i].string);
}
} }
/* clear out the damaged flags */ /* clear out the damaged flags */
...@@ -232,9 +230,8 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp) ...@@ -232,9 +230,8 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp)
struct pnp_dev *pnp = _pnp; struct pnp_dev *pnp = _pnp;
/* true means it matched */ /* true means it matched */
return acpi->flags.hardware_id return !acpi_get_physical_device(acpi->handle)
&& !acpi_get_physical_device(acpi->handle) && compare_pnp_id(pnp->id, acpi_device_hid(acpi));
&& compare_pnp_id(pnp->id, acpi->pnp.hardware_id);
} }
static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle) static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle)
......
...@@ -70,7 +70,6 @@ enum acpi_bus_device_type { ...@@ -70,7 +70,6 @@ enum acpi_bus_device_type {
ACPI_BUS_TYPE_POWER, ACPI_BUS_TYPE_POWER,
ACPI_BUS_TYPE_PROCESSOR, ACPI_BUS_TYPE_PROCESSOR,
ACPI_BUS_TYPE_THERMAL, ACPI_BUS_TYPE_THERMAL,
ACPI_BUS_TYPE_SYSTEM,
ACPI_BUS_TYPE_POWER_BUTTON, ACPI_BUS_TYPE_POWER_BUTTON,
ACPI_BUS_TYPE_SLEEP_BUTTON, ACPI_BUS_TYPE_SLEEP_BUTTON,
ACPI_BUS_DEVICE_TYPE_COUNT ACPI_BUS_DEVICE_TYPE_COUNT
...@@ -142,10 +141,7 @@ struct acpi_device_status { ...@@ -142,10 +141,7 @@ struct acpi_device_status {
struct acpi_device_flags { struct acpi_device_flags {
u32 dynamic_status:1; u32 dynamic_status:1;
u32 hardware_id:1;
u32 compatible_ids:1;
u32 bus_address:1; u32 bus_address:1;
u32 unique_id:1;
u32 removable:1; u32 removable:1;
u32 ejectable:1; u32 ejectable:1;
u32 lockable:1; u32 lockable:1;
...@@ -154,7 +150,7 @@ struct acpi_device_flags { ...@@ -154,7 +150,7 @@ struct acpi_device_flags {
u32 performance_manageable:1; u32 performance_manageable:1;
u32 wake_capable:1; /* Wakeup(_PRW) supported? */ u32 wake_capable:1; /* Wakeup(_PRW) supported? */
u32 force_power_state:1; u32 force_power_state:1;
u32 reserved:19; u32 reserved:22;
}; };
/* File System */ /* File System */
...@@ -172,20 +168,23 @@ typedef unsigned long acpi_bus_address; ...@@ -172,20 +168,23 @@ typedef unsigned long acpi_bus_address;
typedef char acpi_device_name[40]; typedef char acpi_device_name[40];
typedef char acpi_device_class[20]; typedef char acpi_device_class[20];
struct acpi_hardware_id {
struct list_head list;
char *id;
};
struct acpi_device_pnp { struct acpi_device_pnp {
acpi_bus_id bus_id; /* Object name */ acpi_bus_id bus_id; /* Object name */
acpi_bus_address bus_address; /* _ADR */ acpi_bus_address bus_address; /* _ADR */
char *hardware_id; /* _HID */
struct acpica_device_id_list *cid_list; /* _CIDs */
char *unique_id; /* _UID */ char *unique_id; /* _UID */
struct list_head ids; /* _HID and _CIDs */
acpi_device_name device_name; /* Driver-determined */ acpi_device_name device_name; /* Driver-determined */
acpi_device_class device_class; /* " */ acpi_device_class device_class; /* " */
}; };
#define acpi_device_bid(d) ((d)->pnp.bus_id) #define acpi_device_bid(d) ((d)->pnp.bus_id)
#define acpi_device_adr(d) ((d)->pnp.bus_address) #define acpi_device_adr(d) ((d)->pnp.bus_address)
#define acpi_device_hid(d) ((d)->pnp.hardware_id) char *acpi_device_hid(struct acpi_device *device);
#define acpi_device_uid(d) ((d)->pnp.unique_id)
#define acpi_device_name(d) ((d)->pnp.device_name) #define acpi_device_name(d) ((d)->pnp.device_name)
#define acpi_device_class(d) ((d)->pnp.device_class) #define acpi_device_class(d) ((d)->pnp.device_class)
...@@ -262,7 +261,8 @@ struct acpi_device_wakeup { ...@@ -262,7 +261,8 @@ struct acpi_device_wakeup {
/* Device */ /* Device */
struct acpi_device { struct acpi_device {
acpi_handle handle; int device_type;
acpi_handle handle; /* no handle for fixed hardware */
struct acpi_device *parent; struct acpi_device *parent;
struct list_head children; struct list_head children;
struct list_head node; struct list_head node;
...@@ -322,6 +322,8 @@ extern void unregister_acpi_bus_notifier(struct notifier_block *nb); ...@@ -322,6 +322,8 @@ extern void unregister_acpi_bus_notifier(struct notifier_block *nb);
int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device); int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device);
void acpi_bus_data_handler(acpi_handle handle, void *context); void acpi_bus_data_handler(acpi_handle handle, void *context);
acpi_status acpi_bus_get_status_handle(acpi_handle handle,
unsigned long long *sta);
int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_get_status(struct acpi_device *device);
int acpi_bus_get_power(acpi_handle handle, int *state); int acpi_bus_get_power(acpi_handle handle, int *state);
int acpi_bus_set_power(acpi_handle handle, int state); int acpi_bus_set_power(acpi_handle handle, int state);
......
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