| /* | |
| * Atheros CARL9170 driver | |
| * | |
| * LED handling | |
| * | |
| * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> | |
| * Copyright 2009, 2010, Christian Lamparer <chunkeey@googlemail.com> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; see the file COPYING. If not, see | |
| * http://www.gnu.org/licenses/. | |
| * | |
| * This file incorporates work covered by the following copyright and | |
| * permission notice: | |
| * Copyright (c) 2007-2008 Atheros Communications, Inc. | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| */ | |
| #include "carl9170.h" | |
| #include "cmd.h" | |
| int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state) | |
| { | |
| return carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_DATA, led_state); | |
| } | |
| int carl9170_led_init(struct ar9170 *ar) | |
| { | |
| int err; | |
| /* disable LEDs */ | |
| /* GPIO [0/1 mode: output, 2/3: input] */ | |
| err = carl9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3); | |
| if (err) | |
| goto out; | |
| /* GPIO 0/1 value: off */ | |
| err = carl9170_led_set_state(ar, 0); | |
| out: | |
| return err; | |
| } | |
| #ifdef CONFIG_CARL9170_LEDS | |
| static void carl9170_led_update(struct work_struct *work) | |
| { | |
| struct ar9170 *ar = container_of(work, struct ar9170, led_work.work); | |
| int i, tmp = 300, blink_delay = 1000; | |
| u32 led_val = 0; | |
| bool rerun = false; | |
| if (!IS_ACCEPTING_CMD(ar)) | |
| return; | |
| mutex_lock(&ar->mutex); | |
| for (i = 0; i < AR9170_NUM_LEDS; i++) { | |
| if (ar->leds[i].registered) { | |
| if (ar->leds[i].last_state || | |
| ar->leds[i].toggled) { | |
| if (ar->leds[i].toggled) | |
| tmp = 70 + 200 / (ar->leds[i].toggled); | |
| if (tmp < blink_delay) | |
| blink_delay = tmp; | |
| led_val |= 1 << i; | |
| ar->leds[i].toggled = 0; | |
| rerun = true; | |
| } | |
| } | |
| } | |
| carl9170_led_set_state(ar, led_val); | |
| mutex_unlock(&ar->mutex); | |
| if (!rerun) | |
| return; | |
| ieee80211_queue_delayed_work(ar->hw, | |
| &ar->led_work, | |
| msecs_to_jiffies(blink_delay)); | |
| } | |
| static void carl9170_led_set_brightness(struct led_classdev *led, | |
| enum led_brightness brightness) | |
| { | |
| struct carl9170_led *arl = container_of(led, struct carl9170_led, l); | |
| struct ar9170 *ar = arl->ar; | |
| if (!arl->registered) | |
| return; | |
| if (arl->last_state != !!brightness) { | |
| arl->toggled++; | |
| arl->last_state = !!brightness; | |
| } | |
| if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) | |
| ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10); | |
| } | |
| static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name, | |
| const char *trigger) | |
| { | |
| int err; | |
| snprintf(ar->leds[i].name, sizeof(ar->leds[i].name), | |
| "carl9170-%s::%s", wiphy_name(ar->hw->wiphy), name); | |
| ar->leds[i].ar = ar; | |
| ar->leds[i].l.name = ar->leds[i].name; | |
| ar->leds[i].l.brightness_set = carl9170_led_set_brightness; | |
| ar->leds[i].l.brightness = 0; | |
| ar->leds[i].l.default_trigger = trigger; | |
| err = led_classdev_register(wiphy_dev(ar->hw->wiphy), | |
| &ar->leds[i].l); | |
| if (err) { | |
| wiphy_err(ar->hw->wiphy, "failed to register %s LED (%d).\n", | |
| ar->leds[i].name, err); | |
| } else { | |
| ar->leds[i].registered = true; | |
| } | |
| return err; | |
| } | |
| void carl9170_led_unregister(struct ar9170 *ar) | |
| { | |
| int i; | |
| for (i = 0; i < AR9170_NUM_LEDS; i++) | |
| if (ar->leds[i].registered) { | |
| led_classdev_unregister(&ar->leds[i].l); | |
| ar->leds[i].registered = false; | |
| ar->leds[i].toggled = 0; | |
| } | |
| cancel_delayed_work_sync(&ar->led_work); | |
| } | |
| int carl9170_led_register(struct ar9170 *ar) | |
| { | |
| int err; | |
| INIT_DELAYED_WORK(&ar->led_work, carl9170_led_update); | |
| err = carl9170_led_register_led(ar, 0, "tx", | |
| ieee80211_get_tx_led_name(ar->hw)); | |
| if (err) | |
| goto fail; | |
| if (ar->features & CARL9170_ONE_LED) | |
| return 0; | |
| err = carl9170_led_register_led(ar, 1, "assoc", | |
| ieee80211_get_assoc_led_name(ar->hw)); | |
| if (err) | |
| goto fail; | |
| return 0; | |
| fail: | |
| carl9170_led_unregister(ar); | |
| return err; | |
| } | |
| #endif /* CONFIG_CARL9170_LEDS */ |