/* * Copyright (C) 2015 Spreadtrum Communications Inc. * * Abstract : This file is an implementation for cfg80211 subsystem * * Authors: * Chaojie Xu * * 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); }