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