|  | /* | 
|  | * 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, | 
|  | 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 */ |