Re: Adding rfkill support to thinkpad_acpi

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 5/21/07, Henrique de Moraes Holschuh <hmh@xxxxxxxxxx> wrote:
On Mon, 21 May 2007, Richard Hughes wrote:
> Has there been any work on implementing rfkill for thinkpad_acpi? If
> there is nobody working on this I am offering to add support into
> thinkpad_acpi this week.

Yes, I have been talking to the rfkill people, but we had some disagreements
on how to best to it.  I let it rest for a week or so to think better about
some issues they raised, and also in order not to waste too much bandwidth
from either side of the issue.

Basically, I want a "fetch data from the hardware to know what state it is
in for real" interface.  They want a "find a way to make sure the hardware
is doing what we told it to do" interface.

I might even find out that we are better off not using rfkill in
thinkpad-acpi itself, and letting the bluetooth and wwan drivers process it.
We shall see.  The thinkpad ACPI interface does a lot more than just
radio-kill, it actually kills power to the *devices* themselves, AFAIK
(removes them from the USB bus, causing hotunplug events).  Anyone with a
bluetooth and WWAN device, please speak up.

So far, what I got is that rfkill is all about handling "please disable
radios" system-wide events, and not about providing drivers with a standard
sysfs interface to enable/disable radio functionality.  At least for now.


RFkill is not only about "please disable radios", it _does_ provide
the standard sysfs interface to control the radios as well.

You know I got curious and looked at thinkpad_acpi driver and adding
rfkill support for bluetooth to it turned out to be pretty easy. What
was missing is export on rfkill_toggle_radio to accomodate needs of
thinkpad_acpi legacy sysfs and procfs interfaces otherwise the patch
is really tiny.

Sorry for attachment.

--
Dmitry
Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx>
---
 drivers/misc/thinkpad_acpi.c |  143 +++++++++++++++++++++++++++++++------------
 drivers/misc/thinkpad_acpi.h |    1 
 include/linux/rfkill.h       |    1 
 net/rfkill/rfkill.c          |   16 +++-
 4 files changed, 121 insertions(+), 40 deletions(-)

Index: linux/drivers/misc/thinkpad_acpi.c
===================================================================
--- linux.orig/drivers/misc/thinkpad_acpi.c
+++ linux/drivers/misc/thinkpad_acpi.c
@@ -1020,6 +1020,97 @@ static struct ibm_struct hotkey_driver_d
  * Bluetooth subdriver
  */
 
+struct rfkill *bluetooth_rfkill;
+static enum rfkill_state bluetooth_state;
+
+static int bluetooth_get_radiosw(void)
+{
+	int status;
+
+	if (!tp_features.bluetooth)
+		return -ENODEV;
+
+	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+		return -EIO;
+
+	return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
+}
+
+static int bluetooth_set_radiosw(int radio_on)
+{
+	int status;
+
+	if (!tp_features.bluetooth)
+		return -ENODEV;
+
+	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+		return -EIO;
+	if (radio_on)
+		status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+	else
+		status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
+	if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+		return -EIO;
+
+	return 0;
+}
+
+static int bluetooth_toggle_radio(void *data, enum rfkill_state state)
+{
+	int error;
+
+	if (bluetooth_state != state) {
+		error = bluetooth_set_radiosw(state == RFKILL_STATE_ON ? 1 : 0);
+		if (error)
+			return error;
+
+		bluetooth_state = state;
+	}
+
+	return 0;
+}
+
+static int bluetooth_register_rfkill(void)
+{
+	int retval;
+
+	bluetooth_rfkill = rfkill_allocate(&tpacpi_pdev->dev,
+					RFKILL_TYPE_BLUETOOTH);
+	if (!bluetooth_rfkill) {
+		printk(IBM_ERR "not enough memory for bluetooth RF switch\n");
+		return -ENOMEM;
+	}
+
+	retval = bluetooth_get_radiosw();
+	if (retval < 0) {
+		printk(IBM_ERR "failed to get bluettoth radio state, "
+			"asuming ON\n");
+		retval = 0;
+	}
+	bluetooth_state = retval ? RFKILL_STATE_ON : RFKILL_STATE_OFF;
+
+	bluetooth_rfkill->toggle_radio = bluetooth_toggle_radio;
+
+	retval = rfkill_register(bluetooth_rfkill);
+	if (retval) {
+		printk(IBM_ERR "failed to register bluetooth RF switch, "
+			"error %d\n", retval);
+		rfkill_free(bluetooth_rfkill);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void bluetooth_unregister_rfkill(void)
+{
+	if (bluetooth_rfkill) {
+		rfkill_unregister(bluetooth_rfkill);
+		rfkill_free(bluetooth_rfkill);
+		bluetooth_rfkill = NULL;
+	}
+}
+
 /* sysfs bluetooth enable ---------------------------------------------- */
 static ssize_t bluetooth_enable_show(struct device *dev,
 			   struct device_attribute *attr,
@@ -1044,7 +1135,8 @@ static ssize_t bluetooth_enable_store(st
 	if (parse_strtoul(buf, 1, &t))
 		return -EINVAL;
 
-	res = bluetooth_set_radiosw(t);
+	res = rfkill_toggle_radio(bluetooth_rfkill,
+			t ? RFKILL_STATE_ON : RFKILL_STATE_OFF);
 
 	return (res) ? res : count;
 }
@@ -1090,10 +1182,16 @@ static int __init bluetooth_init(struct 
 			dbg_printk(TPACPI_DBG_INIT,
 				   "bluetooth hardware not installed\n");
 		} else {
+			res = bluetooth_register_rfkill();
+			if (res)
+				return res;
+
 			res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
 					&bluetooth_attr_group);
-			if (res)
+			if (res) {
+				bluetooth_unregister_rfkill();
 				return res;
+			}
 		}
 	}
 
@@ -1103,39 +1201,8 @@ static int __init bluetooth_init(struct 
 static void bluetooth_exit(void)
 {
 	sysfs_remove_group(&tpacpi_pdev->dev.kobj,
-			&bluetooth_attr_group);
-}
-
-static int bluetooth_get_radiosw(void)
-{
-	int status;
-
-	if (!tp_features.bluetooth)
-		return -ENODEV;
-
-	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
-		return -EIO;
-
-	return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
-}
-
-static int bluetooth_set_radiosw(int radio_on)
-{
-	int status;
-
-	if (!tp_features.bluetooth)
-		return -ENODEV;
-
-	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
-		return -EIO;
-	if (radio_on)
-		status |= TP_ACPI_BLUETOOTH_RADIOSSW;
-	else
-		status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
-	if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
-		return -EIO;
-
-	return 0;
+			   &bluetooth_attr_group);
+	bluetooth_unregister_rfkill();
 }
 
 /* procfs -------------------------------------------------------------- */
@@ -1164,9 +1231,11 @@ static int bluetooth_write(char *buf)
 
 	while ((cmd = next_cmd(&buf))) {
 		if (strlencmp(cmd, "enable") == 0) {
-			bluetooth_set_radiosw(1);
+			rfkill_toggle_radio(bluetooth_rfkill,
+					RFKILL_STATE_ON);
 		} else if (strlencmp(cmd, "disable") == 0) {
-			bluetooth_set_radiosw(0);
+			rfkill_toggle_radio(bluetooth_rfkill,
+					RFKILL_STATE_OFF);
 		} else
 			return -EINVAL;
 	}
Index: linux/drivers/misc/thinkpad_acpi.h
===================================================================
--- linux.orig/drivers/misc/thinkpad_acpi.h
+++ linux/drivers/misc/thinkpad_acpi.h
@@ -39,6 +39,7 @@
 #include <linux/platform_device.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
+#include <linux/rfkill.h>
 #include <asm/uaccess.h>
 
 #include <linux/dmi.h>
Index: linux/include/linux/rfkill.h
===================================================================
--- linux.orig/include/linux/rfkill.h
+++ linux/include/linux/rfkill.h
@@ -84,6 +84,7 @@ void rfkill_free(struct rfkill *rfkill);
 int rfkill_register(struct rfkill *rfkill);
 void rfkill_unregister(struct rfkill *rfkill);
 
+int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state);
 void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
 
 #endif /* RFKILL_H */
Index: linux/net/rfkill/rfkill.c
===================================================================
--- linux.orig/net/rfkill/rfkill.c
+++ linux/net/rfkill/rfkill.c
@@ -37,8 +37,17 @@ static DEFINE_MUTEX(rfkill_mutex);
 
 static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX];
 
-static int rfkill_toggle_radio(struct rfkill *rfkill,
-				enum rfkill_state state)
+/**
+ * rfkill_toggle_radio - change state of a particluar RF switch
+ * @rfkill: switchi to be toggled
+ * @state: new state
+ *
+ * This function changes state of one RF switch in the system. Please
+ * note that this functio is exported for benefit of legacy interfaces,
+ * new drivers should rely on RFkills standard sysfs attributes to
+ * control their raqdio state.
+ */
+int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state)
 {
 	int retval;
 
@@ -55,6 +64,7 @@ static int rfkill_toggle_radio(struct rf
 	mutex_unlock(&rfkill->mutex);
 	return retval;
 }
+EXPORT_SYMBOL(rfkill_toggle_radio);
 
 /**
  * rfkill_switch_all - Toggle state of all switches of given type
@@ -296,7 +306,7 @@ struct rfkill *rfkill_allocate(struct de
 	struct device *dev;
 
 	rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL);
-	if (rfkill)
+	if (!rfkill)
 		return NULL;
 
 	mutex_init(&rfkill->mutex);
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
ibm-acpi-devel mailing list
ibm-acpi-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/ibm-acpi-devel

[Index of Archives]     [Linux ACPI]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Photo]     [Yosemite Photos]     [Yosemite Advice]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]

  Powered by Linux