Files
mt76/mac80211.c
T
2015-12-08 10:21:52 +01:00

223 lines
5.5 KiB
C

/*
* Copyright (C) 2015 Felix Fietkau <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "mt76.h"
#define CHAN2G(_idx, _freq) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 30, \
}
#define CHAN5G(_idx, _freq) { \
.band = IEEE80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 30, \
}
static const struct ieee80211_channel mt76_channels_2ghz[] = {
CHAN2G(1, 2412),
CHAN2G(2, 2417),
CHAN2G(3, 2422),
CHAN2G(4, 2427),
CHAN2G(5, 2432),
CHAN2G(6, 2437),
CHAN2G(7, 2442),
CHAN2G(8, 2447),
CHAN2G(9, 2452),
CHAN2G(10, 2457),
CHAN2G(11, 2462),
CHAN2G(12, 2467),
CHAN2G(13, 2472),
CHAN2G(14, 2484),
};
static const struct ieee80211_channel mt76_channels_5ghz[] = {
CHAN5G(36, 5180),
CHAN5G(40, 5200),
CHAN5G(44, 5220),
CHAN5G(48, 5240),
CHAN5G(52, 5260),
CHAN5G(56, 5280),
CHAN5G(60, 5300),
CHAN5G(64, 5320),
CHAN5G(100, 5500),
CHAN5G(104, 5520),
CHAN5G(108, 5540),
CHAN5G(112, 5560),
CHAN5G(116, 5580),
CHAN5G(120, 5600),
CHAN5G(124, 5620),
CHAN5G(128, 5640),
CHAN5G(132, 5660),
CHAN5G(136, 5680),
CHAN5G(140, 5700),
CHAN5G(149, 5745),
CHAN5G(153, 5765),
CHAN5G(157, 5785),
CHAN5G(161, 5805),
CHAN5G(165, 5825),
};
static int
mt76_init_sband(struct mt76_dev *dev, struct ieee80211_supported_band *sband,
const struct ieee80211_channel *chan, int n_chan,
struct ieee80211_rate *rates, int n_rates, bool vht)
{
struct ieee80211_sta_ht_cap *ht_cap;
struct ieee80211_sta_vht_cap *vht_cap;
void *chanlist;
u16 mcs_map;
int size;
size = n_chan * sizeof(*chan);
chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
if (!chanlist)
return -ENOMEM;
sband->channels = chanlist;
sband->n_channels = n_chan;
sband->bitrates = rates;
sband->n_bitrates = n_rates;
ht_cap = &sband->ht_cap;
ht_cap->ht_supported = true;
ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_TX_STBC |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
ht_cap->mcs.rx_mask[0] = 0xff;
ht_cap->mcs.rx_mask[1] = 0xff;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
if (!vht)
return 0;
vht_cap = &sband->vht_cap;
vht_cap->vht_supported = true;
mcs_map = (IEEE80211_VHT_MCS_SUPPORT_0_9 << (0 * 2)) |
(IEEE80211_VHT_MCS_SUPPORT_0_9 << (1 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (2 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (3 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (4 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (5 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (6 * 2)) |
(IEEE80211_VHT_MCS_NOT_SUPPORTED << (7 * 2));
vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
vht_cap->cap = IEEE80211_VHT_CAP_RXLDPC |
IEEE80211_VHT_CAP_TXSTBC |
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_SHORT_GI_80;
return 0;
}
static int
mt76_init_sband_2g(struct mt76_dev *dev, struct ieee80211_rate *rates,
int n_rates)
{
dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &dev->sband_2g;
return mt76_init_sband(dev, &dev->sband_2g,
mt76_channels_2ghz,
ARRAY_SIZE(mt76_channels_2ghz),
rates, n_rates, false);
}
static int
mt76_init_sband_5g(struct mt76_dev *dev, struct ieee80211_rate *rates,
int n_rates, bool vht)
{
dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &dev->sband_5g;
return mt76_init_sband(dev, &dev->sband_5g,
mt76_channels_5ghz,
ARRAY_SIZE(mt76_channels_5ghz),
rates, n_rates, vht);
}
static const struct ieee80211_iface_limit if_limits[] = {
{
.max = 1,
.types = BIT(NL80211_IFTYPE_ADHOC)
}, {
.max = 8,
.types = BIT(NL80211_IFTYPE_STATION) |
#ifdef CONFIG_MAC80211_MESH
BIT(NL80211_IFTYPE_MESH_POINT) |
#endif
BIT(NL80211_IFTYPE_AP)
},
};
static const struct ieee80211_iface_combination if_comb[] = {
{
.limits = if_limits,
.n_limits = ARRAY_SIZE(if_limits),
.max_interfaces = 8,
.num_different_channels = 1,
.beacon_int_infra_match = true,
}
};
int mt76_register_device(struct mt76_dev *dev, int bands, bool vht,
struct ieee80211_rate *rates, int n_rates)
{
struct ieee80211_hw *hw = dev->hw;
struct wiphy *wiphy = hw->wiphy;
int ret;
SET_IEEE80211_DEV(hw, dev->dev);
wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
#ifdef CONFIG_MAC80211_MESH
BIT(NL80211_IFTYPE_MESH_POINT) |
#endif
BIT(NL80211_IFTYPE_ADHOC);
wiphy->iface_combinations = if_comb;
wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
if (bands & BIT(IEEE80211_BAND_2GHZ)) {
ret = mt76_init_sband_2g(dev, rates, n_rates);
if (ret)
return ret;
}
if (bands & BIT(IEEE80211_BAND_5GHZ)) {
ret = mt76_init_sband_5g(dev, rates + 4, n_rates - 4, vht);
if (ret)
return ret;
}
return ieee80211_register_hw(hw);
}
EXPORT_SYMBOL_GPL(mt76_register_device);