qtnfmac: implement cfg80211 channel_switch handler

This patch implements cfg80211 channel_switch handler enabling CSA
channel-switch procedure.

Driver performs only basic validation of the requested new channel
and then sends command to firmware. Beacon IEs are not sent since
beacon update is handled by firmware.

Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Signed-off-by: Avinash Patil <avinashp@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Sergey Matyukevich 2017-07-28 02:06:50 +03:00 committed by Kalle Valo
parent 34f1145b2c
commit 97883695d5
6 changed files with 210 additions and 2 deletions

View File

@ -812,6 +812,59 @@ qtnf_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
return 0;
}
static int qtnf_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params)
{
struct qtnf_wmac *mac = wiphy_priv(wiphy);
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev);
int ret;
pr_debug("%s: chan(%u) count(%u) radar(%u) block_tx(%u)\n", dev->name,
params->chandef.chan->hw_value, params->count,
params->radar_required, params->block_tx);
switch (vif->wdev.iftype) {
case NL80211_IFTYPE_AP:
if (!(vif->bss_status & QTNF_STATE_AP_START)) {
pr_warn("AP not started on %s\n", dev->name);
return -ENOTCONN;
}
break;
default:
pr_err("unsupported vif type (%d) on %s\n",
vif->wdev.iftype, dev->name);
return -EOPNOTSUPP;
}
if (vif->vifid != 0) {
if (!(mac->status & QTNF_MAC_CSA_ACTIVE))
return -EOPNOTSUPP;
if (!cfg80211_chandef_identical(&params->chandef,
&mac->csa_chandef))
return -EINVAL;
return 0;
}
if (!cfg80211_chandef_valid(&params->chandef)) {
pr_err("%s: invalid channel\n", dev->name);
return -EINVAL;
}
if (cfg80211_chandef_identical(&params->chandef, &mac->chandef)) {
pr_err("%s: switch request to the same channel\n", dev->name);
return -EALREADY;
}
ret = qtnf_cmd_send_chan_switch(mac, params);
if (ret)
pr_warn("%s: failed to switch to channel (%u)\n",
dev->name, params->chandef.chan->hw_value);
return ret;
}
static struct cfg80211_ops qtn_cfg80211_ops = {
.add_virtual_intf = qtnf_add_virtual_intf,
.change_virtual_intf = qtnf_change_virtual_intf,
@ -834,7 +887,8 @@ static struct cfg80211_ops qtn_cfg80211_ops = {
.connect = qtnf_connect,
.disconnect = qtnf_disconnect,
.dump_survey = qtnf_dump_survey,
.get_channel = qtnf_get_channel
.get_channel = qtnf_get_channel,
.channel_switch = qtnf_channel_switch
};
static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy_in,
@ -981,6 +1035,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->iface_combinations = iface_comb;
wiphy->n_iface_combinations = 1;
wiphy->max_num_csa_counters = 2;
/* Initialize cipher suits */
wiphy->cipher_suites = qtnf_cipher_suites;
@ -988,7 +1043,8 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
WIPHY_FLAG_AP_UAPSD;
WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_HAS_CHANNEL_SWITCH;
wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2;

View File

@ -2283,3 +2283,58 @@ int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
consume_skb(resp_skb);
return ret;
}
int qtnf_cmd_send_chan_switch(struct qtnf_wmac *mac,
struct cfg80211_csa_settings *params)
{
struct qlink_cmd_chan_switch *cmd;
struct sk_buff *cmd_skb;
u16 res_code = QLINK_CMD_RESULT_OK;
int ret;
cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0x0,
QLINK_CMD_CHAN_SWITCH,
sizeof(*cmd));
if (unlikely(!cmd_skb))
return -ENOMEM;
qtnf_bus_lock(mac->bus);
cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data;
cmd->channel = cpu_to_le16(params->chandef.chan->hw_value);
cmd->radar_required = params->radar_required;
cmd->block_tx = params->block_tx;
cmd->beacon_count = params->count;
ret = qtnf_cmd_send(mac->bus, cmd_skb, &res_code);
if (unlikely(ret))
goto out;
switch (res_code) {
case QLINK_CMD_RESULT_OK:
memcpy(&mac->csa_chandef, &params->chandef,
sizeof(mac->csa_chandef));
mac->status |= QTNF_MAC_CSA_ACTIVE;
ret = 0;
break;
case QLINK_CMD_RESULT_ENOTFOUND:
ret = -ENOENT;
break;
case QLINK_CMD_RESULT_ENOTSUPP:
ret = -EOPNOTSUPP;
break;
case QLINK_CMD_RESULT_EALREADY:
ret = -EALREADY;
break;
case QLINK_CMD_RESULT_INVALID:
default:
ret = -EFAULT;
break;
}
out:
qtnf_bus_unlock(mac->bus);
return ret;
}

View File

@ -73,5 +73,7 @@ int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif,
int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req);
int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel,
struct qtnf_chan_stats *stats);
int qtnf_cmd_send_chan_switch(struct qtnf_wmac *mac,
struct cfg80211_csa_settings *params);
#endif /* QLINK_COMMANDS_H_ */

View File

@ -88,6 +88,10 @@ enum qtnf_sta_state {
QTNF_STA_CONNECTED
};
enum qtnf_mac_status {
QTNF_MAC_CSA_ACTIVE = BIT(0)
};
struct qtnf_vif {
struct wireless_dev wdev;
u8 vifid;
@ -136,11 +140,13 @@ struct qtnf_wmac {
u8 macid;
u8 wiphy_registered;
u8 macaddr[ETH_ALEN];
u32 status;
struct qtnf_bus *bus;
struct qtnf_mac_info macinfo;
struct qtnf_vif iflist[QTNF_MAX_INTF];
struct cfg80211_scan_request *scan_req;
struct cfg80211_chan_def chandef;
struct cfg80211_chan_def csa_chandef;
};
struct qtnf_hw_info {

View File

@ -350,6 +350,63 @@ qtnf_event_handle_scan_complete(struct qtnf_wmac *mac,
return 0;
}
static int
qtnf_event_handle_freq_change(struct qtnf_wmac *mac,
const struct qlink_event_freq_change *data,
u16 len)
{
struct wiphy *wiphy = priv_to_wiphy(mac);
struct cfg80211_chan_def chandef;
struct ieee80211_channel *chan;
struct qtnf_vif *vif;
int freq;
int i;
if (len < sizeof(*data)) {
pr_err("payload is too short\n");
return -EINVAL;
}
freq = le32_to_cpu(data->freq);
chan = ieee80211_get_channel(wiphy, freq);
if (!chan) {
pr_err("channel at %d MHz not found\n", freq);
return -EINVAL;
}
pr_debug("MAC%d switch to new channel %u MHz\n", mac->macid, freq);
if (mac->status & QTNF_MAC_CSA_ACTIVE) {
mac->status &= ~QTNF_MAC_CSA_ACTIVE;
if (chan->hw_value != mac->csa_chandef.chan->hw_value)
pr_warn("unexpected switch to %u during CSA to %u\n",
chan->hw_value,
mac->csa_chandef.chan->hw_value);
}
/* FIXME: need to figure out proper nl80211_channel_type value */
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
/* fall-back to minimal safe chandef description */
if (!cfg80211_chandef_valid(&chandef))
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
memcpy(&mac->chandef, &chandef, sizeof(mac->chandef));
for (i = 0; i < QTNF_MAX_INTF; i++) {
vif = &mac->iflist[i];
if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)
continue;
if (vif->netdev) {
mutex_lock(&vif->wdev.mtx);
cfg80211_ch_switch_notify(vif->netdev, &chandef);
mutex_unlock(&vif->wdev.mtx);
}
}
return 0;
}
static int qtnf_event_parse(struct qtnf_wmac *mac,
const struct sk_buff *event_skb)
{
@ -400,6 +457,10 @@ static int qtnf_event_parse(struct qtnf_wmac *mac,
ret = qtnf_event_handle_bss_leave(vif, (const void *)event,
event_len);
break;
case QLINK_EVENT_FREQ_CHANGE:
ret = qtnf_event_handle_freq_change(mac, (const void *)event,
event_len);
break;
default:
pr_warn("unknown event type: %x\n", event_id);
break;

View File

@ -153,6 +153,7 @@ enum qlink_cmd_type {
QLINK_CMD_UPDOWN_INTF = 0x0018,
QLINK_CMD_REG_NOTIFY = 0x0019,
QLINK_CMD_CHANS_INFO_GET = 0x001A,
QLINK_CMD_CHAN_SWITCH = 0x001B,
QLINK_CMD_CONFIG_AP = 0x0020,
QLINK_CMD_START_AP = 0x0021,
QLINK_CMD_STOP_AP = 0x0022,
@ -482,6 +483,22 @@ struct qlink_cmd_reg_notify {
u8 user_reg_hint_type;
} __packed;
/**
* struct qlink_cmd_chan_switch - data for QLINK_CMD_CHAN_SWITCH command
*
* @channel: channel number according to 802.11 17.3.8.3.2 and Annex J
* @radar_required: whether radar detection is required on the new channel
* @block_tx: whether transmissions should be blocked while changing
* @beacon_count: number of beacons until switch
*/
struct qlink_cmd_chan_switch {
struct qlink_cmd chdr;
__le16 channel;
u8 radar_required;
u8 block_tx;
u8 beacon_count;
} __packed;
/* QLINK Command Responses messages related definitions
*/
@ -667,6 +684,7 @@ enum qlink_event_type {
QLINK_EVENT_SCAN_COMPLETE = 0x0025,
QLINK_EVENT_BSS_JOIN = 0x0026,
QLINK_EVENT_BSS_LEAVE = 0x0027,
QLINK_EVENT_FREQ_CHANGE = 0x0028,
};
/**
@ -736,6 +754,16 @@ struct qlink_event_bss_leave {
__le16 reason;
} __packed;
/**
* struct qlink_event_freq_change - data for QLINK_EVENT_FREQ_CHANGE event
*
* @freq: new operating frequency in MHz
*/
struct qlink_event_freq_change {
struct qlink_event ehdr;
__le32 freq;
} __packed;
enum qlink_rxmgmt_flags {
QLINK_RXMGMT_FLAG_ANSWERED = 1 << 0,
};