Re: [PATCH 8/9] rfkill: Userspace control for airplane mode

From: Dan Williams
Date: Mon Feb 08 2016 - 11:11:36 EST


On Mon, 2016-02-08 at 10:41 -0500, JoÃo Paulo Rechi Vita wrote:
> Provide an interface for the airplane-mode indicator be controlled
> from
> userspace. User has to first acquire the control through
> RFKILL_OP_AIRPLANE_MODE_ACQUIRE and keep the fd open for the whole
> time
> it wants to be in control of the indicator. Closing the fd or using
> RFKILL_OP_AIRPLANE_MODE_RELEASE restores the default policy.
>
> To change state of the indicator, the RFKILL_OP_AIRPLANE_MODE_CHANGE
> operation is used, passing the value on "struct rfkill_event.soft".
> If
> the caller has not acquired the airplane-mode control beforehand, the
> operation fails.

I'd like to clarify a bit, so tell me if I'm correct or not. Using
RFKILL_OP_AIRPLANE_MODE_CHANGE does not actually change any device
state. It's just an indicator with no relationship to any of the
registered rfkill switches, right?

I wonder if setting RFKILL_OP_AIRPLANE_MODE_CHANGE(true) shouldn't also
softblock all switches, otherwise you can set airplane mode all day
long with RFKILL_OP_AIRPLANE_MODE_CHANGE and it doesn't actually enable
airplane mode at all?

Dan

> Signed-off-by: JoÃo Paulo Rechi Vita <jprvita@xxxxxxxxxxxx>
> ---
> Documentation/rfkill.txt | 10 ++++++++++
> include/uapi/linux/rfkill.h | 3 +++
> net/rfkill/core.c | 47
> ++++++++++++++++++++++++++++++++++++++++++---
> 3 files changed, 57 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
> index b13025a..aa6e014 100644
> --- a/Documentation/rfkill.txt
> +++ b/Documentation/rfkill.txt
> @@ -87,6 +87,7 @@ RFKill provides per-switch LED triggers, which can
> be used to drive LEDs
> according to the switch state (LED_FULL when blocked, LED_OFF
> otherwise).
> An airplane-mode indicator LED trigger is also available, which
> triggers
> LED_FULL when all radios known by RFKill are blocked, and LED_OFF
> otherwise.
> +The airplane-mode indicator LED trigger policy can be overridden by
> userspace.
>
>
> 5. Userspace support
> @@ -123,5 +124,14 @@ RFKILL_TYPE
> The contents of these variables corresponds to the "name", "state"
> and
> "type" sysfs files explained above.
>
> +Userspace can also override the default airplane-mode indicator
> policy through
> +/dev/rfkill. Control of the airplane mode indicator has to be
> acquired first,
> +using RFKILL_OP_AIRPLANE_MODE_ACQUIRE, and is only available for one
> userspace
> +application at a time. Closing the fd or using
> RFKILL_OP_AIRPLANE_MODE_RELEASE
> +reverts the airplane-mode indicator back to the default kernel
> policy and makes
> +it available for other applications to take control. Changes to the
> +airplane-mode indicator state can be made using
> RFKILL_OP_AIRPLANE_MODE_CHANGE,
> +passing the new value in the 'soft' field of 'struct rfkill_event'.
> +
>
> For further details consult Documentation/ABI/stable/sysfs-class
> -rfkill.
> diff --git a/include/uapi/linux/rfkill.h
> b/include/uapi/linux/rfkill.h
> index 2e00dce..9cb999b 100644
> --- a/include/uapi/linux/rfkill.h
> +++ b/include/uapi/linux/rfkill.h
> @@ -67,6 +67,9 @@ enum rfkill_operation {
> RFKILL_OP_DEL,
> RFKILL_OP_CHANGE,
> RFKILL_OP_CHANGE_ALL,
> + RFKILL_OP_AIRPLANE_MODE_ACQUIRE,
> + RFKILL_OP_AIRPLANE_MODE_RELEASE,
> + RFKILL_OP_AIRPLANE_MODE_CHANGE,
> };
>
> /**
> diff --git a/net/rfkill/core.c b/net/rfkill/core.c
> index fb11547..8067701 100644
> --- a/net/rfkill/core.c
> +++ b/net/rfkill/core.c
> @@ -89,6 +89,7 @@ struct rfkill_data {
> struct mutex mtx;
> wait_queue_head_t read_wait;
> bool input_handler;
> + bool is_apm_owner;
> };
>
>
> @@ -123,7 +124,7 @@ static struct {
> } rfkill_global_states[NUM_RFKILL_TYPES];
>
> static bool rfkill_epo_lock_active;
> -
> +static bool rfkill_apm_owned;
>
> #ifdef CONFIG_RFKILL_LEDS
> static struct led_trigger rfkill_apm_led_trigger;
> @@ -350,7 +351,8 @@ static void rfkill_update_global_state(enum
> rfkill_type type, bool blocked)
>
> for (i = 0; i < NUM_RFKILL_TYPES; i++)
> rfkill_global_states[i].cur = blocked;
> - rfkill_apm_led_trigger_event(blocked);
> + if (!rfkill_apm_owned)
> + rfkill_apm_led_trigger_event(blocked);
> }
>
> #ifdef CONFIG_RFKILL_INPUT
> @@ -1183,6 +1185,7 @@ static ssize_t rfkill_fop_read(struct file
> *file, char __user *buf,
> static ssize_t rfkill_fop_write(struct file *file, const char __user
> *buf,
> size_t count, loff_t *pos)
> {
> + struct rfkill_data *data = file->private_data;
> struct rfkill *rfkill;
> struct rfkill_event ev;
>
> @@ -1199,7 +1202,7 @@ static ssize_t rfkill_fop_write(struct file
> *file, const char __user *buf,
> if (copy_from_user(&ev, buf, count))
> return -EFAULT;
>
> - if (ev.op != RFKILL_OP_CHANGE && ev.op !=
> RFKILL_OP_CHANGE_ALL)
> + if (ev.op < RFKILL_OP_CHANGE)
> return -EINVAL;
>
> if (ev.type >= NUM_RFKILL_TYPES)
> @@ -1207,6 +1210,34 @@ static ssize_t rfkill_fop_write(struct file
> *file, const char __user *buf,
>
> mutex_lock(&rfkill_global_mutex);
>
> + if (ev.op == RFKILL_OP_AIRPLANE_MODE_ACQUIRE) {
> + if (rfkill_apm_owned && !data->is_apm_owner) {
> + count = -EACCES;
> + } else {
> + rfkill_apm_owned = true;
> + data->is_apm_owner = true;
> + }
> + }
> +
> + if (ev.op == RFKILL_OP_AIRPLANE_MODE_RELEASE) {
> + if (rfkill_apm_owned && !data->is_apm_owner) {
> + count = -EACCES;
> + } else {
> + bool state =
> rfkill_global_states[RFKILL_TYPE_ALL].cur;
> +
> + rfkill_apm_owned = false;
> + data->is_apm_owner = false;
> + rfkill_apm_led_trigger_event(state);
> + }
> + }
> +
> + if (ev.op == RFKILL_OP_AIRPLANE_MODE_CHANGE) {
> + if (rfkill_apm_owned && data->is_apm_owner)
> + rfkill_apm_led_trigger_event(ev.soft);
> + else
> + count = -EACCES;
> + }
> +
> if (ev.op == RFKILL_OP_CHANGE_ALL)
> rfkill_update_global_state(ev.type, ev.soft);
>
> @@ -1230,7 +1261,17 @@ static int rfkill_fop_release(struct inode
> *inode, struct file *file)
> struct rfkill_int_event *ev, *tmp;
>
> mutex_lock(&rfkill_global_mutex);
> +
> + if (data->is_apm_owner) {
> + bool state =
> rfkill_global_states[RFKILL_TYPE_ALL].cur;
> +
> + rfkill_apm_owned = false;
> + data->is_apm_owner = false;
> + rfkill_apm_led_trigger_event(state);
> + }
> +
> list_del(&data->list);
> +
> mutex_unlock(&rfkill_global_mutex);
>
> mutex_destroy(&data->mtx);