Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Rewrote rfkill (bluetooth and WWAN) subdriver. Added UWB support. Rip…

…ped out useless sysfs interface to bluetooth and friends (the standard rfkill interface is perfectly good enough). In theory, the driver should now work with kernel 2.6.31.
  • Loading branch information...
commit 96ed58024cdaeb3e3f3cb06c49f18afc2f2d9e82 1 parent 664a5b8
@tetromino authored
Showing with 221 additions and 236 deletions.
  1. +1 −0  README
  2. +220 −236 lenovo-sl-laptop.c
View
1  README
@@ -6,6 +6,7 @@ the thinkpad_acpi driver.
Works: hotkeys, bluetooth, the Lenovo Care LED, the fan
Experimental: backlight brightness, WWAN
+Not tested: UWB
Not implemented: hdaps accelerometer
NB: to make the Lenovo Care LED blink, make sure the LED timer
View
456 lenovo-sl-laptop.c
@@ -95,24 +95,29 @@ static int debug_ec;
static int control_backlight;
static int bluetooth_auto_enable = 1;
static int wwan_auto_enable = 1;
+static int uwb_auto_enable = 1;
module_param(debug_ec, bool, S_IRUGO);
MODULE_PARM_DESC(debug_ec,
"Present EC debugging interface in procfs. WARNING: writing to the "
"EC can hang your system and possibly damage your hardware.");
module_param(control_backlight, bool, S_IRUGO);
MODULE_PARM_DESC(control_backlight,
- "Control backlight brightness; can conflict with ACPI video driver");
+ "Control backlight brightness; can conflict with ACPI video driver.");
module_param_named(debug, dbg_level, uint, S_IRUGO);
MODULE_PARM_DESC(debug,
- "Set debug verbosity level (0 = nothing, 7 = everything)");
+ "Set debug verbosity level (0 = nothing, 7 = everything).");
module_param(bluetooth_auto_enable, bool, S_IRUGO);
MODULE_PARM_DESC(bluetooth_auto_enable,
"Automatically enable bluetooth (if supported by hardware) when the "
- "module is loaded");
+ "module is loaded.");
module_param(wwan_auto_enable, bool, S_IRUGO);
MODULE_PARM_DESC(wwan_auto_enable,
"Automatically enable WWAN (if supported by hardware) when the "
- "module is loaded");
+ "module is loaded.");
+module_param(uwb_auto_enable, bool, S_IRUGO);
+MODULE_PARM_DESC(wwan_auto_enable,
+ "Automatically enable UWB (if supported by hardware) when the "
+ "module is loaded.");
/* general */
@@ -186,33 +191,35 @@ static int lensl_acpi_int_func(acpi_handle handle, char *pathname, int *ret,
}
/*************************************************************************
- Bluetooth and WWAN - copied nearly verbatim from thinkpad_acpi.c
+ Bluetooth, WWAN, UWB
*************************************************************************/
-typedef enum {
- LENSL_BLUETOOTH = 0,
- LENSL_WWAN,
-} btwwan_type;
-
-enum {
- LENSL_RFK_BLUETOOTH_SW_ID = 0,
- LENSL_RFK_WWAN_SW_ID,
-};
-
enum {
- /* ACPI GBDC/SBDC and GWAN/SWAN bits */
- LENSL_BTWWAN_HWPRESENT = 0x01, /* Bluetooth/WWAN hw available */
- LENSL_BTWWAN_RADIOSSW = 0x02, /* Bluetooth/WWAN radio enabled */
- LENSL_BTWWAN_UNK = 0x04, /* unknown function */
+ /* ACPI GBDC/SBDC, GWAN/SWAN, GUWB/SUWB bits */
+ LENSL_RADIO_HWPRESENT = 0x01, /* hardware is available */
+ LENSL_RADIO_RADIOSSW = 0x02, /* radio is enabled */
+ LENSL_RADIO_RESUMECTRL = 0x04, /* state at resume: off/last state */
};
-/* btwwan_rfkill[0] is bluetooth rfkill, btwwan_rfkill[1] is WWAN rfkill etc */
-static struct rfkill *btwwan_rfkill[2];
-static int btwwan_present[2];
-static int btwwan_pretend_blocked[2];
-static char *btwwan_name[2] = {
- "bluetooth",
- "WWAN",
+typedef enum {
+ LENSL_BLUETOOTH = 0,
+ LENSL_WWAN,
+ LENSL_UWB,
+} lensl_radio_type;
+
+/* pretend_blocked indicates whether we pretend that the device is
+ hardware-blocked (used primarily to prevent the device from coming
+ online when the module is loaded) */
+struct lensl_radio {
+ lensl_radio_type type;
+ enum rfkill_type rfktype;
+ int present;
+ char *name;
+ char *rfkname;
+ struct rfkill *rfk;
+ int (*get_acpi)(int *);
+ int (*set_acpi)(int);
+ int *auto_enable;
};
static inline int get_wlsw(int *value)
@@ -230,13 +237,9 @@ static inline int get_gwan(int *value)
return lensl_acpi_int_func(hkey_handle, "GWAN", value, 0);
}
-static int get_gbdcwan(btwwan_type btwwan, int *value)
+static inline int get_guwb(int *value)
{
- if (btwwan == LENSL_BLUETOOTH)
- return get_gbdc(value);
- if (btwwan == LENSL_WWAN)
- return get_gwan(value);
- return 0;
+ return lensl_acpi_int_func(hkey_handle, "GUWB", value, 0);
}
static inline int set_sbdc(int value)
@@ -249,224 +252,184 @@ static inline int set_swan(int value)
return lensl_acpi_int_func(hkey_handle, "SWAN", NULL, 1, value);
}
-static int set_sbdcwan(btwwan_type btwwan, int value)
+static inline int set_suwb(int value)
{
- if (btwwan == LENSL_BLUETOOTH)
- return set_sbdc(value);
- if (btwwan == LENSL_WWAN)
- return set_swan(value);
- return 0;
+ return lensl_acpi_int_func(hkey_handle, "SUWB", NULL, 1, value);
}
-static int btwwan_get_radiosw(btwwan_type btwwan)
+static int lensl_radio_get(struct lensl_radio *radio, int *hw_blocked,
+ int *value)
{
- int value = 0;
+ int wlsw;
- if (!btwwan_present[btwwan])
+ *hw_blocked = 0;
+ if (!radio)
+ return -EINVAL;
+ if (!radio->present)
return -ENODEV;
-
- /* WLSW overrides bluetooth and WWAN in firmware/hardware;
- reflect that */
- if (btwwan_pretend_blocked[btwwan] || (!get_wlsw(&value) && !value))
- return RFKILL_STATE_HARD_BLOCKED;
-
- if (get_gbdcwan(btwwan, &value))
+ if (!get_wlsw(&wlsw) && !wlsw)
+ *hw_blocked = 1;
+ if (radio->get_acpi(value))
return -EIO;
-
- return ((value & LENSL_BTWWAN_RADIOSSW) != 0) ?
- RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
-}
-
-static void btwwan_update_rfk(btwwan_type btwwan)
-{
- int result;
-
- if (!btwwan_rfkill[btwwan])
- return;
-
- result = btwwan_get_radiosw(btwwan);
- if (result < 0)
- return;
- rfkill_force_state(btwwan_rfkill[btwwan], result);
+ return 0;
}
-static int btwwan_set_radiosw(btwwan_type btwwan, int radio_on, int update_rfk)
+static int lensl_radio_set_on(struct lensl_radio *radio, int *hw_blocked,
+ bool on)
{
- int value;
-
- if (!btwwan_present[btwwan])
- return -ENODEV;
-
- /* WLSW overrides bluetooth and WWAN in firmware/hardware,
- but there is no reason to risk weird behaviour. */
- if (get_wlsw(&value) && !value && radio_on)
- return -EPERM;
-
- if (get_gbdcwan(btwwan, &value))
- return -EIO;
- if (radio_on)
- value |= LENSL_BTWWAN_RADIOSSW;
+ int value, ret;
+ if ((ret = lensl_radio_get(radio, hw_blocked, &value)) < 0)
+ return ret;
+ /* WLSW overrides radio in firmware/hardware, but there is
+ no reason to risk weird behaviour. */
+ if (*hw_blocked)
+ return ret;
+ if (on)
+ value |= LENSL_RADIO_RADIOSSW;
else
- value &= ~LENSL_BTWWAN_RADIOSSW;
-
- if (set_sbdcwan(btwwan, value))
+ value &= ~LENSL_RADIO_RADIOSSW;
+ if (radio->set_acpi(value))
return -EIO;
-
- if (update_rfk)
- btwwan_update_rfk(btwwan);
-
return 0;
}
-/*************************************************************************
- Bluetooth and WWAN sysfs - copied nearly verbatim from thinkpad_acpi.c
- *************************************************************************/
+/* Bluetooth/WWAN/UWB rfkill interface */
-static ssize_t btwwan_enable_show(btwwan_type btwwan, struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int status;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,30)
- status = btwwan_get_radiosw(btwwan);
- if (status < 0)
- return status;
+static int lensl_radio_rfkill_get_state(void *data, enum rfkill_state *state)
+{
+ int ret, value, hw_blocked = 0;
+ ret = lensl_radio_get((struct lensl_radio *)data,
+ &hw_blocked, &value);
- return snprintf(buf, PAGE_SIZE, "%d\n",
- (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
-}
+ if (hw_blocked) {
+ *state = RFKILL_STATE_HARD_BLOCKED;
+ return 0;
+ }
-static ssize_t bluetooth_enable_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return btwwan_enable_show(LENSL_BLUETOOTH, dev, attr, buf);
-}
+ if (ret)
+ return ret;
-static ssize_t wwan_enable_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return btwwan_enable_show(LENSL_WWAN, dev, attr, buf);
+ if (value & LENSL_RADIO_RADIOSSW)
+ *state = RFKILL_STATE_UNBLOCKED;
+ else
+ *state = RFKILL_STATE_SOFT_BLOCKED;
+ return 0;
}
-static ssize_t btwwan_enable_store(btwwan_type btwwan, struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int lensl_radio_rfkill_toggle_radio(void *data, enum rfkill_state state)
{
- unsigned long t;
- int res;
+ int ret, value, hw_blocked = 0;
+ ret = lensl_radio_get((struct lensl_radio *)data,
+ &hw_blocked, &value);
- if (parse_strtoul(buf, 1, &t))
- return -EINVAL;
-
- res = btwwan_set_radiosw(btwwan, t, 1);
+ if (state == RFKILL_STATE_UNBLOCKED) {
+ if (hw_blocked)
+ return -EPERM;
+ if (ret)
+ return ret;
+ value = 1;
+ } else {
+ if (ret && !hw_blocked)
+ return ret;
+ value = 0;
+ }
- return (res) ? res : count;
-}
+ ret = lensl_radio_set_on((struct lensl_radio *)data,
+ &hw_blocked, value);
-static ssize_t bluetooth_enable_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- return btwwan_enable_store(LENSL_BLUETOOTH, dev, attr, buf, count);
+ if (hw_blocked)
+ return 0;
+ return ret;
}
-static ssize_t wwan_enable_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int lensl_radio_new_rfkill(struct lensl_radio *radio,
+ struct rfkill **rfk, bool sw_blocked,
+ bool hw_blocked)
{
- return btwwan_enable_store(LENSL_WWAN, dev, attr, buf, count);
-}
-
-static struct device_attribute dev_attr_bluetooth_enable =
- __ATTR(btwwan_enable[LENSL_BLUETOOTH], S_IWUSR | S_IRUGO,
- bluetooth_enable_show, bluetooth_enable_store);
+ int res;
-static struct device_attribute dev_attr_wwan_enable =
- __ATTR(btwwan_enable[LENSL_WAN], S_IWUSR | S_IRUGO,
- wwan_enable_show, wwan_enable_store);
+ *rfk = rfkill_allocate(&lensl_pdev->dev, radio->rfktype);
+ if (!*rfk) {
+ vdbg_printk(LENSL_ERR,
+ "Failed to allocate memory for rfkill class\n");
+ return -ENOMEM;
+ }
-static struct attribute *bluetooth_attributes[] = {
- &dev_attr_bluetooth_enable.attr,
- NULL
-};
+ (*rfk)->name = radio->rfkname;
+ (*rfk)->get_state = lensl_radio_rfkill_get_state;
+ (*rfk)->toggle_radio = lensl_radio_rfkill_toggle_radio;
+ (*rfk)->data = radio;
-static struct attribute *wwan_attributes[] = {
- &dev_attr_wwan_enable.attr,
- NULL
-};
+ if (hw_blocked)
+ (*rfk)->state = RFKILL_STATE_HARD_BLOCKED;
+ else if (sw_blocked)
+ (*rfk)->state = RFKILL_STATE_SOFT_BLOCKED;
+ else
+ (*rfk)->state = RFKILL_STATE_UNBLOCKED;
-static const struct attribute_group btwwan_attr_group[2] = {
- {
- .attrs = bluetooth_attributes,
- },
- {
- .attrs = wwan_attributes,
+ res = rfkill_register(*rfk);
+ if (res < 0) {
+ vdbg_printk(LENSL_ERR,
+ "Failed to register %s rfkill switch: %d\n",
+ radio->rfkname, res);
+ rfkill_free(*rfk);
+ *rfk = NULL;
+ return res;
}
-};
-static int btwwan_rfk_get(btwwan_type btwwan, void *data, enum rfkill_state *state)
-{
- int bts = btwwan_get_radiosw(btwwan);
-
- if (bts < 0)
- return bts;
-
- *state = bts;
return 0;
}
-static int bluetooth_rfk_get(void *data, enum rfkill_state *state)
-{
- return btwwan_rfk_get(LENSL_BLUETOOTH, data, state);
-}
-
-static int wwan_rfk_get(void *data, enum rfkill_state *state)
-{
- return btwwan_rfk_get(LENSL_WWAN, data, state);
-}
+#else
-static int bluetooth_rfk_set(void *data, enum rfkill_state state)
+static void lensl_radio_rfkill_query(struct rfkill *rfk, void *data)
{
- return btwwan_set_radiosw(LENSL_BLUETOOTH, (state == RFKILL_STATE_UNBLOCKED), 0);
+ int ret, value, hw_blocked = 0;
+ ret = lensl_radio_get((struct lensl_radio *)data,
+ &hw_blocked, &value);
+ rfkill_set_hw_state(rfk, hw_blocked);
}
-static int wwan_rfk_set(void *data, enum rfkill_state state)
+static int lensl_radio_rfkill_set_block(void *data, bool blocked)
{
- return btwwan_set_radiosw(LENSL_WWAN, (state == RFKILL_STATE_UNBLOCKED), 0);
+ int ret, hw_blocked = 0;
+ ret = lensl_radio_set_on((struct lensl_radio *)data,
+ &hw_blocked, blocked);
+ /* rfkill spec: return 0 on hard block */
+ if (hw_blocked)
+ return 0;
+ return ret;
}
-static int lensl_new_rfkill(const unsigned int id,
- struct rfkill **rfk,
- const enum rfkill_type rfktype,
- const char *name,
- int (*toggle_radio)(void *, enum rfkill_state),
- int (*get_state)(void *, enum rfkill_state *))
+static int lensl_radio_new_rfkill(struct lensl_radio *radio,
+ struct rfkill **rfk, bool sw_blocked,
+ bool hw_blocked)
{
int res;
- enum rfkill_state initial_state;
-
- *rfk = rfkill_allocate(&lensl_pdev->dev, rfktype);
+ struct rfkill_ops ops = {NULL,
+ lensl_radio_rfkill_query,
+ lensl_radio_rfkill_set_block,
+ };
+
+ *rfk = rfkill_alloc(radio->rfkname, &lensl_pdev->dev, radio->rfktype,
+ &ops, radio);
if (!*rfk) {
vdbg_printk(LENSL_ERR,
"Failed to allocate memory for rfkill class\n");
return -ENOMEM;
}
- (*rfk)->name = name;
- (*rfk)->get_state = get_state;
- (*rfk)->toggle_radio = toggle_radio;
-
- if (!get_state(NULL, &initial_state))
- (*rfk)->state = initial_state;
+ rfkill_set_hw_state(*rfk, hw_blocked);
+ rfkill_set_sw_state(*rfk, sw_blocked);
res = rfkill_register(*rfk);
if (res < 0) {
vdbg_printk(LENSL_ERR,
"Failed to register %s rfkill switch: %d\n",
- name, res);
- rfkill_free(*rfk);
+ radio->rfkname, res);
+ rfkill_destroy(*rfk);
*rfk = NULL;
return res;
}
@@ -474,61 +437,80 @@ static int lensl_new_rfkill(const unsigned int id,
return 0;
}
-static void btwwan_exit(btwwan_type btwwan)
-{
- if (btwwan_rfkill[btwwan])
- rfkill_unregister(btwwan_rfkill[btwwan]);
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,30) */
+
+/* Bluetooth/WWAN/UWB init and exit */
+
+static struct lensl_radio lensl_radios[3] = {
+ {
+ LENSL_BLUETOOTH,
+ RFKILL_TYPE_BLUETOOTH,
+ 0,
+ "bluetooth",
+ "lensl_bluetooth_sw",
+ NULL,
+ get_gbdc,
+ set_sbdc,
+ &bluetooth_auto_enable,
+ },
+ {
+ LENSL_WWAN,
+ RFKILL_TYPE_WWAN,
+ 0,
+ "WWAN",
+ "lensl_wwan_sw",
+ NULL,
+ get_gwan,
+ set_swan,
+ &wwan_auto_enable,
+ },
+ {
+ LENSL_UWB,
+ RFKILL_TYPE_UWB,
+ 0,
+ "UWB",
+ "lensl_uwb_sw",
+ NULL,
+ get_guwb,
+ set_suwb,
+ &uwb_auto_enable,
+ },
+};
- sysfs_remove_group(&lensl_pdev->dev.kobj,
- &btwwan_attr_group[btwwan]);
+static void radio_exit(lensl_radio_type type)
+{
+ if (lensl_radios[type].rfk)
+ rfkill_unregister(lensl_radios[type].rfk);
}
-static int btwwan_init(btwwan_type btwwan)
+static int radio_init(lensl_radio_type type)
{
- int value, res;
- btwwan_present[btwwan] = 0;
+ int value, res, hw_blocked = 0, sw_blocked = 1;
+
if (!hkey_handle)
return -ENODEV;
- if (get_gbdcwan(btwwan, &value))
+ lensl_radios[type].present = 1; /* need for lensl_radio_get */
+ res = lensl_radio_get(&lensl_radios[type], &hw_blocked, &value);
+ lensl_radios[type].present = 0;
+ if (res && !hw_blocked)
return -EIO;
- if (!(value & LENSL_BTWWAN_HWPRESENT))
+ if (!(value & LENSL_RADIO_HWPRESENT))
return -ENODEV;
- btwwan_present[btwwan] = 1;
+ lensl_radios[type].present = 1;
- res = sysfs_create_group(&lensl_pdev->dev.kobj,
- &btwwan_attr_group[btwwan]);
- if (res) {
- vdbg_printk(LENSL_ERR,
- "Failed to register %s sysfs group\n",
- btwwan_name[btwwan]);
- return res;
- }
+ if (*lensl_radios[type].auto_enable
+ && (value & LENSL_RADIO_RADIOSSW))
+ sw_blocked = 0;
- if (btwwan == LENSL_BLUETOOTH) {
- btwwan_pretend_blocked[btwwan] = !bluetooth_auto_enable;
- res = lensl_new_rfkill(LENSL_RFK_BLUETOOTH_SW_ID,
- &btwwan_rfkill[btwwan],
- RFKILL_TYPE_BLUETOOTH,
- "lensl_bluetooth_sw",
- bluetooth_rfk_set,
- bluetooth_rfk_get);
- } else if (btwwan == LENSL_WWAN) {
- btwwan_pretend_blocked[btwwan] = !wwan_auto_enable;
- res = lensl_new_rfkill(LENSL_RFK_WWAN_SW_ID,
- &btwwan_rfkill[btwwan],
- RFKILL_TYPE_WWAN,
- "lensl_wwan_sw",
- wwan_rfk_set,
- wwan_rfk_get);
- }
+ res = lensl_radio_new_rfkill(&lensl_radios[type], &lensl_radios[type].rfk,
+ sw_blocked, hw_blocked);
- btwwan_pretend_blocked[btwwan] = 0;
if (res) {
- btwwan_exit(btwwan);
+ radio_exit(type);
return res;
}
vdbg_printk(LENSL_DEBUG, "Initialized %s subdriver\n",
- btwwan_name[btwwan]);
+ lensl_radios[type].name);
return 0;
}
@@ -1392,8 +1374,9 @@ static int __init lenovo_sl_laptop_init(void)
if (ret)
return -ENODEV;
- btwwan_init(LENSL_BLUETOOTH);
- btwwan_init(LENSL_WWAN);
+ radio_init(LENSL_BLUETOOTH);
+ radio_init(LENSL_WWAN);
+ radio_init(LENSL_UWB);
if (control_backlight)
backlight_init();
@@ -1416,8 +1399,9 @@ static void __exit lenovo_sl_laptop_exit(void)
hkey_poll_stop();
led_exit();
backlight_exit();
- btwwan_exit(LENSL_WWAN);
- btwwan_exit(LENSL_BLUETOOTH);
+ radio_exit(LENSL_UWB);
+ radio_exit(LENSL_WWAN);
+ radio_exit(LENSL_BLUETOOTH);
hkey_inputdev_exit();
if (lensl_pdev)
platform_device_unregister(lensl_pdev);
Please sign in to comment.
Something went wrong with that request. Please try again.