rfkill: rewrite

This patch completely rewrites the rfkill core to address
the following deficiencies:

 * all rfkill drivers need to implement polling where necessary
   rather than having one central implementation

 * updating the rfkill state cannot be done from arbitrary
   contexts, forcing drivers to use schedule_work and requiring
   lots of code

 * rfkill drivers need to keep track of soft/hard blocked
   internally -- the core should do this

 * the rfkill API has many unexpected quirks, for example being
   asymmetric wrt. alloc/free and register/unregister

 * rfkill can call back into a driver from within a function the
   driver called -- this is prone to deadlocks and generally
   should be avoided

 * rfkill-input pointlessly is a separate module

 * drivers need to #ifdef rfkill functions (unless they want to
   depend on or select RFKILL) -- rfkill should provide inlines
   that do nothing if it isn't compiled in

 * the rfkill structure is not opaque -- drivers need to initialise
   it correctly (lots of sanity checking code required) -- instead
   force drivers to pass the right variables to rfkill_alloc()

 * the documentation is hard to read because it always assumes the
   reader is completely clueless and contains way TOO MANY CAPS

 * the rfkill code needlessly uses a lot of locks and atomic
   operations in locked sections

 * fix LED trigger to actually change the LED when the radio state
   changes -- this wasn't done before

Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad]
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Johannes Berg
2009-06-02 13:01:37 +02:00
committed by John W. Linville
parent 0f6399c4c5
commit 19d337dff9
46 changed files with 2567 additions and 3304 deletions

View File

@@ -21,7 +21,7 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
depends on RFKILL
depends on RFKILL || RFKILL = n
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +60,7 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL
depends on RFKILL || RFKILL = n
depends on POWER_SUPPLY
default n
---help---
@@ -117,7 +117,7 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
depends on RFKILL
depends on RFKILL || RFKILL = n
help
Say Y here if you want to support WMI-based hotkeys on HP laptops and
to read data from WMI such as docking or ambient light sensor state.
@@ -196,14 +196,13 @@ config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
select NEW_LEDS
select LEDS_CLASS
select NET
select RFKILL
---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
@@ -338,9 +337,9 @@ config EEEPC_LAPTOP
depends on ACPI
depends on INPUT
depends on EXPERIMENTAL
depends on RFKILL || RFKILL = n
select BACKLIGHT_CLASS_DEVICE
select HWMON
select RFKILL
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
It also adds the ability to switch camera/wlan on/off.
@@ -405,9 +404,8 @@ config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_POLLDEV
select NET
select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings

View File

@@ -958,58 +958,50 @@ static void acer_rfkill_update(struct work_struct *ignored)
status = get_u32(&state, ACER_CAP_WIRELESS);
if (ACPI_SUCCESS(status))
rfkill_force_state(wireless_rfkill, state ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
rfkill_set_sw_state(wireless_rfkill, !!state);
if (has_cap(ACER_CAP_BLUETOOTH)) {
status = get_u32(&state, ACER_CAP_BLUETOOTH);
if (ACPI_SUCCESS(status))
rfkill_force_state(bluetooth_rfkill, state ?
RFKILL_STATE_UNBLOCKED :
RFKILL_STATE_SOFT_BLOCKED);
rfkill_set_sw_state(bluetooth_rfkill, !!state);
}
schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
}
static int acer_rfkill_set(void *data, enum rfkill_state state)
static int acer_rfkill_set(void *data, bool blocked)
{
acpi_status status;
u32 *cap = data;
status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
u32 cap = (unsigned long)data;
status = set_u32(!!blocked, cap);
if (ACPI_FAILURE(status))
return -ENODEV;
return 0;
}
static struct rfkill * acer_rfkill_register(struct device *dev,
enum rfkill_type type, char *name, u32 cap)
static const struct rfkill_ops acer_rfkill_ops = {
.set_block = acer_rfkill_set,
};
static struct rfkill *acer_rfkill_register(struct device *dev,
enum rfkill_type type,
char *name, u32 cap)
{
int err;
u32 state;
u32 *data;
struct rfkill *rfkill_dev;
rfkill_dev = rfkill_allocate(dev, type);
rfkill_dev = rfkill_alloc(name, dev, type,
&acer_rfkill_ops,
(void *)(unsigned long)cap);
if (!rfkill_dev)
return ERR_PTR(-ENOMEM);
rfkill_dev->name = name;
get_u32(&state, cap);
rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
RFKILL_STATE_SOFT_BLOCKED;
data = kzalloc(sizeof(u32), GFP_KERNEL);
if (!data) {
rfkill_free(rfkill_dev);
return ERR_PTR(-ENOMEM);
}
*data = cap;
rfkill_dev->data = data;
rfkill_dev->toggle_radio = acer_rfkill_set;
rfkill_set_sw_state(rfkill_dev, !state);
err = rfkill_register(rfkill_dev);
if (err) {
kfree(rfkill_dev->data);
rfkill_free(rfkill_dev);
rfkill_destroy(rfkill_dev);
return ERR_PTR(err);
}
return rfkill_dev;
@@ -1027,8 +1019,8 @@ static int acer_rfkill_init(struct device *dev)
RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
ACER_CAP_BLUETOOTH);
if (IS_ERR(bluetooth_rfkill)) {
kfree(wireless_rfkill->data);
rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
return PTR_ERR(bluetooth_rfkill);
}
}
@@ -1041,11 +1033,13 @@ static int acer_rfkill_init(struct device *dev)
static void acer_rfkill_exit(void)
{
cancel_delayed_work_sync(&acer_rfkill_work);
kfree(wireless_rfkill->data);
rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
if (has_cap(ACER_CAP_BLUETOOTH)) {
kfree(bluetooth_rfkill->data);
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
}
return;
}

View File

@@ -174,10 +174,11 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
result[3]: NVRAM format version number
*/
static int dell_rfkill_set(int radio, enum rfkill_state state)
static int dell_rfkill_set(void *data, bool blocked)
{
struct calling_interface_buffer buffer;
int disable = (state == RFKILL_STATE_UNBLOCKED) ? 0 : 1;
int disable = blocked ? 0 : 1;
unsigned long radio = (unsigned long)data;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
buffer.input[0] = (1 | (radio<<8) | (disable << 16));
@@ -186,56 +187,24 @@ static int dell_rfkill_set(int radio, enum rfkill_state state)
return 0;
}
static int dell_wifi_set(void *data, enum rfkill_state state)
{
return dell_rfkill_set(1, state);
}
static int dell_bluetooth_set(void *data, enum rfkill_state state)
{
return dell_rfkill_set(2, state);
}
static int dell_wwan_set(void *data, enum rfkill_state state)
{
return dell_rfkill_set(3, state);
}
static int dell_rfkill_get(int bit, enum rfkill_state *state)
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
struct calling_interface_buffer buffer;
int status;
int new_state = RFKILL_STATE_HARD_BLOCKED;
int bit = (unsigned long)data + 16;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];
if (status & (1<<16))
new_state = RFKILL_STATE_SOFT_BLOCKED;
if (status & (1<<bit))
*state = new_state;
else
*state = RFKILL_STATE_UNBLOCKED;
return 0;
if (status & BIT(bit))
rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
}
static int dell_wifi_get(void *data, enum rfkill_state *state)
{
return dell_rfkill_get(17, state);
}
static int dell_bluetooth_get(void *data, enum rfkill_state *state)
{
return dell_rfkill_get(18, state);
}
static int dell_wwan_get(void *data, enum rfkill_state *state)
{
return dell_rfkill_get(19, state);
}
static const struct rfkill_ops dell_rfkill_ops = {
.set_block = dell_rfkill_set,
.query = dell_rfkill_query,
};
static int dell_setup_rfkill(void)
{
@@ -248,36 +217,37 @@ static int dell_setup_rfkill(void)
status = buffer.output[1];
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WLAN);
if (!wifi_rfkill)
wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN,
&dell_rfkill_ops, (void *) 1);
if (!wifi_rfkill) {
ret = -ENOMEM;
goto err_wifi;
wifi_rfkill->name = "dell-wifi";
wifi_rfkill->toggle_radio = dell_wifi_set;
wifi_rfkill->get_state = dell_wifi_get;
}
ret = rfkill_register(wifi_rfkill);
if (ret)
goto err_wifi;
}
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH);
if (!bluetooth_rfkill)
bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL,
RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2);
if (!bluetooth_rfkill) {
ret = -ENOMEM;
goto err_bluetooth;
bluetooth_rfkill->name = "dell-bluetooth";
bluetooth_rfkill->toggle_radio = dell_bluetooth_set;
bluetooth_rfkill->get_state = dell_bluetooth_get;
}
ret = rfkill_register(bluetooth_rfkill);
if (ret)
goto err_bluetooth;
}
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN);
if (!wwan_rfkill)
wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN,
&dell_rfkill_ops, (void *) 3);
if (!wwan_rfkill) {
ret = -ENOMEM;
goto err_wwan;
wwan_rfkill->name = "dell-wwan";
wwan_rfkill->toggle_radio = dell_wwan_set;
wwan_rfkill->get_state = dell_wwan_get;
}
ret = rfkill_register(wwan_rfkill);
if (ret)
goto err_wwan;
@@ -285,22 +255,15 @@ static int dell_setup_rfkill(void)
return 0;
err_wwan:
if (wwan_rfkill)
rfkill_free(wwan_rfkill);
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
bluetooth_rfkill = NULL;
}
err_bluetooth:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill)
rfkill_free(bluetooth_rfkill);
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
wifi_rfkill = NULL;
}
err_wifi:
rfkill_unregister(bluetooth_rfkill);
err_bluetooth:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill)
rfkill_free(wifi_rfkill);
rfkill_unregister(wifi_rfkill);
err_wifi:
rfkill_destroy(wifi_rfkill);
return ret;
}

View File

@@ -299,39 +299,22 @@ static int update_bl_status(struct backlight_device *bd)
* Rfkill helpers
*/
static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
{
if (state == RFKILL_STATE_SOFT_BLOCKED)
return set_acpi(CM_ASL_WLAN, 0);
else
return set_acpi(CM_ASL_WLAN, 1);
}
static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
static bool eeepc_wlan_rfkill_blocked(void)
{
if (get_acpi(CM_ASL_WLAN) == 1)
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
return 0;
return false;
return true;
}
static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
static int eeepc_rfkill_set(void *data, bool blocked)
{
if (state == RFKILL_STATE_SOFT_BLOCKED)
return set_acpi(CM_ASL_BLUETOOTH, 0);
else
return set_acpi(CM_ASL_BLUETOOTH, 1);
unsigned long asl = (unsigned long)data;
return set_acpi(asl, !blocked);
}
static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
{
if (get_acpi(CM_ASL_BLUETOOTH) == 1)
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
return 0;
}
static const struct rfkill_ops eeepc_rfkill_ops = {
.set_block = eeepc_rfkill_set,
};
/*
* Sys helpers
@@ -531,9 +514,9 @@ static int notify_brn(void)
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
enum rfkill_state state;
struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1);
bool blocked;
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
@@ -543,9 +526,8 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
return;
}
eeepc_wlan_rfkill_state(ehotk->eeepc_wlan_rfkill, &state);
if (state == RFKILL_STATE_UNBLOCKED) {
blocked = eeepc_wlan_rfkill_blocked();
if (!blocked) {
dev = pci_get_slot(bus, 0);
if (dev) {
/* Device already present */
@@ -566,7 +548,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
}
}
rfkill_force_state(ehotk->eeepc_wlan_rfkill, state);
rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked);
}
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
@@ -684,26 +666,17 @@ static int eeepc_hotk_add(struct acpi_device *device)
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
if (get_acpi(CM_ASL_WLAN) != -1) {
ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
RFKILL_TYPE_WLAN);
ehotk->eeepc_wlan_rfkill = rfkill_alloc("eeepc-wlan",
&device->dev,
RFKILL_TYPE_WLAN,
&eeepc_rfkill_ops,
(void *)CM_ASL_WLAN);
if (!ehotk->eeepc_wlan_rfkill)
goto wlan_fail;
ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
if (get_acpi(CM_ASL_WLAN) == 1) {
ehotk->eeepc_wlan_rfkill->state =
RFKILL_STATE_UNBLOCKED;
rfkill_set_default(RFKILL_TYPE_WLAN,
RFKILL_STATE_UNBLOCKED);
} else {
ehotk->eeepc_wlan_rfkill->state =
RFKILL_STATE_SOFT_BLOCKED;
rfkill_set_default(RFKILL_TYPE_WLAN,
RFKILL_STATE_SOFT_BLOCKED);
}
rfkill_set_global_sw_state(RFKILL_TYPE_WLAN,
get_acpi(CM_ASL_WLAN) != 1);
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result)
goto wlan_fail;
@@ -711,28 +684,17 @@ static int eeepc_hotk_add(struct acpi_device *device)
if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
rfkill_alloc("eeepc-bluetooth",
&device->dev,
RFKILL_TYPE_BLUETOOTH,
&eeepc_rfkill_ops,
(void *)CM_ASL_BLUETOOTH);
if (!ehotk->eeepc_bluetooth_rfkill)
goto bluetooth_fail;
ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
ehotk->eeepc_bluetooth_rfkill->toggle_radio =
eeepc_bluetooth_rfkill_set;
ehotk->eeepc_bluetooth_rfkill->get_state =
eeepc_bluetooth_rfkill_state;
if (get_acpi(CM_ASL_BLUETOOTH) == 1) {
ehotk->eeepc_bluetooth_rfkill->state =
RFKILL_STATE_UNBLOCKED;
rfkill_set_default(RFKILL_TYPE_BLUETOOTH,
RFKILL_STATE_UNBLOCKED);
} else {
ehotk->eeepc_bluetooth_rfkill->state =
RFKILL_STATE_SOFT_BLOCKED;
rfkill_set_default(RFKILL_TYPE_BLUETOOTH,
RFKILL_STATE_SOFT_BLOCKED);
}
rfkill_set_global_sw_state(RFKILL_TYPE_BLUETOOTH,
get_acpi(CM_ASL_BLUETOOTH) != 1);
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result)
goto bluetooth_fail;
@@ -741,13 +703,10 @@ static int eeepc_hotk_add(struct acpi_device *device)
return 0;
bluetooth_fail:
if (ehotk->eeepc_bluetooth_rfkill)
rfkill_free(ehotk->eeepc_bluetooth_rfkill);
rfkill_destroy(ehotk->eeepc_bluetooth_rfkill);
rfkill_unregister(ehotk->eeepc_wlan_rfkill);
ehotk->eeepc_wlan_rfkill = NULL;
wlan_fail:
if (ehotk->eeepc_wlan_rfkill)
rfkill_free(ehotk->eeepc_wlan_rfkill);
rfkill_destroy(ehotk->eeepc_wlan_rfkill);
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
ehotk_fail:

View File

@@ -154,58 +154,46 @@ static int hp_wmi_dock_state(void)
return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
}
static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
static int hp_wmi_set_block(void *data, bool blocked)
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
unsigned long b = (unsigned long) data;
int query = BIT(b + 8) | ((!!blocked) << b);
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);
}
static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
}
static const struct rfkill_ops hp_wmi_rfkill_ops = {
.set_block = hp_wmi_set_block,
};
static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
}
static int hp_wmi_wifi_state(void)
static bool hp_wmi_wifi_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x100)
return RFKILL_STATE_UNBLOCKED;
return false;
else
return RFKILL_STATE_SOFT_BLOCKED;
return true;
}
static int hp_wmi_bluetooth_state(void)
static bool hp_wmi_bluetooth_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x10000)
return RFKILL_STATE_UNBLOCKED;
return false;
else
return RFKILL_STATE_SOFT_BLOCKED;
return true;
}
static int hp_wmi_wwan_state(void)
static bool hp_wmi_wwan_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x1000000)
return RFKILL_STATE_UNBLOCKED;
return false;
else
return RFKILL_STATE_SOFT_BLOCKED;
return true;
}
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
@@ -347,14 +335,14 @@ static void hp_wmi_notify(u32 value, void *context)
}
} else if (eventcode == 0x5) {
if (wifi_rfkill)
rfkill_force_state(wifi_rfkill,
hp_wmi_wifi_state());
rfkill_set_sw_state(wifi_rfkill,
hp_wmi_wifi_state());
if (bluetooth_rfkill)
rfkill_force_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
rfkill_set_sw_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
if (wwan_rfkill)
rfkill_force_state(wwan_rfkill,
hp_wmi_wwan_state());
rfkill_set_sw_state(wwan_rfkill,
hp_wmi_wwan_state());
} else
printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
eventcode);
@@ -430,31 +418,34 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
goto add_sysfs_error;
if (wireless & 0x1) {
wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
wifi_rfkill->name = "hp-wifi";
wifi_rfkill->state = hp_wmi_wifi_state();
wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
RFKILL_TYPE_WLAN,
&hp_wmi_rfkill_ops,
(void *) 0);
rfkill_set_sw_state(wifi_rfkill, hp_wmi_wifi_state());
err = rfkill_register(wifi_rfkill);
if (err)
goto add_sysfs_error;
goto register_wifi_error;
}
if (wireless & 0x2) {
bluetooth_rfkill = rfkill_allocate(&device->dev,
RFKILL_TYPE_BLUETOOTH);
bluetooth_rfkill->name = "hp-bluetooth";
bluetooth_rfkill->state = hp_wmi_bluetooth_state();
bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
RFKILL_TYPE_BLUETOOTH,
&hp_wmi_rfkill_ops,
(void *) 1);
rfkill_set_sw_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
err = rfkill_register(bluetooth_rfkill);
if (err)
goto register_bluetooth_error;
}
if (wireless & 0x4) {
wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
wwan_rfkill->name = "hp-wwan";
wwan_rfkill->state = hp_wmi_wwan_state();
wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
RFKILL_TYPE_WWAN,
&hp_wmi_rfkill_ops,
(void *) 2);
rfkill_set_sw_state(wwan_rfkill, hp_wmi_wwan_state());
err = rfkill_register(wwan_rfkill);
if (err)
goto register_wwan_err;
@@ -462,11 +453,15 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
return 0;
register_wwan_err:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
register_bluetooth_error:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
register_wifi_error:
rfkill_destroy(wifi_rfkill);
add_sysfs_error:
cleanup_sysfs(device);
return err;
@@ -476,12 +471,18 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
{
cleanup_sysfs(device);
if (wifi_rfkill)
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
if (bluetooth_rfkill)
rfkill_destroy(wifi_rfkill);
}
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
if (wwan_rfkill)
rfkill_destroy(wifi_rfkill);
}
if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
return 0;
}

View File

@@ -128,11 +128,11 @@ enum sony_nc_rfkill {
SONY_BLUETOOTH,
SONY_WWAN,
SONY_WIMAX,
SONY_RFKILL_MAX,
N_SONY_RFKILL,
};
static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
static void sony_nc_rfkill_update(void);
/*********** Input Devices ***********/
@@ -1051,147 +1051,98 @@ static void sony_nc_rfkill_cleanup(void)
{
int i;
for (i = 0; i < SONY_RFKILL_MAX; i++) {
if (sony_rfkill_devices[i])
for (i = 0; i < N_SONY_RFKILL; i++) {
if (sony_rfkill_devices[i]) {
rfkill_unregister(sony_rfkill_devices[i]);
rfkill_destroy(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)
static int sony_nc_rfkill_set(void *data, bool blocked)
{
int result;
int argument = sony_rfkill_address[(long) data] + 0x100;
if (state == RFKILL_STATE_UNBLOCKED)
if (!blocked)
argument |= 0xff0000;
return sony_call_snc_handle(0x124, argument, &result);
}
static int sony_nc_setup_wifi_rfkill(struct acpi_device *device)
static const struct rfkill_ops sony_rfkill_ops = {
.set_block = sony_nc_rfkill_set,
};
static int sony_nc_setup_rfkill(struct acpi_device *device,
enum sony_nc_rfkill nc_type)
{
int err = 0;
struct rfkill *sony_wifi_rfkill;
struct rfkill *rfk;
enum rfkill_type type;
const char *name;
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->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;
sony_nc_rfkill_set(sony_wifi_rfkill->data,
RFKILL_STATE_UNBLOCKED);
switch (nc_type) {
case SONY_WIFI:
type = RFKILL_TYPE_WLAN;
name = "sony-wifi";
break;
case SONY_BLUETOOTH:
type = RFKILL_TYPE_BLUETOOTH;
name = "sony-bluetooth";
break;
case SONY_WWAN:
type = RFKILL_TYPE_WWAN;
name = "sony-wwan";
break;
case SONY_WIMAX:
type = RFKILL_TYPE_WIMAX;
name = "sony-wimax";
break;
default:
return -EINVAL;
}
return err;
}
static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
{
int err = 0;
struct rfkill *sony_bluetooth_rfkill;
rfk = rfkill_alloc(name, &device->dev, type,
&sony_rfkill_ops, (void *)nc_type);
if (!rfk)
return -ENOMEM;
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->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;
sony_nc_rfkill_set(sony_bluetooth_rfkill->data,
RFKILL_STATE_UNBLOCKED);
}
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->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;
sony_nc_rfkill_set(sony_wwan_rfkill->data,
RFKILL_STATE_UNBLOCKED);
}
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->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;
sony_nc_rfkill_set(sony_wimax_rfkill->data,
RFKILL_STATE_UNBLOCKED);
err = rfkill_register(rfk);
if (err) {
rfkill_destroy(rfk);
return err;
}
sony_rfkill_devices[nc_type] = rfk;
sony_nc_rfkill_set((void *)nc_type, false);
return err;
}
static void sony_nc_rfkill_update()
{
int i;
enum rfkill_state state;
enum sony_nc_rfkill i;
int result;
bool hwblock;
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);
sony_call_snc_handle(0x124, 0x200, &result);
hwblock = !(result & 0x1);
for (i = 0; i < N_SONY_RFKILL; i++) {
int argument = sony_rfkill_address[i];
if (!sony_rfkill_devices[i])
continue;
if (hwblock) {
if (rfkill_set_hw_state(sony_rfkill_devices[i], true))
sony_nc_rfkill_set(sony_rfkill_devices[i],
true);
continue;
}
sony_call_snc_handle(0x124, argument, &result);
rfkill_set_states(sony_rfkill_devices[i],
!(result & 0xf), false);
}
}
@@ -1210,13 +1161,13 @@ static int sony_nc_rfkill_setup(struct acpi_device *device)
}
if (result & 0x1)
sony_nc_setup_wifi_rfkill(device);
sony_nc_setup_rfkill(device, SONY_WIFI);
if (result & 0x2)
sony_nc_setup_bluetooth_rfkill(device);
sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
if (result & 0x1c)
sony_nc_setup_wwan_rfkill(device);
sony_nc_setup_rfkill(device, SONY_WWAN);
if (result & 0x20)
sony_nc_setup_wimax_rfkill(device);
sony_nc_setup_rfkill(device, SONY_WIMAX);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,6 @@
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/input-polldev.h>
#include <asm/uaccess.h>
@@ -250,21 +249,15 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
struct toshiba_acpi_dev {
struct platform_device *p_dev;
struct rfkill *rfk_dev;
struct input_polled_dev *poll_dev;
struct rfkill *bt_rfk;
const char *bt_name;
const char *rfk_name;
bool last_rfk_state;
struct mutex mutex;
};
static struct toshiba_acpi_dev toshiba_acpi = {
.bt_name = "Toshiba Bluetooth",
.rfk_name = "Toshiba RFKill Switch",
.last_rfk_state = false,
};
/* Bluetooth rfkill handlers */
@@ -283,21 +276,6 @@ static u32 hci_get_bt_present(bool *present)
return hci_result;
}
static u32 hci_get_bt_on(bool *on)
{
u32 hci_result;
u32 value, value2;
value = 0;
value2 = 0x0001;
hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
if (hci_result == HCI_SUCCESS)
*on = (value & HCI_WIRELESS_BT_POWER) &&
(value & HCI_WIRELESS_BT_ATTACH);
return hci_result;
}
static u32 hci_get_radio_state(bool *radio_state)
{
u32 hci_result;
@@ -311,70 +289,67 @@ static u32 hci_get_radio_state(bool *radio_state)
return hci_result;
}
static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state)
static int bt_rfkill_set_block(void *data, bool blocked)
{
struct toshiba_acpi_dev *dev = data;
u32 result1, result2;
u32 value;
int err;
bool radio_state;
struct toshiba_acpi_dev *dev = data;
value = (state == RFKILL_STATE_UNBLOCKED);
if (hci_get_radio_state(&radio_state) != HCI_SUCCESS)
return -EFAULT;
switch (state) {
case RFKILL_STATE_UNBLOCKED:
if (!radio_state)
return -EPERM;
break;
case RFKILL_STATE_SOFT_BLOCKED:
break;
default:
return -EINVAL;
}
value = (blocked == false);
mutex_lock(&dev->mutex);
if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
err = -EBUSY;
goto out;
}
if (!radio_state) {
err = 0;
goto out;
}
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
mutex_unlock(&dev->mutex);
if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
return -EFAULT;
return 0;
err = -EBUSY;
else
err = 0;
out:
mutex_unlock(&dev->mutex);
return err;
}
static void bt_poll_rfkill(struct input_polled_dev *poll_dev)
static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
{
bool state_changed;
bool new_rfk_state;
bool value;
u32 hci_result;
struct toshiba_acpi_dev *dev = poll_dev->private;
struct toshiba_acpi_dev *dev = data;
mutex_lock(&dev->mutex);
hci_result = hci_get_radio_state(&value);
if (hci_result != HCI_SUCCESS)
return; /* Can't do anything useful */
if (hci_result != HCI_SUCCESS) {
/* Can't do anything useful */
mutex_unlock(&dev->mutex);
}
new_rfk_state = value;
mutex_lock(&dev->mutex);
state_changed = new_rfk_state != dev->last_rfk_state;
dev->last_rfk_state = new_rfk_state;
mutex_unlock(&dev->mutex);
if (unlikely(state_changed)) {
rfkill_force_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
input_report_switch(poll_dev->input, SW_RFKILL_ALL,
new_rfk_state);
input_sync(poll_dev->input);
}
if (rfkill_set_hw_state(rfkill, !new_rfk_state))
bt_rfkill_set_block(data, true);
}
static const struct rfkill_ops toshiba_rfk_ops = {
.set_block = bt_rfkill_set_block,
.poll = bt_rfkill_poll,
};
static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
static struct backlight_device *toshiba_backlight_device;
static int force_fan;
@@ -702,14 +677,11 @@ static struct backlight_ops toshiba_backlight_data = {
static void toshiba_acpi_exit(void)
{
if (toshiba_acpi.poll_dev) {
input_unregister_polled_device(toshiba_acpi.poll_dev);
input_free_polled_device(toshiba_acpi.poll_dev);
if (toshiba_acpi.bt_rfk) {
rfkill_unregister(toshiba_acpi.bt_rfk);
rfkill_destroy(toshiba_acpi.bt_rfk);
}
if (toshiba_acpi.rfk_dev)
rfkill_unregister(toshiba_acpi.rfk_dev);
if (toshiba_backlight_device)
backlight_device_unregister(toshiba_backlight_device);
@@ -728,8 +700,6 @@ static int __init toshiba_acpi_init(void)
acpi_status status = AE_OK;
u32 hci_result;
bool bt_present;
bool bt_on;
bool radio_on;
int ret = 0;
if (acpi_disabled)
@@ -793,60 +763,21 @@ static int __init toshiba_acpi_init(void)
/* Register rfkill switch for Bluetooth */
if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev,
RFKILL_TYPE_BLUETOOTH);
if (!toshiba_acpi.rfk_dev) {
toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name,
&toshiba_acpi.p_dev->dev,
RFKILL_TYPE_BLUETOOTH,
&toshiba_rfk_ops,
&toshiba_acpi);
if (!toshiba_acpi.bt_rfk) {
printk(MY_ERR "unable to allocate rfkill device\n");
toshiba_acpi_exit();
return -ENOMEM;
}
toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name;
toshiba_acpi.rfk_dev->toggle_radio = bt_rfkill_toggle_radio;
toshiba_acpi.rfk_dev->data = &toshiba_acpi;
if (hci_get_bt_on(&bt_on) == HCI_SUCCESS && bt_on) {
toshiba_acpi.rfk_dev->state = RFKILL_STATE_UNBLOCKED;
} else if (hci_get_radio_state(&radio_on) == HCI_SUCCESS &&
radio_on) {
toshiba_acpi.rfk_dev->state = RFKILL_STATE_SOFT_BLOCKED;
} else {
toshiba_acpi.rfk_dev->state = RFKILL_STATE_HARD_BLOCKED;
}
ret = rfkill_register(toshiba_acpi.rfk_dev);
ret = rfkill_register(toshiba_acpi.bt_rfk);
if (ret) {
printk(MY_ERR "unable to register rfkill device\n");
toshiba_acpi_exit();
return -ENOMEM;
}
/* Register input device for kill switch */
toshiba_acpi.poll_dev = input_allocate_polled_device();
if (!toshiba_acpi.poll_dev) {
printk(MY_ERR
"unable to allocate kill-switch input device\n");
toshiba_acpi_exit();
return -ENOMEM;
}
toshiba_acpi.poll_dev->private = &toshiba_acpi;
toshiba_acpi.poll_dev->poll = bt_poll_rfkill;
toshiba_acpi.poll_dev->poll_interval = 1000; /* msecs */
toshiba_acpi.poll_dev->input->name = toshiba_acpi.rfk_name;
toshiba_acpi.poll_dev->input->id.bustype = BUS_HOST;
/* Toshiba USB ID */
toshiba_acpi.poll_dev->input->id.vendor = 0x0930;
set_bit(EV_SW, toshiba_acpi.poll_dev->input->evbit);
set_bit(SW_RFKILL_ALL, toshiba_acpi.poll_dev->input->swbit);
input_report_switch(toshiba_acpi.poll_dev->input,
SW_RFKILL_ALL, TRUE);
input_sync(toshiba_acpi.poll_dev->input);
ret = input_register_polled_device(toshiba_acpi.poll_dev);
if (ret) {
printk(MY_ERR
"unable to register kill-switch input device\n");
rfkill_destroy(toshiba_acpi.bt_rfk);
toshiba_acpi_exit();
return ret;
}