diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-led.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-led.c | 201 |
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); | |||
48 | MODULE_PARM_DESC(led_mode, "0=system default, " | 48 | MODULE_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 | ||
51 | static const struct { | 51 | static 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 */ |
100 | static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx) | 88 | static 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 | ||
121 | int iwl_led_start(struct iwl_priv *priv) | 119 | static 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; |
125 | EXPORT_SYMBOL(iwl_led_start); | ||
126 | 124 | ||
127 | int 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 | } |
136 | EXPORT_SYMBOL(iwl_led_associate); | ||
137 | 130 | ||
138 | int iwl_led_disassociate(struct iwl_priv *priv) | 131 | static 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 | } |
144 | EXPORT_SYMBOL(iwl_led_disassociate); | ||
145 | 139 | ||
146 | /* | 140 | void iwl_leds_init(struct iwl_priv *priv) |
147 | * calculate blink rate according to last second Tx/Rx activities | ||
148 | */ | ||
149 | static 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 | */ | ||
183 | void 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 | } |
218 | EXPORT_SYMBOL(iwl_leds_background); | 178 | EXPORT_SYMBOL(iwl_leds_init); |
219 | 179 | ||
220 | void iwl_leds_init(struct iwl_priv *priv) | 180 | void 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 | } |
229 | EXPORT_SYMBOL(iwl_leds_init); | 188 | EXPORT_SYMBOL(iwl_leds_exit); |