sony-laptop: Add rfkill support on new models
Newer Vaios provide a full featured rfkill implementation via their platform methods. Add support for enumerating the available devices and providing rfkill access to them. Support for the physical kill switch is added, with the devices moving into the HARD_BLOCKED state when toggled. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Mattia Dongili <malattia@linux.it> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
parent
9b57896e62
commit
6cc056bc31
|
@ -64,6 +64,7 @@
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <linux/sonypi.h>
|
#include <linux/sonypi.h>
|
||||||
#include <linux/sony-laptop.h>
|
#include <linux/sony-laptop.h>
|
||||||
|
#include <linux/rfkill.h>
|
||||||
#ifdef CONFIG_SONYPI_COMPAT
|
#ifdef CONFIG_SONYPI_COMPAT
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
|
@ -123,6 +124,18 @@ MODULE_PARM_DESC(minor,
|
||||||
"default is -1 (automatic)");
|
"default is -1 (automatic)");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum sony_nc_rfkill {
|
||||||
|
SONY_WIFI,
|
||||||
|
SONY_BLUETOOTH,
|
||||||
|
SONY_WWAN,
|
||||||
|
SONY_WIMAX,
|
||||||
|
SONY_RFKILL_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
|
||||||
|
static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
|
||||||
|
static void sony_nc_rfkill_update(void);
|
||||||
|
|
||||||
/*********** Input Devices ***********/
|
/*********** Input Devices ***********/
|
||||||
|
|
||||||
#define SONY_LAPTOP_BUF_SIZE 128
|
#define SONY_LAPTOP_BUF_SIZE 128
|
||||||
|
@ -134,6 +147,7 @@ struct sony_laptop_input_s {
|
||||||
spinlock_t fifo_lock;
|
spinlock_t fifo_lock;
|
||||||
struct workqueue_struct *wq;
|
struct workqueue_struct *wq;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sony_laptop_input_s sony_laptop_input = {
|
static struct sony_laptop_input_s sony_laptop_input = {
|
||||||
.users = ATOMIC_INIT(0),
|
.users = ATOMIC_INIT(0),
|
||||||
};
|
};
|
||||||
|
@ -891,6 +905,9 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||||
if (!sony_nc_events[i].data)
|
if (!sony_nc_events[i].data)
|
||||||
printk(KERN_INFO DRV_PFX
|
printk(KERN_INFO DRV_PFX
|
||||||
"Unknown event: %x %x\n", origev, ev);
|
"Unknown event: %x %x\n", origev, ev);
|
||||||
|
} else if (sony_find_snc_handle(0x124) == ev) {
|
||||||
|
sony_nc_rfkill_update();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,6 +990,172 @@ static int sony_nc_resume(struct acpi_device *device)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sony_nc_rfkill_cleanup(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < SONY_RFKILL_MAX; i++) {
|
||||||
|
if (sony_rfkill_devices[i])
|
||||||
|
rfkill_unregister(sony_rfkill_devices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_rfkill_get(void *data, enum rfkill_state *state)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int argument = sony_rfkill_address[(long) data];
|
||||||
|
|
||||||
|
sony_call_snc_handle(0x124, 0x200, &result);
|
||||||
|
if (result & 0x1) {
|
||||||
|
sony_call_snc_handle(0x124, argument, &result);
|
||||||
|
if (result & 0xf)
|
||||||
|
*state = RFKILL_STATE_UNBLOCKED;
|
||||||
|
else
|
||||||
|
*state = RFKILL_STATE_SOFT_BLOCKED;
|
||||||
|
} else {
|
||||||
|
*state = RFKILL_STATE_HARD_BLOCKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_rfkill_set(void *data, enum rfkill_state state)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
int argument = sony_rfkill_address[(long) data] + 0x100;
|
||||||
|
|
||||||
|
if (state == RFKILL_STATE_UNBLOCKED)
|
||||||
|
argument |= 0xff0000;
|
||||||
|
|
||||||
|
return sony_call_snc_handle(0x124, argument, &result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_setup_wifi_rfkill(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct rfkill *sony_wifi_rfkill;
|
||||||
|
|
||||||
|
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 *)SONY_WIFI;
|
||||||
|
err = rfkill_register(sony_wifi_rfkill);
|
||||||
|
if (err)
|
||||||
|
rfkill_free(sony_wifi_rfkill);
|
||||||
|
else
|
||||||
|
sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct rfkill *sony_bluetooth_rfkill;
|
||||||
|
|
||||||
|
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 *)SONY_BLUETOOTH;
|
||||||
|
err = rfkill_register(sony_bluetooth_rfkill);
|
||||||
|
if (err)
|
||||||
|
rfkill_free(sony_bluetooth_rfkill);
|
||||||
|
else
|
||||||
|
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct rfkill *sony_wwan_rfkill;
|
||||||
|
|
||||||
|
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 *)SONY_WWAN;
|
||||||
|
err = rfkill_register(sony_wwan_rfkill);
|
||||||
|
if (err)
|
||||||
|
rfkill_free(sony_wwan_rfkill);
|
||||||
|
else
|
||||||
|
sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct rfkill *sony_wimax_rfkill;
|
||||||
|
|
||||||
|
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 *)SONY_WIMAX;
|
||||||
|
err = rfkill_register(sony_wimax_rfkill);
|
||||||
|
if (err)
|
||||||
|
rfkill_free(sony_wimax_rfkill);
|
||||||
|
else
|
||||||
|
sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sony_nc_rfkill_update()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
enum rfkill_state state;
|
||||||
|
|
||||||
|
for (i = 0; i < SONY_RFKILL_MAX; i++) {
|
||||||
|
if (sony_rfkill_devices[i]) {
|
||||||
|
sony_rfkill_devices[i]->
|
||||||
|
get_state(sony_rfkill_devices[i]->data,
|
||||||
|
&state);
|
||||||
|
rfkill_force_state(sony_rfkill_devices[i], state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sony_nc_rfkill_setup(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
int result, ret;
|
||||||
|
|
||||||
|
if (sony_find_snc_handle(0x124) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = sony_call_snc_handle(0x124, 0xb00, &result);
|
||||||
|
if (ret) {
|
||||||
|
printk(KERN_INFO DRV_PFX
|
||||||
|
"Unable to enumerate rfkill devices: %x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
static int sony_nc_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
@ -1026,6 +1209,7 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
&handle))) {
|
&handle))) {
|
||||||
dprintk("Doing SNC setup\n");
|
dprintk("Doing SNC setup\n");
|
||||||
sony_nc_function_setup(device);
|
sony_nc_function_setup(device);
|
||||||
|
sony_nc_rfkill_setup(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* setup input devices and helper fifo */
|
/* setup input devices and helper fifo */
|
||||||
|
@ -1132,6 +1316,7 @@ static int sony_nc_add(struct acpi_device *device)
|
||||||
sony_laptop_remove_input();
|
sony_laptop_remove_input();
|
||||||
|
|
||||||
outwalk:
|
outwalk:
|
||||||
|
sony_nc_rfkill_cleanup();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,6 +1342,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
|
||||||
|
|
||||||
sony_pf_remove();
|
sony_pf_remove();
|
||||||
sony_laptop_remove_input();
|
sony_laptop_remove_input();
|
||||||
|
sony_nc_rfkill_cleanup();
|
||||||
dprintk(SONY_NC_DRIVER_NAME " removed.\n");
|
dprintk(SONY_NC_DRIVER_NAME " removed.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue