Enable events on all Vaios with the new-style ACPI interface, and use it to support rfkill where available. Signed-off-by: Matthew Garrett <mjg@xxxxxxxxxx> --- This one has less apostrophe abuse in the commit log. No content changes. diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 537959d..c57f54c 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -64,6 +64,7 @@ #include <asm/uaccess.h> #include <linux/sonypi.h> #include <linux/sony-laptop.h> +#include <linux/rfkill.h> #ifdef CONFIG_SONYPI_COMPAT #include <linux/poll.h> #include <linux/miscdevice.h> @@ -143,6 +144,11 @@ struct sony_laptop_keypress { int key; }; +static struct rfkill *sony_wifi_rfkill; +static struct rfkill *sony_bluetooth_rfkill; +static struct rfkill *sony_wwan_rfkill; +static struct rfkill *sony_wimax_rfkill; + /* Correspondance table between sonypi events * and input layer indexes in the keymap */ @@ -981,6 +987,145 @@ static int sony_nc_resume(struct acpi_device *device) return 0; } +static void sony_rfkill_cleanup(void) +{ + if (sony_wifi_rfkill) + rfkill_unregister(sony_wifi_rfkill); + if (sony_bluetooth_rfkill) + rfkill_unregister(sony_bluetooth_rfkill); + if (sony_wwan_rfkill) + rfkill_unregister(sony_wwan_rfkill); + if (sony_wimax_rfkill) + rfkill_unregister(sony_wimax_rfkill); +} + +static int sony_nc_rfkill_get(void *data, enum rfkill_state *state) +{ + int result; + + acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x3 | ((long) data << 8), + &result); + if (result & 0xf) + *state = RFKILL_STATE_UNBLOCKED; + else + *state = RFKILL_STATE_SOFT_BLOCKED; + return 0; +} + +static int sony_nc_rfkill_set(void *data, enum rfkill_state state) +{ + int result; + int call = 0x3 | (((long) data + 1) << 8); + + if (state == RFKILL_STATE_UNBLOCKED) + call |= 0xff0000; + + return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", call, &result); +} + +static int sony_nc_setup_wifi_rfkill(struct acpi_device *device) +{ + int err = 0; + + sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); + if (!sony_wifi_rfkill) + return -1; + sony_wifi_rfkill->name = "sony-wifi"; + sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_wifi_rfkill->get_state = sony_nc_rfkill_get; + sony_wifi_rfkill->user_claim_unsupported = 1; + sony_wifi_rfkill->data = (void *)3; + err = rfkill_register(sony_wifi_rfkill); + if (err) + rfkill_unregister(sony_wifi_rfkill); + return err; +} + +static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device) +{ + int err = 0; + + sony_bluetooth_rfkill = rfkill_allocate(&device->dev, + RFKILL_TYPE_BLUETOOTH); + if (!sony_bluetooth_rfkill) + return -1; + sony_bluetooth_rfkill->name = "sony-bluetooth"; + sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get; + sony_bluetooth_rfkill->user_claim_unsupported = 1; + sony_bluetooth_rfkill->data = (void *)5; + err = rfkill_register(sony_bluetooth_rfkill); + if (err) + rfkill_unregister(sony_bluetooth_rfkill); + return err; +} + +static int sony_nc_setup_wwan_rfkill(struct acpi_device *device) +{ + int err = 0; + + sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); + if (!sony_wwan_rfkill) + return -1; + sony_wwan_rfkill->name = "sony-wwan"; + sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_wwan_rfkill->get_state = sony_nc_rfkill_get; + sony_wwan_rfkill->user_claim_unsupported = 1; + sony_wwan_rfkill->data = (void *)7; + err = rfkill_register(sony_wwan_rfkill); + if (err) + rfkill_unregister(sony_wwan_rfkill); + return err; +} + +static int sony_nc_setup_wimax_rfkill(struct acpi_device *device) +{ + int err = 0; + + sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); + if (!sony_wimax_rfkill) + return -1; + sony_wimax_rfkill->name = "sony-wimax"; + sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_wimax_rfkill->get_state = sony_nc_rfkill_get; + sony_wimax_rfkill->user_claim_unsupported = 1; + sony_wimax_rfkill->data = (void *)9; + err = rfkill_register(sony_wimax_rfkill); + if (err) + rfkill_unregister(sony_wimax_rfkill); + return err; +} + +static int sony_nc_function_setup(struct acpi_device *device) +{ + int result; + + /* Enable all events */ + acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); + + /* Setup hotkey decoding */ + acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result); + + /* Eaable hotkey event generation */ + acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0, &result); + + /* Set BCHA, whatever /that/ does */ + acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result); + + acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0xb03, &result); + + if (result & 0x1) + sony_nc_setup_wifi_rfkill(device); + if (result & 0x2) + sony_nc_setup_bluetooth_rfkill(device); + if (result & 0x1c) + sony_nc_setup_wwan_rfkill(device); + if (result & 0x20) + sony_nc_setup_wimax_rfkill(device); + + return 0; +} + static int sony_nc_add(struct acpi_device *device) { acpi_status status; @@ -1024,6 +1169,12 @@ static int sony_nc_add(struct acpi_device *device) dprintk("_INI Method failed\n"); } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN07", + &handle))) { + dprintk("Doing SNC setup\n"); + sony_nc_function_setup(device); + } + /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { @@ -1131,6 +1282,7 @@ static int sony_nc_add(struct acpi_device *device) sony_laptop_remove_input(); outwalk: + sony_rfkill_cleanup(); return result; } @@ -1156,6 +1308,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) sony_pf_remove(); sony_laptop_remove_input(); + sony_rfkill_cleanup(); dprintk(SONY_NC_DRIVER_NAME " removed.\n"); return 0; -- Matthew Garrett | mjg59@xxxxxxxxxxxxx -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html