Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[linux-2.6-microblaze.git] / net / rfkill / core.c
index 97101c5..68d6ef9 100644 (file)
@@ -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)