1509 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * eeepc-laptop.c - Asus Eee PC extras4 *5 * Based on asus_acpi.c as patched for the Eee PC by Asus:6 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar7 * Based on eee.c from eeepc-linux8 */9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/init.h>15#include <linux/types.h>16#include <linux/platform_device.h>17#include <linux/backlight.h>18#include <linux/hwmon.h>19#include <linux/hwmon-sysfs.h>20#include <linux/slab.h>21#include <linux/acpi.h>22#include <linux/uaccess.h>23#include <linux/input.h>24#include <linux/input/sparse-keymap.h>25#include <linux/rfkill.h>26#include <linux/pci.h>27#include <linux/pci_hotplug.h>28#include <linux/leds.h>29#include <linux/dmi.h>30#include <acpi/video.h>31 32#define EEEPC_LAPTOP_VERSION "0.1"33#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"34#define EEEPC_LAPTOP_FILE "eeepc"35 36#define EEEPC_ACPI_CLASS "hotkey"37#define EEEPC_ACPI_DEVICE_NAME "Hotkey"38#define EEEPC_ACPI_HID "ASUS010"39 40MODULE_AUTHOR("Corentin Chary, Eric Cooper");41MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);42MODULE_LICENSE("GPL");43 44static bool hotplug_disabled;45 46module_param(hotplug_disabled, bool, 0444);47MODULE_PARM_DESC(hotplug_disabled,48 "Disable hotplug for wireless device. "49 "If your laptop need that, please report to "50 "acpi4asus-user@lists.sourceforge.net.");51 52/*53 * Definitions for Asus EeePC54 */55#define NOTIFY_BRN_MIN 0x2056#define NOTIFY_BRN_MAX 0x2f57 58enum {59 DISABLE_ASL_WLAN = 0x0001,60 DISABLE_ASL_BLUETOOTH = 0x0002,61 DISABLE_ASL_IRDA = 0x0004,62 DISABLE_ASL_CAMERA = 0x0008,63 DISABLE_ASL_TV = 0x0010,64 DISABLE_ASL_GPS = 0x0020,65 DISABLE_ASL_DISPLAYSWITCH = 0x0040,66 DISABLE_ASL_MODEM = 0x0080,67 DISABLE_ASL_CARDREADER = 0x0100,68 DISABLE_ASL_3G = 0x0200,69 DISABLE_ASL_WIMAX = 0x0400,70 DISABLE_ASL_HWCF = 0x080071};72 73enum {74 CM_ASL_WLAN = 0,75 CM_ASL_BLUETOOTH,76 CM_ASL_IRDA,77 CM_ASL_1394,78 CM_ASL_CAMERA,79 CM_ASL_TV,80 CM_ASL_GPS,81 CM_ASL_DVDROM,82 CM_ASL_DISPLAYSWITCH,83 CM_ASL_PANELBRIGHT,84 CM_ASL_BIOSFLASH,85 CM_ASL_ACPIFLASH,86 CM_ASL_CPUFV,87 CM_ASL_CPUTEMPERATURE,88 CM_ASL_FANCPU,89 CM_ASL_FANCHASSIS,90 CM_ASL_USBPORT1,91 CM_ASL_USBPORT2,92 CM_ASL_USBPORT3,93 CM_ASL_MODEM,94 CM_ASL_CARDREADER,95 CM_ASL_3G,96 CM_ASL_WIMAX,97 CM_ASL_HWCF,98 CM_ASL_LID,99 CM_ASL_TYPE,100 CM_ASL_PANELPOWER, /*P901*/101 CM_ASL_TPD102};103 104static const char *cm_getv[] = {105 "WLDG", "BTHG", NULL, NULL,106 "CAMG", NULL, NULL, NULL,107 NULL, "PBLG", NULL, NULL,108 "CFVG", NULL, NULL, NULL,109 "USBG", NULL, NULL, "MODG",110 "CRDG", "M3GG", "WIMG", "HWCF",111 "LIDG", "TYPE", "PBPG", "TPDG"112};113 114static const char *cm_setv[] = {115 "WLDS", "BTHS", NULL, NULL,116 "CAMS", NULL, NULL, NULL,117 "SDSP", "PBLS", "HDPS", NULL,118 "CFVS", NULL, NULL, NULL,119 "USBG", NULL, NULL, "MODS",120 "CRDS", "M3GS", "WIMS", NULL,121 NULL, NULL, "PBPS", "TPDS"122};123 124static const struct key_entry eeepc_keymap[] = {125 { KE_KEY, 0x10, { KEY_WLAN } },126 { KE_KEY, 0x11, { KEY_WLAN } },127 { KE_KEY, 0x12, { KEY_PROG1 } },128 { KE_KEY, 0x13, { KEY_MUTE } },129 { KE_KEY, 0x14, { KEY_VOLUMEDOWN } },130 { KE_KEY, 0x15, { KEY_VOLUMEUP } },131 { KE_KEY, 0x16, { KEY_DISPLAY_OFF } },132 { KE_KEY, 0x1a, { KEY_COFFEE } },133 { KE_KEY, 0x1b, { KEY_ZOOM } },134 { KE_KEY, 0x1c, { KEY_PROG2 } },135 { KE_KEY, 0x1d, { KEY_PROG3 } },136 { KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } },137 { KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } },138 { KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } },139 { KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } },140 { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } },141 { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */142 { KE_KEY, 0x38, { KEY_F14 } },143 { KE_IGNORE, 0x50, { KEY_RESERVED } }, /* AC plugged */144 { KE_IGNORE, 0x51, { KEY_RESERVED } }, /* AC unplugged */145 { KE_END, 0 },146};147 148/*149 * This is the main structure, we can use it to store useful information150 */151struct eeepc_laptop {152 acpi_handle handle; /* the handle of the acpi device */153 u32 cm_supported; /* the control methods supported154 by this BIOS */155 bool cpufv_disabled;156 bool hotplug_disabled;157 u16 event_count[128]; /* count for each event */158 159 struct platform_device *platform_device;160 struct acpi_device *device; /* the device we are in */161 struct backlight_device *backlight_device;162 163 struct input_dev *inputdev;164 165 struct rfkill *wlan_rfkill;166 struct rfkill *bluetooth_rfkill;167 struct rfkill *wwan3g_rfkill;168 struct rfkill *wimax_rfkill;169 170 struct hotplug_slot hotplug_slot;171 struct mutex hotplug_lock;172 173 struct led_classdev tpd_led;174 int tpd_led_wk;175 struct workqueue_struct *led_workqueue;176 struct work_struct tpd_led_work;177};178 179/*180 * ACPI Helpers181 */182static int write_acpi_int(acpi_handle handle, const char *method, int val)183{184 acpi_status status;185 186 status = acpi_execute_simple_method(handle, (char *)method, val);187 188 return (status == AE_OK ? 0 : -1);189}190 191static int read_acpi_int(acpi_handle handle, const char *method, int *val)192{193 acpi_status status;194 unsigned long long result;195 196 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);197 if (ACPI_FAILURE(status)) {198 *val = -1;199 return -1;200 } else {201 *val = result;202 return 0;203 }204}205 206static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value)207{208 const char *method = cm_setv[cm];209 210 if (method == NULL)211 return -ENODEV;212 if ((eeepc->cm_supported & (0x1 << cm)) == 0)213 return -ENODEV;214 215 if (write_acpi_int(eeepc->handle, method, value))216 pr_warn("Error writing %s\n", method);217 return 0;218}219 220static int get_acpi(struct eeepc_laptop *eeepc, int cm)221{222 const char *method = cm_getv[cm];223 int value;224 225 if (method == NULL)226 return -ENODEV;227 if ((eeepc->cm_supported & (0x1 << cm)) == 0)228 return -ENODEV;229 230 if (read_acpi_int(eeepc->handle, method, &value))231 pr_warn("Error reading %s\n", method);232 return value;233}234 235static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,236 acpi_handle *handle)237{238 const char *method = cm_setv[cm];239 acpi_status status;240 241 if (method == NULL)242 return -ENODEV;243 if ((eeepc->cm_supported & (0x1 << cm)) == 0)244 return -ENODEV;245 246 status = acpi_get_handle(eeepc->handle, (char *)method,247 handle);248 if (status != AE_OK) {249 pr_warn("Error finding %s\n", method);250 return -ENODEV;251 }252 return 0;253}254 255 256/*257 * Sys helpers258 */259static int parse_arg(const char *buf, int *val)260{261 if (sscanf(buf, "%i", val) != 1)262 return -EINVAL;263 return 0;264}265 266static ssize_t store_sys_acpi(struct device *dev, int cm,267 const char *buf, size_t count)268{269 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);270 int rv, value;271 272 rv = parse_arg(buf, &value);273 if (rv < 0)274 return rv;275 rv = set_acpi(eeepc, cm, value);276 if (rv < 0)277 return -EIO;278 return count;279}280 281static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)282{283 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);284 int value = get_acpi(eeepc, cm);285 286 if (value < 0)287 return -EIO;288 return sprintf(buf, "%d\n", value);289}290 291#define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \292 static ssize_t _name##_show(struct device *dev, \293 struct device_attribute *attr, \294 char *buf) \295 { \296 return show_sys_acpi(dev, _cm, buf); \297 }298 299#define EEEPC_ACPI_STORE_FUNC(_name, _cm) \300 static ssize_t _name##_store(struct device *dev, \301 struct device_attribute *attr, \302 const char *buf, size_t count) \303 { \304 return store_sys_acpi(dev, _cm, buf, count); \305 }306 307#define EEEPC_CREATE_DEVICE_ATTR_RW(_name, _cm) \308 EEEPC_ACPI_SHOW_FUNC(_name, _cm) \309 EEEPC_ACPI_STORE_FUNC(_name, _cm) \310 static DEVICE_ATTR_RW(_name)311 312#define EEEPC_CREATE_DEVICE_ATTR_WO(_name, _cm) \313 EEEPC_ACPI_STORE_FUNC(_name, _cm) \314 static DEVICE_ATTR_WO(_name)315 316EEEPC_CREATE_DEVICE_ATTR_RW(camera, CM_ASL_CAMERA);317EEEPC_CREATE_DEVICE_ATTR_RW(cardr, CM_ASL_CARDREADER);318EEEPC_CREATE_DEVICE_ATTR_WO(disp, CM_ASL_DISPLAYSWITCH);319 320struct eeepc_cpufv {321 int num;322 int cur;323};324 325static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)326{327 c->cur = get_acpi(eeepc, CM_ASL_CPUFV);328 if (c->cur < 0)329 return -ENODEV;330 331 c->num = (c->cur >> 8) & 0xff;332 c->cur &= 0xff;333 if (c->num == 0 || c->num > 12)334 return -ENODEV;335 return 0;336}337 338static ssize_t available_cpufv_show(struct device *dev,339 struct device_attribute *attr,340 char *buf)341{342 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);343 struct eeepc_cpufv c;344 int i;345 ssize_t len = 0;346 347 if (get_cpufv(eeepc, &c))348 return -ENODEV;349 for (i = 0; i < c.num; i++)350 len += sprintf(buf + len, "%d ", i);351 len += sprintf(buf + len, "\n");352 return len;353}354 355static ssize_t cpufv_show(struct device *dev,356 struct device_attribute *attr,357 char *buf)358{359 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);360 struct eeepc_cpufv c;361 362 if (get_cpufv(eeepc, &c))363 return -ENODEV;364 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);365}366 367static ssize_t cpufv_store(struct device *dev,368 struct device_attribute *attr,369 const char *buf, size_t count)370{371 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);372 struct eeepc_cpufv c;373 int rv, value;374 375 if (eeepc->cpufv_disabled)376 return -EPERM;377 if (get_cpufv(eeepc, &c))378 return -ENODEV;379 rv = parse_arg(buf, &value);380 if (rv < 0)381 return rv;382 if (value < 0 || value >= c.num)383 return -EINVAL;384 rv = set_acpi(eeepc, CM_ASL_CPUFV, value);385 if (rv)386 return rv;387 return count;388}389 390static ssize_t cpufv_disabled_show(struct device *dev,391 struct device_attribute *attr,392 char *buf)393{394 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);395 396 return sprintf(buf, "%d\n", eeepc->cpufv_disabled);397}398 399static ssize_t cpufv_disabled_store(struct device *dev,400 struct device_attribute *attr,401 const char *buf, size_t count)402{403 struct eeepc_laptop *eeepc = dev_get_drvdata(dev);404 int rv, value;405 406 rv = parse_arg(buf, &value);407 if (rv < 0)408 return rv;409 410 switch (value) {411 case 0:412 if (eeepc->cpufv_disabled)413 pr_warn("cpufv enabled (not officially supported on this model)\n");414 eeepc->cpufv_disabled = false;415 return count;416 case 1:417 return -EPERM;418 default:419 return -EINVAL;420 }421}422 423 424static DEVICE_ATTR_RW(cpufv);425static DEVICE_ATTR_RO(available_cpufv);426static DEVICE_ATTR_RW(cpufv_disabled);427 428static struct attribute *platform_attributes[] = {429 &dev_attr_camera.attr,430 &dev_attr_cardr.attr,431 &dev_attr_disp.attr,432 &dev_attr_cpufv.attr,433 &dev_attr_available_cpufv.attr,434 &dev_attr_cpufv_disabled.attr,435 NULL436};437 438static const struct attribute_group platform_attribute_group = {439 .attrs = platform_attributes440};441 442static int eeepc_platform_init(struct eeepc_laptop *eeepc)443{444 int result;445 446 eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, PLATFORM_DEVID_NONE);447 if (!eeepc->platform_device)448 return -ENOMEM;449 platform_set_drvdata(eeepc->platform_device, eeepc);450 451 result = platform_device_add(eeepc->platform_device);452 if (result)453 goto fail_platform_device;454 455 result = sysfs_create_group(&eeepc->platform_device->dev.kobj,456 &platform_attribute_group);457 if (result)458 goto fail_sysfs;459 return 0;460 461fail_sysfs:462 platform_device_del(eeepc->platform_device);463fail_platform_device:464 platform_device_put(eeepc->platform_device);465 return result;466}467 468static void eeepc_platform_exit(struct eeepc_laptop *eeepc)469{470 sysfs_remove_group(&eeepc->platform_device->dev.kobj,471 &platform_attribute_group);472 platform_device_unregister(eeepc->platform_device);473}474 475/*476 * LEDs477 */478/*479 * These functions actually update the LED's, and are called from a480 * workqueue. By doing this as separate work rather than when the LED481 * subsystem asks, we avoid messing with the Asus ACPI stuff during a482 * potentially bad time, such as a timer interrupt.483 */484static void tpd_led_update(struct work_struct *work)485{486 struct eeepc_laptop *eeepc;487 488 eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);489 490 set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);491}492 493static void tpd_led_set(struct led_classdev *led_cdev,494 enum led_brightness value)495{496 struct eeepc_laptop *eeepc;497 498 eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);499 500 eeepc->tpd_led_wk = (value > 0) ? 1 : 0;501 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);502}503 504static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)505{506 struct eeepc_laptop *eeepc;507 508 eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);509 510 return get_acpi(eeepc, CM_ASL_TPD);511}512 513static int eeepc_led_init(struct eeepc_laptop *eeepc)514{515 int rv;516 517 if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)518 return 0;519 520 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");521 if (!eeepc->led_workqueue)522 return -ENOMEM;523 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);524 525 eeepc->tpd_led.name = "eeepc::touchpad";526 eeepc->tpd_led.brightness_set = tpd_led_set;527 if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */528 eeepc->tpd_led.brightness_get = tpd_led_get;529 eeepc->tpd_led.max_brightness = 1;530 531 rv = led_classdev_register(&eeepc->platform_device->dev,532 &eeepc->tpd_led);533 if (rv) {534 destroy_workqueue(eeepc->led_workqueue);535 return rv;536 }537 538 return 0;539}540 541static void eeepc_led_exit(struct eeepc_laptop *eeepc)542{543 led_classdev_unregister(&eeepc->tpd_led);544 if (eeepc->led_workqueue)545 destroy_workqueue(eeepc->led_workqueue);546}547 548/*549 * PCI hotplug (for wlan rfkill)550 */551static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)552{553 if (get_acpi(eeepc, CM_ASL_WLAN) == 1)554 return false;555 return true;556}557 558static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)559{560 struct pci_dev *port;561 struct pci_dev *dev;562 struct pci_bus *bus;563 bool blocked = eeepc_wlan_rfkill_blocked(eeepc);564 bool absent;565 u32 l;566 567 if (eeepc->wlan_rfkill)568 rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);569 570 mutex_lock(&eeepc->hotplug_lock);571 pci_lock_rescan_remove();572 573 if (!eeepc->hotplug_slot.ops)574 goto out_unlock;575 576 port = acpi_get_pci_dev(handle);577 if (!port) {578 pr_warn("Unable to find port\n");579 goto out_unlock;580 }581 582 bus = port->subordinate;583 584 if (!bus) {585 pr_warn("Unable to find PCI bus 1?\n");586 goto out_put_dev;587 }588 589 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {590 pr_err("Unable to read PCI config space?\n");591 goto out_put_dev;592 }593 594 absent = (l == 0xffffffff);595 596 if (blocked != absent) {597 pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",598 blocked ? "blocked" : "unblocked",599 absent ? "absent" : "present");600 pr_warn("skipped wireless hotplug as probably inappropriate for this model\n");601 goto out_put_dev;602 }603 604 if (!blocked) {605 dev = pci_get_slot(bus, 0);606 if (dev) {607 /* Device already present */608 pci_dev_put(dev);609 goto out_put_dev;610 }611 dev = pci_scan_single_device(bus, 0);612 if (dev) {613 pci_bus_assign_resources(bus);614 pci_bus_add_device(dev);615 }616 } else {617 dev = pci_get_slot(bus, 0);618 if (dev) {619 pci_stop_and_remove_bus_device(dev);620 pci_dev_put(dev);621 }622 }623out_put_dev:624 pci_dev_put(port);625 626out_unlock:627 pci_unlock_rescan_remove();628 mutex_unlock(&eeepc->hotplug_lock);629}630 631static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node)632{633 acpi_status status = AE_OK;634 acpi_handle handle;635 636 status = acpi_get_handle(NULL, node, &handle);637 638 if (ACPI_SUCCESS(status))639 eeepc_rfkill_hotplug(eeepc, handle);640}641 642static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)643{644 struct eeepc_laptop *eeepc = data;645 646 if (event != ACPI_NOTIFY_BUS_CHECK)647 return;648 649 eeepc_rfkill_hotplug(eeepc, handle);650}651 652static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,653 char *node)654{655 acpi_status status;656 acpi_handle handle;657 658 status = acpi_get_handle(NULL, node, &handle);659 660 if (ACPI_FAILURE(status))661 return -ENODEV;662 663 status = acpi_install_notify_handler(handle,664 ACPI_SYSTEM_NOTIFY,665 eeepc_rfkill_notify,666 eeepc);667 if (ACPI_FAILURE(status))668 pr_warn("Failed to register notify on %s\n", node);669 670 /*671 * Refresh pci hotplug in case the rfkill state was672 * changed during setup.673 */674 eeepc_rfkill_hotplug(eeepc, handle);675 return 0;676}677 678static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,679 char *node)680{681 acpi_status status = AE_OK;682 acpi_handle handle;683 684 status = acpi_get_handle(NULL, node, &handle);685 686 if (ACPI_FAILURE(status))687 return;688 689 status = acpi_remove_notify_handler(handle,690 ACPI_SYSTEM_NOTIFY,691 eeepc_rfkill_notify);692 if (ACPI_FAILURE(status))693 pr_err("Error removing rfkill notify handler %s\n",694 node);695 /*696 * Refresh pci hotplug in case the rfkill697 * state was changed after698 * eeepc_unregister_rfkill_notifier()699 */700 eeepc_rfkill_hotplug(eeepc, handle);701}702 703static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,704 u8 *value)705{706 struct eeepc_laptop *eeepc;707 int val;708 709 eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot);710 val = get_acpi(eeepc, CM_ASL_WLAN);711 712 if (val == 1 || val == 0)713 *value = val;714 else715 return -EINVAL;716 717 return 0;718}719 720static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = {721 .get_adapter_status = eeepc_get_adapter_status,722 .get_power_status = eeepc_get_adapter_status,723};724 725static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)726{727 int ret = -ENOMEM;728 struct pci_bus *bus = pci_find_bus(0, 1);729 730 if (!bus) {731 pr_err("Unable to find wifi PCI bus\n");732 return -ENODEV;733 }734 735 eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops;736 737 ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi");738 if (ret) {739 pr_err("Unable to register hotplug slot - %d\n", ret);740 goto error_register;741 }742 743 return 0;744 745error_register:746 eeepc->hotplug_slot.ops = NULL;747 return ret;748}749 750/*751 * Rfkill devices752 */753static int eeepc_rfkill_set(void *data, bool blocked)754{755 acpi_handle handle = data;756 757 return write_acpi_int(handle, NULL, !blocked);758}759 760static const struct rfkill_ops eeepc_rfkill_ops = {761 .set_block = eeepc_rfkill_set,762};763 764static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,765 struct rfkill **rfkill,766 const char *name,767 enum rfkill_type type, int cm)768{769 acpi_handle handle;770 int result;771 772 result = acpi_setter_handle(eeepc, cm, &handle);773 if (result < 0)774 return result;775 776 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,777 &eeepc_rfkill_ops, handle);778 779 if (!*rfkill)780 return -EINVAL;781 782 rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);783 result = rfkill_register(*rfkill);784 if (result) {785 rfkill_destroy(*rfkill);786 *rfkill = NULL;787 return result;788 }789 return 0;790}791 792static char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5";793static char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6";794static char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7";795 796static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)797{798 eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);799 eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);800 eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);801 if (eeepc->wlan_rfkill) {802 rfkill_unregister(eeepc->wlan_rfkill);803 rfkill_destroy(eeepc->wlan_rfkill);804 eeepc->wlan_rfkill = NULL;805 }806 807 if (eeepc->hotplug_slot.ops)808 pci_hp_deregister(&eeepc->hotplug_slot);809 810 if (eeepc->bluetooth_rfkill) {811 rfkill_unregister(eeepc->bluetooth_rfkill);812 rfkill_destroy(eeepc->bluetooth_rfkill);813 eeepc->bluetooth_rfkill = NULL;814 }815 if (eeepc->wwan3g_rfkill) {816 rfkill_unregister(eeepc->wwan3g_rfkill);817 rfkill_destroy(eeepc->wwan3g_rfkill);818 eeepc->wwan3g_rfkill = NULL;819 }820 if (eeepc->wimax_rfkill) {821 rfkill_unregister(eeepc->wimax_rfkill);822 rfkill_destroy(eeepc->wimax_rfkill);823 eeepc->wimax_rfkill = NULL;824 }825}826 827static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)828{829 int result = 0;830 831 mutex_init(&eeepc->hotplug_lock);832 833 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,834 "eeepc-wlan", RFKILL_TYPE_WLAN,835 CM_ASL_WLAN);836 837 if (result && result != -ENODEV)838 goto exit;839 840 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,841 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,842 CM_ASL_BLUETOOTH);843 844 if (result && result != -ENODEV)845 goto exit;846 847 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,848 "eeepc-wwan3g", RFKILL_TYPE_WWAN,849 CM_ASL_3G);850 851 if (result && result != -ENODEV)852 goto exit;853 854 result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,855 "eeepc-wimax", RFKILL_TYPE_WIMAX,856 CM_ASL_WIMAX);857 858 if (result && result != -ENODEV)859 goto exit;860 861 if (eeepc->hotplug_disabled)862 return 0;863 864 result = eeepc_setup_pci_hotplug(eeepc);865 /*866 * If we get -EBUSY then something else is handling the PCI hotplug -867 * don't fail in this case868 */869 if (result == -EBUSY)870 result = 0;871 872 eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1);873 eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2);874 eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3);875 876exit:877 if (result && result != -ENODEV)878 eeepc_rfkill_exit(eeepc);879 return result;880}881 882/*883 * Platform driver - hibernate/resume callbacks884 */885static int eeepc_hotk_thaw(struct device *device)886{887 struct eeepc_laptop *eeepc = dev_get_drvdata(device);888 889 if (eeepc->wlan_rfkill) {890 int wlan;891 892 /*893 * Work around bios bug - acpi _PTS turns off the wireless led894 * during suspend. Normally it restores it on resume, but895 * we should kick it ourselves in case hibernation is aborted.896 */897 wlan = get_acpi(eeepc, CM_ASL_WLAN);898 if (wlan >= 0)899 set_acpi(eeepc, CM_ASL_WLAN, wlan);900 }901 902 return 0;903}904 905static int eeepc_hotk_restore(struct device *device)906{907 struct eeepc_laptop *eeepc = dev_get_drvdata(device);908 909 /* Refresh both wlan rfkill state and pci hotplug */910 if (eeepc->wlan_rfkill) {911 eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1);912 eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2);913 eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3);914 }915 916 if (eeepc->bluetooth_rfkill)917 rfkill_set_sw_state(eeepc->bluetooth_rfkill,918 get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);919 if (eeepc->wwan3g_rfkill)920 rfkill_set_sw_state(eeepc->wwan3g_rfkill,921 get_acpi(eeepc, CM_ASL_3G) != 1);922 if (eeepc->wimax_rfkill)923 rfkill_set_sw_state(eeepc->wimax_rfkill,924 get_acpi(eeepc, CM_ASL_WIMAX) != 1);925 926 return 0;927}928 929static const struct dev_pm_ops eeepc_pm_ops = {930 .thaw = eeepc_hotk_thaw,931 .restore = eeepc_hotk_restore,932};933 934static struct platform_driver platform_driver = {935 .driver = {936 .name = EEEPC_LAPTOP_FILE,937 .pm = &eeepc_pm_ops,938 }939};940 941/*942 * Hwmon device943 */944 945#define EEEPC_EC_SC00 0x61946#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */947#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */948#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */949 950#define EEEPC_EC_SFB0 0xD0951#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */952 953static inline int eeepc_pwm_to_lmsensors(int value)954{955 return value * 255 / 100;956}957 958static inline int eeepc_lmsensors_to_pwm(int value)959{960 value = clamp_val(value, 0, 255);961 return value * 100 / 255;962}963 964static int eeepc_get_fan_pwm(void)965{966 u8 value = 0;967 968 ec_read(EEEPC_EC_FAN_PWM, &value);969 return eeepc_pwm_to_lmsensors(value);970}971 972static void eeepc_set_fan_pwm(int value)973{974 value = eeepc_lmsensors_to_pwm(value);975 ec_write(EEEPC_EC_FAN_PWM, value);976}977 978static int eeepc_get_fan_rpm(void)979{980 u8 high = 0;981 u8 low = 0;982 983 ec_read(EEEPC_EC_FAN_HRPM, &high);984 ec_read(EEEPC_EC_FAN_LRPM, &low);985 return high << 8 | low;986}987 988#define EEEPC_EC_FAN_CTRL_BIT 0x02989#define EEEPC_FAN_CTRL_MANUAL 1990#define EEEPC_FAN_CTRL_AUTO 2991 992static int eeepc_get_fan_ctrl(void)993{994 u8 value = 0;995 996 ec_read(EEEPC_EC_FAN_CTRL, &value);997 if (value & EEEPC_EC_FAN_CTRL_BIT)998 return EEEPC_FAN_CTRL_MANUAL;999 else1000 return EEEPC_FAN_CTRL_AUTO;1001}1002 1003static void eeepc_set_fan_ctrl(int manual)1004{1005 u8 value = 0;1006 1007 ec_read(EEEPC_EC_FAN_CTRL, &value);1008 if (manual == EEEPC_FAN_CTRL_MANUAL)1009 value |= EEEPC_EC_FAN_CTRL_BIT;1010 else1011 value &= ~EEEPC_EC_FAN_CTRL_BIT;1012 ec_write(EEEPC_EC_FAN_CTRL, value);1013}1014 1015static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)1016{1017 int rv, value;1018 1019 rv = parse_arg(buf, &value);1020 if (rv < 0)1021 return rv;1022 set(value);1023 return count;1024}1025 1026static ssize_t show_sys_hwmon(int (*get)(void), char *buf)1027{1028 return sprintf(buf, "%d\n", get());1029}1030 1031#define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \1032 static ssize_t _name##_show(struct device *dev, \1033 struct device_attribute *attr, \1034 char *buf) \1035 { \1036 return show_sys_hwmon(_get, buf); \1037 }1038 1039#define EEEPC_SENSOR_STORE_FUNC(_name, _set) \1040 static ssize_t _name##_store(struct device *dev, \1041 struct device_attribute *attr, \1042 const char *buf, size_t count) \1043 { \1044 return store_sys_hwmon(_set, buf, count); \1045 }1046 1047#define EEEPC_CREATE_SENSOR_ATTR_RW(_name, _get, _set) \1048 EEEPC_SENSOR_SHOW_FUNC(_name, _get) \1049 EEEPC_SENSOR_STORE_FUNC(_name, _set) \1050 static DEVICE_ATTR_RW(_name)1051 1052#define EEEPC_CREATE_SENSOR_ATTR_RO(_name, _get) \1053 EEEPC_SENSOR_SHOW_FUNC(_name, _get) \1054 static DEVICE_ATTR_RO(_name)1055 1056EEEPC_CREATE_SENSOR_ATTR_RO(fan1_input, eeepc_get_fan_rpm);1057EEEPC_CREATE_SENSOR_ATTR_RW(pwm1, eeepc_get_fan_pwm,1058 eeepc_set_fan_pwm);1059EEEPC_CREATE_SENSOR_ATTR_RW(pwm1_enable, eeepc_get_fan_ctrl,1060 eeepc_set_fan_ctrl);1061 1062static struct attribute *hwmon_attrs[] = {1063 &dev_attr_pwm1.attr,1064 &dev_attr_fan1_input.attr,1065 &dev_attr_pwm1_enable.attr,1066 NULL1067};1068ATTRIBUTE_GROUPS(hwmon);1069 1070static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)1071{1072 struct device *dev = &eeepc->platform_device->dev;1073 struct device *hwmon;1074 1075 hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL,1076 hwmon_groups);1077 if (IS_ERR(hwmon)) {1078 pr_err("Could not register eeepc hwmon device\n");1079 return PTR_ERR(hwmon);1080 }1081 return 0;1082}1083 1084/*1085 * Backlight device1086 */1087static int read_brightness(struct backlight_device *bd)1088{1089 struct eeepc_laptop *eeepc = bl_get_data(bd);1090 1091 return get_acpi(eeepc, CM_ASL_PANELBRIGHT);1092}1093 1094static int set_brightness(struct backlight_device *bd, int value)1095{1096 struct eeepc_laptop *eeepc = bl_get_data(bd);1097 1098 return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);1099}1100 1101static int update_bl_status(struct backlight_device *bd)1102{1103 return set_brightness(bd, bd->props.brightness);1104}1105 1106static const struct backlight_ops eeepcbl_ops = {1107 .get_brightness = read_brightness,1108 .update_status = update_bl_status,1109};1110 1111static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)1112{1113 struct backlight_device *bd = eeepc->backlight_device;1114 int old = bd->props.brightness;1115 1116 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);1117 1118 return old;1119}1120 1121static int eeepc_backlight_init(struct eeepc_laptop *eeepc)1122{1123 struct backlight_properties props;1124 struct backlight_device *bd;1125 1126 memset(&props, 0, sizeof(struct backlight_properties));1127 props.type = BACKLIGHT_PLATFORM;1128 props.max_brightness = 15;1129 bd = backlight_device_register(EEEPC_LAPTOP_FILE,1130 &eeepc->platform_device->dev, eeepc,1131 &eeepcbl_ops, &props);1132 if (IS_ERR(bd)) {1133 pr_err("Could not register eeepc backlight device\n");1134 eeepc->backlight_device = NULL;1135 return PTR_ERR(bd);1136 }1137 eeepc->backlight_device = bd;1138 bd->props.brightness = read_brightness(bd);1139 bd->props.power = BACKLIGHT_POWER_ON;1140 backlight_update_status(bd);1141 return 0;1142}1143 1144static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)1145{1146 backlight_device_unregister(eeepc->backlight_device);1147 eeepc->backlight_device = NULL;1148}1149 1150 1151/*1152 * Input device (i.e. hotkeys)1153 */1154static int eeepc_input_init(struct eeepc_laptop *eeepc)1155{1156 struct input_dev *input;1157 int error;1158 1159 input = input_allocate_device();1160 if (!input)1161 return -ENOMEM;1162 1163 input->name = "Asus EeePC extra buttons";1164 input->phys = EEEPC_LAPTOP_FILE "/input0";1165 input->id.bustype = BUS_HOST;1166 input->dev.parent = &eeepc->platform_device->dev;1167 1168 error = sparse_keymap_setup(input, eeepc_keymap, NULL);1169 if (error) {1170 pr_err("Unable to setup input device keymap\n");1171 goto err_free_dev;1172 }1173 1174 error = input_register_device(input);1175 if (error) {1176 pr_err("Unable to register input device\n");1177 goto err_free_dev;1178 }1179 1180 eeepc->inputdev = input;1181 return 0;1182 1183err_free_dev:1184 input_free_device(input);1185 return error;1186}1187 1188static void eeepc_input_exit(struct eeepc_laptop *eeepc)1189{1190 if (eeepc->inputdev)1191 input_unregister_device(eeepc->inputdev);1192 eeepc->inputdev = NULL;1193}1194 1195/*1196 * ACPI driver1197 */1198static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)1199{1200 if (!eeepc->inputdev)1201 return;1202 if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))1203 pr_info("Unknown key %x pressed\n", event);1204}1205 1206static void eeepc_acpi_notify(struct acpi_device *device, u32 event)1207{1208 struct eeepc_laptop *eeepc = acpi_driver_data(device);1209 int old_brightness, new_brightness;1210 u16 count;1211 1212 if (event > ACPI_MAX_SYS_NOTIFY)1213 return;1214 count = eeepc->event_count[event % 128]++;1215 acpi_bus_generate_netlink_event(device->pnp.device_class,1216 dev_name(&device->dev), event,1217 count);1218 1219 /* Brightness events are special */1220 if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) {1221 eeepc_input_notify(eeepc, event);1222 return;1223 }1224 1225 /* Ignore them completely if the acpi video driver is used */1226 if (!eeepc->backlight_device)1227 return;1228 1229 /* Update the backlight device. */1230 old_brightness = eeepc_backlight_notify(eeepc);1231 1232 /* Convert event to keypress (obsolescent hack) */1233 new_brightness = event - NOTIFY_BRN_MIN;1234 1235 if (new_brightness < old_brightness) {1236 event = NOTIFY_BRN_MIN; /* brightness down */1237 } else if (new_brightness > old_brightness) {1238 event = NOTIFY_BRN_MAX; /* brightness up */1239 } else {1240 /*1241 * no change in brightness - already at min/max,1242 * event will be desired value (or else ignored)1243 */1244 }1245 eeepc_input_notify(eeepc, event);1246}1247 1248static void eeepc_dmi_check(struct eeepc_laptop *eeepc)1249{1250 const char *model;1251 1252 model = dmi_get_system_info(DMI_PRODUCT_NAME);1253 if (!model)1254 return;1255 1256 /*1257 * Blacklist for setting cpufv (cpu speed).1258 *1259 * EeePC 4G ("701") implements CFVS, but it is not supported1260 * by the pre-installed OS, and the original option to change it1261 * in the BIOS setup screen was removed in later versions.1262 *1263 * Judging by the lack of "Super Hybrid Engine" on Asus product pages,1264 * this applies to all "701" models (4G/4G Surf/2G Surf).1265 *1266 * So Asus made a deliberate decision not to support it on this model.1267 * We have several reports that using it can cause the system to hang1268 *1269 * The hang has also been reported on a "702" (Model name "8G"?).1270 *1271 * We avoid dmi_check_system() / dmi_match(), because they use1272 * substring matching. We don't want to affect the "701SD"1273 * and "701SDX" models, because they do support S.H.E.1274 */1275 if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) {1276 eeepc->cpufv_disabled = true;1277 pr_info("model %s does not officially support setting cpu speed\n",1278 model);1279 pr_info("cpufv disabled to avoid instability\n");1280 }1281 1282 /*1283 * Blacklist for wlan hotplug1284 *1285 * Eeepc 1005HA doesn't work like others models and don't need the1286 * hotplug code. In fact, current hotplug code seems to unplug another1287 * device...1288 */1289 if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 ||1290 strcmp(model, "1005PE") == 0) {1291 eeepc->hotplug_disabled = true;1292 pr_info("wlan hotplug disabled\n");1293 }1294}1295 1296static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)1297{1298 int dummy;1299 1300 /* Some BIOSes do not report cm although it is available.1301 Check if cm_getv[cm] works and, if yes, assume cm should be set. */1302 if (!(eeepc->cm_supported & (1 << cm))1303 && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {1304 pr_info("%s (%x) not reported by BIOS, enabling anyway\n",1305 name, 1 << cm);1306 eeepc->cm_supported |= 1 << cm;1307 }1308}1309 1310static void cmsg_quirks(struct eeepc_laptop *eeepc)1311{1312 cmsg_quirk(eeepc, CM_ASL_LID, "LID");1313 cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");1314 cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");1315 cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");1316}1317 1318static int eeepc_acpi_init(struct eeepc_laptop *eeepc)1319{1320 unsigned int init_flags;1321 int result;1322 1323 result = acpi_bus_get_status(eeepc->device);1324 if (result)1325 return result;1326 if (!eeepc->device->status.present) {1327 pr_err("Hotkey device not present, aborting\n");1328 return -ENODEV;1329 }1330 1331 init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;1332 pr_notice("Hotkey init flags 0x%x\n", init_flags);1333 1334 if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {1335 pr_err("Hotkey initialization failed\n");1336 return -ENODEV;1337 }1338 1339 /* get control methods supported */1340 if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {1341 pr_err("Get control methods supported failed\n");1342 return -ENODEV;1343 }1344 cmsg_quirks(eeepc);1345 pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);1346 1347 return 0;1348}1349 1350static void eeepc_enable_camera(struct eeepc_laptop *eeepc)1351{1352 /*1353 * If the following call to set_acpi() fails, it's because there's no1354 * camera so we can ignore the error.1355 */1356 if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)1357 set_acpi(eeepc, CM_ASL_CAMERA, 1);1358}1359 1360static bool eeepc_device_present;1361 1362static int eeepc_acpi_add(struct acpi_device *device)1363{1364 struct eeepc_laptop *eeepc;1365 int result;1366 1367 pr_notice(EEEPC_LAPTOP_NAME "\n");1368 eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);1369 if (!eeepc)1370 return -ENOMEM;1371 eeepc->handle = device->handle;1372 strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);1373 strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);1374 device->driver_data = eeepc;1375 eeepc->device = device;1376 1377 eeepc->hotplug_disabled = hotplug_disabled;1378 1379 eeepc_dmi_check(eeepc);1380 1381 result = eeepc_acpi_init(eeepc);1382 if (result)1383 goto fail_platform;1384 eeepc_enable_camera(eeepc);1385 1386 /*1387 * Register the platform device first. It is used as a parent for the1388 * sub-devices below.1389 *1390 * Note that if there are multiple instances of this ACPI device it1391 * will bail out, because the platform device is registered with a1392 * fixed name. Of course it doesn't make sense to have more than one,1393 * and machine-specific scripts find the fixed name convenient. But1394 * It's also good for us to exclude multiple instances because both1395 * our hwmon and our wlan rfkill subdevice use global ACPI objects1396 * (the EC and the PCI wlan slot respectively).1397 */1398 result = eeepc_platform_init(eeepc);1399 if (result)1400 goto fail_platform;1401 1402 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {1403 result = eeepc_backlight_init(eeepc);1404 if (result)1405 goto fail_backlight;1406 }1407 1408 result = eeepc_input_init(eeepc);1409 if (result)1410 goto fail_input;1411 1412 result = eeepc_hwmon_init(eeepc);1413 if (result)1414 goto fail_hwmon;1415 1416 result = eeepc_led_init(eeepc);1417 if (result)1418 goto fail_led;1419 1420 result = eeepc_rfkill_init(eeepc);1421 if (result)1422 goto fail_rfkill;1423 1424 eeepc_device_present = true;1425 return 0;1426 1427fail_rfkill:1428 eeepc_led_exit(eeepc);1429fail_led:1430fail_hwmon:1431 eeepc_input_exit(eeepc);1432fail_input:1433 eeepc_backlight_exit(eeepc);1434fail_backlight:1435 eeepc_platform_exit(eeepc);1436fail_platform:1437 kfree(eeepc);1438 1439 return result;1440}1441 1442static void eeepc_acpi_remove(struct acpi_device *device)1443{1444 struct eeepc_laptop *eeepc = acpi_driver_data(device);1445 1446 eeepc_backlight_exit(eeepc);1447 eeepc_rfkill_exit(eeepc);1448 eeepc_input_exit(eeepc);1449 eeepc_led_exit(eeepc);1450 eeepc_platform_exit(eeepc);1451 1452 kfree(eeepc);1453}1454 1455 1456static const struct acpi_device_id eeepc_device_ids[] = {1457 {EEEPC_ACPI_HID, 0},1458 {"", 0},1459};1460MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);1461 1462static struct acpi_driver eeepc_acpi_driver = {1463 .name = EEEPC_LAPTOP_NAME,1464 .class = EEEPC_ACPI_CLASS,1465 .ids = eeepc_device_ids,1466 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,1467 .ops = {1468 .add = eeepc_acpi_add,1469 .remove = eeepc_acpi_remove,1470 .notify = eeepc_acpi_notify,1471 },1472};1473 1474 1475static int __init eeepc_laptop_init(void)1476{1477 int result;1478 1479 result = platform_driver_register(&platform_driver);1480 if (result < 0)1481 return result;1482 1483 result = acpi_bus_register_driver(&eeepc_acpi_driver);1484 if (result < 0)1485 goto fail_acpi_driver;1486 1487 if (!eeepc_device_present) {1488 result = -ENODEV;1489 goto fail_no_device;1490 }1491 1492 return 0;1493 1494fail_no_device:1495 acpi_bus_unregister_driver(&eeepc_acpi_driver);1496fail_acpi_driver:1497 platform_driver_unregister(&platform_driver);1498 return result;1499}1500 1501static void __exit eeepc_laptop_exit(void)1502{1503 acpi_bus_unregister_driver(&eeepc_acpi_driver);1504 platform_driver_unregister(&platform_driver);1505}1506 1507module_init(eeepc_laptop_init);1508module_exit(eeepc_laptop_exit);1509