2025-05-10 21:58:58 +08:00

210 lines
5.5 KiB
C

/*
* Copyright (C) 2015 Spreadtrum Communications Inc.
*
* Abstract : This file is an implementation for cfg80211 subsystem
*
* Authors:
* Chaojie Xu <chaojie.xu@spreadtrum.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 "ibss.h"
#include "sprdwl.h"
#define IBSS_INITIAL_SCAN_ALLOWED (1)
#define IBSS_COALESCE_ALLOWED (0)
#define IBSS_COLESCE (0)
#define IBSS_SCAN_SUPPRESS (0)
#define IBSS_ATIM (10)
#define WPA_RSN (2)
/* cfg80211 */
int sprdwl_cfg80211_join_ibss(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_ibss_params *params)
{
int ret = 0;
struct ieee80211_channel *chan;
struct sprdwl_join_params join_params;
u32 join_params_size;
u8 coalesce = IBSS_COLESCE;
u8 scan_suppress = IBSS_SCAN_SUPPRESS;
u8 atim = IBSS_ATIM;
#ifdef IBSS_RSN_SUPPORT
u8 wpa_version = WPA_RSN;
#endif /* IBSS_RSN_SUPPORT */
struct sprdwl_vif *vif = netdev_priv(ndev);
wl_ndev_log(L_DBG, ndev, "%s enter\n", __func__);
if (SPRDWL_MODE_IBSS != vif->mode) {
wl_ndev_log(L_ERR, ndev, "%s invalid mode: %d\n", __func__,
vif->mode);
return -EINVAL;
}
if (!params->ssid || params->ssid_len <= 0) {
wl_ndev_log(L_ERR, ndev, "%s invalid SSID\n", __func__);
return -EINVAL;
}
/* set channel */
chan = params->chandef.chan;
if (chan) {
ret = sprdwl_set_channel(vif->priv, vif->mode,
ieee80211_frequency_to_channel(
chan->center_freq));
if (ret < 0) {
wl_ndev_log(L_ERR, ndev, "%s set channel failed(%d)\n",
__func__, ret);
return ret;
}
}
/* Join with specific SSID */
wl_ndev_log(L_INFO, ndev, "%s params->ssid=%s\n", __func__, params->ssid);
wl_ndev_log(L_INFO, ndev, "%s params->ssid_len=%d\n",
__func__, params->ssid_len);
join_params_size = sizeof(join_params);
memset(&join_params, 0, join_params_size);
memcpy(join_params.ssid, params->ssid, params->ssid_len);
join_params.ssid_len = params->ssid_len;
if (params->bssid) {
join_params.bssid_len = ETH_ALEN;
ether_addr_copy(join_params.bssid, params->bssid);
} else {
join_params.bssid_len = 0;
memset(join_params.bssid, 0, ETH_ALEN);
}
wl_ndev_log(L_INFO, ndev, "%s join_params.ssid=%s\n",
__func__, join_params.ssid);
wl_ndev_log(L_INFO, ndev, "%s join_params.ssid_len=%d\n",
__func__, join_params.ssid_len);
/* attribute */
ret = sprdwl_set_ibss_attribute(vif->priv, vif->mode,
SPRDWL_IBSS_COALESCE, coalesce);
if (ret) {
wl_ndev_log(L_ERR, ndev, "%s set coalesce failed (%d)\n",
__func__, ret);
return ret;
}
ret = sprdwl_set_ibss_attribute(vif->priv, vif->mode,
SPRDWL_IBSS_SCAN_SUPPRESS,
scan_suppress);
if (ret) {
wl_ndev_log(L_ERR, ndev, "%s set scan_suppress failed (%d)\n",
__func__, ret);
return ret;
}
ret = sprdwl_set_ibss_attribute(vif->priv, vif->mode,
SPRDWL_IBSS_ATIM, atim);
if (ret) {
wl_ndev_log(L_ERR, ndev, "%s set ATIM failed (%d)\n",
__func__, ret);
return ret;
}
#ifdef IBSS_RSN_SUPPORT
ret = sprdwl_set_ibss_attribute(vif->priv, vif->mode,
SPRDWL_IBSS_WPA_VERSION, wpa_version);
if (ret) {
wl_ndev_log(L_ERR, ndev, "%s set wpa_version failed (%d)\n",
__func__, ret);
return ret;
}
#endif /* IBSS_RSN_SUPPORT */
ret = sprdwl_ibss_join(vif->priv, vif->mode,
&join_params, join_params_size);
if (ret) {
wl_ndev_log(L_ERR, ndev, "%s join failed (%d)\n", __func__, ret);
return ret;
}
/* update */
ether_addr_copy(vif->bssid, join_params.bssid);
memcpy(vif->ssid, join_params.ssid, join_params.ssid_len);
vif->ssid_len = join_params.ssid_len;
return ret;
}
int sprdwl_cfg80211_leave_ibss(struct wiphy *wiphy,
struct net_device *ndev)
{
struct sprdwl_vif *vif = netdev_priv(ndev);
enum sm_state old_state = vif->sm_state;
int ret = 0;
wl_ndev_log(L_DBG, ndev, "%s enter\n", __func__);
if (SPRDWL_MODE_IBSS != vif->mode) {
wl_ndev_log(L_ERR, ndev, "%s invalid mode: %d\n", __func__,
vif->mode);
return -EINVAL;
}
vif->sm_state = SPRDWL_DISCONNECTING;
/* disconect, use reason code 0 temporarily*/
ret = sprdwl_disconnect(vif->priv, vif->mode, 0);
if (ret < 0) {
vif->sm_state = old_state;
wl_ndev_log(L_ERR, ndev, "%s disconnect failed (%d)\n", __func__, ret);
return ret;
}
memset(vif->ssid, 0, sizeof(vif->ssid));
memset(vif->bssid, 0, ETH_ALEN);
return ret;
}
/* cmd */
int sprdwl_set_ibss_attribute(struct sprdwl_priv *priv, u8 vif_mode,
u8 sub_type, u8 value)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_ibss_attr *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_mode,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_IBSS_ATTR);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_ibss_attr *)msg->data;
p->sub_type = sub_type;
p->value = value;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_ibss_join(struct sprdwl_priv *priv, u8 vif_mode,
struct sprdwl_join_params *params, u32 params_len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_join_params *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_mode,
SPRDWL_HEAD_RSP, WIFI_CMD_IBSS_JOIN);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_join_params *)msg->data;
memcpy(p, params, params_len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}