aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-led.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-led.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.c201
1 files changed, 80 insertions, 121 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c
index 46ccdf406e8e..074ad2275228 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -48,31 +48,19 @@ module_param(led_mode, int, S_IRUGO);
48MODULE_PARM_DESC(led_mode, "0=system default, " 48MODULE_PARM_DESC(led_mode, "0=system default, "
49 "1=On(RF On)/Off(RF Off), 2=blinking"); 49 "1=On(RF On)/Off(RF Off), 2=blinking");
50 50
51static const struct { 51static const struct ieee80211_tpt_blink iwl_blink[] = {
52 u16 tpt; /* Mb/s */ 52 { .throughput = 0 * 1024 - 1, .blink_time = 334 },
53 u8 on_time; 53 { .throughput = 1 * 1024 - 1, .blink_time = 260 },
54 u8 off_time; 54 { .throughput = 5 * 1024 - 1, .blink_time = 220 },
55} blink_tbl[] = 55 { .throughput = 10 * 1024 - 1, .blink_time = 190 },
56{ 56 { .throughput = 20 * 1024 - 1, .blink_time = 170 },
57 {300, 25, 25}, 57 { .throughput = 50 * 1024 - 1, .blink_time = 150 },
58 {200, 40, 40}, 58 { .throughput = 70 * 1024 - 1, .blink_time = 130 },
59 {100, 55, 55}, 59 { .throughput = 100 * 1024 - 1, .blink_time = 110 },
60 {70, 65, 65}, 60 { .throughput = 200 * 1024 - 1, .blink_time = 80 },
61 {50, 75, 75}, 61 { .throughput = 300 * 1024 - 1, .blink_time = 50 },
62 {20, 85, 85},
63 {10, 95, 95},
64 {5, 110, 110},
65 {1, 130, 130},
66 {0, 167, 167},
67 /* SOLID_ON */
68 {-1, IWL_LED_SOLID, 0}
69}; 62};
70 63
71#define IWL_1MB_RATE (128 * 1024)
72#define IWL_LED_THRESHOLD (16)
73#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
74#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
75
76/* 64/*
77 * Adjust led blink rate to compensate on a MAC Clock difference on every HW 65 * Adjust led blink rate to compensate on a MAC Clock difference on every HW
78 * Led blink rate analysis showed an average deviation of 0% on 3945, 66 * Led blink rate analysis showed an average deviation of 0% on 3945,
@@ -97,133 +85,104 @@ static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
97} 85}
98 86
99/* Set led pattern command */ 87/* Set led pattern command */
100static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx) 88static int iwl_led_cmd(struct iwl_priv *priv,
89 unsigned long on,
90 unsigned long off)
101{ 91{
102 struct iwl_led_cmd led_cmd = { 92 struct iwl_led_cmd led_cmd = {
103 .id = IWL_LED_LINK, 93 .id = IWL_LED_LINK,
104 .interval = IWL_DEF_LED_INTRVL 94 .interval = IWL_DEF_LED_INTRVL
105 }; 95 };
96 int ret;
106 97
107 BUG_ON(idx > IWL_MAX_BLINK_TBL); 98 if (!test_bit(STATUS_READY, &priv->status))
99 return -EBUSY;
108 100
109 IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n", 101 if (priv->blink_on == on && priv->blink_off == off)
102 return 0;
103
104 IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n",
110 priv->cfg->base_params->led_compensation); 105 priv->cfg->base_params->led_compensation);
111 led_cmd.on = 106 led_cmd.on = iwl_blink_compensation(priv, on,
112 iwl_blink_compensation(priv, blink_tbl[idx].on_time,
113 priv->cfg->base_params->led_compensation); 107 priv->cfg->base_params->led_compensation);
114 led_cmd.off = 108 led_cmd.off = iwl_blink_compensation(priv, off,
115 iwl_blink_compensation(priv, blink_tbl[idx].off_time,
116 priv->cfg->base_params->led_compensation); 109 priv->cfg->base_params->led_compensation);
117 110
118 return priv->cfg->ops->led->cmd(priv, &led_cmd); 111 ret = priv->cfg->ops->led->cmd(priv, &led_cmd);
112 if (!ret) {
113 priv->blink_on = on;
114 priv->blink_off = off;
115 }
116 return ret;
119} 117}
120 118
121int iwl_led_start(struct iwl_priv *priv) 119static void iwl_led_brightness_set(struct led_classdev *led_cdev,
120 enum led_brightness brightness)
122{ 121{
123 return priv->cfg->ops->led->on(priv); 122 struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
124} 123 unsigned long on = 0;
125EXPORT_SYMBOL(iwl_led_start);
126 124
127int iwl_led_associate(struct iwl_priv *priv) 125 if (brightness > 0)
128{ 126 on = IWL_LED_SOLID;
129 IWL_DEBUG_LED(priv, "Associated\n");
130 if (priv->cfg->led_mode == IWL_LED_BLINK)
131 priv->allow_blinking = 1;
132 priv->last_blink_time = jiffies;
133 127
134 return 0; 128 iwl_led_cmd(priv, on, 0);
135} 129}
136EXPORT_SYMBOL(iwl_led_associate);
137 130
138int iwl_led_disassociate(struct iwl_priv *priv) 131static int iwl_led_blink_set(struct led_classdev *led_cdev,
132 unsigned long *delay_on,
133 unsigned long *delay_off)
139{ 134{
140 priv->allow_blinking = 0; 135 struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
141 136
142 return 0; 137 return iwl_led_cmd(priv, *delay_on, *delay_off);
143} 138}
144EXPORT_SYMBOL(iwl_led_disassociate);
145 139
146/* 140void iwl_leds_init(struct iwl_priv *priv)
147 * calculate blink rate according to last second Tx/Rx activities
148 */
149static int iwl_get_blink_rate(struct iwl_priv *priv)
150{
151 int i;
152 /* count both tx and rx traffic to be able to
153 * handle traffic in either direction
154 */
155 u64 current_tpt = priv->tx_stats.data_bytes +
156 priv->rx_stats.data_bytes;
157 s64 tpt = current_tpt - priv->led_tpt;
158
159 if (tpt < 0) /* wraparound */
160 tpt = -tpt;
161
162 IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
163 (long long)tpt,
164 (unsigned long long)current_tpt);
165 priv->led_tpt = current_tpt;
166
167 if (!priv->allow_blinking)
168 i = IWL_MAX_BLINK_TBL;
169 else
170 for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
171 if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
172 break;
173
174 IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
175 return i;
176}
177
178/*
179 * this function called from handler. Since setting Led command can
180 * happen very frequent we postpone led command to be called from
181 * REPLY handler so we know ucode is up
182 */
183void iwl_leds_background(struct iwl_priv *priv)
184{ 141{
185 u8 blink_idx; 142 int mode = led_mode;
186 143 int ret;
187 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { 144
188 priv->last_blink_time = 0; 145 if (mode == IWL_LED_DEFAULT)
189 return; 146 mode = priv->cfg->led_mode;
190 } 147
191 if (iwl_is_rfkill(priv)) { 148 priv->led.name = kasprintf(GFP_KERNEL, "%s-led",
192 priv->last_blink_time = 0; 149 wiphy_name(priv->hw->wiphy));
193 return; 150 priv->led.brightness_set = iwl_led_brightness_set;
151 priv->led.blink_set = iwl_led_blink_set;
152 priv->led.max_brightness = 1;
153
154 switch (mode) {
155 case IWL_LED_DEFAULT:
156 WARN_ON(1);
157 break;
158 case IWL_LED_BLINK:
159 priv->led.default_trigger =
160 ieee80211_create_tpt_led_trigger(priv->hw,
161 IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
162 iwl_blink, ARRAY_SIZE(iwl_blink));
163 break;
164 case IWL_LED_RF_STATE:
165 priv->led.default_trigger =
166 ieee80211_get_radio_led_name(priv->hw);
167 break;
194 } 168 }
195 169
196 if (!priv->allow_blinking) { 170 ret = led_classdev_register(&priv->pci_dev->dev, &priv->led);
197 priv->last_blink_time = 0; 171 if (ret) {
198 if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) { 172 kfree(priv->led.name);
199 priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
200 iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
201 }
202 return; 173 return;
203 } 174 }
204 if (!priv->last_blink_time ||
205 !time_after(jiffies, priv->last_blink_time +
206 msecs_to_jiffies(1000)))
207 return;
208
209 blink_idx = iwl_get_blink_rate(priv);
210 175
211 /* call only if blink rate change */ 176 priv->led_registered = true;
212 if (blink_idx != priv->last_blink_rate)
213 iwl_led_pattern(priv, blink_idx);
214
215 priv->last_blink_time = jiffies;
216 priv->last_blink_rate = blink_idx;
217} 177}
218EXPORT_SYMBOL(iwl_leds_background); 178EXPORT_SYMBOL(iwl_leds_init);
219 179
220void iwl_leds_init(struct iwl_priv *priv) 180void iwl_leds_exit(struct iwl_priv *priv)
221{ 181{
222 priv->last_blink_rate = 0; 182 if (!priv->led_registered)
223 priv->last_blink_time = 0; 183 return;
224 priv->allow_blinking = 0; 184
225 if (led_mode != IWL_LED_DEFAULT && 185 led_classdev_unregister(&priv->led);
226 led_mode != priv->cfg->led_mode) 186 kfree(priv->led.name);
227 priv->cfg->led_mode = led_mode;
228} 187}
229EXPORT_SYMBOL(iwl_leds_init); 188EXPORT_SYMBOL(iwl_leds_exit);