diff options
| author | Johannes Berg <johannes.berg@intel.com> | 2014-01-06 17:12:12 -0500 |
|---|---|---|
| committer | Johannes Berg <johannes.berg@intel.com> | 2014-01-10 14:12:58 -0500 |
| commit | de0421d53bfb5f7783e5228f8cf49f2ae7140d54 (patch) | |
| tree | d0390a69f5cb622845f4313b8db5ada782919db0 | |
| parent | 9ddd12af103f60fc284103740c07219d888d1aee (diff) | |
mac80211_hwsim: shuffle code to prepare for dynamic radios
This will make the next patch, adding support for netlink,
smaller and more readable. The code is not modified here.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
| -rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 826 |
1 files changed, 413 insertions, 413 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index ca156a5ed305..1c51c33c385f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
| @@ -215,10 +215,53 @@ static const struct ieee80211_rate hwsim_rates[] = { | |||
| 215 | { .bitrate = 540 } | 215 | { .bitrate = 540 } |
| 216 | }; | 216 | }; |
| 217 | 217 | ||
| 218 | static const struct ieee80211_iface_limit hwsim_if_limits[] = { | ||
| 219 | { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, | ||
| 220 | { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | | ||
| 221 | BIT(NL80211_IFTYPE_P2P_CLIENT) | | ||
| 222 | #ifdef CONFIG_MAC80211_MESH | ||
| 223 | BIT(NL80211_IFTYPE_MESH_POINT) | | ||
| 224 | #endif | ||
| 225 | BIT(NL80211_IFTYPE_AP) | | ||
| 226 | BIT(NL80211_IFTYPE_P2P_GO) }, | ||
| 227 | { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, | ||
| 228 | }; | ||
| 229 | |||
| 230 | static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { | ||
| 231 | { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, | ||
| 232 | }; | ||
| 233 | |||
| 234 | static const struct ieee80211_iface_combination hwsim_if_comb[] = { | ||
| 235 | { | ||
| 236 | .limits = hwsim_if_limits, | ||
| 237 | .n_limits = ARRAY_SIZE(hwsim_if_limits), | ||
| 238 | .max_interfaces = 2048, | ||
| 239 | .num_different_channels = 1, | ||
| 240 | }, | ||
| 241 | { | ||
| 242 | .limits = hwsim_if_dfs_limits, | ||
| 243 | .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), | ||
| 244 | .max_interfaces = 8, | ||
| 245 | .num_different_channels = 1, | ||
| 246 | .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | | ||
| 247 | BIT(NL80211_CHAN_WIDTH_20) | | ||
| 248 | BIT(NL80211_CHAN_WIDTH_40) | | ||
| 249 | BIT(NL80211_CHAN_WIDTH_80) | | ||
| 250 | BIT(NL80211_CHAN_WIDTH_160), | ||
| 251 | } | ||
| 252 | }; | ||
| 253 | |||
| 218 | static spinlock_t hwsim_radio_lock; | 254 | static spinlock_t hwsim_radio_lock; |
| 219 | static struct list_head hwsim_radios; | 255 | static struct list_head hwsim_radios; |
| 220 | static int hwsim_radio_idx; | 256 | static int hwsim_radio_idx; |
| 221 | 257 | ||
| 258 | static struct platform_driver mac80211_hwsim_driver = { | ||
| 259 | .driver = { | ||
| 260 | .name = "mac80211_hwsim", | ||
| 261 | .owner = THIS_MODULE, | ||
| 262 | }, | ||
| 263 | }; | ||
| 264 | |||
| 222 | struct mac80211_hwsim_data { | 265 | struct mac80211_hwsim_data { |
| 223 | struct list_head list; | 266 | struct list_head list; |
| 224 | struct ieee80211_hw *hw; | 267 | struct ieee80211_hw *hw; |
| @@ -311,6 +354,161 @@ static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { | |||
| 311 | [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, | 354 | [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, |
| 312 | }; | 355 | }; |
| 313 | 356 | ||
| 357 | static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | ||
| 358 | struct sk_buff *skb, | ||
| 359 | struct ieee80211_channel *chan); | ||
| 360 | |||
| 361 | /* sysfs attributes */ | ||
| 362 | static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) | ||
| 363 | { | ||
| 364 | struct mac80211_hwsim_data *data = dat; | ||
| 365 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
| 366 | struct sk_buff *skb; | ||
| 367 | struct ieee80211_pspoll *pspoll; | ||
| 368 | |||
| 369 | if (!vp->assoc) | ||
| 370 | return; | ||
| 371 | |||
| 372 | wiphy_debug(data->hw->wiphy, | ||
| 373 | "%s: send PS-Poll to %pM for aid %d\n", | ||
| 374 | __func__, vp->bssid, vp->aid); | ||
| 375 | |||
| 376 | skb = dev_alloc_skb(sizeof(*pspoll)); | ||
| 377 | if (!skb) | ||
| 378 | return; | ||
| 379 | pspoll = (void *) skb_put(skb, sizeof(*pspoll)); | ||
| 380 | pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | | ||
| 381 | IEEE80211_STYPE_PSPOLL | | ||
| 382 | IEEE80211_FCTL_PM); | ||
| 383 | pspoll->aid = cpu_to_le16(0xc000 | vp->aid); | ||
| 384 | memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); | ||
| 385 | memcpy(pspoll->ta, mac, ETH_ALEN); | ||
| 386 | |||
| 387 | rcu_read_lock(); | ||
| 388 | mac80211_hwsim_tx_frame(data->hw, skb, | ||
| 389 | rcu_dereference(vif->chanctx_conf)->def.chan); | ||
| 390 | rcu_read_unlock(); | ||
| 391 | } | ||
| 392 | |||
| 393 | static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, | ||
| 394 | struct ieee80211_vif *vif, int ps) | ||
| 395 | { | ||
| 396 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
| 397 | struct sk_buff *skb; | ||
| 398 | struct ieee80211_hdr *hdr; | ||
| 399 | |||
| 400 | if (!vp->assoc) | ||
| 401 | return; | ||
| 402 | |||
| 403 | wiphy_debug(data->hw->wiphy, | ||
| 404 | "%s: send data::nullfunc to %pM ps=%d\n", | ||
| 405 | __func__, vp->bssid, ps); | ||
| 406 | |||
| 407 | skb = dev_alloc_skb(sizeof(*hdr)); | ||
| 408 | if (!skb) | ||
| 409 | return; | ||
| 410 | hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); | ||
| 411 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
| 412 | IEEE80211_STYPE_NULLFUNC | | ||
| 413 | (ps ? IEEE80211_FCTL_PM : 0)); | ||
| 414 | hdr->duration_id = cpu_to_le16(0); | ||
| 415 | memcpy(hdr->addr1, vp->bssid, ETH_ALEN); | ||
| 416 | memcpy(hdr->addr2, mac, ETH_ALEN); | ||
| 417 | memcpy(hdr->addr3, vp->bssid, ETH_ALEN); | ||
| 418 | |||
| 419 | rcu_read_lock(); | ||
| 420 | mac80211_hwsim_tx_frame(data->hw, skb, | ||
| 421 | rcu_dereference(vif->chanctx_conf)->def.chan); | ||
| 422 | rcu_read_unlock(); | ||
| 423 | } | ||
| 424 | |||
| 425 | |||
| 426 | static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, | ||
| 427 | struct ieee80211_vif *vif) | ||
| 428 | { | ||
| 429 | struct mac80211_hwsim_data *data = dat; | ||
| 430 | hwsim_send_nullfunc(data, mac, vif, 1); | ||
| 431 | } | ||
| 432 | |||
| 433 | static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, | ||
| 434 | struct ieee80211_vif *vif) | ||
| 435 | { | ||
| 436 | struct mac80211_hwsim_data *data = dat; | ||
| 437 | hwsim_send_nullfunc(data, mac, vif, 0); | ||
| 438 | } | ||
| 439 | |||
| 440 | static int hwsim_fops_ps_read(void *dat, u64 *val) | ||
| 441 | { | ||
| 442 | struct mac80211_hwsim_data *data = dat; | ||
| 443 | *val = data->ps; | ||
| 444 | return 0; | ||
| 445 | } | ||
| 446 | |||
| 447 | static int hwsim_fops_ps_write(void *dat, u64 val) | ||
| 448 | { | ||
| 449 | struct mac80211_hwsim_data *data = dat; | ||
| 450 | enum ps_mode old_ps; | ||
| 451 | |||
| 452 | if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && | ||
| 453 | val != PS_MANUAL_POLL) | ||
| 454 | return -EINVAL; | ||
| 455 | |||
| 456 | old_ps = data->ps; | ||
| 457 | data->ps = val; | ||
| 458 | |||
| 459 | if (val == PS_MANUAL_POLL) { | ||
| 460 | ieee80211_iterate_active_interfaces(data->hw, | ||
| 461 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 462 | hwsim_send_ps_poll, data); | ||
| 463 | data->ps_poll_pending = true; | ||
| 464 | } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { | ||
| 465 | ieee80211_iterate_active_interfaces(data->hw, | ||
| 466 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 467 | hwsim_send_nullfunc_ps, | ||
| 468 | data); | ||
| 469 | } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { | ||
| 470 | ieee80211_iterate_active_interfaces(data->hw, | ||
| 471 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 472 | hwsim_send_nullfunc_no_ps, | ||
| 473 | data); | ||
| 474 | } | ||
| 475 | |||
| 476 | return 0; | ||
| 477 | } | ||
| 478 | |||
| 479 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, | ||
| 480 | "%llu\n"); | ||
| 481 | |||
| 482 | static int hwsim_write_simulate_radar(void *dat, u64 val) | ||
| 483 | { | ||
| 484 | struct mac80211_hwsim_data *data = dat; | ||
| 485 | |||
| 486 | ieee80211_radar_detected(data->hw); | ||
| 487 | |||
| 488 | return 0; | ||
| 489 | } | ||
| 490 | |||
| 491 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL, | ||
| 492 | hwsim_write_simulate_radar, "%llu\n"); | ||
| 493 | |||
| 494 | static int hwsim_fops_group_read(void *dat, u64 *val) | ||
| 495 | { | ||
| 496 | struct mac80211_hwsim_data *data = dat; | ||
| 497 | *val = data->group; | ||
| 498 | return 0; | ||
| 499 | } | ||
| 500 | |||
| 501 | static int hwsim_fops_group_write(void *dat, u64 val) | ||
| 502 | { | ||
| 503 | struct mac80211_hwsim_data *data = dat; | ||
| 504 | data->group = val; | ||
| 505 | return 0; | ||
| 506 | } | ||
| 507 | |||
| 508 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, | ||
| 509 | hwsim_fops_group_read, hwsim_fops_group_write, | ||
| 510 | "%llx\n"); | ||
| 511 | |||
| 314 | static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, | 512 | static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, |
| 315 | struct net_device *dev) | 513 | struct net_device *dev) |
| 316 | { | 514 | { |
| @@ -1283,8 +1481,6 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { | |||
| 1283 | [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, | 1481 | [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, |
| 1284 | }; | 1482 | }; |
| 1285 | 1483 | ||
| 1286 | static int hwsim_fops_ps_write(void *dat, u64 val); | ||
| 1287 | |||
| 1288 | static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, | 1484 | static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, |
| 1289 | struct ieee80211_vif *vif, | 1485 | struct ieee80211_vif *vif, |
| 1290 | void *data, int len) | 1486 | void *data, int len) |
| @@ -1622,6 +1818,221 @@ static const struct ieee80211_ops mac80211_hwsim_ops = { | |||
| 1622 | 1818 | ||
| 1623 | static struct ieee80211_ops mac80211_hwsim_mchan_ops; | 1819 | static struct ieee80211_ops mac80211_hwsim_mchan_ops; |
| 1624 | 1820 | ||
| 1821 | static int __init mac80211_hwsim_create_radio(void) | ||
| 1822 | { | ||
| 1823 | int err; | ||
| 1824 | u8 addr[ETH_ALEN]; | ||
| 1825 | struct mac80211_hwsim_data *data; | ||
| 1826 | struct ieee80211_hw *hw; | ||
| 1827 | enum ieee80211_band band; | ||
| 1828 | const struct ieee80211_ops *ops = &mac80211_hwsim_ops; | ||
| 1829 | int idx; | ||
| 1830 | |||
| 1831 | spin_lock_bh(&hwsim_radio_lock); | ||
| 1832 | idx = hwsim_radio_idx++; | ||
| 1833 | spin_unlock_bh(&hwsim_radio_lock); | ||
| 1834 | |||
| 1835 | if (channels > 1) | ||
| 1836 | ops = &mac80211_hwsim_mchan_ops; | ||
| 1837 | hw = ieee80211_alloc_hw(sizeof(*data), ops); | ||
| 1838 | if (!hw) { | ||
| 1839 | printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n"); | ||
| 1840 | err = -ENOMEM; | ||
| 1841 | goto failed; | ||
| 1842 | } | ||
| 1843 | data = hw->priv; | ||
| 1844 | data->hw = hw; | ||
| 1845 | |||
| 1846 | data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); | ||
| 1847 | if (IS_ERR(data->dev)) { | ||
| 1848 | printk(KERN_DEBUG | ||
| 1849 | "mac80211_hwsim: device_create failed (%ld)\n", | ||
| 1850 | PTR_ERR(data->dev)); | ||
| 1851 | err = -ENOMEM; | ||
| 1852 | goto failed_drvdata; | ||
| 1853 | } | ||
| 1854 | data->dev->driver = &mac80211_hwsim_driver.driver; | ||
| 1855 | err = device_bind_driver(data->dev); | ||
| 1856 | if (err != 0) { | ||
| 1857 | printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n", | ||
| 1858 | err); | ||
| 1859 | goto failed_hw; | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | skb_queue_head_init(&data->pending); | ||
| 1863 | |||
| 1864 | SET_IEEE80211_DEV(hw, data->dev); | ||
| 1865 | memset(addr, 0, ETH_ALEN); | ||
| 1866 | addr[0] = 0x02; | ||
| 1867 | addr[3] = idx >> 8; | ||
| 1868 | addr[4] = idx; | ||
| 1869 | memcpy(data->addresses[0].addr, addr, ETH_ALEN); | ||
| 1870 | memcpy(data->addresses[1].addr, addr, ETH_ALEN); | ||
| 1871 | data->addresses[1].addr[0] |= 0x40; | ||
| 1872 | hw->wiphy->n_addresses = 2; | ||
| 1873 | hw->wiphy->addresses = data->addresses; | ||
| 1874 | |||
| 1875 | data->channels = channels; | ||
| 1876 | |||
| 1877 | if (data->channels > 1) { | ||
| 1878 | hw->wiphy->max_scan_ssids = 255; | ||
| 1879 | hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; | ||
| 1880 | hw->wiphy->max_remain_on_channel_duration = 1000; | ||
| 1881 | /* For channels > 1 DFS is not allowed */ | ||
| 1882 | hw->wiphy->n_iface_combinations = 1; | ||
| 1883 | hw->wiphy->iface_combinations = &data->if_combination; | ||
| 1884 | data->if_combination = hwsim_if_comb[0]; | ||
| 1885 | data->if_combination.num_different_channels = data->channels; | ||
| 1886 | } else { | ||
| 1887 | hw->wiphy->iface_combinations = hwsim_if_comb; | ||
| 1888 | hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); | ||
| 1889 | } | ||
| 1890 | |||
| 1891 | INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); | ||
| 1892 | INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); | ||
| 1893 | |||
| 1894 | hw->queues = 5; | ||
| 1895 | hw->offchannel_tx_hw_queue = 4; | ||
| 1896 | hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | ||
| 1897 | BIT(NL80211_IFTYPE_AP) | | ||
| 1898 | BIT(NL80211_IFTYPE_P2P_CLIENT) | | ||
| 1899 | BIT(NL80211_IFTYPE_P2P_GO) | | ||
| 1900 | BIT(NL80211_IFTYPE_ADHOC) | | ||
| 1901 | BIT(NL80211_IFTYPE_MESH_POINT) | | ||
| 1902 | BIT(NL80211_IFTYPE_P2P_DEVICE); | ||
| 1903 | |||
| 1904 | hw->flags = IEEE80211_HW_MFP_CAPABLE | | ||
| 1905 | IEEE80211_HW_SIGNAL_DBM | | ||
| 1906 | IEEE80211_HW_SUPPORTS_STATIC_SMPS | | ||
| 1907 | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | | ||
| 1908 | IEEE80211_HW_AMPDU_AGGREGATION | | ||
| 1909 | IEEE80211_HW_WANT_MONITOR_VIF | | ||
| 1910 | IEEE80211_HW_QUEUE_CONTROL | | ||
| 1911 | IEEE80211_HW_SUPPORTS_HT_CCK_RATES; | ||
| 1912 | if (rctbl) | ||
| 1913 | hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; | ||
| 1914 | |||
| 1915 | hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | | ||
| 1916 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | | ||
| 1917 | WIPHY_FLAG_AP_UAPSD; | ||
| 1918 | hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; | ||
| 1919 | |||
| 1920 | /* ask mac80211 to reserve space for magic */ | ||
| 1921 | hw->vif_data_size = sizeof(struct hwsim_vif_priv); | ||
| 1922 | hw->sta_data_size = sizeof(struct hwsim_sta_priv); | ||
| 1923 | hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); | ||
| 1924 | |||
| 1925 | memcpy(data->channels_2ghz, hwsim_channels_2ghz, | ||
| 1926 | sizeof(hwsim_channels_2ghz)); | ||
| 1927 | memcpy(data->channels_5ghz, hwsim_channels_5ghz, | ||
| 1928 | sizeof(hwsim_channels_5ghz)); | ||
| 1929 | memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); | ||
| 1930 | |||
| 1931 | for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { | ||
| 1932 | struct ieee80211_supported_band *sband = &data->bands[band]; | ||
| 1933 | switch (band) { | ||
| 1934 | case IEEE80211_BAND_2GHZ: | ||
| 1935 | sband->channels = data->channels_2ghz; | ||
| 1936 | sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); | ||
| 1937 | sband->bitrates = data->rates; | ||
| 1938 | sband->n_bitrates = ARRAY_SIZE(hwsim_rates); | ||
| 1939 | break; | ||
| 1940 | case IEEE80211_BAND_5GHZ: | ||
| 1941 | sband->channels = data->channels_5ghz; | ||
| 1942 | sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); | ||
| 1943 | sband->bitrates = data->rates + 4; | ||
| 1944 | sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; | ||
| 1945 | break; | ||
| 1946 | default: | ||
| 1947 | continue; | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | sband->ht_cap.ht_supported = true; | ||
| 1951 | sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | ||
| 1952 | IEEE80211_HT_CAP_GRN_FLD | | ||
| 1953 | IEEE80211_HT_CAP_SGI_40 | | ||
| 1954 | IEEE80211_HT_CAP_DSSSCCK40; | ||
| 1955 | sband->ht_cap.ampdu_factor = 0x3; | ||
| 1956 | sband->ht_cap.ampdu_density = 0x6; | ||
| 1957 | memset(&sband->ht_cap.mcs, 0, | ||
| 1958 | sizeof(sband->ht_cap.mcs)); | ||
| 1959 | sband->ht_cap.mcs.rx_mask[0] = 0xff; | ||
| 1960 | sband->ht_cap.mcs.rx_mask[1] = 0xff; | ||
| 1961 | sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; | ||
| 1962 | |||
| 1963 | hw->wiphy->bands[band] = sband; | ||
| 1964 | |||
| 1965 | sband->vht_cap.vht_supported = true; | ||
| 1966 | sband->vht_cap.cap = | ||
| 1967 | IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | | ||
| 1968 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | | ||
| 1969 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | | ||
| 1970 | IEEE80211_VHT_CAP_RXLDPC | | ||
| 1971 | IEEE80211_VHT_CAP_SHORT_GI_80 | | ||
| 1972 | IEEE80211_VHT_CAP_SHORT_GI_160 | | ||
| 1973 | IEEE80211_VHT_CAP_TXSTBC | | ||
| 1974 | IEEE80211_VHT_CAP_RXSTBC_1 | | ||
| 1975 | IEEE80211_VHT_CAP_RXSTBC_2 | | ||
| 1976 | IEEE80211_VHT_CAP_RXSTBC_3 | | ||
| 1977 | IEEE80211_VHT_CAP_RXSTBC_4 | | ||
| 1978 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; | ||
| 1979 | sband->vht_cap.vht_mcs.rx_mcs_map = | ||
| 1980 | cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | | ||
| 1981 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | | ||
| 1982 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | | ||
| 1983 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | | ||
| 1984 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | | ||
| 1985 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | | ||
| 1986 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | | ||
| 1987 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); | ||
| 1988 | sband->vht_cap.vht_mcs.tx_mcs_map = | ||
| 1989 | sband->vht_cap.vht_mcs.rx_mcs_map; | ||
| 1990 | } | ||
| 1991 | |||
| 1992 | /* By default all radios belong to the first group */ | ||
| 1993 | data->group = 1; | ||
| 1994 | mutex_init(&data->mutex); | ||
| 1995 | |||
| 1996 | /* Enable frame retransmissions for lossy channels */ | ||
| 1997 | hw->max_rates = 4; | ||
| 1998 | hw->max_rate_tries = 11; | ||
| 1999 | |||
| 2000 | err = ieee80211_register_hw(hw); | ||
| 2001 | if (err < 0) { | ||
| 2002 | printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", | ||
| 2003 | err); | ||
| 2004 | goto failed_hw; | ||
| 2005 | } | ||
| 2006 | |||
| 2007 | wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); | ||
| 2008 | |||
| 2009 | data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); | ||
| 2010 | debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); | ||
| 2011 | debugfs_create_file("group", 0666, data->debugfs, data, | ||
| 2012 | &hwsim_fops_group); | ||
| 2013 | if (data->channels == 1) | ||
| 2014 | debugfs_create_file("dfs_simulate_radar", 0222, | ||
| 2015 | data->debugfs, | ||
| 2016 | data, &hwsim_simulate_radar); | ||
| 2017 | |||
| 2018 | tasklet_hrtimer_init(&data->beacon_timer, | ||
| 2019 | mac80211_hwsim_beacon, | ||
| 2020 | CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); | ||
| 2021 | |||
| 2022 | spin_lock_bh(&hwsim_radio_lock); | ||
| 2023 | list_add_tail(&data->list, &hwsim_radios); | ||
| 2024 | spin_unlock_bh(&hwsim_radio_lock); | ||
| 2025 | |||
| 2026 | return 0; | ||
| 2027 | |||
| 2028 | failed_hw: | ||
| 2029 | device_unregister(data->dev); | ||
| 2030 | failed_drvdata: | ||
| 2031 | ieee80211_free_hw(hw); | ||
| 2032 | failed: | ||
| 2033 | return err; | ||
| 2034 | } | ||
| 2035 | |||
| 1625 | static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) | 2036 | static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) |
| 1626 | { | 2037 | { |
| 1627 | debugfs_remove_recursive(data->debugfs); | 2038 | debugfs_remove_recursive(data->debugfs); |
| @@ -1648,13 +2059,6 @@ static void mac80211_hwsim_free(void) | |||
| 1648 | class_destroy(hwsim_class); | 2059 | class_destroy(hwsim_class); |
| 1649 | } | 2060 | } |
| 1650 | 2061 | ||
| 1651 | static struct platform_driver mac80211_hwsim_driver = { | ||
| 1652 | .driver = { | ||
| 1653 | .name = "mac80211_hwsim", | ||
| 1654 | .owner = THIS_MODULE, | ||
| 1655 | }, | ||
| 1656 | }; | ||
| 1657 | |||
| 1658 | static const struct net_device_ops hwsim_netdev_ops = { | 2062 | static const struct net_device_ops hwsim_netdev_ops = { |
| 1659 | .ndo_start_xmit = hwsim_mon_xmit, | 2063 | .ndo_start_xmit = hwsim_mon_xmit, |
| 1660 | .ndo_change_mtu = eth_change_mtu, | 2064 | .ndo_change_mtu = eth_change_mtu, |
| @@ -1673,159 +2077,6 @@ static void hwsim_mon_setup(struct net_device *dev) | |||
| 1673 | dev->dev_addr[0] = 0x12; | 2077 | dev->dev_addr[0] = 0x12; |
| 1674 | } | 2078 | } |
| 1675 | 2079 | ||
| 1676 | |||
| 1677 | static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) | ||
| 1678 | { | ||
| 1679 | struct mac80211_hwsim_data *data = dat; | ||
| 1680 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
| 1681 | struct sk_buff *skb; | ||
| 1682 | struct ieee80211_pspoll *pspoll; | ||
| 1683 | |||
| 1684 | if (!vp->assoc) | ||
| 1685 | return; | ||
| 1686 | |||
| 1687 | wiphy_debug(data->hw->wiphy, | ||
| 1688 | "%s: send PS-Poll to %pM for aid %d\n", | ||
| 1689 | __func__, vp->bssid, vp->aid); | ||
| 1690 | |||
| 1691 | skb = dev_alloc_skb(sizeof(*pspoll)); | ||
| 1692 | if (!skb) | ||
| 1693 | return; | ||
| 1694 | pspoll = (void *) skb_put(skb, sizeof(*pspoll)); | ||
| 1695 | pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | | ||
| 1696 | IEEE80211_STYPE_PSPOLL | | ||
| 1697 | IEEE80211_FCTL_PM); | ||
| 1698 | pspoll->aid = cpu_to_le16(0xc000 | vp->aid); | ||
| 1699 | memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); | ||
| 1700 | memcpy(pspoll->ta, mac, ETH_ALEN); | ||
| 1701 | |||
| 1702 | rcu_read_lock(); | ||
| 1703 | mac80211_hwsim_tx_frame(data->hw, skb, | ||
| 1704 | rcu_dereference(vif->chanctx_conf)->def.chan); | ||
| 1705 | rcu_read_unlock(); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, | ||
| 1709 | struct ieee80211_vif *vif, int ps) | ||
| 1710 | { | ||
| 1711 | struct hwsim_vif_priv *vp = (void *)vif->drv_priv; | ||
| 1712 | struct sk_buff *skb; | ||
| 1713 | struct ieee80211_hdr *hdr; | ||
| 1714 | |||
| 1715 | if (!vp->assoc) | ||
| 1716 | return; | ||
| 1717 | |||
| 1718 | wiphy_debug(data->hw->wiphy, | ||
| 1719 | "%s: send data::nullfunc to %pM ps=%d\n", | ||
| 1720 | __func__, vp->bssid, ps); | ||
| 1721 | |||
| 1722 | skb = dev_alloc_skb(sizeof(*hdr)); | ||
| 1723 | if (!skb) | ||
| 1724 | return; | ||
| 1725 | hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); | ||
| 1726 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | ||
| 1727 | IEEE80211_STYPE_NULLFUNC | | ||
| 1728 | (ps ? IEEE80211_FCTL_PM : 0)); | ||
| 1729 | hdr->duration_id = cpu_to_le16(0); | ||
| 1730 | memcpy(hdr->addr1, vp->bssid, ETH_ALEN); | ||
| 1731 | memcpy(hdr->addr2, mac, ETH_ALEN); | ||
| 1732 | memcpy(hdr->addr3, vp->bssid, ETH_ALEN); | ||
| 1733 | |||
| 1734 | rcu_read_lock(); | ||
| 1735 | mac80211_hwsim_tx_frame(data->hw, skb, | ||
| 1736 | rcu_dereference(vif->chanctx_conf)->def.chan); | ||
| 1737 | rcu_read_unlock(); | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | |||
| 1741 | static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, | ||
| 1742 | struct ieee80211_vif *vif) | ||
| 1743 | { | ||
| 1744 | struct mac80211_hwsim_data *data = dat; | ||
| 1745 | hwsim_send_nullfunc(data, mac, vif, 1); | ||
| 1746 | } | ||
| 1747 | |||
| 1748 | |||
| 1749 | static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, | ||
| 1750 | struct ieee80211_vif *vif) | ||
| 1751 | { | ||
| 1752 | struct mac80211_hwsim_data *data = dat; | ||
| 1753 | hwsim_send_nullfunc(data, mac, vif, 0); | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | |||
| 1757 | static int hwsim_fops_ps_read(void *dat, u64 *val) | ||
| 1758 | { | ||
| 1759 | struct mac80211_hwsim_data *data = dat; | ||
| 1760 | *val = data->ps; | ||
| 1761 | return 0; | ||
| 1762 | } | ||
| 1763 | |||
| 1764 | static int hwsim_fops_ps_write(void *dat, u64 val) | ||
| 1765 | { | ||
| 1766 | struct mac80211_hwsim_data *data = dat; | ||
| 1767 | enum ps_mode old_ps; | ||
| 1768 | |||
| 1769 | if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && | ||
| 1770 | val != PS_MANUAL_POLL) | ||
| 1771 | return -EINVAL; | ||
| 1772 | |||
| 1773 | old_ps = data->ps; | ||
| 1774 | data->ps = val; | ||
| 1775 | |||
| 1776 | if (val == PS_MANUAL_POLL) { | ||
| 1777 | ieee80211_iterate_active_interfaces(data->hw, | ||
| 1778 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 1779 | hwsim_send_ps_poll, data); | ||
| 1780 | data->ps_poll_pending = true; | ||
| 1781 | } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { | ||
| 1782 | ieee80211_iterate_active_interfaces(data->hw, | ||
| 1783 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 1784 | hwsim_send_nullfunc_ps, | ||
| 1785 | data); | ||
| 1786 | } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { | ||
| 1787 | ieee80211_iterate_active_interfaces(data->hw, | ||
| 1788 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 1789 | hwsim_send_nullfunc_no_ps, | ||
| 1790 | data); | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | return 0; | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, | ||
| 1797 | "%llu\n"); | ||
| 1798 | |||
| 1799 | static int hwsim_write_simulate_radar(void *dat, u64 val) | ||
| 1800 | { | ||
| 1801 | struct mac80211_hwsim_data *data = dat; | ||
| 1802 | |||
| 1803 | ieee80211_radar_detected(data->hw); | ||
| 1804 | |||
| 1805 | return 0; | ||
| 1806 | } | ||
| 1807 | |||
| 1808 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL, | ||
| 1809 | hwsim_write_simulate_radar, "%llu\n"); | ||
| 1810 | |||
| 1811 | static int hwsim_fops_group_read(void *dat, u64 *val) | ||
| 1812 | { | ||
| 1813 | struct mac80211_hwsim_data *data = dat; | ||
| 1814 | *val = data->group; | ||
| 1815 | return 0; | ||
| 1816 | } | ||
| 1817 | |||
| 1818 | static int hwsim_fops_group_write(void *dat, u64 val) | ||
| 1819 | { | ||
| 1820 | struct mac80211_hwsim_data *data = dat; | ||
| 1821 | data->group = val; | ||
| 1822 | return 0; | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, | ||
| 1826 | hwsim_fops_group_read, hwsim_fops_group_write, | ||
| 1827 | "%llx\n"); | ||
| 1828 | |||
| 1829 | static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) | 2080 | static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) |
| 1830 | { | 2081 | { |
| 1831 | struct mac80211_hwsim_data *data; | 2082 | struct mac80211_hwsim_data *data; |
| @@ -2073,257 +2324,6 @@ static void hwsim_exit_netlink(void) | |||
| 2073 | genl_unregister_family(&hwsim_genl_family); | 2324 | genl_unregister_family(&hwsim_genl_family); |
| 2074 | } | 2325 | } |
| 2075 | 2326 | ||
| 2076 | static const struct ieee80211_iface_limit hwsim_if_limits[] = { | ||
| 2077 | { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, | ||
| 2078 | { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | | ||
| 2079 | BIT(NL80211_IFTYPE_P2P_CLIENT) | | ||
| 2080 | #ifdef CONFIG_MAC80211_MESH | ||
| 2081 | BIT(NL80211_IFTYPE_MESH_POINT) | | ||
| 2082 | #endif | ||
| 2083 | BIT(NL80211_IFTYPE_AP) | | ||
| 2084 | BIT(NL80211_IFTYPE_P2P_GO) }, | ||
| 2085 | { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, | ||
| 2086 | }; | ||
| 2087 | |||
| 2088 | static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { | ||
| 2089 | { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, | ||
| 2090 | }; | ||
| 2091 | |||
| 2092 | static const struct ieee80211_iface_combination hwsim_if_comb[] = { | ||
| 2093 | { | ||
| 2094 | .limits = hwsim_if_limits, | ||
| 2095 | .n_limits = ARRAY_SIZE(hwsim_if_limits), | ||
| 2096 | .max_interfaces = 2048, | ||
| 2097 | .num_different_channels = 1, | ||
| 2098 | }, | ||
| 2099 | { | ||
| 2100 | .limits = hwsim_if_dfs_limits, | ||
| 2101 | .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), | ||
| 2102 | .max_interfaces = 8, | ||
| 2103 | .num_different_channels = 1, | ||
| 2104 | .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | | ||
| 2105 | BIT(NL80211_CHAN_WIDTH_20) | | ||
| 2106 | BIT(NL80211_CHAN_WIDTH_40) | | ||
| 2107 | BIT(NL80211_CHAN_WIDTH_80) | | ||
| 2108 | BIT(NL80211_CHAN_WIDTH_160), | ||
| 2109 | } | ||
| 2110 | }; | ||
| 2111 | |||
| 2112 | static int __init mac80211_hwsim_create_radio(void) | ||
| 2113 | { | ||
| 2114 | int err; | ||
| 2115 | u8 addr[ETH_ALEN]; | ||
| 2116 | struct mac80211_hwsim_data *data; | ||
| 2117 | struct ieee80211_hw *hw; | ||
| 2118 | enum ieee80211_band band; | ||
| 2119 | const struct ieee80211_ops *ops = &mac80211_hwsim_ops; | ||
| 2120 | int idx; | ||
| 2121 | |||
| 2122 | spin_lock_bh(&hwsim_radio_lock); | ||
| 2123 | idx = hwsim_radio_idx++; | ||
| 2124 | spin_unlock_bh(&hwsim_radio_lock); | ||
| 2125 | |||
| 2126 | if (channels > 1) | ||
| 2127 | ops = &mac80211_hwsim_mchan_ops; | ||
| 2128 | hw = ieee80211_alloc_hw(sizeof(*data), ops); | ||
| 2129 | if (!hw) { | ||
| 2130 | printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n"); | ||
| 2131 | err = -ENOMEM; | ||
| 2132 | goto failed; | ||
| 2133 | } | ||
| 2134 | data = hw->priv; | ||
| 2135 | data->hw = hw; | ||
| 2136 | |||
| 2137 | data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); | ||
| 2138 | if (IS_ERR(data->dev)) { | ||
| 2139 | printk(KERN_DEBUG | ||
| 2140 | "mac80211_hwsim: device_create failed (%ld)\n", | ||
| 2141 | PTR_ERR(data->dev)); | ||
| 2142 | err = -ENOMEM; | ||
| 2143 | goto failed_drvdata; | ||
| 2144 | } | ||
| 2145 | data->dev->driver = &mac80211_hwsim_driver.driver; | ||
| 2146 | err = device_bind_driver(data->dev); | ||
| 2147 | if (err != 0) { | ||
| 2148 | printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n", | ||
| 2149 | err); | ||
| 2150 | goto failed_hw; | ||
| 2151 | } | ||
| 2152 | |||
| 2153 | skb_queue_head_init(&data->pending); | ||
| 2154 | |||
| 2155 | SET_IEEE80211_DEV(hw, data->dev); | ||
| 2156 | memset(addr, 0, ETH_ALEN); | ||
| 2157 | addr[0] = 0x02; | ||
| 2158 | addr[3] = idx >> 8; | ||
| 2159 | addr[4] = idx; | ||
| 2160 | memcpy(data->addresses[0].addr, addr, ETH_ALEN); | ||
| 2161 | memcpy(data->addresses[1].addr, addr, ETH_ALEN); | ||
| 2162 | data->addresses[1].addr[0] |= 0x40; | ||
| 2163 | hw->wiphy->n_addresses = 2; | ||
| 2164 | hw->wiphy->addresses = data->addresses; | ||
| 2165 | |||
| 2166 | data->channels = channels; | ||
| 2167 | |||
| 2168 | if (data->channels > 1) { | ||
| 2169 | hw->wiphy->max_scan_ssids = 255; | ||
| 2170 | hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; | ||
| 2171 | hw->wiphy->max_remain_on_channel_duration = 1000; | ||
| 2172 | /* For channels > 1 DFS is not allowed */ | ||
| 2173 | hw->wiphy->n_iface_combinations = 1; | ||
| 2174 | hw->wiphy->iface_combinations = &data->if_combination; | ||
| 2175 | data->if_combination = hwsim_if_comb[0]; | ||
| 2176 | data->if_combination.num_different_channels = data->channels; | ||
| 2177 | } else { | ||
| 2178 | hw->wiphy->iface_combinations = hwsim_if_comb; | ||
| 2179 | hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); | ||
| 2183 | INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); | ||
| 2184 | |||
| 2185 | hw->queues = 5; | ||
| 2186 | hw->offchannel_tx_hw_queue = 4; | ||
| 2187 | hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | ||
| 2188 | BIT(NL80211_IFTYPE_AP) | | ||
| 2189 | BIT(NL80211_IFTYPE_P2P_CLIENT) | | ||
| 2190 | BIT(NL80211_IFTYPE_P2P_GO) | | ||
| 2191 | BIT(NL80211_IFTYPE_ADHOC) | | ||
| 2192 | BIT(NL80211_IFTYPE_MESH_POINT) | | ||
| 2193 | BIT(NL80211_IFTYPE_P2P_DEVICE); | ||
| 2194 | |||
| 2195 | hw->flags = IEEE80211_HW_MFP_CAPABLE | | ||
| 2196 | IEEE80211_HW_SIGNAL_DBM | | ||
| 2197 | IEEE80211_HW_SUPPORTS_STATIC_SMPS | | ||
| 2198 | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | | ||
| 2199 | IEEE80211_HW_AMPDU_AGGREGATION | | ||
| 2200 | IEEE80211_HW_WANT_MONITOR_VIF | | ||
| 2201 | IEEE80211_HW_QUEUE_CONTROL | | ||
| 2202 | IEEE80211_HW_SUPPORTS_HT_CCK_RATES; | ||
| 2203 | if (rctbl) | ||
| 2204 | hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; | ||
| 2205 | |||
| 2206 | hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | | ||
| 2207 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | | ||
| 2208 | WIPHY_FLAG_AP_UAPSD; | ||
| 2209 | hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; | ||
| 2210 | |||
| 2211 | /* ask mac80211 to reserve space for magic */ | ||
| 2212 | hw->vif_data_size = sizeof(struct hwsim_vif_priv); | ||
| 2213 | hw->sta_data_size = sizeof(struct hwsim_sta_priv); | ||
| 2214 | hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); | ||
| 2215 | |||
| 2216 | memcpy(data->channels_2ghz, hwsim_channels_2ghz, | ||
| 2217 | sizeof(hwsim_channels_2ghz)); | ||
| 2218 | memcpy(data->channels_5ghz, hwsim_channels_5ghz, | ||
| 2219 | sizeof(hwsim_channels_5ghz)); | ||
| 2220 | memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); | ||
| 2221 | |||
| 2222 | for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { | ||
| 2223 | struct ieee80211_supported_band *sband = &data->bands[band]; | ||
| 2224 | switch (band) { | ||
| 2225 | case IEEE80211_BAND_2GHZ: | ||
| 2226 | sband->channels = data->channels_2ghz; | ||
| 2227 | sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); | ||
| 2228 | sband->bitrates = data->rates; | ||
| 2229 | sband->n_bitrates = ARRAY_SIZE(hwsim_rates); | ||
| 2230 | break; | ||
| 2231 | case IEEE80211_BAND_5GHZ: | ||
| 2232 | sband->channels = data->channels_5ghz; | ||
| 2233 | sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); | ||
| 2234 | sband->bitrates = data->rates + 4; | ||
| 2235 | sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; | ||
| 2236 | break; | ||
| 2237 | default: | ||
| 2238 | continue; | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | sband->ht_cap.ht_supported = true; | ||
| 2242 | sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | ||
| 2243 | IEEE80211_HT_CAP_GRN_FLD | | ||
| 2244 | IEEE80211_HT_CAP_SGI_40 | | ||
| 2245 | IEEE80211_HT_CAP_DSSSCCK40; | ||
| 2246 | sband->ht_cap.ampdu_factor = 0x3; | ||
| 2247 | sband->ht_cap.ampdu_density = 0x6; | ||
| 2248 | memset(&sband->ht_cap.mcs, 0, | ||
| 2249 | sizeof(sband->ht_cap.mcs)); | ||
| 2250 | sband->ht_cap.mcs.rx_mask[0] = 0xff; | ||
| 2251 | sband->ht_cap.mcs.rx_mask[1] = 0xff; | ||
| 2252 | sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; | ||
| 2253 | |||
| 2254 | hw->wiphy->bands[band] = sband; | ||
| 2255 | |||
| 2256 | sband->vht_cap.vht_supported = true; | ||
| 2257 | sband->vht_cap.cap = | ||
| 2258 | IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | | ||
| 2259 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | | ||
| 2260 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | | ||
| 2261 | IEEE80211_VHT_CAP_RXLDPC | | ||
| 2262 | IEEE80211_VHT_CAP_SHORT_GI_80 | | ||
| 2263 | IEEE80211_VHT_CAP_SHORT_GI_160 | | ||
| 2264 | IEEE80211_VHT_CAP_TXSTBC | | ||
| 2265 | IEEE80211_VHT_CAP_RXSTBC_1 | | ||
| 2266 | IEEE80211_VHT_CAP_RXSTBC_2 | | ||
| 2267 | IEEE80211_VHT_CAP_RXSTBC_3 | | ||
| 2268 | IEEE80211_VHT_CAP_RXSTBC_4 | | ||
| 2269 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; | ||
| 2270 | sband->vht_cap.vht_mcs.rx_mcs_map = | ||
| 2271 | cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | | ||
| 2272 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | | ||
| 2273 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | | ||
| 2274 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | | ||
| 2275 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | | ||
| 2276 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | | ||
| 2277 | IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | | ||
| 2278 | IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); | ||
| 2279 | sband->vht_cap.vht_mcs.tx_mcs_map = | ||
| 2280 | sband->vht_cap.vht_mcs.rx_mcs_map; | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | /* By default all radios belong to the first group */ | ||
| 2284 | data->group = 1; | ||
| 2285 | mutex_init(&data->mutex); | ||
| 2286 | |||
| 2287 | /* Enable frame retransmissions for lossy channels */ | ||
| 2288 | hw->max_rates = 4; | ||
| 2289 | hw->max_rate_tries = 11; | ||
| 2290 | |||
| 2291 | err = ieee80211_register_hw(hw); | ||
| 2292 | if (err < 0) { | ||
| 2293 | printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", | ||
| 2294 | err); | ||
| 2295 | goto failed_hw; | ||
| 2296 | } | ||
| 2297 | |||
| 2298 | wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); | ||
| 2299 | |||
| 2300 | data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); | ||
| 2301 | debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); | ||
| 2302 | debugfs_create_file("group", 0666, data->debugfs, data, | ||
| 2303 | &hwsim_fops_group); | ||
| 2304 | if (data->channels == 1) | ||
| 2305 | debugfs_create_file("dfs_simulate_radar", 0222, | ||
| 2306 | data->debugfs, | ||
| 2307 | data, &hwsim_simulate_radar); | ||
| 2308 | |||
| 2309 | tasklet_hrtimer_init(&data->beacon_timer, | ||
| 2310 | mac80211_hwsim_beacon, | ||
| 2311 | CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); | ||
| 2312 | |||
| 2313 | spin_lock_bh(&hwsim_radio_lock); | ||
| 2314 | list_add_tail(&data->list, &hwsim_radios); | ||
| 2315 | spin_unlock_bh(&hwsim_radio_lock); | ||
| 2316 | |||
| 2317 | return 0; | ||
| 2318 | |||
| 2319 | failed_hw: | ||
| 2320 | device_unregister(data->dev); | ||
| 2321 | failed_drvdata: | ||
| 2322 | ieee80211_free_hw(hw); | ||
| 2323 | failed: | ||
| 2324 | return err; | ||
| 2325 | } | ||
| 2326 | |||
| 2327 | static int __init init_mac80211_hwsim(void) | 2327 | static int __init init_mac80211_hwsim(void) |
| 2328 | { | 2328 | { |
| 2329 | int i, err; | 2329 | int i, err; |
