led.c 4.0 KB
Newer Older
C
Christian Lamparter 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*
 * Common code for mac80211 Prism54 drivers
 *
 * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
 * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
 *
 * Based on:
 * - the islsm (softmac prism54) driver, which is:
 *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
 * - stlc45xx driver
 *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>

#include <net/mac80211.h>
#ifdef CONFIG_P54_LEDS
#include <linux/leds.h>
#endif /* CONFIG_P54_LEDS */

#include "p54.h"
#include "lmac.h"

static void p54_update_leds(struct work_struct *work)
{
	struct p54_common *priv = container_of(work, struct p54_common,
					       led_work.work);
	int err, i, tmp, blink_delay = 400;
	bool rerun = false;

	/* Don't toggle the LED, when the device is down. */
	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
		return ;

	for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
		if (priv->leds[i].toggled) {
			priv->softled_state |= BIT(i);

			tmp = 70 + 200 / (priv->leds[i].toggled);
			if (tmp < blink_delay)
				blink_delay = tmp;

			if (priv->leds[i].led_dev.brightness == LED_OFF)
				rerun = true;

			priv->leds[i].toggled =
				!!priv->leds[i].led_dev.brightness;
		} else
			priv->softled_state &= ~BIT(i);

	err = p54_set_leds(priv);
	if (err && net_ratelimit())
		printk(KERN_ERR "%s: failed to update LEDs (%d).\n",
			wiphy_name(priv->hw->wiphy), err);

	if (rerun)
64
		ieee80211_queue_delayed_work(priv->hw, &priv->led_work,
C
Christian Lamparter 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
			msecs_to_jiffies(blink_delay));
}

static void p54_led_brightness_set(struct led_classdev *led_dev,
				   enum led_brightness brightness)
{
	struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev,
					       led_dev);
	struct ieee80211_hw *dev = led->hw_dev;
	struct p54_common *priv = dev->priv;

	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
		return ;

	if ((brightness) && (led->registered)) {
		led->toggled++;
81
		ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10);
C
Christian Lamparter 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
	}
}

static int p54_register_led(struct p54_common *priv,
			    unsigned int led_index,
			    char *name, char *trigger)
{
	struct p54_led_dev *led = &priv->leds[led_index];
	int err;

	if (led->registered)
		return -EEXIST;

	snprintf(led->name, sizeof(led->name), "p54-%s::%s",
		 wiphy_name(priv->hw->wiphy), name);
	led->hw_dev = priv->hw;
	led->index = led_index;
	led->led_dev.name = led->name;
	led->led_dev.default_trigger = trigger;
	led->led_dev.brightness_set = p54_led_brightness_set;

	err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev);
	if (err)
		printk(KERN_ERR "%s: Failed to register %s LED.\n",
			wiphy_name(priv->hw->wiphy), name);
	else
		led->registered = 1;

	return err;
}

int p54_init_leds(struct p54_common *priv)
{
	int err;

	/*
	 * TODO:
	 * Figure out if the EEPROM contains some hints about the number
	 * of available/programmable LEDs of the device.
	 */

	INIT_DELAYED_WORK(&priv->led_work, p54_update_leds);

	err = p54_register_led(priv, 0, "assoc",
			       ieee80211_get_assoc_led_name(priv->hw));
	if (err)
		return err;

	err = p54_register_led(priv, 1, "tx",
			       ieee80211_get_tx_led_name(priv->hw));
	if (err)
		return err;

	err = p54_register_led(priv, 2, "rx",
			       ieee80211_get_rx_led_name(priv->hw));
	if (err)
		return err;

	err = p54_register_led(priv, 3, "radio",
			       ieee80211_get_radio_led_name(priv->hw));
	if (err)
		return err;

	err = p54_set_leds(priv);
	return err;
}

void p54_unregister_leds(struct p54_common *priv)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(priv->leds); i++) {
		if (priv->leds[i].registered) {
			priv->leds[i].registered = false;
			priv->leds[i].toggled = 0;
			led_classdev_unregister(&priv->leds[i].led_dev);
		}
	}

	cancel_delayed_work_sync(&priv->led_work);
}