summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/toshiba_acpi.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-06-02 13:01:37 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-06-03 14:06:13 -0400
commit19d337dff95cbf76edd3ad95c0cee2732c3e1ec5 (patch)
tree33326eeb09cb9664cc8427a5dc7cd2b08b5a57c3 /drivers/platform/x86/toshiba_acpi.c
parent0f6399c4c525b518644a9b09f8d6fb125a418c4d (diff)
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>
Diffstat (limited to 'drivers/platform/x86/toshiba_acpi.c')
-rw-r--r--drivers/platform/x86/toshiba_acpi.c159
1 files changed, 45 insertions, 114 deletions
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 4345089f517..81d31ea507d 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -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);
+ value = (blocked == false);
- if (hci_get_radio_state(&radio_state) != HCI_SUCCESS)
- return -EFAULT;
+ mutex_lock(&dev->mutex);
+ if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
+ err = -EBUSY;
+ goto out;
+ }
- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
+ if (!radio_state) {
+ err = 0;
+ goto out;
}
- mutex_lock(&dev->mutex);
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;
}