Merge branch 'asus' into release

This commit is contained in:
Len Brown
2009-09-19 01:55:27 -04:00
9 changed files with 832 additions and 195 deletions

View File

@@ -77,15 +77,16 @@
* Flags for hotk status
* WL_ON and BT_ON are also used for wireless_status()
*/
#define WL_ON 0x01 //internal Wifi
#define BT_ON 0x02 //internal Bluetooth
#define MLED_ON 0x04 //mail LED
#define TLED_ON 0x08 //touchpad LED
#define RLED_ON 0x10 //Record LED
#define PLED_ON 0x20 //Phone LED
#define GLED_ON 0x40 //Gaming LED
#define LCD_ON 0x80 //LCD backlight
#define GPS_ON 0x100 //GPS
#define WL_ON 0x01 /* internal Wifi */
#define BT_ON 0x02 /* internal Bluetooth */
#define MLED_ON 0x04 /* mail LED */
#define TLED_ON 0x08 /* touchpad LED */
#define RLED_ON 0x10 /* Record LED */
#define PLED_ON 0x20 /* Phone LED */
#define GLED_ON 0x40 /* Gaming LED */
#define LCD_ON 0x80 /* LCD backlight */
#define GPS_ON 0x100 /* GPS */
#define KEY_ON 0x200 /* Keyboard backlight */
#define ASUS_LOG ASUS_HOTK_FILE ": "
#define ASUS_ERR KERN_ERR ASUS_LOG
@@ -98,7 +99,8 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
MODULE_DESCRIPTION(ASUS_HOTK_NAME);
MODULE_LICENSE("GPL");
/* WAPF defines the behavior of the Fn+Fx wlan key
/*
* WAPF defines the behavior of the Fn+Fx wlan key
* The significance of values is yet to be found, but
* most of the time:
* 0x0 will do nothing
@@ -125,7 +127,8 @@ ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
/* LEDD */
ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
/* Bluetooth and WLAN
/*
* Bluetooth and WLAN
* WLED and BLED are not handled like other XLED, because in some dsdt
* they also control the WLAN/Bluetooth device.
*/
@@ -149,22 +152,32 @@ ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
/* Display */
ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G
M6A M6V VX-1 V6J V6V W3Z */
"\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
S5A M5A z33A W1Jc W2V G1 */
"\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
"\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
"\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */
"\\_SB.PCI0.VGA.GETD", /* Z96F */
"\\ACTD", /* A2D */
"\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
"\\DNXT", /* P30 */
"\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
"\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */
ASUS_HANDLE(display_get,
/* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
"\\_SB.PCI0.P0P1.VGA.GETD",
/* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
"\\_SB.PCI0.P0P2.VGA.GETD",
/* A6V A6Q */
"\\_SB.PCI0.P0P3.VGA.GETD",
/* A6T, A6M */
"\\_SB.PCI0.P0PA.VGA.GETD",
/* L3C */
"\\_SB.PCI0.PCI1.VGAC.NMAP",
/* Z96F */
"\\_SB.PCI0.VGA.GETD",
/* A2D */
"\\ACTD",
/* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
"\\ADVG",
/* P30 */
"\\DNXT",
/* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
"\\INFB",
/* A3F A6F A3N A3L M6N W3N W6A */
"\\SSTE");
ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
/* GPS */
/* R2H use different handle for GPS on/off */
@@ -172,19 +185,23 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */
ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
/* Keyboard light */
ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB");
ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB");
/*
* This is the main structure, we can use it to store anything interesting
* about the hotk device
*/
struct asus_hotk {
char *name; //laptop name
struct acpi_device *device; //the device we are in
acpi_handle handle; //the handle of the hotk device
char status; //status of the hotk, for LEDs, ...
u32 ledd_status; //status of the LED display
u8 light_level; //light sensor level
u8 light_switch; //light sensor switch value
u16 event_count[128]; //count for each event TODO make this better
char *name; /* laptop name */
struct acpi_device *device; /* the device we are in */
acpi_handle handle; /* the handle of the hotk device */
char status; /* status of the hotk, for LEDs, ... */
u32 ledd_status; /* status of the LED display */
u8 light_level; /* light sensor level */
u8 light_switch; /* light sensor switch value */
u16 event_count[128]; /* count for each event TODO make this better */
struct input_dev *inputdev;
u16 *keycode_map;
};
@@ -237,28 +254,35 @@ static struct backlight_ops asusbl_ops = {
.update_status = update_bl_status,
};
/* These functions actually update the LED's, and are called from a
/*
* 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. */
* potentially bad time, such as a timer interrupt.
*/
static struct workqueue_struct *led_workqueue;
#define ASUS_LED(object, ledname) \
#define ASUS_LED(object, ledname, max) \
static void object##_led_set(struct led_classdev *led_cdev, \
enum led_brightness value); \
static enum led_brightness object##_led_get( \
struct led_classdev *led_cdev); \
static void object##_led_update(struct work_struct *ignored); \
static int object##_led_wk; \
static DECLARE_WORK(object##_led_work, object##_led_update); \
static struct led_classdev object##_led = { \
.name = "asus::" ledname, \
.brightness_set = object##_led_set, \
.brightness_get = object##_led_get, \
.max_brightness = max \
}
ASUS_LED(mled, "mail");
ASUS_LED(tled, "touchpad");
ASUS_LED(rled, "record");
ASUS_LED(pled, "phone");
ASUS_LED(gled, "gaming");
ASUS_LED(mled, "mail", 1);
ASUS_LED(tled, "touchpad", 1);
ASUS_LED(rled, "record", 1);
ASUS_LED(pled, "phone", 1);
ASUS_LED(gled, "gaming", 1);
ASUS_LED(kled, "kbd_backlight", 3);
struct key_entry {
char type;
@@ -278,16 +302,23 @@ static struct key_entry asus_keymap[] = {
{KE_KEY, 0x41, KEY_NEXTSONG},
{KE_KEY, 0x43, KEY_STOPCD},
{KE_KEY, 0x45, KEY_PLAYPAUSE},
{KE_KEY, 0x4c, KEY_MEDIA},
{KE_KEY, 0x50, KEY_EMAIL},
{KE_KEY, 0x51, KEY_WWW},
{KE_KEY, 0x55, KEY_CALC},
{KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */
{KE_KEY, 0x5D, KEY_WLAN},
{KE_KEY, 0x5E, KEY_WLAN},
{KE_KEY, 0x5F, KEY_WLAN},
{KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
{KE_KEY, 0x82, KEY_CAMERA},
{KE_KEY, 0x8A, KEY_PROG1},
{KE_KEY, 0x95, KEY_MEDIA},
{KE_KEY, 0x99, KEY_PHONE},
{KE_KEY, 0xc4, KEY_KBDILLUMUP},
{KE_KEY, 0xc5, KEY_KBDILLUMDOWN},
{KE_END, 0},
};
@@ -301,8 +332,8 @@ static struct key_entry asus_keymap[] = {
static int write_acpi_int(acpi_handle handle, const char *method, int val,
struct acpi_buffer *output)
{
struct acpi_object_list params; //list of input parameters (an int here)
union acpi_object in_obj; //the only param we use
struct acpi_object_list params; /* list of input parameters (an int) */
union acpi_object in_obj; /* the only param we use */
acpi_status status;
if (!handle)
@@ -399,6 +430,11 @@ static void write_status(acpi_handle handle, int out, int mask)
{ \
int value = object##_led_wk; \
write_status(object##_set_handle, value, (mask)); \
} \
static enum led_brightness object##_led_get( \
struct led_classdev *led_cdev) \
{ \
return led_cdev->brightness; \
}
ASUS_LED_HANDLER(mled, MLED_ON);
@@ -407,6 +443,60 @@ ASUS_LED_HANDLER(rled, RLED_ON);
ASUS_LED_HANDLER(tled, TLED_ON);
ASUS_LED_HANDLER(gled, GLED_ON);
/*
* Keyboard backlight
*/
static int get_kled_lvl(void)
{
unsigned long long kblv;
struct acpi_object_list params;
union acpi_object in_obj;
acpi_status rv;
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = 2;
rv = acpi_evaluate_integer(kled_get_handle, NULL, &params, &kblv);
if (ACPI_FAILURE(rv)) {
pr_warning("Error reading kled level\n");
return 0;
}
return kblv;
}
static int set_kled_lvl(int kblv)
{
if (kblv > 0)
kblv = (1 << 7) | (kblv & 0x7F);
else
kblv = 0;
if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) {
pr_warning("Keyboard LED display write failed\n");
return -EINVAL;
}
return 0;
}
static void kled_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
kled_led_wk = value;
queue_work(led_workqueue, &kled_led_work);
}
static void kled_led_update(struct work_struct *ignored)
{
set_kled_lvl(kled_led_wk);
}
static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
{
return get_kled_lvl();
}
static int get_lcd_state(void)
{
return read_status(LCD_ON);
@@ -498,7 +588,7 @@ static ssize_t show_infos(struct device *dev,
{
int len = 0;
unsigned long long temp;
char buf[16]; //enough for all info
char buf[16]; /* enough for all info */
acpi_status rv = AE_OK;
/*
@@ -516,7 +606,17 @@ static ssize_t show_infos(struct device *dev,
*/
rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
if (!ACPI_FAILURE(rv))
len += sprintf(page + len, "SFUN value : 0x%04x\n",
len += sprintf(page + len, "SFUN value : %#x\n",
(uint) temp);
/*
* The HWRS method return informations about the hardware.
* 0x80 bit is for WLAN, 0x100 for Bluetooth.
* The significance of others is yet to be found.
* If we don't find the method, we assume the device are present.
*/
rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp);
if (!ACPI_FAILURE(rv))
len += sprintf(page + len, "HRWS value : %#x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
@@ -527,7 +627,7 @@ static ssize_t show_infos(struct device *dev,
*/
rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
if (!ACPI_FAILURE(rv))
len += sprintf(page + len, "ASYM value : 0x%04x\n",
len += sprintf(page + len, "ASYM value : %#x\n",
(uint) temp);
if (asus_info) {
snprintf(buf, 16, "%d", asus_info->length);
@@ -648,8 +748,10 @@ static int read_display(void)
unsigned long long value = 0;
acpi_status rv = AE_OK;
/* In most of the case, we know how to set the display, but sometime
we can't read it */
/*
* In most of the case, we know how to set the display, but sometime
* we can't read it
*/
if (display_get_handle) {
rv = acpi_evaluate_integer(display_get_handle, NULL,
NULL, &value);
@@ -1037,6 +1139,9 @@ static int asus_hotk_get_info(void)
ASUS_HANDLE_INIT(ledd_set);
ASUS_HANDLE_INIT(kled_set);
ASUS_HANDLE_INIT(kled_get);
/*
* The HWRS method return informations about the hardware.
* 0x80 bit is for WLAN, 0x100 for Bluetooth.
@@ -1063,8 +1168,10 @@ static int asus_hotk_get_info(void)
ASUS_HANDLE_INIT(display_set);
ASUS_HANDLE_INIT(display_get);
/* There is a lot of models with "ALSL", but a few get
a real light sens, so we need to check it. */
/*
* There is a lot of models with "ALSL", but a few get
* a real light sens, so we need to check it.
*/
if (!ASUS_HANDLE_INIT(ls_switch))
ASUS_HANDLE_INIT(ls_level);
@@ -1168,6 +1275,10 @@ static int asus_hotk_add(struct acpi_device *device)
/* LCD Backlight is on by default */
write_status(NULL, 1, LCD_ON);
/* Keyboard Backlight is on by default */
if (kled_set_handle)
set_kled_lvl(1);
/* LED display is off by default */
hotk->ledd_status = 0xFFF;
@@ -1222,6 +1333,7 @@ static void asus_led_exit(void)
ASUS_LED_UNREGISTER(pled);
ASUS_LED_UNREGISTER(rled);
ASUS_LED_UNREGISTER(gled);
ASUS_LED_UNREGISTER(kled);
}
static void asus_input_exit(void)
@@ -1301,13 +1413,20 @@ static int asus_led_init(struct device *dev)
if (rv)
goto out4;
led_workqueue = create_singlethread_workqueue("led_workqueue");
if (!led_workqueue)
if (kled_set_handle && kled_get_handle)
rv = ASUS_LED_REGISTER(kled, dev);
if (rv)
goto out5;
led_workqueue = create_singlethread_workqueue("led_workqueue");
if (!led_workqueue)
goto out6;
return 0;
out5:
out6:
rv = -ENOMEM;
ASUS_LED_UNREGISTER(kled);
out5:
ASUS_LED_UNREGISTER(gled);
out4:
ASUS_LED_UNREGISTER(pled);

View File

@@ -142,18 +142,28 @@ struct eeepc_hotk {
struct rfkill *wlan_rfkill;
struct rfkill *bluetooth_rfkill;
struct rfkill *wwan3g_rfkill;
struct rfkill *wimax_rfkill;
struct hotplug_slot *hotplug_slot;
struct work_struct hotplug_work;
struct mutex hotplug_lock;
};
/* The actual device the driver binds to */
static struct eeepc_hotk *ehotk;
/* Platform device/driver */
static int eeepc_hotk_thaw(struct device *device);
static int eeepc_hotk_restore(struct device *device);
static struct dev_pm_ops eeepc_pm_ops = {
.thaw = eeepc_hotk_thaw,
.restore = eeepc_hotk_restore,
};
static struct platform_driver platform_driver = {
.driver = {
.name = EEEPC_HOTK_FILE,
.owner = THIS_MODULE,
.pm = &eeepc_pm_ops,
}
};
@@ -192,7 +202,6 @@ static struct key_entry eeepc_keymap[] = {
*/
static int eeepc_hotk_add(struct acpi_device *device);
static int eeepc_hotk_remove(struct acpi_device *device, int type);
static int eeepc_hotk_resume(struct acpi_device *device);
static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id eeepc_device_ids[] = {
@@ -209,7 +218,6 @@ static struct acpi_driver eeepc_hotk_driver = {
.ops = {
.add = eeepc_hotk_add,
.remove = eeepc_hotk_remove,
.resume = eeepc_hotk_resume,
.notify = eeepc_hotk_notify,
},
};
@@ -579,7 +587,6 @@ static void cmsg_quirks(void)
static int eeepc_hotk_check(void)
{
const struct key_entry *key;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
int result;
@@ -604,31 +611,6 @@ static int eeepc_hotk_check(void)
pr_info("Get control methods supported: 0x%x\n",
ehotk->cm_supported);
}
ehotk->inputdev = input_allocate_device();
if (!ehotk->inputdev) {
pr_info("Unable to allocate input device\n");
return 0;
}
ehotk->inputdev->name = "Asus EeePC extra buttons";
ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
ehotk->inputdev->id.bustype = BUS_HOST;
ehotk->inputdev->getkeycode = eeepc_getkeycode;
ehotk->inputdev->setkeycode = eeepc_setkeycode;
for (key = eeepc_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, ehotk->inputdev->evbit);
set_bit(key->keycode, ehotk->inputdev->keybit);
break;
}
}
result = input_register_device(ehotk->inputdev);
if (result) {
pr_info("Unable to register input device\n");
input_free_device(ehotk->inputdev);
return 0;
}
} else {
pr_err("Hotkey device not present, aborting\n");
return -EINVAL;
@@ -661,40 +643,48 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
return 0;
}
static void eeepc_hotplug_work(struct work_struct *work)
static void eeepc_rfkill_hotplug(void)
{
struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1);
bool blocked;
struct pci_bus *bus;
bool blocked = eeepc_wlan_rfkill_blocked();
if (!bus) {
pr_warning("Unable to find PCI bus 1?\n");
return;
}
if (ehotk->wlan_rfkill)
rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
blocked = eeepc_wlan_rfkill_blocked();
if (!blocked) {
dev = pci_get_slot(bus, 0);
if (dev) {
/* Device already present */
pci_dev_put(dev);
return;
mutex_lock(&ehotk->hotplug_lock);
if (ehotk->hotplug_slot) {
bus = pci_find_bus(0, 1);
if (!bus) {
pr_warning("Unable to find PCI bus 1?\n");
goto out_unlock;
}
dev = pci_scan_single_device(bus, 0);
if (dev) {
pci_bus_assign_resources(bus);
if (pci_bus_add_device(dev))
pr_err("Unable to hotplug wifi\n");
}
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
pci_remove_bus_device(dev);
pci_dev_put(dev);
if (!blocked) {
dev = pci_get_slot(bus, 0);
if (dev) {
/* Device already present */
pci_dev_put(dev);
goto out_unlock;
}
dev = pci_scan_single_device(bus, 0);
if (dev) {
pci_bus_assign_resources(bus);
if (pci_bus_add_device(dev))
pr_err("Unable to hotplug wifi\n");
}
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
pci_remove_bus_device(dev);
pci_dev_put(dev);
}
}
}
rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
out_unlock:
mutex_unlock(&ehotk->hotplug_lock);
}
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
@@ -702,7 +692,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
schedule_work(&ehotk->hotplug_work);
eeepc_rfkill_hotplug();
}
static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
@@ -839,66 +829,38 @@ error_slot:
return ret;
}
static int eeepc_hotk_add(struct acpi_device *device)
{
int result;
if (!device)
return -EINVAL;
pr_notice(EEEPC_HOTK_NAME "\n");
ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
if (!ehotk)
return -ENOMEM;
ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
ehotk->handle = device->handle;
strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
device->driver_data = ehotk;
ehotk->device = device;
result = eeepc_hotk_check();
if (result)
goto ehotk_fail;
return 0;
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
return result;
}
static int eeepc_hotk_remove(struct acpi_device *device, int type)
{
if (!device || !acpi_driver_data(device))
return -EINVAL;
kfree(ehotk);
return 0;
}
static int eeepc_hotk_resume(struct acpi_device *device)
static int eeepc_hotk_thaw(struct device *device)
{
if (ehotk->wlan_rfkill) {
bool wlan;
/* Workaround - it seems that _PTS disables the wireless
without notification or changing the value read by WLAN.
Normally this is fine because the correct value is restored
from the non-volatile storage on resume, but we need to do
it ourself if case suspend is aborted, or we lose wireless.
/*
* 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(CM_ASL_WLAN);
set_acpi(CM_ASL_WLAN, wlan);
rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1);
schedule_work(&ehotk->hotplug_work);
}
return 0;
}
static int eeepc_hotk_restore(struct device *device)
{
/* Refresh both wlan rfkill state and pci hotplug */
if (ehotk->wlan_rfkill)
eeepc_rfkill_hotplug();
if (ehotk->bluetooth_rfkill)
rfkill_set_sw_state(ehotk->bluetooth_rfkill,
get_acpi(CM_ASL_BLUETOOTH) != 1);
if (ehotk->wwan3g_rfkill)
rfkill_set_sw_state(ehotk->wwan3g_rfkill,
get_acpi(CM_ASL_3G) != 1);
if (ehotk->wimax_rfkill)
rfkill_set_sw_state(ehotk->wimax_rfkill,
get_acpi(CM_ASL_WIMAX) != 1);
return 0;
}
@@ -1019,16 +981,37 @@ static void eeepc_backlight_exit(void)
static void eeepc_rfkill_exit(void)
{
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
if (ehotk->wlan_rfkill)
if (ehotk->wlan_rfkill) {
rfkill_unregister(ehotk->wlan_rfkill);
if (ehotk->bluetooth_rfkill)
rfkill_unregister(ehotk->bluetooth_rfkill);
if (ehotk->wwan3g_rfkill)
rfkill_unregister(ehotk->wwan3g_rfkill);
rfkill_destroy(ehotk->wlan_rfkill);
ehotk->wlan_rfkill = NULL;
}
/*
* Refresh pci hotplug in case the rfkill state was changed after
* eeepc_unregister_rfkill_notifier()
*/
eeepc_rfkill_hotplug();
if (ehotk->hotplug_slot)
pci_hp_deregister(ehotk->hotplug_slot);
if (ehotk->bluetooth_rfkill) {
rfkill_unregister(ehotk->bluetooth_rfkill);
rfkill_destroy(ehotk->bluetooth_rfkill);
ehotk->bluetooth_rfkill = NULL;
}
if (ehotk->wwan3g_rfkill) {
rfkill_unregister(ehotk->wwan3g_rfkill);
rfkill_destroy(ehotk->wwan3g_rfkill);
ehotk->wwan3g_rfkill = NULL;
}
if (ehotk->wimax_rfkill) {
rfkill_unregister(ehotk->wimax_rfkill);
rfkill_destroy(ehotk->wimax_rfkill);
ehotk->wimax_rfkill = NULL;
}
}
static void eeepc_input_exit(void)
@@ -1050,19 +1033,6 @@ static void eeepc_hwmon_exit(void)
eeepc_hwmon_device = NULL;
}
static void __exit eeepc_laptop_exit(void)
{
eeepc_backlight_exit();
eeepc_rfkill_exit();
eeepc_input_exit();
eeepc_hwmon_exit();
acpi_bus_unregister_driver(&eeepc_hotk_driver);
sysfs_remove_group(&platform_device->dev.kobj,
&platform_attribute_group);
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
static int eeepc_new_rfkill(struct rfkill **rfkill,
const char *name, struct device *dev,
enum rfkill_type type, int cm)
@@ -1094,10 +1064,7 @@ static int eeepc_rfkill_init(struct device *dev)
{
int result = 0;
INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work);
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
mutex_init(&ehotk->hotplug_lock);
result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
"eeepc-wlan", dev,
@@ -1120,6 +1087,13 @@ static int eeepc_rfkill_init(struct device *dev)
if (result && result != -ENODEV)
goto exit;
result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
"eeepc-wimax", dev,
RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
if (result && result != -ENODEV)
goto exit;
result = eeepc_setup_pci_hotplug();
/*
* If we get -EBUSY then something else is handling the PCI hotplug -
@@ -1128,6 +1102,15 @@ static int eeepc_rfkill_init(struct device *dev)
if (result == -EBUSY)
result = 0;
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
/*
* Refresh pci hotplug in case the rfkill state was changed during
* setup.
*/
eeepc_rfkill_hotplug();
exit:
if (result && result != -ENODEV)
eeepc_rfkill_exit();
@@ -1172,21 +1155,61 @@ static int eeepc_hwmon_init(struct device *dev)
return result;
}
static int __init eeepc_laptop_init(void)
static int eeepc_input_init(struct device *dev)
{
const struct key_entry *key;
int result;
ehotk->inputdev = input_allocate_device();
if (!ehotk->inputdev) {
pr_info("Unable to allocate input device\n");
return -ENOMEM;
}
ehotk->inputdev->name = "Asus EeePC extra buttons";
ehotk->inputdev->dev.parent = dev;
ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
ehotk->inputdev->id.bustype = BUS_HOST;
ehotk->inputdev->getkeycode = eeepc_getkeycode;
ehotk->inputdev->setkeycode = eeepc_setkeycode;
for (key = eeepc_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, ehotk->inputdev->evbit);
set_bit(key->keycode, ehotk->inputdev->keybit);
break;
}
}
result = input_register_device(ehotk->inputdev);
if (result) {
pr_info("Unable to register input device\n");
input_free_device(ehotk->inputdev);
return result;
}
return 0;
}
static int eeepc_hotk_add(struct acpi_device *device)
{
struct device *dev;
int result;
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&eeepc_hotk_driver);
if (result < 0)
return result;
if (!ehotk) {
acpi_bus_unregister_driver(&eeepc_hotk_driver);
return -ENODEV;
}
if (!device)
return -EINVAL;
pr_notice(EEEPC_HOTK_NAME "\n");
ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
if (!ehotk)
return -ENOMEM;
ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
ehotk->handle = device->handle;
strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
device->driver_data = ehotk;
ehotk->device = device;
result = eeepc_hotk_check();
if (result)
goto fail_platform_driver;
eeepc_enable_camera();
/* Register platform stuff */
@@ -1216,6 +1239,10 @@ static int __init eeepc_laptop_init(void)
pr_info("Backlight controlled by ACPI video "
"driver\n");
result = eeepc_input_init(dev);
if (result)
goto fail_input;
result = eeepc_hwmon_init(dev);
if (result)
goto fail_hwmon;
@@ -1225,9 +1252,12 @@ static int __init eeepc_laptop_init(void)
goto fail_rfkill;
return 0;
fail_rfkill:
eeepc_hwmon_exit();
fail_hwmon:
eeepc_input_exit();
fail_input:
eeepc_backlight_exit();
fail_backlight:
sysfs_remove_group(&platform_device->dev.kobj,
@@ -1239,9 +1269,49 @@ fail_platform_device2:
fail_platform_device1:
platform_driver_unregister(&platform_driver);
fail_platform_driver:
eeepc_input_exit();
kfree(ehotk);
return result;
}
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();
sysfs_remove_group(&platform_device->dev.kobj,
&platform_attribute_group);
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
kfree(ehotk);
return 0;
}
static int __init eeepc_laptop_init(void)
{
int result;
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&eeepc_hotk_driver);
if (result < 0)
return result;
if (!ehotk) {
acpi_bus_unregister_driver(&eeepc_hotk_driver);
return -ENODEV;
}
return 0;
}
static void __exit eeepc_laptop_exit(void)
{
acpi_bus_unregister_driver(&eeepc_hotk_driver);
}
module_init(eeepc_laptop_init);
module_exit(eeepc_laptop_exit);