summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/rfkill.h24
-rw-r--r--include/uapi/linux/rfkill.h16
-rw-r--r--net/rfkill/core.c41
3 files changed, 72 insertions, 9 deletions
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
index 8ad2487a86d5..231e06b74b50 100644
--- a/include/linux/rfkill.h
+++ b/include/linux/rfkill.h
@@ -138,6 +138,17 @@ void rfkill_unregister(struct rfkill *rfkill);
void rfkill_destroy(struct rfkill *rfkill);
/**
+ * rfkill_set_hw_state_reason - Set the internal rfkill hardware block state
+ * with a reason
+ * @rfkill: pointer to the rfkill class to modify.
+ * @blocked: the current hardware block state to set
+ * @reason: one of &enum rfkill_hard_block_reasons
+ *
+ * Prefer to use rfkill_set_hw_state if you don't need any special reason.
+ */
+bool rfkill_set_hw_state_reason(struct rfkill *rfkill,
+ bool blocked, unsigned long reason);
+/**
* rfkill_set_hw_state - Set the internal rfkill hardware block state
* @rfkill: pointer to the rfkill class to modify.
* @blocked: the current hardware block state to set
@@ -156,7 +167,11 @@ void rfkill_destroy(struct rfkill *rfkill);
* should be blocked) so that drivers need not keep track of the soft
* block state -- which they might not be able to.
*/
-bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked);
+static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
+{
+ return rfkill_set_hw_state_reason(rfkill, blocked,
+ RFKILL_HARD_BLOCK_SIGNAL);
+}
/**
* rfkill_set_sw_state - Set the internal rfkill software block state
@@ -256,6 +271,13 @@ static inline void rfkill_destroy(struct rfkill *rfkill)
{
}
+static inline bool rfkill_set_hw_state_reason(struct rfkill *rfkill,
+ bool blocked,
+ unsigned long reason)
+{
+ return blocked;
+}
+
static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
{
return blocked;
diff --git a/include/uapi/linux/rfkill.h b/include/uapi/linux/rfkill.h
index 2e00dcebebd0..03e8af87b364 100644
--- a/include/uapi/linux/rfkill.h
+++ b/include/uapi/linux/rfkill.h
@@ -70,12 +70,24 @@ enum rfkill_operation {
};
/**
+ * enum rfkill_hard_block_reasons - hard block reasons
+ * @RFKILL_HARD_BLOCK_SIGNAL: the hardware rfkill signal is active
+ * @RFKILL_HARD_BLOCK_NOT_OWNER: the NIC is not owned by the host
+ */
+enum rfkill_hard_block_reasons {
+ RFKILL_HARD_BLOCK_SIGNAL = 1 << 0,
+ RFKILL_HARD_BLOCK_NOT_OWNER = 1 << 1,
+};
+
+/**
* struct rfkill_event - events for userspace on /dev/rfkill
* @idx: index of dev rfkill
* @type: type of the rfkill struct
* @op: operation code
* @hard: hard state (0/1)
* @soft: soft state (0/1)
+ * @hard_block_reasons: valid if hard is set. One or several reasons from
+ * &enum rfkill_hard_block_reasons.
*
* Structure used for userspace communication on /dev/rfkill,
* used for events from the kernel and control to the kernel.
@@ -84,7 +96,9 @@ struct rfkill_event {
__u32 idx;
__u8 type;
__u8 op;
- __u8 soft, hard;
+ __u8 soft;
+ __u8 hard;
+ __u8 hard_block_reasons;
} __attribute__((packed));
/*
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 97101c55763d..68d6ef9e59fc 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -40,6 +40,7 @@ struct rfkill {
enum rfkill_type type;
unsigned long state;
+ unsigned long hard_block_reasons;
u32 idx;
@@ -265,6 +266,7 @@ static void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill,
ev->hard = !!(rfkill->state & RFKILL_BLOCK_HW);
ev->soft = !!(rfkill->state & (RFKILL_BLOCK_SW |
RFKILL_BLOCK_SW_PREV));
+ ev->hard_block_reasons = rfkill->hard_block_reasons;
spin_unlock_irqrestore(&rfkill->lock, flags);
}
@@ -522,19 +524,29 @@ bool rfkill_get_global_sw_state(const enum rfkill_type type)
}
#endif
-bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
+bool rfkill_set_hw_state_reason(struct rfkill *rfkill,
+ bool blocked, unsigned long reason)
{
unsigned long flags;
bool ret, prev;
BUG_ON(!rfkill);
+ if (WARN(reason &
+ ~(RFKILL_HARD_BLOCK_SIGNAL | RFKILL_HARD_BLOCK_NOT_OWNER),
+ "hw_state reason not supported: 0x%lx", reason))
+ return blocked;
+
spin_lock_irqsave(&rfkill->lock, flags);
- prev = !!(rfkill->state & RFKILL_BLOCK_HW);
- if (blocked)
+ prev = !!(rfkill->hard_block_reasons & reason);
+ if (blocked) {
rfkill->state |= RFKILL_BLOCK_HW;
- else
- rfkill->state &= ~RFKILL_BLOCK_HW;
+ rfkill->hard_block_reasons |= reason;
+ } else {
+ rfkill->hard_block_reasons &= ~reason;
+ if (!rfkill->hard_block_reasons)
+ rfkill->state &= ~RFKILL_BLOCK_HW;
+ }
ret = !!(rfkill->state & RFKILL_BLOCK_ANY);
spin_unlock_irqrestore(&rfkill->lock, flags);
@@ -546,7 +558,7 @@ bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
return ret;
}
-EXPORT_SYMBOL(rfkill_set_hw_state);
+EXPORT_SYMBOL(rfkill_set_hw_state_reason);
static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
@@ -744,6 +756,16 @@ static ssize_t soft_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(soft);
+static ssize_t hard_block_reasons_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ return sprintf(buf, "0x%lx\n", rfkill->hard_block_reasons);
+}
+static DEVICE_ATTR_RO(hard_block_reasons);
+
static u8 user_state_from_blocked(unsigned long state)
{
if (state & RFKILL_BLOCK_HW)
@@ -796,6 +818,7 @@ static struct attribute *rfkill_dev_attrs[] = {
&dev_attr_state.attr,
&dev_attr_soft.attr,
&dev_attr_hard.attr,
+ &dev_attr_hard_block_reasons.attr,
NULL,
};
ATTRIBUTE_GROUPS(rfkill_dev);
@@ -811,6 +834,7 @@ static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct rfkill *rfkill = to_rfkill(dev);
unsigned long flags;
+ unsigned long reasons;
u32 state;
int error;
@@ -823,10 +847,13 @@ static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
return error;
spin_lock_irqsave(&rfkill->lock, flags);
state = rfkill->state;
+ reasons = rfkill->hard_block_reasons;
spin_unlock_irqrestore(&rfkill->lock, flags);
error = add_uevent_var(env, "RFKILL_STATE=%d",
user_state_from_blocked(state));
- return error;
+ if (error)
+ return error;
+ return add_uevent_var(env, "RFKILL_HW_BLOCK_REASON=0x%lx", reasons);
}
void rfkill_pause_polling(struct rfkill *rfkill)