diff options
Diffstat (limited to 'drivers/net/wireless/mac80211_hwsim.c')
-rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 211 |
1 files changed, 168 insertions, 43 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 248d31a7aa33..c9e4a435b2fc 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
@@ -14,6 +14,8 @@ | |||
14 | * - RX filtering based on filter configuration (data->rx_filter) | 14 | * - RX filtering based on filter configuration (data->rx_filter) |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/spinlock.h> | ||
17 | #include <net/mac80211.h> | 19 | #include <net/mac80211.h> |
18 | #include <net/ieee80211_radiotap.h> | 20 | #include <net/ieee80211_radiotap.h> |
19 | #include <linux/if_arp.h> | 21 | #include <linux/if_arp.h> |
@@ -28,11 +30,56 @@ static int radios = 2; | |||
28 | module_param(radios, int, 0444); | 30 | module_param(radios, int, 0444); |
29 | MODULE_PARM_DESC(radios, "Number of simulated radios"); | 31 | MODULE_PARM_DESC(radios, "Number of simulated radios"); |
30 | 32 | ||
33 | struct hwsim_vif_priv { | ||
34 | u32 magic; | ||
35 | }; | ||
36 | |||
37 | #define HWSIM_VIF_MAGIC 0x69537748 | ||
38 | |||
39 | static inline void hwsim_check_magic(struct ieee80211_vif *vif) | ||
40 | { | ||
41 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
42 | WARN_ON(vp->magic != HWSIM_VIF_MAGIC); | ||
43 | } | ||
44 | |||
45 | static inline void hwsim_set_magic(struct ieee80211_vif *vif) | ||
46 | { | ||
47 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
48 | vp->magic = HWSIM_VIF_MAGIC; | ||
49 | } | ||
50 | |||
51 | static inline void hwsim_clear_magic(struct ieee80211_vif *vif) | ||
52 | { | ||
53 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
54 | vp->magic = 0; | ||
55 | } | ||
56 | |||
57 | struct hwsim_sta_priv { | ||
58 | u32 magic; | ||
59 | }; | ||
60 | |||
61 | #define HWSIM_STA_MAGIC 0x6d537748 | ||
62 | |||
63 | static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta) | ||
64 | { | ||
65 | struct hwsim_sta_priv *sp = (void *)sta->drv_priv; | ||
66 | WARN_ON(sp->magic != HWSIM_VIF_MAGIC); | ||
67 | } | ||
68 | |||
69 | static inline void hwsim_set_sta_magic(struct ieee80211_sta *sta) | ||
70 | { | ||
71 | struct hwsim_sta_priv *sp = (void *)sta->drv_priv; | ||
72 | sp->magic = HWSIM_VIF_MAGIC; | ||
73 | } | ||
74 | |||
75 | static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta) | ||
76 | { | ||
77 | struct hwsim_sta_priv *sp = (void *)sta->drv_priv; | ||
78 | sp->magic = 0; | ||
79 | } | ||
31 | 80 | ||
32 | static struct class *hwsim_class; | 81 | static struct class *hwsim_class; |
33 | 82 | ||
34 | static struct ieee80211_hw **hwsim_radios; | ||
35 | static int hwsim_radio_count; | ||
36 | static struct net_device *hwsim_mon; /* global monitor netdev */ | 83 | static struct net_device *hwsim_mon; /* global monitor netdev */ |
37 | 84 | ||
38 | 85 | ||
@@ -68,7 +115,12 @@ static const struct ieee80211_rate hwsim_rates[] = { | |||
68 | { .bitrate = 540 } | 115 | { .bitrate = 540 } |
69 | }; | 116 | }; |
70 | 117 | ||
118 | static spinlock_t hwsim_radio_lock; | ||
119 | static struct list_head hwsim_radios; | ||
120 | |||
71 | struct mac80211_hwsim_data { | 121 | struct mac80211_hwsim_data { |
122 | struct list_head list; | ||
123 | struct ieee80211_hw *hw; | ||
72 | struct device *dev; | 124 | struct device *dev; |
73 | struct ieee80211_supported_band band; | 125 | struct ieee80211_supported_band band; |
74 | struct ieee80211_channel channels[ARRAY_SIZE(hwsim_channels)]; | 126 | struct ieee80211_channel channels[ARRAY_SIZE(hwsim_channels)]; |
@@ -144,11 +196,11 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, | |||
144 | } | 196 | } |
145 | 197 | ||
146 | 198 | ||
147 | static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | 199 | static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, |
148 | struct sk_buff *skb) | 200 | struct sk_buff *skb) |
149 | { | 201 | { |
150 | struct mac80211_hwsim_data *data = hw->priv; | 202 | struct mac80211_hwsim_data *data = hw->priv, *data2; |
151 | int i, ack = 0; | 203 | bool ack = false; |
152 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 204 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
153 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 205 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
154 | struct ieee80211_rx_status rx_status; | 206 | struct ieee80211_rx_status rx_status; |
@@ -161,13 +213,13 @@ static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | |||
161 | /* TODO: simulate signal strength (and optional packet drop) */ | 213 | /* TODO: simulate signal strength (and optional packet drop) */ |
162 | 214 | ||
163 | /* Copy skb to all enabled radios that are on the current frequency */ | 215 | /* Copy skb to all enabled radios that are on the current frequency */ |
164 | for (i = 0; i < hwsim_radio_count; i++) { | 216 | spin_lock(&hwsim_radio_lock); |
165 | struct mac80211_hwsim_data *data2; | 217 | list_for_each_entry(data2, &hwsim_radios, list) { |
166 | struct sk_buff *nskb; | 218 | struct sk_buff *nskb; |
167 | 219 | ||
168 | if (hwsim_radios[i] == NULL || hwsim_radios[i] == hw) | 220 | if (data == data2) |
169 | continue; | 221 | continue; |
170 | data2 = hwsim_radios[i]->priv; | 222 | |
171 | if (!data2->started || !data2->radio_enabled || | 223 | if (!data2->started || !data2->radio_enabled || |
172 | data->channel->center_freq != data2->channel->center_freq) | 224 | data->channel->center_freq != data2->channel->center_freq) |
173 | continue; | 225 | continue; |
@@ -176,11 +228,12 @@ static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | |||
176 | if (nskb == NULL) | 228 | if (nskb == NULL) |
177 | continue; | 229 | continue; |
178 | 230 | ||
179 | if (memcmp(hdr->addr1, hwsim_radios[i]->wiphy->perm_addr, | 231 | if (memcmp(hdr->addr1, data2->hw->wiphy->perm_addr, |
180 | ETH_ALEN) == 0) | 232 | ETH_ALEN) == 0) |
181 | ack = 1; | 233 | ack = true; |
182 | ieee80211_rx_irqsafe(hwsim_radios[i], nskb, &rx_status); | 234 | ieee80211_rx_irqsafe(data2->hw, nskb, &rx_status); |
183 | } | 235 | } |
236 | spin_unlock(&hwsim_radio_lock); | ||
184 | 237 | ||
185 | return ack; | 238 | return ack; |
186 | } | 239 | } |
@@ -189,7 +242,7 @@ static int mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | |||
189 | static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | 242 | static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) |
190 | { | 243 | { |
191 | struct mac80211_hwsim_data *data = hw->priv; | 244 | struct mac80211_hwsim_data *data = hw->priv; |
192 | int ack; | 245 | bool ack; |
193 | struct ieee80211_tx_info *txi; | 246 | struct ieee80211_tx_info *txi; |
194 | 247 | ||
195 | mac80211_hwsim_monitor_rx(hw, skb); | 248 | mac80211_hwsim_monitor_rx(hw, skb); |
@@ -210,6 +263,12 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
210 | ack = mac80211_hwsim_tx_frame(hw, skb); | 263 | ack = mac80211_hwsim_tx_frame(hw, skb); |
211 | 264 | ||
212 | txi = IEEE80211_SKB_CB(skb); | 265 | txi = IEEE80211_SKB_CB(skb); |
266 | |||
267 | if (txi->control.vif) | ||
268 | hwsim_check_magic(txi->control.vif); | ||
269 | if (txi->control.sta) | ||
270 | hwsim_check_sta_magic(txi->control.sta); | ||
271 | |||
213 | memset(&txi->status, 0, sizeof(txi->status)); | 272 | memset(&txi->status, 0, sizeof(txi->status)); |
214 | if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) { | 273 | if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) { |
215 | if (ack) | 274 | if (ack) |
@@ -246,6 +305,7 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, | |||
246 | printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n", | 305 | printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n", |
247 | wiphy_name(hw->wiphy), __func__, conf->type, | 306 | wiphy_name(hw->wiphy), __func__, conf->type, |
248 | print_mac(mac, conf->mac_addr)); | 307 | print_mac(mac, conf->mac_addr)); |
308 | hwsim_set_magic(conf->vif); | ||
249 | return 0; | 309 | return 0; |
250 | } | 310 | } |
251 | 311 | ||
@@ -257,6 +317,8 @@ static void mac80211_hwsim_remove_interface( | |||
257 | printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n", | 317 | printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n", |
258 | wiphy_name(hw->wiphy), __func__, conf->type, | 318 | wiphy_name(hw->wiphy), __func__, conf->type, |
259 | print_mac(mac, conf->mac_addr)); | 319 | print_mac(mac, conf->mac_addr)); |
320 | hwsim_check_magic(conf->vif); | ||
321 | hwsim_clear_magic(conf->vif); | ||
260 | } | 322 | } |
261 | 323 | ||
262 | 324 | ||
@@ -267,7 +329,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, | |||
267 | struct sk_buff *skb; | 329 | struct sk_buff *skb; |
268 | struct ieee80211_tx_info *info; | 330 | struct ieee80211_tx_info *info; |
269 | 331 | ||
270 | if (vif->type != IEEE80211_IF_TYPE_AP) | 332 | hwsim_check_magic(vif); |
333 | |||
334 | if (vif->type != NL80211_IFTYPE_AP) | ||
271 | return; | 335 | return; |
272 | 336 | ||
273 | skb = ieee80211_beacon_get(hw, vif); | 337 | skb = ieee80211_beacon_get(hw, vif); |
@@ -341,7 +405,45 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, | |||
341 | *total_flags = data->rx_filter; | 405 | *total_flags = data->rx_filter; |
342 | } | 406 | } |
343 | 407 | ||
408 | static int mac80211_hwsim_config_interface(struct ieee80211_hw *hw, | ||
409 | struct ieee80211_vif *vif, | ||
410 | struct ieee80211_if_conf *conf) | ||
411 | { | ||
412 | hwsim_check_magic(vif); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, | ||
417 | struct ieee80211_vif *vif, | ||
418 | struct ieee80211_bss_conf *info, | ||
419 | u32 changed) | ||
420 | { | ||
421 | hwsim_check_magic(vif); | ||
422 | } | ||
423 | |||
424 | static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw, | ||
425 | struct ieee80211_vif *vif, | ||
426 | enum sta_notify_cmd cmd, | ||
427 | struct ieee80211_sta *sta) | ||
428 | { | ||
429 | hwsim_check_magic(vif); | ||
430 | switch (cmd) { | ||
431 | case STA_NOTIFY_ADD: | ||
432 | hwsim_set_sta_magic(sta); | ||
433 | break; | ||
434 | case STA_NOTIFY_REMOVE: | ||
435 | hwsim_clear_sta_magic(sta); | ||
436 | break; | ||
437 | } | ||
438 | } | ||
344 | 439 | ||
440 | static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw, | ||
441 | struct ieee80211_sta *sta, | ||
442 | bool set) | ||
443 | { | ||
444 | hwsim_check_sta_magic(sta); | ||
445 | return 0; | ||
446 | } | ||
345 | 447 | ||
346 | static const struct ieee80211_ops mac80211_hwsim_ops = | 448 | static const struct ieee80211_ops mac80211_hwsim_ops = |
347 | { | 449 | { |
@@ -352,23 +454,30 @@ static const struct ieee80211_ops mac80211_hwsim_ops = | |||
352 | .remove_interface = mac80211_hwsim_remove_interface, | 454 | .remove_interface = mac80211_hwsim_remove_interface, |
353 | .config = mac80211_hwsim_config, | 455 | .config = mac80211_hwsim_config, |
354 | .configure_filter = mac80211_hwsim_configure_filter, | 456 | .configure_filter = mac80211_hwsim_configure_filter, |
457 | .config_interface = mac80211_hwsim_config_interface, | ||
458 | .bss_info_changed = mac80211_hwsim_bss_info_changed, | ||
459 | .sta_notify = mac80211_hwsim_sta_notify, | ||
460 | .set_tim = mac80211_hwsim_set_tim, | ||
355 | }; | 461 | }; |
356 | 462 | ||
357 | 463 | ||
358 | static void mac80211_hwsim_free(void) | 464 | static void mac80211_hwsim_free(void) |
359 | { | 465 | { |
360 | int i; | 466 | struct list_head tmplist, *i, *tmp; |
361 | 467 | struct mac80211_hwsim_data *data; | |
362 | for (i = 0; i < hwsim_radio_count; i++) { | 468 | |
363 | if (hwsim_radios[i]) { | 469 | INIT_LIST_HEAD(&tmplist); |
364 | struct mac80211_hwsim_data *data; | 470 | |
365 | data = hwsim_radios[i]->priv; | 471 | spin_lock_bh(&hwsim_radio_lock); |
366 | ieee80211_unregister_hw(hwsim_radios[i]); | 472 | list_for_each_safe(i, tmp, &hwsim_radios) |
367 | device_unregister(data->dev); | 473 | list_move(i, &tmplist); |
368 | ieee80211_free_hw(hwsim_radios[i]); | 474 | spin_unlock_bh(&hwsim_radio_lock); |
369 | } | 475 | |
476 | list_for_each_entry(data, &tmplist, list) { | ||
477 | ieee80211_unregister_hw(data->hw); | ||
478 | device_unregister(data->dev); | ||
479 | ieee80211_free_hw(data->hw); | ||
370 | } | 480 | } |
371 | kfree(hwsim_radios); | ||
372 | class_destroy(hwsim_class); | 481 | class_destroy(hwsim_class); |
373 | } | 482 | } |
374 | 483 | ||
@@ -398,37 +507,32 @@ static int __init init_mac80211_hwsim(void) | |||
398 | struct ieee80211_hw *hw; | 507 | struct ieee80211_hw *hw; |
399 | DECLARE_MAC_BUF(mac); | 508 | DECLARE_MAC_BUF(mac); |
400 | 509 | ||
401 | if (radios < 1 || radios > 65535) | 510 | if (radios < 1 || radios > 100) |
402 | return -EINVAL; | 511 | return -EINVAL; |
403 | 512 | ||
404 | hwsim_radio_count = radios; | 513 | spin_lock_init(&hwsim_radio_lock); |
405 | hwsim_radios = kcalloc(hwsim_radio_count, | 514 | INIT_LIST_HEAD(&hwsim_radios); |
406 | sizeof(struct ieee80211_hw *), GFP_KERNEL); | ||
407 | if (hwsim_radios == NULL) | ||
408 | return -ENOMEM; | ||
409 | 515 | ||
410 | hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); | 516 | hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); |
411 | if (IS_ERR(hwsim_class)) { | 517 | if (IS_ERR(hwsim_class)) |
412 | kfree(hwsim_radios); | ||
413 | return PTR_ERR(hwsim_class); | 518 | return PTR_ERR(hwsim_class); |
414 | } | ||
415 | 519 | ||
416 | memset(addr, 0, ETH_ALEN); | 520 | memset(addr, 0, ETH_ALEN); |
417 | addr[0] = 0x02; | 521 | addr[0] = 0x02; |
418 | 522 | ||
419 | for (i = 0; i < hwsim_radio_count; i++) { | 523 | for (i = 0; i < radios; i++) { |
420 | printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n", | 524 | printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n", |
421 | i); | 525 | i); |
422 | hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops); | 526 | hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops); |
423 | if (hw == NULL) { | 527 | if (!hw) { |
424 | printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw " | 528 | printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw " |
425 | "failed\n"); | 529 | "failed\n"); |
426 | err = -ENOMEM; | 530 | err = -ENOMEM; |
427 | goto failed; | 531 | goto failed; |
428 | } | 532 | } |
429 | hwsim_radios[i] = hw; | ||
430 | |||
431 | data = hw->priv; | 533 | data = hw->priv; |
534 | data->hw = hw; | ||
535 | |||
432 | data->dev = device_create_drvdata(hwsim_class, NULL, 0, hw, | 536 | data->dev = device_create_drvdata(hwsim_class, NULL, 0, hw, |
433 | "hwsim%d", i); | 537 | "hwsim%d", i); |
434 | if (IS_ERR(data->dev)) { | 538 | if (IS_ERR(data->dev)) { |
@@ -446,7 +550,15 @@ static int __init init_mac80211_hwsim(void) | |||
446 | SET_IEEE80211_PERM_ADDR(hw, addr); | 550 | SET_IEEE80211_PERM_ADDR(hw, addr); |
447 | 551 | ||
448 | hw->channel_change_time = 1; | 552 | hw->channel_change_time = 1; |
449 | hw->queues = 1; | 553 | hw->queues = 4; |
554 | hw->wiphy->interface_modes = | ||
555 | BIT(NL80211_IFTYPE_STATION) | | ||
556 | BIT(NL80211_IFTYPE_AP); | ||
557 | hw->ampdu_queues = 1; | ||
558 | |||
559 | /* ask mac80211 to reserve space for magic */ | ||
560 | hw->vif_data_size = sizeof(struct hwsim_vif_priv); | ||
561 | hw->sta_data_size = sizeof(struct hwsim_sta_priv); | ||
450 | 562 | ||
451 | memcpy(data->channels, hwsim_channels, sizeof(hwsim_channels)); | 563 | memcpy(data->channels, hwsim_channels, sizeof(hwsim_channels)); |
452 | memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); | 564 | memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); |
@@ -454,6 +566,19 @@ static int __init init_mac80211_hwsim(void) | |||
454 | data->band.n_channels = ARRAY_SIZE(hwsim_channels); | 566 | data->band.n_channels = ARRAY_SIZE(hwsim_channels); |
455 | data->band.bitrates = data->rates; | 567 | data->band.bitrates = data->rates; |
456 | data->band.n_bitrates = ARRAY_SIZE(hwsim_rates); | 568 | data->band.n_bitrates = ARRAY_SIZE(hwsim_rates); |
569 | data->band.ht_info.ht_supported = 1; | ||
570 | data->band.ht_info.cap = IEEE80211_HT_CAP_SUP_WIDTH | | ||
571 | IEEE80211_HT_CAP_GRN_FLD | | ||
572 | IEEE80211_HT_CAP_SGI_40 | | ||
573 | IEEE80211_HT_CAP_DSSSCCK40; | ||
574 | data->band.ht_info.ampdu_factor = 0x3; | ||
575 | data->band.ht_info.ampdu_density = 0x6; | ||
576 | memset(data->band.ht_info.supp_mcs_set, 0, | ||
577 | sizeof(data->band.ht_info.supp_mcs_set)); | ||
578 | data->band.ht_info.supp_mcs_set[0] = 0xff; | ||
579 | data->band.ht_info.supp_mcs_set[1] = 0xff; | ||
580 | data->band.ht_info.supp_mcs_set[12] = | ||
581 | IEEE80211_HT_CAP_MCS_TX_DEFINED; | ||
457 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &data->band; | 582 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &data->band; |
458 | 583 | ||
459 | err = ieee80211_register_hw(hw); | 584 | err = ieee80211_register_hw(hw); |
@@ -469,6 +594,8 @@ static int __init init_mac80211_hwsim(void) | |||
469 | 594 | ||
470 | setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, | 595 | setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, |
471 | (unsigned long) hw); | 596 | (unsigned long) hw); |
597 | |||
598 | list_add_tail(&data->list, &hwsim_radios); | ||
472 | } | 599 | } |
473 | 600 | ||
474 | hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); | 601 | hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); |
@@ -500,7 +627,6 @@ failed_hw: | |||
500 | device_unregister(data->dev); | 627 | device_unregister(data->dev); |
501 | failed_drvdata: | 628 | failed_drvdata: |
502 | ieee80211_free_hw(hw); | 629 | ieee80211_free_hw(hw); |
503 | hwsim_radios[i] = NULL; | ||
504 | failed: | 630 | failed: |
505 | mac80211_hwsim_free(); | 631 | mac80211_hwsim_free(); |
506 | return err; | 632 | return err; |
@@ -509,8 +635,7 @@ failed: | |||
509 | 635 | ||
510 | static void __exit exit_mac80211_hwsim(void) | 636 | static void __exit exit_mac80211_hwsim(void) |
511 | { | 637 | { |
512 | printk(KERN_DEBUG "mac80211_hwsim: unregister %d radios\n", | 638 | printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n"); |
513 | hwsim_radio_count); | ||
514 | 639 | ||
515 | unregister_netdev(hwsim_mon); | 640 | unregister_netdev(hwsim_mon); |
516 | mac80211_hwsim_free(); | 641 | mac80211_hwsim_free(); |