diff options
author | Larry Finger <Larry.Finger@lwfinger.net> | 2010-12-08 12:12:31 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-12-15 16:17:49 -0500 |
commit | 0c8173385e549f95cd80c3fff5aab87b4f881d8d (patch) | |
tree | eb818f70ed027eecbc5e170b046aa0d785168d43 /drivers/net/wireless/rtlwifi/base.c | |
parent | 412b31334b831a8c2909afaca017c5a236ac2dd0 (diff) |
rtl8192ce: Add new driver
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rtlwifi/base.c')
-rw-r--r-- | drivers/net/wireless/rtlwifi/base.c | 958 |
1 files changed, 958 insertions, 0 deletions
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c new file mode 100644 index 000000000000..9e860ff30b52 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/base.c | |||
@@ -0,0 +1,958 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Copyright(c) 2009-2010 Realtek Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of version 2 of the GNU General Public License as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called LICENSE. | ||
20 | * | ||
21 | * Contact Information: | ||
22 | * wlanfae <wlanfae@realtek.com> | ||
23 | * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, | ||
24 | * Hsinchu 300, Taiwan. | ||
25 | * | ||
26 | * Larry Finger <Larry.Finger@lwfinger.net> | ||
27 | * | ||
28 | *****************************************************************************/ | ||
29 | |||
30 | #include <linux/ip.h> | ||
31 | #include "wifi.h" | ||
32 | #include "rc.h" | ||
33 | #include "base.h" | ||
34 | #include "efuse.h" | ||
35 | #include "cam.h" | ||
36 | #include "ps.h" | ||
37 | #include "regd.h" | ||
38 | |||
39 | /* | ||
40 | *NOTICE!!!: This file will be very big, we hsould | ||
41 | *keep it clear under follwing roles: | ||
42 | * | ||
43 | *This file include follwing part, so, if you add new | ||
44 | *functions into this file, please check which part it | ||
45 | *should includes. or check if you should add new part | ||
46 | *for this file: | ||
47 | * | ||
48 | *1) mac80211 init functions | ||
49 | *2) tx information functions | ||
50 | *3) functions called by core.c | ||
51 | *4) wq & timer callback functions | ||
52 | *5) frame process functions | ||
53 | *6) sysfs functions | ||
54 | *7) ... | ||
55 | */ | ||
56 | |||
57 | /********************************************************* | ||
58 | * | ||
59 | * mac80211 init functions | ||
60 | * | ||
61 | *********************************************************/ | ||
62 | static struct ieee80211_channel rtl_channeltable[] = { | ||
63 | {.center_freq = 2412, .hw_value = 1,}, | ||
64 | {.center_freq = 2417, .hw_value = 2,}, | ||
65 | {.center_freq = 2422, .hw_value = 3,}, | ||
66 | {.center_freq = 2427, .hw_value = 4,}, | ||
67 | {.center_freq = 2432, .hw_value = 5,}, | ||
68 | {.center_freq = 2437, .hw_value = 6,}, | ||
69 | {.center_freq = 2442, .hw_value = 7,}, | ||
70 | {.center_freq = 2447, .hw_value = 8,}, | ||
71 | {.center_freq = 2452, .hw_value = 9,}, | ||
72 | {.center_freq = 2457, .hw_value = 10,}, | ||
73 | {.center_freq = 2462, .hw_value = 11,}, | ||
74 | {.center_freq = 2467, .hw_value = 12,}, | ||
75 | {.center_freq = 2472, .hw_value = 13,}, | ||
76 | {.center_freq = 2484, .hw_value = 14,}, | ||
77 | }; | ||
78 | |||
79 | static struct ieee80211_rate rtl_ratetable[] = { | ||
80 | {.bitrate = 10, .hw_value = 0x00,}, | ||
81 | {.bitrate = 20, .hw_value = 0x01,}, | ||
82 | {.bitrate = 55, .hw_value = 0x02,}, | ||
83 | {.bitrate = 110, .hw_value = 0x03,}, | ||
84 | {.bitrate = 60, .hw_value = 0x04,}, | ||
85 | {.bitrate = 90, .hw_value = 0x05,}, | ||
86 | {.bitrate = 120, .hw_value = 0x06,}, | ||
87 | {.bitrate = 180, .hw_value = 0x07,}, | ||
88 | {.bitrate = 240, .hw_value = 0x08,}, | ||
89 | {.bitrate = 360, .hw_value = 0x09,}, | ||
90 | {.bitrate = 480, .hw_value = 0x0a,}, | ||
91 | {.bitrate = 540, .hw_value = 0x0b,}, | ||
92 | }; | ||
93 | |||
94 | static const struct ieee80211_supported_band rtl_band_2ghz = { | ||
95 | .band = IEEE80211_BAND_2GHZ, | ||
96 | |||
97 | .channels = rtl_channeltable, | ||
98 | .n_channels = ARRAY_SIZE(rtl_channeltable), | ||
99 | |||
100 | .bitrates = rtl_ratetable, | ||
101 | .n_bitrates = ARRAY_SIZE(rtl_ratetable), | ||
102 | |||
103 | .ht_cap = {0}, | ||
104 | }; | ||
105 | |||
106 | static void _rtl_init_hw_ht_capab(struct ieee80211_hw *hw, | ||
107 | struct ieee80211_sta_ht_cap *ht_cap) | ||
108 | { | ||
109 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
110 | struct rtl_phy *rtlphy = &(rtlpriv->phy); | ||
111 | |||
112 | ht_cap->ht_supported = true; | ||
113 | ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | ||
114 | IEEE80211_HT_CAP_SGI_40 | | ||
115 | IEEE80211_HT_CAP_SGI_20 | | ||
116 | IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_MAX_AMSDU; | ||
117 | |||
118 | /* | ||
119 | *Maximum length of AMPDU that the STA can receive. | ||
120 | *Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets) | ||
121 | */ | ||
122 | ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; | ||
123 | |||
124 | /*Minimum MPDU start spacing , */ | ||
125 | ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; | ||
126 | |||
127 | ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; | ||
128 | |||
129 | /* | ||
130 | *hw->wiphy->bands[IEEE80211_BAND_2GHZ] | ||
131 | *base on ant_num | ||
132 | *rx_mask: RX mask | ||
133 | *if rx_ant =1 rx_mask[0]=0xff;==>MCS0-MCS7 | ||
134 | *if rx_ant =2 rx_mask[1]=0xff;==>MCS8-MCS15 | ||
135 | *if rx_ant >=3 rx_mask[2]=0xff; | ||
136 | *if BW_40 rx_mask[4]=0x01; | ||
137 | *highest supported RX rate | ||
138 | */ | ||
139 | if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_2T2R) { | ||
140 | |||
141 | RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, ("1T2R or 2T2R\n")); | ||
142 | |||
143 | ht_cap->mcs.rx_mask[0] = 0xFF; | ||
144 | ht_cap->mcs.rx_mask[1] = 0xFF; | ||
145 | ht_cap->mcs.rx_mask[4] = 0x01; | ||
146 | |||
147 | ht_cap->mcs.rx_highest = MAX_BIT_RATE_40MHZ_MCS15; | ||
148 | } else if (get_rf_type(rtlphy) == RF_1T1R) { | ||
149 | |||
150 | RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, ("1T1R\n")); | ||
151 | |||
152 | ht_cap->mcs.rx_mask[0] = 0xFF; | ||
153 | ht_cap->mcs.rx_mask[1] = 0x00; | ||
154 | ht_cap->mcs.rx_mask[4] = 0x01; | ||
155 | |||
156 | ht_cap->mcs.rx_highest = MAX_BIT_RATE_40MHZ_MCS7; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static void _rtl_init_mac80211(struct ieee80211_hw *hw) | ||
161 | { | ||
162 | struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); | ||
163 | struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); | ||
164 | struct ieee80211_supported_band *sband; | ||
165 | |||
166 | /* <1> use mac->bands as mem for hw->wiphy->bands */ | ||
167 | sband = &(rtlmac->bands[IEEE80211_BAND_2GHZ]); | ||
168 | |||
169 | /* | ||
170 | * <2> set hw->wiphy->bands[IEEE80211_BAND_2GHZ] | ||
171 | * to default value(1T1R) | ||
172 | */ | ||
173 | memcpy(&(rtlmac->bands[IEEE80211_BAND_2GHZ]), &rtl_band_2ghz, | ||
174 | sizeof(struct ieee80211_supported_band)); | ||
175 | |||
176 | /* <3> init ht cap base on ant_num */ | ||
177 | _rtl_init_hw_ht_capab(hw, &sband->ht_cap); | ||
178 | |||
179 | /* <4> set mac->sband to wiphy->sband */ | ||
180 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; | ||
181 | |||
182 | /* <5> set hw caps */ | ||
183 | hw->flags = IEEE80211_HW_SIGNAL_DBM | | ||
184 | IEEE80211_HW_RX_INCLUDES_FCS | | ||
185 | IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_AMPDU_AGGREGATION | /*PS*/ | ||
186 | /*IEEE80211_HW_SUPPORTS_PS | */ | ||
187 | /*IEEE80211_HW_PS_NULLFUNC_STACK | */ | ||
188 | /*IEEE80211_HW_SUPPORTS_DYNAMIC_PS | */ | ||
189 | IEEE80211_HW_REPORTS_TX_ACK_STATUS | 0; | ||
190 | |||
191 | hw->wiphy->interface_modes = | ||
192 | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); | ||
193 | |||
194 | hw->wiphy->rts_threshold = 2347; | ||
195 | |||
196 | hw->queues = AC_MAX; | ||
197 | hw->extra_tx_headroom = RTL_TX_HEADER_SIZE; | ||
198 | |||
199 | /* TODO: Correct this value for our hw */ | ||
200 | /* TODO: define these hard code value */ | ||
201 | hw->channel_change_time = 100; | ||
202 | hw->max_listen_interval = 5; | ||
203 | hw->max_rate_tries = 4; | ||
204 | /* hw->max_rates = 1; */ | ||
205 | |||
206 | /* <6> mac address */ | ||
207 | if (is_valid_ether_addr(rtlefuse->dev_addr)) { | ||
208 | SET_IEEE80211_PERM_ADDR(hw, rtlefuse->dev_addr); | ||
209 | } else { | ||
210 | u8 rtlmac[] = { 0x00, 0xe0, 0x4c, 0x81, 0x92, 0x00 }; | ||
211 | get_random_bytes((rtlmac + (ETH_ALEN - 1)), 1); | ||
212 | SET_IEEE80211_PERM_ADDR(hw, rtlmac); | ||
213 | } | ||
214 | |||
215 | } | ||
216 | |||
217 | static void _rtl_init_deferred_work(struct ieee80211_hw *hw) | ||
218 | { | ||
219 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
220 | |||
221 | /* <1> timer */ | ||
222 | init_timer(&rtlpriv->works.watchdog_timer); | ||
223 | setup_timer(&rtlpriv->works.watchdog_timer, | ||
224 | rtl_watch_dog_timer_callback, (unsigned long)hw); | ||
225 | |||
226 | /* <2> work queue */ | ||
227 | rtlpriv->works.hw = hw; | ||
228 | rtlpriv->works.rtl_wq = create_workqueue(rtlpriv->cfg->name); | ||
229 | INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq, | ||
230 | (void *)rtl_watchdog_wq_callback); | ||
231 | INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq, | ||
232 | (void *)rtl_ips_nic_off_wq_callback); | ||
233 | |||
234 | } | ||
235 | |||
236 | void rtl_deinit_deferred_work(struct ieee80211_hw *hw) | ||
237 | { | ||
238 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
239 | |||
240 | del_timer_sync(&rtlpriv->works.watchdog_timer); | ||
241 | |||
242 | cancel_delayed_work(&rtlpriv->works.watchdog_wq); | ||
243 | cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); | ||
244 | } | ||
245 | |||
246 | void rtl_init_rfkill(struct ieee80211_hw *hw) | ||
247 | { | ||
248 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
249 | |||
250 | bool radio_state; | ||
251 | bool blocked; | ||
252 | u8 valid = 0; | ||
253 | |||
254 | /*set init state to rf on */ | ||
255 | rtlpriv->rfkill.rfkill_state = 1; | ||
256 | |||
257 | radio_state = rtlpriv->cfg->ops->radio_onoff_checking(hw, &valid); | ||
258 | |||
259 | if (valid) { | ||
260 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, | ||
261 | (KERN_INFO "wireless switch is %s\n", | ||
262 | rtlpriv->rfkill.rfkill_state ? "on" : "off")); | ||
263 | |||
264 | rtlpriv->rfkill.rfkill_state = radio_state; | ||
265 | |||
266 | blocked = (rtlpriv->rfkill.rfkill_state == 1) ? 0 : 1; | ||
267 | wiphy_rfkill_set_hw_state(hw->wiphy, blocked); | ||
268 | } | ||
269 | |||
270 | wiphy_rfkill_start_polling(hw->wiphy); | ||
271 | } | ||
272 | |||
273 | void rtl_deinit_rfkill(struct ieee80211_hw *hw) | ||
274 | { | ||
275 | wiphy_rfkill_stop_polling(hw->wiphy); | ||
276 | } | ||
277 | |||
278 | int rtl_init_core(struct ieee80211_hw *hw) | ||
279 | { | ||
280 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
281 | struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); | ||
282 | |||
283 | /* <1> init mac80211 */ | ||
284 | _rtl_init_mac80211(hw); | ||
285 | rtlmac->hw = hw; | ||
286 | |||
287 | /* <2> rate control register */ | ||
288 | if (rtl_rate_control_register()) { | ||
289 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | ||
290 | ("rtl: Unable to register rtl_rc," | ||
291 | "use default RC !!\n")); | ||
292 | } else { | ||
293 | hw->rate_control_algorithm = "rtl_rc"; | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * <3> init CRDA must come after init | ||
298 | * mac80211 hw in _rtl_init_mac80211. | ||
299 | */ | ||
300 | if (rtl_regd_init(hw, rtl_reg_notifier)) { | ||
301 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("REGD init failed\n")); | ||
302 | return 1; | ||
303 | } else { | ||
304 | /* CRDA regd hint must after init CRDA */ | ||
305 | if (regulatory_hint(hw->wiphy, rtlpriv->regd.alpha2)) { | ||
306 | RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, | ||
307 | ("regulatory_hint fail\n")); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | /* <4> locks */ | ||
312 | sema_init(&rtlpriv->locks.ips_sem, 1); | ||
313 | sema_init(&rtlpriv->locks.conf_sem, 1); | ||
314 | spin_lock_init(&rtlpriv->locks.irq_th_lock); | ||
315 | spin_lock_init(&rtlpriv->locks.h2c_lock); | ||
316 | spin_lock_init(&rtlpriv->locks.rf_ps_lock); | ||
317 | spin_lock_init(&rtlpriv->locks.rf_lock); | ||
318 | spin_lock_init(&rtlpriv->locks.lps_lock); | ||
319 | |||
320 | rtlmac->link_state = MAC80211_NOLINK; | ||
321 | |||
322 | /* <5> init deferred work */ | ||
323 | _rtl_init_deferred_work(hw); | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | void rtl_deinit_core(struct ieee80211_hw *hw) | ||
329 | { | ||
330 | /*RC*/ | ||
331 | rtl_rate_control_unregister(); | ||
332 | } | ||
333 | |||
334 | void rtl_init_rx_config(struct ieee80211_hw *hw) | ||
335 | { | ||
336 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
337 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
338 | |||
339 | rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *) (&mac->rx_conf)); | ||
340 | rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_MGT_FILTER, | ||
341 | (u8 *) (&mac->rx_mgt_filter)); | ||
342 | rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_CTRL_FILTER, | ||
343 | (u8 *) (&mac->rx_ctrl_filter)); | ||
344 | rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_DATA_FILTER, | ||
345 | (u8 *) (&mac->rx_data_filter)); | ||
346 | } | ||
347 | |||
348 | /********************************************************* | ||
349 | * | ||
350 | * tx information functions | ||
351 | * | ||
352 | *********************************************************/ | ||
353 | static void _rtl_qurey_shortpreamble_mode(struct ieee80211_hw *hw, | ||
354 | struct rtl_tcb_desc *tcb_desc, | ||
355 | struct ieee80211_tx_info *info) | ||
356 | { | ||
357 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
358 | u8 rate_flag = info->control.rates[0].flags; | ||
359 | |||
360 | tcb_desc->use_shortpreamble = false; | ||
361 | |||
362 | /* 1M can only use Long Preamble. 11B spec */ | ||
363 | if (tcb_desc->hw_rate == rtlpriv->cfg->maps[RTL_RC_CCK_RATE1M]) | ||
364 | return; | ||
365 | else if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) | ||
366 | tcb_desc->use_shortpreamble = true; | ||
367 | |||
368 | return; | ||
369 | } | ||
370 | |||
371 | static void _rtl_query_shortgi(struct ieee80211_hw *hw, | ||
372 | struct rtl_tcb_desc *tcb_desc, | ||
373 | struct ieee80211_tx_info *info) | ||
374 | { | ||
375 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
376 | u8 rate_flag = info->control.rates[0].flags; | ||
377 | |||
378 | tcb_desc->use_shortgi = false; | ||
379 | |||
380 | if (!mac->ht_enable) | ||
381 | return; | ||
382 | |||
383 | if (!mac->sgi_40 && !mac->sgi_20) | ||
384 | return; | ||
385 | |||
386 | if ((mac->bw_40 == true) && mac->sgi_40) | ||
387 | tcb_desc->use_shortgi = true; | ||
388 | else if ((mac->bw_40 == false) && mac->sgi_20) | ||
389 | tcb_desc->use_shortgi = true; | ||
390 | |||
391 | if (!(rate_flag & IEEE80211_TX_RC_SHORT_GI)) | ||
392 | tcb_desc->use_shortgi = false; | ||
393 | |||
394 | } | ||
395 | |||
396 | static void _rtl_query_protection_mode(struct ieee80211_hw *hw, | ||
397 | struct rtl_tcb_desc *tcb_desc, | ||
398 | struct ieee80211_tx_info *info) | ||
399 | { | ||
400 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
401 | u8 rate_flag = info->control.rates[0].flags; | ||
402 | |||
403 | /* Common Settings */ | ||
404 | tcb_desc->b_rts_stbc = false; | ||
405 | tcb_desc->b_cts_enable = false; | ||
406 | tcb_desc->rts_sc = 0; | ||
407 | tcb_desc->b_rts_bw = false; | ||
408 | tcb_desc->b_rts_use_shortpreamble = false; | ||
409 | tcb_desc->b_rts_use_shortgi = false; | ||
410 | |||
411 | if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) { | ||
412 | /* Use CTS-to-SELF in protection mode. */ | ||
413 | tcb_desc->b_rts_enable = true; | ||
414 | tcb_desc->b_cts_enable = true; | ||
415 | tcb_desc->rts_rate = rtlpriv->cfg->maps[RTL_RC_OFDM_RATE24M]; | ||
416 | } else if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { | ||
417 | /* Use RTS-CTS in protection mode. */ | ||
418 | tcb_desc->b_rts_enable = true; | ||
419 | tcb_desc->rts_rate = rtlpriv->cfg->maps[RTL_RC_OFDM_RATE24M]; | ||
420 | } | ||
421 | |||
422 | } | ||
423 | |||
424 | static void _rtl_txrate_selectmode(struct ieee80211_hw *hw, | ||
425 | struct rtl_tcb_desc *tcb_desc) | ||
426 | { | ||
427 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
428 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
429 | |||
430 | if (!tcb_desc->disable_ratefallback || !tcb_desc->use_driver_rate) { | ||
431 | if (mac->opmode == NL80211_IFTYPE_STATION) | ||
432 | tcb_desc->ratr_index = 0; | ||
433 | else if (mac->opmode == NL80211_IFTYPE_ADHOC) { | ||
434 | if (tcb_desc->b_multicast || tcb_desc->b_broadcast) { | ||
435 | tcb_desc->hw_rate = | ||
436 | rtlpriv->cfg->maps[RTL_RC_CCK_RATE2M]; | ||
437 | tcb_desc->use_driver_rate = 1; | ||
438 | } else { | ||
439 | /* TODO */ | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | if (rtlpriv->dm.b_useramask) { | ||
445 | /* TODO we will differentiate adhoc and station futrue */ | ||
446 | tcb_desc->mac_id = 0; | ||
447 | |||
448 | if ((mac->mode == WIRELESS_MODE_N_24G) || | ||
449 | (mac->mode == WIRELESS_MODE_N_5G)) { | ||
450 | tcb_desc->ratr_index = RATR_INX_WIRELESS_NGB; | ||
451 | } else if (mac->mode & WIRELESS_MODE_G) { | ||
452 | tcb_desc->ratr_index = RATR_INX_WIRELESS_GB; | ||
453 | } else if (mac->mode & WIRELESS_MODE_B) { | ||
454 | tcb_desc->ratr_index = RATR_INX_WIRELESS_B; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | } | ||
459 | |||
460 | static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw, | ||
461 | struct rtl_tcb_desc *tcb_desc) | ||
462 | { | ||
463 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
464 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
465 | |||
466 | tcb_desc->b_packet_bw = false; | ||
467 | |||
468 | if (!mac->bw_40 || !mac->ht_enable) | ||
469 | return; | ||
470 | |||
471 | if (tcb_desc->b_multicast || tcb_desc->b_broadcast) | ||
472 | return; | ||
473 | |||
474 | /*use legency rate, shall use 20MHz */ | ||
475 | if (tcb_desc->hw_rate <= rtlpriv->cfg->maps[RTL_RC_OFDM_RATE54M]) | ||
476 | return; | ||
477 | |||
478 | tcb_desc->b_packet_bw = true; | ||
479 | } | ||
480 | |||
481 | static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw) | ||
482 | { | ||
483 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
484 | struct rtl_phy *rtlphy = &(rtlpriv->phy); | ||
485 | u8 hw_rate; | ||
486 | |||
487 | if (get_rf_type(rtlphy) == RF_2T2R) | ||
488 | hw_rate = rtlpriv->cfg->maps[RTL_RC_HT_RATEMCS15]; | ||
489 | else | ||
490 | hw_rate = rtlpriv->cfg->maps[RTL_RC_HT_RATEMCS7]; | ||
491 | |||
492 | return hw_rate; | ||
493 | } | ||
494 | |||
495 | void rtl_get_tcb_desc(struct ieee80211_hw *hw, | ||
496 | struct ieee80211_tx_info *info, | ||
497 | struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc) | ||
498 | { | ||
499 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
500 | struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); | ||
501 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); | ||
502 | struct ieee80211_rate *txrate; | ||
503 | u16 fc = le16_to_cpu(hdr->frame_control); | ||
504 | |||
505 | memset(tcb_desc, 0, sizeof(struct rtl_tcb_desc)); | ||
506 | |||
507 | if (ieee80211_is_data(fc)) { | ||
508 | txrate = ieee80211_get_tx_rate(hw, info); | ||
509 | tcb_desc->hw_rate = txrate->hw_value; | ||
510 | |||
511 | /* | ||
512 | *we set data rate RTL_RC_CCK_RATE1M | ||
513 | *in rtl_rc.c if skb is special data or | ||
514 | *mgt which need low data rate. | ||
515 | */ | ||
516 | |||
517 | /* | ||
518 | *So tcb_desc->hw_rate is just used for | ||
519 | *special data and mgt frames | ||
520 | */ | ||
521 | if (tcb_desc->hw_rate < rtlpriv->cfg->maps[RTL_RC_CCK_RATE11M]) { | ||
522 | tcb_desc->use_driver_rate = true; | ||
523 | tcb_desc->ratr_index = 7; | ||
524 | |||
525 | tcb_desc->hw_rate = | ||
526 | rtlpriv->cfg->maps[RTL_RC_CCK_RATE1M]; | ||
527 | tcb_desc->disable_ratefallback = 1; | ||
528 | } else { | ||
529 | /* | ||
530 | *because hw will nerver use hw_rate | ||
531 | *when tcb_desc->use_driver_rate = false | ||
532 | *so we never set highest N rate here, | ||
533 | *and N rate will all be controled by FW | ||
534 | *when tcb_desc->use_driver_rate = false | ||
535 | */ | ||
536 | if (rtlmac->ht_enable) { | ||
537 | tcb_desc->hw_rate = _rtl_get_highest_n_rate(hw); | ||
538 | } else { | ||
539 | if (rtlmac->mode == WIRELESS_MODE_B) { | ||
540 | tcb_desc->hw_rate = | ||
541 | rtlpriv->cfg->maps[RTL_RC_CCK_RATE11M]; | ||
542 | } else { | ||
543 | tcb_desc->hw_rate = | ||
544 | rtlpriv->cfg->maps[RTL_RC_OFDM_RATE54M]; | ||
545 | } | ||
546 | } | ||
547 | } | ||
548 | |||
549 | if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) | ||
550 | tcb_desc->b_multicast = 1; | ||
551 | else if (is_broadcast_ether_addr(ieee80211_get_DA(hdr))) | ||
552 | tcb_desc->b_broadcast = 1; | ||
553 | |||
554 | _rtl_txrate_selectmode(hw, tcb_desc); | ||
555 | _rtl_query_bandwidth_mode(hw, tcb_desc); | ||
556 | _rtl_qurey_shortpreamble_mode(hw, tcb_desc, info); | ||
557 | _rtl_query_shortgi(hw, tcb_desc, info); | ||
558 | _rtl_query_protection_mode(hw, tcb_desc, info); | ||
559 | } else { | ||
560 | tcb_desc->use_driver_rate = true; | ||
561 | tcb_desc->ratr_index = 7; | ||
562 | tcb_desc->disable_ratefallback = 1; | ||
563 | tcb_desc->mac_id = 0; | ||
564 | |||
565 | tcb_desc->hw_rate = rtlpriv->cfg->maps[RTL_RC_CCK_RATE1M]; | ||
566 | } | ||
567 | } | ||
568 | EXPORT_SYMBOL(rtl_get_tcb_desc); | ||
569 | |||
570 | bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb) | ||
571 | { | ||
572 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
573 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
574 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); | ||
575 | u16 fc = le16_to_cpu(hdr->frame_control); | ||
576 | |||
577 | if (ieee80211_is_auth(fc)) { | ||
578 | RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, ("MAC80211_LINKING\n")); | ||
579 | rtl_ips_nic_on(hw); | ||
580 | |||
581 | mac->link_state = MAC80211_LINKING; | ||
582 | } | ||
583 | |||
584 | return true; | ||
585 | } | ||
586 | |||
587 | bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) | ||
588 | { | ||
589 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
590 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); | ||
591 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
592 | u16 fc = le16_to_cpu(hdr->frame_control); | ||
593 | u8 *act = (u8 *) (((u8 *) skb->data + MAC80211_3ADDR_LEN)); | ||
594 | u8 category; | ||
595 | |||
596 | if (!ieee80211_is_action(fc)) | ||
597 | return true; | ||
598 | |||
599 | category = *act; | ||
600 | act++; | ||
601 | switch (category) { | ||
602 | case ACT_CAT_BA: | ||
603 | switch (*act) { | ||
604 | case ACT_ADDBAREQ: | ||
605 | if (mac->act_scanning) | ||
606 | return false; | ||
607 | |||
608 | RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, | ||
609 | ("%s ACT_ADDBAREQ From :" MAC_FMT "\n", | ||
610 | is_tx ? "Tx" : "Rx", MAC_ARG(hdr->addr2))); | ||
611 | break; | ||
612 | case ACT_ADDBARSP: | ||
613 | RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, | ||
614 | ("%s ACT_ADDBARSP From :" MAC_FMT "\n", | ||
615 | is_tx ? "Tx" : "Rx", MAC_ARG(hdr->addr2))); | ||
616 | break; | ||
617 | case ACT_DELBA: | ||
618 | RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, | ||
619 | ("ACT_ADDBADEL From :" MAC_FMT "\n", | ||
620 | MAC_ARG(hdr->addr2))); | ||
621 | break; | ||
622 | } | ||
623 | break; | ||
624 | default: | ||
625 | break; | ||
626 | } | ||
627 | |||
628 | return true; | ||
629 | } | ||
630 | |||
631 | /*should call before software enc*/ | ||
632 | u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) | ||
633 | { | ||
634 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
635 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); | ||
636 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
637 | u16 fc = le16_to_cpu(hdr->frame_control); | ||
638 | u16 ether_type; | ||
639 | u8 mac_hdr_len = ieee80211_get_hdrlen_from_skb(skb); | ||
640 | const struct iphdr *ip; | ||
641 | |||
642 | if (!ieee80211_is_data(fc)) | ||
643 | goto end; | ||
644 | |||
645 | if (ieee80211_is_nullfunc(fc)) | ||
646 | return true; | ||
647 | |||
648 | ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len + | ||
649 | SNAP_SIZE + PROTOC_TYPE_SIZE); | ||
650 | ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE); | ||
651 | ether_type = ntohs(ether_type); | ||
652 | |||
653 | if (ETH_P_IP == ether_type) { | ||
654 | if (IPPROTO_UDP == ip->protocol) { | ||
655 | struct udphdr *udp = (struct udphdr *)((u8 *) ip + | ||
656 | (ip->ihl << 2)); | ||
657 | if (((((u8 *) udp)[1] == 68) && | ||
658 | (((u8 *) udp)[3] == 67)) || | ||
659 | ((((u8 *) udp)[1] == 67) && | ||
660 | (((u8 *) udp)[3] == 68))) { | ||
661 | /* | ||
662 | * 68 : UDP BOOTP client | ||
663 | * 67 : UDP BOOTP server | ||
664 | */ | ||
665 | RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), | ||
666 | DBG_DMESG, ("dhcp %s !!\n", | ||
667 | (is_tx) ? "Tx" : "Rx")); | ||
668 | |||
669 | if (is_tx) { | ||
670 | rtl_lps_leave(hw); | ||
671 | ppsc->last_delaylps_stamp_jiffies = | ||
672 | jiffies; | ||
673 | } | ||
674 | |||
675 | return true; | ||
676 | } | ||
677 | } | ||
678 | } else if (ETH_P_ARP == ether_type) { | ||
679 | if (is_tx) { | ||
680 | rtl_lps_leave(hw); | ||
681 | ppsc->last_delaylps_stamp_jiffies = jiffies; | ||
682 | } | ||
683 | |||
684 | return true; | ||
685 | } else if (ETH_P_PAE == ether_type) { | ||
686 | RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, | ||
687 | ("802.1X %s EAPOL pkt!!\n", (is_tx) ? "Tx" : "Rx")); | ||
688 | |||
689 | if (is_tx) { | ||
690 | rtl_lps_leave(hw); | ||
691 | ppsc->last_delaylps_stamp_jiffies = jiffies; | ||
692 | } | ||
693 | |||
694 | return true; | ||
695 | } else if (0x86DD == ether_type) { | ||
696 | return true; | ||
697 | } | ||
698 | |||
699 | end: | ||
700 | return false; | ||
701 | } | ||
702 | |||
703 | /********************************************************* | ||
704 | * | ||
705 | * functions called by core.c | ||
706 | * | ||
707 | *********************************************************/ | ||
708 | int rtl_tx_agg_start(struct ieee80211_hw *hw, const u8 *ra, u16 tid, u16 *ssn) | ||
709 | { | ||
710 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
711 | struct rtl_tid_data *tid_data; | ||
712 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
713 | |||
714 | RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, | ||
715 | ("on ra = %pM tid = %d\n", ra, tid)); | ||
716 | |||
717 | if (unlikely(tid >= MAX_TID_COUNT)) | ||
718 | return -EINVAL; | ||
719 | |||
720 | if (mac->tids[tid].agg.agg_state != RTL_AGG_OFF) { | ||
721 | RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, | ||
722 | ("Start AGG when state is not RTL_AGG_OFF !\n")); | ||
723 | return -ENXIO; | ||
724 | } | ||
725 | |||
726 | tid_data = &mac->tids[tid]; | ||
727 | *ssn = SEQ_TO_SN(tid_data->seq_number); | ||
728 | |||
729 | RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, | ||
730 | ("HW queue is empty tid:%d\n", tid)); | ||
731 | tid_data->agg.agg_state = RTL_AGG_ON; | ||
732 | |||
733 | ieee80211_start_tx_ba_cb_irqsafe(mac->vif, ra, tid); | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | int rtl_tx_agg_stop(struct ieee80211_hw *hw, const u8 * ra, u16 tid) | ||
739 | { | ||
740 | int ssn = -1; | ||
741 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
742 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
743 | struct rtl_tid_data *tid_data; | ||
744 | |||
745 | if (!ra) { | ||
746 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("ra = NULL\n")); | ||
747 | return -EINVAL; | ||
748 | } | ||
749 | |||
750 | if (unlikely(tid >= MAX_TID_COUNT)) | ||
751 | return -EINVAL; | ||
752 | |||
753 | if (mac->tids[tid].agg.agg_state != RTL_AGG_ON) | ||
754 | RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, | ||
755 | ("Stopping AGG while state not ON or starting\n")); | ||
756 | |||
757 | tid_data = &mac->tids[tid]; | ||
758 | ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; | ||
759 | |||
760 | mac->tids[tid].agg.agg_state = RTL_AGG_OFF; | ||
761 | |||
762 | ieee80211_stop_tx_ba_cb_irqsafe(mac->vif, ra, tid); | ||
763 | |||
764 | return 0; | ||
765 | } | ||
766 | |||
767 | /********************************************************* | ||
768 | * | ||
769 | * wq & timer callback functions | ||
770 | * | ||
771 | *********************************************************/ | ||
772 | void rtl_watchdog_wq_callback(void *data) | ||
773 | { | ||
774 | struct rtl_works *rtlworks = container_of_dwork_rtl(data, | ||
775 | struct rtl_works, | ||
776 | watchdog_wq); | ||
777 | struct ieee80211_hw *hw = rtlworks->hw; | ||
778 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
779 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | ||
780 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
781 | |||
782 | bool b_busytraffic = false; | ||
783 | bool b_higher_busytraffic = false; | ||
784 | bool b_higher_busyrxtraffic = false; | ||
785 | bool b_higher_busytxtraffic = false; | ||
786 | |||
787 | u8 idx = 0; | ||
788 | u32 rx_cnt_inp4eriod = 0; | ||
789 | u32 tx_cnt_inp4eriod = 0; | ||
790 | u32 aver_rx_cnt_inperiod = 0; | ||
791 | u32 aver_tx_cnt_inperiod = 0; | ||
792 | |||
793 | bool benter_ps = false; | ||
794 | |||
795 | if (is_hal_stop(rtlhal)) | ||
796 | return; | ||
797 | |||
798 | /* <1> Determine if action frame is allowed */ | ||
799 | if (mac->link_state > MAC80211_NOLINK) { | ||
800 | if (mac->cnt_after_linked < 20) | ||
801 | mac->cnt_after_linked++; | ||
802 | } else { | ||
803 | mac->cnt_after_linked = 0; | ||
804 | } | ||
805 | |||
806 | /* <2> DM */ | ||
807 | rtlpriv->cfg->ops->dm_watchdog(hw); | ||
808 | |||
809 | /* | ||
810 | *<3> to check if traffic busy, if | ||
811 | * busytraffic we don't change channel | ||
812 | */ | ||
813 | if (mac->link_state >= MAC80211_LINKED) { | ||
814 | |||
815 | /* (1) get aver_rx_cnt_inperiod & aver_tx_cnt_inperiod */ | ||
816 | for (idx = 0; idx <= 2; idx++) { | ||
817 | rtlpriv->link_info.num_rx_in4period[idx] = | ||
818 | rtlpriv->link_info.num_rx_in4period[idx + 1]; | ||
819 | rtlpriv->link_info.num_tx_in4period[idx] = | ||
820 | rtlpriv->link_info.num_tx_in4period[idx + 1]; | ||
821 | } | ||
822 | rtlpriv->link_info.num_rx_in4period[3] = | ||
823 | rtlpriv->link_info.num_rx_inperiod; | ||
824 | rtlpriv->link_info.num_tx_in4period[3] = | ||
825 | rtlpriv->link_info.num_tx_inperiod; | ||
826 | for (idx = 0; idx <= 3; idx++) { | ||
827 | rx_cnt_inp4eriod += | ||
828 | rtlpriv->link_info.num_rx_in4period[idx]; | ||
829 | tx_cnt_inp4eriod += | ||
830 | rtlpriv->link_info.num_tx_in4period[idx]; | ||
831 | } | ||
832 | aver_rx_cnt_inperiod = rx_cnt_inp4eriod / 4; | ||
833 | aver_tx_cnt_inperiod = tx_cnt_inp4eriod / 4; | ||
834 | |||
835 | /* (2) check traffic busy */ | ||
836 | if (aver_rx_cnt_inperiod > 100 || aver_tx_cnt_inperiod > 100) | ||
837 | b_busytraffic = true; | ||
838 | |||
839 | /* Higher Tx/Rx data. */ | ||
840 | if (aver_rx_cnt_inperiod > 4000 || | ||
841 | aver_tx_cnt_inperiod > 4000) { | ||
842 | b_higher_busytraffic = true; | ||
843 | |||
844 | /* Extremely high Rx data. */ | ||
845 | if (aver_rx_cnt_inperiod > 5000) | ||
846 | b_higher_busyrxtraffic = true; | ||
847 | else | ||
848 | b_higher_busytxtraffic = false; | ||
849 | } | ||
850 | |||
851 | if (((rtlpriv->link_info.num_rx_inperiod + | ||
852 | rtlpriv->link_info.num_tx_inperiod) > 8) || | ||
853 | (rtlpriv->link_info.num_rx_inperiod > 2)) | ||
854 | benter_ps = false; | ||
855 | else | ||
856 | benter_ps = true; | ||
857 | |||
858 | /* LeisurePS only work in infra mode. */ | ||
859 | if (benter_ps) | ||
860 | rtl_lps_enter(hw); | ||
861 | else | ||
862 | rtl_lps_leave(hw); | ||
863 | } | ||
864 | |||
865 | rtlpriv->link_info.num_rx_inperiod = 0; | ||
866 | rtlpriv->link_info.num_tx_inperiod = 0; | ||
867 | |||
868 | rtlpriv->link_info.b_busytraffic = b_busytraffic; | ||
869 | rtlpriv->link_info.b_higher_busytraffic = b_higher_busytraffic; | ||
870 | rtlpriv->link_info.b_higher_busyrxtraffic = b_higher_busyrxtraffic; | ||
871 | |||
872 | } | ||
873 | |||
874 | void rtl_watch_dog_timer_callback(unsigned long data) | ||
875 | { | ||
876 | struct ieee80211_hw *hw = (struct ieee80211_hw *)data; | ||
877 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
878 | |||
879 | queue_delayed_work(rtlpriv->works.rtl_wq, | ||
880 | &rtlpriv->works.watchdog_wq, 0); | ||
881 | |||
882 | mod_timer(&rtlpriv->works.watchdog_timer, | ||
883 | jiffies + MSECS(RTL_WATCH_DOG_TIME)); | ||
884 | } | ||
885 | |||
886 | /********************************************************* | ||
887 | * | ||
888 | * sysfs functions | ||
889 | * | ||
890 | *********************************************************/ | ||
891 | static ssize_t rtl_show_debug_level(struct device *d, | ||
892 | struct device_attribute *attr, char *buf) | ||
893 | { | ||
894 | struct ieee80211_hw *hw = dev_get_drvdata(d); | ||
895 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
896 | |||
897 | return sprintf(buf, "0x%08X\n", rtlpriv->dbg.global_debuglevel); | ||
898 | } | ||
899 | |||
900 | static ssize_t rtl_store_debug_level(struct device *d, | ||
901 | struct device_attribute *attr, | ||
902 | const char *buf, size_t count) | ||
903 | { | ||
904 | struct ieee80211_hw *hw = dev_get_drvdata(d); | ||
905 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
906 | unsigned long val; | ||
907 | int ret; | ||
908 | |||
909 | ret = strict_strtoul(buf, 0, &val); | ||
910 | if (ret) { | ||
911 | printk(KERN_DEBUG "%s is not in hex or decimal form.\n", buf); | ||
912 | } else { | ||
913 | rtlpriv->dbg.global_debuglevel = val; | ||
914 | printk(KERN_DEBUG "debuglevel:%x\n", | ||
915 | rtlpriv->dbg.global_debuglevel); | ||
916 | } | ||
917 | |||
918 | return strnlen(buf, count); | ||
919 | } | ||
920 | |||
921 | static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, | ||
922 | rtl_show_debug_level, rtl_store_debug_level); | ||
923 | |||
924 | static struct attribute *rtl_sysfs_entries[] = { | ||
925 | |||
926 | &dev_attr_debug_level.attr, | ||
927 | |||
928 | NULL | ||
929 | }; | ||
930 | |||
931 | /* | ||
932 | * "name" is folder name witch will be | ||
933 | * put in device directory like : | ||
934 | * sys/devices/pci0000:00/0000:00:1c.4/ | ||
935 | * 0000:06:00.0/rtl_sysfs | ||
936 | */ | ||
937 | struct attribute_group rtl_attribute_group = { | ||
938 | .name = "rtlsysfs", | ||
939 | .attrs = rtl_sysfs_entries, | ||
940 | }; | ||
941 | |||
942 | MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>"); | ||
943 | MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); | ||
944 | MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>"); | ||
945 | MODULE_LICENSE("GPL"); | ||
946 | MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core"); | ||
947 | |||
948 | static int __init rtl_core_module_init(void) | ||
949 | { | ||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | static void __exit rtl_core_module_exit(void) | ||
954 | { | ||
955 | } | ||
956 | |||
957 | module_init(rtl_core_module_init); | ||
958 | module_exit(rtl_core_module_exit); | ||