Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | // SPDX-License-Identifier: GPL-2.0-only /* * Linux LED driver for RTL8187 * * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net> * * Based on the LED handling in the r8187 driver, which is: * Copyright (c) Realtek Semiconductor Corp. All rights reserved. * * Thanks to Realtek for their support! */ #ifdef CONFIG_RTL8187_LEDS #include <net/mac80211.h> #include <linux/usb.h> #include <linux/eeprom_93cx6.h> #include "rtl8187.h" #include "leds.h" static void led_turn_on(struct work_struct *work) { /* As this routine does read/write operations on the hardware, it must * be run from a work queue. */ u8 reg; struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, led_on.work); struct rtl8187_led *led = &priv->led_tx; /* Don't change the LED, when the device is down. */ if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) return ; /* Skip if the LED is not registered. */ if (!led->dev) return; mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); break; case LED_PIN_LED0: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_LED1: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_HW: default: break; } mutex_unlock(&priv->conf_mutex); } static void led_turn_off(struct work_struct *work) { /* As this routine does read/write operations on the hardware, it must * be run from a work queue. */ u8 reg; struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, led_off.work); struct rtl8187_led *led = &priv->led_tx; /* Don't change the LED, when the device is down. */ if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) return ; /* Skip if the LED is not registered. */ if (!led->dev) return; mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); break; case LED_PIN_LED0: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_LED1: reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); break; case LED_PIN_HW: default: break; } mutex_unlock(&priv->conf_mutex); } /* Callback from the LED subsystem. */ static void rtl8187_led_brightness_set(struct led_classdev *led_dev, enum led_brightness brightness) { struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, led_dev); struct ieee80211_hw *hw = led->dev; struct rtl8187_priv *priv; static bool radio_on; if (!hw) return; priv = hw->priv; if (led->is_radio) { if (brightness == LED_FULL) { ieee80211_queue_delayed_work(hw, &priv->led_on, 0); radio_on = true; } else if (radio_on) { radio_on = false; cancel_delayed_work(&priv->led_on); ieee80211_queue_delayed_work(hw, &priv->led_off, 0); } } else if (radio_on) { if (brightness == LED_OFF) { ieee80211_queue_delayed_work(hw, &priv->led_off, 0); /* The LED is off for 1/20 sec - it just blinks. */ ieee80211_queue_delayed_work(hw, &priv->led_on, HZ / 20); } else ieee80211_queue_delayed_work(hw, &priv->led_on, 0); } } static int rtl8187_register_led(struct ieee80211_hw *dev, struct rtl8187_led *led, const char *name, const char *default_trigger, u8 ledpin, bool is_radio) { int err; struct rtl8187_priv *priv = dev->priv; if (led->dev) return -EEXIST; if (!default_trigger) return -EINVAL; led->dev = dev; led->ledpin = ledpin; led->is_radio = is_radio; strscpy(led->name, name, sizeof(led->name)); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; led->led_dev.brightness_set = rtl8187_led_brightness_set; err = led_classdev_register(&priv->udev->dev, &led->led_dev); if (err) { printk(KERN_INFO "LEDs: Failed to register %s\n", name); led->dev = NULL; return err; } return 0; } static void rtl8187_unregister_led(struct rtl8187_led *led) { struct ieee80211_hw *hw = led->dev; struct rtl8187_priv *priv = hw->priv; led_classdev_unregister(&led->led_dev); flush_delayed_work(&priv->led_off); led->dev = NULL; } void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) { struct rtl8187_priv *priv = dev->priv; char name[RTL8187_LED_MAX_NAME_LEN + 1]; u8 ledpin; int err; /* According to the vendor driver, the LED operation depends on the * customer ID encoded in the EEPROM */ printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); switch (custid) { case EEPROM_CID_RSVD0: case EEPROM_CID_RSVD1: case EEPROM_CID_SERCOMM_PS: case EEPROM_CID_QMI: case EEPROM_CID_DELL: case EEPROM_CID_TOSHIBA: ledpin = LED_PIN_GPIO0; break; case EEPROM_CID_ALPHA0: ledpin = LED_PIN_LED0; break; case EEPROM_CID_HW: ledpin = LED_PIN_HW; break; default: ledpin = LED_PIN_GPIO0; } INIT_DELAYED_WORK(&priv->led_on, led_turn_on); INIT_DELAYED_WORK(&priv->led_off, led_turn_off); snprintf(name, sizeof(name), "rtl8187-%s::radio", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_radio, name, ieee80211_get_radio_led_name(dev), ledpin, true); if (err) return; snprintf(name, sizeof(name), "rtl8187-%s::tx", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_tx, name, ieee80211_get_tx_led_name(dev), ledpin, false); if (err) goto err_tx; snprintf(name, sizeof(name), "rtl8187-%s::rx", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_rx, name, ieee80211_get_rx_led_name(dev), ledpin, false); if (!err) return; /* registration of RX LED failed - unregister */ rtl8187_unregister_led(&priv->led_tx); err_tx: rtl8187_unregister_led(&priv->led_radio); } void rtl8187_leds_exit(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; rtl8187_unregister_led(&priv->led_radio); rtl8187_unregister_led(&priv->led_rx); rtl8187_unregister_led(&priv->led_tx); cancel_delayed_work_sync(&priv->led_off); cancel_delayed_work_sync(&priv->led_on); } #endif /* def CONFIG_RTL8187_LEDS */ |