162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
462306a36Sopenharmony_ci	<http://rt2x00.serialmonkey.com>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci	Module: rt2x00lib
1062306a36Sopenharmony_ci	Abstract: rt2x00 led specific routines.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "rt2x00.h"
1762306a36Sopenharmony_ci#include "rt2x00lib.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_civoid rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct rt2x00_led *led = &rt2x00dev->led_qual;
2262306a36Sopenharmony_ci	unsigned int brightness;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED))
2562306a36Sopenharmony_ci		return;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/*
2862306a36Sopenharmony_ci	 * Led handling requires a positive value for the rssi,
2962306a36Sopenharmony_ci	 * to do that correctly we need to add the correction.
3062306a36Sopenharmony_ci	 */
3162306a36Sopenharmony_ci	rssi += rt2x00dev->rssi_offset;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/*
3462306a36Sopenharmony_ci	 * Get the rssi level, this is used to convert the rssi
3562306a36Sopenharmony_ci	 * to a LED value inside the range LED_OFF - LED_FULL.
3662306a36Sopenharmony_ci	 */
3762306a36Sopenharmony_ci	if (rssi <= 30)
3862306a36Sopenharmony_ci		rssi = 0;
3962306a36Sopenharmony_ci	else if (rssi <= 39)
4062306a36Sopenharmony_ci		rssi = 1;
4162306a36Sopenharmony_ci	else if (rssi <= 49)
4262306a36Sopenharmony_ci		rssi = 2;
4362306a36Sopenharmony_ci	else if (rssi <= 53)
4462306a36Sopenharmony_ci		rssi = 3;
4562306a36Sopenharmony_ci	else if (rssi <= 63)
4662306a36Sopenharmony_ci		rssi = 4;
4762306a36Sopenharmony_ci	else
4862306a36Sopenharmony_ci		rssi = 5;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/*
5162306a36Sopenharmony_ci	 * Note that we must _not_ send LED_OFF since the driver
5262306a36Sopenharmony_ci	 * is going to calculate the value and might use it in a
5362306a36Sopenharmony_ci	 * division.
5462306a36Sopenharmony_ci	 */
5562306a36Sopenharmony_ci	brightness = ((LED_FULL / 6) * rssi) + 1;
5662306a36Sopenharmony_ci	if (brightness != led->led_dev.brightness) {
5762306a36Sopenharmony_ci		led->led_dev.brightness_set(&led->led_dev, brightness);
5862306a36Sopenharmony_ci		led->led_dev.brightness = brightness;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	unsigned int brightness = enabled ? LED_FULL : LED_OFF;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (!(led->flags & LED_REGISTERED))
6762306a36Sopenharmony_ci		return;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	led->led_dev.brightness_set(&led->led_dev, brightness);
7062306a36Sopenharmony_ci	led->led_dev.brightness = brightness;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_civoid rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY)
7662306a36Sopenharmony_ci		rt2x00led_led_simple(&rt2x00dev->led_qual, enabled);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC)
8262306a36Sopenharmony_ci		rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_civoid rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (rt2x00dev->led_radio.type == LED_TYPE_RADIO)
8862306a36Sopenharmony_ci		rt2x00led_led_simple(&rt2x00dev->led_radio, enabled);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
9262306a36Sopenharmony_ci				   struct rt2x00_led *led,
9362306a36Sopenharmony_ci				   const char *name)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct device *device = wiphy_dev(rt2x00dev->hw->wiphy);
9662306a36Sopenharmony_ci	int retval;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	led->led_dev.name = name;
9962306a36Sopenharmony_ci	led->led_dev.brightness = LED_OFF;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	retval = led_classdev_register(device, &led->led_dev);
10262306a36Sopenharmony_ci	if (retval) {
10362306a36Sopenharmony_ci		rt2x00_err(rt2x00dev, "Failed to register led handler\n");
10462306a36Sopenharmony_ci		return retval;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	led->flags |= LED_REGISTERED;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	char name[36];
11562306a36Sopenharmony_ci	int retval;
11662306a36Sopenharmony_ci	unsigned long on_period;
11762306a36Sopenharmony_ci	unsigned long off_period;
11862306a36Sopenharmony_ci	const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_INITIALIZED) {
12162306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s::radio",
12262306a36Sopenharmony_ci			 rt2x00dev->ops->name, phy_name);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		retval = rt2x00leds_register_led(rt2x00dev,
12562306a36Sopenharmony_ci						 &rt2x00dev->led_radio,
12662306a36Sopenharmony_ci						 name);
12762306a36Sopenharmony_ci		if (retval)
12862306a36Sopenharmony_ci			goto exit_fail;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) {
13262306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s::assoc",
13362306a36Sopenharmony_ci			 rt2x00dev->ops->name, phy_name);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		retval = rt2x00leds_register_led(rt2x00dev,
13662306a36Sopenharmony_ci						 &rt2x00dev->led_assoc,
13762306a36Sopenharmony_ci						 name);
13862306a36Sopenharmony_ci		if (retval)
13962306a36Sopenharmony_ci			goto exit_fail;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_INITIALIZED) {
14362306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s::quality",
14462306a36Sopenharmony_ci			 rt2x00dev->ops->name, phy_name);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		retval = rt2x00leds_register_led(rt2x00dev,
14762306a36Sopenharmony_ci						 &rt2x00dev->led_qual,
14862306a36Sopenharmony_ci						 name);
14962306a36Sopenharmony_ci		if (retval)
15062306a36Sopenharmony_ci			goto exit_fail;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * Initialize blink time to default value:
15562306a36Sopenharmony_ci	 * On period: 70ms
15662306a36Sopenharmony_ci	 * Off period: 30ms
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	if (rt2x00dev->led_radio.led_dev.blink_set) {
15962306a36Sopenharmony_ci		on_period = 70;
16062306a36Sopenharmony_ci		off_period = 30;
16162306a36Sopenharmony_ci		rt2x00dev->led_radio.led_dev.blink_set(
16262306a36Sopenharmony_ci		    &rt2x00dev->led_radio.led_dev, &on_period, &off_period);
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciexit_fail:
16862306a36Sopenharmony_ci	rt2x00leds_unregister(rt2x00dev);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void rt2x00leds_unregister_led(struct rt2x00_led *led)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	led_classdev_unregister(&led->led_dev);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * This might look weird, but when we are unregistering while
17762306a36Sopenharmony_ci	 * suspended the led is already off, and since we haven't
17862306a36Sopenharmony_ci	 * fully resumed yet, access to the device might not be
17962306a36Sopenharmony_ci	 * possible yet.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	if (!(led->led_dev.flags & LED_SUSPENDED))
18262306a36Sopenharmony_ci		led->led_dev.brightness_set(&led->led_dev, LED_OFF);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	led->flags &= ~LED_REGISTERED;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
19062306a36Sopenharmony_ci		rt2x00leds_unregister_led(&rt2x00dev->led_qual);
19162306a36Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
19262306a36Sopenharmony_ci		rt2x00leds_unregister_led(&rt2x00dev->led_assoc);
19362306a36Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
19462306a36Sopenharmony_ci		rt2x00leds_unregister_led(&rt2x00dev->led_radio);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic inline void rt2x00leds_suspend_led(struct rt2x00_led *led)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	led_classdev_suspend(&led->led_dev);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* This shouldn't be needed, but just to be safe */
20262306a36Sopenharmony_ci	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
20362306a36Sopenharmony_ci	led->led_dev.brightness = LED_OFF;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_civoid rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
20962306a36Sopenharmony_ci		rt2x00leds_suspend_led(&rt2x00dev->led_qual);
21062306a36Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
21162306a36Sopenharmony_ci		rt2x00leds_suspend_led(&rt2x00dev->led_assoc);
21262306a36Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
21362306a36Sopenharmony_ci		rt2x00leds_suspend_led(&rt2x00dev->led_radio);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic inline void rt2x00leds_resume_led(struct rt2x00_led *led)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	led_classdev_resume(&led->led_dev);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Device might have enabled the LEDS during resume */
22162306a36Sopenharmony_ci	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
22262306a36Sopenharmony_ci	led->led_dev.brightness = LED_OFF;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_civoid rt2x00leds_resume(struct rt2x00_dev *rt2x00dev)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
22862306a36Sopenharmony_ci		rt2x00leds_resume_led(&rt2x00dev->led_radio);
22962306a36Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
23062306a36Sopenharmony_ci		rt2x00leds_resume_led(&rt2x00dev->led_assoc);
23162306a36Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
23262306a36Sopenharmony_ci		rt2x00leds_resume_led(&rt2x00dev->led_qual);
23362306a36Sopenharmony_ci}
234