18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
48c2ecf20Sopenharmony_ci	<http://rt2x00.serialmonkey.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci	Module: rt2x00lib
108c2ecf20Sopenharmony_ci	Abstract: rt2x00 led specific routines.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "rt2x00.h"
178c2ecf20Sopenharmony_ci#include "rt2x00lib.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct rt2x00_led *led = &rt2x00dev->led_qual;
228c2ecf20Sopenharmony_ci	unsigned int brightness;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED))
258c2ecf20Sopenharmony_ci		return;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	/*
288c2ecf20Sopenharmony_ci	 * Led handling requires a positive value for the rssi,
298c2ecf20Sopenharmony_ci	 * to do that correctly we need to add the correction.
308c2ecf20Sopenharmony_ci	 */
318c2ecf20Sopenharmony_ci	rssi += rt2x00dev->rssi_offset;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/*
348c2ecf20Sopenharmony_ci	 * Get the rssi level, this is used to convert the rssi
358c2ecf20Sopenharmony_ci	 * to a LED value inside the range LED_OFF - LED_FULL.
368c2ecf20Sopenharmony_ci	 */
378c2ecf20Sopenharmony_ci	if (rssi <= 30)
388c2ecf20Sopenharmony_ci		rssi = 0;
398c2ecf20Sopenharmony_ci	else if (rssi <= 39)
408c2ecf20Sopenharmony_ci		rssi = 1;
418c2ecf20Sopenharmony_ci	else if (rssi <= 49)
428c2ecf20Sopenharmony_ci		rssi = 2;
438c2ecf20Sopenharmony_ci	else if (rssi <= 53)
448c2ecf20Sopenharmony_ci		rssi = 3;
458c2ecf20Sopenharmony_ci	else if (rssi <= 63)
468c2ecf20Sopenharmony_ci		rssi = 4;
478c2ecf20Sopenharmony_ci	else
488c2ecf20Sopenharmony_ci		rssi = 5;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/*
518c2ecf20Sopenharmony_ci	 * Note that we must _not_ send LED_OFF since the driver
528c2ecf20Sopenharmony_ci	 * is going to calculate the value and might use it in a
538c2ecf20Sopenharmony_ci	 * division.
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci	brightness = ((LED_FULL / 6) * rssi) + 1;
568c2ecf20Sopenharmony_ci	if (brightness != led->led_dev.brightness) {
578c2ecf20Sopenharmony_ci		led->led_dev.brightness_set(&led->led_dev, brightness);
588c2ecf20Sopenharmony_ci		led->led_dev.brightness = brightness;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	unsigned int brightness = enabled ? LED_FULL : LED_OFF;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (!(led->flags & LED_REGISTERED))
678c2ecf20Sopenharmony_ci		return;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	led->led_dev.brightness_set(&led->led_dev, brightness);
708c2ecf20Sopenharmony_ci	led->led_dev.brightness = brightness;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_civoid rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY)
768c2ecf20Sopenharmony_ci		rt2x00led_led_simple(&rt2x00dev->led_qual, enabled);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_civoid rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC)
828c2ecf20Sopenharmony_ci		rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_civoid rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	if (rt2x00dev->led_radio.type == LED_TYPE_RADIO)
888c2ecf20Sopenharmony_ci		rt2x00led_led_simple(&rt2x00dev->led_radio, enabled);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
928c2ecf20Sopenharmony_ci				   struct rt2x00_led *led,
938c2ecf20Sopenharmony_ci				   const char *name)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct device *device = wiphy_dev(rt2x00dev->hw->wiphy);
968c2ecf20Sopenharmony_ci	int retval;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	led->led_dev.name = name;
998c2ecf20Sopenharmony_ci	led->led_dev.brightness = LED_OFF;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	retval = led_classdev_register(device, &led->led_dev);
1028c2ecf20Sopenharmony_ci	if (retval) {
1038c2ecf20Sopenharmony_ci		rt2x00_err(rt2x00dev, "Failed to register led handler\n");
1048c2ecf20Sopenharmony_ci		return retval;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	led->flags |= LED_REGISTERED;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_civoid rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	char name[36];
1158c2ecf20Sopenharmony_ci	int retval;
1168c2ecf20Sopenharmony_ci	unsigned long on_period;
1178c2ecf20Sopenharmony_ci	unsigned long off_period;
1188c2ecf20Sopenharmony_ci	const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_INITIALIZED) {
1218c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s::radio",
1228c2ecf20Sopenharmony_ci			 rt2x00dev->ops->name, phy_name);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		retval = rt2x00leds_register_led(rt2x00dev,
1258c2ecf20Sopenharmony_ci						 &rt2x00dev->led_radio,
1268c2ecf20Sopenharmony_ci						 name);
1278c2ecf20Sopenharmony_ci		if (retval)
1288c2ecf20Sopenharmony_ci			goto exit_fail;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) {
1328c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s::assoc",
1338c2ecf20Sopenharmony_ci			 rt2x00dev->ops->name, phy_name);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		retval = rt2x00leds_register_led(rt2x00dev,
1368c2ecf20Sopenharmony_ci						 &rt2x00dev->led_assoc,
1378c2ecf20Sopenharmony_ci						 name);
1388c2ecf20Sopenharmony_ci		if (retval)
1398c2ecf20Sopenharmony_ci			goto exit_fail;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_INITIALIZED) {
1438c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s::quality",
1448c2ecf20Sopenharmony_ci			 rt2x00dev->ops->name, phy_name);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		retval = rt2x00leds_register_led(rt2x00dev,
1478c2ecf20Sopenharmony_ci						 &rt2x00dev->led_qual,
1488c2ecf20Sopenharmony_ci						 name);
1498c2ecf20Sopenharmony_ci		if (retval)
1508c2ecf20Sopenharmony_ci			goto exit_fail;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/*
1548c2ecf20Sopenharmony_ci	 * Initialize blink time to default value:
1558c2ecf20Sopenharmony_ci	 * On period: 70ms
1568c2ecf20Sopenharmony_ci	 * Off period: 30ms
1578c2ecf20Sopenharmony_ci	 */
1588c2ecf20Sopenharmony_ci	if (rt2x00dev->led_radio.led_dev.blink_set) {
1598c2ecf20Sopenharmony_ci		on_period = 70;
1608c2ecf20Sopenharmony_ci		off_period = 30;
1618c2ecf20Sopenharmony_ci		rt2x00dev->led_radio.led_dev.blink_set(
1628c2ecf20Sopenharmony_ci		    &rt2x00dev->led_radio.led_dev, &on_period, &off_period);
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciexit_fail:
1688c2ecf20Sopenharmony_ci	rt2x00leds_unregister(rt2x00dev);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void rt2x00leds_unregister_led(struct rt2x00_led *led)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	led_classdev_unregister(&led->led_dev);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/*
1768c2ecf20Sopenharmony_ci	 * This might look weird, but when we are unregistering while
1778c2ecf20Sopenharmony_ci	 * suspended the led is already off, and since we haven't
1788c2ecf20Sopenharmony_ci	 * fully resumed yet, access to the device might not be
1798c2ecf20Sopenharmony_ci	 * possible yet.
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	if (!(led->led_dev.flags & LED_SUSPENDED))
1828c2ecf20Sopenharmony_ci		led->led_dev.brightness_set(&led->led_dev, LED_OFF);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	led->flags &= ~LED_REGISTERED;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_civoid rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
1908c2ecf20Sopenharmony_ci		rt2x00leds_unregister_led(&rt2x00dev->led_qual);
1918c2ecf20Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
1928c2ecf20Sopenharmony_ci		rt2x00leds_unregister_led(&rt2x00dev->led_assoc);
1938c2ecf20Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
1948c2ecf20Sopenharmony_ci		rt2x00leds_unregister_led(&rt2x00dev->led_radio);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic inline void rt2x00leds_suspend_led(struct rt2x00_led *led)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	led_classdev_suspend(&led->led_dev);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* This shouldn't be needed, but just to be safe */
2028c2ecf20Sopenharmony_ci	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
2038c2ecf20Sopenharmony_ci	led->led_dev.brightness = LED_OFF;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_civoid rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
2098c2ecf20Sopenharmony_ci		rt2x00leds_suspend_led(&rt2x00dev->led_qual);
2108c2ecf20Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
2118c2ecf20Sopenharmony_ci		rt2x00leds_suspend_led(&rt2x00dev->led_assoc);
2128c2ecf20Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
2138c2ecf20Sopenharmony_ci		rt2x00leds_suspend_led(&rt2x00dev->led_radio);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic inline void rt2x00leds_resume_led(struct rt2x00_led *led)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	led_classdev_resume(&led->led_dev);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Device might have enabled the LEDS during resume */
2218c2ecf20Sopenharmony_ci	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
2228c2ecf20Sopenharmony_ci	led->led_dev.brightness = LED_OFF;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_civoid rt2x00leds_resume(struct rt2x00_dev *rt2x00dev)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (rt2x00dev->led_radio.flags & LED_REGISTERED)
2288c2ecf20Sopenharmony_ci		rt2x00leds_resume_led(&rt2x00dev->led_radio);
2298c2ecf20Sopenharmony_ci	if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
2308c2ecf20Sopenharmony_ci		rt2x00leds_resume_led(&rt2x00dev->led_assoc);
2318c2ecf20Sopenharmony_ci	if (rt2x00dev->led_qual.flags & LED_REGISTERED)
2328c2ecf20Sopenharmony_ci		rt2x00leds_resume_led(&rt2x00dev->led_qual);
2338c2ecf20Sopenharmony_ci}
234