linux/drivers/net/wireless/p54/led.c
<<
>>
Prefs
   1/*
   2 * Common code for mac80211 Prism54 drivers
   3 *
   4 * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
   5 * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
   6 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
   7 *
   8 * Based on:
   9 * - the islsm (softmac prism54) driver, which is:
  10 *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
  11 * - stlc45xx driver
  12 *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License version 2 as
  16 * published by the Free Software Foundation.
  17 */
  18
  19#include <linux/init.h>
  20#include <linux/firmware.h>
  21#include <linux/etherdevice.h>
  22
  23#include <net/mac80211.h>
  24#ifdef CONFIG_P54_LEDS
  25#include <linux/leds.h>
  26#endif /* CONFIG_P54_LEDS */
  27
  28#include "p54.h"
  29#include "lmac.h"
  30
  31static void p54_update_leds(struct work_struct *work)
  32{
  33        struct p54_common *priv = container_of(work, struct p54_common,
  34                                               led_work.work);
  35        int err, i, tmp, blink_delay = 400;
  36        bool rerun = false;
  37
  38        /* Don't toggle the LED, when the device is down. */
  39        if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
  40                return ;
  41
  42        for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
  43                if (priv->leds[i].toggled) {
  44                        priv->softled_state |= BIT(i);
  45
  46                        tmp = 70 + 200 / (priv->leds[i].toggled);
  47                        if (tmp < blink_delay)
  48                                blink_delay = tmp;
  49
  50                        if (priv->leds[i].led_dev.brightness == LED_OFF)
  51                                rerun = true;
  52
  53                        priv->leds[i].toggled =
  54                                !!priv->leds[i].led_dev.brightness;
  55                } else
  56                        priv->softled_state &= ~BIT(i);
  57
  58        err = p54_set_leds(priv);
  59        if (err && net_ratelimit())
  60                printk(KERN_ERR "%s: failed to update LEDs (%d).\n",
  61                        wiphy_name(priv->hw->wiphy), err);
  62
  63        if (rerun)
  64                ieee80211_queue_delayed_work(priv->hw, &priv->led_work,
  65                        msecs_to_jiffies(blink_delay));
  66}
  67
  68static void p54_led_brightness_set(struct led_classdev *led_dev,
  69                                   enum led_brightness brightness)
  70{
  71        struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev,
  72                                               led_dev);
  73        struct ieee80211_hw *dev = led->hw_dev;
  74        struct p54_common *priv = dev->priv;
  75
  76        if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
  77                return ;
  78
  79        if ((brightness) && (led->registered)) {
  80                led->toggled++;
  81                ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10);
  82        }
  83}
  84
  85static int p54_register_led(struct p54_common *priv,
  86                            unsigned int led_index,
  87                            char *name, char *trigger)
  88{
  89        struct p54_led_dev *led = &priv->leds[led_index];
  90        int err;
  91
  92        if (led->registered)
  93                return -EEXIST;
  94
  95        snprintf(led->name, sizeof(led->name), "p54-%s::%s",
  96                 wiphy_name(priv->hw->wiphy), name);
  97        led->hw_dev = priv->hw;
  98        led->index = led_index;
  99        led->led_dev.name = led->name;
 100        led->led_dev.default_trigger = trigger;
 101        led->led_dev.brightness_set = p54_led_brightness_set;
 102
 103        err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev);
 104        if (err)
 105                printk(KERN_ERR "%s: Failed to register %s LED.\n",
 106                        wiphy_name(priv->hw->wiphy), name);
 107        else
 108                led->registered = 1;
 109
 110        return err;
 111}
 112
 113int p54_init_leds(struct p54_common *priv)
 114{
 115        int err;
 116
 117        /*
 118         * TODO:
 119         * Figure out if the EEPROM contains some hints about the number
 120         * of available/programmable LEDs of the device.
 121         */
 122
 123        INIT_DELAYED_WORK(&priv->led_work, p54_update_leds);
 124
 125        err = p54_register_led(priv, 0, "assoc",
 126                               ieee80211_get_assoc_led_name(priv->hw));
 127        if (err)
 128                return err;
 129
 130        err = p54_register_led(priv, 1, "tx",
 131                               ieee80211_get_tx_led_name(priv->hw));
 132        if (err)
 133                return err;
 134
 135        err = p54_register_led(priv, 2, "rx",
 136                               ieee80211_get_rx_led_name(priv->hw));
 137        if (err)
 138                return err;
 139
 140        err = p54_register_led(priv, 3, "radio",
 141                               ieee80211_get_radio_led_name(priv->hw));
 142        if (err)
 143                return err;
 144
 145        err = p54_set_leds(priv);
 146        return err;
 147}
 148
 149void p54_unregister_leds(struct p54_common *priv)
 150{
 151        int i;
 152
 153        for (i = 0; i < ARRAY_SIZE(priv->leds); i++) {
 154                if (priv->leds[i].registered) {
 155                        priv->leds[i].registered = false;
 156                        priv->leds[i].toggled = 0;
 157                        led_classdev_unregister(&priv->leds[i].led_dev);
 158                }
 159        }
 160
 161        cancel_delayed_work_sync(&priv->led_work);
 162}
 163