// SPDX-License-Identifier: GPL-2.0-or-later /* * eeepc-laptop.c - Asus Eee PC extras * * Based on asus_acpi.c as patched for the Eee PC by Asus: * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar * Based on eee.c from eeepc-linux
*/
MODULE_AUTHOR("Corentin Chary, Eric Cooper");
MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
MODULE_LICENSE("GPL");
staticbool hotplug_disabled;
module_param(hotplug_disabled, bool, 0444);
MODULE_PARM_DESC(hotplug_disabled, "Disable hotplug for wireless device. " "If your laptop need that, please report to " "acpi4asus-user@lists.sourceforge.net.");
/* * Definitions for Asus EeePC
*/ #define NOTIFY_BRN_MIN 0x20 #define NOTIFY_BRN_MAX 0x2f
/* * This is the main structure, we can use it to store useful information
*/ struct eeepc_laptop {
acpi_handle handle; /* the handle of the acpi device */
u32 cm_supported; /* the control methods supported
by this BIOS */ bool cpufv_disabled; bool hotplug_disabled;
u16 event_count[128]; /* count for each event */
struct platform_device *platform_device; struct acpi_device *device; /* the device we are in */ struct backlight_device *backlight_device;
static ssize_t store_sys_acpi(struct device *dev, int cm, constchar *buf, size_t count)
{ struct eeepc_laptop *eeepc = dev_get_drvdata(dev); int rv, value;
rv = parse_arg(buf, &value); if (rv < 0) return rv;
rv = set_acpi(eeepc, cm, value); if (rv < 0) return -EIO; return count;
}
static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
{ struct eeepc_laptop *eeepc = dev_get_drvdata(dev); int value = get_acpi(eeepc, cm);
if (get_cpufv(eeepc, &c)) return -ENODEV; for (i = 0; i < c.num; i++)
len += sprintf(buf + len, "%d ", i);
len += sprintf(buf + len, "\n"); return len;
}
/* * LEDs
*/ /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED * subsystem asks, we avoid messing with the Asus ACPI stuff during a * potentially bad time, such as a timer interrupt.
*/ staticvoid tpd_led_update(struct work_struct *work)
{ struct eeepc_laptop *eeepc;
port = acpi_get_pci_dev(handle); if (!port) {
pr_warn("Unable to find port\n"); goto out_unlock;
}
bus = port->subordinate;
if (!bus) {
pr_warn("Unable to find PCI bus 1?\n"); goto out_put_dev;
}
if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
pr_err("Unable to read PCI config space?\n"); goto out_put_dev;
}
absent = (l == 0xffffffff);
if (blocked != absent) {
pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
blocked ? "blocked" : "unblocked",
absent ? "absent" : "present");
pr_warn("skipped wireless hotplug as probably inappropriate for this model\n"); goto out_put_dev;
}
if (!blocked) {
dev = pci_get_slot(bus, 0); if (dev) { /* Device already present */
pci_dev_put(dev); goto out_put_dev;
}
dev = pci_scan_single_device(bus, 0); if (dev) {
pci_bus_assign_resources(bus);
pci_bus_add_device(dev);
}
} else {
dev = pci_get_slot(bus, 0); if (dev) {
pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
out_put_dev:
pci_dev_put(port);
status = acpi_install_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
eeepc_rfkill_notify,
eeepc); if (ACPI_FAILURE(status))
pr_warn("Failed to register notify on %s\n", node);
/* * Refresh pci hotplug in case the rfkill state was * changed during setup.
*/
eeepc_rfkill_hotplug(eeepc, handle); return 0;
}
status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
eeepc_rfkill_notify); if (ACPI_FAILURE(status))
pr_err("Error removing rfkill notify handler %s\n",
node); /* * Refresh pci hotplug in case the rfkill * state was changed after * eeepc_unregister_rfkill_notifier()
*/
eeepc_rfkill_hotplug(eeepc, handle);
}
if (eeepc->hotplug_slot.ops)
pci_hp_deregister(&eeepc->hotplug_slot);
if (eeepc->bluetooth_rfkill) {
rfkill_unregister(eeepc->bluetooth_rfkill);
rfkill_destroy(eeepc->bluetooth_rfkill);
eeepc->bluetooth_rfkill = NULL;
} if (eeepc->wwan3g_rfkill) {
rfkill_unregister(eeepc->wwan3g_rfkill);
rfkill_destroy(eeepc->wwan3g_rfkill);
eeepc->wwan3g_rfkill = NULL;
} if (eeepc->wimax_rfkill) {
rfkill_unregister(eeepc->wimax_rfkill);
rfkill_destroy(eeepc->wimax_rfkill);
eeepc->wimax_rfkill = NULL;
}
}
staticint eeepc_rfkill_init(struct eeepc_laptop *eeepc)
{ int result = 0;
mutex_init(&eeepc->hotplug_lock);
result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, "eeepc-wlan", RFKILL_TYPE_WLAN,
CM_ASL_WLAN);
if (result && result != -ENODEV) gotoexit;
result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
CM_ASL_BLUETOOTH);
if (result && result != -ENODEV) gotoexit;
result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, "eeepc-wwan3g", RFKILL_TYPE_WWAN,
CM_ASL_3G);
if (result && result != -ENODEV) gotoexit;
result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, "eeepc-wimax", RFKILL_TYPE_WIMAX,
CM_ASL_WIMAX);
if (result && result != -ENODEV) gotoexit;
if (eeepc->hotplug_disabled) return 0;
result = eeepc_setup_pci_hotplug(eeepc); /* * If we get -EBUSY then something else is handling the PCI hotplug - * don't fail in this case
*/ if (result == -EBUSY)
result = 0;
/* * Work around bios bug - acpi _PTS turns off the wireless led * during suspend. Normally it restores it on resume, but * we should kick it ourselves in case hibernation is aborted.
*/
wlan = get_acpi(eeepc, CM_ASL_WLAN); if (wlan >= 0)
set_acpi(eeepc, CM_ASL_WLAN, wlan);
}
/* Refresh both wlan rfkill state and pci hotplug */ if (eeepc->wlan_rfkill) {
eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1);
eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2);
eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3);
}
if (eeepc->bluetooth_rfkill)
rfkill_set_sw_state(eeepc->bluetooth_rfkill,
get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1); if (eeepc->wwan3g_rfkill)
rfkill_set_sw_state(eeepc->wwan3g_rfkill,
get_acpi(eeepc, CM_ASL_3G) != 1); if (eeepc->wimax_rfkill)
rfkill_set_sw_state(eeepc->wimax_rfkill,
get_acpi(eeepc, CM_ASL_WIMAX) != 1);
model = dmi_get_system_info(DMI_PRODUCT_NAME); if (!model) return;
/* * Blacklist for setting cpufv (cpu speed). * * EeePC 4G ("701") implements CFVS, but it is not supported * by the pre-installed OS, and the original option to change it * in the BIOS setup screen was removed in later versions. * * Judging by the lack of "Super Hybrid Engine" on Asus product pages, * this applies to all "701" models (4G/4G Surf/2G Surf). * * So Asus made a deliberate decision not to support it on this model. * We have several reports that using it can cause the system to hang * * The hang has also been reported on a "702" (Model name "8G"?). * * We avoid dmi_check_system() / dmi_match(), because they use * substring matching. We don't want to affect the "701SD" * and "701SDX" models, because they do support S.H.E.
*/ if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {
eeepc->cpufv_disabled = true;
pr_info("model %s does not officially support setting cpu speed\n",
model);
pr_info("cpufv disabled to avoid instability\n");
}
/* * Blacklist for wlan hotplug * * Eeepc 1005HA doesn't work like others models and don't need the * hotplug code. In fact, current hotplug code seems to unplug another * device...
*/ if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||
strcmp(model, "1005PE") == 0) {
eeepc->hotplug_disabled = true;
pr_info("wlan hotplug disabled\n");
}
}
staticvoid cmsg_quirk(struct eeepc_laptop *eeepc, int cm, constchar *name)
{ int dummy;
/* Some BIOSes do not report cm although it is available.
Check if cm_getv[cm] works and, if yes, assume cm should be set. */ if (!(eeepc->cm_supported & (1 << cm))
&& !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
pr_info("%s (%x) not reported by BIOS, enabling anyway\n",
name, 1 << cm);
eeepc->cm_supported |= 1 << cm;
}
}
staticint eeepc_acpi_init(struct eeepc_laptop *eeepc)
{ unsignedint init_flags; int result;
result = acpi_bus_get_status(eeepc->device); if (result) return result; if (!eeepc->device->status.present) {
pr_err("Hotkey device not present, aborting\n"); return -ENODEV;
}
/* get control methods supported */ if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
pr_err("Get control methods supported failed\n"); return -ENODEV;
}
cmsg_quirks(eeepc);
pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
return 0;
}
staticvoid eeepc_enable_camera(struct eeepc_laptop *eeepc)
{ /* * If the following call to set_acpi() fails, it's because there's no * camera so we can ignore the error.
*/ if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
set_acpi(eeepc, CM_ASL_CAMERA, 1);
}
staticbool eeepc_device_present;
staticint eeepc_acpi_add(struct acpi_device *device)
{ struct eeepc_laptop *eeepc; int result;
result = eeepc_acpi_init(eeepc); if (result) goto fail_platform;
eeepc_enable_camera(eeepc);
/* * Register the platform device first. It is used as a parent for the * sub-devices below. * * Note that if there are multiple instances of this ACPI device it * will bail out, because the platform device is registered with a * fixed name. Of course it doesn't make sense to have more than one, * and machine-specific scripts find the fixed name convenient. But * It's also good for us to exclude multiple instances because both * our hwmon and our wlan rfkill subdevice use global ACPI objects * (the EC and the PCI wlan slot respectively).
*/
result = eeepc_platform_init(eeepc); if (result) goto fail_platform;
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
result = eeepc_backlight_init(eeepc); if (result) goto fail_backlight;
}
result = eeepc_input_init(eeepc); if (result) goto fail_input;
result = eeepc_hwmon_init(eeepc); if (result) goto fail_hwmon;
result = eeepc_led_init(eeepc); if (result) goto fail_led;
result = eeepc_rfkill_init(eeepc); if (result) goto fail_rfkill;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.