]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/rfkill/rfkill.c
sysdev: Convert the x86 mce tolerant sysdev attribute to generic attribute
[linux-2.6-omap-h63xx.git] / net / rfkill / rfkill.c
index a561e350a70a8d2f3a1efc17a3735f33837ac4a7..7a560b785097e2971aa916d73c2ea2b72107cb8d 100644 (file)
@@ -39,7 +39,7 @@ MODULE_LICENSE("GPL");
 static LIST_HEAD(rfkill_list); /* list of registered rf switches */
 static DEFINE_MUTEX(rfkill_mutex);
 
-static unsigned int rfkill_default_state = RFKILL_STATE_ON;
+static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED;
 module_param_named(default_state, rfkill_default_state, uint, 0444);
 MODULE_PARM_DESC(default_state,
                 "Default initial state for all radio types, 0 = radio off");
@@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill,
 
        if (!led->name)
                return;
-       if (state == RFKILL_STATE_OFF)
+       if (state != RFKILL_STATE_UNBLOCKED)
                led_trigger_event(led, LED_OFF);
        else
                led_trigger_event(led, LED_FULL);
@@ -128,6 +128,30 @@ static void update_rfkill_state(struct rfkill *rfkill)
        }
 }
 
+/**
+ * rfkill_toggle_radio - wrapper for toggle_radio hook
+ *
+ * @rfkill: the rfkill struct to use
+ * @force: calls toggle_radio even if cache says it is not needed,
+ *     and also makes sure notifications of the state will be
+ *     sent even if it didn't change
+ * @state: the new state to call toggle_radio() with
+ *
+ * Calls rfkill->toggle_radio, enforcing the API for toggle_radio
+ * calls and handling all the red tape such as issuing notifications
+ * if the call is successful.
+ *
+ * Note that @force cannot override a (possibly cached) state of
+ * RFKILL_STATE_HARD_BLOCKED.  Any device making use of
+ * RFKILL_STATE_HARD_BLOCKED implements either get_state() or
+ * rfkill_force_state(), so the cache either is bypassed or valid.
+ *
+ * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED
+ * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to
+ * give the driver a hint that it should double-BLOCK the transmitter.
+ *
+ * Caller must have aquired rfkill_mutex.
+ */
 static int rfkill_toggle_radio(struct rfkill *rfkill,
                                enum rfkill_state state,
                                int force)
@@ -141,9 +165,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill,
            !rfkill->get_state(rfkill->data, &newstate))
                rfkill->state = newstate;
 
+       switch (state) {
+       case RFKILL_STATE_HARD_BLOCKED:
+               /* typically happens when refreshing hardware state,
+                * such as on resume */
+               state = RFKILL_STATE_SOFT_BLOCKED;
+               break;
+       case RFKILL_STATE_UNBLOCKED:
+               /* force can't override this, only rfkill_force_state() can */
+               if (rfkill->state == RFKILL_STATE_HARD_BLOCKED)
+                       return -EPERM;
+               break;
+       case RFKILL_STATE_SOFT_BLOCKED:
+               /* nothing to do, we want to give drivers the hint to double
+                * BLOCK even a transmitter that is already in state
+                * RFKILL_STATE_HARD_BLOCKED */
+               break;
+       }
+
        if (force || state != rfkill->state) {
                retval = rfkill->toggle_radio(rfkill->data, state);
-               if (!retval)
+               /* never allow a HARD->SOFT downgrade! */
+               if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED)
                        rfkill->state = state;
        }
 
@@ -181,6 +224,24 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
 }
 EXPORT_SYMBOL(rfkill_switch_all);
 
+/**
+ * rfkill_epo - emergency power off all transmitters
+ *
+ * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring
+ * everything in its path but rfkill_mutex.
+ */
+void rfkill_epo(void)
+{
+       struct rfkill *rfkill;
+
+       mutex_lock(&rfkill_mutex);
+       list_for_each_entry(rfkill, &rfkill_list, node) {
+               rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
+       }
+       mutex_unlock(&rfkill_mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_epo);
+
 /**
  * rfkill_force_state - Force the internal rfkill radio state
  * @rfkill: pointer to the rfkill class to modify.
@@ -197,8 +258,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state)
 {
        enum rfkill_state oldstate;
 
-       if (state != RFKILL_STATE_OFF &&
-           state != RFKILL_STATE_ON)
+       if (state != RFKILL_STATE_SOFT_BLOCKED &&
+           state != RFKILL_STATE_UNBLOCKED &&
+           state != RFKILL_STATE_HARD_BLOCKED)
                return -EINVAL;
 
        mutex_lock(&rfkill->mutex);
@@ -224,34 +286,31 @@ static ssize_t rfkill_name_show(struct device *dev,
        return sprintf(buf, "%s\n", rfkill->name);
 }
 
-static ssize_t rfkill_type_show(struct device *dev,
-                               struct device_attribute *attr,
-                               char *buf)
+static const char *rfkill_get_type_str(enum rfkill_type type)
 {
-       struct rfkill *rfkill = to_rfkill(dev);
-       const char *type;
-
-       switch (rfkill->type) {
+       switch (type) {
        case RFKILL_TYPE_WLAN:
-               type = "wlan";
-               break;
+               return "wlan";
        case RFKILL_TYPE_BLUETOOTH:
-               type = "bluetooth";
-               break;
+               return "bluetooth";
        case RFKILL_TYPE_UWB:
-               type = "ultrawideband";
-               break;
+               return "ultrawideband";
        case RFKILL_TYPE_WIMAX:
-               type = "wimax";
-               break;
+               return "wimax";
        case RFKILL_TYPE_WWAN:
-               type = "wwan";
-               break;
+               return "wwan";
        default:
                BUG();
        }
+}
 
-       return sprintf(buf, "%s\n", type);
+static ssize_t rfkill_type_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct rfkill *rfkill = to_rfkill(dev);
+
+       return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type));
 }
 
 static ssize_t rfkill_state_show(struct device *dev,
@@ -275,11 +334,14 @@ static ssize_t rfkill_state_store(struct device *dev,
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
 
+       /* RFKILL_STATE_HARD_BLOCKED is illegal here... */
+       if (state != RFKILL_STATE_UNBLOCKED &&
+           state != RFKILL_STATE_SOFT_BLOCKED)
+               return -EINVAL;
+
        if (mutex_lock_interruptible(&rfkill->mutex))
                return -ERESTARTSYS;
-       error = rfkill_toggle_radio(rfkill,
-                       state ? RFKILL_STATE_ON : RFKILL_STATE_OFF,
-                       0);
+       error = rfkill_toggle_radio(rfkill, state, 0);
        mutex_unlock(&rfkill->mutex);
 
        return error ? error : count;
@@ -358,7 +420,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state)
                        update_rfkill_state(rfkill);
 
                        mutex_lock(&rfkill->mutex);
-                       rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF);
+                       rfkill->toggle_radio(rfkill->data,
+                                               RFKILL_STATE_SOFT_BLOCKED);
                        mutex_unlock(&rfkill->mutex);
                }
 
@@ -389,34 +452,71 @@ static int rfkill_resume(struct device *dev)
 #define rfkill_resume NULL
 #endif
 
+static int rfkill_blocking_uevent_notifier(struct notifier_block *nb,
+                                       unsigned long eventid,
+                                       void *data)
+{
+       struct rfkill *rfkill = (struct rfkill *)data;
+
+       switch (eventid) {
+       case RFKILL_STATE_CHANGED:
+               kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block rfkill_blocking_uevent_nb = {
+       .notifier_call  = rfkill_blocking_uevent_notifier,
+       .priority       = 0,
+};
+
+static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct rfkill *rfkill = to_rfkill(dev);
+       int error;
+
+       error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
+       if (error)
+               return error;
+       error = add_uevent_var(env, "RFKILL_TYPE=%s",
+                               rfkill_get_type_str(rfkill->type));
+       if (error)
+               return error;
+       error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state);
+       return error;
+}
+
 static struct class rfkill_class = {
        .name           = "rfkill",
        .dev_release    = rfkill_release,
        .dev_attrs      = rfkill_dev_attrs,
        .suspend        = rfkill_suspend,
        .resume         = rfkill_resume,
+       .dev_uevent     = rfkill_dev_uevent,
 };
 
 static int rfkill_add_switch(struct rfkill *rfkill)
 {
-       int error;
-
        mutex_lock(&rfkill_mutex);
 
-       error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0);
-       if (!error)
-               list_add_tail(&rfkill->node, &rfkill_list);
+       rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0);
+
+       list_add_tail(&rfkill->node, &rfkill_list);
 
        mutex_unlock(&rfkill_mutex);
 
-       return error;
+       return 0;
 }
 
 static void rfkill_remove_switch(struct rfkill *rfkill)
 {
        mutex_lock(&rfkill_mutex);
        list_del_init(&rfkill->node);
-       rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1);
+       rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
        mutex_unlock(&rfkill_mutex);
 }
 
@@ -556,8 +656,9 @@ static int __init rfkill_init(void)
        int error;
        int i;
 
-       if (rfkill_default_state != RFKILL_STATE_OFF &&
-           rfkill_default_state != RFKILL_STATE_ON)
+       /* RFKILL_STATE_HARD_BLOCKED is illegal here... */
+       if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED &&
+           rfkill_default_state != RFKILL_STATE_UNBLOCKED)
                return -EINVAL;
 
        for (i = 0; i < ARRAY_SIZE(rfkill_states); i++)
@@ -569,11 +670,14 @@ static int __init rfkill_init(void)
                return error;
        }
 
+       register_rfkill_notifier(&rfkill_blocking_uevent_nb);
+
        return 0;
 }
 
 static void __exit rfkill_exit(void)
 {
+       unregister_rfkill_notifier(&rfkill_blocking_uevent_nb);
        class_unregister(&rfkill_class);
 }