summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c')
-rw-r--r--drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c1762
1 files changed, 1762 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c
new file mode 100644
index 000000000000..e83d4de4d282
--- /dev/null
+++ b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c
@@ -0,0 +1,1762 @@
+/** @file moal_uap_cfg80211.c
+ *
+ * @brief This file contains the functions for uAP CFG80211.
+ *
+ * Copyright (C) 2011-2012, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+
+#include "moal_cfg80211.h"
+#include "moal_uap_cfg80211.h"
+
+/* these 3 function will be called in woal_cfg80211_wifi_direct_ops */
+int woal_cfg80211_add_beacon(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct beacon_parameters *params);
+
+int woal_cfg80211_set_beacon(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct beacon_parameters *params);
+
+int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev);
+
+static int woal_uap_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_scan_request *request);
+
+static int woal_uap_cfg80211_connect(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme);
+
+static int woal_uap_cfg80211_disconnect(struct wiphy *wiphy,
+ struct net_device *dev,
+ t_u16 reason_code);
+
+int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 * mac, struct station_info *stainfo);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS)
+static const struct ieee80211_txrx_stypes
+ ieee80211_uap_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_ADHOC] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+ [NL80211_IFTYPE_STATION] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4),
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4),
+ },
+ [NL80211_IFTYPE_AP_VLAN] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+ [NL80211_IFTYPE_MESH_POINT] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+};
+#endif
+
+/** cfg80211 uAP operations */
+static struct cfg80211_ops woal_cfg80211_uap_ops = {
+ .add_virtual_intf = woal_cfg80211_add_virtual_intf,
+ .del_virtual_intf = woal_cfg80211_del_virtual_intf,
+ .set_channel = woal_cfg80211_set_channel,
+ .scan = woal_uap_cfg80211_scan,
+ .connect = woal_uap_cfg80211_connect,
+ .disconnect = woal_uap_cfg80211_disconnect,
+ .set_wiphy_params = woal_cfg80211_set_wiphy_params,
+ .change_virtual_intf = woal_cfg80211_change_virtual_intf,
+ .add_key = woal_cfg80211_add_key,
+ .del_key = woal_cfg80211_del_key,
+ .set_default_key = woal_cfg80211_set_default_key,
+ .add_beacon = woal_cfg80211_add_beacon,
+ .set_beacon = woal_cfg80211_set_beacon,
+ .del_beacon = woal_cfg80211_del_beacon,
+ .get_station = woal_uap_cfg80211_get_station,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS)
+ .mgmt_frame_register = woal_cfg80211_mgmt_frame_register,
+ .mgmt_tx = woal_cfg80211_mgmt_tx,
+#endif
+};
+
+/********************************************************
+ Local Variables
+********************************************************/
+
+/********************************************************
+ Global Variables
+********************************************************/
+
+/********************************************************
+ Local Functions
+********************************************************/
+
+/********************************************************
+ Global Functions
+********************************************************/
+/**
+ * @brief Filter specific IE in ie buf
+ *
+ * @param ie Pointer to IEs
+ * @param len Total length of ie
+ * @param ie_out Pointer to out IE buf
+ *
+ * @return out IE length
+ */
+static t_u16
+woal_filter_beacon_ies(const t_u8 * ie, int len, t_u8 * ie_out)
+{
+ int left_len = len;
+ const t_u8 *pos = ie;
+ int length;
+ t_u8 id = 0;
+ t_u16 out_len = 0;
+
+ /* ERP_INFO and RSN IE will be fileter out */
+ while (left_len >= 2) {
+ length = *(pos + 1);
+ id = *pos;
+ if ((length + 2) > left_len)
+ break;
+ switch (id) {
+ case ERP_INFO:
+ case RSN_IE:
+ break;
+ default:
+ memcpy(ie_out + out_len, pos, length + 2);
+ out_len += length + 2;
+ break;
+ }
+ pos += (length + 2);
+ left_len -= (length + 2);
+ }
+ return out_len;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS)
+/**
+ * @brief Verify RSN IE
+ *
+ * @param rsn_ie Pointer RSN IE
+ * @param sys_config Pointer to mlan_uap_bss_param structure
+ *
+ * @return MTRUE/MFALSE
+ */
+static t_u8
+woal_check_rsn_ie(IEEEtypes_Rsn_t * rsn_ie, mlan_uap_bss_param * sys_config)
+{
+ int left = 0;
+ int count = 0;
+ int i = 0;
+ wpa_suite_auth_key_mgmt_t *key_mgmt = NULL;
+ left = rsn_ie->len + 2;
+ if (left < sizeof(IEEEtypes_Rsn_t))
+ return MFALSE;
+ sys_config->wpa_cfg.group_cipher = 0;
+ sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0;
+ /* check the group cipher */
+ switch (rsn_ie->group_cipher.type) {
+ case WPA_CIPHER_TKIP:
+ sys_config->wpa_cfg.group_cipher = CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+ count = le16_to_cpu(rsn_ie->pairwise_cipher.count);
+ for (i = 0; i < count; i++) {
+ switch (rsn_ie->pairwise_cipher.list[i].type) {
+ case WPA_CIPHER_TKIP:
+ sys_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ sys_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+ }
+ left -= sizeof(IEEEtypes_Rsn_t) + (count - 1) * sizeof(wpa_suite);
+ if (left < sizeof(wpa_suite_auth_key_mgmt_t))
+ return MFALSE;
+ key_mgmt =
+ (wpa_suite_auth_key_mgmt_t *) ((u8 *) rsn_ie + sizeof(IEEEtypes_Rsn_t) +
+ (count - 1) * sizeof(wpa_suite));
+ count = le16_to_cpu(key_mgmt->count);
+ if (left <
+ (sizeof(wpa_suite_auth_key_mgmt_t) + (count - 1) * sizeof(wpa_suite)))
+ return MFALSE;
+
+ for (i = 0; i < count; i++) {
+ switch (key_mgmt->list[i].type) {
+ case RSN_AKM_8021X:
+ sys_config->key_mgmt = KEY_MGMT_EAP;
+ break;
+ case RSN_AKM_PSK:
+ sys_config->key_mgmt = KEY_MGMT_PSK;
+ break;
+ }
+ }
+ return MTRUE;
+}
+
+/**
+ * @brief Verify WPA IE
+ *
+ * @param wpa_ie Pointer WPA IE
+ * @param sys_config Pointer to mlan_uap_bss_param structure
+ *
+ * @return MTRUE/MFALSE
+ */
+static t_u8
+woal_check_wpa_ie(IEEEtypes_Wpa_t * wpa_ie, mlan_uap_bss_param * sys_config)
+{
+ int left = 0;
+ int count = 0;
+ int i = 0;
+ wpa_suite_auth_key_mgmt_t *key_mgmt = NULL;
+ left = wpa_ie->len + 2;
+ if (left < sizeof(IEEEtypes_Wpa_t))
+ return MFALSE;
+ sys_config->wpa_cfg.group_cipher = 0;
+ sys_config->wpa_cfg.pairwise_cipher_wpa = 0;
+ switch (wpa_ie->group_cipher.type) {
+ case WPA_CIPHER_TKIP:
+ sys_config->wpa_cfg.group_cipher = CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+ count = le16_to_cpu(wpa_ie->pairwise_cipher.count);
+ for (i = 0; i < count; i++) {
+ switch (wpa_ie->pairwise_cipher.list[i].type) {
+ case WPA_CIPHER_TKIP:
+ sys_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_AES_CCM:
+ sys_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+ }
+ left -= sizeof(IEEEtypes_Wpa_t) + (count - 1) * sizeof(wpa_suite);
+ if (left < sizeof(wpa_suite_auth_key_mgmt_t))
+ return MFALSE;
+ key_mgmt =
+ (wpa_suite_auth_key_mgmt_t *) ((u8 *) wpa_ie + sizeof(IEEEtypes_Wpa_t) +
+ (count - 1) * sizeof(wpa_suite));
+ count = le16_to_cpu(key_mgmt->count);
+ if (left <
+ (sizeof(wpa_suite_auth_key_mgmt_t) + (count - 1) * sizeof(wpa_suite)))
+ return MFALSE;
+ for (i = 0; i < count; i++) {
+ switch (key_mgmt->list[i].type) {
+ case RSN_AKM_8021X:
+ sys_config->key_mgmt = KEY_MGMT_EAP;
+ break;
+ case RSN_AKM_PSK:
+ sys_config->key_mgmt = KEY_MGMT_PSK;
+ break;
+ }
+ }
+ return MTRUE;
+}
+
+/**
+ * @brief Find RSN/WPA IES
+ *
+ * @param ie Pointer IE buffer
+ * @param sys_config Pointer to mlan_uap_bss_param structure
+ *
+ * @return MTRUE/MFALSE
+ */
+static t_u8
+woal_find_wpa_ies(t_u8 * ie, int len, mlan_uap_bss_param * sys_config)
+{
+ int bytes_left = len;
+ t_u8 *pcurrent_ptr = ie;
+ t_u16 total_ie_len;
+ t_u8 element_len;
+ t_u8 wpa2 = 0;
+ t_u8 wpa = 0;
+ t_u8 ret = MFALSE;
+ IEEEtypes_ElementId_e element_id;
+ IEEEtypes_VendorSpecific_t *pvendor_ie;
+ const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
+
+ while (bytes_left >= 2) {
+ element_id = (IEEEtypes_ElementId_e) (*((t_u8 *) pcurrent_ptr));
+ element_len = *((t_u8 *) pcurrent_ptr + 1);
+ total_ie_len = element_len + sizeof(IEEEtypes_Header_t);
+ if (bytes_left < total_ie_len) {
+ PRINTM(MERROR, "InterpretIE: Error in processing IE, "
+ "bytes left < IE length\n");
+ bytes_left = 0;
+ continue;
+ }
+ switch (element_id) {
+ case RSN_IE:
+ wpa2 =
+ woal_check_rsn_ie((IEEEtypes_Rsn_t *) pcurrent_ptr, sys_config);
+ break;
+ case VENDOR_SPECIFIC_221:
+ pvendor_ie = (IEEEtypes_VendorSpecific_t *) pcurrent_ptr;
+ if (!memcmp(pvendor_ie->vend_hdr.oui, wpa_oui, sizeof(wpa_oui)))
+ wpa =
+ woal_check_wpa_ie((IEEEtypes_Wpa_t *) pcurrent_ptr,
+ sys_config);
+ break;
+ default:
+ break;
+ }
+ pcurrent_ptr += element_len + 2;
+ /* Need to account for IE ID and IE Len */
+ bytes_left -= (element_len + 2);
+ }
+ if (wpa && wpa2) {
+ sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2;
+ ret = MTRUE;
+ } else if (wpa2) {
+ sys_config->protocol = PROTOCOL_WPA2;
+ ret = MTRUE;
+ } else if (wpa) {
+ sys_config->protocol = PROTOCOL_WPA;
+ ret = MTRUE;
+ }
+ return ret;
+}
+#endif
+
+/**
+ * @brief Look up specific IE in a buf
+ *
+ * @param ie Pointer to IEs
+ * @param len Total length of ie
+ * @param id Element id to lookup
+ *
+ * @return Pointer of the specific IE -- success, NULL -- fail
+ */
+static const t_u8 *
+woal_parse_ie_tlv(const t_u8 * ie, int len, t_u8 id)
+{
+ int left_len = len;
+ const t_u8 *pos = ie;
+ int length;
+
+ /* IE format: | u8 | id | | u8 | len | | var | data | */
+ while (left_len >= 2) {
+ length = *(pos + 1);
+ if ((*pos == id) && (length + 2) <= left_len)
+ return pos;
+ pos += (length + 2);
+ left_len -= (length + 2);
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Add custom ie to mgmt frames.
+ *
+ * @param priv A pointer to moal private structure
+ * @param beacon_ies_data Beacon ie
+ * @param beacon_index The index for beacon when auto index
+ * @param proberesp_ies_data Probe resp ie
+ * @param proberesp_index The index for probe resp when auto index
+ * @param assocresp_ies_data Assoc resp ie
+ * @param assocresp_index The index for assoc resp when auto index
+ * @param probereq_ies_data Probe req ie
+ * @param probereq_index The index for probe req when auto index *
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int
+woal_cfg80211_custom_ie(moal_private * priv,
+ custom_ie * beacon_ies_data, t_u16 * beacon_index,
+ custom_ie * proberesp_ies_data, t_u16 * proberesp_index,
+ custom_ie * assocresp_ies_data, t_u16 * assocresp_index,
+ custom_ie * probereq_ies_data, t_u16 * probereq_index)
+{
+ mlan_ioctl_req *ioctl_req = NULL;
+ mlan_ds_misc_cfg *misc = NULL;
+ mlan_ds_misc_custom_ie *custom_ie = NULL;
+ t_u8 *pos = NULL;
+ t_u16 len = 0;
+ int ret = 0;
+
+ ENTER();
+
+ if (!(custom_ie = kmalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ memset(custom_ie, 0x00, sizeof(mlan_ds_misc_custom_ie));
+ custom_ie->type = TLV_TYPE_MGMT_IE;
+
+ pos = (t_u8 *) custom_ie->ie_data_list;
+ if (beacon_ies_data) {
+ len = sizeof(*beacon_ies_data) - MAX_IE_SIZE
+ + beacon_ies_data->ie_length;
+ memcpy(pos, beacon_ies_data, len);
+ pos += len;
+ custom_ie->len += len;
+ }
+
+ if (proberesp_ies_data) {
+ len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE
+ + proberesp_ies_data->ie_length;
+ memcpy(pos, proberesp_ies_data, len);
+ pos += len;
+ custom_ie->len += len;
+ }
+
+ if (assocresp_ies_data) {
+ len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE
+ + assocresp_ies_data->ie_length;
+ memcpy(pos, assocresp_ies_data, len);
+ custom_ie->len += len;
+ }
+
+ if (probereq_ies_data) {
+ len = sizeof(*probereq_ies_data) - MAX_IE_SIZE
+ + probereq_ies_data->ie_length;
+ memcpy(pos, probereq_ies_data, len);
+ pos += len;
+ custom_ie->len += len;
+ }
+
+ ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (ioctl_req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ misc = (mlan_ds_misc_cfg *) ioctl_req->pbuf;
+ misc->sub_command = MLAN_OID_MISC_CUSTOM_IE;
+ ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
+ ioctl_req->action = MLAN_ACT_SET;
+
+ memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie));
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* get the assigned index */
+ pos = (t_u8 *) (&misc->param.cust_ie.ie_data_list[0].ie_index);
+ if (beacon_ies_data && beacon_ies_data->ie_length
+ && beacon_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) {
+ /* save beacon ie index after auto-indexing */
+ *beacon_index = misc->param.cust_ie.ie_data_list[0].ie_index;
+ len = sizeof(*beacon_ies_data) - MAX_IE_SIZE
+ + beacon_ies_data->ie_length;
+ pos += len;
+ }
+
+ if (proberesp_ies_data && proberesp_ies_data->ie_length
+ && proberesp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) {
+ /* save probe resp ie index after auto-indexing */
+ *proberesp_index = *((t_u16 *) pos);
+ len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE
+ + proberesp_ies_data->ie_length;
+ pos += len;
+ }
+
+ if (assocresp_ies_data && assocresp_ies_data->ie_length
+ && assocresp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) {
+ /* save assoc resp ie index after auto-indexing */
+ *assocresp_index = *((t_u16 *) pos);
+ len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE
+ + assocresp_ies_data->ie_length;
+ pos += len;
+ }
+ if (probereq_ies_data && probereq_ies_data->ie_length
+ && probereq_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) {
+ /* save probe resp ie index after auto-indexing */
+ *probereq_index = *((t_u16 *) pos);
+ len = sizeof(*probereq_ies_data) - MAX_IE_SIZE
+ + probereq_ies_data->ie_length;
+ pos += len;
+ }
+
+ if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL)
+ ret = -EFAULT;
+
+ done:
+ if (ioctl_req)
+ kfree(ioctl_req);
+ if (custom_ie)
+ kfree(custom_ie);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function returns priv
+ * based on mgmt ie index
+ *
+ * @param handle A pointer to moal_handle
+ * @param index mgmt ie index
+ *
+ * @return Pointer to moal_private
+ */
+static moal_private *
+woal_get_priv_by_mgmt_index(moal_handle * handle, t_u16 index)
+{
+ int i;
+
+ for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
+ if (handle->priv[i]) {
+ if (handle->priv[i]->probereq_index == index)
+ return (handle->priv[i]);
+ }
+ }
+ return NULL;
+}
+
+/**
+ * @brief config AP or GO for mgmt frame ies.
+ *
+ * @param priv A pointer to moal private structure
+ * @param beacon_ies A pointer to beacon ies
+ * @param beacon_ies_len Beacon ies length
+ * @param proberesp_ies A pointer to probe resp ies
+ * @param proberesp_ies_len Probe resp ies length
+ * @param assocresp_ies A pointer to probe resp ies
+ * @param assocresp_ies_len Assoc resp ies length
+ * @param probereq_ies A pointer to probe req ies
+ * @param probereq_ies_len Probe req ies length *
+ * @param mask Mgmt frame mask
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int
+woal_cfg80211_mgmt_frame_ie(moal_private * priv,
+ const t_u8 * beacon_ies, size_t beacon_ies_len,
+ const t_u8 * proberesp_ies,
+ size_t proberesp_ies_len,
+ const t_u8 * assocresp_ies,
+ size_t assocresp_ies_len, const t_u8 * probereq_ies,
+ size_t probereq_ies_len, t_u16 mask)
+{
+ int ret = 0;
+ t_u8 *pos = NULL;
+ custom_ie *beacon_ies_data = NULL;
+ custom_ie *proberesp_ies_data = NULL;
+ custom_ie *assocresp_ies_data = NULL;
+ custom_ie *probereq_ies_data = NULL;
+
+ /* static variables for mgmt frame ie auto-indexing */
+ static t_u16 beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ static t_u16 proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ static t_u16 assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ static t_u16 probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ moal_private *pmpriv = NULL;
+ static t_u16 rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ const t_u8 *rsn_ie;
+
+ ENTER();
+
+ if (mask & MGMT_MASK_BEACON) {
+ if (!(beacon_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ if (beacon_ies && beacon_ies_len) {
+ rsn_ie =
+ woal_parse_ie_tlv((t_u8 *) beacon_ies, (int) beacon_ies_len,
+ RSN_IE);
+ if (rsn_ie) {
+ beacon_ies_data->ie_index = rsn_index;
+ beacon_ies_data->mgmt_subtype_mask =
+ MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP |
+ MGMT_MASK_ASSOC_RESP;
+ beacon_ies_data->ie_length = rsn_ie[1] + 2;
+ memcpy(beacon_ies_data->ie_buffer, rsn_ie, rsn_ie[1] + 2);
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index,
+ NULL, &proberesp_index, NULL,
+ &assocresp_index, NULL,
+ &probereq_index)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ } else {
+ /* clear rsn_ie */
+ if (rsn_index <= MAX_MGMT_IE_INDEX) {
+ beacon_ies_data->ie_index = rsn_index;
+ beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK;
+ beacon_ies_data->ie_length = 0;
+ rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index,
+ NULL, &proberesp_index, NULL,
+ &assocresp_index, NULL,
+ &probereq_index)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ }
+
+ if (mask & MGMT_MASK_PROBE_RESP) {
+ if (!(proberesp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ if (mask & MGMT_MASK_ASSOC_RESP) {
+ if (!(assocresp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+ if (mask & MGMT_MASK_PROBE_REQ) {
+ if (!(probereq_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+
+ if (beacon_ies_data) {
+ memset(beacon_ies_data, 0x00, sizeof(custom_ie));
+ if (beacon_ies && beacon_ies_len) {
+ /* set the beacon ies */
+ beacon_ies_data->ie_index = beacon_index;
+ beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON;
+ beacon_ies_data->mgmt_subtype_mask |= MGMT_MASK_ASSOC_RESP;
+ beacon_ies_data->ie_length = woal_filter_beacon_ies(beacon_ies,
+ beacon_ies_len,
+ beacon_ies_data->
+ ie_buffer);
+ } else {
+ /* clear the beacon ies */
+ if (beacon_index > MAX_MGMT_IE_INDEX) {
+ PRINTM(MERROR, "Invalid beacon index for mgmt frame ie.\n");
+ goto done;
+ }
+
+ beacon_ies_data->ie_index = beacon_index;
+ beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK;
+ beacon_ies_data->ie_length = 0;
+ beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ }
+ }
+
+ if (proberesp_ies_data) {
+ memset(proberesp_ies_data, 0x00, sizeof(custom_ie));
+ if (proberesp_ies && proberesp_ies_len) {
+ /* set the probe response ies */
+ // proberesp_ies_data->ie_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ proberesp_ies_data->ie_index = proberesp_index;
+ proberesp_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_RESP;
+ proberesp_ies_data->ie_length = proberesp_ies_len;
+ pos = proberesp_ies_data->ie_buffer;
+ memcpy(pos, proberesp_ies, proberesp_ies_len);
+ } else {
+ /* clear the probe response ies */
+ if (proberesp_index > MAX_MGMT_IE_INDEX) {
+ PRINTM(MERROR, "Invalid probe resp index for mgmt frame ie.\n");
+ goto done;
+ }
+
+ proberesp_ies_data->ie_index = proberesp_index;
+ proberesp_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK;
+ proberesp_ies_data->ie_length = 0;
+ proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ }
+ }
+ if (assocresp_ies_data) {
+ memset(assocresp_ies_data, 0x00, sizeof(custom_ie));
+ if (assocresp_ies && assocresp_ies_len) {
+ /* set the assoc response ies */
+ assocresp_ies_data->ie_index = assocresp_index;
+ assocresp_ies_data->mgmt_subtype_mask = MGMT_MASK_ASSOC_RESP;
+ assocresp_ies_data->ie_length = assocresp_ies_len;
+ pos = assocresp_ies_data->ie_buffer;
+ memcpy(pos, assocresp_ies, assocresp_ies_len);
+ } else {
+ /* clear the assoc response ies */
+ if (assocresp_index > MAX_MGMT_IE_INDEX) {
+ PRINTM(MERROR, "Invalid assoc resp index for mgmt frame ie.\n");
+ goto done;
+ }
+
+ assocresp_ies_data->ie_index = assocresp_index;
+ assocresp_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK;
+ assocresp_ies_data->ie_length = 0;
+ assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ }
+ }
+
+ if (probereq_ies_data) {
+ memset(probereq_ies_data, 0x00, sizeof(custom_ie));
+ if ((probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) &&
+ (priv->probereq_index != probereq_index)) {
+ pmpriv = woal_get_priv_by_mgmt_index(priv->phandle, probereq_index);
+ if (pmpriv) {
+ probereq_ies_data->ie_index = probereq_index;
+ probereq_ies_data->mgmt_subtype_mask =
+ MLAN_CUSTOM_IE_DELETE_MASK;
+ probereq_ies_data->ie_length = 0;
+ probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ pmpriv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_custom_ie(pmpriv, NULL, &beacon_index,
+ NULL, &proberesp_index,
+ NULL, &assocresp_index,
+ probereq_ies_data,
+ &probereq_index)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ memset(probereq_ies_data, 0x00, sizeof(custom_ie));
+ }
+ }
+ if (probereq_ies && probereq_ies_len) {
+ /* set the probe req ies */
+ probereq_ies_data->ie_index = probereq_index;
+ probereq_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_REQ;
+ probereq_ies_data->ie_length = probereq_ies_len;
+ pos = probereq_ies_data->ie_buffer;
+ memcpy(pos, probereq_ies, probereq_ies_len);
+ } else {
+ /* clear the probe req ies */
+ if (probereq_index > MAX_MGMT_IE_INDEX) {
+ PRINTM(MERROR, "Invalid probe resp index for mgmt frame ie.\n");
+ goto done;
+ }
+ probereq_ies_data->ie_index = probereq_index;
+ probereq_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK;
+ probereq_ies_data->ie_length = 0;
+ probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
+ }
+ }
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_custom_ie(priv, beacon_ies_data, &beacon_index,
+ proberesp_ies_data, &proberesp_index,
+ assocresp_ies_data, &assocresp_index,
+ probereq_ies_data, &probereq_index)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (probereq_ies_data)
+ priv->probereq_index = probereq_index;
+
+ done:
+ if (beacon_ies_data)
+ kfree(beacon_ies_data);
+ if (proberesp_ies_data)
+ kfree(proberesp_ies_data);
+ if (assocresp_ies_data)
+ kfree(assocresp_ies_data);
+
+ LEAVE();
+
+ return ret;
+}
+
+/**
+ * @brief initialize AP or GO bss config
+ *
+ * @param priv A pointer to moal private structure
+ * @param params A pointer to beacon_parameters structure
+ * @return 0 -- success, otherwise fail
+ */
+static int
+woal_cfg80211_beacon_config(moal_private * priv,
+ struct beacon_parameters *params)
+{
+ int ret = 0;
+ mlan_uap_bss_param sys_config;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS)
+ int i = 0;
+#else
+ const t_u8 *ssid_ie = NULL;
+ struct ieee80211_mgmt *head = NULL;
+ t_u16 capab_info = 0;
+#endif
+
+ ENTER();
+
+ if (params == NULL) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (priv->bss_type != MLAN_BSS_TYPE_UAP
+#ifdef WIFI_DIRECT_SUPPORT
+ && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT
+#endif
+ ) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Initialize the uap bss values which are uploaded from firmware */
+ if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
+ MLAN_ACT_GET,
+ MOAL_IOCTL_WAIT,
+ &sys_config)) {
+ PRINTM(MERROR, "Error getting AP confiruration\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Setting the default values */
+ sys_config.channel = 6;
+ sys_config.preamble_type = 0;
+
+ if (priv->bss_type == MLAN_BSS_TYPE_UAP) {
+ if (params->interval)
+ sys_config.beacon_period = params->interval;
+ if (params->dtim_period)
+ sys_config.dtim_period = params->dtim_period;
+ }
+ if (priv->channel)
+ sys_config.channel = priv->channel;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS)
+ if (!params->ssid || !params->ssid_len) {
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(sys_config.ssid.ssid, params->ssid,
+ MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len));
+ sys_config.ssid.ssid_len = MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len);
+ if (params->hidden_ssid)
+ sys_config.bcast_ssid_ctl = 0;
+ else
+ sys_config.bcast_ssid_ctl = 1;
+ if (params->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+ sys_config.auth_mode = MLAN_AUTH_MODE_SHARED;
+ else
+ sys_config.auth_mode = MLAN_AUTH_MODE_OPEN;
+
+ for (i = 0; i < params->crypto.n_akm_suites; i++) {
+ switch (params->crypto.akm_suites[i]) {
+ case WLAN_AKM_SUITE_8021X:
+ sys_config.key_mgmt = KEY_MGMT_EAP;
+ if ((params->crypto.wpa_versions & NL80211_WPA_VERSION_1) &&
+ (params->crypto.wpa_versions & NL80211_WPA_VERSION_2))
+ sys_config.protocol = PROTOCOL_WPA | PROTOCOL_WPA2;
+ else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ sys_config.protocol = PROTOCOL_WPA2;
+ else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ sys_config.protocol = PROTOCOL_WPA;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ sys_config.key_mgmt = KEY_MGMT_PSK;
+ if ((params->crypto.wpa_versions & NL80211_WPA_VERSION_1) &&
+ (params->crypto.wpa_versions & NL80211_WPA_VERSION_2))
+ sys_config.protocol = PROTOCOL_WPA | PROTOCOL_WPA2;
+ else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ sys_config.protocol = PROTOCOL_WPA2;
+ else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ sys_config.protocol = PROTOCOL_WPA;
+ break;
+ }
+ }
+ sys_config.wpa_cfg.pairwise_cipher_wpa = 0;
+ sys_config.wpa_cfg.pairwise_cipher_wpa2 = 0;
+ for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) {
+ switch (params->crypto.ciphers_pairwise[i]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ sys_config.wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP;
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ sys_config.wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ sys_config.wpa_cfg.pairwise_cipher_wpa |= CIPHER_AES_CCMP;
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ sys_config.wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_AES_CCMP;
+ break;
+ }
+ }
+ switch (params->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ sys_config.protocol = PROTOCOL_STATIC_WEP;
+ sys_config.key_mgmt = KEY_MGMT_NONE;
+ sys_config.wpa_cfg.length = 0;
+ sys_config.wep_cfg.key0.key_index = priv->key_index;
+ sys_config.wep_cfg.key0.is_default = 1;
+ sys_config.wep_cfg.key0.length = priv->key_len;
+ memcpy(sys_config.wep_cfg.key0.key, priv->key_material,
+ priv->key_len);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ sys_config.wpa_cfg.group_cipher = CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ sys_config.wpa_cfg.group_cipher = CIPHER_AES_CCMP;
+ break;
+ }
+#else
+ /* Since in Android ICS 4.0.1's wpa_supplicant, there is no way to set ssid
+ when GO (AP) starts up, so get it from beacon head parameter TODO: right
+ now use hard code 24 -- ieee80211 header lenth, 12 -- fixed element
+ length for beacon */
+#define BEACON_IE_OFFSET 36
+ /* Find SSID in head SSID IE id: 0, right now use hard code */
+ ssid_ie = woal_parse_ie_tlv(params->head + BEACON_IE_OFFSET,
+ params->head_len - BEACON_IE_OFFSET, 0);
+ if (!ssid_ie) {
+ PRINTM(MERROR, "No ssid IE found.\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (*(ssid_ie + 1) > 32) {
+ PRINTM(MERROR, "ssid len error: %d\n", *(ssid_ie + 1));
+ ret = -EFAULT;
+ goto done;
+ }
+ memcpy(sys_config.ssid.ssid, ssid_ie + 2, *(ssid_ie + 1));
+ sys_config.ssid.ssid_len = *(ssid_ie + 1);
+ head = (struct ieee80211_mgmt *) params->head;
+ capab_info = le16_to_cpu(head->u.beacon.capab_info);
+ PRINTM(MIOCTL, "capab_info=0x%x\n", head->u.beacon.capab_info);
+ sys_config.auth_mode = MLAN_AUTH_MODE_OPEN;
+ /** For ICS, we don't support OPEN mode */
+ if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ sys_config.protocol = PROTOCOL_STATIC_WEP;
+ sys_config.key_mgmt = KEY_MGMT_NONE;
+ sys_config.wpa_cfg.length = 0;
+ sys_config.wep_cfg.key0.key_index = priv->key_index;
+ sys_config.wep_cfg.key0.is_default = 1;
+ sys_config.wep_cfg.key0.length = priv->key_len;
+ memcpy(sys_config.wep_cfg.key0.key, priv->key_material, priv->key_len);
+ } else {
+ /** Get cipher and key_mgmt from RSN/WPA IE */
+ if (capab_info & WLAN_CAPABILITY_PRIVACY) {
+ if (MFALSE ==
+ woal_find_wpa_ies(params->tail, params->tail_len,
+ &sys_config)) {
+ /* hard code setting to wpa2-psk */
+ sys_config.protocol = PROTOCOL_WPA2;
+ sys_config.key_mgmt = KEY_MGMT_PSK;
+ sys_config.wpa_cfg.pairwise_cipher_wpa2 = CIPHER_AES_CCMP;
+ sys_config.wpa_cfg.group_cipher = CIPHER_AES_CCMP;
+ }
+ }
+ }
+#endif /* COMPAT_WIRELESS */
+ /* If the security mode is configured as WEP or WPA-PSK, it will disable
+ 11n automatically, and if configured as open(off) or wpa2-psk, it will
+ automatically enable 11n */
+ if ((sys_config.protocol == PROTOCOL_STATIC_WEP) ||
+ (sys_config.protocol == PROTOCOL_WPA))
+ woal_uap_set_11n_status(&sys_config, MLAN_ACT_DISABLE);
+ else
+ woal_uap_set_11n_status(&sys_config, MLAN_ACT_ENABLE);
+ if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
+ MLAN_ACT_SET,
+ MOAL_IOCTL_WAIT,
+ &sys_config)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ done:
+ LEAVE();
+ return ret;
+}
+
+static int
+woal_mon_open(struct net_device *ndev)
+{
+ ENTER();
+ LEAVE();
+ return 0;
+}
+
+static int
+woal_mon_close(struct net_device *ndev)
+{
+ ENTER();
+ LEAVE();
+ return 0;
+}
+
+static int
+woal_mon_set_mac_address(struct net_device *ndev, void *addr)
+{
+ ENTER();
+ LEAVE();
+ return 0;
+}
+
+static void
+woal_mon_set_multicast_list(struct net_device *ndev)
+{
+ ENTER();
+ LEAVE();
+}
+
+static int
+woal_mon_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ int len_rthdr;
+ int qos_len = 0;
+ int dot11_hdr_len = 24;
+ int snap_len = 6;
+ unsigned char *pdata;
+ unsigned short fc;
+ unsigned char src_mac_addr[6];
+ unsigned char dst_mac_addr[6];
+ struct ieee80211_hdr *dot11_hdr;
+ struct ieee80211_radiotap_header *prthdr =
+ (struct ieee80211_radiotap_header *) skb->data;
+ monitor_iface *mon_if = netdev_priv(ndev);
+
+ if (mon_if == NULL || mon_if->base_ndev == NULL) {
+ goto fail;
+ }
+
+ ENTER();
+ /* check for not even having the fixed radiotap header part */
+ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) {
+ PRINTM(MERROR, "Invalid radiotap hdr length,"
+ "skb->len: %d\n", skb->len);
+ goto fail; /* too short to be possibly valid */
+ }
+
+ /* is it a header version we can trust to find length from? */
+ if (unlikely(prthdr->it_version))
+ goto fail; /* only version 0 is supported */
+
+ /* then there must be a radiotap header with a length we can use */
+ len_rthdr = ieee80211_get_radiotap_len(skb->data);
+
+ /* does the skb contain enough to deliver on the alleged length? */
+ if (unlikely(skb->len < len_rthdr)) {
+ PRINTM(MERROR, "Invalid data length," "skb->len: %d\n", skb->len);
+ goto fail; /* skb too short for claimed rt header extent */
+ }
+
+ /* Skip the ratiotap header */
+ skb_pull(skb, len_rthdr);
+
+ dot11_hdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(dot11_hdr->frame_control);
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
+ /* Check if this ia a Wireless Distribution System (WDS) frame which
+ has 4 MAC addresses */
+ if (dot11_hdr->frame_control & 0x0080)
+ qos_len = 2;
+ if ((dot11_hdr->frame_control & 0x0300) == 0x0300)
+ dot11_hdr_len += 6;
+
+ memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr));
+ memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr));
+
+ /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for
+ for two MAC addresses */
+ skb_pull(skb,
+ dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2);
+ pdata = (unsigned char *) skb->data;
+ memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr));
+ memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr,
+ sizeof(src_mac_addr));
+
+ LEAVE();
+ return woal_hard_start_xmit(skb, mon_if->base_ndev);
+ }
+
+ fail:
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops woal_cfg80211_mon_if_ops = {
+ .ndo_open = woal_mon_open,
+ .ndo_start_xmit = woal_mon_hard_start_xmit,
+ .ndo_stop = woal_mon_close,
+ .ndo_set_mac_address = woal_mon_set_mac_address,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
+ .ndo_set_rx_mode = woal_mon_set_multicast_list,
+#else
+ .ndo_set_multicast_list = woal_mon_set_multicast_list,
+#endif
+};
+
+static void
+woal_mon_if_setup(struct net_device *dev)
+{
+ ENTER();
+ ether_setup(dev);
+ dev->netdev_ops = &woal_cfg80211_mon_if_ops;
+ dev->destructor = free_netdev;
+ LEAVE();
+}
+
+/**
+ * @brief Request the driver to add a monitor interface
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param name Virtual interface name
+ * @param flags Flags for the virtual interface
+ * @param params A pointer to vif_params structure
+ * @param new_dev Netdevice to be passed out
+ *
+ * @return A pointer to net_device -- success, otherwise null
+ */
+static int
+woal_cfg80211_add_mon_if(struct wiphy *wiphy, char *name,
+ u32 * flags, struct vif_params *params,
+ struct net_device **new_dev)
+{
+ int ret;
+ struct net_device *ndev;
+ monitor_iface *mon_if;
+ moal_private *priv;
+
+ ENTER();
+
+ ASSERT_RTNL();
+
+ priv = (moal_private *) woal_get_wiphy_priv(wiphy);
+
+ ndev = alloc_netdev_mq(sizeof(*mon_if), name, woal_mon_if_setup, 1);
+ if (!ndev) {
+ PRINTM(MFATAL, "Init virtual ethernet device failed\n");
+ ret = -EFAULT;
+ goto fail;
+ }
+
+ dev_net_set(ndev, wiphy_net(wiphy));
+
+ ret = dev_alloc_name(ndev, ndev->name);
+ if (ret < 0) {
+ PRINTM(MFATAL, "Net device alloc name fail.\n");
+ goto fail;
+ }
+
+ memcpy(ndev->perm_addr, wiphy->perm_addr, ETH_ALEN);
+ memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
+ SET_NETDEV_DEV(ndev, wiphy_dev(wiphy));
+
+ mon_if = netdev_priv(ndev);
+ ndev->ieee80211_ptr = &mon_if->wdev;
+ mon_if->wdev.iftype = NL80211_IFTYPE_MONITOR;
+ mon_if->wdev.wiphy = wiphy;
+ memcpy(mon_if->ifname, ndev->name, IFNAMSIZ);
+
+ ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+ ndev->netdev_ops = &woal_cfg80211_mon_if_ops;
+
+ mon_if->priv = priv;
+ mon_if->mon_ndev = ndev;
+ mon_if->base_ndev = priv->netdev;
+ mon_if->radiotap_enabled = 1;
+ mon_if->flag = 1;
+
+ ret = register_netdevice(ndev);
+ if (ret) {
+ PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret);
+ goto fail;
+ }
+
+ if (new_dev)
+ *new_dev = ndev;
+
+ fail:
+ if (ret && ndev)
+ free_netdev(ndev);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Request the driver to add a virtual interface
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param name Virtual interface name
+ * @param type Virtual interface type
+ * @param flags Flags for the virtual interface
+ * @param params A pointer to vif_params structure
+ *
+ * @return A pointer to net_device -- success, otherwise null
+ */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS)
+struct net_device *
+woal_cfg80211_add_virtual_intf(struct wiphy *wiphy,
+ char *name, enum nl80211_iftype type,
+ u32 * flags, struct vif_params *params)
+#else
+int
+woal_cfg80211_add_virtual_intf(struct wiphy *wiphy,
+ char *name, enum nl80211_iftype type,
+ u32 * flags, struct vif_params *params)
+#endif
+{
+ struct net_device *ndev = NULL;
+ int ret = 0;
+
+ ENTER();
+ PRINTM(MIOCTL, "add virtual intf: %d\n", type);
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ ret = woal_cfg80211_add_mon_if(wiphy, name, flags, params, &ndev);
+ break;
+ default:
+ PRINTM(MWARN, "Not supported if type: %d\n", type);
+ ret = -EFAULT;
+ break;
+ }
+ LEAVE();
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS)
+ if (ret)
+ return NULL;
+ else
+ return ndev;
+#else
+ return ret;
+#endif
+}
+
+/**
+ * @brief Request the driver to del a virtual interface
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev The pointer to net_device
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int
+woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev)
+{
+ ENTER();
+
+ PRINTM(MIOCTL, "del virtual intf\n");
+ ASSERT_RTNL();
+ unregister_netdevice(dev);
+
+ LEAVE();
+ return 0;
+}
+
+static int
+woal_uap_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_scan_request *request)
+{
+ ENTER();
+
+ cfg80211_scan_done(request, MTRUE);
+
+ LEAVE();
+ return 0;
+}
+
+static int
+woal_uap_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ ENTER();
+ LEAVE();
+ return 0;
+}
+
+static int
+woal_uap_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ t_u16 reason_code)
+{
+ ENTER();
+ LEAVE();
+ return 0;
+}
+
+/**
+ * @brief initialize AP or GO parameters
+
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param params A pointer to beacon_parameters structure
+ * @return 0 -- success, otherwise fail
+ */
+int
+woal_cfg80211_add_beacon(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct beacon_parameters *params)
+{
+ moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy);
+ int ret = 0;
+
+ ENTER();
+
+ PRINTM(MIOCTL, "add beacon\n");
+ if (params != NULL) {
+ /* bss config */
+ if (MLAN_STATUS_SUCCESS != woal_cfg80211_beacon_config(priv, params)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* set mgmt frame ies */
+ if (MLAN_STATUS_SUCCESS != woal_cfg80211_mgmt_frame_ie(priv,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS)
+ params->tail,
+ params->tail_len,
+ NULL, 0, NULL, 0,
+ NULL, 0,
+ MGMT_MASK_BEACON
+#else
+ params->tail,
+ params->tail_len,
+ params->
+ proberesp_ies,
+ params->
+ proberesp_ies_len,
+ params->
+ assocresp_ies,
+ params->
+ assocresp_ies_len,
+ NULL, 0,
+ MGMT_MASK_BEACON
+ |
+ MGMT_MASK_PROBE_RESP
+ |
+ MGMT_MASK_ASSOC_RESP
+#endif
+ )) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ /* if the bss is stopped, then start it */
+ if (priv->bss_started == MFALSE) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief set AP or GO parameter
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param params A pointer to beacon_parameters structure
+ * @return 0 -- success, otherwise fail
+ */
+int
+woal_cfg80211_set_beacon(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct beacon_parameters *params)
+{
+ moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy);
+ int ret = 0;
+
+ ENTER();
+
+ PRINTM(MIOCTL, "set beacon\n");
+ if (params != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS)
+ if (params->tail && params->tail_len) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(priv,
+ params->tail, params->tail_len,
+ NULL, 0, NULL, 0, NULL, 0,
+ MGMT_MASK_BEACON)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+#else
+ if (params->beacon_ies && params->beacon_ies_len) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(priv, params->tail,
+ params->tail_len, NULL, 0, NULL, 0,
+ NULL, 0, MGMT_MASK_BEACON)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (params->proberesp_ies && params->proberesp_ies_len) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(priv, NULL, 0,
+ params->proberesp_ies,
+ params->proberesp_ies_len, NULL, 0,
+ NULL, 0, MGMT_MASK_PROBE_RESP)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (params->assocresp_ies && params->assocresp_ies_len) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0,
+ params->assocresp_ies,
+ params->assocresp_ies_len, NULL, 0,
+ MGMT_MASK_ASSOC_RESP)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+#endif
+ }
+
+ done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief reset AP or GO parameters
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int
+woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
+{
+ moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy);
+ int ret = 0;
+
+ ENTER();
+
+ PRINTM(MIOCTL, "del beacon\n");
+ /* if the bss is still running, then stop it */
+ if (priv->bss_started == MTRUE) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_RESET)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ /* clear mgmt frame ies */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
+ MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP |
+ MGMT_MASK_ASSOC_RESP)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ priv->cipher = 0;
+ priv->key_len = 0;
+ done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Get station info
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param mac A pointer to station mac address
+ * @param stainfo A pointer to station_info structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int
+woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 * mac, struct station_info *stainfo)
+{
+ moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy);
+ int ret = -EFAULT;
+ int i = 0;
+ mlan_ds_get_info *info = NULL;
+ mlan_ioctl_req *ioctl_req = NULL;
+
+ ENTER();
+ if (priv->media_connected == MFALSE) {
+ PRINTM(MINFO, "cfg80211: Media not connected!\n");
+ LEAVE();
+ return -ENOENT;
+ }
+
+ /* Allocate an IOCTL request buffer */
+ ioctl_req =
+ (mlan_ioctl_req *) woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
+ if (ioctl_req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ info = (mlan_ds_get_info *) ioctl_req->pbuf;
+ info->sub_command = MLAN_OID_UAP_STA_LIST;
+ ioctl_req->req_id = MLAN_IOCTL_GET_INFO;
+ ioctl_req->action = MLAN_ACT_GET;
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) {
+ goto done;
+ }
+ for (i = 0; i < info->param.sta_list.sta_count; i++) {
+ if (!memcmp(info->param.sta_list.info[i].mac_address, mac, ETH_ALEN)) {
+ PRINTM(MIOCTL,
+ "Get station: %02x:%02x:%02x:%02x:%02x:%02x RSSI=%d\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ (int) info->param.sta_list.info[i].rssi);
+ stainfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_SIGNAL;
+ stainfo->inactive_time = 0;
+ stainfo->signal = info->param.sta_list.info[i].rssi;
+ ret = 0;
+ break;
+ }
+ }
+ done:
+ if (ioctl_req)
+ kfree(ioctl_req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Sets up the CFG802.11 specific HT capability fields
+ * with default values
+ *
+ * @param ht_info A pointer to ieee80211_sta_ht_cap structure
+ * @param ap_cfg A pointer to mlan_uap_bss_param
+ *
+ * @return N/A
+ */
+static void
+woal_cfg80211_setup_uap_ht_cap(struct ieee80211_sta_ht_cap *ht_info,
+ mlan_uap_bss_param * ap_cfg)
+{
+ ENTER();
+
+ ht_info->ht_supported = true;
+ ht_info->ampdu_factor = ap_cfg->ampdu_param & (MBIT(1) | MBIT(0));
+ ht_info->ampdu_density =
+ ap_cfg->ampdu_param & (MBIT(4) | MBIT(3) | MBIT(2));
+
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+ memcpy(ht_info->mcs.rx_mask, ap_cfg->supported_mcs_set,
+ sizeof(ht_info->mcs.rx_mask));
+
+ ht_info->cap = 0;
+ if (ap_cfg->ht_cap_info & MBIT(1)) /* 20/40 Mhz enable */
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ if (ap_cfg->ht_cap_info & MBIT(4)) /* Green field supported */
+ ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
+ if (ap_cfg->ht_cap_info & MBIT(5)) /* Short GI @ 20Mhz supported */
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+ if (ap_cfg->ht_cap_info & MBIT(6)) /* Short GI @ 40Mhz supported */
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ if (ap_cfg->ht_cap_info & MBIT(12)) /* DSS/CCK mode in 40MHz enable */
+ ht_info->cap |= IEEE80211_HT_CAP_DSSSCCK40;
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+
+ LEAVE();
+}
+
+/**
+ * @brief Initialize the uAP wiphy
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+woal_cfg80211_uap_init_wiphy(moal_private * priv, t_u8 wait_option)
+{
+ struct wiphy *wiphy = priv->wdev->wiphy;
+ mlan_uap_bss_param ap_cfg;
+
+ ENTER();
+
+ if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv,
+ MLAN_ACT_GET,
+ wait_option, &ap_cfg)) {
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ /* Initialize parameters for 2GHz and 5GHz bands */
+ woal_cfg80211_setup_uap_ht_cap(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap,
+ &ap_cfg);
+ if (wiphy->bands[IEEE80211_BAND_5GHZ])
+ woal_cfg80211_setup_uap_ht_cap(&wiphy->bands[IEEE80211_BAND_5GHZ]->
+ ht_cap, &ap_cfg);
+
+ /* Set retry limit count to wiphy */
+ wiphy->retry_long = (t_u8) ap_cfg.retry_limit;
+ wiphy->retry_short = (t_u8) ap_cfg.retry_limit;
+ wiphy->max_scan_ie_len = MAX_IE_SIZE;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS)
+ wiphy->mgmt_stypes = ieee80211_uap_mgmt_stypes;
+#endif
+ /* Set RTS threshold to wiphy */
+ wiphy->rts_threshold = (t_u32) ap_cfg.rts_threshold;
+
+ /* Set fragment threshold to wiphy */
+ wiphy->frag_threshold = (t_u32) ap_cfg.frag_threshold;
+
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief Register the device with cfg80211
+ *
+ * @param dev A pointer to net_device structure
+ * @param bss_type BSS type
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+woal_register_uap_cfg80211(struct net_device * dev, t_u8 bss_type)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ moal_private *priv = (moal_private *) netdev_priv(dev);
+ void *wdev_priv = NULL;
+ struct wireless_dev *wdev = NULL;
+ mlan_fw_info fw_info;
+
+ ENTER();
+
+ /* Allocate wireless device */
+ wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!wdev) {
+ PRINTM(MERROR, "Could not allocate wireless device\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto err_wdev;
+ }
+
+ /* Allocate wiphy */
+ wdev->wiphy = wiphy_new(&woal_cfg80211_uap_ops, sizeof(moal_private *));
+ if (!wdev->wiphy) {
+ PRINTM(MERROR, "Could not allocate wiphy device\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto err_wdev;
+ }
+ if (bss_type == MLAN_BSS_TYPE_UAP) {
+ dev_set_name(&wdev->wiphy->dev, dev->name);
+ wdev->iftype = NL80211_IFTYPE_AP;
+ wdev->wiphy->interface_modes =
+ MBIT(NL80211_IFTYPE_AP) | MBIT(NL80211_IFTYPE_STATION) |
+ MBIT(NL80211_IFTYPE_MONITOR);
+ wdev->wiphy->max_scan_ssids = 10;
+ }
+
+ /* Make this wiphy known to this driver only */
+ wdev->wiphy->privid = mrvl_wiphy_privid;
+
+ /* Supported bands */
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz;
+ if (MLAN_STATUS_SUCCESS ==
+ woal_request_get_fw_info(priv, MOAL_CMD_WAIT, &fw_info)) {
+ if (fw_info.fw_bands & BAND_A)
+ wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz;
+ }
+
+ /* Initialize cipher suits */
+ wdev->wiphy->cipher_suites = cfg80211_cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites);
+
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ /* We are using custom domains */
+ wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+
+ wdev->wiphy->reg_notifier = NULL; // TODO: woal_cfg80211_reg_notifier;
+
+ /* Set moal_private pointer in wiphy_priv */
+ wdev_priv = wiphy_priv(wdev->wiphy);
+
+ *(unsigned long *) wdev_priv = (unsigned long) priv;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) || defined(COMPAT_WIRELESS)
+ set_wiphy_dev(wdev->wiphy, (struct device *) priv->phandle->hotplug_device);
+#endif
+
+ if (wiphy_register(wdev->wiphy) < 0) {
+ PRINTM(MERROR, "Wiphy device registration failed!\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto err_wdev;
+ }
+
+ dev_net_set(dev, wiphy_net(wdev->wiphy));
+ dev->ieee80211_ptr = wdev;
+ SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
+ priv->wdev = wdev;
+
+ if (ret != MLAN_STATUS_SUCCESS) {
+ PRINTM(MERROR, "Wiphy device registration failed!\n");
+ } else {
+ PRINTM(MINFO, "Successfully registered wiphy device\n");
+ LEAVE();
+ return ret;
+ }
+
+ wiphy_unregister(wdev->wiphy);
+ err_wdev:
+ dev->ieee80211_ptr = NULL;
+ if (wdev && wdev->wiphy)
+ wiphy_free(wdev->wiphy);
+ kfree(wdev);
+ LEAVE();
+ return ret;
+}