#include <linux/rfkill.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/leds.h>
#define EEEPC_LAPTOP_VERSION "0.1"
/* The actual device the driver binds to */
static struct eeepc_hotk *ehotk;
-static void eeepc_rfkill_hotplug(bool real);
-
/* Platform device/driver */
static int eeepc_hotk_thaw(struct device *device);
static int eeepc_hotk_restore(struct device *device);
static int eeepc_rfkill_set(void *data, bool blocked)
{
unsigned long asl = (unsigned long)data;
- int ret;
-
- if (asl != CM_ASL_WLAN)
- return set_acpi(asl, !blocked);
-
- /* hack to avoid panic with rt2860sta */
- if (blocked)
- eeepc_rfkill_hotplug(false);
- ret = set_acpi(asl, !blocked);
- return ret;
+ return set_acpi(asl, !blocked);
}
static const struct rfkill_ops eeepc_rfkill_ops = {
* If the following call to set_acpi() fails, it's because there's no
* camera so we can ignore the error.
*/
- set_acpi(CM_ASL_CAMERA, 1);
+ if (get_acpi(CM_ASL_CAMERA) == 0)
+ set_acpi(CM_ASL_CAMERA, 1);
}
/*
if (rv > 0)
value = set_acpi(cm, value);
if (value < 0)
- return value;
+ return -EIO;
return rv;
}
int value = get_acpi(cm);
if (value < 0)
- return value;
+ return -EIO;
return sprintf(buf, "%d\n", value);
}
-#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
+#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
static ssize_t show_##_name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
static struct device_attribute dev_attr_##_name = { \
.attr = { \
.name = __stringify(_name), \
- .mode = 0644 }, \
+ .mode = _mode }, \
.show = show_##_name, \
.store = store_##_name, \
}
-EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
-EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
-EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
+EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
+EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
+EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
struct eeepc_cpufv {
int num;
.attrs = platform_attributes
};
+/*
+ * 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.
+ */
+static int tpd_led_wk;
+
+static void tpd_led_update(struct work_struct *ignored)
+{
+ int value = tpd_led_wk;
+ set_acpi(CM_ASL_TPD, value);
+}
+
+static struct workqueue_struct *led_workqueue;
+static DECLARE_WORK(tpd_led_work, tpd_led_update);
+
+static void tpd_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ tpd_led_wk = (value > 0) ? 1 : 0;
+ queue_work(led_workqueue, &tpd_led_work);
+}
+
+static struct led_classdev tpd_led = {
+ .name = "eeepc::touchpad",
+ .brightness_set = tpd_led_set,
+ .max_brightness = 1
+};
+
/*
* Hotkey functions
*/
return 0;
}
-static void eeepc_rfkill_hotplug(bool real)
+static void eeepc_rfkill_hotplug(void)
{
struct pci_dev *dev;
struct pci_bus *bus;
- bool blocked = real ? eeepc_wlan_rfkill_blocked() : true;
+ bool blocked = eeepc_wlan_rfkill_blocked();
- if (real && ehotk->wlan_rfkill)
+ if (ehotk->wlan_rfkill)
rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
mutex_lock(&ehotk->hotplug_lock);
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
- eeepc_rfkill_hotplug(true);
+ eeepc_rfkill_hotplug();
}
static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
{
/* Refresh both wlan rfkill state and pci hotplug */
if (ehotk->wlan_rfkill)
- eeepc_rfkill_hotplug(true);
+ eeepc_rfkill_hotplug();
if (ehotk->bluetooth_rfkill)
rfkill_set_sw_state(ehotk->bluetooth_rfkill,
* Refresh pci hotplug in case the rfkill state was changed after
* eeepc_unregister_rfkill_notifier()
*/
- eeepc_rfkill_hotplug(true);
+ eeepc_rfkill_hotplug();
if (ehotk->hotplug_slot)
pci_hp_deregister(ehotk->hotplug_slot);
eeepc_hwmon_device = NULL;
}
+static void eeepc_led_exit(void)
+{
+ if (led_workqueue)
+ destroy_workqueue(led_workqueue);
+ if (tpd_led.dev)
+ led_classdev_unregister(&tpd_led);
+}
+
static int eeepc_new_rfkill(struct rfkill **rfkill,
const char *name, struct device *dev,
enum rfkill_type type, int cm)
* Refresh pci hotplug in case the rfkill state was changed during
* setup.
*/
- eeepc_rfkill_hotplug(true);
+ eeepc_rfkill_hotplug();
exit:
if (result && result != -ENODEV)
return 0;
}
+static int eeepc_led_init(struct device *dev)
+{
+ int rv;
+
+ if (get_acpi(CM_ASL_TPD) == -ENODEV)
+ return 0;
+
+ rv = led_classdev_register(dev, &tpd_led);
+ if (rv)
+ return rv;
+
+ led_workqueue = create_singlethread_workqueue("led_workqueue");
+ if (!led_workqueue)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int __devinit eeepc_hotk_add(struct acpi_device *device)
{
struct device *dev;
int result;
- if (!device)
- return -EINVAL;
pr_notice(EEEPC_HOTK_NAME "\n");
ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
if (!ehotk)
if (result)
goto fail_hwmon;
+ result = eeepc_led_init(dev);
+ if (result)
+ goto fail_led;
+
result = eeepc_rfkill_init(dev);
if (result)
goto fail_rfkill;
return 0;
fail_rfkill:
+ eeepc_led_exit();
+fail_led:
eeepc_hwmon_exit();
fail_hwmon:
eeepc_input_exit();
static int eeepc_hotk_remove(struct acpi_device *device, int type)
{
- if (!device || !acpi_driver_data(device))
- return -EINVAL;
-
eeepc_backlight_exit();
eeepc_rfkill_exit();
eeepc_input_exit();
eeepc_hwmon_exit();
+ eeepc_led_exit();
sysfs_remove_group(&platform_device->dev.kobj,
&platform_attribute_group);
platform_device_unregister(platform_device);
{
int result;
- if (acpi_disabled)
- return -ENODEV;
result = acpi_bus_register_driver(&eeepc_hotk_driver);
if (result < 0)
return result;