| 
									
										
										
										
											2010-09-06 00:48:55 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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)) | 
					
						
							| 
									
										
										
										
											2011-07-13 21:38:18 -04:00
										 |  |  | 		ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10); | 
					
						
							| 
									
										
										
										
											2010-09-06 00:48:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 */
 |