forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NET]: rfkill: add support for input key to control wireless radio
The RF kill patch that provides infrastructure for implementing switches controlling radio states on various network and other cards. [[email protected]: address review comments] [[email protected]: cleanups, build fixes] Signed-off-by: Ivo van Doorn <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
- Loading branch information
Showing
7 changed files
with
703 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#ifndef __RFKILL_H | ||
#define __RFKILL_H | ||
|
||
/* | ||
* Copyright (C) 2006 Ivo van Doorn | ||
* Copyright (C) 2007 Dmitry Torokhov | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the | ||
* Free Software Foundation, Inc., | ||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
*/ | ||
|
||
#include <linux/types.h> | ||
#include <linux/kernel.h> | ||
#include <linux/list.h> | ||
#include <linux/mutex.h> | ||
#include <linux/device.h> | ||
|
||
/** | ||
* enum rfkill_type - type of rfkill switch. | ||
* RFKILL_TYPE_WLAN: switch is no a Wireless network devices. | ||
* RFKILL_TYPE_BlUETOOTH: switch is on a bluetooth device. | ||
* RFKILL_TYPE_IRDA: switch is on an infrared devices. | ||
*/ | ||
enum rfkill_type { | ||
RFKILL_TYPE_WLAN = 0, | ||
RFKILL_TYPE_BLUETOOTH = 1, | ||
RFKILL_TYPE_IRDA = 2, | ||
RFKILL_TYPE_MAX = 3, | ||
}; | ||
|
||
enum rfkill_state { | ||
RFKILL_STATE_OFF = 0, | ||
RFKILL_STATE_ON = 1, | ||
}; | ||
|
||
/** | ||
* struct rfkill - rfkill control structure. | ||
* @name: Name of the switch. | ||
* @type: Radio type which the button controls, the value stored | ||
* here should be a value from enum rfkill_type. | ||
* @state: State of the switch (on/off). | ||
* @user_claim: Set when the switch is controlled exlusively by userspace. | ||
* @mutex: Guards switch state transitions | ||
* @data: Pointer to the RF button drivers private data which will be | ||
* passed along when toggling radio state. | ||
* @toggle_radio(): Mandatory handler to control state of the radio. | ||
* @dev: Device structure integrating the switch into device tree. | ||
* @node: Used to place switch into list of all switches known to the | ||
* the system. | ||
* | ||
* This structure represents a RF switch located on a network device. | ||
*/ | ||
struct rfkill { | ||
char *name; | ||
enum rfkill_type type; | ||
|
||
enum rfkill_state state; | ||
bool user_claim; | ||
|
||
struct mutex mutex; | ||
|
||
void *data; | ||
int (*toggle_radio)(void *data, enum rfkill_state state); | ||
|
||
struct device dev; | ||
struct list_head node; | ||
}; | ||
#define to_rfkill(d) container_of(d, struct rfkill, dev) | ||
|
||
struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type); | ||
void rfkill_free(struct rfkill *rfkill); | ||
int rfkill_register(struct rfkill *rfkill); | ||
void rfkill_unregister(struct rfkill *rfkill); | ||
|
||
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); | ||
|
||
#endif /* RFKILL_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -225,6 +225,8 @@ source "net/ieee80211/Kconfig" | |
|
||
endmenu | ||
|
||
source "net/rfkill/Kconfig" | ||
|
||
endif # if NET | ||
endmenu # Networking | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# | ||
# RF switch subsystem configuration | ||
# | ||
menuconfig RFKILL | ||
tristate "RF switch subsystem support" | ||
help | ||
Say Y here if you want to have control over RF switches | ||
found on many WiFi, Bluetooth and IRDA cards. | ||
|
||
To compile this driver as a module, choose M here: the | ||
module will be called rfkill. | ||
|
||
config RFKILL_INPUT | ||
tristate "Input layer to RF switch connector" | ||
depends on RFKILL && INPUT | ||
help | ||
Say Y here if you want kernel automatically toggle state | ||
of RF switches on and off when user presses appropriate | ||
button or a key on the keyboard. Without this module you | ||
need a some kind of userspace application to control | ||
state of the switches. | ||
|
||
To compile this driver as a module, choose M here: the | ||
module will be called rfkill-input. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# | ||
# Makefile for the RF switch subsystem. | ||
# | ||
|
||
obj-$(CONFIG_RFKILL) += rfkill.o | ||
obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/* | ||
* Input layer to RF Kill interface connector | ||
* | ||
* Copyright (c) 2007 Dmitry Torokhov | ||
*/ | ||
|
||
/* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 as published | ||
* by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/input.h> | ||
#include <linux/slab.h> | ||
#include <linux/workqueue.h> | ||
#include <linux/init.h> | ||
#include <linux/rfkill.h> | ||
|
||
MODULE_AUTHOR("Dmitry Torokhov <[email protected]>"); | ||
MODULE_DESCRIPTION("Input layer to RF switch connector"); | ||
MODULE_LICENSE("GPL"); | ||
|
||
struct rfkill_task { | ||
struct work_struct work; | ||
enum rfkill_type type; | ||
struct mutex mutex; /* ensures that task is serialized */ | ||
spinlock_t lock; /* for accessing last and desired state */ | ||
unsigned long last; /* last schedule */ | ||
enum rfkill_state desired_state; /* on/off */ | ||
enum rfkill_state current_state; /* on/off */ | ||
}; | ||
|
||
static void rfkill_task_handler(struct work_struct *work) | ||
{ | ||
struct rfkill_task *task = container_of(work, struct rfkill_task, work); | ||
enum rfkill_state state; | ||
|
||
mutex_lock(&task->mutex); | ||
|
||
/* | ||
* Use temp variable to fetch desired state to keep it | ||
* consistent even if rfkill_schedule_toggle() runs in | ||
* another thread or interrupts us. | ||
*/ | ||
state = task->desired_state; | ||
|
||
if (state != task->current_state) { | ||
rfkill_switch_all(task->type, state); | ||
task->current_state = state; | ||
} | ||
|
||
mutex_unlock(&task->mutex); | ||
} | ||
|
||
static void rfkill_schedule_toggle(struct rfkill_task *task) | ||
{ | ||
unsigned int flags; | ||
|
||
spin_lock_irqsave(&task->lock, flags); | ||
|
||
if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { | ||
task->desired_state = !task->desired_state; | ||
task->last = jiffies; | ||
schedule_work(&task->work); | ||
} | ||
|
||
spin_unlock_irqrestore(&task->lock, flags); | ||
} | ||
|
||
#define DEFINE_RFKILL_TASK(n, t) \ | ||
struct rfkill_task n = { \ | ||
.work = __WORK_INITIALIZER(n.work, \ | ||
rfkill_task_handler), \ | ||
.type = t, \ | ||
.mutex = __MUTEX_INITIALIZER(n.mutex), \ | ||
.lock = __SPIN_LOCK_UNLOCKED(n.lock), \ | ||
.desired_state = RFKILL_STATE_ON, \ | ||
.current_state = RFKILL_STATE_ON, \ | ||
} | ||
|
||
static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); | ||
static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); | ||
|
||
static void rfkill_event(struct input_handle *handle, unsigned int type, | ||
unsigned int code, int down) | ||
{ | ||
if (type == EV_KEY && down == 1) { | ||
switch (code) { | ||
case KEY_WLAN: | ||
rfkill_schedule_toggle(&rfkill_wlan); | ||
break; | ||
case KEY_BLUETOOTH: | ||
rfkill_schedule_toggle(&rfkill_bt); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
} | ||
|
||
static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, | ||
const struct input_device_id *id) | ||
{ | ||
struct input_handle *handle; | ||
int error; | ||
|
||
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); | ||
if (!handle) | ||
return -ENOMEM; | ||
|
||
handle->dev = dev; | ||
handle->handler = handler; | ||
handle->name = "rfkill"; | ||
|
||
error = input_register_handle(handle); | ||
if (error) | ||
goto err_free_handle; | ||
|
||
error = input_open_device(handle); | ||
if (error) | ||
goto err_unregister_handle; | ||
|
||
return 0; | ||
|
||
err_unregister_handle: | ||
input_unregister_handle(handle); | ||
err_free_handle: | ||
kfree(handle); | ||
return error; | ||
} | ||
|
||
static void rfkill_disconnect(struct input_handle *handle) | ||
{ | ||
input_close_device(handle); | ||
input_unregister_handle(handle); | ||
kfree(handle); | ||
} | ||
|
||
static const struct input_device_id rfkill_ids[] = { | ||
{ | ||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | ||
.evbit = { BIT(EV_KEY) }, | ||
.keybit = { [LONG(KEY_WLAN)] = BIT(KEY_WLAN) }, | ||
}, | ||
{ | ||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | ||
.evbit = { BIT(EV_KEY) }, | ||
.keybit = { [LONG(KEY_BLUETOOTH)] = BIT(KEY_BLUETOOTH) }, | ||
}, | ||
{ } | ||
}; | ||
|
||
static struct input_handler rfkill_handler = { | ||
.event = rfkill_event, | ||
.connect = rfkill_connect, | ||
.disconnect = rfkill_disconnect, | ||
.name = "rfkill", | ||
.id_table = rfkill_ids, | ||
}; | ||
|
||
static int __init rfkill_handler_init(void) | ||
{ | ||
return input_register_handler(&rfkill_handler); | ||
} | ||
|
||
static void __exit rfkill_handler_exit(void) | ||
{ | ||
input_unregister_handler(&rfkill_handler); | ||
flush_scheduled_work(); | ||
} | ||
|
||
module_init(rfkill_handler_init); | ||
module_exit(rfkill_handler_exit); |
Oops, something went wrong.