11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
233aca94dSKalle Valo /*
333aca94dSKalle Valo 	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
433aca94dSKalle Valo 	<http://rt2x00.serialmonkey.com>
533aca94dSKalle Valo 
633aca94dSKalle Valo  */
733aca94dSKalle Valo 
833aca94dSKalle Valo /*
933aca94dSKalle Valo 	Module: rt2x00lib
1033aca94dSKalle Valo 	Abstract: rt2x00 led specific routines.
1133aca94dSKalle Valo  */
1233aca94dSKalle Valo 
1333aca94dSKalle Valo #include <linux/kernel.h>
1433aca94dSKalle Valo #include <linux/module.h>
1533aca94dSKalle Valo 
1633aca94dSKalle Valo #include "rt2x00.h"
1733aca94dSKalle Valo #include "rt2x00lib.h"
1833aca94dSKalle Valo 
rt2x00leds_led_quality(struct rt2x00_dev * rt2x00dev,int rssi)1933aca94dSKalle Valo void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi)
2033aca94dSKalle Valo {
2133aca94dSKalle Valo 	struct rt2x00_led *led = &rt2x00dev->led_qual;
2233aca94dSKalle Valo 	unsigned int brightness;
2333aca94dSKalle Valo 
2433aca94dSKalle Valo 	if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED))
2533aca94dSKalle Valo 		return;
2633aca94dSKalle Valo 
2733aca94dSKalle Valo 	/*
2833aca94dSKalle Valo 	 * Led handling requires a positive value for the rssi,
2933aca94dSKalle Valo 	 * to do that correctly we need to add the correction.
3033aca94dSKalle Valo 	 */
3133aca94dSKalle Valo 	rssi += rt2x00dev->rssi_offset;
3233aca94dSKalle Valo 
3333aca94dSKalle Valo 	/*
3433aca94dSKalle Valo 	 * Get the rssi level, this is used to convert the rssi
3533aca94dSKalle Valo 	 * to a LED value inside the range LED_OFF - LED_FULL.
3633aca94dSKalle Valo 	 */
3733aca94dSKalle Valo 	if (rssi <= 30)
3833aca94dSKalle Valo 		rssi = 0;
3933aca94dSKalle Valo 	else if (rssi <= 39)
4033aca94dSKalle Valo 		rssi = 1;
4133aca94dSKalle Valo 	else if (rssi <= 49)
4233aca94dSKalle Valo 		rssi = 2;
4333aca94dSKalle Valo 	else if (rssi <= 53)
4433aca94dSKalle Valo 		rssi = 3;
4533aca94dSKalle Valo 	else if (rssi <= 63)
4633aca94dSKalle Valo 		rssi = 4;
4733aca94dSKalle Valo 	else
4833aca94dSKalle Valo 		rssi = 5;
4933aca94dSKalle Valo 
5033aca94dSKalle Valo 	/*
5133aca94dSKalle Valo 	 * Note that we must _not_ send LED_OFF since the driver
5233aca94dSKalle Valo 	 * is going to calculate the value and might use it in a
5333aca94dSKalle Valo 	 * division.
5433aca94dSKalle Valo 	 */
5533aca94dSKalle Valo 	brightness = ((LED_FULL / 6) * rssi) + 1;
5633aca94dSKalle Valo 	if (brightness != led->led_dev.brightness) {
5733aca94dSKalle Valo 		led->led_dev.brightness_set(&led->led_dev, brightness);
5833aca94dSKalle Valo 		led->led_dev.brightness = brightness;
5933aca94dSKalle Valo 	}
6033aca94dSKalle Valo }
6133aca94dSKalle Valo 
rt2x00led_led_simple(struct rt2x00_led * led,bool enabled)6233aca94dSKalle Valo static void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled)
6333aca94dSKalle Valo {
6433aca94dSKalle Valo 	unsigned int brightness = enabled ? LED_FULL : LED_OFF;
6533aca94dSKalle Valo 
6633aca94dSKalle Valo 	if (!(led->flags & LED_REGISTERED))
6733aca94dSKalle Valo 		return;
6833aca94dSKalle Valo 
6933aca94dSKalle Valo 	led->led_dev.brightness_set(&led->led_dev, brightness);
7033aca94dSKalle Valo 	led->led_dev.brightness = brightness;
7133aca94dSKalle Valo }
7233aca94dSKalle Valo 
rt2x00led_led_activity(struct rt2x00_dev * rt2x00dev,bool enabled)7333aca94dSKalle Valo void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled)
7433aca94dSKalle Valo {
7533aca94dSKalle Valo 	if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY)
7633aca94dSKalle Valo 		rt2x00led_led_simple(&rt2x00dev->led_qual, enabled);
7733aca94dSKalle Valo }
7833aca94dSKalle Valo 
rt2x00leds_led_assoc(struct rt2x00_dev * rt2x00dev,bool enabled)7933aca94dSKalle Valo void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled)
8033aca94dSKalle Valo {
8133aca94dSKalle Valo 	if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC)
8233aca94dSKalle Valo 		rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled);
8333aca94dSKalle Valo }
8433aca94dSKalle Valo 
rt2x00leds_led_radio(struct rt2x00_dev * rt2x00dev,bool enabled)8533aca94dSKalle Valo void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled)
8633aca94dSKalle Valo {
8733aca94dSKalle Valo 	if (rt2x00dev->led_radio.type == LED_TYPE_RADIO)
8833aca94dSKalle Valo 		rt2x00led_led_simple(&rt2x00dev->led_radio, enabled);
8933aca94dSKalle Valo }
9033aca94dSKalle Valo 
rt2x00leds_register_led(struct rt2x00_dev * rt2x00dev,struct rt2x00_led * led,const char * name)9133aca94dSKalle Valo static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
9233aca94dSKalle Valo 				   struct rt2x00_led *led,
9333aca94dSKalle Valo 				   const char *name)
9433aca94dSKalle Valo {
9533aca94dSKalle Valo 	struct device *device = wiphy_dev(rt2x00dev->hw->wiphy);
9633aca94dSKalle Valo 	int retval;
9733aca94dSKalle Valo 
9833aca94dSKalle Valo 	led->led_dev.name = name;
9933aca94dSKalle Valo 	led->led_dev.brightness = LED_OFF;
10033aca94dSKalle Valo 
10133aca94dSKalle Valo 	retval = led_classdev_register(device, &led->led_dev);
10233aca94dSKalle Valo 	if (retval) {
10333aca94dSKalle Valo 		rt2x00_err(rt2x00dev, "Failed to register led handler\n");
10433aca94dSKalle Valo 		return retval;
10533aca94dSKalle Valo 	}
10633aca94dSKalle Valo 
10733aca94dSKalle Valo 	led->flags |= LED_REGISTERED;
10833aca94dSKalle Valo 
10933aca94dSKalle Valo 	return 0;
11033aca94dSKalle Valo }
11133aca94dSKalle Valo 
rt2x00leds_register(struct rt2x00_dev * rt2x00dev)11233aca94dSKalle Valo void rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
11333aca94dSKalle Valo {
11433aca94dSKalle Valo 	char name[36];
11533aca94dSKalle Valo 	int retval;
11633aca94dSKalle Valo 	unsigned long on_period;
11733aca94dSKalle Valo 	unsigned long off_period;
11833aca94dSKalle Valo 	const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy);
11933aca94dSKalle Valo 
12033aca94dSKalle Valo 	if (rt2x00dev->led_radio.flags & LED_INITIALIZED) {
12133aca94dSKalle Valo 		snprintf(name, sizeof(name), "%s-%s::radio",
12233aca94dSKalle Valo 			 rt2x00dev->ops->name, phy_name);
12333aca94dSKalle Valo 
12433aca94dSKalle Valo 		retval = rt2x00leds_register_led(rt2x00dev,
12533aca94dSKalle Valo 						 &rt2x00dev->led_radio,
12633aca94dSKalle Valo 						 name);
12733aca94dSKalle Valo 		if (retval)
12833aca94dSKalle Valo 			goto exit_fail;
12933aca94dSKalle Valo 	}
13033aca94dSKalle Valo 
13133aca94dSKalle Valo 	if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) {
13233aca94dSKalle Valo 		snprintf(name, sizeof(name), "%s-%s::assoc",
13333aca94dSKalle Valo 			 rt2x00dev->ops->name, phy_name);
13433aca94dSKalle Valo 
13533aca94dSKalle Valo 		retval = rt2x00leds_register_led(rt2x00dev,
13633aca94dSKalle Valo 						 &rt2x00dev->led_assoc,
13733aca94dSKalle Valo 						 name);
13833aca94dSKalle Valo 		if (retval)
13933aca94dSKalle Valo 			goto exit_fail;
14033aca94dSKalle Valo 	}
14133aca94dSKalle Valo 
14233aca94dSKalle Valo 	if (rt2x00dev->led_qual.flags & LED_INITIALIZED) {
14333aca94dSKalle Valo 		snprintf(name, sizeof(name), "%s-%s::quality",
14433aca94dSKalle Valo 			 rt2x00dev->ops->name, phy_name);
14533aca94dSKalle Valo 
14633aca94dSKalle Valo 		retval = rt2x00leds_register_led(rt2x00dev,
14733aca94dSKalle Valo 						 &rt2x00dev->led_qual,
14833aca94dSKalle Valo 						 name);
14933aca94dSKalle Valo 		if (retval)
15033aca94dSKalle Valo 			goto exit_fail;
15133aca94dSKalle Valo 	}
15233aca94dSKalle Valo 
15333aca94dSKalle Valo 	/*
15433aca94dSKalle Valo 	 * Initialize blink time to default value:
15533aca94dSKalle Valo 	 * On period: 70ms
15633aca94dSKalle Valo 	 * Off period: 30ms
15733aca94dSKalle Valo 	 */
15833aca94dSKalle Valo 	if (rt2x00dev->led_radio.led_dev.blink_set) {
15933aca94dSKalle Valo 		on_period = 70;
16033aca94dSKalle Valo 		off_period = 30;
16133aca94dSKalle Valo 		rt2x00dev->led_radio.led_dev.blink_set(
16233aca94dSKalle Valo 		    &rt2x00dev->led_radio.led_dev, &on_period, &off_period);
16333aca94dSKalle Valo 	}
16433aca94dSKalle Valo 
16533aca94dSKalle Valo 	return;
16633aca94dSKalle Valo 
16733aca94dSKalle Valo exit_fail:
16833aca94dSKalle Valo 	rt2x00leds_unregister(rt2x00dev);
16933aca94dSKalle Valo }
17033aca94dSKalle Valo 
rt2x00leds_unregister_led(struct rt2x00_led * led)17133aca94dSKalle Valo static void rt2x00leds_unregister_led(struct rt2x00_led *led)
17233aca94dSKalle Valo {
17333aca94dSKalle Valo 	led_classdev_unregister(&led->led_dev);
17433aca94dSKalle Valo 
17533aca94dSKalle Valo 	/*
17633aca94dSKalle Valo 	 * This might look weird, but when we are unregistering while
17733aca94dSKalle Valo 	 * suspended the led is already off, and since we haven't
17833aca94dSKalle Valo 	 * fully resumed yet, access to the device might not be
17933aca94dSKalle Valo 	 * possible yet.
18033aca94dSKalle Valo 	 */
18133aca94dSKalle Valo 	if (!(led->led_dev.flags & LED_SUSPENDED))
18233aca94dSKalle Valo 		led->led_dev.brightness_set(&led->led_dev, LED_OFF);
18333aca94dSKalle Valo 
18433aca94dSKalle Valo 	led->flags &= ~LED_REGISTERED;
18533aca94dSKalle Valo }
18633aca94dSKalle Valo 
rt2x00leds_unregister(struct rt2x00_dev * rt2x00dev)18733aca94dSKalle Valo void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev)
18833aca94dSKalle Valo {
18933aca94dSKalle Valo 	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
19033aca94dSKalle Valo 		rt2x00leds_unregister_led(&rt2x00dev->led_qual);
19133aca94dSKalle Valo 	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
19233aca94dSKalle Valo 		rt2x00leds_unregister_led(&rt2x00dev->led_assoc);
19333aca94dSKalle Valo 	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
19433aca94dSKalle Valo 		rt2x00leds_unregister_led(&rt2x00dev->led_radio);
19533aca94dSKalle Valo }
19633aca94dSKalle Valo 
rt2x00leds_suspend_led(struct rt2x00_led * led)19733aca94dSKalle Valo static inline void rt2x00leds_suspend_led(struct rt2x00_led *led)
19833aca94dSKalle Valo {
19933aca94dSKalle Valo 	led_classdev_suspend(&led->led_dev);
20033aca94dSKalle Valo 
20133aca94dSKalle Valo 	/* This shouldn't be needed, but just to be safe */
20233aca94dSKalle Valo 	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
20333aca94dSKalle Valo 	led->led_dev.brightness = LED_OFF;
20433aca94dSKalle Valo }
20533aca94dSKalle Valo 
rt2x00leds_suspend(struct rt2x00_dev * rt2x00dev)20633aca94dSKalle Valo void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev)
20733aca94dSKalle Valo {
20833aca94dSKalle Valo 	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
20933aca94dSKalle Valo 		rt2x00leds_suspend_led(&rt2x00dev->led_qual);
21033aca94dSKalle Valo 	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
21133aca94dSKalle Valo 		rt2x00leds_suspend_led(&rt2x00dev->led_assoc);
21233aca94dSKalle Valo 	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
21333aca94dSKalle Valo 		rt2x00leds_suspend_led(&rt2x00dev->led_radio);
21433aca94dSKalle Valo }
21533aca94dSKalle Valo 
rt2x00leds_resume_led(struct rt2x00_led * led)21633aca94dSKalle Valo static inline void rt2x00leds_resume_led(struct rt2x00_led *led)
21733aca94dSKalle Valo {
21833aca94dSKalle Valo 	led_classdev_resume(&led->led_dev);
21933aca94dSKalle Valo 
22033aca94dSKalle Valo 	/* Device might have enabled the LEDS during resume */
22133aca94dSKalle Valo 	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
22233aca94dSKalle Valo 	led->led_dev.brightness = LED_OFF;
22333aca94dSKalle Valo }
22433aca94dSKalle Valo 
rt2x00leds_resume(struct rt2x00_dev * rt2x00dev)22533aca94dSKalle Valo void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev)
22633aca94dSKalle Valo {
22733aca94dSKalle Valo 	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
22833aca94dSKalle Valo 		rt2x00leds_resume_led(&rt2x00dev->led_radio);
22933aca94dSKalle Valo 	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
23033aca94dSKalle Valo 		rt2x00leds_resume_led(&rt2x00dev->led_assoc);
23133aca94dSKalle Valo 	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
23233aca94dSKalle Valo 		rt2x00leds_resume_led(&rt2x00dev->led_qual);
23333aca94dSKalle Valo }
234