Merge branch 'asus' into release
This commit is contained in:
@@ -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, ¶ms, &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);
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user