Skip to content

Commit

Permalink
ath6kl: support for P2P mgmt operations on station interface
Browse files Browse the repository at this point in the history
This patch enables support for doing P2P management operations like device
discovery on top of a station interface. After group formation, the station
interface will become a P2P GO/client interface as the case may be.

This feature requires modifications to a couple of existing WMI structures and
therefore new command ids and structures have been defined in order to be
compatible with older firmware versions and other chips. The exception here is
the wmi_connect_cmd. Adding a new field to the end of the structure will not
cause any issues with previous firmware versions since firmware only checks for
minimum length of the command. The other structures are of variable length,
hence it was not possible to add new fields to the end.

The new command ids have to be added to the end of enum wmi_cmd_id, so it has
updated to match the firmware.

The driver will support both the 'old' and the 'new' commands for a while by
checking the firmware capabilities.

Signed-off-by: Aarthi Thiruvengadam <athiruve@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
  • Loading branch information
Aarthi Thiruvengadam authored and Kalle Valo committed Dec 14, 2011
1 parent 39586bf commit 3ca9d1f
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 10 deletions.
60 changes: 52 additions & 8 deletions drivers/net/wireless/ath/ath6kl/cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
int status;
u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;

ath6kl_cfg80211_sscan_disable(vif);

Expand Down Expand Up @@ -555,6 +556,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,

vif->nw_type = vif->next_mode;

if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
nw_subtype = SUBTYPE_P2PCLIENT;

ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
"%s: connect called with authmode %d dot11 auth %d"
" PW crypto %d PW crypto len %d GRP crypto %d"
Expand All @@ -572,7 +576,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
vif->grp_crypto, vif->grp_crypto_len,
vif->ssid_len, vif->ssid,
vif->req_bssid, vif->ch_hint,
ar->connect_ctrl_flags);
ar->connect_ctrl_flags, nw_subtype);

up(&ar->sem);

Expand Down Expand Up @@ -914,9 +918,25 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
if (test_bit(CONNECTED, &vif->flags))
force_fg_scan = 1;

ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
force_fg_scan, false, 0, 0, n_channels,
channels);
if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
ar->fw_capabilities)) {
/*
* If capable of doing P2P mgmt operations using
* station interface, send additional information like
* supported rates to advertise and xmit rates for
* probe requests
*/
ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
WMI_LONG_SCAN, force_fg_scan,
false, 0, 0, n_channels,
channels, request->no_cck,
request->rates);
} else {
ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
WMI_LONG_SCAN, force_fg_scan,
false, 0, 0, n_channels,
channels);
}
if (ret)
ath6kl_err("wmi_startscan_cmd failed\n");
else
Expand Down Expand Up @@ -1485,7 +1505,7 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
vif->grp_crypto, vif->grp_crypto_len,
vif->ssid_len, vif->ssid,
vif->req_bssid, vif->ch_hint,
ar->connect_ctrl_flags);
ar->connect_ctrl_flags, SUBTYPE_NONE);
set_bit(CONNECT_PEND, &vif->flags);

return 0;
Expand Down Expand Up @@ -2192,6 +2212,16 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
p.dot11_auth_mode = vif->dot11_auth_mode;
p.ch = cpu_to_le16(vif->next_chan);

if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
p.nw_subtype = SUBTYPE_P2PGO;
} else {
/*
* Due to firmware limitation, it is not possible to
* do P2P mgmt operations in AP mode
*/
p.nw_subtype = SUBTYPE_NONE;
}

res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
if (res < 0)
return res;
Expand Down Expand Up @@ -2357,9 +2387,23 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
}

*cookie = id;
return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
chan->center_freq, wait,
buf, len);

if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
ar->fw_capabilities)) {
/*
* If capable of doing P2P mgmt operations using
* station interface, send additional information like
* supported rates to advertise and xmit rates for
* probe requests
*/
return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
chan->center_freq, wait,
buf, len, no_cck);
} else {
return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
chan->center_freq, wait,
buf, len);
}
}

static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/wireless/ath/ath6kl/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ enum ath6kl_fw_capability {
ATH6KL_FW_CAPABILITY_HOST_P2P = 0,
ATH6KL_FW_CAPABILITY_SCHED_SCAN = 1,

/*
* Firmware is capable of supporting P2P mgmt operations on a
* station interface. After group formation, the station
* interface will become a P2P client/GO interface as the case may be
*/
ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,

/* this needs to be last */
ATH6KL_FW_CAPABILITY_MAX,
};
Expand Down
113 changes: 112 additions & 1 deletion drivers/net/wireless/ath/ath6kl/wmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,8 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx,
u8 pairwise_crypto_len,
enum crypto_type group_crypto,
u8 group_crypto_len, int ssid_len, u8 *ssid,
u8 *bssid, u16 channel, u32 ctrl_flags)
u8 *bssid, u16 channel, u32 ctrl_flags,
u8 nw_subtype)
{
struct sk_buff *skb;
struct wmi_connect_cmd *cc;
Expand Down Expand Up @@ -1744,6 +1745,7 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx,
cc->grp_crypto_len = group_crypto_len;
cc->ch = cpu_to_le16(channel);
cc->ctrl_flags = cpu_to_le32(ctrl_flags);
cc->nw_subtype = nw_subtype;

if (bssid != NULL)
memcpy(cc->bssid, bssid, ETH_ALEN);
Expand Down Expand Up @@ -1796,6 +1798,72 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx)
return ret;
}

int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx,
enum wmi_scan_type scan_type,
u32 force_fgscan, u32 is_legacy,
u32 home_dwell_time, u32 force_scan_interval,
s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates)
{
struct sk_buff *skb;
struct wmi_begin_scan_cmd *sc;
s8 size;
int i, band, ret;
struct ath6kl *ar = wmi->parent_dev;
int num_rates;

size = sizeof(struct wmi_begin_scan_cmd);

if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN))
return -EINVAL;

if (num_chan > WMI_MAX_CHANNELS)
return -EINVAL;

if (num_chan)
size += sizeof(u16) * (num_chan - 1);

skb = ath6kl_wmi_get_new_buf(size);
if (!skb)
return -ENOMEM;

sc = (struct wmi_begin_scan_cmd *) skb->data;
sc->scan_type = scan_type;
sc->force_fg_scan = cpu_to_le32(force_fgscan);
sc->is_legacy = cpu_to_le32(is_legacy);
sc->home_dwell_time = cpu_to_le32(home_dwell_time);
sc->force_scan_intvl = cpu_to_le32(force_scan_interval);
sc->no_cck = cpu_to_le32(no_cck);
sc->num_ch = num_chan;

for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband =
ar->wiphy->bands[band];
u32 ratemask = rates[band];
u8 *supp_rates = sc->supp_rates[band].rates;
num_rates = 0;

for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & ratemask) == 0)
continue; /* skip rate */
supp_rates[num_rates++] =
(u8) (sband->bitrates[i].bitrate / 5);
}
sc->supp_rates[band].nrates = num_rates;
}

for (i = 0; i < num_chan; i++)
sc->ch_list[i] = cpu_to_le16(ch_list[i]);

ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_BEGIN_SCAN_CMDID,
NO_SYNC_WMIFLAG);

return ret;
}

/* ath6kl_wmi_start_scan_cmd is to be deprecated. Use
* ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P
* mgmt operations using station interface.
*/
int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx,
enum wmi_scan_type scan_type,
u32 force_fgscan, u32 is_legacy,
Expand Down Expand Up @@ -3006,6 +3074,10 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur)
NO_SYNC_WMIFLAG);
}

/* ath6kl_wmi_send_action_cmd is to be deprecated. Use
* ath6kl_wmi_send_mgmt_cmd instead. The new function supports P2P
* mgmt operations using station interface.
*/
int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
u32 wait, const u8 *data, u16 data_len)
{
Expand Down Expand Up @@ -3043,6 +3115,45 @@ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
NO_SYNC_WMIFLAG);
}

int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
u32 wait, const u8 *data, u16 data_len,
u32 no_cck)
{
struct sk_buff *skb;
struct wmi_send_mgmt_cmd *p;
u8 *buf;

if (wait)
return -EINVAL; /* Offload for wait not supported */

buf = kmalloc(data_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;

skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
if (!skb) {
kfree(buf);
return -ENOMEM;
}

kfree(wmi->last_mgmt_tx_frame);
memcpy(buf, data, data_len);
wmi->last_mgmt_tx_frame = buf;
wmi->last_mgmt_tx_frame_len = data_len;

ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u "
"len=%u\n", id, freq, wait, data_len);
p = (struct wmi_send_mgmt_cmd *) skb->data;
p->id = cpu_to_le32(id);
p->freq = cpu_to_le32(freq);
p->wait = cpu_to_le32(wait);
p->no_cck = cpu_to_le32(no_cck);
p->len = cpu_to_le16(data_len);
memcpy(p->data, data, data_len);
return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SEND_MGMT_CMDID,
NO_SYNC_WMIFLAG);
}

int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
const u8 *dst, const u8 *data,
u16 data_len)
Expand Down
Loading

0 comments on commit 3ca9d1f

Please sign in to comment.