From patchwork Mon Sep 30 06:36:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815417 X-Patchwork-Delegate: kvalo@adurom.com Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010001.outbound.protection.outlook.com [52.101.69.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3D20F7462; Mon, 30 Sep 2024 06:37:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.1 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678259; cv=fail; b=PXNCEuYpoTi7jeo0pNjbsve1uscOztNil/9FzVqpn+O5v0BiJP5Zg1zhZEIRrSJwtqw0iVGFmdjZvrOxzNh7sKt8pJPbO5UFkVw4BRYCFuCWxIDF/n/NDzWk4Tp71X57YSith9vYiTPjyN1ZmLHbyalZICnkiuMkHzpNQ8OhceE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678259; c=relaxed/simple; bh=wnReHUm9OOtap1t5tBYQOwwSVSZOeovF3xzq6QoVg0A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=TfP0v2uXFiGkxYidVuZQe603+045UjgATNqNBCvjddmKOp9pxUdq4vdvLjz6lj1Egu8piVvSp2qj+TPPYvB3Bc874tYK0EwVCSc8v6B2QElp68EKHKfS5tCND13VQzLkVu312XlBnZWgKPcILAdmIV+SCEk4G4LOUczU0wkwoLs= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=ZXPD/tSu; arc=fail smtp.client-ip=52.101.69.1 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="ZXPD/tSu" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=A2qnmvd2eB6+Ws94tCTtUDqQayYPllnUYugOWBcxJ9IHYANp5R2CJeczi4zKzR0oXaXAqj3SZow7LaF7JYRdCllXJkGkhEAXdv624curMheveItuIYhhm5PCmCma1Ysjdi1lvkhnZ6/NOCba3mgfh9VQPsvVurH7+Ajabq/F1pKTqdyMv59tGFrgM/c4J5QOoiHRAtPL6fbLGJZvAMbCcx+Fyx+F9KaDXxMLnL4310FxSJ28OKNk0hNE6G9EDf0bMkXs8UewSgUVAtzvFa1SMQFjncm9NZoWrKFDkUn+N7vYczohPScNFK57Dfg42LwGmrWs3Z3ownQVJr19YDRJwg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Jfc1KkdBH60Wkqa8T1NLtl150HQxe0jMEWFSfoigAI4=; b=MHsCLGMR/sIfaOk8BhQjZ+q8Xud2PYsWcdsKaudjyK6jxHTdw6cdVy1Hr1DaXxUXZgAEdn1mRPhFTriCDNe20ofdHxRx3MjAE0b2icZVUqv9azThRwrLhoQYTLiGuNKAZHDvybjy/R+HiOvwjNCi250zsngQyrm755Zcx2nHZHXM6CFM/7jlrTC39wNjsoQPZo6o8RriArITUSNDwaEWnr3CajnCD9W4GlXDd3/UCqjOMqlBf1FNH0rpJxz0B/vDngUpeeaaErcuc4V9TLR4pTQfSu3p6EoiTEsqhD1PXd7yaW6b8tPoP8Vazn3sBnGl2ZvTULY1N4qaflzp30hRIw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Jfc1KkdBH60Wkqa8T1NLtl150HQxe0jMEWFSfoigAI4=; b=ZXPD/tSuVevAwOBzRodUf6bSdYZZe72FFvu5AcRLO43F0Xcn1r5v9CjsHUGRS43nw9dyLGKFUV0oo/atMSaly+YfsYfUBluhueonXnGWl4kl1th8MGngPGzmzeHi36zddQJr6UhoGbhHPXPdKlCkoTv8k9UuBLMdR89ynIyoQSrt/6395EN4mbjHXmo333qIg86AAbiGj+y6StSTRr63wG/t9PHGKkY/kUlq4qK2+dcQ0dAd6FXu3Bh4BwDMWNExF1hZNOr6yzwFNlX7YWpqHK6Dkba+ozGmw5o3whNgKULZ7AVbqOpO9ZnnloCGtYZHg0Bj/PSZIQIiGs/WWbE7ow== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by DU2PR04MB9000.eurprd04.prod.outlook.com (2603:10a6:10:2e3::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7918.25; Mon, 30 Sep 2024 06:37:31 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:31 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 01/22] wifi: nxpwifi: add 802.11n files Date: Mon, 30 Sep 2024 14:36:40 +0800 Message-Id: <20240930063701.2566520-2-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|DU2PR04MB9000:EE_ X-MS-Office365-Filtering-Correlation-Id: f1f9b57b-c240-4844-b7ea-08dce11a5bbf X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: 3xO8VbQF32Zez5LRJF1Y5I3E1UVVwGwGaX4tloHnuxzohjmtQ+AUQ8ga90rEHrbKjuEgVyJAOUTl+8ZGx5OCql1jE4P/zwmPZsiOLNBUjeq2xIOBDY4GcotrClto+old/QA1KW6UEgb51l0Xo1ukGgXsjuWTyBiUlajUl1FrsW/Pxdln/ivQoECyUSP2w+WCTtzSC9NoR7BRgek2dfgDKCAATM606l1bP7a8RsDrhqfqXapLHhm2hREzxwA/eDxK4PGrsZxq2o5KksHFak7g8oROMIU94cS6inj5tGdUnCwo2noZ92oMsim3GhGDj5Jki4jacTN45S9zoGb1aCqfJTfr5B4xA5X56VMdSJruOYYcKetURh3yh0blB62ME3U9nu4+SFZPaI8TJMxpWY0hgF9ql6K0dfkP39A+p07+8FJFG2oFVuIZSLhnOChYZO4Y74wdgyT9A0xbnSAgZPsZMIM+5v3FyAl5C8UL646mtb5Ps8r3+D5K2FC/+ZYGEkOmvZGUr8ENxPqSTtZVT1fH5fcJXmQu7rI3PwkakzcRuE5AW48F0R1AnwX8kTpvtkY4wpEnBxsOswiqA4h+v/2xX1X+9PnuuSqQF6xwxV/BJ9V6nD5WiFaU1gxbu6ypk2MfnoHNZF5dLyNLZLrNkhYeVccf5hnfReRRKL2eghzbBkQ1GH68bUQxIr782U+L/JB/sb5ROrf0XTTvx3HHqlUGcANu0ykl2sVWYIAiDejtWOOWxtRXae0MZzqvmMTi2SQVqEoyb/f1h7bImuD6UdxX3XDsqidFBl6epyxDPrHtBpcMR7PY5UlfTavWrw+3OYj9HmE31aFGglotkmgFJTATIFUJIe43CweUlYmPJEejWxDBz5jWzGrld7lZXV2+g1iVSsu5PoSk36m4COXK/4i/V6F96w5miIi2qRqJaU3OOGGxjaynEUwwR13N5W/CUDki4NQ+HGC74hbxPrYDh1unArj12Q8KEtYQWYzkMggEsIVv7FOaagHtRQehl6aPu3ByWfdfpUnicNHMzA1qHUkeVZgsa6APAp5SUUqaS7/YIY4yzai681M2ubUQXyhAyG1Z4ZuOCyhKIVjvG4zEqfPTsIAj+r3C0RmkcadbSQ8orBruXk/8uxHeOQxt26zY9GvUbIu6piKUCRdenczdzjn1BtZY826EHzjc4RQ35h0HI+MaaDZ15yPUd/QEFGVoXBoT1fTX3TLkda7tR95AqOX9mi10eE3yy3X2H6yiZ3oc2nl085Ngm2jthfZuEci7TwwiI8clVuatOylRRU2AWSEipUwhZhlq6p+138YbLZcXoPNpiQk+EZDgnr3F1yfo8x8AQYWQg5bATfjZsPSlLhAqtg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: WgAFwBBiNns2LjVKprqAZdxQxByjLZE1VKqVWkBzjEQDlJ8EubMTjYBcW1dDNfxJ2rpB7ECp5KCvweSCOBekl7LqZBD0/pQqFsaKWcZaGp+GInSvcC+kUwTuIQutJa++2OuGkOF/ayX+IJzhGGyKMRf1pMkWGVQ0P5HlBYO34sQraV1TRf96iqWLuSs/dDyZnuFF9Afp1dpXy17lz/hX5MmjW3vVgY2hKBQkf5XqcZhlGfezBg/cJR+SvJ0vuZ3Rl4EgIZ5khVqpWMIKcyvvMoXDywebzppb0AHouOVz9UJ0aU7fkVtwyYYs1jnajdA6id3d2g+fI4BOTSiivHlW1jJ6neWc2f4Iiiz6kJmLaMo0DUHukmJ8Q9GzRZkCM3ilXHUL+U+Jiz5furjnIo82NUBrcsKQO/c6E73dytJ1qbU6bbhSdwz55nY2okdlMb+LfblObpXSThKTn4eSyYROZW6kWotljqv38vZgUSI0FivhXVxG0DERp4vlDHGwFMhdlkk9QO8emFzkeEshrBWioJ8UqNLUrng+lDEb0tU/gzPVMmkauXFdaxzfKR2Inh94xdkV21tfpvmMveLDspKgwjjZshFTFp2/duQG5ULp9oaRQnD6NZBzS267fwHYVws5LlleXP0FwJD8M6dmWOTyjg9hGTToifyf3mkGzS1lJmONYEAS7gAYxYyrCNAnTEo5HSnQh4olhgoW2UgWmkNrpaW7JvbEOlK1osyoO92eepGvL/KWDE3+UHserFmy8pJOpxdczxw9IYRsOzihc3E3uTKhSUIPIybXosch8FA2qsTp7XZdsJ2O5IqaCFVHq3aouGdWnfCn/wuolRpD9nHx9Fcd3e5CPRzOipDYhEWXkrndGSyGPvA/P0tD5pYbj8V2dOg6XHV+nvRejWLQZo/QA7xzdsEbkENHosnrTLX0a4TXwb2G2zKOUO2v4aPQk4ahGUzi4guIu3omL6xggkDbLU/y8wukXbeYqWtWUlm0lZPbMtE1ymeTigJX2NJflYkXOXP6IGl3gxiXpO0B8jUBzSKDmrzWyr1q9g5zQY4dFJcx9D3hrQqEMM2fx9wyvedLr+HdXT7+PQNDs9YmFaatMCyIaJ/xTkY20lGnbnfbdNC0PiPrkCB0t6XlGHXSSlVpnSFBUtud5IBWNtwheeLLP2YeBQRGdQJBPdP7366osbKZk1zDXVFYiIfu4rHKiEG3HbT9KHf3/+yrFqbi6Y1i6gcFpIRnMmvdW3YHOHSLWBpndPnNHO4TMMIzgBWuqOVYnvIH1axq+GiuYxMNL1NBTLuF76CCYAk3vIvHqxQvoGAW5diOvhQIwrgOHPbOZZ9x86zFesOaVh7GWyWAYG14LUuL+rjMbGT11CpCvxFM5RMsjLAtIq/N7AHenewICQ8KwwCPV2/fQfYNJr8Oet//TH2ZMhjQZ/OzTLu+LGO3hvCyG+6va7/0LJKJVxFgvfX8E7G61OxvvLz4SXrDpLOOuK8JhqRFYTGH//MvYKrsvjTHthaN3GbHVIOYaRWAF1VFfV/fJ+JX+ckCQO1k1bjboJEQCN50HGnnLs0Ye2XaH0MGF2J2QCaKCjVuo9iQVAEn X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: f1f9b57b-c240-4844-b7ea-08dce11a5bbf X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:31.3872 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: FNx+KebX+oa0G9HH2FHkJ1MsKFLjgrc0KHmiOCYRcYxTstf0Jr6EiYcd4lRBZSg6DW77QznSAJ6NZEpUnP2xvA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU2PR04MB9000 For client mode, NXP firmware will help association process via host command HOST_CMD_802_11_ASSOCIATE. IEs for 802.11n should be converted from parameters passed from cfg80211 to TLVs appended to host command HOST_CMD_802_11_ASSOCIATE. For AP mode, IEs for 802.11n should be converted from parameters passed from cfg80211 to TLVs appened to host command HOST_CMD_UAP_SYS_CONFIG. Because resource limitation of FW, AMSDU and BA Rx buffer and reorder should be done by driver. Files in this commit will support 802.11n features based on above descriptions. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/11n.c | 848 ++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/11n.h | 161 ++++ drivers/net/wireless/nxp/nxpwifi/11n_aggr.c | 275 ++++++ drivers/net/wireless/nxp/nxpwifi/11n_aggr.h | 21 + .../net/wireless/nxp/nxpwifi/11n_rxreorder.c | 910 ++++++++++++++++++ .../net/wireless/nxp/nxpwifi/11n_rxreorder.h | 72 ++ 6 files changed, 2287 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n.h create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_aggr.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_aggr.h create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h diff --git a/drivers/net/wireless/nxp/nxpwifi/11n.c b/drivers/net/wireless/nxp/nxpwifi/11n.c new file mode 100644 index 000000000000..a1b4e6e998a1 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n.c @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: 802.11n + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" + +/* Fills HT capability information field, AMPDU Parameters field, HT extended + * capability field, and supported MCS set fields. + * + * HT capability information field, AMPDU Parameters field, supported MCS set + * fields are retrieved from cfg80211 stack + * + * RD responder bit to set to clear in the extended capability header. + */ +int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type, + struct ieee80211_ht_cap *ht_cap) +{ + u16 ht_cap_info; + u16 bcn_ht_cap = le16_to_cpu(ht_cap->cap_info); + u16 ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); + struct ieee80211_supported_band *sband = + priv->wdev.wiphy->bands[radio_type]; + + if (WARN_ON_ONCE(!sband)) { + nxpwifi_dbg(priv->adapter, ERROR, "Invalid radio type!\n"); + return -EINVAL; + } + + ht_cap->ampdu_params_info = + (sband->ht_cap.ampdu_factor & + IEEE80211_HT_AMPDU_PARM_FACTOR) | + ((sband->ht_cap.ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & + IEEE80211_HT_AMPDU_PARM_DENSITY); + + memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, + sizeof(sband->ht_cap.mcs)); + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + priv->adapter->sec_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_NONE)) + /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ + SETHT_MCS32(ht_cap->mcs.rx_mask); + + /* Clear RD responder bit */ + ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; + + ht_cap_info = sband->ht_cap.cap; + if (bcn_ht_cap) { + if (!(bcn_ht_cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + ht_cap_info &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if (!(bcn_ht_cap & IEEE80211_HT_CAP_SGI_40)) + ht_cap_info &= ~IEEE80211_HT_CAP_SGI_40; + if (!(bcn_ht_cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) + ht_cap_info &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + } + ht_cap->cap_info = cpu_to_le16(ht_cap_info); + ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + ht_cap->tx_BF_cap_info = cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP); + + return 0; +} + +/* This function returns the pointer to an entry in BA Stream + * table which matches the requested BA status. + */ +static struct nxpwifi_tx_ba_stream_tbl * +nxpwifi_get_ba_status(struct nxpwifi_private *priv, + enum nxpwifi_ba_status ba_status) +{ + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_tsr_tbl->ba_status == ba_status) { + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + return tx_ba_tsr_tbl; + } + } + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + return NULL; +} + +/* This function handles the command response of delete a block + * ack request. + * + * The function checks the response success status and takes action + * accordingly (send an add BA request in case of success, or recreate + * the deleted stream in case of failure, if the add BA was also + * initiated by us). + */ +int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + int tid; + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl; + struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; + u16 del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); + + tid = del_ba_param_set >> DELBA_TID_POS; + if (del_ba->del_result == BA_RESULT_SUCCESS) { + nxpwifi_del_ba_tbl(priv, tid, del_ba->peer_mac_addr, + TYPE_DELBA_SENT, + INITIATOR_BIT(del_ba_param_set)); + + tx_ba_tbl = nxpwifi_get_ba_status(priv, BA_SETUP_INPROGRESS); + if (tx_ba_tbl) + nxpwifi_send_addba(priv, tx_ba_tbl->tid, + tx_ba_tbl->ra); + } else { /* + * In case of failure, recreate the deleted stream in case + * we initiated the DELBA + */ + if (!INITIATOR_BIT(del_ba_param_set)) + return 0; + + nxpwifi_create_ba_tbl(priv, del_ba->peer_mac_addr, tid, + BA_SETUP_INPROGRESS); + + tx_ba_tbl = nxpwifi_get_ba_status(priv, BA_SETUP_INPROGRESS); + + if (tx_ba_tbl) + nxpwifi_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra, + TYPE_DELBA_SENT, true); + } + + return 0; +} + +/* This function handles the command response of add a block + * ack request. + * + * Handling includes changing the header fields to CPU formats, checking + * the response success status and taking actions accordingly (delete the + * BA stream table in case of failure). + */ +int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + int tid, tid_down; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl; + struct nxpwifi_ra_list_tbl *ra_list; + u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); + + add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) + & SSN_MASK); + + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + + tid_down = nxpwifi_wmm_downgrade_tid(priv, tid); + ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, + add_ba_rsp->peer_mac_addr); + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + if (ra_list) { + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + } + nxpwifi_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, true); + if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) + priv->aggr_prio_tbl[tid].ampdu_ap = + BA_STREAM_NOT_ALLOWED; + return 0; + } + + tx_ba_tbl = nxpwifi_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); + if (tx_ba_tbl) { + nxpwifi_dbg(priv->adapter, EVENT, "info: BA stream complete\n"); + tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.tx_amsdu && + priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) + tx_ba_tbl->amsdu = true; + else + tx_ba_tbl->amsdu = false; + if (ra_list) { + ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu; + ra_list->ba_status = BA_SETUP_COMPLETE; + } + } else { + nxpwifi_dbg(priv->adapter, ERROR, "BA stream not created\n"); + } + + return 0; +} + +/* This function prepares command of reconfigure Tx buffer. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Tx buffer size (for SET only) + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, int cmd_action, + u16 *buf_size) +{ + struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; + u16 action = (u16)cmd_action; + + cmd->command = cpu_to_le16(HOST_CMD_RECONFIGURE_TX_BUFF); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); + tx_buf->action = cpu_to_le16(action); + switch (action) { + case HOST_ACT_GEN_SET: + nxpwifi_dbg(priv->adapter, CMD, + "cmd: set tx_buf=%d\n", *buf_size); + tx_buf->buff_size = cpu_to_le16(*buf_size); + break; + case HOST_ACT_GEN_GET: + default: + tx_buf->buff_size = 0; + break; + } + return 0; +} + +/* This function prepares command of AMSDU aggregation control. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting AMSDU control parameters (for SET only) + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, + int cmd_action, + struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl) +{ + struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = + &cmd->params.amsdu_aggr_ctrl; + u16 action = (u16)cmd_action; + + cmd->command = cpu_to_le16(HOST_CMD_AMSDU_AGGR_CTRL); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) + + S_DS_GEN); + amsdu_ctrl->action = cpu_to_le16(action); + switch (action) { + case HOST_ACT_GEN_SET: + amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); + amsdu_ctrl->curr_buf_size = 0; + break; + case HOST_ACT_GEN_GET: + default: + amsdu_ctrl->curr_buf_size = 0; + break; + } + return 0; +} + +/* This function prepares 11n configuration command. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting HT Tx capability and HT Tx information fields + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_ds_11n_tx_cfg *txcfg) +{ + struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; + + cmd->command = cpu_to_le16(HOST_CMD_11N_CFG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); + htcfg->action = cpu_to_le16(cmd_action); + htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); + htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); + + if (priv->adapter->is_hw_11ac_capable) + htcfg->misc_config = cpu_to_le16(txcfg->misc_config); + + return 0; +} + +/* This function appends an 11n TLV to a buffer. + * + * Buffer allocation is responsibility of the calling + * function. No size validation is made here. + * + * The function fills up the following sections, if applicable - + * - HT capability IE + * - HT information IE (with channel list) + * - 20/40 BSS Coexistence IE + * - HT Extended Capabilities IE + */ +int +nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct nxpwifi_ie_types_htcap *ht_cap; + struct nxpwifi_ie_types_chan_list_param_set *chan_list; + struct nxpwifi_ie_types_2040bssco *bss_co_2040; + struct nxpwifi_ie_types_extcap *ext_cap; + int ret_len = 0; + struct ieee80211_supported_band *sband; + struct element *hdr; + u8 radio_type; + + if (!buffer || !*buffer) + return ret_len; + + radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + sband = priv->wdev.wiphy->bands[radio_type]; + + if (bss_desc->bcn_ht_cap) { + ht_cap = (struct nxpwifi_ie_types_htcap *)*buffer; + memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy((u8 *)ht_cap + sizeof(struct nxpwifi_ie_types_header), + (u8 *)bss_desc->bcn_ht_cap, + le16_to_cpu(ht_cap->header.len)); + + nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + /* Update HT40 capability from current channel information */ + if (bss_desc->bcn_ht_oper) { + u8 ht_param = bss_desc->bcn_ht_oper->ht_param; + u8 radio = + nxpwifi_band_to_radio_type(bss_desc->bss_band); + int freq = + ieee80211_channel_to_frequency(bss_desc->channel, + radio); + struct ieee80211_channel *chan = + ieee80211_get_channel(priv->adapter->wiphy, freq); + + switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) { + ht_cap->ht_cap.cap_info &= + cpu_to_le16 + (~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + ht_cap->ht_cap.cap_info &= + cpu_to_le16(~IEEE80211_HT_CAP_SGI_40); + } + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) { + ht_cap->ht_cap.cap_info &= + cpu_to_le16 + (~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + ht_cap->ht_cap.cap_info &= + cpu_to_le16(~IEEE80211_HT_CAP_SGI_40); + } + break; + } + } + + *buffer += sizeof(struct nxpwifi_ie_types_htcap); + ret_len += sizeof(struct nxpwifi_ie_types_htcap); + } + + if (bss_desc->bcn_ht_oper) { + chan_list = + (struct nxpwifi_ie_types_chan_list_param_set *)*buffer; + memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1)); + chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_list->header.len = + cpu_to_le16(sizeof(struct nxpwifi_chan_scan_param_set)); + chan_list->chan_scan_param[0].chan_number = + bss_desc->bcn_ht_oper->primary_chan; + chan_list->chan_scan_param[0].radio_type = + nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) + SET_SECONDARYCHAN + (chan_list->chan_scan_param[0].radio_type, + (bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); + + *buffer += struct_size(chan_list, chan_scan_param, 1); + ret_len += struct_size(chan_list, chan_scan_param, 1); + } + + if (bss_desc->bcn_bss_co_2040) { + bss_co_2040 = (struct nxpwifi_ie_types_2040bssco *)*buffer; + memset(bss_co_2040, 0, + sizeof(struct nxpwifi_ie_types_2040bssco)); + bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); + bss_co_2040->header.len = + cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); + + memcpy((u8 *)bss_co_2040 + + sizeof(struct nxpwifi_ie_types_header), + bss_desc->bcn_bss_co_2040 + + sizeof(struct element), + le16_to_cpu(bss_co_2040->header.len)); + + *buffer += sizeof(struct nxpwifi_ie_types_2040bssco); + ret_len += sizeof(struct nxpwifi_ie_types_2040bssco); + } + + if (bss_desc->bcn_ext_cap) { + hdr = (void *)bss_desc->bcn_ext_cap; + ext_cap = (struct nxpwifi_ie_types_extcap *)*buffer; + memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap)); + ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + ext_cap->header.len = cpu_to_le16(hdr->datalen); + + memcpy((u8 *)ext_cap->ext_capab, + bss_desc->bcn_ext_cap + sizeof(struct element), + le16_to_cpu(ext_cap->header.len)); + + if (hdr->datalen > 3 && + ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) + priv->hs2_enabled = true; + else + priv->hs2_enabled = false; + + *buffer += sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen; + ret_len += sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen; + } + + return ret_len; +} + +/* This function checks if the given pointer is valid entry of + * Tx BA Stream table. + */ +static int +nxpwifi_is_tx_ba_stream_ptr_valid(struct nxpwifi_private *priv, + struct nxpwifi_tx_ba_stream_tbl *tx_tbl_ptr) +{ + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl; + + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_tsr_tbl == tx_tbl_ptr) + return true; + } + + return false; +} + +/* This function deletes the given entry in Tx BA Stream table. + * + * The function also performs a validity check on the supplied + * pointer before trying to delete. + */ +void +nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv, + struct nxpwifi_tx_ba_stream_tbl *tbl) +{ + if (!tbl && nxpwifi_is_tx_ba_stream_ptr_valid(priv, tbl)) + return; + + nxpwifi_dbg(priv->adapter, INFO, + "info: tx_ba_tsr_tbl %p\n", tbl); + + list_del(&tbl->list); + + kfree(tbl); +} + +/* This function deletes all the entries in Tx BA Stream table. + */ +void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv) +{ + int i; + struct nxpwifi_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->tx_ba_stream_tbl_ptr, list) + nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); + + for (i = 0; i < MAX_NUM_TID; ++i) + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user; +} + +/* This function returns the pointer to an entry in BA Stream + * table which matches the given RA/TID pair. + */ +struct nxpwifi_tx_ba_stream_tbl * +nxpwifi_get_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *ra) +{ + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && + tx_ba_tsr_tbl->tid == tid) { + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + return tx_ba_tsr_tbl; + } + } + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + return NULL; +} + +/* This function creates an entry in Tx BA stream table for the + * given RA/TID pair. + */ +void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid, + enum nxpwifi_ba_status ba_status) +{ + struct nxpwifi_tx_ba_stream_tbl *new_node; + struct nxpwifi_ra_list_tbl *ra_list; + int tid_down; + + if (!nxpwifi_get_ba_tbl(priv, tid, ra)) { + new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC); + if (!new_node) + return; + + tid_down = nxpwifi_wmm_downgrade_tid(priv, tid); + ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, ra); + if (ra_list) { + ra_list->ba_status = ba_status; + ra_list->amsdu_in_ampdu = false; + } + INIT_LIST_HEAD(&new_node->list); + + new_node->tid = tid; + new_node->ba_status = ba_status; + memcpy(new_node->ra, ra, ETH_ALEN); + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + } +} + +/* This function sends an add BA request to the given TID/RA pair. + */ +int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac) +{ + struct host_cmd_ds_11n_addba_req add_ba_req; + u32 tx_win_size = priv->add_ba_param.tx_win_size; + static u8 dialog_tok; + int ret; + u16 block_ack_param_set; + + nxpwifi_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); + + memset(&add_ba_req, 0, sizeof(add_ba_req)); + + block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | + tx_win_size << BLOCKACKPARAM_WINSIZE_POS | + IMMEDIATE_BLOCK_ACK); + + /* enable AMSDU inside AMPDU */ + if (priv->add_ba_param.tx_amsdu && + priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) + block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; + + add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set); + add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); + + ++dialog_tok; + + if (dialog_tok == 0) + dialog_tok = 1; + + add_ba_req.dialog_token = dialog_tok; + memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_REQ, + 0, 0, &add_ba_req, false); + + return ret; +} + +/* This function sends a delete BA request to the given TID/RA pair. + */ +int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac, + int initiator) +{ + struct host_cmd_ds_11n_delba delba; + int ret; + u16 del_ba_param_set; + + memset(&delba, 0, sizeof(delba)); + + del_ba_param_set = tid << DELBA_TID_POS; + + if (initiator) + del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; + else + del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; + + delba.del_ba_param_set = cpu_to_le16(del_ba_param_set); + memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, + HOST_ACT_GEN_SET, 0, &delba, false); + + return ret; +} + +/* This function sends delba to specific tid + */ +void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid) +{ + struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { + if (rx_reor_tbl_ptr->tid == tid) { + dev_dbg(priv->adapter->dev, + "Send delba to tid=%d, %pM\n", + tid, rx_reor_tbl_ptr->ta); + nxpwifi_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0); + goto exit; + } + } +exit: + spin_unlock_bh(&priv->rx_reorder_tbl_lock); +} + +/* This function handles the command response of a delete BA request. + */ +void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba) +{ + struct host_cmd_ds_11n_delba *cmd_del_ba = + (struct host_cmd_ds_11n_delba *)del_ba; + u16 del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); + int tid; + + tid = del_ba_param_set >> DELBA_TID_POS; + + nxpwifi_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr, + TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set)); +} + +/* This function retrieves the Rx reordering table. + */ +int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv, + struct nxpwifi_ds_rx_reorder_tbl *buf) +{ + int i; + struct nxpwifi_ds_rx_reorder_tbl *rx_reo_tbl = buf; + struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr; + int count = 0; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, + list) { + rx_reo_tbl->tid = (u16)rx_reorder_tbl_ptr->tid; + memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); + rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; + rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; + for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + rx_reo_tbl->buffer[i] = true; + else + rx_reo_tbl->buffer[i] = false; + } + rx_reo_tbl++; + count++; + + if (count >= NXPWIFI_MAX_RX_BASTREAM_SUPPORTED) + break; + } + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + return count; +} + +/* This function retrieves the Tx BA stream table. + */ +int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv, + struct nxpwifi_ds_tx_ba_stream_tbl *buf) +{ + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl; + struct nxpwifi_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; + int count = 0; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + rx_reo_tbl->tid = (u16)tx_ba_tsr_tbl->tid; + nxpwifi_dbg(priv->adapter, DATA, "data: %s tid=%d\n", + __func__, rx_reo_tbl->tid); + memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); + rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu; + rx_reo_tbl++; + count++; + if (count >= NXPWIFI_MAX_TX_BASTREAM_SUPPORTED) + break; + } + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + + return count; +} + +/* This function retrieves the entry for specific tx BA stream table by RA and + * deletes it. + */ +void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra) +{ + struct nxpwifi_tx_ba_stream_tbl *tbl, *tmp; + + if (!ra) + return; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) + if (!memcmp(tbl->ra, ra, ETH_ALEN)) + nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); +} + +/* This function initializes the BlockACK setup information for given + * nxpwifi_private structure. + */ +void nxpwifi_set_ba_params(struct nxpwifi_private *priv) +{ + priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size = + NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size = + NXPWIFI_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + NXPWIFI_STA_AMPDU_DEF_RXWINSIZE; + } + + priv->add_ba_param.tx_amsdu = true; + priv->add_ba_param.rx_amsdu = true; +} + +u8 nxpwifi_get_sec_chan_offset(int chan) +{ + u8 sec_offset; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case 165: + default: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + + return sec_offset; +} + +/* This function will send DELBA to entries in the priv's + * Tx BA stream table + */ +static void +nxpwifi_send_delba_txbastream_tbl(struct nxpwifi_private *priv, u8 tid) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr; + + list_for_each_entry(tx_ba_stream_tbl_ptr, + &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) { + if (tid == tx_ba_stream_tbl_ptr->tid) { + dev_dbg(adapter->dev, + "Tx:Send delba to tid=%d, %pM\n", tid, + tx_ba_stream_tbl_ptr->ra); + nxpwifi_send_delba(priv, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + return; + } + } + } +} + +/* This function updates all the tx_win_size + */ +void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *adapter) +{ + u8 i, j; + u32 tx_win_size; + struct nxpwifi_private *priv; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + tx_win_size = priv->add_ba_param.tx_win_size; + + if (priv->bss_type == NXPWIFI_BSS_TYPE_STA) + priv->add_ba_param.tx_win_size = + NXPWIFI_STA_AMPDU_DEF_TXWINSIZE; + + if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP) + priv->add_ba_param.tx_win_size = + NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE; + + if (adapter->coex_win_size) { + if (adapter->coex_tx_win_size) + priv->add_ba_param.tx_win_size = + adapter->coex_tx_win_size; + } + + if (tx_win_size != priv->add_ba_param.tx_win_size) { + if (!priv->media_connected) + continue; + for (j = 0; j < MAX_NUM_TID; j++) + nxpwifi_send_delba_txbastream_tbl(priv, j); + } + } +} diff --git a/drivers/net/wireless/nxp/nxpwifi/11n.h b/drivers/net/wireless/nxp/nxpwifi/11n.h new file mode 100644 index 000000000000..627d8dff38dc --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: 802.11n + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_11N_H_ +#define _NXPWIFI_11N_H_ + +#include "11n_aggr.h" +#include "11n_rxreorder.h" +#include "wmm.h" + +int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp); +int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp); +int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_ds_11n_tx_cfg *txcfg); +int nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer); +int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type, + struct ieee80211_ht_cap *ht_cap); +int nxpwifi_set_get_11n_htcap_cfg(struct nxpwifi_private *priv, + u16 action, int *htcap_cfg); +void nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv, + struct nxpwifi_tx_ba_stream_tbl + *tx_tbl); +void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv); +struct nxpwifi_tx_ba_stream_tbl *nxpwifi_get_ba_tbl(struct nxpwifi_private + *priv, int tid, u8 *ra); +void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid, + enum nxpwifi_ba_status ba_status); +int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac); +int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac, + int initiator); +void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba); +int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv, + struct nxpwifi_ds_rx_reorder_tbl *buf); +int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv, + struct nxpwifi_ds_tx_ba_stream_tbl *buf); +int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + int cmd_action, u16 *buf_size); +int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, + int cmd_action, + struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl); +void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra); +u8 nxpwifi_get_sec_chan_offset(int chan); + +static inline u8 +nxpwifi_is_station_ampdu_allowed(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, int tid) +{ + struct nxpwifi_sta_node *node = nxpwifi_get_sta_entry(priv, ptr->ra); + + if (unlikely(!node)) + return false; + + return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; +} + +/* This function checks whether AMPDU is allowed or not for a particular TID. */ +static inline u8 +nxpwifi_is_ampdu_allowed(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, int tid) +{ + u8 ret; + + if (is_broadcast_ether_addr(ptr->ra)) + return false; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) + ret = nxpwifi_is_station_ampdu_allowed(priv, ptr, tid); + else + ret = (priv->aggr_prio_tbl[tid].ampdu_ap != + BA_STREAM_NOT_ALLOWED) ? true : false; + + return ret; +} + +/* This function checks whether AMSDU is allowed or not for a particular TID. + */ +static inline u8 +nxpwifi_is_amsdu_allowed(struct nxpwifi_private *priv, int tid) +{ + return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && + (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03))) + ? true : false); +} + +/* This function checks whether a space is available for new BA stream or not. + */ +static inline u8 +nxpwifi_space_avail_for_new_ba_stream(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + u8 i; + size_t ba_stream_num = 0, ba_stream_max; + + ba_stream_max = NXPWIFI_MAX_TX_BASTREAM_SUPPORTED; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + ba_stream_num += list_count_nodes(&priv->tx_ba_stream_tbl_ptr); + } + + if (adapter->fw_api_ver == NXPWIFI_FW_V15) { + ba_stream_max = + GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); + if (!ba_stream_max) + ba_stream_max = NXPWIFI_MAX_TX_BASTREAM_SUPPORTED; + } + + return ((ba_stream_num < ba_stream_max) ? true : false); +} + +/* This function finds the correct Tx BA stream to delete. + * + * Upon successfully locating, both the TID and the RA are returned. + */ +static inline u8 +nxpwifi_find_stream_to_delete(struct nxpwifi_private *priv, int ptr_tid, + int *ptid, u8 *ra) +{ + int tid; + u8 ret = false; + struct nxpwifi_tx_ba_stream_tbl *tx_tbl; + + tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { + tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; + *ptid = tx_tbl->tid; + memcpy(ra, tx_tbl->ra, ETH_ALEN); + ret = true; + } + } + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + + return ret; +} + +/* This function checks whether associated station is 11n enabled + */ +static inline int nxpwifi_is_sta_11n_enabled(struct nxpwifi_private *priv, + struct nxpwifi_sta_node *node) +{ + if (!node || (priv->bss_role == NXPWIFI_BSS_ROLE_UAP && + !priv->ap_11n_enabled)) + return 0; + + return node->is_11n_enabled; +} + +#endif /* !_NXPWIFI_11N_H_ */ diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c new file mode 100644 index 000000000000..06698c0af56b --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: 802.11n Aggregation + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_aggr.h" + +/* Creates an AMSDU subframe for aggregation into one AMSDU packet. + * + * The resultant AMSDU subframe format is - + * + * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ + * | DA | SA | Length | SNAP header | MSDU | + * | data[0..5] | data[6..11] | | | data[14..] | + * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ + * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> + * + * This function also computes the amount of padding required to make the + * buffer length multiple of 4 bytes. + * + * Data => |DA|SA|SNAP-TYPE|........ .| + * MSDU => |DA|SA|Length|SNAP|...... ..| + */ +static int +nxpwifi_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, + struct sk_buff *skb_src, int *pad) + +{ + int dt_offset; + struct rfc_1042_hdr snap = { + 0xaa, /* LLC DSAP */ + 0xaa, /* LLC SSAP */ + 0x03, /* LLC CTRL */ + {0x00, 0x00, 0x00}, /* SNAP OUI */ + 0x0000 /* SNAP type */ + /* This field will be overwritten + * later with ethertype + */ + }; + struct tx_packet_hdr *tx_header; + + tx_header = skb_put(skb_aggr, sizeof(*tx_header)); + + /* Copy DA and SA */ + dt_offset = 2 * ETH_ALEN; + memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); + + /* Copy SNAP header */ + snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; + + dt_offset += sizeof(__be16); + + memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); + + skb_pull(skb_src, dt_offset); + + /* Update Length field */ + tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); + + /* Add payload */ + skb_put_data(skb_aggr, skb_src->data, skb_src->len); + + /* Add padding for new MSDU to start from 4 byte boundary */ + *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; + + return skb_aggr->len + *pad; +} + +/* Adds TxPD to AMSDU header. + * + * Each AMSDU packet will contain one TxPD at the beginning, + * followed by multiple AMSDU subframes. + */ +static void +nxpwifi_11n_form_amsdu_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct txpd *local_tx_pd; + + skb_push(skb, sizeof(*local_tx_pd)); + + local_tx_pd = (struct txpd *)skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + + /* Original priority has been overwritten */ + local_tx_pd->priority = (u8)skb->priority; + local_tx_pd->pkt_delay_2ms = + nxpwifi_wmm_compute_drv_pkt_delay(priv, skb); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + /* Always zero as the data is followed by struct txpd */ + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); + local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - + sizeof(*local_tx_pd)); + + if (local_tx_pd->tx_control == 0) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA && + priv->adapter->pps_uapsd_mode) { + if (nxpwifi_check_last_packet_indication(priv)) { + priv->adapter->tx_lock_flag = true; + local_tx_pd->flags = + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET; + } + } +} + +/* Create aggregated packet. + * + * This function creates an aggregated MSDU packet, by combining buffers + * from the RA list. Each individual buffer is encapsulated as an AMSDU + * subframe and all such subframes are concatenated together to form the + * AMSDU packet. + * + * A TxPD is also added to the front of the resultant AMSDU packets for + * transmission. The resultant packets format is - + * + * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ + * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| + * | | 1 | 2 | .. | n | + * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ + */ +int +nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *pra_list, + int ptrindex) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct sk_buff *skb_aggr, *skb_src; + struct nxpwifi_txinfo *tx_info_aggr, *tx_info_src; + int pad = 0, aggr_num = 0, ret; + struct nxpwifi_tx_param tx_param; + struct txpd *ptx_pd = NULL; + int headroom = adapter->intf_hdr_len; + + skb_src = skb_peek(&pra_list->skb_head); + if (!skb_src) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + return 0; + } + + tx_info_src = NXPWIFI_SKB_TXCB(skb_src); + skb_aggr = nxpwifi_alloc_dma_align_buf(adapter->tx_buf_size, + GFP_ATOMIC); + if (!skb_aggr) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + return -ENOMEM; + } + + /* skb_aggr->data already 64 byte align, just reserve bus interface + * header and txpd. + */ + skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); + tx_info_aggr = NXPWIFI_SKB_TXCB(skb_aggr); + + memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); + tx_info_aggr->bss_type = tx_info_src->bss_type; + tx_info_aggr->bss_num = tx_info_src->bss_num; + + tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_AGGR_PKT; + skb_aggr->priority = skb_src->priority; + skb_aggr->tstamp = skb_src->tstamp; + + do { + /* Check if AMSDU can accommodate this MSDU */ + if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) > + adapter->tx_buf_size) + break; + + skb_src = skb_dequeue(&pra_list->skb_head); + pra_list->total_pkt_count--; + atomic_dec(&priv->wmm.tx_pkts_queued); + aggr_num++; + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); + + nxpwifi_write_data_complete(adapter, skb_src, 0, 0); + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + return -ENOENT; + } + + if (skb_tailroom(skb_aggr) < pad) { + pad = 0; + break; + } + skb_put(skb_aggr, pad); + + skb_src = skb_peek(&pra_list->skb_head); + + } while (skb_src); + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + + /* Last AMSDU packet does not need padding */ + skb_trim(skb_aggr, skb_aggr->len - pad); + + /* Form AMSDU */ + nxpwifi_11n_form_amsdu_txpd(priv, skb_aggr); + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) + ptx_pd = (struct txpd *)skb_aggr->data; + + skb_push(skb_aggr, headroom); + tx_info_aggr->aggr_num = aggr_num * 2; + if (adapter->data_sent || adapter->tx_lock_flag) { + atomic_add(aggr_num * 2, &adapter->tx_queued); + skb_queue_tail(&adapter->tx_data_q, skb_aggr); + return 0; + } + + if (skb_src) + tx_param.next_pkt_len = skb_src->len + sizeof(struct txpd); + else + tx_param.next_pkt_len = 0; + + ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA, + skb_aggr, &tx_param); + + switch (ret) { + case -EBUSY: + spin_lock_bh(&priv->wmm.ra_list_spinlock); + if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_write_data_complete(adapter, skb_aggr, 1, -1); + return -EINVAL; + } + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA && + adapter->pps_uapsd_mode && adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag = false; + if (ptx_pd) + ptx_pd->flags = 0; + } + + skb_queue_tail(&pra_list->skb_head, skb_aggr); + + pra_list->total_pkt_count++; + + atomic_inc(&priv->wmm.tx_pkts_queued); + + tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT; + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -EINPROGRESS: + break; + case 0: + nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret); + break; + default: + nxpwifi_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret); + break; + } + if (ret != -EBUSY) + nxpwifi_rotate_priolists(priv, pra_list, ptrindex); + + return 0; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h new file mode 100644 index 000000000000..be9f0f8f4e48 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: 802.11n Aggregation + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_11N_AGGR_H_ +#define _NXPWIFI_11N_AGGR_H_ + +#define PKT_TYPE_AMSDU 0xE6 +#define MIN_NUM_AMSDU 2 + +int nxpwifi_11n_deaggregate_pkt(struct nxpwifi_private *priv, + struct sk_buff *skb); +int nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, + int ptr_index) + __releases(&priv->wmm.ra_list_spinlock); + +#endif /* !_NXPWIFI_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c new file mode 100644 index 000000000000..cb037d577528 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c @@ -0,0 +1,910 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: 802.11n RX Re-ordering + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" + +/* This function will dispatch amsdu packet and forward it to kernel/upper + * layer. + */ +static int nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); + int ret; + + if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { + struct sk_buff_head list; + struct sk_buff *rx_skb; + + __skb_queue_head_init(&list); + + skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); + skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); + + ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, + priv->wdev.iftype, 0, NULL, NULL, false); + + while (!skb_queue_empty(&list)) { + rx_skb = __skb_dequeue(&list); + + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) + ret = nxpwifi_uap_recv_packet(priv, rx_skb); + else + ret = nxpwifi_recv_packet(priv, rx_skb); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "Rx of A-MSDU failed"); + } + return 0; + } + + return -EINVAL; +} + +/* This function will process the rx packet and forward it to kernel/upper + * layer. + */ +static int nxpwifi_11n_dispatch_pkt(struct nxpwifi_private *priv, + struct sk_buff *payload) +{ + int ret; + + if (!payload) { + nxpwifi_dbg(priv->adapter, INFO, "info: fw drop data\n"); + return 0; + } + + ret = nxpwifi_11n_dispatch_amsdu_pkt(priv, payload); + if (!ret) + return 0; + + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) + return nxpwifi_handle_uap_rx_forward(priv, payload); + + return nxpwifi_process_rx_packet(priv, payload); +} + +/* This function dispatches all packets in the Rx reorder table until the + * start window. + * + * There could be holes in the buffer, which are skipped by the function. + * Since the buffer is linear, the function uses rotation to simulate + * circular buffer. + */ +static void +nxpwifi_11n_dispatch_pkt_until_start_win(struct nxpwifi_private *priv, + struct nxpwifi_rx_reorder_tbl *tbl, + int start_win) +{ + struct sk_buff_head list; + struct sk_buff *skb; + int pkt_to_send, i; + + __skb_queue_head_init(&list); + spin_lock_bh(&priv->rx_reorder_tbl_lock); + + pkt_to_send = (start_win > tbl->start_win) ? + min((start_win - tbl->start_win), tbl->win_size) : + tbl->win_size; + + for (i = 0; i < pkt_to_send; ++i) { + if (tbl->rx_reorder_ptr[i]) { + skb = tbl->rx_reorder_ptr[i]; + __skb_queue_tail(&list, skb); + tbl->rx_reorder_ptr[i] = NULL; + } + } + + /* We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { + tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; + tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; + } + + tbl->start_win = start_win; + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + while ((skb = __skb_dequeue(&list))) + nxpwifi_11n_dispatch_pkt(priv, skb); +} + +/* This function dispatches all packets in the Rx reorder table until + * a hole is found. + * + * The start window is adjusted automatically when a hole is located. + * Since the buffer is linear, the function uses rotation to simulate + * circular buffer. + */ +static void +nxpwifi_11n_scan_and_dispatch(struct nxpwifi_private *priv, + struct nxpwifi_rx_reorder_tbl *tbl) +{ + struct sk_buff_head list; + struct sk_buff *skb; + int i, j, xchg; + + __skb_queue_head_init(&list); + spin_lock_bh(&priv->rx_reorder_tbl_lock); + + for (i = 0; i < tbl->win_size; ++i) { + if (!tbl->rx_reorder_ptr[i]) + break; + skb = tbl->rx_reorder_ptr[i]; + __skb_queue_tail(&list, skb); + tbl->rx_reorder_ptr[i] = NULL; + } + + /* We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + if (i > 0) { + xchg = tbl->win_size - i; + for (j = 0; j < xchg; ++j) { + tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; + tbl->rx_reorder_ptr[i + j] = NULL; + } + } + tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); + + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + while ((skb = __skb_dequeue(&list))) + nxpwifi_11n_dispatch_pkt(priv, skb); +} + +/* This function deletes the Rx reorder table and frees the memory. + * + * The function stops the associated timer and dispatches all the + * pending packets in the Rx reorder table before deletion. + */ +static void +nxpwifi_del_rx_reorder_entry(struct nxpwifi_private *priv, + struct nxpwifi_rx_reorder_tbl *tbl) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int start_win; + + if (!tbl) + return; + + tasklet_disable(&adapter->rx_task); + + start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); + nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + + del_timer_sync(&tbl->timer_context.timer); + tbl->timer_context.timer_is_set = false; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_del(&tbl->list); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + kfree(tbl->rx_reorder_ptr); + kfree(tbl); + + tasklet_enable(&adapter->rx_task); +} + +/* This function returns the pointer to an entry in Rx reordering + * table which matches the given TA/TID pair. + */ +struct nxpwifi_rx_reorder_tbl * +nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta) +{ + struct nxpwifi_rx_reorder_tbl *tbl; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + return tbl; + } + } + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + return NULL; +} + +/* This function retrieves the pointer to an entry in Rx reordering + * table which matches the given TA and deletes it. + */ +void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta) +{ + struct nxpwifi_rx_reorder_tbl *tbl, *tmp; + + if (!ta) + return; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN)) { + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + nxpwifi_del_rx_reorder_entry(priv, tbl); + spin_lock_bh(&priv->rx_reorder_tbl_lock); + } + } + spin_unlock_bh(&priv->rx_reorder_tbl_lock); +} + +/* This function finds the last sequence number used in the packets + * buffered in Rx reordering table. + */ +static int +nxpwifi_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) +{ + struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; + struct nxpwifi_private *priv = ctx->priv; + int i; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + return i; + } + } + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + return -EINVAL; +} + +/* This function flushes all the packets in Rx reordering table. + * + * The function checks if any packets are currently buffered in the + * table or not. In case there are packets available, it dispatches + * them and then dumps the Rx reordering table. + */ +static void +nxpwifi_flush_data(struct timer_list *t) +{ + struct reorder_tmr_cnxt *ctx = + from_timer(ctx, t, timer); + int start_win, seq_num; + + ctx->timer_is_set = false; + seq_num = nxpwifi_11n_find_last_seq_num(ctx); + + if (seq_num < 0) + return; + + nxpwifi_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); + start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); + nxpwifi_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, + start_win); +} + +/* This function creates an entry in Rx reordering table for the + * given TA/TID. + * + * The function also initializes the entry with sequence number, window + * size as well as initializes the timer. + * + * If the received TA/TID pair is already present, all the packets are + * dispatched and the window size is moved until the SSN. + */ +static void +nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta, + int tid, int win_size, int seq_num) +{ + int i; + struct nxpwifi_rx_reorder_tbl *tbl, *new_node; + u16 last_seq = 0; + struct nxpwifi_sta_node *node; + + /* If we get a TID, ta pair which is already present dispatch all + * the packets and move the window size until the ssn + */ + tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta); + if (tbl) { + nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); + return; + } + /* if !tbl then create one */ + new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) + return; + + INIT_LIST_HEAD(&new_node->list); + new_node->tid = tid; + memcpy(new_node->ta, ta, ETH_ALEN); + new_node->start_win = seq_num; + new_node->init_win = seq_num; + new_node->flags = 0; + + spin_lock_bh(&priv->sta_list_spinlock); + if (nxpwifi_queuing_ra_based(priv)) { + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) { + node = nxpwifi_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + } + } else { + node = nxpwifi_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; + } + spin_unlock_bh(&priv->sta_list_spinlock); + + nxpwifi_dbg(priv->adapter, INFO, + "info: last_seq=%d start_win=%d\n", + last_seq, new_node->start_win); + + if (last_seq != NXPWIFI_DEF_11N_RX_SEQ_NUM && + last_seq >= new_node->start_win) { + new_node->start_win = last_seq + 1; + new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; + } + + new_node->win_size = win_size; + + new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *), + GFP_KERNEL); + if (!new_node->rx_reorder_ptr) { + kfree(new_node); + nxpwifi_dbg(priv->adapter, ERROR, + "%s: failed to alloc reorder_ptr\n", __func__); + return; + } + + new_node->timer_context.ptr = new_node; + new_node->timer_context.priv = priv; + new_node->timer_context.timer_is_set = false; + + timer_setup(&new_node->timer_context.timer, nxpwifi_flush_data, 0); + + for (i = 0; i < win_size; ++i) + new_node->rx_reorder_ptr[i] = NULL; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); +} + +static void +nxpwifi_11n_rxreorder_timer_restart(struct nxpwifi_rx_reorder_tbl *tbl) +{ + u32 min_flush_time; + + if (tbl->win_size >= NXPWIFI_BA_WIN_SIZE_32) + min_flush_time = MIN_FLUSH_TIMER_15_MS; + else + min_flush_time = MIN_FLUSH_TIMER_MS; + + mod_timer(&tbl->timer_context.timer, + jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); + + tbl->timer_context.timer_is_set = true; +} + +/* This function prepares command for adding a BA request. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting add BA request buffer + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; + + cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_REQ); + cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); + memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); + + return 0; +} + +/* This function prepares command for adding a BA response. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting add BA response buffer + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + struct host_cmd_ds_11n_addba_req + *cmd_addba_req) +{ + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; + u32 rx_win_size = priv->add_ba_param.rx_win_size; + u8 tid; + int win_size; + u16 block_ack_param_set; + + cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_RSP); + cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); + + memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, + ETH_ALEN); + add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; + add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; + add_ba_rsp->ssn = cmd_addba_req->ssn; + + block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); + block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; + + /* If we don't support AMSDU inside AMPDU, reset the bit */ + if (!priv->add_ba_param.rx_amsdu || + priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED) + block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; + add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); + win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) + & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); + + nxpwifi_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, + tid, win_size, + le16_to_cpu(cmd_addba_req->ssn)); + return 0; +} + +/* This function prepares command for deleting a BA request. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting del BA request buffer + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; + + cmd->command = cpu_to_le16(HOST_CMD_11N_DELBA); + cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); + memcpy(del_ba, data_buf, sizeof(*del_ba)); + + return 0; +} + +/* This function identifies if Rx reordering is needed for a received packet. + * + * In case reordering is required, the function will do the reordering + * before sending it to kernel. + * + * The Rx reorder table is checked first with the received TID/TA pair. If + * not found, the received packet is dispatched immediately. But if found, + * the packet is reordered and all the packets in the updated Rx reordering + * table is dispatched until a hole is found. + * + * For sequence number less than the starting window, the packet is dropped. + */ +int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *priv, + u16 seq_num, u16 tid, + u8 *ta, u8 pkt_type, void *payload) +{ + struct nxpwifi_rx_reorder_tbl *tbl; + int prev_start_win, start_win, end_win, win_size; + u16 pkt_index; + bool init_window_shift = false; + int ret = 0; + + tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta); + if (!tbl) { + if (pkt_type != PKT_TYPE_BAR) + nxpwifi_11n_dispatch_pkt(priv, payload); + return ret; + } + + if (pkt_type == PKT_TYPE_AMSDU && !tbl->amsdu) { + nxpwifi_11n_dispatch_pkt(priv, payload); + return ret; + } + + start_win = tbl->start_win; + prev_start_win = start_win; + win_size = tbl->win_size; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { + init_window_shift = true; + tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; + } + + if (tbl->flags & RXREOR_FORCE_NO_DROP) { + nxpwifi_dbg(priv->adapter, INFO, + "RXREOR_FORCE_NO_DROP when HS is activated\n"); + tbl->flags &= ~RXREOR_FORCE_NO_DROP; + } else if (init_window_shift && seq_num < start_win && + seq_num >= tbl->init_win) { + nxpwifi_dbg(priv->adapter, INFO, + "Sender TID sequence number reset %d->%d for SSN %d\n", + start_win, seq_num, tbl->init_win); + start_win = seq_num; + tbl->start_win = start_win; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + } else { + /* If seq_num is less then starting win then ignore and drop + * the packet + */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + TWOPOW11) & + (MAX_TID_VALUE - 1)) && + seq_num < start_win) { + ret = -EINVAL; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num >= (start_win + TWOPOW11))) { + ret = -EINVAL; + goto done; + } + } + + /* If this packet is a BAR we adjust seq_num as + * WinStart = seq_num + */ + if (pkt_type == PKT_TYPE_BAR) + seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); + + if ((end_win < start_win && + seq_num < start_win && seq_num > end_win) || + (end_win > start_win && (seq_num > end_win || + seq_num < start_win))) { + end_win = seq_num; + if (((end_win - win_size) + 1) >= 0) + start_win = (end_win - win_size) + 1; + else + start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; + nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + } + + if (pkt_type != PKT_TYPE_BAR) { + if (seq_num >= start_win) + pkt_index = seq_num - start_win; + else + pkt_index = (seq_num + MAX_TID_VALUE) - start_win; + + if (tbl->rx_reorder_ptr[pkt_index]) { + ret = -EINVAL; + goto done; + } + + tbl->rx_reorder_ptr[pkt_index] = payload; + } + + /* Dispatch all packets sequentially from start_win until a + * hole is found and adjust the start_win appropriately + */ + nxpwifi_11n_scan_and_dispatch(priv, tbl); + +done: + if (!tbl->timer_context.timer_is_set || + prev_start_win != tbl->start_win) + nxpwifi_11n_rxreorder_timer_restart(tbl); + return ret; +} + +/* This function deletes an entry for a given TID/TA pair. + * + * The TID/TA are taken from del BA event body. + */ +void +nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *peer_mac, + u8 type, int initiator) +{ + struct nxpwifi_rx_reorder_tbl *tbl; + struct nxpwifi_tx_ba_stream_tbl *ptx_tbl; + struct nxpwifi_ra_list_tbl *ra_list; + u8 cleanup_rx_reorder_tbl; + int tid_down; + + if (type == TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl = (initiator) ? true : false; + else + cleanup_rx_reorder_tbl = (initiator) ? false : true; + + nxpwifi_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", + peer_mac, tid, initiator); + + if (cleanup_rx_reorder_tbl) { + tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, peer_mac); + if (!tbl) { + nxpwifi_dbg(priv->adapter, EVENT, + "event: TID, TA not found in table\n"); + return; + } + nxpwifi_del_rx_reorder_entry(priv, tbl); + } else { + ptx_tbl = nxpwifi_get_ba_tbl(priv, tid, peer_mac); + if (!ptx_tbl) { + nxpwifi_dbg(priv->adapter, EVENT, + "event: TID, RA not found in table\n"); + return; + } + + tid_down = nxpwifi_wmm_downgrade_tid(priv, tid); + ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu = false; + ra_list->ba_status = BA_SETUP_NONE; + } + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); + nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); + } +} + +/* This function handles the command response of an add BA response. + * + * Handling includes changing the header fields into CPU format and + * creating the stream, provided the add BA is accepted. + */ +int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; + int tid, win_size; + struct nxpwifi_rx_reorder_tbl *tbl; + u16 block_ack_param_set; + + block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); + + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + /* Check if we had rejected the ADDBA, if yes then do not create + * the stream + */ + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + nxpwifi_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", + add_ba_rsp->peer_mac_addr, tid); + + tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) + nxpwifi_del_rx_reorder_entry(priv, tbl); + + return 0; + } + + win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) { + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu && + priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) + tbl->amsdu = true; + else + tbl->amsdu = false; + } + + nxpwifi_dbg(priv->adapter, CMD, + "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", + add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); + + return 0; +} + +/* This function handles BA stream timeout event by preparing and sending + * a command to the firmware. + */ +void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv, + struct host_cmd_ds_11n_batimeout *event) +{ + struct host_cmd_ds_11n_delba delba; + + memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); + memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); + + delba.del_ba_param_set |= + cpu_to_le16((u16)event->tid << DELBA_TID_POS); + delba.del_ba_param_set |= + cpu_to_le16((u16)event->origninator << DELBA_INITIATOR_POS); + delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); + nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, 0, 0, &delba, false); +} + +/* This function cleans up the Rx reorder table by deleting all the entries + * and re-initializing. + */ +void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv) +{ + struct nxpwifi_rx_reorder_tbl *del_tbl_ptr, *tmp_node; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->rx_reorder_tbl_ptr, list) { + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + nxpwifi_del_rx_reorder_entry(priv, del_tbl_ptr); + spin_lock_bh(&priv->rx_reorder_tbl_lock); + } + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + nxpwifi_reset_11n_rx_seq_num(priv); +} + +/* This function updates all rx_reorder_tbl's flags. + */ +void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags) +{ + struct nxpwifi_private *priv; + struct nxpwifi_rx_reorder_tbl *tbl; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + spin_lock_bh(&priv->rx_reorder_tbl_lock); + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) + tbl->flags = flags; + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + } +} + +/* This function update all the rx_win_size based on coex flag + */ +static void nxpwifi_update_ampdu_rxwinsize(struct nxpwifi_adapter *adapter, + bool coex_flag) +{ + u8 i; + u32 rx_win_size; + struct nxpwifi_private *priv; + + dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + rx_win_size = priv->add_ba_param.rx_win_size; + if (coex_flag) { + if (priv->bss_type == NXPWIFI_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE; + } else { + if (priv->bss_type == NXPWIFI_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + NXPWIFI_STA_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE; + } + + if (adapter->coex_win_size && adapter->coex_rx_win_size) + priv->add_ba_param.rx_win_size = + adapter->coex_rx_win_size; + + if (rx_win_size != priv->add_ba_param.rx_win_size) { + if (!priv->media_connected) + continue; + for (i = 0; i < MAX_NUM_TID; i++) + nxpwifi_11n_delba(priv, i); + } + } +} + +/* This function check coex for RX BA + */ +void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter) +{ + u8 i; + struct nxpwifi_private *priv; + u8 count = 0; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) { + if (priv->media_connected) + count++; + } + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + if (priv->bss_started) + count++; + } + if (count >= NXPWIFI_BSS_COEX_COUNT) + break; + } + if (count >= NXPWIFI_BSS_COEX_COUNT) + nxpwifi_update_ampdu_rxwinsize(adapter, true); + else + nxpwifi_update_ampdu_rxwinsize(adapter, false); +} + +/* This function handles rxba_sync event + */ +void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv, + u8 *event_buf, u16 len) +{ + struct nxpwifi_ie_types_rxba_sync *tlv_rxba = (void *)event_buf; + u16 tlv_type, tlv_len; + struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr; + u8 i, j; + u16 seq_num, tlv_seq_num, tlv_bitmap_len; + int tlv_buf_left = len; + int ret; + u8 *tmp; + + nxpwifi_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:", + event_buf, len); + while (tlv_buf_left > sizeof(*tlv_rxba)) { + tlv_type = le16_to_cpu(tlv_rxba->header.type); + tlv_len = le16_to_cpu(tlv_rxba->header.len); + if (size_add(sizeof(tlv_rxba->header), tlv_len) > tlv_buf_left) { + nxpwifi_dbg(priv->adapter, WARN, + "TLV size (%zu) overflows event_buf buf_left=%d\n", + size_add(sizeof(tlv_rxba->header), tlv_len), + tlv_buf_left); + return; + } + + if (tlv_type != TLV_TYPE_RXBA_SYNC) { + nxpwifi_dbg(priv->adapter, ERROR, + "Wrong TLV id=0x%x\n", tlv_type); + return; + } + + tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num); + tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len); + if (size_add(sizeof(*tlv_rxba), tlv_bitmap_len) > tlv_buf_left) { + nxpwifi_dbg(priv->adapter, WARN, + "TLV size (%zu) overflows event_buf buf_left=%d\n", + size_add(sizeof(*tlv_rxba), tlv_bitmap_len), + tlv_buf_left); + return; + } + + nxpwifi_dbg(priv->adapter, INFO, + "%pM tid=%d seq_num=%d bitmap_len=%d\n", + tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num, + tlv_bitmap_len); + + rx_reor_tbl_ptr = + nxpwifi_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid, + tlv_rxba->mac); + if (!rx_reor_tbl_ptr) { + nxpwifi_dbg(priv->adapter, ERROR, + "Can not find rx_reorder_tbl!"); + return; + } + + for (i = 0; i < tlv_bitmap_len; i++) { + for (j = 0 ; j < 8; j++) { + if (tlv_rxba->bitmap[i] & (1 << j)) { + seq_num = (MAX_TID_VALUE - 1) & + (tlv_seq_num + i * 8 + j); + + nxpwifi_dbg(priv->adapter, ERROR, + "drop packet,seq=%d\n", + seq_num); + + ret = nxpwifi_11n_rx_reorder_pkt + (priv, seq_num, tlv_rxba->tid, + tlv_rxba->mac, 0, NULL); + + if (ret) + nxpwifi_dbg(priv->adapter, + ERROR, + "Fail to drop packet"); + } + } + } + + tlv_buf_left -= (sizeof(tlv_rxba->header) + tlv_len); + tmp = (u8 *)tlv_rxba + sizeof(tlv_rxba->header) + tlv_len; + tlv_rxba = (struct nxpwifi_ie_types_rxba_sync *)tmp; + } +} diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h new file mode 100644 index 000000000000..9b5dd4899f0e --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: 802.11n RX Re-ordering + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_11N_RXREORDER_H_ +#define _NXPWIFI_11N_RXREORDER_H_ + +#define MIN_FLUSH_TIMER_MS 50 +#define MIN_FLUSH_TIMER_15_MS 15 +#define NXPWIFI_BA_WIN_SIZE_32 32 + +#define PKT_TYPE_BAR 0xE7 +#define MAX_TID_VALUE (2 << 11) +#define TWOPOW11 (2 << 10) + +#define BLOCKACKPARAM_TID_POS 2 +#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 +#define BLOCKACKPARAM_WINSIZE_POS 6 +#define DELBA_TID_POS 12 +#define DELBA_INITIATOR_POS 11 +#define TYPE_DELBA_SENT 1 +#define TYPE_DELBA_RECEIVE 2 +#define IMMEDIATE_BLOCK_ACK 0x2 + +#define ADDBA_RSP_STATUS_ACCEPT 0 + +#define NXPWIFI_DEF_11N_RX_SEQ_NUM 0xffff +#define BA_SETUP_MAX_PACKET_THRESHOLD 16 +#define BA_SETUP_PACKET_OFFSET 16 + +enum nxpwifi_rxreor_flags { + RXREOR_FORCE_NO_DROP = 1 << 0, + RXREOR_INIT_WINDOW_SHIFT = 1 << 1, +}; + +static inline void nxpwifi_reset_11n_rx_seq_num(struct nxpwifi_private *priv) +{ + memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); +} + +int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *, + u16 seqNum, + u16 tid, u8 *ta, + u8 pkttype, void *payload); +void nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid, + u8 *peer_mac, u8 type, int initiator); +void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv, + struct host_cmd_ds_11n_batimeout *event); +int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv, + struct host_cmd_ds_command + *resp); +int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd, + void *data_buf); +int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + struct host_cmd_ds_11n_addba_req + *cmd_addba_req); +int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, + void *data_buf); +void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv); +struct nxpwifi_rx_reorder_tbl * +nxpwifi_11n_get_rxreorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta); +struct nxpwifi_rx_reorder_tbl * +nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta); +void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta); +void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags); +void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv, + u8 *event_buf, u16 len); +#endif /* _NXPWIFI_11N_RXREORDER_H_ */ From patchwork Mon Sep 30 06:36:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815418 X-Patchwork-Delegate: kvalo@adurom.com Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010001.outbound.protection.outlook.com [52.101.69.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0147B15098F; Mon, 30 Sep 2024 06:37:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.1 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678262; cv=fail; b=WkiyaHMHGx+9vpCJ5nGgwMQsBliPGXsgigwlhBh3DpjneoimP9OKgJxZx/qOtac7le82OC/iJJ2oQ5RD4DRa6ekkJcyPIDYVOJx2EYoOPi/ZtUSS0MmqNSGQKUw8wiTMB4ZYXsffYuq50OqjGuaSCZZtwkBH8m/d0N54XqE18nI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678262; c=relaxed/simple; bh=jNep0sI2fAyiX/Wwn7FxlO9lDarKLe034MBc82q+H7k=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=aJdPn0upOjL8fG1rGoocM9a5fy6xkOvH5JGbr9zQSt7TXIuyHWEGWcWS3Z+rm1z4HglbopP/wskwM6GydXwijF/YUMTNwenpk18XRbxMmrOj/6Onf3K5096h9Pu4FKDOJHXN98xQn1etamAeiGYjLywuJVpWkbmsRqKh6WmW1PY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=azBKDeii; arc=fail smtp.client-ip=52.101.69.1 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="azBKDeii" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=TGU7JNAlDGr8vf7M4L5U3cyQs4HAIAOJitD3zn0mQ6ADpQfylz5H5e7hFkYywhIkxxhJ9PtbTataw3P14mv54y/OO5OLWs236CjwJfK3HYgD4d/NnXwd9ewgX697OMf9p0EfCGV2/qfw9YBKQUCxDzJyY9tQtYdk+P/3+TOzCxMX0oMbZwGjc0IJ0DIPh5lJmJwMPHIdeRMI/d0p7l/AnFwSzo2cWAUdK5JnhSMNvNeR/M091zfZGvUWfWvrjHW/ox2SR5Z5KU8zRk7ECTxzw/etBFEA/pZzpg1cGhGlbLaMdQ7GeBcs+4brE2sMp1thQkoctUrlW0oxGHFIeiK8QQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=FUAxdtkboWmu261KiDlKFxQKJXn8Jc2UPPpJzsyOcsY=; b=HyyeHZoBfaoowKCzHKpJpe1n9hNJsxwdS57hdNW2GIXpEfLxe+9FqYMrIwrBldLP/KvbKsIXHnntTv+bQJgR/m37I1ZAoa/zve/KL6RHmvmQcNnfH/PJvC5F78sUkt9/0qV1XwotdzxXH0cp/mvhtxJiMWTojXliqb25amhpua+X9Fh7GKdiUDn5MHuPWCWFOzOz0ju9Kp/VtX3s6ObtNwQh1x9yafu0Ty3t1EHG6HwTq2gYaSqkHiXqZ+kLBspdMg1om08OJTkihUXT63L7F88r8NxJimKQRykWiv2sAEsDTlb00Qj4FRAfpX0rQoLMn8ItjVsWzj2DrDf2jb5hDA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=FUAxdtkboWmu261KiDlKFxQKJXn8Jc2UPPpJzsyOcsY=; b=azBKDeiicLKrEsDR45mivEkA0ymBii9kmruWqTngrz4AgJz3tUIQbdnNGbIjjdWFNWIXyX4em6fABdKEw9c0CVI2UyWLSh85kNnS34hW2vwDCMLunR8rF7tTPEm4DS0SlQ+no6uyoXd0xlRH7x6z9o2MWhmZQBkoMnxyP/1rCMW7flLmNnodKnMkpNJZYSYaxeOUXZtoouxcPv1/h1ClyCNcRVAM4EXqbV/schPBM7v/P9nFUOELCcmpfqzcP7/vlNoATynul62IY0+m1Mi7nhoD8CagwrRrDU2n4Cppaw/eEt72ZK6ZPN7e0P3o/9JoIGbYqLLmiMVJ5TFW4guqkQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by DU2PR04MB9000.eurprd04.prod.outlook.com (2603:10a6:10:2e3::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7918.25; Mon, 30 Sep 2024 06:37:34 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:34 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 02/22] wifi: nxpwifi: add 802.11ac files Date: Mon, 30 Sep 2024 14:36:41 +0800 Message-Id: <20240930063701.2566520-3-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|DU2PR04MB9000:EE_ X-MS-Office365-Filtering-Correlation-Id: 8c950705-b719-4bf8-3a2a-08dce11a5dd3 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: VD6gP3DdF4HW++SZyBnrxlOQhCuJXXoH2WL70edAWCWHxdG5VKecAYMsIwLgzDyFHynLsUvZloMiPgwti9sLSF4LBip0axnPwdnkPkWYAr+oSNoKhONN3bQKsPSDt3h73S/cI1yNri0w3MbwN5buuWz2uNBv1ZWwy55J5yT/U4B2XbWEQxX+IzX/vOoHh80EDcVk/crEFWiHsdA4016ARvg9BVHzeRmeWmpOfZ+lvAJdnvQvB6b/d2kIK47fTiRMtbAYDBrVAA0OyMmd2w48UwQzFZ7fSrW21cDvsZZvC05+lSH1kdN1fHc7I43pLWr2dURYilPjKz14iurG5lRmwiYU9abkAXDbO+XTzf1OGYJxXGYd83foF8eXffzgfmMBP82oZr6aBqcICBecC00V1vnflktSQjmC+Qsj+5fcLMwkfkL3txBzE71ewN2V6Ax2v6xqhCK9jK8LsoLYCnKKbiTcepbJoPfTjgf5Dm7ahB0nBpAgJ1lHwf4tTCB+uzoBUKkFoFa81JBYK1kjU0XlMRhQnnH+GFhy0UWWvglv8O6LGCfRsH/nvUateMHbw1smSitgHUNxwnpLHtkDNYhbuOL3VHRgtgBdWXc54G6iJyYiv2seRn5+VhfJ23jflgmbAnjlHvVxsi7scMAXu+yRzQ/FDBEcgxVKrfoWzIikFYvl+G4lH10BqdvUM7ElAYYErThNv5nyLuvA1dLd2XxPo+/7ORQ+MNa64IW/jiRT1nucBdEYoiil2FztTRtYxWioDiCNd1xXDOASl9yo4RmqdVIHUeHPmx1vAe7j4MVttlLlKwyW2JO5yPP3wEotYd4Os9FfOT4ZsnS1srPqzU+o4hvU+6VHhkE107+NNUZiU6vPEOFm2FaOwUTHlMPj3pjkNzxpbdkQ+AdttY3zgz1epDux0AhS0bQJ4HnTF18J+vuFoWU12142g2KrYnbgdcY03aRGzCMEdo+oaGySYMAA9nSFhJBjAc0mwaGT0/NEq1Lakq4/RZ7wi6nugufmSZVYgFo0sXKILeGsaBjh2HYha9h/Su7DJWyXDKj79OY4ZA8pFTrkAe51djsBWR0+s4OYrf8WaG4Vcm0NHFIh+egvIZU1qU1aorKJE9iC8WVckxvxgxlSfaO9jItJuqQNzQ5w/EhtBYGZ4nNsU60rfaVLQKo3D60mnSwdkGOfaPLgD9x1K3RTdRl5Oy/u7O8i7W2rTDl6QxWEypT4gt1BX7nMB4bURr0qvLmrCgGOCtNJ/HZB5JTUWn5djzOeiuZMKsFMJm0PZyyJeDyuFF3niiZi914rUK76KhNBbKJMDz8Kbm7wC7eZJ9JDkIPJFuf1oKcMutp9KoVZInGZJXg+JZJYAg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: UsxnBR49/jtKl/VRksn4XMwmMTywPOlYuR4QrHjS7HDhjdr1TsXPWn08TyoHpmICr01i78dXu+akZK3cwXQtyK3Hbq9w1+cA63UjmAcDkHMg7UJEyFwvlJDwfIOtMsgH2Q5bknzDJFfp2Ka1NCApOT8xpbz7cOLKdVnmbOy5bv00c47wj5Iv8oHUG/Exh3F2SenSr82KOK5BodhV7HIVe1M3qflDAbzQpnByYtrjXFWlU9v8hUge9mTsWRKogWuuSJhX64W5V97pgzrFwQtwQ11odM1Of5Izm/lRFrVTgsmZHMYZs6+L6dq7kekATX+c7kiN+hgbiFh8sqa/UXOpk6nXHw6Lc8lY7+3UC+YAe6mjaqM2LLZ033WMH8ZnCj1I0BPCrezfWgTyvrvkr9XDoTzz5gEQc3tvwmATYese9+Zk0iqwl6+KsHpXfDYkucXJox/Dpy72kqUf1B7QnbSS0JwbRso1FIFbbH7aprhlZKYAO6OCBGJNBkdQanO9j51Sc/qrGC7Fg+23UmdDZSkAiEoiP+Rtac71phfUVV8GBwRwkTt3Cl/q12gIZQGXPB3iYSne3Pxvskj8N0pKdEgSJm3EzgdfhMmFLdD0HU8faSMgwNAhnQckT7qsROaIMuDu+GAOCw0lsr1QjXu2fXZLP+eFDwQhwsXLZTIgSC8cKA+vhfFE9S3oxSHu37mDBY+DhocnFAiGZ2ljqApeb2CRL5P08aVxLNampkEB1TobkEhRj10qCUqLbut4xxKioXpEsfi2zwieETE61l4eDs55dMJrfnRQOKbkLWkuFQ4DBEwoUC+13ZnuPUNn+cY5rNKSz1napgEN1HaxeHbd3ztbyCsTBGls0R5BgyiA1Zar0LP5cXbG0BWsPslPiSTS6pIGhz3KI4ZDHWAjmExX8dQNrCqvRT4Mk78byPhzEQ99SZRj400ewhzGeYpbdFbT1vmDx1R9VFsUqPu7g4ztFPHUeCs1QgfC7kb33EbcxoqBnyV+bfE5NPq3kpHKEij5LSRfjMseG+jxxGQD2nbrbyIhRIPdosVpOHzPCBs0U2JVx6SFITK23CGN69aLOSH7RbwogGZWw1mOEUC2ATxxMxE3FuSl68jZaG1zZpyRyVsKYf1I2GCXWaVxciQdEM2yaqnSABCBYkUolP5VbgavGaZhwy5ed21MBe74ejQ8wcO6sAt+5gFcer4NQre2uZ82bJ9RV8LPOMT9mGBq/RZYl8wkA/auPCsMKQBrwBYKprzZiRFUYRexgmZefzxSg8JGJsVdFe+yQa6FE4isVv24odEYZ5EDdtmrJ3DKE0xCRml3Dpzucp1kt6gzvcxS/U2atvfMdMw8zwRjSqvm3VrLm96RuPYbF7LUqpJmbbTn9GhGzwP0MDLx2VfRxoMo2JdMFCaxfYAN2pjl8F+KZC6QErXAv8kukQ+9oEb7RcuMs5BIObIkdE52NLGcr/5uPFExyUomNIYejANdABTnQ80yn0E7c6Gcj9S/0u3jpDvq7X3eSknJBLwSm7fCbhPd6gqc+M2HS4dMK12nS5BbBqGcbCdM8ji0NScLPBThITmrqo+LkuDM/9RdugiJzct4PPKKm+BQ X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8c950705-b719-4bf8-3a2a-08dce11a5dd3 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:34.6111 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 0koEsLoQb8D2ol+n/XTTsUHVmA0SZ4A8LR4nU5vzBF1ajSpvUWzun7l+mvCxnE9bKrZJDC3H8pIHFvxTh4VN3g== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU2PR04MB9000 For client mode, NXP firmware will help association process via host command HOST_CMD_802_11_ASSOCIATE. IEs for 802.11ac should be converted from parameters passed from cfg80211 to TLVs appended to host command HOST_CMD_802_11_ASSOCIATE. For AP mode, IEs for 802.11ac should be converted from parameters passed from cfg80211 to host command HOST_CMD_11AC_CFG. Files in this commit will support 802.11ac features based on above descriptions. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/11ac.c | 288 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/11ac.h | 32 +++ 2 files changed, 320 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ac.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ac.h diff --git a/drivers/net/wireless/nxp/nxpwifi/11ac.c b/drivers/net/wireless/nxp/nxpwifi/11ac.c new file mode 100644 index 000000000000..40079e43c571 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ac.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: 802.11ac + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "fw.h" +#include "main.h" +#include "11ac.h" + +/* Tables of the MCS map to the highest data rate (in Mbps) supported + * for long GI. + */ +static const u16 max_rate_lgi_80MHZ[8][3] = { + {0x124, 0x15F, 0x186}, /* NSS = 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ + {0x36D, 0x41D, 0x492}, /* NSS = 3 */ + {0x492, 0x57C, 0x618}, /* NSS = 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ + {0x924, 0xAF8, 0xC30} /* NSS = 8 */ +}; + +static const u16 max_rate_lgi_160MHZ[8][3] = { + {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ + {0x492, 0x57C, 0x618}, /* NSS = 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ +}; + +/* This function converts the 2-bit MCS map to the highest long GI + * VHT data rate. + */ +static u16 +nxpwifi_convert_mcsmap_to_maxrate(struct nxpwifi_private *priv, + u16 bands, u16 mcs_map) +{ + u8 i, nss, mcs; + u16 max_rate = 0; + u32 usr_vht_cap_info = 0; + struct nxpwifi_adapter *adapter = priv->adapter; + + if (bands & BAND_AAC) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the max NSS supported */ + nss = 1; + for (i = 1; i <= 8; i++) { + mcs = GET_VHTNSSMCS(mcs_map, i); + if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) + nss = i; + } + mcs = GET_VHTNSSMCS(mcs_map, nss); + + /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ + if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; + + if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { + /* support 160 MHz */ + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS6 */ + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; + } else { + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS3 */ + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; + } + + return max_rate; +} + +static void +nxpwifi_fill_vht_cap_info(struct nxpwifi_private *priv, + struct ieee80211_vht_cap *vht_cap, u16 bands) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (bands & BAND_A) + vht_cap->vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); + else + vht_cap->vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); +} + +static void +nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv, + struct ieee80211_vht_cap *vht_cap, u16 bands) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss, tmp; + + /* Fill VHT cap info */ + nxpwifi_fill_vht_cap_info(priv, vht_cap, bands); + + /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + + if (mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED || + mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); + + /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ + mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + if (mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED || + mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); +} + +int nxpwifi_cmd_append_11ac_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct nxpwifi_ie_types_vhtcap *vht_cap; + struct nxpwifi_ie_types_oper_mode_ntf *oper_ntf; + struct ieee_types_oper_mode_ntf *ieee_oper_ntf; + struct nxpwifi_ie_types_vht_oper *vht_op; + struct nxpwifi_adapter *adapter = priv->adapter; + u8 supp_chwd_set; + u32 usr_vht_cap_info; + int ret_len = 0; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* VHT Capabilities IE */ + if (bss_desc->bcn_vht_cap) { + vht_cap = (struct nxpwifi_ie_types_vhtcap *)*buffer; + memset(vht_cap, 0, sizeof(*vht_cap)); + vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy((u8 *)vht_cap + sizeof(struct nxpwifi_ie_types_header), + (u8 *)bss_desc->bcn_vht_cap, + le16_to_cpu(vht_cap->header.len)); + + nxpwifi_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, + bss_desc->bss_band); + *buffer += sizeof(*vht_cap); + ret_len += sizeof(*vht_cap); + } + + /* VHT Operation IE */ + if (bss_desc->bcn_vht_oper) { + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + vht_op = (struct nxpwifi_ie_types_vht_oper *)*buffer; + memset(vht_op, 0, sizeof(*vht_op)); + vht_op->header.type = + cpu_to_le16(WLAN_EID_VHT_OPERATION); + vht_op->header.len = cpu_to_le16(sizeof(*vht_op) - + sizeof(struct nxpwifi_ie_types_header)); + memcpy((u8 *)vht_op + + sizeof(struct nxpwifi_ie_types_header), + (u8 *)bss_desc->bcn_vht_oper, + le16_to_cpu(vht_op->header.len)); + + /* negotiate the channel width and central freq + * and keep the central freq as the peer suggests + */ + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + + switch (supp_chwd_set) { + case 0: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 1: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 2: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + default: + vht_op->chan_width = + IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + *buffer += sizeof(*vht_op); + ret_len += sizeof(*vht_op); + } + } + + /* Operating Mode Notification IE */ + if (bss_desc->oper_mode) { + ieee_oper_ntf = bss_desc->oper_mode; + oper_ntf = (void *)*buffer; + memset(oper_ntf, 0, sizeof(*oper_ntf)); + oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF); + oper_ntf->header.len = cpu_to_le16(sizeof(u8)); + oper_ntf->oper_mode = ieee_oper_ntf->oper_mode; + *buffer += sizeof(*oper_ntf); + ret_len += sizeof(*oper_ntf); + } + + return ret_len; +} + +int nxpwifi_cmd_11ac_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_11ac_vht_cfg *cfg) +{ + struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg; + + cmd->command = cpu_to_le16(HOST_CMD_11AC_CFG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) + + S_DS_GEN); + vhtcfg->action = cpu_to_le16(cmd_action); + vhtcfg->band_config = cfg->band_config; + vhtcfg->misc_config = cfg->misc_config; + vhtcfg->cap_info = cpu_to_le32(cfg->cap_info); + vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set); + vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set); + + return 0; +} + +/* This function initializes the BlockACK setup information for given + * nxpwifi_private structure for 11ac enabled networks. + */ +void nxpwifi_set_11ac_ba_params(struct nxpwifi_private *priv) +{ + priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size = + NXPWIFI_11AC_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + NXPWIFI_11AC_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size = + NXPWIFI_11AC_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + NXPWIFI_11AC_STA_AMPDU_DEF_RXWINSIZE; + } +} diff --git a/drivers/net/wireless/nxp/nxpwifi/11ac.h b/drivers/net/wireless/nxp/nxpwifi/11ac.h new file mode 100644 index 000000000000..c89956f247f4 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ac.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: 802.11ac + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_11AC_H_ +#define _NXPWIFI_11AC_H_ + +#define VHT_CFG_2GHZ BIT(0) +#define VHT_CFG_5GHZ BIT(1) + +enum vht_cfg_misc_config { + VHT_CAP_TX_OPERATION = 1, + VHT_CAP_ASSOCIATION, + VHT_CAP_UAP_ONLY +}; + +#define DEFAULT_VHT_MCS_SET 0xfffe +#define DISABLE_VHT_MCS_SET 0xffff + +#define VHT_BW_80_160_80P80 BIT(2) + +int nxpwifi_cmd_append_11ac_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer); +int nxpwifi_cmd_11ac_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_11ac_vht_cfg *cfg); + +#endif /* _NXPWIFI_11AC_H_ */ From patchwork Mon Sep 30 06:36:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815419 X-Patchwork-Delegate: kvalo@adurom.com Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010001.outbound.protection.outlook.com [52.101.69.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7D99784D02; Mon, 30 Sep 2024 06:37:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.1 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678264; cv=fail; b=lm8VRxH9P/KZGJjVAdNs0+yBepYa0HthwZAO7+fiLigUnvQl3zkaLvENrSrsG5N2/G0+BGxeuuY7heRxXZJI+j/Hd1z5d2UhxSKpf5AmX8xE+qA3vvOp1IdKgtlkxHvjTsqStD++N4NURXkbj9WcYCtlei5jahV6bvA2wDNMQPw= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678264; c=relaxed/simple; bh=PN8jxJEORBwQwSFHGwCpsH55jYI6jCDcjDsbui3Uzi4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=CFQvjShKWrGjgcYFLt4TNDAsz7LpXuvY/WVcGQ7S1Tu0su9F13G88cBjel7QF/hzzrYUyGIgmzY1YmUqYMtfa2W9k4WkU1Qq9KLkg65or30dkyn86gUADbuo+wvpqsJPHjuXy2JTUjXuimg3S+ckHfVcztUYFfswBYrUK8vCWkA= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=LREUvFJP; arc=fail smtp.client-ip=52.101.69.1 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="LREUvFJP" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=y8AQtFmwS+l38ZGD1UEonYrLaOlnFkYXBHp7LLnyrLEGNDIhdx9Cuw58YA4E6xE4mI6mSUglQf7ENmT5lGOD+Pb0vEGA+m8R1x7hLEmFKp3BiFUiHiuIvaA6lXh0QRbhl33TrSkX4eF7FrqIIULLXWFQkqTp6pY+47jZZ6EvmioviyrSDYkuON2VSTFa2Z4+TjYb7V488ViYJ05aKf1RmW0w90P/uLZHQckF6IJCRFB0BBI0p6i14Ym/Ej7sMS9dmWmQWCX6LUmGzHU1pD8iH8cLNf+Gm+1IZkt6wf+Cv3BwUtDCvaR5p4ormcqCuyOVOEIw8BMXC12+AfYQneI32g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=b16w7IAgMPYPy0CWdJeYv+KadFDHo8AjpnUJjk7Fe9M=; b=n5Rx3tY6DQlP+8BkvZmzGY7SMWq7TQmVctDp9JMh8Ni2ltX2sTAuRIk0n1xKXuPB+5Od99RnpNZY0fCpBX+WQXAPnglMQLlg7C/UuaVPPj0GHquL3aPHdd2MYsw7YOnbLH0aqMfuPwzHmQJMjlr9a402IVSk+rrriJVmMJMXsYIE9ggw8FRpWPg7kcer7eP6ZIOHv6VDMLoDkI4eI5zvumHQcSYaYDHPomBfGiDZRI5Pn9IncACnW/iz2GZuZR2Q1so9Uotm6l8TEFbfislSf3VkA7qb5rXS4sXEwNqjoGFb8e8z6ZBCiI5Gq2LOJoPkXbfCH5DKZ6uWMLpQ1Uybjw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=b16w7IAgMPYPy0CWdJeYv+KadFDHo8AjpnUJjk7Fe9M=; b=LREUvFJP6SYgeUPI2E4sM4f/ypGOYK7PgZc0lWkIpsNQBKCwtvap6DH+dIE9upEMt2DyJWhnc+xKTJ3KE2WIKXq/Gi0sqA2HctvV34od+3AzcEnysxuIc1vR+Ckm7egf0KGGJFlqwuzY2RIzupiL/UXhphS5LdJkAjAc2CIHxM3SquLG3cieZACRwC3zoCr6lZ4TKro9TApc1K9JuxNVAqio8hlCngoCUGrexJZzRPo2k1CGSSw4cLCJlhPUaV8E/3pskDJKEh7nTwTCAIVUC4cnKwtZk3gfBLb/sW16YID0787e4DQvI67EoGzRJ2xeS9lJ1Z8EbGGNGTiZykcOYQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by DU2PR04MB9000.eurprd04.prod.outlook.com (2603:10a6:10:2e3::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7918.25; Mon, 30 Sep 2024 06:37:38 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:38 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 03/22] wifi: nxpwifi: add 802.11ax files Date: Mon, 30 Sep 2024 14:36:42 +0800 Message-Id: <20240930063701.2566520-4-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|DU2PR04MB9000:EE_ X-MS-Office365-Filtering-Correlation-Id: 8447f303-1607-49fb-5f9a-08dce11a6005 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: m8BB6GSLUldt72HN4PF+ejRVixGM1QJV1iqtCyuXHhR92ad/RUGjksYhjhCFx0Ea7MGqDU8WJFfu/GvCSSK/GAF8qCoUETHrQjvNwGc/kfPGxfgqTMoe599kvEDkqHPlC+civx1xoDMrSnWDyw+fEjTzHygd3tj6qMcqN7I9hv5iyce+dpW3LM6hF5QuSpYRkyE+zRCB06/W48PkEaD5t/x7rEh5v9Do2fjmgERp39LoESpN4eqfSoNKCec1pb7kAKCaPQRawE+aEIBaiA9jBdYmhwcdr90Q+LwNPrZdQspdmgHyF7rJlP8O+8TPUVfQj8ztuFJUGlUTVMwwc9amCD8UQ8JRF1GorOZa+kV/YrOrJkUX3dlbIvhn+tlcibyIt+pPBofSmNja1j/5lQhsCmsbKaKwoUpOKtH+DuNHh9CMj5cRqDploULL57XqS8z7+0vQn9Nb2QH3Zsq+urjtZEgDiBEJue/Lrt6qTBO6s1zqwY144UUaD0MxJNmqfqYh9kzVEVE3cManAyLuA7aeYFfmjUvB9vDC9Pv6lpoCQRe9zzx8sqB/2PB8ZbuOrvDt/je6JOtzIuVCWprvp+RXLHzxGzUe459EUeEAB5IDfgFh1rC0qwP8Yyi26as/AKtXnzweSO3KBQaGhqvKvjc52XnAB0rrXJ5jd1Olje/UwYHHuIyLFelLsGaq+5EVUPeHihb8izVyM50r7wpOPodZ2oTx2z+M3gmhYPi3Tln+MxUDipALLyeGP5dpXxJIzKz/UEDYy77OzQBXwvLytgZRs6Wu79PkhGY8SWLMdHYQda+OVexVNByK0OX7BYa3w7A03YzF0L4Zip1xGSLvg8lNMuOf/+HCOSEmbQGaRFsYYMQCPrXbp2FGIslFAEyxgaBJsBiDPX+0wLRqZXPZBP3W0TmZixdG06MWe6z5cKKSC48CrnM8y5xRnSy0tPJYaRUo+/wafslOMZnYlUL1EeCVYCj3/x23okbluJE47Y2tM9bfWudlJFQujlvWJqlPEwGrHq7LM133SFoHB5KBIxaCDHTGvG6gRczJJLER/aGH35pcxHCFNhdxWXozfmC89KaV7lFoHm1CYwgzOGPrcrrs4m+SDUnzkJfORukJxq6101k7gudwPysWkcl9v2qE3Lhkta/uBe6ZZoFTIV3uwhH863FWLmdps2y8943GtnnON1ryXY3FKrNwawJT7h/R+AOH5wkVpzMmoH8PQxiSzb9Ft0yVQM5aDTJkOpvxcz0g145D6HCBCegKe4h2vXWwo0ErnLXe9SHQDo0Yaps3+cQ9GwvgB9y8eM8a8yRTMATcFLGwPgW11yPWY68hbgIObckz/Tq7BWFXsgHfz9ARBxaJyw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: IAAZOPHmAnSX/IiA5UY7NwrdFQBTZ8eWq0F0CdWr3G3VAIbzMNvfoniBpk/AzWqv7QeN5GsGQOWhxNQCiz/TewPI/jykdPF+PQ6VpGV0h+s+33xtnkyBScJ4YgWTTdVZWWqldHWU/N7ww4gywjX/w6iEtugDESjJcxNPjVJ0aWiuRNvRC3gWjPFO2RoZZo7dvE1BFfq02WgR9hP5I8Qojc+WVPs2Aw3YAvtpCuBvhYWYOj8gM/gd+BtGT5Mlu8m1Hk2a4iuHiWZ+ArBvJbULCT4EJL0yi5MCD2MwGzM0c3k95Lsh+0iREvUROdekk/d8mvN4tK2MLkz/s8I8Pyr2DTLh/eb6GFlu8YPrmjta6cclvjwirmU/IpC+3o70/5L32uu7aQ+iyd03g8T8A4PNnqzVN2kcOFFw0qONG+huduMGVC6i6axJdPpFOsfWJdq1yzVjqz8N4P/kkpKuyZifCmpvnFmM0jWx+I0W7/VaxA9lHq7QvVjwMBiDH+J1y3BysXB2SkFdBzLr7gHxJgCyKxob1JxAVL1jv8hU+OMqONhhA2ESU0Ir7P6SSRQNrAGVRMJistYiX/IdbIBGHyS9seDin4iRjCj9gG8XyWJbluXbh5U6lsBx1kmDEAOjTdG/vYB/BrscQkyiMnmzxclswE+6kty7VqUNzoSNJ4PleWII+copkPenZ0PZP+qs0mOG/uVE2vlmRHO7Cm8H9AQVvkq6y2NQyCVcQ0aQV9YGCDf2cF4zn+vt9rzbMij9ons+LZVmMWlDZyraijZTlSBiVQbmVRLbukyQ6QrodiIDgeGXkIjKrDb0GsvIn4KfiucokwEsSsyQhqZSRNdy0Z251nkvdAG5OJwj48I39HW0lkhaDclYfbjKJp6jMpR0a/2qzoCCG/xrQ25LDhHVbBHCCEis8ne6FLAzhskZiGCa/7ubAi56kEdRyssGlJkS4+U12U8g/PeZWs82afsgPSlOIBGqgm9pZ+RRTdTITkfwE/lXmvI1JE4IcTgL+a6qqOnyVhghbxWj5QfLwPXRG3Wsjn7j4Qbtxcre8ghX4wrQogMykqMn2hbgFVwxJF0+26KAEeZaypDYD3a/xgE7WhW9c9ZeJSMpUgtw1cWPtTjC0zD92amJE+viHKo7FvN7puBqF/c5ivAg+5Ly3sxe+JLHV3DnJxISGTVKvpn9nGk+gkx/32z9n1gk/vf23DhrM+9BCSmwlm/GfBRAcA8Fto48SgV6RCKh3js0lhQrf0G0ixMuhd1KutTuDicCUdG4leVeU2y7Qy4xfot745n3EUTFQTbkBxkZhmbYNt2RJoqQbhl7yumPzAiypVVuhZJ5OdiwoUnrHfTp8bHF0CdQ9FxLN0r9NpixLujXOzQXYCFW39P32x49I533UJsz6+3jbtbmG+89x5D7VZl+UyAJvPYzdj/omBSm/m3O/E6Le9zx8SQ/MksUaeXpma5Tod/QEapp15V5juvLxvW83h+VAj+iRF5GpsgMUZSK12MqxRQBiwsqLeqOJ3OKCMs0CUktDhfy29JmaxJGaT/fmcUcJf6ANm3TBH0NE+oHLZdgbOKNsyY4wkUGqKGEtQ759lORO+vD X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8447f303-1607-49fb-5f9a-08dce11a6005 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:38.1175 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: vYD5b8L/7bqOpDWTz+tIPxSUBhnwFI296F6vGedjs87JqXDOu5ns3mneW6aFKFjrsmd+HhN6P88HncCAfNizMw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU2PR04MB9000 For client mode, NXP firmware will help association process via host command HOST_CMD_802_11_ASSOCIATE. IEs for 802.11ax should be converted from parameters passed from cfg80211 to TLVs appended to host command HOST_CMD_802_11_ASSOCIATE. For AP mode, IEs for 802.11ax should be converted from parameters passed from cfg80211 to host command HOST_CMD_11AX_CFG. Files in this commit will support 802.11ax features based on above descriptions. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/11ax.c | 388 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/11ax.h | 61 ++++ 2 files changed, 449 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ax.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ax.h diff --git a/drivers/net/wireless/nxp/nxpwifi/11ax.c b/drivers/net/wireless/nxp/nxpwifi/11ax.c new file mode 100644 index 000000000000..923f8100b576 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ax.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: 802.11ax + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "fw.h" +#include "main.h" +#include "11ax.h" + +void nxpwifi_update_11ax_cap(struct nxpwifi_adapter *adapter, + struct hw_spec_extension *hw_he_cap) +{ + struct nxpwifi_private *priv; + struct nxpwifi_ie_types_he_cap *he_cap = NULL; + struct nxpwifi_ie_types_he_cap *user_he_cap = NULL; + u8 header_len = sizeof(struct nxpwifi_ie_types_header); + u16 data_len = le16_to_cpu(hw_he_cap->header.len); + bool he_cap_2g = false; + int i; + + if ((data_len + header_len) > sizeof(adapter->hw_he_cap)) { + nxpwifi_dbg(adapter, ERROR, + "hw_he_cap too big, len=%d\n", + data_len); + return; + } + + he_cap = (struct nxpwifi_ie_types_he_cap *)hw_he_cap; + + if (he_cap->he_phy_cap[0] & + (AX_2G_40MHZ_SUPPORT | AX_2G_20MHZ_SUPPORT)) { + adapter->hw_2g_he_cap_len = data_len + header_len; + memcpy(adapter->hw_2g_he_cap, (u8 *)hw_he_cap, + adapter->hw_2g_he_cap_len); + adapter->fw_bands |= BAND_GAX; + he_cap_2g = true; + nxpwifi_dbg_dump(adapter, CMD_D, "2.4G HE capability IE ", + adapter->hw_2g_he_cap, + adapter->hw_2g_he_cap_len); + } else { + adapter->hw_he_cap_len = data_len + header_len; + memcpy(adapter->hw_he_cap, (u8 *)hw_he_cap, + adapter->hw_he_cap_len); + adapter->fw_bands |= BAND_AAX; + nxpwifi_dbg_dump(adapter, CMD_D, "5G HE capability IE ", + adapter->hw_he_cap, + adapter->hw_he_cap_len); + } + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + if (he_cap_2g) { + priv->user_2g_he_cap_len = adapter->hw_2g_he_cap_len; + memcpy(priv->user_2g_he_cap, adapter->hw_2g_he_cap, + sizeof(adapter->hw_2g_he_cap)); + user_he_cap = (struct nxpwifi_ie_types_he_cap *) + priv->user_2g_he_cap; + } else { + priv->user_he_cap_len = adapter->hw_he_cap_len; + memcpy(priv->user_he_cap, adapter->hw_he_cap, + sizeof(adapter->hw_he_cap)); + user_he_cap = (struct nxpwifi_ie_types_he_cap *) + priv->user_he_cap; + } + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) + user_he_cap->he_mac_cap[0] &= + HE_MAC_CAP_TWT_RESP_SUPPORT; + else + user_he_cap->he_mac_cap[0] &= + HE_MAC_CAP_TWT_REQ_SUPPORT; + } + + adapter->is_hw_11ax_capable = true; +} + +bool nxpwifi_11ax_bandconfig_allowed(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + u16 bss_band = bss_desc->bss_band; + + if (bss_desc->disable_11n) + return false; + + if (bss_band & BAND_G) + return (priv->config_bands & BAND_GAX); + else if (bss_band & BAND_A) + return (priv->config_bands & BAND_AAX); + + return false; +} + +int nxpwifi_cmd_append_11ax_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie_types_he_cap *he_cap = NULL; + struct nxpwifi_ie_types_he_cap *hw_he_cap = NULL; + int ret_len; + u16 rx_nss, tx_nss; + u8 nss; + u16 cfg_value; + u16 hw_value; + + if (!bss_desc->bcn_he_cap) + return -EOPNOTSUPP; + + he_cap = (struct nxpwifi_ie_types_he_cap *)*buffer; + if (bss_desc->bss_band & BAND_A) { + memcpy(*buffer, priv->user_he_cap, priv->user_he_cap_len); + *buffer += priv->user_he_cap_len; + ret_len = priv->user_he_cap_len; + hw_he_cap = (struct nxpwifi_ie_types_he_cap *) + adapter->hw_he_cap; + } else { + memcpy(*buffer, priv->user_2g_he_cap, priv->user_2g_he_cap_len); + *buffer += priv->user_2g_he_cap_len; + ret_len = priv->user_2g_he_cap_len; + hw_he_cap = (struct nxpwifi_ie_types_he_cap *) + adapter->hw_2g_he_cap; + } + + if (bss_desc->bss_band & BAND_A) { + rx_nss = GET_RXMCSSUPP(adapter->user_htstream >> 8); + tx_nss = GET_TXMCSSUPP(adapter->user_htstream >> 8) & 0x0f; + } else { + rx_nss = GET_RXMCSSUPP(adapter->user_htstream); + tx_nss = GET_TXMCSSUPP(adapter->user_htstream) & 0x0f; + } + + for (nss = 1; nss <= 8; nss++) { + cfg_value = nxpwifi_get_he_nss_mcs(he_cap->rx_mcs_80, nss); + hw_value = nxpwifi_get_he_nss_mcs(hw_he_cap->rx_mcs_80, nss); + if (rx_nss != 0 && nss > rx_nss) + cfg_value = NO_NSS_SUPPORT; + if (hw_value == NO_NSS_SUPPORT || + cfg_value == NO_NSS_SUPPORT) + nxpwifi_set_he_nss_mcs(&he_cap->rx_mcs_80, nss, + NO_NSS_SUPPORT); + else + nxpwifi_set_he_nss_mcs(&he_cap->rx_mcs_80, nss, + min(cfg_value, hw_value)); + } + + for (nss = 1; nss <= 8; nss++) { + cfg_value = nxpwifi_get_he_nss_mcs(he_cap->tx_mcs_80, nss); + hw_value = nxpwifi_get_he_nss_mcs(hw_he_cap->tx_mcs_80, nss); + if (tx_nss != 0 && nss > tx_nss) + cfg_value = NO_NSS_SUPPORT; + if (hw_value == NO_NSS_SUPPORT || + cfg_value == NO_NSS_SUPPORT) + nxpwifi_set_he_nss_mcs(&he_cap->tx_mcs_80, nss, + NO_NSS_SUPPORT); + else + nxpwifi_set_he_nss_mcs(&he_cap->tx_mcs_80, nss, + min(cfg_value, hw_value)); + } + + return ret_len; +} + +int nxpwifi_cmd_11ax_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_11ax_he_cfg *ax_cfg) +{ + struct host_cmd_11ax_cfg *he_cfg = &cmd->params.ax_cfg; + u16 cmd_size; + struct nxpwifi_ie_types_header *header; + + cmd->command = cpu_to_le16(HOST_CMD_11AX_CFG); + cmd_size = sizeof(struct host_cmd_11ax_cfg) + S_DS_GEN; + + he_cfg->action = cpu_to_le16(cmd_action); + he_cfg->band_config = ax_cfg->band; + + if (ax_cfg->he_cap_cfg.len && + ax_cfg->he_cap_cfg.ext_id == WLAN_EID_EXT_HE_CAPABILITY) { + header = (struct nxpwifi_ie_types_header *)he_cfg->tlv; + header->type = cpu_to_le16(ax_cfg->he_cap_cfg.id); + header->len = cpu_to_le16(ax_cfg->he_cap_cfg.len); + memcpy(he_cfg->tlv + sizeof(*header), + &ax_cfg->he_cap_cfg.ext_id, + ax_cfg->he_cap_cfg.len); + cmd_size += (sizeof(*header) + ax_cfg->he_cap_cfg.len); + } + + cmd->size = cpu_to_le16(cmd_size); + + return 0; +} + +int nxpwifi_ret_11ax_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + struct nxpwifi_11ax_he_cfg *ax_cfg) +{ + struct host_cmd_11ax_cfg *he_cfg = &resp->params.ax_cfg; + struct nxpwifi_ie_types_header *header; + u16 left_len, tlv_type, tlv_len; + u8 ext_id; + struct nxpwifi_11ax_he_cap_cfg *he_cap = &ax_cfg->he_cap_cfg; + + left_len = resp->size - sizeof(*he_cfg) - S_DS_GEN; + header = (struct nxpwifi_ie_types_header *)he_cfg->tlv; + + while (left_len > sizeof(*header)) { + tlv_type = le16_to_cpu(header->type); + tlv_len = le16_to_cpu(header->len); + + if (tlv_type == TLV_TYPE_EXTENSION_ID) { + ext_id = *((u8 *)header + sizeof(*header) + 1); + if (ext_id == WLAN_EID_EXT_HE_CAPABILITY) { + he_cap->id = tlv_type; + he_cap->len = tlv_len; + memcpy((u8 *)&he_cap->ext_id, + (u8 *)header + sizeof(*header) + 1, + tlv_len); + if (he_cfg->band_config & BIT(1)) { + memcpy(priv->user_he_cap, + (u8 *)header, + sizeof(*header) + tlv_len); + priv->user_he_cap_len = + sizeof(*header) + tlv_len; + } else { + memcpy(priv->user_2g_he_cap, + (u8 *)header, + sizeof(*header) + tlv_len); + priv->user_2g_he_cap_len = + sizeof(*header) + tlv_len; + } + } + } + + left_len -= (sizeof(*header) + tlv_len); + header = (struct nxpwifi_ie_types_header *)((u8 *)header + + sizeof(*header) + + tlv_len); + } + + return 0; +} + +int nxpwifi_cmd_11ax_cmd(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_11ax_cmd_cfg *ax_cmd) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_11ax_cmd *he_cmd = &cmd->params.ax_cmd; + u16 cmd_size; + + cmd->command = cpu_to_le16(HOST_CMD_11AX_CMD); + cmd_size = sizeof(struct host_cmd_11ax_cmd) + S_DS_GEN; + + he_cmd->action = cpu_to_le16(cmd_action); + he_cmd->sub_id = cpu_to_le16(ax_cmd->sub_id); + + switch (ax_cmd->sub_command) { + case NXPWIFI_11AXCMD_SR_SUBID: + struct nxpwifi_11ax_sr_cmd *sr_cmd = + (struct nxpwifi_11ax_sr_cmd *)&ax_cmd->param; + struct nxpwifi_ie_types_data *tlv; + + tlv = (struct nxpwifi_ie_types_data *)he_cmd->val; + tlv->header.type = cpu_to_le16(sr_cmd->type); + tlv->header.len = cpu_to_le16(sr_cmd->len); + memcpy(tlv->data, sr_cmd->param.obss_pd_offset.offset, + sr_cmd->len); + cmd_size += (sizeof(tlv->header) + sr_cmd->len); + break; + case NXPWIFI_11AXCMD_BEAM_SUBID: + struct nxpwifi_11ax_beam_cmd *beam_cmd = + (struct nxpwifi_11ax_beam_cmd *)&ax_cmd->param; + + he_cmd->val[0] = beam_cmd->value; + cmd_size += sizeof(*beam_cmd); + break; + case NXPWIFI_11AXCMD_HTC_SUBID: + struct nxpwifi_11ax_htc_cmd *htc_cmd = + (struct nxpwifi_11ax_htc_cmd *)&ax_cmd->param; + + he_cmd->val[0] = htc_cmd->value; + cmd_size += sizeof(*htc_cmd); + break; + case NXPWIFI_11AXCMD_TXOMI_SUBID: + struct nxpwifi_11ax_txomi_cmd *txmoi_cmd = + (struct nxpwifi_11ax_txomi_cmd *)&ax_cmd->param; + + memcpy(he_cmd->val, &txmoi_cmd->omi, sizeof(*txmoi_cmd)); + cmd_size += sizeof(*txmoi_cmd); + break; + case NXPWIFI_11AXCMD_OBSS_TOLTIME_SUBID: + struct nxpwifi_11ax_toltime_cmd *toltime_cmd = + (struct nxpwifi_11ax_toltime_cmd *)&ax_cmd->param; + + memcpy(he_cmd->val, &toltime_cmd->tol_time, + sizeof(toltime_cmd->tol_time)); + cmd_size += sizeof(*toltime_cmd); + break; + case NXPWIFI_11AXCMD_TXOPRTS_SUBID: + struct nxpwifi_11ax_txop_cmd *txop_cmd = + (struct nxpwifi_11ax_txop_cmd *)&ax_cmd->param; + + memcpy(he_cmd->val, &txop_cmd->rts_thres, + sizeof(txop_cmd->rts_thres)); + cmd_size += sizeof(*txop_cmd); + break; + case NXPWIFI_11AXCMD_SET_BSRP_SUBID: + struct nxpwifi_11ax_set_bsrp_cmd *set_bsrp_cmd = + (struct nxpwifi_11ax_set_bsrp_cmd *)&ax_cmd->param; + + he_cmd->val[0] = set_bsrp_cmd->value; + cmd_size += sizeof(*set_bsrp_cmd); + break; + case NXPWIFI_11AXCMD_LLDE_SUBID: + struct nxpwifi_11ax_llde_cmd *llde_cmd = + (struct nxpwifi_11ax_llde_cmd *)&ax_cmd->param; + + memcpy(he_cmd->val, &llde_cmd->llde, sizeof(*llde_cmd)); + cmd_size += sizeof(*llde_cmd); + break; + default: + nxpwifi_dbg(adapter, ERROR, + "%s: Unknown sub command: %d\n", + __func__, ax_cmd->sub_command); + return -EINVAL; + } + + cmd->size = cpu_to_le16(cmd_size); + + return 0; +} + +int nxpwifi_ret_11ax_cmd(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + struct nxpwifi_11ax_cmd_cfg *ax_cmd) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_11ax_cmd *he_cmd = &resp->params.ax_cmd; + + ax_cmd->sub_id = le16_to_cpu(he_cmd->sub_id); + + switch (ax_cmd->sub_command) { + case NXPWIFI_11AXCMD_SR_SUBID: + struct nxpwifi_ie_types_data *tlv; + + tlv = (struct nxpwifi_ie_types_data *)he_cmd->val; + memcpy(ax_cmd->param.sr_cfg.param.obss_pd_offset.offset, + tlv->data, + ax_cmd->param.sr_cfg.len); + break; + case NXPWIFI_11AXCMD_BEAM_SUBID: + ax_cmd->param.beam_cfg.value = *he_cmd->val; + break; + case NXPWIFI_11AXCMD_HTC_SUBID: + ax_cmd->param.htc_cfg.value = *he_cmd->val; + break; + case NXPWIFI_11AXCMD_TXOMI_SUBID: + memcpy(&ax_cmd->param.txomi_cfg, + he_cmd->val, sizeof(ax_cmd->param.txomi_cfg)); + break; + case NXPWIFI_11AXCMD_OBSS_TOLTIME_SUBID: + memcpy(&ax_cmd->param.toltime_cfg.tol_time, + he_cmd->val, sizeof(ax_cmd->param.toltime_cfg)); + break; + case NXPWIFI_11AXCMD_TXOPRTS_SUBID: + memcpy(&ax_cmd->param.txop_cfg.rts_thres, + he_cmd->val, sizeof(ax_cmd->param.txop_cfg)); + break; + case NXPWIFI_11AXCMD_SET_BSRP_SUBID: + ax_cmd->param.setbsrp_cfg.value = *he_cmd->val; + break; + case NXPWIFI_11AXCMD_LLDE_SUBID: + memcpy(&ax_cmd->param.llde_cfg, + he_cmd->val, sizeof(ax_cmd->param.llde_cfg)); + break; + default: + nxpwifi_dbg(adapter, ERROR, + "%s: Unknown sub command: %d\n", + __func__, ax_cmd->sub_command); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/11ax.h b/drivers/net/wireless/nxp/nxpwifi/11ax.h new file mode 100644 index 000000000000..41e318b39482 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ax.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: 802.11ax + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_11AX_H_ +#define _NXPWIFI_11AX_H_ + +/* device support 2.4G 40MHZ*/ +#define AX_2G_40MHZ_SUPPORT BIT(1) +/* device support 2.4G 242 tone RUs */ +#define AX_2G_20MHZ_SUPPORT BIT(5) + +/* 0 indicates support for HE-MCS 0-7 for n spatial streams + * 1 indicates support for HE-MCS 0-9 for n spatial streams + * 2 indicates support for HE-MCS 0-11 for n spatial streams + * 3 indicates that n spatial streams is not supported for HE PPDUs + */ +static inline u16 +nxpwifi_get_he_nss_mcs(__le16 mcs_map_set, int nss) { + return ((le16_to_cpu(mcs_map_set) >> (2 * (nss - 1))) & 0x3); +} + +static inline void +nxpwifi_set_he_nss_mcs(__le16 *mcs_map_set, int nss, int value) { + u16 temp; + + temp = le16_to_cpu(*mcs_map_set); + temp |= ((value & 0x3) << (2 * (nss - 1))); + *mcs_map_set = cpu_to_le16(temp); +} + +void nxpwifi_update_11ax_cap(struct nxpwifi_adapter *adapter, + struct hw_spec_extension *hw_he_cap); + +bool nxpwifi_11ax_bandconfig_allowed(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc); + +int nxpwifi_cmd_append_11ax_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer); + +int nxpwifi_cmd_11ax_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_11ax_he_cfg *ax_cfg); + +int nxpwifi_ret_11ax_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + struct nxpwifi_11ax_he_cfg *ax_cfg); + +int nxpwifi_cmd_11ax_cmd(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_11ax_cmd_cfg *ax_cmd); + +int nxpwifi_ret_11ax_cmd(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + struct nxpwifi_11ax_cmd_cfg *ax_cmd); + +#endif /* _NXPWIFI_11AX_H_ */ From patchwork Mon Sep 30 06:36:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815420 X-Patchwork-Delegate: kvalo@adurom.com Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010001.outbound.protection.outlook.com [52.101.69.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1036814EC7E; Mon, 30 Sep 2024 06:37:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.1 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678267; cv=fail; b=L9F4TNxkORjnqwZcIWLBw1Tp4Tl8RO8U+3/7g96S1745Kvo7+9uQbHvB/LkDwxcatDpXwXnjvl7WjAasa6PfneewPArYB+4I73MvfzD2LOBkwUfFL7IMRhDIjQI1qs94FCQoF4B4xYUXmbhD0+WrAWT/4/dh4N7xAa//VVEi1Q0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678267; c=relaxed/simple; bh=QcUxkzRP7Sgc/N/v9OJLSLyqKTfqjDvXC/IJONJq0+w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=RD2bRd4YyV6v65KEfvxsLYYSqaAAVYSgJiGKEtc4MpjaO6l0LV9TJl4zC4xQpkYij+8uhTZIynxFEfTVPb5VKyCiQMnxj9xdA6EDkXvr+vkFE1mBNJRO3dTiW4R3EsoVFpwYKHv2spEpo9X47DT96HSMaI+l1BJ8crtq4cCdXVg= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=R8pqDarU; arc=fail smtp.client-ip=52.101.69.1 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="R8pqDarU" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=HNtiZJiqSoCuJwaP0FPiUhkhz/wbVxVQAmrv6Wm0dnoD4QL0OqdOYaRSfNWTkCaXqIQvxs7NHMAoY4na0M3IDqPTEZPuQQ9RxxcxlQs5sGDY70239mM4jwmy6hwdTUObAbmKdsawAAxDp30vTHxdX41hn9aiG2P9eZ18IPjbiVLzEY3OqSQIDMjbrHTJgQalWFjiI8x405DagAuNZPoWaQahpRWftLCW4lDIwto+YYkgAxKQxnvIjR8WJaT5JiHBcJs0HUAxj8Wf8qxqGXtYtkasZuJ8qtDzGBJoPoa9qNQKggM4pLDR5M9v44Wy9eP7RbIvgRZyrrQ+AxuyQ9BE4Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=FULkcCzR6T559FRIRY0guR7B4psEiUwfA4Ly2P/z0O0=; b=XV1ZUevgtcf0EOw139XnkqyZPW8cXfTB/vUzn47sTanXH2CYDoKRIPVC7UPkblhOO+7ZXaqDpJg0hx5CjBlQDpFiZkwrmymZerbTUQ3G/BCc5T2/oT4acy2WGbHKC77LK673JeWvysexyXMhXmPmLNTyEMrHG/4Wlhuv/LUJuA3fxn3MlxwGggtxVh9b/xrH09aDLDhJrirNn+pKGO5FcJsCGGcGppSRHdprySiO9etXLcG6eRqt/GQYnuqSxC17OfgC5q4AXfXX92iqRYgGhIQ1Mf1IVknt2z8JfYjf+m7Yum/PflyDrEVQkCLJSLJDCeQ1xGItbUvGRYbX5eUhqw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=FULkcCzR6T559FRIRY0guR7B4psEiUwfA4Ly2P/z0O0=; b=R8pqDarU5UJ3O6pxApDgG7YZmPMg6qlGnT2Z4bCMW8THQ2/mcJTCT2mXxb5D2FyTZx1YicyFLxL9UU6noghMtmXsIYx6VBd3EX6mzftGjajLw3od9zQGw6OxmoF7y3/O7a7OyC80AL6NfZ4L8We7DDHyBfChA2bTwh6cTAjHwnzUk2VnQSQ/OTGFl/gaHPwuUVNJwBLxwprZdtdjaESCPlx+aIX5oMuo3O6LNU9iWuUWg/6zI5xD3ueHXlTsHKM4ZpHyqALQCB84BeTM7c1uUkXLmw/jCoGf471JnuLD1ipjYDz4MRXvjXr5CmhrUCD67TzRE9M9WpL+R5NwEWc17Q== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by DU2PR04MB9000.eurprd04.prod.outlook.com (2603:10a6:10:2e3::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7918.25; Mon, 30 Sep 2024 06:37:41 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:41 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 04/22] wifi: nxpwifi: add 802.11h file Date: Mon, 30 Sep 2024 14:36:43 +0800 Message-Id: <20240930063701.2566520-5-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|DU2PR04MB9000:EE_ X-MS-Office365-Filtering-Correlation-Id: 9af8729f-3cc3-4f19-e438-08dce11a61db X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: EgEvy1WSvV1+lkEkNNfah9+axoWTaHc4Mk2ypkiIVkNIlb1S1REQfVHlaEZz3YtN/Q/gOTdt5CZsVPuu8gi8bqq3IhSt5OpCkru+CIU/oHYeYd5euCp3qsug9WIOWktsaHDfT8O8BHh4GIUhL9gQf4qEXlLFxCs+S3bX8gs6YkUhYwAAMCHcTYfgll/SP3nZ0bcgp0rYcTXrg9qbYEuJ30JIn/eEiZYUnlJ6TLK6HF3198/pv1LLG4BFOrltqdzyfbRGd89rQTTFyFiZgBYJ8uXh9Mjfmk5fFbs20Y1U9wok8clUKg9YatcrT6xLQo5gyfaPZ3vKoWD6syYWa4qmzEfZvkpOPyJeL0SiiBpnRV2egSJkX51HJWCSeTE3CLpEkEt7KyMuSZ/fu5+qvh5cfPicCAEwtn/inewTnUdMfS+GQ7QZNUjzapkoTDCdoWkfG/MXVFD/ob8xIl6LnAtLa7qJ1OzDkEVM7YzxaOE8qiJGDFO0NXxNBNzXA84tANExgX8+0CBTrHtY60Djr1YOdl3ZOF8jqnrHfEbiIM4D/jrKFux+NG4tYTdakPbZZKn0ucFl5J3ImaJnwFdRKI8ytBRmvqSNruII0V74K1JH9F0Xrb5efZGI7IW130Ze6PQ1mekn/6+VaWAiq1GNYOy9ZwWBxi8PPgjSbWJ0EtqgyZBqkN8+A3u66g52mOndbsSkPQHqPdOp2sC03D4ufMVTKiJs5QKygwFPhaEFG8auBFpIq4/Rb1tDTLCAuiSZP77i0y5gXIuKxKuD7tWFV2r3pBnvNgdoTpoIHcuy8UD7SS4w1XWVxfj5Te3by8zJ5PfRJupmK6+lJXaS9w4XRFdpIc+27AxPZNbT7y3XGNSJBTvUfepQEI4cKrX2nuuVJN7nB5r2hgeGjuyagvna8IHVHk2bGSn/leYbinxUAkXBtW8qnjLNjr7qiGdQ/tYr5c1BkE3r0Z45hD9OhtVztApUVQNM4ePuxOClLcV2u6PStmMdAjR/vZhWD0WtMHvPpMomtVqFitCBBp96eRdiZuWA8CDGA0cMqlXeb6dowRjj71RyGCtGh6wCW+r5Ciml7koxTKzB4j5UC1i46hdVb1SSb3o8H663fhs4Nto458Zfs4SnfT8WKX7H0k7MaswFcUlIyApO5m+HqH5cs/iedFGI7ZeYnLqlVCP7DkjYCh41xNZVj880GmzY9+hFv3YgN73Gom4kSIGg1gGaXzlZ4v6FJKPR3VmSa8bDXyh2FaW6OsR/9myzOzf6HpaJijV1mEEldUOhwEFulyRNyu/SIoW9COElsoV9ihmEN8urFyxMTn7zSSeDe0dMnXamX9j0pCF7NkwtBYqGY6h5SqNbULgM/A== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: hejt2UrWY33GEB6UVC7n91T5Vd4+Ep61ddBuqRdFwrJxFtaXlsYjsMevyyJqK56HdOlxvJax8b8T4lxnWX1O0HZD5YLigHbYfwNlKbZtUA2jd7CpRd8P/nGHseplrQ+yj7CJzI+MJcCoyvvJRnjkTo4e/DMK1v908zxIDm2FFbdPrjCsJ135E3Rf6xuzkY0L4xpyJnqLO4T4T9wLDvO9quqR7CuT+J1TvZrYonHdMpZG2eFW40d+PJSpuobELnENQH7zsEKY/67ujvttUiMn8G4FjaK8e73PfZdA10l736mIiPSOzwX4vSV8qaxdARkY4MrdtW21EWwmvwM57GgucblqJUaXUPK13pnWPUs/eWt+NPyDMcm0CT7SBY29KRCEKdFHrgtMdlS8i8I8I5ef7w3Mf0/3sjpWq0M/TCEki+C7d8j635b3FLUyxxP5CiQHwoqwdwF35zEnrOUglF/UR8PVTQYlF0Fg+DMDD7G8TMoFfzyC/P69k6y2eoWrd57hl3kYnQB8HdfmxCxqOj2AksHlQ+ILO0NjRv//QV0EgV7UZV3of+n+tmoDElkuV1nR4rp0QupYxxBN/rHDUx9oBFeI8VakP3XgVSzmVFURfxNHcFNvHUuNpZ+DvAUt5u6yQOJ3rpdSWfksyu8cfELCtONsiLUzQNQPGFOmKuW7yStGGP8aVTLwogLyzRZVlg/FDFM0TTmZ74CAlCQCXKHsaYSkO6inoKSvj3AYl40xFy61xOR5IidDCOvrUo+KLSC/SErJud1GiiubkBq6jYZv0dh7ibSamKuPgUlKX1J71tmRwW4bw1jKgvXixFTi+ThbdJT6MQPvpNj2HrWb0jElvhaf8y7n6+lrEAQyRxoax/Xnat7t7ucdmE0OhsaiUgzN8DA+yJ1YWvS3sksHCalJ1uNbKOjTfN84mllOL1bu9T+GT0kulnQ94UZobdMZvauvZGayCz1+SLMV29MjmjjDC6p6Bzv4ALewjbPu3HPgZKXwCjarYyHgVPMCYwPBO1DWXAnp7twugdXvIBhBkq1TvBZvgJjlErgI52rg5U9Eec7KLqXIuQMNz/MavBavyUcS5A4QcHlFXywEPFvL+kCtNB7w8rJl7avF3AVPIUHuHMyUgkjlzZ3QC0RuB9WMR8KR8vvxPMadtjay8nO0S4g9TrMN3WU+WoK1K4xqBd7IRqVcBKsXNToCwVnGJ3DaZV41OPn+IQP4iI13tcl58QTlRSUNELzioCOmLDXY6HPRLwu0FWkRg1VQqsAr5SWFPMwV64nRKbyiNvlJJApTWgrzi3wWpeHFBEAaOsmR9UtuZkvKJvGl4Eu+FrUStijFXda7h/esYTdQ5/PfuCHU6ISRKAhIA+37FqDirjABKZiRmqSDy9ojAbRiAR1h6c7crvU128pa+PsDJcAU4hpS0KHDJR44NB0g2byd/+uvGpqv0FkHvqhMNvDPbS3atuVgbUB1ayhE7WH8OAAbBW1nju/yTfuS51NXWKtC/1GlQP3ZW6x/H/XtB5q4z+ZFpDVNbSq4XWGH9bU4qyOanUeWY3XnxulJYNmidkIi9Za2x7hPo1xOMVzNs7LzEE+wW2HICYeF X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9af8729f-3cc3-4f19-e438-08dce11a61db X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:41.0074 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: +IMebKBKNfOsJnLbpcjWerdVCfsYcxkBbN5mtYtPA8raMXDPIHytNuyYY2t14WUFExv/on5DzL/mQaTsY94eHA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU2PR04MB9000 File 802.11h is used to support 802.11h features (DFS and TPC). Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/11h.c | 432 +++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/11h.c diff --git a/drivers/net/wireless/nxp/nxpwifi/11h.c b/drivers/net/wireless/nxp/nxpwifi/11h.c new file mode 100644 index 000000000000..f2830d136941 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11h.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: 802.11h + * + * Copyright 2011-2024 NXP + */ + +#include "main.h" +#include "cmdevt.h" +#include "fw.h" +#include "cfg80211.h" + +void nxpwifi_init_11h_params(struct nxpwifi_private *priv) +{ + priv->state_11h.is_11h_enabled = true; + priv->state_11h.is_11h_active = false; +} + +inline int nxpwifi_is_11h_active(struct nxpwifi_private *priv) +{ + return priv->state_11h.is_11h_active; +} + +/* This function appends 11h info to a buffer while joining an + * infrastructure BSS + */ +static void +nxpwifi_11h_process_infra_join(struct nxpwifi_private *priv, u8 **buffer, + struct nxpwifi_bssdescriptor *bss_desc) +{ + struct nxpwifi_ie_types_header *ie_header; + struct nxpwifi_ie_types_pwr_capability *cap; + struct nxpwifi_ie_types_local_pwr_constraint *constraint; + struct ieee80211_supported_band *sband; + u8 radio_type; + int i; + + if (!buffer || !(*buffer)) + return; + + radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + sband = priv->wdev.wiphy->bands[radio_type]; + + cap = (struct nxpwifi_ie_types_pwr_capability *)*buffer; + cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); + cap->header.len = cpu_to_le16(2); + cap->min_pwr = 0; + cap->max_pwr = 0; + *buffer += sizeof(*cap); + + constraint = (struct nxpwifi_ie_types_local_pwr_constraint *)*buffer; + constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); + constraint->header.len = cpu_to_le16(2); + constraint->chan = bss_desc->channel; + constraint->constraint = bss_desc->local_constraint; + *buffer += sizeof(*constraint); + + ie_header = (struct nxpwifi_ie_types_header *)*buffer; + ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); + *buffer += sizeof(*ie_header); + *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; + *(*buffer)++ = 2 * sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + u32 center_freq; + + center_freq = sband->channels[i].center_freq; + *(*buffer)++ = ieee80211_frequency_to_channel(center_freq); + *(*buffer)++ = 1; /* one channel in the subband */ + } +} + +/* Enable or disable the 11h extensions in the firmware */ +int nxpwifi_11h_activate(struct nxpwifi_private *priv, bool flag) +{ + u32 enable = flag; + + /* enable master mode radar detection on AP interface */ + if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) && enable) + enable |= NXPWIFI_MASTER_RADAR_DET_MASK; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_SET, DOT11H_I, &enable, true); +} + +/* This functions processes TLV buffer for a pending BSS Join command. + * + * Activate 11h functionality in the firmware if the spectrum management + * capability bit is found in the network we are joining. Also, necessary + * TLVs are set based on requested network's 11h capability. + */ +void nxpwifi_11h_process_join(struct nxpwifi_private *priv, u8 **buffer, + struct nxpwifi_bssdescriptor *bss_desc) +{ + if (bss_desc->sensed_11h) { + /* Activate 11h functions in firmware, turns on capability + * bit + */ + nxpwifi_11h_activate(priv, true); + priv->state_11h.is_11h_active = true; + bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; + nxpwifi_11h_process_infra_join(priv, buffer, bss_desc); + } else { + /* Deactivate 11h functions in the firmware */ + nxpwifi_11h_activate(priv, false); + priv->state_11h.is_11h_active = false; + bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; + } +} + +/* This is DFS CAC work function. + * This delayed work emits CAC finished event for cfg80211 if + * CAC was started earlier. + */ +void nxpwifi_dfs_cac_work(struct work_struct *work) +{ + struct cfg80211_chan_def chandef; + struct delayed_work *delayed_work = to_delayed_work(work); + struct nxpwifi_private *priv = container_of(delayed_work, + struct nxpwifi_private, + dfs_cac_work); + + chandef = priv->dfs_chandef; + if (priv->wdev.links[0].cac_started) { + nxpwifi_dbg(priv->adapter, MSG, + "CAC timer finished; No radar detected\n"); + cfg80211_cac_event(priv->netdev, &chandef, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL, 0); + } +} + +static u8 nxpwifi_get_channel_2_offset(int chan) +{ + u8 chan2_offset = SEC_CHAN_NONE; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + case 165: + case 173: + chan2_offset = SEC_CHAN_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + case 169: + case 177: + chan2_offset = SEC_CHAN_BELOW; + break; + } + + return chan2_offset; +} + +static void nxpwifi_convert_chan_to_band_cfg(u8 *band_cfg, + struct cfg80211_chan_def *chan_def) +{ + u8 chan_band = 0, chan_width = 0, chan2_offset = 0; + + switch (chan_def->chan->band) { + case NL80211_BAND_2GHZ: + chan_band = BAND_2GHZ; + break; + case NL80211_BAND_5GHZ: + chan_band = BAND_5GHZ; + break; + default: + break; + } + + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + chan_width = CHAN_BW_20MHZ; + break; + case NL80211_CHAN_WIDTH_40: + chan_width = CHAN_BW_40MHZ; + if (chan_def->center_freq1 > chan_def->chan->center_freq) + chan2_offset = SEC_CHAN_ABOVE; + else + chan2_offset = SEC_CHAN_BELOW; + break; + case NL80211_CHAN_WIDTH_80: + chan2_offset = + nxpwifi_get_channel_2_offset(chan_def->chan->hw_value); + chan_width = CHAN_BW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + default: + break; + } + + *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) & + BAND_CFG_CHAN2_OFFSET_MASK) | + ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) & + BAND_CFG_CHAN_WIDTH_MASK) | + ((chan_band << BAND_CFG_CHAN_BAND_SHIFT_BIT) & + BAND_CFG_CHAN_BAND_MASK); +} + +/* This function prepares channel report request command to FW for + * starting radar detection. + */ +int nxpwifi_cmd_issue_chan_report_request(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; + struct nxpwifi_radar_params *radar_params = (void *)data_buf; + u16 size; + + cmd->command = cpu_to_le16(HOST_CMD_CHAN_REPORT_REQUEST); + size = S_DS_GEN; + + cr_req->chan_desc.start_freq = cpu_to_le16(NXPWIFI_A_BAND_START_FREQ); + nxpwifi_convert_chan_to_band_cfg(&cr_req->chan_desc.band_cfg, + radar_params->chandef); + cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; + cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); + size += sizeof(*cr_req); + + if (radar_params->cac_time_ms) { + struct nxpwifi_ie_types_chan_rpt_data *rpt; + + rpt = (struct nxpwifi_ie_types_chan_rpt_data *)((u8 *)cmd + size); + rpt->header.type = cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC); + rpt->header.len = cpu_to_le16(sizeof(u8)); + rpt->meas_rpt_map = 1 << MEAS_RPT_MAP_RADAR_SHIFT_BIT; + size += sizeof(*rpt); + + nxpwifi_dbg(priv->adapter, MSG, + "11h: issuing DFS Radar check for channel=%d\n", + radar_params->chandef->chan->hw_value); + } else { + nxpwifi_dbg(priv->adapter, MSG, "cancelling CAC\n"); + } + + cmd->size = cpu_to_le16(size); + + return 0; +} + +int nxpwifi_stop_radar_detection(struct nxpwifi_private *priv, + struct cfg80211_chan_def *chandef) +{ + struct nxpwifi_radar_params radar_params; + + memset(&radar_params, 0, sizeof(struct nxpwifi_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = 0; + + return nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST, + HOST_ACT_GEN_SET, 0, &radar_params, true); +} + +/* This function is to abort ongoing CAC upon stopping AP operations + * or during unload. + */ +void nxpwifi_abort_cac(struct nxpwifi_private *priv) +{ + if (priv->wdev.links[0].cac_started) { + if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef)) + nxpwifi_dbg(priv->adapter, ERROR, + "failed to stop CAC in FW\n"); + nxpwifi_dbg(priv->adapter, MSG, + "Aborting delayed work for CAC.\n"); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); + } +} + +/* This function handles channel report event from FW during CAC period. + * If radar is detected during CAC, driver indicates the same to cfg80211 + * and also cancels ongoing delayed work. + */ +int nxpwifi_11h_handle_chanrpt_ready(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct host_cmd_ds_chan_rpt_event *rpt_event; + struct nxpwifi_ie_types_chan_rpt_data *rpt; + u16 event_len, tlv_len; + + rpt_event = (void *)(skb->data + sizeof(u32)); + event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event) + + sizeof(u32)); + + if (le32_to_cpu(rpt_event->result) != HOST_RESULT_OK) { + nxpwifi_dbg(priv->adapter, ERROR, + "Error in channel report event\n"); + return -EINVAL; + } + + while (event_len >= sizeof(struct nxpwifi_ie_types_header)) { + rpt = (void *)&rpt_event->tlvbuf; + tlv_len = le16_to_cpu(rpt->header.len); + + switch (le16_to_cpu(rpt->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + if (rpt->meas_rpt_map & MEAS_RPT_MAP_RADAR_MASK) { + nxpwifi_dbg(priv->adapter, MSG, + "RADAR Detected on channel %d!\n", + priv->dfs_chandef.chan->hw_value); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, + &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL, 0); + cfg80211_radar_event(priv->adapter->wiphy, + &priv->dfs_chandef, + GFP_KERNEL); + } + break; + default: + break; + } + + event_len -= (tlv_len + sizeof(rpt->header)); + } + + return 0; +} + +/* Handler for radar detected event from FW.*/ +int nxpwifi_11h_handle_radar_detected(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_radar_det_event *rdr_event; + + rdr_event = (void *)(skb->data + sizeof(u32)); + + nxpwifi_dbg(priv->adapter, MSG, + "radar detected; indicating kernel\n"); + if (priv->wdev.links[0].cac_started) { + if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef)) + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to stop CAC in FW\n"); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); + } + cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, + GFP_KERNEL); + nxpwifi_dbg(priv->adapter, MSG, "regdomain: %d\n", + rdr_event->reg_domain); + nxpwifi_dbg(priv->adapter, MSG, "radar detection type: %d\n", + rdr_event->det_type); + + return 0; +} + +/* This is work function for channel switch handling. + * This function takes care of updating new channel definitin to + * bss config structure, restart AP and indicate channel switch success + * to cfg80211. + */ +void nxpwifi_dfs_chan_sw_work(struct work_struct *work) +{ + struct nxpwifi_uap_bss_param *bss_cfg; + struct delayed_work *delayed_work = to_delayed_work(work); + struct nxpwifi_private *priv = container_of(delayed_work, + struct nxpwifi_private, + dfs_chan_sw_work); + struct nxpwifi_adapter *adapter = priv->adapter; + + if (nxpwifi_del_mgmt_ies(priv)) + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + bss_cfg = &priv->bss_cfg; + if (!bss_cfg->beacon_period) { + nxpwifi_dbg(adapter, ERROR, + "channel switch: AP already stopped\n"); + return; + } + + if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP, + HOST_ACT_GEN_SET, 0, NULL, true)) { + nxpwifi_dbg(adapter, ERROR, + "channel switch: Failed to stop the BSS\n"); + return; + } + + if (nxpwifi_cfg80211_change_beacon_data(adapter->wiphy, + priv->netdev, + &priv->beacon_after)) { + nxpwifi_dbg(adapter, ERROR, + "channel switch: Failed to set beacon\n"); + return; + } + + nxpwifi_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); + + if (nxpwifi_config_start_uap(priv, bss_cfg)) { + nxpwifi_dbg(adapter, ERROR, + "Failed to start AP after channel switch\n"); + return; + } + + nxpwifi_dbg(adapter, MSG, + "indicating channel switch completion to kernel\n"); + wiphy_lock(priv->wdev.wiphy); + cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0); + wiphy_unlock(priv->wdev.wiphy); + + if (priv->uap_stop_tx) { + netif_carrier_on(priv->netdev); + nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter); + priv->uap_stop_tx = false; + } +} From patchwork Mon Sep 30 06:36:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815421 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 289AA166F00; Mon, 30 Sep 2024 06:37:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678275; cv=fail; b=qymQTbOFzj0eiHTkCR2WTTqxeU6XxZ6Z2TRftcMos1DZ0bDqGsOpd1ST7NboONcs/NAzW3yhTSxiEZ2i30SQRu5zzofo9u8dy+BWzER5bi/iaIfXtVeGa1TdMWXh/GxdEpoez0MmxzIQfl8JFgP0fjJbjMQHEd7Omnm5lXKIpm4= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678275; c=relaxed/simple; bh=o6cwCN1VoviECzVnG8Kh4NxnKM2dBHiM9kb00MsUshA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=LjdrlHFAvPJCTBgNu+VoWp9CWrQLlpXQe3jw0iv5fHjSfvRQNVOm/XHVIOh1JacVWptQkpou3DuhiYGKMJmfGtvCnFNWpNYZj08tJC39lWLrbqAHQ6+P/8+eKYDErvoPN8ghAych6k155BctP5siLz0CTlGT/q/+hLVkV6UEMoU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=V2ryYGkD; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="V2ryYGkD" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=KYxZE/Fm5l2Y/UjoM7OEV3SAyzc5qx+6OkvZR3d9eDnpghnBFi6UKWD7H1+fd7LpoLOij1CGDYPnx96VIz57dxmQ3AE4dKZBV4BUbMT6yoCQmJxFDkkgHr+obalv8Kmrg9Ud1PygMZ8Urd/azABWqxRBXsYMQSSTWsr4ViSLb6cELjzNgcyF1DXhXEwNy3pJuPKXH4qMVZyutenZzjjQMcweBAaXp+3VFnPD8WsDmD76BLZa7fX0v/SxUkRY6uE2xor6ikgcafUnaZSeQmg9zCpbt8YAZqfyDxbkIouFWovmcPAdHKjGBSb3pqhjSHm9S1ZIPwMkTMLIAQHGg5sxtw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=bgr4c3wg0+HYybyImLfRlPKnXaqDZ05ORN9eMJH5Yi4=; b=hGIPWvPrNmVFDDvAXAFEKU5OT8QS5HkKspOSOZvchUEN+UR9x6c9pqhFz6iSgdBSXeXuVrElLl1p7ruW1+8wewnvZKiT+SfXaSMZ+bNixrc9q8WiP7uAoisxAebL6VNpnz5Xn1BhmwPR9wR13HXRP+tJYAlfhm39Y49Y0wiC4ozRtWb4uVJQZK8USEQq8t7krRdGv8kZk8jNzrYLU3Ys9dmxLWCb6NVzL7lyR5vxXWkXJU6VfHILqfYKd1BB8hC1mfItxFb7AEh59dS6d+rWC3SLbsgj3iU0mMw/HUD5mdOZxGrv2yv4E3kCKcKcCXbM7YzcL4dmXrRfA16dZruQbA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=bgr4c3wg0+HYybyImLfRlPKnXaqDZ05ORN9eMJH5Yi4=; b=V2ryYGkDfMX0uM7PTL8DqYTp6G8TvIaTp33PkXFVWKV1ekhQdhF4z7zQZFxg898E73CJ+DTNuUw0zIC3AaXmTVK8IK7c/jHr+68XSpnyGFD0Tz9jOOEwUGrWMy/HVRzUdt8YEjHQbJyjV4a16NadT9uQxfNrlulyU38LW1YWiax7n9PEC7iZ+7wBftAx4CVrV5+MQprLMeY1E5ARNm4XvsR5GNdMJiJQov1UDATEdVfbbK6JK/lkN+hS8VpMXcPDpz4E/OS9Xr+DeRXx/ESmURfyreP8keUIa1Ms0z6Wwf8eiXwXrauacY6OZ/+fG1Sfxh3pONsjZYOEjqkRIGsaAQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:37:47 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:43 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 05/22] wifi: nxpwifi: add WMM files Date: Mon, 30 Sep 2024 14:36:44 +0800 Message-Id: <20240930063701.2566520-6-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 5aa3d224-b61c-4385-6cd1-08dce11a6386 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: +8DKWkQd2kOHH3/8rcuWytPsbWI8UJO2qtzDe/MO4j4WyCDq4WomPq/Nn++fCRg9PvQkd2cHC/FGJqBruF8sdqJk9OIV98OaWBKX9puCglZ4U++W5Gt6nS6K7qVTJQbM+l1PnSDSCKPKaLL2Iu/T1NATvfrM0u5vuC1ZHq3aUWc6miAzuRS8uQk8QYmEgGtuaX8D/lrFXe/7D/3Jldzl71vnLivqF+oi+A2zRPH0nC+zJFODOMI40F6esd4vyrVs55pYBxxP3OT1bUnykdPWhR5OoiNZCSuR8aQfPWDBayaK+KgIviDTMilTzdj58XczqoEp7U9kYm+krhFhiYOaFszhdZsTtsEebjp7UNNwBMYZ4Lr/obFtAB2vfaUeHM+heAWgB8jZZLmPrE/uq2q1giTdAIG37zaMlf0V4MRr84v0CH+4DB/T6xb4FCkUKzf6FRISPKpNLIvS6sUr3uTxkJ3E+eLyxbSxQ1YRHEqfPwsVhorPYZbdfWrmkQhNY2U8GWeZk8m35hEe0QazEs+zuylXMtAudYBiIt/ccMj3wXuEqbA0mFIQpVQMg5PGQwAWAtXQGP4Ic/CRL7P2XoLmkuzKoO1gTyB5X7/2hBhBP10nChdaM3U7rO7fXSB7nirJmSFlPD3EfwNYa+gOFu/JqkA2N4sAO0G6ht9eWs7ZAim7NsieqtzyVYN8CPbRwh6QBvIkE5B9Mt5Mrt21sVn9z8MYN42dPXb8OB453RzUsYzZb79bqRqHtg1eZwPvfS4r/gIuccZFKCzCKn9Neaxz26mDz/znI82IMdR3zbxyUP3wbHSisL1VhX9VlJ3ALqnCmKnQ6in+FLYTXtoo38l00U09aWmi+du+4x0lf7A17e00K02cvr6pVwHhAXONRLRbYUimOC0DBJUwd+KqP5G8NtpypWcKZjYFfkZ2kVjUI/NDhjlMtqtSyBtEsXpD0OFRoEhoFzSL2yrDiPpYQ109d5A3+Mo2W1t5ojV99uVFFa7h98tXqmqnab/LojUxiu0jKakAR/Ue3NuUZtNXUpNR91KdQo19yjhr4f8MD6uLw0K/NOpBRUUQWfy9Zu3BjkPMG2hsGuMHLzfrgWlzHNAF8UxbNWfCi6/Br8OfIO5FtI1A6iCJkX+mOuVwrKB3bhRD4Dby9Lb4b61ziJAvKeH0mW3NWpam1kROwy7Y2HqDUfcjf9uaasgmFH9hEVKpLNmsShFLNr+/wB3/VrYLFvG16vAQd8eemUEbEZBm8JlIyhpts7bZiSXB5/zK+vB1XxjlZUUanz7kEz9+arPkF0dzanMD3lWqq1Gon5LTXfnFeKSsUDT7nopuUlThJ+l90vdkBKehteYWunFHpCP0WBaVfy6UCsIHXcMiVTRltrZ33kzvZDruW3LWIBmEqjq03jfIuIMdmvZF9pFuxtVwnPOYNg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: q8szM+qh7BvxaUHwiydKWDoNJek6XVLYse9C+5n9EnLZeAQiqq64liW7pa346gqEOUSxf3pDCHMJDoIZioZ7Wqb6za1beGjkxZ10kptnUc774Or+0Ouc1b8eaf4Mc/J9s47J27Qa0i8evJWU69pcilasF7ifKGwK5oVSpt6hXzwck7rY/74DIQmLf+fykfLR8zN/Vwe95VPk29lXnFLMEj64fX0JKTqJGT6FA698jOn2jM6g9++hE0+EIRyHwwPoaqepjm5G3dHhtvrjjFjxPgidACjHnb6UZHVMS7GKmVkzKz+RBMubqid+2uv8W8gbyGjm04m5TxPMiHxZrlsnX2KI7F7M9Ex8bmDrlwLIAf9yJ+/WXui+4Q7179H5gDJpfyALtvGY1fVtSsm1ndJXP2NyR79VdsrAk3cxY85HhMS9AC8I6t2Ta3JEatE1HKBD0gnJtUgzWhL9aSviZP9X0n9LSKchM2dcXcB5U70e5pD7PiKt09/6uS3D39YV78dSLKcOn1e8b/zqqn9Oy2YWkGDrWY7XoLNtZQy9LrsflUzjIalIRVL2yc5Sey7Ii9f/5wG82Mc13Tl/lpvbRZs4/vVFniMzGeVdrUsXMyZco/Td8kmbb6NKRcgvJrk3XeEBWvo8n00VJNDpTbHV9W2eyR7Lmai2wsfEK+ANQiFt/yoQv4p0WToHJxTKH/gHCR4zSoUEeX7ExVEskeaoUQhgoQ+HBw1gn4syA7CNtZsOndY00+AG5792WhiqHIYCbzjoEFuu+mFsb5lTdSAlx3VGDOlYFHEdS6brzGz6usHzCcg2a4twupDb6vZr3X1SYD/DljZcTWffz1G2jVDKANjb9ceJZEmmIkoAH7iXZA25PPhqX/zN5Z4+ISAsnb3hDe7aBYccF1Sx5aNv5IckccoZs8shfbDE/02RNLabvBbCjiDbx+VKeL7Z/Xrgl1OZAs1qnaBxV6nvJmnVTzlNke2hDBt+esQdfqOLrd59kQV4KXhzQkOOtrf8onDr/DY+HESDI0GRbBvvcMSUp2TCp5FNR5jxuRp7O/TE7F+dGjeANBRD2O1xvVj7jwymtNyiJCm3EaNbKciqZzFT9I9yDwTjSQK52+qn3pIzQWSqf3hgfdHRmhG1ZbeDmlHhgWrk7ptCdaoZxaDTACoaKCcWlRBU3FTD7m3kJ40jZnu7Qkmc/1QEA2q2gbdyC++OGgsEdh4u/aYcqil1QnWE/vwwSaTBae/pkzFQy4fSNQ7Dr+FcuGmfbnLevkbi5RQf3J5yjV4UTH/bKP3dwfvdpK9qu17r7jD31GCkN+fRwCCg6iBCX/gg9tbJVg3SyQaR3xFWT+DMLPfF/hgXTY+EBrri/fZeXLeXSIIpKwfCHf982uCXSyMZcNOp3N8QU6Wwkt1MoDuvA7QE+W+QqGRmQE0jyENJdimb8c2WmmOMPPZxrDmApj8kE27dmWYrL88i4GYIGNx1/X//5Ytvr3XTfF7E9pIKfLZ7z7EAmpEDlDSYg/gDmf6T/7406geiCRoxsAzCMJ4QYoB9S+hsGfe6BqrQoyzHZesEQxWTw3Gnah1UTs54FwF/f5xbWrl30zBXPhxQ3cuA X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 5aa3d224-b61c-4385-6cd1-08dce11a6386 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:43.8487 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: EjgfmRNqS7mhUepJ5N8/GWpns/q5vUtGpnWpob4Rc1rK4InM63swnfBzrg1trHxIiLPvlUjTX/qQzfLrruvACg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Files in this commit is used to support WMM feature. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/wmm.c | 1369 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/wmm.h | 78 ++ 2 files changed, 1447 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/wmm.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/wmm.h diff --git a/drivers/net/wireless/nxp/nxpwifi/wmm.c b/drivers/net/wireless/nxp/nxpwifi/wmm.c new file mode 100644 index 000000000000..a728c422194a --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/wmm.c @@ -0,0 +1,1369 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: WMM + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* Maximum value FW can accept for driver delay in packet transmission */ +#define DRV_PKT_DELAY_TO_FW_MAX 512 + +#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 + +#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 + +/* Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +static bool disable_tx_amsdu; + +/* This table inverses the tos_to_tid operation to get a priority + * which is in sequential order, and can be compared. + * Use this to compare the priority of two different TIDs. + */ +const u8 tos_to_tid_inv[] = { + 0x02, /* from tos_to_tid[2] = 0 */ + 0x00, /* from tos_to_tid[0] = 1 */ + 0x01, /* from tos_to_tid[1] = 2 */ + 0x03, + 0x04, + 0x05, + 0x06, + 0x07 +}; + +/* WMM information IE */ +static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, + 0x00, 0x50, 0xf2, 0x02, + 0x00, 0x01, 0x00 +}; + +static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VO +}; + +static u8 tos_to_tid[] = { + /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ + 0x01, /* 0 1 0 AC_BK */ + 0x02, /* 0 0 0 AC_BK */ + 0x00, /* 0 0 1 AC_BE */ + 0x03, /* 0 1 1 AC_BE */ + 0x04, /* 1 0 0 AC_VI */ + 0x05, /* 1 0 1 AC_VI */ + 0x06, /* 1 1 0 AC_VO */ + 0x07 /* 1 1 1 AC_VO */ +}; + +static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; + +/* This function debug prints the priority parameters for a WMM AC. + */ +static void +nxpwifi_wmm_ac_debug_print(const struct ieee80211_wmm_ac_param *ac_param) +{ + static const char * const ac_str[] = { "BK", "BE", "VI", "VO" }; + + pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, ", + ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn + & NXPWIFI_ACI) >> 5]], + (ac_param->aci_aifsn & NXPWIFI_ACI) >> 5, + (ac_param->aci_aifsn & NXPWIFI_ACM) >> 4, + ac_param->aci_aifsn & NXPWIFI_AIFSN); + pr_debug("EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", + ac_param->cw & NXPWIFI_ECW_MIN, + (ac_param->cw & NXPWIFI_ECW_MAX) >> 4, + le16_to_cpu(ac_param->txop_limit)); +} + +/* This function allocates a route address list. + * + * The function also initializes the list with the provided RA. + */ +static struct nxpwifi_ra_list_tbl * +nxpwifi_wmm_allocate_ralist_node(struct nxpwifi_adapter *adapter, const u8 *ra) +{ + struct nxpwifi_ra_list_tbl *ra_list; + + ra_list = kzalloc(sizeof(*ra_list), GFP_ATOMIC); + if (!ra_list) + return NULL; + + INIT_LIST_HEAD(&ra_list->list); + skb_queue_head_init(&ra_list->skb_head); + + memcpy(ra_list->ra, ra, ETH_ALEN); + + ra_list->total_pkt_count = 0; + + nxpwifi_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); + + return ra_list; +} + +/* This function returns random no between 16 and 32 to be used as threshold + * for no of packets after which BA setup is initiated. + */ +static u8 nxpwifi_get_random_ba_threshold(void) +{ + u64 ns; + /* setup ba_packet_threshold here random number between + * [BA_SETUP_PACKET_OFFSET, + * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] + */ + ns = ktime_get_ns(); + ns += (ns >> 32) + (ns >> 16); + + return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; +} + +/* This function allocates and adds a RA list for all TIDs + * with the given RA. + */ +void nxpwifi_ralist_add(struct nxpwifi_private *priv, const u8 *ra) +{ + int i; + struct nxpwifi_ra_list_tbl *ra_list; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_sta_node *node; + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = nxpwifi_wmm_allocate_ralist_node(adapter, ra); + nxpwifi_dbg(adapter, INFO, + "info: created ra_list %p\n", ra_list); + + if (!ra_list) + break; + + ra_list->is_11n_enabled = 0; + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + if (!nxpwifi_queuing_ra_based(priv)) { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + } else { + spin_lock_bh(&priv->sta_list_spinlock); + node = nxpwifi_get_sta_entry(priv, ra); + if (node) + ra_list->tx_paused = node->tx_pause; + ra_list->is_11n_enabled = + nxpwifi_is_sta_11n_enabled(priv, node); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = node->max_amsdu; + spin_unlock_bh(&priv->sta_list_spinlock); + } + + nxpwifi_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", + ra_list, ra_list->is_11n_enabled); + + if (ra_list->is_11n_enabled) { + ra_list->ba_pkt_count = 0; + ra_list->ba_packet_thr = + nxpwifi_get_random_ba_threshold(); + } + list_add_tail(&ra_list->list, + &priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +/* This function sets the WMM queue priorities to their default values. + */ +static void nxpwifi_wmm_default_queue_priorities(struct nxpwifi_private *priv) +{ + /* Default queue priorities: VO->VI->BE->BK */ + priv->wmm.queue_priority[0] = WMM_AC_VO; + priv->wmm.queue_priority[1] = WMM_AC_VI; + priv->wmm.queue_priority[2] = WMM_AC_BE; + priv->wmm.queue_priority[3] = WMM_AC_BK; +} + +/* This function map ACs to TIDs. + */ +static void +nxpwifi_wmm_queue_priorities_tid(struct nxpwifi_private *priv) +{ + struct nxpwifi_wmm_desc *wmm = &priv->wmm; + u8 *queue_priority = wmm->queue_priority; + int i; + + for (i = 0; i < 4; ++i) { + tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; + tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; + } + + for (i = 0; i < MAX_NUM_TID; ++i) + priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; + + atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); +} + +/* This function initializes WMM priority queues. + */ +void +nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private *priv, + struct ieee80211_wmm_param_ie *wmm_ie) +{ + u16 cw_min, avg_back_off, tmp[4]; + u32 i, j, num_ac; + u8 ac_idx; + + if (!wmm_ie || !priv->wmm_enabled) { + /* WMM is not enabled, just set the defaults and return */ + nxpwifi_wmm_default_queue_priorities(priv); + return; + } + + nxpwifi_dbg(priv->adapter, INFO, + "info: WMM Parameter IE: version=%d,\t" + "qos_info Parameter Set Count=%d, Reserved=%#x\n", + wmm_ie->version, wmm_ie->qos_info & + IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, + wmm_ie->reserved); + + for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac); num_ac++) { + u8 ecw = wmm_ie->ac[num_ac].cw; + u8 aci_aifsn = wmm_ie->ac[num_ac].aci_aifsn; + + cw_min = (1 << (ecw & NXPWIFI_ECW_MIN)) - 1; + avg_back_off = (cw_min >> 1) + (aci_aifsn & NXPWIFI_AIFSN); + + ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & NXPWIFI_ACI) >> 5]; + priv->wmm.queue_priority[ac_idx] = ac_idx; + tmp[ac_idx] = avg_back_off; + + nxpwifi_dbg(priv->adapter, INFO, + "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", + (1 << ((ecw & NXPWIFI_ECW_MAX) >> 4)) - 1, + cw_min, avg_back_off); + nxpwifi_wmm_ac_debug_print(&wmm_ie->ac[num_ac]); + } + + /* Bubble sort */ + for (i = 0; i < num_ac; i++) { + for (j = 1; j < num_ac - i; j++) { + if (tmp[j - 1] > tmp[j]) { + swap(tmp[j - 1], tmp[j]); + swap(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } else if (tmp[j - 1] == tmp[j]) { + if (priv->wmm.queue_priority[j - 1] + < priv->wmm.queue_priority[j]) + swap(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } + } + } + + nxpwifi_wmm_queue_priorities_tid(priv); +} + +/* This function evaluates whether or not an AC is to be downgraded. + * + * In case the AC is not enabled, the highest AC is returned that is + * enabled and does not require admission control. + */ +static enum nxpwifi_wmm_ac_e +nxpwifi_wmm_eval_downgrade_ac(struct nxpwifi_private *priv, + enum nxpwifi_wmm_ac_e eval_ac) +{ + int down_ac; + enum nxpwifi_wmm_ac_e ret_ac; + struct nxpwifi_wmm_ac_status *ac_status; + + ac_status = &priv->wmm.ac_status[eval_ac]; + + if (!ac_status->disabled) + /* Okay to use this AC, its enabled */ + return eval_ac; + + /* Setup a default return value of the lowest priority */ + ret_ac = WMM_AC_BK; + + /* Find the highest AC that is enabled and does not require + * admission control. The spec disallows downgrading to an AC, + * which is enabled due to a completed admission control. + * Unadmitted traffic is not to be sent on an AC with admitted + * traffic. + */ + for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { + ac_status = &priv->wmm.ac_status[down_ac]; + + if (!ac_status->disabled && !ac_status->flow_required) + /* AC is enabled and does not require admission + * control + */ + ret_ac = (enum nxpwifi_wmm_ac_e)down_ac; + } + + return ret_ac; +} + +/* This function downgrades WMM priority queue. + */ +void +nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private *priv) +{ + int ac_val; + + nxpwifi_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t" + "BK(0), BE(1), VI(2), VO(3)\n"); + + if (!priv->wmm_enabled) { + /* WMM is not enabled, default priorities */ + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) + priv->wmm.ac_down_graded_vals[ac_val] = + (enum nxpwifi_wmm_ac_e)ac_val; + } else { + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] = + nxpwifi_wmm_eval_downgrade_ac + (priv, (enum nxpwifi_wmm_ac_e)ac_val); + nxpwifi_dbg(priv->adapter, INFO, + "info: WMM: AC PRIO %d maps to %d\n", + ac_val, + priv->wmm.ac_down_graded_vals[ac_val]); + } + } +} + +/* This function converts the IP TOS field to an WMM AC + * Queue assignment. + */ +static enum nxpwifi_wmm_ac_e +nxpwifi_wmm_convert_tos_to_ac(struct nxpwifi_adapter *adapter, u32 tos) +{ + /* Map of TOS UP values to WMM AC */ + static const enum nxpwifi_wmm_ac_e tos_to_ac[] = { + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO + }; + + if (tos >= ARRAY_SIZE(tos_to_ac)) + return WMM_AC_BE; + + return tos_to_ac[tos]; +} + +/* This function evaluates a given TID and downgrades it to a lower + * TID if the WMM Parameter IE received from the AP indicates that the + * AP is disabled (due to call admission control (ACM bit). Mapping + * of TID to AC is taken care of internally. + */ +u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid) +{ + enum nxpwifi_wmm_ac_e ac, ac_down; + u8 new_tid; + + ac = nxpwifi_wmm_convert_tos_to_ac(priv->adapter, tid); + ac_down = priv->wmm.ac_down_graded_vals[ac]; + + /* Send the index to tid array, picking from the array will be + * taken care by dequeuing function + */ + new_tid = ac_to_tid[ac_down][tid % 2]; + + return new_tid; +} + +/* This function initializes the WMM state information and the + * WMM data path queues. + */ +void +nxpwifi_wmm_init(struct nxpwifi_adapter *adapter) +{ + int i, j; + struct nxpwifi_private *priv; + + for (j = 0; j < adapter->priv_num; ++j) { + priv = adapter->priv[j]; + + for (i = 0; i < MAX_NUM_TID; ++i) { + if (!disable_tx_amsdu && + adapter->tx_buf_size > NXPWIFI_TX_DATA_BUF_SIZE_2K) + priv->aggr_prio_tbl[i].amsdu = + priv->tos_to_tid_inv[i]; + else + priv->aggr_prio_tbl[i].amsdu = + BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[i].ampdu_ap = + priv->tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_user = + priv->tos_to_tid_inv[i]; + } + + priv->aggr_prio_tbl[6].amsdu = + priv->aggr_prio_tbl[6].ampdu_ap = + priv->aggr_prio_tbl[6].ampdu_user = + BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].amsdu = + priv->aggr_prio_tbl[7].ampdu_ap = + priv->aggr_prio_tbl[7].ampdu_user = + BA_STREAM_NOT_ALLOWED; + + nxpwifi_set_ba_params(priv); + nxpwifi_reset_11n_rx_seq_num(priv); + + priv->wmm.drv_pkt_delay_max = NXPWIFI_WMM_DRV_DELAY_MAX; + atomic_set(&priv->wmm.tx_pkts_queued, 0); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } +} + +bool nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!skb_queue_empty(&priv->bypass_txq)) + return false; + } + + return true; +} + +/* This function checks if WMM Tx queue is empty. + */ +bool nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter) +{ + int i; + struct nxpwifi_private *priv; + + for (i = 0; i < adapter->priv_num; ++i) { + priv = adapter->priv[i]; + if (!priv->port_open) + continue; + if (atomic_read(&priv->wmm.tx_pkts_queued)) + return false; + } + + return true; +} + +/* This function deletes all packets in an RA list node. + * + * The packet sent completion callback handler are called with + * status failure, after they are dequeued to ensure proper + * cleanup. The RA list node itself is freed at the end. + */ +static void +nxpwifi_wmm_del_pkts_in_ralist_node(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra_list) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { + skb_unlink(skb, &ra_list->skb_head); + nxpwifi_write_data_complete(adapter, skb, 0, -1); + } +} + +/* This function deletes all packets in an RA list. + * + * Each nodes in the RA list are freed individually first, and then + * the RA list itself is freed. + */ +static void +nxpwifi_wmm_del_pkts_in_ralist(struct nxpwifi_private *priv, + struct list_head *ra_list_head) +{ + struct nxpwifi_ra_list_tbl *ra_list; + + list_for_each_entry(ra_list, ra_list_head, list) + nxpwifi_wmm_del_pkts_in_ralist_node(priv, ra_list); +} + +/* This function deletes all packets in all RA lists. + */ +static void nxpwifi_wmm_cleanup_queues(struct nxpwifi_private *priv) +{ + int i; + + for (i = 0; i < MAX_NUM_TID; i++) + nxpwifi_wmm_del_pkts_in_ralist + (priv, &priv->wmm.tid_tbl_ptr[i].ra_list); + + atomic_set(&priv->wmm.tx_pkts_queued, 0); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); +} + +/* This function deletes all route addresses from all RA lists. + */ +static void nxpwifi_wmm_delete_all_ralist(struct nxpwifi_private *priv) +{ + struct nxpwifi_ra_list_tbl *ra_list, *tmp_node; + int i; + + for (i = 0; i < MAX_NUM_TID; ++i) { + nxpwifi_dbg(priv->adapter, INFO, + "info: ra_list: freeing buf for tid %d\n", i); + list_for_each_entry_safe(ra_list, tmp_node, + &priv->wmm.tid_tbl_ptr[i].ra_list, + list) { + list_del(&ra_list->list); + kfree(ra_list); + } + + INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +static int nxpwifi_free_ack_frame(int id, void *p, void *data) +{ + pr_warn("Have pending ack frames!\n"); + kfree_skb(p); + return 0; +} + +/* This function cleans up the Tx and Rx queues. + * + * Cleanup includes - + * - All packets in RA lists + * - All entries in Rx reorder table + * - All entries in Tx BA stream table + * - MPA buffer (if required) + * - All RA lists + */ +void +nxpwifi_clean_txrx(struct nxpwifi_private *priv) +{ + struct sk_buff *skb, *tmp; + + nxpwifi_11n_cleanup_reorder_tbl(priv); + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + nxpwifi_wmm_cleanup_queues(priv); + nxpwifi_11n_delete_all_tx_ba_stream_tbl(priv); + + if (priv->adapter->if_ops.cleanup_mpa_buf) + priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); + + nxpwifi_wmm_delete_all_ralist(priv); + memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) { + skb_unlink(skb, &priv->bypass_txq); + nxpwifi_write_data_complete(priv->adapter, skb, 0, -1); + } + atomic_set(&priv->adapter->bypass_tx_pending, 0); + + idr_for_each(&priv->ack_status_frames, nxpwifi_free_ack_frame, NULL); + idr_destroy(&priv->ack_status_frames); +} + +/* This function retrieves a particular RA list node, matching with the + * given TID and RA address. + */ +struct nxpwifi_ra_list_tbl * +nxpwifi_wmm_get_ralist_node(struct nxpwifi_private *priv, u8 tid, + const u8 *ra_addr) +{ + struct nxpwifi_ra_list_tbl *ra_list; + + list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, + list) { + if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) + return ra_list; + } + + return NULL; +} + +void nxpwifi_update_ralist_tx_pause(struct nxpwifi_private *priv, u8 *mac, + u8 tx_pause) +{ + struct nxpwifi_ra_list_tbl *ra_list; + u32 pkt_cnt = 0, tx_pkts_queued; + int i; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = nxpwifi_wmm_get_ralist_node(priv, i, mac); + if (ra_list && ra_list->tx_paused != tx_pause) { + pkt_cnt += ra_list->total_pkt_count; + ra_list->tx_paused = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += + ra_list->total_pkt_count; + else + priv->wmm.pkts_paused[i] -= + ra_list->total_pkt_count; + } + } + + if (pkt_cnt) { + tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + + atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } + spin_unlock_bh(&priv->wmm.ra_list_spinlock); +} + +/* This function retrieves an RA list node for a given TID and + * RA address pair. + * + * If no such node is found, a new node is added first and then + * retrieved. + */ +struct nxpwifi_ra_list_tbl * +nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private *priv, u8 tid, + const u8 *ra_addr) +{ + struct nxpwifi_ra_list_tbl *ra_list; + + ra_list = nxpwifi_wmm_get_ralist_node(priv, tid, ra_addr); + if (ra_list) + return ra_list; + nxpwifi_ralist_add(priv, ra_addr); + + return nxpwifi_wmm_get_ralist_node(priv, tid, ra_addr); +} + +/* This function deletes RA list nodes for given mac for all TIDs. + * Function also decrements TX pending count accordingly. + */ +void +nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private *priv, const u8 *ra_addr) +{ + struct nxpwifi_ra_list_tbl *ra_list; + int i; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = nxpwifi_wmm_get_ralist_node(priv, i, ra_addr); + + if (!ra_list) + continue; + nxpwifi_wmm_del_pkts_in_ralist_node(priv, ra_list); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; + else + atomic_sub(ra_list->total_pkt_count, + &priv->wmm.tx_pkts_queued); + list_del(&ra_list->list); + kfree(ra_list); + } + spin_unlock_bh(&priv->wmm.ra_list_spinlock); +} + +/* This function checks if a particular RA list node exists in a given TID + * table index. + */ +bool nxpwifi_is_ralist_valid(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra_list, int ptr_index) +{ + struct nxpwifi_ra_list_tbl *rlist; + + list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, + list) { + if (rlist == ra_list) + return true; + } + + return false; +} + +/* This function adds a packet to bypass TX queue. + * This is special TX queue for packets which can be sent even when port_open + * is false. + */ +void +nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + skb_queue_tail(&priv->bypass_txq, skb); +} + +/* This function adds a packet to WMM queue. + * + * In disconnected state the packet is immediately dropped and the + * packet send completion callback is called with status failure. + * + * Otherwise, the correct RA list node is located and the packet + * is queued at the list tail. + */ +void +nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u32 tid; + struct nxpwifi_ra_list_tbl *ra_list; + u8 ra[ETH_ALEN], tid_down; + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + + memcpy(ra, eth_hdr->h_dest, ETH_ALEN); + + if (!priv->media_connected && !nxpwifi_is_skb_mgmt_frame(skb)) { + nxpwifi_dbg(adapter, DATA, "data: drop packet in disconnect\n"); + nxpwifi_write_data_complete(adapter, skb, 0, -1); + return; + } + + tid = skb->priority; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + tid_down = nxpwifi_wmm_downgrade_tid(priv, tid); + + /* In case of infra as we have already created the list during + * association we just don't have to call get_queue_raptr, we will + * have only 1 raptr for a tid in case of infra + */ + memcpy(ra, skb->data, ETH_ALEN); + if (is_multicast_ether_addr(ra) || nxpwifi_is_skb_mgmt_frame(skb)) + eth_broadcast_addr(ra); + ra_list = nxpwifi_wmm_get_queue_raptr(priv, tid_down, ra); + + if (!ra_list) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + priv->tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + priv->tos_to_tid_inv[tid_down]); + + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid_down]++; + else + atomic_inc(&priv->wmm.tx_pkts_queued); + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); +} + +/* This function processes the get WMM status command response from firmware. + * + * The response may contain multiple TLVs - + * - AC Queue status TLVs + * - Current WMM Parameter IE TLV + * - Admission Control action frame TLVs + * + * This function parses the TLVs and then calls further specific functions + * to process any changes in the queue prioritize or state. + */ +int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv, + const struct host_cmd_ds_command *resp) +{ + u8 *curr = (u8 *)&resp->params.get_wmm_status; + u16 resp_len = le16_to_cpu(resp->size), tlv_len; + int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + bool valid = true; + + struct nxpwifi_ie_types_data *tlv_hdr; + struct nxpwifi_ie_types_wmm_queue_status *wmm_qs; + struct ieee80211_wmm_param_ie *wmm_param_ie = NULL; + struct nxpwifi_wmm_ac_status *ac_status; + + nxpwifi_dbg(priv->adapter, INFO, + "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", + resp_len); + + while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { + tlv_hdr = (struct nxpwifi_ie_types_data *)curr; + tlv_len = le16_to_cpu(tlv_hdr->header.len); + + if (resp_len < tlv_len + sizeof(tlv_hdr->header)) + break; + + switch (le16_to_cpu(tlv_hdr->header.type)) { + case TLV_TYPE_WMMQSTATUS: + wmm_qs = (struct nxpwifi_ie_types_wmm_queue_status *) + tlv_hdr; + nxpwifi_dbg(priv->adapter, CMD, + "info: CMD_RESP: WMM_GET_STATUS:\t" + "QSTATUS TLV: %d, %d, %d\n", + wmm_qs->queue_index, + wmm_qs->flow_required, + wmm_qs->disabled); + + ac_status = &priv->wmm.ac_status[wmm_qs->queue_index]; + ac_status->disabled = wmm_qs->disabled; + ac_status->flow_required = wmm_qs->flow_required; + ac_status->flow_created = wmm_qs->flow_created; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* Point the regular IEEE IE 2 bytes into the NXP IE + * and setup the IEEE IE type and length byte fields + */ + + wmm_param_ie = + (struct ieee80211_wmm_param_ie *)(curr + 2); + wmm_param_ie->len = (u8)tlv_len; + wmm_param_ie->element_id = WLAN_EID_VENDOR_SPECIFIC; + + nxpwifi_dbg(priv->adapter, CMD, + "info: CMD_RESP: WMM_GET_STATUS:\t" + "WMM Parameter Set Count: %d\n", + wmm_param_ie->qos_info & mask); + + if (wmm_param_ie->len + 2 > + sizeof(struct ieee80211_wmm_param_ie)) + break; + + memcpy(&priv->curr_bss_params.bss_descriptor.wmm_ie, + wmm_param_ie, wmm_param_ie->len + 2); + + break; + + default: + valid = false; + break; + } + + curr += (tlv_len + sizeof(tlv_hdr->header)); + resp_len -= (tlv_len + sizeof(tlv_hdr->header)); + } + + nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie); + nxpwifi_wmm_setup_ac_downgrade(priv); + + return 0; +} + +/* Callback handler from the command module to allow insertion of a WMM TLV. + * + * If the BSS we are associating to supports WMM, this function adds the + * required WMM Information IE to the association request command buffer in + * the form of a NXP extended IEEE IE. + */ +u32 +nxpwifi_wmm_process_association_req(struct nxpwifi_private *priv, + u8 **assoc_buf, + struct ieee80211_wmm_param_ie *wmm_ie, + struct ieee80211_ht_cap *ht_cap) +{ + struct nxpwifi_ie_types_wmm_param_set *wmm_tlv; + u32 ret_len = 0; + + /* Null checks */ + if (!assoc_buf) + return 0; + if (!(*assoc_buf)) + return 0; + + if (!wmm_ie) + return 0; + + nxpwifi_dbg(priv->adapter, INFO, + "info: WMM: process assoc req: bss->wmm_ie=%#x\n", + wmm_ie->element_id); + + if ((priv->wmm_required || + (ht_cap && (priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN))) && + wmm_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) { + wmm_tlv = (struct nxpwifi_ie_types_wmm_param_set *)*assoc_buf; + wmm_tlv->header.type = cpu_to_le16((u16)wmm_info_ie[0]); + wmm_tlv->header.len = cpu_to_le16((u16)wmm_info_ie[1]); + memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], + le16_to_cpu(wmm_tlv->header.len)); + if (wmm_ie->qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) + memcpy((u8 *)(wmm_tlv->wmm_ie + + le16_to_cpu(wmm_tlv->header.len) + - sizeof(priv->wmm_qosinfo)), + &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); + + ret_len = sizeof(wmm_tlv->header) + + le16_to_cpu(wmm_tlv->header.len); + + *assoc_buf += ret_len; + } + + return ret_len; +} + +/* This function computes the time delay in the driver queues for a + * given packet. + * + * When the packet is received at the OS/Driver interface, the current + * time is set in the packet structure. The difference between the present + * time and that received time is computed in this function and limited + * based on pre-compiled limits in the driver. + */ +u8 +nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv, + const struct sk_buff *skb) +{ + u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); + u8 ret_val; + + /* Queue delay is passed as a uint8 in units of 2ms (ms shifted + * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. + * + * Pass max value if queue_delay is beyond the uint8 range + */ + ret_val = (u8)(min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); + + nxpwifi_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t" + "%d ms sent to FW\n", queue_delay, ret_val); + + return ret_val; +} + +/* This function retrieves the highest priority RA list table pointer. + */ +static struct nxpwifi_ra_list_tbl * +nxpwifi_wmm_get_highest_priolist_ptr(struct nxpwifi_adapter *adapter, + struct nxpwifi_private **priv, int *tid) +{ + struct nxpwifi_private *priv_tmp; + struct nxpwifi_ra_list_tbl *ptr; + struct nxpwifi_tid_tbl *tid_ptr; + atomic_t *hqp; + int i, j; + u8 to_tid; + + /* check the BSS with highest priority first */ + for (j = adapter->priv_num - 1; j >= 0; --j) { + /* iterate over BSS with the equal priority */ + list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, + &adapter->bss_prio_tbl[j].bss_prio_head, + list) { +try_again: + priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; + + if (!priv_tmp->port_open || + (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) + continue; + + /* iterate over the WMM queues of the BSS */ + hqp = &priv_tmp->wmm.highest_queued_prio; + for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { + spin_lock_bh(&priv_tmp->wmm.ra_list_spinlock); + + to_tid = tos_to_tid[i]; + tid_ptr = &(priv_tmp)->wmm.tid_tbl_ptr[to_tid]; + + /* iterate over receiver addresses */ + list_for_each_entry(ptr, &tid_ptr->ra_list, + list) { + if (!ptr->tx_paused && + !skb_queue_empty(&ptr->skb_head)) + /* holds both locks */ + goto found; + } + + spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock); + } + + if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) != 0) { + atomic_set(&priv_tmp->wmm.highest_queued_prio, + HIGH_PRIO_TID); + /* Iterate current private once more, since + * there still exist packets in data queue + */ + goto try_again; + } else { + atomic_set(&priv_tmp->wmm.highest_queued_prio, + NO_PKT_PRIO_TID); + } + } + } + + return NULL; + +found: + /* holds ra_list_spinlock */ + if (atomic_read(hqp) > i) + atomic_set(hqp, i); + spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock); + + *priv = priv_tmp; + *tid = tos_to_tid[i]; + + return ptr; +} + +/* This functions rotates ra and bss lists so packets are picked round robin. + * + * After a packet is successfully transmitted, rotate the ra list, so the ra + * next to the one transmitted, will come first in the list. This way we pick + * the ra' in a round robin fashion. Same applies to bss nodes of equal + * priority. + * + * Function also increments wmm.packets_out counter. + */ +void nxpwifi_rotate_priolists(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra, + int tid) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl; + struct nxpwifi_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; + + spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock); + /* dirty trick: we remove 'head' temporarily and reinsert it after + * curr bss node. imagine list to stay fixed while head is moved + */ + list_move(&tbl[priv->bss_priority].bss_prio_head, + &tbl[priv->bss_priority].bss_prio_cur->list); + spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock); + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + if (nxpwifi_is_ralist_valid(priv, ra, tid)) { + priv->wmm.packets_out[tid]++; + /* same as above */ + list_move(&tid_ptr->ra_list, &ra->list); + } + spin_unlock_bh(&priv->wmm.ra_list_spinlock); +} + +/* This function checks if 11n aggregation is possible. + */ +static bool +nxpwifi_is_11n_aggragation_possible(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, + int max_buf_size) +{ + int count = 0, total_size = 0; + struct sk_buff *skb, *tmp; + int max_amsdu_size; + + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP && priv->ap_11n_enabled && + ptr->is_11n_enabled) + max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); + else + max_amsdu_size = max_buf_size; + + skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { + total_size += skb->len; + if (total_size >= max_amsdu_size) + break; + if (++count >= MIN_NUM_AMSDU) + return true; + } + + return false; +} + +/* This function sends a single packet to firmware for transmission. + */ +static void +nxpwifi_send_single_packet(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, int ptr_index) +__releases(&priv->wmm.ra_list_spinlock) +{ + struct sk_buff *skb, *skb_next; + struct nxpwifi_tx_param tx_param; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_dbg(adapter, DATA, "data: nothing to send\n"); + return; + } + + skb = skb_dequeue(&ptr->skb_head); + + tx_info = NXPWIFI_SKB_TXCB(skb); + nxpwifi_dbg(adapter, DATA, + "data: dequeuing the packet %p %p\n", ptr, skb); + + ptr->total_pkt_count--; + + if (!skb_queue_empty(&ptr->skb_head)) + skb_next = skb_peek(&ptr->skb_head); + else + skb_next = NULL; + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + + tx_param.next_pkt_len = ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + + if (nxpwifi_process_tx(priv, skb, &tx_param) == -EBUSY) { + /* Queue the packet back at the head */ + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ptr->skb_head, skb); + + ptr->total_pkt_count++; + ptr->ba_pkt_count++; + tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT; + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + } else { + nxpwifi_rotate_priolists(priv, ptr, ptr_index); + atomic_dec(&priv->wmm.tx_pkts_queued); + } +} + +/* This function checks if the first packet in the given RA list + * is already processed or not. + */ +static bool +nxpwifi_is_ptr_processed(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr) +{ + struct sk_buff *skb; + struct nxpwifi_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) + return false; + + skb = skb_peek(&ptr->skb_head); + + tx_info = NXPWIFI_SKB_TXCB(skb); + if (tx_info->flags & NXPWIFI_BUF_FLAG_REQUEUED_PKT) + return true; + + return false; +} + +/* This function sends a single processed packet to firmware for + * transmission. + */ +static void +nxpwifi_send_processed_packet(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, int ptr_index) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct nxpwifi_tx_param tx_param; + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + struct sk_buff *skb, *skb_next; + struct nxpwifi_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + return; + } + + skb = skb_dequeue(&ptr->skb_head); + + if (adapter->data_sent || adapter->tx_lock_flag) { + ptr->total_pkt_count--; + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_dec(&priv->wmm.tx_pkts_queued); + atomic_inc(&adapter->tx_queued); + return; + } + + if (!skb_queue_empty(&ptr->skb_head)) + skb_next = skb_peek(&ptr->skb_head); + else + skb_next = NULL; + + tx_info = NXPWIFI_SKB_TXCB(skb); + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + + tx_param.next_pkt_len = + ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + + ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA, + skb, &tx_param); + + switch (ret) { + case -EBUSY: + nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + nxpwifi_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ptr->skb_head, skb); + + tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT; + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + break; + case -EINPROGRESS: + break; + case 0: + nxpwifi_write_data_complete(adapter, skb, 0, ret); + break; + default: + nxpwifi_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); + adapter->dbg.num_tx_host_to_card_failure++; + nxpwifi_write_data_complete(adapter, skb, 0, ret); + break; + } + + if (ret != -EBUSY) { + nxpwifi_rotate_priolists(priv, ptr, ptr_index); + atomic_dec(&priv->wmm.tx_pkts_queued); + spin_lock_bh(&priv->wmm.ra_list_spinlock); + ptr->total_pkt_count--; + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + } +} + +/* This function dequeues a packet from the highest priority list + * and transmits it. + */ +static int +nxpwifi_dequeue_tx_packet(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_ra_list_tbl *ptr; + struct nxpwifi_private *priv = NULL; + int ptr_index = 0; + u8 ra[ETH_ALEN]; + int tid_del = 0, tid = 0; + + ptr = nxpwifi_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); + if (!ptr) + return -ENOENT; + + tid = nxpwifi_get_tid(ptr); + + nxpwifi_dbg(adapter, DATA, "data: tid=%d\n", tid); + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + return -EINVAL; + } + + if (nxpwifi_is_ptr_processed(priv, ptr)) { + nxpwifi_send_processed_packet(priv, ptr, ptr_index); + /* ra_list_spinlock has been freed in + * nxpwifi_send_processed_packet() + */ + return 0; + } + + if (!ptr->is_11n_enabled || + ptr->ba_status || + priv->wps.session_enable) { + if (ptr->is_11n_enabled && + ptr->ba_status && + ptr->amsdu_in_ampdu && + nxpwifi_is_amsdu_allowed(priv, tid) && + nxpwifi_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + nxpwifi_11n_aggregate_pkt(priv, ptr, ptr_index); + /* ra_list_spinlock has been freed in + * nxpwifi_11n_aggregate_pkt() + */ + else + nxpwifi_send_single_packet(priv, ptr, ptr_index); + /* ra_list_spinlock has been freed in + * nxpwifi_send_single_packet() + */ + } else { + if (nxpwifi_is_ampdu_allowed(priv, ptr, tid) && + ptr->ba_pkt_count > ptr->ba_packet_thr) { + if (nxpwifi_space_avail_for_new_ba_stream(adapter)) { + nxpwifi_create_ba_tbl(priv, ptr->ra, tid, + BA_SETUP_INPROGRESS); + nxpwifi_send_addba(priv, tid, ptr->ra); + } else if (nxpwifi_find_stream_to_delete + (priv, tid, &tid_del, ra)) { + nxpwifi_create_ba_tbl(priv, ptr->ra, tid, + BA_SETUP_INPROGRESS); + nxpwifi_send_delba(priv, tid_del, ra, 1); + } + } + if (nxpwifi_is_amsdu_allowed(priv, tid) && + nxpwifi_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + nxpwifi_11n_aggregate_pkt(priv, ptr, ptr_index); + /* ra_list_spinlock has been freed in + * nxpwifi_11n_aggregate_pkt() + */ + else + nxpwifi_send_single_packet(priv, ptr, ptr_index); + /* ra_list_spinlock has been freed in + * nxpwifi_send_single_packet() + */ + } + return 0; +} + +void nxpwifi_process_bypass_tx(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_tx_param tx_param; + struct sk_buff *skb; + struct nxpwifi_txinfo *tx_info; + struct nxpwifi_private *priv; + int i; + + if (adapter->data_sent || adapter->tx_lock_flag) + return; + + for (i = 0; i < adapter->priv_num; ++i) { + priv = adapter->priv[i]; + + if (skb_queue_empty(&priv->bypass_txq)) + continue; + + skb = skb_dequeue(&priv->bypass_txq); + tx_info = NXPWIFI_SKB_TXCB(skb); + + /* no aggregation for bypass packets */ + tx_param.next_pkt_len = 0; + + if (nxpwifi_process_tx(priv, skb, &tx_param) == -EBUSY) { + skb_queue_head(&priv->bypass_txq, skb); + tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT; + } else { + atomic_dec(&adapter->bypass_tx_pending); + } + } +} + +/* This function transmits the highest priority packet awaiting in the + * WMM Queues. + */ +void +nxpwifi_wmm_process_tx(struct nxpwifi_adapter *adapter) +{ + do { + if (nxpwifi_dequeue_tx_packet(adapter)) + break; + if (adapter->iface_type != NXPWIFI_SDIO) { + if (adapter->data_sent || + adapter->tx_lock_flag) + break; + } else { + if (atomic_read(&adapter->tx_queued) >= + NXPWIFI_MAX_PKTS_TXQ) + break; + } + } while (!nxpwifi_wmm_lists_empty(adapter)); +} diff --git a/drivers/net/wireless/nxp/nxpwifi/wmm.h b/drivers/net/wireless/nxp/nxpwifi/wmm.h new file mode 100644 index 000000000000..fc9bbb989144 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/wmm.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: WMM + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_WMM_H_ +#define _NXPWIFI_WMM_H_ + +enum ieee_types_wmm_aciaifsn_bitmasks { + NXPWIFI_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + NXPWIFI_ACM = BIT(4), + NXPWIFI_ACI = (BIT(5) | BIT(6)), +}; + +enum ieee_types_wmm_ecw_bitmasks { + NXPWIFI_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + NXPWIFI_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), +}; + +extern const u16 nxpwifi_1d_to_wmm_queue[]; +extern const u8 tos_to_tid_inv[]; + +/* This function retrieves the TID of the given RA list. + */ +static inline int +nxpwifi_get_tid(struct nxpwifi_ra_list_tbl *ptr) +{ + struct sk_buff *skb; + + if (skb_queue_empty(&ptr->skb_head)) + return 0; + + skb = skb_peek(&ptr->skb_head); + + return skb->priority; +} + +void nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv, + struct sk_buff *skb); +void nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv, + struct sk_buff *skb); +void nxpwifi_ralist_add(struct nxpwifi_private *priv, const u8 *ra); +void nxpwifi_rotate_priolists(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra, int tid); + +bool nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter); +bool nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter *adapter); +void nxpwifi_wmm_process_tx(struct nxpwifi_adapter *adapter); +void nxpwifi_process_bypass_tx(struct nxpwifi_adapter *adapter); +bool nxpwifi_is_ralist_valid(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra_list, int tid); + +u8 nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv, + const struct sk_buff *skb); +void nxpwifi_wmm_init(struct nxpwifi_adapter *adapter); + +u32 nxpwifi_wmm_process_association_req(struct nxpwifi_private *priv, + u8 **assoc_buf, + struct ieee80211_wmm_param_ie *wmmie, + struct ieee80211_ht_cap *htcap); + +void nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private *priv, + struct ieee80211_wmm_param_ie *wmm_ie); +void nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private *priv); +int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv, + const struct host_cmd_ds_command *resp); +struct nxpwifi_ra_list_tbl * +nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private *priv, u8 tid, + const u8 *ra_addr); +u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid); +void nxpwifi_update_ralist_tx_pause(struct nxpwifi_private *priv, u8 *mac, + u8 tx_pause); + +struct nxpwifi_ra_list_tbl *nxpwifi_wmm_get_ralist_node(struct nxpwifi_private + *priv, u8 tid, const u8 *ra_addr); +#endif /* !_NXPWIFI_WMM_H_ */ From patchwork Mon Sep 30 06:36:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815422 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 406A315381A; Mon, 30 Sep 2024 06:37:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678280; cv=fail; b=fGlLFNGN51pZZeFID10/ZAkGxsX9lizgsXp3PoSozeayv/hACRFR79etHWoDwDkdzlJ8zdJc0dmBJnhswvqXZVf3/5cXxIydu3hp0Kg8hzeGSgHJvlUgCi2I+QfLzJyyOCbh+6uDl9BTj9q8ysZSofA6dfzKDz13xJdi9eaAK+Q= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678280; c=relaxed/simple; bh=P/FnIsUdbMWD+KjpjDWHId16ZZ5Q+jewHbzcIF79XVU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=auqQuhoTfCnmFibr0lVYrYchHZB0/FcTMckyhQrdsdwz9iKgXrxsnuJQFP8htMxtLPrfdUDqYeQNEd1b6z8wMfOqisEUgA1gcmAmXMpgSiQPcBuJACDHF3D58IWxNag2mr0X7xodYsOYvgzykPANp/TzuQ2/cJIU6c5ZQsw8ihM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=VWbT+22d; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="VWbT+22d" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=ps/3ZdWq7K2JMkA1+1tnmNGRYlECD424d0YmjxqNaKEx6htac2HsuBM3dqqrx9YNy9frurcl57/LuabvmJmCbY00HG2iyc/JxL7IA4m64QemNKbnSt/zZ89SoTXPIvN46QojkF7EUAw0qraFs2UEGxs9OqVEkVUdGUdzibv5dy5JknagBTg3XdtBwBGZCMnInnV7AL2QKiTZ7FccyrLhpuCADrBbsWxcmn9NbmYw/36YVTdohvevI9swgx6UmDj8qgQ53u75uaWnNRZMANFZfnqCbiWhJqjd8RCAp9gDebikii8BiCU63mnTCrTtcUZNWCj/7YCZTy05lWcS68x0bQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=pJj42UAEqM2sIWe+foWpUf+TNnWuf/xfldThRlb50nk=; b=sJPIxwOLkTjyRRt4+fk//Scf7bus1AO4s1VBYyLeq1R+mhKLQlOzvPXs+3x+M4tTw92OrYEa5KCz9m6AGWLM75ICxDdiI69575219XTrX9Tzf6qAzdztHabAh9Fmpw10gJ0brQvZCDU5xPuYThv9HyiRiyjvO9eYglkFtuvrRAvoZw/G5MbNhUSP0XxbU+h+kK48d1DHiPS01v3Q00a/WQTvCxiiHJAhvnFwppPak7Mdwb2KiLCXzNflN9XkXW6+Dj40dlncTrD4swM4XuZ2vNsyoLdiHZdCXdAvZ4DeT1RAKtPG98HhYEfknF4UaCQsFFtLISHvOdkSxkOj+nHfEQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=pJj42UAEqM2sIWe+foWpUf+TNnWuf/xfldThRlb50nk=; b=VWbT+22djYyrzakO9MLdw5BIv+CQ4qe6BBg8MuAwAJWcTUBBqqJ728/w3bvNVcBO727r+5ArOz780StLCIoVpPLQKQ9k9y3lBM8e5ANSyQCAoK89kGtHk9OboRAYdN3Cs88McLBdND7HYIMR0zozRDcU5I69f3/zHgqy+Z9fObt/PwEO6CwoPwllEyooLTmNUJ6tgPOTI/nbu5tTT4vTN5vhaoYtzSjFXxObut1dUT1Ntx91kXZx0YV8lKqfblDexp1ViHtcfLVQDROU10I5rgDaqN7/W1NGZjlYOxjWlNuPxvLRf+4Y32StqITovXWQYulqv3vyrQ1kv3XOfgOL+w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:37:47 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:47 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 06/22] wifi: nxpwifi: add scan.c Date: Mon, 30 Sep 2024 14:36:45 +0800 Message-Id: <20240930063701.2566520-7-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: f8ff4226-29d8-444f-332c-08dce11a6540 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: Dd8XQsAF3nR9Nkb5M3nlxZdErg/mJLZR6nIn6t63Ia6qTYj3+E+W9Jdcbffis33P+pUUJtUbXTt/wvXvnSXpiKhQOn8OLR4wuVHDSEtyVkuOLCT/6b3SRFvgrMJcn6b0eqpZHPuE1zBohI2frm8Vp9zanFxQtc5VBSBJI2gMifXvvhr1o02D2mjOOfbcuYBtCIhltgGUDlwl34gAkIK9Cdib2CVoHGKNRfzQEuhudMC62GeldIeYX0MH74KveUy5CmhhlGNMq66AaWhec4/YcExcRWbJN+QoibRJd/CKCxmlanLFt/fkixAGZkspNoqUfEFHSfhIMeJiRNTPp+kWbMUkQFt7Lk4xEyQvQttnSjTyfYM1t1aaFGdNzHk7Mheq64o7dghHOt0p43sZXQFxRwgjybm47JcWhJ/R9HLsuU1sNcHreboeqfu0fhR26HKFOKPx9g163cYq2Lqm3kDdlicV2alOWfbTX4U9JcngGG61ruM32SuuVBA2+L3MhHp8RtluFNaLi76T14nIbTjDtvpiP9ZA2Z9xX7AhdOUcQgtcrbsaTXgWWWHmRGoLBkmqeqNvz4DHT8Z3tzNrM5ujGiRTaGHhWXbp+nDWOEgsGIaHk2YqPkwR3+CWIZ4LfoGDiBpGqO6VZxPlroy6DG6XaEPvN8+i4vWh1YAsLo6QVOEm6Y357HXCSHYLkKKEOo6S0mrIUr98Idd0dJsAFvwWtzL/hWZfWjpDmtPt6Y06qwlWVse6KmYdIFKvBl+QOrC37OsTU0OYfiFMgOrcasdcROYsAqf4+K0wOA9TwfYEkgk//KvpAy6KjJfJ7ZTVrP8YjjJVg4Uy4lGJrFNDtHAl6K8QQsmfi7HSf73K6kBWnqGROfQDNv6Y+tLe2eSd1csUmnkhzAhd9348OoIzsLRyzRADq7qg7wIPc94rx2nk1lsPiYQPiJgBouXm1XdxcprmHqvI5NbW4RCIMsSQEeX6Xk2xcafMAvTA4ymgvXbWku7BDDPF6LeTWD9dvB7kbpl44O9wV93Ui1Gh9DOMHU4TN0cW/M59LC2AjemANv1g+FkdEIy0vlUFIEGbijvDYID2li5jvtloInywPM4Fj1eRykkIE2JSr+paz9uCjMcBokelUHpIQD//FRyg+zdxUveESGeARk+u62TqIp2wMiBIOgR1dQ5wAJ9Rz9fAs7BYBd/A9VEZnl8Epry7UerbtRJsqPH0W80vNRTWVJhRC0XQ5FP8j9BisnlTfrsBEvBuZJkTpTgE7/I6GOXTqG2/Q9rgTq9WDy9auV6fPApQzLzawNel47SpdOfDNPjRM25W0QQLahjP4dVC6Rp05zx4/QsXj3hhSmTFjbmWOB9PFOnvPdKpbWTKZxKeiM4p/VO7ih1uocOAkHEkoLiJyi/J/my7W0Lx8j0G65Pqhl6kd51SlQ== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: gZGygayzYFvcZypBQ5NiByVdkRBFHRn3xIKBq3sztEVFPhymZLOZ2T0CSTGfgM/CJrEo0iaPk6mOZQgUe/LCSA5qiOdvX680GMvf18JW35o9pSv9aWtOd3fjuOn7q6q4+j1RqEBGkWC4XN6d28u3gUMl64ZfWwOtSlK/2Wm2K6+1Hv3PdvA4gxc8Za/SdNYmDdfcufRUAGyFfU5R6RIB98PzsLyNB5Spiio5Lieh20XxpdzsxoSbTDM8nOTRczlj6w4J3T5PIGkAZHImCYu9ezanrNBvxWMdBJZMimitgP+Dxwed4Swz3ASw6JTwA0mV9qEdInh5Cr6KKkTneXbam6WNaHXnKmfeMIaBlopmtGrVfaDD5SGLNH/lPM3jM/swuhc9h6Oju+mEMTqNWkZrmsDGtLS+ZxTr35kkzy4mbpWNMmXKi9Iu6dXxtjEEDrZi7Ly/H9/ArNG3dwNtM2XUVei4OS6dNFIurDWcfIfrgVJgVZ3QWYyXiXcULE5ALac8NWkOIcDTDi0dRPnD5QqYZH2R+NFQrmkFx8Ywlw02/IMN7+fCTk18iP8iFXSH0ManRXZ261RsP/Y5z0/o0ZT38d4numP7Wee9uW+pnatWNlA30ThKNmOjCnj6oXtJLvnCAaz6fsnvQk7tHmiOOsB68YESvoJgCFdC2mkDATOFq4bz4oTMwtzGIqKaYhhfcoBb7urbQMv9H41/zzE1oPEnokac67787G68DSUThOVMtb/s+BEPGLTYjQBENaNjP+vNvWz9CJcaXjGkPupGsXbgVVJSsV9NyMRZ6YMh7BXLsRJ18nqKyp7168AMuVVfZGyhcpJHtSrQtYyGH7Rgsefe/jlMpdGztNMMBKCrpBUy5Npaz0NZ1fA3bym6fRzaFBslZNQU6SmU1gGTUegvTbICREEPcp8XX6TSWaPsH4gKamVySRrhjgsjRPVeOj0+3PMpR3fUcQX6YI+F9REvQdiK3IGhSnyU+SUi4y18PjFdXKZkIRLX4+nXrNFse8Ar/Gtbkg7S89lxXPTA78hqYAexliJm68EymEDF0UigpgF8suIl2ljpXGdLuCZiMPoLyeRU1+vgGMq3K5OgY1Tae3bbXDMpujn1b/MSsV+TW5zBAxYHrHxh6PWN0jHud1kvV4A7JEH54MpnwhH6LJPI96D+HXRExsmuXitK28PJjO/WuoBiE678iqMz+Ih+pSRjrb45vdjmmrm8iau2V44tA6wIFHLxmUUqa9G0oQ7l0ywNr/Mo1uhdIG9TmCjmVa9tcRG8Xi5WK1Cz3Pq3EkhF9WDZB6BYsmErvVuKgdt6BHWHAkjJdl2n7lV2hMXPoObhV4xQPz2PhmrqPYw0jWIBnHrZeI24GxUd3GSJp3TDrTU0ZzTFu/Wo2rpkhIXMeAsnPcF2X4eJ6JXM/t89QutGN7lLdrMy44VvnqvItJqgrSFrf0fEhAvwF18DBs0cvgh67e99Gvf8uJhqDG0AlqllJ5O/FVCNxwAHxKVWPrTsIf/A6XRteImMbMvr1xdxL68UnbWBLxVODW1gZd+RVjI1Pk/ZmrtK/fJBblahIgBkb9qOL+40jezsnLqmHnFZ+uoTANs7 X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: f8ff4226-29d8-444f-332c-08dce11a6540 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:47.1138 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: C+krnbUIC7T1Io7oi8eB3KgcqmkSjBc6EZsFqJ3zay4mG1DwIVuQlK90a/PoHOsb4e2lVjHOn1ks1niUlIADfA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 File scan.c is used to support scan process. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/scan.c | 2831 +++++++++++++++++++++++ 1 file changed, 2831 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/scan.c diff --git a/drivers/net/wireless/nxp/nxpwifi/scan.c b/drivers/net/wireless/nxp/nxpwifi/scan.c new file mode 100644 index 000000000000..8b0aa0d08250 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/scan.c @@ -0,0 +1,2831 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: scan ioctl and command handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "11n.h" +#include "cfg80211.h" + +/* The maximum number of channels the firmware can scan per command */ +#define NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 + +#define NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD 4 + +/* Memory needed to store a max sized Channel List TLV for a firmware scan */ +#define CHAN_TLV_MAX_SIZE (sizeof(struct nxpwifi_ie_types_header) \ + + (NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN \ + * sizeof(struct nxpwifi_chan_scan_param_set))) + +/* Memory needed to store supported rate */ +#define RATE_TLV_MAX_SIZE (sizeof(struct nxpwifi_ie_types_rates_param_set) \ + + HOSTCMD_SUPPORTED_RATES) + +/* Memory needed to store a max number/size WildCard SSID TLV for a firmware + * scan + */ +#define WILDCARD_SSID_TLV_MAX_SIZE \ + (NXPWIFI_MAX_SSID_LIST_LENGTH * \ + (sizeof(struct nxpwifi_ie_types_wildcard_ssid_params) \ + + IEEE80211_MAX_SSID_LEN)) + +/* Maximum memory needed for a nxpwifi_scan_cmd_config with all TLVs at max */ +#define MAX_SCAN_CFG_ALLOC (sizeof(struct nxpwifi_scan_cmd_config) \ + + sizeof(struct nxpwifi_ie_types_num_probes) \ + + sizeof(struct nxpwifi_ie_types_htcap) \ + + CHAN_TLV_MAX_SIZE \ + + RATE_TLV_MAX_SIZE \ + + WILDCARD_SSID_TLV_MAX_SIZE) + +union nxpwifi_scan_cmd_config_tlv { + /* Scan configuration (variable length) */ + struct nxpwifi_scan_cmd_config config; + /* Max allocated block */ + u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; +}; + +enum cipher_suite { + CIPHER_SUITE_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_MAX +}; + +static u8 nxpwifi_wpa_oui[CIPHER_SUITE_MAX][4] = { + { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ + { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ +}; + +static u8 nxpwifi_rsn_oui[CIPHER_SUITE_MAX][4] = { + { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ + { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ +}; + +static void +_dbg_security_flags(int log_level, const char *func, const char *desc, + struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + _nxpwifi_dbg(priv->adapter, log_level, + "info: %s: %s:\twpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\tEncMode=%#x privacy=%#x\n", + func, desc, + bss_desc->bcn_wpa_ie ? + bss_desc->bcn_wpa_ie->vend_hdr.element_id : 0, + bss_desc->bcn_rsn_ie ? + bss_desc->bcn_rsn_ie->id : 0, + priv->sec_info.wep_enabled ? "e" : "d", + priv->sec_info.wpa_enabled ? "e" : "d", + priv->sec_info.wpa2_enabled ? "e" : "d", + priv->sec_info.encryption_mode, + bss_desc->privacy); +} + +#define dbg_security_flags(mask, desc, priv, bss_desc) \ + _dbg_security_flags(NXPWIFI_DBG_##mask, desc, __func__, priv, bss_desc) + +static bool +has_ieee_hdr(struct element *ie, u8 key) +{ + return (ie && ie->id == key); +} + +static bool +has_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key) +{ + return (ie && ie->vend_hdr.element_id == key); +} + +/* This function parses a given IE for a given OUI. + * + * This is used to parse a WPA/RSN IE to find if it has + * a given oui in PTK. + */ +static u8 +nxpwifi_search_oui_in_ie(struct ie_body *iebody, u8 *oui) +{ + u8 count; + + count = iebody->ptk_cnt[0]; + + /* There could be multiple OUIs for PTK hence + * 1) Take the length. + * 2) Check all the OUIs for AES. + * 3) If one of them is AES then pass success. + */ + while (count) { + if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) + return NXPWIFI_OUI_PRESENT; + + --count; + if (count) + iebody = (struct ie_body *)((u8 *)iebody + + sizeof(iebody->ptk_body)); + } + + pr_debug("info: %s: OUI is not found in PTK\n", __func__); + return NXPWIFI_OUI_NOT_PRESENT; +} + +/* This function checks if a given OUI is present in a RSN IE. + * + * The function first checks if a RSN IE is present or not in the + * BSS descriptor. It tries to locate the OUI only if such an IE is + * present. + */ +static u8 +nxpwifi_is_rsn_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cipher) +{ + u8 *oui; + struct ie_body *iebody; + u8 ret = NXPWIFI_OUI_NOT_PRESENT; + + if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) { + iebody = (struct ie_body *) + (((u8 *)bss_desc->bcn_rsn_ie->data) + + RSN_GTK_OUI_OFFSET); + oui = &nxpwifi_rsn_oui[cipher][0]; + ret = nxpwifi_search_oui_in_ie(iebody, oui); + if (ret) + return ret; + } + return ret; +} + +/* This function checks if a given OUI is present in a WPA IE. + * + * The function first checks if a WPA IE is present or not in the + * BSS descriptor. It tries to locate the OUI only if such an IE is + * present. + */ +static u8 +nxpwifi_is_wpa_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cipher) +{ + u8 *oui; + struct ie_body *iebody; + u8 ret = NXPWIFI_OUI_NOT_PRESENT; + + if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) { + iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data + + WPA_GTK_OUI_OFFSET); + oui = &nxpwifi_wpa_oui[cipher][0]; + ret = nxpwifi_search_oui_in_ie(iebody, oui); + if (ret) + return ret; + } + return ret; +} + +/* This function checks if driver is configured with no security mode and + * scanned network is compatible with it. + */ +static bool +nxpwifi_is_bss_no_sec(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) && + !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) && + !priv->sec_info.encryption_mode && !bss_desc->privacy) { + return true; + } + return false; +} + +/* This function checks if static WEP is enabled in driver and scanned network + * is compatible with it. + */ +static bool +nxpwifi_is_bss_static_wep(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && bss_desc->privacy) { + return true; + } + return false; +} + +/* This function checks if wpa is enabled in driver and scanned network is + * compatible with it. + */ +static bool +nxpwifi_is_bss_wpa(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) + /* Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && bss_desc->privacy + */ + ) { + dbg_security_flags(INFO, "WPA", priv, bss_desc); + return true; + } + return false; +} + +/* This function checks if wpa2 is enabled in driver and scanned network is + * compatible with it. + */ +static bool +nxpwifi_is_bss_wpa2(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + priv->sec_info.wpa2_enabled && + has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) { + /* Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && bss_desc->privacy + */ + dbg_security_flags(INFO, "WAP2", priv, bss_desc); + return true; + } + return false; +} + +/* This function checks if dynamic WEP is enabled in driver and scanned network + * is compatible with it. + */ +static bool +nxpwifi_is_bss_dynamic_wep(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) && + !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) && + priv->sec_info.encryption_mode && bss_desc->privacy) { + dbg_security_flags(INFO, "dynamic", priv, bss_desc); + return true; + } + return false; +} + +/* This function checks if a scanned network is compatible with the driver + * settings. + * + * WEP WPA WPA2 encrypt Network + * -enabled -enabled -enabled mode Privacy WPA WPA2 Compatible + * 0 0 0 NONE 0 0 0 yes No security + * 0 1 0 x 1x 1 x yes WPA (disable + * HT if no AES) + * 0 0 1 x 1x x 1 yes WPA2 (disable + * HT if no AES) + * 1 0 0 NONE 1 0 0 yes Static WEP + * (disable HT) + * 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * Compatibility is not matched while roaming, except for mode. + */ +static int +nxpwifi_is_network_compatible(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, u32 mode) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + bss_desc->disable_11n = false; + + /* Don't check for compatibility if roaming */ + if (priv->media_connected && + priv->bss_mode == NL80211_IFTYPE_STATION && + bss_desc->bss_mode == NL80211_IFTYPE_STATION) + return 0; + + if (priv->wps.session_enable) { + nxpwifi_dbg(adapter, IOCTL, + "info: return success directly in WPS period\n"); + return 0; + } + + if (bss_desc->chan_sw_ie_present) { + nxpwifi_dbg(adapter, INFO, + "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); + return -EPERM; + } + + if (bss_desc->bss_mode == mode) { + if (nxpwifi_is_bss_no_sec(priv, bss_desc)) { + /* No security */ + return 0; + } else if (nxpwifi_is_bss_static_wep(priv, bss_desc)) { + /* Static WEP enabled */ + nxpwifi_dbg(adapter, INFO, + "info: Disable 11n in WEP mode.\n"); + bss_desc->disable_11n = true; + return 0; + } else if (nxpwifi_is_bss_wpa(priv, bss_desc)) { + /* WPA enabled */ + if (((priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !nxpwifi_is_wpa_oui_present(bss_desc, + CIPHER_SUITE_CCMP)) { + if (nxpwifi_is_wpa_oui_present + (bss_desc, CIPHER_SUITE_TKIP)) { + nxpwifi_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n = true; + } else { + return -EINVAL; + } + } + return 0; + } else if (nxpwifi_is_bss_wpa2(priv, bss_desc)) { + /* WPA2 enabled */ + if (((priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !nxpwifi_is_rsn_oui_present(bss_desc, + CIPHER_SUITE_CCMP)) { + if (nxpwifi_is_rsn_oui_present + (bss_desc, CIPHER_SUITE_TKIP)) { + nxpwifi_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n = true; + } else { + return -EINVAL; + } + } + return 0; + } else if (nxpwifi_is_bss_dynamic_wep(priv, bss_desc)) { + /* Dynamic WEP enabled */ + return 0; + } + + /* Security doesn't match */ + dbg_security_flags(ERROR, "failed", priv, bss_desc); + return -EINVAL; + } + + /* Mode doesn't match */ + return -EINVAL; +} + +/* This function creates a channel list for the driver to scan, based + * on region/band information. + * + * This routine is used for any scan that is not provided with a + * specific channel list to scan. + */ +static int +nxpwifi_scan_create_channel_list(struct nxpwifi_private *priv, + const struct nxpwifi_user_scan_cfg + *user_scan_in, + struct nxpwifi_chan_scan_param_set + *scan_chan_list, + u8 filtered_scan) +{ + enum nl80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct nxpwifi_adapter *adapter = priv->adapter; + int chan_idx = 0, i; + u16 scan_time = 0; + + if (user_scan_in) + scan_time = (u16)user_scan_in->chan_list[0].scan_time; + + for (band = 0; (band < NUM_NL80211_BANDS) ; band++) { + if (!priv->wdev.wiphy->bands[band]) + continue; + + sband = priv->wdev.wiphy->bands[band]; + + for (i = 0; (i < sband->n_channels) ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_chan_list[chan_idx].radio_type = band; + + if (scan_time) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(scan_time); + else if ((ch->flags & IEEE80211_CHAN_NO_IR) || + (ch->flags & IEEE80211_CHAN_RADAR)) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->active_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bmap |= + (NXPWIFI_PASSIVE_SCAN | NXPWIFI_HIDDEN_SSID_REPORT); + else + scan_chan_list[chan_idx].chan_scan_mode_bmap &= + ~NXPWIFI_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_number = (u32)ch->hw_value; + scan_chan_list[chan_idx].chan_scan_mode_bmap |= + NXPWIFI_DISABLE_CHAN_FILT; + + if (filtered_scan && + !((ch->flags & IEEE80211_CHAN_NO_IR) || + (ch->flags & IEEE80211_CHAN_RADAR))) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->specific_scan_time); + + chan_idx++; + } + } + return chan_idx; +} + +/* This function creates a channel list tlv for bgscan config, based + * on region/band information. + */ +static int +nxpwifi_bgscan_create_channel_list(struct nxpwifi_private *priv, + const struct nxpwifi_bg_scan_cfg + *bgscan_cfg_in, + struct nxpwifi_chan_scan_param_set + *scan_chan_list) +{ + enum nl80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct nxpwifi_adapter *adapter = priv->adapter; + int chan_idx = 0, i; + u16 scan_time = 0, specific_scan_time = adapter->specific_scan_time; + + if (bgscan_cfg_in) + scan_time = (u16)bgscan_cfg_in->chan_list[0].scan_time; + + for (band = 0; (band < NUM_NL80211_BANDS); band++) { + if (!priv->wdev.wiphy->bands[band]) + continue; + + sband = priv->wdev.wiphy->bands[band]; + + for (i = 0; (i < sband->n_channels) ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_chan_list[chan_idx].radio_type = band; + + if (scan_time) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(scan_time); + else if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(specific_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bmap |= + NXPWIFI_PASSIVE_SCAN; + else + scan_chan_list[chan_idx].chan_scan_mode_bmap &= + ~NXPWIFI_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_number = (u32)ch->hw_value; + chan_idx++; + } + } + return chan_idx; +} + +/* This function appends rate TLV to scan config command. */ +static int +nxpwifi_append_rate_tlv(struct nxpwifi_private *priv, + struct nxpwifi_scan_cmd_config *scan_cfg_out, + u8 radio) +{ + struct nxpwifi_ie_types_rates_param_set *rates_tlv; + u8 rates[NXPWIFI_SUPPORTED_RATES], *tlv_pos; + u32 rates_size; + + memset(rates, 0, sizeof(rates)); + + tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; + + if (priv->scan_request) + rates_size = nxpwifi_get_rates_from_cfg80211(priv, rates, + radio); + else + rates_size = nxpwifi_get_supported_rates(priv, rates); + + nxpwifi_dbg(priv->adapter, CMD, + "info: SCAN_CMD: Rates size = %d\n", + rates_size); + rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)tlv_pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16)rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; + + return rates_size; +} + +/* This function constructs and sends multiple scan config commands to + * the firmware. + * + * Previous routines in the code flow have created a scan command configuration + * with any requested TLVs. This function splits the channel TLV into maximum + * channels supported per scan lists and sends the portion of the channel TLV, + * along with the other TLVs, to the firmware. + */ +static int +nxpwifi_scan_channel_list(struct nxpwifi_private *priv, + u32 max_chan_per_scan, u8 filtered_scan, + struct nxpwifi_scan_cmd_config *scan_cfg_out, + struct nxpwifi_ie_types_chan_list_param_set *tlv_o, + struct nxpwifi_chan_scan_param_set *scan_chan_list) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret = 0; + struct nxpwifi_chan_scan_param_set *tmp_chan_list; + u32 tlv_idx, rates_size, cmd_no; + u32 total_scan_time; + u32 done_early; + u8 radio_type; + + if (!scan_cfg_out || !tlv_o || !scan_chan_list) { + nxpwifi_dbg(priv->adapter, ERROR, + "info: Scan: Null detect: %p, %p, %p\n", + scan_cfg_out, tlv_o, scan_chan_list); + return -EINVAL; + } + + /* Check csa channel expiry before preparing scan list */ + nxpwifi_11h_get_csa_closed_channel(priv); + + tlv_o->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired + * list + */ + tmp_chan_list = scan_chan_list; + + /* Loop through the desired channel list, sending a new firmware scan + * commands for each max_chan_per_scan channels (or for 1,6,11 + * individually if configured accordingly) + */ + while (tmp_chan_list->chan_number) { + tlv_idx = 0; + total_scan_time = 0; + radio_type = 0; + tlv_o->header.len = 0; + done_early = false; + + /* Construct the Channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlv_idx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired channel + * list) + * - done_early is set (controlling individual scanning of + * 1,6,11) + */ + while (tlv_idx < max_chan_per_scan && + tmp_chan_list->chan_number && !done_early) { + if (tmp_chan_list->chan_number == priv->csa_chan) { + tmp_chan_list++; + continue; + } + + radio_type = tmp_chan_list->radio_type; + nxpwifi_dbg(priv->adapter, INFO, + "info: Scan: Chan(%3d), Radio(%d),\t" + "Mode(%d, %d), Dur(%d)\n", + tmp_chan_list->chan_number, + tmp_chan_list->radio_type, + tmp_chan_list->chan_scan_mode_bmap + & NXPWIFI_PASSIVE_SCAN, + (tmp_chan_list->chan_scan_mode_bmap + & NXPWIFI_DISABLE_CHAN_FILT) >> 1, + le16_to_cpu(tmp_chan_list->max_scan_time)); + + /* Copy the current channel TLV to the command being + * prepared + */ + memcpy(&tlv_o->chan_scan_param[tlv_idx], + tmp_chan_list, + sizeof(*tlv_o->chan_scan_param)); + + /* Increment the TLV header length by the size + * appended + */ + le16_unaligned_add_cpu(&tlv_o->header.len, + sizeof(*tlv_o->chan_scan_param)); + + /* The tlv buffer length is set to the number of bytes + * of the between the channel tlv pointer and the start + * of the tlv buffer. This compensates for any TLVs + * that were appended before the channel list. + */ + scan_cfg_out->tlv_buf_len = + (u32)((u8 *)tlv_o - scan_cfg_out->tlv_buf); + + /* Add the size of the channel tlv header and the data + * length + */ + scan_cfg_out->tlv_buf_len += + (sizeof(tlv_o->header) + + le16_to_cpu(tlv_o->header.len)); + + /* Increment the index to the channel tlv we are + * constructing + */ + tlv_idx++; + + /* Count the total scan time per command */ + total_scan_time += + le16_to_cpu(tmp_chan_list->max_scan_time); + + done_early = false; + + /* Stop the loop if the *current* channel is in the + * 1,6,11 set and we are not filtering on a BSSID + * or SSID. + */ + if (!filtered_scan && + (tmp_chan_list->chan_number == 1 || + tmp_chan_list->chan_number == 6 || + tmp_chan_list->chan_number == 11)) + done_early = true; + + /* Increment the tmp pointer to the next channel to + * be scanned + */ + tmp_chan_list++; + + /* Stop the loop if the *next* channel is in the 1,6,11 + * set. This will cause it to be the only channel + * scanned on the next interation + */ + if (!filtered_scan && + (tmp_chan_list->chan_number == 1 || + tmp_chan_list->chan_number == 6 || + tmp_chan_list->chan_number == 11)) + done_early = true; + } + + /* The total scan time should be less than scan command timeout + * value + */ + if (total_scan_time > NXPWIFI_MAX_TOTAL_SCAN_TIME) { + nxpwifi_dbg(priv->adapter, ERROR, + "total scan time %dms\t" + "is over limit (%dms), scan skipped\n", + total_scan_time, + NXPWIFI_MAX_TOTAL_SCAN_TIME); + ret = -EINVAL; + break; + } + + rates_size = nxpwifi_append_rate_tlv(priv, scan_cfg_out, + radio_type); + + /* Send the scan command to the firmware with the specified + * cfg + */ + if (priv->adapter->ext_scan) + cmd_no = HOST_CMD_802_11_SCAN_EXT; + else + cmd_no = HOST_CMD_802_11_SCAN; + + ret = nxpwifi_send_cmd(priv, cmd_no, HOST_ACT_GEN_SET, + 0, scan_cfg_out, false); + + /* rate IE is updated per scan command but same starting + * pointer is used each time so that rate IE from earlier + * scan_cfg_out->buf is overwritten with new one. + */ + scan_cfg_out->tlv_buf_len -= + sizeof(struct nxpwifi_ie_types_header) + rates_size; + + if (ret) { + nxpwifi_cancel_pending_scan_cmd(adapter); + break; + } + } + + return ret; +} + +/* This function constructs a scan command configuration structure to use + * in scan commands. + * + * Application layer or other functions can invoke network scanning + * with a scan configuration supplied in a user scan configuration structure. + * This structure is used as the basis of one or many scan command configuration + * commands that are sent to the command processing module and eventually to the + * firmware. + * + * This function creates a scan command configuration structure based on the + * following user supplied parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - Channel list + * + * If the SSID or BSSID filter is not present, the filter is disabled/cleared. + * If the number of probes is not set, adapter default setting is used. + */ +static void +nxpwifi_config_scan(struct nxpwifi_private *priv, + const struct nxpwifi_user_scan_cfg *user_scan_in, + struct nxpwifi_scan_cmd_config *scan_cfg_out, + struct nxpwifi_ie_types_chan_list_param_set **chan_list_out, + struct nxpwifi_chan_scan_param_set *scan_chan_list, + u8 *max_chan_per_scan, u8 *filtered_scan, + u8 *scan_current_only) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie_types_num_probes *num_probes_tlv; + struct nxpwifi_ie_types_scan_chan_gap *chan_gap_tlv; + struct nxpwifi_ie_types_random_mac *random_mac_tlv; + struct nxpwifi_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct nxpwifi_ie_types_bssid_list *bssid_tlv; + u8 *tlv_pos; + u32 num_probes; + u32 ssid_len; + u32 chan_idx; + u32 scan_time; + u32 scan_type; + u16 scan_dur; + u8 channel; + u8 radio_type; + int i; + u8 ssid_filter; + struct nxpwifi_ie_types_htcap *ht_cap; + struct nxpwifi_ie_types_bss_mode *bss_mode; + + /* The tlv_buf_len is calculated for each scan command. The TLVs added + * in this routine will be preserved since the routine that sends the + * command will append channelTLVs at *chan_list_out. The difference + * between the *chan_list_out and the tlv_buf start will be used to + * calculate the size of anything we add in this routine. + */ + scan_cfg_out->tlv_buf_len = 0; + + /* Running tlv pointer. Assigned to chan_list_out at end of function + * so later routines know where channels can be added to the command + * buf + */ + tlv_pos = scan_cfg_out->tlv_buf; + + /* Initialize the scan as un-filtered; the flag is later set to TRUE + * below if a SSID or BSSID filter is sent in the command + */ + *filtered_scan = false; + + /* Initialize the scan as not being only on the current channel. If + * the channel list is customized, only contains one channel, and is + * the active channel, this is set true and data flow is not halted. + */ + *scan_current_only = false; + + if (user_scan_in) { + u8 tmpaddr[ETH_ALEN]; + + /* Default the ssid_filter flag to TRUE, set false under + * certain wildcard conditions and qualified by the existence + * of an SSID list before marking the scan as filtered + */ + ssid_filter = true; + + /* Set the BSS type scan filter, use Adapter setting if + * unset + */ + scan_cfg_out->bss_mode = + (u8)(user_scan_in->bss_mode ?: adapter->scan_mode); + + /* Set the number of probes to send, use Adapter setting + * if unset + */ + num_probes = user_scan_in->num_probes ?: adapter->scan_probes; + + /* Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled + * (all zeros). + */ + memcpy(scan_cfg_out->specific_bssid, + user_scan_in->specific_bssid, + sizeof(scan_cfg_out->specific_bssid)); + + memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN); + + if (adapter->ext_scan && + !is_zero_ether_addr(tmpaddr)) { + bssid_tlv = + (struct nxpwifi_ie_types_bssid_list *)tlv_pos; + bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); + memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, + ETH_ALEN); + tlv_pos += sizeof(struct nxpwifi_ie_types_bssid_list); + } + + for (i = 0; i < user_scan_in->num_ssids; i++) { + ssid_len = user_scan_in->ssid_list[i].ssid_len; + + wildcard_ssid_tlv = + (struct nxpwifi_ie_types_wildcard_ssid_params *) + tlv_pos; + wildcard_ssid_tlv->header.type = + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len = + cpu_to_le16((u16)(ssid_len + sizeof(u8))); + + /* max_ssid_length = 0 tells firmware to perform + * specific scan for the SSID filled, whereas + * max_ssid_length = IEEE80211_MAX_SSID_LEN is for + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length = 0; + else + wildcard_ssid_tlv->max_ssid_length = + IEEE80211_MAX_SSID_LEN; + + if (!memcmp(user_scan_in->ssid_list[i].ssid, + "DIRECT-", 7)) + wildcard_ssid_tlv->max_ssid_length = 0xfe; + + memcpy(wildcard_ssid_tlv->ssid, + user_scan_in->ssid_list[i].ssid, ssid_len); + + tlv_pos += (sizeof(wildcard_ssid_tlv->header) + + le16_to_cpu(wildcard_ssid_tlv->header.len)); + + nxpwifi_dbg(adapter, INFO, + "info: scan: ssid[%d]: %s, %d\n", + i, wildcard_ssid_tlv->ssid, + wildcard_ssid_tlv->max_ssid_length); + + /* Empty wildcard ssid with a maxlen will match many or + * potentially all SSIDs (maxlen == 32), therefore do + * not treat the scan as + * filtered. + */ + if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) + ssid_filter = false; + } + + /* The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not + * truncate scan results. That is not an issue with an SSID + * or BSSID filter applied to the scan results in the firmware. + */ + memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN); + if ((i && ssid_filter) || + !is_zero_ether_addr(tmpaddr)) + *filtered_scan = true; + + if (user_scan_in->scan_chan_gap) { + nxpwifi_dbg(adapter, INFO, + "info: scan: channel gap = %d\n", + user_scan_in->scan_chan_gap); + *max_chan_per_scan = + NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN; + + chan_gap_tlv = (void *)tlv_pos; + chan_gap_tlv->header.type = + cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + chan_gap_tlv->header.len = + cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); + chan_gap_tlv->chan_gap = + cpu_to_le16((user_scan_in->scan_chan_gap)); + tlv_pos += + sizeof(struct nxpwifi_ie_types_scan_chan_gap); + } + + if (!is_zero_ether_addr(user_scan_in->random_mac)) { + random_mac_tlv = (void *)tlv_pos; + random_mac_tlv->header.type = + cpu_to_le16(TLV_TYPE_RANDOM_MAC); + random_mac_tlv->header.len = + cpu_to_le16(sizeof(random_mac_tlv->mac)); + ether_addr_copy(random_mac_tlv->mac, + user_scan_in->random_mac); + tlv_pos += + sizeof(struct nxpwifi_ie_types_random_mac); + } + } else { + scan_cfg_out->bss_mode = (u8)adapter->scan_mode; + num_probes = adapter->scan_probes; + } + + /* If a specific BSSID or SSID is used, the number of channels in the + * scan command will be increased to the absolute maximum. + */ + if (*filtered_scan) { + *max_chan_per_scan = NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN; + } else { + if (!priv->media_connected) + *max_chan_per_scan = NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD; + else + *max_chan_per_scan = + NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD / 2; + } + + if (adapter->ext_scan) { + bss_mode = (struct nxpwifi_ie_types_bss_mode *)tlv_pos; + bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE); + bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode)); + bss_mode->bss_mode = scan_cfg_out->bss_mode; + tlv_pos += sizeof(bss_mode->header) + + le16_to_cpu(bss_mode->header.len); + } + + /* If the input config or adapter has the number of Probes set, + * add tlv + */ + if (num_probes) { + nxpwifi_dbg(adapter, INFO, + "info: scan: num_probes = %d\n", + num_probes); + + num_probes_tlv = (struct nxpwifi_ie_types_num_probes *)tlv_pos; + num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len = + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes); + + tlv_pos += sizeof(num_probes_tlv->header) + + le16_to_cpu(num_probes_tlv->header.len); + } + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN)) { + ht_cap = (struct nxpwifi_ie_types_htcap *)tlv_pos; + memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + radio_type = + nxpwifi_band_to_radio_type(priv->config_bands); + nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + tlv_pos += sizeof(struct nxpwifi_ie_types_htcap); + } + + /* Append vendor specific IE TLV */ + nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_SCAN, &tlv_pos); + + /* Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this function (SSID, num_probes). + * Channel TLVs will be added past this for each scan command, + * preserving the TLVs that were previously added. + */ + *chan_list_out = + (struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos; + + if (user_scan_in && user_scan_in->chan_list[0].chan_number) { + nxpwifi_dbg(adapter, INFO, + "info: Scan: Using supplied channel list\n"); + + for (chan_idx = 0; + chan_idx < NXPWIFI_USER_SCAN_CHAN_MAX && + user_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + channel = user_scan_in->chan_list[chan_idx].chan_number; + scan_chan_list[chan_idx].chan_number = channel; + + radio_type = + user_scan_in->chan_list[chan_idx].radio_type; + scan_chan_list[chan_idx].radio_type = radio_type; + + scan_type = user_scan_in->chan_list[chan_idx].scan_type; + + if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE) + scan_chan_list[chan_idx].chan_scan_mode_bmap |= + (NXPWIFI_PASSIVE_SCAN | + NXPWIFI_HIDDEN_SSID_REPORT); + else + scan_chan_list[chan_idx].chan_scan_mode_bmap &= + ~NXPWIFI_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_scan_mode_bmap |= + NXPWIFI_DISABLE_CHAN_FILT; + + scan_time = user_scan_in->chan_list[chan_idx].scan_time; + + if (scan_time) { + scan_dur = (u16)scan_time; + } else { + if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE) + scan_dur = adapter->passive_scan_time; + else if (*filtered_scan) + scan_dur = adapter->specific_scan_time; + else + scan_dur = adapter->active_scan_time; + } + + scan_chan_list[chan_idx].min_scan_time = + cpu_to_le16(scan_dur); + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(scan_dur); + } + + /* Check if we are only scanning the current channel */ + if (chan_idx == 1 && + user_scan_in->chan_list[0].chan_number == + priv->curr_bss_params.bss_descriptor.channel) { + *scan_current_only = true; + nxpwifi_dbg(adapter, INFO, + "info: Scan: Scanning current channel only\n"); + } + } else { + nxpwifi_dbg(adapter, INFO, + "info: Scan: Creating full region channel list\n"); + nxpwifi_scan_create_channel_list(priv, user_scan_in, + scan_chan_list, + *filtered_scan); + } +} + +/* This function inspects the scan response buffer for pointers to + * expected TLVs. + * + * TLVs can be included at the end of the scan response BSS information. + * + * Data in the buffer is parsed pointers to TLVs that can potentially + * be passed back in the response. + */ +static void +nxpwifi_ret_802_11_scan_get_tlv_ptrs(struct nxpwifi_adapter *adapter, + struct nxpwifi_ie_types_data *tlv, + u32 tlv_buf_size, u32 req_tlv_type, + struct nxpwifi_ie_types_data **tlv_data) +{ + struct nxpwifi_ie_types_data *current_tlv; + u32 tlv_buf_left; + u32 tlv_type; + u32 tlv_len; + + current_tlv = tlv; + tlv_buf_left = tlv_buf_size; + *tlv_data = NULL; + + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: tlv_buf_size = %d\n", + tlv_buf_size); + + while (tlv_buf_left >= sizeof(struct nxpwifi_ie_types_header)) { + tlv_type = le16_to_cpu(current_tlv->header.type); + tlv_len = le16_to_cpu(current_tlv->header.len); + + if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { + nxpwifi_dbg(adapter, ERROR, + "SCAN_RESP: TLV buffer corrupt\n"); + break; + } + + if (req_tlv_type == tlv_type) { + switch (tlv_type) { + case TLV_TYPE_TSFTIMESTAMP: + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: TSF\t" + "timestamp TLV, len = %d\n", + tlv_len); + *tlv_data = current_tlv; + break; + case TLV_TYPE_CHANNELBANDLIST: + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: channel\t" + "band list TLV, len = %d\n", + tlv_len); + *tlv_data = current_tlv; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "SCAN_RESP: unhandled TLV = %d\n", + tlv_type); + /* Give up, this seems corrupted */ + return; + } + } + + if (*tlv_data) + break; + + tlv_buf_left -= (sizeof(tlv->header) + tlv_len); + current_tlv = + (struct nxpwifi_ie_types_data *)(current_tlv->data + + tlv_len); + } /* while */ +} + +/* This function parses provided beacon buffer and updates + * respective fields in bss descriptor structure. + */ +int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter, + struct nxpwifi_bssdescriptor *bss_entry) +{ + u8 element_id; + u16 elem_size = sizeof(struct element); + struct ieee_types_fh_param_set *fh_param_set; + struct ieee_types_ds_param_set *ds_param_set; + struct ieee_types_cf_param_set *cf_param_set; + u8 *current_ptr; + u8 *rate; + u8 element_len; + u16 total_ie_len; + u8 bytes_to_copy; + u8 rate_size; + u8 found_data_rate_ie; + u32 bytes_left; + struct ieee_types_vendor_specific *vendor_ie; + const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + found_data_rate_ie = false; + rate_size = 0; + current_ptr = bss_entry->beacon_buf; + bytes_left = bss_entry->beacon_buf_size; + + /* Process variable IE */ + while (bytes_left >= 2) { + element_id = *current_ptr; + element_len = *(current_ptr + 1); + total_ie_len = element_len + elem_size; + + if (bytes_left < total_ie_len) { + nxpwifi_dbg(adapter, ERROR, + "err: InterpretIE: in processing\t" + "IE, bytes left < IE length\n"); + return -EINVAL; + } + switch (element_id) { + case WLAN_EID_SSID: + if (element_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + bss_entry->ssid.ssid_len = element_len; + memcpy(bss_entry->ssid.ssid, (current_ptr + 2), + element_len); + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: ssid: %-32s\n", + bss_entry->ssid.ssid); + break; + + case WLAN_EID_SUPP_RATES: + if (element_len > NXPWIFI_SUPPORTED_RATES) + return -EINVAL; + memcpy(bss_entry->data_rates, current_ptr + 2, + element_len); + memcpy(bss_entry->supported_rates, current_ptr + 2, + element_len); + rate_size = element_len; + found_data_rate_ie = true; + break; + + case WLAN_EID_FH_PARAMS: + if (total_ie_len < sizeof(*fh_param_set)) + return -EINVAL; + fh_param_set = + (struct ieee_types_fh_param_set *)current_ptr; + memcpy(&bss_entry->phy_param_set.fh_param_set, + fh_param_set, + sizeof(struct ieee_types_fh_param_set)); + break; + + case WLAN_EID_DS_PARAMS: + if (total_ie_len < sizeof(*ds_param_set)) + return -EINVAL; + ds_param_set = + (struct ieee_types_ds_param_set *)current_ptr; + + bss_entry->channel = ds_param_set->current_chan; + + memcpy(&bss_entry->phy_param_set.ds_param_set, + ds_param_set, + sizeof(struct ieee_types_ds_param_set)); + break; + + case WLAN_EID_CF_PARAMS: + if (total_ie_len < sizeof(*cf_param_set)) + return -EINVAL; + cf_param_set = + (struct ieee_types_cf_param_set *)current_ptr; + memcpy(&bss_entry->cf_param_set, + cf_param_set, + sizeof(struct ieee_types_cf_param_set)); + break; + + case WLAN_EID_ERP_INFO: + if (!element_len) + return -EINVAL; + bss_entry->erp_flags = *(current_ptr + 2); + break; + + case WLAN_EID_PWR_CONSTRAINT: + if (!element_len) + return -EINVAL; + bss_entry->local_constraint = *(current_ptr + 2); + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_CHANNEL_SWITCH: + bss_entry->chan_sw_ie_present = true; + fallthrough; + case WLAN_EID_PWR_CAPABILITY: + case WLAN_EID_TPC_REPORT: + case WLAN_EID_QUIET: + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_EXT_SUPP_RATES: + /* Only process extended supported rate + * if data rate is already found. + * Data rate IE should come before + * extended supported rate IE + */ + if (found_data_rate_ie) { + if ((element_len + rate_size) > + NXPWIFI_SUPPORTED_RATES) + bytes_to_copy = + (NXPWIFI_SUPPORTED_RATES - + rate_size); + else + bytes_to_copy = element_len; + + rate = (u8 *)bss_entry->data_rates; + rate += rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + + rate = (u8 *)bss_entry->supported_rates; + rate += rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + } + break; + + case WLAN_EID_VENDOR_SPECIFIC: + vendor_ie = (struct ieee_types_vendor_specific *) + current_ptr; + + /* 802.11 requires at least 3-byte OUI. */ + if (element_len < sizeof(vendor_ie->vend_hdr.oui)) + return -EINVAL; + + /* Not long enough for a match? Skip it. */ + if (element_len < sizeof(wpa_oui)) + break; + + if (!memcmp(&vendor_ie->vend_hdr.oui, wpa_oui, + sizeof(wpa_oui))) { + bss_entry->bcn_wpa_ie = + (struct ieee_types_vendor_specific *) + current_ptr; + bss_entry->wpa_offset = + (u16)(current_ptr - + bss_entry->beacon_buf); + } else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui))) { + if (total_ie_len == + sizeof(struct ieee80211_wmm_param_ie) || + total_ie_len == + sizeof(struct ieee_types_wmm_info)) + /* Only accept and copy the WMM IE if + * it matches the size expected for the + * WMM Info IE or the WMM Parameter IE. + */ + memcpy((u8 *)&bss_entry->wmm_ie, + current_ptr, total_ie_len); + } + break; + case WLAN_EID_RSN: + bss_entry->bcn_rsn_ie = + (struct element *)current_ptr; + bss_entry->rsn_offset = + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_RSNX: + bss_entry->bcn_rsnx_ie = + (struct element *)current_ptr; + bss_entry->rsnx_offset = + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_HT_CAPABILITY: + bss_entry->bcn_ht_cap = + (struct ieee80211_ht_cap *)(current_ptr + + elem_size); + bss_entry->ht_cap_offset = + (u16)(current_ptr + elem_size - + bss_entry->beacon_buf); + break; + case WLAN_EID_HT_OPERATION: + bss_entry->bcn_ht_oper = + (struct ieee80211_ht_operation *)(current_ptr + + elem_size); + bss_entry->ht_info_offset = + (u16)(current_ptr + elem_size - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_CAPABILITY: + bss_entry->disable_11ac = false; + bss_entry->bcn_vht_cap = (void *)(current_ptr + + elem_size); + bss_entry->vht_cap_offset = + (u16)((u8 *)bss_entry->bcn_vht_cap - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_OPERATION: + bss_entry->bcn_vht_oper = + (void *)(current_ptr + elem_size); + bss_entry->vht_info_offset = + (u16)((u8 *)bss_entry->bcn_vht_oper - + bss_entry->beacon_buf); + break; + case WLAN_EID_BSS_COEX_2040: + bss_entry->bcn_bss_co_2040 = current_ptr; + bss_entry->bss_co_2040_offset = + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_EXT_CAPABILITY: + bss_entry->bcn_ext_cap = current_ptr; + bss_entry->ext_cap_offset = + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_OPMODE_NOTIF: + bss_entry->oper_mode = (void *)current_ptr; + bss_entry->oper_mode_offset = + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_EXTENSION: + struct element *elem = (struct element *)current_ptr; + + switch (elem->data[0]) { + case WLAN_EID_EXT_HE_CAPABILITY: + bss_entry->disable_11ax = false; + bss_entry->bcn_he_cap = + (void *)(current_ptr + elem_size + 1); + bss_entry->he_cap_offset = + (u16)((u8 *)bss_entry->bcn_he_cap - + bss_entry->beacon_buf); + break; + case WLAN_EID_EXT_HE_OPERATION: + bss_entry->bcn_he_oper = + (void *)(current_ptr + elem_size + 1); + bss_entry->he_info_offset = + (u16)((u8 *)bss_entry->bcn_he_oper - + bss_entry->beacon_buf); + default: + break; + } + break; + default: + break; + } + + current_ptr += total_ie_len; + bytes_left -= total_ie_len; + + } /* while (bytes_left > 2) */ + return 0; +} + +/* This function converts radio type scan parameter to a band configuration + * to be used in join command. + */ +static u8 +nxpwifi_radio_type_to_band(u8 radio_type) +{ + switch (radio_type) { + case HOST_SCAN_RADIO_TYPE_A: + return BAND_A; + case HOST_SCAN_RADIO_TYPE_BG: + default: + return BAND_G; + } +} + +/* This is an internal function used to start a scan based on an input + * configuration. + * + * This uses the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table. + */ +int nxpwifi_scan_networks(struct nxpwifi_private *priv, + const struct nxpwifi_user_scan_cfg *user_scan_in) +{ + int ret; + struct nxpwifi_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + union nxpwifi_scan_cmd_config_tlv *scan_cfg_out; + struct nxpwifi_ie_types_chan_list_param_set *chan_list_out; + struct nxpwifi_chan_scan_param_set *scan_chan_list; + u8 filtered_scan; + u8 scan_current_chan_only; + u8 max_chan_per_scan; + + if (adapter->scan_processing) { + nxpwifi_dbg(adapter, WARN, + "cmd: Scan already in process...\n"); + return -EBUSY; + } + + if (priv->scan_block) { + nxpwifi_dbg(adapter, WARN, + "cmd: Scan is blocked during association...\n"); + return -EBUSY; + } + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) || + test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) { + nxpwifi_dbg(adapter, ERROR, + "Ignore scan. Card removed or firmware in bad state\n"); + return -EPERM; + } + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->scan_processing = true; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + scan_cfg_out = kzalloc(sizeof(union nxpwifi_scan_cmd_config_tlv), + GFP_KERNEL); + if (!scan_cfg_out) { + ret = -ENOMEM; + goto done; + } + + scan_chan_list = kcalloc(NXPWIFI_USER_SCAN_CHAN_MAX, + sizeof(struct nxpwifi_chan_scan_param_set), + GFP_KERNEL); + if (!scan_chan_list) { + kfree(scan_cfg_out); + ret = -ENOMEM; + goto done; + } + + nxpwifi_config_scan(priv, user_scan_in, &scan_cfg_out->config, + &chan_list_out, scan_chan_list, &max_chan_per_scan, + &filtered_scan, &scan_current_chan_only); + + ret = nxpwifi_scan_channel_list(priv, max_chan_per_scan, filtered_scan, + &scan_cfg_out->config, chan_list_out, + scan_chan_list); + + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + if (!ret) { + spin_lock_bh(&adapter->scan_pending_q_lock); + if (!list_empty(&adapter->scan_pending_q)) { + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_bh(&adapter->scan_pending_q_lock); + nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node); + nxpwifi_queue_work(adapter, &adapter->main_work); + + /* Perform internal scan synchronously */ + if (!priv->scan_request) { + nxpwifi_dbg(adapter, INFO, + "wait internal scan\n"); + nxpwifi_wait_queue_complete(adapter, cmd_node); + } + } else { + spin_unlock_bh(&adapter->scan_pending_q_lock); + } + } + + kfree(scan_cfg_out); + kfree(scan_chan_list); +done: + if (ret) { + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->scan_processing = false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + } + return ret; +} + +/* This function prepares a scan command to be sent to the firmware. + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a scan command structure + * to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * Preparation also includes - + * - Setting command ID, and proper size + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_802_11_scan(struct host_cmd_ds_command *cmd, + struct nxpwifi_scan_cmd_config *scan_cfg) +{ + struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; + + /* Set fixed field variables in scan command */ + scan_cmd->bss_mode = scan_cfg->bss_mode; + memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, + sizeof(scan_cmd->bssid)); + memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HOST_CMD_802_11_SCAN); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16)(sizeof(scan_cmd->bss_mode) + + sizeof(scan_cmd->bssid) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* This function checks compatibility of requested network with current + * driver settings. + */ +int nxpwifi_check_network_compatibility(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + int ret = 0; + + if (!bss_desc) + return -EINVAL; + + if ((nxpwifi_get_cfp(priv, (u8)bss_desc->bss_band, + (u16)bss_desc->channel, 0))) { + switch (priv->bss_mode) { + case NL80211_IFTYPE_STATION: + ret = nxpwifi_is_network_compatible(priv, bss_desc, + priv->bss_mode); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "Incompatible network settings\n"); + break; + default: + ret = 0; + } + } + + return ret; +} + +/* This function checks if SSID string contains all zeroes or length is zero */ +static bool nxpwifi_is_hidden_ssid(struct cfg80211_ssid *ssid) +{ + int idx; + + for (idx = 0; idx < ssid->ssid_len; idx++) { + if (ssid->ssid[idx]) + return false; + } + + return true; +} + +/* This function checks if any hidden SSID found in passive scan channels + * and save those channels for specific SSID active scan + */ +static int nxpwifi_save_hidden_ssid_channels(struct nxpwifi_private *priv, + struct cfg80211_bss *bss) +{ + struct nxpwifi_bssdescriptor *bss_desc; + int ret; + int chid; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + if (nxpwifi_is_hidden_ssid(&bss_desc->ssid)) { + nxpwifi_dbg(priv->adapter, INFO, "found hidden SSID\n"); + for (chid = 0 ; chid < NXPWIFI_USER_SCAN_CHAN_MAX; chid++) { + if (priv->hidden_chan[chid].chan_number == + bss->channel->hw_value) + break; + + if (!priv->hidden_chan[chid].chan_number) { + priv->hidden_chan[chid].chan_number = + bss->channel->hw_value; + priv->hidden_chan[chid].radio_type = + bss->channel->band; + priv->hidden_chan[chid].scan_type = + NXPWIFI_SCAN_TYPE_ACTIVE; + break; + } + } + } + +done: + /* beacon_ie buffer was allocated in function + * nxpwifi_fill_new_bss_desc(). Free it now. + */ + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return ret; +} + +static int nxpwifi_update_curr_bss_params(struct nxpwifi_private *priv, + struct cfg80211_bss *bss) +{ + struct nxpwifi_bssdescriptor *bss_desc; + int ret; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + ret = nxpwifi_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + spin_lock_bh(&priv->curr_bcn_buf_lock); + /* Make a copy of current BSSID descriptor */ + memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, + sizeof(priv->curr_bss_params.bss_descriptor)); + + /* The contents of beacon_ie will be copied to its own buffer + * in nxpwifi_save_curr_bcn() + */ + nxpwifi_save_curr_bcn(priv); + spin_unlock_bh(&priv->curr_bcn_buf_lock); + +done: + /* beacon_ie buffer was allocated in function + * nxpwifi_fill_new_bss_desc(). Free it now. + */ + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return ret; +} + +static int +nxpwifi_parse_single_response_buf(struct nxpwifi_private *priv, u8 **bss_info, + u32 *bytes_left, u64 fw_tsf, u8 *radio_type, + bool ext_scan, s32 rssi_val) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_chan_freq_power *cfp; + struct cfg80211_bss *bss; + u8 bssid[ETH_ALEN]; + s32 rssi; + const u8 *ie_buf; + size_t ie_len; + u16 channel = 0; + u16 beacon_size = 0; + u32 curr_bcn_bytes; + u32 freq; + u16 beacon_period; + u16 cap_info_bitmap; + u8 *current_ptr; + u64 timestamp; + struct nxpwifi_fixed_bcn_param *bcn_param; + struct nxpwifi_bss_priv *bss_priv; + + if (*bytes_left >= sizeof(beacon_size)) { + /* Extract & convert beacon size from command buffer */ + beacon_size = get_unaligned_le16((*bss_info)); + *bytes_left -= sizeof(beacon_size); + *bss_info += sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + *bss_info += *bytes_left; + *bytes_left = 0; + return -EINVAL; + } + + /* Initialize the current working beacon pointer for this BSS + * iteration + */ + current_ptr = *bss_info; + + /* Advance the return beacon pointer past the current beacon */ + *bss_info += beacon_size; + *bytes_left -= beacon_size; + + curr_bcn_bytes = beacon_size; + + /* First 5 fields are bssid, RSSI(for legacy scan only), + * time stamp, beacon interval, and capability information + */ + if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + + sizeof(struct nxpwifi_fixed_bcn_param)) { + nxpwifi_dbg(adapter, ERROR, + "InterpretIE: not enough bytes left\n"); + return -EINVAL; + } + + memcpy(bssid, current_ptr, ETH_ALEN); + current_ptr += ETH_ALEN; + curr_bcn_bytes -= ETH_ALEN; + + if (!ext_scan) { + rssi = (s32)*current_ptr; + rssi = (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr += sizeof(u8); + curr_bcn_bytes -= sizeof(u8); + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: RSSI=%d\n", rssi); + } else { + rssi = rssi_val; + } + + bcn_param = (struct nxpwifi_fixed_bcn_param *)current_ptr; + current_ptr += sizeof(*bcn_param); + curr_bcn_bytes -= sizeof(*bcn_param); + + timestamp = le64_to_cpu(bcn_param->timestamp); + beacon_period = le16_to_cpu(bcn_param->beacon_period); + + cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: capabilities=0x%X\n", + cap_info_bitmap); + + /* Rest of the current buffer are IE's */ + ie_buf = current_ptr; + ie_len = curr_bcn_bytes; + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: IELength for this AP = %d\n", + curr_bcn_bytes); + + while (curr_bcn_bytes >= sizeof(struct element)) { + u8 element_id, element_len; + + element_id = *current_ptr; + element_len = *(current_ptr + 1); + if (curr_bcn_bytes < element_len + + sizeof(struct element)) { + nxpwifi_dbg(adapter, ERROR, + "%s: bytes left < IE length\n", __func__); + return -EFAULT; + } + if (element_id == WLAN_EID_DS_PARAMS) { + channel = *(current_ptr + + sizeof(struct element)); + break; + } + + current_ptr += element_len + sizeof(struct element); + curr_bcn_bytes -= element_len + + sizeof(struct element); + } + + if (channel) { + struct ieee80211_channel *chan; + struct nxpwifi_bssdescriptor *bss_desc; + u8 band; + + /* Skip entry if on csa closed channel */ + if (channel == priv->csa_chan) { + nxpwifi_dbg(adapter, WARN, + "Dropping entry on csa closed channel\n"); + return 0; + } + + band = BAND_G; + if (radio_type) + band = nxpwifi_radio_type_to_band(*radio_type & + (BIT(0) | BIT(1))); + + cfp = nxpwifi_get_cfp(priv, band, channel, 0); + + freq = cfp ? cfp->freq : 0; + + chan = ieee80211_get_channel(priv->wdev.wiphy, freq); + + if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, + CFG80211_BSS_FTYPE_UNKNOWN, + bssid, timestamp, + cap_info_bitmap, + beacon_period, + ie_buf, ie_len, rssi, + GFP_ATOMIC); + if (bss) { + bss_priv = (struct nxpwifi_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + bss_desc = + &priv->curr_bss_params.bss_descriptor; + if (priv->media_connected && + !memcmp(bssid, bss_desc->mac_address, + ETH_ALEN)) + nxpwifi_update_curr_bss_params(priv, + bss); + + if ((chan->flags & IEEE80211_CHAN_RADAR) || + (chan->flags & IEEE80211_CHAN_NO_IR)) { + nxpwifi_dbg(adapter, INFO, + "radar or passive channel %d\n", + channel); + nxpwifi_save_hidden_ssid_channels(priv, + bss); + } + + cfg80211_put_bss(priv->wdev.wiphy, bss); + } + } + } else { + nxpwifi_dbg(adapter, WARN, "missing BSS channel IE\n"); + } + + return 0; +} + +static void nxpwifi_complete_scan(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->survey_idx = 0; + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = 0; + if (!priv->scan_request) { + nxpwifi_dbg(adapter, INFO, + "complete internal scan\n"); + nxpwifi_complete_cmd(adapter, adapter->curr_cmd); + } + } +} + +/* This function checks if any hidden SSID found in passive scan channels + * and do specific SSID active scan for those channels + */ +static int +nxpwifi_active_scan_req_for_passive_chan(struct nxpwifi_private *priv) +{ + int ret; + struct nxpwifi_adapter *adapter = priv->adapter; + u8 id = 0; + struct nxpwifi_user_scan_cfg *user_scan_cfg; + + if (adapter->active_scan_triggered || !priv->scan_request || + priv->scan_aborting) { + adapter->active_scan_triggered = false; + return 0; + } + + if (!priv->hidden_chan[0].chan_number) { + nxpwifi_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); + return 0; + } + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + + if (!user_scan_cfg) + return -ENOMEM; + + for (id = 0; id < NXPWIFI_USER_SCAN_CHAN_MAX; id++) { + if (!priv->hidden_chan[id].chan_number) + break; + memcpy(&user_scan_cfg->chan_list[id], + &priv->hidden_chan[id], + sizeof(struct nxpwifi_user_scan_chan)); + } + + adapter->active_scan_triggered = true; + if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + ether_addr_copy(user_scan_cfg->random_mac, + priv->scan_request->mac_addr); + user_scan_cfg->num_ssids = priv->scan_request->n_ssids; + user_scan_cfg->ssid_list = priv->scan_request->ssids; + + ret = nxpwifi_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + + memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); + + if (ret) + dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + + return ret; +} + +static void nxpwifi_check_next_scan_command(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + + spin_lock_bh(&adapter->scan_pending_q_lock); + if (list_empty(&adapter->scan_pending_q)) { + spin_unlock_bh(&adapter->scan_pending_q_lock); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->scan_processing = false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + nxpwifi_active_scan_req_for_passive_chan(priv); + + if (!adapter->ext_scan) + nxpwifi_complete_scan(priv); + + if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + nxpwifi_dbg(adapter, INFO, + "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request = NULL; + priv->scan_aborting = false; + } else { + priv->scan_aborting = false; + nxpwifi_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } else if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { + spin_unlock_bh(&adapter->scan_pending_q_lock); + + nxpwifi_cancel_pending_scan_cmd(adapter); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->scan_processing = false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + if (!adapter->active_scan_triggered) { + if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + nxpwifi_dbg(adapter, INFO, + "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request = NULL; + priv->scan_aborting = false; + } else { + priv->scan_aborting = false; + nxpwifi_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } + } else { + /* Get scan command from scan_pending_q and put to + * cmd_pending_q + */ + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_bh(&adapter->scan_pending_q_lock); + nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node); + } +} + +void nxpwifi_cancel_scan(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + int i; + + nxpwifi_cancel_pending_scan_cmd(adapter); + + if (adapter->scan_processing) { + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->scan_processing = false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + nxpwifi_dbg(adapter, INFO, + "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request = NULL; + priv->scan_aborting = false; + } + } + } +} + +/* This function handles the command response of scan. + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-------------------------------------------------------------. + * | Header (4 * sizeof(t_u16)): Standard command response hdr | + * .-------------------------------------------------------------. + * | BufSize (t_u16) : sizeof the BSS Description data | + * .-------------------------------------------------------------. + * | NumOfSet (t_u8) : Number of BSS Descs returned | + * .-------------------------------------------------------------. + * | BSSDescription data (variable, size given in BufSize) | + * .-------------------------------------------------------------. + * | TLV data (variable, size calculated using Header->Size, | + * | BufSize and sizeof the fixed fields above) | + * .-------------------------------------------------------------. + */ +int nxpwifi_ret_802_11_scan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_scan_rsp *scan_rsp; + struct nxpwifi_ie_types_data *tlv_data; + struct nxpwifi_ie_types_tsf_timestamp *tsf_tlv; + u8 *bss_info; + u32 scan_resp_size; + u32 bytes_left; + u32 idx; + u32 tlv_buf_size; + struct nxpwifi_ie_types_chan_band_list_param_set *chan_band_tlv; + struct chan_band_param_set *chan_band; + u8 is_bgscan_resp; + __le64 fw_tsf = 0; + u8 *radio_type; + struct cfg80211_wowlan_nd_match *pmatch; + struct cfg80211_sched_scan_request *nd_config = NULL; + + is_bgscan_resp = (le16_to_cpu(resp->command) + == HOST_CMD_802_11_BG_SCAN_QUERY); + if (is_bgscan_resp) + scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; + else + scan_rsp = &resp->params.scan_resp; + + if (scan_rsp->number_of_sets > NXPWIFI_MAX_AP) { + nxpwifi_dbg(adapter, ERROR, + "SCAN_RESP: too many AP returned (%d)\n", + scan_rsp->number_of_sets); + ret = -EINVAL; + goto check_next_scan; + } + + /* Check csa channel expiry before parsing scan response */ + nxpwifi_11h_get_csa_closed_channel(priv); + + bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: bss_descript_size %d\n", + bytes_left); + + scan_resp_size = le16_to_cpu(resp->size); + + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: returned %d APs before parsing\n", + scan_rsp->number_of_sets); + + bss_info = scan_rsp->bss_desc_and_tlv_buffer; + + /* The size of the TLV buffer is equal to the entire command response + * size (scan_resp_size) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bss_descript_size as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlv_buf_size = scan_resp_size - (bytes_left + + sizeof(scan_rsp->bss_descript_size) + + sizeof(scan_rsp->number_of_sets) + + S_DS_GEN); + + tlv_data = (struct nxpwifi_ie_types_data *) + (scan_rsp->bss_desc_and_tlv_buffer + bytes_left); + + /* Search the TLV buffer space in the scan response for any valid + * TLVs + */ + nxpwifi_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, + TLV_TYPE_TSFTIMESTAMP, + (struct nxpwifi_ie_types_data **) + &tsf_tlv); + + /* Search the TLV buffer space in the scan response for any valid + * TLVs + */ + nxpwifi_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, + TLV_TYPE_CHANNELBANDLIST, + (struct nxpwifi_ie_types_data **) + &chan_band_tlv); + +#ifdef CONFIG_PM + if (priv->wdev.wiphy->wowlan_config) + nd_config = priv->wdev.wiphy->wowlan_config->nd_config; +#endif + + if (nd_config) { + adapter->nd_info = + kzalloc(struct_size(adapter->nd_info, matches, + scan_rsp->number_of_sets), + GFP_ATOMIC); + + if (adapter->nd_info) + adapter->nd_info->n_matches = scan_rsp->number_of_sets; + } + + for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { + /* If the TSF TLV was appended to the scan results, save this + * entry's TSF value in the fw_tsf field. It is the firmware's + * TSF value at the time the beacon or probe response was + * received. + */ + if (tsf_tlv) + memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(fw_tsf)); + + if (chan_band_tlv) { + chan_band = &chan_band_tlv->chan_band_param[idx]; + radio_type = &chan_band->radio_type; + } else { + radio_type = NULL; + } + + if (chan_band_tlv && adapter->nd_info) { + adapter->nd_info->matches[idx] = + kzalloc(sizeof(*pmatch) + sizeof(u32), + GFP_ATOMIC); + + pmatch = adapter->nd_info->matches[idx]; + + if (pmatch) { + pmatch->n_channels = 1; + pmatch->channels[0] = chan_band->chan_number; + } + } + + ret = nxpwifi_parse_single_response_buf(priv, &bss_info, + &bytes_left, + le64_to_cpu(fw_tsf), + radio_type, false, 0); + if (ret) + goto check_next_scan; + } + +check_next_scan: + nxpwifi_check_next_scan_command(priv); + return ret; +} + +/* This function prepares an extended scan command to be sent to the firmware + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a extended scan command + * structure to send to firmware. + */ +int nxpwifi_cmd_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; + struct nxpwifi_scan_cmd_config *scan_cfg = data_buf; + + memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HOST_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* This function prepares an background scan config command to be sent + * to the firmware + */ +int nxpwifi_cmd_802_11_bg_scan_config(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_bg_scan_config *bgscan_config = + &cmd->params.bg_scan_config; + struct nxpwifi_bg_scan_cfg *bgscan_cfg_in = data_buf; + u8 *tlv_pos = bgscan_config->tlv; + u8 num_probes; + u32 ssid_len, chan_idx, scan_time, scan_type, scan_dur, chan_num; + int i; + struct nxpwifi_ie_types_num_probes *num_probes_tlv; + struct nxpwifi_ie_types_repeat_count *repeat_count_tlv; + struct nxpwifi_ie_types_min_rssi_threshold *rssi_threshold_tlv; + struct nxpwifi_ie_types_bgscan_start_later *start_later_tlv; + struct nxpwifi_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct nxpwifi_ie_types_chan_list_param_set *tlv_l; + struct nxpwifi_chan_scan_param_set *temp_chan; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_BG_SCAN_CONFIG); + cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN); + + bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action); + bgscan_config->enable = bgscan_cfg_in->enable; + bgscan_config->bss_type = bgscan_cfg_in->bss_type; + bgscan_config->scan_interval = + cpu_to_le32(bgscan_cfg_in->scan_interval); + bgscan_config->report_condition = + cpu_to_le32(bgscan_cfg_in->report_condition); + + /* stop sched scan */ + if (!bgscan_config->enable) + return 0; + + bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan; + + num_probes = (bgscan_cfg_in->num_probes ? + bgscan_cfg_in->num_probes : priv->adapter->scan_probes); + + if (num_probes) { + num_probes_tlv = (struct nxpwifi_ie_types_num_probes *)tlv_pos; + num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len = + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes); + + tlv_pos += sizeof(num_probes_tlv->header) + + le16_to_cpu(num_probes_tlv->header.len); + } + + if (bgscan_cfg_in->repeat_count) { + repeat_count_tlv = + (struct nxpwifi_ie_types_repeat_count *)tlv_pos; + repeat_count_tlv->header.type = + cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + repeat_count_tlv->header.len = + cpu_to_le16(sizeof(repeat_count_tlv->repeat_count)); + repeat_count_tlv->repeat_count = + cpu_to_le16(bgscan_cfg_in->repeat_count); + + tlv_pos += sizeof(repeat_count_tlv->header) + + le16_to_cpu(repeat_count_tlv->header.len); + } + + if (bgscan_cfg_in->rssi_threshold) { + rssi_threshold_tlv = + (struct nxpwifi_ie_types_min_rssi_threshold *)tlv_pos; + rssi_threshold_tlv->header.type = + cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_threshold_tlv->header.len = + cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold)); + rssi_threshold_tlv->rssi_threshold = + cpu_to_le16(bgscan_cfg_in->rssi_threshold); + + tlv_pos += sizeof(rssi_threshold_tlv->header) + + le16_to_cpu(rssi_threshold_tlv->header.len); + } + + for (i = 0; i < bgscan_cfg_in->num_ssids; i++) { + ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len; + + wildcard_ssid_tlv = + (struct nxpwifi_ie_types_wildcard_ssid_params *)tlv_pos; + wildcard_ssid_tlv->header.type = + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len = + cpu_to_le16((u16)(ssid_len + sizeof(u8))); + + /* max_ssid_length = 0 tells firmware to perform + * specific scan for the SSID filled, whereas + * max_ssid_length = IEEE80211_MAX_SSID_LEN is for + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length = 0; + else + wildcard_ssid_tlv->max_ssid_length = + IEEE80211_MAX_SSID_LEN; + + memcpy(wildcard_ssid_tlv->ssid, + bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len); + + tlv_pos += (sizeof(wildcard_ssid_tlv->header) + + le16_to_cpu(wildcard_ssid_tlv->header.len)); + } + + tlv_l = (struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos; + + if (bgscan_cfg_in->chan_list[0].chan_number) { + dev_dbg(priv->adapter->dev, "info: bgscan: Using supplied channel list\n"); + + tlv_l->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + for (chan_idx = 0; + chan_idx < NXPWIFI_BG_SCAN_CHAN_MAX && + bgscan_cfg_in->chan_list[chan_idx].chan_number; + chan_idx++) { + temp_chan = &tlv_l->chan_scan_param[chan_idx]; + + /* Increment the TLV header length by size appended */ + le16_unaligned_add_cpu(&tlv_l->header.len, + sizeof(*tlv_l->chan_scan_param)); + + temp_chan->chan_number = + bgscan_cfg_in->chan_list[chan_idx].chan_number; + temp_chan->radio_type = + bgscan_cfg_in->chan_list[chan_idx].radio_type; + + scan_type = + bgscan_cfg_in->chan_list[chan_idx].scan_type; + + if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE) + temp_chan->chan_scan_mode_bmap |= + NXPWIFI_PASSIVE_SCAN; + else + temp_chan->chan_scan_mode_bmap &= + ~NXPWIFI_PASSIVE_SCAN; + + scan_time = bgscan_cfg_in->chan_list[chan_idx].scan_time; + + if (scan_time) { + scan_dur = (u16)scan_time; + } else { + scan_dur = (scan_type == + NXPWIFI_SCAN_TYPE_PASSIVE) ? + priv->adapter->passive_scan_time : + priv->adapter->specific_scan_time; + } + + temp_chan->min_scan_time = cpu_to_le16(scan_dur); + temp_chan->max_scan_time = cpu_to_le16(scan_dur); + } + } else { + dev_dbg(priv->adapter->dev, + "info: bgscan: Creating full region channel list\n"); + chan_num = + nxpwifi_bgscan_create_channel_list + (priv, bgscan_cfg_in, + tlv_l->chan_scan_param); + le16_unaligned_add_cpu(&tlv_l->header.len, + chan_num * + sizeof(*tlv_l->chan_scan_param)); + } + + tlv_pos += (sizeof(tlv_l->header) + + le16_to_cpu(tlv_l->header.len)); + + if (bgscan_cfg_in->start_later) { + start_later_tlv = + (struct nxpwifi_ie_types_bgscan_start_later *)tlv_pos; + start_later_tlv->header.type = + cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER); + start_later_tlv->header.len = + cpu_to_le16(sizeof(start_later_tlv->start_later)); + start_later_tlv->start_later = + cpu_to_le16(bgscan_cfg_in->start_later); + + tlv_pos += sizeof(start_later_tlv->header) + + le16_to_cpu(start_later_tlv->header.len); + } + + /* Append vendor specific IE TLV */ + nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_BGSCAN, &tlv_pos); + + le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv); + + return 0; +} + +int nxpwifi_stop_bg_scan(struct nxpwifi_private *priv) +{ + struct nxpwifi_bg_scan_cfg *bgscan_cfg; + int ret; + + if (!priv->sched_scanning) { + dev_dbg(priv->adapter->dev, "bgscan already stopped!\n"); + return 0; + } + + bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); + if (!bgscan_cfg) + return -ENOMEM; + + bgscan_cfg->bss_type = NXPWIFI_BSS_MODE_INFRA; + bgscan_cfg->action = NXPWIFI_BGSCAN_ACT_SET; + bgscan_cfg->enable = false; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG, + HOST_ACT_GEN_SET, 0, bgscan_cfg, true); + if (!ret) + priv->sched_scanning = false; + + kfree(bgscan_cfg); + return ret; +} + +static void +nxpwifi_update_chan_statistics(struct nxpwifi_private *priv, + struct nxpwifi_ietypes_chanstats *tlv_stat) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u8 i, num_chan; + struct nxpwifi_fw_chan_stats *fw_chan_stats; + struct nxpwifi_chan_stats chan_stats; + + fw_chan_stats = (void *)((u8 *)tlv_stat + + sizeof(struct nxpwifi_ie_types_header)); + num_chan = le16_to_cpu(tlv_stat->header.len) / + sizeof(struct nxpwifi_chan_stats); + + for (i = 0 ; i < num_chan; i++) { + if (adapter->survey_idx >= adapter->num_in_chan_stats) { + nxpwifi_dbg(adapter, WARN, + "FW reported too many channel results (max %d)\n", + adapter->num_in_chan_stats); + return; + } + chan_stats.chan_num = fw_chan_stats->chan_num; + chan_stats.bandcfg = fw_chan_stats->bandcfg; + chan_stats.flags = fw_chan_stats->flags; + chan_stats.noise = fw_chan_stats->noise; + chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss); + chan_stats.cca_scan_dur = + le16_to_cpu(fw_chan_stats->cca_scan_dur); + chan_stats.cca_busy_dur = + le16_to_cpu(fw_chan_stats->cca_busy_dur); + nxpwifi_dbg(adapter, INFO, + "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", + chan_stats.chan_num, + chan_stats.noise, + chan_stats.total_bss, + chan_stats.cca_scan_dur, + chan_stats.cca_busy_dur); + memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats, + sizeof(struct nxpwifi_chan_stats)); + fw_chan_stats++; + } +} + +/* This function handles the command response of extended scan */ +int nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_scan_ext *ext_scan_resp; + struct nxpwifi_ie_types_header *tlv; + struct nxpwifi_ietypes_chanstats *tlv_stat; + u16 buf_left, type, len; + + struct host_cmd_ds_command *cmd_ptr; + struct cmd_ctrl_node *cmd_node; + bool complete_scan = false; + + nxpwifi_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); + + ext_scan_resp = &resp->params.ext_scan; + + tlv = (void *)ext_scan_resp->tlv_buffer; + buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN); + + while (buf_left >= sizeof(struct nxpwifi_ie_types_header)) { + type = le16_to_cpu(tlv->type); + len = le16_to_cpu(tlv->len); + + if (buf_left < (sizeof(struct nxpwifi_ie_types_header) + len)) { + nxpwifi_dbg(adapter, ERROR, + "error processing scan response TLVs"); + break; + } + + switch (type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_stat = (void *)tlv; + nxpwifi_update_chan_statistics(priv, tlv_stat); + break; + default: + break; + } + + buf_left -= len + sizeof(struct nxpwifi_ie_types_header); + tlv = (void *)((u8 *)tlv + len + + sizeof(struct nxpwifi_ie_types_header)); + } + + spin_lock_bh(&adapter->cmd_pending_q_lock); + spin_lock_bh(&adapter->scan_pending_q_lock); + if (list_empty(&adapter->scan_pending_q)) { + complete_scan = true; + list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { + cmd_ptr = (void *)cmd_node->cmd_skb->data; + if (le16_to_cpu(cmd_ptr->command) == + HOST_CMD_802_11_SCAN_EXT) { + nxpwifi_dbg(adapter, INFO, + "Scan pending in command pending list"); + complete_scan = false; + break; + } + } + } + spin_unlock_bh(&adapter->scan_pending_q_lock); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + + if (complete_scan) + nxpwifi_complete_scan(priv); + + return 0; +} + +/* This function This function handles the event extended scan report. It + * parses extended scan results and informs to cfg80211 stack. + */ +int nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private *priv, + void *buf) +{ + int ret = 0; + struct nxpwifi_adapter *adapter = priv->adapter; + u8 *bss_info; + u32 bytes_left, bytes_left_for_tlv, idx; + u16 type, len; + struct nxpwifi_ie_types_data *tlv; + struct nxpwifi_ie_types_scan_rsp *scan_rsp_tlv; + struct nxpwifi_ie_types_scan_inf *scan_info_tlv; + u8 *radio_type; + u64 fw_tsf = 0; + s32 rssi = 0; + struct nxpwifi_event_scan_result *event_scan = buf; + u8 num_of_set = event_scan->num_of_set; + u8 *scan_resp = buf + sizeof(struct nxpwifi_event_scan_result); + u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); + + if (num_of_set > NXPWIFI_MAX_AP) { + nxpwifi_dbg(adapter, ERROR, + "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + num_of_set); + ret = -EINVAL; + goto check_next_scan; + } + + bytes_left = scan_resp_size; + nxpwifi_dbg(adapter, INFO, + "EXT_SCAN: size %d, returned %d APs...", + scan_resp_size, num_of_set); + nxpwifi_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf, + scan_resp_size + + sizeof(struct nxpwifi_event_scan_result)); + + tlv = (struct nxpwifi_ie_types_data *)scan_resp; + + for (idx = 0; idx < num_of_set && bytes_left; idx++) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left < sizeof(struct nxpwifi_ie_types_header) + len) { + nxpwifi_dbg(adapter, ERROR, + "EXT_SCAN: Error bytes left < TLV length\n"); + break; + } + scan_rsp_tlv = NULL; + scan_info_tlv = NULL; + bytes_left_for_tlv = bytes_left; + + /* BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor + */ + if (type != TLV_TYPE_BSS_SCAN_RSP) + break; + + bss_info = (u8 *)tlv; + scan_rsp_tlv = (struct nxpwifi_ie_types_scan_rsp *)tlv; + tlv = (struct nxpwifi_ie_types_data *)(tlv->data + len); + bytes_left_for_tlv -= + (len + sizeof(struct nxpwifi_ie_types_header)); + + while (bytes_left_for_tlv >= + sizeof(struct nxpwifi_ie_types_header) && + le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left_for_tlv < + sizeof(struct nxpwifi_ie_types_header) + len) { + nxpwifi_dbg(adapter, ERROR, + "EXT_SCAN: Error in processing TLV,\t" + "bytes left < TLV length\n"); + scan_rsp_tlv = NULL; + bytes_left_for_tlv = 0; + continue; + } + switch (type) { + case TLV_TYPE_BSS_SCAN_INFO: + scan_info_tlv = + (struct nxpwifi_ie_types_scan_inf *)tlv; + if (len != + sizeof(struct nxpwifi_ie_types_scan_inf) - + sizeof(struct nxpwifi_ie_types_header)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; + } + tlv = (struct nxpwifi_ie_types_data *)(tlv->data + len); + bytes_left -= + (len + sizeof(struct nxpwifi_ie_types_header)); + bytes_left_for_tlv -= + (len + sizeof(struct nxpwifi_ie_types_header)); + } + + if (!scan_rsp_tlv) + break; + + /* Advance pointer to the beacon buffer length and + * update the bytes count so that the function + * wlan_interpret_bss_desc_with_ie() can handle the + * scan buffer withut any change + */ + bss_info += sizeof(u16); + bytes_left -= sizeof(u16); + + if (scan_info_tlv) { + rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); + rssi *= 100; /* Convert dBm to mBm */ + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: RSSI=%d\n", rssi); + fw_tsf = le64_to_cpu(scan_info_tlv->tsf); + radio_type = &scan_info_tlv->radio_type; + } else { + radio_type = NULL; + } + ret = nxpwifi_parse_single_response_buf(priv, &bss_info, + &bytes_left, fw_tsf, + radio_type, true, rssi); + if (ret) + goto check_next_scan; + } + +check_next_scan: + if (!event_scan->more_event) + nxpwifi_check_next_scan_command(priv); + + return ret; +} + +/* This function prepares command for background scan query. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting background scan flush parameter + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) +{ + struct host_cmd_ds_802_11_bg_scan_query *bg_query = + &cmd->params.bg_scan_query; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_BG_SCAN_QUERY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) + + S_DS_GEN); + + bg_query->flush = 1; + + return 0; +} + +/* This function inserts scan command node to the scan pending queue. + */ +void +nxpwifi_queue_scan_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + cmd_node->wait_q_enabled = true; + cmd_node->condition = &adapter->scan_wait_q_woken; + spin_lock_bh(&adapter->scan_pending_q_lock); + list_add_tail(&cmd_node->list, &adapter->scan_pending_q); + spin_unlock_bh(&adapter->scan_pending_q_lock); +} + +/* This function appends the vendor specific IE TLV to a buffer. + */ +int +nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private *priv, + u16 vsie_mask, u8 **buffer) +{ + int id, ret_len = 0; + struct nxpwifi_ie_types_vendor_param_set *vs_param_set; + + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* Traverse through the saved vendor specific IE array and append + * the selected(scan/assoc) IE as TLV to the command + */ + for (id = 0; id < NXPWIFI_MAX_VSIE_NUM; id++) { + if (priv->vs_ie[id].mask & vsie_mask) { + vs_param_set = + (struct nxpwifi_ie_types_vendor_param_set *) + *buffer; + vs_param_set->header.type = + cpu_to_le16(TLV_TYPE_PASSTHROUGH); + vs_param_set->header.len = + cpu_to_le16((((u16)priv->vs_ie[id].ie[1]) + & 0x00FF) + 2); + if (le16_to_cpu(vs_param_set->header.len) > + NXPWIFI_MAX_VSIE_LEN) { + nxpwifi_dbg(priv->adapter, ERROR, + "Invalid param length!\n"); + break; + } + + memcpy(vs_param_set->ie, priv->vs_ie[id].ie, + le16_to_cpu(vs_param_set->header.len)); + *buffer += le16_to_cpu(vs_param_set->header.len) + + sizeof(struct nxpwifi_ie_types_header); + ret_len += le16_to_cpu(vs_param_set->header.len) + + sizeof(struct nxpwifi_ie_types_header); + } + } + return ret_len; +} + +/* This function saves a beacon buffer of the current BSS descriptor. + * + * The current beacon buffer is saved so that it can be restored in the + * following cases that makes the beacon buffer not to contain the current + * ssid's beacon buffer. + * - The current ssid was not found somehow in the last scan. + * - The current ssid was the last entry of the scan table and overloaded. + */ +void +nxpwifi_save_curr_bcn(struct nxpwifi_private *priv) +{ + struct nxpwifi_bssdescriptor *curr_bss = + &priv->curr_bss_params.bss_descriptor; + + if (!curr_bss->beacon_buf_size) + return; + + /* allocate beacon buffer at 1st time; or if it's size has changed */ + if (!priv->curr_bcn_buf || + priv->curr_bcn_size != curr_bss->beacon_buf_size) { + priv->curr_bcn_size = curr_bss->beacon_buf_size; + + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, + GFP_ATOMIC); + if (!priv->curr_bcn_buf) + return; + } + + memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, + curr_bss->beacon_buf_size); + nxpwifi_dbg(priv->adapter, INFO, + "info: current beacon saved %d\n", + priv->curr_bcn_size); + + curr_bss->beacon_buf = priv->curr_bcn_buf; + + /* adjust the pointers in the current BSS descriptor */ + if (curr_bss->bcn_wpa_ie) + curr_bss->bcn_wpa_ie = + (struct ieee_types_vendor_specific *) + (curr_bss->beacon_buf + + curr_bss->wpa_offset); + + if (curr_bss->bcn_rsn_ie) + curr_bss->bcn_rsn_ie = + (struct element *)(curr_bss->beacon_buf + + curr_bss->rsn_offset); + + if (curr_bss->bcn_ht_cap) + curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) + (curr_bss->beacon_buf + + curr_bss->ht_cap_offset); + + if (curr_bss->bcn_ht_oper) + curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) + (curr_bss->beacon_buf + + curr_bss->ht_info_offset); + + if (curr_bss->bcn_vht_cap) + curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + + curr_bss->vht_cap_offset); + + if (curr_bss->bcn_vht_oper) + curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + + curr_bss->vht_info_offset); + + if (curr_bss->bcn_he_cap) + curr_bss->bcn_he_cap = (void *)(curr_bss->beacon_buf + + curr_bss->he_cap_offset); + + if (curr_bss->bcn_he_oper) + curr_bss->bcn_he_oper = (void *)(curr_bss->beacon_buf + + curr_bss->he_info_offset); + + if (curr_bss->bcn_bss_co_2040) + curr_bss->bcn_bss_co_2040 = + (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); + + if (curr_bss->bcn_ext_cap) + curr_bss->bcn_ext_cap = curr_bss->beacon_buf + + curr_bss->ext_cap_offset; + + if (curr_bss->oper_mode) + curr_bss->oper_mode = (void *)(curr_bss->beacon_buf + + curr_bss->oper_mode_offset); +} + +/* This function frees the current BSS descriptor beacon buffer. + */ +void +nxpwifi_free_curr_bcn(struct nxpwifi_private *priv) +{ + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf = NULL; +} From patchwork Mon Sep 30 06:36:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815423 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 41C6C16A395; Mon, 30 Sep 2024 06:38:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678284; cv=fail; b=CpacTZLKpyrRlaUb9Z3ZZxpZCd3U5gSJE4Bwv/mwxd/d+gA4fba5rJmvCTc5QpLGOhNIUYmPvrmGYMrR1EkeJ+yrrg1AZc7/OG1XDz43oEZAiMJ9Yrifh9eg8gJi35/L+AS0Ueho10VWiuVjfqWB5ltXWwXUIKDfBPQsdc0W82s= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678284; c=relaxed/simple; bh=mRXdyv2IuG4cDFZWG41EDkE7dIhJfYrIj+Ku8U1MoC8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=foBZyzhQebtgOIh4R/xyOYfz0IPRwJfeN9X6pl1IsX7vq/F9lSBIA93KYF66gt6dRC3phNjSEEc+GHzjMwD4LTdRydBkTBHq4Dxk3Pj+aoy0NlcFpDThrPkbqYtjg/N39pzdO/2xmvRwfBA8/B6yXVEEorCLWxZzwFtqLQpNnqU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=YSsMNIz2; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="YSsMNIz2" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=AKjDf9cJ4yoFBp9xTwH9OF2xJU9ly0pdjFXDtZ7u4/Qj6kTJeCK075RAjp7mre7hCOwqJaKl8t7VroZa7AJ6RC2149zshwQHo96ub6vyRPH4+qzlUvIrvpVjVJqwtbt0CV8+TH7UOfpKZCaiHEOn++lBo7HsHk1KxOZHhKxn9ULVo2u+eLoagfy/0x+R89RfK5JpvFN3oIgWasOW37PRbeCR6DHZmwtL6z6btwYrRW7hQFdCicHGEu9TPuHyuuZ2yAmcFhtvz+LjREjuphYaOH5Vr+UPsNvDFLCGMzUj6qJXFgppmNFJvfvok4/FinXF/VHXUMBFi26xOfeGlZm1Mg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=nQYIZmNDwm0/heMWKnoupo+5tfIpCdhAw0AwPGin+BI=; b=ZAdJ4yUHUTaEFbE5SsQ0qE5iNvDYZBW/04qOY2+Tfb89W6PMyIwUT/KryGRVtiDrBBG267BZyX2PtPSPU2WEYpzLX7mblmDAax0H9ikqXB1cCAJlyvAtCKUXgG2w14OdtNHzAVEsRivMCg7Hq03CULFbyFUVCrzQnYsarg1otyjcafN37ROYiyEtkz0xTDs8Q/O7xzG/Ode/JU5jV8smafnvb+yLrKiGUqd7D8knyiF4L8pUuDNNfTvTe54TtzBXkIUTQbsGPQGlqNdYxlntDLC8q6ISDmZCMnyrd0CVRu2/jwxB8LZQ5SFbUSRFVsjX7wpLFWYb7DMtEhOrnr2mpg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=nQYIZmNDwm0/heMWKnoupo+5tfIpCdhAw0AwPGin+BI=; b=YSsMNIz2dwWx1iRhmZKaxM5hYb+gb/GBKxKYMsp5YiwyQTSYzMISXmoXMKBeEXuTMJm20pfohOlNYIiWh8z9VoFUjeFAqrbodISBc6Np6UcgMhSQ1D9RyZX4dR4i/HuATj1yuItORe2bWQOLIcIF5I4Wi/XqhUOZflRaj7ashAlmrwDxKH2hQoWZwtpKDUSlKbkyuPKYEFRnQIbwAiIgQfe/BAeSXNMfbNEDQ4cVE5at6ppblnZb/X7NAgiAaua6PdafbUTtXaYRj+363Gjdi/yAFL4WktWBlZcfrgvW50U7GbioMhH6Kq8I/MjznbFAhWgtvB1320vlpvG5+vwybw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:37:50 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:50 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 07/22] wifi: nxpwifi: add join.c Date: Mon, 30 Sep 2024 14:36:46 +0800 Message-Id: <20240930063701.2566520-8-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 8bebc7ab-a214-4dbe-176d-08dce11a6731 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: rt4uI29SnWjr099syvGFO3pCeN5c5lp2Yq14YCoM5kaLxwcoAREsmRebdMLopWMMryS30d57FMRt3ymXVnWqFst6BbtWDTkHIxgLOjkTqB+zIZRrTkBzZ/abygUK12PxZL9p5+Nklyewn7nUMPO/q6VH5GG5J7zgAjk8QL4Si0CDugcxIVsFdMqOTHR0yqkHAsl+FsbQEdsqw+X1v+Rgl5eNapoL+5KyO6R2luQbzmuxZn4yeqMke+eqteKw/7rEQiRpcsyODVHBWyLJiMHjGLmSaMIhOQUHEMqtVHj85NDWdMqP2iGJ+T8T3OCPK56K6VpM8DuyMc2TLGD3kwCn4p6f3dKkwgrZonwRUWz/Svr5bNY0D5xCI4gYm9xQ2l3Ojvh/gfn4IC0WEqCeE9f6WvZG0MncEjtJGx2kCU/3nn1B8jAcPtl3kQafKoch7dSfAxruC+E2Wpy4h70Zp093oS7H+Ebhuwhn7kX75cbRjrJrHDl4OAXcdW/NIOfB5tNH9VxiWeGGpR4SlQtNBZ4g1e73u8fv9gjJ/+k4dN4xJZa5hfCsU5mjmVLqw5ovdMKYSbnETMKy41K/jTICYSHr48Ba/jlxAVYc7cRJFkMHFDAtYHO74DZALitlD+0/Es0/aZwK4xiNeYxBsgfiYFYY8+ZLbq6Zn5XPJGVbUVR8xWrWSwiIDSu2I7OZO8PJgrtbOlSHpLWZ6QpsqxgScYZ+jq3luiR25BXhsWQ3zytwYlL6bWyWpJXhixMfEOSSR5UCN8FMWnbMN6C+SOljDrBTQxmuh8SjbzTOzQcKDuyBaUqY0tiEVTrauEgvpp6qsGvizXv55IMNJvYUZBnabxr6huZa/T+b29AqQTQ3ZQ2gh/Vgav3S42s5JiYTGCRXwSsjHotpRIxmCNr5dX5JHcrPQHw8bgvSL2bt47lGAZ4vWXrVdZ+jcCNew4Sw9rWTJ43x3TAoaYj7U2G/ChIDPZyWGKEdS4wY9te9iwx6HFIoOkDS8e3ehZ/yXr+B//8Tx8J2uXlxY0PaCU/muDMyYWtMnH5zSF7ZRrZqiueJ5pX8R0by6tyvFJCdG0lY6NfFRmvZ1CV38UJuILaIWGVHUYJmolG72i2smd8v126O3FjIAJYUKxaA0y5FrqEYeGcKHg8ixTNuUgN9J+RdrwSAkZ0wYWi7o1cw8awOI5CK11JbSjUPYCGP5mxqKPsCfq+XKmTp6cy5muu04KPubrcjxS7EdniZbX9F94XQeRd3h1RlhZx24/IwOU18I4sOe32qrA7A1D1NFZHU1mDqo2GKaZgShcyVCKn7mMDV/a4y5d1jaJjI6gSL6TMle8HkLiOdILomxsT4gtPCdihJS6EidifJ7nhOa9jspp4JY2qS/RlfZMSLjCbJXH4+uMsjUXi1D4cZ3h55Fa8DV2XtriCJT6Vb+Q== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: qxD6NoeuDcXXdrLCZ+iBYeEi45tpHrW1ImMSMdtFcMGIHynhrfduEFc5aTCcaiPSRHipGhVmaTcn7EwAljyBbjTBgZ0ejENbapUhccMmsTWCRFSCF6pFd1vEwCw5j9tKY4vk1AgUKTywHI8qtnyzZEsKWVVhwOMjdmZsM3mqYyCZO0cgEne/cEXxN8+GrFy+G+KZAtg4vt7hKy6lRp3LxMG1cXT2pZXbH+DBKbnjcKEQ2SZ2/Rb9VNQqlHweX+t/q5US3FTmub+KgUJG6OyQoMXC0bffxFm5OyXZxI8QW2oA7JW3VlFieLVSPNdw2qVz/hnizm3FOCfvxXardrFHSzhPp34ALNx8zfKeoSmL43djhb3sd2Yaa2mxBQdwIJzq0OfQzZMtTYs2gvEZk6P2skNebugezSO3RGSQy6eH3A/RgaNGaL4mflL6yIybzhM/GAkuUX5LViXA4m57MAYmjJB6bl4R4tC98/79hUQNFQ0XAcjaawxWW+DNmc8GIz9CeBies1LtHMgUx3OCwKhPJGyEdPUclRbnRC3djAk1k1NqWvUr6yarBNXFaff2Rk2FLgeuJI+uC1z9zCvizvs8SuKdRleMJIx0atpaFjM01VXjAAL2GRXBTtRTo5QZbPnus9AJz1/MuThsj6l7hgcuOW0XTFjlEYXWF3bysQNtFer4g2psATIVTBfdULwq8A86JO+SgykGU1mlOWwm69/8klC2NugPTi3+7u+SZFtW4b7Qq6KGAKRpywdBp2Na4ZqijsNp/n8Yh9j3Saw+0ib9ovHqXTyb3G4Ote17QDxV9MctcqQv+nmIPMJBfZquzA0sqShxDpKJWw7qiLuqnfcvxV3ad+FQ+qwsz3q98YEDhVHYsS/duVJMCEWeL6Ri41Hvcs+D0FYivSH8ObF8KkX2qcUaKj1prDD7xx8fyetkX01O9BiR7LEhI4PzrF8gcTNjPCDYh8fE9WnRw0MKSrkM882PnOBlZn+H0xyAxcYbsQooSbcbJcT7O3lxJsPN084KpUq2UMhjBggY6WOFdTifYCoi03zKAzo524im2+o+Eg6LRQFGkmJKz1Fp78gUbZVr22/t9uNSrrNzE/8I7IIWyM4YISUKF9zRjVN/HrtlTwsoK16CfkY8aIlGQjmPrzRPTw5nf5LOD9cHTGn0S9P3KDaGNFxO81xVY1kpelEkTtINyArCbR78cc0c96qmf+YlUXgfp2UhUUsYo/JPOrFlP7oKGIPljOwrg1D6SLxrdXuhTS5tYK0QPXvCDJb9QefOEXNFSjOXtcWOQJZ/o8h5ZsB1eTaoHOaVAQMLO4YgmOcqFbNjaASd6HjwU7IhJcrccpEcpJz1mT7wiJvPzSiLro+kIezXBpnonhHfaqeaIXQ69PnobZrGtHWbr4X109L1rXytM5BuI8m5IFN7KTqmRrwzyWEMOGVcwGWBsLBFO/DJqh0CUL348vNBe35sx8wtpdulsyKvMLXKULGTm96Se70rPg2ndsrQUpkDahjNpqHiQU0PTmcfm169kT9EAhGWPe0HQmi45FSCJy4dFlpv2aglkJb0+THB3/ef/QGIHmqxXvZdt5CHLcD2Y3NqmqrT X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8bebc7ab-a214-4dbe-176d-08dce11a6731 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:49.9325 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: CD2CDNTD0Gg/RnLx0ifl6nxngFMVB8CLRU8ubSbFVnRT97U+GeZyaS15nNfPR8CQodFa5QSzqYzWGgEqRQMqiw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 File join.c is used to support join porcess. Mostly related association. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/join.c | 910 ++++++++++++++++++++++++ 1 file changed, 910 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/join.c diff --git a/drivers/net/wireless/nxp/nxpwifi/join.c b/drivers/net/wireless/nxp/nxpwifi/join.c new file mode 100644 index 000000000000..edc858a12bc7 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/join.c @@ -0,0 +1,910 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: association and ad-hoc start/join + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" +#include "11ax.h" + +#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) + +/* Append a generic IE as a pass through TLV to a TLV buffer. + * + * This function is called from the network join command preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a pass through TLV type to the request. + */ +static int +nxpwifi_cmd_append_generic_ie(struct nxpwifi_private *priv, u8 **buffer) +{ + int ret_len = 0; + struct nxpwifi_ie_types_header ie_header; + + /* Null Checks */ + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* If there is a generic ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->gen_ie_buf_len) { + nxpwifi_dbg(priv->adapter, INFO, + "info: %s: append generic ie len %d to %p\n", + __func__, priv->gen_ie_buf_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer + * param + */ + *buffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + /* Copy the generic IE buffer to the output buffer, advance + * pointer + */ + memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); + + /* Increment the return size and the return buffer pointer + * param + */ + *buffer += priv->gen_ie_buf_len; + ret_len += priv->gen_ie_buf_len; + + /* Reset the generic IE buffer */ + priv->gen_ie_buf_len = 0; + } + + /* return the length appended to the buffer */ + return ret_len; +} + +/* Append TSF tracking info from the scan table for the target AP. + * + * This function is called from the network join command preparation routine. + * + * The TSF table TSF sent to the firmware contains two TSF values: + * - The TSF of the target AP from its previous beacon/probe response + * - The TSF timestamp of our local MAC at the time we observed the + * beacon/probe response. + * + * The firmware uses the timestamp values to set an initial TSF value + * in the MAC for the new association after a reassociation attempt. + */ +static int +nxpwifi_cmd_append_tsf_tlv(struct nxpwifi_private *priv, u8 **buffer, + struct nxpwifi_bssdescriptor *bss_desc) +{ + struct nxpwifi_ie_types_tsf_timestamp tsf_tlv; + __le64 tsf_val; + + /* Null Checks */ + if (!buffer) + return 0; + if (!*buffer) + return 0; + + memset(&tsf_tlv, 0x00, sizeof(struct nxpwifi_ie_types_tsf_timestamp)); + + tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); + tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); + + memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); + *buffer += sizeof(tsf_tlv.header); + + /* TSF at the time when beacon/probe_response was received */ + tsf_val = cpu_to_le64(bss_desc->fw_tsf); + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer += sizeof(tsf_val); + + tsf_val = cpu_to_le64(bss_desc->timestamp); + + nxpwifi_dbg(priv->adapter, INFO, + "info: %s: TSF offset calc: %016llx - %016llx\n", + __func__, bss_desc->timestamp, bss_desc->fw_tsf); + + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer += sizeof(tsf_val); + + return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); +} + +/* This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates needs to be taken + * care of, either before or after calling this function. + */ +static int nxpwifi_get_common_rates(struct nxpwifi_private *priv, u8 *rate1, + u32 rate1_size, u8 *rate2, u32 rate2_size) +{ + int ret; + u8 *ptr = rate1, *tmp; + u32 i, j; + + tmp = kmemdup(rate1, rate1_size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + memset(rate1, 0, rate1_size); + + for (i = 0; i < rate2_size && rate2[i]; i++) { + for (j = 0; j < rate1_size && tmp[j]; j++) { + /* Check common rate, excluding the bit for + * basic rate + */ + if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { + *rate1++ = tmp[j]; + break; + } + } + } + + nxpwifi_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n", + priv->data_rate); + + if (!priv->is_data_rate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == priv->data_rate) { + ret = 0; + goto done; + } + ptr++; + } + nxpwifi_dbg(priv->adapter, ERROR, + "previously set fixed data rate %#x\t" + "is not compatible with the network\n", + priv->data_rate); + + ret = -EPERM; + goto done; + } + + ret = 0; +done: + kfree(tmp); + return ret; +} + +/* This function creates the intersection of the rates supported by a + * target BSS and our adapter settings for use in an assoc/join command. + */ +static int +nxpwifi_setup_rates_from_bssdesc(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 *out_rates, u32 *out_rates_size) +{ + u8 card_rates[NXPWIFI_SUPPORTED_RATES]; + u32 card_rates_size; + int ret; + + /* Copy AP supported rates */ + memcpy(out_rates, bss_desc->supported_rates, NXPWIFI_SUPPORTED_RATES); + /* Get the STA supported rates */ + card_rates_size = nxpwifi_get_active_data_rates(priv, card_rates); + /* Get the common rates between AP and STA supported rates */ + ret = nxpwifi_get_common_rates(priv, out_rates, NXPWIFI_SUPPORTED_RATES, + card_rates, card_rates_size); + if (ret) { + *out_rates_size = 0; + nxpwifi_dbg(priv->adapter, ERROR, + "%s: cannot get common rates\n", + __func__); + } else { + *out_rates_size = + min_t(size_t, strlen(out_rates), NXPWIFI_SUPPORTED_RATES); + } + + return ret; +} + +/* This function appends a WPS IE. It is called from the network join command + * preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a WPS TLV type to the request. + */ +static int +nxpwifi_cmd_append_wps_ie(struct nxpwifi_private *priv, u8 **buffer) +{ + int ret_len = 0; + struct nxpwifi_ie_types_header ie_header; + + if (!buffer || !*buffer) + return 0; + + /* If there is a wps ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wps_ie_len) { + nxpwifi_dbg(priv->adapter, CMD, + "cmd: append wps ie %d to %p\n", + priv->wps_ie_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = cpu_to_le16(priv->wps_ie_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + *buffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); + *buffer += priv->wps_ie_len; + ret_len += priv->wps_ie_len; + } + + kfree(priv->wps_ie); + priv->wps_ie_len = 0; + return ret_len; +} + +/* This function appends rsn ie tlv for wpa/wpa2 security modes. + * It is called from the network join command preparation routine. + */ +static int nxpwifi_append_rsn_ie_wpa_wpa2(struct nxpwifi_private *priv, + u8 **buffer) +{ + struct nxpwifi_ie_types_rsn_param_set *rsn_ie_tlv; + int rsn_ie_len; + + if (!buffer || !(*buffer)) + return 0; + + rsn_ie_tlv = (struct nxpwifi_ie_types_rsn_param_set *)(*buffer); + rsn_ie_tlv->header.type = cpu_to_le16((u16)priv->wpa_ie[0]); + rsn_ie_tlv->header.type = + cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); + rsn_ie_tlv->header.len = cpu_to_le16((u16)priv->wpa_ie[1]); + rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) + & 0x00FF); + if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) + memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], + le16_to_cpu(rsn_ie_tlv->header.len)); + else + return -ENOMEM; + + rsn_ie_len = sizeof(rsn_ie_tlv->header) + + le16_to_cpu(rsn_ie_tlv->header.len); + *buffer += rsn_ie_len; + + return rsn_ie_len; +} + +/* This function prepares command for association. + * + * This sets the following parameters - + * - Peer MAC address + * - Listen interval + * - Beacon interval + * - Capability information + * + * ...and the following TLVs, as required - + * - SSID TLV + * - PHY TLV + * - SS TLV + * - Rates TLV + * - Authentication TLV + * - Channel TLV + * - WPA/WPA2 IE + * - 11n TLV + * - Vendor specific TLV + * - WMM TLV + * - Generic IE + * - TSF TLV + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int nxpwifi_cmd_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + struct nxpwifi_bssdescriptor *bss_desc) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; + struct nxpwifi_ie_types_host_mlme *host_mlme_tlv; + struct nxpwifi_ie_types_ssid_param_set *ssid_tlv; + struct nxpwifi_ie_types_phy_param_set *phy_tlv; + struct nxpwifi_ie_types_ss_param_set *ss_tlv; + struct nxpwifi_ie_types_rates_param_set *rates_tlv; + struct nxpwifi_ie_types_auth_type *auth_tlv; + struct nxpwifi_ie_types_sae_pwe_mode *sae_pwe_tlv; + struct nxpwifi_ie_types_chan_list_param_set *chan_tlv; + u8 rates[NXPWIFI_SUPPORTED_RATES]; + u32 rates_size; + u16 tmp_cap; + u8 *pos; + int rsn_ie_len = 0; + int ret; + + pos = (u8 *)assoc; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_ASSOCIATE); + + /* Save so we know which BSS Desc to use in the response handler */ + priv->attempted_bss_desc = bss_desc; + + memcpy(assoc->peer_sta_addr, + bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); + pos += sizeof(assoc->peer_sta_addr); + + /* Set the listen interval */ + assoc->listen_interval = cpu_to_le16(priv->listen_interval); + /* Set the beacon period */ + assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); + + pos += sizeof(assoc->cap_info_bitmap); + pos += sizeof(assoc->listen_interval); + pos += sizeof(assoc->beacon_period); + pos += sizeof(assoc->dtim_period); + + host_mlme_tlv = (struct nxpwifi_ie_types_host_mlme *)pos; + host_mlme_tlv->header.type = cpu_to_le16(TLV_TYPE_HOST_MLME); + host_mlme_tlv->header.len = cpu_to_le16(sizeof(host_mlme_tlv->host_mlme)); + host_mlme_tlv->host_mlme = 1; + pos += sizeof(host_mlme_tlv->header) + sizeof(host_mlme_tlv->host_mlme); + + ssid_tlv = (struct nxpwifi_ie_types_ssid_param_set *)pos; + ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); + ssid_tlv->header.len = cpu_to_le16((u16)bss_desc->ssid.ssid_len); + memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, + le16_to_cpu(ssid_tlv->header.len)); + pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); + + phy_tlv = (struct nxpwifi_ie_types_phy_param_set *)pos; + phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); + phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); + memcpy(&phy_tlv->fh_ds.ds_param_set, + &bss_desc->phy_param_set.ds_param_set.current_chan, + sizeof(phy_tlv->fh_ds.ds_param_set)); + pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); + + ss_tlv = (struct nxpwifi_ie_types_ss_param_set *)pos; + ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); + ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); + pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); + + /* Get the common rates supported between the driver and the BSS Desc */ + ret = nxpwifi_setup_rates_from_bssdesc(priv, bss_desc, + rates, &rates_size); + if (ret) + return ret; + + /* Save the data rates into Current BSS state structure */ + priv->curr_bss_params.num_of_rates = rates_size; + memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); + + /* Setup the Rates TLV in the association command */ + rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16)rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + pos += sizeof(rates_tlv->header) + rates_size; + nxpwifi_dbg(adapter, INFO, "info: ASSOC_CMD: rates size = %d\n", + rates_size); + + /* Add the Authentication type */ + auth_tlv = (struct nxpwifi_ie_types_auth_type *)pos; + auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); + if (priv->sec_info.wep_enabled) + auth_tlv->auth_type = + cpu_to_le16((u16)priv->sec_info.authentication_mode); + else + auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); + + pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); + + if (priv->sec_info.authentication_mode == WLAN_AUTH_SAE) { + auth_tlv->auth_type = cpu_to_le16(NXPWIFI_AUTHTYPE_SAE); + if (bss_desc->bcn_rsnx_ie && + bss_desc->bcn_rsnx_ie->datalen && + (bss_desc->bcn_rsnx_ie->data[0] & + WLAN_RSNX_CAPA_SAE_H2E)) { + sae_pwe_tlv = + (struct nxpwifi_ie_types_sae_pwe_mode *)pos; + sae_pwe_tlv->header.type = + cpu_to_le16(TLV_TYPE_SAE_PWE_MODE); + sae_pwe_tlv->header.len = + cpu_to_le16(sizeof(sae_pwe_tlv->pwe[0])); + sae_pwe_tlv->pwe[0] = bss_desc->bcn_rsnx_ie->data[0]; + pos += sizeof(sae_pwe_tlv->header) + + sizeof(sae_pwe_tlv->pwe[0]); + } + } + + if (IS_SUPPORT_MULTI_BANDS(adapter) && + !(ISSUPP_11NENABLED(adapter->fw_cap_info) && + !bss_desc->disable_11n && + (priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap)) { + /* Append a channel TLV for the channel the attempted AP was + * found on + */ + chan_tlv = (struct nxpwifi_ie_types_chan_list_param_set *)pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct nxpwifi_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct nxpwifi_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (bss_desc->phy_param_set.ds_param_set.current_chan); + nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Chan = %d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type = + nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + + nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Band = %d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct nxpwifi_chan_scan_param_set); + } + + if (!priv->wps.session_enable) { + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + rsn_ie_len = nxpwifi_append_rsn_ie_wpa_wpa2(priv, &pos); + + if (rsn_ie_len == -ENOMEM) + return -ENOMEM; + } + + if (ISSUPP_11NENABLED(adapter->fw_cap_info) && + !bss_desc->disable_11n && + (priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN)) + nxpwifi_cmd_append_11n_tlv(priv, bss_desc, &pos); + + if (ISSUPP_11ACENABLED(adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->config_bands & BAND_AAC) + nxpwifi_cmd_append_11ac_tlv(priv, bss_desc, &pos); + + if (ISSUPP_11AXENABLED(adapter->fw_cap_ext) && + nxpwifi_11ax_bandconfig_allowed(priv, bss_desc)) + nxpwifi_cmd_append_11ax_tlv(priv, bss_desc, &pos); + + /* Append vendor specific IE TLV */ + nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_ASSOC, &pos); + + nxpwifi_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, + bss_desc->bcn_ht_cap); + + if (priv->wps.session_enable && priv->wps_ie_len) + nxpwifi_cmd_append_wps_ie(priv, &pos); + + nxpwifi_cmd_append_generic_ie(priv, &pos); + + nxpwifi_cmd_append_tsf_tlv(priv, &pos, bss_desc); + + nxpwifi_11h_process_join(priv, &pos, bss_desc); + + cmd->size = cpu_to_le16((u16)(pos - (u8 *)assoc) + S_DS_GEN); + + /* Set the Capability info at last */ + tmp_cap = bss_desc->cap_info_bitmap; + + if (priv->config_bands == BAND_B) + tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; + + tmp_cap &= CAPINFO_MASK; + nxpwifi_dbg(adapter, INFO, + "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); + + return ret; +} + +static const char *assoc_failure_reason_to_str(u16 cap_info) +{ + switch (cap_info) { + case CONNECT_ERR_AUTH_ERR_STA_FAILURE: + return "CONNECT_ERR_AUTH_ERR_STA_FAILURE"; + case CONNECT_ERR_AUTH_MSG_UNHANDLED: + return "CONNECT_ERR_AUTH_MSG_UNHANDLED"; + case CONNECT_ERR_ASSOC_ERR_TIMEOUT: + return "CONNECT_ERR_ASSOC_ERR_TIMEOUT"; + case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED: + return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED"; + case CONNECT_ERR_STA_FAILURE: + return "CONNECT_ERR_STA_FAILURE"; + } + + return "Unknown connect failure"; +} + +/* Association firmware command response handler + * + * The response buffer for the association command has the following + * memory layout. + * + * For cases where an association response was not received (indicated + * by the CapInfo and AId field): + * + * .------------------------------------------------------------. + * | Header(4 * sizeof(t_u16)): Standard command response hdr | + * .------------------------------------------------------------. + * | cap_info/Error Return(t_u16): | + * | 0xFFFF(-1): Internal error | + * | 0xFFFE(-2): Authentication unhandled message | + * | 0xFFFD(-3): Authentication refused | + * | 0xFFFC(-4): Timeout waiting for AP response | + * .------------------------------------------------------------. + * | status_code(t_u16): | + * | If cap_info is -1: | + * | An internal firmware failure prevented the | + * | command from being processed. The status_code | + * | will be set to 1. | + * | | + * | If cap_info is -2: | + * | An authentication frame was received but was | + * | not handled by the firmware. IEEE Status | + * | code for the failure is returned. | + * | | + * | If cap_info is -3: | + * | An authentication frame was received and the | + * | status_code is the IEEE Status reported in the | + * | response. | + * | | + * | If cap_info is -4: | + * | (1) Association response timeout | + * | (2) Authentication response timeout | + * .------------------------------------------------------------. + * | a_id(t_u16): 0xFFFF | + * .------------------------------------------------------------. + * + * + * For cases where an association response was received, the IEEE + * standard association response frame is returned: + * + * .------------------------------------------------------------. + * | Header(4 * sizeof(t_u16)): Standard command response hdr | + * .------------------------------------------------------------. + * | cap_info(t_u16): IEEE Capability | + * .------------------------------------------------------------. + * | status_code(t_u16): IEEE Status Code | + * .------------------------------------------------------------. + * | a_id(t_u16): IEEE Association ID | + * .------------------------------------------------------------. + * | IEEE IEs(variable): Any received IEs comprising the | + * | remaining portion of a received | + * | association response frame. | + * .------------------------------------------------------------. + * + * For simplistic handling, the status_code field can be used to determine + * an association success (0) or failure (non-zero). + */ +int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret = 0; + struct ieee_types_assoc_rsp *assoc_rsp; + struct nxpwifi_bssdescriptor *bss_desc; + bool enable_data = true; + u16 cap_info, status_code, aid; + const u8 *ie_ptr; + struct ieee80211_ht_operation *assoc_resp_ht_oper; + struct ieee80211_mgmt *hdr; + + if (!priv->attempted_bss_desc) { + nxpwifi_dbg(adapter, ERROR, + "%s: failed, association terminated by host\n", + __func__); + goto done; + } + + hdr = (struct ieee80211_mgmt *)&resp->params; + if (!memcmp(hdr->bssid, priv->attempted_bss_desc->mac_address, + ETH_ALEN)) + assoc_rsp = (struct ieee_types_assoc_rsp *)&hdr->u.assoc_resp; + else + assoc_rsp = (struct ieee_types_assoc_rsp *)&resp->params; + + cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap); + status_code = le16_to_cpu(assoc_rsp->status_code); + aid = le16_to_cpu(assoc_rsp->a_id); + + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) + dev_err(adapter->dev, + "invalid AID value 0x%x; bits 15:14 not set\n", + aid); + + aid &= ~(BIT(15) | BIT(14)); + + priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, + sizeof(priv->assoc_rsp_buf)); + + assoc_rsp->a_id = cpu_to_le16(aid); + memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); + + if (status_code) { + adapter->dbg.num_cmd_assoc_failure++; + nxpwifi_dbg(adapter, ERROR, + "ASSOC_RESP: failed,\t" + "status code=%d err=%#x a_id=%#x\n", + status_code, cap_info, + le16_to_cpu(assoc_rsp->a_id)); + + nxpwifi_dbg(adapter, ERROR, "assoc failure: reason %s\n", + assoc_failure_reason_to_str(cap_info)); + if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) { + if (status_code == NXPWIFI_ASSOC_CMD_FAILURE_AUTH) { + ret = WLAN_STATUS_AUTH_TIMEOUT; + nxpwifi_dbg(adapter, ERROR, + "ASSOC_RESP: AUTH timeout\n"); + } else { + ret = WLAN_STATUS_UNSPECIFIED_FAILURE; + nxpwifi_dbg(adapter, ERROR, + "ASSOC_RESP: UNSPECIFIED failure\n"); + } + + priv->assoc_rsp_size = 0; + } else { + ret = status_code; + } + + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + priv->media_connected = true; + + adapter->ps_state = PS_STATE_AWAKE; + adapter->pps_uapsd_mode = false; + adapter->tx_lock_flag = false; + + /* Set the attempted BSSID Index to current */ + bss_desc = priv->attempted_bss_desc; + + nxpwifi_dbg(adapter, INFO, "info: ASSOC_RESP: %s\n", + bss_desc->ssid.ssid); + + /* Make a copy of current BSSID descriptor */ + memcpy(&priv->curr_bss_params.bss_descriptor, + bss_desc, sizeof(struct nxpwifi_bssdescriptor)); + + /* Update curr_bss_params */ + priv->curr_bss_params.bss_descriptor.channel = + bss_desc->phy_param_set.ds_param_set.current_chan; + + priv->curr_bss_params.band = (u8)bss_desc->bss_band; + + if (bss_desc->wmm_ie.element_id == WLAN_EID_VENDOR_SPECIFIC) + priv->curr_bss_params.wmm_enabled = true; + else + priv->curr_bss_params.wmm_enabled = false; + + if ((priv->wmm_required || bss_desc->bcn_ht_cap) && + priv->curr_bss_params.wmm_enabled) + priv->wmm_enabled = true; + else + priv->wmm_enabled = false; + + priv->curr_bss_params.wmm_uapsd_enabled = false; + + if (priv->wmm_enabled) + priv->curr_bss_params.wmm_uapsd_enabled = + ((bss_desc->wmm_ie.qos_info & + IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); + + /* Store the bandwidth information from assoc response */ + ie_ptr = cfg80211_find_ie(WLAN_EID_HT_OPERATION, assoc_rsp->ie_buffer, + priv->assoc_rsp_size + - sizeof(struct ieee_types_assoc_rsp)); + if (ie_ptr) { + assoc_resp_ht_oper = (struct ieee80211_ht_operation *)(ie_ptr + + sizeof(struct element)); + priv->assoc_resp_ht_param = assoc_resp_ht_oper->ht_param; + priv->ht_param_present = true; + } else { + priv->ht_param_present = false; + } + + nxpwifi_dbg(adapter, INFO, + "info: ASSOC_RESP: curr_pkt_filter is %#x\n", + priv->curr_pkt_filter); + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + priv->wpa_is_gtk_set = false; + + if (priv->wmm_enabled) { + /* Don't re-enable carrier until we get the WMM_GET_STATUS + * event + */ + enable_data = false; + } else { + /* Since WMM is not enabled, setup the queues with the + * defaults + */ + nxpwifi_wmm_setup_queue_priorities(priv, NULL); + nxpwifi_wmm_setup_ac_downgrade(priv); + } + + if (enable_data) + nxpwifi_dbg(adapter, INFO, + "info: post association, re-enabling data flow\n"); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_htinfo = 0; + + nxpwifi_save_curr_bcn(priv); + + adapter->dbg.num_cmd_assoc_success++; + + nxpwifi_dbg(adapter, MSG, "assoc: associated with %pM\n", + priv->attempted_bss_desc->mac_address); + + /* Add the ra_list here for infra mode as there will be only 1 ra + * always + */ + nxpwifi_ralist_add(priv, + priv->curr_bss_params.bss_descriptor.mac_address); + + netif_carrier_on(priv->netdev); + nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter); + + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + priv->scan_block = true; + else + priv->port_open = true; + +done: + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + if (ret) + adapter->cmd_wait_q.status = -1; + else + adapter->cmd_wait_q.status = 0; + } + + return ret; +} + +/* This function associates to a specific BSS discovered in a scan. + * + * It clears any past association response stored for application + * retrieval and calls the command preparation routine to send the + * command to firmware. + */ +int nxpwifi_associate(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + /* Return error if the adapter is not STA role or table entry + * is not marked as infra. + */ + if ((GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA) || + bss_desc->bss_mode != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->config_bands & BAND_AAC) + nxpwifi_set_11ac_ba_params(priv); + else + nxpwifi_set_ba_params(priv); + + /* Clear any past association response stored for application + * retrieval + */ + priv->assoc_rsp_size = 0; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_ASSOCIATE, + HOST_ACT_GEN_SET, 0, bss_desc, true); +} + +/* This function deauthenticates/disconnects from infra network by sending + * deauthentication request. + */ +static int nxpwifi_deauthenticate_infra(struct nxpwifi_private *priv, u8 *mac) +{ + u8 mac_address[ETH_ALEN]; + int ret; + + if (!mac || is_zero_ether_addr(mac)) + memcpy(mac_address, + priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + else + memcpy(mac_address, mac, ETH_ALEN); + + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE, + HOST_ACT_GEN_SET, 0, mac_address, true); + + return ret; +} + +/* This function deauthenticates/disconnects from a BSS. + * + * In case of infra made, it sends deauthentication request, and + * in case of ad-hoc mode, a stop network request is sent to the firmware. + * In AP mode, a command to stop bss is sent to firmware. + */ +int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac) +{ + int ret = 0; + + if (!priv->media_connected) + return 0; + + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + priv->host_mlme_reg = false; + priv->mgmt_frame_mask = 0; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG, + HOST_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "could not unregister mgmt frame rx\n"); + return ret; + } + + switch (priv->bss_mode) { + case NL80211_IFTYPE_STATION: + ret = nxpwifi_deauthenticate_infra(priv, mac); + if (ret) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, + true, GFP_KERNEL); + break; + case NL80211_IFTYPE_AP: + ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP, + HOST_ACT_GEN_SET, 0, NULL, true); + default: + break; + } + + return ret; +} + +/* This function deauthenticates/disconnects from all BSS. */ +void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + nxpwifi_deauthenticate(priv, NULL); + } +} +EXPORT_SYMBOL_GPL(nxpwifi_deauthenticate_all); + +/* This function converts band to radio type used in channel TLV. + */ +u8 nxpwifi_band_to_radio_type(u16 config_bands) +{ + if (config_bands & BAND_A || config_bands & BAND_AN || + config_bands & BAND_AAC || config_bands & BAND_AAX) + return HOST_SCAN_RADIO_TYPE_A; + + return HOST_SCAN_RADIO_TYPE_BG; +} From patchwork Mon Sep 30 06:36:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815424 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5080E1714AC; Mon, 30 Sep 2024 06:38:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678286; cv=fail; b=kvm/DaD8OLDPurKG/WP/pMQEKeFh2DOW5dJxhIJkUN4v+fRFkkFFUue0aYrzSWJPvZs5SBsylEFJR7mxnz255CG7b3JmgewhR0kUcHjfxaGEcdcTnOckVuWx2o4jqCvQcX5+cr2OzBs7xAJPc4TJDtHVbULjrg3DAksyKb4YhiA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678286; c=relaxed/simple; bh=gBHNpfomL5C8aByMeM5Ue32AskzufHPucinPsSA/JS0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=nprqc0hcjT+CUgvOwjTnBBfLzNF6pEMbdqsO5LzmdqeTlR5m6dL/uVSVk79+uOkgZM7PHvWhzgFzleD+6Iztw3s93KA8ZxsVoDkAjniQjI0it0shmOGRsoll/VXkWWEB/AGLioOehsm+t307xrCcaiDYLQVkVbRrvJ3WfFOiiNM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=bZC0F00H; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="bZC0F00H" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=XWdlLEvtMOo+WLw1CwWGQRpC7Kra2Afx9noqW29vGvFv/v1bIjD+VIXwRYtVlDFdQneWtWD+CHHXy7++crM6BrLg1AfLKgHEBrgh90ehauVr2Dm0xNFWLYsQau6dSlhwRL6+vlt5UgkbMUiw2f0Sx7X6AlBKvaNhWoMWRRRxh/EfuFkxA6czmaeZA1XZSjd8ArXy+tPKdWon9BgKg/W8YkN+UOd7ubK3Cu9gtAfFJw9m6k29DLiuREQ4g6fXrO9LgyxUjIwDN51EPNawyadBaF6MjFLXkJfQ2+KLDJzZR25ITwTW8rqYM64+V3NWsgX2SCKUbCUsxeWEfsu8ZVqiPg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=By5U3BFeUvqrtkv8vqZIMAo49PlwXH5yvtLg3RIGris=; b=cr2Fs0aiuXYsfj2MrL7/5ngIhgYYaOMeAtHTHEznQKM+lcdwwOyWDVESvtwzixmt16ach/ldRq9yVuidP5lyYqmA6rUl4QdLo8kItQflY7bh7c5JxV0IJSB4ZpxYfXafW93xyna2lPrAocdW/4UsIgj61eqigw28FkLzINyDa169jL099QInAfhNYLKhN4ZTdJkde9wWpILnWHHDChX8glBdUyTDtuaTxKdb3BMM57UGRw90csLM2do6inRqjM9Qdr1qZATprpyAhLk+0mnps7dLS7b5QGQ3E8cI1atJT5x61gq3TGAFstF51NjurNFG0XEvARg3VKksqyscB+W6qA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=By5U3BFeUvqrtkv8vqZIMAo49PlwXH5yvtLg3RIGris=; b=bZC0F00H5KGeaEC9Qj5mNAMdemHzTHrQYPdP41U5UB0eLAJ1spTxMmujG/GCEi8Z4BrdJLfKdS3XbNvHI8ra4vjI27Ht/e6i/kFd45TsS7vjvoJwgyCMMGmzRToj26TNR2A4Tc9+/4CjpM8AusR6t4a+IFykBFDqVLLtz+UDNn7KcXqfF8Z5RQwtUBg5Ou9kzpSgTT17RkoVtsbnbBGZxPWuBormZKJv/eOglEHxqUROWVvUcjnsJUNvICKa3sLtkcs67okWAr/ElO30huXSgovSbe/ets+maDlvxLgBtNbK9W9AUFOb3qDpPAASVTISkXAEM3tYdv2zM7u233ig+g== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:37:52 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:52 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 08/22] wifi: nxpwifi: add cfp.c Date: Mon, 30 Sep 2024 14:36:47 +0800 Message-Id: <20240930063701.2566520-9-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: f4394a42-491b-4ad5-c89d-08dce11a68d8 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: taUKaOAKA5bKLofPftSAYvEyfubfCadKbVSiI4A7uWRqlEoQpJN6F+x5SY37caehbJUbBKYNYpce91Nmq5GCnlayDd+mdftHq0Sd/dKIbD11ox5uvIsw9WwKcLN4JtokAMdp2dgkapLTZZ8Eicn5le839iR6dAfwkcLVR1h6XeAdXpakFK2Xg/9w10kaWv8iJPpLeW//Kiis0mQTfnG0nfuAUa/qxLBacU0LPOlU4Ttk3O0BNXiPepHII+u2TaTdUu3D1fdUFNN558xpUAo82oAVxtq9ZqVynyQKxMy+YC6QXnnuSNp5FhI7YoQU0HNGKEpTfoPJrHzZirshkUEaNJxSeIHd7RvERlgGnhpwM1A2l5OTk/8dC8E4g7xOmMI310r+/YEqkdvbfGHmORKHuuItLaO8YPUVLVmo76UimAjeMBLwsbrRshdPWRZemP1EpLGnShlRnLTcyv5LDfNKHqBH0BywE5bCPM3jn2p0Lfktcjb8OY3NGNccGpoI5fMPIzcVH0/1eNGygOrG+kZ5vAppAVlMfXf06jT6ilpyLOxYNrnZV0sorvI6DCaUHBHnv2iW2vy314tgUpSCtDJs9s6HJ4Ngyxf/tpqDbkkwj69N9KmdcUeZgj/QgEglIxEzaL7TgX8Geqvn8hXtfZWnnu1shP+/lWYjAKviciAOtrxaHJ5Hce06wFFQDa+0ALm3OELIWlNbIBuM/vzepdz9nETGjDDtcug87Hk02Ur9aUZzjOO5IzRndqlyPOl5gL8cvT8ifugdbLhPhr4PYz9aANUH/u6KeUt9pFSguWgIg7eNVKUiNQHh3oVldk12eTXG9BNHGJ4bwhp3fGFtU8sOZUTI4LtUKb7tqO9UYpixwQO8CpQv9/TzZsutDnll448/u7qt6xWxkeE0UM/uB9CUU/1z6j+m59lVxL0/6Q8AoQcNGP7rCWMXIdo9SHmqO7xjATR5uy9Md61h09bxxvwfkuIT1ueeluOFXvtu+Q565DDGgueEPq4HCgGLDQWiXAJ+Lf2KW5grTjASAASNakNh48OCrrOXt/cK7Mbcbe7l0upa3jwapRmrTgKtbfwqa7mxyPrt7oezfEVs6/xkdi1bBq7YtYysGZefsBfubuWGtREsVFPiNMi1fumFKojh3XiVg4AyieEZmQMVu74T9S/j/kayoB00AjYCaeo/2aG/GMHwew0OPaL/VHwQJDiuHeM5GMG+20zpp+SWBHxVQk5LGY3b4vSxYiQxT5W1jAWf1mTfEfC3DKQDIICFIyxgkJAHXhA12kXyLCONLvTPeHcHqY+2fFD2yawPlOl67quWafzU6GmiqVTufUVwgwzhRmkEyuZ+okkKJhnSh45nT7HjR+Cs1rLPSK6setr70BfZ2EFnntu/c92JTysfpxSmLCRafQUtaiC1uxBuEvq25CdwfA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: BL6Sg8DgqaXVgjbZKBryaLZ3R10xFXqca8ls4Y2ddD4VoosuTnHRj0OmVkmHq2zE45WV0643Us8rTvNaX3vL8r37/56PR1FaKMOppKtrBON64eKrHcQaLkU6ezMTmk9+uvUk8gasMltwHKOIgyCQI5VJi3sIvTlEJGSh18Z5jSWPSdt9Mwtdm0Vq8ZloAZPGi0XJGApE+cDnaRF52OuRd7KOtelbxRKnBE4LST/mMb3oVHg8EKU0r8vT6UKGWi2Sr9xRZe4OQqzitgF+u2gKkK6tDKt94JfxcQa0V2iAsXEUilvR2TjnEXRKdcUVsaQRQ45gmjZev1tUFtXnbRpqATVfdopfJFXxxveG9WR8MrdcgayD+pv/TEGGjPWWpfrQoEn+VSfsY0P+9qaFwrKkhaw/ANy8hoGas1ci07E3MRMehBhcXEfcyeWh0zTLXCC/gM4agP6kpCc9X1mdINxN00arjK0ppiD6nG2UUx8/kjmRzErzD1uFTo2xeE/mQCBdoh4IeR0IQ9VA1YAma+qjg8X/2mQwnnByOZk+psIL+3wS+PsRbHDJWHK4H8CmbBjt5DEVFZkIGLMpXAq2LvhY1fBqqVt9+01pxw3N2jVWLsGjgb7bNFFGxorhofkI6eQmlQHVSp4537UYeCnVd+Pmz5OB70eM2E0YSKgicGv1wySz0aStw3d7BjoR4gm6ozfUjbd94zSHJhKRqsSJ1WGbFb3l8BOZJ8wKzj6bOJhs9Gr/0ijPRfBEKAl4GSs9cluSXUeNAyNGY9Gg+BbBXr9+OXbsVrrDmC32oywfXWVMD5ove7iM02uBn6Eed3biUUrbOF0q1g9x5zrWWvn5Q0nM6vpJnPpwkCFikKt/rPtreeYvwKDKBKuNF/Wug4hrT9rckhzTK5lAwLmD1Km2u1B5kSONvHNS+3i6MoF/bYrj7SKSMc9I9J9s8Un9ctb+5GthxVk3UiZlV81jmfdkDmzTXw49qKkCybgOL5jf0AxuinDLgmRnfe7joWcxfedwLvDGxTTYsSY025cUugnP3HRi6rnVWraub8CTglJDATJ4rMl79jV2G8eJqvOPF92XRzvj9qAnLqEhIqC+ta1wm3luzBqrRq9jmiVqlHLaJIC/evs8v5/aDkns69O5Tir+srJedrScxfFXrrDswiwt94ra4KCV1eppxV345vplMZ+N9kEAy+kDfGvDFzjdl74uKulzBRqOSdpxQVf+/sA3LSZEdzdBIc/fR5fyhHWxoeFUS9ZZLuTci5XDcH0q/JHDMu2IgdEofaV6hMU1L9SbTkePJbKqh8s9TtPKF5MzJsWaJ0ha3kBItxXkDlID4AFpHWoj6gB+1H0mW5Oa6um+LAJB6xZV1HH8xTEiy3yDojC/u7hJHaLRL/+awCUmuK4SZ9+Jc5vvBJJQUWuvj5kiDJgsIiiPYsNg2Pk9WTRaiUkZXKEQFkPeOetZfovnrmjYuFkY2mI6y9CFgVxhbyrnPKqTEvHdrkg0rvmRzX5KzpJOz2235rxW2fbNRLQph3tDQk5UWdxvIDBwzlV3HS+GWl6cTuoC5PyZ5dBTFai85K4vdCjDepInRfpEhRa6hk/+Dh9S X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: f4394a42-491b-4ad5-c89d-08dce11a68d8 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:52.6939 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Q7GNyE2pJ7ZiTI5OvQbHBKI2wOyvBidMH1JB4ywTxCrYo+mJ4B5xS7Qn6uImt+v1YXN1pP02y4rIkH91z9xRgg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Files cfp.c supports functions related to channel/frequency/power. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/cfp.c | 475 +++++++++++++++++++++++++ 1 file changed, 475 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfp.c diff --git a/drivers/net/wireless/nxp/nxpwifi/cfp.c b/drivers/net/wireless/nxp/nxpwifi/cfp.c new file mode 100644 index 000000000000..cd0dd4a25d2a --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfp.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: Channel, Frequency and Power + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cfg80211.h" + +/* 100mW */ +#define NXPWIFI_TX_PWR_DEFAULT 20 +/* 100mW */ +#define NXPWIFI_TX_PWR_US_DEFAULT 20 +/* 50mW */ +#define NXPWIFI_TX_PWR_JP_DEFAULT 16 +/* 100mW */ +#define NXPWIFI_TX_PWR_FR_100MW 20 +/* 10mW */ +#define NXPWIFI_TX_PWR_FR_10MW 10 +/* 100mW */ +#define NXPWIFI_TX_PWR_EMEA_DEFAULT 20 + +static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; +static u16 nxpwifi_data_rates[NXPWIFI_SUPPORTED_RATES_EXT] = { 0x02, 0x04, + 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, + 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90, + 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, + 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51, + 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 }; + +static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; + +static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, 0 }; + +static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, + 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0 }; + +u16 region_code_index[NXPWIFI_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30, + 0x31, 0x32, 0x40, 0x41, 0x50 }; + +/* For every mcs_rate line, the first 8 bytes are for stream 1x1, + * and all 16 bytes are for stream 2x2. + */ +static const u16 mcs_rate[4][16] = { + /* LGI 40M */ + { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, + 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, + + /* SGI 40M */ + { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, + 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, + + /* LGI 20M */ + { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, + 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, + + /* SGI 20M */ + { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, + 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } +}; + +/* AC rates */ +static const u16 ac_mcs_rate_nss1[8][10] = { + /* LG 160M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 160M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 80M */ + { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, + 0x249, 0x2BE, 0x30C }, + + /* SG 80M */ + { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, + 0x28A, 0x30C, 0x363 }, + + /* LG 40M */ + { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, + 0x10E, 0x144, 0x168 }, + + /* SG 40M */ + { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, + 0x12C, 0x168, 0x190 }, + + /* LG 20M */ + { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, + + /* SG 20M */ + { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, +}; + +/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ +static const u16 ac_mcs_rate_nss2[8][10] = { + /* LG 160M */ + { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, + 0x924, 0xAF8, 0xC30 }, + + /* SG 160M */ + { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, + 0xA28, 0xC30, 0xD8B }, + + /* LG 80M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 80M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 40M */ + { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, + 0x21C, 0x288, 0x2D0 }, + + /* SG 40M */ + { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, + 0x258, 0x2D0, 0x320 }, + + /* LG 20M */ + { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, + 0x138, 0x00 }, + + /* SG 20M */ + { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, + 0x15B, 0x00 }, +}; + +struct region_code_mapping { + u8 code; + u8 region[IEEE80211_COUNTRY_STRING_LEN]; +}; + +static struct region_code_mapping region_code_mapping_t[] = { + { 0x10, "US " }, /* US FCC */ + { 0x20, "CA " }, /* IC Canada */ + { 0x30, "FR " }, /* France */ + { 0x31, "ES " }, /* Spain */ + { 0x32, "FR " }, /* France */ + { 0x40, "JP " }, /* Japan */ + { 0x41, "JP " }, /* Japan */ + { 0x50, "CN " }, /* China */ +}; + +/* This function converts integer code to region string */ +u8 *nxpwifi_11d_code_2_region(u8 code) +{ + u8 i; + + /* Look for code in mapping table */ + for (i = 0; i < ARRAY_SIZE(region_code_mapping_t); i++) + if (region_code_mapping_t[i].code == code) + return region_code_mapping_t[i].region; + + return NULL; +} + +/* This function maps an index in supported rates table into + * the corresponding data rate. + */ +u32 nxpwifi_index_to_acs_data_rate(struct nxpwifi_private *priv, + u8 index, u8 ht_info) +{ + u32 rate = 0; + u8 mcs_index = 0; + u8 bw = 0; + u8 gi = 0; + + if ((ht_info & 0x3) == NXPWIFI_RATE_FORMAT_VHT) { + mcs_index = min(index & 0xF, 9); + + /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + bw = (ht_info & 0xC) >> 2; + + /* LGI: gi =0, SGI: gi = 1 */ + gi = (ht_info & 0x10) >> 4; + + if ((index >> 4) == 1) /* NSS = 2 */ + rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index]; + else /* NSS = 1 */ + rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index]; + } else if ((ht_info & 0x3) == NXPWIFI_RATE_FORMAT_HT) { + /* 20M: bw=0, 40M: bw=1 */ + bw = (ht_info & 0xC) >> 2; + + /* LGI: gi =0, SGI: gi = 1 */ + gi = (ht_info & 0x10) >> 4; + + if (index == NXPWIFI_RATE_BITMAP_MCS0) { + if (gi == 1) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < 16) { + if (bw == 1 || bw == 0) + rate = mcs_rate[2 * (1 - bw) + gi][index]; + else + rate = nxpwifi_data_rates[0]; + } else { + rate = nxpwifi_data_rates[0]; + } + } else { + /* 11n non-HT rates */ + if (index >= NXPWIFI_SUPPORTED_RATES_EXT) + index = 0; + rate = nxpwifi_data_rates[index]; + } + + return rate; +} + +/* This function maps an index in supported rates table into + * the corresponding data rate. + */ +u32 nxpwifi_index_to_data_rate(struct nxpwifi_private *priv, + u8 index, u8 ht_info) +{ + u32 mcs_num_supp = + (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; + u32 rate; + + if (priv->adapter->is_hw_11ac_capable) + return nxpwifi_index_to_acs_data_rate(priv, index, ht_info); + + if (ht_info & BIT(0)) { + if (index == NXPWIFI_RATE_BITMAP_MCS0) { + if (ht_info & BIT(2)) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < mcs_num_supp) { + if (ht_info & BIT(1)) { + if (ht_info & BIT(2)) + /* SGI, 40M */ + rate = mcs_rate[1][index]; + else + /* LGI, 40M */ + rate = mcs_rate[0][index]; + } else { + if (ht_info & BIT(2)) + /* SGI, 20M */ + rate = mcs_rate[3][index]; + else + /* LGI, 20M */ + rate = mcs_rate[2][index]; + } + } else { + rate = nxpwifi_data_rates[0]; + } + } else { + if (index >= NXPWIFI_SUPPORTED_RATES_EXT) + index = 0; + rate = nxpwifi_data_rates[index]; + } + return rate; +} + +/* This function returns the current active data rates. + * + * The result may vary depending upon connection status. + */ +u32 nxpwifi_get_active_data_rates(struct nxpwifi_private *priv, u8 *rates) +{ + if (!priv->media_connected) + return nxpwifi_get_supported_rates(priv, rates); + else + return nxpwifi_copy_rates(rates, 0, + priv->curr_bss_params.data_rates, + priv->curr_bss_params.num_of_rates); +} + +/* This function locates the Channel-Frequency-Power triplet based upon + * band and channel/frequency parameters. + */ +struct nxpwifi_chan_freq_power * +nxpwifi_get_cfp(struct nxpwifi_private *priv, u8 band, u16 channel, u32 freq) +{ + struct nxpwifi_chan_freq_power *cfp = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch = NULL; + int i; + + if (!channel && !freq) + return cfp; + + if (nxpwifi_band_to_radio_type(band) == HOST_SCAN_RADIO_TYPE_BG) + sband = priv->wdev.wiphy->bands[NL80211_BAND_2GHZ]; + else + sband = priv->wdev.wiphy->bands[NL80211_BAND_5GHZ]; + + if (!sband) { + nxpwifi_dbg(priv->adapter, ERROR, + "%s: cannot find cfp by band %d\n", + __func__, band); + return cfp; + } + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (freq) { + if (ch->center_freq == freq) + break; + } else { + /* find by valid channel*/ + if (ch->hw_value == channel || + channel == FIRST_VALID_CHANNEL) + break; + } + } + if (i == sband->n_channels) { + nxpwifi_dbg(priv->adapter, WARN, + "%s: cannot find cfp by band %d\t" + "& channel=%d freq=%d\n", + __func__, band, channel, freq); + } else { + if (!ch) + return cfp; + + priv->cfp.channel = ch->hw_value; + priv->cfp.freq = ch->center_freq; + priv->cfp.max_tx_power = ch->max_power; + cfp = &priv->cfp; + } + + return cfp; +} + +/* This function checks if the data rate is set to auto. + */ +u8 +nxpwifi_is_rate_auto(struct nxpwifi_private *priv) +{ + u32 i; + int rate_num = 0; + + for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) + if (priv->bitmap_rates[i]) + rate_num++; + + if (rate_num > 1) + return true; + else + return false; +} + +/* This function gets the supported data rates from bitmask inside + * cfg80211_scan_request. + */ +u32 nxpwifi_get_rates_from_cfg80211(struct nxpwifi_private *priv, + u8 *rates, u8 radio_type) +{ + struct wiphy *wiphy = priv->adapter->wiphy; + struct cfg80211_scan_request *request = priv->scan_request; + u32 num_rates, rate_mask; + struct ieee80211_supported_band *sband; + int i; + + if (radio_type) { + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[NL80211_BAND_5GHZ]; + } else { + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[NL80211_BAND_2GHZ]; + } + + num_rates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((BIT(i) & rate_mask) == 0) + continue; /* skip rate */ + rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); + } + + return num_rates; +} + +/* Convert config_bands to B/G/A band + */ +static u16 nxpwifi_convert_config_bands(u16 config_bands) +{ + u16 bands = 0; + + if (config_bands & BAND_B) + bands |= BAND_B; + if (config_bands & BAND_G || config_bands & BAND_GN || + config_bands & BAND_GAC || config_bands & BAND_GAX) + bands |= BAND_G; + if (config_bands & BAND_A || config_bands & BAND_AN || + config_bands & BAND_AAC || config_bands & BAND_AAX) + bands |= BAND_A; + + return bands; +} + +/* This function gets the supported data rates. The function works in + * infra mode by printing the band and returning the data rates. + */ +u32 nxpwifi_get_supported_rates(struct nxpwifi_private *priv, u8 *rates) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u32 k = 0; + u16 bands = 0; + + bands = nxpwifi_convert_config_bands(adapter->fw_bands); + + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + if (bands == BAND_B) { + /* B only */ + nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_b\n", + priv->config_bands); + k = nxpwifi_copy_rates(rates, k, supported_rates_b, + sizeof(supported_rates_b)); + } else if (bands == BAND_G) { + /* G only */ + nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_g\n", + priv->config_bands); + k = nxpwifi_copy_rates(rates, k, supported_rates_g, + sizeof(supported_rates_g)); + } else if (bands & (BAND_B | BAND_G)) { + /* BG only */ + nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_bg\n", + priv->config_bands); + k = nxpwifi_copy_rates(rates, k, supported_rates_bg, + sizeof(supported_rates_bg)); + } else if (bands & BAND_A) { + /* support A */ + nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_a\n", + priv->config_bands); + k = nxpwifi_copy_rates(rates, k, supported_rates_a, + sizeof(supported_rates_a)); + } + } + + return k; +} + +u8 nxpwifi_adjust_data_rate(struct nxpwifi_private *priv, + u8 rx_rate, u8 rate_info) +{ + u8 rate_index = 0; + + /* HT40 */ + if ((rate_info & BIT(0)) && (rate_info & BIT(1))) + rate_index = NXPWIFI_RATE_INDEX_MCS0 + + NXPWIFI_BW20_MCS_NUM + rx_rate; + else if (rate_info & BIT(0)) /* HT20 */ + rate_index = NXPWIFI_RATE_INDEX_MCS0 + rx_rate; + else + rate_index = (rx_rate > NXPWIFI_RATE_INDEX_OFDM0) ? + rx_rate - 1 : rx_rate; + + if (rate_index >= NXPWIFI_MAX_AC_RX_RATES) + rate_index = NXPWIFI_MAX_AC_RX_RATES - 1; + + return rate_index; +} From patchwork Mon Sep 30 06:36:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815425 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EED0E175D4C; Mon, 30 Sep 2024 06:38:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678290; cv=fail; b=BUhESopk8ifaNlTKEUvtJTue6WqCzB5ar/jKsCL0aoNaSxu28uL0WqDGDWPsbBY1pOaQ7WWgD2tIAaZy1Ak5OVRpZafipjPpysygLX5N3qG5i9s86LbS6BMaeyAOyrFp+3uel4g0X1H2LGGJZuM8Sew1k9nwXKyc9/0HOA5QzAs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678290; c=relaxed/simple; bh=VcdB3JgYG/LipjeVhob7QqQ6ZzmkJ4CKhEJwHXVB//g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=or1MbLZZU7fo4bvsf3d8o44gf3iAuISb+weovO2hHoih1J8eysw4FwVh9HVDcvbBBHA9mTKFGzCbqeAQ8S4VnfRYfGFd2Dokg5PuuGJt/eYSkUDm8U8nq/9W88F8nV0PUEHnV2dCym5xqMXDNZxrImNV1EBMj21ipRsTf2gY+Po= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=Y4m0l6gT; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="Y4m0l6gT" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=JyE8LsQ9MaRrJhBMvTEOsU8DX0fTnqh1hY8KgUCA+wA9c7wIoTI47BPOcmlW9bCqgC0xqiI7y6UoRytevE15oXgsgndoB2sEILmYtXkqpky253PiqwMnKqhxwca4oOq2eVSGIujAIxanUtO1ye714JC08yhBn4vEi4aJ6aNwTaXVqgKQTuzJDd761N19YrFgv64pBC+Ql62UhpmRhoIK0z3xqnmZKJBd5zpD3EUjwmYVM+BeSrWe6Mo342rp/ApKlbCZgMe0HAKzTACkt2ORP6HFqR1A0UWqwYCX6CyovzXAM+FlUPtAIfAw1cVm5+W0MO0qIHTLJABUjVRcHqRR8w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=D0zDSZeBOyt17vs6ECOwPsjkNRNjt2O0icKdovxCG/4=; b=xbaYCF4Rj/uxe4ayX9mOQEPf/cGZu6xI+/jnGkZ1CnUgH1xo0w2CmhNGKX2wlF90PMBbR6TCa+SrizwuepFS1F78Ll5TNb3Fkw+22ss2G2LB4WCc/K5cz5CbCc2WGWbvhUMYVqdsx6ZJc1MBS8ADqEfOiZ0hzqhCbWkYJ9r4Y1E9TJ4wgQlrrlD7PXIYYHxKdyWqH8h707NCdEkaJDiCz52S0yXTop/86YMlrIfJ6xp8VBovJIdhS1wi721nqoPpOvcxCWveXv27VhhmNckEKXtWwHtXrSfGlfL2J7ejSdE/7j71Fu/S9k0RJ7h9CMpz9Tln6eNxxtAdncTK19cySg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=D0zDSZeBOyt17vs6ECOwPsjkNRNjt2O0icKdovxCG/4=; b=Y4m0l6gTBTsnkXhZw89r5v7rPTvbM4i6kE5onkoldSYeBmw33P4k71PUMHd4P3Ho51oRtoW3Tky1qBn/iM7dZAh69FlED+/ntkfLTg4NqUHdR1j2OhRcTeEErR/Gp7hfdppeaqKL3I75dvhy8aLAeTm5hi1X33HDSHIMhntEn/WYypjNhRMt6qCTEEeuwdA+uxtmshXQPIrZtNFfu6E8MH2eBT0ikG/qto1M+Js/PDC8uD3aGNhZp4ijw/wAm5uYC3wG5mHC6k58OxFND6urYP7yD9gyLmQfyxxwPOjclFrJL5SBT/nHvxiHajAWzGcGJaMKBb+6F6vT7GUzYT0qDg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:37:55 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:55 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 09/22] wifi: nxpwifi: add configuration files Date: Mon, 30 Sep 2024 14:36:48 +0800 Message-Id: <20240930063701.2566520-10-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 96b8cb65-6d35-4c79-8adf-08dce11a6a7f X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: okzMDGbH7ZIZe3zLajWC6HhI6hQduQhg+kzAKHlBKmzI3hc/V0oMEARD267j0dp/jZCVdEc8AvQthSFXbwHmGeA2Gx9wEjJaaGTR72UfqHJO7QNTEPPUH/QwYgwIADpc9tHRRdQRLN0oRVi7Gq7vnbRqetNa7JxEjNeJ1U8s8YgeVUPNNORLkCK2wCCnetXVI/s3jnu6qxa1yaJBLx5D3J7nEW82/iu5ZPQlJkR/Bxor3cwEOCxL1k0GSt/5MW2Q+OTX7uaaGOMXwmzouWegif1n2am2hMIA+OUAS9EPd8z1t+k3V8CFrmCXuHL6SKX5ak+ewv2BH0J6BPnnO8e1Vq84R1vk8aiySxPhs7T+0Fl3lzbxnyqHSoNlB+5M4pk5YQfE/9r+HIaaHLdUExHPUvOHrU9v+WvAggkTVXjji8FdlMqvUyPr4jYqlt/vBprFVqTTHApv/6Styf90XKYQKLlMw7Z0Q7EU3WCZldrxX2FLVBsMHcERk7W7R0VZo51N1b9fYhTJhrpO+UDPG2uyYdaohiX4Kwa4a9PCB/SXMlWr2RTzDqq/pr77z1ZkkMGWsG2Si0oVlfBycM+i2azJDJSQaUf47VbQnrHSg9ZdrH/+d/wxwr4ql/YkeLqG5V6v713IxKsAMNzxiX9g8t1w/xoMo3L9SUJHFOICf4oEr8NlmFy5CnSi9u+4YimL9AXLUKpz2+IQeXcw0/l8ww+kIWPEsaF0U1hLgWeJMVRmAKL6xk2czFYWCEJm6LNKSDVAVnchamKlR3GL4o+ICpIZGDOZrtw+maJClOH1suKrAqghP77MjdP2bzqFt/Q/iePGQkfb/vCRvf8znY8Obt9yX6RL2C1iqjhrGBCQQr7WQvO2UyYTALCjx2F0p1GGVSGfl74b56+BNcBh0hfrXkf34KJqTbyWh153D1XiSHfodOt6gDhxxEhvsYb3ZousmSY57ExEyqZUuy0gtM76JzHLvmjcqqpt+727yqLp0NuwzHUAHYVKQ9xHzA9/dh4Zg9OYdr/fsDKvigZCTSSnhsPTgl7IrmMw+Op+14ELnB1nNvcD7CAWdMXwdczyagGTxYGabUPZxbIqWPKcha51AHND7qYpM/MwOSWGUgL1ULUBmShq6IAid+xH1mlQ5dBLaoB8ks7tf8LJ5yVonVhXlDoVt8x/eEjvGkg+4SQ2up4+4OPXaQr/X1OuNabRj0s08ECyAWCO8nsN1JBfGZzke3fdwBmiYlnNcCb8pKBu/oPly5NsrrRcOGJeB2Ubw1GSbrdcRKLOJ5wICTkDzmdHGqgHZJYuEkirWGR2L7HJV9CWUw8uca63thgk/ROskqI1T40TFTOE3w7jMss322+wHuCcsQuEesJcwZq9d8tHScX2H+ydj2UEn2pVJA7HU/5438CwCgXMeqF/BfgR6twbzpdnuA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: IXjXCrp+X6Bww818I+BRrrER2SCoR90MpXAjMMDjhO1o9+l2c2xmUX9ZSuwqronzX+PBneRyqVRgScKRmBUr6/2CWQFiEwVZKJB2vc2uTvFzQGhD9jtaMo8SY2lrim2Nk7s6FpXebN1nLAU7BeHZtJxcZkQhjPDXrxDOnx69HJXQtxPOJs+RlHgM4U6bFtI5f9yCatHv79F/eRxLRaThHXsNjS+mZYP7UD86tZ7LU564XcjyeO2oc9noPcbTdjTXYgYwBRWGo4bqUhC4vo6cD+gC8bbodkr1A7IS4B44FRKpBnKwkRkKcywdOMhj5Jz0H0eclQMFTvPmBn1i7fhbWxEv6wAAPcKG8ZMLOcYcnkjkkkwqQuGIMt2cMpPId1Zuw/gBJgSbJ9XAyV2tiV8Io6+n88W+ZwBLhE5INiuKT2QPg2kcOgHTKooefsIC43p/vsil/wUv9HTur38aXPjJJScH0PV/o4Ht5WLQn8AZy4oscZlZ4XYWUZCcrCTc6/L/ZCWBhqAgwmUvVxQSxe0J8JNk/WdLLqy++MkLFpzjmxrF8J6zYTeG5rWdwPZviAcoN2n1EwuaGWWZYkDZKOBipsbFkzUJniZeH/zt2WCos/EXBKumle9QOFVGmyxNEGEdANPj8yrDlF28fZNulbnyfbVDoJaZrDKA/Qpcwkg7umf6RFsu87Pv5eTl/LPZTdRH10NagzqTQkToSuYOEzOrY9l0xLrDKxytKSgwFVo9nk53YbKHXNduVm7x2Uwr4jSMHNqCHYcJg4tlDED6DNULXUttEgRH2rSlp0EQ1tXxkcVI3d9Th+JAMrmAkt7tlbiLQY+Lo8RIk5SmcyYoMPP4WScxjMjbYUyF90hcoD8J2L6xR2HQCA8jpTS67u+lxBL+uCZu74gFnRtEWrfNuSM0mkEFE1rurTRWfZ0o+qZvf+OUqkkoJ2c90F+t+/WeTRUw3GsGLFtoeiS9yq4tGg1os+ofEbjz6R24TtVSZYkwRvXoRQDEnDB+e6Hlg6tAtBWiualYEwCRM1nS0JDbZrB1A7I/+iPStpo2wOXn6tOL9gRuRvPSoS1J6viQK4yN1A+lBxKbr5fKQaVPnbLsoNKcFIvcDgx1APiKk4KVjldBmDSb8Y9KwdjmQEdCHoPsaAA4vgVJmMuBSj3fzbtQBRDE2s5gjGrHvFRVw+AmrspQZggd4ikRL1z6pEOIZkNh3ssh2OjHU/V6tXF42tzfH2EJcn3cUBTvSgsJZ/uhDH2/hoeJ76/k2MZiWOCpskiEnNPztULQbRsJUL/5rCoTjRMK9Mu9ZoY5N7WpXOwDstFNpj+uQSCwwYjJ1nIEhdx5zE+gXZSpafl+E10THrgCoXECuznE2Ban3P0qCBje4jGOgYpS794gNUSA10xwcaFs4ayqFrXRMJDKev23qj+LyO+BqFaDQjiL04Wpp3PzcHDe9KoDRkoKxFckG5ux1rSxrxbXKZPMxI3uhodEvnr3r1kTjgvrq6A3XHYoolM2pjL3lqrtFOhVfmpYzkvhFVQOsKSguyraRkUxZ9WfLlawuRMfp5Gmca5E5YdpAmvWekK530paRAEnclHmanU5njwNQdka X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 96b8cb65-6d35-4c79-8adf-08dce11a6a7f X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:55.4861 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Vd03RmSgqgOSi7p8I5YNYOcOC88+A+aWCazHTxdYMVKVBIDMRVMsciTLAvqqYbbhlNj+Lqxu2uDMc0na/fgl4Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 File cfg.h defines constants and data structure used to configure nxpwifi. File sta_cfg.c supports common configuration functions used to configure nxpwifi or get status of nxpwifi. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/cfg.h | 874 +++++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_cfg.c | 1311 ++++++++++++++++++++ 2 files changed, 2185 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfg.h create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_cfg.c diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg.h b/drivers/net/wireless/nxp/nxpwifi/cfg.h new file mode 100644 index 000000000000..6d13655f5a8c --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfg.h @@ -0,0 +1,874 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: ioctl data structures & APIs + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_CFG_H_ +#define _NXPWIFI_CFG_H_ + +#include +#include +#include +#include +#include +#include + +#define NXPWIFI_BSS_COEX_COUNT 2 +#define NXPWIFI_MAX_BSS_NUM (2) + +#define NXPWIFI_MAX_CSA_COUNTERS 5 + +#define NXPWIFI_DMA_ALIGN_SZ 64 +#define NXPWIFI_RX_HEADROOM 64 +#define MAX_TXPD_SZ 32 +#define INTF_HDR_ALIGN 4 +/* special FW 4 address management header */ +#define NXPWIFI_MIN_DATA_HEADER_LEN (NXPWIFI_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ + MAX_TXPD_SZ) + +#define NXPWIFI_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) + * + sizeof(tx_control) + */ + +#define FRMCTL_LEN 2 +#define DURATION_LEN 2 +#define SEQCTL_LEN 2 +#define NXPWIFI_MGMT_HEADER_LEN (FRMCTL_LEN + FRMCTL_LEN + ETH_ALEN + \ + ETH_ALEN + ETH_ALEN + SEQCTL_LEN + ETH_ALEN) + +#define AUTH_ALG_LEN 2 +#define AUTH_TRANSACTION_LEN 2 +#define AUTH_STATUS_LEN 2 +#define NXPWIFI_AUTH_BODY_LEN (AUTH_ALG_LEN + AUTH_TRANSACTION_LEN + \ + AUTH_STATUS_LEN) + +#define HOST_MLME_AUTH_PENDING BIT(0) +#define HOST_MLME_AUTH_DONE BIT(1) + +#define HOST_MLME_MGMT_MASK (BIT(IEEE80211_STYPE_AUTH >> 4) | \ + BIT(IEEE80211_STYPE_DEAUTH >> 4) | \ + BIT(IEEE80211_STYPE_DISASSOC >> 4)) + +#define AUTH_TX_DEFAULT_WAIT_TIME 2400 + +#define WLAN_AUTH_NONE 0xFFFF + +#define NXPWIFI_MAX_TX_BASTREAM_SUPPORTED 2 +#define NXPWIFI_MAX_RX_BASTREAM_SUPPORTED 16 + +#define NXPWIFI_STA_AMPDU_DEF_TXWINSIZE 64 +#define NXPWIFI_STA_AMPDU_DEF_RXWINSIZE 64 +#define NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE 16 + +#define NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE 32 + +#define NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 + +#define NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE 16 +#define NXPWIFI_11AC_STA_AMPDU_DEF_TXWINSIZE 64 +#define NXPWIFI_11AC_STA_AMPDU_DEF_RXWINSIZE 64 +#define NXPWIFI_11AC_UAP_AMPDU_DEF_TXWINSIZE 64 +#define NXPWIFI_11AC_UAP_AMPDU_DEF_RXWINSIZE 64 + +#define NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff + +#define NXPWIFI_RATE_BITMAP_MCS0 32 + +#define NXPWIFI_RX_DATA_BUF_SIZE (4 * 1024) +#define NXPWIFI_RX_CMD_BUF_SIZE (2 * 1024) + +#define MAX_BEACON_PERIOD (4000) +#define MIN_BEACON_PERIOD (50) +#define MAX_DTIM_PERIOD (100) +#define MIN_DTIM_PERIOD (1) + +#define NXPWIFI_RTS_MIN_VALUE (0) +#define NXPWIFI_RTS_MAX_VALUE (2347) +#define NXPWIFI_FRAG_MIN_VALUE (256) +#define NXPWIFI_FRAG_MAX_VALUE (2346) +#define NXPWIFI_WMM_VERSION 0x01 +#define NXPWIFI_WMM_SUBTYPE 0x01 + +#define NXPWIFI_RETRY_LIMIT 14 +#define NXPWIFI_SDIO_BLOCK_SIZE 256 + +#define NXPWIFI_BUF_FLAG_REQUEUED_PKT BIT(0) +#define NXPWIFI_BUF_FLAG_BRIDGED_PKT BIT(1) +#define NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS BIT(3) +#define NXPWIFI_BUF_FLAG_ACTION_TX_STATUS BIT(4) +#define NXPWIFI_BUF_FLAG_AGGR_PKT BIT(5) + +#define NXPWIFI_BRIDGED_PKTS_THR_HIGH 1024 +#define NXPWIFI_BRIDGED_PKTS_THR_LOW 128 + +/* 54M rates, index from 0 to 11 */ +#define NXPWIFI_RATE_INDEX_MCS0 12 +/* 12-27=MCS0-15(BW20) */ +#define NXPWIFI_BW20_MCS_NUM 15 + +/* Rate index for OFDM 0 */ +#define NXPWIFI_RATE_INDEX_OFDM0 4 + +#define NXPWIFI_MAX_STA_NUM 3 +#define NXPWIFI_MAX_UAP_NUM 3 + +#define NXPWIFI_A_BAND_START_FREQ 5000 + +/* SDIO Aggr data packet special info */ +#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255) +#define BLOCK_NUMBER_OFFSET 15 +#define SDIO_HEADER_OFFSET 28 + +#define NXPWIFI_SIZE_4K 0x4000 + +enum nxpwifi_bss_type { + NXPWIFI_BSS_TYPE_STA = 0, + NXPWIFI_BSS_TYPE_UAP = 1, + NXPWIFI_BSS_TYPE_ANY = 0xff, +}; + +enum nxpwifi_bss_role { + NXPWIFI_BSS_ROLE_STA = 0, + NXPWIFI_BSS_ROLE_UAP = 1, + NXPWIFI_BSS_ROLE_ANY = 0xff, +}; + +#define BSS_ROLE_BIT_MASK BIT(0) + +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) + +enum nxpwifi_data_frame_type { + NXPWIFI_DATA_FRAME_TYPE_ETH_II = 0, + NXPWIFI_DATA_FRAME_TYPE_802_11, +}; + +struct nxpwifi_fw_image { + u8 *helper_buf; + u32 helper_len; + u8 *fw_buf; + u32 fw_len; +}; + +struct nxpwifi_802_11_ssid { + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +}; + +struct nxpwifi_wait_queue { + wait_queue_head_t wait; + int status; +}; + +struct nxpwifi_rxinfo { + struct sk_buff *parent; + u8 bss_num; + u8 bss_type; + u8 use_count; + u8 buf_type; + u16 pkt_len; +}; + +struct nxpwifi_txinfo { + u8 flags; + u8 bss_num; + u8 bss_type; + u8 aggr_num; + u32 pkt_len; + u8 ack_frame_id; + u64 cookie; +}; + +enum nxpwifi_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} __packed; + +struct nxpwifi_types_wmm_info { + u8 oui[4]; + u8 subtype; + u8 version; + u8 qos_info; + u8 reserved; + struct ieee80211_wmm_ac_param ac[IEEE80211_NUM_ACS]; +} __packed; + +struct nxpwifi_arp_eth_header { + struct arphdr hdr; + u8 ar_sha[ETH_ALEN]; + u8 ar_sip[4]; + u8 ar_tha[ETH_ALEN]; + u8 ar_tip[4]; +} __packed; + +struct nxpwifi_chan_stats { + u8 chan_num; + u8 bandcfg; + u8 flags; + s8 noise; + u16 total_bss; + u16 cca_scan_dur; + u16 cca_busy_dur; +} __packed; + +#define NXPWIFI_HIST_MAX_SAMPLES 1048576 +#define NXPWIFI_MAX_RX_RATES 44 +#define NXPWIFI_MAX_AC_RX_RATES 74 +#define NXPWIFI_MAX_SNR 256 +#define NXPWIFI_MAX_NOISE_FLR 256 +#define NXPWIFI_MAX_SIG_STRENGTH 256 + +struct nxpwifi_histogram_data { + atomic_t rx_rate[NXPWIFI_MAX_AC_RX_RATES]; + atomic_t snr[NXPWIFI_MAX_SNR]; + atomic_t noise_flr[NXPWIFI_MAX_NOISE_FLR]; + atomic_t sig_str[NXPWIFI_MAX_SIG_STRENGTH]; + atomic_t num_samples; +}; + +struct nxpwifi_iface_comb { + u8 sta_intf; + u8 uap_intf; +}; + +struct nxpwifi_radar_params { + struct cfg80211_chan_def *chandef; + u32 cac_time_ms; +} __packed; + +struct nxpwifi_11h_intf_state { + bool is_11h_enabled; + bool is_11h_active; +} __packed; + +#define NXPWIFI_FW_DUMP_IDX 0xff +#define NXPWIFI_FW_DUMP_MAX_MEMSIZE 0x160000 +#define NXPWIFI_DRV_INFO_IDX 20 +#define FW_DUMP_MAX_NAME_LEN 8 +#define FW_DUMP_HOST_READY 0xEE +#define FW_DUMP_DONE 0xFF +#define FW_DUMP_READ_DONE 0xFE + +struct memory_type_mapping { + u8 mem_name[FW_DUMP_MAX_NAME_LEN]; + u8 *mem_ptr; + u32 mem_size; + u8 done_flag; +}; + +enum rdwr_status { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +}; + +enum nxpwifi_chan_band { + BAND_2GHZ = 0, + BAND_5GHZ, + BAND_6GHZ, + BAND_4GHZ, +}; + +enum nxpwifi_chan_width { + CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, + CHAN_BW_80MHZ, + CHAN_BW_8080MHZ, + CHAN_BW_160MHZ, + CHAN_BW_5MHZ, +}; + +enum nxpwifi_chan_offset { + SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_5MHZ = 2, + SEC_CHAN_BELOW = 3 +}; + +enum { + NXPWIFI_SCAN_TYPE_UNCHANGED = 0, + NXPWIFI_SCAN_TYPE_ACTIVE, + NXPWIFI_SCAN_TYPE_PASSIVE +}; + +#define NXPWIFI_PROMISC_MODE 1 +#define NXPWIFI_MULTICAST_MODE 2 +#define NXPWIFI_ALL_MULTI_MODE 4 +#define NXPWIFI_MAX_MULTICAST_LIST_SIZE 32 + +struct nxpwifi_multicast_list { + u32 mode; + u32 num_multicast_addr; + u8 mac_list[NXPWIFI_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; +}; + +struct nxpwifi_chan_freq { + u32 channel; + u32 freq; +}; + +struct nxpwifi_ssid_bssid { + struct cfg80211_ssid ssid; + u8 bssid[ETH_ALEN]; +}; + +enum { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, + BAND_GAC = 32, + BAND_AAC = 64, + BAND_GAX = 256, + BAND_AAX = 512, +}; + +#define NXPWIFI_WPA_PASSHPHRASE_LEN 64 +struct wpa_param { + u8 pairwise_cipher_wpa; + u8 pairwise_cipher_wpa2; + u8 group_cipher; + u32 length; + u8 passphrase[NXPWIFI_WPA_PASSHPHRASE_LEN]; +}; + +struct wep_key { + u8 key_index; + u8 is_default; + u16 length; + u8 key[WLAN_KEY_LEN_WEP104]; +}; + +#define KEY_MGMT_ON_HOST 0x03 +#define NXPWIFI_AUTH_MODE_AUTO 0xFF +#define BAND_CONFIG_BG 0x00 +#define BAND_CONFIG_A 0x01 +#define NXPWIFI_SEC_CHAN_BELOW 0x30 +#define NXPWIFI_SEC_CHAN_ABOVE 0x10 +#define NXPWIFI_SUPPORTED_RATES 14 +#define NXPWIFI_SUPPORTED_RATES_EXT 32 +#define NXPWIFI_PRIO_BK 2 +#define NXPWIFI_PRIO_VI 5 +#define NXPWIFI_SUPPORTED_CHANNELS 2 +#define NXPWIFI_OPERATING_CLASSES 16 + +struct nxpwifi_uap_bss_param { + u8 mac_addr[ETH_ALEN]; + u8 channel; + u8 band_cfg; + u16 rts_threshold; + u16 frag_threshold; + u8 retry_limit; + struct nxpwifi_802_11_ssid ssid; + u8 bcast_ssid_ctl; + u8 radio_ctl; + u8 dtim_period; + u16 beacon_period; + u16 auth_mode; + u16 protocol; + u16 key_mgmt; + u16 key_mgmt_operation; + struct wpa_param wpa_cfg; + struct wep_key wep_cfg[NUM_WEP_KEYS]; + struct ieee80211_ht_cap ht_cap; + struct ieee80211_vht_cap vht_cap; + u8 rates[NXPWIFI_SUPPORTED_RATES]; + u32 sta_ao_timer; + u32 ps_sta_ao_timer; + u8 qos_info; + u8 power_constraint; + struct nxpwifi_types_wmm_info wmm_info; +}; + +struct nxpwifi_ds_get_stats { + u32 mcast_tx_frame; + u32 failed; + u32 retry; + u32 multi_retry; + u32 frame_dup; + u32 rts_success; + u32 rts_failure; + u32 ack_failure; + u32 rx_frag; + u32 mcast_rx_frame; + u32 fcs_error; + u32 tx_frame; + u32 wep_icv_error[4]; + u32 bcn_rcv_cnt; + u32 bcn_miss_cnt; +}; + +#define NXPWIFI_MAX_VER_STR_LEN 128 + +struct nxpwifi_ver_ext { + u32 version_str_sel; + char version_str[NXPWIFI_MAX_VER_STR_LEN]; +}; + +struct nxpwifi_bss_info { + u32 bss_mode; + struct cfg80211_ssid ssid; + u32 bss_chan; + u8 country_code[3]; + u32 media_connected; + u32 max_power_level; + u32 min_power_level; + signed int bcn_nf_last; + u32 wep_status; + u32 is_hs_configured; + u32 is_deep_sleep; + u8 bssid[ETH_ALEN]; +}; + +struct nxpwifi_sta_info { + u8 peer_mac[ETH_ALEN]; + struct station_parameters *params; +}; + +#define MAX_NUM_TID 8 + +#define MAX_RX_WINSIZE 64 + +struct nxpwifi_ds_rx_reorder_tbl { + u16 tid; + u8 ta[ETH_ALEN]; + u32 start_win; + u32 win_size; + u32 buffer[MAX_RX_WINSIZE]; +}; + +struct nxpwifi_ds_tx_ba_stream_tbl { + u16 tid; + u8 ra[ETH_ALEN]; + u8 amsdu; +}; + +#define DBG_CMD_NUM 5 +#define NXPWIFI_DBG_SDIO_MP_NUM 10 + +struct nxpwifi_debug_info { + unsigned int debug_mask; + u32 int_counter; + u32 packets_out[MAX_NUM_TID]; + u32 tx_buf_size; + u32 curr_tx_buf_size; + u32 tx_tbl_num; + struct nxpwifi_ds_tx_ba_stream_tbl + tx_tbl[NXPWIFI_MAX_TX_BASTREAM_SUPPORTED]; + u32 rx_tbl_num; + struct nxpwifi_ds_rx_reorder_tbl rx_tbl + [NXPWIFI_MAX_RX_BASTREAM_SUPPORTED]; + u16 ps_mode; + u32 ps_state; + u8 is_deep_sleep; + u8 pm_wakeup_card_req; + u32 pm_wakeup_fw_try; + u8 is_hs_configured; + u8 hs_activated; + u32 num_cmd_host_to_card_failure; + u32 num_cmd_sleep_cfm_host_to_card_failure; + u32 num_tx_host_to_card_failure; + u32 num_event_deauth; + u32 num_event_disassoc; + u32 num_event_link_lost; + u32 num_cmd_deauth; + u32 num_cmd_assoc_success; + u32 num_cmd_assoc_failure; + u32 num_tx_timeout; + u8 is_cmd_timedout; + u16 timeout_cmd_id; + u16 timeout_cmd_act; + u16 last_cmd_id[DBG_CMD_NUM]; + u16 last_cmd_act[DBG_CMD_NUM]; + u16 last_cmd_index; + u16 last_cmd_resp_id[DBG_CMD_NUM]; + u16 last_cmd_resp_index; + u16 last_event[DBG_CMD_NUM]; + u16 last_event_index; + u8 data_sent; + u8 cmd_sent; + u8 cmd_resp_received; + u8 event_received; + u32 last_mp_wr_bitmap[NXPWIFI_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_ports[NXPWIFI_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_len[NXPWIFI_DBG_SDIO_MP_NUM]; + u32 last_mp_curr_wr_port[NXPWIFI_DBG_SDIO_MP_NUM]; + u8 last_sdio_mp_index; +}; + +#define NXPWIFI_KEY_INDEX_UNICAST 0x40000000 +#define PN_LEN 16 + +struct nxpwifi_ds_encrypt_key { + u32 key_disable; + u32 key_index; + u32 key_len; + u8 key_material[WLAN_MAX_KEY_LEN]; + u8 mac_addr[ETH_ALEN]; + u8 pn[PN_LEN]; /* packet number */ + u8 pn_len; + u8 is_igtk_key; + u8 is_current_wep_key; + u8 is_rx_seq_valid; + u8 is_igtk_def_key; +}; + +struct nxpwifi_power_cfg { + u32 is_power_auto; + u32 is_power_fixed; + u32 power_level; +}; + +struct nxpwifi_ds_hs_cfg { + u32 is_invoke_hostcmd; + /* Bit0: non-unicast data + * Bit1: unicast data + * Bit2: mac events + * Bit3: magic packet + */ + u32 conditions; + u32 gpio; + u32 gap; +}; + +struct nxpwifi_ds_wakeup_reason { + u16 hs_wakeup_reason; +}; + +#define DEEP_SLEEP_ON 1 +#define DEEP_SLEEP_OFF 0 +#define DEEP_SLEEP_IDLE_TIME 100 +#define PS_MODE_AUTO 1 + +struct nxpwifi_ds_auto_ds { + u16 auto_ds; + u16 idle_time; +}; + +struct nxpwifi_ds_pm_cfg { + union { + u32 ps_mode; + struct nxpwifi_ds_hs_cfg hs_cfg; + struct nxpwifi_ds_auto_ds auto_deep_sleep; + u32 sleep_period; + } param; +}; + +struct nxpwifi_11ac_vht_cfg { + u8 band_config; + u8 misc_config; + u32 cap_info; + u32 mcs_tx_set; + u32 mcs_rx_set; +}; + +struct nxpwifi_ds_11n_tx_cfg { + u16 tx_htcap; + u16 tx_htinfo; + u16 misc_config; /* Needed for 802.11AC cards only */ +}; + +struct nxpwifi_ds_11n_amsdu_aggr_ctrl { + u16 enable; + u16 curr_buf_size; +}; + +struct nxpwifi_ds_ant_cfg { + u32 tx_ant; + u32 rx_ant; +}; + +#define NXPWIFI_NUM_OF_CMD_BUFFER 50 +#define NXPWIFI_SIZE_OF_CMD_BUFFER 2048 + +enum { + NXPWIFI_IE_TYPE_GEN_IE = 0, + NXPWIFI_IE_TYPE_ARP_FILTER, +}; + +enum { + NXPWIFI_REG_MAC = 1, + NXPWIFI_REG_BBP, + NXPWIFI_REG_RF, + NXPWIFI_REG_PMIC, + NXPWIFI_REG_CAU, +}; + +struct nxpwifi_ds_reg_rw { + u32 type; + u32 offset; + u32 value; +}; + +#define MAX_EEPROM_DATA 256 + +struct nxpwifi_ds_read_eeprom { + u16 offset; + u16 byte_count; + u8 value[MAX_EEPROM_DATA]; +}; + +struct nxpwifi_ds_mem_rw { + u32 addr; + u32 value; +}; + +#define IEEE_MAX_IE_SIZE 256 + +#define NXPWIFI_IE_HDR_SIZE (sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE) + +struct nxpwifi_ds_misc_gen_ie { + u32 type; + u32 len; + u8 ie_data[IEEE_MAX_IE_SIZE]; +}; + +struct nxpwifi_ds_misc_cmd { + u32 len; + u8 cmd[NXPWIFI_SIZE_OF_CMD_BUFFER]; +}; + +#define BITMASK_BCN_RSSI_LOW BIT(0) +#define BITMASK_BCN_RSSI_HIGH BIT(4) + +enum subsc_evt_rssi_state { + EVENT_HANDLED, + RSSI_LOW_RECVD, + RSSI_HIGH_RECVD +}; + +struct subsc_evt_cfg { + u8 abs_value; + u8 evt_freq; +}; + +struct nxpwifi_ds_misc_subsc_evt { + u16 action; + u16 events; + struct subsc_evt_cfg bcn_l_rssi_cfg; + struct subsc_evt_cfg bcn_h_rssi_cfg; +}; + +#define NXPWIFI_MEF_MAX_BYTESEQ 6 /* non-adjustable */ +#define NXPWIFI_MEF_MAX_FILTERS 10 + +struct nxpwifi_mef_filter { + u16 repeat; + u16 offset; + s8 byte_seq[NXPWIFI_MEF_MAX_BYTESEQ + 1]; + u8 filt_type; + u8 filt_action; +}; + +struct nxpwifi_mef_entry { + u8 mode; + u8 action; + struct nxpwifi_mef_filter filter[NXPWIFI_MEF_MAX_FILTERS]; +}; + +struct nxpwifi_ds_mef_cfg { + u32 criteria; + u16 num_entries; + struct nxpwifi_mef_entry *mef_entry; +}; + +#define NXPWIFI_MAX_VSIE_LEN (256) +#define NXPWIFI_MAX_VSIE_NUM (8) +#define NXPWIFI_VSIE_MASK_CLEAR 0x00 +#define NXPWIFI_VSIE_MASK_SCAN 0x01 +#define NXPWIFI_VSIE_MASK_ASSOC 0x02 +#define NXPWIFI_VSIE_MASK_BGSCAN 0x08 + +enum { + NXPWIFI_FUNC_INIT = 1, + NXPWIFI_FUNC_SHUTDOWN, +}; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define NXPWIFI_COALESCE_MAX_RULES 8 +#define NXPWIFI_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define NXPWIFI_COALESCE_MAX_FILTERS 4 +#define NXPWIFI_MAX_COALESCING_DELAY 100 /* in msecs */ + +struct filt_field_param { + u8 operation; + u8 operand_len; + u16 offset; + u8 operand_byte_stream[NXPWIFI_COALESCE_MAX_BYTESEQ]; +}; + +struct nxpwifi_coalesce_rule { + u16 max_coalescing_delay; + u8 num_of_fields; + u8 pkt_type; + struct filt_field_param params[NXPWIFI_COALESCE_MAX_FILTERS]; +}; + +struct nxpwifi_ds_coalesce_cfg { + u16 num_of_rules; + struct nxpwifi_coalesce_rule rule[NXPWIFI_COALESCE_MAX_RULES]; +}; + +struct nxpwifi_11ax_he_cap_cfg { + u16 id; + u16 len; + u8 ext_id; + struct ieee80211_he_cap_elem cap_elem; + u8 he_txrx_mcs_support[4]; + u8 val[28]; +}; + +#define HE_CAP_MAX_SIZE 54 + +struct nxpwifi_11ax_he_cfg { + u8 band; + union { + struct nxpwifi_11ax_he_cap_cfg he_cap_cfg; + u8 data[HE_CAP_MAX_SIZE]; + }; +}; + +#define NXPWIFI_11AXCMD_CFG_ID_SR_OBSS_PD_OFFSET 1 +#define NXPWIFI_11AXCMD_CFG_ID_SR_ENABLE 2 +#define NXPWIFI_11AXCMD_CFG_ID_BEAM_CHANGE 3 +#define NXPWIFI_11AXCMD_CFG_ID_HTC_ENABLE 4 +#define NXPWIFI_11AXCMD_CFG_ID_TXOP_RTS 5 +#define NXPWIFI_11AXCMD_CFG_ID_TX_OMI 6 +#define NXPWIFI_11AXCMD_CFG_ID_OBSSNBRU_TOLTIME 7 +#define NXPWIFI_11AXCMD_CFG_ID_SET_BSRP 8 +#define NXPWIFI_11AXCMD_CFG_ID_LLDE 9 + +#define NXPWIFI_11AXCMD_SR_SUBID 0x102 +#define NXPWIFI_11AXCMD_BEAM_SUBID 0x103 +#define NXPWIFI_11AXCMD_HTC_SUBID 0x104 +#define NXPWIFI_11AXCMD_TXOMI_SUBID 0x105 +#define NXPWIFI_11AXCMD_OBSS_TOLTIME_SUBID 0x106 +#define NXPWIFI_11AXCMD_TXOPRTS_SUBID 0x108 +#define NXPWIFI_11AXCMD_SET_BSRP_SUBID 0x109 +#define NXPWIFI_11AXCMD_LLDE_SUBID 0x110 + +#define NXPWIFI_11AX_TWT_SETUP_SUBID 0x114 +#define NXPWIFI_11AX_TWT_TEARDOWN_SUBID 0x115 +#define NXPWIFI_11AX_TWT_REPORT_SUBID 0x116 + +struct nxpwifi_11axcmdcfg_obss_pd_offset { + /* */ + u8 offset[2]; +}; + +struct nxpwifi_11axcmdcfg_sr_control { + /* 1 enable, 0 disable */ + u8 control; +}; + +struct nxpwifi_11ax_sr_cmd { + /* type */ + u16 type; + /* length of TLV */ + u16 len; + /* value */ + union { + struct nxpwifi_11axcmdcfg_obss_pd_offset obss_pd_offset; + struct nxpwifi_11axcmdcfg_sr_control sr_control; + } param; +}; + +struct nxpwifi_11ax_beam_cmd { + /* command value: 1 is disable, 0 is enable */ + u8 value; +}; + +struct nxpwifi_11ax_htc_cmd { + /* command value: 1 is enable, 0 is disable */ + u8 value; +}; + +struct nxpwifi_11ax_txomi_cmd { + /* 11ax spec 9.2.4.6a.2 OM Control 12 bits. Bit 0 to bit 11 */ + u16 omi; + /* tx option + * 0: send OMI in QoS NULL; 1: send OMI in QoS data; 0xFF: set OMI in + * both + */ + u8 tx_option; + /* if OMI is sent in QoS data, specify the number of consecutive data + * packets containing the OMI + */ + u8 num_data_pkts; +}; + +struct nxpwifi_11ax_toltime_cmd { + /* OBSS Narrow Bandwidth RU Tolerance Time */ + u32 tol_time; +}; + +struct nxpwifi_11ax_txop_cmd { + /* Two byte rts threshold value of which only 10 bits, bit 0 to bit 9 + * are valid + */ + u16 rts_thres; +}; + +struct nxpwifi_11ax_set_bsrp_cmd { + /* command value: 1 is enable, 0 is disable */ + u8 value; +}; + +struct nxpwifi_11ax_llde_cmd { + /* Uplink LLDE: enable=1,disable=0 */ + u8 llde; + /* operation mode: default=0,carplay=1,gameplay=2 */ + u8 mode; + /* trigger frame rate: auto=0xff */ + u8 fixrate; + /* cap airtime limit index: auto=0xff */ + u8 trigger_limit; + /* cap peak UL rate */ + u8 peak_ul_rate; + /* Downlink LLDE: enable=1,disable=0 */ + u8 dl_llde; + /* Set trigger frame interval(us): auto=0 */ + u16 poll_interval; + /* Set TxOp duration */ + u16 tx_op_duration; + /* for other configurations */ + u16 llde_ctrl; + u16 mu_rts_successcnt; + u16 mu_rts_failcnt; + u16 basic_trigger_successcnt; + u16 basic_trigger_failcnt; + u16 tbppdu_nullcnt; + u16 tbppdu_datacnt; +}; + +struct nxpwifi_11ax_cmd_cfg { + u32 sub_command; + u32 sub_id; + union { + struct nxpwifi_11ax_sr_cmd sr_cfg; + struct nxpwifi_11ax_beam_cmd beam_cfg; + struct nxpwifi_11ax_htc_cmd htc_cfg; + struct nxpwifi_11ax_txomi_cmd txomi_cfg; + struct nxpwifi_11ax_toltime_cmd toltime_cfg; + struct nxpwifi_11ax_txop_cmd txop_cfg; + struct nxpwifi_11ax_set_bsrp_cmd setbsrp_cfg; + struct nxpwifi_11ax_llde_cmd llde_cfg; + } param; +}; + +#endif /* !_NXPWIFI_CFG_H_ */ diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c new file mode 100644 index 000000000000..cf7f1f146d97 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c @@ -0,0 +1,1311 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: functions for station ioctl + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" +#include "cfg80211.h" + +static int disconnect_on_suspend; + +/* Copies the multicast address list from device to driver. + * + * This function does not validate the destination memory for + * size, and the calling function must ensure enough memory is + * available. + */ +int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist, + struct net_device *dev) +{ + int i = 0; + struct netdev_hw_addr *ha; + + netdev_for_each_mc_addr(ha, dev) + memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); + + return i; +} + +/* Wait queue completion handler. + * + * This function waits on a cmd wait queue. It also cancels the pending + * request after waking up, in case of errors. + */ +int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_queued) +{ + int status; + + /* Wait for completion */ + status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, + *cmd_queued->condition, + (12 * HZ)); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + nxpwifi_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", + status); + nxpwifi_cancel_all_pending_cmd(adapter); + return status; + } + + status = adapter->cmd_wait_q.status; + adapter->cmd_wait_q.status = 0; + + return status; +} + +/* This function prepares the correct firmware command and + * issues it to set the multicast list. + * + * This function can be used to enable promiscuous mode, or enable all + * multicast packets, or to enable selective multicast. + */ +int +nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv, + struct nxpwifi_multicast_list *mcast_list) +{ + int ret = 0; + u16 old_pkt_filter; + + old_pkt_filter = priv->curr_pkt_filter; + + if (mcast_list->mode == NXPWIFI_PROMISC_MODE) { + nxpwifi_dbg(priv->adapter, INFO, + "info: Enable Promiscuous mode\n"); + priv->curr_pkt_filter |= HOST_ACT_MAC_PROMISCUOUS_ENABLE; + priv->curr_pkt_filter &= + ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + /* Multicast */ + priv->curr_pkt_filter &= ~HOST_ACT_MAC_PROMISCUOUS_ENABLE; + if (mcast_list->mode == NXPWIFI_ALL_MULTI_MODE) { + nxpwifi_dbg(priv->adapter, INFO, + "info: Enabling All Multicast!\n"); + priv->curr_pkt_filter |= + HOST_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + priv->curr_pkt_filter &= + ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE; + nxpwifi_dbg(priv->adapter, INFO, + "info: Set multicast list=%d\n", + mcast_list->num_multicast_addr); + /* Send multicast addresses to firmware */ + ret = nxpwifi_send_cmd(priv, + HOST_CMD_MAC_MULTICAST_ADR, + HOST_ACT_GEN_SET, 0, + mcast_list, false); + } + } + nxpwifi_dbg(priv->adapter, INFO, + "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", + old_pkt_filter, priv->curr_pkt_filter); + if (old_pkt_filter != priv->curr_pkt_filter) { + ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL, + HOST_ACT_GEN_SET, + 0, &priv->curr_pkt_filter, false); + } + + return ret; +} + +/* This function fills bss descriptor structure using provided + * information. + * beacon_ie buffer is allocated in this function. It is caller's + * responsibility to free the memory. + */ +int nxpwifi_fill_new_bss_desc(struct nxpwifi_private *priv, + struct cfg80211_bss *bss, + struct nxpwifi_bssdescriptor *bss_desc) +{ + u8 *beacon_ie; + size_t beacon_ie_len; + struct nxpwifi_bss_priv *bss_priv = (void *)bss->priv; + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + ies = rcu_dereference(bss->ies); + beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); + beacon_ie_len = ies->len; + bss_desc->timestamp = ies->tsf; + rcu_read_unlock(); + + if (!beacon_ie) { + nxpwifi_dbg(priv->adapter, ERROR, + " failed to alloc beacon_ie\n"); + return -ENOMEM; + } + + memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); + bss_desc->rssi = bss->signal; + /* The caller of this function will free beacon_ie */ + bss_desc->beacon_buf = beacon_ie; + bss_desc->beacon_buf_size = beacon_ie_len; + bss_desc->beacon_period = bss->beacon_interval; + bss_desc->cap_info_bitmap = bss->capability; + bss_desc->bss_band = bss_priv->band; + bss_desc->fw_tsf = bss_priv->fw_tsf; + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { + nxpwifi_dbg(priv->adapter, INFO, + "info: InterpretIE: AP WEP enabled\n"); + bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_8021X_WEP; + } else { + bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL; + } + bss_desc->bss_mode = NL80211_IFTYPE_STATION; + + /* Disable 11ac by default. Enable it only where there + * exist VHT_CAP IE in AP beacon + */ + bss_desc->disable_11ac = true; + /* Disable 11ax by default. Enable it only where there + * exist HE_CAP IE in AP beacon + */ + bss_desc->disable_11ax = true; + + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) + bss_desc->sensed_11h = true; + + return nxpwifi_update_bss_desc_with_ie(priv->adapter, bss_desc); +} + +void nxpwifi_dnld_txpwr_table(struct nxpwifi_private *priv) +{ + if (priv->adapter->dt_node) { + char txpwr[] = {"nxp,00_txpwrlimit"}; + + memcpy(&txpwr[8], priv->adapter->country_code, 2); + nxpwifi_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); + } +} + +static int nxpwifi_process_country_ie(struct nxpwifi_private *priv, + struct cfg80211_bss *bss) +{ + const u8 *country_ie; + u8 country_ie_len; + struct nxpwifi_802_11d_domain_reg *domain_info = + &priv->adapter->domain_reg; + int ret; + + rcu_read_lock(); + country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + rcu_read_unlock(); + return 0; + } + + country_ie_len = country_ie[1]; + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { + rcu_read_unlock(); + return 0; + } + + if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { + rcu_read_unlock(); + nxpwifi_dbg(priv->adapter, INFO, + "11D: skip setting domain info in FW\n"); + return 0; + } + + if (country_ie_len > + (IEEE80211_COUNTRY_STRING_LEN + NXPWIFI_MAX_TRIPLET_802_11D)) { + rcu_read_unlock(); + nxpwifi_dbg(priv->adapter, ERROR, + "11D: country_ie_len overflow!, deauth AP\n"); + return -EINVAL; + } + + memcpy(priv->adapter->country_code, &country_ie[2], 2); + + domain_info->country_code[0] = country_ie[2]; + domain_info->country_code[1] = country_ie[3]; + domain_info->country_code[2] = ' '; + + country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; + + domain_info->no_of_triplet = + country_ie_len / sizeof(struct ieee80211_country_ie_triplet); + + memcpy((u8 *)domain_info->triplet, + &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); + + rcu_read_unlock(); + + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11D_DOMAIN_INFO, + HOST_ACT_GEN_SET, 0, NULL, false); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "11D: setting domain info in FW fail\n"); + else + nxpwifi_dnld_txpwr_table(priv); + + return ret; +} + +/* In infra mode, an deauthentication is performed + * first. + */ +int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss, + struct cfg80211_ssid *req_ssid) +{ + int ret; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_bssdescriptor *bss_desc = NULL; + u16 config_bands; + + priv->scan_block = false; + + if (adapter->region_code == 0x00 && + nxpwifi_process_country_ie(priv, bss)) + return -EINVAL; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + if (nxpwifi_band_to_radio_type(bss_desc->bss_band) == + HOST_SCAN_RADIO_TYPE_BG) { + config_bands = BAND_B | BAND_G | BAND_GN; + } else { + config_bands = BAND_A | BAND_AN; + if (adapter->fw_bands & BAND_AAC) + config_bands |= BAND_AAC; + if (adapter->fw_bands & BAND_AAX) + config_bands |= BAND_AAX; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) + priv->config_bands = config_bands; + + ret = nxpwifi_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + if (nxpwifi_11h_get_csa_closed_channel(priv) == (u8)bss_desc->channel) { + nxpwifi_dbg(adapter, ERROR, + "Attempt to reconnect on csa closed chan(%d)\n", + bss_desc->channel); + ret = -EINVAL; + goto done; + } + + nxpwifi_stop_net_dev_queue(priv->netdev, adapter); + netif_carrier_off(priv->netdev); + + /* Clear any past association response stored for + * application retrieval + */ + priv->assoc_rsp_size = 0; + ret = nxpwifi_associate(priv, bss_desc); + + /* If auth type is auto and association fails using open mode, + * try to connect using shared mode + */ + if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + priv->sec_info.is_authtype_auto && + priv->sec_info.wep_enabled) { + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_SHARED_KEY; + ret = nxpwifi_associate(priv, bss_desc); + } + +done: + /* beacon_ie buffer was allocated in function + * nxpwifi_fill_new_bss_desc(). Free it now. + */ + if (bss_desc) + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + + if (ret < 0) + priv->attempted_bss_desc = NULL; + + return ret; +} + +/* IOCTL request handler to set host sleep configuration. + * + * This function prepares the correct firmware command and + * issues it. + */ +int nxpwifi_set_hs_params(struct nxpwifi_private *priv, u16 action, + int cmd_type, struct nxpwifi_ds_hs_cfg *hs_cfg) + +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int status = 0; + u32 prev_cond = 0; + + if (!hs_cfg) + return -ENOMEM; + + switch (action) { + case HOST_ACT_GEN_SET: + if (adapter->pps_uapsd_mode) { + nxpwifi_dbg(adapter, INFO, + "info: Host Sleep IOCTL\t" + "is blocked in UAPSD/PPS mode\n"); + status = -EPERM; + break; + } + if (hs_cfg->is_invoke_hostcmd) { + if (hs_cfg->conditions == HS_CFG_CANCEL) { + if (!test_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags)) + /* Already cancelled */ + break; + /* Save previous condition */ + prev_cond = le32_to_cpu(adapter->hs_cfg + .conditions); + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + } else if (hs_cfg->conditions) { + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; + if (hs_cfg->gap) + adapter->hs_cfg.gap = (u8)hs_cfg->gap; + } else if (adapter->hs_cfg.conditions == + cpu_to_le32(HS_CFG_CANCEL)) { + /* Return failure if no parameters for HS + * enable + */ + status = -EINVAL; + break; + } + + status = nxpwifi_send_cmd(priv, + HOST_CMD_802_11_HS_CFG_ENH, + HOST_ACT_GEN_SET, 0, + &adapter->hs_cfg, + cmd_type == NXPWIFI_SYNC_CMD); + + if (hs_cfg->conditions == HS_CFG_CANCEL) + /* Restore previous condition */ + adapter->hs_cfg.conditions = + cpu_to_le32(prev_cond); + } else { + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; + adapter->hs_cfg.gap = (u8)hs_cfg->gap; + } + break; + case HOST_ACT_GEN_GET: + hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); + hs_cfg->gpio = adapter->hs_cfg.gpio; + hs_cfg->gap = adapter->hs_cfg.gap; + break; + default: + status = -EINVAL; + break; + } + + return status; +} + +/* Sends IOCTL request to cancel the existing Host Sleep configuration. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type) +{ + struct nxpwifi_ds_hs_cfg hscfg; + + hscfg.conditions = HS_CFG_CANCEL; + hscfg.is_invoke_hostcmd = true; + + return nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET, + cmd_type, &hscfg); +} +EXPORT_SYMBOL_GPL(nxpwifi_cancel_hs); + +/* Sends IOCTL request to cancel the existing Host Sleep configuration. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +bool nxpwifi_enable_hs(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_ds_hs_cfg hscfg; + struct nxpwifi_private *priv; + int i; + + if (disconnect_on_suspend) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + nxpwifi_deauthenticate(priv, NULL); + } + } + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + + if (priv && priv->sched_scanning) { +#ifdef CONFIG_PM + if (priv->wdev.wiphy->wowlan_config && + !priv->wdev.wiphy->wowlan_config->nd_config) { +#endif + nxpwifi_dbg(adapter, CMD, "aborting bgscan!\n"); + nxpwifi_stop_bg_scan(priv); + cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0); +#ifdef CONFIG_PM + } +#endif + } + + if (adapter->hs_activated) { + nxpwifi_dbg(adapter, CMD, + "cmd: HS Already activated\n"); + return true; + } + + adapter->hs_activate_wait_q_woken = false; + + memset(&hscfg, 0, sizeof(hscfg)); + hscfg.is_invoke_hostcmd = true; + + set_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags); + nxpwifi_cancel_all_pending_cmd(adapter); + + if (nxpwifi_set_hs_params(nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_STA), + HOST_ACT_GEN_SET, NXPWIFI_SYNC_CMD, + &hscfg)) { + nxpwifi_dbg(adapter, ERROR, + "IOCTL request HS enable failed\n"); + return false; + } + + if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken, + (10 * HZ)) <= 0) { + nxpwifi_dbg(adapter, ERROR, + "hs_activate_wait_q terminated\n"); + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(nxpwifi_enable_hs); + +/* IOCTL request handler to get BSS information. + * + * This function collates the information from different driver structures + * to send to the user. + */ +int nxpwifi_get_bss_info(struct nxpwifi_private *priv, + struct nxpwifi_bss_info *info) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_bssdescriptor *bss_desc; + + if (!info) + return -EINVAL; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + info->bss_mode = priv->bss_mode; + + memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid)); + + memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); + + info->bss_chan = bss_desc->channel; + + memcpy(info->country_code, adapter->country_code, + IEEE80211_COUNTRY_STRING_LEN); + + info->media_connected = priv->media_connected; + + info->max_power_level = priv->max_tx_power_level; + info->min_power_level = priv->min_tx_power_level; + + info->bcn_nf_last = priv->bcn_nf_last; + + if (priv->sec_info.wep_enabled) + info->wep_status = true; + else + info->wep_status = false; + + info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + info->is_deep_sleep = adapter->is_deep_sleep; + + return 0; +} + +/* The function disables auto deep sleep mode. + */ +int nxpwifi_disable_auto_ds(struct nxpwifi_private *priv) +{ + struct nxpwifi_ds_auto_ds auto_ds = { + .auto_ds = DEEP_SLEEP_OFF, + }; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, + DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); +} +EXPORT_SYMBOL_GPL(nxpwifi_disable_auto_ds); + +/* Sends IOCTL request to get the data rate. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate) +{ + int ret; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY, + HOST_ACT_GEN_GET, 0, NULL, true); + + if (!ret) { + if (priv->is_data_rate_auto) + *rate = nxpwifi_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + else + *rate = priv->data_rate; + } + + return ret; +} + +/* IOCTL request handler to set tx power configuration. + * + * This function prepares the correct firmware command and + * issues it. + * + * For non-auto power mode, all the following power groups are set - + * - Modulation class HR/DSSS + * - Modulation class OFDM + * - Modulation class HTBW20 + * - Modulation class HTBW40 + */ +int nxpwifi_set_tx_power(struct nxpwifi_private *priv, + struct nxpwifi_power_cfg *power_cfg) +{ + int ret; + struct host_cmd_ds_txpwr_cfg *txp_cfg; + struct nxpwifi_types_power_group *pg_tlv; + struct nxpwifi_power_group *pg; + u8 *buf; + u16 dbm = 0; + + if (!power_cfg->is_power_auto) { + dbm = (u16)power_cfg->power_level; + if (dbm < priv->min_tx_power_level || + dbm > priv->max_tx_power_level) { + nxpwifi_dbg(priv->adapter, ERROR, + "txpower value %d dBm\t" + "is out of range (%d dBm-%d dBm)\n", + dbm, priv->min_tx_power_level, + priv->max_tx_power_level); + return -EINVAL; + } + } + buf = kzalloc(NXPWIFI_SIZE_OF_CMD_BUFFER, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + txp_cfg = (struct host_cmd_ds_txpwr_cfg *)buf; + txp_cfg->action = cpu_to_le16(HOST_ACT_GEN_SET); + if (!power_cfg->is_power_auto) { + u16 dbm_min = power_cfg->is_power_fixed ? + dbm : priv->min_tx_power_level; + + txp_cfg->mode = cpu_to_le32(1); + pg_tlv = (struct nxpwifi_types_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); + pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP); + pg_tlv->length = + cpu_to_le16(4 * sizeof(struct nxpwifi_power_group)); + pg = (struct nxpwifi_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg) + + sizeof(struct nxpwifi_types_power_group)); + /* Power group for modulation class HR/DSSS */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x03; + pg->modulation_class = MOD_CLASS_HR_DSSS; + pg->power_step = 0; + pg->power_min = (s8)dbm_min; + pg->power_max = (s8)dbm; + pg++; + /* Power group for modulation class OFDM */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x07; + pg->modulation_class = MOD_CLASS_OFDM; + pg->power_step = 0; + pg->power_min = (s8)dbm_min; + pg->power_max = (s8)dbm; + pg++; + /* Power group for modulation class HTBW20 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (s8)dbm_min; + pg->power_max = (s8)dbm; + pg->ht_bandwidth = HT_BW_20; + pg++; + /* Power group for modulation class HTBW40 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (s8)dbm_min; + pg->power_max = (s8)dbm; + pg->ht_bandwidth = HT_BW_40; + } + ret = nxpwifi_send_cmd(priv, HOST_CMD_TXPWR_CFG, + HOST_ACT_GEN_SET, 0, buf, true); + + kfree(buf); + return ret; +} + +/* IOCTL request handler to get power save mode. + * + * This function prepares the correct firmware command and + * issues it. + */ +int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode) +{ + int ret; + struct nxpwifi_adapter *adapter = priv->adapter; + u16 sub_cmd; + + if (*ps_mode) + adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP; + else + adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM; + sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, + sub_cmd, BITMAP_STA_PS, NULL, true); + if (!ret && sub_cmd == DIS_AUTO_PS) + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, + GET_PS, 0, NULL, false); + + return ret; +} + +/* IOCTL request handler to set/reset WPA IE. + * + * The supplied WPA IE is treated as a opaque buffer. Only the first field + * is checked to determine WPA version. If buffer length is zero, the existing + * WPA IE is reset. + */ +static int nxpwifi_set_wpa_ie(struct nxpwifi_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > sizeof(priv->wpa_ie)) { + nxpwifi_dbg(priv->adapter, ERROR, + "failed to copy WPA IE, too big\n"); + return -EINVAL; + } + memcpy(priv->wpa_ie, ie_data_ptr, ie_len); + priv->wpa_ie_len = ie_len; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: Set Wpa_ie_len=%d IE=%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + + if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { + priv->sec_info.wpa_enabled = true; + } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { + priv->sec_info.wpa2_enabled = true; + } else { + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + } + } else { + memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + priv->wpa_ie_len = 0; + nxpwifi_dbg(priv->adapter, INFO, + "info: reset wpa_ie_len=%d IE=%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + } + + return 0; +} + +/* IOCTL request handler to set/reset WPS IE. + * + * The supplied WPS IE is treated as a opaque buffer. Only the first field + * is checked to internally enable WPS. If buffer length is zero, the existing + * WPS IE is reset. + */ +static int nxpwifi_set_wps_ie(struct nxpwifi_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > NXPWIFI_MAX_VSIE_LEN) { + nxpwifi_dbg(priv->adapter, ERROR, + "info: failed to copy WPS IE, too big\n"); + return -EINVAL; + } + + priv->wps_ie = kzalloc(NXPWIFI_MAX_VSIE_LEN, GFP_KERNEL); + if (!priv->wps_ie) + return -ENOMEM; + + memcpy(priv->wps_ie, ie_data_ptr, ie_len); + priv->wps_ie_len = ie_len; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: Set wps_ie_len=%d IE=%#x\n", + priv->wps_ie_len, priv->wps_ie[0]); + } else { + kfree(priv->wps_ie); + priv->wps_ie_len = ie_len; + nxpwifi_dbg(priv->adapter, INFO, + "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); + } + return 0; +} + +/* IOCTL request handler to set WEP network key. + * + * This function prepares the correct firmware command and + * issues it, after validation checks. + */ +static int +nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private *priv, + struct nxpwifi_ds_encrypt_key *encrypt_key) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + struct nxpwifi_wep_key *wep_key; + int index; + + if (priv->wep_key_curr_index >= NUM_WEP_KEYS) + priv->wep_key_curr_index = 0; + wep_key = &priv->wep_key[priv->wep_key_curr_index]; + index = encrypt_key->key_index; + if (encrypt_key->key_disable) { + priv->sec_info.wep_enabled = 0; + } else if (!encrypt_key->key_len) { + /* Copy the required key as the current key */ + wep_key = &priv->wep_key[index]; + if (!wep_key->key_length) { + nxpwifi_dbg(adapter, ERROR, + "key not set, so cannot enable it\n"); + return -EINVAL; + } + + memcpy(encrypt_key->key_material, + wep_key->key_material, wep_key->key_length); + encrypt_key->key_len = wep_key->key_length; + + priv->wep_key_curr_index = (u16)index; + priv->sec_info.wep_enabled = 1; + } else { + wep_key = &priv->wep_key[index]; + memset(wep_key, 0, sizeof(struct nxpwifi_wep_key)); + /* Copy the key in the driver */ + memcpy(wep_key->key_material, + encrypt_key->key_material, + encrypt_key->key_len); + wep_key->key_index = index; + wep_key->key_length = encrypt_key->key_len; + priv->sec_info.wep_enabled = 1; + } + if (wep_key->key_length) { + void *enc_key; + + if (encrypt_key->key_disable) { + memset(&priv->wep_key[index], 0, + sizeof(struct nxpwifi_wep_key)); + goto done; + } + + enc_key = encrypt_key; + + /* Send request to firmware */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL, + HOST_ACT_GEN_SET, 0, enc_key, false); + if (ret) + return ret; + } + +done: + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HOST_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HOST_ACT_MAC_WEP_ENABLE; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL, + HOST_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + + return ret; +} + +/* IOCTL request handler to set WPA key. + * + * This function prepares the correct firmware command and + * issues it, after validation checks. + * + * Current driver only supports key length of up to 32 bytes. + * + * This function can also be used to disable a currently set key. + */ +static int +nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private *priv, + struct nxpwifi_ds_encrypt_key *encrypt_key) +{ + int ret; + u8 remove_key = false; + + /* Current driver only supports key length of up to 32 bytes */ + if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { + nxpwifi_dbg(priv->adapter, ERROR, + "key length too long\n"); + return -EINVAL; + } + + if (!encrypt_key->key_index) + encrypt_key->key_index = NXPWIFI_KEY_INDEX_UNICAST; + + if (remove_key) + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL, + HOST_ACT_GEN_SET, + !KEY_INFO_ENABLED, encrypt_key, true); + else + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL, + HOST_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, true); + + return ret; +} + +/* IOCTL request handler to set/get network keys. + * + * This is a generic key handling function which supports WEP and WPA. + */ +static int +nxpwifi_sec_ioctl_encrypt_key(struct nxpwifi_private *priv, + struct nxpwifi_ds_encrypt_key *encrypt_key) +{ + int status; + + if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) + status = nxpwifi_sec_ioctl_set_wpa_key(priv, encrypt_key); + else + status = nxpwifi_sec_ioctl_set_wep_key(priv, encrypt_key); + + return status; +} + +/* This function returns the driver version. + */ +int +nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter, char *version, + int max_len) +{ + union { + __le32 l; + u8 c[4]; + } ver; + char fw_ver[32]; + + ver.l = cpu_to_le32(adapter->fw_release_number); + sprintf(fw_ver, "%u.%u.%u.p%u.%u", ver.c[2], ver.c[1], + ver.c[0], ver.c[3], adapter->fw_hotfix_ver); + + snprintf(version, max_len, driver_version, fw_ver); + + nxpwifi_dbg(adapter, MSG, "info: NXPWIFI VERSION: %s\n", version); + + return 0; +} + +/* Sends IOCTL request to set encoding parameters. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int nxpwifi_set_encode(struct nxpwifi_private *priv, struct key_params *kp, + const u8 *key, int key_len, u8 key_index, + const u8 *mac_addr, int disable) +{ + struct nxpwifi_ds_encrypt_key encrypt_key; + + memset(&encrypt_key, 0, sizeof(encrypt_key)); + encrypt_key.key_len = key_len; + encrypt_key.key_index = key_index; + + if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) + encrypt_key.is_igtk_key = true; + + if (!disable) { + if (key_len) + memcpy(encrypt_key.key_material, key, key_len); + else + encrypt_key.is_current_wep_key = true; + + if (mac_addr) + memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); + if (kp && kp->seq && kp->seq_len) { + memcpy(encrypt_key.pn, kp->seq, kp->seq_len); + encrypt_key.pn_len = kp->seq_len; + encrypt_key.is_rx_seq_valid = true; + } + } else { + encrypt_key.key_disable = true; + if (mac_addr) + memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); + } + + return nxpwifi_sec_ioctl_encrypt_key(priv, &encrypt_key); +} + +/* Sends IOCTL request to get extended version. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +nxpwifi_get_ver_ext(struct nxpwifi_private *priv, u32 version_str_sel) +{ + struct nxpwifi_ver_ext ver_ext; + + memset(&ver_ext, 0, sizeof(ver_ext)); + ver_ext.version_str_sel = version_str_sel; + + return nxpwifi_send_cmd(priv, HOST_CMD_VERSION_EXT, + HOST_ACT_GEN_GET, 0, &ver_ext, true); +} + +int +nxpwifi_remain_on_chan_cfg(struct nxpwifi_private *priv, u16 action, + struct ieee80211_channel *chan, + unsigned int duration) +{ + struct host_cmd_ds_remain_on_chan roc_cfg; + u8 sc; + int ret; + + memset(&roc_cfg, 0, sizeof(roc_cfg)); + roc_cfg.action = cpu_to_le16(action); + if (action == HOST_ACT_GEN_SET) { + roc_cfg.band_cfg = chan->band; + sc = nxpwifi_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); + roc_cfg.band_cfg |= (sc << 2); + + roc_cfg.channel = + ieee80211_frequency_to_channel(chan->center_freq); + roc_cfg.duration = cpu_to_le32(duration); + } + ret = nxpwifi_send_cmd(priv, HOST_CMD_REMAIN_ON_CHAN, + action, 0, &roc_cfg, true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "failed to remain on channel\n"); + return ret; + } + + return roc_cfg.status; +} + +/* Sends IOCTL request to get statistics information. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +nxpwifi_get_stats_info(struct nxpwifi_private *priv, + struct nxpwifi_ds_get_stats *log) +{ + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_GET_LOG, + HOST_ACT_GEN_GET, 0, log, true); +} + +/* IOCTL request handler to read/write register. + * + * This function prepares the correct firmware command and + * issues it. + * + * Access to the following registers are supported - + * - MAC + * - BBP + * - RF + * - PMIC + * - CAU + */ +static int nxpwifi_reg_mem_ioctl_reg_rw(struct nxpwifi_private *priv, + struct nxpwifi_ds_reg_rw *reg_rw, + u16 action) +{ + u16 cmd_no; + + switch (reg_rw->type) { + case NXPWIFI_REG_MAC: + cmd_no = HOST_CMD_MAC_REG_ACCESS; + break; + case NXPWIFI_REG_BBP: + cmd_no = HOST_CMD_BBP_REG_ACCESS; + break; + case NXPWIFI_REG_RF: + cmd_no = HOST_CMD_RF_REG_ACCESS; + break; + case NXPWIFI_REG_PMIC: + cmd_no = HOST_CMD_PMIC_REG_ACCESS; + break; + case NXPWIFI_REG_CAU: + cmd_no = HOST_CMD_CAU_REG_ACCESS; + break; + default: + return -EINVAL; + } + + return nxpwifi_send_cmd(priv, cmd_no, action, 0, reg_rw, true); +} + +/* Sends IOCTL request to write to a register. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +nxpwifi_reg_write(struct nxpwifi_private *priv, u32 reg_type, + u32 reg_offset, u32 reg_value) +{ + struct nxpwifi_ds_reg_rw reg_rw; + + reg_rw.type = reg_type; + reg_rw.offset = reg_offset; + reg_rw.value = reg_value; + + return nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_SET); +} + +/* Sends IOCTL request to read from a register. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +nxpwifi_reg_read(struct nxpwifi_private *priv, u32 reg_type, + u32 reg_offset, u32 *value) +{ + int ret; + struct nxpwifi_ds_reg_rw reg_rw; + + reg_rw.type = reg_type; + reg_rw.offset = reg_offset; + ret = nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_GET); + + if (!ret) + *value = reg_rw.value; + + return ret; +} + +/* Sends IOCTL request to read from EEPROM. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +nxpwifi_eeprom_read(struct nxpwifi_private *priv, u16 offset, u16 bytes, + u8 *value) +{ + int ret; + struct nxpwifi_ds_read_eeprom rd_eeprom; + + rd_eeprom.offset = offset; + rd_eeprom.byte_count = bytes; + + /* Send request to firmware */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_EEPROM_ACCESS, + HOST_ACT_GEN_GET, 0, &rd_eeprom, true); + + if (!ret) + memcpy(value, rd_eeprom.value, + min((u16)MAX_EEPROM_DATA, rd_eeprom.byte_count)); + return ret; +} + +/* This function sets a generic IE. In addition to generic IE, it can + * also handle WPA and WPA2 IEs. + */ +static int +nxpwifi_set_gen_ie_helper(struct nxpwifi_private *priv, u8 *ie_data_ptr, + u16 ie_len) +{ + struct ieee80211_vendor_ie *pvendor_ie; + static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; + static const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + u16 unparsed_len = ie_len, cur_ie_len; + + /* If the passed length is zero, reset the buffer */ + if (!ie_len) { + priv->gen_ie_buf_len = 0; + priv->wps.session_enable = false; + return 0; + } else if (!ie_data_ptr || + ie_len <= sizeof(struct element)) { + return -EINVAL; + } + pvendor_ie = (struct ieee80211_vendor_ie *)ie_data_ptr; + + while (pvendor_ie) { + cur_ie_len = pvendor_ie->len + sizeof(struct element); + + if (pvendor_ie->element_id == WLAN_EID_RSN) { + /* IE is a WPA/WPA2 IE so call set_wpa function */ + nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len); + priv->wps.session_enable = false; + goto next_ie; + } + + if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) { + /* Test to see if it is a WPA IE, if not, then + * it is a gen IE + */ + if (!memcmp(&pvendor_ie->oui, wpa_oui, + sizeof(wpa_oui))) { + /* IE is a WPA/WPA2 IE so call set_wpa function + */ + nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, + cur_ie_len); + priv->wps.session_enable = false; + goto next_ie; + } + + if (!memcmp(&pvendor_ie->oui, wps_oui, + sizeof(wps_oui))) { + /* Test to see if it is a WPS IE, + * if so, enable wps session flag + */ + priv->wps.session_enable = true; + nxpwifi_dbg(priv->adapter, MSG, + "WPS Session Enabled.\n"); + nxpwifi_set_wps_ie(priv, (u8 *)pvendor_ie, + cur_ie_len); + goto next_ie; + } + } + + /* Verify that the passed length is not larger than the + * available space remaining in the buffer + */ + if (cur_ie_len < + (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { + /* Append the passed data to the end + * of the genIeBuffer + */ + memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, + (u8 *)pvendor_ie, cur_ie_len); + /* Increment the stored buffer length by the + * size passed + */ + priv->gen_ie_buf_len += cur_ie_len; + } + +next_ie: + unparsed_len -= cur_ie_len; + + if (unparsed_len <= sizeof(struct element)) + pvendor_ie = NULL; + else + pvendor_ie = (struct ieee80211_vendor_ie *) + (((u8 *)pvendor_ie) + cur_ie_len); + } + + return 0; +} + +/* IOCTL request handler to set/get generic IE. + * + * In addition to various generic IEs, this function can also be + * used to set the ARP filter. + */ +static int nxpwifi_misc_ioctl_gen_ie(struct nxpwifi_private *priv, + struct nxpwifi_ds_misc_gen_ie *gen_ie, + u16 action) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + switch (gen_ie->type) { + case NXPWIFI_IE_TYPE_GEN_IE: + if (action == HOST_ACT_GEN_GET) { + gen_ie->len = priv->wpa_ie_len; + memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); + } else { + nxpwifi_set_gen_ie_helper(priv, gen_ie->ie_data, + (u16)gen_ie->len); + } + break; + case NXPWIFI_IE_TYPE_ARP_FILTER: + memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { + adapter->arp_filter_size = 0; + nxpwifi_dbg(adapter, ERROR, + "invalid ARP filter size\n"); + return -EINVAL; + } + memcpy(adapter->arp_filter, gen_ie->ie_data, gen_ie->len); + adapter->arp_filter_size = gen_ie->len; + break; + default: + nxpwifi_dbg(adapter, ERROR, "invalid IE type\n"); + return -EINVAL; + } + return 0; +} + +/* Sends IOCTL request to set a generic IE. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +nxpwifi_set_gen_ie(struct nxpwifi_private *priv, const u8 *ie, int ie_len) +{ + struct nxpwifi_ds_misc_gen_ie gen_ie; + + if (ie_len > IEEE_MAX_IE_SIZE) + return -EFAULT; + + gen_ie.type = NXPWIFI_IE_TYPE_GEN_IE; + gen_ie.len = ie_len; + memcpy(gen_ie.ie_data, ie, ie_len); + + return nxpwifi_misc_ioctl_gen_ie(priv, &gen_ie, HOST_ACT_GEN_SET); +} + +/* This function get Host Sleep wake up reason. + */ +int nxpwifi_get_wakeup_reason(struct nxpwifi_private *priv, u16 action, + int cmd_type, + struct nxpwifi_ds_wakeup_reason *wakeup_reason) +{ + return nxpwifi_send_cmd(priv, HOST_CMD_HS_WAKEUP_REASON, + HOST_ACT_GEN_GET, 0, wakeup_reason, + cmd_type == NXPWIFI_SYNC_CMD); +} + +int nxpwifi_get_chan_info(struct nxpwifi_private *priv, + struct nxpwifi_channel_band *channel_band) +{ + return nxpwifi_send_cmd(priv, HOST_CMD_STA_CONFIGURE, + HOST_ACT_GEN_GET, 0, channel_band, + NXPWIFI_SYNC_CMD); +} From patchwork Mon Sep 30 06:36:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815426 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 48C9617B500; Mon, 30 Sep 2024 06:38:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678297; cv=fail; b=d/U/W4oBSIa4Mx8DcLBAv0htFkeScrWJ8CnkkgFkvKlEaKqdC0qBR+i7rx/xFzkCEAKvuXLGGkmP1YUnPggMiDIM+G7xdeVXxF5r4qKcqsUMUiW4VzjNLLPsO32AXiZRF32sxJl6b5xqiiUPloVHUkqQk2lZ0WsbZ+hFCvvJ8d4= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678297; c=relaxed/simple; bh=XrFVAuxBFHYmT8HstK2gseiVf/9Mw6wfPwvY7hJdyR8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=bkmE4rcuLvD2xzjjVe3U6xWkmDdDpYLj68MV4zkslrYfRTIX4GtdU8ak3QJs55jsIYSTdxL4oSR7/uRjtDCLAiS6jGUK2xVSxSnLHiKBEjEiXbUJcLpvS9mbvk8r2v66urITnIyQuW/siBLzUv2x03x1eK/AtNzjA5mLF0HvtpE= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=LsYR4XWd; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="LsYR4XWd" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=qbyuB7Prx6qkYEyMjn4JmRhv0DhIbDlLKJY4exsfbms19+28QcHqQu1aaYY9v12UHVQ9hY//T/l4nmSZQexF0gafaebG+Np9nZj/HJjgxHgqqSmgo8Xp2EPtW/AqpepZ2ydd9UiGyxAghtTtaAaam9jyX2PEfcI9CcWg7YZatmADp30eXuixz2wUtKnmeJz6bgeccGNvpk2KouZ8PjDv8y7Vj8h4vxeBhUDlcDXmopz4xfd4Bj4Yo9Xnp6/1Ww2+4vfAJVQqsqOAwpmrD1E8jmwL+bnqbA+BfIuCHLK5U+1pyP/u1ZiwDRaZGiotWIKYR4leMPGxHmqP8N4kRP/W0w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=wBC8wXSaHOip9X/Bk/1I2AupG9W9hNNpvF4OZQA+c7g=; b=y0kpGE3GxhtFpX4PI/wVMPTjd3jbgxc8mMpNTUjFchYDFUBCgN1x47QbedRqAbu1aspa8P3QLVAw6OYQGhfQCRr9rKiM99sA4VjKO8ojHB9nc1nOH+EplzDlVKOxucP2tpVT+wqhXYBFGtp4I762iFTQpD+GgHohdl4epgzNaFH/sRXd5RrcXJxWakD6jmcuZfVevYtRT2056GejcDSKSXM1nn6WvI2tlH2hIXEU5JgG5eN53sPb7Wau7u7vO+9dOytdc50dd4JacYxJTA4PREKtO3Lwae9Gw1qvrGb/kZaKFYe0QQFcuj+pD1Zzrx8nFoPpQfps/Qn44B5UTluRPQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=wBC8wXSaHOip9X/Bk/1I2AupG9W9hNNpvF4OZQA+c7g=; b=LsYR4XWdRowU5HgmGOlX28VGemqO/TzloLfH6PE2fttB1CNSC80b8LhDR6lbn5zRzNxdrsBQeVfaTs25jWBH8uoXhVjVUILW4g8BumMddUpbS6/0B3s5jJFVV4b7Gla+uHThIw6SphOLX+TOXRaK2KIgFHAuNLpkKbp6RywXQxxVFYUGcV8EJA0HqCn7A4PzZnFWaOygN9iQvRU/KuvsMCYI1Rf8qqaic5or/cKX7Ae2FRYfqm5ppuIrZwOxl8MdRt2Rp7wmnly4OcOmLhWXdTVY2VQkhXnGsvB6oa9uoW6HxBX4tIPHYoa73I3H/ZtA05BUx+0H5coNBNfWvIK/lA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:37:58 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:37:58 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 10/22] wifi: nxpwifi: implement cfg80211 ops Date: Mon, 30 Sep 2024 14:36:49 +0800 Message-Id: <20240930063701.2566520-11-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 0d9787e1-5d59-4372-7659-08dce11a6c2d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|4022899009|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: RM6OTbnkFK05ZYNH+s+g1nVRvBbDz6fuhlJ10Ly4Xlows2ixFP+TJ/+NjzAMgsMJHoqBT5Y2T396BwNiXOGqzu9isXjvqnXJdKy9Lf0JK263X6Ntg4aS50rVeahFD9ERIc/AC0R1qzeV/kftWR5AyWuYJJVlOjGiZ94Evl48b6xTnmrr5ya3qSOjLti6uoQ1OdAtx+2DNf6Acoe2iLK7gs+CocWWhzOS135XGHsUkugG5jKSA8PpXp3GiEB+NA57J6VUExS6kB2I7vCgCY5ZAoMXqDpCqqDTCfX2yVAM/3mIKTQZmV9d9PZCCDuJTOoiHNxt0EebILAg/1f2Ds2kKqmalxip3ZYdJ34D5ZFk33kHa7Zt8bbaV7RjuaLzdSdR2nry9T42AL9ZKEvY2Av2i2Kh603e8NtJu/00NXS4lpkS68/zCRbUj8BibR5y7MeHi2/nOx6vNr848W4XNZE1ZkxLgoXJ82q3nPEtRHRs8FfqUzQX+gvHVgHCnwy5BOPA+rJ0awa7Iw9l/opr5MX/CK59mQ02VPByQYe20pyj1rlPXERaAPSrsgkcvfgbORTqCwWug7pF99YqCc/NxyWMoe/GYJTFB8zxnQj10rXgVxDiilrucgKd7eWn82Raf67iOlaCJdq+ytfZPs7cf7PeIsJEytuRs4hBfr2BXzyMZMpy14u0Db271LLAomlvvty1NY9zauOSuCgDEAzlzoJG2k5uBwPuhNdQ68HeKQa8mB5pW9WlvwsoKbRZSP14YGhU1IwVRz8K1CZyTyEwIL5eaISryejMdR6HSLDrdZtPAGT2wbVS7rWa8kElHg06L4ebyZD4gKraONI6/hD/rtbe5joYOd44J9fETQnyQWIdJLdVSYNo4mJLoANXq/pzWUi8U1eUGQwYs6XplRkujFvMsWPmuIv67sHNlfNYgDzmeQBqnxzoBvamSfVQvifSEIq3vpcG1O4Izaz9XwVu1uHtEm5z85Mn4LpxXp40UoaCq0KsezOh1LBJrBJ7pQ0HNBM412LzZFyZgdpTl6ho4tu7pjOIqTa/6DQ7jXjwfJWTWcyguhhGQH9/tw1C3wbUwUZF3R0nihAcjkorDMzX1RQD/M4+2geT/Xn0Vp0PrYR4Ib2oLKpNK47qiLpWWXNqauWXRmDBKmg8hc/+QAdPPhAc2eO9wm0ByKkTkDrN1bI29pHaGqQ5xzSTn3/J+ADS1L/WvL27Cxt5FhWW56xtYlut8MlXGbWGsMvQVzi0b0CwdnlbqOqy1K0bpGDn2nbZK0VetwmjoaosU93COrbKfbXiQgiHUMz16xRrsy5IHBEDketWL6r1CDpoeh0ivdmsu9C43ogpTc7bWzvaAaUs3lnXIUPqNof1/fC9nu2CpSEKOG6iRnowah+UeVWbvHbr8pHWVKru0AEQ0QqGEHcQ0oWDBA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(4022899009)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: +fV6SdmzXmqRGF39Dxh7t8Gc+/3poaVWq+FnymCBRCyGs0WAyFhPl04j9WWcf3FHkNDIQo0qv8ou6G9dJjU5pWw3O+thmQ3wlQcCyJ6aNcblXDnQC+sC5IeGyjG/0LDg2Vm/g9YFtGhuTIV4t4IMdiMqnicu175THzK1qTHvEX70fOf988QmBtenJRNwW3Vn4uW6ovdCOILtMjPfFgy9wYPF/m7dj0ftCMXa4fbeshCJRNz+HZ75+UfXjDoJouDd3z0uYTWZkqEwIeStlmfoMETd086mtEbYFS7VBV7drIEGtYSS1Munso2ygrzDuo9RlU/NTwJChe2cK6W/Q0XY3c4BdUvWZuzo9UWHiddtkDeXDJyShZk3P53QUWRbKmI1KZsV7nZWiqovFf71Ly6a5FJ5FpzVjY6tdBYHnE/swlrIwH0CrSnRUSvePgrtlULQHuGvfih+u396lvKuezMwIzhSweKMsYeWKzlwcWdX1wQnPC9M3IE9ALtCmPt8m3hlBQXw+S3XGcKRV46MOp+WP4ED9tLmHVVLHwThdPsIWKBmkVPptVJWivcXOKPS1OznbhwkhYjPUf6H7tyCus8AijMfyJ1nT0Oqg2a7yMgBk71RRm50r5t6aZH5DeNMM7EU4LCHyGS9+Esm+oVkenRVn5hqeGzFO3D5SZncWNAKlZiuT+jw94m5GjCgGNbl9aMheanpRbYjUCogTerGD2LntG82QNzqQpfdaqITJgvNfB/99tvxytIKZNX0sxH+OGfe451Rt1KjNiDjt3QfsN5UYXTn1K48A675fjM0GxyDo2Zm2/Dfj759KnucF1up1qpvslUuaHFW2qNa86bDbKL/X7Nv33KkOsb2vp/5vnuO7G6L00q4LAf2bG/0MFEqDiRNpIj8cges2Hbvhs6sh6RPLbV5tc7rat7IvpxpYULcPzYbMA1A0YUMjh3xxDnMu9vzVgrD7un7GFto8vWzIRp/wzCnggeYCh/MLSkV5bl0bPPz/kbLyNsjgNmlgQdN4DYtX0EJ/Xe+PBhx6/ut8xNYKE743ji+aDujgs4lRnrD/md7Ax2WU7A7M4iI0WUCu7DoLCSt6Ao7ZbjQmamvEgYOiLo4PgDnJRJ/jZflwjQFd4pnrphT+PbJhJVp90twBTcdtsgUADU06CHuFJnUmCmQMEV62XQfVlit3u3kawEJpnGUk8cUXe5kOfspCmbW/eAj68+uWBhKV7Jm1LnK4XejSS5kbq/1G5Q2NDCTZa4ypcXhUGbnKI3LzFrOtz+xsdrmxS4DWvI/FoHDfQ9Ia0UvxziKiSNFAOOl3/3fuGdzyI87Z7Y9hGGHfRDLp2KujiM0gRO5o4QubNJXRu4wfFO8I7dnqtLkurUCoEgYN9+6RLHoLoRIMOiUllxzTGPJVRH00SBh+E68pb0i3MlfmEYUVvnn1u6FZnz4yu7Sm2Mjfh4Uvz2Ej8la1J0t5dCTO9LLfGwuo2G5j22HTVGPuJS9/8ROM1D2fFzxM0sjMSSovk93Yz/UYRvpohSzzuF5YqH1L4cICG2J/e67kWt3YCHn+1IANnqZUaD5vzrukEPuw6SeDMIFQvK5rl+zsqcXky5M X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0d9787e1-5d59-4372-7659-08dce11a6c2d X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:37:58.7432 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 0WkSMkN3nZXXy6jpzuywI1UGVSLDLKGwyokeyf/hO9zg3Qmuvp4P9kXfPWYiPg1jQ3vOH323/YC6vQR1RZt8NA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement needed functions of struct cfg80211_ops to support client and AP mode on top of NXP FW. Nxpwifi hooks separate auth and assoc in order to use SAE of wpa_supplicant and hostapd. Because NXP FW still involves in association process (driver should use association command to communicate with FW to do association process), so it is impossible for nxpwifi to leverage mac80211. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 4003 +++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/cfg80211.h | 19 + 2 files changed, 4022 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfg80211.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfg80211.h diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c new file mode 100644 index 000000000000..f8b081405ff7 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -0,0 +1,4003 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: CFG80211 + * + * Copyright 2011-2024 NXP + */ + +#include "cfg80211.h" +#include "main.h" +#include "cmdevt.h" +#include "11n.h" +#include "wmm.h" + +static const struct ieee80211_iface_limit nxpwifi_ap_sta_limits[] = { + { + .max = NXPWIFI_MAX_BSS_NUM, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_combination +nxpwifi_iface_comb_ap_sta = { + .limits = nxpwifi_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(nxpwifi_ap_sta_limits), + .max_interfaces = NXPWIFI_MAX_BSS_NUM, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40), +}; + +static const struct ieee80211_iface_combination +nxpwifi_iface_comb_ap_sta_vht = { + .limits = nxpwifi_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(nxpwifi_ap_sta_limits), + .max_interfaces = NXPWIFI_MAX_BSS_NUM, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), +}; + +/* This function maps the nl802.11 channel type into driver channel type. + * + * The mapping is as follows - + * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE + * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE + * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE + * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW + * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE + */ +u8 nxpwifi_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) +{ + switch (chan_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + return IEEE80211_HT_PARAM_CHA_SEC_NONE; + case NL80211_CHAN_HT40PLUS: + return IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + case NL80211_CHAN_HT40MINUS: + return IEEE80211_HT_PARAM_CHA_SEC_BELOW; + default: + return IEEE80211_HT_PARAM_CHA_SEC_NONE; + } +} + +/* This function maps IEEE HT secondary channel type to NL80211 channel type + */ +u8 nxpwifi_get_chan_type(struct nxpwifi_private *priv) +{ + struct nxpwifi_channel_band channel_band; + int ret; + + ret = nxpwifi_get_chan_info(priv, &channel_band); + + if (!ret) { + switch (channel_band.band_config.chan_width) { + case CHAN_BW_20MHZ: + if (IS_11N_ENABLED(priv)) + return NL80211_CHAN_HT20; + else + return NL80211_CHAN_NO_HT; + case CHAN_BW_40MHZ: + if (channel_band.band_config.chan2_offset == + SEC_CHAN_ABOVE) + return NL80211_CHAN_HT40PLUS; + else + return NL80211_CHAN_HT40MINUS; + default: + return NL80211_CHAN_HT20; + } + } + + return NL80211_CHAN_HT20; +} + +/* This function retrieves the private structure from kernel wiphy structure. + */ +static void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy) +{ + return (void *)(*(unsigned long *)wiphy_priv(wiphy)); +} + +/* CFG802.11 operation handler to delete a network key. + */ +static int +nxpwifi_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(netdev); + static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + int ret; + + ret = nxpwifi_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "crypto keys deleted failed %d\n", ret); + else + nxpwifi_dbg(priv->adapter, INFO, "info: crypto keys deleted\n"); + + return ret; +} + +/* This function forms an skb for management frame. + */ +static void +nxpwifi_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) +{ + u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + u16 pkt_len; + u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; + + pkt_len = len + ETH_ALEN; + + skb_reserve(skb, NXPWIFI_MIN_DATA_HEADER_LEN + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); + + memcpy(skb_push(skb, sizeof(tx_control)), + &tx_control, sizeof(tx_control)); + + memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); + + /* Add packet data and address4 */ + skb_put_data(skb, buf, sizeof(struct ieee80211_hdr_3addr)); + skb_put_data(skb, addr, ETH_ALEN); + skb_put_data(skb, buf + sizeof(struct ieee80211_hdr_3addr), + len - sizeof(struct ieee80211_hdr_3addr)); + + skb->priority = LOW_PRIO_TID; + __net_timestamp(skb); +} + +/* CFG802.11 operation handler to transmit a management frame. + */ +static int +nxpwifi_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + const u8 *buf = params->buf; + size_t len = params->len; + struct sk_buff *skb; + u16 pkt_len; + const struct ieee80211_mgmt *mgmt; + struct nxpwifi_txinfo *tx_info; + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + + if (!buf || !len) { + nxpwifi_dbg(priv->adapter, ERROR, "invalid buffer and length\n"); + return -EINVAL; + } + + mgmt = (const struct ieee80211_mgmt *)buf; + if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Since we support offload probe resp, we need to skip probe + * resp in AP or GO mode + */ + nxpwifi_dbg(priv->adapter, INFO, + "info: skip to send probe resp in AP or GO mode\n"); + return 0; + } + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + if (ieee80211_is_auth(mgmt->frame_control)) + nxpwifi_dbg(priv->adapter, MSG, + "auth: send auth to %pM\n", mgmt->da); + if (ieee80211_is_deauth(mgmt->frame_control)) + nxpwifi_dbg(priv->adapter, MSG, + "auth: send deauth to %pM\n", mgmt->da); + if (ieee80211_is_disassoc(mgmt->frame_control)) + nxpwifi_dbg(priv->adapter, MSG, + "assoc: send disassoc to %pM\n", mgmt->da); + if (ieee80211_is_assoc_resp(mgmt->frame_control)) + nxpwifi_dbg(priv->adapter, MSG, + "assoc: send assoc resp to %pM\n", + mgmt->da); + if (ieee80211_is_reassoc_resp(mgmt->frame_control)) + nxpwifi_dbg(priv->adapter, MSG, + "assoc: send reassoc resp to %pM\n", + mgmt->da); + } + + pkt_len = len + ETH_ALEN; + skb = dev_alloc_skb(NXPWIFI_MIN_DATA_HEADER_LEN + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + + pkt_len + sizeof(pkt_len)); + + if (!skb) { + nxpwifi_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + tx_info = NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = pkt_len; + + nxpwifi_form_mgmt_frame(skb, buf, len); + *cookie = nxpwifi_roc_cookie(priv->adapter); + + if (ieee80211_is_action(mgmt->frame_control)) + skb = nxpwifi_clone_skb_for_tx_status(priv, + skb, + NXPWIFI_BUF_FLAG_ACTION_TX_STATUS, cookie); + else + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + GFP_ATOMIC); + + nxpwifi_queue_tx_pkt(priv, skb); + + nxpwifi_dbg(priv->adapter, INFO, "info: management frame transmitted\n"); + return 0; +} + +/* CFG802.11 operation handler to register a mgmt frame. + */ +static void +nxpwifi_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct mgmt_frame_regs *upd) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + u32 mask = upd->interface_stypes; + + if (mask != priv->mgmt_frame_mask) { + priv->mgmt_frame_mask = mask; + if (priv->host_mlme_reg && + GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) + priv->mgmt_frame_mask |= HOST_MLME_MGMT_MASK; + nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG, + HOST_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + nxpwifi_dbg(priv->adapter, INFO, "info: mgmt frame registered\n"); + } +} + +/* CFG802.11 operation handler to remain on channel. + */ +static int +nxpwifi_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + + if (!chan || !cookie) { + nxpwifi_dbg(adapter, ERROR, "Invalid parameter for ROC\n"); + return -EINVAL; + } + + if (priv->roc_cfg.cookie) { + nxpwifi_dbg(adapter, INFO, + "info: ongoing ROC, cookie = 0x%llx\n", + priv->roc_cfg.cookie); + return -EBUSY; + } + + ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_SET, chan, + duration); + + if (!ret) { + *cookie = nxpwifi_roc_cookie(adapter); + priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.chan = *chan; + + cfg80211_ready_on_channel(wdev, *cookie, chan, + duration, GFP_ATOMIC); + + nxpwifi_dbg(adapter, INFO, + "info: ROC, cookie = 0x%llx\n", *cookie); + } + + return ret; +} + +/* CFG802.11 operation handler to cancel remain on channel. + */ +static int +nxpwifi_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + int ret; + + if (cookie != priv->roc_cfg.cookie) + return -ENOENT; + + ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_REMOVE, + &priv->roc_cfg.chan, 0); + + if (!ret) { + cfg80211_remain_on_channel_expired(wdev, cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0, sizeof(struct nxpwifi_roc_cfg)); + + nxpwifi_dbg(priv->adapter, INFO, + "info: cancel ROC, cookie = 0x%llx\n", cookie); + } + + return ret; +} + +/* CFG802.11 operation handler to set Tx power. + */ +static int +nxpwifi_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_power_cfg power_cfg; + int dbm = MBM_TO_DBM(mbm); + + switch (type) { + case NL80211_TX_POWER_FIXED: + power_cfg.is_power_auto = 0; + power_cfg.is_power_fixed = 1; + power_cfg.power_level = dbm; + break; + case NL80211_TX_POWER_LIMITED: + power_cfg.is_power_auto = 0; + power_cfg.is_power_fixed = 0; + power_cfg.power_level = dbm; + break; + case NL80211_TX_POWER_AUTOMATIC: + power_cfg.is_power_auto = 1; + break; + } + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + return nxpwifi_set_tx_power(priv, &power_cfg); +} + +/* CFG802.11 operation handler to get Tx power. + */ +static int +nxpwifi_cfg80211_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv = nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY); + int ret = nxpwifi_send_cmd(priv, HOST_CMD_RF_TX_PWR, + HOST_ACT_GEN_GET, 0, NULL, true); + + if (ret < 0) + return ret; + + /* tx_power_level is set in HOST_CMD_RF_TX_PWR command handler */ + *dbm = priv->tx_power_level; + + return 0; +} + +/* CFG802.11 operation handler to set Power Save option. + * + * The timeout value, if provided, is currently ignored. + */ +static int +nxpwifi_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool enabled, int timeout) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + u32 ps_mode; + + if (timeout) + nxpwifi_dbg(priv->adapter, INFO, + "info: ignore timeout value for IEEE Power Save\n"); + + ps_mode = enabled; + + return nxpwifi_drv_set_power(priv, &ps_mode); +} + +/* CFG802.11 operation handler to set the default network key. + */ +static int +nxpwifi_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool unicast, + bool multicast) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(netdev); + int ret = 0; + + /* Return if WEP key not configured */ + if (!priv->sec_info.wep_enabled) + return 0; + + if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP) { + priv->wep_key_curr_index = key_index; + } else { + ret = nxpwifi_set_encode(priv, NULL, NULL, 0, key_index, + NULL, 0); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "failed to set default Tx key index\n"); + } + + return ret; +} + +/* CFG802.11 operation handler to add a network key. + */ +static int +nxpwifi_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + int link_id, u8 key_index, bool pairwise, + const u8 *mac_addr, struct key_params *params) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(netdev); + struct nxpwifi_wep_key *wep_key; + static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + int ret; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP && + (params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104)) { + if (params->key && params->key_len) { + wep_key = &priv->wep_key[key_index]; + memset(wep_key, 0, sizeof(struct nxpwifi_wep_key)); + memcpy(wep_key->key_material, params->key, + params->key_len); + wep_key->key_index = key_index; + wep_key->key_length = params->key_len; + priv->sec_info.wep_enabled = 1; + } + return 0; + } + + ret = nxpwifi_set_encode(priv, params, params->key, params->key_len, + key_index, peer_mac, 0); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "failed to add crypto keys\n"); + + return ret; +} + +/* CFG802.11 operation handler to set default mgmt key. + */ +static int +nxpwifi_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + int link_id, + u8 key_index) +{ + return 0; +} + +/* This function sends domain information to the firmware. + * + * The following information are passed to the firmware - + * - Country codes + * - Sub bands (first channel, number of channels, maximum Tx power) + */ +int nxpwifi_send_domain_info_cmd_fw(struct wiphy *wiphy, enum nl80211_band band) +{ + u8 no_of_triplet = 0; + struct ieee80211_country_ie_triplet *t; + u8 no_of_parsed_chan = 0; + u8 first_chan = 0, next_chan = 0, max_pwr = 0; + u8 i, flag = 0; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_802_11d_domain_reg *domain_info = &adapter->domain_reg; + int ret; + + domain_info->dfs_region = adapter->dfs_region; + + /* Set country code */ + domain_info->country_code[0] = adapter->country_code[0]; + domain_info->country_code[1] = adapter->country_code[1]; + domain_info->country_code[2] = ' '; + + if (!wiphy->bands[band]) { + nxpwifi_dbg(adapter, ERROR, + "11D: setting domain info in FW\n"); + return -EINVAL; + } + + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + first_chan = (u32)ch->hw_value; + next_chan = first_chan; + max_pwr = ch->max_power; + no_of_parsed_chan = 1; + continue; + } + + if (ch->hw_value == next_chan + 1 && + ch->max_power == max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + t = &domain_info->triplet[no_of_triplet]; + t->chans.first_channel = first_chan; + t->chans.num_channels = no_of_parsed_chan; + t->chans.max_power = max_pwr; + no_of_triplet++; + first_chan = (u32)ch->hw_value; + next_chan = first_chan; + max_pwr = ch->max_power; + no_of_parsed_chan = 1; + } + } + + if (flag) { + t = &domain_info->triplet[no_of_triplet]; + t->chans.first_channel = first_chan; + t->chans.num_channels = no_of_parsed_chan; + t->chans.max_power = max_pwr; + no_of_triplet++; + } + + domain_info->no_of_triplet = no_of_triplet; + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11D_DOMAIN_INFO, + HOST_ACT_GEN_SET, 0, NULL, false); + + if (ret) + nxpwifi_dbg(adapter, INFO, + "11D: failed to set domain info in FW\n"); + + return ret; +} + +static void nxpwifi_reg_apply_radar_flags(struct wiphy *wiphy) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + unsigned int i; + + if (!wiphy->bands[NL80211_BAND_5GHZ]) + return; + sband = wiphy->bands[NL80211_BAND_5GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + if ((!(chan->flags & IEEE80211_CHAN_DISABLED)) && + (chan->flags & IEEE80211_CHAN_RADAR)) + chan->flags |= IEEE80211_CHAN_NO_IR; + } +} + +/* CFG802.11 regulatory domain callback function. + * + * This function is called when the regulatory domain is changed due to the + * following reasons - + * - Set by driver + * - Set by system core + * - Set by user + * - Set bt Country IE + */ +static void nxpwifi_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv = nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY); + nxpwifi_dbg(adapter, INFO, + "info: cfg80211 regulatory domain callback for %c%c\n", + request->alpha2[0], request->alpha2[1]); + nxpwifi_reg_apply_radar_flags(wiphy); + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + break; + default: + nxpwifi_dbg(adapter, ERROR, + "unknown regdom initiator: %d\n", + request->initiator); + return; + } + + /* Don't send world or same regdom info to firmware */ + if (strncmp(request->alpha2, "00", 2) && + strncmp(request->alpha2, adapter->country_code, + sizeof(request->alpha2))) { + memcpy(adapter->country_code, request->alpha2, + sizeof(request->alpha2)); + adapter->dfs_region = request->dfs_region; + nxpwifi_send_domain_info_cmd_fw(wiphy, NL80211_BAND_2GHZ); + if (adapter->fw_bands & BAND_A) + nxpwifi_send_domain_info_cmd_fw(wiphy, + NL80211_BAND_5GHZ); + nxpwifi_dnld_txpwr_table(priv); + } +} + +/* This function sets the fragmentation threshold. + * + * The fragmentation threshold value must lie between NXPWIFI_FRAG_MIN_VALUE + * and NXPWIFI_FRAG_MAX_VALUE. + */ +static int +nxpwifi_set_frag(struct nxpwifi_private *priv, u32 frag_thr) +{ + if (frag_thr < NXPWIFI_FRAG_MIN_VALUE || + frag_thr > NXPWIFI_FRAG_MAX_VALUE) + frag_thr = NXPWIFI_FRAG_MAX_VALUE; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_SET, FRAG_THRESH_I, + &frag_thr, true); +} + +/* This function sets the RTS threshold. + * + * The rts value must lie between NXPWIFI_RTS_MIN_VALUE + * and NXPWIFI_RTS_MAX_VALUE. + */ +static int +nxpwifi_set_rts(struct nxpwifi_private *priv, u32 rts_thr) +{ + if (rts_thr < NXPWIFI_RTS_MIN_VALUE || rts_thr > NXPWIFI_RTS_MAX_VALUE) + rts_thr = NXPWIFI_RTS_MAX_VALUE; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_SET, RTS_THRESH_I, + &rts_thr, true); +} + +/* CFG802.11 operation handler to set wiphy parameters. + * + * This function can be used to set the RTS threshold and the + * Fragmentation threshold of the driver. + */ +static int +nxpwifi_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_uap_bss_param *bss_cfg; + int ret = 0; + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + switch (priv->bss_role) { + case NXPWIFI_BSS_ROLE_UAP: + bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); + if (!bss_cfg) { + ret = -ENOMEM; + break; + } + + nxpwifi_set_sys_config_invalid_data(bss_cfg); + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + bss_cfg->rts_threshold = wiphy->rts_threshold; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + bss_cfg->frag_threshold = wiphy->frag_threshold; + if (changed & WIPHY_PARAM_RETRY_LONG) + bss_cfg->retry_limit = wiphy->retry_long; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG, + HOST_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, + false); + + kfree(bss_cfg); + if (ret) + nxpwifi_dbg(adapter, ERROR, + "Failed to set wiphy phy params\n"); + break; + + case NXPWIFI_BSS_ROLE_STA: + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + ret = nxpwifi_set_rts(priv, + wiphy->rts_threshold); + if (ret) + break; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + ret = nxpwifi_set_frag(priv, + wiphy->frag_threshold); + break; + } + + return ret; +} + +static int nxpwifi_deinit_priv_params(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + unsigned long flags; + int ret; + + priv->host_mlme_reg = false; + priv->mgmt_frame_mask = 0; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG, + HOST_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "could not unregister mgmt frame rx\n"); + return ret; + } + + nxpwifi_deauthenticate(priv, NULL); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = true; + if (adapter->nxpwifi_processing) { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + flush_workqueue(adapter->workqueue); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } + + tasklet_disable(&adapter->rx_task); + + nxpwifi_free_priv(priv); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + + return ret; +} + +static int +nxpwifi_init_new_priv_params(struct nxpwifi_private *priv, + struct net_device *dev, + enum nl80211_iftype type) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + unsigned long flags; + + nxpwifi_init_priv(priv); + + priv->bss_mode = type; + priv->wdev.iftype = type; + + nxpwifi_init_priv_params(priv, priv->netdev); + priv->bss_started = 0; + + switch (type) { + case NL80211_IFTYPE_STATION: + priv->bss_role = NXPWIFI_BSS_ROLE_STA; + priv->bss_type = NXPWIFI_BSS_TYPE_STA; + break; + case NL80211_IFTYPE_AP: + priv->bss_role = NXPWIFI_BSS_ROLE_UAP; + priv->bss_type = NXPWIFI_BSS_TYPE_UAP; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + priv->bss_num = nxpwifi_get_unused_bss_num(adapter, priv->bss_type); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + + tasklet_enable(&adapter->rx_task); + + nxpwifi_set_mac_address(priv, dev, false, NULL); + + return 0; +} + +static bool +is_vif_type_change_allowed(struct nxpwifi_adapter *adapter, + enum nl80211_iftype old_iftype, + enum nl80211_iftype new_iftype) +{ + switch (old_iftype) { + case NL80211_IFTYPE_STATION: + switch (new_iftype) { + case NL80211_IFTYPE_AP: + return adapter->curr_iface_comb.uap_intf != + adapter->iface_limit.uap_intf; + default: + return false; + } + + case NL80211_IFTYPE_AP: + switch (new_iftype) { + case NL80211_IFTYPE_STATION: + return adapter->curr_iface_comb.sta_intf != + adapter->iface_limit.sta_intf; + default: + return false; + } + + default: + break; + } + + return false; +} + +static void +update_vif_type_counter(struct nxpwifi_adapter *adapter, + enum nl80211_iftype iftype, + int change) +{ + switch (iftype) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + adapter->curr_iface_comb.sta_intf += change; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf += change; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "%s: Unsupported iftype passed: %d\n", + __func__, iftype); + break; + } +} + +static int +nxpwifi_change_vif_to_sta(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, + struct vif_params *params) +{ + struct nxpwifi_private *priv; + struct nxpwifi_adapter *adapter; + int ret; + + priv = nxpwifi_netdev_get_priv(dev); + + if (!priv) + return -EINVAL; + + adapter = priv->adapter; + + nxpwifi_dbg(adapter, INFO, + "%s: changing role to station\n", dev->name); + + ret = nxpwifi_deinit_priv_params(priv); + if (ret) + goto done; + ret = nxpwifi_init_new_priv_params(priv, dev, type); + if (ret) + goto done; + + update_vif_type_counter(adapter, curr_iftype, -1); + update_vif_type_counter(adapter, type, 1); + dev->ieee80211_ptr->iftype = type; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) + goto done; + + ret = nxpwifi_sta_init_cmd(priv, false, false); + +done: + return ret; +} + +static int +nxpwifi_change_vif_to_ap(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, + struct vif_params *params) +{ + struct nxpwifi_private *priv; + struct nxpwifi_adapter *adapter; + int ret; + + priv = nxpwifi_netdev_get_priv(dev); + + if (!priv) + return -EINVAL; + + adapter = priv->adapter; + + nxpwifi_dbg(adapter, INFO, + "%s: changing role to AP\n", dev->name); + + ret = nxpwifi_deinit_priv_params(priv); + if (ret) + goto done; + + ret = nxpwifi_init_new_priv_params(priv, dev, type); + if (ret) + goto done; + + update_vif_type_counter(adapter, curr_iftype, -1); + update_vif_type_counter(adapter, type, 1); + dev->ieee80211_ptr->iftype = type; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) + goto done; + + ret = nxpwifi_sta_init_cmd(priv, false, false); + +done: + return ret; +} + +/* CFG802.11 operation handler to change interface type. + */ +static int +nxpwifi_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, + struct vif_params *params) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype; + + if (priv->scan_request) { + nxpwifi_dbg(priv->adapter, ERROR, + "change virtual interface: scan in process\n"); + return -EBUSY; + } + + if (type == NL80211_IFTYPE_UNSPECIFIED) { + nxpwifi_dbg(priv->adapter, INFO, + "%s: no new type specified, keeping old type %d\n", + dev->name, curr_iftype); + return 0; + } + + if (curr_iftype == type) { + nxpwifi_dbg(priv->adapter, INFO, + "%s: interface already is of type %d\n", + dev->name, curr_iftype); + return 0; + } + + if (!is_vif_type_change_allowed(priv->adapter, curr_iftype, type)) { + nxpwifi_dbg(priv->adapter, ERROR, + "%s: change from type %d to %d is not allowed\n", + dev->name, curr_iftype, type); + return -EOPNOTSUPP; + } + + switch (curr_iftype) { + case NL80211_IFTYPE_STATION: + switch (type) { + case NL80211_IFTYPE_AP: + return nxpwifi_change_vif_to_ap(dev, curr_iftype, type, + params); + default: + goto errnotsupp; + } + + case NL80211_IFTYPE_AP: + switch (type) { + case NL80211_IFTYPE_STATION: + return nxpwifi_change_vif_to_sta(dev, curr_iftype, + type, params); + break; + default: + goto errnotsupp; + } + + default: + goto errnotsupp; + } + + return 0; + +errnotsupp: + nxpwifi_dbg(priv->adapter, ERROR, + "unsupported interface type transition: %d to %d\n", + curr_iftype, type); + return -EOPNOTSUPP; +} + +#define RATE_FORMAT_LG 0 +#define RATE_FORMAT_HT 1 +#define RATE_FORMAT_VHT 2 +#define RATE_FORMAT_HE 3 + +static void +nxpwifi_parse_htinfo(struct nxpwifi_private *priv, u8 rateinfo, u8 htinfo, + struct rate_info *rate) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u8 rate_format; + u8 he_dcm; + u8 stbc; + u8 gi; + u8 bw; + + rate_format = htinfo & 0x3; + + switch (rate_format) { + case RATE_FORMAT_LG: + /* Bitrates in multiples of 100kb/s. */ + static const int legacy_rates[] = { + [0] = 10, + [1] = 20, + [2] = 55, + [3] = 110, + [4] = 60, /* NXPWIFI_RATE_INDEX_OFDM0 */ + [5] = 60, + [6] = 90, + [7] = 120, + [8] = 180, + [9] = 240, + [10] = 360, + [11] = 480, + [12] = 540, + }; + if (rateinfo < ARRAY_SIZE(legacy_rates)) + rate->legacy = legacy_rates[rateinfo]; + break; + case RATE_FORMAT_HT: + rate->mcs = rateinfo; + rate->flags |= RATE_INFO_FLAGS_MCS; + break; + case RATE_FORMAT_VHT: + rate->mcs = rateinfo & 0xF; + rate->flags |= RATE_INFO_FLAGS_VHT_MCS; + break; + case RATE_FORMAT_HE: + rate->mcs = rateinfo & 0xF; + rate->flags |= RATE_INFO_FLAGS_HE_MCS; + he_dcm = 0; /* ToDo: ext_rate_info */ + gi = (htinfo & BIT(4)) >> 4 | + (htinfo & BIT(7)) >> 6; + stbc = (htinfo & BIT(5)) >> 5; + if (gi > 3) { + nxpwifi_dbg(adapter, ERROR, "Invalid gi value\n"); + break; + } + if (gi == 3 && stbc && he_dcm) { + gi = 0; + stbc = 0; + he_dcm = 0; + } + if (gi > 0) + gi -= 1; + rate->he_gi = gi; + rate->he_dcm = he_dcm; + break; + } + + bw = (htinfo & 0xC) >> 2; + + switch (bw) { + case 0: + rate->bw = RATE_INFO_BW_20; + break; + case 1: + rate->bw = RATE_INFO_BW_40; + break; + case 2: + rate->bw = RATE_INFO_BW_80; + break; + case 3: + rate->bw = RATE_INFO_BW_160; + break; + } + + if (rate_format != RATE_FORMAT_HE && (htinfo & BIT(4))) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + + if ((rateinfo >> 4) == 1) + rate->nss = 2; + else + rate->nss = 1; +} + +/* This function dumps the station information on a buffer. + * + * The following information are shown - + * - Total bytes transmitted + * - Total bytes received + * - Total packets transmitted + * - Total packets received + * - Signal quality level + * - Transmission rate + */ +static int +nxpwifi_dump_station_info(struct nxpwifi_private *priv, + struct nxpwifi_sta_node *node, + struct station_info *sinfo) +{ + u32 rate; + int ret; + + sinfo->filled = BIT_ULL(NL80211_STA_INFO_RX_BYTES) | + BIT_ULL(NL80211_STA_INFO_TX_BYTES) | + BIT_ULL(NL80211_STA_INFO_RX_PACKETS) | + BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | + BIT_ULL(NL80211_STA_INFO_TX_BITRATE) | + BIT_ULL(NL80211_STA_INFO_SIGNAL) | + BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + if (!node) + return -ENOENT; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) | + BIT_ULL(NL80211_STA_INFO_TX_FAILED); + sinfo->inactive_time = + jiffies_to_msecs(jiffies - node->stats.last_rx); + + sinfo->signal = node->stats.rssi; + sinfo->signal_avg = node->stats.rssi; + sinfo->rx_bytes = node->stats.rx_bytes; + sinfo->tx_bytes = node->stats.tx_bytes; + sinfo->rx_packets = node->stats.rx_packets; + sinfo->tx_packets = node->stats.tx_packets; + sinfo->tx_failed = node->stats.tx_failed; + + nxpwifi_parse_htinfo(priv, priv->tx_rate, + node->stats.last_tx_htinfo, + &sinfo->txrate); + sinfo->txrate.legacy = node->stats.last_tx_rate * 5; + + return 0; + } + + /* Get signal information from the firmware */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO, + HOST_ACT_GEN_GET, 0, NULL, true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "failed to get signal information\n"); + goto done; + } + + ret = nxpwifi_drv_get_data_rate(priv, &rate); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "getting data rate error\n"); + goto done; + } + + /* Get DTIM period information from firmware */ + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, DTIM_PERIOD_I, + &priv->dtim_period, true); + + nxpwifi_parse_htinfo(priv, priv->tx_rate, priv->tx_htinfo, + &sinfo->txrate); + + sinfo->signal_avg = priv->bcn_rssi_avg; + sinfo->rx_bytes = priv->stats.rx_bytes; + sinfo->tx_bytes = priv->stats.tx_bytes; + sinfo->rx_packets = priv->stats.rx_packets; + sinfo->tx_packets = priv->stats.tx_packets; + sinfo->signal = priv->bcn_rssi_avg; + /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->txrate.legacy = rate * 5; + + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); + nxpwifi_parse_htinfo(priv, priv->rxpd_rate, priv->rxpd_htinfo, + &sinfo->rxrate); + + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM); + sinfo->bss_param.flags = 0; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = priv->dtim_period; + sinfo->bss_param.beacon_interval = + priv->curr_bss_params.bss_descriptor.beacon_period; + } + +done: + return ret; +} + +/* CFG802.11 operation handler to get station information. + * + * This function only works in connected mode, and dumps the + * requested station information, if available. + */ +static int +nxpwifi_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + + if (!priv->media_connected) + return -ENOENT; + if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) + return -ENOENT; + + return nxpwifi_dump_station_info(priv, NULL, sinfo); +} + +/* CFG802.11 operation handler to dump station information. + */ +static int +nxpwifi_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_sta_node *node; + int i; + + if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) && + priv->media_connected && idx == 0) { + ether_addr_copy(mac, priv->cfg_bssid); + return nxpwifi_dump_station_info(priv, NULL, sinfo); + } else if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + nxpwifi_send_cmd(priv, HOST_CMD_APCMD_STA_LIST, + HOST_ACT_GEN_GET, 0, NULL, true); + + i = 0; + list_for_each_entry(node, &priv->sta_list, list) { + if (i++ != idx) + continue; + ether_addr_copy(mac, node->mac_addr); + return nxpwifi_dump_station_info(priv, node, sinfo); + } + } + + return -ENOENT; +} + +static int +nxpwifi_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, + int idx, struct survey_info *survey) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_chan_stats *pchan_stats = priv->adapter->chan_stats; + enum nl80211_band band; + u8 chan_num; + + nxpwifi_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx); + + memset(survey, 0, sizeof(struct survey_info)); + + if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) && + priv->media_connected && idx == 0) { + u8 curr_bss_band = priv->curr_bss_params.band; + u32 chan = priv->curr_bss_params.bss_descriptor.channel; + + band = nxpwifi_band_to_radio_type(curr_bss_band); + survey->channel = ieee80211_get_channel + (wiphy, + ieee80211_channel_to_frequency(chan, band)); + + if (priv->bcn_nf_last) { + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = priv->bcn_nf_last; + } + return 0; + } + + if (idx >= priv->adapter->num_in_chan_stats) + return -ENOENT; + + if (!pchan_stats[idx].cca_scan_dur) + return 0; + + band = pchan_stats[idx].bandcfg; + chan_num = pchan_stats[idx].chan_num; + survey->channel = ieee80211_get_channel + (wiphy, + ieee80211_channel_to_frequency(chan_num, band)); + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->noise = pchan_stats[idx].noise; + survey->time = pchan_stats[idx].cca_scan_dur; + survey->time_busy = pchan_stats[idx].cca_busy_dur; + + return 0; +} + +/* Supported rates to be advertised to the cfg80211 */ +static struct ieee80211_rate nxpwifi_rates[] = { + {.bitrate = 10, .hw_value = 2, }, + {.bitrate = 20, .hw_value = 4, }, + {.bitrate = 55, .hw_value = 11, }, + {.bitrate = 110, .hw_value = 22, }, + {.bitrate = 60, .hw_value = 12, }, + {.bitrate = 90, .hw_value = 18, }, + {.bitrate = 120, .hw_value = 24, }, + {.bitrate = 180, .hw_value = 36, }, + {.bitrate = 240, .hw_value = 48, }, + {.bitrate = 360, .hw_value = 72, }, + {.bitrate = 480, .hw_value = 96, }, + {.bitrate = 540, .hw_value = 108, }, +}; + +/* Channel definitions to be advertised to cfg80211 */ +static struct ieee80211_channel nxpwifi_channels_2ghz[] = { + {.center_freq = 2412, .hw_value = 1, }, + {.center_freq = 2417, .hw_value = 2, }, + {.center_freq = 2422, .hw_value = 3, }, + {.center_freq = 2427, .hw_value = 4, }, + {.center_freq = 2432, .hw_value = 5, }, + {.center_freq = 2437, .hw_value = 6, }, + {.center_freq = 2442, .hw_value = 7, }, + {.center_freq = 2447, .hw_value = 8, }, + {.center_freq = 2452, .hw_value = 9, }, + {.center_freq = 2457, .hw_value = 10, }, + {.center_freq = 2462, .hw_value = 11, }, + {.center_freq = 2467, .hw_value = 12, }, + {.center_freq = 2472, .hw_value = 13, }, + {.center_freq = 2484, .hw_value = 14, }, +}; + +static struct ieee80211_supported_band nxpwifi_band_2ghz = { + .band = NL80211_BAND_2GHZ, + .channels = nxpwifi_channels_2ghz, + .n_channels = ARRAY_SIZE(nxpwifi_channels_2ghz), + .bitrates = nxpwifi_rates, + .n_bitrates = ARRAY_SIZE(nxpwifi_rates), +}; + +static struct ieee80211_channel nxpwifi_channels_5ghz[] = { + {.center_freq = 5040, .hw_value = 8, }, + {.center_freq = 5060, .hw_value = 12, }, + {.center_freq = 5080, .hw_value = 16, }, + {.center_freq = 5170, .hw_value = 34, }, + {.center_freq = 5190, .hw_value = 38, }, + {.center_freq = 5210, .hw_value = 42, }, + {.center_freq = 5230, .hw_value = 46, }, + {.center_freq = 5180, .hw_value = 36, }, + {.center_freq = 5200, .hw_value = 40, }, + {.center_freq = 5220, .hw_value = 44, }, + {.center_freq = 5240, .hw_value = 48, }, + {.center_freq = 5260, .hw_value = 52, }, + {.center_freq = 5280, .hw_value = 56, }, + {.center_freq = 5300, .hw_value = 60, }, + {.center_freq = 5320, .hw_value = 64, }, + {.center_freq = 5500, .hw_value = 100, }, + {.center_freq = 5520, .hw_value = 104, }, + {.center_freq = 5540, .hw_value = 108, }, + {.center_freq = 5560, .hw_value = 112, }, + {.center_freq = 5580, .hw_value = 116, }, + {.center_freq = 5600, .hw_value = 120, }, + {.center_freq = 5620, .hw_value = 124, }, + {.center_freq = 5640, .hw_value = 128, }, + {.center_freq = 5660, .hw_value = 132, }, + {.center_freq = 5680, .hw_value = 136, }, + {.center_freq = 5700, .hw_value = 140, }, + {.center_freq = 5745, .hw_value = 149, }, + {.center_freq = 5765, .hw_value = 153, }, + {.center_freq = 5785, .hw_value = 157, }, + {.center_freq = 5805, .hw_value = 161, }, + {.center_freq = 5825, .hw_value = 165, }, +}; + +static struct ieee80211_supported_band nxpwifi_band_5ghz = { + .band = NL80211_BAND_5GHZ, + .channels = nxpwifi_channels_5ghz, + .n_channels = ARRAY_SIZE(nxpwifi_channels_5ghz), + .bitrates = nxpwifi_rates + 4, + .n_bitrates = ARRAY_SIZE(nxpwifi_rates) - 4, +}; + +/* Supported crypto cipher suits to be advertised to cfg80211 */ +static const u32 nxpwifi_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_SMS4, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* Supported mgmt frame types to be advertised to cfg80211 */ +static const struct ieee80211_txrx_stypes +nxpwifi_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 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), + }, +}; + +/* CFG802.11 operation handler for setting bit rates. + * + * Function configures data rates to firmware using bitrate mask + * provided by cfg80211. + */ +static int +nxpwifi_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + unsigned int link_id, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + enum nl80211_band band; + struct nxpwifi_adapter *adapter = priv->adapter; + + if (!priv->media_connected) { + nxpwifi_dbg(adapter, ERROR, + "Can not set Tx data rate in disconnected state\n"); + return -EINVAL; + } + + band = nxpwifi_band_to_radio_type(priv->curr_bss_params.band); + + memset(bitmap_rates, 0, sizeof(bitmap_rates)); + + /* Fill HR/DSSS rates. */ + if (band == NL80211_BAND_2GHZ) + bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == NL80211_BAND_2GHZ) + bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; + else + bitmap_rates[1] = mask->control[band].legacy; + + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; + + /* Fill VHT MCS rates */ + if (adapter->fw_api_ver == NXPWIFI_FW_V15) { + bitmap_rates[10] = mask->control[band].vht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[11] = mask->control[band].vht_mcs[1]; + } + + return nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG, + HOST_ACT_GEN_SET, 0, bitmap_rates, true); +} + +/* CFG802.11 operation handler for connection quality monitoring. + * + * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI + * events to FW. + */ +static int nxpwifi_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_ds_misc_subsc_evt subsc_evt; + int ret = 0; + + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + memset(&subsc_evt, 0x00, sizeof(struct nxpwifi_ds_misc_subsc_evt)); + subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + + /* Subscribe/unsubscribe low and high rssi events */ + if (rssi_thold && rssi_hyst) { + subsc_evt.action = HOST_ACT_BITWISE_SET; + subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; + ret = nxpwifi_send_cmd(priv, + HOST_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } else { + subsc_evt.action = HOST_ACT_BITWISE_CLR; + ret = nxpwifi_send_cmd(priv, + HOST_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } + + return ret; +} + +/* cfg80211 operation handler for change_beacon. + * Function retrieves and sets modified management IEs to FW. + */ +int nxpwifi_cfg80211_change_beacon_data(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *data) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + + nxpwifi_cancel_scan(adapter); + + if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) { + nxpwifi_dbg(priv->adapter, ERROR, + "%s: bss_type mismatched\n", __func__); + return -EINVAL; + } + + ret = nxpwifi_set_mgmt_ies(priv, data); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "%s: setting mgmt ies failed\n", __func__); + + return ret; +} + +/* cfg80211 operation handler for change_beacon. + * Function retrieves and sets modified management IEs to FW. + */ +static int nxpwifi_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_update *params) +{ + return nxpwifi_cfg80211_change_beacon_data(wiphy, dev, ¶ms->beacon); +} + +/* cfg80211 operation handler for del_station. + * Function deauthenticates station which value is provided in mac parameter. + * If mac is NULL/broadcast, all stations in associated station list are + * deauthenticated. If bss is not started or there are no stations in + * associated stations list, no action is taken. + */ +static int +nxpwifi_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + struct station_del_parameters *params) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_sta_node *sta_node; + u8 deauth_mac[ETH_ALEN]; + int ret = 0; + + if (!priv->bss_started && priv->wdev.links[0].cac_started) { + nxpwifi_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__); + nxpwifi_abort_cac(priv); + } + + if (list_empty(&priv->sta_list) || !priv->bss_started) + return 0; + + if (!params->mac || is_broadcast_ether_addr(params->mac)) + return 0; + + nxpwifi_dbg(priv->adapter, INFO, "%s: mac address %pM\n", + __func__, params->mac); + + eth_zero_addr(deauth_mac); + + spin_lock_bh(&priv->sta_list_spinlock); + sta_node = nxpwifi_get_sta_entry(priv, params->mac); + if (sta_node) + ether_addr_copy(deauth_mac, params->mac); + spin_unlock_bh(&priv->sta_list_spinlock); + + if (is_valid_ether_addr(deauth_mac)) + ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_STA_DEAUTH, + HOST_ACT_GEN_SET, 0, + deauth_mac, true); + + return ret; +} + +static int +nxpwifi_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv = nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY); + struct nxpwifi_ds_ant_cfg ant_cfg; + + if (!tx_ant || !rx_ant) + return -EOPNOTSUPP; + + if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { + /* Not a MIMO chip. User should provide specific antenna number + * for Tx/Rx path or enable all antennas for diversity + */ + if (tx_ant != rx_ant) + return -EOPNOTSUPP; + + if ((tx_ant & (tx_ant - 1)) && + (tx_ant != BIT(adapter->number_of_antenna) - 1)) + return -EOPNOTSUPP; + + if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && + priv->adapter->number_of_antenna > 1) { + tx_ant = RF_ANTENNA_AUTO; + rx_ant = RF_ANTENNA_AUTO; + } + } else { + struct ieee80211_sta_ht_cap *ht_info; + int rx_mcs_supp; + enum nl80211_band band; + + if ((tx_ant == 0x1 && rx_ant == 0x1)) { + adapter->user_dev_mcs_support = HT_STREAM_1X1; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + NXPWIFI_11AC_MCS_MAP_1X1; + } else { + adapter->user_dev_mcs_support = HT_STREAM_2X2; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + NXPWIFI_11AC_MCS_MAP_2X2; + } + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (!adapter->wiphy->bands[band]) + continue; + + ht_info = &adapter->wiphy->bands[band]->ht_cap; + rx_mcs_supp = + GET_RXMCSSUPP(adapter->user_dev_mcs_support); + memset(&ht_info->mcs, 0, adapter->number_of_antenna); + memset(&ht_info->mcs, 0xff, rx_mcs_supp); + } + } + + ant_cfg.tx_ant = tx_ant; + ant_cfg.rx_ant = rx_ant; + + return nxpwifi_send_cmd(priv, HOST_CMD_RF_ANTENNA, + HOST_ACT_GEN_SET, 0, &ant_cfg, true); +} + +static int +nxpwifi_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv = nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY); + int ret; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_RF_ANTENNA, + HOST_ACT_GEN_GET, 0, NULL, true); + + if (!ret) { + *tx_ant = priv->tx_ant; + *rx_ant = priv->rx_ant; + } + + return ret; +} + +/* cfg80211 operation handler for stop ap. + * Function stops BSS running at uAP interface. + */ +static int nxpwifi_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, + unsigned int link_id) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + int ret; + + nxpwifi_abort_cac(priv); + + if (nxpwifi_del_mgmt_ies(priv)) + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + priv->ap_11n_enabled = 0; + memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); + + ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to stop the BSS\n"); + goto done; + } + + ret = nxpwifi_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to reset BSS\n"); + goto done; + } + + netif_carrier_off(priv->netdev); + nxpwifi_stop_net_dev_queue(priv->netdev, priv->adapter); + +done: + return ret; +} + +/* cfg80211 operation handler for start_ap. + * Function sets beacon period, DTIM period, SSID and security into + * AP config structure. + * AP is configured with these settings and BSS is started. + */ +static int nxpwifi_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +{ + struct nxpwifi_uap_bss_param *bss_cfg; + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + + if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) + return -EINVAL; + + if (!nxpwifi_is_channel_setting_allowable(priv, params->chandef.chan)) + return -EOPNOTSUPP; + + bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + nxpwifi_set_sys_config_invalid_data(bss_cfg); + + memcpy(bss_cfg->mac_addr, priv->curr_addr, ETH_ALEN); + + if (params->beacon_interval) + bss_cfg->beacon_period = params->beacon_interval; + if (params->dtim_period) + bss_cfg->dtim_period = params->dtim_period; + + if (params->ssid && params->ssid_len) { + memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); + bss_cfg->ssid.ssid_len = params->ssid_len; + } + if (params->inactivity_timeout > 0) { + /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ + bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; + bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; + } + + switch (params->hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + bss_cfg->bcast_ssid_ctl = 1; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + bss_cfg->bcast_ssid_ctl = 0; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + bss_cfg->bcast_ssid_ctl = 2; + break; + default: + kfree(bss_cfg); + return -EINVAL; + } + + nxpwifi_uap_set_channel(priv, bss_cfg, params->chandef); + nxpwifi_set_uap_rates(bss_cfg, params); + + ret = nxpwifi_set_secure_params(priv, bss_cfg, params); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "Failed to parse security parameters!\n"); + goto done; + } + + nxpwifi_set_ht_params(priv, bss_cfg, params); + + if (adapter->is_hw_11ac_capable) { + nxpwifi_set_vht_params(priv, bss_cfg, params); + nxpwifi_set_vht_width(priv, params->chandef.width, + priv->ap_11ac_enabled); + } + + if (priv->ap_11ac_enabled) + nxpwifi_set_11ac_ba_params(priv); + else + nxpwifi_set_ba_params(priv); + + if (adapter->is_hw_11ax_capable) { + priv->ap_11ax_enabled = + nxpwifi_check_11ax_capability(priv, bss_cfg, params); + if (priv->ap_11ax_enabled) + nxpwifi_set_11ax_status(priv, bss_cfg, params); + } + + nxpwifi_set_wmm_params(priv, bss_cfg, params); + + if (nxpwifi_is_11h_active(priv)) + nxpwifi_set_tpc_params(priv, bss_cfg, params); + + if (nxpwifi_is_11h_active(priv) && + !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef, + priv->bss_mode)) { + nxpwifi_dbg(priv->adapter, INFO, + "Disable 11h extensions in FW\n"); + ret = nxpwifi_11h_activate(priv, false); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to disable 11h extensions!!"); + goto done; + } + priv->state_11h.is_11h_active = false; + } + + nxpwifi_config_uap_11d(priv, ¶ms->beacon); + + ret = nxpwifi_config_start_uap(priv, bss_cfg); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to start AP\n"); + goto done; + } + + ret = nxpwifi_set_mgmt_ies(priv, ¶ms->beacon); + if (ret) + goto done; + + netif_carrier_on(priv->netdev); + nxpwifi_wake_up_net_dev_queue(priv->netdev, priv->adapter); + + memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg)); + +done: + kfree(bss_cfg); + return ret; +} + +/* CFG802.11 operation handler for scan request. + * + * This function issues a scan request to the firmware based upon + * the user specified scan configuration. On successful completion, + * it also informs the results. + */ +static int +nxpwifi_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct net_device *dev = request->wdev->netdev; + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + int i, offset, ret; + struct ieee80211_channel *chan; + struct element *ie; + struct nxpwifi_user_scan_cfg *user_scan_cfg; + u8 mac_addr[ETH_ALEN]; + + nxpwifi_dbg(priv->adapter, CMD, + "info: received scan request on %s\n", dev->name); + + /* Block scan request if scan operation or scan cleanup when interface + * is disabled is in process + */ + if (priv->scan_request || priv->scan_aborting) { + nxpwifi_dbg(priv->adapter, WARN, + "cmd: Scan already in process..\n"); + return -EBUSY; + } + + if (!priv->wdev.connected && priv->scan_block) + priv->scan_block = false; + + if (!nxpwifi_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); + + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) + return -ENOMEM; + + priv->scan_request = request; + + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + get_random_mask_addr(mac_addr, request->mac_addr, + request->mac_addr_mask); + ether_addr_copy(request->mac_addr, mac_addr); + ether_addr_copy(user_scan_cfg->random_mac, mac_addr); + } + + user_scan_cfg->num_ssids = request->n_ssids; + user_scan_cfg->ssid_list = request->ssids; + + if (request->ie && request->ie_len) { + offset = 0; + for (i = 0; i < NXPWIFI_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask != NXPWIFI_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask = NXPWIFI_VSIE_MASK_SCAN; + ie = (struct element *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, + sizeof(*ie) + ie->datalen); + offset += sizeof(*ie) + ie->datalen; + + if (offset >= request->ie_len) + break; + } + } + + for (i = 0; i < min_t(u32, request->n_channels, + NXPWIFI_USER_SCAN_CHAN_MAX); i++) { + chan = request->channels[i]; + user_scan_cfg->chan_list[i].chan_number = chan->hw_value; + user_scan_cfg->chan_list[i].radio_type = chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + user_scan_cfg->chan_list[i].scan_type = + NXPWIFI_SCAN_TYPE_PASSIVE; + else + user_scan_cfg->chan_list[i].scan_type = + NXPWIFI_SCAN_TYPE_ACTIVE; + + user_scan_cfg->chan_list[i].scan_time = 0; + } + + if (priv->adapter->scan_chan_gap_enabled && + nxpwifi_is_any_intf_active(priv)) + user_scan_cfg->scan_chan_gap = + priv->adapter->scan_chan_gap_time; + + ret = nxpwifi_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "scan failed: %d\n", ret); + priv->scan_aborting = false; + priv->scan_request = NULL; + return ret; + } + + if (request->ie && request->ie_len) { + for (i = 0; i < NXPWIFI_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask == NXPWIFI_VSIE_MASK_SCAN) { + priv->vs_ie[i].mask = NXPWIFI_VSIE_MASK_CLEAR; + memset(&priv->vs_ie[i].ie, 0, + NXPWIFI_MAX_VSIE_LEN); + } + } + } + return 0; +} + +/* CFG802.11 operation handler for sched_scan_start. + * + * This function issues a bgscan config request to the firmware based upon + * the user specified sched_scan configuration. On successful completion, + * firmware will generate BGSCAN_REPORT event, driver should issue bgscan + * query command to get sched_scan results from firmware. + */ +static int +nxpwifi_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + int i, offset; + struct ieee80211_channel *chan; + struct nxpwifi_bg_scan_cfg *bgscan_cfg; + struct element *ie; + int ret; + + if (!request || (!request->n_ssids && !request->n_match_sets)) { + wiphy_err(wiphy, "%s : Invalid Sched_scan parameters", + __func__); + return -EINVAL; + } + + wiphy_info(wiphy, "sched_scan start : n_ssids=%d n_match_sets=%d ", + request->n_ssids, request->n_match_sets); + wiphy_info(wiphy, "n_channels=%d interval=%d ie_len=%d\n", + request->n_channels, request->scan_plans->interval, + (int)request->ie_len); + + bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); + if (!bgscan_cfg) + return -ENOMEM; + + if (priv->scan_request || priv->scan_aborting) + bgscan_cfg->start_later = true; + + bgscan_cfg->num_ssids = request->n_match_sets; + bgscan_cfg->ssid_list = request->match_sets; + + if (request->ie && request->ie_len) { + offset = 0; + for (i = 0; i < NXPWIFI_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask != NXPWIFI_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask = NXPWIFI_VSIE_MASK_BGSCAN; + ie = (struct element *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, + sizeof(*ie) + ie->datalen); + offset += sizeof(*ie) + ie->datalen; + + if (offset >= request->ie_len) + break; + } + } + + for (i = 0; i < min_t(u32, request->n_channels, + NXPWIFI_BG_SCAN_CHAN_MAX); i++) { + chan = request->channels[i]; + bgscan_cfg->chan_list[i].chan_number = chan->hw_value; + bgscan_cfg->chan_list[i].radio_type = chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + bgscan_cfg->chan_list[i].scan_type = + NXPWIFI_SCAN_TYPE_PASSIVE; + else + bgscan_cfg->chan_list[i].scan_type = + NXPWIFI_SCAN_TYPE_ACTIVE; + + bgscan_cfg->chan_list[i].scan_time = 0; + } + + bgscan_cfg->chan_per_scan = min_t(u32, request->n_channels, + NXPWIFI_BG_SCAN_CHAN_MAX); + + /* Use at least 15 second for per scan cycle */ + bgscan_cfg->scan_interval = (request->scan_plans->interval > + NXPWIFI_BGSCAN_INTERVAL) ? + request->scan_plans->interval : + NXPWIFI_BGSCAN_INTERVAL; + + bgscan_cfg->repeat_count = NXPWIFI_BGSCAN_REPEAT_COUNT; + bgscan_cfg->report_condition = NXPWIFI_BGSCAN_SSID_MATCH | + NXPWIFI_BGSCAN_WAIT_ALL_CHAN_DONE; + bgscan_cfg->bss_type = NXPWIFI_BSS_MODE_INFRA; + bgscan_cfg->action = NXPWIFI_BGSCAN_ACT_SET; + bgscan_cfg->enable = true; + if (request->min_rssi_thold != NL80211_SCAN_RSSI_THOLD_OFF) { + bgscan_cfg->report_condition |= NXPWIFI_BGSCAN_SSID_RSSI_MATCH; + bgscan_cfg->rssi_threshold = request->min_rssi_thold; + } + + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG, + HOST_ACT_GEN_SET, 0, bgscan_cfg, true); + + if (!ret) + priv->sched_scanning = true; + + kfree(bgscan_cfg); + return ret; +} + +/* CFG802.11 operation handler for sched_scan_stop. + * + * This function issues a bgscan config command to disable + * previous bgscan configuration in the firmware + */ +static int nxpwifi_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev, u64 reqid) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + + wiphy_info(wiphy, "sched scan stop!"); + return nxpwifi_stop_bg_scan(priv); +} + +/* This function sets up the CFG802.11 specific HT capability fields + * with default values. + * + * The following default values are set - + * - HT Supported = True + * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K + * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE + * - HT Capabilities supported by firmware + * - MCS information, Rx mask = 0xff + * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) + */ +static void +nxpwifi_setup_ht_caps(struct nxpwifi_private *priv, + struct ieee80211_sta_ht_cap *ht_info) +{ + int rx_mcs_supp; + struct ieee80211_mcs_info mcs_set; + u8 *mcs = (u8 *)&mcs_set; + struct nxpwifi_adapter *adapter = priv->adapter; + + ht_info->ht_supported = true; + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + /* Fill HT capability information */ + if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; + + if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; + + if (adapter->user_dev_mcs_support == HT_STREAM_2X2) + ht_info->cap |= 2 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + else + ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + + if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + else + ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; + + if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + else + ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD; + + if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + else + ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + + if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + else + ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING; + + ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; + ht_info->cap |= IEEE80211_HT_CAP_SM_PS; + + rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(mcs, 0xff, rx_mcs_supp); + /* Clear all the other values */ + memset(&mcs[rx_mcs_supp], 0, + sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); + if (priv->bss_mode == NL80211_IFTYPE_STATION || + ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ + SETHT_MCS32(mcs_set.rx_mask); + + memcpy((u8 *)&ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); + + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static void +nxpwifi_setup_vht_caps(struct nxpwifi_private *priv, + struct ieee80211_sta_vht_cap *vht_info) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + vht_info->vht_supported = true; + + vht_info->cap = adapter->hw_dot_11ac_dev_cap; + /* Update MCS support for VHT */ + vht_info->vht_mcs.rx_mcs_map = + cpu_to_le16(adapter->hw_dot_11ac_mcs_support & 0xFFFF); + vht_info->vht_mcs.rx_highest = 0; + vht_info->vht_mcs.tx_mcs_map = + cpu_to_le16(adapter->hw_dot_11ac_mcs_support >> 16); + vht_info->vht_mcs.tx_highest = 0; +} + +/* =============== + * 11AX CAP for uAP + * =============== + * Note: bits not mentioned below are set to 0. + * + * 5G + * === + * HE MAC Cap: + * Bit0: 1 (+HTC HE Support) + * Bit25: 1 (OM Control Support. But uAP does not support + * Tx OM received from the STA, as it does not support UL OFDMA) + * + * HE PHY Cap: + * Bit1-7: 0x2 (Supported Channel Width Set. + * Note it would be changed after 80+80 MHz is supported) + * Bit8-11: 0x3 (Punctured Preamble Rx. + * Note: it would be changed after 80+80 MHz is supported) + * Bit12: 0x0 (Device Class) + * Bit13: 0x1 (LDPC coding in Payload) + * Bit17: 0x1 (NDP with 4xHE-LTF+3.2usGI) + * Bit18: 0x1 (STBC Tx <= 80 MHz) + * Bit19: 0x1 (STBC Rx <= 80 MHz) + * Bit20: 0x1 (Doppler Tx) + * Bit21: 0x1 (Doppler Rx) + * Bit24-25: 0x1 (DCM Max Constellation Tx) + * Bit27-28: 0x1 (DCM Max Constellation Rx) + * Bit31: 0x1 (SU Beamformer) + * Bit32: 0x1 (SU BeamFormee) + * Bit34-36: 0x7 (Beamformee STS <= 80 MHz) + * Bit40-42: 0x1 (Number of Sounding Dimentions <= 80 MHz) + * Bit53: 0x1 (Partial Bandwidth Extended Range) + * Bit55: 0x1 (PPE Threshold Present. + * Note: PPE threshold may have some changes later) + * Bit58: 0x1 (HE SU PPDU and HE MU PPDU with 4xHE-LTF+0.8usGI) + * Bit59-61: 0x1 (Max Nc) + * Bit75: 0x1 (Rx 1024-QAM Support < 242-tone RU) + */ + +#define UAP_HE_MAC_CAP0_MASK 0x06 +#define UAP_HE_MAC_CAP1_MASK 0x00 +#define UAP_HE_MAC_CAP2_MASK 0x10 +#define UAP_HE_MAC_CAP3_MASK 0x02 +#define UAP_HE_MAC_CAP4_MASK 0x00 +#define UAP_HE_MAC_CAP5_MASK 0x00 +#define UAP_HE_PHY_CAP0_MASK 0x04 +#define UAP_HE_PHY_CAP1_MASK 0x23 +#define UAP_HE_PHY_CAP2_MASK 0x3E +#define UAP_HE_PHY_CAP3_MASK 0x89 +#define UAP_HE_PHY_CAP4_MASK 0x1D +#define UAP_HE_PHY_CAP5_MASK 0x01 +#define UAP_HE_PHY_CAP6_MASK 0xA0 +#define UAP_HE_PHY_CAP7_MASK 0x0C +#define UAP_HE_PHY_CAP8_MASK 0x00 +#define UAP_HE_PHY_CAP9_MASK 0x08 +#define UAP_HE_PHY_CAP10_MASK 0x00 + +/* 2G + * === + * HE MAC Cap: + * Bit0: 1 (+HTC HE Support) + * Bit25: 1 (OM Control Support. Note: uAP does not support + * Tx OM received from the STA, as it does not support UL OFDMA) + * + * HE PHY Cap: + * Bit1-7: 0x1 (Supported Channel Width Set) + * Bit8-11: 0x0 (Punctured Preamble Rx) + * Bit12: 0x0 (Device Class) + * Bit13: 0x1 (LDPC coding in Payload) + * Bit17: 0x1 (NDP with 4xLTF+3.2usGI) + * Bit18: 0x1 (STBC Tx <= 80 MHz) + * Bit19: 0x1 (STBC Rx <= 80 MHz) + * Bit20: 0x1 (Doppler Tx) + * Bit21: 0x1 (Doppler Rx) + * Bit24-25: 0x1 (DCM Max Constellation Tx) + * Bit27-28: 0x1 (DCM Max Constellation Rx) + * Bit31: 0x1 (SU Beamformer) + * Bit32: 0x1 (SU BeamFormee) + * Bit34-36: 0x7 (Beamformee STS <= 80 MHz) + * Bit40-42: 0x1 (Number of Sounding Dimentions <= 80 MHz) + * Bit53: 0x1 (Partial Bandwidth Extended Range) + * Bit55: 0x1 (PPE Threshold Present. + * Note: PPE threshold may have some changes later) + * Bit58: 0x1 (HE SU PPDU and HE MU PPDU with 4xHE-LTF+0.8usGI) + * Bit59-61: 0x1 (Max Nc) + * Bit75: 0x1 (Rx 1024-QAM Support < 242-tone RU) + */ +#define UAP_HE_2G_MAC_CAP0_MASK 0x00 +#define UAP_HE_2G_MAC_CAP1_MASK 0x00 +#define UAP_HE_2G_MAC_CAP2_MASK 0x00 +#define UAP_HE_2G_MAC_CAP3_MASK 0x02 +#define UAP_HE_2G_MAC_CAP4_MASK 0x00 +#define UAP_HE_2G_MAC_CAP5_MASK 0x00 +#define UAP_HE_2G_PHY_CAP0_MASK 0x02 +#define UAP_HE_2G_PHY_CAP1_MASK 0x20 +#define UAP_HE_2G_PHY_CAP2_MASK 0x3E +#define UAP_HE_2G_PHY_CAP3_MASK 0x89 +#define UAP_HE_2G_PHY_CAP4_MASK 0x1D +#define UAP_HE_2G_PHY_CAP5_MASK 0x01 +#define UAP_HE_2G_PHY_CAP6_MASK 0xA0 +#define UAP_HE_2G_PHY_CAP7_MASK 0x0C +#define UAP_HE_2G_PHY_CAP8_MASK 0x00 +#define UAP_HE_2G_PHY_CAP9_MASK 0x08 +#define UAP_HE_2G_PHY_CAP10_MASK 0x00 + +#define HE_CAP_FIX_SIZE 22 + +static void +nxpwifi_update_11ax_ie(u8 band, + struct nxpwifi_11ax_he_cap_cfg *he_cap_cfg) +{ + if (band == BAND_A) { + he_cap_cfg->cap_elem.mac_cap_info[0] &= UAP_HE_MAC_CAP0_MASK; + he_cap_cfg->cap_elem.mac_cap_info[1] &= UAP_HE_MAC_CAP1_MASK; + he_cap_cfg->cap_elem.mac_cap_info[2] &= UAP_HE_MAC_CAP2_MASK; + he_cap_cfg->cap_elem.mac_cap_info[3] &= UAP_HE_MAC_CAP3_MASK; + he_cap_cfg->cap_elem.mac_cap_info[4] &= UAP_HE_MAC_CAP4_MASK; + he_cap_cfg->cap_elem.mac_cap_info[5] &= UAP_HE_MAC_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[0] &= UAP_HE_PHY_CAP0_MASK; + he_cap_cfg->cap_elem.phy_cap_info[1] &= UAP_HE_PHY_CAP1_MASK; + he_cap_cfg->cap_elem.phy_cap_info[2] &= UAP_HE_PHY_CAP2_MASK; + he_cap_cfg->cap_elem.phy_cap_info[3] &= UAP_HE_PHY_CAP3_MASK; + he_cap_cfg->cap_elem.phy_cap_info[4] &= UAP_HE_PHY_CAP4_MASK; + he_cap_cfg->cap_elem.phy_cap_info[5] &= UAP_HE_PHY_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[6] &= UAP_HE_PHY_CAP6_MASK; + he_cap_cfg->cap_elem.phy_cap_info[7] &= UAP_HE_PHY_CAP7_MASK; + he_cap_cfg->cap_elem.phy_cap_info[8] &= UAP_HE_PHY_CAP8_MASK; + he_cap_cfg->cap_elem.phy_cap_info[9] &= UAP_HE_PHY_CAP9_MASK; + he_cap_cfg->cap_elem.phy_cap_info[10] &= UAP_HE_PHY_CAP10_MASK; + } else { + he_cap_cfg->cap_elem.mac_cap_info[0] &= UAP_HE_2G_MAC_CAP0_MASK; + he_cap_cfg->cap_elem.mac_cap_info[1] &= UAP_HE_2G_MAC_CAP1_MASK; + he_cap_cfg->cap_elem.mac_cap_info[2] &= UAP_HE_2G_MAC_CAP2_MASK; + he_cap_cfg->cap_elem.mac_cap_info[3] &= UAP_HE_2G_MAC_CAP3_MASK; + he_cap_cfg->cap_elem.mac_cap_info[4] &= UAP_HE_2G_MAC_CAP4_MASK; + he_cap_cfg->cap_elem.mac_cap_info[5] &= UAP_HE_2G_MAC_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[0] &= UAP_HE_2G_PHY_CAP0_MASK; + he_cap_cfg->cap_elem.phy_cap_info[1] &= UAP_HE_2G_PHY_CAP1_MASK; + he_cap_cfg->cap_elem.phy_cap_info[2] &= UAP_HE_2G_PHY_CAP2_MASK; + he_cap_cfg->cap_elem.phy_cap_info[3] &= UAP_HE_2G_PHY_CAP3_MASK; + he_cap_cfg->cap_elem.phy_cap_info[4] &= UAP_HE_2G_PHY_CAP4_MASK; + he_cap_cfg->cap_elem.phy_cap_info[5] &= UAP_HE_2G_PHY_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[6] &= UAP_HE_2G_PHY_CAP6_MASK; + he_cap_cfg->cap_elem.phy_cap_info[7] &= UAP_HE_2G_PHY_CAP7_MASK; + he_cap_cfg->cap_elem.phy_cap_info[8] &= UAP_HE_2G_PHY_CAP8_MASK; + he_cap_cfg->cap_elem.phy_cap_info[9] &= UAP_HE_2G_PHY_CAP9_MASK; + he_cap_cfg->cap_elem.phy_cap_info[10] &= UAP_HE_2G_PHY_CAP10_MASK; + } +} + +static void +nxpwifi_setup_he_caps(struct nxpwifi_private *priv, + struct ieee80211_supported_band *band) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct ieee80211_sband_iftype_data *iftype_data; + struct nxpwifi_11ax_he_cap_cfg he_cap_cfg; + u8 hw_he_cap_len; + u8 extra_mcs_size; + int ppe_threshold_len; + + if (band->band == NL80211_BAND_5GHZ) { + hw_he_cap_len = adapter->hw_he_cap_len; + memcpy(&he_cap_cfg, adapter->hw_he_cap, hw_he_cap_len); + nxpwifi_update_11ax_ie(BAND_A, &he_cap_cfg); + } else { + hw_he_cap_len = adapter->hw_2g_he_cap_len; + memcpy(&he_cap_cfg, adapter->hw_2g_he_cap, hw_he_cap_len); + nxpwifi_update_11ax_ie(BAND_G, &he_cap_cfg); + } + + if (!hw_he_cap_len) + return; + + iftype_data = kmalloc(sizeof(*iftype_data), GFP_KERNEL); + if (!iftype_data) + return; + memset(iftype_data, 0, sizeof(*iftype_data)); + + iftype_data->types_mask = + BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + iftype_data->he_cap.has_he = true; + + memcpy(iftype_data->he_cap.he_cap_elem.mac_cap_info, + he_cap_cfg.cap_elem.mac_cap_info, + sizeof(he_cap_cfg.cap_elem.mac_cap_info)); + memcpy(iftype_data->he_cap.he_cap_elem.phy_cap_info, + he_cap_cfg.cap_elem.phy_cap_info, + sizeof(he_cap_cfg.cap_elem.phy_cap_info)); + memset(&iftype_data->he_cap.he_mcs_nss_supp, + 0xff, + sizeof(iftype_data->he_cap.he_mcs_nss_supp)); + memcpy(&iftype_data->he_cap.he_mcs_nss_supp, + he_cap_cfg.he_txrx_mcs_support, + sizeof(he_cap_cfg.he_txrx_mcs_support)); + + extra_mcs_size = 0; + /* Support 160Mhz */ + if (he_cap_cfg.cap_elem.phy_cap_info[0] & BIT(3)) + extra_mcs_size += 4; + /* Support 80+80 */ + if (he_cap_cfg.cap_elem.phy_cap_info[0] & BIT(4)) + extra_mcs_size += 4; + if (extra_mcs_size) + memcpy((u8 *)&iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_160, + he_cap_cfg.val, extra_mcs_size); + + /* Support PPE threshold */ + ppe_threshold_len = he_cap_cfg.len - HE_CAP_FIX_SIZE - extra_mcs_size; + if (he_cap_cfg.cap_elem.phy_cap_info[6] & BIT(7) && ppe_threshold_len) { + memcpy(iftype_data->he_cap.ppe_thres, + &he_cap_cfg.val[extra_mcs_size], + ppe_threshold_len); + } else { + iftype_data->he_cap.he_cap_elem.phy_cap_info[6] &= BIT(7); + } + + band->n_iftype_data = 1; + band->iftype_data = iftype_data; +} + +/* create a new virtual interface with the given name and name assign type + */ +struct wireless_dev *nxpwifi_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + struct vif_params *params) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct net_device *dev; + void *mdev_priv; + int ret; + + if (!adapter) + return ERR_PTR(-EFAULT); + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + if (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf) { + nxpwifi_dbg(adapter, ERROR, + "cannot create multiple sta ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = nxpwifi_get_unused_priv_by_bss_type + (adapter, NXPWIFI_BSS_TYPE_STA); + if (!priv) { + nxpwifi_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_STATION; + + if (type == NL80211_IFTYPE_UNSPECIFIED) + priv->bss_mode = NL80211_IFTYPE_STATION; + else + priv->bss_mode = type; + + priv->bss_type = NXPWIFI_BSS_TYPE_STA; + priv->frame_type = NXPWIFI_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = NXPWIFI_BSS_ROLE_STA; + + break; + case NL80211_IFTYPE_AP: + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + nxpwifi_dbg(adapter, ERROR, + "cannot create multiple AP ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = nxpwifi_get_unused_priv_by_bss_type + (adapter, NXPWIFI_BSS_TYPE_UAP); + if (!priv) { + nxpwifi_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_AP; + + priv->bss_type = NXPWIFI_BSS_TYPE_UAP; + priv->frame_type = NXPWIFI_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = NXPWIFI_BSS_ROLE_UAP; + priv->bss_started = 0; + priv->bss_mode = type; + + break; + default: + nxpwifi_dbg(adapter, ERROR, "type not supported\n"); + return ERR_PTR(-EINVAL); + } + + dev = alloc_netdev_mqs(sizeof(struct nxpwifi_private *), name, + name_assign_type, ether_setup, + IEEE80211_NUM_ACS, 1); + if (!dev) { + nxpwifi_dbg(adapter, ERROR, + "no memory available for netdevice\n"); + ret = -ENOMEM; + goto err_alloc_netdev; + } + + nxpwifi_init_priv_params(priv, dev); + + priv->netdev = dev; + + nxpwifi_set_mac_address(priv, dev, false, NULL); + + ret = nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) + goto err_set_bss_mode; + + ret = nxpwifi_sta_init_cmd(priv, false, false); + if (ret) + goto err_sta_init; + + dev_net_set(dev, wiphy_net(wiphy)); + dev->ieee80211_ptr = &priv->wdev; + dev->ieee80211_ptr->iftype = priv->bss_mode; + SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); + + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo = NXPWIFI_DEFAULT_WATCHDOG_TIMEOUT; + dev->needed_headroom = NXPWIFI_MIN_DATA_HEADER_LEN; + dev->ethtool_ops = &nxpwifi_ethtool_ops; + + mdev_priv = netdev_priv(dev); + *((unsigned long *)mdev_priv) = (unsigned long)priv; + + SET_NETDEV_DEV(dev, adapter->dev); + + INIT_DELAYED_WORK(&priv->dfs_cac_work, nxpwifi_dfs_cac_work); + + INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, nxpwifi_dfs_chan_sw_work); + + /* Register network device */ + if (cfg80211_register_netdevice(dev)) { + nxpwifi_dbg(adapter, ERROR, "cannot register network device\n"); + ret = -EFAULT; + goto err_reg_netdev; + } + + nxpwifi_dbg(adapter, INFO, + "info: %s: NXP 802.11 Adapter\n", dev->name); + +#ifdef CONFIG_DEBUG_FS + nxpwifi_dev_debugfs_init(priv); +#endif + + update_vif_type_counter(adapter, type, 1); + + return &priv->wdev; + +err_reg_netdev: + free_netdev(dev); + priv->netdev = NULL; +err_sta_init: +err_set_bss_mode: +err_alloc_netdev: + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(nxpwifi_add_virtual_intf); + +/* del_virtual_intf: remove the virtual interface determined by dev + */ +int nxpwifi_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + struct nxpwifi_adapter *adapter = priv->adapter; + struct sk_buff *skb, *tmp; + +#ifdef CONFIG_DEBUG_FS + nxpwifi_dev_debugfs_remove(priv); +#endif + + if (priv->sched_scanning) + priv->sched_scanning = false; + + nxpwifi_stop_net_dev_queue(priv->netdev, adapter); + + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) { + skb_unlink(skb, &priv->bypass_txq); + nxpwifi_write_data_complete(priv->adapter, skb, 0, -1); + } + + netif_carrier_off(priv->netdev); + + if (wdev->netdev->reg_state == NETREG_REGISTERED) + cfg80211_unregister_netdevice(wdev->netdev); + + /* Clear the priv in adapter */ + priv->netdev = NULL; + + update_vif_type_counter(adapter, priv->bss_mode, -1); + + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) + kfree(priv->hist_data); + + return 0; +} +EXPORT_SYMBOL_GPL(nxpwifi_del_virtual_intf); + +static bool +nxpwifi_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, + u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + memcpy(byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + /* wildcard bytes record as the offset + * before the valid byte + */ + if (!valid_byte_cnt && !dont_care_byte) + pat->pkt_offset++; + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +#ifdef CONFIG_PM +static void nxpwifi_set_auto_arp_mef_entry(struct nxpwifi_private *priv, + struct nxpwifi_mef_entry *mef_entry) +{ + int i, filt_num = 0, num_ipv4 = 0; + struct in_device *in_dev; + struct in_ifaddr *ifa; + __be32 ips[NXPWIFI_MAX_SUPPORTED_IPADDR]; + struct nxpwifi_adapter *adapter = priv->adapter; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_AUTO_ARP; + + /* Enable ARP offload feature */ + memset(ips, 0, sizeof(ips)); + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]->netdev) { + in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); + if (!in_dev) + continue; + ifa = rtnl_dereference(in_dev->ifa_list); + if (!ifa || !ifa->ifa_local) + continue; + ips[i] = ifa->ifa_local; + num_ipv4++; + } + } + + for (i = 0; i < num_ipv4; i++) { + if (!ips[i]) + continue; + mef_entry->filter[filt_num].repeat = 1; + memcpy(mef_entry->filter[filt_num].byte_seq, + (u8 *)&ips[i], sizeof(ips[i])); + mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] = + sizeof(ips[i]); + mef_entry->filter[filt_num].offset = 46; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) { + mef_entry->filter[filt_num].filt_action = + TYPE_OR; + } + filt_num++; + } + + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].byte_seq[0] = 0x08; + mef_entry->filter[filt_num].byte_seq[1] = 0x06; + mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] = 2; + mef_entry->filter[filt_num].offset = 20; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_AND; +} + +static int nxpwifi_set_wowlan_mef_entry(struct nxpwifi_private *priv, + struct nxpwifi_ds_mef_cfg *mef_cfg, + struct nxpwifi_mef_entry *mef_entry, + struct cfg80211_wowlan *wowlan) +{ + int i, filt_num = 0, ret = 0; + bool first_pat = true; + u8 byte_seq[NXPWIFI_MEF_MAX_BYTESEQ + 1]; + static const u8 ipv4_mc_mac[] = {0x33, 0x33}; + static const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; + + for (i = 0; i < wowlan->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!nxpwifi_is_pattern_supported + (&wowlan->patterns[i], byte_seq, + NXPWIFI_MEF_MAX_BYTESEQ)) { + nxpwifi_dbg(priv->adapter, ERROR, + "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!wowlan->patterns[i].pkt_offset) { + if (is_unicast_ether_addr(byte_seq) && + byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] == 1) { + mef_cfg->criteria |= NXPWIFI_CRITERIA_UNICAST; + continue; + } else if (is_broadcast_ether_addr(byte_seq)) { + mef_cfg->criteria |= NXPWIFI_CRITERIA_BROADCAST; + continue; + } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + (byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] == 2)) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + (byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] == 3))) { + mef_cfg->criteria |= NXPWIFI_CRITERIA_MULTICAST; + continue; + } + } + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].offset = + wowlan->patterns[i].pkt_offset; + memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, + sizeof(byte_seq)); + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + + if (first_pat) { + first_pat = false; + nxpwifi_dbg(priv->adapter, INFO, "Wake on patterns\n"); + } else { + mef_entry->filter[filt_num].filt_action = TYPE_AND; + } + + filt_num++; + } + + if (wowlan->magic_pkt) { + mef_cfg->criteria |= NXPWIFI_CRITERIA_UNICAST; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 28; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) + mef_entry->filter[filt_num].filt_action = TYPE_OR; + + filt_num++; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 56; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_OR; + nxpwifi_dbg(priv->adapter, INFO, "Wake on magic packet\n"); + } + return ret; +} + +static int nxpwifi_set_mef_filter(struct nxpwifi_private *priv, + struct cfg80211_wowlan *wowlan) +{ + int ret = 0, num_entries = 1; + struct nxpwifi_ds_mef_cfg mef_cfg; + struct nxpwifi_mef_entry *mef_entry; + + if (wowlan->n_patterns || wowlan->magic_pkt) + num_entries++; + + mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); + if (!mef_entry) + return -ENOMEM; + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.criteria |= NXPWIFI_CRITERIA_BROADCAST | + NXPWIFI_CRITERIA_UNICAST; + mef_cfg.num_entries = num_entries; + mef_cfg.mef_entry = mef_entry; + + nxpwifi_set_auto_arp_mef_entry(priv, &mef_entry[0]); + + if (wowlan->n_patterns || wowlan->magic_pkt) { + ret = nxpwifi_set_wowlan_mef_entry(priv, &mef_cfg, + &mef_entry[1], wowlan); + if (ret) + goto done; + } + + if (!mef_cfg.criteria) + mef_cfg.criteria = NXPWIFI_CRITERIA_BROADCAST | + NXPWIFI_CRITERIA_UNICAST | + NXPWIFI_CRITERIA_MULTICAST; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_MEF_CFG, HOST_ACT_GEN_SET, 0, + &mef_cfg, true); + +done: + kfree(mef_entry); + return ret; +} + +static int nxpwifi_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_ds_hs_cfg hs_cfg; + int i, ret = 0, retry_num = 10; + struct nxpwifi_private *priv; + struct nxpwifi_private *sta_priv = + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + + sta_priv->scan_aborting = true; + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + nxpwifi_abort_cac(priv); + } + + nxpwifi_cancel_all_pending_cmd(adapter); + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv->netdev) + netif_device_detach(priv->netdev); + } + + for (i = 0; i < retry_num; i++) { + if (!nxpwifi_wmm_lists_empty(adapter) || + !nxpwifi_bypass_txlist_empty(adapter) || + !skb_queue_empty(&adapter->tx_data_q)) + usleep_range(10000, 15000); + else + break; + } + + if (!wowlan) { + nxpwifi_dbg(adapter, INFO, + "None of the WOWLAN triggers enabled\n"); + ret = 0; + goto done; + } + + if (!sta_priv->media_connected && !wowlan->nd_config) { + nxpwifi_dbg(adapter, ERROR, + "Can not configure WOWLAN in disconnected state\n"); + ret = 0; + goto done; + } + + ret = nxpwifi_set_mef_filter(sta_priv, wowlan); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "Failed to set MEF filter\n"); + goto done; + } + + memset(&hs_cfg, 0, sizeof(hs_cfg)); + hs_cfg.conditions = le32_to_cpu(adapter->hs_cfg.conditions); + + if (wowlan->nd_config) { + nxpwifi_dbg(adapter, INFO, "Wake on net detect\n"); + hs_cfg.conditions |= HS_CFG_COND_MAC_EVENT; + nxpwifi_cfg80211_sched_scan_start(wiphy, sta_priv->netdev, + wowlan->nd_config); + } + + if (wowlan->disconnect) { + hs_cfg.conditions |= HS_CFG_COND_MAC_EVENT; + nxpwifi_dbg(sta_priv->adapter, INFO, "Wake on device disconnect\n"); + } + + hs_cfg.is_invoke_hostcmd = false; + hs_cfg.gpio = adapter->hs_cfg.gpio; + hs_cfg.gap = adapter->hs_cfg.gap; + ret = nxpwifi_set_hs_params(sta_priv, HOST_ACT_GEN_SET, + NXPWIFI_SYNC_CMD, &hs_cfg); + if (ret) + nxpwifi_dbg(adapter, ERROR, "Failed to set HS params\n"); + +done: + sta_priv->scan_aborting = false; + return ret; +} + +static int nxpwifi_cfg80211_resume(struct wiphy *wiphy) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_ds_wakeup_reason wakeup_reason; + struct cfg80211_wowlan_wakeup wakeup_report; + int i; + bool report_wakeup_reason = true; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv->netdev) + netif_device_attach(priv->netdev); + } + + if (!wiphy->wowlan_config) + goto done; + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + nxpwifi_get_wakeup_reason(priv, HOST_ACT_GEN_GET, NXPWIFI_SYNC_CMD, + &wakeup_reason); + memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup)); + + wakeup_report.pattern_idx = -1; + + switch (wakeup_reason.hs_wakeup_reason) { + case NO_HSWAKEUP_REASON: + break; + case BCAST_DATA_MATCHED: + break; + case MCAST_DATA_MATCHED: + break; + case UCAST_DATA_MATCHED: + break; + case MASKTABLE_EVENT_MATCHED: + break; + case NON_MASKABLE_EVENT_MATCHED: + if (wiphy->wowlan_config->disconnect) + wakeup_report.disconnect = true; + if (wiphy->wowlan_config->nd_config) + wakeup_report.net_detect = adapter->nd_info; + break; + case NON_MASKABLE_CONDITION_MATCHED: + break; + case MAGIC_PATTERN_MATCHED: + if (wiphy->wowlan_config->magic_pkt) + wakeup_report.magic_pkt = true; + if (wiphy->wowlan_config->n_patterns) + wakeup_report.pattern_idx = 1; + break; + case GTK_REKEY_FAILURE: + if (wiphy->wowlan_config->gtk_rekey_failure) + wakeup_report.gtk_rekey_failure = true; + break; + default: + report_wakeup_reason = false; + break; + } + + if (report_wakeup_reason) + cfg80211_report_wowlan_wakeup(&priv->wdev, &wakeup_report, + GFP_KERNEL); + +done: + if (adapter->nd_info) { + for (i = 0 ; i < adapter->nd_info->n_matches ; i++) + kfree(adapter->nd_info->matches[i]); + kfree(adapter->nd_info); + adapter->nd_info = NULL; + } + + return 0; +} + +static void nxpwifi_cfg80211_set_wakeup(struct wiphy *wiphy, + bool enabled) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + + device_set_wakeup_enable(adapter->dev, enabled); +} +#endif + +static int nxpwifi_get_coalesce_pkt_type(u8 *byte_seq) +{ + static const u8 ipv4_mc_mac[] = {0x33, 0x33}; + static const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; + + if ((byte_seq[0] & 0x01) && + byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] == 1) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int +nxpwifi_fill_coalesce_rule_info(struct nxpwifi_private *priv, + struct cfg80211_coalesce_rules *crule, + struct nxpwifi_coalesce_rule *mrule) +{ + u8 byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!nxpwifi_is_pattern_supported(&crule->patterns[i], + byte_seq, + NXPWIFI_COALESCE_MAX_BYTESEQ)) { + nxpwifi_dbg(priv->adapter, ERROR, + "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = nxpwifi_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + nxpwifi_dbg(priv->adapter, ERROR, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + nxpwifi_dbg(priv->adapter, ERROR, + "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int nxpwifi_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy); + int i, ret; + struct nxpwifi_ds_coalesce_cfg coalesce_cfg; + struct nxpwifi_private *priv = + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + nxpwifi_dbg(adapter, WARN, + "Disable coalesce and reset all previous rules\n"); + return nxpwifi_send_cmd(priv, HOST_CMD_COALESCE_CFG, + HOST_ACT_GEN_SET, 0, + &coalesce_cfg, true); + } + + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = nxpwifi_fill_coalesce_rule_info(priv, &coalesce->rules[i], + &coalesce_cfg.rule[i]); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + + return nxpwifi_send_cmd(priv, HOST_CMD_COALESCE_CFG, + HOST_ACT_GEN_SET, 0, &coalesce_cfg, true); +} + +static int +nxpwifi_cfg80211_uap_add_station(struct nxpwifi_private *priv, const u8 *mac, + struct station_parameters *params) +{ + struct nxpwifi_sta_info add_sta; + int ret; + + memcpy(add_sta.peer_mac, mac, ETH_ALEN); + add_sta.params = params; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_ADD_NEW_STATION, + HOST_ACT_ADD_STA, 0, (void *)&add_sta, true); + + if (!ret) { + struct station_info *sinfo = NULL; + + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + + if (sinfo) { + cfg80211_new_sta(priv->netdev, mac, sinfo, GFP_KERNEL); + kfree(sinfo); + } + } + + return ret; +} + +static int +nxpwifi_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + int ret = -EOPNOTSUPP; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) + ret = nxpwifi_cfg80211_uap_add_station(priv, mac, params); + + return ret; +} + +static int +nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + int chsw_msec; + int ret; + + if (priv->adapter->scan_processing) { + nxpwifi_dbg(priv->adapter, ERROR, + "radar detection: scan in process...\n"); + return -EBUSY; + } + + if (priv->wdev.links[0].cac_started) + return -EBUSY; + + if (cfg80211_chandef_identical(¶ms->chandef, + &priv->dfs_chandef)) + return -EINVAL; + + if (params->block_tx) { + netif_carrier_off(priv->netdev); + nxpwifi_stop_net_dev_queue(priv->netdev, priv->adapter); + priv->uap_stop_tx = true; + } + + ret = nxpwifi_del_mgmt_ies(priv); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + ret = nxpwifi_set_mgmt_ies(priv, ¶ms->beacon_csa); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "%s: setting mgmt ies failed\n", __func__); + goto done; + } + + memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); + memcpy(&priv->beacon_after, ¶ms->beacon_after, + sizeof(priv->beacon_after)); + + chsw_msec = max(params->count * priv->bss_cfg.beacon_period, 100); + nxpwifi_queue_delayed_work(priv->adapter, &priv->dfs_chan_sw_work, + msecs_to_jiffies(chsw_msec)); + +done: + return ret; +} + +static int nxpwifi_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + unsigned int link_id, + struct cfg80211_chan_def *chandef) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + struct nxpwifi_bssdescriptor *curr_bss; + struct ieee80211_channel *chan; + enum nl80211_channel_type chan_type; + enum nl80211_band band; + int freq; + int ret = -ENODATA; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP && + cfg80211_chandef_valid(&priv->bss_chandef)) { + *chandef = priv->bss_chandef; + ret = 0; + } else if (priv->media_connected) { + curr_bss = &priv->curr_bss_params.bss_descriptor; + band = nxpwifi_band_to_radio_type(priv->curr_bss_params.band); + freq = ieee80211_channel_to_frequency(curr_bss->channel, band); + chan = ieee80211_get_channel(wiphy, freq); + + if (priv->ht_param_present) { + chan_type = nxpwifi_get_chan_type(priv); + cfg80211_chandef_create(chandef, chan, chan_type); + } else { + cfg80211_chandef_create(chandef, chan, + NL80211_CHAN_NO_HT); + } + ret = 0; + } + + return ret; +} + +#ifdef CONFIG_NL80211_TESTMODE + +enum nxpwifi_tm_attr { + __NXPWIFI_TM_ATTR_INVALID = 0, + NXPWIFI_TM_ATTR_CMD = 1, + NXPWIFI_TM_ATTR_DATA = 2, + + /* keep last */ + __NXPWIFI_TM_ATTR_AFTER_LAST, + NXPWIFI_TM_ATTR_MAX = __NXPWIFI_TM_ATTR_AFTER_LAST - 1, +}; + +static const struct nla_policy nxpwifi_tm_policy[NXPWIFI_TM_ATTR_MAX + 1] = { + [NXPWIFI_TM_ATTR_CMD] = { .type = NLA_U32 }, + [NXPWIFI_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = NXPWIFI_SIZE_OF_CMD_BUFFER }, +}; + +enum nxpwifi_tm_command { + NXPWIFI_TM_CMD_HOSTCMD = 0, +}; + +static int nxpwifi_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev); + struct nxpwifi_ds_misc_cmd *hostcmd; + struct nlattr *tb[NXPWIFI_TM_ATTR_MAX + 1]; + struct sk_buff *skb; + int err; + + if (!priv) + return -EINVAL; + + err = nla_parse_deprecated(tb, NXPWIFI_TM_ATTR_MAX, data, len, + nxpwifi_tm_policy, NULL); + if (err) + return err; + + if (!tb[NXPWIFI_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[NXPWIFI_TM_ATTR_CMD])) { + case NXPWIFI_TM_CMD_HOSTCMD: + if (!tb[NXPWIFI_TM_ATTR_DATA]) + return -EINVAL; + + hostcmd = kzalloc(sizeof(*hostcmd), GFP_KERNEL); + if (!hostcmd) + return -ENOMEM; + + hostcmd->len = nla_len(tb[NXPWIFI_TM_ATTR_DATA]); + memcpy(hostcmd->cmd, nla_data(tb[NXPWIFI_TM_ATTR_DATA]), + hostcmd->len); + + if (nxpwifi_send_cmd(priv, 0, 0, 0, hostcmd, true)) { + dev_err(priv->adapter->dev, "Failed to process hostcmd\n"); + kfree(hostcmd); + return -EFAULT; + } + + /* process hostcmd response*/ + skb = cfg80211_testmode_alloc_reply_skb(wiphy, hostcmd->len); + if (!skb) { + kfree(hostcmd); + return -ENOMEM; + } + err = nla_put(skb, NXPWIFI_TM_ATTR_DATA, + hostcmd->len, hostcmd->cmd); + if (err) { + kfree(hostcmd); + kfree_skb(skb); + return -EMSGSIZE; + } + + err = cfg80211_testmode_reply(skb); + kfree(hostcmd); + return err; + default: + return -EOPNOTSUPP; + } +} +#endif + +static int +nxpwifi_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms, int link_id) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_radar_params radar_params; + int ret; + + if (priv->adapter->scan_processing) { + nxpwifi_dbg(priv->adapter, ERROR, + "radar detection: scan already in process...\n"); + return -EBUSY; + } + + if (!nxpwifi_is_11h_active(priv)) { + nxpwifi_dbg(priv->adapter, INFO, + "Enable 11h extensions in FW\n"); + if (nxpwifi_11h_activate(priv, true)) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to activate 11h extensions!!"); + return -EPERM; + } + priv->state_11h.is_11h_active = true; + } + + memset(&radar_params, 0, sizeof(struct nxpwifi_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = cac_time_ms; + + memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); + + ret = nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST, + HOST_ACT_GEN_SET, 0, &radar_params, true); + if (!ret) + nxpwifi_queue_delayed_work(priv->adapter, + &priv->dfs_cac_work, + msecs_to_jiffies(cac_time_ms)); + + return ret; +} + +static int +nxpwifi_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) +{ + return 0; +} + +static int +nxpwifi_cfg80211_authenticate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_auth_request *req) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter = priv->adapter; + struct sk_buff *skb; + u16 pkt_len, auth_alg; + int ret; + struct nxpwifi_ieee80211_mgmt *mgmt; + struct nxpwifi_txinfo *tx_info; + u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; + u8 trans = 1, status_code = 0; + u8 *varptr = NULL; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + nxpwifi_dbg(adapter, ERROR, "Interface role is AP\n"); + return -EINVAL; + } + + if (priv->wdev.iftype != NL80211_IFTYPE_STATION) { + nxpwifi_dbg(adapter, ERROR, + "Interface type is not correct (type %d)\n", + priv->wdev.iftype); + return -EINVAL; + } + + if (!nxpwifi_is_channel_setting_allowable(priv, req->bss->channel)) + return -EOPNOTSUPP; + + if (priv->auth_alg != WLAN_AUTH_SAE && + (priv->auth_flag & HOST_MLME_AUTH_PENDING)) { + nxpwifi_dbg(adapter, ERROR, "Pending auth on going\n"); + return -EBUSY; + } + + if (!priv->host_mlme_reg) { + priv->host_mlme_reg = true; + priv->mgmt_frame_mask |= HOST_MLME_MGMT_MASK; + nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG, + HOST_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + } + + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + auth_alg = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + auth_alg = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + auth_alg = WLAN_AUTH_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + auth_alg = WLAN_AUTH_LEAP; + break; + case NL80211_AUTHTYPE_SAE: + auth_alg = WLAN_AUTH_SAE; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "unsupported auth type=%d\n", req->auth_type); + return -EOPNOTSUPP; + } + + if (!priv->auth_flag) { + ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_SET, + req->bss->channel, + AUTH_TX_DEFAULT_WAIT_TIME); + + if (!ret) { + priv->roc_cfg.cookie = + nxpwifi_roc_cookie(adapter); + priv->roc_cfg.chan = *req->bss->channel; + } else { + return -EPERM; + } + } + + priv->sec_info.authentication_mode = auth_alg; + + nxpwifi_cancel_scan(adapter); + + pkt_len = (u16)req->ie_len + req->auth_data_len + + NXPWIFI_MGMT_HEADER_LEN + NXPWIFI_AUTH_BODY_LEN; + if (req->auth_data_len >= 4) + pkt_len -= 4; + + skb = dev_alloc_skb(NXPWIFI_MIN_DATA_HEADER_LEN + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + + pkt_len + sizeof(pkt_len)); + if (!skb) { + nxpwifi_dbg(adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + tx_info = NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = pkt_len; + + skb_reserve(skb, NXPWIFI_MIN_DATA_HEADER_LEN + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); + memcpy(skb_push(skb, sizeof(tx_control)), + &tx_control, sizeof(tx_control)); + memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); + + mgmt = (struct nxpwifi_ieee80211_mgmt *)skb_put(skb, pkt_len); + memset(mgmt, 0, pkt_len); + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); + memcpy(mgmt->da, req->bss->bssid, ETH_ALEN); + memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->bssid, req->bss->bssid, ETH_ALEN); + eth_broadcast_addr(mgmt->addr4); + + if (req->auth_data_len >= 4) { + if (req->auth_type == NL80211_AUTHTYPE_SAE) { + __le16 *pos = (__le16 *)req->auth_data; + + trans = le16_to_cpu(pos[0]); + status_code = le16_to_cpu(pos[1]); + } + memcpy((u8 *)(&mgmt->auth.variable), req->auth_data + 4, + req->auth_data_len - 4); + varptr = (u8 *)&mgmt->auth.variable + + (req->auth_data_len - 4); + } + + mgmt->auth.auth_alg = cpu_to_le16(auth_alg); + mgmt->auth.auth_transaction = cpu_to_le16(trans); + mgmt->auth.status_code = cpu_to_le16(status_code); + + if (req->ie && req->ie_len) { + if (!varptr) + varptr = (u8 *)&mgmt->auth.variable; + memcpy((u8 *)varptr, req->ie, req->ie_len); + } + + priv->auth_flag = HOST_MLME_AUTH_PENDING; + priv->auth_alg = auth_alg; + + skb->priority = WMM_HIGHEST_PRIORITY; + __net_timestamp(skb); + + nxpwifi_dbg(adapter, MSG, + "auth: send authentication to %pM\n", req->bss->bssid); + + nxpwifi_queue_tx_pkt(priv, skb); + + return 0; +} + +static int +nxpwifi_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + struct cfg80211_ssid req_ssid; + const u8 *ssid_ie; + + if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA) { + nxpwifi_dbg(adapter, ERROR, + "%s: reject infra assoc request in non-STA role\n", + dev->name); + return -EINVAL; + } + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) || + test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) { + nxpwifi_dbg(adapter, ERROR, + "%s: Ignore association.\t" + "Card removed or FW in bad state\n", + dev->name); + return -EPERM; + } + + if (priv->auth_alg == WLAN_AUTH_SAE) + priv->auth_flag = HOST_MLME_AUTH_DONE; + + if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE)) + return -EBUSY; + + if (priv->roc_cfg.cookie) { + ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_REMOVE, + &priv->roc_cfg.chan, 0); + if (!ret) + memset(&priv->roc_cfg, 0, + sizeof(struct nxpwifi_roc_cfg)); + else + return ret; + } + + if (!nxpwifi_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); + + memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); + rcu_read_lock(); + ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + + if (!ssid_ie) + goto ssid_err; + + req_ssid.ssid_len = ssid_ie[1]; + if (req_ssid.ssid_len > IEEE80211_MAX_SSID_LEN) { + nxpwifi_dbg(adapter, ERROR, "invalid SSID - aborting\n"); + goto ssid_err; + } + + memcpy(req_ssid.ssid, ssid_ie + 2, req_ssid.ssid_len); + if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { + nxpwifi_dbg(adapter, ERROR, "invalid SSID - aborting\n"); + goto ssid_err; + } + rcu_read_unlock(); + + /* As this is new association, clear locally stored + * keys and security related flags + */ + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + priv->wep_key_curr_index = 0; + priv->sec_info.encryption_mode = 0; + priv->sec_info.is_authtype_auto = 0; + ret = nxpwifi_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); + + if (req->crypto.n_ciphers_pairwise) + priv->sec_info.encryption_mode = + req->crypto.ciphers_pairwise[0]; + + if (req->crypto.cipher_group) + priv->sec_info.encryption_mode = req->crypto.cipher_group; + + if (req->ie) + ret = nxpwifi_set_gen_ie(priv, req->ie, req->ie_len); + + memcpy(priv->cfg_bssid, req->bss->bssid, ETH_ALEN); + + nxpwifi_dbg(adapter, MSG, + "assoc: send association to %pM\n", req->bss->bssid); + + cfg80211_ref_bss(adapter->wiphy, req->bss); + + ret = nxpwifi_bss_start(priv, req->bss, &req_ssid); + + if (ret) { + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + eth_zero_addr(priv->cfg_bssid); + } else { + if (priv->assoc_rsp_size) { + priv->req_bss = req->bss; + adapter->assoc_resp_received = true; + nxpwifi_queue_work(adapter, + &adapter->host_mlme_work); + } + } + + cfg80211_put_bss(priv->adapter->wiphy, req->bss); + + return ret; + +ssid_err: + + rcu_read_unlock(); + return -EINVAL; +} + +static int +nxpwifi_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + int ret; + + if (!nxpwifi_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); + + ret = nxpwifi_deauthenticate(priv, NULL); + if (!ret) { + eth_zero_addr(priv->cfg_bssid); + priv->hs2_enabled = false; + } + + return ret; +} + +static int +nxpwifi_cfg80211_deauthenticate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + return nxpwifi_cfg80211_disconnect(wiphy, dev, req->reason_code); +} + +static int +nxpwifi_cfg80211_disassociate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + return nxpwifi_cfg80211_disconnect(wiphy, dev, req->reason_code); +} + +static int +nxpwifi_cfg80211_probe_client(struct wiphy *wiphy, + struct net_device *dev, const u8 *peer, + u64 *cookie) +{ + /* hostapd looks for NL80211_CMD_PROBE_CLIENT support; otherwise, + * it requires monitor-mode support (which mwifiex doesn't support). + * Provide fake probe_client support to work around this. + */ + return -EOPNOTSUPP; +} + +/* station cfg80211 operations */ +static const struct cfg80211_ops nxpwifi_cfg80211_ops = { + .add_virtual_intf = nxpwifi_add_virtual_intf, + .del_virtual_intf = nxpwifi_del_virtual_intf, + .change_virtual_intf = nxpwifi_cfg80211_change_virtual_intf, + .scan = nxpwifi_cfg80211_scan, + .auth = nxpwifi_cfg80211_authenticate, + .assoc = nxpwifi_cfg80211_associate, + .deauth = nxpwifi_cfg80211_deauthenticate, + .disassoc = nxpwifi_cfg80211_disassociate, + .probe_client = nxpwifi_cfg80211_probe_client, + .get_station = nxpwifi_cfg80211_get_station, + .dump_station = nxpwifi_cfg80211_dump_station, + .dump_survey = nxpwifi_cfg80211_dump_survey, + .set_wiphy_params = nxpwifi_cfg80211_set_wiphy_params, + .add_key = nxpwifi_cfg80211_add_key, + .del_key = nxpwifi_cfg80211_del_key, + .set_default_mgmt_key = nxpwifi_cfg80211_set_default_mgmt_key, + .mgmt_tx = nxpwifi_cfg80211_mgmt_tx, + .update_mgmt_frame_registrations = + nxpwifi_cfg80211_update_mgmt_frame_registrations, + .remain_on_channel = nxpwifi_cfg80211_remain_on_channel, + .cancel_remain_on_channel = nxpwifi_cfg80211_cancel_remain_on_channel, + .set_default_key = nxpwifi_cfg80211_set_default_key, + .set_power_mgmt = nxpwifi_cfg80211_set_power_mgmt, + .set_tx_power = nxpwifi_cfg80211_set_tx_power, + .get_tx_power = nxpwifi_cfg80211_get_tx_power, + .set_bitrate_mask = nxpwifi_cfg80211_set_bitrate_mask, + .start_ap = nxpwifi_cfg80211_start_ap, + .stop_ap = nxpwifi_cfg80211_stop_ap, + .change_beacon = nxpwifi_cfg80211_change_beacon, + .set_cqm_rssi_config = nxpwifi_cfg80211_set_cqm_rssi_config, + .set_antenna = nxpwifi_cfg80211_set_antenna, + .get_antenna = nxpwifi_cfg80211_get_antenna, + .del_station = nxpwifi_cfg80211_del_station, + .sched_scan_start = nxpwifi_cfg80211_sched_scan_start, + .sched_scan_stop = nxpwifi_cfg80211_sched_scan_stop, + .change_station = nxpwifi_cfg80211_change_station, +#ifdef CONFIG_PM + .suspend = nxpwifi_cfg80211_suspend, + .resume = nxpwifi_cfg80211_resume, + .set_wakeup = nxpwifi_cfg80211_set_wakeup, +#endif + .set_coalesce = nxpwifi_cfg80211_set_coalesce, + .add_station = nxpwifi_cfg80211_add_station, + CFG80211_TESTMODE_CMD(nxpwifi_tm_cmd) + .get_channel = nxpwifi_cfg80211_get_channel, + .start_radar_detection = nxpwifi_cfg80211_start_radar_detection, + .channel_switch = nxpwifi_cfg80211_channel_switch, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support nxpwifi_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_NET_DETECT | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + .n_patterns = NXPWIFI_MEF_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = NXPWIFI_MAX_PATTERN_LEN, + .max_pkt_offset = NXPWIFI_MAX_OFFSET_LEN, + .max_nd_match_sets = NXPWIFI_MAX_ND_MATCH_SETS, +}; + +static const struct wiphy_wowlan_support nxpwifi_wowlan_support_no_gtk = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_NET_DETECT, + .n_patterns = NXPWIFI_MEF_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = NXPWIFI_MAX_PATTERN_LEN, + .max_pkt_offset = NXPWIFI_MAX_OFFSET_LEN, + .max_nd_match_sets = NXPWIFI_MAX_ND_MATCH_SETS, +}; +#endif + +static const struct wiphy_coalesce_support nxpwifi_coalesce_support = { + .n_rules = NXPWIFI_COALESCE_MAX_RULES, + .max_delay = NXPWIFI_MAX_COALESCING_DELAY, + .n_patterns = NXPWIFI_COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = NXPWIFI_MAX_PATTERN_LEN, + .max_pkt_offset = NXPWIFI_MAX_OFFSET_LEN, +}; + +int nxpwifi_init_channel_scan_gap(struct nxpwifi_adapter *adapter) +{ + u32 n_channels_bg, n_channels_a = 0; + + n_channels_bg = nxpwifi_band_2ghz.n_channels; + + if (adapter->fw_bands & BAND_A) + n_channels_a = nxpwifi_band_5ghz.n_channels; + + /* allocate twice the number total channels, since the driver issues an + * additional active scan request for hidden SSIDs on passive channels. + */ + adapter->num_in_chan_stats = 2 * (n_channels_bg + n_channels_a); + adapter->chan_stats = vmalloc(array_size(sizeof(*adapter->chan_stats), + adapter->num_in_chan_stats)); + + if (!adapter->chan_stats) + return -ENOMEM; + + return 0; +} + +/* This function registers the device with CFG802.11 subsystem. + * + * The function creates the wireless device/wiphy, populates it with + * default parameters and handler function pointers, and finally + * registers the device. + */ + +int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter) +{ + int ret; + void *wdev_priv; + struct wiphy *wiphy; + struct nxpwifi_private *priv = adapter->priv[NXPWIFI_BSS_TYPE_STA]; + struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_sta_vht_cap *vht_cap; + u8 *country_code; + u32 thr, retry; + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&nxpwifi_cfg80211_ops, + sizeof(struct nxpwifi_adapter *)); + if (!wiphy) { + nxpwifi_dbg(adapter, ERROR, + "%s: creating new wiphy\n", __func__); + return -ENOMEM; + } + + wiphy->max_scan_ssids = NXPWIFI_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len = NXPWIFI_MAX_VSIE_LEN; + + wiphy->mgmt_stypes = nxpwifi_mgmt_stypes; + wiphy->max_remain_on_channel_duration = 5000; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP); + + wiphy->max_num_akm_suites = CFG80211_MAX_NUM_AKM_SUITES; + + wiphy->bands[NL80211_BAND_2GHZ] = + devm_kmemdup(adapter->dev, &nxpwifi_band_2ghz, + sizeof(nxpwifi_band_2ghz), GFP_KERNEL); + if (!wiphy->bands[NL80211_BAND_2GHZ]) { + ret = -ENOMEM; + goto err; + } + + if (adapter->fw_bands & BAND_A) { + wiphy->bands[NL80211_BAND_5GHZ] = + devm_kmemdup(adapter->dev, &nxpwifi_band_5ghz, + sizeof(nxpwifi_band_5ghz), GFP_KERNEL); + if (!wiphy->bands[NL80211_BAND_5GHZ]) { + ret = -ENOMEM; + goto err; + } + } else { + wiphy->bands[NL80211_BAND_5GHZ] = NULL; + } + + ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap; + nxpwifi_setup_ht_caps(priv, ht_cap); + + if (adapter->is_hw_11ac_capable) { + vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap; + nxpwifi_setup_vht_caps(priv, vht_cap); + } + + if (adapter->is_hw_11ax_capable) + nxpwifi_setup_he_caps(priv, wiphy->bands[NL80211_BAND_2GHZ]); + + if (adapter->fw_bands & BAND_A) { + ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap; + nxpwifi_setup_ht_caps(priv, ht_cap); + + if (adapter->is_hw_11ac_capable) { + vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap; + nxpwifi_setup_vht_caps(priv, vht_cap); + } + + if (adapter->is_hw_11ax_capable) + nxpwifi_setup_he_caps(priv, wiphy->bands[NL80211_BAND_5GHZ]); + } + + if (adapter->is_hw_11ac_capable) + wiphy->iface_combinations = &nxpwifi_iface_comb_ap_sta_vht; + else + wiphy->iface_combinations = &nxpwifi_iface_comb_ap_sta; + wiphy->n_iface_combinations = 1; + + wiphy->max_ap_assoc_sta = adapter->max_sta_conn; + + /* Initialize cipher suits */ + wiphy->cipher_suites = nxpwifi_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(nxpwifi_cipher_suites); + + if (adapter->regd) { + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + wiphy_apply_custom_regulatory(wiphy, adapter->regd); + } + + ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_REPORTS_OBSS | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_NETNS_OK | + WIPHY_FLAG_PS_ON_BY_DEFAULT; + wiphy->max_num_csa_counters = NXPWIFI_MAX_CSA_COUNTERS; + +#ifdef CONFIG_PM + if (ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info)) + wiphy->wowlan = &nxpwifi_wowlan_support; + else + wiphy->wowlan = &nxpwifi_wowlan_support_no_gtk; +#endif + + wiphy->coalesce = &nxpwifi_coalesce_support; + + wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; + + wiphy->max_sched_scan_reqs = 1; + wiphy->max_sched_scan_ssids = NXPWIFI_MAX_SSID_LIST_LENGTH; + wiphy->max_sched_scan_ie_len = NXPWIFI_MAX_VSIE_LEN; + wiphy->max_match_sets = NXPWIFI_MAX_SSID_LIST_LENGTH; + + wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; + wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; + + wiphy->features |= NL80211_FEATURE_SAE | + NL80211_FEATURE_INACTIVITY_TIMER | + NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_NEED_OBSS_SCAN; + + if (ISSUPP_RANDOM_MAC(adapter->fw_cap_info)) + wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + + if (adapter->fw_api_ver == NXPWIFI_FW_V15) + wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; + + /* Reserve space for nxpwifi specific private data for BSS */ + wiphy->bss_priv_size = sizeof(struct nxpwifi_bss_priv); + + wiphy->reg_notifier = nxpwifi_reg_notifier; + + /* Set struct nxpwifi_adapter pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wiphy); + *(unsigned long *)wdev_priv = (unsigned long)adapter; + + set_wiphy_dev(wiphy, priv->adapter->dev); + + ret = wiphy_register(wiphy); + if (ret < 0) { + nxpwifi_dbg(adapter, ERROR, + "%s: wiphy_register failed: %d\n", __func__, ret); + goto err; + } + + if (!adapter->regd) { + if (adapter->region_code == 0x00) { + nxpwifi_dbg(adapter, WARN, + "Ignore world regulatory domain\n"); + } else { + wiphy->regulatory_flags |= + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + country_code = + nxpwifi_11d_code_2_region(adapter->region_code); + if (country_code && + regulatory_hint(wiphy, country_code)) + nxpwifi_dbg(priv->adapter, ERROR, + "regulatory_hint() failed\n"); + } + } + + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); + wiphy->frag_threshold = thr; + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, RTS_THRESH_I, &thr, true); + wiphy->rts_threshold = thr; + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); + wiphy->retry_short = (u8)retry; + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); + wiphy->retry_long = (u8)retry; + + adapter->wiphy = wiphy; + return ret; + +err: + wiphy_free(wiphy); + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.h b/drivers/net/wireless/nxp/nxpwifi/cfg80211.h new file mode 100644 index 000000000000..086ec9ca4cc9 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: CFG80211 + * + * Copyright 2011-2024 NXP + */ + +#ifndef __NXPWIFI_CFG80211__ +#define __NXPWIFI_CFG80211__ + +#include "main.h" + +int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter); + +int nxpwifi_cfg80211_change_beacon_data(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *data); + +#endif From patchwork Mon Sep 30 06:36:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815427 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B813117BED4; Mon, 30 Sep 2024 06:38:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678301; cv=fail; b=BW/ZVgtimsV0wVMtjMYLJAaOpwnz0iVpU6UTLZv3gw4SBme1Pb1gcX1ti7QSeRN/3fKtBvN9If7myL6HCPa4tUvWzmqrQebjgiWcvhkDMLTayE9s/O9MTfLW+dGOw9FDVYlbrWcIRoFcvOsjdkb5zz8dqxLp+wuJ0SuljTyLgSI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678301; c=relaxed/simple; bh=MfJc8NKtPUqSzghU7Z7zlNaffbxaWWiwhEXO9Fliap0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=O3TUkM+hG94u+8hWGta3L4u/RTgmP6XS9VC6i9MEJISFD5Cy04Q3IDfs641bGkmRaOSiwlz+5Nd697h+F1QuN2nLgpT6g0UtA2NcqMQ34Bo1KR/KITEhQIpVexEX3bikbUBeyPPcx0caBMjBBnWvW9QXn4mMhUCbIcrbXCyExhs= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=W01JQETa; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="W01JQETa" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=uIip/WP7u9CnBmxOo5m+pYBgfAlw0UvfpKLNdNzldnZbBVyGELDRgqyMw3A21BhwJI4wnMkq2S2qFTtrn3uMeLY1Z5qVlkSOBbH1348B8lIAAst/kL8HxnsX9dRNne11kPe1z8e1LTOzl1LS+ypZmSGAOAGxKrILTPIjWY2toXb6UajoErV8Tfvpq+nfdtNiGqdimWLzBBZFIsinf/6B2zqzca5D/19ANVu+KyKsJp2sIeMDIlB8WgGOuen4DOnAvHlNKO9Fky9kdLwS6n095f9oC/cuqtDvQQu+/tokUmWJDB3VnlF5ORxzbUdKZxYX3dzRQ+DEaqwDzxla5UdN+w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=vtWTOrFYIx5s+oqzjSfVYpnyxOhD+hf0eHZz8fIWC8M=; b=l+/WK2dPHzBQOOxyhUH8rbLMXANJ7ypqiOmgWFb40JxC6pr73sJG6xah/tpyADrvDVVg9mWgi9JiRGd8XtCmcKmkreQeJnvY9XIZdFc+fPTh0U5ekysNZ0BO7yWbeCSWHkJ9CR31jaXppKoXcH/G94Pa40lECDPBnJQlAtp0hJOqR4pgj2zsIrL3DFN4TIaJaMqQp8prBR4PNJv7gQTRCy5X240XZaEuylKX1/+ZYfMV1dbJbm9V3b6gUR7UL9fsvxF1fdjE7B2SXKp25vjoOnGq+Gq2a+iaB/YegWfCJS8uLL2/K5Rv4v5Z0Z2jDKPPAqck81ok7vAv6arO8a1AwA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=vtWTOrFYIx5s+oqzjSfVYpnyxOhD+hf0eHZz8fIWC8M=; b=W01JQETax0fBdfQjbuGS+3RaLTpkdq9PpjybVgW0LnTaI4lS8yB7HDxM2am60wFlOOW0W5rcOCPJogEZVSa4mCta47VGagvDIzKMjvNF00mfYuiUbnvYtgLPPEPq+GrjxXexHKtuFLNGOjqyldRIqxp9wcWnS/bDXoe0qf29eI45M+Stdd/gcZXkEf5730jf4FsBK95ezUG2ClNwLtJla006bBKOULjWA20pTqnlPMBW5i1EIIiqhPf+W77dsrGfwQlj0/HqGG+Bp3PmpDXf5uq79DMF8hoE8MvPum+krAaZ2nWoAC2WsZB9Q1zBbJioSovJ+0lZlFjeQC6eF/826w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:01 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:01 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 11/22] wifi: nxpwifi: add host command file Date: Mon, 30 Sep 2024 14:36:50 +0800 Message-Id: <20240930063701.2566520-12-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: da203d92-cde3-4146-b59b-08dce11a6e1f X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: jwp/kxt4OAGxdAoP36GHurEmvWXuFMTAwLGmNi6K7VDCOIwMQPRBm7PuO9kIQil3ljAhYreMr4j6AcTtEYIEIW1EBRYkTRVMiPKNnK+aPcbqFGItlz4lngCDBPo46VuQWvRJux2A5l36PeNEYwhpU9wmgg/qdpcZriCEj9foaqLTA1Q8FukJipVFkHohBczkUyJkHHOqQ94YVZ4pli+92vG/Aa88WlI9hVUIWz2CAx3aMv8dQZw3au6br9fFXvmureugJxajV7XrHtFqLGFE5QQoZnaczjoohVRoa21Gk3cWwSH0vlpjrN+g/gINCgEZBBYlTQ+W4/SU8GODg1dLuCwQ4eM1BEwKOKLVXRxWFg/1yjCFxNOSZk0CiJlrfp0mCNxErMtWFcLr4rm34UyXfYXU8fTh0wipGUpwT7PPqFyOrw2nzj+yumXjw1v37DogYBFpqw4Zn/TY87Lyeyc72NUXoKo6lra7wdE4tTqTMderH8gmuQGG0xppp3o935J7thzMULqhfQackZ7R9g/DqTyt6Nhp9A+eoT5zWZOdCBQMsE5TSlS+m1BY5w4Ht0iEH4NG6Fi6XkRUoQ8QU197IUzLn3ee2CKWshId941I9yD4uoSZNQo0tv6XayJLoJXslYcSaPTEX4+FJnebKcYJlxVaQlj6vZ7+3rHCgvhzN/Hz5j+xa/uBKVBfyuWZ/g6cI1U/oPlwR4qIO2rX4Sijb/5hBo0HHFeULOjEk0Nz8VFgGOGIJw+GOXBbkvOxmhYS+o5k259zzW0PRrn5BVDy33WCjFK7T4M7VDMC3WdCmaW47cjclQsS+adENS5cWBCQTvAWmagP90mniqOmaH3ePLO+tyRdjkDlmKs9q0tmj4gm+NTKo8+bUNCbNMNY4nCclcKdS/LdqNpydPPU6wuV7zL1vLh507hsBQ8JY1leTi+BL0sCzPoimLVLVzh4vCUXhOJgVY3oMxjGOE1qfbghJgSlCct8zXFY5+0R72oJxF/1Q7S7FemciA/zoOF36YmysgVQyGBXEtCI9p5wufNqJnzN3kofIc7ahyof+J2wRdYhmY0M7Nkd4RF8bUQnsF3PAlihx2e9MwrrNi/GyQqQzhikWUnwyyGGrPR+6ucHQ0Z/RRBDWQMTiTA5lWDFDgbt23Wv3N7r3ZsI6hFxBFF7xaVbfNCFL7EPpcogc23D+i45lR6u0yp0/ajZhzQoQnzLKE+VVoda289LoAHbBnfrMt9emDjOlR6SjSCPI9gtAdabiAzM62R1Wv4jmBjU9d13FiKC4WIAAQk46c13aWI3GSUHuESszxK9Ru/d8B4uXqwdhCLNdOhMjqVBMnXW9zcyf3ThQvMqdCfN9Dst9T+k1ryYzsiYVdrresOMleGADWRsq+tzT7Hb0bsfwIMyjBznWiOXc8VeLcXsdBZnegwlQQ== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: 21ySY9t5+1PCD5sQSibKtUMpOwZXzqesyLCazasSJq0/zKUjck5ubu9uJEI8BcckKs7dyWK2+KfTQwX+OHqAkNa6Tqu/Wbcuge+sxoUYKXSRVHBEVatIrjEiWUMjK6ApzGucyZKiA82L/xxYIQUXY0V8uFwwr3YWH/suSGTdr8SEZ4b3uRYEkJ15PdkBVQ1qej1vwHYUWIYvfYJk8upx062ioH2XPXofD1+WWmStXjojPuCu9nZER2G+wKswnu8Y3DG6GSqhuOPPkVd4qHNsUyABIZE3M5t+2caGhrhuzu90L04C6gAyYbvQdhT3oRvf3dWmf5C/VSZ+jNUKE6mZBJwZ34BXOD/Pn5FHjIdvUYWPVMfgnhuvZDML4kHki+BWlzANBNZocwDWf/EQqnrk+HJH8CHN9o6+qFhqUQdQkpDPwn5Mlb3xu2pbWh3tYCQMsNlP0ymcHAX7vSziMqLkKQbFsBVn4RoQSbs5MxoIANspNyU0ZONP3248M6z0poGAMg8rrpNyDYP6+TGnoI+RgaakofL1y9ykd6EXHf2eH4ojcXjFwAV5GpZn7x4xPHaDtPn9ZM0bVlDlVEj2ySb+qQ5ooDaZZRJqBmDim+K3NqTKriXPpnKo6wPkdAs+QkytO1rHTEoYqfmeWQJmxoWOebHUEN3RGdG5DKaEZQidI06g/okOF+92qIYIMoLwCPAFeeXivE2Vht1NtMWHCK7DEDdpcp5PnjzkZv1V+/OxEZhBR6aD8Kh1dpe7q2AtpJSiEOmLomfgS0kJpkNbtwBR9PaNt332wuDsz/s6GYM3xDVNkRwx6XhhzZpTywGlzs0+6H7U7YJZQ+vx9pqOgQyygh+vKaZo84TMT5jbGyvwz73oMvUTE3F0MgXN/TJSXoWRJg74wDyOUrqC6HFmDHkailP6P9M/05/a0RgHFovCrk3RYczoR+DhpVRZHH8leeHy5qp2+xngmqaSq1Hzyvi4INL7ThWWg7fPFzUezPIRiU/nzBN2aIuxVfJZeuzdn+MprKpHqed9C0vGMb5/836boMuIHiGuz8knuUkOilge28sjNwjr1TM1iW1/3AeQgtawK3FoPyo3IufURyl0i0r1htp0buG/xo1CDa02axuv9GHz3AaAwG8tQxOA0MTGoP1TNgbxecDkQLT8IYxOaVB3OyDn3PK764IoHX0+TTt8pDwB0BwWjda4Y/mA+XI7pifTHU9lU1WInYOfMsuhjlqNKZbTe29PgJE37yJeHmht1ym8gkehSNllrEYbKlST245pFc1Nedz01tTVrGKvjM2uh+S1Zb/GgrQNpa6JsvylokoOAJFfGXlXmwEukjVGZjHKP/EDmGhsZ5IwHgAQGONgRzCqT64fHOB5DIhFIjCngKV7Z88lhS3HX9sqeTpQN5P/GKt5hIkjYJP2yAvQIjpn0PHmF2MJmQSOMew2bsHfF2Earn3QhTvx/xKeamEucMxPohu2ILxQ5UJSwW0cw3Q3sHg1VZBtXDjWJClZqyn2E13/6pMQ9qeY1Fl9rCoJHJq9chWtAVFWWzgl2oE+AFUuKoSCyGQJJXCWBatO72Jhnq7swaCiPYnxnHVzKKZDlwgz X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: da203d92-cde3-4146-b59b-08dce11a6e1f X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:01.6433 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: EWU0ClmKfGQ4zq4oNqsC7yc9uZfTmiQZz8tflt/ZBpAxSbro5QljtXHu2nTvFNY0AOx7Md2WZuHtBaUdqdWVuw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Commands and NXP TLVs (Type/Length/Value) are defined here to communicate with NXP FW. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/fw.h | 2322 +++++++++++++++++++++++++ 1 file changed, 2322 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/fw.h diff --git a/drivers/net/wireless/nxp/nxpwifi/fw.h b/drivers/net/wireless/nxp/nxpwifi/fw.h new file mode 100644 index 000000000000..eba37b5e7cf0 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/fw.h @@ -0,0 +1,2322 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: Firmware specific macros & structures + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_FW_H_ +#define _NXPWIFI_FW_H_ + +#include + +#define INTF_HEADER_LEN 4 + +struct rfc_1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +} __packed; + +struct rx_packet_hdr { + struct ethhdr eth803_hdr; + struct rfc_1042_hdr rfc1042_hdr; +} __packed; + +struct tx_packet_hdr { + struct ethhdr eth803_hdr; + struct rfc_1042_hdr rfc1042_hdr; +} __packed; + +struct nxpwifi_fw_header { + __le32 dnld_cmd; + __le32 base_addr; + __le32 data_length; + __le32 crc; +} __packed; + +struct nxpwifi_fw_data { + struct nxpwifi_fw_header header; + __le32 seq_num; + u8 data[]; +} __packed; + +struct nxpwifi_fw_dump_header { + __le16 seq_num; + __le16 reserved; + __le16 type; + __le16 len; +} __packed; + +#define FW_DUMP_INFO_ENDED 0x0002 + +#define NXPWIFI_FW_DNLD_CMD_1 0x1 +#define NXPWIFI_FW_DNLD_CMD_5 0x5 +#define NXPWIFI_FW_DNLD_CMD_6 0x6 +#define NXPWIFI_FW_DNLD_CMD_7 0x7 + +#define B_SUPPORTED_RATES 5 +#define G_SUPPORTED_RATES 9 +#define BG_SUPPORTED_RATES 13 +#define A_SUPPORTED_RATES 9 +#define HOSTCMD_SUPPORTED_RATES 14 +#define N_SUPPORTED_RATES 3 +#define ALL_802_11_BANDS \ + (BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC | BAND_GAC) +#define FW_MULTI_BANDS_SUPPORT \ + (BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13)) +#define IS_SUPPORT_MULTI_BANDS(adapter) \ + ((adapter)->fw_cap_info & FW_MULTI_BANDS_SUPPORT) + +/* Get default bands of the firmware: + * need to shift bit 12 and bit 13 in fw_cap_info from the firmware + * to bit 13 and 14 for 11ac so that bit 11 is for GN, bit 12 for AN, + * bit 13 for GAC, and bit 14 for AAC, in order to be compatible with + * the band capability defined in the driver after right shift of 8 bits + */ +#define GET_FW_DEFAULT_BANDS(adapter) ({\ + typeof(adapter) (_adapter) = adapter; \ + (((((_adapter->fw_cap_info & 0x3000) << 1) | \ + (_adapter->fw_cap_info & ~0xF000)) \ + >> 8) & \ + ALL_802_11_BANDS); \ + }) + +#define HOST_WEP_KEY_INDEX_MASK 0x3fff + +#define KEY_INFO_ENABLED 0x01 +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES, + KEY_TYPE_ID_WAPI, + KEY_TYPE_ID_AES_CMAC, + KEY_TYPE_ID_AES_CMAC_DEF, +}; + +#define WPA_PN_SIZE 8 +#define KEY_PARAMS_FIXED_LEN 10 +#define KEY_INDEX_MASK 0xf +#define KEY_API_VER_MAJOR_V2 2 + +#define KEY_MCAST BIT(0) +#define KEY_UNICAST BIT(1) +#define KEY_ENABLED BIT(2) +#define KEY_DEFAULT BIT(3) +#define KEY_TX_KEY BIT(4) +#define KEY_RX_KEY BIT(5) +#define KEY_IGTK BIT(10) + +#define MAX_POLL_TRIES 10000 +#define MAX_FIRMWARE_POLL_TRIES 300 + +#define FIRMWARE_READY_SDIO 0xfedc +#define FIRMWARE_READY_PCIE 0xfedcba00 + +#define NXPWIFI_COEX_MODE_TIMESHARE 0x01 +#define NXPWIFI_COEX_MODE_SPATIAL 0x82 + +enum nxpwifi_usb_ep { + NXPWIFI_USB_EP_CMD_EVENT = 1, + NXPWIFI_USB_EP_DATA = 2, + NXPWIFI_USB_EP_DATA_CH2 = 3, +}; + +enum NXPWIFI_802_11_PRIVACY_FILTER { + NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL, + NXPWIFI_802_11_PRIV_FILTER_8021X_WEP +}; + +#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI) - (s16)(NF))) +#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR) + (s16)(NF))) + +#define UAP_BSS_PARAMS_I 0 +#define UAP_CUSTOM_IE_I 1 +#define NXPWIFI_AUTO_IDX_MASK 0xffff +#define NXPWIFI_DELETE_MASK 0x0000 +#define MGMT_MASK_ASSOC_REQ 0x01 +#define MGMT_MASK_REASSOC_REQ 0x04 +#define MGMT_MASK_ASSOC_RESP 0x02 +#define MGMT_MASK_REASSOC_RESP 0x08 +#define MGMT_MASK_PROBE_REQ 0x10 +#define MGMT_MASK_PROBE_RESP 0x20 +#define MGMT_MASK_BEACON 0x100 + +#define TLV_TYPE_UAP_SSID 0x0000 +#define TLV_TYPE_UAP_RATES 0x0001 +#define TLV_TYPE_PWR_CONSTRAINT 0x0020 +#define TLV_TYPE_HT_CAPABILITY 0x002d +#define TLV_TYPE_EXTENSION_ID 0x00ff + +#define PROPRIETARY_TLV_BASE_ID 0x0100 +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_BGSCAN_START_LATER (PROPRIETARY_TLV_BASE_ID + 30) +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) +#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) +#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) +#define TLV_TYPE_UAP_MAC_ADDRESS (PROPRIETARY_TLV_BASE_ID + 43) +#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) +#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) +#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) +#define TLV_TYPE_UAP_PREAMBLE_CTL (PROPRIETARY_TLV_BASE_ID + 49) +#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) +#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57) +#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) +#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) +#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) +#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) +#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70) +#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) +#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) +#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) +#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) +#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) +#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91) +#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) +#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96) +#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) +#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) +#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) +#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) +#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) +#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) +#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) +#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148) +#define TLV_TYPE_RXBA_SYNC (PROPRIETARY_TLV_BASE_ID + 153) +#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) +#define TLV_TYPE_REGION_DOMAIN_CODE (PROPRIETARY_TLV_BASE_ID + 171) +#define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 176) +#define TLV_TYPE_PS_PARAMS_IN_HS (PROPRIETARY_TLV_BASE_ID + 181) +#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) +#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) +#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) +#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) +#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198) +#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202) +#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203) +#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206) +#define TLV_TYPE_RANDOM_MAC (PROPRIETARY_TLV_BASE_ID + 236) +#define TLV_TYPE_CHAN_ATTR_CFG (PROPRIETARY_TLV_BASE_ID + 237) +#define TLV_TYPE_MAX_CONN (PROPRIETARY_TLV_BASE_ID + 279) +#define TLV_TYPE_HOST_MLME (PROPRIETARY_TLV_BASE_ID + 307) +#define TLV_TYPE_UAP_STA_FLAGS (PROPRIETARY_TLV_BASE_ID + 313) +#define TLV_TYPE_FW_CAP_INFO (PROPRIETARY_TLV_BASE_ID + 318) +#define TLV_TYPE_AX_ENABLE_SR (PROPRIETARY_TLV_BASE_ID + 322) +#define TLV_TYPE_AX_OBSS_PD_OFFSET (PROPRIETARY_TLV_BASE_ID + 323) +#define TLV_TYPE_SAE_PWE_MODE (PROPRIETARY_TLV_BASE_ID + 339) +#define TLV_TYPE_6E_INBAND_FRAMES (PROPRIETARY_TLV_BASE_ID + 345) +#define TLV_TYPE_SECURE_BOOT_UUID (PROPRIETARY_TLV_BASE_ID + 348) + +#define NXPWIFI_TX_DATA_BUF_SIZE_2K 2048 + +#define SSN_MASK 0xfff0 + +#define BA_RESULT_SUCCESS 0x0 +#define BA_RESULT_TIMEOUT 0x2 + +#define IS_BASTREAM_SETUP(ptr) ((ptr)->ba_status) + +#define BA_STREAM_NOT_ALLOWED 0xff + +#define IS_11N_ENABLED(priv) ({ \ + typeof(priv) (_priv) = priv; \ + (((_priv)->config_bands & BAND_GN || \ + (_priv)->config_bands & BAND_AN) && \ + (_priv)->curr_bss_params.bss_descriptor.bcn_ht_cap && \ + !(_priv)->curr_bss_params.bss_descriptor.disable_11n); \ + }) +#define INITIATOR_BIT(del_ba_param_set) (((del_ba_param_set) &\ + BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) + +#define NXPWIFI_TX_DATA_BUF_SIZE_4K 4096 +#define NXPWIFI_TX_DATA_BUF_SIZE_8K 8192 +#define NXPWIFI_TX_DATA_BUF_SIZE_12K 12288 + +#define ISSUPP_11NENABLED(fw_cap_info) ((fw_cap_info) & BIT(11)) +#define ISSUPP_DRCS_ENABLED(fw_cap_info) ((fw_cap_info) & BIT(15)) +#define ISSUPP_SDIO_SPA_ENABLED(fw_cap_info) ((fw_cap_info) & BIT(16)) +#define ISSUPP_RANDOM_MAC(fw_cap_info) ((fw_cap_info) & BIT(27)) +#define ISSUPP_FIRMWARE_SUPPLICANT(fw_cap_info) ((fw_cap_info) & BIT(21)) + +#define NXPWIFI_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ + IEEE80211_HT_CAP_SM_PS) + +#define NXPWIFI_DEF_11N_TX_BF_CAP 0x09E1E008 + +#define NXPWIFI_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR + +/* dev_cap bitmap + * BIT + * 0-16 reserved + * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * 18-22 reserved + * 23 IEEE80211_HT_CAP_SGI_20 + * 24 IEEE80211_HT_CAP_SGI_40 + * 25 IEEE80211_HT_CAP_TX_STBC + * 26 IEEE80211_HT_CAP_RX_STBC + * 27-28 reserved + * 29 IEEE80211_HT_CAP_GRN_FLD + * 30-31 reserved + */ +#define ISSUPP_CHANWIDTH40(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(17)) +#define ISSUPP_SHORTGI20(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(23)) +#define ISSUPP_SHORTGI40(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(24)) +#define ISSUPP_TXSTBC(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(25)) +#define ISSUPP_RXSTBC(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(26)) +#define ISSUPP_GREENFIELD(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(29)) +#define ISENABLED_40MHZ_INTOLERANT(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(8)) +#define ISSUPP_RXLDPC(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(22)) +#define ISSUPP_BEAMFORMING(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(30)) +#define ISALLOWED_CHANWIDTH40(ht_param) ((ht_param) & BIT(2)) +#define GETSUPP_TXBASTREAMS(dot_11n_dev_cap) (((dot_11n_dev_cap) >> 18) & 0xF) + +/* httxcfg bitmap + * 0 reserved + * 1 20/40 Mhz enable(1)/disable(0) + * 2-3 reserved + * 4 green field enable(1)/disable(0) + * 5 short GI in 20 Mhz enable(1)/disable(0) + * 6 short GI in 40 Mhz enable(1)/disable(0) + * 7-15 reserved + */ +#define NXPWIFI_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) + +/* 11AC Tx and Rx MCS map for 1x1 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams + */ +#define NXPWIFI_11AC_MCS_MAP_1X1 0xfffefffe + +/* 11AC Tx and Rx MCS map for 2x2 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams + */ +#define NXPWIFI_11AC_MCS_MAP_2X2 0xfffafffa + +#define GET_TXMCSSUPP(dev_mcs_supported) ((dev_mcs_supported) >> 4) +#define GET_RXMCSSUPP(dev_mcs_supported) ((dev_mcs_supported) & 0x0f) +#define SETHT_MCS32(x) (x[4] |= 1) +#define HT_STREAM_1X1 0x11 +#define HT_STREAM_2X2 0x22 + +#define SET_SECONDARYCHAN(radio_type, sec_chan) \ + ((radio_type) |= ((sec_chan) << 4)) + +#define LLC_SNAP_LEN 8 + +/* HW_SPEC fw_cap_info */ + +#define ISSUPP_11ACENABLED(fw_cap_info) ((fw_cap_info) & BIT(13)) +#define GET_VHTCAP_CHWDSET(vht_cap_info) (((vht_cap_info) >> 2) & 0x3) +#define NO_NSS_SUPPORT 0x3 +#define GET_VHTNSSMCS(mcs_mapset, nss) \ + (((mcs_mapset) >> (2 * ((nss) - 1))) & 0x3) +#define SET_VHTNSSMCS(mcs_mapset, nss, value) \ + ((mcs_mapset) |= ((value) & 0x3) << (2 * ((nss) - 1))) +#define GET_DEVTXMCSMAP(dev_mcs_map) ((dev_mcs_map) >> 16) +#define GET_DEVRXMCSMAP(dev_mcs_map) ((dev_mcs_map) & 0xFFFF) + +/* Clear SU Beanformer, MU beanformer, MU beanformee and + * sounding dimensions bits + */ +#define NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK \ + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) + +#define MOD_CLASS_HR_DSSS 0x03 +#define MOD_CLASS_OFDM 0x07 +#define MOD_CLASS_HT 0x08 +#define HT_BW_20 0 +#define HT_BW_40 1 + +#define DFS_CHAN_MOVE_TIME 10000 + +#define ISSUPP_11AXENABLED(fw_cap_ext) ((fw_cap_ext) & BIT(7)) + +#define HOST_CMD_GET_HW_SPEC 0x0003 +#define HOST_CMD_802_11_SCAN 0x0006 +#define HOST_CMD_802_11_GET_LOG 0x000b +#define HOST_CMD_MAC_MULTICAST_ADR 0x0010 +#define HOST_CMD_802_11_ASSOCIATE 0x0012 +#define HOST_CMD_802_11_SNMP_MIB 0x0016 +#define HOST_CMD_MAC_REG_ACCESS 0x0019 +#define HOST_CMD_BBP_REG_ACCESS 0x001a +#define HOST_CMD_RF_REG_ACCESS 0x001b +#define HOST_CMD_RF_TX_PWR 0x001e +#define HOST_CMD_RF_ANTENNA 0x0020 +#define HOST_CMD_802_11_DEAUTHENTICATE 0x0024 +#define HOST_CMD_MAC_CONTROL 0x0028 +#define HOST_CMD_802_11_MAC_ADDRESS 0x004D +#define HOST_CMD_802_11_EEPROM_ACCESS 0x0059 +#define HOST_CMD_802_11D_DOMAIN_INFO 0x005b +#define HOST_CMD_802_11_KEY_MATERIAL 0x005e +#define HOST_CMD_802_11_BG_SCAN_CONFIG 0x006b +#define HOST_CMD_802_11_BG_SCAN_QUERY 0x006c +#define HOST_CMD_WMM_GET_STATUS 0x0071 +#define HOST_CMD_802_11_SUBSCRIBE_EVENT 0x0075 +#define HOST_CMD_802_11_TX_RATE_QUERY 0x007f +#define HOST_CMD_MEM_ACCESS 0x0086 +#define HOST_CMD_CFG_DATA 0x008f +#define HOST_CMD_VERSION_EXT 0x0097 +#define HOST_CMD_MEF_CFG 0x009a +#define HOST_CMD_RSSI_INFO 0x00a4 +#define HOST_CMD_FUNC_INIT 0x00a9 +#define HOST_CMD_FUNC_SHUTDOWN 0x00aa +#define HOST_CMD_PMIC_REG_ACCESS 0x00ad +#define HOST_CMD_APCMD_SYS_RESET 0x00af +#define HOST_CMD_UAP_SYS_CONFIG 0x00b0 +#define HOST_CMD_UAP_BSS_START 0x00b1 +#define HOST_CMD_UAP_BSS_STOP 0x00b2 +#define HOST_CMD_APCMD_STA_LIST 0x00b3 +#define HOST_CMD_UAP_STA_DEAUTH 0x00b5 +#define HOST_CMD_11N_CFG 0x00cd +#define HOST_CMD_11N_ADDBA_REQ 0x00ce +#define HOST_CMD_11N_ADDBA_RSP 0x00cf +#define HOST_CMD_11N_DELBA 0x00d0 +#define HOST_CMD_TXPWR_CFG 0x00d1 +#define HOST_CMD_TX_RATE_CFG 0x00d6 +#define HOST_CMD_RECONFIGURE_TX_BUFF 0x00d9 +#define HOST_CMD_CHAN_REPORT_REQUEST 0x00dd +#define HOST_CMD_AMSDU_AGGR_CTRL 0x00df +#define HOST_CMD_ROBUST_COEX 0x00e0 +#define HOST_CMD_802_11_PS_MODE_ENH 0x00e4 +#define HOST_CMD_802_11_HS_CFG_ENH 0x00e5 +#define HOST_CMD_CAU_REG_ACCESS 0x00ed +#define HOST_CMD_SET_BSS_MODE 0x00f7 +#define HOST_CMD_PCIE_DESC_DETAILS 0x00fa +#define HOST_CMD_802_11_SCAN_EXT 0x0107 +#define HOST_CMD_COALESCE_CFG 0x010a +#define HOST_CMD_MGMT_FRAME_REG 0x010c +#define HOST_CMD_REMAIN_ON_CHAN 0x010d +#define HOST_CMD_GTK_REKEY_OFFLOAD_CFG 0x010f +#define HOST_CMD_11AC_CFG 0x0112 +#define HOST_CMD_HS_WAKEUP_REASON 0x0116 +#define HOST_CMD_MC_POLICY 0x0121 +#define HOST_CMD_FW_DUMP_EVENT 0x0125 +#define HOST_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 +#define HOST_CMD_STA_CONFIGURE 0x023f +#define HOST_CMD_VDLL 0x0240 +#define HOST_CMD_CHAN_REGION_CFG 0x0242 +#define HOST_CMD_PACKET_AGGR_CTRL 0x0251 +#define HOST_CMD_ADD_NEW_STATION 0x025f +#define HOST_CMD_11AX_CFG 0x0266 +#define HOST_CMD_11AX_CMD 0x026d + +#define PROTOCOL_NO_SECURITY 0x01 +#define PROTOCOL_STATIC_WEP 0x02 +#define PROTOCOL_WPA 0x08 +#define PROTOCOL_WPA2 0x20 +#define PROTOCOL_WPA2_MIXED 0x28 +#define PROTOCOL_EAP 0x40 +#define KEY_MGMT_EAP 0x01 +#define KEY_MGMT_PSK 0x02 +#define KEY_MGMT_NONE 0x04 +#define KEY_MGMT_PSK_SHA256 0x100 +#define KEY_MGMT_OWE 0x200 +#define KEY_MGMT_SAE 0x400 +#define CIPHER_TKIP 0x04 +#define CIPHER_AES_CCMP 0x08 +#define VALID_CIPHER_BITMAP 0x0c + +enum ENH_PS_MODES { + EN_PS = 1, + DIS_PS = 2, + EN_AUTO_DS = 3, + DIS_AUTO_DS = 4, + SLEEP_CONFIRM = 5, + GET_PS = 0, + EN_AUTO_PS = 0xff, + DIS_AUTO_PS = 0xfe, +}; + +enum nxpwifi_channel_flags { + NXPWIFI_CHANNEL_PASSIVE = BIT(0), + NXPWIFI_CHANNEL_DFS = BIT(1), + NXPWIFI_CHANNEL_NOHT40 = BIT(2), + NXPWIFI_CHANNEL_NOHT80 = BIT(3), + NXPWIFI_CHANNEL_DISABLED = BIT(7), +}; + +#define HOST_RET_BIT 0x8000 +#define HOST_ACT_GEN_GET 0x0000 +#define HOST_ACT_GEN_SET 0x0001 +#define HOST_ACT_GEN_REMOVE 0x0004 +#define HOST_ACT_BITWISE_SET 0x0002 +#define HOST_ACT_BITWISE_CLR 0x0003 +#define HOST_RESULT_OK 0x0000 +#define HOST_ACT_MAC_RX_ON BIT(0) +#define HOST_ACT_MAC_TX_ON BIT(1) +#define HOST_ACT_MAC_WEP_ENABLE BIT(3) +#define HOST_ACT_MAC_ETHERNETII_ENABLE BIT(4) +#define HOST_ACT_MAC_PROMISCUOUS_ENABLE BIT(7) +#define HOST_ACT_MAC_ALL_MULTICAST_ENABLE BIT(8) +#define HOST_ACT_MAC_DYNAMIC_BW_ENABLE BIT(16) + +#define HOST_BSS_MODE_IBSS 0x0002 +#define HOST_BSS_MODE_ANY 0x0003 + +#define HOST_SCAN_RADIO_TYPE_BG 0 +#define HOST_SCAN_RADIO_TYPE_A 1 + +#define HS_CFG_CANCEL 0xffffffff +#define HS_CFG_COND_DEF 0x00000000 +#define HS_CFG_GPIO_DEF 0xff +#define HS_CFG_GAP_DEF 0xff +#define HS_CFG_COND_BROADCAST_DATA 0x00000001 +#define HS_CFG_COND_UNICAST_DATA 0x00000002 +#define HS_CFG_COND_MAC_EVENT 0x00000004 +#define HS_CFG_COND_MULTICAST_DATA 0x00000008 + +#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB +#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC +#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD +#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE +#define CONNECT_ERR_STA_FAILURE 0xFFFF + +#define CMD_F_HOSTCMD BIT(0) + +#define HOST_CMD_ID_MASK 0x0fff + +#define HOST_SEQ_NUM_MASK 0x00ff + +#define HOST_BSS_NUM_MASK 0x0f00 + +#define HOST_BSS_TYPE_MASK 0xf000 + +#define HOST_ACT_SET_RX 0x0001 +#define HOST_ACT_SET_TX 0x0002 +#define HOST_ACT_SET_BOTH 0x0003 +#define HOST_ACT_GET_RX 0x0004 +#define HOST_ACT_GET_TX 0x0008 +#define HOST_ACT_GET_BOTH 0x000c + +#define HOST_ACT_REMOVE_STA 0x0 +#define HOST_ACT_ADD_STA 0x1 + +#define RF_ANTENNA_AUTO 0xFFFF + +#define HOST_SET_SEQ_NO_BSS_INFO(seq, num, type) \ + ((((seq) & 0x00ff) | \ + (((num) & 0x000f) << 8)) | \ + (((type) & 0x000f) << 12)) + +#define HOST_GET_SEQ_NO(seq) \ + ((seq) & HOST_SEQ_NUM_MASK) + +#define HOST_GET_BSS_NO(seq) \ + (((seq) & HOST_BSS_NUM_MASK) >> 8) + +#define HOST_GET_BSS_TYPE(seq) \ + (((seq) & HOST_BSS_TYPE_MASK) >> 12) + +#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 +#define EVENT_LINK_LOST 0x00000003 +#define EVENT_LINK_SENSED 0x00000004 +#define EVENT_MIB_CHANGED 0x00000006 +#define EVENT_INIT_DONE 0x00000007 +#define EVENT_DEAUTHENTICATED 0x00000008 +#define EVENT_DISASSOCIATED 0x00000009 +#define EVENT_PS_AWAKE 0x0000000a +#define EVENT_PS_SLEEP 0x0000000b +#define EVENT_MIC_ERR_MULTICAST 0x0000000d +#define EVENT_MIC_ERR_UNICAST 0x0000000e +#define EVENT_DEEP_SLEEP_AWAKE 0x00000010 +#define EVENT_WMM_STATUS_CHANGE 0x00000017 +#define EVENT_BG_SCAN_REPORT 0x00000018 +#define EVENT_RSSI_LOW 0x00000019 +#define EVENT_SNR_LOW 0x0000001a +#define EVENT_MAX_FAIL 0x0000001b +#define EVENT_RSSI_HIGH 0x0000001c +#define EVENT_SNR_HIGH 0x0000001d +#define EVENT_DATA_RSSI_LOW 0x00000024 +#define EVENT_DATA_SNR_LOW 0x00000025 +#define EVENT_DATA_RSSI_HIGH 0x00000026 +#define EVENT_DATA_SNR_HIGH 0x00000027 +#define EVENT_LINK_QUALITY 0x00000028 +#define EVENT_PORT_RELEASE 0x0000002b +#define EVENT_UAP_STA_DEAUTH 0x0000002c +#define EVENT_UAP_STA_ASSOC 0x0000002d +#define EVENT_UAP_BSS_START 0x0000002e +#define EVENT_PRE_BEACON_LOST 0x00000031 +#define EVENT_ADDBA 0x00000033 +#define EVENT_DELBA 0x00000034 +#define EVENT_BA_STREAM_TIEMOUT 0x00000037 +#define EVENT_AMSDU_AGGR_CTRL 0x00000042 +#define EVENT_UAP_BSS_IDLE 0x00000043 +#define EVENT_UAP_BSS_ACTIVE 0x00000044 +#define EVENT_WEP_ICV_ERR 0x00000046 +#define EVENT_HS_ACT_REQ 0x00000047 +#define EVENT_BW_CHANGE 0x00000048 +#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c +#define EVENT_HOSTWAKE_STAIE 0x0000004d +#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 +#define EVENT_RADAR_DETECTED 0x00000053 +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 +#define EVENT_TX_DATA_PAUSE 0x00000055 +#define EVENT_EXT_SCAN_REPORT 0x00000058 +#define EVENT_RXBA_SYNC 0x00000059 +#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f +#define EVENT_UNKNOWN_DEBUG 0x00000063 +#define EVENT_BG_SCAN_STOPPED 0x00000065 +#define EVENT_MULTI_CHAN_INFO 0x0000006a +#define EVENT_FW_DUMP_INFO 0x00000073 +#define EVENT_TX_STATUS_REPORT 0x00000074 +#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076 +#define EVENT_VDLL_IND 0x00000081 + +#define EVENT_ID_MASK 0xffff +#define BSS_NUM_MASK 0xf + +#define EVENT_GET_BSS_NUM(event_cause) \ + (((event_cause) >> 16) & BSS_NUM_MASK) + +#define EVENT_GET_BSS_TYPE(event_cause) \ + (((event_cause) >> 24) & 0x00ff) + +#define NXPWIFI_MAX_PATTERN_LEN 40 +#define NXPWIFI_MAX_OFFSET_LEN 100 +#define NXPWIFI_MAX_ND_MATCH_SETS 10 + +#define STACK_NBYTES 100 +#define TYPE_DNUM 1 +#define TYPE_BYTESEQ 2 +#define MAX_OPERAND 0x40 +#define TYPE_EQ (MAX_OPERAND + 1) +#define TYPE_EQ_DNUM (MAX_OPERAND + 2) +#define TYPE_EQ_BIT (MAX_OPERAND + 3) +#define TYPE_AND (MAX_OPERAND + 4) +#define TYPE_OR (MAX_OPERAND + 5) +#define MEF_MODE_HOST_SLEEP 1 +#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 +#define MEF_ACTION_AUTO_ARP 0x10 +#define NXPWIFI_CRITERIA_BROADCAST BIT(0) +#define NXPWIFI_CRITERIA_UNICAST BIT(1) +#define NXPWIFI_CRITERIA_MULTICAST BIT(3) +#define NXPWIFI_MAX_SUPPORTED_IPADDR 4 + +#define NXPWIFI_DEF_CS_UNIT_TIME 2 +#define NXPWIFI_DEF_CS_THR_OTHERLINK 10 +#define NXPWIFI_DEF_THR_DIRECTLINK 0 +#define NXPWIFI_DEF_CS_TIME 10 +#define NXPWIFI_DEF_CS_TIMEOUT 16 +#define NXPWIFI_DEF_CS_REG_CLASS 12 +#define NXPWIFI_DEF_CS_PERIODICITY 1 + +#define NXPWIFI_FW_V15 15 + +#define NXPWIFI_MASTER_RADAR_DET_MASK BIT(1) + +struct nxpwifi_ie_types_header { + __le16 type; + __le16 len; +} __packed; + +struct nxpwifi_ie_types_data { + struct nxpwifi_ie_types_header header; + u8 data[]; +} __packed; + +#define NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET 0x08 +#define NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS 0x20 + +enum HS_WAKEUP_REASON { + NO_HSWAKEUP_REASON = 0, + BCAST_DATA_MATCHED, + MCAST_DATA_MATCHED, + UCAST_DATA_MATCHED, + MASKTABLE_EVENT_MATCHED, + NON_MASKABLE_EVENT_MATCHED, + NON_MASKABLE_CONDITION_MATCHED, + MAGIC_PATTERN_MATCHED, + CONTROL_FRAME_MATCHED, + MANAGEMENT_FRAME_MATCHED, + GTK_REKEY_FAILURE, + RESERVED +}; + +struct txpd { + u8 bss_type; + u8 bss_num; + __le16 tx_pkt_length; + __le16 tx_pkt_offset; + __le16 tx_pkt_type; + __le32 tx_control; + u8 priority; + u8 flags; + u8 pkt_delay_2ms; + u8 reserved1[2]; + u8 tx_token_id; + u8 reserved[2]; +} __packed; + +struct rxpd { + u8 bss_type; + u8 bss_num; + __le16 rx_pkt_length; + __le16 rx_pkt_offset; + __le16 rx_pkt_type; + __le16 seq_num; + u8 priority; + u8 rx_rate; + s8 snr; + s8 nf; + + /* For: Non-802.11 AC cards + * + * Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 + * + * For: 802.11 AC cards + * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 + * BW80 = 10 BW160 = 11 + * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 + * [Bit 5] STBC support Enabled = 1 + * [Bit 6] LDPC support Enabled = 1 + * [Bit 7] Reserved + */ + u8 ht_info; + u8 reserved[3]; + u8 flags; +} __packed; + +struct uap_txpd { + u8 bss_type; + u8 bss_num; + __le16 tx_pkt_length; + __le16 tx_pkt_offset; + __le16 tx_pkt_type; + __le32 tx_control; + u8 priority; + u8 flags; + u8 pkt_delay_2ms; + u8 reserved1[2]; + u8 tx_token_id; + u8 reserved[2]; +} __packed; + +struct uap_rxpd { + u8 bss_type; + u8 bss_num; + __le16 rx_pkt_length; + __le16 rx_pkt_offset; + __le16 rx_pkt_type; + __le16 seq_num; + u8 priority; + u8 rx_rate; + s8 snr; + s8 nf; + u8 ht_info; + u8 reserved[3]; + u8 flags; +} __packed; + +struct nxpwifi_auth { + __le16 auth_alg; + __le16 auth_transaction; + __le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[]; +} __packed; + +struct nxpwifi_ieee80211_mgmt { + __le16 frame_control; + __le16 duration; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le16 seq_ctrl; + u8 addr4[ETH_ALEN]; + struct nxpwifi_auth auth; +} __packed; + +struct nxpwifi_fw_chan_stats { + u8 chan_num; + u8 bandcfg; + u8 flags; + s8 noise; + __le16 total_bss; + __le16 cca_scan_dur; + __le16 cca_busy_dur; +} __packed; + +enum nxpwifi_chan_scan_mode_bitmasks { + NXPWIFI_PASSIVE_SCAN = BIT(0), + NXPWIFI_DISABLE_CHAN_FILT = BIT(1), + NXPWIFI_HIDDEN_SSID_REPORT = BIT(4), +}; + +struct nxpwifi_chan_scan_param_set { + u8 radio_type; + u8 chan_number; + u8 chan_scan_mode_bmap; + __le16 min_scan_time; + __le16 max_scan_time; +} __packed; + +struct nxpwifi_ie_types_chan_list_param_set { + struct nxpwifi_ie_types_header header; + struct nxpwifi_chan_scan_param_set chan_scan_param[]; +} __packed; + +struct nxpwifi_ie_types_rxba_sync { + struct nxpwifi_ie_types_header header; + u8 mac[ETH_ALEN]; + u8 tid; + u8 reserved; + __le16 seq_num; + __le16 bitmap_len; + u8 bitmap[]; +} __packed; + +struct chan_band_param_set { + u8 radio_type; + u8 chan_number; +}; + +struct nxpwifi_ie_types_chan_band_list_param_set { + struct nxpwifi_ie_types_header header; + struct chan_band_param_set chan_band_param[]; +} __packed; + +struct nxpwifi_ie_types_rates_param_set { + struct nxpwifi_ie_types_header header; + u8 rates[]; +} __packed; + +struct nxpwifi_ie_types_ssid_param_set { + struct nxpwifi_ie_types_header header; + u8 ssid[]; +} __packed; + +struct nxpwifi_ie_types_host_mlme { + struct nxpwifi_ie_types_header header; + u8 host_mlme; +} __packed; + +struct nxpwifi_ie_types_num_probes { + struct nxpwifi_ie_types_header header; + __le16 num_probes; +} __packed; + +struct nxpwifi_ie_types_repeat_count { + struct nxpwifi_ie_types_header header; + __le16 repeat_count; +} __packed; + +struct nxpwifi_ie_types_min_rssi_threshold { + struct nxpwifi_ie_types_header header; + __le16 rssi_threshold; +} __packed; + +struct nxpwifi_ie_types_bgscan_start_later { + struct nxpwifi_ie_types_header header; + __le16 start_later; +} __packed; + +struct nxpwifi_ie_types_scan_chan_gap { + struct nxpwifi_ie_types_header header; + /* time gap in TUs to be used between two consecutive channels scan */ + __le16 chan_gap; +} __packed; + +struct nxpwifi_ie_types_random_mac { + struct nxpwifi_ie_types_header header; + u8 mac[ETH_ALEN]; +} __packed; + +struct nxpwifi_ietypes_chanstats { + struct nxpwifi_ie_types_header header; + struct nxpwifi_fw_chan_stats chanstats[]; +} __packed; + +struct nxpwifi_ie_types_wildcard_ssid_params { + struct nxpwifi_ie_types_header header; + u8 max_ssid_length; + u8 ssid[]; +} __packed; + +#define TSF_DATA_SIZE 8 +struct nxpwifi_ie_types_tsf_timestamp { + struct nxpwifi_ie_types_header header; + u8 tsf_data[]; +} __packed; + +struct nxpwifi_cf_param_set { + u8 cfp_cnt; + u8 cfp_period; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; +} __packed; + +struct nxpwifi_ibss_param_set { + __le16 atim_window; +} __packed; + +struct nxpwifi_ie_types_ss_param_set { + struct nxpwifi_ie_types_header header; + union { + struct nxpwifi_cf_param_set cf_param_set[1]; + struct nxpwifi_ibss_param_set ibss_param_set[1]; + } cf_ibss; +} __packed; + +struct nxpwifi_fh_param_set { + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; +} __packed; + +struct nxpwifi_ds_param_set { + u8 current_chan; +} __packed; + +struct nxpwifi_ie_types_phy_param_set { + struct nxpwifi_ie_types_header header; + union { + struct nxpwifi_fh_param_set fh_param_set[1]; + struct nxpwifi_ds_param_set ds_param_set[1]; + } fh_ds; +} __packed; + +struct nxpwifi_ie_types_auth_type { + struct nxpwifi_ie_types_header header; + __le16 auth_type; +} __packed; + +struct nxpwifi_ie_types_vendor_param_set { + struct nxpwifi_ie_types_header header; + u8 ie[NXPWIFI_MAX_VSIE_LEN]; +}; + +#define NXPWIFI_AUTHTYPE_SAE 6 + +struct nxpwifi_ie_types_sae_pwe_mode { + struct nxpwifi_ie_types_header header; + u8 pwe[]; +} __packed; + +struct nxpwifi_ie_types_rsn_param_set { + struct nxpwifi_ie_types_header header; + u8 rsn_ie[]; +} __packed; + +#define KEYPARAMSET_FIXED_LEN 6 + +#define IGTK_PN_LEN 8 + +struct nxpwifi_cmac_param { + u8 ipn[IGTK_PN_LEN]; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct nxpwifi_wep_param { + __le16 key_len; + u8 key[WLAN_KEY_LEN_WEP104]; +} __packed; + +struct nxpwifi_tkip_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_TKIP]; +} __packed; + +struct nxpwifi_aes_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_CCMP_256]; +} __packed; + +struct nxpwifi_cmac_aes_param { + u8 ipn[IGTK_PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct nxpwifi_ie_type_key_param_set { + __le16 type; + __le16 len; + u8 mac_addr[ETH_ALEN]; + u8 key_idx; + u8 key_type; + __le16 key_info; + union { + struct nxpwifi_wep_param wep; + struct nxpwifi_tkip_param tkip; + struct nxpwifi_aes_param aes; + struct nxpwifi_cmac_aes_param cmac_aes; + } key_params; +} __packed; + +struct host_cmd_ds_802_11_key_material { + __le16 action; + struct nxpwifi_ie_type_key_param_set key_param_set; +} __packed; + +struct host_cmd_ds_gen { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; +}; + +#define S_DS_GEN sizeof(struct host_cmd_ds_gen) + +enum sleep_resp_ctrl { + RESP_NOT_NEEDED = 0, + RESP_NEEDED, +}; + +struct nxpwifi_ps_param { + __le16 null_pkt_interval; + __le16 multiple_dtims; + __le16 bcn_miss_timeout; + __le16 local_listen_interval; + __le16 reserved; + __le16 mode; + __le16 delay_to_ps; +} __packed; + +#define HS_DEF_WAKE_INTERVAL 100 +#define HS_DEF_INACTIVITY_TIMEOUT 50 + +struct nxpwifi_ps_param_in_hs { + struct nxpwifi_ie_types_header header; + __le32 hs_wake_int; + __le32 hs_inact_timeout; +} __packed; + +#define BITMAP_AUTO_DS 0x01 +#define BITMAP_STA_PS 0x10 + +struct nxpwifi_ie_types_auto_ds_param { + struct nxpwifi_ie_types_header header; + __le16 deep_sleep_timeout; +} __packed; + +struct nxpwifi_ie_types_ps_param { + struct nxpwifi_ie_types_header header; + struct nxpwifi_ps_param param; +} __packed; + +struct host_cmd_ds_802_11_ps_mode_enh { + __le16 action; + + union { + struct nxpwifi_ps_param opt_ps; + __le16 ps_bitmap; + } params; +} __packed; + +enum API_VER_ID { + KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, + UAP_FW_API_VER_ID = 3, + CHANRPT_API_VER_ID = 4, + FW_HOTFIX_VER_ID = 5, +}; + +struct hw_spec_api_rev { + struct nxpwifi_ie_types_header header; + __le16 api_id; + u8 major_ver; + u8 minor_ver; +} __packed; + +struct hw_spec_max_conn { + struct nxpwifi_ie_types_header header; + u8 reserved; + u8 max_sta_conn; +} __packed; + +struct hw_spec_extension { + struct nxpwifi_ie_types_header header; + u8 ext_id; + u8 tlv[]; +} __packed; + +/* HE MAC Capabilities Information field BIT 1 for TWT Req */ +#define HE_MAC_CAP_TWT_REQ_SUPPORT BIT(1) +/* HE MAC Capabilities Information field BIT 2 for TWT Resp*/ +#define HE_MAC_CAP_TWT_RESP_SUPPORT BIT(2) + +struct nxpwifi_ie_types_he_cap { + struct nxpwifi_ie_types_header header; + u8 ext_id; + u8 he_mac_cap[6]; + u8 he_phy_cap[11]; + __le16 rx_mcs_80; + __le16 tx_mcs_80; + __le16 rx_mcs_160; + __le16 tx_mcs_160; + __le16 rx_mcs_80p80; + __le16 tx_mcs_80p80; + u8 val[20]; +} __packed; + +struct nxpwifi_ie_types_he_op { + struct nxpwifi_ie_types_header header; + u8 ext_id; + __le16 he_op_param1; + u8 he_op_param2; + u8 bss_color_info; + __le16 basic_he_mcs_nss; + u8 option[9]; +} __packed; + +struct hw_spec_secure_boot_uuid { + struct nxpwifi_ie_types_header header; + __le64 uuid_lo; + __le64 uuid_hi; +} __packed; + +struct hw_spec_fw_cap_info { + struct nxpwifi_ie_types_header header; + __le32 fw_cap_info; + __le32 fw_cap_ext; +} __packed; + +struct host_cmd_ds_get_hw_spec { + __le16 hw_if_version; + __le16 version; + __le16 reserved; + __le16 num_of_mcast_adr; + u8 permanent_addr[ETH_ALEN]; + __le16 region_code; + __le16 number_of_antenna; + __le32 fw_release_number; + __le32 hw_dev_cap; + __le32 reserved_1; + __le32 reserved_2; + __le32 fw_cap_info; + __le32 dot_11n_dev_cap; + u8 dev_mcs_support; + __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ + __le16 mgmt_buf_count; /* mgmt IE buffer count */ + __le32 reserved_3; + __le32 reserved_4; + __le32 dot_11ac_dev_cap; + __le32 dot_11ac_mcs_support; + u8 tlv[]; +} __packed; + +struct host_cmd_ds_802_11_rssi_info { + __le16 action; + __le16 ndata; + __le16 nbcn; + __le16 reserved[9]; + long long reserved_1; +} __packed; + +struct host_cmd_ds_802_11_rssi_info_rsp { + __le16 action; + __le16 ndata; + __le16 nbcn; + __le16 data_rssi_last; + __le16 data_nf_last; + __le16 data_rssi_avg; + __le16 data_nf_avg; + __le16 bcn_rssi_last; + __le16 bcn_nf_last; + __le16 bcn_rssi_avg; + __le16 bcn_nf_avg; + long long tsf_bcn; +} __packed; + +struct host_cmd_ds_802_11_mac_address { + __le16 action; + u8 mac_addr[ETH_ALEN]; +} __packed; + +struct host_cmd_ds_mac_control { + __le32 action; +}; + +struct host_cmd_ds_mac_multicast_adr { + __le16 action; + __le16 num_of_adrs; + u8 mac_list[NXPWIFI_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; +} __packed; + +struct host_cmd_ds_802_11_deauthenticate { + u8 mac_addr[ETH_ALEN]; + __le16 reason_code; +} __packed; + +struct host_cmd_ds_802_11_associate { + u8 peer_sta_addr[ETH_ALEN]; + __le16 cap_info_bitmap; + __le16 listen_interval; + __le16 beacon_period; + u8 dtim_period; +} __packed; + +struct ieee_types_assoc_rsp { + __le16 cap_info_bitmap; + __le16 status_code; + __le16 a_id; + u8 ie_buffer[]; +} __packed; + +struct host_cmd_ds_802_11_associate_rsp { + struct ieee_types_assoc_rsp assoc_rsp; +} __packed; + +struct ieee_types_cf_param_set { + u8 element_id; + u8 len; + u8 cfp_cnt; + u8 cfp_period; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; +} __packed; + +struct ieee_types_fh_param_set { + u8 element_id; + u8 len; + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; +} __packed; + +struct ieee_types_ds_param_set { + u8 element_id; + u8 len; + u8 current_chan; +} __packed; + +union ieee_types_phy_param_set { + struct ieee_types_fh_param_set fh_param_set; + struct ieee_types_ds_param_set ds_param_set; +} __packed; + +struct ieee_types_oper_mode_ntf { + u8 element_id; + u8 len; + u8 oper_mode; +} __packed; + +struct host_cmd_ds_802_11_get_log { + __le32 mcast_tx_frame; + __le32 failed; + __le32 retry; + __le32 multi_retry; + __le32 frame_dup; + __le32 rts_success; + __le32 rts_failure; + __le32 ack_failure; + __le32 rx_frag; + __le32 mcast_rx_frame; + __le32 fcs_error; + __le32 tx_frame; + __le32 reserved; + __le32 wep_icv_err_cnt[4]; + __le32 bcn_rcv_cnt; + __le32 bcn_miss_cnt; +} __packed; + +/* Enumeration for rate format */ +enum nxpwifi_rate_format { + NXPWIFI_RATE_FORMAT_LG = 0, + NXPWIFI_RATE_FORMAT_HT, + NXPWIFI_RATE_FORMAT_VHT, + NXPWIFI_RATE_FORMAT_AUTO = 0xFF, +}; + +struct host_cmd_ds_tx_rate_query { + u8 tx_rate; + /* Tx Rate Info: For 802.11 AC cards + * + * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2 + * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3 + * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1 + * + * For non-802.11 AC cards + * Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 + */ + u8 ht_info; +} __packed; + +struct nxpwifi_tx_pause_tlv { + struct nxpwifi_ie_types_header header; + u8 peermac[ETH_ALEN]; + u8 tx_pause; + u8 pkt_cnt; +} __packed; + +enum host_sleep_action { + HS_CONFIGURE = 0x0001, + HS_ACTIVATE = 0x0002, +}; + +struct nxpwifi_hs_config_param { + __le32 conditions; + u8 gpio; + u8 gap; +} __packed; + +struct hs_activate_param { + __le16 resp_ctrl; +} __packed; + +struct host_cmd_ds_802_11_hs_cfg_enh { + __le16 action; + + union { + struct nxpwifi_hs_config_param hs_config; + struct hs_activate_param hs_activate; + } params; +} __packed; + +enum SNMP_MIB_INDEX { + OP_RATE_SET_I = 1, + DTIM_PERIOD_I = 3, + RTS_THRESH_I = 5, + SHORT_RETRY_LIM_I = 6, + LONG_RETRY_LIM_I = 7, + FRAG_THRESH_I = 8, + DOT11D_I = 9, + DOT11H_I = 10, +}; + +enum nxpwifi_assocmd_failurepoint { + NXPWIFI_ASSOC_CMD_SUCCESS = 0, + NXPWIFI_ASSOC_CMD_FAILURE_ASSOC, + NXPWIFI_ASSOC_CMD_FAILURE_AUTH, + NXPWIFI_ASSOC_CMD_FAILURE_JOIN +}; + +#define MAX_SNMP_BUF_SIZE 128 + +struct host_cmd_ds_802_11_snmp_mib { + __le16 query_type; + __le16 oid; + __le16 buf_size; + u8 value[]; +} __packed; + +struct nxpwifi_rate_scope { + __le16 type; + __le16 length; + __le16 hr_dsss_rate_bitmap; + __le16 ofdm_rate_bitmap; + __le16 ht_mcs_rate_bitmap[8]; + __le16 vht_mcs_rate_bitmap[8]; +} __packed; + +struct nxpwifi_rate_drop_pattern { + __le16 type; + __le16 length; + __le32 rate_drop_mode; +} __packed; + +struct host_cmd_ds_tx_rate_cfg { + __le16 action; + __le16 cfg_index; +} __packed; + +struct nxpwifi_power_group { + u8 modulation_class; + u8 first_rate_code; + u8 last_rate_code; + s8 power_step; + s8 power_min; + s8 power_max; + u8 ht_bandwidth; + u8 reserved; +} __packed; + +struct nxpwifi_types_power_group { + __le16 type; + __le16 length; +} __packed; + +struct host_cmd_ds_txpwr_cfg { + __le16 action; + __le16 cfg_index; + __le32 mode; +} __packed; + +struct host_cmd_ds_rf_tx_pwr { + __le16 action; + __le16 cur_level; + u8 max_power; + u8 min_power; +} __packed; + +struct host_cmd_ds_rf_ant_mimo { + __le16 action_tx; + __le16 tx_ant_mode; + __le16 action_rx; + __le16 rx_ant_mode; +} __packed; + +struct host_cmd_ds_rf_ant_siso { + __le16 action; + __le16 ant_mode; +} __packed; + +#define BAND_CFG_CHAN_BAND_MASK 0x03 +#define BAND_CFG_CHAN_BAND_SHIFT_BIT 0 +#define BAND_CFG_CHAN_WIDTH_MASK 0x0C +#define BAND_CFG_CHAN_WIDTH_SHIFT_BIT 2 +#define BAND_CFG_CHAN2_OFFSET_MASK 0x30 +#define BAND_CFG_CHAN2_SHIFT_BIT 4 + +struct nxpwifi_chan_desc { + __le16 start_freq; + u8 band_cfg; + u8 chan_num; +} __packed; + +struct host_cmd_ds_chan_rpt_req { + struct nxpwifi_chan_desc chan_desc; + __le32 msec_dwell_time; +} __packed; + +struct host_cmd_ds_chan_rpt_event { + __le32 result; + __le64 start_tsf; + __le32 duration; + u8 tlvbuf[]; +} __packed; + +struct host_cmd_sdio_sp_rx_aggr_cfg { + u8 action; + u8 enable; + __le16 block_size; +} __packed; + +struct nxpwifi_fixed_bcn_param { + __le64 timestamp; + __le16 beacon_period; + __le16 cap_info_bitmap; +} __packed; + +struct nxpwifi_event_scan_result { + __le16 event_id; + u8 bss_index; + u8 bss_type; + u8 more_event; + u8 reserved[3]; + __le16 buf_size; + u8 num_of_set; +} __packed; + +struct tx_status_event { + u8 packet_type; + u8 tx_token_id; + u8 status; +} __packed; + +#define NXPWIFI_USER_SCAN_CHAN_MAX 50 + +#define NXPWIFI_MAX_SSID_LIST_LENGTH 10 + +struct nxpwifi_scan_cmd_config { + /* BSS mode to be sent in the firmware command + */ + u8 bss_mode; + + /* Specific BSSID used to filter scan results in the firmware */ + u8 specific_bssid[ETH_ALEN]; + + /* Length of TLVs sent in command starting at tlvBuffer */ + u32 tlv_buf_len; + + /* SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * TLV_TYPE_CHANLIST, nxpwifi_ie_types_chan_list_param_set + * WLAN_EID_SSID, nxpwifi_ie_types_ssid_param_set + */ + u8 tlv_buf[]; /* SSID TLV(s) and ChanList TLVs are stored here */ +} __packed; + +struct nxpwifi_user_scan_chan { + u8 chan_number; + u8 radio_type; + u8 scan_type; + u8 reserved; + u32 scan_time; +} __packed; + +struct nxpwifi_user_scan_cfg { + /* BSS mode to be sent in the firmware command + */ + u8 bss_mode; + /* Configure the number of probe requests for active chan scans */ + u8 num_probes; + u8 reserved; + /* BSSID filter sent in the firmware command to limit the results */ + u8 specific_bssid[ETH_ALEN]; + /* SSID filter list used in the firmware to limit the scan results */ + struct cfg80211_ssid *ssid_list; + u8 num_ssids; + /* Variable number (fixed maximum) of channels to scan up */ + struct nxpwifi_user_scan_chan chan_list[NXPWIFI_USER_SCAN_CHAN_MAX]; + u16 scan_chan_gap; + u8 random_mac[ETH_ALEN]; +} __packed; + +#define NXPWIFI_BG_SCAN_CHAN_MAX 38 +#define NXPWIFI_BSS_MODE_INFRA 1 +#define NXPWIFI_BGSCAN_ACT_GET 0x0000 +#define NXPWIFI_BGSCAN_ACT_SET 0x0001 +#define NXPWIFI_BGSCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define NXPWIFI_BGSCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define NXPWIFI_BGSCAN_SSID_RSSI_MATCH 0x0004 +/**wait for all channel scan to complete to report scan result*/ +#define NXPWIFI_BGSCAN_WAIT_ALL_CHAN_DONE 0x80000000 + +struct nxpwifi_bg_scan_cfg { + u16 action; + u8 enable; + u8 bss_type; + u8 chan_per_scan; + u32 scan_interval; + u32 report_condition; + u8 num_probes; + u8 rssi_threshold; + u8 snr_threshold; + u16 repeat_count; + u16 start_later; + struct cfg80211_match_set *ssid_list; + u8 num_ssids; + struct nxpwifi_user_scan_chan chan_list[NXPWIFI_BG_SCAN_CHAN_MAX]; + u16 scan_chan_gap; +} __packed; + +struct ie_body { + u8 grp_key_oui[4]; + u8 ptk_cnt[2]; + u8 ptk_body[4]; +} __packed; + +struct host_cmd_ds_802_11_scan { + u8 bss_mode; + u8 bssid[ETH_ALEN]; + u8 tlv_buffer[]; +} __packed; + +struct host_cmd_ds_802_11_scan_rsp { + __le16 bss_descript_size; + u8 number_of_sets; + u8 bss_desc_and_tlv_buffer[]; +} __packed; + +struct host_cmd_ds_802_11_scan_ext { + u32 reserved; + u8 tlv_buffer[]; +} __packed; + +struct nxpwifi_ie_types_bss_mode { + struct nxpwifi_ie_types_header header; + u8 bss_mode; +} __packed; + +struct nxpwifi_ie_types_scan_rsp { + struct nxpwifi_ie_types_header header; + u8 bssid[ETH_ALEN]; + u8 frame_body[]; +} __packed; + +struct nxpwifi_ie_types_scan_inf { + struct nxpwifi_ie_types_header header; + __le16 rssi; + __le16 anpi; + u8 cca_busy_fraction; + u8 radio_type; + u8 channel; + u8 reserved; + __le64 tsf; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_config { + __le16 action; + u8 enable; + u8 bss_type; + u8 chan_per_scan; + u8 reserved; + __le16 reserved1; + __le32 scan_interval; + __le32 reserved2; + __le32 report_condition; + __le16 reserved3; + u8 tlv[]; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_query { + u8 flush; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_query_rsp { + __le32 report_condition; + struct host_cmd_ds_802_11_scan_rsp scan_resp; +} __packed; + +struct nxpwifi_ietypes_domain_code { + struct nxpwifi_ie_types_header header; + u8 domain_code; + u8 reserved; +} __packed; + +struct nxpwifi_ietypes_domain_param_set { + struct nxpwifi_ie_types_header header; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + struct ieee80211_country_ie_triplet triplet[]; +} __packed; + +struct host_cmd_ds_802_11d_domain_info { + __le16 action; + struct nxpwifi_ietypes_domain_param_set domain; +} __packed; + +struct host_cmd_ds_802_11d_domain_info_rsp { + __le16 action; + struct nxpwifi_ietypes_domain_param_set domain; +} __packed; + +struct host_cmd_ds_11n_addba_req { + u8 add_req_result; + u8 peer_mac_addr[ETH_ALEN]; + u8 dialog_token; + __le16 block_ack_param_set; + __le16 block_ack_tmo; + __le16 ssn; +} __packed; + +struct host_cmd_ds_11n_addba_rsp { + u8 add_rsp_result; + u8 peer_mac_addr[ETH_ALEN]; + u8 dialog_token; + __le16 status_code; + __le16 block_ack_param_set; + __le16 block_ack_tmo; + __le16 ssn; +} __packed; + +struct host_cmd_ds_11n_delba { + u8 del_result; + u8 peer_mac_addr[ETH_ALEN]; + __le16 del_ba_param_set; + __le16 reason_code; + u8 reserved; +} __packed; + +struct host_cmd_ds_11n_batimeout { + u8 tid; + u8 peer_mac_addr[ETH_ALEN]; + u8 origninator; +} __packed; + +struct host_cmd_ds_11n_cfg { + __le16 action; + __le16 ht_tx_cap; + __le16 ht_tx_info; + __le16 misc_config; /* Needed for 802.11AC cards only */ +} __packed; + +struct host_cmd_ds_txbuf_cfg { + __le16 action; + __le16 buff_size; + __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ + __le16 reserved3; +} __packed; + +struct host_cmd_ds_amsdu_aggr_ctrl { + __le16 action; + __le16 enable; + __le16 curr_buf_size; +} __packed; + +struct host_cmd_ds_sta_deauth { + u8 mac[ETH_ALEN]; + __le16 reason; +} __packed; + +struct nxpwifi_ie_types_sta_info { + struct nxpwifi_ie_types_header header; + u8 mac[ETH_ALEN]; + u8 power_mfg_status; + s8 rssi; +}; + +struct host_cmd_ds_sta_list { + __le16 sta_count; + u8 tlv[]; +} __packed; + +struct nxpwifi_ie_types_pwr_capability { + struct nxpwifi_ie_types_header header; + s8 min_pwr; + s8 max_pwr; +}; + +struct nxpwifi_ie_types_local_pwr_constraint { + struct nxpwifi_ie_types_header header; + u8 chan; + u8 constraint; +}; + +struct nxpwifi_ie_types_wmm_param_set { + struct nxpwifi_ie_types_header header; + u8 wmm_ie[]; +} __packed; + +struct nxpwifi_ie_types_mgmt_frame { + struct nxpwifi_ie_types_header header; + __le16 frame_control; + u8 frame_contents[]; +}; + +struct nxpwifi_ie_types_wmm_queue_status { + struct nxpwifi_ie_types_header header; + u8 queue_index; + u8 disabled; + __le16 medium_time; + u8 flow_required; + u8 flow_created; + u32 reserved; +}; + +struct ieee_types_wmm_info { + /* WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + struct ieee80211_vendor_ie vend_hdr; + u8 oui_subtype; + u8 version; + + u8 qos_info_bitmap; +} __packed; + +struct host_cmd_ds_wmm_get_status { + u8 queue_status_tlv[sizeof(struct nxpwifi_ie_types_wmm_queue_status) * + IEEE80211_NUM_ACS]; + u8 wmm_param_tlv[sizeof(struct ieee80211_wmm_param_ie) + 2]; +} __packed; + +struct nxpwifi_wmm_ac_status { + u8 disabled; + u8 flow_required; + u8 flow_created; +}; + +struct nxpwifi_ie_types_htcap { + struct nxpwifi_ie_types_header header; + struct ieee80211_ht_cap ht_cap; +} __packed; + +struct nxpwifi_ie_types_vhtcap { + struct nxpwifi_ie_types_header header; + struct ieee80211_vht_cap vht_cap; +} __packed; + +struct nxpwifi_ie_types_aid { + struct nxpwifi_ie_types_header header; + __le16 aid; +} __packed; + +struct nxpwifi_ie_types_oper_mode_ntf { + struct nxpwifi_ie_types_header header; + u8 oper_mode; +} __packed; + +/* VHT Operations IE */ +struct nxpwifi_ie_types_vht_oper { + struct nxpwifi_ie_types_header header; + u8 chan_width; + u8 chan_center_freq_1; + u8 chan_center_freq_2; + /* Basic MCS set map, each 2 bits stands for a NSS */ + __le16 basic_mcs_map; +} __packed; + +struct nxpwifi_ie_types_wmmcap { + struct nxpwifi_ie_types_header header; + struct nxpwifi_types_wmm_info wmm_info; +} __packed; + +struct nxpwifi_ie_types_htinfo { + struct nxpwifi_ie_types_header header; + struct ieee80211_ht_operation ht_oper; +} __packed; + +struct nxpwifi_ie_types_2040bssco { + struct nxpwifi_ie_types_header header; + u8 bss_co_2040; +} __packed; + +struct nxpwifi_ie_types_extcap { + struct nxpwifi_ie_types_header header; + u8 ext_capab[]; +} __packed; + +struct host_cmd_ds_mem_access { + __le16 action; + __le16 reserved; + __le32 addr; + __le32 value; +} __packed; + +struct nxpwifi_ie_types_qos_info { + struct nxpwifi_ie_types_header header; + u8 qos_info; +} __packed; + +struct host_cmd_ds_mac_reg_access { + __le16 action; + __le16 offset; + __le32 value; +} __packed; + +struct host_cmd_ds_bbp_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_rf_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_pmic_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_802_11_eeprom_access { + __le16 action; + + __le16 offset; + __le16 byte_count; + u8 value; +} __packed; + +struct nxpwifi_assoc_event { + u8 sta_addr[ETH_ALEN]; + __le16 type; + __le16 len; + __le16 frame_control; + __le16 cap_info; + __le16 listen_interval; + u8 data[]; +} __packed; + +struct host_cmd_ds_sys_config { + __le16 action; + u8 tlv[]; +}; + +struct host_cmd_11ac_vht_cfg { + __le16 action; + u8 band_config; + u8 misc_config; + __le32 cap_info; + __le32 mcs_tx_set; + __le32 mcs_rx_set; +} __packed; + +struct host_cmd_tlv_akmp { + struct nxpwifi_ie_types_header header; + __le16 key_mgmt; + __le16 key_mgmt_operation; +} __packed; + +struct host_cmd_tlv_pwk_cipher { + struct nxpwifi_ie_types_header header; + __le16 proto; + u8 cipher; + u8 reserved; +} __packed; + +struct host_cmd_tlv_gwk_cipher { + struct nxpwifi_ie_types_header header; + u8 cipher; + u8 reserved; +} __packed; + +struct host_cmd_tlv_passphrase { + struct nxpwifi_ie_types_header header; + u8 passphrase[]; +} __packed; + +struct host_cmd_tlv_wep_key { + struct nxpwifi_ie_types_header header; + u8 key_index; + u8 is_default; + u8 key[]; +}; + +struct host_cmd_tlv_auth_type { + struct nxpwifi_ie_types_header header; + u8 auth_type; + u8 pwe_derivation; + u8 transition_disable; +} __packed; + +struct host_cmd_tlv_encrypt_protocol { + struct nxpwifi_ie_types_header header; + __le16 proto; +} __packed; + +struct host_cmd_tlv_ssid { + struct nxpwifi_ie_types_header header; + u8 ssid[]; +} __packed; + +struct host_cmd_tlv_rates { + struct nxpwifi_ie_types_header header; + u8 rates[]; +} __packed; + +struct nxpwifi_ie_types_bssid_list { + struct nxpwifi_ie_types_header header; + u8 bssid[ETH_ALEN]; +} __packed; + +struct host_cmd_tlv_bcast_ssid { + struct nxpwifi_ie_types_header header; + u8 bcast_ctl; +} __packed; + +struct host_cmd_tlv_beacon_period { + struct nxpwifi_ie_types_header header; + __le16 period; +} __packed; + +struct host_cmd_tlv_dtim_period { + struct nxpwifi_ie_types_header header; + u8 period; +} __packed; + +struct host_cmd_tlv_frag_threshold { + struct nxpwifi_ie_types_header header; + __le16 frag_thr; +} __packed; + +struct host_cmd_tlv_rts_threshold { + struct nxpwifi_ie_types_header header; + __le16 rts_thr; +} __packed; + +struct host_cmd_tlv_retry_limit { + struct nxpwifi_ie_types_header header; + u8 limit; +} __packed; + +struct host_cmd_tlv_mac_addr { + struct nxpwifi_ie_types_header header; + u8 mac_addr[ETH_ALEN]; +} __packed; + +struct host_cmd_tlv_channel_band { + struct nxpwifi_ie_types_header header; + u8 band_config; + u8 channel; +} __packed; + +struct host_cmd_tlv_ageout_timer { + struct nxpwifi_ie_types_header header; + __le32 sta_ao_timer; +} __packed; + +struct host_cmd_tlv_power_constraint { + struct nxpwifi_ie_types_header header; + u8 constraint; +} __packed; + +struct nxpwifi_ie_types_btcoex_scan_time { + struct nxpwifi_ie_types_header header; + u8 coex_scan; + u8 reserved; + __le16 min_scan_time; + __le16 max_scan_time; +} __packed; + +struct nxpwifi_ie_types_btcoex_aggr_win_size { + struct nxpwifi_ie_types_header header; + u8 coex_win_size; + u8 tx_win_size; + u8 rx_win_size; + u8 reserved; +} __packed; + +struct nxpwifi_ie_types_robust_coex { + struct nxpwifi_ie_types_header header; + __le32 mode; +} __packed; + +#define NXPWIFI_VERSION_STR_LENGTH 128 + +struct host_cmd_ds_version_ext { + u8 version_str_sel; + char version_str[NXPWIFI_VERSION_STR_LENGTH]; +} __packed; + +struct host_cmd_ds_mgmt_frame_reg { + __le16 action; + __le32 mask; +} __packed; + +struct host_cmd_ds_remain_on_chan { + __le16 action; + u8 status; + u8 reserved; + u8 band_cfg; + u8 channel; + __le32 duration; +} __packed; + +struct host_cmd_ds_802_11_ibss_status { + __le16 action; + __le16 enable; + u8 bssid[ETH_ALEN]; + __le16 beacon_interval; + __le16 atim_window; + __le16 use_g_rate_protect; +} __packed; + +struct nxpwifi_fw_mef_entry { + u8 mode; + u8 action; + __le16 exprsize; + u8 expr[]; +} __packed; + +struct host_cmd_ds_mef_cfg { + __le32 criteria; + __le16 num_entries; + u8 mef_entry_data[]; +} __packed; + +#define CONNECTION_TYPE_INFRA 0 +#define CONNECTION_TYPE_AP 2 + +struct host_cmd_ds_set_bss_mode { + u8 con_type; +} __packed; + +struct host_cmd_ds_pcie_details { + /* TX buffer descriptor ring address */ + __le32 txbd_addr_lo; + __le32 txbd_addr_hi; + /* TX buffer descriptor ring count */ + __le32 txbd_count; + + /* RX buffer descriptor ring address */ + __le32 rxbd_addr_lo; + __le32 rxbd_addr_hi; + /* RX buffer descriptor ring count */ + __le32 rxbd_count; + + /* Event buffer descriptor ring address */ + __le32 evtbd_addr_lo; + __le32 evtbd_addr_hi; + /* Event buffer descriptor ring count */ + __le32 evtbd_count; + + /* Sleep cookie buffer physical address */ + __le32 sleep_cookie_addr_lo; + __le32 sleep_cookie_addr_hi; +} __packed; + +struct nxpwifi_ie_types_rssi_threshold { + struct nxpwifi_ie_types_header header; + u8 abs_value; + u8 evt_freq; +} __packed; + +#define NXPWIFI_DFS_REC_HDR_LEN 8 +#define NXPWIFI_DFS_REC_HDR_NUM 10 +#define NXPWIFI_BIN_COUNTER_LEN 7 + +struct nxpwifi_radar_det_event { + __le32 detect_count; + u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/ + u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/ + __le16 pw_chirp_type; + u8 pw_chirp_idx; + u8 pw_value; + u8 pri_radar_type; + u8 pri_bincnt; + u8 bin_counter[NXPWIFI_BIN_COUNTER_LEN]; + u8 num_dfs_records; + u8 dfs_record_hdr[NXPWIFI_DFS_REC_HDR_NUM][NXPWIFI_DFS_REC_HDR_LEN]; + __le32 passed; +} __packed; + +struct nxpwifi_ie_types_multi_chan_info { + struct nxpwifi_ie_types_header header; + __le16 status; + u8 tlv_buffer[]; +} __packed; + +struct nxpwifi_ie_types_mc_group_info { + struct nxpwifi_ie_types_header header; + u8 chan_group_id; + u8 chan_buf_weight; + u8 band_config; + u8 chan_num; + __le32 chan_time; + __le32 reserved; + union { + u8 sdio_func_num; + u8 usb_ep_num; + } hid_num; + u8 intf_num; + u8 bss_type_numlist[]; +} __packed; + +#define MEAS_RPT_MAP_RADAR_MASK 0x08 +#define MEAS_RPT_MAP_RADAR_SHIFT_BIT 3 + +struct nxpwifi_ie_types_chan_rpt_data { + struct nxpwifi_ie_types_header header; + u8 meas_rpt_map; +} __packed; + +struct host_cmd_ds_802_11_subsc_evt { + __le16 action; + __le16 events; +} __packed; + +struct chan_switch_result { + u8 cur_chan; + u8 status; + u8 reason; +} __packed; + +struct nxpwifi_ie { + __le16 ie_index; + __le16 mgmt_subtype_mask; + __le16 ie_length; + u8 ie_buffer[IEEE_MAX_IE_SIZE]; +} __packed; + +#define MAX_MGMT_IE_INDEX 16 +struct nxpwifi_ie_list { + __le16 type; + __le16 len; + struct nxpwifi_ie ie_list[MAX_MGMT_IE_INDEX]; +} __packed; + +struct coalesce_filt_field_param { + u8 operation; + u8 operand_len; + __le16 offset; + u8 operand_byte_stream[4]; +}; + +struct coalesce_receive_filt_rule { + struct nxpwifi_ie_types_header header; + u8 num_of_fields; + u8 pkt_type; + __le16 max_coalescing_delay; + struct coalesce_filt_field_param params[]; +} __packed; + +struct host_cmd_ds_coalesce_cfg { + __le16 action; + __le16 num_of_rules; + u8 rule_data[]; +} __packed; + +struct host_cmd_ds_multi_chan_policy { + __le16 action; + __le16 policy; +} __packed; + +struct host_cmd_ds_robust_coex { + __le16 action; + __le16 reserved; +} __packed; + +struct host_cmd_ds_wakeup_reason { + __le16 wakeup_reason; +} __packed; + +struct host_cmd_ds_gtk_rekey_params { + __le16 action; + u8 kck[NL80211_KCK_LEN]; + u8 kek[NL80211_KEK_LEN]; + __le32 replay_ctr_low; + __le32 replay_ctr_high; +} __packed; + +struct host_cmd_ds_chan_region_cfg { + __le16 action; +} __packed; + +struct host_cmd_ds_pkt_aggr_ctrl { + __le16 action; + __le16 enable; + __le16 tx_aggr_max_size; + __le16 tx_aggr_max_num; + __le16 tx_aggr_align; +} __packed; + +struct host_cmd_ds_sta_configure { + __le16 action; + u8 tlv_buffer[]; +} __packed; + +struct nxpwifi_ie_types_sta_flag { + struct nxpwifi_ie_types_header header; + __le32 sta_flags; +} __packed; + +struct host_cmd_ds_add_station { + __le16 action; + __le16 aid; + u8 peer_mac[ETH_ALEN]; + __le32 listen_interval; + __le16 cap_info; + u8 tlv[]; +} __packed; + +struct host_cmd_11ax_cfg { + __le16 action; + u8 band_config; + u8 tlv[]; +} __packed; + +struct host_cmd_11ax_cmd { + __le16 action; + __le16 sub_id; + u8 val[]; +} __packed; + +struct host_cmd_ds_command { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; + union { + struct host_cmd_ds_get_hw_spec hw_spec; + struct host_cmd_ds_mac_control mac_ctrl; + struct host_cmd_ds_802_11_mac_address mac_addr; + struct host_cmd_ds_mac_multicast_adr mc_addr; + struct host_cmd_ds_802_11_get_log get_log; + struct host_cmd_ds_802_11_rssi_info rssi_info; + struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; + struct host_cmd_ds_802_11_snmp_mib smib; + struct host_cmd_ds_tx_rate_query tx_rate; + struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; + struct host_cmd_ds_txpwr_cfg txp_cfg; + struct host_cmd_ds_rf_tx_pwr txp; + struct host_cmd_ds_rf_ant_mimo ant_mimo; + struct host_cmd_ds_rf_ant_siso ant_siso; + struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; + struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; + struct host_cmd_ds_802_11_scan scan; + struct host_cmd_ds_802_11_scan_ext ext_scan; + struct host_cmd_ds_802_11_scan_rsp scan_resp; + struct host_cmd_ds_802_11_bg_scan_config bg_scan_config; + struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; + struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; + struct host_cmd_ds_802_11_associate associate; + struct host_cmd_ds_802_11_associate_rsp associate_rsp; + struct host_cmd_ds_802_11_deauthenticate deauth; + struct host_cmd_ds_802_11d_domain_info domain_info; + struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; + struct host_cmd_ds_11n_addba_req add_ba_req; + struct host_cmd_ds_11n_addba_rsp add_ba_rsp; + struct host_cmd_ds_11n_delba del_ba; + struct host_cmd_ds_txbuf_cfg tx_buf; + struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; + struct host_cmd_ds_11n_cfg htcfg; + struct host_cmd_ds_wmm_get_status get_wmm_status; + struct host_cmd_ds_802_11_key_material key_material; + struct host_cmd_ds_version_ext verext; + struct host_cmd_ds_mgmt_frame_reg reg_mask; + struct host_cmd_ds_remain_on_chan roc_cfg; + struct host_cmd_ds_802_11_ibss_status ibss_coalescing; + struct host_cmd_ds_mef_cfg mef_cfg; + struct host_cmd_ds_mem_access mem; + struct host_cmd_ds_mac_reg_access mac_reg; + struct host_cmd_ds_bbp_reg_access bbp_reg; + struct host_cmd_ds_rf_reg_access rf_reg; + struct host_cmd_ds_pmic_reg_access pmic_reg; + struct host_cmd_ds_set_bss_mode bss_mode; + struct host_cmd_ds_pcie_details pcie_host_spec; + struct host_cmd_ds_802_11_eeprom_access eeprom; + struct host_cmd_ds_802_11_subsc_evt subsc_evt; + struct host_cmd_ds_sys_config uap_sys_config; + struct host_cmd_ds_sta_deauth sta_deauth; + struct host_cmd_ds_sta_list sta_list; + struct host_cmd_11ac_vht_cfg vht_cfg; + struct host_cmd_ds_coalesce_cfg coalesce_cfg; + struct host_cmd_ds_chan_rpt_req chan_rpt_req; + struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; + struct host_cmd_ds_multi_chan_policy mc_policy; + struct host_cmd_ds_robust_coex coex; + struct host_cmd_ds_wakeup_reason hs_wakeup_reason; + struct host_cmd_ds_gtk_rekey_params rekey; + struct host_cmd_ds_chan_region_cfg reg_cfg; + struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl; + struct host_cmd_ds_sta_configure sta_cfg; + struct host_cmd_ds_add_station sta_info; + struct host_cmd_11ax_cfg ax_cfg; + struct host_cmd_11ax_cmd ax_cmd; + } params; +} __packed; + +struct nxpwifi_opt_sleep_confirm { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; + __le16 action; + __le16 resp_ctrl; +} __packed; + +#define VDLL_IND_TYPE_REQ 0 +#define VDLL_IND_TYPE_OFFSET 1 +#define VDLL_IND_TYPE_ERR_SIG 2 +#define VDLL_IND_TYPE_ERR_ID 3 +#define VDLL_IND_TYPE_SEC_ERR_ID 4 +#define VDLL_IND_TYPE_INTF_RESET 5 + +struct vdll_ind_event { + __le16 type; + __le16 vdll_id; + __le32 offset; + __le16 block_len; +} __packed; + +#endif /* !_NXPWIFI_FW_H_ */ From patchwork Mon Sep 30 06:36:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815428 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0563115099B; Mon, 30 Sep 2024 06:38:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678312; cv=fail; b=S7CH9ien1Ftp2YXKGgJr/yu2vUtuFMKq1Tx31Hm40Aj9aZ7+qMYKzKtANZVgdUIScqVu0o/WBj3WR05bZTLLnoq8CPcPq9loVR41nGl0IYIPxwcrEOQqMabvqCrQybGstJO9DKmYmJCLCA4mw7aQCk/zE1h9ZJDVETZFJRJIvsw= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678312; c=relaxed/simple; bh=b3Vu/JGUBOfRgXAestqFo4w+59iBKXJ22Cp7be6nw5Y=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=GAKcd3m39wMD8a07OyZ4utKloeucD1buBQ0Gu4HB2gaGWdNak1QlWk9VkAtch0TpgMCVSk0lAbNZ0ig3IDgcteefXnPSaTpMj1be6ysYuwjlj1ZlJHhV6SUw4dJUtD10u9U07cvRACKenmLRd/MZoS1JhtXzWj3O4VLQ2c1dHs4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=HWxqhWSM; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="HWxqhWSM" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=hRlWo8tUloITcVC7O+SHcAtcjqG3BLO1vG8LtTnC2lRDU6z5CuoPPNd6HpmNjVr+27gEv0gWML3hU6jAygJ56A/qhFygph6u4hTFPaZLDuudpmuxM2zafyCd8hiOwJfSyVjTQtZrBQI1PjeEB220pIINokAzDeCvcQZZx3u9BACCjiYytnlk4enyqKOu9yHt8mrZ4crlEgk8+878FjkPYvS8Qp5W2MH6C+/P/MT7A51xJWoqIaTQDhKDshGji4nYugeS0vbR3URLUAc2Lp/c2z41+RVTaxB64R+WPSExoYVbCG0zI1gzlu1LHghLtOl8Pmazfb41fCbuHP83Ajl1ew== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=0ip+k7MXcL2XkilWBek+Vih01W+SV9YUNOvUt3bGVEo=; b=SkH9HHJIaT3eBW/123J7dan3r7dYjfn1rA22lMvIAyJzx4uVamwS4CXT4mg+QLaIk4lTN+j5bPlMYY3z+YC7LiM0xys71N1mq/2v5eD/uOYaQksqvICpaN4A+2gL76aqDsW1rv7oZlOFFwuguutMma4ZMLrNy4CwQluBXE2pzGQOs3tU3zzjaU+Kdvkwlc2t+F//JR/RhUGcp1xE4QM5TRyJyj8Vb2610kXSBeBKE3eG9Ln3BzY6GWnu6zjRwNnznYUpBoszSJgsXCsIFXO+nnDRq69+qT57mesJSXLus0iwKCcv1ysBE6ieMFzjhe4HXSNNCTAb9d7GvJ2ldVmzlA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=0ip+k7MXcL2XkilWBek+Vih01W+SV9YUNOvUt3bGVEo=; b=HWxqhWSMuxayLbWrwNFMIeNgM4Y1mA7kFcPEZiHtZa4v0upNZkYXggEZi4w/4wQcNfiyKTw/Rd3SrKkz8KrMa82QEcRY3mmKtP7lhH+ovzIBe+ptgprfJp0iZE4i+mbqqZslQ2b9O4vzjAbnV5M38Ns/kCavmM5476Yj4XsCJJGZDLd39851zBgbldwOE0JtR811yWvKUhjP7000Px4wlcVzl/agmEdQmBBv0yrOvu6YGsEU9X2sqVDM+73gjdUvHCtwNrgtYwXWQ8FgntHQT0Siz5WSQ8s4IoG5wQv8R2PTsKGYCEaIl2UY9PHg4IsaS19cKkeS52hfC96hL2sG2w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:06 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:06 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 12/22] wifi: nxpwifi: add command and event files Date: Mon, 30 Sep 2024 14:36:51 +0800 Message-Id: <20240930063701.2566520-13-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 039063e7-5c32-450b-a776-08dce11a7020 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: MLuBzrpN10xNbrb40QzwziDuIK/CE+Cnb9RWQAd0F8jnrj4BaErsg3b9/GgG3BetoR7bmgz3s5r3IAl8ba/YE/tvX6w1vamTlUm+YsXxRRweqQjByplORvPEZGdya0avr4KTvGhfH6bxfZdwCUdXKRMdZWZR8NbtA6+IQCGq0pJsaRqc4W/6fpSPvtFKWdN7XJS7+S5lccH0oz9IeLdidjDe+J4tiurMJjr3F+cEo49U1oQDgutoZMdrMHn0gacXnzTlIOfp+gpM2xjXLmdcC5YIQphDib5OeS6b521sZAebbBTQwWw8ulWb4uvbhtQik6ibOcA3246iXhBu6qZtzMrJTlh4MT7PIyFvm5ZnHktM3r2FakhE0NeG2uUZvYy8LBa2+0pWPUeqwxpnPm1/MBh4Nt4RUrsHCk6pWWsQM8mlk1RSmgC6Fk7CLav+gHVc1s1qIlrPheR0CVDHK47+V+2F7lbLdNTgvWgsbohAOIhBvpQqLSMp7R0KLvzbWaof0Vy1qqm7x+xRPFIFAUzCEHqQRa0qOFeD92fnseBXwgtfpB4KJjMyVq9sDAVKg4O1MmhvhOLocljy7DqLkT/if4EMa8u0Lq1ZhBqzZyYndSKkA0+KYgrEDnlSpBniPvfNef+ZUoJOJ4bm09BiYTcbOsn/EIH9vjhq0cBU4Ji4ruMmjnLPDN/901Vuz6W4dAro2e32WNPe7+z+x9ATA8O4pni732ORJvi+BFYLwiR31AK1JDzLyNyZVhIE2lGGhiKnAu54JQkIAf0rgWVmHxkWReJdglfYrpm6W+yZxdQR00tLXzUL8RWCRev1uSxHVjqZ1j2pDFoLXyA7i/wU0VaDX37CSEvxPQf4eZbDipDfdW7QHaFpx/7pkYOJGPj8+7O9NFBHTsE03g8/9dBS+jMAL58udHNUtyPP4qj/X0lnhYsDYmFEbxR3TVuEPLpQOp2sktE/42IYSZMf/IaAP4JxRPD73/c1iViDeLvRCAdSKLwe5nPcWAvsSJNJR3lwoyPAiyYXOVBiY5NjUgTBCNisrcqw3AqqJbvJ9dHAPMZSNaC+YLkt3vzYPTD6rBRo53LzfG3lcszJ1cuAP76ao2mu6TZpBd8Dqr50quc4F0piuqEBgJGY2E1gSVGuKQmNPAdW926mtmCP1WbBzxp3eRKnooP2oPV66kn/C/N5qpIVj8Jt6yluhPJwWddf7QjuQqezaXQQIOjUca4ELy2/sMIQ75rikFR6HWHqQVEY6EX4TlLzGpXjJH+6oQDus8kirqecJ1kV5b8vamJPwdHpplhoxJrKrpk1W3OHU+6kxzigX4Jshm39lc5heJtZ1zbTYHKmeuJE6GXZ3br2hdtfogN1WISeyzAXK8nea2cABhorlUDzstL9G54B4WMXejvbwzpB0f1/OEKN0tGeEvS+AVZkSg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: qHzwU0Bnt5CEKgFt9nKAgPSk3owqfHKxquE47/gAhYlOb75bT1Bu0Gvm5DnOiyz4vmeAZu84h62BiwB10AAenLfnWpb9buv4Fs0kglaMSTbVWSI7Lneh7wSyeaGM6js8MTmaZPdW9fU/8K5zb3ojHcIxwZDI+yKRR2QoX9gW1V95LbCueVbLKBDig3VOyEv7sydEmMjgUxcNuPSJXAG9LRj2GLmIPX6has91Nxk2RtRbghbbrp1eu7mMzFe08pxO6TLLab5EAPUYRctqwt6OJmHzPw23Kz1fzYlUM/cDbGiVgXm3B5QzNP7E1loLBGqCeYWHuo3xpK8kXoXbrSNDSJ7JVW4nQo8xdVSTd7qVOT0Ohk3PCNhTrW2r+Yg1vAfjm7DRiuTuj8j7p5Bbsz2AuRMDkb8na0H0caWCsti5vKpct9GaPFSFMRraR4URYOOY3CLqT4lj9dA64+BLge6NcZyMF7tJHLtGPNjWRxDSIsOlliujF9+YuLg77Bdx6CQs4oZBCWnooADKmjsI8u8BEjOKSF8C2hnWNVeB0GyvM3POuB9a2SRtJ9KJr6q3HqNF5KQGh3XBDTM6jBKK92WxGomiQUgfYPIjFh5zIvmj3/TcRpUxC9Si8rAhyZL/gxdlnBzWeLRXYBWNGetL+LBJuNvsbvBFWZWAFPOhHBmJUFeZ4OUCrzR9SfjimPbW26tKJmvEltSZiboCIV5EN6QD14XHEJZat93ix91pPd19G9DRP4oMaosNt1G9toa/c3b+9urPa84CkSLNoR+ksdoB5PPNKP8LgAo8JfeGPPZOJB4BKDSoFXQhLxn1C2MPg0xbHzS96LXl2wPJ290Hhuw64h7dxDN/Zdie3PLB//sxK9ZYeF07n+7bWSepPevGW7nvmh8Nt/qwkcriZ2spHdS7fhyUXlWotmoCWMCvz6PJejsSRtp/flxchsM/BjK3M3WTvCPBDDo56MA1PgKJ4xNnvKjtHCxRh8R50nTt9GLnqgL284RcD2QnlEZK6MaVGF0Gf7MAIEs5xNWFslA6Y4KdbBeyyZa3jqtrcqlwxXSyib/TkSkewP+eQ2m+gvJMH4a1968SaRbIEe4oVVIyZyIiQMT8EeVmlH2crAQw4pR/cFOEKIWl61+W/bcj04HVwOYcqvsi+g2FOUUEWuCC7hVxNr9AGOgHhkPlpX3/l+7eNxqEdhjd2EoQG4lcVkCfN7le8wRgRbVDjV/61u722c+duKopJWXm7GO2PhmYwk6B6N+lEQLgf7buzG6XN+IiAQIq6BkS39FZOvNC/JEHUOLZVrHMRzbB5h/eooTz1yz7tnorssPlkwCK4juRh1e7k3JkZi0yNxLOYhe+PRM9gCQthgsLfHH+XBbQLxoDmpHKHL2DnbPFWxyJzBbwri9UPa9o3L08HWYWiYKAW2qMVdZGTbTZl7TeaPYOrUIMP9h/On+Mb4d1noDXqIrfDMhWnCGcPdik3CVVUfvcbAQPJUnuaqBTA48/Jh8ToPcpIJ0kargsoXtA8968ud0V0GdUxyzbZIZ7VQgI6ipomZG7Hzfaa4A3Aw7O3Aic0UXc2MQZCQlBMhnNpKAe12Off6//mlxs X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 039063e7-5c32-450b-a776-08dce11a7020 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:06.3684 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: kZGydXe6sOwtOtUVGyQpd4oI59atIRQ9EDC+28PtUaTFKtSKnHYjLq5GmOu0K7BhoEzWH5KTdjhqCFTX9AMB6g== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement command and event to communicate with NXP FW. Function table is used for the search of command and event. Handler for command response will be hooked to command node for the first search of corresponding command. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/cmdevt.c | 1286 +++++++ drivers/net/wireless/nxp/nxpwifi/cmdevt.h | 98 + drivers/net/wireless/nxp/nxpwifi/sta_cmd.c | 3309 ++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_event.c | 861 +++++ drivers/net/wireless/nxp/nxpwifi/uap_cmd.c | 1254 +++++++ drivers/net/wireless/nxp/nxpwifi/uap_event.c | 491 +++ 6 files changed, 7299 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/cmdevt.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/cmdevt.h create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_cmd.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_event.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/uap_cmd.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/uap_event.c diff --git a/drivers/net/wireless/nxp/nxpwifi/cmdevt.c b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c new file mode 100644 index 000000000000..66fed8d3847f --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: commands and events + * + * Copyright 2011-2024 NXP + */ + +#include +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" + +static void nxpwifi_cancel_pending_ioctl(struct nxpwifi_adapter *adapter); + +/* This function initializes a command node. + * + * The actual allocation of the node is not done by this function. It only + * initiates a node by filling it with default parameters. Similarly, + * allocation of the different buffers used (IOCTL buffer, data buffer) are + * not done by this function either. + */ +static void +nxpwifi_init_cmd_node(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node, + u32 cmd_no, void *data_buf, bool sync) +{ + cmd_node->priv = priv; + cmd_node->cmd_no = cmd_no; + + if (sync) { + cmd_node->wait_q_enabled = true; + cmd_node->cmd_wait_q_woken = false; + cmd_node->condition = &cmd_node->cmd_wait_q_woken; + } + cmd_node->data_buf = data_buf; + cmd_node->cmd_skb = cmd_node->skb; + cmd_node->cmd_resp = NULL; +} + +/* This function returns a command node from the free queue depending upon + * availability. + */ +static struct cmd_ctrl_node * +nxpwifi_get_cmd_node(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node; + + spin_lock_bh(&adapter->cmd_free_q_lock); + if (list_empty(&adapter->cmd_free_q)) { + nxpwifi_dbg(adapter, ERROR, + "GET_CMD_NODE: cmd node not available\n"); + spin_unlock_bh(&adapter->cmd_free_q_lock); + return NULL; + } + cmd_node = list_first_entry(&adapter->cmd_free_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_bh(&adapter->cmd_free_q_lock); + + return cmd_node; +} + +/* This function cleans up a command node. + * + * The function resets the fields including the buffer pointers. + * This function does not try to free the buffers. They must be + * freed before calling this function. + * + * This function will however call the receive completion callback + * in case a response buffer is still available before resetting + * the pointer. + */ +static void +nxpwifi_clean_cmd_node(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + cmd_node->cmd_no = 0; + cmd_node->cmd_flag = 0; + cmd_node->data_buf = NULL; + cmd_node->wait_q_enabled = false; + + if (cmd_node->cmd_skb) + skb_trim(cmd_node->cmd_skb, 0); + + if (cmd_node->resp_skb) { + adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); + cmd_node->resp_skb = NULL; + } +} + +/* This function returns a command to the command free queue. + * + * The function also calls the completion callback if required, before + * cleaning the command node and re-inserting it into the free queue. + */ +static void +nxpwifi_insert_cmd_to_free_q(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + if (!cmd_node) + return; + + if (cmd_node->wait_q_enabled) + nxpwifi_complete_cmd(adapter, cmd_node); + /* Clean the node */ + nxpwifi_clean_cmd_node(adapter, cmd_node); + + /* Insert node into cmd_free_q */ + spin_lock_bh(&adapter->cmd_free_q_lock); + list_add_tail(&cmd_node->list, &adapter->cmd_free_q); + spin_unlock_bh(&adapter->cmd_free_q_lock); +} + +/* This function reuses a command node. */ +void nxpwifi_recycle_cmd_node(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data; + + nxpwifi_insert_cmd_to_free_q(adapter, cmd_node); + + atomic_dec(&adapter->cmd_pending); + nxpwifi_dbg(adapter, CMD, + "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n", + le16_to_cpu(host_cmd->command), + atomic_read(&adapter->cmd_pending)); +} + +/* This function sends a host command to the firmware. + * + * The function copies the host command into the driver command + * buffer, which will be transferred to the firmware later by the + * main thread. + */ +static int nxpwifi_cmd_host_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct host_cmd_ds_command *cmd; + struct nxpwifi_ds_misc_cmd *pcmd_ptr; + + cmd = (struct host_cmd_ds_command *)cmd_node->skb->data; + pcmd_ptr = (struct nxpwifi_ds_misc_cmd *)cmd_node->data_buf; + + /* Copy the HOST command to command buffer */ + memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len); + nxpwifi_dbg(priv->adapter, CMD, + "cmd: host cmd size = %d\n", pcmd_ptr->len); + return 0; +} + +/* This function downloads a command to the firmware. + * + * The function performs sanity tests, sets the command sequence + * number and size, converts the header fields to CPU format before + * sending. Afterwards, it logs the command ID and action for debugging + * and sets up the command timeout timer. + */ +static int nxpwifi_dnld_cmd_to_fw(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + struct host_cmd_ds_command *host_cmd; + u16 cmd_code; + u16 cmd_size; + + if (!adapter || !cmd_node) + return -EINVAL; + + host_cmd = (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data); + + /* Sanity test */ + if (host_cmd->size == 0) { + nxpwifi_dbg(adapter, ERROR, + "DNLD_CMD: host_cmd is null\t" + "or cmd size is 0, not sending\n"); + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + nxpwifi_recycle_cmd_node(adapter, cmd_node); + return -EINVAL; + } + + cmd_code = le16_to_cpu(host_cmd->command); + cmd_node->cmd_no = cmd_code; + cmd_size = le16_to_cpu(host_cmd->size); + + if (adapter->hw_status == NXPWIFI_HW_STATUS_RESET && + cmd_code != HOST_CMD_FUNC_SHUTDOWN && + cmd_code != HOST_CMD_FUNC_INIT) { + nxpwifi_dbg(adapter, ERROR, + "DNLD_CMD: FW in reset state, ignore cmd %#x\n", + cmd_code); + nxpwifi_recycle_cmd_node(adapter, cmd_node); + nxpwifi_queue_work(adapter, &adapter->main_work); + return -EPERM; + } + + /* Set command sequence number */ + adapter->seq_num++; + host_cmd->seq_num = cpu_to_le16(HOST_SET_SEQ_NO_BSS_INFO + (adapter->seq_num, + cmd_node->priv->bss_num, + cmd_node->priv->bss_type)); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd = cmd_node; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + /* Adjust skb length */ + if (cmd_node->cmd_skb->len > cmd_size) + /* cmd_size is less than sizeof(struct host_cmd_ds_command). + * Trim off the unused portion. + */ + skb_trim(cmd_node->cmd_skb, cmd_size); + else if (cmd_node->cmd_skb->len < cmd_size) + /* cmd_size is larger than sizeof(struct host_cmd_ds_command) + * because we have appended custom IE TLV. Increase skb length + * accordingly. + */ + skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); + + nxpwifi_dbg(adapter, CMD, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + cmd_code, + get_unaligned_le16((u8 *)host_cmd + S_DS_GEN), + cmd_size, le16_to_cpu(host_cmd->seq_num)); + nxpwifi_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size); + + skb_push(cmd_node->cmd_skb, adapter->intf_hdr_len); + ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_CMD, + cmd_node->cmd_skb, NULL); + skb_pull(cmd_node->cmd_skb, adapter->intf_hdr_len); + + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "DNLD_CMD: host to card failed\n"); + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd = NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + adapter->dbg.num_cmd_host_to_card_failure++; + return ret; + } + + /* Save the last command id and action to debug log */ + adapter->dbg.last_cmd_index = + (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = + get_unaligned_le16((u8 *)host_cmd + S_DS_GEN); + + /* Setup the timer after transmit command, except that specific + * command might not have command response. + */ + if (cmd_code != HOST_CMD_FW_DUMP_EVENT) + mod_timer(&adapter->cmd_timer, + jiffies + msecs_to_jiffies(NXPWIFI_TIMER_10S)); + + /* Clear BSS_NO_BITS from HOST */ + cmd_code &= HOST_CMD_ID_MASK; + + return 0; +} + +/* This function downloads a sleep confirm command to the firmware. + * + * The function performs sanity tests, sets the command sequence + * number and size, converts the header fields to CPU format before + * sending. + * + * No responses are needed for sleep confirm command. + */ +static int nxpwifi_dnld_sleep_confirm_cmd(struct nxpwifi_adapter *adapter) +{ + int ret; + struct nxpwifi_private *priv; + struct nxpwifi_opt_sleep_confirm *sleep_cfm_buf = + (struct nxpwifi_opt_sleep_confirm *) + adapter->sleep_cfm->data; + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + adapter->seq_num++; + sleep_cfm_buf->seq_num = + cpu_to_le16(HOST_SET_SEQ_NO_BSS_INFO + (adapter->seq_num, priv->bss_num, + priv->bss_type)); + + nxpwifi_dbg(adapter, CMD, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + le16_to_cpu(sleep_cfm_buf->command), + le16_to_cpu(sleep_cfm_buf->action), + le16_to_cpu(sleep_cfm_buf->size), + le16_to_cpu(sleep_cfm_buf->seq_num)); + nxpwifi_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf, + le16_to_cpu(sleep_cfm_buf->size)); + + skb_push(adapter->sleep_cfm, adapter->intf_hdr_len); + ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_CMD, + adapter->sleep_cfm, NULL); + skb_pull(adapter->sleep_cfm, adapter->intf_hdr_len); + + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SLEEP_CFM: failed\n"); + adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; + return ret; + } + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) + /* Response is not needed for sleep confirm command */ + adapter->ps_state = PS_STATE_SLEEP; + else + adapter->ps_state = PS_STATE_SLEEP_CFM; + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && + (test_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags) && + !adapter->sleep_period.period)) { + adapter->pm_wakeup_card_req = true; + nxpwifi_hs_activated_event(nxpwifi_get_priv + (adapter, NXPWIFI_BSS_ROLE_ANY), true); + } + + return ret; +} + +/* This function allocates the command buffers and links them to + * the command free queue. + * + * The driver uses a pre allocated number of command buffers, which + * are created at driver initializations and freed at driver cleanup. + * Every command needs to obtain a command buffer from this pool before + * it can be issued. The command free queue lists the command buffers + * currently free to use, while the command pending queue lists the + * command buffers already in use and awaiting handling. Command buffers + * are returned to the free queue after use. + */ +int nxpwifi_alloc_cmd_buffer(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_array; + u32 i; + + /* Allocate and initialize struct cmd_ctrl_node */ + cmd_array = kcalloc(NXPWIFI_NUM_OF_CMD_BUFFER, + sizeof(struct cmd_ctrl_node), GFP_KERNEL); + if (!cmd_array) + return -ENOMEM; + + adapter->cmd_pool = cmd_array; + + /* Allocate and initialize command buffers */ + for (i = 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) { + cmd_array[i].skb = dev_alloc_skb(NXPWIFI_SIZE_OF_CMD_BUFFER); + if (!cmd_array[i].skb) + return -ENOMEM; + } + + for (i = 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) + nxpwifi_insert_cmd_to_free_q(adapter, &cmd_array[i]); + + return 0; +} + +/* This function frees the command buffers. + * + * The function calls the completion callback for all the command + * buffers that still have response buffers associated with them. + */ +void nxpwifi_free_cmd_buffer(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_array; + u32 i; + + /* Need to check if cmd pool is allocated or not */ + if (!adapter->cmd_pool) { + nxpwifi_dbg(adapter, FATAL, + "info: FREE_CMD_BUF: cmd_pool is null\n"); + return; + } + + cmd_array = adapter->cmd_pool; + + /* Release shared memory buffers */ + for (i = 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) { + if (cmd_array[i].skb) { + nxpwifi_dbg(adapter, CMD, + "cmd: free cmd buffer %d\n", i); + dev_kfree_skb_any(cmd_array[i].skb); + } + if (!cmd_array[i].resp_skb) + continue; + + dev_kfree_skb_any(cmd_array[i].resp_skb); + } + /* Release struct cmd_ctrl_node */ + if (adapter->cmd_pool) { + nxpwifi_dbg(adapter, CMD, + "cmd: free cmd pool\n"); + kfree(adapter->cmd_pool); + adapter->cmd_pool = NULL; + } +} + +/* This function handles events generated by firmware. + * + * Event body of events received from firmware are not used (though they are + * saved), only the event ID is used. Some events are re-invoked by + * the driver, with a new event body. + * + * After processing, the function calls the completion callback + * for cleanup. + */ +int nxpwifi_process_event(struct nxpwifi_adapter *adapter) +{ + int ret, i; + struct nxpwifi_private *priv = + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + struct sk_buff *skb = adapter->event_skb; + u32 eventcause; + struct nxpwifi_rxinfo *rx_info; + + if ((adapter->event_cause & EVENT_ID_MASK) == EVENT_RADAR_DETECTED) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (nxpwifi_is_11h_active(priv)) { + adapter->event_cause |= + ((priv->bss_num & 0xff) << 16) | + ((priv->bss_type & 0xff) << 24); + break; + } + } + } + + eventcause = adapter->event_cause; + + /* Save the last event to debug log */ + adapter->dbg.last_event_index = + (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_event[adapter->dbg.last_event_index] = + (u16)eventcause; + + /* Get BSS number and corresponding priv */ + priv = nxpwifi_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (!priv) + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + /* Clear BSS_NO_BITS from event */ + eventcause &= EVENT_ID_MASK; + adapter->event_cause = eventcause; + + if (skb) { + rx_info = NXPWIFI_SKB_RXCB(skb); + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num = priv->bss_num; + rx_info->bss_type = priv->bss_type; + nxpwifi_dbg_dump(adapter, EVT_D, "Event Buf:", + skb->data, skb->len); + } + + nxpwifi_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause); + + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) + ret = nxpwifi_process_uap_event(priv); + else + ret = nxpwifi_process_sta_event(priv); + + adapter->event_cause = 0; + adapter->event_skb = NULL; + adapter->if_ops.event_complete(adapter, skb); + + return ret; +} + +/* This function prepares a command and send it to the firmware. + * + * Preparation includes - + * - Sanity tests to make sure the card is still present or the FW + * is not reset + * - Getting a new command node from the command free queue + * - Initializing the command node for default parameters + * - Fill up the non-default parameters and buffer pointers + * - Add the command to pending queue + */ +int nxpwifi_send_cmd(struct nxpwifi_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync) +{ + int ret; + struct nxpwifi_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + + if (!adapter) { + pr_err("PREP_CMD: adapter is NULL\n"); + return -EINVAL; + } + + if (test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: device in suspended state\n"); + return -EPERM; + } + + if (test_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags) && + cmd_no != HOST_CMD_802_11_HS_CFG_ENH) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: host entering sleep state\n"); + return -EPERM; + } + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: card is removed\n"); + return -EPERM; + } + + if (test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: FW is in bad state\n"); + return -EPERM; + } + + if (adapter->hw_status == NXPWIFI_HW_STATUS_RESET) { + if (cmd_no != HOST_CMD_FUNC_INIT) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: FW in reset state\n"); + return -EPERM; + } + } + + if (priv->adapter->hs_activated_manually && + cmd_no != HOST_CMD_802_11_HS_CFG_ENH) { + nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD); + priv->adapter->hs_activated_manually = false; + } + + /* Get a new command node */ + cmd_node = nxpwifi_get_cmd_node(adapter); + + if (!cmd_node) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: no free cmd node\n"); + return -ENOMEM; + } + + /* Initialize the command node */ + nxpwifi_init_cmd_node(priv, cmd_node, cmd_no, data_buf, sync); + + if (!cmd_node->cmd_skb) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: no free cmd buf\n"); + return -ENOMEM; + } + + skb_put_zero(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)); + + /* Prepare command */ + if (cmd_no) { + switch (cmd_no) { + case HOST_CMD_UAP_SYS_CONFIG: + case HOST_CMD_UAP_BSS_START: + case HOST_CMD_UAP_BSS_STOP: + case HOST_CMD_UAP_STA_DEAUTH: + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_STA_LIST: + case HOST_CMD_CHAN_REPORT_REQUEST: + case HOST_CMD_ADD_NEW_STATION: + ret = nxpwifi_uap_prepare_cmd(priv, cmd_node, + cmd_action, cmd_oid); + break; + default: + ret = nxpwifi_sta_prepare_cmd(priv, cmd_node, + cmd_action, cmd_oid); + break; + } + } else { + ret = nxpwifi_cmd_host_cmd(priv, cmd_node); + cmd_node->cmd_flag |= CMD_F_HOSTCMD; + } + + /* Return error, since the command preparation failed */ + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "PREP_CMD: cmd %#x preparation failed\n", + cmd_no); + nxpwifi_insert_cmd_to_free_q(adapter, cmd_node); + return ret; + } + + /* Send command */ + if (cmd_no == HOST_CMD_802_11_SCAN || + cmd_no == HOST_CMD_802_11_SCAN_EXT) { + nxpwifi_queue_scan_cmd(priv, cmd_node); + } else { + nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node); + nxpwifi_queue_work(adapter, &adapter->main_work); + if (cmd_node->wait_q_enabled) + ret = nxpwifi_wait_queue_complete(adapter, cmd_node); + } + + return ret; +} + +/* This function queues a command to the command pending queue. + * + * This in effect adds the command to the command list to be executed. + * Exit PS command is handled specially, by placing it always to the + * front of the command queue. + */ +void +nxpwifi_insert_cmd_to_pending_q(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + struct host_cmd_ds_command *host_cmd = NULL; + u16 command; + bool add_tail = true; + + host_cmd = (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data); + if (!host_cmd) { + nxpwifi_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n"); + return; + } + + command = le16_to_cpu(host_cmd->command); + + /* Exit_PS command needs to be queued in the header always. */ + if (command == HOST_CMD_802_11_PS_MODE_ENH) { + struct host_cmd_ds_802_11_ps_mode_enh *pm = + &host_cmd->params.psmode_enh; + if ((le16_to_cpu(pm->action) == DIS_PS) || + (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { + if (adapter->ps_state != PS_STATE_AWAKE) + add_tail = false; + } + } + + /* Same with exit host sleep cmd, luckily that can't happen at the same time as EXIT_PS */ + if (command == HOST_CMD_802_11_HS_CFG_ENH) { + struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = + &host_cmd->params.opt_hs_cfg; + + if (le16_to_cpu(hs_cfg->action) == HS_ACTIVATE) + add_tail = false; + } + + spin_lock_bh(&adapter->cmd_pending_q_lock); + if (add_tail) + list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); + else + list_add(&cmd_node->list, &adapter->cmd_pending_q); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + + atomic_inc(&adapter->cmd_pending); + nxpwifi_dbg(adapter, CMD, + "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n", + command, atomic_read(&adapter->cmd_pending)); +} + +/* This function executes the next command in command pending queue. + * + * This function will fail if a command is already in processing stage, + * otherwise it will dequeue the first command from the command pending + * queue and send to the firmware. + * + * If the device is currently in host sleep mode, any commands, except the + * host sleep configuration command will de-activate the host sleep. For PS + * mode, the function will put the firmware back to sleep if applicable. + */ +int nxpwifi_exec_next_cmd(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + struct cmd_ctrl_node *cmd_node; + int ret = 0; + struct host_cmd_ds_command *host_cmd; + + /* Check if already in processing */ + if (adapter->curr_cmd) { + nxpwifi_dbg(adapter, FATAL, + "EXEC_NEXT_CMD: cmd in processing\n"); + return -EBUSY; + } + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + /* Check if any command is pending */ + spin_lock_bh(&adapter->cmd_pending_q_lock); + if (list_empty(&adapter->cmd_pending_q)) { + spin_unlock_bh(&adapter->cmd_pending_q_lock); + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + return 0; + } + cmd_node = list_first_entry(&adapter->cmd_pending_q, + struct cmd_ctrl_node, list); + + host_cmd = (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data); + priv = cmd_node->priv; + + if (adapter->ps_state != PS_STATE_AWAKE) { + nxpwifi_dbg(adapter, ERROR, + "%s: cannot send cmd in sleep state,\t" + "this should not happen\n", __func__); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + return ret; + } + + list_del(&cmd_node->list); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + ret = nxpwifi_dnld_cmd_to_fw(priv, cmd_node); + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + /* Any command sent to the firmware when host is in sleep + * mode should de-configure host sleep. We should skip the + * host sleep configuration command itself though + */ + if (priv && host_cmd->command != + cpu_to_le16(HOST_CMD_802_11_HS_CFG_ENH)) { + if (adapter->hs_activated) { + clear_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + nxpwifi_hs_activated_event(priv, false); + } + } + + return ret; +} + +static void +nxpwifi_process_cmdresp_error(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *pm; + + nxpwifi_dbg(adapter, ERROR, + "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + + switch (le16_to_cpu(resp->command)) { + case HOST_CMD_802_11_PS_MODE_ENH: + pm = &resp->params.psmode_enh; + nxpwifi_dbg(adapter, ERROR, + "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", + resp->result, le16_to_cpu(pm->action)); + break; + case HOST_CMD_802_11_SCAN: + case HOST_CMD_802_11_SCAN_EXT: + nxpwifi_cancel_scan(adapter); + break; + + case HOST_CMD_MAC_CONTROL: + break; + + case HOST_CMD_SDIO_SP_RX_AGGR_CFG: + nxpwifi_dbg(adapter, MSG, + "SDIO RX single-port aggregation Not support\n"); + break; + + default: + break; + } + /* Handling errors here */ + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd = NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); +} + +/* This function handles the command response. + * + * After processing, the function cleans the command node and puts + * it back to the command free queue. + */ +int nxpwifi_process_cmdresp(struct nxpwifi_adapter *adapter) +{ + struct host_cmd_ds_command *resp; + struct nxpwifi_private *priv = + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + int ret = 0; + u16 orig_cmdresp_no; + u16 cmdresp_no; + u16 cmdresp_result; + + if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { + resp = (struct host_cmd_ds_command *)adapter->upld_buf; + nxpwifi_dbg(adapter, ERROR, + "CMD_RESP: NULL curr_cmd, %#x\n", + le16_to_cpu(resp->command)); + return -EINVAL; + } + + resp = (struct host_cmd_ds_command *)adapter->curr_cmd->resp_skb->data; + orig_cmdresp_no = le16_to_cpu(resp->command); + cmdresp_no = (orig_cmdresp_no & HOST_CMD_ID_MASK); + + if (adapter->curr_cmd->cmd_no != cmdresp_no) { + nxpwifi_dbg(adapter, ERROR, + "cmdresp error: cmd=0x%x cmd_resp=0x%x\n", + adapter->curr_cmd->cmd_no, cmdresp_no); + return -EINVAL; + } + /* Now we got response from FW, cancel the command timer */ + del_timer_sync(&adapter->cmd_timer); + clear_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags); + + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + /* Copy original response back to response buffer */ + struct nxpwifi_ds_misc_cmd *hostcmd; + u16 size = le16_to_cpu(resp->size); + + nxpwifi_dbg(adapter, INFO, + "info: host cmd resp size = %d\n", size); + size = min_t(u16, size, NXPWIFI_SIZE_OF_CMD_BUFFER); + if (adapter->curr_cmd->data_buf) { + hostcmd = adapter->curr_cmd->data_buf; + hostcmd->len = size; + memcpy(hostcmd->cmd, resp, size); + } + } + + /* Get BSS number and corresponding priv */ + priv = nxpwifi_get_priv_by_id + (adapter, HOST_GET_BSS_NO(le16_to_cpu(resp->seq_num)), + HOST_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); + if (!priv) + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + /* Clear RET_BIT from HOST */ + resp->command = cpu_to_le16(orig_cmdresp_no & HOST_CMD_ID_MASK); + + cmdresp_no = le16_to_cpu(resp->command); + cmdresp_result = le16_to_cpu(resp->result); + + /* Save the last command response to debug log */ + adapter->dbg.last_cmd_resp_index = + (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = + orig_cmdresp_no; + + nxpwifi_dbg(adapter, CMD, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + orig_cmdresp_no, cmdresp_result, + le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); + nxpwifi_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp, + le16_to_cpu(resp->size)); + + if (!(orig_cmdresp_no & HOST_RET_BIT)) { + nxpwifi_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n"); + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd = NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + return -EINVAL; + } + + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; + if (cmdresp_result == HOST_RESULT_OK && + cmdresp_no == HOST_CMD_802_11_HS_CFG_ENH) + ret = nxpwifi_ret_802_11_hs_cfg(priv, resp); + } else { + if (resp->result != HOST_RESULT_OK) { + nxpwifi_process_cmdresp_error(priv, resp); + return -EFAULT; + } + if (adapter->curr_cmd->cmd_resp) { + void *data_buf = adapter->curr_cmd->data_buf; + + ret = adapter->curr_cmd->cmd_resp(priv, resp, + cmdresp_no, + data_buf); + } + } + + /* Check init command response */ + if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING) { + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: cmd %#x failed during\t" + "initialization\n", __func__, cmdresp_no); + nxpwifi_init_fw_complete(adapter); + return ret; + } else if (adapter->last_init_cmd == cmdresp_no) { + adapter->hw_status = NXPWIFI_HW_STATUS_INIT_DONE; + } + } + + if (adapter->curr_cmd) { + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = ret; + + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd = NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + } + + return ret; +} + +void nxpwifi_process_assoc_resp(struct nxpwifi_adapter *adapter) +{ + struct cfg80211_rx_assoc_resp_data assoc_resp = { + .uapsd_queues = -1, + }; + struct nxpwifi_private *priv = + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + + if (priv->assoc_rsp_size) { + assoc_resp.links[0].bss = priv->req_bss; + assoc_resp.buf = priv->assoc_rsp_buf; + assoc_resp.len = priv->assoc_rsp_size; + wiphy_lock(priv->wdev.wiphy); + cfg80211_rx_assoc_resp(priv->netdev, + &assoc_resp); + wiphy_unlock(priv->wdev.wiphy); + priv->assoc_rsp_size = 0; + } +} + +/* This function handles the timeout of command sending. + * + * It will re-send the same command again. + */ +void +nxpwifi_cmd_timeout_func(struct timer_list *t) +{ + struct nxpwifi_adapter *adapter = from_timer(adapter, t, cmd_timer); + struct cmd_ctrl_node *cmd_node; + + set_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags); + if (!adapter->curr_cmd) { + nxpwifi_dbg(adapter, ERROR, + "cmd: empty curr_cmd\n"); + return; + } + cmd_node = adapter->curr_cmd; + if (cmd_node) { + adapter->dbg.timeout_cmd_id = + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; + adapter->dbg.timeout_cmd_act = + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; + nxpwifi_dbg(adapter, MSG, + "%s: Timeout cmd id = %#x, act = %#x\n", __func__, + adapter->dbg.timeout_cmd_id, + adapter->dbg.timeout_cmd_act); + + nxpwifi_dbg(adapter, MSG, + "num_data_h2c_failure = %d\n", + adapter->dbg.num_tx_host_to_card_failure); + nxpwifi_dbg(adapter, MSG, + "num_cmd_h2c_failure = %d\n", + adapter->dbg.num_cmd_host_to_card_failure); + + nxpwifi_dbg(adapter, MSG, + "is_cmd_timedout = %d\n", + test_bit(NXPWIFI_IS_CMD_TIMEDOUT, + &adapter->work_flags)); + nxpwifi_dbg(adapter, MSG, + "num_tx_timeout = %d\n", + adapter->dbg.num_tx_timeout); + + nxpwifi_dbg(adapter, MSG, + "last_cmd_index = %d\n", + adapter->dbg.last_cmd_index); + nxpwifi_dbg(adapter, MSG, + "last_cmd_id: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_id), + adapter->dbg.last_cmd_id); + nxpwifi_dbg(adapter, MSG, + "last_cmd_act: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_act), + adapter->dbg.last_cmd_act); + + nxpwifi_dbg(adapter, MSG, + "last_cmd_resp_index = %d\n", + adapter->dbg.last_cmd_resp_index); + nxpwifi_dbg(adapter, MSG, + "last_cmd_resp_id: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_resp_id), + adapter->dbg.last_cmd_resp_id); + + nxpwifi_dbg(adapter, MSG, + "last_event_index = %d\n", + adapter->dbg.last_event_index); + nxpwifi_dbg(adapter, MSG, + "last_event: %*ph\n", + (int)sizeof(adapter->dbg.last_event), + adapter->dbg.last_event); + + nxpwifi_dbg(adapter, MSG, + "data_sent=%d cmd_sent=%d\n", + adapter->data_sent, adapter->cmd_sent); + + nxpwifi_dbg(adapter, MSG, + "ps_mode=%d ps_state=%d\n", + adapter->ps_mode, adapter->ps_state); + + if (cmd_node->wait_q_enabled) { + adapter->cmd_wait_q.status = -ETIMEDOUT; + nxpwifi_cancel_pending_ioctl(adapter); + } + } + if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING) { + nxpwifi_init_fw_complete(adapter); + return; + } + + if (adapter->if_ops.device_dump) + adapter->if_ops.device_dump(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +void +nxpwifi_cancel_pending_scan_cmd(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; + + /* Cancel all pending scan command */ + spin_lock_bh(&adapter->scan_pending_q_lock); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + nxpwifi_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_bh(&adapter->scan_pending_q_lock); +} + +/* This function cancels all the pending commands. + * + * The current command, all commands in command pending queue and all scan + * commands in scan pending queue are cancelled. All the completion callbacks + * are called with failure status to ensure cleanup. + */ +void +nxpwifi_cancel_all_pending_cmd(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + /* Cancel current cmd */ + if (adapter->curr_cmd && adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = -1; + nxpwifi_complete_cmd(adapter, adapter->curr_cmd); + adapter->curr_cmd->wait_q_enabled = false; + /* no recycle probably wait for response */ + } + /* Cancel all pending command */ + spin_lock_bh(&adapter->cmd_pending_q_lock); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->cmd_pending_q, list) { + list_del(&cmd_node->list); + + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + nxpwifi_recycle_cmd_node(adapter, cmd_node); + } + spin_unlock_bh(&adapter->cmd_pending_q_lock); + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + nxpwifi_cancel_scan(adapter); +} + +/* This function cancels all pending commands that matches with + * the given IOCTL request. + * + * Both the current command buffer and the pending command queue are + * searched for matching IOCTL request. The completion callback of + * the matched command is called with failure status to ensure cleanup. + * In case of scan commands, all pending commands in scan pending queue + * are cancelled. + */ +static void +nxpwifi_cancel_pending_ioctl(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL; + + if (adapter->curr_cmd && + adapter->curr_cmd->wait_q_enabled) { + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + cmd_node = adapter->curr_cmd; + /* setting curr_cmd to NULL is quite dangerous, because + * nxpwifi_process_cmdresp checks curr_cmd to be != NULL + * at the beginning then relies on it and dereferences + * it at will + * this probably works since nxpwifi_cmd_timeout_func + * is the only caller of this function and responses + * at that point + */ + adapter->curr_cmd = NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + nxpwifi_recycle_cmd_node(adapter, cmd_node); + } + + nxpwifi_cancel_scan(adapter); +} + +/* This function sends the sleep confirm command to firmware, if + * possible. + * + * The sleep confirm command cannot be issued if command response, + * data response or event response is awaiting handling, or if we + * are in the middle of sending a command, or expecting a command + * response. + */ +void +nxpwifi_check_ps_cond(struct nxpwifi_adapter *adapter) +{ + if (!adapter->cmd_sent && !atomic_read(&adapter->tx_hw_pending) && + !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) + nxpwifi_dnld_sleep_confirm_cmd(adapter); + else + nxpwifi_dbg(adapter, CMD, + "cmd: Delay Sleep Confirm (%s%s%s%s)\n", + (adapter->cmd_sent) ? "D" : "", + atomic_read(&adapter->tx_hw_pending) ? "T" : "", + (adapter->curr_cmd) ? "C" : "", + (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); +} + +/* This function sends a Host Sleep activated event to applications. + * + * This event is generated by the driver, with a blank event body. + */ +void +nxpwifi_hs_activated_event(struct nxpwifi_private *priv, u8 activated) +{ + if (activated) { + if (test_bit(NXPWIFI_IS_HS_CONFIGURED, + &priv->adapter->work_flags)) { + priv->adapter->hs_activated = true; + nxpwifi_update_rxreor_flags(priv->adapter, + RXREOR_FORCE_NO_DROP); + nxpwifi_dbg(priv->adapter, EVENT, + "event: hs_activated\n"); + priv->adapter->hs_activate_wait_q_woken = true; + wake_up_interruptible(&priv->adapter->hs_activate_wait_q); + } else { + nxpwifi_dbg(priv->adapter, EVENT, + "event: HS not configured\n"); + } + } else { + nxpwifi_dbg(priv->adapter, EVENT, + "event: hs_deactivated\n"); + priv->adapter->hs_activated = false; + } +} + +/* This function handles the command response of a Host Sleep configuration + * command. + * + * Handling includes changing the header fields into CPU format + * and setting the current host sleep activation status in driver. + * + * In case host sleep status change, the function generates an event to + * notify the applications. + */ +int nxpwifi_ret_802_11_hs_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = + &resp->params.opt_hs_cfg; + u32 conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); + + if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE)) { + nxpwifi_hs_activated_event(priv, true); + goto done; + } else { + nxpwifi_dbg(adapter, CMD, + "cmd: CMD_RESP: HS_CFG cmd reply\t" + " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", + resp->result, conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap); + } + if (conditions != HS_CFG_CANCEL) { + set_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags); + } else { + clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags); + if (adapter->hs_activated) + nxpwifi_hs_activated_event(priv, false); + } + +done: + return 0; +} + +/* This function wakes up the adapter and generates a Host Sleep + * cancel event on receiving the power up interrupt. + */ +void +nxpwifi_process_hs_config(struct nxpwifi_adapter *adapter) +{ + nxpwifi_dbg(adapter, INFO, + "info: %s: auto cancelling host sleep\t" + "since there is interrupt from the firmware\n", + __func__); + + adapter->if_ops.wakeup(adapter); + + if (adapter->hs_activated_manually) { + nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY), + NXPWIFI_ASYNC_CMD); + adapter->hs_activated_manually = false; + } + + adapter->hs_activated = false; + clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags); + clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + nxpwifi_hs_activated_event(nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY), + false); +} +EXPORT_SYMBOL_GPL(nxpwifi_process_hs_config); + +/* This function handles the command response of a sleep confirm command. + * + * The function sets the card state to SLEEP if the response indicates success. + */ +void +nxpwifi_process_sleep_confirm_resp(struct nxpwifi_adapter *adapter, + u8 *pbuf, u32 upld_len) +{ + struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *)pbuf; + u16 result = le16_to_cpu(cmd->result); + u16 command = le16_to_cpu(cmd->command); + u16 seq_num = le16_to_cpu(cmd->seq_num); + + if (!upld_len) { + nxpwifi_dbg(adapter, ERROR, + "%s: cmd size is 0\n", __func__); + return; + } + + nxpwifi_dbg(adapter, CMD, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + command, result, le16_to_cpu(cmd->size), seq_num); + + /* Update sequence number */ + seq_num = HOST_GET_SEQ_NO(seq_num); + /* Clear RET_BIT from HOST */ + command &= HOST_CMD_ID_MASK; + + if (command != HOST_CMD_802_11_PS_MODE_ENH) { + nxpwifi_dbg(adapter, ERROR, + "%s: rcvd unexpected resp for cmd %#x, result = %x\n", + __func__, command, result); + return; + } + + if (result) { + nxpwifi_dbg(adapter, ERROR, + "%s: sleep confirm cmd failed\n", + __func__); + adapter->pm_wakeup_card_req = false; + adapter->ps_state = PS_STATE_AWAKE; + return; + } + adapter->pm_wakeup_card_req = true; + if (test_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags)) + nxpwifi_hs_activated_event(nxpwifi_get_priv + (adapter, NXPWIFI_BSS_ROLE_ANY), + true); + adapter->ps_state = PS_STATE_SLEEP; + cmd->command = cpu_to_le16(command); + cmd->seq_num = cpu_to_le16(seq_num); +} +EXPORT_SYMBOL_GPL(nxpwifi_process_sleep_confirm_resp); diff --git a/drivers/net/wireless/nxp/nxpwifi/cmdevt.h b/drivers/net/wireless/nxp/nxpwifi/cmdevt.h new file mode 100644 index 000000000000..ccd4f23c39f2 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: commands and events + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_CMD_EVT_H_ +#define _NXPWIFI_CMD_EVT_H_ + +struct nxpwifi_cmd_entry { + u16 cmd_no; + int (*prepare_cmd)(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type); + int (*cmd_resp)(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf); +}; + +struct nxpwifi_evt_entry { + u32 event_cause; + int (*event_handler)(struct nxpwifi_private *priv); +}; + +static inline int +nxpwifi_cmd_fill_head_only(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(cmd_no); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +int nxpwifi_send_cmd(struct nxpwifi_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync); +int nxpwifi_sta_prepare_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node, + u16 cmd_action, u32 cmd_oid); +int nxpwifi_dnld_dt_cfgdata(struct nxpwifi_private *priv, + struct device_node *node, const char *prefix); +int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool init); +int nxpwifi_uap_prepare_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node, + u16 cmd_action, u32 type); +int nxpwifi_set_secure_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_config, + struct cfg80211_ap_settings *params); +void nxpwifi_set_ht_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void nxpwifi_set_vht_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void nxpwifi_set_tpc_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void nxpwifi_set_uap_rates(struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void nxpwifi_set_vht_width(struct nxpwifi_private *priv, + enum nl80211_chan_width width, + bool ap_11ac_disable); +bool nxpwifi_check_11ax_capability(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +int nxpwifi_set_11ax_status(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void nxpwifi_set_sys_config_invalid_data(struct nxpwifi_uap_bss_param *config); +void nxpwifi_set_wmm_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void nxpwifi_config_uap_11d(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *beacon_data); +void nxpwifi_uap_set_channel(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef); +int nxpwifi_config_start_uap(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg); + +int nxpwifi_process_event(struct nxpwifi_adapter *adapter); +int nxpwifi_process_sta_event(struct nxpwifi_private *priv); +int nxpwifi_process_uap_event(struct nxpwifi_private *priv); +void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason, + bool from_ap); +void nxpwifi_process_multi_chan_event(struct nxpwifi_private *priv, + struct sk_buff *event_skb); +void nxpwifi_process_tx_pause_event(struct nxpwifi_private *priv, + struct sk_buff *event); +void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv, + struct sk_buff *event_skb); + +#endif /* !_NXPWIFI_CMD_EVT_H_ */ diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c new file mode 100644 index 000000000000..11a179bca0d5 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c @@ -0,0 +1,3309 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: station command handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" +#include "11ax.h" + +static bool disable_auto_ds; + +static int +nxpwifi_cmd_sta_get_hw_spec(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; + + cmd->command = cpu_to_le16(HOST_CMD_GET_HW_SPEC); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + + S_DS_GEN); + memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); + + return 0; +} + +static int +nxpwifi_ret_sta_get_hw_spec(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie_types_header *tlv; + struct hw_spec_api_rev *api_rev; + struct hw_spec_max_conn *max_conn; + struct hw_spec_extension *hw_he_cap; + struct hw_spec_fw_cap_info *fw_cap; + struct hw_spec_secure_boot_uuid *sb_uuid; + u16 resp_size, api_id; + int i, left_len, parsed_len = 0; + + adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); + + if (IS_SUPPORT_MULTI_BANDS(adapter)) + adapter->fw_bands = GET_FW_DEFAULT_BANDS(adapter); + else + adapter->fw_bands = BAND_B; + + if ((adapter->fw_bands & BAND_A) && (adapter->fw_bands & BAND_GN)) + adapter->fw_bands |= BAND_AN; + if (!(adapter->fw_bands & BAND_G) && (adapter->fw_bands & BAND_GN)) + adapter->fw_bands &= ~BAND_GN; + + adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); + adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; + adapter->number_of_antenna = + le16_to_cpu(hw_spec->number_of_antenna) & 0xf; + + if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { + adapter->is_hw_11ac_capable = true; + + /* Copy 11AC cap */ + adapter->hw_dot_11ac_dev_cap = + le32_to_cpu(hw_spec->dot_11ac_dev_cap); + adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap + & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK; + adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap + & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK; + + /* Copy 11AC mcs */ + adapter->hw_dot_11ac_mcs_support = + le32_to_cpu(hw_spec->dot_11ac_mcs_support); + adapter->usr_dot_11ac_mcs_support = + adapter->hw_dot_11ac_mcs_support; + } else { + adapter->is_hw_11ac_capable = false; + } + + resp_size = le16_to_cpu(resp->size) - S_DS_GEN; + if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { + /* we have variable HW SPEC information */ + left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); + while (left_len > sizeof(struct nxpwifi_ie_types_header)) { + tlv = (void *)&hw_spec->tlv + parsed_len; + switch (le16_to_cpu(tlv->type)) { + case TLV_TYPE_API_REV: + api_rev = (struct hw_spec_api_rev *)tlv; + api_id = le16_to_cpu(api_rev->api_id); + switch (api_id) { + case KEY_API_VER_ID: + adapter->key_api_major_ver = + api_rev->major_ver; + adapter->key_api_minor_ver = + api_rev->minor_ver; + nxpwifi_dbg(adapter, INFO, + "key_api v%d.%d\n", + adapter->key_api_major_ver, + adapter->key_api_minor_ver); + break; + case FW_API_VER_ID: + adapter->fw_api_ver = + api_rev->major_ver; + nxpwifi_dbg(adapter, MSG, + "Firmware api version %d.%d\n", + adapter->fw_api_ver, + api_rev->minor_ver); + break; + case UAP_FW_API_VER_ID: + nxpwifi_dbg(adapter, INFO, + "uAP api version %d.%d\n", + api_rev->major_ver, + api_rev->minor_ver); + break; + case CHANRPT_API_VER_ID: + nxpwifi_dbg(adapter, INFO, + "channel report api version %d.%d\n", + api_rev->major_ver, + api_rev->minor_ver); + break; + case FW_HOTFIX_VER_ID: + adapter->fw_hotfix_ver = + api_rev->major_ver; + nxpwifi_dbg(adapter, INFO, + "Firmware hotfix version %d\n", + api_rev->major_ver); + break; + default: + nxpwifi_dbg(adapter, FATAL, + "Unknown api_id: %d\n", + api_id); + break; + } + break; + case TLV_TYPE_MAX_CONN: + max_conn = (struct hw_spec_max_conn *)tlv; + adapter->max_sta_conn = max_conn->max_sta_conn; + nxpwifi_dbg(adapter, INFO, + "max sta connections: %u\n", + adapter->max_sta_conn); + break; + case TLV_TYPE_EXTENSION_ID: + hw_he_cap = (struct hw_spec_extension *)tlv; + if (hw_he_cap->ext_id == + WLAN_EID_EXT_HE_CAPABILITY) + nxpwifi_update_11ax_cap(adapter, hw_he_cap); + break; + case TLV_TYPE_FW_CAP_INFO: + fw_cap = (struct hw_spec_fw_cap_info *)tlv; + adapter->fw_cap_info = + le32_to_cpu(fw_cap->fw_cap_info); + adapter->fw_cap_ext = + le32_to_cpu(fw_cap->fw_cap_ext); + nxpwifi_dbg(adapter, INFO, + "fw_cap_info:%#x fw_cap_ext:%#x\n", + adapter->fw_cap_info, + adapter->fw_cap_ext); + break; + case TLV_TYPE_SECURE_BOOT_UUID: + sb_uuid = (struct hw_spec_secure_boot_uuid *)tlv; + adapter->uuid_lo = + le64_to_cpu(sb_uuid->uuid_lo); + adapter->uuid_hi = + le64_to_cpu(sb_uuid->uuid_hi); + nxpwifi_dbg(adapter, INFO, + "uuid: %#llx%#llx\n", + adapter->uuid_lo, adapter->uuid_hi); + break; + default: + nxpwifi_dbg(adapter, FATAL, + "Unknown GET_HW_SPEC TLV type: %#x\n", + le16_to_cpu(tlv->type)); + break; + } + parsed_len += le16_to_cpu(tlv->len) + + sizeof(struct nxpwifi_ie_types_header); + left_len -= le16_to_cpu(tlv->len) + + sizeof(struct nxpwifi_ie_types_header); + } + } + + if (adapter->key_api_major_ver < KEY_API_VER_MAJOR_V2) + return -EOPNOTSUPP; + + nxpwifi_dbg(adapter, INFO, + "info: GET_HW_SPEC: fw_release_number- %#x\n", + adapter->fw_release_number); + nxpwifi_dbg(adapter, INFO, + "info: GET_HW_SPEC: permanent addr: %pM\n", + hw_spec->permanent_addr); + nxpwifi_dbg(adapter, INFO, + "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", + le16_to_cpu(hw_spec->hw_if_version), + le16_to_cpu(hw_spec->version)); + + ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); + adapter->region_code = le16_to_cpu(hw_spec->region_code); + + for (i = 0; i < NXPWIFI_MAX_REGION_CODE; i++) + /* Use the region code to search for the index */ + if (adapter->region_code == region_code_index[i]) + break; + + /* If it's unidentified region code, use the default (world) */ + if (i >= NXPWIFI_MAX_REGION_CODE) { + adapter->region_code = 0x00; + nxpwifi_dbg(adapter, WARN, + "cmd: unknown region code, use default (USA)\n"); + } + + adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); + adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; + adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; + adapter->user_htstream = adapter->hw_dev_mcs_support; + if (adapter->fw_bands & BAND_A) + adapter->user_htstream |= (adapter->user_htstream << 8); + + if (adapter->if_ops.update_mp_end_port) { + u16 mp_end_port; + + mp_end_port = le16_to_cpu(hw_spec->mp_end_port); + adapter->if_ops.update_mp_end_port(adapter, mp_end_port); + } + + if (adapter->fw_api_ver == NXPWIFI_FW_V15) + adapter->scan_chan_gap_enabled = true; + + for (i = 0; i < adapter->priv_num; i++) + adapter->priv[i]->config_bands = adapter->fw_bands; + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_scan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_802_11_scan(cmd, data_buf); +} + +static int +nxpwifi_ret_sta_802_11_scan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + + ret = nxpwifi_ret_802_11_scan(priv, resp); + adapter->curr_cmd->wait_q_enabled = false; + + return ret; +} + +static int +nxpwifi_cmd_sta_802_11_get_log(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(HOST_CMD_802_11_GET_LOG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_get_log(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_get_log *get_log = + &resp->params.get_log; + struct nxpwifi_ds_get_stats *stats = + (struct nxpwifi_ds_get_stats *)data_buf; + + if (stats) { + stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); + stats->failed = le32_to_cpu(get_log->failed); + stats->retry = le32_to_cpu(get_log->retry); + stats->multi_retry = le32_to_cpu(get_log->multi_retry); + stats->frame_dup = le32_to_cpu(get_log->frame_dup); + stats->rts_success = le32_to_cpu(get_log->rts_success); + stats->rts_failure = le32_to_cpu(get_log->rts_failure); + stats->ack_failure = le32_to_cpu(get_log->ack_failure); + stats->rx_frag = le32_to_cpu(get_log->rx_frag); + stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); + stats->fcs_error = le32_to_cpu(get_log->fcs_error); + stats->tx_frame = le32_to_cpu(get_log->tx_frame); + stats->wep_icv_error[0] = + le32_to_cpu(get_log->wep_icv_err_cnt[0]); + stats->wep_icv_error[1] = + le32_to_cpu(get_log->wep_icv_err_cnt[1]); + stats->wep_icv_error[2] = + le32_to_cpu(get_log->wep_icv_err_cnt[2]); + stats->wep_icv_error[3] = + le32_to_cpu(get_log->wep_icv_err_cnt[3]); + stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); + stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); + } + + return 0; +} + +static int +nxpwifi_cmd_sta_mac_multicast_adr(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; + struct nxpwifi_multicast_list *mcast_list = + (struct nxpwifi_multicast_list *)data_buf; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HOST_CMD_MAC_MULTICAST_ADR); + + mcast_addr->action = cpu_to_le16(cmd_action); + mcast_addr->num_of_adrs = + cpu_to_le16((u16)mcast_list->num_multicast_addr); + memcpy(mcast_addr->mac_list, mcast_list->mac_list, + mcast_list->num_multicast_addr * ETH_ALEN); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_802_11_associate(priv, cmd, data_buf); +} + +static int +nxpwifi_ret_sta_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_802_11_associate(priv, resp); +} + +static int +nxpwifi_cmd_sta_802_11_snmp_mib(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; + u16 *ul_temp = (u16 *)data_buf; + + nxpwifi_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_type); + cmd->command = cpu_to_le16(HOST_CMD_802_11_SNMP_MIB); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) + + S_DS_GEN); + + snmp_mib->oid = cpu_to_le16((u16)cmd_type); + if (cmd_action == HOST_ACT_GEN_GET) { + snmp_mib->query_type = cpu_to_le16(HOST_ACT_GEN_GET); + snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); + le16_unaligned_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); + } else if (cmd_action == HOST_ACT_GEN_SET) { + snmp_mib->query_type = cpu_to_le16(HOST_ACT_GEN_SET); + snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); + put_unaligned_le16(*ul_temp, snmp_mib->value); + le16_unaligned_add_cpu(&cmd->size, sizeof(u16)); + } + + nxpwifi_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" + "OIDSize=0x%x, Value=0x%x\n", + cmd_action, cmd_type, le16_to_cpu(snmp_mib->buf_size), + get_unaligned_le16(snmp_mib->value)); + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_snmp_mib(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 query_type = le16_to_cpu(smib->query_type); + u32 ul_temp; + + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: oid value = %#x,\t" + "query_type = %#x, buf size = %#x\n", + oid, query_type, le16_to_cpu(smib->buf_size)); + if (query_type == HOST_ACT_GEN_GET) { + ul_temp = get_unaligned_le16(smib->value); + if (data_buf) + *(u32 *)data_buf = ul_temp; + switch (oid) { + case FRAG_THRESH_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: FragThsd =%u\n", + ul_temp); + break; + case RTS_THRESH_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: RTSThsd =%u\n", + ul_temp); + break; + case SHORT_RETRY_LIM_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: TxRetryCount=%u\n", + ul_temp); + break; + case DTIM_PERIOD_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: DTIM period=%u\n", + ul_temp); + break; + default: + break; + } + } + + return 0; +} + +static int nxpwifi_cmd_sta_reg_access(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_ds_reg_rw *reg_rw = data_buf; + + cmd->command = cpu_to_le16(cmd_no); + + switch (cmd_no) { + case HOST_CMD_MAC_REG_ACCESS: + { + struct host_cmd_ds_mac_reg_access *mac_reg; + + cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); + mac_reg = &cmd->params.mac_reg; + mac_reg->action = cpu_to_le16(cmd_action); + mac_reg->offset = cpu_to_le16((u16)reg_rw->offset); + mac_reg->value = cpu_to_le32(reg_rw->value); + break; + } + case HOST_CMD_BBP_REG_ACCESS: + { + struct host_cmd_ds_bbp_reg_access *bbp_reg; + + cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); + bbp_reg = &cmd->params.bbp_reg; + bbp_reg->action = cpu_to_le16(cmd_action); + bbp_reg->offset = cpu_to_le16((u16)reg_rw->offset); + bbp_reg->value = (u8)reg_rw->value; + break; + } + case HOST_CMD_RF_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *rf_reg; + + cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); + rf_reg = &cmd->params.rf_reg; + rf_reg->action = cpu_to_le16(cmd_action); + rf_reg->offset = cpu_to_le16((u16)reg_rw->offset); + rf_reg->value = (u8)reg_rw->value; + break; + } + case HOST_CMD_PMIC_REG_ACCESS: + { + struct host_cmd_ds_pmic_reg_access *pmic_reg; + + cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); + pmic_reg = &cmd->params.pmic_reg; + pmic_reg->action = cpu_to_le16(cmd_action); + pmic_reg->offset = cpu_to_le16((u16)reg_rw->offset); + pmic_reg->value = (u8)reg_rw->value; + break; + } + case HOST_CMD_CAU_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *cau_reg; + + cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); + cau_reg = &cmd->params.rf_reg; + cau_reg->action = cpu_to_le16(cmd_action); + cau_reg->offset = cpu_to_le16((u16)reg_rw->offset); + cau_reg->value = (u8)reg_rw->value; + break; + } + case HOST_CMD_802_11_EEPROM_ACCESS: + { + struct nxpwifi_ds_read_eeprom *rd_eeprom = data_buf; + struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = + &cmd->params.eeprom; + + cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); + cmd_eeprom->action = cpu_to_le16(cmd_action); + cmd_eeprom->offset = cpu_to_le16(rd_eeprom->offset); + cmd_eeprom->byte_count = cpu_to_le16(rd_eeprom->byte_count); + cmd_eeprom->value = 0; + break; + } + default: + return -EINVAL; + } + + return 0; +} + +static int +nxpwifi_ret_sta_reg_access(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_ds_reg_rw *reg_rw; + struct nxpwifi_ds_read_eeprom *eeprom; + union reg { + struct host_cmd_ds_mac_reg_access *mac; + struct host_cmd_ds_bbp_reg_access *bbp; + struct host_cmd_ds_rf_reg_access *rf; + struct host_cmd_ds_pmic_reg_access *pmic; + struct host_cmd_ds_802_11_eeprom_access *eeprom; + } r; + + if (!data_buf) + return 0; + + reg_rw = data_buf; + eeprom = data_buf; + switch (cmdresp_no) { + case HOST_CMD_MAC_REG_ACCESS: + r.mac = &resp->params.mac_reg; + reg_rw->offset = (u32)le16_to_cpu(r.mac->offset); + reg_rw->value = le32_to_cpu(r.mac->value); + break; + case HOST_CMD_BBP_REG_ACCESS: + r.bbp = &resp->params.bbp_reg; + reg_rw->offset = (u32)le16_to_cpu(r.bbp->offset); + reg_rw->value = (u32)r.bbp->value; + break; + + case HOST_CMD_RF_REG_ACCESS: + r.rf = &resp->params.rf_reg; + reg_rw->offset = (u32)le16_to_cpu(r.rf->offset); + reg_rw->value = (u32)r.bbp->value; + break; + case HOST_CMD_PMIC_REG_ACCESS: + r.pmic = &resp->params.pmic_reg; + reg_rw->offset = (u32)le16_to_cpu(r.pmic->offset); + reg_rw->value = (u32)r.pmic->value; + break; + case HOST_CMD_CAU_REG_ACCESS: + r.rf = &resp->params.rf_reg; + reg_rw->offset = (u32)le16_to_cpu(r.rf->offset); + reg_rw->value = (u32)r.rf->value; + break; + case HOST_CMD_802_11_EEPROM_ACCESS: + r.eeprom = &resp->params.eeprom; + pr_debug("info: EEPROM read len=%x\n", + le16_to_cpu(r.eeprom->byte_count)); + if (eeprom->byte_count < le16_to_cpu(r.eeprom->byte_count)) { + eeprom->byte_count = 0; + pr_debug("info: EEPROM read length is too big\n"); + return -ENOMEM; + } + eeprom->offset = le16_to_cpu(r.eeprom->offset); + eeprom->byte_count = le16_to_cpu(r.eeprom->byte_count); + if (eeprom->byte_count > 0) + memcpy(&eeprom->value, &r.eeprom->value, + min((u16)MAX_EEPROM_DATA, eeprom->byte_count)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int +nxpwifi_cmd_sta_rf_tx_pwr(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HOST_CMD_RF_TX_PWR); + txp->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int +nxpwifi_ret_sta_rf_tx_pwr(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; + u16 action = le16_to_cpu(txp->action); + + priv->tx_power_level = le16_to_cpu(txp->cur_level); + + if (action == HOST_ACT_GEN_GET) { + priv->max_tx_power_level = txp->max_power; + priv->min_tx_power_level = txp->min_power; + } + + nxpwifi_dbg(priv->adapter, INFO, + "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + +static int +nxpwifi_cmd_sta_rf_antenna(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; + struct nxpwifi_ds_ant_cfg *ant_cfg = + (struct nxpwifi_ds_ant_cfg *)data_buf; + + cmd->command = cpu_to_le16(HOST_CMD_RF_ANTENNA); + + switch (cmd_action) { + case HOST_ACT_GEN_SET: + if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { + cmd->size = cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_mimo) + + S_DS_GEN); + ant_mimo->action_tx = cpu_to_le16(HOST_ACT_SET_TX); + ant_mimo->tx_ant_mode = + cpu_to_le16((u16)ant_cfg->tx_ant); + ant_mimo->action_rx = cpu_to_le16(HOST_ACT_SET_RX); + ant_mimo->rx_ant_mode = + cpu_to_le16((u16)ant_cfg->rx_ant); + } else { + cmd->size = cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_siso) + + S_DS_GEN); + ant_siso->action = cpu_to_le16(HOST_ACT_SET_BOTH); + ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); + } + break; + case HOST_ACT_GEN_GET: + if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { + cmd->size = cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_mimo) + + S_DS_GEN); + ant_mimo->action_tx = cpu_to_le16(HOST_ACT_GET_TX); + ant_mimo->action_rx = cpu_to_le16(HOST_ACT_GET_RX); + } else { + cmd->size = cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_siso) + + S_DS_GEN); + ant_siso->action = cpu_to_le16(HOST_ACT_GET_BOTH); + } + break; + } + return 0; +} + +static int +nxpwifi_ret_sta_rf_antenna(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; + struct nxpwifi_adapter *adapter = priv->adapter; + + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) { + priv->tx_ant = le16_to_cpu(ant_mimo->tx_ant_mode); + priv->rx_ant = le16_to_cpu(ant_mimo->rx_ant_mode); + nxpwifi_dbg(adapter, INFO, + "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" + "Rx action = 0x%x, Rx Mode = 0x%04x\n", + le16_to_cpu(ant_mimo->action_tx), + le16_to_cpu(ant_mimo->tx_ant_mode), + le16_to_cpu(ant_mimo->action_rx), + le16_to_cpu(ant_mimo->rx_ant_mode)); + } else { + priv->tx_ant = le16_to_cpu(ant_siso->ant_mode); + priv->rx_ant = le16_to_cpu(ant_siso->ant_mode); + nxpwifi_dbg(adapter, INFO, + "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", + le16_to_cpu(ant_siso->action), + le16_to_cpu(ant_siso->ant_mode)); + } + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_deauthenticate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; + u8 *mac = (u8 *)data_buf; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_DEAUTHENTICATE); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* Set AP MAC address */ + memcpy(deauth->mac_addr, mac, ETH_ALEN); + + nxpwifi_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr); + + deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_deauthenticate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->dbg.num_cmd_deauth++; + if (!memcmp(resp->params.deauth.mac_addr, + &priv->curr_bss_params.bss_descriptor.mac_address, + sizeof(resp->params.deauth.mac_addr))) + nxpwifi_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING, + false); + + return 0; +} + +static int +nxpwifi_cmd_sta_mac_control(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; + u32 *action = (u32 *)data_buf; + + if (cmd_action != HOST_ACT_GEN_SET) { + nxpwifi_dbg(priv->adapter, ERROR, + "mac_control: only support set cmd\n"); + return -EINVAL; + } + + cmd->command = cpu_to_le16(HOST_CMD_MAC_CONTROL); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); + mac_ctrl->action = cpu_to_le32(*action); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_mac_address(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(HOST_CMD_802_11_MAC_ADDRESS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.mac_addr.action = cpu_to_le16(cmd_action); + + if (cmd_action == HOST_ACT_GEN_SET) + memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, + ETH_ALEN); + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_mac_address(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_mac_address *cmd_mac_addr; + + cmd_mac_addr = &resp->params.mac_addr; + + memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); + + nxpwifi_dbg(priv->adapter, INFO, + "info: set mac address: %pM\n", priv->curr_addr); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11d_domain_info(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11d_domain_info *domain_info = + &cmd->params.domain_info; + struct nxpwifi_ietypes_domain_param_set *domain = + &domain_info->domain; + struct nxpwifi_ietypes_domain_code *domain_code; + u8 no_of_triplet = adapter->domain_reg.no_of_triplet; + int triplet_size; + + nxpwifi_dbg(adapter, INFO, + "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); + + cmd->command = cpu_to_le16(HOST_CMD_802_11D_DOMAIN_INFO); + cmd->size = cpu_to_le16(S_DS_GEN); + domain_info->action = cpu_to_le16(cmd_action); + le16_unaligned_add_cpu(&cmd->size, sizeof(domain_info->action)); + + if (cmd_action == HOST_ACT_GEN_GET) + return 0; + + triplet_size = no_of_triplet * + sizeof(struct ieee80211_country_ie_triplet); + + domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); + domain->header.len = + cpu_to_le16(sizeof(domain->country_code) + triplet_size); + memcpy(domain->country_code, adapter->domain_reg.country_code, + sizeof(domain->country_code)); + if (no_of_triplet) + memcpy(domain->triplet, adapter->domain_reg.triplet, + triplet_size); + le16_unaligned_add_cpu(&cmd->size, sizeof(*domain) + triplet_size); + + domain_code = (struct nxpwifi_ietypes_domain_code *)((u8 *)cmd + + le16_to_cpu(cmd->size)); + domain_code->header.type = cpu_to_le16(TLV_TYPE_REGION_DOMAIN_CODE); + domain_code->header.len = + cpu_to_le16(sizeof(*domain_code) - + sizeof(struct nxpwifi_ie_types_header)); + le16_unaligned_add_cpu(&cmd->size, sizeof(*domain_code)); + + return 0; +} + +static int +nxpwifi_ret_sta_802_11d_domain_info(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = + &resp->params.domain_info_resp; + struct nxpwifi_ietypes_domain_param_set *domain = &domain_info->domain; + u16 action = le16_to_cpu(domain_info->action); + u8 no_of_triplet; + + no_of_triplet = (u8)((le16_to_cpu(domain->header.len) + - IEEE80211_COUNTRY_STRING_LEN) + / sizeof(struct ieee80211_country_ie_triplet)); + + nxpwifi_dbg(priv->adapter, INFO, + "info: 11D Domain Info Resp: no_of_triplet=%d\n", + no_of_triplet); + + if (no_of_triplet > NXPWIFI_MAX_TRIPLET_802_11D) { + nxpwifi_dbg(priv->adapter, FATAL, + "11D: invalid number of triplets %d returned\n", + no_of_triplet); + return -EINVAL; + } + + switch (action) { + case HOST_ACT_GEN_SET: /* Proc Set Action */ + break; + case HOST_ACT_GEN_GET: + break; + default: + nxpwifi_dbg(priv->adapter, ERROR, + "11D: invalid action:%d\n", domain_info->action); + return -EINVAL; + } + + return 0; +} + +static int nxpwifi_set_aes_key(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + struct nxpwifi_ds_encrypt_key *enc_key, + struct host_cmd_ds_802_11_key_material *km) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 size, len = KEY_PARAMS_FIXED_LEN; + + if (enc_key->is_igtk_key) { + nxpwifi_dbg(adapter, INFO, + "%s: Set CMAC AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.cmac_aes.ipn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); + km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); + km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + km->key_param_set.key_params.cmac_aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.cmac_aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct nxpwifi_cmac_aes_param); + } else if (enc_key->is_igtk_def_key) { + nxpwifi_dbg(adapter, INFO, + "%s: Set CMAC default Key index\n", __func__); + km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC_DEF; + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + } else { + nxpwifi_dbg(adapter, INFO, + "%s: Set AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.aes.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_AES; + km->key_param_set.key_params.aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct nxpwifi_aes_param); + } + + km->key_param_set.len = cpu_to_le16(len); + size = len + sizeof(struct nxpwifi_ie_types_header) + + sizeof(km->action) + S_DS_GEN; + cmd->size = cpu_to_le16(size); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_key_material(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ds_encrypt_key *enc_key = + (struct nxpwifi_ds_encrypt_key *)data_buf; + u8 *mac = enc_key->mac_addr; + u16 key_info, len = KEY_PARAMS_FIXED_LEN; + struct host_cmd_ds_802_11_key_material *km = + &cmd->params.key_material; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_KEY_MATERIAL); + km->action = cpu_to_le16(cmd_action); + + if (cmd_action == HOST_ACT_GEN_GET) { + nxpwifi_dbg(adapter, INFO, "%s: Get key\n", __func__); + km->key_param_set.key_idx = + enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_index & NXPWIFI_KEY_INDEX_UNICAST) + key_info = KEY_UNICAST; + else + key_info = KEY_MCAST; + + if (enc_key->is_igtk_key) + key_info |= KEY_IGTK; + + km->key_param_set.key_info = cpu_to_le16(key_info); + + cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + memset(&km->key_param_set, 0, + sizeof(struct nxpwifi_ie_type_key_param_set)); + + if (enc_key->key_disable) { + nxpwifi_dbg(adapter, INFO, "%s: Remove key\n", __func__); + km->action = cpu_to_le16(HOST_ACT_GEN_REMOVE); + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + key_info = KEY_MCAST | KEY_UNICAST; + km->key_param_set.key_info = cpu_to_le16(key_info); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + km->action = cpu_to_le16(HOST_ACT_GEN_SET); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + key_info = KEY_ENABLED; + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { + nxpwifi_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); + len += sizeof(struct nxpwifi_wep_param); + km->key_param_set.len = cpu_to_le16(len); + km->key_param_set.key_type = KEY_TYPE_ID_WEP; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + key_info |= KEY_MCAST | KEY_UNICAST; + } else { + if (enc_key->is_current_wep_key) { + key_info |= KEY_MCAST | KEY_UNICAST; + if (km->key_param_set.key_idx == + (priv->wep_key_curr_index & KEY_INDEX_MASK)) + key_info |= KEY_DEFAULT; + } else { + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST; + else + key_info |= KEY_UNICAST | KEY_DEFAULT; + } + } + km->key_param_set.key_info = cpu_to_le16(key_info); + + km->key_param_set.key_params.wep.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wep.key, + enc_key->key_material, enc_key->key_len); + + cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST | KEY_RX_KEY; + else + key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; + + /* Enable default key for WPA/WPA2 */ + if (!priv->wpa_is_gtk_set) + key_info |= KEY_DEFAULT; + + km->key_param_set.key_info = cpu_to_le16(key_info); + + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) + return nxpwifi_set_aes_key(priv, cmd, enc_key, km); + + if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + nxpwifi_dbg(adapter, INFO, + "%s: Set TKIP Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.tkip.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_TKIP; + km->key_param_set.key_params.tkip.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.tkip.key, + enc_key->key_material, enc_key->key_len); + + len += sizeof(struct nxpwifi_tkip_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + } + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_key_material(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_key_material *key; + int len; + + key = &resp->params.key_material; + + len = le16_to_cpu(key->key_param_set.key_params.aes.key_len); + if (len > sizeof(key->key_param_set.key_params.aes.key)) + return -EINVAL; + + if (le16_to_cpu(key->action) == HOST_ACT_GEN_SET) { + if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { + nxpwifi_dbg(priv->adapter, INFO, + "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + priv->port_open = true; + } + } + + if (key->key_param_set.key_type != KEY_TYPE_ID_AES) + return 0; + + memset(priv->aes_key.key_param_set.key_params.aes.key, 0, + sizeof(key->key_param_set.key_params.aes.key)); + priv->aes_key.key_param_set.key_params.aes.key_len = cpu_to_le16(len); + memcpy(priv->aes_key.key_param_set.key_params.aes.key, + key->key_param_set.key_params.aes.key, len); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_bg_scan_config(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_802_11_bg_scan_config(priv, cmd, data_buf); +} + +static int +nxpwifi_cmd_sta_802_11_bg_scan_query(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_802_11_bg_scan_query(cmd); +} + +static int +nxpwifi_ret_sta_802_11_bg_scan_query(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + + ret = nxpwifi_ret_802_11_scan(priv, resp); + cfg80211_sched_scan_results(priv->wdev.wiphy, 0); + nxpwifi_dbg(adapter, CMD, + "info: CMD_RESP: BG_SCAN result is ready!\n"); + + return ret; +} + +static int +nxpwifi_cmd_sta_wmm_get_status(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(HOST_CMD_WMM_GET_STATUS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_ret_sta_wmm_get_status(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_wmm_get_status(priv, resp); +} + +static int +nxpwifi_cmd_sta_802_11_subsc_evt(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; + struct nxpwifi_ds_misc_subsc_evt *subsc_evt_cfg = + (struct nxpwifi_ds_misc_subsc_evt *)data_buf; + struct nxpwifi_ie_types_rssi_threshold *rssi_tlv; + u16 event_bitmap; + u8 *pos; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_SUBSCRIBE_EVENT); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + + S_DS_GEN); + + subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); + nxpwifi_dbg(priv->adapter, CMD, + "cmd: action: %d\n", subsc_evt_cfg->action); + + /*For query requests, no configuration TLV structures are to be added.*/ + if (subsc_evt_cfg->action == HOST_ACT_GEN_GET) + return 0; + + subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); + + event_bitmap = subsc_evt_cfg->events; + nxpwifi_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", + event_bitmap); + + if ((subsc_evt_cfg->action == HOST_ACT_BITWISE_CLR || + subsc_evt_cfg->action == HOST_ACT_BITWISE_SET) && + event_bitmap == 0) { + nxpwifi_dbg(priv->adapter, ERROR, + "Error: No event specified\t" + "for bitwise action type\n"); + return -EINVAL; + } + + /* Append TLV structures for each of the specified events for + * subscribing or re-configuring. This is not required for + * bitwise unsubscribing request. + */ + if (subsc_evt_cfg->action == HOST_ACT_BITWISE_CLR) + return 0; + + pos = ((u8 *)subsc_evt) + + sizeof(struct host_cmd_ds_802_11_subsc_evt); + + if (event_bitmap & BITMASK_BCN_RSSI_LOW) { + rssi_tlv = (struct nxpwifi_ie_types_rssi_threshold *)pos; + + rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len = + cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; + rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; + + nxpwifi_dbg(priv->adapter, EVENT, + "Cfg Beacon Low Rssi event,\t" + "RSSI:-%d dBm, Freq:%d\n", + subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, + subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); + + pos += sizeof(struct nxpwifi_ie_types_rssi_threshold); + le16_unaligned_add_cpu + (&cmd->size, + sizeof(struct nxpwifi_ie_types_rssi_threshold)); + } + + if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { + rssi_tlv = (struct nxpwifi_ie_types_rssi_threshold *)pos; + + rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_tlv->header.len = + cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; + rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; + + nxpwifi_dbg(priv->adapter, EVENT, + "Cfg Beacon High Rssi event,\t" + "RSSI:-%d dBm, Freq:%d\n", + subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, + subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); + + pos += sizeof(struct nxpwifi_ie_types_rssi_threshold); + le16_unaligned_add_cpu + (&cmd->size, + sizeof(struct nxpwifi_ie_types_rssi_threshold)); + } + + return 0; +} + +static int +nxpwifi_ret_sta_subsc_evt(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = + &resp->params.subsc_evt; + + /* For every subscribe event command (Get/Set/Clear), FW reports the + * current set of subscribed events + */ + nxpwifi_dbg(priv->adapter, EVENT, + "Bitmap of currently subscribed events: %16x\n", + le16_to_cpu(cmd_sub_event->events)); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_tx_rate_query(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(HOST_CMD_802_11_TX_RATE_QUERY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + + S_DS_GEN); + priv->tx_rate = 0; + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_tx_rate_query(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + priv->tx_rate = resp->params.tx_rate.tx_rate; + priv->tx_htinfo = resp->params.tx_rate.ht_info; + if (!priv->is_data_rate_auto) + priv->data_rate = + nxpwifi_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + + return 0; +} + +static int +nxpwifi_cmd_sta_mem_access(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_ds_mem_rw *mem_rw = + (struct nxpwifi_ds_mem_rw *)data_buf; + struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem; + + cmd->command = cpu_to_le16(HOST_CMD_MEM_ACCESS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + + S_DS_GEN); + + mem_access->action = cpu_to_le16(cmd_action); + mem_access->addr = cpu_to_le32(mem_rw->addr); + mem_access->value = cpu_to_le32(mem_rw->value); + + return 0; +} + +static int +nxpwifi_ret_sta_mem_access(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem; + + priv->mem_rw.addr = le32_to_cpu(mem->addr); + priv->mem_rw.value = le32_to_cpu(mem->value); + + return 0; +} + +static u32 nxpwifi_parse_cal_cfg(u8 *src, size_t len, u8 *dst) +{ + u8 *s = src, *d = dst; + + while (s - src < len) { + if (*s && (isspace(*s) || *s == '\t')) { + s++; + continue; + } + if (isxdigit(*s)) { + if (kstrtou8(s, 16, d)) + return 0; + d++; + s += 2; + } else { + s++; + } + } + + return d - dst; +} + +static int +nxpwifi_cmd_sta_cfg_data(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct property *prop = data_buf; + u32 len; + u8 *data = (u8 *)cmd + S_DS_GEN; + int ret; + + if (prop) { + len = prop->length; + ret = of_property_read_u8_array(adapter->dt_node, prop->name, + data, len); + if (ret) + return ret; + nxpwifi_dbg(adapter, INFO, + "download cfg_data from device tree: %s\n", + prop->name); + } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { + len = nxpwifi_parse_cal_cfg((u8 *)adapter->cal_data->data, + adapter->cal_data->size, data); + nxpwifi_dbg(adapter, INFO, + "download cfg_data from config file\n"); + } else { + return -EINVAL; + } + + cmd->command = cpu_to_le16(HOST_CMD_CFG_DATA); + cmd->size = cpu_to_le16(S_DS_GEN + len); + + return 0; +} + +static int +nxpwifi_ret_sta_cfg_data(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + if (resp->result != HOST_RESULT_OK) { + nxpwifi_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); + return -EINVAL; + } + + return 0; +} + +static int +nxpwifi_cmd_sta_ver_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(cmd_no); + cmd->params.verext.version_str_sel = + (u8)(get_unaligned((u32 *)data_buf)); + memcpy(&cmd->params, data_buf, sizeof(struct host_cmd_ds_version_ext)); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_ret_sta_ver_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; + struct host_cmd_ds_version_ext *version_ext = + (struct host_cmd_ds_version_ext *)data_buf; + + if (test_and_clear_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &priv->adapter->work_flags)) { + if (strncmp(ver_ext->version_str, "ChipRev:20, BB:9b(10.00), RF:40(21)", + NXPWIFI_VERSION_STR_LENGTH) == 0) { + struct nxpwifi_ds_auto_ds auto_ds = { + .auto_ds = DEEP_SLEEP_OFF, + }; + + nxpwifi_dbg(priv->adapter, MSG, + "Bad HW revision detected, disabling deep sleep\n"); + + if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, + DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, false)) { + nxpwifi_dbg(priv->adapter, MSG, + "Disabling deep sleep failed.\n"); + } + } + + return 0; + } + + if (version_ext) { + version_ext->version_str_sel = ver_ext->version_str_sel; + memcpy(version_ext->version_str, ver_ext->version_str, + NXPWIFI_VERSION_STR_LENGTH); + memcpy(priv->version_str, ver_ext->version_str, + NXPWIFI_VERSION_STR_LENGTH); + + /* Ensure the version string from the firmware is 0-terminated */ + priv->version_str[NXPWIFI_VERSION_STR_LENGTH - 1] = '\0'; + } + return 0; +} + +static int +nxpwifi_cmd_append_rpn_expression(struct nxpwifi_private *priv, + struct nxpwifi_mef_entry *mef_entry, + u8 **buffer) +{ + struct nxpwifi_mef_filter *filter = mef_entry->filter; + int i, byte_len; + u8 *stack_ptr = *buffer; + + for (i = 0; i < NXPWIFI_MEF_MAX_FILTERS; i++) { + filter = &mef_entry->filter[i]; + if (!filter->filt_type) + break; + put_unaligned_le32((u32)filter->repeat, stack_ptr); + stack_ptr += 4; + *stack_ptr = TYPE_DNUM; + stack_ptr += 1; + + byte_len = filter->byte_seq[NXPWIFI_MEF_MAX_BYTESEQ]; + memcpy(stack_ptr, filter->byte_seq, byte_len); + stack_ptr += byte_len; + *stack_ptr = byte_len; + stack_ptr += 1; + *stack_ptr = TYPE_BYTESEQ; + stack_ptr += 1; + put_unaligned_le32((u32)filter->offset, stack_ptr); + stack_ptr += 4; + *stack_ptr = TYPE_DNUM; + stack_ptr += 1; + + *stack_ptr = filter->filt_type; + stack_ptr += 1; + + if (filter->filt_action) { + *stack_ptr = filter->filt_action; + stack_ptr += 1; + } + + if (stack_ptr - *buffer > STACK_NBYTES) + return -ENOMEM; + } + + *buffer = stack_ptr; + return 0; +} + +static int +nxpwifi_cmd_sta_mef_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; + struct nxpwifi_ds_mef_cfg *mef = + (struct nxpwifi_ds_mef_cfg *)data_buf; + struct nxpwifi_fw_mef_entry *mef_entry = NULL; + u8 *pos = (u8 *)mef_cfg; + u16 i; + int ret = 0; + + cmd->command = cpu_to_le16(HOST_CMD_MEF_CFG); + + mef_cfg->criteria = cpu_to_le32(mef->criteria); + mef_cfg->num_entries = cpu_to_le16(mef->num_entries); + pos += sizeof(*mef_cfg); + + for (i = 0; i < mef->num_entries; i++) { + mef_entry = (struct nxpwifi_fw_mef_entry *)pos; + mef_entry->mode = mef->mef_entry[i].mode; + mef_entry->action = mef->mef_entry[i].action; + pos += sizeof(*mef_entry); + + ret = nxpwifi_cmd_append_rpn_expression(priv, + &mef->mef_entry[i], + &pos); + if (ret) + return ret; + + mef_entry->exprsize = + cpu_to_le16(pos - mef_entry->expr); + } + cmd->size = cpu_to_le16((u16)(pos - (u8 *)mef_cfg) + S_DS_GEN); + + return ret; +} + +static int +nxpwifi_cmd_sta_802_11_rssi_info(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(HOST_CMD_RSSI_INFO); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + + S_DS_GEN); + cmd->params.rssi_info.action = cpu_to_le16(cmd_action); + cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); + cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); + + /* Reset SNR/NF/RSSI values in private structure */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_rssi_info(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = + &resp->params.rssi_info_rsp; + struct nxpwifi_ds_misc_subsc_evt *subsc_evt = + &priv->async_subsc_evt_storage; + + priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); + priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); + + priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); + priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); + + priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); + priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); + + priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); + priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); + + if (priv->subsc_evt_rssi_state == EVENT_HANDLED) + return 0; + + memset(subsc_evt, 0x00, sizeof(struct nxpwifi_ds_misc_subsc_evt)); + + /* Resubscribe low and high rssi events with new thresholds */ + subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + subsc_evt->action = HOST_ACT_BITWISE_SET; + if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - + priv->cqm_rssi_hyst); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + + priv->cqm_rssi_hyst); + } + subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; + + priv->subsc_evt_rssi_state = EVENT_HANDLED; + + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, subsc_evt, false); + + return 0; +} + +static int +nxpwifi_cmd_sta_func_init(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + if (priv->adapter->hw_status == NXPWIFI_HW_STATUS_RESET) + priv->adapter->hw_status = NXPWIFI_HW_STATUS_READY; + cmd->command = cpu_to_le16(cmd_no); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +static int +nxpwifi_cmd_sta_func_shutdown(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + priv->adapter->hw_status = NXPWIFI_HW_STATUS_RESET; + cmd->command = cpu_to_le16(cmd_no); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +static int +nxpwifi_cmd_sta_11n_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11n_cfg(priv, cmd, cmd_action, data_buf); +} + +static int +nxpwifi_cmd_sta_11n_addba_req(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11n_addba_req(cmd, data_buf); +} + +static int +nxpwifi_ret_sta_11n_addba_req(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_11n_addba_req(priv, resp); +} + +static int +nxpwifi_cmd_sta_11n_addba_rsp(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11n_addba_rsp_gen(priv, cmd, data_buf); +} + +static int +nxpwifi_ret_sta_11n_addba_rsp(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_11n_addba_resp(priv, resp); +} + +static int +nxpwifi_cmd_sta_11n_delba(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11n_delba(cmd, data_buf); +} + +static int +nxpwifi_ret_sta_11n_delba(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_11n_delba(priv, resp); +} + +static int +nxpwifi_cmd_sta_tx_power_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_types_power_group *pg_tlv; + struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; + struct host_cmd_ds_txpwr_cfg *txp = + (struct host_cmd_ds_txpwr_cfg *)data_buf; + + cmd->command = cpu_to_le16(HOST_CMD_TXPWR_CFG); + cmd->size = + cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); + switch (cmd_action) { + case HOST_ACT_GEN_SET: + if (txp->mode) { + pg_tlv = (struct nxpwifi_types_power_group + *)((unsigned long)txp + + sizeof(struct host_cmd_ds_txpwr_cfg)); + memmove(cmd_txp_cfg, txp, + sizeof(struct host_cmd_ds_txpwr_cfg) + + sizeof(struct nxpwifi_types_power_group) + + le16_to_cpu(pg_tlv->length)); + + pg_tlv = (struct nxpwifi_types_power_group *)((u8 *) + cmd_txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + + sizeof(struct nxpwifi_types_power_group) + + le16_to_cpu(pg_tlv->length)); + } else { + memmove(cmd_txp_cfg, txp, sizeof(*txp)); + } + cmd_txp_cfg->action = cpu_to_le16(cmd_action); + break; + case HOST_ACT_GEN_GET: + cmd_txp_cfg->action = cpu_to_le16(cmd_action); + break; + } + + return 0; +} + +static int nxpwifi_get_power_level(struct nxpwifi_private *priv, void *data_buf) +{ + int length, max_power = -1, min_power = -1; + struct nxpwifi_types_power_group *pg_tlv_hdr; + struct nxpwifi_power_group *pg; + + if (!data_buf) + return -ENOMEM; + + pg_tlv_hdr = (struct nxpwifi_types_power_group *)((u8 *)data_buf); + pg = (struct nxpwifi_power_group *) + ((u8 *)pg_tlv_hdr + sizeof(struct nxpwifi_types_power_group)); + length = le16_to_cpu(pg_tlv_hdr->length); + + /* At least one structure required to update power */ + if (length < sizeof(struct nxpwifi_power_group)) + return 0; + + max_power = pg->power_max; + min_power = pg->power_min; + length -= sizeof(struct nxpwifi_power_group); + + while (length >= sizeof(struct nxpwifi_power_group)) { + pg++; + if (max_power < pg->power_max) + max_power = pg->power_max; + + if (min_power > pg->power_min) + min_power = pg->power_min; + + length -= sizeof(struct nxpwifi_power_group); + } + priv->min_tx_power_level = (u8)min_power; + priv->max_tx_power_level = (u8)max_power; + + return 0; +} + +static int +nxpwifi_ret_sta_tx_power_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; + struct nxpwifi_types_power_group *pg_tlv_hdr; + struct nxpwifi_power_group *pg; + u16 action = le16_to_cpu(txp_cfg->action); + u16 tlv_buf_left; + + pg_tlv_hdr = (struct nxpwifi_types_power_group *) + ((u8 *)txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + + pg = (struct nxpwifi_power_group *) + ((u8 *)pg_tlv_hdr + + sizeof(struct nxpwifi_types_power_group)); + + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); + if (tlv_buf_left < + le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) + return 0; + + switch (action) { + case HOST_ACT_GEN_GET: + if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING) + nxpwifi_get_power_level(priv, pg_tlv_hdr); + + priv->tx_power_level = (u16)pg->power_min; + break; + + case HOST_ACT_GEN_SET: + if (!le32_to_cpu(txp_cfg->mode)) + break; + + if (pg->power_max == pg->power_min) + priv->tx_power_level = (u16)pg->power_min; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "CMD_RESP: unknown cmd action %d\n", + action); + return 0; + } + nxpwifi_dbg(adapter, INFO, + "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + +static int +nxpwifi_cmd_sta_tx_rate_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; + u16 *pbitmap_rates = (u16 *)data_buf; + struct nxpwifi_rate_scope *rate_scope; + struct nxpwifi_rate_drop_pattern *rate_drop; + u32 i; + + cmd->command = cpu_to_le16(HOST_CMD_TX_RATE_CFG); + + rate_cfg->action = cpu_to_le16(cmd_action); + rate_cfg->cfg_index = 0; + + rate_scope = (struct nxpwifi_rate_scope *)((u8 *)rate_cfg + + sizeof(struct host_cmd_ds_tx_rate_cfg)); + rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); + rate_scope->length = cpu_to_le16 + (sizeof(*rate_scope) - sizeof(struct nxpwifi_ie_types_header)); + if (pbitmap_rates) { + rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); + for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[10 + i]); + } + } else { + rate_scope->hr_dsss_rate_bitmap = + cpu_to_le16(priv->bitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + cpu_to_le16(priv->bitmap_rates[1]); + for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[10 + i]); + } + } + + rate_drop = (struct nxpwifi_rate_drop_pattern *)((u8 *)rate_scope + + sizeof(struct nxpwifi_rate_scope)); + rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); + rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); + rate_drop->rate_drop_mode = 0; + + cmd->size = + cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + + sizeof(struct nxpwifi_rate_scope) + + sizeof(struct nxpwifi_rate_drop_pattern)); + + return 0; +} + +static void nxpwifi_ret_rate_scope(struct nxpwifi_private *priv, u8 *tlv_buf) +{ + struct nxpwifi_rate_scope *rate_scope; + int i; + + rate_scope = (struct nxpwifi_rate_scope *)tlv_buf; + priv->bitmap_rates[0] = + le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); + priv->bitmap_rates[1] = + le16_to_cpu(rate_scope->ofdm_rate_bitmap); + for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + priv->bitmap_rates[2 + i] = + le16_to_cpu(rate_scope->ht_mcs_rate_bitmap[i]); + + if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) { + for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + priv->bitmap_rates[10 + i] = + le16_to_cpu(rate_scope->vht_mcs_rate_bitmap[i]); + } +} + +static int +nxpwifi_ret_sta_tx_rate_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; + struct nxpwifi_ie_types_header *head; + u16 tlv, tlv_buf_len, tlv_buf_left; + u8 *tlv_buf; + + tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); + + while (tlv_buf_left >= sizeof(*head)) { + head = (struct nxpwifi_ie_types_header *)tlv_buf; + tlv = le16_to_cpu(head->type); + tlv_buf_len = le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + + switch (tlv) { + case TLV_TYPE_RATE_SCOPE: + nxpwifi_ret_rate_scope(priv, tlv_buf); + break; + /* Add RATE_DROP tlv here */ + } + + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } + + priv->is_data_rate_auto = nxpwifi_is_rate_auto(priv); + + if (priv->is_data_rate_auto) + priv->data_rate = 0; + else + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY, + HOST_ACT_GEN_GET, 0, NULL, false); + + return 0; +} + +static int +nxpwifi_cmd_sta_reconfigure_rx_buff(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_recfg_tx_buf(priv, cmd, cmd_action, data_buf); +} + +static int +nxpwifi_ret_sta_reconfigure_rx_buff(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (0xffff != (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { + adapter->tx_buf_size = + (u16)le16_to_cpu(resp->params.tx_buf.buff_size); + adapter->tx_buf_size = + (adapter->tx_buf_size / NXPWIFI_SDIO_BLOCK_SIZE) * + NXPWIFI_SDIO_BLOCK_SIZE; + adapter->curr_tx_buf_size = adapter->tx_buf_size; + nxpwifi_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n", + adapter->curr_tx_buf_size); + + if (adapter->if_ops.update_mp_end_port) { + u16 mp_end_port; + + mp_end_port = + le16_to_cpu(resp->params.tx_buf.mp_end_port); + adapter->if_ops.update_mp_end_port(adapter, + mp_end_port); + } + } + + return 0; +} + +static int +nxpwifi_cmd_sta_chan_report_request(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_issue_chan_report_request(priv, cmd, data_buf); +} + +static int +nxpwifi_cmd_sta_amsdu_aggr_ctrl(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_amsdu_aggr_ctrl(cmd, cmd_action, data_buf); +} + +static int +nxpwifi_cmd_sta_robust_coex(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_robust_coex *coex = &cmd->params.coex; + bool *is_timeshare = (bool *)data_buf; + struct nxpwifi_ie_types_robust_coex *coex_tlv; + + cmd->command = cpu_to_le16(HOST_CMD_ROBUST_COEX); + cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); + + coex->action = cpu_to_le16(cmd_action); + coex_tlv = (struct nxpwifi_ie_types_robust_coex *) + ((u8 *)coex + sizeof(*coex)); + coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX); + coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode)); + + if (coex->action == HOST_ACT_GEN_GET) + return 0; + + if (*is_timeshare) + coex_tlv->mode = cpu_to_le32(NXPWIFI_COEX_MODE_TIMESHARE); + else + coex_tlv->mode = cpu_to_le32(NXPWIFI_COEX_MODE_SPATIAL); + + return 0; +} + +static int +nxpwifi_ret_sta_robust_coex(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_robust_coex *coex = &resp->params.coex; + bool *is_timeshare = (bool *)data_buf; + struct nxpwifi_ie_types_robust_coex *coex_tlv; + u16 action = le16_to_cpu(coex->action); + u32 mode; + + coex_tlv = (struct nxpwifi_ie_types_robust_coex + *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); + if (action == HOST_ACT_GEN_GET) { + mode = le32_to_cpu(coex_tlv->mode); + if (mode == NXPWIFI_COEX_MODE_TIMESHARE) + *is_timeshare = true; + else + *is_timeshare = false; + } + + return 0; +} + +static int +nxpwifi_cmd_sta_enh_power_mode(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = + &cmd->params.psmode_enh; + u16 ps_bitmap = (u16)cmd_type; + struct nxpwifi_ds_auto_ds *auto_ds = + (struct nxpwifi_ds_auto_ds *)data_buf; + u8 *tlv; + u16 cmd_size = 0; + + cmd->command = cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH); + if (cmd_action == DIS_AUTO_PS) { + psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action == GET_PS) { + psmode_enh->action = cpu_to_le16(GET_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action == EN_AUTO_PS) { + psmode_enh->action = cpu_to_le16(EN_AUTO_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap); + tlv = (u8 *)cmd + cmd_size; + if (ps_bitmap & BITMAP_STA_PS) { + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie_types_ps_param *ps_tlv = + (struct nxpwifi_ie_types_ps_param *)tlv; + struct nxpwifi_ps_param *ps_mode = &ps_tlv->param; + + ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); + ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - + sizeof(struct nxpwifi_ie_types_header)); + cmd_size += sizeof(*ps_tlv); + tlv += sizeof(*ps_tlv); + nxpwifi_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter PS\n"); + ps_mode->null_pkt_interval = + cpu_to_le16(adapter->null_pkt_interval); + ps_mode->multiple_dtims = + cpu_to_le16(adapter->multiple_dtim); + ps_mode->bcn_miss_timeout = + cpu_to_le16(adapter->bcn_miss_time_out); + ps_mode->local_listen_interval = + cpu_to_le16(adapter->local_listen_interval); + ps_mode->delay_to_ps = + cpu_to_le16(adapter->delay_to_ps); + ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); + } + if (ps_bitmap & BITMAP_AUTO_DS) { + struct nxpwifi_ie_types_auto_ds_param *auto_ds_tlv = + (struct nxpwifi_ie_types_auto_ds_param *)tlv; + u16 idletime = 0; + + auto_ds_tlv->header.type = + cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); + auto_ds_tlv->header.len = + cpu_to_le16(sizeof(*auto_ds_tlv) - + sizeof(struct nxpwifi_ie_types_header)); + cmd_size += sizeof(*auto_ds_tlv); + tlv += sizeof(*auto_ds_tlv); + if (auto_ds) + idletime = auto_ds->idle_time; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter Auto Deep Sleep\n"); + auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); + } + cmd->size = cpu_to_le16(cmd_size); + } + return 0; +} + +static int +nxpwifi_ret_sta_enh_power_mode(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = + &resp->params.psmode_enh; + struct nxpwifi_ds_pm_cfg *pm_cfg = + (struct nxpwifi_ds_pm_cfg *)data_buf; + u16 action = le16_to_cpu(ps_mode->action); + u16 ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); + u16 auto_ps_bitmap = + le16_to_cpu(ps_mode->params.ps_bitmap); + + nxpwifi_dbg(adapter, INFO, + "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", + __func__, resp->result, action); + if (action == EN_AUTO_PS) { + if (auto_ps_bitmap & BITMAP_AUTO_DS) { + nxpwifi_dbg(adapter, CMD, + "cmd: Enabled auto deep sleep\n"); + priv->adapter->is_deep_sleep = true; + } + if (auto_ps_bitmap & BITMAP_STA_PS) { + nxpwifi_dbg(adapter, CMD, + "cmd: Enabled STA power save\n"); + if (adapter->sleep_period.period) + nxpwifi_dbg(adapter, CMD, + "cmd: set to uapsd/pps mode\n"); + } + } else if (action == DIS_AUTO_PS) { + if (ps_bitmap & BITMAP_AUTO_DS) { + priv->adapter->is_deep_sleep = false; + nxpwifi_dbg(adapter, CMD, + "cmd: Disabled auto deep sleep\n"); + } + if (ps_bitmap & BITMAP_STA_PS) { + nxpwifi_dbg(adapter, CMD, + "cmd: Disabled STA power save\n"); + if (adapter->sleep_period.period) { + adapter->delay_null_pkt = false; + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + } + } + } else if (action == GET_PS) { + if (ps_bitmap & BITMAP_STA_PS) + adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP; + else + adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM; + + nxpwifi_dbg(adapter, CMD, + "cmd: ps_bitmap=%#x\n", ps_bitmap); + + if (pm_cfg) { + /* This section is for get power save mode */ + if (ps_bitmap & BITMAP_STA_PS) + pm_cfg->param.ps_mode = 1; + else + pm_cfg->param.ps_mode = 0; + } + } + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_hs_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; + struct nxpwifi_hs_config_param *hscfg_param = + (struct nxpwifi_hs_config_param *)data_buf; + u8 *tlv = (u8 *)hs_cfg + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); + struct nxpwifi_ps_param_in_hs *psparam_tlv = NULL; + bool hs_activate = false; + u16 size; + + if (!hscfg_param) + /* New Activate command */ + hs_activate = true; + cmd->command = cpu_to_le16(HOST_CMD_802_11_HS_CFG_ENH); + + if (!hs_activate && + hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL) && + (adapter->arp_filter_size > 0 && + adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE)) { + nxpwifi_dbg(adapter, CMD, + "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", + adapter->arp_filter_size); + memcpy(((u8 *)hs_cfg) + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), + adapter->arp_filter, adapter->arp_filter_size); + size = adapter->arp_filter_size + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + S_DS_GEN; + tlv = (u8 *)hs_cfg + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + adapter->arp_filter_size; + } else { + size = S_DS_GEN + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); + } + if (hs_activate) { + hs_cfg->action = cpu_to_le16(HS_ACTIVATE); + hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); + + adapter->hs_activated_manually = true; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: Activating host sleep manually\n"); + } else { + hs_cfg->action = cpu_to_le16(HS_CONFIGURE); + hs_cfg->params.hs_config.conditions = hscfg_param->conditions; + hs_cfg->params.hs_config.gpio = hscfg_param->gpio; + hs_cfg->params.hs_config.gap = hscfg_param->gap; + + size += sizeof(struct nxpwifi_ps_param_in_hs); + psparam_tlv = (struct nxpwifi_ps_param_in_hs *)tlv; + psparam_tlv->header.type = + cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS); + psparam_tlv->header.len = + cpu_to_le16(sizeof(struct nxpwifi_ps_param_in_hs) + - sizeof(struct nxpwifi_ie_types_header)); + psparam_tlv->hs_wake_int = cpu_to_le32(HS_DEF_WAKE_INTERVAL); + psparam_tlv->hs_inact_timeout = + cpu_to_le32(HS_DEF_INACTIVITY_TIMEOUT); + + nxpwifi_dbg(adapter, CMD, + "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", + hs_cfg->params.hs_config.conditions, + hs_cfg->params.hs_config.gpio, + hs_cfg->params.hs_config.gap); + } + cmd->size = cpu_to_le16(size); + + return 0; +} + +static int +nxpwifi_ret_sta_802_11_hs_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_802_11_hs_cfg(priv, resp); +} + +static int +nxpwifi_cmd_sta_set_bss_mode(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(cmd_no); + if (priv->bss_mode == NL80211_IFTYPE_STATION) + cmd->params.bss_mode.con_type = CONNECTION_TYPE_INFRA; + else if (priv->bss_mode == NL80211_IFTYPE_AP) + cmd->params.bss_mode.con_type = CONNECTION_TYPE_AP; + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_set_bss_mode) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_802_11_scan_ext(priv, cmd, data_buf); +} + +static int +nxpwifi_ret_sta_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + + ret = nxpwifi_ret_802_11_scan_ext(priv, resp); + adapter->curr_cmd->wait_q_enabled = false; + + return ret; +} + +static int +nxpwifi_cmd_sta_coalesce_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_coalesce_cfg *coalesce_cfg = + &cmd->params.coalesce_cfg; + struct nxpwifi_ds_coalesce_cfg *cfg = + (struct nxpwifi_ds_coalesce_cfg *)data_buf; + struct coalesce_filt_field_param *param; + u16 cnt, idx, length; + struct coalesce_receive_filt_rule *rule; + + cmd->command = cpu_to_le16(HOST_CMD_COALESCE_CFG); + cmd->size = cpu_to_le16(S_DS_GEN); + + coalesce_cfg->action = cpu_to_le16(cmd_action); + coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); + rule = (void *)coalesce_cfg->rule_data; + + for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay = + cpu_to_le16(cfg->rule[cnt].max_coalescing_delay); + rule->pkt_type = cfg->rule[cnt].pkt_type; + rule->num_of_fields = cfg->rule[cnt].num_of_fields; + + length = 0; + + param = rule->params; + for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) { + param->operation = cfg->rule[cnt].params[idx].operation; + param->operand_len = + cfg->rule[cnt].params[idx].operand_len; + param->offset = + cpu_to_le16(cfg->rule[cnt].params[idx].offset); + memcpy(param->operand_byte_stream, + cfg->rule[cnt].params[idx].operand_byte_stream, + param->operand_len); + + length += sizeof(struct coalesce_filt_field_param); + + param++; + } + + /* Total rule length is sizeof max_coalescing_delay(u16), + * num_of_fields(u8), pkt_type(u8) and total length of the all + * params + */ + rule->header.len = cpu_to_le16(length + sizeof(u16) + + sizeof(u8) + sizeof(u8)); + + /* Add the rule length to the command size*/ + le16_unaligned_add_cpu(&cmd->size, + le16_to_cpu(rule->header.len) + + sizeof(struct nxpwifi_ie_types_header)); + + rule = (void *)((u8 *)rule->params + length); + } + + /* Add sizeof action, num_of_rules to total command length */ + le16_unaligned_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); + + return 0; +} + +static int +nxpwifi_cmd_sta_mgmt_frame_reg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(cmd_no); + cmd->params.reg_mask.action = cpu_to_le16(cmd_action); + cmd->params.reg_mask.mask = + cpu_to_le32(get_unaligned((u32 *)data_buf)); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_cmd_sta_remain_on_chan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(cmd_no); + memcpy(&cmd->params, data_buf, + sizeof(struct host_cmd_ds_remain_on_chan)); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_ret_sta_remain_on_chan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; + struct host_cmd_ds_remain_on_chan *roc_cfg = + (struct host_cmd_ds_remain_on_chan *)data_buf; + + if (roc_cfg) + memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); + + return 0; +} + +static int +nxpwifi_cmd_sta_gtk_rekey_offload(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_gtk_rekey_params *rekey = &cmd->params.rekey; + struct cfg80211_gtk_rekey_data *data = + (struct cfg80211_gtk_rekey_data *)data_buf; + u64 rekey_ctr; + + cmd->command = cpu_to_le16(HOST_CMD_GTK_REKEY_OFFLOAD_CFG); + cmd->size = cpu_to_le16(sizeof(*rekey) + S_DS_GEN); + + rekey->action = cpu_to_le16(cmd_action); + if (cmd_action == HOST_ACT_GEN_SET) { + memcpy(rekey->kek, data->kek, NL80211_KEK_LEN); + memcpy(rekey->kck, data->kck, NL80211_KCK_LEN); + rekey_ctr = be64_to_cpup((__be64 *)data->replay_ctr); + rekey->replay_ctr_low = cpu_to_le32((u32)rekey_ctr); + rekey->replay_ctr_high = + cpu_to_le32((u32)((u64)rekey_ctr >> 32)); + } + + return 0; +} + +static int +nxpwifi_cmd_sta_11ac_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11ac_cfg(priv, cmd, cmd_action, data_buf); +} + +static int +nxpwifi_cmd_sta_hs_wakeup_reason(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(HOST_CMD_HS_WAKEUP_REASON); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wakeup_reason) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_ret_sta_hs_wakeup_reason(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_wakeup_reason *wakeup_reason = + (struct host_cmd_ds_wakeup_reason *)data_buf; + wakeup_reason->wakeup_reason = + resp->params.hs_wakeup_reason.wakeup_reason; + + return 0; +} + +static int +nxpwifi_cmd_sta_mc_policy(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy; + const u16 *drcs_info = data_buf; + + mc_pol->action = cpu_to_le16(cmd_action); + mc_pol->policy = cpu_to_le16(*drcs_info); + cmd->command = cpu_to_le16(HOST_CMD_MC_POLICY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) + + S_DS_GEN); + return 0; +} + +static int +nxpwifi_cmd_sta_sdio_rx_aggr_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &cmd->params.sdio_rx_aggr_cfg; + + cmd->command = cpu_to_le16(HOST_CMD_SDIO_SP_RX_AGGR_CFG); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + + S_DS_GEN); + cfg->action = cmd_action; + if (cmd_action == HOST_ACT_GEN_SET) + cfg->enable = *(u8 *)data_buf; + + return 0; +} + +static int +nxpwifi_ret_sta_sdio_rx_aggr_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &resp->params.sdio_rx_aggr_cfg; + + adapter->sdio_rx_aggr_enable = cfg->enable; + adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); + + return 0; +} + +static int +nxpwifi_cmd_sta_get_chan_info(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_sta_configure *sta_cfg_cmd = &cmd->params.sta_cfg; + struct host_cmd_tlv_channel_band *tlv_band_channel = + (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer; + + cmd->command = cpu_to_le16(HOST_CMD_STA_CONFIGURE); + cmd->size = cpu_to_le16(sizeof(*sta_cfg_cmd) + + sizeof(*tlv_band_channel) + S_DS_GEN); + sta_cfg_cmd->action = cpu_to_le16(cmd_action); + memset(tlv_band_channel, 0, sizeof(*tlv_band_channel)); + tlv_band_channel->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + tlv_band_channel->header.len = cpu_to_le16(sizeof(*tlv_band_channel) - + sizeof(struct nxpwifi_ie_types_header)); + + return 0; +} + +static int +nxpwifi_ret_sta_get_chan_info(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_sta_configure *sta_cfg_cmd = &resp->params.sta_cfg; + struct nxpwifi_channel_band *channel_band = + (struct nxpwifi_channel_band *)data_buf; + struct host_cmd_tlv_channel_band *tlv_band_channel; + + tlv_band_channel = + (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer; + memcpy(&channel_band->band_config, &tlv_band_channel->band_config, + sizeof(struct nxpwifi_band_config)); + channel_band->channel = tlv_band_channel->channel; + + return 0; +} + +static int +nxpwifi_cmd_sta_chan_region_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_chan_region_cfg *reg = &cmd->params.reg_cfg; + + cmd->command = cpu_to_le16(HOST_CMD_CHAN_REGION_CFG); + cmd->size = cpu_to_le16(sizeof(*reg) + S_DS_GEN); + + if (cmd_action == HOST_ACT_GEN_GET) + reg->action = cpu_to_le16(cmd_action); + + return 0; +} + +static struct ieee80211_regdomain * +nxpwifi_create_custom_regdomain(struct nxpwifi_private *priv, + u8 *buf, u16 buf_len) +{ + u16 num_chan = buf_len / 2; + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *rule; + bool new_rule; + int idx, freq, prev_freq = 0; + u32 bw, prev_bw = 0; + u8 chflags, prev_chflags = 0, valid_rules = 0; + + if (WARN_ON_ONCE(num_chan > NL80211_MAX_SUPP_REG_RULES)) + return ERR_PTR(-EINVAL); + + regd = kzalloc(struct_size(regd, reg_rules, num_chan), GFP_KERNEL); + if (!regd) + return ERR_PTR(-ENOMEM); + + for (idx = 0; idx < num_chan; idx++) { + u8 chan; + enum nl80211_band band; + + chan = *buf++; + if (!chan) { + kfree(regd); + return NULL; + } + chflags = *buf++; + band = (chan <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + freq = ieee80211_channel_to_frequency(chan, band); + new_rule = false; + + if (chflags & NXPWIFI_CHANNEL_DISABLED) + continue; + + if (band == NL80211_BAND_5GHZ) { + if (!(chflags & NXPWIFI_CHANNEL_NOHT80)) + bw = MHZ_TO_KHZ(80); + else if (!(chflags & NXPWIFI_CHANNEL_NOHT40)) + bw = MHZ_TO_KHZ(40); + else + bw = MHZ_TO_KHZ(20); + } else { + if (!(chflags & NXPWIFI_CHANNEL_NOHT40)) + bw = MHZ_TO_KHZ(40); + else + bw = MHZ_TO_KHZ(20); + } + + if (idx == 0 || prev_chflags != chflags || prev_bw != bw || + freq - prev_freq > 20) { + valid_rules++; + new_rule = true; + } + + rule = ®d->reg_rules[valid_rules - 1]; + + rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10); + + prev_chflags = chflags; + prev_freq = freq; + prev_bw = bw; + + if (!new_rule) + continue; + + rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10); + rule->power_rule.max_eirp = DBM_TO_MBM(19); + + if (chflags & NXPWIFI_CHANNEL_PASSIVE) + rule->flags = NL80211_RRF_NO_IR; + + if (chflags & NXPWIFI_CHANNEL_DFS) + rule->flags = NL80211_RRF_DFS; + + rule->freq_range.max_bandwidth_khz = bw; + } + + regd->n_reg_rules = valid_rules; + regd->alpha2[0] = '9'; + regd->alpha2[1] = '9'; + + return regd; +} + +static int +nxpwifi_ret_sta_chan_region_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_chan_region_cfg *reg = &resp->params.reg_cfg; + u16 action = le16_to_cpu(reg->action); + u16 tlv, tlv_buf_len, tlv_buf_left; + struct nxpwifi_ie_types_header *head; + struct ieee80211_regdomain *regd; + u8 *tlv_buf; + + if (action != HOST_ACT_GEN_GET) + return 0; + + tlv_buf = (u8 *)reg + sizeof(*reg); + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*reg); + + while (tlv_buf_left >= sizeof(*head)) { + head = (struct nxpwifi_ie_types_header *)tlv_buf; + tlv = le16_to_cpu(head->type); + tlv_buf_len = le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + + switch (tlv) { + case TLV_TYPE_CHAN_ATTR_CFG: + nxpwifi_dbg_dump(priv->adapter, CMD_D, "CHAN:", + (u8 *)head + sizeof(*head), + tlv_buf_len); + regd = nxpwifi_create_custom_regdomain(priv, (u8 *)head + + sizeof(*head), + tlv_buf_len); + if (!IS_ERR(regd)) + priv->adapter->regd = regd; + break; + } + + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } + + return 0; +} + +static int +nxpwifi_cmd_sta_pkt_aggr_ctrl(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + cmd->command = cpu_to_le16(cmd_no); + cmd->params.pkt_aggr_ctrl.action = cpu_to_le16(cmd_action); + cmd->params.pkt_aggr_ctrl.enable = cpu_to_le16(*(u16 *)data_buf); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_pkt_aggr_ctrl) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_ret_sta_pkt_aggr_ctrl(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_pkt_aggr_ctrl *pkt_aggr_ctrl = + &resp->params.pkt_aggr_ctrl; + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->bus_aggr.enable = le16_to_cpu(pkt_aggr_ctrl->enable); + if (adapter->bus_aggr.enable) + adapter->intf_hdr_len = INTF_HEADER_LEN; + adapter->bus_aggr.mode = NXPWIFI_BUS_AGGR_MODE_LEN_V2; + adapter->bus_aggr.tx_aggr_max_size = + le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_size); + adapter->bus_aggr.tx_aggr_max_num = + le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_num); + adapter->bus_aggr.tx_aggr_align = + le16_to_cpu(pkt_aggr_ctrl->tx_aggr_align); + + return 0; +} + +static int +nxpwifi_cmd_sta_11ax_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11ax_cfg(priv, cmd, cmd_action, data_buf); +} + +static int +nxpwifi_ret_sta_11ax_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_11ax_cfg(priv, resp, data_buf); +} + +static int +nxpwifi_cmd_sta_11ax_cmd(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_11ax_cmd(priv, cmd, cmd_action, data_buf); +} + +static int +nxpwifi_ret_sta_11ax_cmd(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_11ax_cmd(priv, resp, data_buf); +} + +static const struct nxpwifi_cmd_entry cmd_table_sta[] = { + {.cmd_no = HOST_CMD_GET_HW_SPEC, + .prepare_cmd = nxpwifi_cmd_sta_get_hw_spec, + .cmd_resp = nxpwifi_ret_sta_get_hw_spec}, + {.cmd_no = HOST_CMD_802_11_SCAN, + .prepare_cmd = nxpwifi_cmd_sta_802_11_scan, + .cmd_resp = nxpwifi_ret_sta_802_11_scan}, + {.cmd_no = HOST_CMD_802_11_GET_LOG, + .prepare_cmd = nxpwifi_cmd_sta_802_11_get_log, + .cmd_resp = nxpwifi_ret_sta_802_11_get_log}, + {.cmd_no = HOST_CMD_MAC_MULTICAST_ADR, + .prepare_cmd = nxpwifi_cmd_sta_mac_multicast_adr, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_802_11_ASSOCIATE, + .prepare_cmd = nxpwifi_cmd_sta_802_11_associate, + .cmd_resp = nxpwifi_ret_sta_802_11_associate}, + {.cmd_no = HOST_CMD_802_11_SNMP_MIB, + .prepare_cmd = nxpwifi_cmd_sta_802_11_snmp_mib, + .cmd_resp = nxpwifi_ret_sta_802_11_snmp_mib}, + {.cmd_no = HOST_CMD_MAC_REG_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_reg_access, + .cmd_resp = nxpwifi_ret_sta_reg_access}, + {.cmd_no = HOST_CMD_BBP_REG_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_reg_access, + .cmd_resp = nxpwifi_ret_sta_reg_access}, + {.cmd_no = HOST_CMD_RF_REG_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_reg_access, + .cmd_resp = nxpwifi_ret_sta_reg_access}, + {.cmd_no = HOST_CMD_RF_TX_PWR, + .prepare_cmd = nxpwifi_cmd_sta_rf_tx_pwr, + .cmd_resp = nxpwifi_ret_sta_rf_tx_pwr}, + {.cmd_no = HOST_CMD_RF_ANTENNA, + .prepare_cmd = nxpwifi_cmd_sta_rf_antenna, + .cmd_resp = nxpwifi_ret_sta_rf_antenna}, + {.cmd_no = HOST_CMD_802_11_DEAUTHENTICATE, + .prepare_cmd = nxpwifi_cmd_sta_802_11_deauthenticate, + .cmd_resp = nxpwifi_ret_sta_802_11_deauthenticate}, + {.cmd_no = HOST_CMD_MAC_CONTROL, + .prepare_cmd = nxpwifi_cmd_sta_mac_control, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_802_11_MAC_ADDRESS, + .prepare_cmd = nxpwifi_cmd_sta_802_11_mac_address, + .cmd_resp = nxpwifi_ret_sta_802_11_mac_address}, + {.cmd_no = HOST_CMD_802_11_EEPROM_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_reg_access, + .cmd_resp = nxpwifi_ret_sta_reg_access}, + {.cmd_no = HOST_CMD_802_11D_DOMAIN_INFO, + .prepare_cmd = nxpwifi_cmd_sta_802_11d_domain_info, + .cmd_resp = nxpwifi_ret_sta_802_11d_domain_info}, + {.cmd_no = HOST_CMD_802_11_KEY_MATERIAL, + .prepare_cmd = nxpwifi_cmd_sta_802_11_key_material, + .cmd_resp = nxpwifi_ret_sta_802_11_key_material}, + {.cmd_no = HOST_CMD_802_11_BG_SCAN_CONFIG, + .prepare_cmd = nxpwifi_cmd_sta_802_11_bg_scan_config, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_802_11_BG_SCAN_QUERY, + .prepare_cmd = nxpwifi_cmd_sta_802_11_bg_scan_query, + .cmd_resp = nxpwifi_ret_sta_802_11_bg_scan_query}, + {.cmd_no = HOST_CMD_WMM_GET_STATUS, + .prepare_cmd = nxpwifi_cmd_sta_wmm_get_status, + .cmd_resp = nxpwifi_ret_sta_wmm_get_status}, + {.cmd_no = HOST_CMD_802_11_SUBSCRIBE_EVENT, + .prepare_cmd = nxpwifi_cmd_sta_802_11_subsc_evt, + .cmd_resp = nxpwifi_ret_sta_subsc_evt}, + {.cmd_no = HOST_CMD_802_11_TX_RATE_QUERY, + .prepare_cmd = nxpwifi_cmd_sta_802_11_tx_rate_query, + .cmd_resp = nxpwifi_ret_sta_802_11_tx_rate_query}, + {.cmd_no = HOST_CMD_MEM_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_mem_access, + .cmd_resp = nxpwifi_ret_sta_mem_access}, + {.cmd_no = HOST_CMD_CFG_DATA, + .prepare_cmd = nxpwifi_cmd_sta_cfg_data, + .cmd_resp = nxpwifi_ret_sta_cfg_data}, + {.cmd_no = HOST_CMD_VERSION_EXT, + .prepare_cmd = nxpwifi_cmd_sta_ver_ext, + .cmd_resp = nxpwifi_ret_sta_ver_ext}, + {.cmd_no = HOST_CMD_MEF_CFG, + .prepare_cmd = nxpwifi_cmd_sta_mef_cfg, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_RSSI_INFO, + .prepare_cmd = nxpwifi_cmd_sta_802_11_rssi_info, + .cmd_resp = nxpwifi_ret_sta_802_11_rssi_info}, + {.cmd_no = HOST_CMD_FUNC_INIT, + .prepare_cmd = nxpwifi_cmd_sta_func_init, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_FUNC_SHUTDOWN, + .prepare_cmd = nxpwifi_cmd_sta_func_shutdown, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_PMIC_REG_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_reg_access, + .cmd_resp = nxpwifi_ret_sta_reg_access}, + {.cmd_no = HOST_CMD_11N_CFG, + .prepare_cmd = nxpwifi_cmd_sta_11n_cfg, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_11N_ADDBA_REQ, + .prepare_cmd = nxpwifi_cmd_sta_11n_addba_req, + .cmd_resp = nxpwifi_ret_sta_11n_addba_req}, + {.cmd_no = HOST_CMD_11N_ADDBA_RSP, + .prepare_cmd = nxpwifi_cmd_sta_11n_addba_rsp, + .cmd_resp = nxpwifi_ret_sta_11n_addba_rsp}, + {.cmd_no = HOST_CMD_11N_DELBA, + .prepare_cmd = nxpwifi_cmd_sta_11n_delba, + .cmd_resp = nxpwifi_ret_sta_11n_delba}, + {.cmd_no = HOST_CMD_TXPWR_CFG, + .prepare_cmd = nxpwifi_cmd_sta_tx_power_cfg, + .cmd_resp = nxpwifi_ret_sta_tx_power_cfg}, + {.cmd_no = HOST_CMD_TX_RATE_CFG, + .prepare_cmd = nxpwifi_cmd_sta_tx_rate_cfg, + .cmd_resp = nxpwifi_ret_sta_tx_rate_cfg}, + {.cmd_no = HOST_CMD_RECONFIGURE_TX_BUFF, + .prepare_cmd = nxpwifi_cmd_sta_reconfigure_rx_buff, + .cmd_resp = nxpwifi_ret_sta_reconfigure_rx_buff}, + {.cmd_no = HOST_CMD_CHAN_REPORT_REQUEST, + .prepare_cmd = nxpwifi_cmd_sta_chan_report_request, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_AMSDU_AGGR_CTRL, + .prepare_cmd = nxpwifi_cmd_sta_amsdu_aggr_ctrl, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_ROBUST_COEX, + .prepare_cmd = nxpwifi_cmd_sta_robust_coex, + .cmd_resp = nxpwifi_ret_sta_robust_coex}, + {.cmd_no = HOST_CMD_802_11_PS_MODE_ENH, + .prepare_cmd = nxpwifi_cmd_sta_enh_power_mode, + .cmd_resp = nxpwifi_ret_sta_enh_power_mode}, + {.cmd_no = HOST_CMD_802_11_HS_CFG_ENH, + .prepare_cmd = nxpwifi_cmd_sta_802_11_hs_cfg, + .cmd_resp = nxpwifi_ret_sta_802_11_hs_cfg}, + {.cmd_no = HOST_CMD_CAU_REG_ACCESS, + .prepare_cmd = nxpwifi_cmd_sta_reg_access, + .cmd_resp = nxpwifi_ret_sta_reg_access}, + {.cmd_no = HOST_CMD_SET_BSS_MODE, + .prepare_cmd = nxpwifi_cmd_sta_set_bss_mode, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_802_11_SCAN_EXT, + .prepare_cmd = nxpwifi_cmd_sta_802_11_scan_ext, + .cmd_resp = nxpwifi_ret_sta_802_11_scan_ext}, + {.cmd_no = HOST_CMD_COALESCE_CFG, + .prepare_cmd = nxpwifi_cmd_sta_coalesce_cfg, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_MGMT_FRAME_REG, + .prepare_cmd = nxpwifi_cmd_sta_mgmt_frame_reg, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_REMAIN_ON_CHAN, + .prepare_cmd = nxpwifi_cmd_sta_remain_on_chan, + .cmd_resp = nxpwifi_ret_sta_remain_on_chan}, + {.cmd_no = HOST_CMD_GTK_REKEY_OFFLOAD_CFG, + .prepare_cmd = nxpwifi_cmd_sta_gtk_rekey_offload, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_11AC_CFG, + .prepare_cmd = nxpwifi_cmd_sta_11ac_cfg, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_HS_WAKEUP_REASON, + .prepare_cmd = nxpwifi_cmd_sta_hs_wakeup_reason, + .cmd_resp = nxpwifi_ret_sta_hs_wakeup_reason}, + {.cmd_no = HOST_CMD_MC_POLICY, + .prepare_cmd = nxpwifi_cmd_sta_mc_policy, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_FW_DUMP_EVENT, + .prepare_cmd = nxpwifi_cmd_fill_head_only, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_SDIO_SP_RX_AGGR_CFG, + .prepare_cmd = nxpwifi_cmd_sta_sdio_rx_aggr_cfg, + .cmd_resp = nxpwifi_ret_sta_sdio_rx_aggr_cfg}, + {.cmd_no = HOST_CMD_STA_CONFIGURE, + .prepare_cmd = nxpwifi_cmd_sta_get_chan_info, + .cmd_resp = nxpwifi_ret_sta_get_chan_info}, + {.cmd_no = HOST_CMD_CHAN_REGION_CFG, + .prepare_cmd = nxpwifi_cmd_sta_chan_region_cfg, + .cmd_resp = nxpwifi_ret_sta_chan_region_cfg}, + {.cmd_no = HOST_CMD_PACKET_AGGR_CTRL, + .prepare_cmd = nxpwifi_cmd_sta_pkt_aggr_ctrl, + .cmd_resp = nxpwifi_ret_sta_pkt_aggr_ctrl}, + {.cmd_no = HOST_CMD_11AX_CFG, + .prepare_cmd = nxpwifi_cmd_sta_11ax_cfg, + .cmd_resp = nxpwifi_ret_sta_11ax_cfg}, + {.cmd_no = HOST_CMD_11AX_CMD, + .prepare_cmd = nxpwifi_cmd_sta_11ax_cmd, + .cmd_resp = nxpwifi_ret_sta_11ax_cmd}, +}; + +/* This function prepares the commands before sending them to the firmware. + * + * This is a generic function which calls specific command preparation + * routines based upon the command number. + */ +int nxpwifi_sta_prepare_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node, + u16 cmd_action, u32 cmd_oid) + +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 cmd_no = cmd_node->cmd_no; + struct host_cmd_ds_command *cmd = + (struct host_cmd_ds_command *)cmd_node->skb->data; + void *data_buf = cmd_node->data_buf; + int i, ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(cmd_table_sta); i++) { + if (cmd_no == cmd_table_sta[i].cmd_no) { + if (cmd_table_sta[i].prepare_cmd) + ret = cmd_table_sta[i].prepare_cmd(priv, cmd, + cmd_no, + data_buf, + cmd_action, + cmd_oid); + cmd_node->cmd_resp = cmd_table_sta[i].cmd_resp; + break; + } + } + + if (i == ARRAY_SIZE(cmd_table_sta)) + nxpwifi_dbg(adapter, ERROR, + "%s: unknown command: %#x\n", + __func__, cmd_no); + else + nxpwifi_dbg(adapter, CMD, + "%s: command: %#x\n", + __func__, cmd_no); + + return ret; +} + +int nxpwifi_dnld_dt_cfgdata(struct nxpwifi_private *priv, + struct device_node *node, const char *prefix) +{ +#ifdef CONFIG_OF + struct property *prop; + size_t len = strlen(prefix); + int ret; + + /* look for all matching property names */ + for_each_property_of_node(node, prop) { + if (len > strlen(prop->name) || + strncmp(prop->name, prefix, len)) + continue; + + /* property header is 6 bytes, data must fit in cmd buffer */ + if (prop->value && prop->length > 6 && + prop->length <= NXPWIFI_SIZE_OF_CMD_BUFFER - S_DS_GEN) { + ret = nxpwifi_send_cmd(priv, HOST_CMD_CFG_DATA, + HOST_ACT_GEN_SET, 0, + prop, true); + if (ret) + return ret; + } + } +#endif + return 0; +} + +/* This function issues commands to initialize firmware. + * + * This is called after firmware download to bring the card to + * working state. + * Function is also called during reinitialization of virtual + * interfaces. + */ +int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool init) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + struct nxpwifi_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + struct nxpwifi_ds_auto_ds auto_ds; + enum state_11d_t state_11d; + struct nxpwifi_ds_11n_tx_cfg tx_cfg; + u8 sdio_sp_rx_aggr_enable; + int data; + + if (first_sta) { + ret = nxpwifi_send_cmd(priv, HOST_CMD_FUNC_INIT, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) + return ret; + + /* Download calibration data to firmware. + * The cal-data can be read from device tree and/or + * a configuration file and downloaded to firmware. + */ + if (adapter->dt_node) { + if (of_property_read_u32(adapter->dt_node, + "nxp,wakeup-pin", + &data) == 0) { + pr_debug("Wakeup pin = 0x%x\n", data); + adapter->hs_cfg.gpio = data; + } + + nxpwifi_dnld_dt_cfgdata(priv, adapter->dt_node, + "nxp,caldata"); + } + + if (adapter->cal_data) + nxpwifi_send_cmd(priv, HOST_CMD_CFG_DATA, + HOST_ACT_GEN_SET, 0, NULL, true); + + /* Read MAC address from HW */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_GET_HW_SPEC, + HOST_ACT_GEN_GET, 0, NULL, true); + if (ret) + return ret; + + /** Set SDIO Single Port RX Aggr Info */ + if (priv->adapter->iface_type == NXPWIFI_SDIO && + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && + !priv->adapter->host_disable_sdio_rx_aggr) { + sdio_sp_rx_aggr_enable = true; + ret = nxpwifi_send_cmd(priv, + HOST_CMD_SDIO_SP_RX_AGGR_CFG, + HOST_ACT_GEN_SET, 0, + &sdio_sp_rx_aggr_enable, + true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "error while enabling SP aggregation..disable it"); + adapter->sdio_rx_aggr_enable = false; + } + } + + /* Reconfigure tx buf size */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_RECONFIGURE_TX_BUFF, + HOST_ACT_GEN_SET, 0, + &priv->adapter->tx_buf_size, true); + if (ret) + return ret; + + if (priv->bss_type != NXPWIFI_BSS_TYPE_UAP) { + /* Enable IEEE PS by default */ + priv->adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP; + ret = nxpwifi_send_cmd(priv, + HOST_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_STA_PS, NULL, + true); + if (ret) + return ret; + } + + nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REGION_CFG, + HOST_ACT_GEN_GET, 0, NULL, true); + } + + /* get tx rate */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG, + HOST_ACT_GEN_GET, 0, NULL, true); + if (ret) + return ret; + priv->data_rate = 0; + + /* get tx power */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_RF_TX_PWR, + HOST_ACT_GEN_GET, 0, NULL, true); + if (ret) + return ret; + + memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); + amsdu_aggr_ctrl.enable = true; + /* Send request to firmware */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_AMSDU_AGGR_CTRL, + HOST_ACT_GEN_SET, 0, + &amsdu_aggr_ctrl, true); + if (ret) + return ret; + /* MAC Control must be the last command in init_fw */ + /* set MAC Control */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL, + HOST_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + if (ret) + return ret; + + if (!disable_auto_ds && first_sta && + priv->bss_type != NXPWIFI_BSS_TYPE_UAP) { + /* Enable auto deep sleep */ + auto_ds.auto_ds = DEEP_SLEEP_ON; + auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_AUTO_DS, + &auto_ds, true); + if (ret) + return ret; + } + + if (priv->bss_type != NXPWIFI_BSS_TYPE_UAP) { + /* Send cmd to FW to enable/disable 11D function */ + state_11d = ENABLE_11D; + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_SET, DOT11D_I, + &state_11d, true); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + } + + /* Send cmd to FW to configure 11n specific configuration + * (Short GI, Channel BW, Green field support etc.) for transmit + */ + tx_cfg.tx_htcap = NXPWIFI_FW_DEF_HTTXCFG; + ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_CFG, + HOST_ACT_GEN_SET, 0, &tx_cfg, true); + + if (init) { + /* set last_init_cmd before sending the command */ + priv->adapter->last_init_cmd = HOST_CMD_11N_CFG; + ret = -EINPROGRESS; + } + + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_event.c b/drivers/net/wireless/nxp/nxpwifi/sta_event.c new file mode 100644 index 000000000000..a33291b799b6 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_event.c @@ -0,0 +1,861 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: station event handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" + +static int +nxpwifi_sta_event_link_lost(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->dbg.num_event_link_lost++; + if (priv->media_connected) { + adapter->priv_link_lost = priv; + adapter->host_mlme_link_lost = true; + nxpwifi_queue_work(adapter, + &adapter->host_mlme_work); + } + + return 0; +} + +static int +nxpwifi_sta_event_link_sensed(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + netif_carrier_on(priv->netdev); + nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter); + + return 0; +} + +static int +nxpwifi_sta_event_deauthenticated(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 reason_code; + + if (priv->wps.session_enable) { + nxpwifi_dbg(adapter, INFO, + "info: receive deauth event in wps session\n"); + } else { + adapter->dbg.num_event_deauth++; + if (priv->media_connected) { + reason_code = + get_unaligned_le16(adapter->event_body); + nxpwifi_reset_connect_state(priv, reason_code, true); + } + } + + return 0; +} + +static int +nxpwifi_sta_event_disassociated(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 reason_code; + + if (priv->wps.session_enable) { + nxpwifi_dbg(adapter, INFO, + "info: receive disassoc event in wps session\n"); + } else { + adapter->dbg.num_event_disassoc++; + if (priv->media_connected) { + reason_code = + get_unaligned_le16(adapter->event_body); + nxpwifi_reset_connect_state(priv, reason_code, true); + } + } + + return 0; +} + +static int +nxpwifi_sta_event_ps_awake(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (!adapter->pps_uapsd_mode && + priv->port_open && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + nxpwifi_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag = false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (nxpwifi_check_last_packet_indication(priv)) { + if (adapter->data_sent) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + } else { + if (!nxpwifi_send_null_packet + (priv, + NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET | + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = PS_STATE_SLEEP; + } + + return 0; + } + } + + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + + return 0; +} + +static int +nxpwifi_sta_event_ps_sleep(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->ps_state = PS_STATE_PRE_SLEEP; + nxpwifi_check_ps_cond(adapter); + + return 0; +} + +static int +nxpwifi_sta_event_mic_err_multicast(struct nxpwifi_private *priv) +{ + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_GROUP, + -1, NULL, GFP_KERNEL); + + return 0; +} + +static int +nxpwifi_sta_event_mic_err_unicast(struct nxpwifi_private *priv) +{ + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_PAIRWISE, + -1, NULL, GFP_KERNEL); + + return 0; +} + +static int +nxpwifi_sta_event_deep_sleep_awake(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->if_ops.wakeup_complete(adapter); + if (adapter->is_deep_sleep) + adapter->is_deep_sleep = false; + + return 0; +} + +static int +nxpwifi_sta_event_wmm_status_change(struct nxpwifi_private *priv) +{ + return nxpwifi_send_cmd(priv, HOST_CMD_WMM_GET_STATUS, + 0, 0, NULL, false); +} + +static int +nxpwifi_sta_event_bs_scan_report(struct nxpwifi_private *priv) +{ + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_QUERY, + HOST_ACT_GEN_GET, 0, NULL, false); +} + +static int +nxpwifi_sta_event_rssi_low(struct nxpwifi_private *priv) +{ + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + 0, GFP_KERNEL); + priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; + + return nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO, + HOST_ACT_GEN_GET, 0, NULL, false); +} + +static int +nxpwifi_sta_event_rssi_high(struct nxpwifi_private *priv) +{ + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + 0, GFP_KERNEL); + priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; + + return nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO, + HOST_ACT_GEN_GET, 0, NULL, false); +} + +static int +nxpwifi_sta_event_port_release(struct nxpwifi_private *priv) +{ + priv->port_open = true; + + return 0; +} + +static int +nxpwifi_sta_event_addba(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_RSP, + HOST_ACT_GEN_SET, 0, + adapter->event_body, false); +} + +static int +nxpwifi_sta_event_delba(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_11n_delete_ba_stream(priv, adapter->event_body); + + return 0; +} + +static int +nxpwifi_sta_event_bs_stream_timeout(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_11n_batimeout *event = + (struct host_cmd_ds_11n_batimeout *)adapter->event_body; + + nxpwifi_11n_ba_stream_timeout(priv, event); + + return 0; +} + +static int +nxpwifi_sta_event_amsdu_aggr_ctrl(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 ctrl; + + ctrl = get_unaligned_le16(adapter->event_body); + adapter->tx_buf_size = min_t(u16, adapter->curr_tx_buf_size, ctrl); + + return 0; +} + +static int +nxpwifi_sta_event_hs_act_req(struct nxpwifi_private *priv) +{ + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_HS_CFG_ENH, + 0, 0, NULL, false); +} + +static int +nxpwifi_sta_event_channel_switch_ann(struct nxpwifi_private *priv) +{ + struct nxpwifi_bssdescriptor *bss_desc; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + priv->csa_expire_time = jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); + priv->csa_chan = bss_desc->channel; + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE, + HOST_ACT_GEN_SET, 0, + bss_desc->mac_address, false); +} + +static int +nxpwifi_sta_event_radar_detected(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_11h_handle_radar_detected(priv, adapter->event_skb); +} + +static int +nxpwifi_sta_event_channel_report_rdy(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_11h_handle_chanrpt_ready(priv, adapter->event_skb); +} + +static int +nxpwifi_sta_event_tx_data_pause(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_process_tx_pause_event(priv, adapter->event_skb); + + return 0; +} + +static int +nxpwifi_sta_event_ext_scan_report(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + void *buf = adapter->event_skb->data; + int ret = 0; + + /* We intend to skip this event during suspend, but handle + * it in interface disabled case + */ + if (adapter->ext_scan && (!priv->scan_aborting || + !netif_running(priv->netdev))) + ret = nxpwifi_handle_event_ext_scan_report(priv, buf); + + return ret; +} + +static int +nxpwifi_sta_event_rxba_sync(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_11n_rxba_sync_event(priv, adapter->event_body, + adapter->event_skb->len - + sizeof(adapter->event_cause)); + + return 0; +} + +static int +nxpwifi_sta_event_remain_on_chan_expired(struct nxpwifi_private *priv) +{ + if (priv->auth_flag & HOST_MLME_AUTH_PENDING) { + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + } else { + cfg80211_remain_on_channel_expired(&priv->wdev, + priv->roc_cfg.cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + } + + memset(&priv->roc_cfg, 0x00, sizeof(struct nxpwifi_roc_cfg)); + + return 0; +} + +static int +nxpwifi_sta_event_bg_scan_stopped(struct nxpwifi_private *priv) +{ + cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0); + if (priv->sched_scanning) + priv->sched_scanning = false; + + return 0; +} + +static int +nxpwifi_sta_event_multi_chan_info(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_process_multi_chan_event(priv, adapter->event_skb); + + return 0; +} + +static int +nxpwifi_sta_event_tx_status_report(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_parse_tx_status_event(priv, adapter->event_body); + + return 0; +} + +static int +nxpwifi_sta_event_bt_coex_wlan_para_change(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (!adapter->ignore_btcoex_events) + nxpwifi_bt_coex_wlan_param_update_event(priv, + adapter->event_skb); + + return 0; +} + +static int +nxpwifi_sta_event_vdll_ind(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_process_vdll_event(priv, adapter->event_skb); +} + +static const struct nxpwifi_evt_entry evt_table_sta[] = { + {.event_cause = EVENT_LINK_LOST, + .event_handler = nxpwifi_sta_event_link_lost}, + {.event_cause = EVENT_LINK_SENSED, + .event_handler = nxpwifi_sta_event_link_sensed}, + {.event_cause = EVENT_DEAUTHENTICATED, + .event_handler = nxpwifi_sta_event_deauthenticated}, + {.event_cause = EVENT_DISASSOCIATED, + .event_handler = nxpwifi_sta_event_disassociated}, + {.event_cause = EVENT_PS_AWAKE, + .event_handler = nxpwifi_sta_event_ps_awake}, + {.event_cause = EVENT_PS_SLEEP, + .event_handler = nxpwifi_sta_event_ps_sleep}, + {.event_cause = EVENT_MIC_ERR_MULTICAST, + .event_handler = nxpwifi_sta_event_mic_err_multicast}, + {.event_cause = EVENT_MIC_ERR_UNICAST, + .event_handler = nxpwifi_sta_event_mic_err_unicast}, + {.event_cause = EVENT_DEEP_SLEEP_AWAKE, + .event_handler = nxpwifi_sta_event_deep_sleep_awake}, + {.event_cause = EVENT_WMM_STATUS_CHANGE, + .event_handler = nxpwifi_sta_event_wmm_status_change}, + {.event_cause = EVENT_BG_SCAN_REPORT, + .event_handler = nxpwifi_sta_event_bs_scan_report}, + {.event_cause = EVENT_RSSI_LOW, + .event_handler = nxpwifi_sta_event_rssi_low}, + {.event_cause = EVENT_RSSI_HIGH, + .event_handler = nxpwifi_sta_event_rssi_high}, + {.event_cause = EVENT_PORT_RELEASE, + .event_handler = nxpwifi_sta_event_port_release}, + {.event_cause = EVENT_ADDBA, + .event_handler = nxpwifi_sta_event_addba}, + {.event_cause = EVENT_DELBA, + .event_handler = nxpwifi_sta_event_delba}, + {.event_cause = EVENT_BA_STREAM_TIEMOUT, + .event_handler = nxpwifi_sta_event_bs_stream_timeout}, + {.event_cause = EVENT_AMSDU_AGGR_CTRL, + .event_handler = nxpwifi_sta_event_amsdu_aggr_ctrl}, + {.event_cause = EVENT_HS_ACT_REQ, + .event_handler = nxpwifi_sta_event_hs_act_req}, + {.event_cause = EVENT_CHANNEL_SWITCH_ANN, + .event_handler = nxpwifi_sta_event_channel_switch_ann}, + {.event_cause = EVENT_RADAR_DETECTED, + .event_handler = nxpwifi_sta_event_radar_detected}, + {.event_cause = EVENT_CHANNEL_REPORT_RDY, + .event_handler = nxpwifi_sta_event_channel_report_rdy}, + {.event_cause = EVENT_TX_DATA_PAUSE, + .event_handler = nxpwifi_sta_event_tx_data_pause}, + {.event_cause = EVENT_EXT_SCAN_REPORT, + .event_handler = nxpwifi_sta_event_ext_scan_report}, + {.event_cause = EVENT_RXBA_SYNC, + .event_handler = nxpwifi_sta_event_rxba_sync}, + {.event_cause = EVENT_REMAIN_ON_CHAN_EXPIRED, + .event_handler = nxpwifi_sta_event_remain_on_chan_expired}, + {.event_cause = EVENT_BG_SCAN_STOPPED, + .event_handler = nxpwifi_sta_event_bg_scan_stopped}, + {.event_cause = EVENT_MULTI_CHAN_INFO, + .event_handler = nxpwifi_sta_event_multi_chan_info}, + {.event_cause = EVENT_TX_STATUS_REPORT, + .event_handler = nxpwifi_sta_event_tx_status_report}, + {.event_cause = EVENT_BT_COEX_WLAN_PARA_CHANGE, + .event_handler = nxpwifi_sta_event_bt_coex_wlan_para_change}, + {.event_cause = EVENT_VDLL_IND, + .event_handler = nxpwifi_sta_event_vdll_ind}, + {.event_cause = EVENT_DUMMY_HOST_WAKEUP_SIGNAL, + .event_handler = NULL}, + {.event_cause = EVENT_MIB_CHANGED, + .event_handler = NULL}, + {.event_cause = EVENT_INIT_DONE, + .event_handler = NULL}, + {.event_cause = EVENT_SNR_LOW, + .event_handler = NULL}, + {.event_cause = EVENT_MAX_FAIL, + .event_handler = NULL}, + {.event_cause = EVENT_SNR_HIGH, + .event_handler = NULL}, + {.event_cause = EVENT_DATA_RSSI_LOW, + .event_handler = NULL}, + {.event_cause = EVENT_DATA_SNR_LOW, + .event_handler = NULL}, + {.event_cause = EVENT_DATA_RSSI_HIGH, + .event_handler = NULL}, + {.event_cause = EVENT_DATA_SNR_HIGH, + .event_handler = NULL}, + {.event_cause = EVENT_LINK_QUALITY, + .event_handler = NULL}, + {.event_cause = EVENT_PRE_BEACON_LOST, + .event_handler = NULL}, + {.event_cause = EVENT_WEP_ICV_ERR, + .event_handler = NULL}, + {.event_cause = EVENT_BW_CHANGE, + .event_handler = NULL}, + {.event_cause = EVENT_HOSTWAKE_STAIE, + .event_handler = NULL}, + {.event_cause = EVENT_UNKNOWN_DEBUG, + .event_handler = NULL}, +}; + +static void nxpwifi_process_uap_tx_pause(struct nxpwifi_private *priv, + struct nxpwifi_ie_types_header *tlv) +{ + struct nxpwifi_tx_pause_tlv *tp; + struct nxpwifi_sta_node *sta_ptr; + + tp = (void *)tlv; + nxpwifi_dbg(priv->adapter, EVENT, + "uap tx_pause: %pM pause=%d, pkts=%d\n", + tp->peermac, tp->tx_pause, + tp->pkt_cnt); + + if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) { + if (tp->tx_pause) + priv->port_open = false; + else + priv->port_open = true; + } else if (is_multicast_ether_addr(tp->peermac)) { + nxpwifi_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); + } else { + spin_lock_bh(&priv->sta_list_spinlock); + sta_ptr = nxpwifi_get_sta_entry(priv, tp->peermac); + if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { + sta_ptr->tx_pause = tp->tx_pause; + spin_unlock_bh(&priv->sta_list_spinlock); + nxpwifi_update_ralist_tx_pause(priv, tp->peermac, + tp->tx_pause); + } else { + spin_unlock_bh(&priv->sta_list_spinlock); + } + } +} + +static void nxpwifi_process_sta_tx_pause(struct nxpwifi_private *priv, + struct nxpwifi_ie_types_header *tlv) +{ + struct nxpwifi_tx_pause_tlv *tp; + + tp = (void *)tlv; + nxpwifi_dbg(priv->adapter, EVENT, + "sta tx_pause: %pM pause=%d, pkts=%d\n", + tp->peermac, tp->tx_pause, + tp->pkt_cnt); + + if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) { + if (tp->tx_pause) + priv->port_open = false; + else + priv->port_open = true; + } +} + +/* This function resets the connection state. + * + * The function is invoked after receiving a disconnect event from firmware, + * and performs the following actions - + * - Set media status to disconnected + * - Clean up Tx and Rx packets + * - Resets SNR/NF/RSSI value in driver + * - Resets security configurations in driver + * - Enables auto data rate + * - Saves the previous SSID and BSSID so that they can + * be used for re-association, if required + * - Erases current SSID and BSSID information + * - Sends a disconnect event to upper layers/applications. + */ +void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason_code, + bool from_ap) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (!priv->media_connected) + return; + + nxpwifi_dbg(adapter, INFO, + "info: handles disconnect event\n"); + + priv->media_connected = false; + + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + + priv->scan_block = false; + priv->port_open = false; + + /* Free Tx and Rx packets, report disconnect to upper layer */ + nxpwifi_clean_txrx(priv); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_htinfo = 0; + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + priv->wpa_ie_len = 0; + + priv->sec_info.encryption_mode = 0; + + /* Enable auto data rate */ + priv->is_data_rate_auto = true; + priv->data_rate = 0; + + priv->assoc_resp_ht_param = 0; + priv->ht_param_present = false; + + if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) && priv->hist_data) + nxpwifi_hist_data_reset(priv); + + /* Memorize the previous SSID and BSSID so + * it could be used for re-assoc + */ + + nxpwifi_dbg(adapter, INFO, + "info: previous SSID=%s, SSID len=%u\n", + priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); + + nxpwifi_dbg(adapter, INFO, + "info: current SSID=%s, SSID len=%u\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid, + priv->curr_bss_params.bss_descriptor.ssid.ssid_len); + + memcpy(&priv->prev_ssid, + &priv->curr_bss_params.bss_descriptor.ssid, + sizeof(struct cfg80211_ssid)); + + memcpy(priv->prev_bssid, + priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); + + /* Need to erase the current SSID and BSSID info */ + memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); + + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + + if (test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) && + adapter->curr_cmd) + return; + + priv->media_connected = false; + nxpwifi_dbg(adapter, MSG, + "info: successfully disconnected from %pM: reason code %d\n", + priv->cfg_bssid, reason_code); + + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + if (adapter->host_mlme_link_lost) + nxpwifi_host_mlme_disconnect(adapter->priv_link_lost, + reason_code, NULL); + else + cfg80211_disconnected(priv->netdev, reason_code, NULL, + 0, !from_ap, GFP_KERNEL); + } + eth_zero_addr(priv->cfg_bssid); + + nxpwifi_stop_net_dev_queue(priv->netdev, adapter); + netif_carrier_off(priv->netdev); + + if (!ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info)) + return; + + nxpwifi_send_cmd(priv, HOST_CMD_GTK_REKEY_OFFLOAD_CFG, + HOST_ACT_GEN_REMOVE, 0, NULL, false); +} + +void nxpwifi_process_multi_chan_event(struct nxpwifi_private *priv, + struct sk_buff *event_skb) +{ + struct nxpwifi_ie_types_multi_chan_info *chan_info; + struct nxpwifi_ie_types_mc_group_info *grp_info; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie_types_header *tlv; + u16 tlv_buf_left, tlv_type, tlv_len; + int intf_num, bss_type, bss_num, i; + struct nxpwifi_private *intf_priv; + + tlv_buf_left = event_skb->len - sizeof(u32); + chan_info = (void *)event_skb->data + sizeof(u32); + + if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || + tlv_buf_left < sizeof(struct nxpwifi_ie_types_multi_chan_info)) { + nxpwifi_dbg(adapter, ERROR, + "unknown TLV in chan_info event\n"); + return; + } + + adapter->usb_mc_status = le16_to_cpu(chan_info->status); + nxpwifi_dbg(adapter, EVENT, "multi chan operation %s\n", + adapter->usb_mc_status ? "started" : "over"); + + tlv_buf_left -= sizeof(struct nxpwifi_ie_types_multi_chan_info); + tlv = (struct nxpwifi_ie_types_header *)chan_info->tlv_buffer; + + while (tlv_buf_left >= (int)sizeof(struct nxpwifi_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) > + tlv_buf_left) { + nxpwifi_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" + "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); + break; + } + if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { + nxpwifi_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", + tlv_type); + break; + } + + grp_info = (struct nxpwifi_ie_types_mc_group_info *)tlv; + intf_num = grp_info->intf_num; + for (i = 0; i < intf_num; i++) { + bss_type = grp_info->bss_type_numlist[i] >> 4; + bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; + intf_priv = nxpwifi_get_priv_by_id(adapter, bss_num, + bss_type); + if (!intf_priv) { + nxpwifi_dbg(adapter, ERROR, + "Invalid bss_type bss_num\t" + "in multi channel event\n"); + continue; + } + } + + tlv_buf_left -= sizeof(struct nxpwifi_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct nxpwifi_ie_types_header)); + } +} + +void nxpwifi_process_tx_pause_event(struct nxpwifi_private *priv, + struct sk_buff *event_skb) +{ + struct nxpwifi_ie_types_header *tlv; + u16 tlv_type, tlv_len; + int tlv_buf_left; + + if (!priv->media_connected) { + nxpwifi_dbg(priv->adapter, ERROR, + "tx_pause event while disconnected; bss_role=%d\n", + priv->bss_role); + return; + } + + tlv_buf_left = event_skb->len - sizeof(u32); + tlv = (void *)event_skb->data + sizeof(u32); + + while (tlv_buf_left >= (int)sizeof(struct nxpwifi_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) > + tlv_buf_left) { + nxpwifi_dbg(priv->adapter, ERROR, + "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_TX_PAUSE) { + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) + nxpwifi_process_sta_tx_pause(priv, tlv); + else + nxpwifi_process_uap_tx_pause(priv, tlv); + } + + tlv_buf_left -= sizeof(struct nxpwifi_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct nxpwifi_ie_types_header)); + } +} + +/* This function handles coex events generated by firmware */ +void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv, + struct sk_buff *event_skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie_types_header *tlv; + struct nxpwifi_ie_types_btcoex_aggr_win_size *winsizetlv; + struct nxpwifi_ie_types_btcoex_scan_time *scantlv; + s32 len = event_skb->len - sizeof(u32); + u8 *cur_ptr = event_skb->data + sizeof(u32); + u16 tlv_type, tlv_len; + + while (len >= sizeof(struct nxpwifi_ie_types_header)) { + tlv = (struct nxpwifi_ie_types_header *)cur_ptr; + tlv_len = le16_to_cpu(tlv->len); + tlv_type = le16_to_cpu(tlv->type); + + if ((tlv_len + sizeof(struct nxpwifi_ie_types_header)) > len) + break; + switch (tlv_type) { + case TLV_BTCOEX_WL_AGGR_WINSIZE: + winsizetlv = + (struct nxpwifi_ie_types_btcoex_aggr_win_size *)tlv; + adapter->coex_win_size = winsizetlv->coex_win_size; + adapter->coex_tx_win_size = + winsizetlv->tx_win_size; + adapter->coex_rx_win_size = + winsizetlv->rx_win_size; + nxpwifi_coex_ampdu_rxwinsize(adapter); + nxpwifi_update_ampdu_txwinsize(adapter); + break; + + case TLV_BTCOEX_WL_SCANTIME: + scantlv = + (struct nxpwifi_ie_types_btcoex_scan_time *)tlv; + adapter->coex_scan = scantlv->coex_scan; + adapter->coex_min_scan_time = le16_to_cpu(scantlv->min_scan_time); + adapter->coex_max_scan_time = le16_to_cpu(scantlv->max_scan_time); + break; + + default: + break; + } + + len -= tlv_len + sizeof(struct nxpwifi_ie_types_header); + cur_ptr += tlv_len + + sizeof(struct nxpwifi_ie_types_header); + } + + dev_dbg(adapter->dev, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", + adapter->coex_scan, adapter->coex_min_scan_time, + adapter->coex_win_size, adapter->coex_tx_win_size, + adapter->coex_rx_win_size); +} + +/* This function handles events generated by firmware. + * + * This is a generic function and handles all events. + * + * Event specific routines are called by this function based + * upon the generated event cause. + */ +int nxpwifi_process_sta_event(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u32 eventcause = adapter->event_cause; + int evt, ret = 0; + + for (evt = 0; evt < ARRAY_SIZE(evt_table_sta); evt++) { + if (eventcause == evt_table_sta[evt].event_cause) { + if (evt_table_sta[evt].event_handler) + ret = evt_table_sta[evt].event_handler(priv); + break; + } + } + + if (evt == ARRAY_SIZE(evt_table_sta)) + nxpwifi_dbg(adapter, EVENT, + "%s: unknown event id: %#x\n", + __func__, eventcause); + else + nxpwifi_dbg(adapter, EVENT, + "%s: event id: %#x\n", + __func__, eventcause); + + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c b/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c new file mode 100644 index 000000000000..6f8400932a3d --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: AP specific command handling + * + * Copyright 2011-2024 NXP + */ + +#include "main.h" +#include "cmdevt.h" +#include "11n.h" +#include "11ac.h" +#include "11ax.h" + +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WPA/WPA2 security. + * These TLVs are appended to command buffer. + */ +static void +nxpwifi_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_pwk_cipher *pwk_cipher; + struct host_cmd_tlv_gwk_cipher *gwk_cipher; + struct host_cmd_tlv_passphrase *passphrase; + struct host_cmd_tlv_akmp *tlv_akmp; + struct nxpwifi_uap_bss_param *bss_cfg = cmd_buf; + u16 cmd_size = *param_size; + u8 *tlv = *tlv_buf; + + tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; + tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - + sizeof(struct nxpwifi_ie_types_header)); + tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); + tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); + cmd_size += sizeof(struct host_cmd_tlv_akmp); + tlv += sizeof(struct host_cmd_tlv_akmp); + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct nxpwifi_ie_types_header)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct nxpwifi_ie_types_header)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { + gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; + gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); + gwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - + sizeof(struct nxpwifi_ie_types_header)); + gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; + cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); + tlv += sizeof(struct host_cmd_tlv_gwk_cipher); + } + + if (bss_cfg->wpa_cfg.length) { + passphrase = (struct host_cmd_tlv_passphrase *)tlv; + passphrase->header.type = + cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); + passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length); + memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, + bss_cfg->wpa_cfg.length); + cmd_size += sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->wpa_cfg.length; + tlv += sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->wpa_cfg.length; + } + + *param_size = cmd_size; + *tlv_buf = tlv; +} + +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WEP encryption. + * These TLVs are appended to command buffer. + */ +static void +nxpwifi_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_wep_key *wep_key; + u16 cmd_size = *param_size; + int i; + u8 *tlv = *tlv_buf; + struct nxpwifi_uap_bss_param *bss_cfg = cmd_buf; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss_cfg->wep_cfg[i].length && + (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || + bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { + wep_key = (struct host_cmd_tlv_wep_key *)tlv; + wep_key->header.type = + cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + wep_key->header.len = + cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); + wep_key->key_index = bss_cfg->wep_cfg[i].key_index; + wep_key->is_default = bss_cfg->wep_cfg[i].is_default; + memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, + bss_cfg->wep_cfg[i].length); + cmd_size += sizeof(struct nxpwifi_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + tlv += sizeof(struct nxpwifi_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + } + } + + *param_size = cmd_size; + *tlv_buf = tlv; +} + +/* This function parses BSS related parameters from structure + * and prepares TLVs. These TLVs are appended to command buffer. + */ +static int +nxpwifi_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_mac_addr *mac_tlv; + struct host_cmd_tlv_dtim_period *dtim_period; + struct host_cmd_tlv_beacon_period *beacon_period; + struct host_cmd_tlv_ssid *ssid; + struct host_cmd_tlv_bcast_ssid *bcast_ssid; + struct host_cmd_tlv_channel_band *chan_band; + struct host_cmd_tlv_frag_threshold *frag_threshold; + struct host_cmd_tlv_rts_threshold *rts_threshold; + struct host_cmd_tlv_retry_limit *retry_limit; + struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; + struct host_cmd_tlv_auth_type *auth_type; + struct host_cmd_tlv_rates *tlv_rates; + struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; + struct host_cmd_tlv_power_constraint *pwr_ct; + struct nxpwifi_ie_types_htcap *htcap; + struct nxpwifi_ie_types_wmmcap *wmm_cap; + struct nxpwifi_uap_bss_param *bss_cfg = cmd_buf; + int i; + u16 cmd_size = *param_size; + + mac_tlv = (struct host_cmd_tlv_mac_addr *)tlv; + mac_tlv->header.type = cpu_to_le16(TLV_TYPE_UAP_MAC_ADDRESS); + mac_tlv->header.len = cpu_to_le16(ETH_ALEN); + memcpy(mac_tlv->mac_addr, bss_cfg->mac_addr, ETH_ALEN); + cmd_size += sizeof(struct host_cmd_tlv_mac_addr); + tlv += sizeof(struct host_cmd_tlv_mac_addr); + + if (bss_cfg->ssid.ssid_len) { + ssid = (struct host_cmd_tlv_ssid *)tlv; + ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID); + ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); + memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); + cmd_size += sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->ssid.ssid_len; + tlv += sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->ssid.ssid_len; + + bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; + bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); + bcast_ssid->header.len = + cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); + bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; + cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); + tlv += sizeof(struct host_cmd_tlv_bcast_ssid); + } + if (bss_cfg->rates[0]) { + tlv_rates = (struct host_cmd_tlv_rates *)tlv; + tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES); + + for (i = 0; i < NXPWIFI_SUPPORTED_RATES && bss_cfg->rates[i]; + i++) + tlv_rates->rates[i] = bss_cfg->rates[i]; + + tlv_rates->header.len = cpu_to_le16(i); + cmd_size += sizeof(struct host_cmd_tlv_rates) + i; + tlv += sizeof(struct host_cmd_tlv_rates) + i; + } + if (bss_cfg->channel && + (((bss_cfg->band_cfg & BIT(0)) == BAND_CONFIG_BG && + bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || + ((bss_cfg->band_cfg & BIT(0)) == BAND_CONFIG_A && + bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { + chan_band = (struct host_cmd_tlv_channel_band *)tlv; + chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + chan_band->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - + sizeof(struct nxpwifi_ie_types_header)); + chan_band->band_config = bss_cfg->band_cfg; + chan_band->channel = bss_cfg->channel; + cmd_size += sizeof(struct host_cmd_tlv_channel_band); + tlv += sizeof(struct host_cmd_tlv_channel_band); + } + if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && + bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { + beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; + beacon_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); + beacon_period->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - + sizeof(struct nxpwifi_ie_types_header)); + beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); + cmd_size += sizeof(struct host_cmd_tlv_beacon_period); + tlv += sizeof(struct host_cmd_tlv_beacon_period); + } + if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && + bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { + dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; + dtim_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + dtim_period->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - + sizeof(struct nxpwifi_ie_types_header)); + dtim_period->period = bss_cfg->dtim_period; + cmd_size += sizeof(struct host_cmd_tlv_dtim_period); + tlv += sizeof(struct host_cmd_tlv_dtim_period); + } + if (bss_cfg->rts_threshold <= NXPWIFI_RTS_MAX_VALUE) { + rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; + rts_threshold->header.type = + cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); + rts_threshold->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); + cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); + tlv += sizeof(struct host_cmd_tlv_frag_threshold); + } + if (bss_cfg->frag_threshold >= NXPWIFI_FRAG_MIN_VALUE && + bss_cfg->frag_threshold <= NXPWIFI_FRAG_MAX_VALUE) { + frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; + frag_threshold->header.type = + cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); + frag_threshold->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); + cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); + tlv += sizeof(struct host_cmd_tlv_frag_threshold); + } + if (bss_cfg->retry_limit <= NXPWIFI_RETRY_LIMIT) { + retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; + retry_limit->header.type = + cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + retry_limit->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - + sizeof(struct nxpwifi_ie_types_header)); + retry_limit->limit = (u8)bss_cfg->retry_limit; + cmd_size += sizeof(struct host_cmd_tlv_retry_limit); + tlv += sizeof(struct host_cmd_tlv_retry_limit); + } + if ((bss_cfg->protocol & PROTOCOL_WPA) || + (bss_cfg->protocol & PROTOCOL_WPA2) || + (bss_cfg->protocol & PROTOCOL_EAP)) + nxpwifi_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); + else + nxpwifi_uap_bss_wep(&tlv, cmd_buf, &cmd_size); + + if (bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY || + bss_cfg->auth_mode == NXPWIFI_AUTH_MODE_AUTO) { + auth_type = (struct host_cmd_tlv_auth_type *)tlv; + auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_type->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - + sizeof(struct nxpwifi_ie_types_header)); + auth_type->auth_type = (u8)bss_cfg->auth_mode; + auth_type->pwe_derivation = 0; + auth_type->transition_disable = 0; + cmd_size += sizeof(struct host_cmd_tlv_auth_type); + tlv += sizeof(struct host_cmd_tlv_auth_type); + } + if (bss_cfg->protocol) { + encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; + encrypt_protocol->header.type = + cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); + encrypt_protocol->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) + - sizeof(struct nxpwifi_ie_types_header)); + encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); + cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); + tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); + } + + if (bss_cfg->ht_cap.cap_info) { + htcap = (struct nxpwifi_ie_types_htcap *)tlv; + htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + htcap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; + htcap->ht_cap.ampdu_params_info = + bss_cfg->ht_cap.ampdu_params_info; + memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, + sizeof(struct ieee80211_mcs_info)); + htcap->ht_cap.extended_ht_cap_info = + bss_cfg->ht_cap.extended_ht_cap_info; + htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; + htcap->ht_cap.antenna_selection_info = + bss_cfg->ht_cap.antenna_selection_info; + cmd_size += sizeof(struct nxpwifi_ie_types_htcap); + tlv += sizeof(struct nxpwifi_ie_types_htcap); + } + + if (bss_cfg->wmm_info.qos_info != 0xFF) { + wmm_cap = (struct nxpwifi_ie_types_wmmcap *)tlv; + wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); + wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info)); + memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, + sizeof(wmm_cap->wmm_info)); + cmd_size += sizeof(struct nxpwifi_ie_types_wmmcap); + tlv += sizeof(struct nxpwifi_ie_types_wmmcap); + } + + if (bss_cfg->sta_ao_timer) { + ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); + ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) - + sizeof(struct nxpwifi_ie_types_header)); + ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); + cmd_size += sizeof(*ao_timer); + tlv += sizeof(*ao_timer); + } + + if (bss_cfg->power_constraint) { + pwr_ct = (void *)tlv; + pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT); + pwr_ct->header.len = cpu_to_le16(sizeof(u8)); + pwr_ct->constraint = bss_cfg->power_constraint; + cmd_size += sizeof(*pwr_ct); + tlv += sizeof(*pwr_ct); + } + + if (bss_cfg->ps_sta_ao_timer) { + ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ps_ao_timer->header.type = + cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); + ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) - + sizeof(struct nxpwifi_ie_types_header)); + ps_ao_timer->sta_ao_timer = + cpu_to_le32(bss_cfg->ps_sta_ao_timer); + cmd_size += sizeof(*ps_ao_timer); + tlv += sizeof(*ps_ao_timer); + } + + *param_size = cmd_size; + + return 0; +} + +/* This function parses custom IEs from IE list and prepares command buffer */ +static int nxpwifi_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) +{ + struct nxpwifi_ie_list *ap_ie = cmd_buf; + struct nxpwifi_ie_types_header *tlv_ie = (void *)tlv; + + if (!ap_ie || !ap_ie->len) + return -EINVAL; + + *ie_size += le16_to_cpu(ap_ie->len) + + sizeof(struct nxpwifi_ie_types_header); + + tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); + tlv_ie->len = ap_ie->len; + tlv += sizeof(struct nxpwifi_ie_types_header); + + memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); + + return 0; +} + +/* Parse AP config structure and prepare TLV based command structure + * to be sent to FW for uAP configuration + */ +static int +nxpwifi_cmd_uap_sys_config(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + u8 *tlv; + u16 cmd_size, param_size, ie_size; + struct host_cmd_ds_sys_config *sys_cfg; + int ret = 0; + + cmd->command = cpu_to_le16(HOST_CMD_UAP_SYS_CONFIG); + cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); + sys_cfg = &cmd->params.uap_sys_config; + sys_cfg->action = cpu_to_le16(cmd_action); + tlv = sys_cfg->tlv; + + switch (cmd_type) { + case UAP_BSS_PARAMS_I: + param_size = cmd_size; + ret = nxpwifi_uap_bss_param_prepare(tlv, data_buf, ¶m_size); + if (ret) + return ret; + cmd->size = cpu_to_le16(param_size); + break; + case UAP_CUSTOM_IE_I: + ie_size = cmd_size; + ret = nxpwifi_uap_custom_ie_prepare(tlv, data_buf, &ie_size); + if (ret) + return ret; + cmd->size = cpu_to_le16(ie_size); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int +nxpwifi_cmd_uap_bss_start(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_ie_types_host_mlme *tlv; + int size; + + cmd->command = cpu_to_le16(HOST_CMD_UAP_BSS_START); + size = S_DS_GEN; + + tlv = (struct nxpwifi_ie_types_host_mlme *)((u8 *)cmd + size); + tlv->header.type = cpu_to_le16(TLV_TYPE_HOST_MLME); + tlv->header.len = cpu_to_le16(sizeof(tlv->host_mlme)); + tlv->host_mlme = 1; + size += sizeof(struct nxpwifi_ie_types_host_mlme); + + cmd->size = cpu_to_le16(size); + + return 0; +} + +static int +nxpwifi_ret_uap_bss_start(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + adapter->delay_null_pkt = false; + priv->bss_started = 1; + + return 0; +} + +static int +nxpwifi_ret_uap_bss_stop(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + priv->bss_started = 0; + + return 0; +} + +static int +nxpwifi_ret_apcmd_sta_list(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_sta_list *sta_list = + &resp->params.sta_list; + struct nxpwifi_ie_types_sta_info *sta_info = (void *)&sta_list->tlv; + int i; + struct nxpwifi_sta_node *sta_node; + + for (i = 0; i < (le16_to_cpu(sta_list->sta_count)); i++) { + sta_node = nxpwifi_get_sta_entry(priv, sta_info->mac); + if (unlikely(!sta_node)) + continue; + + sta_node->stats.rssi = sta_info->rssi; + sta_info++; + } + + return 0; +} + +/* This function prepares AP specific deauth command with mac supplied in + * function parameter. + */ +static int nxpwifi_cmd_uap_sta_deauth(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; + u8 *mac = (u8 *)data_buf; + + cmd->command = cpu_to_le16(HOST_CMD_UAP_STA_DEAUTH); + memcpy(sta_deauth->mac, mac, ETH_ALEN); + sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + + S_DS_GEN); + return 0; +} + +static int +nxpwifi_cmd_uap_chan_report_request(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + return nxpwifi_cmd_issue_chan_report_request(priv, cmd, data_buf); +} + +/* This function prepares AP specific add station command. + */ +static int +nxpwifi_cmd_uap_add_new_station(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct host_cmd_ds_add_station *new_sta = &cmd->params.sta_info; + struct nxpwifi_sta_info *add_sta = (struct nxpwifi_sta_info *)data_buf; + struct station_parameters *params = add_sta->params; + struct nxpwifi_sta_node *sta_ptr; + u16 cmd_size; + u8 *pos, *cmd_end; + u16 tlv_len; + struct nxpwifi_ie_types_sta_flag *sta_flag; + int i; + + cmd->command = cpu_to_le16(HOST_CMD_ADD_NEW_STATION); + new_sta->action = cpu_to_le16(cmd_action); + cmd_size = sizeof(struct host_cmd_ds_add_station) + S_DS_GEN; + + if (cmd_action == HOST_ACT_ADD_STA) + sta_ptr = nxpwifi_add_sta_entry(priv, add_sta->peer_mac); + else + sta_ptr = nxpwifi_get_sta_entry(priv, add_sta->peer_mac); + + if (!sta_ptr) + return -EINVAL; + + memcpy(new_sta->peer_mac, add_sta->peer_mac, ETH_ALEN); + + if (cmd_action == HOST_ACT_REMOVE_STA) { + cmd->size = cpu_to_le16(cmd_size); + return 0; + } + + new_sta->aid = cpu_to_le16(params->aid); + new_sta->listen_interval = cpu_to_le32(params->listen_interval); + new_sta->cap_info = cpu_to_le16(params->capability); + + pos = new_sta->tlv; + cmd_end = (u8 *)cmd; + cmd_end += (NXPWIFI_SIZE_OF_CMD_BUFFER - 1); + + if (params->sta_flags_set & NL80211_STA_FLAG_WME) + sta_ptr->is_wmm_enabled = 1; + sta_flag = (struct nxpwifi_ie_types_sta_flag *)pos; + sta_flag->header.type = cpu_to_le16(TLV_TYPE_UAP_STA_FLAGS); + sta_flag->header.len = cpu_to_le16(sizeof(__le32)); + sta_flag->sta_flags = cpu_to_le32(params->sta_flags_set); + pos += sizeof(struct nxpwifi_ie_types_sta_flag); + cmd_size += sizeof(struct nxpwifi_ie_types_sta_flag); + + if (params->ext_capab_len) { + u8 *data = (u8 *)params->ext_capab; + u16 len = params->ext_capab_len; + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_EXT_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + } + + if (params->link_sta_params.supported_rates_len) { + u8 *data = (u8 *)params->link_sta_params.supported_rates; + u16 len = params->link_sta_params.supported_rates_len; + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_SUPP_RATES, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + } + + if (params->uapsd_queues || params->max_sp) { + u8 qos_capability = params->uapsd_queues | (params->max_sp << 5); + u8 *data = &qos_capability; + u16 len = sizeof(u8); + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_QOS_CAPA, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + sta_ptr->is_wmm_enabled = 1; + } + + if (params->link_sta_params.ht_capa) { + u8 *data = (u8 *)params->link_sta_params.ht_capa; + u16 len = sizeof(struct ieee80211_ht_cap); + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_HT_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + sta_ptr->is_11n_enabled = 1; + sta_ptr->max_amsdu = + le16_to_cpu(params->link_sta_params.ht_capa->cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU ? + NXPWIFI_TX_DATA_BUF_SIZE_8K : + NXPWIFI_TX_DATA_BUF_SIZE_4K; + } + + if (params->link_sta_params.vht_capa) { + u8 *data = (u8 *)params->link_sta_params.vht_capa; + u16 len = sizeof(struct ieee80211_vht_cap); + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_VHT_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + sta_ptr->is_11ac_enabled = 1; + } + + if (params->link_sta_params.opmode_notif_used) { + u8 *data = ¶ms->link_sta_params.opmode_notif; + u16 len = sizeof(u8); + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_OPMODE_NOTIF, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + } + + if (params->link_sta_params.he_capa_len) { + u8 *data = (u8 *)params->link_sta_params.he_capa; + u16 len = params->link_sta_params.he_capa_len; + + tlv_len = nxpwifi_append_data_tlv(WLAN_EID_EXT_HE_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos += tlv_len; + cmd_size += tlv_len; + sta_ptr->is_11ax_enabled = 1; + } + + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled || sta_ptr->is_11ax_enabled) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + else + sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + + memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + + cmd->size = cpu_to_le16(cmd_size); + + return 0; +} + +static const struct nxpwifi_cmd_entry cmd_table_uap[] = { + {.cmd_no = HOST_CMD_APCMD_SYS_RESET, + .prepare_cmd = nxpwifi_cmd_fill_head_only, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_UAP_SYS_CONFIG, + .prepare_cmd = nxpwifi_cmd_uap_sys_config, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_UAP_BSS_START, + .prepare_cmd = nxpwifi_cmd_uap_bss_start, + .cmd_resp = nxpwifi_ret_uap_bss_start}, + {.cmd_no = HOST_CMD_UAP_BSS_STOP, + .prepare_cmd = nxpwifi_cmd_fill_head_only, + .cmd_resp = nxpwifi_ret_uap_bss_stop}, + {.cmd_no = HOST_CMD_APCMD_STA_LIST, + .prepare_cmd = nxpwifi_cmd_fill_head_only, + .cmd_resp = nxpwifi_ret_apcmd_sta_list}, + {.cmd_no = HOST_CMD_UAP_STA_DEAUTH, + .prepare_cmd = nxpwifi_cmd_uap_sta_deauth, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_CHAN_REPORT_REQUEST, + .prepare_cmd = nxpwifi_cmd_uap_chan_report_request, + .cmd_resp = NULL}, + {.cmd_no = HOST_CMD_ADD_NEW_STATION, + .prepare_cmd = nxpwifi_cmd_uap_add_new_station, + .cmd_resp = NULL}, +}; + +/* This function prepares the AP specific commands before sending them + * to the firmware. + * This is a generic function which calls specific command preparation + * routines based upon the command number. + */ +int nxpwifi_uap_prepare_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node, + u16 cmd_action, u32 type) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 cmd_no = cmd_node->cmd_no; + struct host_cmd_ds_command *cmd = + (struct host_cmd_ds_command *)cmd_node->skb->data; + void *data_buf = cmd_node->data_buf; + int i, ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(cmd_table_uap); i++) { + if (cmd_no == cmd_table_uap[i].cmd_no) { + if (cmd_table_uap[i].prepare_cmd) + ret = cmd_table_uap[i].prepare_cmd(priv, cmd, + cmd_no, + data_buf, + cmd_action, + type); + cmd_node->cmd_resp = cmd_table_uap[i].cmd_resp; + break; + } + } + + if (i == ARRAY_SIZE(cmd_table_uap)) + nxpwifi_dbg(adapter, ERROR, + "%s: unknown command: %#x\n", + __func__, cmd_no); + else + nxpwifi_dbg(adapter, CMD, + "%s: command: %#x\n", + __func__, cmd_no); + + return ret; +} + +/* This function parses security related parameters from cfg80211_ap_settings + * and sets into FW understandable bss_config structure. + */ +int nxpwifi_set_secure_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_config, + struct cfg80211_ap_settings *params) +{ + int i; + struct nxpwifi_wep_key wep_key; + + if (!params->privacy) { + bss_config->protocol = PROTOCOL_NO_SECURITY; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + priv->sec_info.wep_enabled = 0; + priv->sec_info.wpa_enabled = 0; + priv->sec_info.wpa2_enabled = 0; + + return 0; + } + + switch (params->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + bss_config->auth_mode = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + bss_config->auth_mode = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + bss_config->auth_mode = WLAN_AUTH_LEAP; + break; + default: + bss_config->auth_mode = NXPWIFI_AUTH_MODE_AUTO; + break; + } + + bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST; + + bss_config->protocol = 0; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->protocol |= PROTOCOL_WPA; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->protocol |= PROTOCOL_WPA2; + + bss_config->key_mgmt = 0; + for (i = 0; i < params->crypto.n_akm_suites; i++) { + switch (params->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + bss_config->key_mgmt |= KEY_MGMT_EAP; + break; + case WLAN_AKM_SUITE_PSK: + bss_config->key_mgmt |= KEY_MGMT_PSK; + break; + case WLAN_AKM_SUITE_PSK_SHA256: + bss_config->key_mgmt |= KEY_MGMT_PSK_SHA256; + break; + case WLAN_AKM_SUITE_OWE: + bss_config->key_mgmt |= KEY_MGMT_OWE; + break; + case WLAN_AKM_SUITE_SAE: + bss_config->key_mgmt |= KEY_MGMT_SAE; + break; + default: + break; + } + } + + 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) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + break; + default: + break; + } + } + + switch (params->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (priv->sec_info.wep_enabled) { + bss_config->protocol = PROTOCOL_STATIC_WEP; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + wep_key = priv->wep_key[i]; + bss_config->wep_cfg[i].key_index = i; + + if (priv->wep_key_curr_index == i) + bss_config->wep_cfg[i].is_default = 1; + else + bss_config->wep_cfg[i].is_default = 0; + + bss_config->wep_cfg[i].length = + wep_key.key_length; + memcpy(&bss_config->wep_cfg[i].key, + &wep_key.key_material, + wep_key.key_length); + } + } + break; + case WLAN_CIPHER_SUITE_TKIP: + bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + + return 0; +} + +/* This function updates 11n related parameters from IE and sets them into + * bss_config structure. + */ +void +nxpwifi_set_ht_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *ht_ie; + + if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) + return; + + ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (ht_ie) { + memcpy(&bss_cfg->ht_cap, ht_ie + 2, + sizeof(struct ieee80211_ht_cap)); + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + bss_cfg->ht_cap.tx_BF_cap_info = + cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP); + priv->ap_11n_enabled = 1; + } else { + memset(&bss_cfg->ht_cap, 0, sizeof(struct ieee80211_ht_cap)); + bss_cfg->ht_cap.cap_info = cpu_to_le16(NXPWIFI_DEF_HT_CAP); + bss_cfg->ht_cap.ampdu_params_info = NXPWIFI_DEF_AMPDU; + } +} + +/* This function updates 11ac related parameters from IE + * and sets them into bss_config structure. + */ +void nxpwifi_set_vht_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vht_ie; + + vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (vht_ie) { + memcpy(&bss_cfg->vht_cap, vht_ie + 2, + sizeof(struct ieee80211_vht_cap)); + priv->ap_11ac_enabled = 1; + } else { + priv->ap_11ac_enabled = 0; + } +} + +/* This function updates 11ac related parameters from IE + * and sets them into bss_config structure. + */ +void nxpwifi_set_tpc_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *tpc_ie; + + tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail, + params->beacon.tail_len); + if (tpc_ie) + bss_cfg->power_constraint = *(tpc_ie + 2); + else + bss_cfg->power_constraint = 0; +} + +/* Enable VHT only when cfg80211_ap_settings has VHT IE. + * Otherwise disable VHT. + */ +void nxpwifi_set_vht_width(struct nxpwifi_private *priv, + enum nl80211_chan_width width, + bool ap_11ac_enable) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_11ac_vht_cfg vht_cfg; + + vht_cfg.band_config = VHT_CFG_5GHZ; + vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap; + + if (!ap_11ac_enable) { + vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET; + vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET; + } else { + vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET; + vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET; + } + + vht_cfg.misc_config = VHT_CAP_UAP_ONLY; + + if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80) + vht_cfg.misc_config |= VHT_BW_80_160_80P80; + + nxpwifi_send_cmd(priv, HOST_CMD_11AC_CFG, + HOST_ACT_GEN_SET, 0, &vht_cfg, true); +} + +bool nxpwifi_check_11ax_capability(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u8 band = bss_cfg->band_cfg & BAND_CFG_CHAN_BAND_MASK; + + if (band == BAND_2GHZ && + !(adapter->fw_bands & BAND_GAX)) + return false; + + if (band == BAND_5GHZ && + !(adapter->fw_bands & BAND_AAX)) + return false; + + if (params->he_cap) + return true; + else + return false; +} + +int nxpwifi_set_11ax_status(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct nxpwifi_11ax_he_cfg ax_cfg; + u8 band = bss_cfg->band_cfg & BAND_CFG_CHAN_BAND_MASK; + const struct element *he_cap; + int ret; + + if (band == BAND_2GHZ) + ax_cfg.band = BIT(0); + else if (band == BAND_5GHZ) + ax_cfg.band = BIT(1); + else + return -EINVAL; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_11AX_CFG, + HOST_ACT_GEN_GET, 0, &ax_cfg, true); + if (ret) + return ret; + + he_cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, + params->beacon.tail, + params->beacon.tail_len); + + if (he_cap) { + ax_cfg.he_cap_cfg.id = he_cap->id; + ax_cfg.he_cap_cfg.len = he_cap->datalen; + memcpy(ax_cfg.data + 4, + he_cap->data, + he_cap->datalen); + } else { + /* disable */ + if (ax_cfg.he_cap_cfg.len && + ax_cfg.he_cap_cfg.ext_id == WLAN_EID_EXT_HE_CAPABILITY) { + memset(ax_cfg.he_cap_cfg.he_txrx_mcs_support, 0xff, + sizeof(ax_cfg.he_cap_cfg.he_txrx_mcs_support)); + } + } + + return nxpwifi_send_cmd(priv, HOST_CMD_11AX_CFG, + HOST_ACT_GEN_SET, 0, &ax_cfg, true); +} + +/* This function finds supported rates IE from beacon parameter and sets + * these rates into bss_config structure. + */ +void +nxpwifi_set_uap_rates(struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct element *rate_ie; + int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos = params->beacon.head + var_offset; + int len = params->beacon.head_len - var_offset; + u8 rate_len = 0; + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); + if (rate_ie) { + if (rate_ie->datalen > NXPWIFI_SUPPORTED_RATES) + return; + memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->datalen); + rate_len = rate_ie->datalen; + } + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, + params->beacon.tail, + params->beacon.tail_len); + if (rate_ie) { + if (rate_ie->datalen > NXPWIFI_SUPPORTED_RATES - rate_len) + return; + memcpy(bss_cfg->rates + rate_len, + rate_ie + 1, rate_ie->datalen); + } +} + +/* This function initializes some of nxpwifi_uap_bss_param variables. + * This helps FW in ignoring invalid values. These values may or may not + * be get updated to valid ones at later stage. + */ +void nxpwifi_set_sys_config_invalid_data(struct nxpwifi_uap_bss_param *config) +{ + config->bcast_ssid_ctl = 0x7F; + config->radio_ctl = 0x7F; + config->dtim_period = 0x7F; + config->beacon_period = 0x7FFF; + config->auth_mode = 0x7F; + config->rts_threshold = 0x7FFF; + config->frag_threshold = 0x7FFF; + config->retry_limit = 0x7F; + config->qos_info = 0xFF; +} + +/* This function parses WMM related parameters from cfg80211_ap_settings + * structure and updates bss_config structure. + */ +void +nxpwifi_set_wmm_params(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vendor_ie; + const u8 *wmm_ie; + static const u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; + + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + params->beacon.tail, + params->beacon.tail_len); + if (vendor_ie) { + wmm_ie = vendor_ie; + if (*(wmm_ie + 1) > sizeof(struct nxpwifi_types_wmm_info)) + return; + memcpy(&bss_cfg->wmm_info, wmm_ie + + sizeof(struct element), *(wmm_ie + 1)); + priv->wmm_enabled = 1; + } else { + memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); + memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui)); + bss_cfg->wmm_info.subtype = NXPWIFI_WMM_SUBTYPE; + bss_cfg->wmm_info.version = NXPWIFI_WMM_VERSION; + priv->wmm_enabled = 0; + } + + bss_cfg->qos_info = 0x00; +} + +/* This function enable 11D if userspace set the country IE. + */ +void nxpwifi_config_uap_11d(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *beacon_data) +{ + enum state_11d_t state_11d; + const u8 *country_ie; + + country_ie = cfg80211_find_ie(WLAN_EID_COUNTRY, beacon_data->tail, + beacon_data->tail_len); + if (country_ie) { + /* Send cmd to FW to enable 11D function */ + state_11d = ENABLE_11D; + if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_SET, DOT11D_I, + &state_11d, true)) { + nxpwifi_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + } + } +} + +void nxpwifi_uap_set_channel(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef) +{ + u8 config_bands = 0, old_bands = priv->config_bands; + + priv->bss_chandef = chandef; + + bss_cfg->channel = + ieee80211_frequency_to_channel(chandef.chan->center_freq); + + /* Set appropriate bands */ + if (chandef.chan->band == NL80211_BAND_2GHZ) { + bss_cfg->band_cfg = BAND_CONFIG_BG; + config_bands = BAND_B | BAND_G; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_GN; + } else { + bss_cfg->band_cfg = BAND_CONFIG_A; + config_bands = BAND_A; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_AN; + + if (chandef.width > NL80211_CHAN_WIDTH_40) + config_bands |= BAND_AAC; + } + + switch (chandef.width) { + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + break; + case NL80211_CHAN_WIDTH_40: + if (chandef.center_freq1 < chandef.chan->center_freq) + bss_cfg->band_cfg |= NXPWIFI_SEC_CHAN_BELOW; + else + bss_cfg->band_cfg |= NXPWIFI_SEC_CHAN_ABOVE; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + bss_cfg->band_cfg |= + nxpwifi_get_sec_chan_offset(bss_cfg->channel) << 4; + break; + default: + nxpwifi_dbg(priv->adapter, + WARN, "Unknown channel width: %d\n", + chandef.width); + break; + } + + priv->config_bands = config_bands; + + if (old_bands != config_bands) { + if (nxpwifi_band_to_radio_type(priv->config_bands) == + HOST_SCAN_RADIO_TYPE_BG) + nxpwifi_send_domain_info_cmd_fw(priv->adapter->wiphy, + NL80211_BAND_2GHZ); + else + nxpwifi_send_domain_info_cmd_fw(priv->adapter->wiphy, + NL80211_BAND_5GHZ); + nxpwifi_dnld_txpwr_table(priv); + } +} + +int nxpwifi_config_start_uap(struct nxpwifi_private *priv, + struct nxpwifi_uap_bss_param *bss_cfg) +{ + int ret; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG, + HOST_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to set AP configuration\n"); + return ret; + } + + ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_START, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to start the BSS\n"); + return ret; + } + + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HOST_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HOST_ACT_MAC_WEP_ENABLE; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL, + HOST_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_event.c b/drivers/net/wireless/nxp/nxpwifi/uap_event.c new file mode 100644 index 000000000000..1bfb41674e62 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/uap_event.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: AP event handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "main.h" +#include "cmdevt.h" +#include "11n.h" + +#define NXPWIFI_BSS_START_EVT_FIX_SIZE 12 + +static int +nxpwifi_uap_event_ps_awake(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (!adapter->pps_uapsd_mode && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + nxpwifi_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag = false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (nxpwifi_check_last_packet_indication(priv)) { + if (adapter->data_sent) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + } else { + if (!nxpwifi_send_null_packet + (priv, + NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET | + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = PS_STATE_SLEEP; + } + + return 0; + } + } + + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + + return 0; +} + +static int +nxpwifi_uap_event_ps_sleep(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + adapter->ps_state = PS_STATE_PRE_SLEEP; + nxpwifi_check_ps_cond(adapter); + + return 0; +} + +static int +nxpwifi_uap_event_sta_deauth(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u8 *deauth_mac; + + deauth_mac = adapter->event_body + + NXPWIFI_UAP_EVENT_EXTRA_HEADER; + cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL); + + if (priv->ap_11n_enabled) { + nxpwifi_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac); + nxpwifi_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac); + } + nxpwifi_wmm_del_peer_ra_list(priv, deauth_mac); + nxpwifi_del_sta_entry(priv, deauth_mac); + + return 0; +} + +static int +nxpwifi_uap_event_sta_assoc(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct station_info *sinfo; + struct nxpwifi_assoc_event *event; + struct nxpwifi_sta_node *node; + int len, i; + + sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + + event = (struct nxpwifi_assoc_event *) + (adapter->event_body + NXPWIFI_UAP_EVENT_EXTRA_HEADER); + if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { + len = -1; + + if (ieee80211_is_assoc_req(event->frame_control)) + len = 0; + else if (ieee80211_is_reassoc_req(event->frame_control)) + /* There will be ETH_ALEN bytes of + * current_ap_addr before the re-assoc ies. + */ + len = ETH_ALEN; + + if (len != -1) { + sinfo->assoc_req_ies = &event->data[len]; + len = (u8 *)sinfo->assoc_req_ies - + (u8 *)&event->frame_control; + sinfo->assoc_req_ies_len = + le16_to_cpu(event->len) - (u16)len; + } + } + cfg80211_new_sta(priv->netdev, event->sta_addr, sinfo, + GFP_KERNEL); + + node = nxpwifi_add_sta_entry(priv, event->sta_addr); + if (!node) { + nxpwifi_dbg(adapter, ERROR, + "could not create station entry!\n"); + kfree(sinfo); + return -ENOENT; + } + + if (!priv->ap_11n_enabled) { + kfree(sinfo); + return 0; + } + + nxpwifi_set_sta_ht_cap(priv, sinfo->assoc_req_ies, + sinfo->assoc_req_ies_len, node); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (node->is_11n_enabled || node->is_11ax_enabled) + node->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + else + node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + memset(node->rx_seq, 0xff, sizeof(node->rx_seq)); + kfree(sinfo); + + return 0; +} + +static int +nxpwifi_check_uap_capabilities(struct nxpwifi_private *priv, + struct sk_buff *event) +{ + int evt_len; + u8 *curr; + u16 tlv_len; + struct nxpwifi_ie_types_data *tlv_hdr; + struct ieee80211_wmm_param_ie *wmm_param_ie = NULL; + int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + + priv->wmm_enabled = false; + skb_pull(event, NXPWIFI_BSS_START_EVT_FIX_SIZE); + evt_len = event->len; + curr = event->data; + + nxpwifi_dbg_dump(priv->adapter, EVT_D, "uap capabilities:", + event->data, event->len); + + skb_push(event, NXPWIFI_BSS_START_EVT_FIX_SIZE); + + while ((evt_len >= sizeof(tlv_hdr->header))) { + tlv_hdr = (struct nxpwifi_ie_types_data *)curr; + tlv_len = le16_to_cpu(tlv_hdr->header.len); + + if (evt_len < tlv_len + sizeof(tlv_hdr->header)) + break; + + switch (le16_to_cpu(tlv_hdr->header.type)) { + case WLAN_EID_HT_CAPABILITY: + priv->ap_11n_enabled = true; + break; + + case WLAN_EID_VHT_CAPABILITY: + priv->ap_11ac_enabled = true; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* Point the regular IEEE IE 2 bytes into the NXP IE + * and setup the IEEE IE type and length byte fields + */ + wmm_param_ie = (void *)(curr + 2); + wmm_param_ie->len = (u8)tlv_len; + wmm_param_ie->element_id = + WLAN_EID_VENDOR_SPECIFIC; + nxpwifi_dbg(priv->adapter, EVENT, + "info: check uap capabilities:\t" + "wmm parameter set count: %d\n", + wmm_param_ie->qos_info & mask); + + nxpwifi_wmm_setup_ac_downgrade(priv); + priv->wmm_enabled = true; + nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie); + break; + + default: + break; + } + + curr += (tlv_len + sizeof(tlv_hdr->header)); + evt_len -= (tlv_len + sizeof(tlv_hdr->header)); + } + + return 0; +} + +static int +nxpwifi_uap_event_bss_start(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + priv->port_open = false; + eth_hw_addr_set(priv->netdev, adapter->event_body + 2); + if (priv->hist_data) + nxpwifi_hist_data_reset(priv); + return nxpwifi_check_uap_capabilities(priv, adapter->event_skb); +} + +static int +nxpwifi_uap_event_addba(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (priv->media_connected) + nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_RSP, + HOST_ACT_GEN_SET, 0, + adapter->event_body, false); + + return 0; +} + +static int +nxpwifi_uap_event_delba(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (priv->media_connected) + nxpwifi_11n_delete_ba_stream(priv, adapter->event_body); + + return 0; +} + +static int +nxpwifi_uap_event_ba_stream_timeout(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct host_cmd_ds_11n_batimeout *ba_timeout; + + if (priv->media_connected) { + ba_timeout = (void *)adapter->event_body; + nxpwifi_11n_ba_stream_timeout(priv, ba_timeout); + } + + return 0; +} + +static int +nxpwifi_uap_event_amsdu_aggr_ctrl(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u16 ctrl; + + ctrl = get_unaligned_le16(adapter->event_body); + nxpwifi_dbg(adapter, EVENT, + "event: AMSDU_AGGR_CTRL %d\n", ctrl); + + if (priv->media_connected) { + adapter->tx_buf_size = + min_t(u16, adapter->curr_tx_buf_size, ctrl); + nxpwifi_dbg(adapter, EVENT, + "event: tx_buf_size %d\n", + adapter->tx_buf_size); + } + + return 0; +} + +static int +nxpwifi_uap_event_bss_idle(struct nxpwifi_private *priv) +{ + priv->media_connected = false; + priv->port_open = false; + nxpwifi_clean_txrx(priv); + nxpwifi_del_all_sta_list(priv); + + return 0; +} + +static int +nxpwifi_uap_event_bss_active(struct nxpwifi_private *priv) +{ + priv->media_connected = true; + priv->port_open = true; + + return 0; +} + +static int +nxpwifi_uap_event_mic_countermeasures(struct nxpwifi_private *priv) +{ + /* For future development */ + + return 0; +} + +static int +nxpwifi_uap_event_radar_detected(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_11h_handle_radar_detected(priv, adapter->event_skb); +} + +static int +nxpwifi_uap_event_channel_report_rdy(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_11h_handle_chanrpt_ready(priv, adapter->event_skb); +} + +static int +nxpwifi_uap_event_tx_data_pause(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_process_tx_pause_event(priv, adapter->event_skb); + + return 0; +} + +static int +nxpwifi_uap_event_ext_scan_report(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + void *buf = adapter->event_skb->data; + int ret = 0; + + if (adapter->ext_scan) + ret = nxpwifi_handle_event_ext_scan_report(priv, buf); + + return ret; +} + +static int +nxpwifi_uap_event_rxba_sync(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_11n_rxba_sync_event(priv, adapter->event_body, + adapter->event_skb->len - + sizeof(adapter->event_cause)); + + return 0; +} + +static int +nxpwifi_uap_event_remain_on_chan_expired(struct nxpwifi_private *priv) +{ + cfg80211_remain_on_channel_expired(&priv->wdev, + priv->roc_cfg.cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + memset(&priv->roc_cfg, 0x00, sizeof(struct nxpwifi_roc_cfg)); + + return 0; +} + +static int +nxpwifi_uap_event_multi_chan_info(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_process_multi_chan_event(priv, adapter->event_skb); + + return 0; +} + +static int +nxpwifi_uap_event_tx_status_report(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_parse_tx_status_event(priv, adapter->event_body); + + return 0; +} + +static int +nxpwifi_uap_event_bt_coex_wlan_para_change(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + nxpwifi_bt_coex_wlan_param_update_event(priv, adapter->event_skb); + + return 0; +} + +static int +nxpwifi_uap_event_vdll_ind(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + return nxpwifi_process_vdll_event(priv, adapter->event_skb); +} + +static const struct nxpwifi_evt_entry evt_table_uap[] = { + {.event_cause = EVENT_PS_AWAKE, + .event_handler = nxpwifi_uap_event_ps_awake}, + {.event_cause = EVENT_PS_SLEEP, + .event_handler = nxpwifi_uap_event_ps_sleep}, + {.event_cause = EVENT_UAP_STA_DEAUTH, + .event_handler = nxpwifi_uap_event_sta_deauth}, + {.event_cause = EVENT_UAP_STA_ASSOC, + .event_handler = nxpwifi_uap_event_sta_assoc}, + {.event_cause = EVENT_UAP_BSS_START, + .event_handler = nxpwifi_uap_event_bss_start}, + {.event_cause = EVENT_ADDBA, + .event_handler = nxpwifi_uap_event_addba}, + {.event_cause = EVENT_DELBA, + .event_handler = nxpwifi_uap_event_delba}, + {.event_cause = EVENT_BA_STREAM_TIEMOUT, + .event_handler = nxpwifi_uap_event_ba_stream_timeout}, + {.event_cause = EVENT_AMSDU_AGGR_CTRL, + .event_handler = nxpwifi_uap_event_amsdu_aggr_ctrl}, + {.event_cause = EVENT_UAP_BSS_IDLE, + .event_handler = nxpwifi_uap_event_bss_idle}, + {.event_cause = EVENT_UAP_BSS_ACTIVE, + .event_handler = nxpwifi_uap_event_bss_active}, + {.event_cause = EVENT_UAP_MIC_COUNTERMEASURES, + .event_handler = nxpwifi_uap_event_mic_countermeasures}, + {.event_cause = EVENT_RADAR_DETECTED, + .event_handler = nxpwifi_uap_event_radar_detected}, + {.event_cause = EVENT_CHANNEL_REPORT_RDY, + .event_handler = nxpwifi_uap_event_channel_report_rdy}, + {.event_cause = EVENT_TX_DATA_PAUSE, + .event_handler = nxpwifi_uap_event_tx_data_pause}, + {.event_cause = EVENT_EXT_SCAN_REPORT, + .event_handler = nxpwifi_uap_event_ext_scan_report}, + {.event_cause = EVENT_RXBA_SYNC, + .event_handler = nxpwifi_uap_event_rxba_sync}, + {.event_cause = EVENT_REMAIN_ON_CHAN_EXPIRED, + .event_handler = nxpwifi_uap_event_remain_on_chan_expired}, + {.event_cause = EVENT_MULTI_CHAN_INFO, + .event_handler = nxpwifi_uap_event_multi_chan_info}, + {.event_cause = EVENT_TX_STATUS_REPORT, + .event_handler = nxpwifi_uap_event_tx_status_report}, + {.event_cause = EVENT_BT_COEX_WLAN_PARA_CHANGE, + .event_handler = nxpwifi_uap_event_bt_coex_wlan_para_change}, + {.event_cause = EVENT_VDLL_IND, + .event_handler = nxpwifi_uap_event_vdll_ind}, +}; + +/* This function handles AP interface specific events generated by firmware. + * + * Event specific routines are called by this function based + * upon the generated event cause. + */ +int nxpwifi_process_uap_event(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u32 eventcause = adapter->event_cause; + int evt, ret = 0; + + for (evt = 0; evt < ARRAY_SIZE(evt_table_uap); evt++) { + if (eventcause == evt_table_uap[evt].event_cause) { + if (evt_table_uap[evt].event_handler) + ret = evt_table_uap[evt].event_handler(priv); + break; + } + } + + if (evt == ARRAY_SIZE(evt_table_uap)) + nxpwifi_dbg(adapter, EVENT, + "%s: unknown event id: %#x\n", + __func__, eventcause); + else + nxpwifi_dbg(adapter, EVENT, + "%s: event id: %#x\n", + __func__, eventcause); + + return ret; +} From patchwork Mon Sep 30 06:36:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815429 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2044.outbound.protection.outlook.com [40.107.21.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EBE6615381A; Mon, 30 Sep 2024 06:38:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.44 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678313; cv=fail; b=JtjXHEo/f1Gv7gcnboWUxzl5l3tAN8ZOYDyUYWcwICFHiKQZBAQ9BJE3A9PbDCoYPX7WVF9p8rGzZFtTi2n02RPuhAJMNjeXPB3ieeeyyX8DEZFzoR3dYhb7BFkJZDT/1LeJs2m3I0gi4OhzqHAgfipLSfRTtco+B0GndWZb1nA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678313; c=relaxed/simple; bh=Ei89tZckXTSaDdEaKeIOTlUq3lUYoUjKP1J/MFC4+oY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=o51Cyonfo0BNHy9LqEJzEEz98q6zC+uwzR+gazf3AoRbYaKoQhFgGN0zl1pkJpzvP2nmXAONIacgbh18iQ7LaFCGM2TjgsgcWu7hMe6KYoZ/DY8+32LnZCz14FwMGBmGFa1XKIXeSWYWgZKePBIOepD6flSLPg32giVpTLI0ShI= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=SJ3Ztg91; arc=fail smtp.client-ip=40.107.21.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="SJ3Ztg91" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=mN4vCg1dn8NggHv/07ZY+q+YwKtLf+yYdsQig0TCbmeVpNbuhdYlbl0x2K1epbytQwM4tJXJc1o6Xxdv9INNAgTFji7PoLpG60LjggX7/VjFPdRy8/JBWCHbVYqpOQd6BktQlS1Vhq7GcJwQvDhTRn1Y0GzDkd4H1CdzGCVPfASH1UV7If06dzDI17t0+GD/Mk7b1ySJ/egey61t0NaemurqVTbz2CjEAlnRclZY8fx0T1IDKV/Yzq4pNYwuUDHr6V3z565TJgwlYLjcbXdQIUM93g9egiwJ9Vw3iwSjp+5pRDLyV7vt1J6eFJ6G8WC7tvZhc3Na6UuVfo2JvHx44Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=md4HzOSKGjJfcpanDSkSvcp5O0C6utVFoRbuIbm0KR0=; b=O83cAPVw70rhmMoQsZYBor4JjPQAFadiYwacuh8w1Xgap+WEb2Zqh7J0M8RmC5AAypQP9EVphUHDDTRGBYmst3VpM85fDORAsPmERlGGrP+yIdUTlr4rvM8Wjq9XT85liRGh3fHxinw5cVL0LaPe9dd2GYQDzH5CU7Jb0OxYsfFU1h932CbG5HxN3SKohFl4f15oudvQ0O/qsG9vxdQqhfmqlrwpFOKehf0QRz9gksPu5RP5Bq0MEsbe/YLzXJvxJ/kV87JPgiHH5J6FFfBOKuqUP5THHqwFIfQFrPC0khbhPv8/kT9av5ev49vSbtpvPfsQ/a6zZgFiby2lJ6Zr+g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=md4HzOSKGjJfcpanDSkSvcp5O0C6utVFoRbuIbm0KR0=; b=SJ3Ztg91yKSJazC3s6fX3izBFVvutNmdPVgmUkEuGU59UMlywk0esYTFfqDt0mAXskbhccJCY2NezMA2256O5JxxLGibGdrDueXpxVTCqALptLPYUoMeoAWwvwfYBL/2c0TcH4VshhTZORL2eOS0WNTQS8VmpOYiyUAGNPEw+0kkJfL+VpkYdFQOpsQtGigUgE5ev7bC6mK2O0/3f7Xyc2yUMiTASisY1ghRC3d4DypH4XUZT7j0oEchc4NSlC1WwQ/zhGV4um2R2PmPxZDtP7cLsekN+vJ0np2Wjq1lPE+wQF+miWe6Mc7UFtL2bAL21zB7uLK/q6axaMrFJ4sMSg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:09 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:09 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 13/22] wifi: nxpwifi: add data path files Date: Mon, 30 Sep 2024 14:36:52 +0800 Message-Id: <20240930063701.2566520-14-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: a6808f60-1ddc-400d-87ce-08dce11a72ac X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: xwbmfQsmps2pPYDRFNciuB4Aodte3BxT7DCedVVxrj9XzgkvPz27VnRnhYzvVbFT6wqnmA6sZAUdu2AGj5F+WqHAMrRwh1RwhrDufrcEZFDl2WRsaX2p54ZduEySyxLQRCyY6R/gEZ8tTVq7SXd1zoHYoKYkw+Be5wKsqe0OLsHw2FPeoAZGV53oIRC7OyNNMmOaPHdjLzjLMQfsH/wvKGSHYqdGGN5yzL7Uv0zB1LPYTRilrYbEQGtRN9HeNDW7+Y+4wKCGJdh1gpSzMSe/O2a/IxcSp8Nma9Gb3wm5U10PUKaZPVOPu/5wcRtUirVOQci534vL/HcA2crahhxcRgcl6mrBk/F19OQAKglsGNnXCzsbaWpqrvEBrnfjCU08y6V+nzU5g/93lOANgprxEedTXgUrg3ffmfnZvgIBp4UMltHb05G8p9pJ0Z3pKIPn5Lz8lm4WUo+Klq0FNLbPei6IP117T+W4/rnefJquNWiDqEPIPqYKI9XkQgrGYGgT+N1eHxHJPP20zus0AR6Oh+phsxd6LVQ3msBvb3bkKUdxqPeW5vbz+v18VpR2M/sbm5ctgUobgvR3W1PNKN4Qu+Y1xjnec/r7i0tBnGcKzIFn24adpeSRNc2y8uICso93dDBJVFMxlesMK3nLAAohIy0HovmG+YDIDaHjh6JTVLUPrAdMh1wRPT3wZIfwzz+47qfCF8TZaYe3M/8yvafFt7rCa6Xp1kL2RTEvI2eIUmSC8HF3huK/lURFDR2JNlo8dr+Fn8SuGs3AflarC1uMr3ExudobHLdORuzscusMOP7wY5mtZ8nd62/UKaYrU5QJSV86MUQPS5p3f+KLX4DjG8MPDgGlA55Dzz9bRDV7SC36cj+VrtsjI2xFf4DcWFYxr0QMviC//NX2W/780FL5PLG3Z+0b3+LNEXXgngrtkx5T1jKvxZljFxA5acN6WaL687nbCSwnz+0PHo4RZnww6P8qUQugOaQTb6GElpNweYtIBjf2q06+itrv9pxSOiIpRof5tnMN8ZK7H0w6eDjP7U7Qelszq/rhkAbA3QyvA59wPVAflWr7DPLk8avKRyyCZ51rhP1LfSGnQdmmFFsDrIokIK6w2fBtdjJhlJyBA1AgGWdVoj+pez8mB4ar1Bz0jmFJzlu84FTHVwez3N3JUWOQP2Kj817MjxK3VkBM0wGOWpNE15OI4nr356xUvEqag/vnIDUBA219PBuA7zcz5sqhFFa1ZgG0fkxYf8mizULjxqrq60uK9PLb0SCb7cT3Rivnaw5VVZFnFzBiVQ0wCvjtJcYt+W2mMEWzg2x74G3J2Eipjt/KuvLHfGFNkT9IOR8vqzKCA5ZI62VeitKh0o1Dbk+H4OQ1/KWg0aKqBymSvUE2KGBntLZqlgsKh67Mr+gJTthTj5XwRglppBN9qg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: 0Rjlx7uxgbL0wM53H+UugckYQjW0EdHseCq+1vSg+Dj6Iq9a7aXeOZn34flr9o78/KcVjeESYsVD7lwzFg13pI+hpYQ6p7wv2OJwuO7wyB+tMPrEKJf9rttkKbN/VSIC2iZmsNF5efFs5izsrf6MM0l1cPZeOYxtxdJReryc+3lIV0NOzznO46EdxRLE06Yr0ycyXSDqXCg2L7GKxqBEgscACyOtG+9txIqRLdVXQdfOjd2nkjk7nijEVG63jl9fbYzB4DMGOhzNYb9PMXANpSwBr549GizRmicwMxV2MYEwyJH1/jDBWktuH+d8nEWlr4tJGVpURZB9vpC76s4yl3x/Z/c6597GxpCTCAPhvHYuSdIxCACYQl+5DwcmUsAQqTYUAdlHVFZIRZDUMONCbua+ELcfl1uVfm+95HlU3CVrptGnk0CyMGlZ2d0m72Wlf5ntFziJQuFsYiCBEfSKhYoVRzuwxhZg3f6B55x7+vBlbMM6L1onKF51C1gyJVOhVC+B8vhY4K2ISEpannuUIkuRgPflS5jptTVe7EAgCMFgHGNMVTfdNRrJhiKEMPugQHgeBZiX2VsNdEgJcprZsapvM9QsYMb+R92hqomJ3lU2vSVnT9VcQSAqrmFvZ++r8AbcVDJ1ainZHI77cdqsm9mQifEGtXF0fghKLZ451TsCB3bMNTKWb1aT2c0M5P2x1duOO0a4Nf+L0CzRfcf5FFmVDDw871N7AE+NZNGHUhO1lUYUeP03NqegQRFLLCQcUW9np5UT7pAcsjY5ZMejEserFWvJb041s+YH+q8yWRd80Z2qNIRnC3dkfgHcnEFSGWDFRi3yNwYPm9XMWQh5Y4rNBE4Bgnnr4xfSyGYB734EK9oP+7LkvLiMXmhz7HWhx6e2tezlqeR1vUNVwcL7L5hZTzgpMh1g/PuZsi0vGbE/zl5aFPP5wSIh/lNESw+W35KhI50gbNeef+1xPGUd7QHLRYR2Png5OHoKgoH1lhlcq5FSYHnS04CDGtnnmI6zvYwAI103fe3H3H9SjccabHep2KcXiapvdUfaUfZMlvZG53R9tipTvbp1yWfhm0XhE7nCLSRoXlXXgd1d2aN3VpBnWYAg7GgnT6+OZ5efVikNzuMyZVHCT/015WFeJYI9cbv5y2Ic1DXEyH5/PZ+cUpXZqOlAQGrcYZO+lKbWa3BEjC/rr09zwK436rMxzrjHwOcRrwTp8PKFaQzDZyROh5L8xe1d0L8BA8WFkqMyBDhSg3YMF8cEOEjnu85Awk0dv4VZQS9giwlW6t32f6XhYBbnsuPrq4BRio68mR0/lyiloVEtf7XPyl7P2vlZ1Yj6SyiZu58f6SodXhpovZAUn61q+Rq9cQ11MgwcKQHxpm0htmn9SuD3ORCQZ5/9aSCHGXMAEI9hgmslp1aVXtgRkTmMMLBAl9EDRoc6spGr1XyjRrt8dEJoaB3ku14C28AAoI6cFbV83ju6KJSjEqlwMYzPziIjl+OCcX+1Hm8PmEl/gHx9xOVqN44OG3gGgq8ijg2iMsZeDZjLoaDiQc6By/H0nxxQ03vVSvOf4JZOgaH/kGc0oPSAG+zJJR0K6v1u X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: a6808f60-1ddc-400d-87ce-08dce11a72ac X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:09.2288 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: cAvbe/2chPnZYJFnJOjuB1yTYwcigtQ5230C8nfAb8+ErjUPN+vZKvx2fwgQnFb21rXtz1QDECx4192f0XA8cQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement Tx and Rx paths for client and AP modes. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/sta_rx.c | 243 ++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_tx.c | 208 ++++++++ drivers/net/wireless/nxp/nxpwifi/txrx.c | 357 ++++++++++++++ drivers/net/wireless/nxp/nxpwifi/uap_txrx.c | 498 ++++++++++++++++++++ 4 files changed, 1306 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_rx.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_tx.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/txrx.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/uap_txrx.c diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_rx.c b/drivers/net/wireless/nxp/nxpwifi/sta_rx.c new file mode 100644 index 000000000000..95883ec0bbd9 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_rx.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: station RX data handling + * + * Copyright 2011-2024 NXP + */ + +#include +#include +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "11n_aggr.h" +#include "11n_rxreorder.h" + +/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement + * frame. If frame has both source and destination mac address as same, this + * function drops such gratuitous frames. + */ +static bool +nxpwifi_discard_gratuitous_arp(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + const struct nxpwifi_arp_eth_header *arp; + struct ethhdr *eth; + struct ipv6hdr *ipv6; + struct icmp6hdr *icmpv6; + + eth = (struct ethhdr *)skb->data; + switch (ntohs(eth->h_proto)) { + case ETH_P_ARP: + arp = (void *)(skb->data + sizeof(struct ethhdr)); + if (arp->hdr.ar_op == htons(ARPOP_REPLY) || + arp->hdr.ar_op == htons(ARPOP_REQUEST)) { + if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) + return true; + } + break; + case ETH_P_IPV6: + ipv6 = (void *)(skb->data + sizeof(struct ethhdr)); + icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) + + sizeof(struct ipv6hdr)); + if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { + if (!memcmp(&ipv6->saddr, &ipv6->daddr, + sizeof(struct in6_addr))) + return true; + } + break; + default: + break; + } + + return false; +} + +/* This function processes the received packet and forwards it + * to kernel/upper layer. + * + * This function parses through the received packet and determines + * if it is a debug packet or normal packet. + * + * For non-debug packets, the function chops off unnecessary leading + * header bytes, reconstructs the packet as an ethernet frame or + * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. + * + * The completion callback is called after processing in complete. + */ +int nxpwifi_process_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + int ret; + struct rx_packet_hdr *rx_pkt_hdr; + struct rxpd *local_rx_pd; + int hdr_chop; + struct ethhdr *eth; + u16 rx_pkt_off; + u8 adj_rx_rate = 0; + + local_rx_pd = (struct rxpd *)(skb->data); + + rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; + + if (sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rfc1042_header) + + rx_pkt_off > skb->len) { + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (sizeof(*rx_pkt_hdr) + rx_pkt_off <= skb->len && + ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_AARP) && + rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_IPX)))) { + /* Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type + * (ethertype). + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + eth = (struct ethhdr *) + ((u8 *)&rx_pkt_hdr->eth803_hdr + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + + memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(eth->h_source)); + memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(eth->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap + * header that was removed. + */ + hdr_chop = (u8 *)eth - (u8 *)local_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)local_rx_pd; + } + + /* Chop off the leading header bytes so the it points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdr_chop); + + if (priv->hs2_enabled && + nxpwifi_discard_gratuitous_arp(priv, skb)) { + nxpwifi_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n"); + dev_kfree_skb_any(skb); + return 0; + } + + /* Only stash RX bitrate for unicast packets. */ + if (likely(!is_multicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest))) { + priv->rxpd_rate = local_rx_pd->rx_rate; + priv->rxpd_htinfo = local_rx_pd->ht_info; + } + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + adj_rx_rate = nxpwifi_adjust_data_rate(priv, + local_rx_pd->rx_rate, + local_rx_pd->ht_info); + nxpwifi_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, + local_rx_pd->nf); + } + + ret = nxpwifi_recv_packet(priv, skb); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "recv packet failed\n"); + + return ret; +} + +/* This function processes the received buffer. + * + * The function looks into the RxPD and performs sanity tests on the + * received buffer to ensure its a valid packet, before processing it + * further. If the packet is determined to be aggregated, it is + * de-aggregated accordingly. Non-unicast packets are sent directly to + * the kernel/upper layers. Unicast packets are handed over to the + * Rx reordering routine if 11n is enabled. + * + * The completion callback is called after processing in complete. + */ +int nxpwifi_process_sta_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret = 0; + struct rxpd *local_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ta[ETH_ALEN]; + u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; + + local_rx_pd = (struct rxpd *)(skb->data); + rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); + rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); + seq_num = le16_to_cpu(local_rx_pd->seq_num); + + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; + + if ((rx_pkt_offset + rx_pkt_length) > skb->len || + sizeof(rx_pkt_hdr->eth803_hdr) + rx_pkt_offset > skb->len) { + nxpwifi_dbg(adapter, ERROR, + "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", + skb->len, rx_pkt_offset, rx_pkt_length); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return ret; + } + + if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = nxpwifi_process_mgmt_packet(priv, skb); + if (ret && (ret != -EINPROGRESS)) + nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed"); + if (ret != -EINPROGRESS) + dev_kfree_skb_any(skb); + return ret; + } + + /* If the packet is not an unicast packet then send the packet + * directly to os. Don't pass thru rx reordering + */ + if (!IS_11N_ENABLED(priv) || + !ether_addr_equal_unaligned(priv->curr_addr, + rx_pkt_hdr->eth803_hdr.h_dest)) { + nxpwifi_process_rx_packet(priv, skb); + return ret; + } + + if (nxpwifi_queuing_ra_based(priv)) { + memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); + } else { + if (rx_pkt_type != PKT_TYPE_BAR && + local_rx_pd->priority < MAX_NUM_TID) + priv->rx_seq[local_rx_pd->priority] = seq_num; + memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + } + + /* Reorder and send to OS */ + ret = nxpwifi_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, + ta, (u8)rx_pkt_type, skb); + + if (ret || rx_pkt_type == PKT_TYPE_BAR) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_tx.c b/drivers/net/wireless/nxp/nxpwifi/sta_tx.c new file mode 100644 index 000000000000..6611db254931 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_tx.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: station TX data handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" + +/* This function fills the TxPD for tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void nxpwifi_process_sta_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd; + struct nxpwifi_txinfo *tx_info = NXPWIFI_SKB_TXCB(skb); + unsigned int pad; + u16 pkt_type, pkt_length, pkt_offset; + int hroom = adapter->intf_hdr_len; + u32 tx_control; + + pkt_type = nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad = ((uintptr_t)skb->data - (sizeof(*local_tx_pd) + hroom)) & + (NXPWIFI_DMA_ALIGN_SZ - 1); + skb_push(skb, sizeof(*local_tx_pd) + pad); + + local_tx_pd = (struct txpd *)skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + + pkt_length = (u16)(skb->len - (sizeof(struct txpd) + pad)); + if (pkt_type == PKT_TYPE_MGMT) + pkt_length -= NXPWIFI_MGMT_FRAME_HEADER_SIZE; + local_tx_pd->tx_pkt_length = cpu_to_le16(pkt_length); + + local_tx_pd->priority = (u8)skb->priority; + local_tx_pd->pkt_delay_2ms = + nxpwifi_wmm_compute_drv_pkt_delay(priv, skb); + + if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS || + tx_info->flags & NXPWIFI_BUF_FLAG_ACTION_TX_STATUS) { + local_tx_pd->tx_token_id = tx_info->ack_frame_id; + local_tx_pd->flags |= NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS; + } + + if (local_tx_pd->priority < + ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) { + /* Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function + */ + tx_control = + priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd->priority]; + local_tx_pd->tx_control = cpu_to_le32(tx_control); + } + + if (adapter->pps_uapsd_mode) { + if (nxpwifi_check_last_packet_indication(priv)) { + adapter->tx_lock_flag = true; + local_tx_pd->flags = + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET; + } + } + + /* Offset of actual data */ + pkt_offset = sizeof(struct txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += NXPWIFI_MGMT_FRAME_HEADER_SIZE; + } + + local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); + + /* make space for adapter->intf_hdr_len */ + skb_push(skb, hroom); + + if (!local_tx_pd->tx_control) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); +} + +/* This function tells firmware to send a NULL data packet. + * + * The function creates a NULL data packet with TxPD and sends to the + * firmware for transmission, with highest priority setting. + */ +int nxpwifi_send_null_packet(struct nxpwifi_private *priv, u8 flags) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd; + struct nxpwifi_tx_param tx_param; +/* sizeof(struct txpd) + Interface specific header */ +#define NULL_PACKET_HDR 64 + u32 data_len = NULL_PACKET_HDR; + struct sk_buff *skb; + int ret; + struct nxpwifi_txinfo *tx_info = NULL; + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) + return -EPERM; + + if (!priv->media_connected) + return -EPERM; + + if (adapter->data_sent) + return -EBUSY; + + skb = dev_alloc_skb(data_len); + if (!skb) + return -ENOMEM; + + tx_info = NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = data_len - + (sizeof(struct txpd) + adapter->intf_hdr_len); + skb_reserve(skb, sizeof(struct txpd) + adapter->intf_hdr_len); + skb_push(skb, sizeof(struct txpd)); + + local_tx_pd = (struct txpd *)skb->data; + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + local_tx_pd->flags = flags; + local_tx_pd->priority = WMM_HIGHEST_PRIORITY; + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + + skb_push(skb, adapter->intf_hdr_len); + tx_param.next_pkt_len = 0; + ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA, + skb, &tx_param); + + switch (ret) { + case -EBUSY: + dev_kfree_skb_any(skb); + nxpwifi_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + case 0: + dev_kfree_skb_any(skb); + nxpwifi_dbg(adapter, DATA, + "data: %s: host_to_card succeeded\n", + __func__); + adapter->tx_lock_flag = true; + break; + case -EINPROGRESS: + adapter->tx_lock_flag = true; + break; + default: + dev_kfree_skb_any(skb); + nxpwifi_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + } + + return ret; +} + +/* This function checks if we need to send last packet indication. + */ +u8 +nxpwifi_check_last_packet_indication(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + u8 ret = false; + + if (!adapter->sleep_period.period) + return ret; + if (nxpwifi_wmm_lists_empty(adapter)) + ret = true; + + if (ret && !adapter->cmd_sent && !adapter->curr_cmd && + !is_command_pending(adapter)) { + adapter->delay_null_pkt = false; + ret = true; + } else { + ret = false; + adapter->delay_null_pkt = true; + } + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/txrx.c b/drivers/net/wireless/nxp/nxpwifi/txrx.c new file mode 100644 index 000000000000..6bd8e263857e --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/txrx.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: generic TX/RX data handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" + +/* This function processes the received buffer. + * + * Main responsibility of this function is to parse the RxPD to + * identify the correct interface this packet is headed for and + * forwarding it to the associated handling function, where the + * packet will be further processed and sent to kernel/upper layer + * if required. + */ +int nxpwifi_handle_rx_packet(struct nxpwifi_adapter *adapter, + struct sk_buff *skb) +{ + struct nxpwifi_private *priv = + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + struct rxpd *local_rx_pd; + struct nxpwifi_rxinfo *rx_info = NXPWIFI_SKB_RXCB(skb); + int ret; + + local_rx_pd = (struct rxpd *)(skb->data); + /* Get the BSS number from rxpd, get corresponding priv */ + priv = nxpwifi_get_priv_by_id(adapter, local_rx_pd->bss_num & + BSS_NUM_MASK, local_rx_pd->bss_type); + if (!priv) + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + if (!priv) { + nxpwifi_dbg(adapter, ERROR, + "data: priv not found. Drop RX packet\n"); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + nxpwifi_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data, + min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); + + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num = priv->bss_num; + rx_info->bss_type = priv->bss_type; + + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) + ret = nxpwifi_process_uap_rx_packet(priv, skb); + else + ret = nxpwifi_process_sta_rx_packet(priv, skb); + + return ret; +} +EXPORT_SYMBOL_GPL(nxpwifi_handle_rx_packet); + +/* This function sends a packet to device. + * + * It processes the packet to add the TxPD, checks condition and + * sends the processed packet to firmware for transmission. + * + * On successful completion, the function calls the completion callback + * and logs the time. + */ +int nxpwifi_process_tx(struct nxpwifi_private *priv, struct sk_buff *skb, + struct nxpwifi_tx_param *tx_param) +{ + int hroom, ret; + struct nxpwifi_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd = NULL; + struct nxpwifi_sta_node *dest_node; + struct ethhdr *hdr = (void *)skb->data; + + if (unlikely(!skb->len || + skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN)) { + ret = -EINVAL; + goto out; + } + + hroom = adapter->intf_hdr_len; + + if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) { + dest_node = nxpwifi_get_sta_entry(priv, hdr->h_dest); + if (dest_node) { + dest_node->stats.tx_bytes += skb->len; + dest_node->stats.tx_packets++; + } + + nxpwifi_process_uap_txpd(priv, skb); + } else { + nxpwifi_process_sta_txpd(priv, skb); + } + + if ((adapter->data_sent || adapter->tx_lock_flag)) { + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return 0; + } + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) + local_tx_pd = (struct txpd *)(skb->data + hroom); + ret = adapter->if_ops.host_to_card(adapter, + NXPWIFI_TYPE_DATA, + skb, tx_param); + nxpwifi_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data, + min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); + +out: + switch (ret) { + case -ENOSR: + nxpwifi_dbg(adapter, DATA, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) && + adapter->pps_uapsd_mode && adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -EINPROGRESS: + break; + case -EINVAL: + nxpwifi_dbg(adapter, ERROR, + "malformed skb (length: %u, headroom: %u)\n", + skb->len, skb_headroom(skb)); + fallthrough; + case 0: + nxpwifi_write_data_complete(adapter, skb, 0, ret); + break; + default: + nxpwifi_dbg(adapter, ERROR, + "nxpwifi_write_data_async failed: 0x%X\n", + ret); + adapter->dbg.num_tx_host_to_card_failure++; + nxpwifi_write_data_complete(adapter, skb, 0, ret); + break; + } + + return ret; +} + +static int nxpwifi_host_to_card(struct nxpwifi_adapter *adapter, + struct sk_buff *skb, + struct nxpwifi_tx_param *tx_param) +{ + struct txpd *local_tx_pd = NULL; + u8 *head_ptr = skb->data; + int ret = 0; + struct nxpwifi_private *priv; + struct nxpwifi_txinfo *tx_info; + + tx_info = NXPWIFI_SKB_TXCB(skb); + priv = nxpwifi_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) { + nxpwifi_dbg(adapter, ERROR, + "data: priv not found. Drop TX packet\n"); + adapter->dbg.num_tx_host_to_card_failure++; + nxpwifi_write_data_complete(adapter, skb, 0, 0); + return ret; + } + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) + local_tx_pd = (struct txpd *)(head_ptr + adapter->intf_hdr_len); + + ret = adapter->if_ops.host_to_card(adapter, + NXPWIFI_TYPE_DATA, + skb, tx_param); + + switch (ret) { + case -ENOSR: + nxpwifi_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) && + adapter->pps_uapsd_mode && + adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + skb_queue_head(&adapter->tx_data_q, skb); + if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT) + atomic_add(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_inc(&adapter->tx_queued); + nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -EINPROGRESS: + break; + case 0: + nxpwifi_write_data_complete(adapter, skb, 0, ret); + break; + default: + nxpwifi_dbg(adapter, ERROR, + "nxpwifi_write_data_async failed: 0x%X\n", ret); + adapter->dbg.num_tx_host_to_card_failure++; + nxpwifi_write_data_complete(adapter, skb, 0, ret); + break; + } + return ret; +} + +static int +nxpwifi_dequeue_tx_queue(struct nxpwifi_adapter *adapter) +{ + struct sk_buff *skb, *skb_next; + struct nxpwifi_txinfo *tx_info; + struct nxpwifi_tx_param tx_param; + + skb = skb_dequeue(&adapter->tx_data_q); + if (!skb) + return -ENOMEM; + + tx_info = NXPWIFI_SKB_TXCB(skb); + if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT) + atomic_sub(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_dec(&adapter->tx_queued); + + if (!skb_queue_empty(&adapter->tx_data_q)) + skb_next = skb_peek(&adapter->tx_data_q); + else + skb_next = NULL; + tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0); + if (!tx_param.next_pkt_len) { + if (!nxpwifi_wmm_lists_empty(adapter)) + tx_param.next_pkt_len = 1; + } + return nxpwifi_host_to_card(adapter, skb, &tx_param); +} + +void +nxpwifi_process_tx_queue(struct nxpwifi_adapter *adapter) +{ + do { + if (adapter->data_sent || adapter->tx_lock_flag) + break; + if (nxpwifi_dequeue_tx_queue(adapter)) + break; + } while (!skb_queue_empty(&adapter->tx_data_q)); +} + +/* Packet send completion callback handler. + * + * It either frees the buffer directly or forwards it to another + * completion callback which checks conditions, updates statistics, + * wakes up stalled traffic queue if required, and then frees the buffer. + */ +int nxpwifi_write_data_complete(struct nxpwifi_adapter *adapter, + struct sk_buff *skb, int aggr, int status) +{ + struct nxpwifi_private *priv; + struct nxpwifi_txinfo *tx_info; + struct netdev_queue *txq; + int index; + + if (!skb) + return 0; + + tx_info = NXPWIFI_SKB_TXCB(skb); + priv = nxpwifi_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) + goto done; + + nxpwifi_set_trans_start(priv->netdev); + + if (tx_info->flags & NXPWIFI_BUF_FLAG_BRIDGED_PKT) + atomic_dec_return(&adapter->pending_bridged_pkts); + + if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT) + goto done; + + if (!status) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += tx_info->pkt_len; + if (priv->tx_timeout_cnt) + priv->tx_timeout_cnt = 0; + } else { + priv->stats.tx_errors++; + } + + if (aggr) + /* For skb_aggr, do not wake up tx queue */ + goto done; + + atomic_dec(&adapter->tx_pending); + + index = nxpwifi_1d_to_wmm_queue[skb->priority]; + if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (netif_tx_queue_stopped(txq)) { + netif_tx_wake_queue(txq); + nxpwifi_dbg(adapter, DATA, "wake queue: %d\n", index); + } + } +done: + dev_kfree_skb_any(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(nxpwifi_write_data_complete); + +void nxpwifi_parse_tx_status_event(struct nxpwifi_private *priv, + void *event_body) +{ + struct tx_status_event *tx_status = (void *)priv->adapter->event_body; + struct sk_buff *ack_skb; + struct nxpwifi_txinfo *tx_info; + + if (!tx_status->tx_token_id) + return; + + spin_lock_bh(&priv->ack_status_lock); + ack_skb = idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); + spin_unlock_bh(&priv->ack_status_lock); + + if (ack_skb) { + tx_info = NXPWIFI_SKB_TXCB(ack_skb); + + if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS) { + /* consumes ack_skb */ + skb_complete_wifi_ack(ack_skb, !tx_status->status); + } else { + /* Remove broadcast address which was added by driver */ + memmove(ack_skb->data + + sizeof(struct ieee80211_hdr_3addr) + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16), + ack_skb->data + + sizeof(struct ieee80211_hdr_3addr) + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + + ETH_ALEN, ack_skb->len - + (sizeof(struct ieee80211_hdr_3addr) + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + + ETH_ALEN)); + ack_skb->len = ack_skb->len - ETH_ALEN; + /* Remove driver's proprietary header including 2 bytes + * of packet length and pass actual management frame buffer + * to cfg80211. + */ + cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie, + ack_skb->data + + NXPWIFI_MGMT_FRAME_HEADER_SIZE + + sizeof(u16), ack_skb->len - + (NXPWIFI_MGMT_FRAME_HEADER_SIZE + + sizeof(u16)), + !tx_status->status, GFP_ATOMIC); + dev_kfree_skb_any(ack_skb); + } + } +} diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c new file mode 100644 index 000000000000..b86d5fbfb531 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: AP TX and RX data handling + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "main.h" +#include "wmm.h" +#include "11n_aggr.h" +#include "11n_rxreorder.h" + +/* This function checks if particular RA list has packets more than low bridge + * packet threshold and then deletes packet from this RA list. + * Function deletes packets from such RA list and returns true. If no such list + * is found, false is returned. + */ +static bool +nxpwifi_uap_del_tx_pkts_in_ralist(struct nxpwifi_private *priv, + struct list_head *ra_list_head, + int tid) +{ + struct nxpwifi_ra_list_tbl *ra_list; + struct sk_buff *skb, *tmp; + bool pkt_deleted = false; + struct nxpwifi_txinfo *tx_info; + struct nxpwifi_adapter *adapter = priv->adapter; + + list_for_each_entry(ra_list, ra_list_head, list) { + if (skb_queue_empty(&ra_list->skb_head)) + continue; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { + tx_info = NXPWIFI_SKB_TXCB(skb); + if (tx_info->flags & NXPWIFI_BUF_FLAG_BRIDGED_PKT) { + __skb_unlink(skb, &ra_list->skb_head); + nxpwifi_write_data_complete(adapter, skb, 0, + -1); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid]--; + else + atomic_dec(&priv->wmm.tx_pkts_queued); + pkt_deleted = true; + } + if ((atomic_read(&adapter->pending_bridged_pkts) <= + NXPWIFI_BRIDGED_PKTS_THR_LOW)) + break; + } + } + + return pkt_deleted; +} + +/* This function deletes packets from particular RA List. RA list index + * from which packets are deleted is preserved so that packets from next RA + * list are deleted upon subsequent call thus maintaining fairness. + */ +static void nxpwifi_uap_cleanup_tx_queues(struct nxpwifi_private *priv) +{ + struct list_head *ra_list; + int i; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { + if (priv->del_list_idx == MAX_NUM_TID) + priv->del_list_idx = 0; + ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; + if (nxpwifi_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { + priv->del_list_idx++; + break; + } + } + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); +} + +static void +nxpwifi_uap_queue_bridged_pkt(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + struct sk_buff *new_skb; + struct nxpwifi_txinfo *tx_info; + int hdr_chop; + struct ethhdr *p_ethhdr; + struct nxpwifi_sta_node *src_node; + int index; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + if ((atomic_read(&adapter->pending_bridged_pkts) >= + NXPWIFI_BRIDGED_PKTS_THR_HIGH)) { + nxpwifi_dbg(adapter, ERROR, + "Tx: Bridge packet limit reached. Drop packet!\n"); + kfree_skb(skb); + nxpwifi_uap_cleanup_tx_queues(priv); + return; + } + + if (sizeof(*rx_pkt_hdr) + + le16_to_cpu(uap_rx_pd->rx_pkt_offset) > skb->len) { + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return; + } + + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_AARP) && + rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_IPX))) { + /* Replace the 803 header and rfc1042 header (llc/snap) with + * an Ethernet II header, keep the src/dst and snap_type + * (ethertype). + * + * The firmware only passes up SNAP frames converting all RX + * data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *)(&rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(p_ethhdr->h_dest)); + /* Chop off the rxpd + the excess memory from + * 802.2/llc/snap header that was removed. + */ + hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; + } + + /* Chop off the leading header bytes so that it points + * to the start of either the reconstructed EthII frame + * or the 802.2/llc/snap frame. + */ + skb_pull(skb, hdr_chop); + + if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) { + nxpwifi_dbg(adapter, ERROR, + "data: Tx: insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = + skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN); + if (unlikely(!new_skb)) { + nxpwifi_dbg(adapter, ERROR, + "Tx: cannot allocate new_skb\n"); + kfree_skb(skb); + priv->stats.tx_dropped++; + return; + } + + kfree_skb(skb); + skb = new_skb; + nxpwifi_dbg(adapter, INFO, + "info: new skb headroom %d\n", + skb_headroom(skb)); + } + + tx_info = NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= NXPWIFI_BUF_FLAG_BRIDGED_PKT; + + src_node = nxpwifi_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + src_node->stats.last_tx_rate = uap_rx_pd->rx_rate; + src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info; + } + + if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) { + /* Update bridge packet statistics as the + * packet is not going to kernel/upper layer. + */ + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + /* Sending bridge packet to TX queue, so save the packet + * length in TXCB to update statistics in TX complete. + */ + tx_info->pkt_len = skb->len; + } + + __net_timestamp(skb); + + index = nxpwifi_1d_to_wmm_queue[skb->priority]; + atomic_inc(&priv->wmm_tx_pending[index]); + nxpwifi_wmm_add_buf_txqueue(priv, skb); + atomic_inc(&adapter->tx_pending); + atomic_inc(&adapter->pending_bridged_pkts); + + nxpwifi_queue_work(adapter, &adapter->main_work); +} + +/* This function contains logic for AP packet forwarding. + * + * If a packet is multicast/broadcast, it is sent to kernel/upper layer + * as well as queued back to AP TX queue so that it can be sent to other + * associated stations. + * If a packet is unicast and RA is present in associated station list, + * it is again requeued into AP TX queue. + * If a packet is unicast and RA is not in associated station list, + * packet is forwarded to kernel to handle routing logic. + */ +int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ra[ETH_ALEN]; + struct sk_buff *skb_uap; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + /* don't do packet forwarding in disconnected state */ + if (!priv->media_connected) { + nxpwifi_dbg(adapter, ERROR, + "drop packet in disconnected state.\n"); + dev_kfree_skb_any(skb); + return 0; + } + + memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN); + + if (is_multicast_ether_addr(ra)) { + skb_uap = skb_copy(skb, GFP_ATOMIC); + if (likely(skb_uap)) { + nxpwifi_uap_queue_bridged_pkt(priv, skb_uap); + } else { + nxpwifi_dbg(adapter, ERROR, + "failed to copy skb for uAP\n"); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return -ENOMEM; + } + } else { + if (nxpwifi_get_sta_entry(priv, ra)) { + /* Requeue Intra-BSS packet */ + nxpwifi_uap_queue_bridged_pkt(priv, skb); + return 0; + } + } + + /* Forward unicat/Inter-BSS packets to kernel. */ + return nxpwifi_process_rx_packet(priv, skb); +} + +int nxpwifi_uap_recv_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_sta_node *src_node; + struct ethhdr *p_ethhdr; + struct sk_buff *skb_uap; + struct nxpwifi_txinfo *tx_info; + + if (!skb) + return -ENOMEM; + + p_ethhdr = (void *)skb->data; + src_node = nxpwifi_get_sta_entry(priv, p_ethhdr->h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + } + + if (is_multicast_ether_addr(p_ethhdr->h_dest) || + nxpwifi_get_sta_entry(priv, p_ethhdr->h_dest)) { + if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) + skb_uap = + skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN); + else + skb_uap = skb_copy(skb, GFP_ATOMIC); + + if (likely(skb_uap)) { + tx_info = NXPWIFI_SKB_TXCB(skb_uap); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= NXPWIFI_BUF_FLAG_BRIDGED_PKT; + __net_timestamp(skb_uap); + nxpwifi_wmm_add_buf_txqueue(priv, skb_uap); + atomic_inc(&adapter->tx_pending); + atomic_inc(&adapter->pending_bridged_pkts); + if ((atomic_read(&adapter->pending_bridged_pkts) >= + NXPWIFI_BRIDGED_PKTS_THR_HIGH)) { + nxpwifi_dbg(adapter, ERROR, + "Tx: Bridge packet limit reached. Drop packet!\n"); + nxpwifi_uap_cleanup_tx_queues(priv); + } + + } else { + nxpwifi_dbg(adapter, ERROR, "failed to allocate skb_uap"); + } + + nxpwifi_queue_work(adapter, &adapter->main_work); + /* Don't forward Intra-BSS unicast packet to upper layer*/ + if (nxpwifi_get_sta_entry(priv, p_ethhdr->h_dest)) + return 0; + } + + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + skb->ip_summed = CHECKSUM_NONE; + + /* Forward multicast/broadcast packet to upper layer*/ + netif_rx(skb); + return 0; +} + +/* This function processes the packet received on AP interface. + * + * The function looks into the RxPD and performs sanity tests on the + * received buffer to ensure its a valid packet before processing it + * further. If the packet is determined to be aggregated, it is + * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic. + * + * The completion callback is called after processing is complete. + */ +int nxpwifi_process_uap_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int ret; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u16 rx_pkt_type; + u8 ta[ETH_ALEN], pkt_type; + struct nxpwifi_sta_node *node; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + if (le16_to_cpu(uap_rx_pd->rx_pkt_offset) + + sizeof(rx_pkt_hdr->eth803_hdr) > skb->len) { + nxpwifi_dbg(adapter, ERROR, + "wrong rx packet for struct ethhdr: len=%d, offset=%d\n", + skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset)); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return 0; + } + + ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source); + + if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) + + le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16)skb->len) { + nxpwifi_dbg(adapter, ERROR, + "wrong rx packet: len=%d, offset=%d, length=%d\n", + skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), + le16_to_cpu(uap_rx_pd->rx_pkt_length)); + priv->stats.rx_dropped++; + + node = nxpwifi_get_sta_entry(priv, ta); + if (node) + node->stats.tx_failed++; + + dev_kfree_skb_any(skb); + return 0; + } + + if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = nxpwifi_process_mgmt_packet(priv, skb); + if (ret && (ret != -EINPROGRESS)) + nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed"); + if (ret != -EINPROGRESS) + dev_kfree_skb_any(skb); + return ret; + } + + if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { + spin_lock_bh(&priv->sta_list_spinlock); + node = nxpwifi_get_sta_entry(priv, ta); + if (node) + node->rx_seq[uap_rx_pd->priority] = + le16_to_cpu(uap_rx_pd->seq_num); + spin_unlock_bh(&priv->sta_list_spinlock); + } + + if (!priv->ap_11n_enabled || + (!nxpwifi_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) && + (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) { + ret = nxpwifi_handle_uap_rx_forward(priv, skb); + return ret; + } + + /* Reorder and send to kernel */ + pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); + ret = nxpwifi_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num), + uap_rx_pd->priority, ta, pkt_type, + skb); + + if (ret || rx_pkt_type == PKT_TYPE_BAR) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} + +/* This function fills the TxPD for AP tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void nxpwifi_process_uap_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct uap_txpd *txpd; + struct nxpwifi_txinfo *tx_info = NXPWIFI_SKB_TXCB(skb); + int pad; + u16 pkt_type, pkt_offset; + int hroom = adapter->intf_hdr_len; + + pkt_type = nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad = ((uintptr_t)skb->data - (sizeof(*txpd) + hroom)) & + (NXPWIFI_DMA_ALIGN_SZ - 1); + + skb_push(skb, sizeof(*txpd) + pad); + + txpd = (struct uap_txpd *)skb->data; + memset(txpd, 0, sizeof(*txpd)); + txpd->bss_num = priv->bss_num; + txpd->bss_type = priv->bss_type; + txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + + pad))); + txpd->priority = (u8)skb->priority; + + txpd->pkt_delay_2ms = nxpwifi_wmm_compute_drv_pkt_delay(priv, skb); + + if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS || + tx_info->flags & NXPWIFI_BUF_FLAG_ACTION_TX_STATUS) { + txpd->tx_token_id = tx_info->ack_frame_id; + txpd->flags |= NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS; + } + + if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) + /* Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function. + */ + txpd->tx_control = + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); + + /* Offset of actual data */ + pkt_offset = sizeof(*txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + txpd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += NXPWIFI_MGMT_FRAME_HEADER_SIZE; + } + + txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); + + /* make space for adapter->intf_hdr_len */ + skb_push(skb, hroom); + + if (!txpd->tx_control) + /* TxCtrl set by user or default */ + txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); +} From patchwork Mon Sep 30 06:36:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815430 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AF5D517E013; Mon, 30 Sep 2024 06:38:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678315; cv=fail; b=GUSlFXRPmNFv4ZTJGc1OF74HsB2+8Y49xpVWIDdImUfNeb9j+P1pUM5B7WIeMh+7IsA9vmTGxm0bDpRhNJxPhH8f5GqSkG1BghLD83Z10K6Ak5xeBJEYSuAAKbvUkualb3JSLrSSHebKVQFZ04RA/QNI7x7frDZnSumJTycWXKs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678315; c=relaxed/simple; bh=OjI5+TggZkMAKiCk0frsg57Uw4VCWYFD0aONqiF7DLs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=EEasI0XpOOB9gPdw+TZSoqio+DBrBGZkDjc6WlLZ+ceJro5SB6z/qftPEBqgxYPsOEkF7ykOYAWiVhHkAthDVGdZTtpiYXOoK+9I9DS8k4wHP6GOKP03c6k2iMDZGNQjUD6cVe0b8gE32k+S4oI2ayFtz+FD2ROODlcsO3S8jyM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=fKNqzDtQ; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="fKNqzDtQ" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=nd36ny6eWZ81S1uoqiatkbq/4wo29k735Bapjz8ofFlPL6/ZrIjGn3VLLjS96hVftGIaaNXXWlvDn5qY04pn951GajIal3ygoi+aOD79rIVApBkelCg4xVmp16egR1ograbIzbfCXW7IhKiR/VPtXiHVR+oTa0oL42K1aMZ4r7TYSrIiwL2XrTclIrE6R+qxoVjLDieAMQ3PsHr5O7ezS+KdtummTQVGDUc0KQkf+jpAQ/FKS69NLUos82R7RzX9tdLqa+i9PoO7NtgC3IO2RY2bCkYMsN4rekS3d29SmEVplgiaTk1XmaAasUZFv+a3+rYyBPbc84al4N6TI3063A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Rq3sxGvv3qClJvKLVtVlGIhpePCcoIG22fx9rqeIGD8=; b=BfILJE+1LjkchXdqbp0cWkSRqCDy8egC3aLN+3XoNK6jCG3hHb/cs8/WYKRmLHd2hFmi3pJWfOMknIQLB4Qhk5XxUaFj5VGRSesxrkoCmxVggNBYWMwWAaZIRagkBZRgx35zEG7pdODu+JEC9AKXaxU6ttLnZw/NAF6DIEizM+zJpYaC8g5rvouqFCodfsrTfpiF4Yx8WCTrY9twuIGNwW335RAtxsWC632dx3IgxQEKHaAgoew6SPTqEjwVj4VdycpzUbtxYP09tDJvPlgXlxEjwtnTiJ5CApQAd72aFlDfJy23w9XklcyUGWQIwpVVIG1cb1LxShQQEmBHVqcc2g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Rq3sxGvv3qClJvKLVtVlGIhpePCcoIG22fx9rqeIGD8=; b=fKNqzDtQOjTeGVnX9+T9CH//YsX0g1J4ffTEey885nGFcPgkyoIR/T7rS/xkMBNVyANgHX7NN1Uryu9zTrsR5zwNN2J8sG7TuKkwzzyuDSZ1jif+7uldtBICWHJFixi/T+GFW6PvUIymN3BGblUJZjxWRoxV64jcjW+2dSfOSKxNfeyTCGUKxMyR6UwM2Hm2J07Y9eBENYoNbdK1il8aqacn0nsT5UMGuPFrao8RP6VDBBh9xwmyEPOLD1Hq9hIKP/Yuj5mMf1BZh3K5SUFydNITxEJhOjf3p7SjUkSFLkqp+bN7sH0ePSLHQXfuX4Y6D3phRqPwroWfsSkER4F64w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:12 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:12 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 14/22] wifi: nxpwifi: add debugfs file Date: Mon, 30 Sep 2024 14:36:53 +0800 Message-Id: <20240930063701.2566520-15-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 9f1bbade-5a72-4da6-531c-08dce11a7465 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: qm6VF5094ai6VYjdHBYTiTC9dGQWx6T08y06g7agbVIZ4ESVzpH2nWSCEM3ZcuuSDsuVBaz51Jx/1fyB7uBRASwFvt/3yUjNsegYxYuPUpg4D/YcLjiySox1VGIe3Z73iVi5Jq5ickcyFiLIQjvWeHZVWdpJhfQOZitzaKvoFa9fUlhNbjpEiIBe3pLZMXWy5hi848BI0ByTvPKYHFoOFecM8D30Ocjhjae6sT4QNCJLEIopJrhZ7Vgzq8FtwPHLM5SXLxX1xFIasmaSJ9DIJVxhvNQwrc5novLA43S8QcTuwtaW4+NUmgfcJssC475rHglacc9OoTVJRCQqF2pOQg5cc37OmPRNY9vw1wA1eFKGfjN6NMnf1TOS6y2+dYIsFghb/dadFXEQRDgfsSkllOIAEw8Z0GN1C89oFmariYkY9jR6XSQS+36c0DapbZOuMSxwK2r+0VCWqJ66zLktldWa8h+pq+XP+qE6uCMoiumJloP6WDavmJC9AlfRsM/CrOarC24mLexytpAok8ayIhyqZ5RAQBUJ8Dki063WjbuveVUpZ+Q32gPYhKuZUwYKJxMKnmv/2QzRMoNc/Gda8diiIUbSZr/E8swwX6eiTGjD5JbHQft+dGz7ybdob3T39i1ZViYslXuiihUghrT/GygKnEdZCLwTD/OLcV/XuTjeeF1NwhYCj1Akwb3K3Q3XBNCz8mQpDkzY34ZMpUqGCP31Cd6+bDWHX5wFh7a3dshvQXUukgBM8BqOU2ngZF1rajAXvwfRanBN53tLpccjW83B3jF8JFyEhi+UExv216jUjeTwqI0rXrp9WKwSH6pmlTZ6q1v8WPXYb8e12Ba7LZZY0Yc+nRQnzDeYFIdG1Ux9ZzURN7IX99BrpiPfaMUzCV/AFYCW8Ecf5mUrvS/mPngr/bLpVPdE6mTplvAnpIxrVEPzRAOzIDqZfzrTSdYwggliiWSjpn1Bq6Ard5a5Xzj+MiCzJmtF408PlVesrN7FFqVGJiLeQBwS96+yHslm5+UeC4qOfvvt4jix/v7AeFuN6qjK652mCrwkW+ryDTz4q97ao6QI72fD3XrsmQ9DYNwdKO8Om4ofvaSoMyud700x6n0ZgOUqf8smm6QqbRrw9cb30hWuwwoSRWv+RwYzo9BxBW/4xK8chNilL/lCjkSSP8hcx113uY9YWUaW09mbFcFMIBIK4q7EEkXDcytwQmJtZwZ0+k7oy0RFveQXQmQi1kzBzrVq2LNLMoKDDzto4CF5a6RyzCxmCMdIIUslocpNE0NssqOoZdvQWd+nPCYrRCAMMEvjzzwtBAqwI6tplzoNG3SbQE//C1Mg/GS1F7jD/0sP3hiAYIy+c5IlBSUZ8q6Ib8i6vKOaIvBp70m2QF70RFzUvNDyps5wF4DiobjZOr7qwSKHDdCGqcHPTA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: +dNntkvytUhdrVFUogV0cm0LFHq9OGrr20V9K5Th5f5ip1mU5VOsikmnyiGUwCJeqmx9RTWjNMoUpmsarf14xjstYyd1h2aKqECEaPZe/wJnavr0f88mZsrNp2/ufrgE59vn/TezXjYMXiwP5KcLTZmW5lALNozPudx/+T6yf787rSrDuSyS5Viq3SJ9X9mp/s02IVA51M6NchwkOAiyUoUeETsZJwmOk7xs7QdPmdbSnoBsfC2QqlXykZS9ZdG94/xNJ4PeREL74l69gps0UEmF50z1tMlpA4oMmPqfbm8Za6kZL+pjhvMXsayhRsK0aK6hYwwPqA5Bussls7N0+NNCm/wUudCNYOMQzYvSQ5i30NZqSMtvnrNOgafxIbcAxvW1MwYrAMSswzbNSQG43da+H/WtzuZX4M9GoaeFBSlVlIO+Mm+rGorTFdpO38Rm/sV4gyPuClQgiP8pdH1wkNPeqWf03QjN9Qdc3iqWXio/KpCMsnJMGSPjUPJvINzd8FJA1cudrqd23WidRPNS5W/hOQSv7iB6p31pwpTHnHs9rqMf3kMqPNYY9VJ81ETE4vgD9pwizsQ81h4PoTz5fZKuQuQUmol5D0hR1LvDaJGAklMoPpDTRl1N9R1yoo1BfcXYHIFLpG9vnhX3aAy4AGwYXPlchXHj8zRbgKA88yiH58NkzkzZlyOClYDHFzThJlhr6Jk/xb7R8+/k9qk5+MTZx7Na2nm3uN7P+lZ82gFY/vydE88v4PjdH4AnxFV33Bn3XJGhlbg8CXNKifkg/HuWZOHakyWONEat5aE5+Dm0cHBMeWLE3Ig8gNT8F/Pbzaek9M2cGtuI9wgU4rimZH6AMDkTFsoGhQmKjFuEbQpO3YNWxGObDT0OkqUN14DyyGDQt9MRAGXMXojmP/6qPPWaFodv4DVYqsxyvvOshz/hrErVWXfLGB0XE0WRviysiUYCZ6BGjR2LfLOxwABqWhmymKCAsvUiNdFROSgm9msU+mmujlFp/RfZR84KNE+xoUfYmM6sOyENFYdyrxHuUTwC4TxC0p3oQbT0CwDjPmmS1riNenbaOo7Xx1gTgSVnbEY5ShY4X74X00e2OWXCcAABRX87DhNPH3ZYD+mN/G36B7FLYRVt9/0yBa+FtCiSl2WqZHYsHqLJh6bHWdYl5jGUo08De4j9nZILgGIpOsqoNd2br/fynbv0ZaDfAxUJdi1NcnJuXWrIYlRYLOBlZNPtZ6VdjM2Cs6bhlF/xLp4+aurwapg+A3FSGk6Y6PgnU/cYHdZwjJCFPRlbSuVwPjf9llLRVJVtmip+33OMfEOMCRSuVstX2ZRR1YderW+NjIp9qIO9owJg4HYXpOYIP5PV1rBI+MGhV44t2yodXWkrZnLUi3BBoTZB2OLKnosDwgVpD4deOftVVbcXIGSGYUYtBtqjOGnKMYoRtBNCGqZw6FiQ1C/dfOuyOlGENAlHDCCjtcHhAggSzjOSo9roOmx/bcbXfHn9yb9f7cZXPGEoyU4b1MuwC5oCZB1smW4Z6nxqMsoIeuf/uknly6OBPv7RBwIsdv2GR9HS7/3Be0nObG5an2wql8Bg32KPVu2x X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9f1bbade-5a72-4da6-531c-08dce11a7465 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:12.1185 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: F9yb7PZd1WIN5V0GLQHFiJ9RKqUuwNSyMaq/JrqVkdf6OUTkRD68lak0pOLQqU713IWb9mZ2cQRCzWmOr5Mqeg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement useful functions to help debug and test nxpwifi. For example, file "fake_radar_detect" can generate fake radar detection which can be used to test AP DFS mode of nxpwifi without real radar detection triggerred by FW. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/debugfs.c | 1041 ++++++++++++++++++++ 1 file changed, 1041 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/debugfs.c diff --git a/drivers/net/wireless/nxp/nxpwifi/debugfs.c b/drivers/net/wireless/nxp/nxpwifi/debugfs.c new file mode 100644 index 000000000000..1cfeb22272b9 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/debugfs.c @@ -0,0 +1,1041 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: debugfs + * + * Copyright 2011-2024 NXP + */ + +#include + +#include "main.h" +#include "cmdevt.h" +#include "11n.h" + +static struct dentry *nxpwifi_dfs_dir; + +static char *bss_modes[] = { + "UNSPECIFIED", + "ADHOC", + "STATION", + "AP", + "AP_VLAN", + "WDS", + "MONITOR", + "MESH_POINT", + "P2P_CLIENT", + "P2P_GO", + "P2P_DEVICE", +}; + +/* Proc info file read handler. + * + * This function is called when the 'info' file is opened for reading. + * It prints the following driver related information - + * - Driver name + * - Driver version + * - Driver extended version + * - Interface name + * - BSS mode + * - Media state (connected or disconnected) + * - MAC address + * - Total number of Tx bytes + * - Total number of Rx bytes + * - Total number of Tx packets + * - Total number of Rx packets + * - Total number of dropped Tx packets + * - Total number of dropped Rx packets + * - Total number of corrupted Tx packets + * - Total number of corrupted Rx packets + * - Carrier status (on or off) + * - Tx queue status (started or stopped) + * + * For STA mode drivers, it also prints the following extra - + * - ESSID + * - BSSID + * - Channel + * - Region code + * - Multicast count + * - Multicast addresses + */ +static ssize_t +nxpwifi_info_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + struct net_device *netdev = priv->netdev; + struct netdev_hw_addr *ha; + struct netdev_queue *txq; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page, fmt[64]; + struct nxpwifi_bss_info info; + ssize_t ret; + int i = 0; + + if (!p) + return -ENOMEM; + + memset(&info, 0, sizeof(info)); + ret = nxpwifi_get_bss_info(priv, &info); + if (ret) + goto free_and_exit; + + nxpwifi_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); + + nxpwifi_get_ver_ext(priv, 0); + + p += sprintf(p, "driver_name = "); + p += sprintf(p, "\"nxpwifi\"\n"); + p += sprintf(p, "driver_version = %s", fmt); + p += sprintf(p, "\nverext = %s", priv->version_str); + p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); + + if (info.bss_mode >= ARRAY_SIZE(bss_modes)) + p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); + else + p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); + + p += sprintf(p, "media_state=\"%s\"\n", + (!priv->media_connected ? "Disconnected" : "Connected")); + p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) { + p += sprintf(p, "multicast_count=\"%d\"\n", + netdev_mc_count(netdev)); + p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len, + info.ssid.ssid); + p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); + p += sprintf(p, "channel=\"%d\"\n", (int)info.bss_chan); + p += sprintf(p, "country_code = \"%s\"\n", info.country_code); + p += sprintf(p, "region_code=\"0x%x\"\n", + priv->adapter->region_code); + + netdev_for_each_mc_addr(ha, netdev) + p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", + i++, ha->addr); + } + + p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) + ? "on" : "off")); + p += sprintf(p, "tx queue"); + for (i = 0; i < netdev->num_tx_queues; i++) { + txq = netdev_get_tx_queue(netdev, i); + p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n"); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* Proc getlog file read handler. + * + * This function is called when the 'getlog' file is opened for reading + * It prints the following log information - + * - Number of multicast Tx frames + * - Number of failed packets + * - Number of Tx retries + * - Number of multicast Tx retries + * - Number of duplicate frames + * - Number of RTS successes + * - Number of RTS failures + * - Number of ACK failures + * - Number of fragmented Rx frames + * - Number of multicast Rx frames + * - Number of FCS errors + * - Number of Tx frames + * - WEP ICV error counts + * - Number of received beacons + * - Number of missed beacons + */ +static ssize_t +nxpwifi_getlog_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + ssize_t ret; + struct nxpwifi_ds_get_stats stats; + + if (!p) + return -ENOMEM; + + memset(&stats, 0, sizeof(stats)); + ret = nxpwifi_get_stats_info(priv, &stats); + if (ret) + goto free_and_exit; + + p += sprintf(p, "\n" + "mcasttxframe %u\n" + "failed %u\n" + "retry %u\n" + "multiretry %u\n" + "framedup %u\n" + "rtssuccess %u\n" + "rtsfailure %u\n" + "ackfailure %u\n" + "rxfrag %u\n" + "mcastrxframe %u\n" + "fcserror %u\n" + "txframe %u\n" + "wepicverrcnt-1 %u\n" + "wepicverrcnt-2 %u\n" + "wepicverrcnt-3 %u\n" + "wepicverrcnt-4 %u\n" + "bcn_rcv_cnt %u\n" + "bcn_miss_cnt %u\n", + stats.mcast_tx_frame, + stats.failed, + stats.retry, + stats.multi_retry, + stats.frame_dup, + stats.rts_success, + stats.rts_failure, + stats.ack_failure, + stats.rx_frag, + stats.mcast_rx_frame, + stats.fcs_error, + stats.tx_frame, + stats.wep_icv_error[0], + stats.wep_icv_error[1], + stats.wep_icv_error[2], + stats.wep_icv_error[3], + stats.bcn_rcv_cnt, + stats.bcn_miss_cnt); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* Sysfs histogram file read handler. + * + * This function is called when the 'histogram' file is opened for reading + * It prints the following histogram information - + * - Number of histogram samples + * - Receive packet number of each rx_rate + * - Receive packet number of each snr + * - Receive packet number of each nosie_flr + * - Receive packet number of each signal streath + */ +static ssize_t +nxpwifi_histogram_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + ssize_t ret; + struct nxpwifi_histogram_data *phist_data; + int i, value; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + + if (!p) + return -ENOMEM; + + if (!priv || !priv->hist_data) { + ret = -EFAULT; + goto free_and_exit; + } + + phist_data = priv->hist_data; + + p += sprintf(p, "\n" + "total samples = %d\n", + atomic_read(&phist_data->num_samples)); + + p += sprintf(p, + "rx rates (in Mbps): 0=1M 1=2M 2=5.5M 3=11M 4=6M 5=9M 6=12M\n" + "7=18M 8=24M 9=36M 10=48M 11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + p += sprintf(p, + "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n"); + } else { + p += sprintf(p, "\n"); + } + + for (i = 0; i < NXPWIFI_MAX_RX_RATES; i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + for (i = NXPWIFI_MAX_RX_RATES; i < NXPWIFI_MAX_AC_RX_RATES; + i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", + i, value); + } + } + + for (i = 0; i < NXPWIFI_MAX_SNR; i++) { + value = atomic_read(&phist_data->snr[i]); + if (value) + p += sprintf(p, "snr[%02ddB] = %d\n", i, value); + } + for (i = 0; i < NXPWIFI_MAX_NOISE_FLR; i++) { + value = atomic_read(&phist_data->noise_flr[i]); + if (value) + p += sprintf(p, "noise_flr[%02ddBm] = %d\n", + (int)(i - 128), value); + } + for (i = 0; i < NXPWIFI_MAX_SIG_STRENGTH; i++) { + value = atomic_read(&phist_data->sig_str[i]); + if (value) + p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", + i, value); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +free_and_exit: + free_page(page); + return ret; +} + +static ssize_t +nxpwifi_histogram_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = (void *)file->private_data; + + if (priv && priv->hist_data) + nxpwifi_hist_data_reset(priv); + return 0; +} + +static struct nxpwifi_debug_info info; + +/* Proc debug file read handler. + * + * This function is called when the 'debug' file is opened for reading + * It prints the following log information - + * - Interrupt count + * - WMM AC VO packets count + * - WMM AC VI packets count + * - WMM AC BE packets count + * - WMM AC BK packets count + * - Maximum Tx buffer size + * - Tx buffer size + * - Current Tx buffer size + * - Power Save mode + * - Power Save state + * - Deep Sleep status + * - Device wakeup required status + * - Number of wakeup tries + * - Host Sleep configured status + * - Host Sleep activated status + * - Number of Tx timeouts + * - Number of command timeouts + * - Last timed out command ID + * - Last timed out command action + * - Last command ID + * - Last command action + * - Last command index + * - Last command response ID + * - Last command response index + * - Last event + * - Last event index + * - Number of host to card command failures + * - Number of sleep confirm command failures + * - Number of host to card data failure + * - Number of deauthentication events + * - Number of disassociation events + * - Number of link lost events + * - Number of deauthentication commands + * - Number of association success commands + * - Number of association failure commands + * - Number of commands sent + * - Number of data packets sent + * - Number of command responses received + * - Number of events received + * - Tx BA stream table (TID, RA) + * - Rx reorder table (TID, TA, Start window, Window size, Buffer) + */ +static ssize_t +nxpwifi_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + ssize_t ret; + + if (!p) + return -ENOMEM; + + ret = nxpwifi_get_debug_info(priv, &info); + if (ret) + goto free_and_exit; + + p += nxpwifi_debug_info_to_buffer(priv, p, &info); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +free_and_exit: + free_page(page); + return ret; +} + +static u32 saved_reg_type, saved_reg_offset, saved_reg_value; + +/* Proc regrdwr file write handler. + * + * This function is called when the 'regrdwr' file is opened for writing + * + * This function can be used to write to a register. + */ +static ssize_t +nxpwifi_regrdwr_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + char *buf; + int ret; + u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; + int rv; + + buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + rv = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); + + if (rv != 3) { + ret = -EINVAL; + goto done; + } + + if (reg_type == 0 || reg_offset == 0) { + ret = -EINVAL; + goto done; + } else { + saved_reg_type = reg_type; + saved_reg_offset = reg_offset; + saved_reg_value = reg_value; + ret = count; + } +done: + kfree(buf); + return ret; +} + +/* Proc regrdwr file read handler. + * + * This function is called when the 'regrdwr' file is opened for reading + * + * This function can be used to read from a register. + */ +static ssize_t +nxpwifi_regrdwr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int pos = 0, ret = 0; + u32 reg_value; + + if (!buf) + return -ENOMEM; + + if (!saved_reg_type) { + /* No command has been given */ + pos += snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + /* Set command has been given */ + if (saved_reg_value != UINT_MAX) { + ret = nxpwifi_reg_write(priv, saved_reg_type, saved_reg_offset, + saved_reg_value); + + pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", + saved_reg_type, saved_reg_offset, + saved_reg_value); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + goto done; + } + /* Get command has been given */ + ret = nxpwifi_reg_read(priv, saved_reg_type, + saved_reg_offset, ®_value); + if (ret) { + ret = -EINVAL; + goto done; + } + + pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, + saved_reg_offset, reg_value); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + +done: + free_page(addr); + return ret; +} + +/* Proc debug_mask file read handler. + * This function is called when the 'debug_mask' file is opened for reading + * This function can be used read driver debugging mask value. + */ +static ssize_t +nxpwifi_debug_mask_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)page; + size_t ret = 0; + int pos = 0; + + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", + priv->adapter->debug_mask); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(page); + return ret; +} + +/* Proc debug_mask file read handler. + * This function is called when the 'debug_mask' file is opened for reading + * This function can be used read driver debugging mask value. + */ +static ssize_t +nxpwifi_debug_mask_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + unsigned long debug_mask; + struct nxpwifi_private *priv = (void *)file->private_data; + char *buf; + + buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + if (kstrtoul(buf, 0, &debug_mask)) { + ret = -EINVAL; + goto done; + } + + priv->adapter->debug_mask = debug_mask; + ret = count; +done: + kfree(buf); + return ret; +} + +/* debugfs verext file write handler. + * This function is called when the 'verext' file is opened for write + */ +static ssize_t +nxpwifi_verext_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + u32 versionstrsel; + struct nxpwifi_private *priv = (void *)file->private_data; + + ret = kstrtou32_from_user(ubuf, count, 10, &versionstrsel); + if (ret) + return ret; + + priv->versionstrsel = versionstrsel; + + return count; +} + +/* Proc verext file read handler. + * This function is called when the 'verext' file is opened for reading + * This function can be used read driver exteneed verion string. + */ +static ssize_t +nxpwifi_verext_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + char buf[256]; + int ret; + + nxpwifi_get_ver_ext(priv, priv->versionstrsel); + ret = snprintf(buf, sizeof(buf), "version string: %s\n", + priv->version_str); + + return simple_read_from_buffer(ubuf, count, ppos, buf, ret); +} + +/* Proc memrw file write handler. + * This function is called when the 'memrw' file is opened for writing + * This function can be used to write to a memory location. + */ +static ssize_t +nxpwifi_memrw_write(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos) +{ + int ret; + char cmd; + struct nxpwifi_ds_mem_rw mem_rw; + u16 cmd_action; + struct nxpwifi_private *priv = (void *)file->private_data; + char *buf; + + buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); + if (ret != 3) { + ret = -EINVAL; + goto done; + } + + if ((cmd == 'r') || (cmd == 'R')) { + cmd_action = HOST_ACT_GEN_GET; + mem_rw.value = 0; + } else if ((cmd == 'w') || (cmd == 'W')) { + cmd_action = HOST_ACT_GEN_SET; + } else { + ret = -EINVAL; + goto done; + } + + memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); + ret = nxpwifi_send_cmd(priv, HOST_CMD_MEM_ACCESS, cmd_action, 0, + &mem_rw, true); + if (!ret) + ret = count; + +done: + kfree(buf); + return ret; +} + +/* Proc memrw file read handler. + * This function is called when the 'memrw' file is opened for reading + * This function can be used to read from a memory location. + */ +static ssize_t +nxpwifi_memrw_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int ret, pos = 0; + + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, + priv->mem_rw.value); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static u32 saved_offset = -1, saved_bytes = -1; + +/* Proc rdeeprom file write handler. + * + * This function is called when the 'rdeeprom' file is opened for writing + * + * This function can be used to write to a RDEEPROM location. + */ +static ssize_t +nxpwifi_rdeeprom_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + char *buf; + int ret = 0; + int offset = -1, bytes = -1; + int rv; + + buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + rv = sscanf(buf, "%d %d", &offset, &bytes); + + if (rv != 2) { + ret = -EINVAL; + goto done; + } + + if (offset == -1 || bytes == -1) { + ret = -EINVAL; + goto done; + } else { + saved_offset = offset; + saved_bytes = bytes; + ret = count; + } +done: + kfree(buf); + return ret; +} + +/* Proc rdeeprom read write handler. + * + * This function is called when the 'rdeeprom' file is opened for reading + * + * This function can be used to read from a RDEEPROM location. + */ +static ssize_t +nxpwifi_rdeeprom_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = + (struct nxpwifi_private *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int pos, ret, i; + u8 value[MAX_EEPROM_DATA]; + + if (!buf) + return -ENOMEM; + + if (saved_offset == -1) { + /* No command has been given */ + pos = snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + + /* Get command has been given */ + ret = nxpwifi_eeprom_read(priv, (u16)saved_offset, + (u16)saved_bytes, value); + if (ret) { + ret = -EINVAL; + goto out_free; + } + + pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); + + for (i = 0; i < saved_bytes; i++) + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); + +done: + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); +out_free: + free_page(addr); + return ret; +} + +/* Proc hscfg file write handler + * This function can be used to configure the host sleep parameters. + */ +static ssize_t +nxpwifi_hscfg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = (void *)file->private_data; + char *buf; + int ret, arg_num; + struct nxpwifi_ds_hs_cfg hscfg; + int conditions = HS_CFG_COND_DEF; + u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; + + buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); + + memset(&hscfg, 0, sizeof(struct nxpwifi_ds_hs_cfg)); + + if (arg_num > 3) { + nxpwifi_dbg(priv->adapter, ERROR, + "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (arg_num >= 1 && arg_num < 3) + nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET, + NXPWIFI_SYNC_CMD, &hscfg); + + if (arg_num) { + if (conditions == HS_CFG_CANCEL) { + nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD); + ret = count; + goto done; + } + hscfg.conditions = conditions; + } + if (arg_num >= 2) + hscfg.gpio = gpio; + if (arg_num == 3) + hscfg.gap = gap; + + hscfg.is_invoke_hostcmd = false; + nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET, + NXPWIFI_SYNC_CMD, &hscfg); + + nxpwifi_enable_hs(priv->adapter); + clear_bit(NXPWIFI_IS_HS_ENABLING, &priv->adapter->work_flags); + ret = count; +done: + kfree(buf); + return ret; +} + +/* Proc hscfg file read handler + * This function can be used to read host sleep configuration + * parameters from driver. + */ +static ssize_t +nxpwifi_hscfg_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int pos, ret; + struct nxpwifi_ds_hs_cfg hscfg; + + if (!buf) + return -ENOMEM; + + nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET, + NXPWIFI_SYNC_CMD, &hscfg); + + pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, + hscfg.gpio, hscfg.gap); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static ssize_t +nxpwifi_timeshare_coex_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = file->private_data; + char buf[3]; + bool timeshare_coex; + int ret; + unsigned int len; + + if (priv->adapter->fw_api_ver != NXPWIFI_FW_V15) + return -EOPNOTSUPP; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX, + HOST_ACT_GEN_GET, 0, ×hare_coex, true); + if (ret) + return ret; + + len = sprintf(buf, "%d\n", timeshare_coex); + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static ssize_t +nxpwifi_timeshare_coex_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + bool timeshare_coex; + struct nxpwifi_private *priv = file->private_data; + int ret; + + if (priv->adapter->fw_api_ver != NXPWIFI_FW_V15) + return -EOPNOTSUPP; + + ret = kstrtobool_from_user(ubuf, count, ×hare_coex); + if (ret) + return ret; + + ret = nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX, + HOST_ACT_GEN_SET, 0, ×hare_coex, true); + if (ret) + return ret; + else + return count; +} + +static ssize_t +nxpwifi_reset_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = file->private_data; + struct nxpwifi_adapter *adapter = priv->adapter; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, count, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + if (adapter->if_ops.card_reset) { + dev_info(adapter->dev, "Resetting per request\n"); + adapter->if_ops.card_reset(adapter); + } + + return count; +} + +static ssize_t +nxpwifi_fake_radar_detect_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv = file->private_data; + struct nxpwifi_adapter *adapter = priv->adapter; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, count, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + if (priv->wdev.links[0].cac_started) { + nxpwifi_dbg(adapter, MSG, + "Generate fake radar detected during CAC\n"); + if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef)) + nxpwifi_dbg(adapter, ERROR, + "Failed to stop CAC in FW\n"); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); + cfg80211_radar_event(adapter->wiphy, &priv->dfs_chandef, + GFP_KERNEL); + } else { + if (priv->bss_chandef.chan->dfs_cac_ms) { + nxpwifi_dbg(adapter, MSG, + "Generate fake radar detected\n"); + cfg80211_radar_event(adapter->wiphy, + &priv->dfs_chandef, + GFP_KERNEL); + } + } + + return count; +} + +#define NXPWIFI_DFS_ADD_FILE(name) debugfs_create_file(#name, 0644, \ + priv->dfs_dev_dir, priv, \ + &nxpwifi_dfs_##name##_fops) + +#define NXPWIFI_DFS_FILE_OPS(name) \ +static const struct file_operations nxpwifi_dfs_##name##_fops = { \ + .read = nxpwifi_##name##_read, \ + .write = nxpwifi_##name##_write, \ + .open = simple_open, \ +} + +#define NXPWIFI_DFS_FILE_READ_OPS(name) \ +static const struct file_operations nxpwifi_dfs_##name##_fops = { \ + .read = nxpwifi_##name##_read, \ + .open = simple_open, \ +} + +#define NXPWIFI_DFS_FILE_WRITE_OPS(name) \ +static const struct file_operations nxpwifi_dfs_##name##_fops = { \ + .write = nxpwifi_##name##_write, \ + .open = simple_open, \ +} + +NXPWIFI_DFS_FILE_READ_OPS(info); +NXPWIFI_DFS_FILE_READ_OPS(debug); +NXPWIFI_DFS_FILE_READ_OPS(getlog); +NXPWIFI_DFS_FILE_OPS(regrdwr); +NXPWIFI_DFS_FILE_OPS(rdeeprom); +NXPWIFI_DFS_FILE_OPS(memrw); +NXPWIFI_DFS_FILE_OPS(hscfg); +NXPWIFI_DFS_FILE_OPS(histogram); +NXPWIFI_DFS_FILE_OPS(debug_mask); +NXPWIFI_DFS_FILE_OPS(timeshare_coex); +NXPWIFI_DFS_FILE_WRITE_OPS(reset); +NXPWIFI_DFS_FILE_WRITE_OPS(fake_radar_detect); +NXPWIFI_DFS_FILE_OPS(verext); + +/* This function creates the debug FS directory structure and the files. + */ +void +nxpwifi_dev_debugfs_init(struct nxpwifi_private *priv) +{ + if (!nxpwifi_dfs_dir || !priv) + return; + + priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, + nxpwifi_dfs_dir); + + NXPWIFI_DFS_ADD_FILE(info); + NXPWIFI_DFS_ADD_FILE(debug); + NXPWIFI_DFS_ADD_FILE(getlog); + NXPWIFI_DFS_ADD_FILE(regrdwr); + NXPWIFI_DFS_ADD_FILE(rdeeprom); + + NXPWIFI_DFS_ADD_FILE(memrw); + NXPWIFI_DFS_ADD_FILE(hscfg); + NXPWIFI_DFS_ADD_FILE(histogram); + NXPWIFI_DFS_ADD_FILE(debug_mask); + NXPWIFI_DFS_ADD_FILE(timeshare_coex); + NXPWIFI_DFS_ADD_FILE(reset); + NXPWIFI_DFS_ADD_FILE(fake_radar_detect); + NXPWIFI_DFS_ADD_FILE(verext); +} + +/* This function removes the debug FS directory structure and the files. + */ +void +nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv) +{ + if (!priv) + return; + + debugfs_remove_recursive(priv->dfs_dev_dir); +} + +/* This function creates the top level proc directory. + */ +void +nxpwifi_debugfs_init(void) +{ + if (!nxpwifi_dfs_dir) + nxpwifi_dfs_dir = debugfs_create_dir("nxpwifi", NULL); +} + +/* This function removes the top level proc directory. + */ +void +nxpwifi_debugfs_remove(void) +{ + debugfs_remove(nxpwifi_dfs_dir); +} From patchwork Mon Sep 30 06:36:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815431 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97B49183CCA; Mon, 30 Sep 2024 06:38:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678319; cv=fail; b=kA+z+oW+vG7oEIOLHroB7DDJgvzcnSIWk2nkWLPDlp+yxnQc0UaWi6UU91LMKzFPXGiY68BZlnrVPPjoLye9xoX62Vc211uQMpntXnLxBviaQhJQndeDaAei8uad5zTv3bM1+ZgiMHdeDWvCi3fbUX4+Zt/PDf48F7yHIaVb4rE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678319; c=relaxed/simple; bh=MqXLYPCMV1eqb2mw4jj/tGIok272B1ZFiqy1ovw1R0s=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=VewtJMkHosbw9duOie8HBhZF7/N4AhjhOteHk23qzJa0asqQ/MYpKPPp39ktb6qJBJoWC5YenDA3CQUQb5di/qt0YgovC+9KoUOQT4rRPotHG+fuZTNYLHKkQJsq0GMBk76OgAVubaPHjx97GQXgCjO5V83HEyfywznU+fYwSWU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=BHXsJF4I; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="BHXsJF4I" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=gn0J+cTyHSvWajHo2QVFQ6UK919qDVPNnGdVASJNkkSla5qYUXUXt6iN5sYWpoBuB3NHiavPRSfD7Ntbg33Bp8xysEJrzPFVifI8aFQKO7JxQ1g8/RusBi4j7nlsNYMzRzZTG4xiyljZ65Fu1p1oqTNnt93ED96l6b7RyIYMjUWu1JhKquDwBNFN5ec+wFDV4EkQo8FfZP85zBVahIg1sNR1SKqKhNyY7OdL33nG1KbdsgzBPQXxICoZAHhjCRnbztLP8W3WrR6Xj+JHkt0nUEjYN5sUTinuZsO5R5XRc8Hj6DXwqiF8uRMRLWOJcmyA155h95Fggb20PthpYbbuhQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=H+b7b3Yp4o7k6ymtI9MtYerGLM1PxttDiaDliLQLklw=; b=HM+XgdLOZjYLuBELKS9jkJaKjuOGJPhc3e1cT9npWIBDHbk1uNLai5LKjxCmqHSRKCCp7YIG3nKamBBMKOh3g9fTqhuNTiFS7h8t5532fbHq9NgeC5r9kGX9mmm1Z/8oTJ2e1pXCuXgN3kdXSLzwmCOs+wWHxEaHjL7Gsr1KmLMn9LEWwM51F9aNgVJTXoZt0OnQ1vI7JUqw+vmXo3M1IAoDwEuusqiKLJBa3/KlcHqo0KCw6QyJ2DQkAFrnNxBTKERrDIh3QEZTFhAaWF0KQqLgkI0vyPL5laE9jOCDDgOGbunO9ahpTdaUxAdzY+6lmGneBUg6WZt2LoClX+8tSQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=H+b7b3Yp4o7k6ymtI9MtYerGLM1PxttDiaDliLQLklw=; b=BHXsJF4Ih7tBHccywHeUI4DHDb40mUFL51aMuXWg42sssu+REa94l8165KH1iF2ZGlasDtSuBl3Egydvb7bBbgxlBH1mya2K41N3kg10fSVbFzG0TMeQ6hFzelkZPFClOPk968dfEAcPG477ryBZDcURCfN6GP5kUxCHOEb6rMCQcdU5BswFqdTzt1eTzpJff5ScPtA04FnsKgQEgLtu0U993bCf4jgLz0xY+eodCytiyshrRqVl0v0jVhBkCc0Dz7o11RIHg1d9ZYsAWXR0L7DANz5ZwzRzG1Szq8zyB6upP/ZYRYzicMRSuGOqBI6f8Jayyk5lmBzEurrdd5Oh+w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:15 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:15 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 15/22] wifi: nxpwifi: add ethtool.c Date: Mon, 30 Sep 2024 14:36:54 +0800 Message-Id: <20240930063701.2566520-16-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 30926ae4-2491-4d0d-a2eb-08dce11a7613 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: BuQUd5EigYQdIXmEtyVjoY9HGI+CcO5GfC9/Cdcrf69AF5ogqGBMiXA0E1bpuRt5o6Mw4a6lS+qK7m4DicvqGm+3DLHJzAcd/qJEvcdfXJz6ZI5TSCLO9h3RgkIKpsymCTHMQSnb2js3hXL/LG50KuV2/TLRrqxi61netGNXSmMxY7W3tqfB/rB+5zvLSQRtMZWV3v4sioyk34AO4r+GPCQgIHddQ8Pk4nwT5rKROS6DbVn9Z3LL+vFvGyKPZoxIXm5gzNeZMJ1pTefEChN8tQ3aD7YvV+BPxbzIVhVZ26Z7Yy4to/dwlq6tchdB8DyaTI1qB5JjRGfhu7SplB/WXkM2qkTggFQlogmDbdTioM3XBC8vOydwtUdReFOJD7jUX9sZx0G3Ivw48gTl5cNkzrBcvau809J8DkKWbO+1G+QO/Jhwvpg4iZs+eMK+FxB7fPQmTit5b90uwoO7oMThHIZOsBfMrzhXYqHWajvkpyPtOijkwxDTgLXHVWMyNHvRcNBEhM2KBeL2Q0XeWjvW8+CmXfQUfci/VXPxQZ1bfL00NQ7kQu0jvJrmi9p/F1jUnrl9RXEkB/47+655BPlWIuPlzX6jK1tRncycZ8u8y88DgDctgL7TxWbc2Hq1zZ8UVTUQW5W6yW2LR6OG6fftTMYYwfb0b7HYk4RWJiBjGbtVQjJ/BoR877u8r1jLmI4kQnJ2OnwybvmOJH8cmx/a9ZmKTXsdOZiv4r1vczoSVUmuWetm+B59pEsfm9+oC5+vx71WNHCSn1f0fZR+M43QYGlQYg02Cn1aRfFX94f7WrPAHMwPDq9b2WSlIn2wc69+6MzStpLcusY2N+JpHhsxKkwdZFddWqFVkem0mDQ+pYkWQZ49BnI6NujoUrpbYQLDHsf3olgp+QuNu4fsNbvbHIpxidqFQiWc02PVwEq9aydncL9z0Y+THj5xPVqTlEPSP7xMM3l1xrGSv59hsc9ETjn35TN79WnGvf6xSP53b8WokiB6g5KOhkwC8zQ8yjfWD/G4IkfjYR9Dv/y9AH1/mcoZvcJ52m3ls/JKEm98+JVqt8MelJzr1hy3vFi896mEfHGQCFD8nKT4HNXCBSlnfSndbqs1n5A9mKpXfV5+sZPI3qol3wSQhaFQrIEnDfe8GYGK8ZYXGDE6tllX+Fds57hZvGs9xNHQd617cPMjD79cwg3FCaMmgSK8xyzMsQITH2RXP4iRz7yh8IJXzR941h+FOlRIzvBIFQP9Stb8izdAgQ4EEhyeoZdo23/n0WGmvmuGuFvAjxnro0vQYBiC10GsWhtvEsLO6ZdB6NAJ39qq2IjgWesHr+81X6e9BpiQWRfZ+XP6yAfUzCSvcSzQjF+E25lHitSWwYBrnaXQPykDNsiWQCsoVih8mnfvy1iaApMFO2Kie4fpzsqg8s+V1Q== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: xtO8/xJ0Lqkgs6YqHS1bK/gL4XBoyTC0eRIPtve7rtBqSfBzjV6cYfGfzkWGQx17+CSqgbq1kp9CyLE7Fx1HsHkiwOJa3q6wT07juABIfKYbnAcYezRu3NyQ3uyFEHHM2EKBImBOQbyBJXsRov+mXb1zJtg3kzp5J+GWqLryEFDeGeOt98zrOUkzhV6GxVCzsVoIZQbf5Tn3t1ZbM/yMWqBxs5j9QNA8WptVkoqR7doU5bisibK8FoAgpbCgq8KKZR/fOJ5hNA+EMWoqqBEd5eikcZ/HhWhDipYvxtQc+6XGP8IdJvS+f9dXB2SvM7veiOhMvCvtk5xbteMM/0rF+il3RcOidrBIFq+75vWzV3CGQp5hyAEBpVboQ23OCEANoX06mADtqpkAAQn5tbaMpE9SO96YCW7dv7f7Wf29KoVdImNnZDKa2BHBys8+SrHwePi5I+INyQipYzrZ89qXzM18IaZHkFuAk4jF81fY3Z7uc/MIKpIXai/CwVf0l2tsOnJz4gb/k6WrxywZS0buYnAmlk983isd9UKeHnpEEspSpbd8kKQ9FED39g8fnhyBwen2iGPjcy2UcxFGZDh0vVE3C+rzP21kXRzQDhGtGrsqB/+H7lmQNZVPSFGtNXK08wIpgah5SVdMUtvaebK3Mg5EmPtXoAy8+H9EW3+LyWS/5eHsgkrHBRbPOzI0J1AWqYuJmUUJ4l648k34NSEP8GuG3Fjdjxb1kzD/XjNt4R5J/WQ3Plrjef3VuYX22gmpVA3rQXS/3H1iZQeJlmpEOxkVw0l9/Ua9qkzx5sUHrVN5zVX7JvPvu6g/ynzwas1T59lawlKa7xvzk1O92KkPSrK8PgvSeiXhXUNcS45PlexEgg4pwnDxBBlSvoU0AKnj3P+MofXUN0TImVZNKnWaPCwcTNM9trMDXy1byejZF1K7FMBX3wG1YYEaNc6rkfc/WJWKBZnVzXm6m8b96hpASVOkmeZatHWqXrJea5hCtZwZtl2rDWqg6M6wCYE67st3ryLZ57OjWszgAZddMpBWu0jP1kbw/SEadsUSDy+HbPy8QJmpKad0dyB5jSc33Dz30s6d8C1SFwDGUuq3mh4q+ZNUPaYvxX37Rif3CWYVwfLu3C+ccrxcZXYEhzvTN3ZDTuqdE6dOz7GRzyYmSI/zYKqH+MLPOKSoEcC25sioKwdk5fzFcJMRGwbQrz1fZmgh00BAp9rcJsPT0oRP4k4/wXBibnCGHk7s75+/lnlgFGq9inLMIf6vr9qeZTY21IoVVFEEF9IIf77k/NtY2X1GNIhGUxhc/vIn039Gu9fyLx9O/vqOk/TXOlbAnm3iauxUNpQFtV4pZoiSLjlfIcSx8rlAgkmBF9G4YlYO8VWQYkG75YdPvyfbrNA7GNvv7HKR/N4GJwwqy3+SJF0kvW2cDprGimTOfjJv8+A2gFavvAV9cASrcfFLpKCfCwuWkiB9dbnCjLUScUnyK7SmYN+/vftZjlr3ZGrD0TfWR2+g7obXv8xpi/0oCJApDKwon7bXua69dgzrocVgSCnqldMwfFK/8K2V0Z5HYftIjWB1GoQ3+jKYFPB6jRtVWDZAg825 X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 30926ae4-2491-4d0d-a2eb-08dce11a7613 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:14.9584 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: ntZYngbPOE1+aXGHfiWcyZVFPxCHxFqvgQzWjUS537jZMAmiNv4OmvH0SPKTP7Gb4WQWHr7nqId1980O348NMQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement functions related to Wake-on-Lan. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/ethtool.c | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/ethtool.c diff --git a/drivers/net/wireless/nxp/nxpwifi/ethtool.c b/drivers/net/wireless/nxp/nxpwifi/ethtool.c new file mode 100644 index 000000000000..6cefa6e6f5b3 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/ethtool.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: ethtool + * + * Copyright 2011-2024 NXP + */ + +#include "main.h" + +static void nxpwifi_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions); + + wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY; + + if (conditions == HS_CFG_COND_DEF) + return; + + if (conditions & HS_CFG_COND_UNICAST_DATA) + wol->wolopts |= WAKE_UCAST; + if (conditions & HS_CFG_COND_MULTICAST_DATA) + wol->wolopts |= WAKE_MCAST; + if (conditions & HS_CFG_COND_BROADCAST_DATA) + wol->wolopts |= WAKE_BCAST; + if (conditions & HS_CFG_COND_MAC_EVENT) + wol->wolopts |= WAKE_PHY; +} + +static int nxpwifi_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + u32 conditions = 0; + + if (wol->wolopts & ~(WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY)) + return -EOPNOTSUPP; + + if (wol->wolopts & WAKE_UCAST) + conditions |= HS_CFG_COND_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + conditions |= HS_CFG_COND_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + conditions |= HS_CFG_COND_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + conditions |= HS_CFG_COND_MAC_EVENT; + if (wol->wolopts == 0) + conditions |= HS_CFG_COND_DEF; + priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions); + + return 0; +} + +const struct ethtool_ops nxpwifi_ethtool_ops = { + .get_wol = nxpwifi_ethtool_get_wol, + .set_wol = nxpwifi_ethtool_set_wol, +}; From patchwork Mon Sep 30 06:36:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815432 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2044.outbound.protection.outlook.com [40.107.21.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97ACB183CC9; Mon, 30 Sep 2024 06:38:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.44 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678322; cv=fail; b=SarBE3uENuyeWkhkgwAxVVONq3gDoa5SBanAzAXivoi1YJipY5xV2HkxoWpEjPCcEf7yTt0OgtdFjCHZCWqBiCSQIFZvZBaEzKhhMrLt69ZpobMeqUJbOtIkgygpmL9gZN8t6PozvBgzzx2pmIboMIYp0fdx58WZCHbtD/bBA0I= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678322; c=relaxed/simple; bh=khOEL4QSPx2+WLPlItw96jNWlrIe187S7L7SlsrfxUI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=kXwHYliZBQhepC2eCz0rf9cmynJV0umQDSHBrnE12qEtCRrWxbpu1xPaLO/HSvJrjqglZ7bOVoe7G8seexQS6GJONjqOmEcUDkjBc4GrXmOxhZ0OQrhGZpky4CeoosA10adi/bUgUn354qRqdZ+qWr1Ot8ehRM54tzn90KlMHms= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=bWVxUrz8; arc=fail smtp.client-ip=40.107.21.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="bWVxUrz8" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=E1oBrG1KJf6kOun4fR7v+7nRZA1IUAIPhabPxmq5E/WC1hhXQ96K5dT/REebAuIGRNpAYIGtB9jbdb+iqH25FqpC3BfJTxaffN3Ifc+BtiW7HHndO8uOW7KeR6K4F/1qMn4sYmBGhVyeWlDpqSbMjDnAeNd3yaODn9ET2hOeSADCINnKvOyaoN7WtMGAjAjRkpYxlegI5rsHnIj2Zhrtr5/2A9gZKCYBc3CIeyMrfLWpsOPKnoR5Nf2VGjRl/7+bQYj4U720e/5DCzUV5G2yi3pEcfYXDXXn1mkaSGpBLwB4xap5qD3b7m1dzAO+MxIf6EKLFJSnChwB+wagV6UcYA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=EfSXVTzzWW6cmkjv87tdw3yMqPhFF0fQ5IqNxXlC1kg=; b=Id7EZGNchppaLdJQWybZX36OVAPvmCMJgMWerSwwXnL6YNbMzEFCCi1uEcz19x+JdkKBzxTruRKPkxGRxwoC+DjYfEIjncVHyaRGPltOR3QfTi18w6cmHARCke2W0wSXyZ+XPQXnkykOtw5fOYQjp82PjFVGXJWCA93K7u7lNnB6FYOwQfWisDF4gyj49eYxAhNMORd2S6LhEUsj6iF9HPgjGOQPLGDcjrlz0tO9BxNqS3UePY2A/qNnrn3+ezd56OId5cWcUmtb2pn/rjc/ELQUpkUANO89r9JG4mFkzD00W7Xfhp1x299MZFeYKlEBPzGb6kFqGbf5f+poIYERfA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=EfSXVTzzWW6cmkjv87tdw3yMqPhFF0fQ5IqNxXlC1kg=; b=bWVxUrz86yXi95rAf8Zifa+rN90twkddfFf0HTuql5QDXhQXjDIFXPWiLaoQCStooxlUS2tlWc0im8zS83hMARCpCTcFqOTGIDAIaPMjvudgPDBZjOvfkkaJ7Exn9rNJYzI6C+JEbJQ8NffXsNCU4vWBCA33kFmGFKlU4iyxgAZxZPf8LTiZqkKeGt22XiP16IBNEtXpTJ1jugHilZc3J61ySuqgaCfuHpawovmBM8/cEHSQLlFg+YM+ZW58PKv8PTWPSWt6GWc1ZLhIGmOMyA10JikC1u3WKkee4a5pdY7ZQwWUTu27znsIBeIpZHcmI71Ly1CxV+hYrvVwAq5M1w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:18 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:18 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 16/22] wifi: nxpwifi: add utility files Date: Mon, 30 Sep 2024 14:36:55 +0800 Message-Id: <20240930063701.2566520-17-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: fb845c92-3417-4a23-b50e-08dce11a77c7 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: Z/9VhOro3/QkE2iKTIdJVY95rJ3Wm6zdcKlpusg2HCUyqZCJmrOH2VjJv3+mUxnGbSVuKnxD/fwF4ICkuCB/z0GsDLQ13oTe03k3VxU7kEThbiNcnI0T8v+K4lvWYqlHCNPCVS0BuKh2rjDMKoBhHP3qBrbm6lUqeBb4hYO83SFfVZIf5LZGE3pWnS7lhRl6m+M51QmyCm8760TlfiA/MTLH0KouwNitWdtRmqlMR/7L06ax9mlCZYAJW/aoUitBn8oJNWvOm2zGOUTU1QSvlXRm4ETyczveCM/DiGBhSqcE9g2MI5bZpraHFysGdBdg0NJo3q3kPt33MN2Xk659DFiCBuqxJgoqZJdPSv6WOMRlzQANQvI1TB4CQWOeSNgLWJa0qFbSeD1L2GRPfDq16VKJkzjibOckaJzGxAa1xlyvmmKenQVQptCG2MAdb++gqYNU/VhhQ45J2lm62giSCoB5jZrySHdTl/i1nhj7wKlk5K4WmKF9CnUtKQMd4sHHX0WPldZQIjmGODg20Lx5t+7du6FxWwaE34T5QhYDMMX98KjZE1jRZDiS+utPjSUtTsck6jH0S6/U2TiQIis3h5QUJfEWxM6SOqMtiXgeHOmHwlCXT24PdmDgW4A8J2sz4yKBto+IzNyNfwawxmjFBkPybYDLhlOXZ79JNcoYYCh1ox5ebkXGq4w/DlMq31M2FO1N5jKZ5hT772GHcTOiLdhfs2+A2i+jzQ94p3LSvQXTtb8l7xXMa2v9hwQsBy95TNyrw8cKKmRklagoeF8Sa0zC8AOgWQ8+/JPvW7ryVcf7/9IrOS02CkHt7+xBIbY3QQe/k4/OXcciZbkcwxfi/wo+PtxvRv9WuHfFHB3lMOK+LIJ/DtVIdvtE+HpBRDWs1NLjiPScF03p2iGPz5wpXaueM1+Qjn2KLOq9rVx3HsYgUCs0Q/SEsASTX9tRic47d7cP4bAXdvg8dC0VVneoxuTEPxT5xy1dX/31IExJi+RWIuwUpxNIVqqdXtgynquLcyI9Nxs5HxYa17cRjrg/KU9uaLu6wh9PCQYLxBj976ghjXyliu09A0AMi0sZ6MfSbgzefj6mekkcKoLrkETD19L69hzLUkAnyzHRXcmJ/rF3WayJQ/e7uOXs/W+C28+zxSO0fHO+v1DvSdBS1QdmWBzLm0z4Zu6Q4Oplsq0kX/8oXZVc6jTpkwsrbXGbhdyCvvdxwtwdH++IgC3dwktDamDMg1WhubJiVTMgiXve0rzUgKpTZasxjeV6mskcsO8+tkqbhAe+7yhibyW9huvzM3e4esWlVJ7nly+nhcheHTek656VYKwX+B9hYr22gkotgV5OuIU2JMqJLjWuHs8id5/eX8w7pwL09Gn5w7s1UZKAcR9oWuPHNtIbWv6p6o6TI79W65S2zd4HuXmAslwTlw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: qg0X0mKq3/ENuCAmneC0UMUmXkPDo2WZ/pXfDvrK5PmfHKPLzyc1ch608pb3mhscINNtjBQAI+z9iD3fZf+wUtgXRby1NZXX2W8O/IFKH2Yumrw3QIktuMgmXngjDssY7Py6dRkNEM4FMzIqQ1b6d+YvZwSeizi6fJx0AcLvRu7HbP7hCfdWk0ohA2/HiPhGGdurmwX0cesmG1jJcOp+ll4AZHMWQLheVXZKZ+Tie6JHPJlKPTY3Y0nk9/K5n0O/TRQuWyeeU+y+H0xrfAMs6zjBawBBO/aCLQvJnU3dKL2Cxp1Nnb+ThBF6ulugndgFilchI0cwig/fplQg3sByUngBuXbvLCJPdgBH+MGktR3VoHHRRXkeGXYZNebiuqGYd1LebQjlA36zIP93YPvBmhoAryzznkwdbrNI/4MbKr5a7tHpiyjS3k8sJSscgCCGDM+uhQfuZgre4rxM8QMhx6uJyq+rhifBdIxQCUYNue7Wyscmh63+KGZpTEAwQ+KHRaRr4iQxYFWlc5pac4seQFY6xpg5J0qtdIplkwPnvA2rGqzIpwVjwoZt3J5tQApoGJf+nneTWkayzCnmAaTgF02v7Jwj5S/Gp2P8XNGn7UN6+KbONfWBtf5Be5nNPhpoAJeYsWALP8JufZFgNEeLd+mE8JMyRZGjUMCKV2/oqf6KXsRx5Fuc3iwMs2oxPRez98AA+sIMh0+ZZJMmgrc3bo+mYhb9eEZKDuJraMCt0kmB+NklF7de/Uf10bTR95Xx2hS1MxaLzk5pzYOgdln+Jc4l+5PEUcUxL96qf/0e/3+fqXXwRzYJkwqt19KrjOa0RaRp+F6d/42GIjvLbsVeUGvKjd6vmG5lJTx06znDf+U7zwP3J7c4o5d8zqSP2e21PqA3JvKHPGIGKQ+vy6XCkZRZRQObqj3rsjmBkv+VYCvfXRy54QE5/eopC4rWFftA5rcbbNeXn9h0WT0/dRU2q+ApTeiKqMg87en0YmH5LSxtJBjFTR/8lEl+XnScC4DIXBswTSRxdQFqAim5TwbGg4iXXxZJURhcLZa7F4VcP3zgyy44E4n4hDZmDT1D7MA6JZBl5GBcPUwkq3fg2Tez/pyR433bM0MPNEN+Nh1hrFFowDkpZAv4kjzIlqT/j2RzVGqCWmamjT+VO4bAhsocCofyYb02HHUXH3x4drUunfwQ0H2n4IjVsJGogvYdg4jjKpN1N+Hyx2oFOy7bFk3doI4yvAX3tMOlzxbHUCh4sIb/fQJ9KzcUgGQ54QIZjNKLHuUETe0/tRKO+0xXQylJyXNj8g/cW4n2HNUVYuDk9JKpEVGofUS+DYNlutawq8IvTrrUQL60LD3D7qff8MISOCHpEJmN2QGYIVbt2WpSakUiYIC61yCDFB7U3j9w2+DQ1cTgRh624N9axfRmD8Dss7zv96vlog77QZwOlib7PamsyqfvQlaAzOtkvt2SEXcf5/vNyGHMA0Ngfoa8w5QMW3wusYptTTlVyUMHiAl65HK/c0ZU3R+EOr1RvyWyyoreRwgKtIB99ZKeSuSoSfMQCkDqCz4ZEsnbReeywf/Hc6Viik3cRK/MH0qjKpYyrwjD X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: fb845c92-3417-4a23-b50e-08dce11a77c7 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:17.9919 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: UEwWu9Co2DQvUuSu1bEgbWX9kdSy3dhzVCxeACc155W2uLIZdJkO261j2yqK1vgjJKDFs2L7/eyWnnTei6gDIQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement useful functions to help IE operations and some common utilities. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/ie.c | 501 ++++++++++++ drivers/net/wireless/nxp/nxpwifi/util.c | 999 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/util.h | 121 +++ 3 files changed, 1621 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/ie.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/util.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/util.h diff --git a/drivers/net/wireless/nxp/nxpwifi/ie.c b/drivers/net/wireless/nxp/nxpwifi/ie.c new file mode 100644 index 000000000000..15bdc3c7a6fa --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/ie.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: management IE handling- setting and + * deleting IE. + * + * Copyright 2011-2024 NXP + */ + +#include "main.h" +#include "cmdevt.h" + +/* This function checks if current IE index is used by any on other interface. + * Return: true: yes, current IE index is used by someone else. + * false: no, current IE index is NOT used by other interface. + */ +static bool +nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private *priv, u16 idx) +{ + int i; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_ie *ie; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i] != priv) { + ie = &adapter->priv[i]->mgmt_ie[idx]; + if (ie->mgmt_subtype_mask && ie->ie_length) + return true; + } + } + + return false; +} + +/* Get unused IE index. This index will be used for setting new IE */ +static int +nxpwifi_ie_get_autoidx(struct nxpwifi_private *priv, u16 subtype_mask, + struct nxpwifi_ie *ie, u16 *index) +{ + u16 mask, len, i; + + for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) { + mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); + len = le16_to_cpu(ie->ie_length); + + if (mask == NXPWIFI_AUTO_IDX_MASK) + continue; + + if (mask == subtype_mask) { + if (len > IEEE_MAX_IE_SIZE) + continue; + + *index = i; + return 0; + } + + if (!priv->mgmt_ie[i].ie_length) { + if (nxpwifi_ie_index_used_by_other_intf(priv, i)) + continue; + + *index = i; + return 0; + } + } + + return -ENOENT; +} + +/* This function prepares IE data buffer for command to be sent to FW */ +static int +nxpwifi_update_autoindex_ies(struct nxpwifi_private *priv, + struct nxpwifi_ie_list *ie_list) +{ + u16 travel_len, index, mask; + s16 input_len, tlv_len; + struct nxpwifi_ie *ie; + u8 *tmp; + + input_len = le16_to_cpu(ie_list->len); + travel_len = sizeof(struct nxpwifi_ie_types_header); + + ie_list->len = 0; + + while (input_len >= sizeof(struct nxpwifi_ie_types_header)) { + ie = (struct nxpwifi_ie *)(((u8 *)ie_list) + travel_len); + tlv_len = le16_to_cpu(ie->ie_length); + travel_len += tlv_len + NXPWIFI_IE_HDR_SIZE; + + if (input_len < tlv_len + NXPWIFI_IE_HDR_SIZE) + return -EINVAL; + index = le16_to_cpu(ie->ie_index); + mask = le16_to_cpu(ie->mgmt_subtype_mask); + + if (index == NXPWIFI_AUTO_IDX_MASK) { + /* automatic addition */ + if (nxpwifi_ie_get_autoidx(priv, mask, ie, &index)) + return -ENOENT; + if (index == NXPWIFI_AUTO_IDX_MASK) + return -EINVAL; + + tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer; + memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); + priv->mgmt_ie[index].ie_length = ie->ie_length; + priv->mgmt_ie[index].ie_index = cpu_to_le16(index); + priv->mgmt_ie[index].mgmt_subtype_mask = + cpu_to_le16(mask); + + ie->ie_index = cpu_to_le16(index); + } else { + if (mask != NXPWIFI_DELETE_MASK) + return -EINVAL; + /* Check if this index is being used on any + * other interface. + */ + if (nxpwifi_ie_index_used_by_other_intf(priv, index)) + return -EPERM; + + ie->ie_length = 0; + memcpy(&priv->mgmt_ie[index], ie, + sizeof(struct nxpwifi_ie)); + } + + le16_unaligned_add_cpu + (&ie_list->len, + le16_to_cpu(priv->mgmt_ie[index].ie_length) + + NXPWIFI_IE_HDR_SIZE); + input_len -= tlv_len + NXPWIFI_IE_HDR_SIZE; + } + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) + return nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG, + HOST_ACT_GEN_SET, + UAP_CUSTOM_IE_I, ie_list, true); + + return 0; +} + +/* Copy individual custom IEs for beacon, probe response and assoc response + * and prepare single structure for IE setting. + * This function also updates allocated IE indices from driver. + */ +static int +nxpwifi_update_uap_custom_ie(struct nxpwifi_private *priv, + struct nxpwifi_ie *beacon_ie, u16 *beacon_idx, + struct nxpwifi_ie *pr_ie, u16 *probe_idx, + struct nxpwifi_ie *ar_ie, u16 *assoc_idx) +{ + struct nxpwifi_ie_list *ap_custom_ie; + u8 *pos; + u16 len; + int ret; + + ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); + if (!ap_custom_ie) + return -ENOMEM; + + ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); + pos = (u8 *)ap_custom_ie->ie_list; + + if (beacon_ie) { + len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + memcpy(pos, beacon_ie, len); + pos += len; + le16_unaligned_add_cpu(&ap_custom_ie->len, len); + } + if (pr_ie) { + len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + memcpy(pos, pr_ie, len); + pos += len; + le16_unaligned_add_cpu(&ap_custom_ie->len, len); + } + if (ar_ie) { + len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(ar_ie->ie_length); + memcpy(pos, ar_ie, len); + pos += len; + le16_unaligned_add_cpu(&ap_custom_ie->len, len); + } + + ret = nxpwifi_update_autoindex_ies(priv, ap_custom_ie); + + pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index); + if (beacon_ie && *beacon_idx == NXPWIFI_AUTO_IDX_MASK) { + /* save beacon ie index after auto-indexing */ + *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); + len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + pos += len; + } + if (pr_ie && le16_to_cpu(pr_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *probe_idx = *((u16 *)pos); + len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + pos += len; + } + if (ar_ie && le16_to_cpu(ar_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK) + /* save assoc resp ie index after auto-indexing */ + *assoc_idx = *((u16 *)pos); + + kfree(ap_custom_ie); + return ret; +} + +/* This function checks if the vendor specified IE is present in passed buffer + * and copies it to nxpwifi_ie structure. + * Function takes pointer to struct nxpwifi_ie pointer as argument. + * If the vendor specified IE is present then memory is allocated for + * nxpwifi_ie pointer and filled in with IE. Caller should take care of freeing + * this memory. + */ +static int nxpwifi_update_vs_ie(const u8 *ies, int ies_len, + struct nxpwifi_ie **ie_ptr, u16 mask, + unsigned int oui, u8 oui_type) +{ + struct element *vs_ie; + struct nxpwifi_ie *ie = *ie_ptr; + const u8 *vendor_ie; + + vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); + if (vendor_ie) { + if (!*ie_ptr) { + *ie_ptr = kzalloc(sizeof(*ie_ptr), GFP_KERNEL); + if (!*ie_ptr) + return -ENOMEM; + ie = *ie_ptr; + } + + vs_ie = (struct element *)vendor_ie; + if (le16_to_cpu(ie->ie_length) + vs_ie->datalen + 2 > + IEEE_MAX_IE_SIZE) + return -EINVAL; + memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), + vs_ie, vs_ie->datalen + 2); + le16_unaligned_add_cpu(&ie->ie_length, vs_ie->datalen + 2); + ie->mgmt_subtype_mask = cpu_to_le16(mask); + ie->ie_index = cpu_to_le16(NXPWIFI_AUTO_IDX_MASK); + } + + *ie_ptr = ie; + return 0; +} + +/* This function parses beacon IEs, probe response IEs, association response IEs + * from cfg80211_ap_settings->beacon and sets these IE to FW. + */ +static int nxpwifi_set_mgmt_beacon_data_ies(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *data) +{ + struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL; + u16 beacon_idx = NXPWIFI_AUTO_IDX_MASK, pr_idx = NXPWIFI_AUTO_IDX_MASK; + u16 ar_idx = NXPWIFI_AUTO_IDX_MASK; + int ret = 0; + + if (data->beacon_ies && data->beacon_ies_len) { + nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } + + if (data->proberesp_ies && data->proberesp_ies_len) { + nxpwifi_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + nxpwifi_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } + + if (data->assocresp_ies && data->assocresp_ies_len) { + nxpwifi_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + nxpwifi_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P); + } + + if (beacon_ie || pr_ie || ar_ie) { + ret = nxpwifi_update_uap_custom_ie(priv, beacon_ie, + &beacon_idx, pr_ie, + &pr_idx, ar_ie, &ar_idx); + if (ret) + goto done; + } + + priv->beacon_idx = beacon_idx; + priv->proberesp_idx = pr_idx; + priv->assocresp_idx = ar_idx; + +done: + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} + +/* This function parses head and tail IEs, from cfg80211_beacon_data and sets + * these IE to FW. + */ +static int nxpwifi_uap_parse_tail_ies(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *info) +{ + struct nxpwifi_ie *gen_ie; + struct element *hdr; + struct ieee80211_vendor_ie *vendorhdr; + u16 gen_idx = NXPWIFI_AUTO_IDX_MASK, ie_len = 0; + int left_len, parsed_len = 0; + unsigned int token_len; + int ret = 0; + + if (!info->tail || !info->tail_len) + return 0; + + gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + left_len = info->tail_len; + + /* Many IEs are generated in FW by parsing bss configuration. + * Let's not add them here; else we may end up duplicating these IEs + */ + while (left_len > sizeof(struct element)) { + hdr = (void *)(info->tail + parsed_len); + token_len = hdr->datalen + sizeof(struct element); + if (token_len > left_len) { + ret = -EINVAL; + goto done; + } + + switch (hdr->id) { + case WLAN_EID_SSID: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_ERP_INFO: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_HT_OPERATION: + case WLAN_EID_VHT_CAPABILITY: + case WLAN_EID_VHT_OPERATION: + break; + case WLAN_EID_VENDOR_SPECIFIC: + /* Skip only Microsoft WMM IE */ + if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + (const u8 *)hdr, + token_len)) + break; + fallthrough; + default: + if (ie_len + token_len > IEEE_MAX_IE_SIZE) { + ret = -EINVAL; + goto done; + } + memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len); + ie_len += token_len; + break; + } + left_len -= token_len; + parsed_len += token_len; + } + + /* parse only WPA vendor IE from tail, WMM IE is configured by + * bss_config command + */ + vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + info->tail, info->tail_len); + if (vendorhdr) { + token_len = vendorhdr->len + sizeof(struct element); + if (ie_len + token_len > IEEE_MAX_IE_SIZE) { + ret = -EINVAL; + goto done; + } + memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len); + ie_len += token_len; + } + + if (!ie_len) + goto done; + + gen_ie->ie_index = cpu_to_le16(gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP); + gen_ie->ie_length = cpu_to_le16(ie_len); + + ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, + NULL, NULL, NULL); + + if (ret) + goto done; + + priv->gen_idx = gen_idx; + + done: + kfree(gen_ie); + return ret; +} + +/* This function parses different IEs-head & tail IEs, beacon IEs, + * probe response IEs, association response IEs from cfg80211_ap_settings + * function and sets these IE to FW. + */ +int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *info) +{ + int ret; + + ret = nxpwifi_uap_parse_tail_ies(priv, info); + + if (ret) + return ret; + + return nxpwifi_set_mgmt_beacon_data_ies(priv, info); +} + +/* This function removes management IE set */ +int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv) +{ + struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL; + struct nxpwifi_ie *ar_ie = NULL, *gen_ie = NULL; + int ret = 0; + + if (priv->gen_idx != NXPWIFI_AUTO_IDX_MASK) { + gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + gen_ie->ie_index = cpu_to_le16(priv->gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK); + gen_ie->ie_length = 0; + ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx, + NULL, &priv->proberesp_idx, + NULL, &priv->assocresp_idx); + if (ret) + goto done; + + priv->gen_idx = NXPWIFI_AUTO_IDX_MASK; + } + + if (priv->beacon_idx != NXPWIFI_AUTO_IDX_MASK) { + beacon_ie = kmalloc(sizeof(*beacon_ie), GFP_KERNEL); + if (!beacon_ie) { + ret = -ENOMEM; + goto done; + } + beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx); + beacon_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK); + beacon_ie->ie_length = 0; + } + if (priv->proberesp_idx != NXPWIFI_AUTO_IDX_MASK) { + pr_ie = kmalloc(sizeof(*pr_ie), GFP_KERNEL); + if (!pr_ie) { + ret = -ENOMEM; + goto done; + } + pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx); + pr_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK); + pr_ie->ie_length = 0; + } + if (priv->assocresp_idx != NXPWIFI_AUTO_IDX_MASK) { + ar_ie = kmalloc(sizeof(*ar_ie), GFP_KERNEL); + if (!ar_ie) { + ret = -ENOMEM; + goto done; + } + ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx); + ar_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK); + ar_ie->ie_length = 0; + } + + if (beacon_ie || pr_ie || ar_ie) + ret = nxpwifi_update_uap_custom_ie(priv, + beacon_ie, &priv->beacon_idx, + pr_ie, &priv->proberesp_idx, + ar_ie, &priv->assocresp_idx); + +done: + kfree(gen_ie); + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/util.c b/drivers/net/wireless/nxp/nxpwifi/util.c new file mode 100644 index 000000000000..39bc38883147 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/util.c @@ -0,0 +1,999 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: utility functions + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" + +static struct nxpwifi_debug_data items[] = { + {"debug_mask", item_size(debug_mask), + item_addr(debug_mask), 1}, + {"int_counter", item_size(int_counter), + item_addr(int_counter), 1}, + {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), + item_addr(packets_out[WMM_AC_VO]), 1}, + {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), + item_addr(packets_out[WMM_AC_VI]), 1}, + {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), + item_addr(packets_out[WMM_AC_BE]), 1}, + {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), + item_addr(packets_out[WMM_AC_BK]), 1}, + {"tx_buf_size", item_size(tx_buf_size), + item_addr(tx_buf_size), 1}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), 1}, + {"ps_mode", item_size(ps_mode), + item_addr(ps_mode), 1}, + {"ps_state", item_size(ps_state), + item_addr(ps_state), 1}, + {"is_deep_sleep", item_size(is_deep_sleep), + item_addr(is_deep_sleep), 1}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), 1}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), 1}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), 1}, + {"hs_activated", item_size(hs_activated), + item_addr(hs_activated), 1}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout), 1}, + {"is_cmd_timedout", item_size(is_cmd_timedout), + item_addr(is_cmd_timedout), 1}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id), 1}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), 1}, + {"last_cmd_id", item_size(last_cmd_id), + item_addr(last_cmd_id), DBG_CMD_NUM}, + {"last_cmd_act", item_size(last_cmd_act), + item_addr(last_cmd_act), DBG_CMD_NUM}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index), 1}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), DBG_CMD_NUM}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), 1}, + {"last_event", item_size(last_event), + item_addr(last_event), DBG_CMD_NUM}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), 1}, + {"last_mp_wr_bitmap", item_size(last_mp_wr_bitmap), + item_addr(last_mp_wr_bitmap), NXPWIFI_DBG_SDIO_MP_NUM}, + {"last_mp_wr_ports", item_size(last_mp_wr_ports), + item_addr(last_mp_wr_ports), NXPWIFI_DBG_SDIO_MP_NUM}, + {"last_mp_wr_len", item_size(last_mp_wr_len), + item_addr(last_mp_wr_len), NXPWIFI_DBG_SDIO_MP_NUM}, + {"last_mp_curr_wr_port", item_size(last_mp_curr_wr_port), + item_addr(last_mp_curr_wr_port), NXPWIFI_DBG_SDIO_MP_NUM}, + {"last_sdio_mp_index", item_size(last_sdio_mp_index), + item_addr(last_sdio_mp_index), 1}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), 1}, + {"num_cmd_sleep_cfm_fail", + item_size(num_cmd_sleep_cfm_host_to_card_failure), + item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), 1}, + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth), 1}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc), 1}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost), 1}, + {"num_cmd_deauth", item_size(num_cmd_deauth), + item_addr(num_cmd_deauth), 1}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success), 1}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure), 1}, + {"cmd_sent", item_size(cmd_sent), + item_addr(cmd_sent), 1}, + {"data_sent", item_size(data_sent), + item_addr(data_sent), 1}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), 1}, + {"event_received", item_size(event_received), + item_addr(event_received), 1}, + + /* variables defined in struct nxpwifi_adapter */ + {"cmd_pending", adapter_item_size(cmd_pending), + adapter_item_addr(cmd_pending), 1}, + {"tx_pending", adapter_item_size(tx_pending), + adapter_item_addr(tx_pending), 1}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/* Firmware initialization complete callback handler. + * + * This function wakes up the function waiting on the init + * wait queue for the firmware initialization to complete. + */ +void nxpwifi_init_fw_complete(struct nxpwifi_adapter *adapter) +{ + adapter->init_wait_q_woken = true; + wake_up_interruptible(&adapter->init_wait_q); +} + +/* This function sends init/shutdown command + * to firmware. + */ +int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv, + u32 func_init_shutdown) +{ + u16 cmd; + + if (func_init_shutdown == NXPWIFI_FUNC_INIT) { + cmd = HOST_CMD_FUNC_INIT; + } else if (func_init_shutdown == NXPWIFI_FUNC_SHUTDOWN) { + cmd = HOST_CMD_FUNC_SHUTDOWN; + } else { + nxpwifi_dbg(priv->adapter, ERROR, + "unsupported parameter\n"); + return -EINVAL; + } + + return nxpwifi_send_cmd(priv, cmd, HOST_ACT_GEN_SET, 0, NULL, true); +} +EXPORT_SYMBOL_GPL(nxpwifi_init_shutdown_fw); + +/* IOCTL request handler to set/get debug information. + * + * This function collates/sets the information from/to different driver + * structures. + */ +int nxpwifi_get_debug_info(struct nxpwifi_private *priv, + struct nxpwifi_debug_info *info) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + + if (info) { + info->debug_mask = adapter->debug_mask; + memcpy(info->packets_out, + priv->wmm.packets_out, + sizeof(priv->wmm.packets_out)); + info->curr_tx_buf_size = (u32)adapter->curr_tx_buf_size; + info->tx_buf_size = (u32)adapter->tx_buf_size; + info->rx_tbl_num = nxpwifi_get_rx_reorder_tbl(priv, + info->rx_tbl); + info->tx_tbl_num = nxpwifi_get_tx_ba_stream_tbl(priv, + info->tx_tbl); + info->ps_mode = adapter->ps_mode; + info->ps_state = adapter->ps_state; + info->is_deep_sleep = adapter->is_deep_sleep; + info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; + info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; + info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + info->hs_activated = adapter->hs_activated; + info->is_cmd_timedout = test_bit(NXPWIFI_IS_CMD_TIMEDOUT, + &adapter->work_flags); + info->num_cmd_host_to_card_failure = + adapter->dbg.num_cmd_host_to_card_failure; + info->num_cmd_sleep_cfm_host_to_card_failure = + adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + info->num_tx_host_to_card_failure = + adapter->dbg.num_tx_host_to_card_failure; + info->num_event_deauth = adapter->dbg.num_event_deauth; + info->num_event_disassoc = adapter->dbg.num_event_disassoc; + info->num_event_link_lost = adapter->dbg.num_event_link_lost; + info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; + info->num_cmd_assoc_success = + adapter->dbg.num_cmd_assoc_success; + info->num_cmd_assoc_failure = + adapter->dbg.num_cmd_assoc_failure; + info->num_tx_timeout = adapter->dbg.num_tx_timeout; + info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; + info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; + memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, + sizeof(adapter->dbg.last_cmd_id)); + memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, + sizeof(adapter->dbg.last_cmd_act)); + info->last_cmd_index = adapter->dbg.last_cmd_index; + memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, + sizeof(adapter->dbg.last_cmd_resp_id)); + info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; + memcpy(info->last_event, adapter->dbg.last_event, + sizeof(adapter->dbg.last_event)); + info->last_event_index = adapter->dbg.last_event_index; + memcpy(info->last_mp_wr_bitmap, adapter->dbg.last_mp_wr_bitmap, + sizeof(adapter->dbg.last_mp_wr_bitmap)); + memcpy(info->last_mp_wr_ports, adapter->dbg.last_mp_wr_ports, + sizeof(adapter->dbg.last_mp_wr_ports)); + memcpy(info->last_mp_curr_wr_port, + adapter->dbg.last_mp_curr_wr_port, + sizeof(adapter->dbg.last_mp_curr_wr_port)); + memcpy(info->last_mp_wr_len, adapter->dbg.last_mp_wr_len, + sizeof(adapter->dbg.last_mp_wr_len)); + info->last_sdio_mp_index = adapter->dbg.last_sdio_mp_index; + info->data_sent = adapter->data_sent; + info->cmd_sent = adapter->cmd_sent; + info->cmd_resp_received = adapter->cmd_resp_received; + } + + return 0; +} + +int nxpwifi_debug_info_to_buffer(struct nxpwifi_private *priv, char *buf, + struct nxpwifi_debug_info *info) +{ + char *p = buf; + struct nxpwifi_debug_data *d = &items[0]; + size_t size, addr; + long val; + int i, j; + + if (!info) + return 0; + + for (i = 0; i < num_of_items; i++) { + p += sprintf(p, "%s=", d[i].name); + + size = d[i].size / d[i].num; + + if (i < (num_of_items - 3)) + addr = d[i].addr + (size_t)info; + else /* The last 3 items are struct nxpwifi_adapter variables */ + addr = d[i].addr + (size_t)priv->adapter; + + for (j = 0; j < d[i].num; j++) { + switch (size) { + case 1: + val = *((u8 *)addr); + break; + case 2: + val = get_unaligned((u16 *)addr); + break; + case 4: + val = get_unaligned((u32 *)addr); + break; + case 8: + val = get_unaligned((long long *)addr); + break; + default: + val = -1; + break; + } + + p += sprintf(p, "%#lx ", val); + addr += size; + } + + p += sprintf(p, "\n"); + } + + if (info->tx_tbl_num) { + p += sprintf(p, "Tx BA stream table:\n"); + for (i = 0; i < info->tx_tbl_num; i++) + p += sprintf(p, "tid = %d, ra = %pM\n", + info->tx_tbl[i].tid, info->tx_tbl[i].ra); + } + + if (info->rx_tbl_num) { + p += sprintf(p, "Rx reorder table:\n"); + for (i = 0; i < info->rx_tbl_num; i++) { + p += sprintf(p, "tid = %d, ta = %pM, ", + info->rx_tbl[i].tid, + info->rx_tbl[i].ta); + p += sprintf(p, "start_win = %d, ", + info->rx_tbl[i].start_win); + p += sprintf(p, "win_size = %d, buffer: ", + info->rx_tbl[i].win_size); + + for (j = 0; j < info->rx_tbl[i].win_size; j++) + p += sprintf(p, "%c ", + info->rx_tbl[i].buffer[j] ? + '1' : '0'); + + p += sprintf(p, "\n"); + } + } + + return p - buf; +} + +bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv, + struct ieee80211_channel *check_chan) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + int i; + struct nxpwifi_private *tmp_priv; + u8 bss_role = GET_BSS_ROLE(priv); + struct ieee80211_channel *set_chan; + + for (i = 0; i < adapter->priv_num; i++) { + tmp_priv = adapter->priv[i]; + if (tmp_priv == priv) + continue; + + set_chan = NULL; + if (bss_role == NXPWIFI_BSS_ROLE_STA) { + if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_UAP && + netif_carrier_ok(tmp_priv->netdev) && + cfg80211_chandef_valid(&tmp_priv->bss_chandef)) + set_chan = tmp_priv->bss_chandef.chan; + } else if (bss_role == NXPWIFI_BSS_ROLE_UAP) { + struct nxpwifi_current_bss_params *bss_params = + &tmp_priv->curr_bss_params; + int channel = bss_params->bss_descriptor.channel; + enum nl80211_band band = + nxpwifi_band_to_radio_type(bss_params->band); + int freq = + ieee80211_channel_to_frequency(channel, band); + + if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_STA && + tmp_priv->media_connected) + set_chan = ieee80211_get_channel(adapter->wiphy, freq); + } + + if (set_chan && !ieee80211_channel_equal(check_chan, set_chan)) { + nxpwifi_dbg(adapter, ERROR, + "AP/STA must run on the same channel\n"); + return false; + } + } + + return true; +} + +static int +nxpwifi_parse_mgmt_packet(struct nxpwifi_private *priv, u8 *payload, u16 len, + struct rxpd *rx_pd) +{ + u16 stype; + u8 category; + struct ieee80211_hdr *ieee_hdr = (void *)payload; + + stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE); + + switch (stype) { + case IEEE80211_STYPE_ACTION: + category = *(payload + sizeof(struct ieee80211_hdr)); + switch (category) { + case WLAN_CATEGORY_BACK: + /*we dont indicate BACK action frames to cfg80211*/ + nxpwifi_dbg(priv->adapter, INFO, + "drop BACK action frames"); + return -EINVAL; + default: + nxpwifi_dbg(priv->adapter, INFO, + "unknown public action frame category %d\n", + category); + } + break; + default: + nxpwifi_dbg(priv->adapter, INFO, + "unknown mgmt frame subtype %#x\n", stype); + return 0; + } + + return 0; +} + +/* This function sends deauth packet to the kernel. */ +void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv, + u16 reason_code, u8 *sa) +{ + u8 frame_buf[100]; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf; + + memset(frame_buf, 0, sizeof(frame_buf)); + mgmt->frame_control = cpu_to_le16(IEEE80211_STYPE_DEAUTH); + mgmt->duration = 0; + mgmt->seq_ctrl = 0; + mgmt->u.deauth.reason_code = cpu_to_le16(reason_code); + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) { + eth_broadcast_addr(mgmt->da); + memcpy(mgmt->sa, + priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + } else { + memcpy(mgmt->da, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->sa, sa, ETH_ALEN); + memcpy(mgmt->bssid, priv->curr_addr, ETH_ALEN); + } + + if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) { + wiphy_lock(priv->wdev.wiphy); + cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26); + wiphy_unlock(priv->wdev.wiphy); + } else { + cfg80211_rx_mgmt(&priv->wdev, + priv->bss_chandef.chan->center_freq, + 0, frame_buf, 26, 0); + } +} + +/* This function processes the received management packet and send it + * to the kernel. + */ +int +nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct rxpd *rx_pd; + u16 pkt_len; + struct ieee80211_hdr *ieee_hdr; + int ret; + + if (!skb) + return -ENOMEM; + + if (!priv->mgmt_frame_mask || + priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) { + nxpwifi_dbg(adapter, ERROR, + "do not receive mgmt frames on uninitialized intf"); + return -EINVAL; + } + + rx_pd = (struct rxpd *)skb->data; + pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); + if (pkt_len < sizeof(struct ieee80211_hdr) + sizeof(pkt_len)) { + nxpwifi_dbg(adapter, ERROR, "invalid rx_pkt_length"); + return -EINVAL; + } + + skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); + skb_pull(skb, sizeof(pkt_len)); + pkt_len -= sizeof(pkt_len); + + ieee_hdr = (void *)skb->data; + if (ieee80211_is_mgmt(ieee_hdr->frame_control)) { + ret = nxpwifi_parse_mgmt_packet(priv, (u8 *)ieee_hdr, + pkt_len, rx_pd); + if (ret) + return ret; + } + /* Remove address4 */ + memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), + skb->data + sizeof(struct ieee80211_hdr), + pkt_len - sizeof(struct ieee80211_hdr)); + + pkt_len -= ETH_ALEN; + rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); + + if (priv->host_mlme_reg && + (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) && + (ieee80211_is_auth(ieee_hdr->frame_control) || + ieee80211_is_deauth(ieee_hdr->frame_control) || + ieee80211_is_disassoc(ieee_hdr->frame_control))) { + struct nxpwifi_rxinfo *rx_info; + + if (ieee80211_is_auth(ieee_hdr->frame_control)) { + if (priv->auth_flag & HOST_MLME_AUTH_PENDING) { + if (priv->auth_alg != WLAN_AUTH_SAE) { + priv->auth_flag &= + ~HOST_MLME_AUTH_PENDING; + priv->auth_flag |= + HOST_MLME_AUTH_DONE; + } + } else { + return 0; + } + + nxpwifi_dbg(adapter, MSG, + "auth: receive authentication from %pM\n", + ieee_hdr->addr3); + } else { + if (!priv->wdev.connected) + return 0; + + if (ieee80211_is_deauth(ieee_hdr->frame_control)) { + nxpwifi_dbg(adapter, MSG, + "auth: receive deauth from %pM\n", + ieee_hdr->addr3); + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + } else { + nxpwifi_dbg(adapter, MSG, + "assoc: receive disassoc from %pM\n", + ieee_hdr->addr3); + } + } + + rx_info = NXPWIFI_SKB_RXCB(skb); + rx_info->pkt_len = pkt_len; + skb_queue_tail(&adapter->rx_mlme_q, skb); + nxpwifi_queue_work(adapter, &adapter->host_mlme_work); + return -EINPROGRESS; + } + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + if (ieee80211_is_auth(ieee_hdr->frame_control)) + nxpwifi_dbg(adapter, MSG, + "auth: receive auth from %pM\n", + ieee_hdr->addr2); + if (ieee80211_is_deauth(ieee_hdr->frame_control)) + nxpwifi_dbg(adapter, MSG, + "auth: receive deauth from %pM\n", + ieee_hdr->addr2); + if (ieee80211_is_disassoc(ieee_hdr->frame_control)) + nxpwifi_dbg(adapter, MSG, + "assoc: receive disassoc from %pM\n", + ieee_hdr->addr2); + if (ieee80211_is_assoc_req(ieee_hdr->frame_control)) + nxpwifi_dbg(adapter, MSG, + "assoc: receive assoc req from %pM\n", + ieee_hdr->addr2); + if (ieee80211_is_reassoc_req(ieee_hdr->frame_control)) + nxpwifi_dbg(adapter, MSG, + "assoc: receive reassoc req from %pM\n", + ieee_hdr->addr2); + } + + cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq, + CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, + 0); + + return 0; +} + +/* This function processes the received packet before sending it to the + * kernel. + * + * It extracts the SKB from the received buffer and sends it to kernel. + * In case the received buffer does not contain the data in SKB format, + * the function creates a blank SKB, fills it with the data from the + * received buffer and then sends this new SKB to the kernel. + */ +int nxpwifi_recv_packet(struct nxpwifi_private *priv, struct sk_buff *skb) +{ + struct nxpwifi_sta_node *src_node; + struct ethhdr *p_ethhdr; + + if (!skb) + return -ENOMEM; + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + p_ethhdr = (void *)skb->data; + src_node = nxpwifi_get_sta_entry(priv, p_ethhdr->h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + } + } + + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + skb->ip_summed = CHECKSUM_NONE; + + netif_rx(skb); + return 0; +} + +/* IOCTL completion callback handler. + * + * This function is called when a pending IOCTL is completed. + * + * If work queue support is enabled, the function wakes up the + * corresponding waiting function. Otherwise, it processes the + * IOCTL response and frees the response buffer. + */ +int nxpwifi_complete_cmd(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + WARN_ON(!cmd_node->wait_q_enabled); + nxpwifi_dbg(adapter, CMD, "cmd completed: status=%d\n", + adapter->cmd_wait_q.status); + + *cmd_node->condition = true; + wake_up_interruptible(&adapter->cmd_wait_q.wait); + + return 0; +} + +/* This function will return the pointer to station entry in station list + * table which matches specified mac address. + * This function should be called after acquiring RA list spinlock. + * NULL is returned if station entry is not found in associated STA list. + */ +struct nxpwifi_sta_node * +nxpwifi_get_sta_entry(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + + if (!mac) + return NULL; + + list_for_each_entry(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) + return node; + } + + return NULL; +} + +/* This function will add a sta_node entry to associated station list + * table with the given mac address. + * If entry exist already, existing entry is returned. + * If received mac address is NULL, NULL is returned. + */ +struct nxpwifi_sta_node * +nxpwifi_add_sta_entry(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + + if (!mac) + return NULL; + + spin_lock_bh(&priv->sta_list_spinlock); + node = nxpwifi_get_sta_entry(priv, mac); + if (node) + goto done; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + goto done; + + memcpy(node->mac_addr, mac, ETH_ALEN); + list_add_tail(&node->list, &priv->sta_list); + +done: + spin_unlock_bh(&priv->sta_list_spinlock); + return node; +} + +/* This function will search for HT IE in association request IEs + * and set station HT parameters accordingly. + */ +void +nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies, + int ies_len, struct nxpwifi_sta_node *node) +{ + struct element *ht_cap_ie; + const struct ieee80211_ht_cap *ht_cap; + + if (!ies) + return; + + ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, + ies_len); + if (ht_cap_ie) { + ht_cap = (void *)(ht_cap_ie + 1); + node->is_11n_enabled = 1; + node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU ? + NXPWIFI_TX_DATA_BUF_SIZE_8K : + NXPWIFI_TX_DATA_BUF_SIZE_4K; + } else { + node->is_11n_enabled = 0; + } +} + +/* This function will delete a station entry from station list */ +void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + + spin_lock_bh(&priv->sta_list_spinlock); + + node = nxpwifi_get_sta_entry(priv, mac); + if (node) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_bh(&priv->sta_list_spinlock); +} + +/* This function will delete all stations from associated station list. */ +void nxpwifi_del_all_sta_list(struct nxpwifi_private *priv) +{ + struct nxpwifi_sta_node *node, *tmp; + + spin_lock_bh(&priv->sta_list_spinlock); + + list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { + list_del(&node->list); + kfree(node); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_bh(&priv->sta_list_spinlock); +} + +/* This function adds histogram data to histogram array*/ +void nxpwifi_hist_data_add(struct nxpwifi_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct nxpwifi_histogram_data *phist_data = priv->hist_data; + + if (atomic_read(&phist_data->num_samples) > NXPWIFI_HIST_MAX_SAMPLES) + nxpwifi_hist_data_reset(priv); + nxpwifi_hist_data_set(priv, rx_rate, snr, nflr); +} + +/* function to add histogram record */ +void nxpwifi_hist_data_set(struct nxpwifi_private *priv, u8 rx_rate, s8 snr, + s8 nflr) +{ + struct nxpwifi_histogram_data *phist_data = priv->hist_data; + s8 nf = -nflr; + s8 rssi = snr - nflr; + + atomic_inc(&phist_data->num_samples); + atomic_inc(&phist_data->rx_rate[rx_rate]); + atomic_inc(&phist_data->snr[snr + 128]); + atomic_inc(&phist_data->noise_flr[nf + 128]); + atomic_inc(&phist_data->sig_str[rssi + 128]); +} + +/* function to reset histogram data during init/reset */ +void nxpwifi_hist_data_reset(struct nxpwifi_private *priv) +{ + int ix; + struct nxpwifi_histogram_data *phist_data = priv->hist_data; + + atomic_set(&phist_data->num_samples, 0); + for (ix = 0; ix < NXPWIFI_MAX_AC_RX_RATES; ix++) + atomic_set(&phist_data->rx_rate[ix], 0); + for (ix = 0; ix < NXPWIFI_MAX_SNR; ix++) + atomic_set(&phist_data->snr[ix], 0); + for (ix = 0; ix < NXPWIFI_MAX_NOISE_FLR; ix++) + atomic_set(&phist_data->noise_flr[ix], 0); + for (ix = 0; ix < NXPWIFI_MAX_SIG_STRENGTH; ix++) + atomic_set(&phist_data->sig_str[ix], 0); +} + +void *nxpwifi_alloc_dma_align_buf(int rx_len, gfp_t flags) +{ + struct sk_buff *skb; + int buf_len, pad; + + buf_len = rx_len + NXPWIFI_RX_HEADROOM + NXPWIFI_DMA_ALIGN_SZ; + + skb = __dev_alloc_skb(buf_len, flags); + + if (!skb) + return NULL; + + skb_reserve(skb, NXPWIFI_RX_HEADROOM); + + pad = NXPWIFI_ALIGN_ADDR(skb->data, NXPWIFI_DMA_ALIGN_SZ) - + (long)skb->data; + + skb_reserve(skb, pad); + + return skb; +} +EXPORT_SYMBOL_GPL(nxpwifi_alloc_dma_align_buf); + +void nxpwifi_fw_dump_event(struct nxpwifi_private *priv) +{ + nxpwifi_send_cmd(priv, HOST_CMD_FW_DUMP_EVENT, HOST_ACT_GEN_SET, + 0, NULL, true); +} +EXPORT_SYMBOL_GPL(nxpwifi_fw_dump_event); + +int nxpwifi_append_data_tlv(u16 id, u8 *data, int len, u8 *pos, u8 *cmd_end) +{ + struct nxpwifi_ie_types_data *tlv; + u16 header_len = sizeof(struct nxpwifi_ie_types_header); + + tlv = (struct nxpwifi_ie_types_data *)pos; + tlv->header.len = cpu_to_le16(len); + + if (id == WLAN_EID_EXT_HE_CAPABILITY) { + if ((pos + header_len + len + 1) > cmd_end) + return 0; + + tlv->header.type = cpu_to_le16(WLAN_EID_EXTENSION); + tlv->data[0] = WLAN_EID_EXT_HE_CAPABILITY; + memcpy(tlv->data + 1, data, len); + } else { + if ((pos + header_len + len) > cmd_end) + return 0; + + tlv->header.type = cpu_to_le16(id); + memcpy(tlv->data, data, len); + } + + return (header_len + len); +} + +static int nxpwifi_get_vdll_image(struct nxpwifi_adapter *adapter, u32 vdll_len) +{ + struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl; + bool req_fw = false; + u32 offset; + + if (ctrl->vdll_mem) { + nxpwifi_dbg(adapter, EVENT, + "VDLL mem is not empty: %p old_len=%d new_len=%d\n", + ctrl->vdll_mem, ctrl->vdll_len, vdll_len); + vfree(ctrl->vdll_mem); + ctrl->vdll_mem = NULL; + ctrl->vdll_len = 0; + } + + ctrl->vdll_mem = vmalloc(vdll_len); + if (!ctrl->vdll_mem) + return -ENOMEM; + + if (!adapter->firmware) { + req_fw = true; + if (request_firmware(&adapter->firmware, adapter->fw_name, + adapter->dev)) + return -ENOENT; + } + + if (adapter->firmware) { + if (vdll_len < adapter->firmware->size) { + offset = adapter->firmware->size - vdll_len; + memcpy(ctrl->vdll_mem, adapter->firmware->data + offset, + vdll_len); + } else { + nxpwifi_dbg(adapter, ERROR, + "Invalid VDLL length = %d, fw_len=%d\n", + vdll_len, (int)adapter->firmware->size); + return -EINVAL; + } + if (req_fw) { + release_firmware(adapter->firmware); + adapter->firmware = NULL; + } + } + + ctrl->vdll_len = vdll_len; + nxpwifi_dbg(adapter, MSG, "VDLL image: len=%d\n", ctrl->vdll_len); + + return 0; +} + +int nxpwifi_download_vdll_block(struct nxpwifi_adapter *adapter, + u8 *block, u16 block_len) +{ + struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl; + struct host_cmd_ds_command *host_cmd; + u16 msg_len = block_len + S_DS_GEN; + int ret = 0; + + skb_trim(ctrl->skb, 0); + skb_put_zero(ctrl->skb, msg_len); + + host_cmd = (struct host_cmd_ds_command *)(ctrl->skb->data); + + host_cmd->command = cpu_to_le16(HOST_CMD_VDLL); + host_cmd->seq_num = cpu_to_le16(0xFF00); + host_cmd->size = cpu_to_le16(msg_len); + memcpy(ctrl->skb->data + S_DS_GEN, block, block_len); + + skb_push(ctrl->skb, adapter->intf_hdr_len); + ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_VDLL, + ctrl->skb, NULL); + skb_pull(ctrl->skb, adapter->intf_hdr_len); + + if (ret) + nxpwifi_dbg(adapter, ERROR, + "Fail to download VDLL: block: %p, len: %d\n", + block, block_len); + + return ret; +} + +int nxpwifi_process_vdll_event(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct vdll_ind_event *vdll_evt = + (struct vdll_ind_event *)(skb->data + sizeof(u32)); + u16 type = le16_to_cpu(vdll_evt->type); + u16 vdll_id = le16_to_cpu(vdll_evt->vdll_id); + u32 offset = le32_to_cpu(vdll_evt->offset); + u16 block_len = le16_to_cpu(vdll_evt->block_len); + struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl; + int ret = 0; + + switch (type) { + case VDLL_IND_TYPE_REQ: + nxpwifi_dbg(adapter, EVENT, + "VDLL IND (REG): ID: %d, offset: %#x, len: %d\n", + vdll_id, offset, block_len); + if (offset <= ctrl->vdll_len) { + block_len = + min((u32)block_len, ctrl->vdll_len - offset); + if (!adapter->cmd_sent) { + ret = nxpwifi_download_vdll_block(adapter, + ctrl->vdll_mem + + offset, + block_len); + if (ret) + nxpwifi_dbg(adapter, ERROR, + "Download VDLL failed\n"); + } else { + nxpwifi_dbg(adapter, EVENT, + "Delay download VDLL block\n"); + ctrl->pending_block_len = block_len; + ctrl->pending_block = ctrl->vdll_mem + offset; + } + } else { + nxpwifi_dbg(adapter, ERROR, + "Err Req: offset=%#x, len=%d, vdll_len=%d\n", + offset, block_len, ctrl->vdll_len); + ret = -EINVAL; + } + break; + case VDLL_IND_TYPE_OFFSET: + nxpwifi_dbg(adapter, EVENT, + "VDLL IND (OFFSET): offset: %#x\n", offset); + ret = nxpwifi_get_vdll_image(adapter, offset); + break; + case VDLL_IND_TYPE_ERR_SIG: + case VDLL_IND_TYPE_ERR_ID: + case VDLL_IND_TYPE_SEC_ERR_ID: + nxpwifi_dbg(adapter, ERROR, "VDLL IND: error: %d\n", type); + break; + case VDLL_IND_TYPE_INTF_RESET: + nxpwifi_dbg(adapter, EVENT, "VDLL IND: interface reset\n"); + break; + default: + nxpwifi_dbg(adapter, ERROR, "VDLL IND: unknown type: %d", type); + ret = -EINVAL; + break; + } + + return ret; +} + +u64 nxpwifi_roc_cookie(struct nxpwifi_adapter *adapter) +{ + adapter->roc_cookie_counter++; + + /* wow, you wrapped 64 bits ... more likely a bug */ + if (WARN_ON(adapter->roc_cookie_counter == 0)) + adapter->roc_cookie_counter++; + + return adapter->roc_cookie_counter; +} + +static bool nxpwifi_can_queue_work(struct nxpwifi_adapter *adapter) +{ + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) || + test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) || + test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) { + nxpwifi_dbg(adapter, WARN, + "queueing nxpwifi work while going to suspend\n"); + return false; + } + + return true; +} + +void nxpwifi_queue_work(struct nxpwifi_adapter *adapter, + struct work_struct *work) +{ + if (!nxpwifi_can_queue_work(adapter)) + return; + + queue_work(adapter->workqueue, work); +} +EXPORT_SYMBOL(nxpwifi_queue_work); + +void nxpwifi_queue_delayed_work(struct nxpwifi_adapter *adapter, + struct delayed_work *dwork, + unsigned long delay) +{ + if (!nxpwifi_can_queue_work(adapter)) + return; + + queue_delayed_work(adapter->workqueue, dwork, delay); +} +EXPORT_SYMBOL(nxpwifi_queue_delayed_work); diff --git a/drivers/net/wireless/nxp/nxpwifi/util.h b/drivers/net/wireless/nxp/nxpwifi/util.h new file mode 100644 index 000000000000..49c5548c28c5 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/util.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: utility functions + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_UTIL_H_ +#define _NXPWIFI_UTIL_H_ + +struct nxpwifi_adapter; + +struct nxpwifi_private; + +struct nxpwifi_dma_mapping { + dma_addr_t addr; + size_t len; +}; + +struct nxpwifi_cb { + struct nxpwifi_dma_mapping dma_mapping; + union { + struct nxpwifi_rxinfo rx_info; + struct nxpwifi_txinfo tx_info; + }; +}; + +/* size/addr for nxpwifi_debug_info */ +#define item_size(n) (sizeof_field(struct nxpwifi_debug_info, n)) +#define item_addr(n) (offsetof(struct nxpwifi_debug_info, n)) + +/* size/addr for struct nxpwifi_adapter */ +#define adapter_item_size(n) (sizeof_field(struct nxpwifi_adapter, n)) +#define adapter_item_addr(n) (offsetof(struct nxpwifi_adapter, n)) + +struct nxpwifi_debug_data { + char name[32]; /* variable/array name */ + u32 size; /* size of the variable/array */ + size_t addr; /* address of the variable/array */ + int num; /* number of variables in an array */ +}; + +static inline struct nxpwifi_rxinfo *NXPWIFI_SKB_RXCB(struct sk_buff *skb) +{ + struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb; + + BUILD_BUG_ON(sizeof(struct nxpwifi_cb) > sizeof(skb->cb)); + return &cb->rx_info; +} + +static inline struct nxpwifi_txinfo *NXPWIFI_SKB_TXCB(struct sk_buff *skb) +{ + struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb; + + return &cb->tx_info; +} + +static inline void nxpwifi_store_mapping(struct sk_buff *skb, + struct nxpwifi_dma_mapping *mapping) +{ + struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb; + + memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); +} + +static inline void nxpwifi_get_mapping(struct sk_buff *skb, + struct nxpwifi_dma_mapping *mapping) +{ + struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb; + + memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); +} + +static inline dma_addr_t NXPWIFI_SKB_DMA_ADDR(struct sk_buff *skb) +{ + struct nxpwifi_dma_mapping mapping; + + nxpwifi_get_mapping(skb, &mapping); + + return mapping.addr; +} + +int nxpwifi_debug_info_to_buffer(struct nxpwifi_private *priv, char *buf, + struct nxpwifi_debug_info *info); + +static inline void le16_unaligned_add_cpu(__le16 *var, u16 val) +{ + put_unaligned_le16(get_unaligned_le16(var) + val, var); +} + +int nxpwifi_append_data_tlv(u16 id, u8 *data, int len, u8 *pos, u8 *cmd_end); + +int nxpwifi_download_vdll_block(struct nxpwifi_adapter *adapter, + u8 *block, u16 block_len); + +int nxpwifi_process_vdll_event(struct nxpwifi_private *priv, + struct sk_buff *skb); + +u64 nxpwifi_roc_cookie(struct nxpwifi_adapter *adapter); + +void nxpwifi_queue_work(struct nxpwifi_adapter *adapter, + struct work_struct *work); + +void nxpwifi_queue_delayed_work(struct nxpwifi_adapter *adapter, + struct delayed_work *dwork, + unsigned long delay); + +/* Current firmware doesn't support AP and STA running on different + * channels simultaneously in normal mode. + * FW crash would occur in such case. + * This function is used to check if check_chan can be set to FW or not. + * + * Return: + * %true if check_chan can be set to FW without issues. + * %false there is already other channel is set to FW, setting of + * check_chan is not allowable. + */ +bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv, + struct ieee80211_channel *check_chan); + +#endif /* !_NXPWIFI_UTIL_H_ */ From patchwork Mon Sep 30 06:36:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815434 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 35B8D184531; Mon, 30 Sep 2024 06:38:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678323; cv=fail; b=bky/xN4YBgcSZPbglWBs98sPrKHcVOAFaZC23Vb5mk6chu4EVu26WvydFkxrnOwMIWT59CHvZsOybseo8qzE6O8+k512qqWV3wH3vCe0MRMYtJiujZAoKz43p3QtZ6OYMEvl+RcjCdNY6mFiZzJ1ZE+uzpr7CAiytSGz3mNoIUY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678323; c=relaxed/simple; bh=Stk11CME79P58C/yuCkMsfCe7R4kP8JaoRQSJLhe94o=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=g6KM6WH+ZYrNXCGTnWb+dCiKCElhDUg+fjiqfz4Vnv92+h6T3MThCqFP1Fp1FSvtVRDNn/VE7CWhwzda7mzSnc6FWr7nHXkx3q7Z7Evt9hXTwqe9asrhb0Sr0tzPPsqUF16PFHlRxb92xuQtXxXNmftVssfUmz5YvD8ZEYh5zTU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=Kny7EwRD; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="Kny7EwRD" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=j0WIKssLyX/DFlwNeKooOnqDryqBbqETTwz1K4GN8JGVvpyZA5EyBZw0lItgAMfUg1qmKat8XyFKhZewQpgfGMO53YC3KPr+dN66bsCQwnOgdvKpcyNlkrOTwnGq55BlwH2zB8ThS0yTlwWuKmZVB3Xcrioc+uxPx8XiIfMrqaUpIilm6RfU4lJeNKyrfTDfcX/Bn92yxCDqR0WBq55ePc3HV+JXupdtlNFkzz4kiRVFAC6IJRCiYsddL+3kXUNCMevG9iOqSnpWAaks+0WN86MEEBo5mc2mAmmbrG9/Lxvm9zi+9IiE9Dt7wX0UXoCIgo6QxoYrfXxcVzQ5MZfq/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=f4YNNPfhwd4/ZzLlKqk0YDVcknMvyP5IRy29gUPWCwY=; b=nbXPygoc4aCSgU0+SuhJZPnHA86kAwonqJKAonXRS+EDyB2wOE4u+cx6zhxsvfIk2ldm4EV5cOuDpJVwCNCiky1ezLwSeLjA/8SIlNzcX9zFT5lQ9JpDQaF8AqgBOhbHfQGl5qVHxTbNYB8vqYWhrsqhwWX0Xd2GyncmRunLTORPZ1vPcPQ2Gj6cK0MomGJqtnsDGfttAcPFHSVqEUW2R0WoJ99hHI//T48bqwATI7n/VHMoVVhBuxF9M/MzmOF0BGh2PBMJSaFdl7uCvud+cozwWMrrGGpCeCXp0Y0nX4DVFqRNCMDGFRsr6UkGSjTGGJRD6oLIhoI0hke2ShkkNA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=f4YNNPfhwd4/ZzLlKqk0YDVcknMvyP5IRy29gUPWCwY=; b=Kny7EwRDqMEGILMyzNBVb82p05aHEV+vyYj1YmNJhWuOJSRW5EkISBz92olNqBdqFGV5mQ+sul/7JyBmIm6+dfsxtB5SujiaFg9jW9zBSDLioLhgVWitc9tPp3c37BSuLV/2HmsoY5I47h2LnJJZ0HV2meTv1gl+hXZbZQ9IGgHovhWqs+vzz3c8zdBEPhHkkdnbM412l5FSRgK6PJhsrnhIl2BCB55JhW2OVGhZkBK+W6lQZsLEQ3J7OrPqn6uNuSOVK+owecD050UK9n4EuzOzgTaaeDMY4T/nSNRkoMIWYm/vztoGughoAsxqDBr9oMGXwWbQKioxxVQUM+A6Tw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:21 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:21 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 17/22] wifi: nxpwifi: add initialization file Date: Mon, 30 Sep 2024 14:36:56 +0800 Message-Id: <20240930063701.2566520-18-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 104237f1-d79e-4f8e-b770-08dce11a79b0 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: 9gtRCIN8Oy2UfQFGz7It+1y0AmBuWBp65L3x9wucoyLUzK5ezP/aFcyJoif5hA7GZI76CJK7AjBfDjPQlPVtQ+2LGSSb4acWyVGT5/7Gt+4iL7ubH/nr7+2jNDzEEjebIsq7SRMZr7VQd46WouZGGDton1cqmN0ia7yFyqRcGzNR/GExKVCBbzIkTs0L41EKz5DzhDQWl3BNE26kG8AdA57vdXflBWcRaT+Gfslx5xOotRlwuI1R57iaqPn/qK8iclQptOQOTKTc5TKLvzey4I1Rgx7n3n5faCZWomSU1hYUoEQ3/8nDTTITMkSFjwq6DKTlonldUm5rYP0iP4nmcH9bvcCaY5yd9qJX3EuLNQDuCTvfCv1UpLLQrlev14g/zmTut8H7sya244eS+F/UCJl4+4JxLc3H+7tQ2LudZndGiWTubH5DSIjyEyT7YkvY5akEV2jaPC0Wo35rHQq2eTZsqTcAHXK1E8MqivYqgWjzha5HcpaSem80uueCsBn4/z9Ye1VPrU7JYNN+ESVcX2IkdmDw0KjA4T0PxZoNzbH1cYZ2uJmtIqc2Tbws4+TugIdFX1ftGiuGDXU6e9RigBzPfWq2DBBnwAcAFPlohfceVVIE/jHjnhTp9OsdMtcUGJIN+kiaaqUFu8GPyS4etaMEgFTW56ugqACSBBD3EflhR7ve9UC16x0ESHmdTD5EQhLDK77/5UifNhOyOgg9MmuX4rMchy1ZyuPBzZyrQeezvTZqc6qHph9GYNC4z72qZh5rk11eBekDBpOY1rMEzgLVADzEqNs4SKMVf7umoVrxIeOndySFLpu872zL1sqpW+rz1z7uvBpN/JZsg0nWmn41KjPSLAhBc+X5gItzdubIM+2BVdCod0D6hDjgRORWXFV3P5YSAlbRKmCW9ZZbdaMn6k+GVG0uDUhTh5nnozAWp69GEH/UFdUxoTfAB/e6lr/RyxQVPoQUeVvBro0tVwoWHYUVCFWpNYgsKOSCqb7HXuHo0DCGmmQLd5sF2JQ/T5WtwGulB3pU2AL7dQKQd+aY1G029cHiiKqWBWxTZwHPCj+HmVfafU2VjChnnNHwL0Z+KfE3SOk1T3/Tpepzd+faPHUHv9pjH+E0V/JQl/za9eNrE6Jfq1BQcVBVrYTii0EVGdByDBiNkKILKaTD206xuBTVJzAsxmA51TZr97+Cajk6n41ogmQVi53RXSRgkm7t4GNLp3toAaM07drHFTKyvjC3Z2EeSnpWL78b+mqRM1//BhfFA4RUidiqiZnfE9CzI8MdOjWAS0eTAUKMUSqykXlbObHfRmbA7ilQHNbqo38mJLagp0HT8zyCwkxZn3XScdPAWAnp0t0l9aJEgTAnShSEKVzKrs+FnpqGpyLfXlKQGIX3cA6yLr58bLT+/lmWDEg0mD6Wycana35oPw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: 8Lzko8kya8EedSCscqTTTsfuGufvxJHcczAnhk9qWfA52HiiymbqhRVV8F85C43EWhzRkXgsuhb4pD97qj4N0/iOLf0OYr97HA+pINgq8Ij0eFzzA7lRgI73BPj5+mYEbHoA0RC46gEA7dfaSjcZ9q5Yl4jLMvU77sbwaaIaiIRVH+/sDjFggnvX4n/6MZQCxFtwcm97c7ACZPadUjEuLOBXPliFohTArn8LNjL4nkptpDsZ4crmZnzIbuhBVQ8VuqkmvshvDqe7uKD6Ja1BKQnHLHd4i/3VyT+fO+IekAUANk1O92MNbU6owpPnOsGg/5v7yNVnxd6M5RMNHEZO0J9PfM6sHPNn/7UbLa5uFCZuGllaolcWdFzO89qQtb5Zfb0MnL2ZKV3NZ88GbzOLmI+u3JxmH+b83XuSeiGG4psWdi1rSQJevcw8Q8ig8YxMtOxVTB2RzCyyL69n02hAnqcOVmw5nFLjYmlryksUY3zkTZVi+9EJyFTbx9IB16rs4jnUxJk5LKYkW+evKtZXMhBxCXnCnQABOAltrUcuuSbIc+wqZVdOrYgW2HroGezxrX9N+4TTho6/R8demoSVsgBMCLPIvSP1Pf+uTV1kEZobys8nZK6V55doXVZkMZY2WDnES21CR/YeLW0V60qmYO9jB4qtj+ub3sxfgwnEqMwOY68ueVJFwM09YGApHKYGR1r1tUNrzneAAvx/wajpUx5Gui/pSvwIdCuA3ixRNGE5A3zXJn8gE93u0HAH3igAuzDuqly431voooctCAh+ltSYJ9RYWhy091LfoQ4ayuTP9I7eKgB5kgfuDp10R31Rp3BqjuH5b/rEns63AOw0sxYhKyntuQA1m9u8F5ED1u/94pM9u48ks+4pcDWCpeChOBt9dnzcKvj+SDCQtM+SnV4MzwVtp+Ngectl8NKpg1nbX+gWyAyg/g1eAVX1uucFy/rM5I98L6/MlgOmDmifLswwT+IX9nWSu9UTcDGF1JkGjMsrV/qnR4XR8Aot5VShmYiw0ppFZImDwAcAed+J9cr77qGkvOMyGjYi8n70/vSBspt2GgY09hhXRRhle2xgzH/sWA4QC0YRuJ51hDR0GJQdSUF2hsxGRz5GsCl6/0mVFwMeheYyFOK2XxpyTfKIiZlOF87jkf2Oy56QzEJkknwdIBbYaC+4VaLwEMJDoTkCcFfUmElWzNHk3i1CFAhoSrI418Em/vKe5/inohGwziDad0XcEOW244GcdU2BilE+C+56qJY4q/nulDnn95v0JQF0/ySF2o4MYu484z2lvcqjdfYwDpk+GOl4DVjTG9rXKFSs22g8oqFlEwp0/pkQxXkHj8IirFnlcUUlfiIw3jFISbekvmwLo49wXwv0XFitmT24jZ1Eadj9hwTW7wK3S7RkJ0jc++hFiBBNEzQg44JAhABTSZDdxCbMpwzLmFeTd6PtSMsXSdgYWvro2dWxUijyzmWoeNq5RLmzfZ4kggN6likbFvnCv+3rHKIgA3Y0lE43uK7urx2tYXuyVtGcb0vhUVwnLGcayForOg003I6SLjnTX9WubMQDwIz+opLkkKBpexCo2edKgUQ7OWao X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 104237f1-d79e-4f8e-b770-08dce11a79b0 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:21.1223 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: soeQdREblpMY9jWoL96OFFxouNrqye6vZyKWqcZgTm1iqVSIaAtJBuwuC+M+BwvSJRdRjjom6353dPGQe7C/dg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Support initialization functions. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/init.c | 680 ++++++++++++++++++++++++ 1 file changed, 680 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/init.c diff --git a/drivers/net/wireless/nxp/nxpwifi/init.c b/drivers/net/wireless/nxp/nxpwifi/init.c new file mode 100644 index 000000000000..ba8af096ad76 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/init.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: HW/FW Initialization + * + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "11n.h" + +/* This function adds a BSS priority table to the table list. + * + * The function allocates a new BSS priority table node and adds it to + * the end of BSS priority table list, kept in driver memory. + */ +static int nxpwifi_add_bss_prio_tbl(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_bss_prio_node *bss_prio; + struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl; + + bss_prio = kzalloc(sizeof(*bss_prio), GFP_KERNEL); + if (!bss_prio) + return -ENOMEM; + + bss_prio->priv = priv; + INIT_LIST_HEAD(&bss_prio->list); + + spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock); + list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); + spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock); + + return 0; +} + +static void wakeup_timer_fn(struct timer_list *t) +{ + struct nxpwifi_adapter *adapter = from_timer(adapter, t, wakeup_timer); + + nxpwifi_dbg(adapter, ERROR, "Firmware wakeup failed\n"); + adapter->hw_status = NXPWIFI_HW_STATUS_RESET; + nxpwifi_cancel_all_pending_cmd(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +/* This function initializes the private structure and sets default + * values to the members. + * + * Additionally, it also initializes all the locks and sets up all the + * lists. + */ +int nxpwifi_init_priv(struct nxpwifi_private *priv) +{ + u32 i; + + priv->media_connected = false; + eth_broadcast_addr(priv->curr_addr); + priv->port_open = false; + priv->usb_port = NXPWIFI_USB_EP_DATA; + priv->pkt_tx_ctrl = 0; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->data_rate = 0; /* Initially indicate the rate as auto */ + priv->is_data_rate_auto = true; + priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + priv->auth_flag = 0; + priv->auth_alg = WLAN_AUTH_NONE; + + priv->sec_info.wep_enabled = 0; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + priv->sec_info.encryption_mode = 0; + for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) + memset(&priv->wep_key[i], 0, sizeof(struct nxpwifi_wep_key)); + priv->wep_key_curr_index = 0; + priv->curr_pkt_filter = HOST_ACT_MAC_DYNAMIC_BW_ENABLE | + HOST_ACT_MAC_RX_ON | HOST_ACT_MAC_TX_ON | + HOST_ACT_MAC_ETHERNETII_ENABLE; + + priv->beacon_period = 100; /* beacon interval */ + priv->attempted_bss_desc = NULL; + memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); + priv->listen_interval = NXPWIFI_DEFAULT_LISTEN_INTERVAL; + + memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); + memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); + memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); + priv->assoc_rsp_size = 0; + priv->atim_window = 0; + priv->tx_power_level = 0; + priv->max_tx_power_level = 0; + priv->min_tx_power_level = 0; + priv->tx_ant = 0; + priv->rx_ant = 0; + priv->tx_rate = 0; + priv->rxpd_htinfo = 0; + priv->rxpd_rate = 0; + priv->rate_bitmap = 0; + priv->data_rssi_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->data_nf_last = 0; + priv->bcn_rssi_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->bcn_nf_last = 0; + memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + memset(&priv->aes_key, 0, sizeof(priv->aes_key)); + priv->wpa_ie_len = 0; + priv->wpa_is_gtk_set = false; + + memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); + priv->assoc_tlv_buf_len = 0; + memset(&priv->wps, 0, sizeof(priv->wps)); + memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); + priv->gen_ie_buf_len = 0; + memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); + + priv->wmm_required = true; + priv->wmm_enabled = false; + priv->wmm_qosinfo = 0; + priv->curr_bcn_buf = NULL; + priv->curr_bcn_size = 0; + priv->wps_ie = NULL; + priv->wps_ie_len = 0; + priv->ap_11n_enabled = 0; + memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); + + priv->scan_block = false; + + priv->csa_chan = 0; + priv->csa_expire_time = 0; + priv->del_list_idx = 0; + priv->hs2_enabled = false; + memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); + + nxpwifi_init_11h_params(priv); + + return nxpwifi_add_bss_prio_tbl(priv); +} + +/* This function allocates buffers for members of the adapter + * structure. + * + * The memory allocated includes scan table, command buffers, and + * sleep confirm command buffer. In addition, the queues are + * also initialized. + */ +static int nxpwifi_allocate_adapter(struct nxpwifi_adapter *adapter) +{ + int ret; + + /* Allocate command buffer */ + ret = nxpwifi_alloc_cmd_buffer(adapter); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: failed to alloc cmd buffer\n", + __func__); + return ret; + } + + adapter->sleep_cfm = + dev_alloc_skb(sizeof(struct nxpwifi_opt_sleep_confirm) + + INTF_HEADER_LEN); + + if (!adapter->sleep_cfm) { + nxpwifi_dbg(adapter, ERROR, + "%s: failed to alloc sleep cfm\t" + " cmd buffer\n", __func__); + return -ENOMEM; + } + skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); + + return 0; +} + +/* This function initializes the adapter structure and sets default + * values to the members of adapter. + * + * This also initializes the WMM related parameters in the driver private + * structures. + */ +static void nxpwifi_init_adapter(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_opt_sleep_confirm *sleep_cfm_buf = NULL; + + skb_put(adapter->sleep_cfm, sizeof(struct nxpwifi_opt_sleep_confirm)); + + adapter->cmd_sent = false; + adapter->data_sent = true; + + adapter->intf_hdr_len = INTF_HEADER_LEN; + + adapter->cmd_resp_received = false; + adapter->event_received = false; + adapter->data_received = false; + adapter->assoc_resp_received = false; + adapter->priv_link_lost = NULL; + adapter->host_mlme_link_lost = false; + + clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + + adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING; + + adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM; + adapter->ps_state = PS_STATE_AWAKE; + adapter->need_to_wakeup = false; + + adapter->scan_mode = HOST_BSS_MODE_ANY; + adapter->specific_scan_time = NXPWIFI_SPECIFIC_SCAN_CHAN_TIME; + adapter->active_scan_time = NXPWIFI_ACTIVE_SCAN_CHAN_TIME; + adapter->passive_scan_time = NXPWIFI_PASSIVE_SCAN_CHAN_TIME; + adapter->scan_chan_gap_time = NXPWIFI_DEF_SCAN_CHAN_GAP_TIME; + + adapter->scan_probes = 1; + + adapter->multiple_dtim = 1; + + /* default value in firmware will be used */ + adapter->local_listen_interval = 0; + + adapter->is_deep_sleep = false; + + adapter->delay_null_pkt = false; + adapter->delay_to_ps = 1000; + adapter->enhanced_ps_mode = PS_MODE_AUTO; + + /* Disable NULL Pkg generation by default */ + adapter->gen_null_pkt = false; + /* Disable pps/uapsd mode by default */ + adapter->pps_uapsd_mode = false; + adapter->pm_wakeup_card_req = false; + + adapter->pm_wakeup_fw_try = false; + + adapter->curr_tx_buf_size = NXPWIFI_TX_DATA_BUF_SIZE_2K; + + clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags); + adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF); + adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF; + adapter->hs_cfg.gap = HS_CFG_GAP_DEF; + adapter->hs_activated = false; + + memset(adapter->event_body, 0, sizeof(adapter->event_body)); + adapter->hw_dot_11n_dev_cap = 0; + adapter->hw_dev_mcs_support = 0; + adapter->sec_chan_offset = 0; + + nxpwifi_wmm_init(adapter); + atomic_set(&adapter->tx_hw_pending, 0); + + sleep_cfm_buf = (struct nxpwifi_opt_sleep_confirm *) + adapter->sleep_cfm->data; + memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); + sleep_cfm_buf->command = cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); + sleep_cfm_buf->result = 0; + sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); + + memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); + adapter->tx_lock_flag = false; + adapter->null_pkt_interval = 0; + adapter->fw_bands = 0; + adapter->fw_release_number = 0; + adapter->fw_cap_info = 0; + memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); + adapter->event_cause = 0; + adapter->region_code = 0; + adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; + memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + adapter->arp_filter_size = 0; + adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; + adapter->key_api_major_ver = 0; + adapter->key_api_minor_ver = 0; + eth_broadcast_addr(adapter->perm_addr); + adapter->iface_limit.sta_intf = NXPWIFI_MAX_STA_NUM; + adapter->iface_limit.uap_intf = NXPWIFI_MAX_UAP_NUM; + adapter->active_scan_triggered = false; + timer_setup(&adapter->wakeup_timer, wakeup_timer_fn, 0); + adapter->devdump_len = 0; + memset(&adapter->vdll_ctrl, 0, sizeof(adapter->vdll_ctrl)); + adapter->vdll_ctrl.skb = dev_alloc_skb(NXPWIFI_SIZE_OF_CMD_BUFFER); +} + +/* This function sets trans_start per tx_queue + */ +void nxpwifi_set_trans_start(struct net_device *dev) +{ + int i; + + for (i = 0; i < dev->num_tx_queues; i++) + txq_trans_cond_update(netdev_get_tx_queue(dev, i)); + + netif_trans_update(dev); +} + +/* This function wakes up all queues in net_device + */ +void nxpwifi_wake_up_net_dev_queue(struct net_device *netdev, + struct nxpwifi_adapter *adapter) +{ + spin_lock_bh(&adapter->queue_lock); + netif_tx_wake_all_queues(netdev); + spin_unlock_bh(&adapter->queue_lock); +} + +/* This function stops all queues in net_device + */ +void nxpwifi_stop_net_dev_queue(struct net_device *netdev, + struct nxpwifi_adapter *adapter) +{ + spin_lock_bh(&adapter->queue_lock); + netif_tx_stop_all_queues(netdev); + spin_unlock_bh(&adapter->queue_lock); +} + +/* This function invalidates the list heads. + */ +static void nxpwifi_invalidate_lists(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + s32 i, j; + + list_del(&adapter->cmd_free_q); + list_del(&adapter->cmd_pending_q); + list_del(&adapter->scan_pending_q); + + for (i = 0; i < adapter->priv_num; i++) + list_del(&adapter->bss_prio_tbl[i].bss_prio_head); + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) + list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); + list_del(&priv->tx_ba_stream_tbl_ptr); + list_del(&priv->rx_reorder_tbl_ptr); + list_del(&priv->sta_list); + } +} + +/* This function performs cleanup for adapter structure. + * + * The cleanup is done recursively, by canceling all pending + * commands, freeing the member buffers previously allocated + * (command buffers, scan table buffer, sleep confirm command + * buffer), stopping the timers and calling the cleanup routines + * for every interface. + */ +static void +nxpwifi_adapter_cleanup(struct nxpwifi_adapter *adapter) +{ + del_timer(&adapter->wakeup_timer); + nxpwifi_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait); + wake_up_interruptible(&adapter->hs_activate_wait_q); + if (adapter->vdll_ctrl.vdll_mem) { + vfree(adapter->vdll_ctrl.vdll_mem); + adapter->vdll_ctrl.vdll_mem = NULL; + adapter->vdll_ctrl.vdll_len = 0; + } + if (adapter->vdll_ctrl.skb) { + dev_kfree_skb_any(adapter->vdll_ctrl.skb); + adapter->vdll_ctrl.skb = NULL; + } +} + +void nxpwifi_free_cmd_buffers(struct nxpwifi_adapter *adapter) +{ + nxpwifi_invalidate_lists(adapter); + + /* Free command buffer */ + nxpwifi_dbg(adapter, INFO, "info: free cmd buffer\n"); + nxpwifi_free_cmd_buffer(adapter); + + if (adapter->sleep_cfm) + dev_kfree_skb_any(adapter->sleep_cfm); +} + +/* This function intializes the lock variables and + * the list heads. + */ +void nxpwifi_init_lock_list(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + s32 i, j; + + spin_lock_init(&adapter->int_lock); + spin_lock_init(&adapter->main_proc_lock); + spin_lock_init(&adapter->nxpwifi_cmd_lock); + spin_lock_init(&adapter->queue_lock); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + spin_lock_init(&priv->wmm.ra_list_spinlock); + spin_lock_init(&priv->curr_bcn_buf_lock); + spin_lock_init(&priv->sta_list_spinlock); + } + + /* Initialize cmd_free_q */ + INIT_LIST_HEAD(&adapter->cmd_free_q); + /* Initialize cmd_pending_q */ + INIT_LIST_HEAD(&adapter->cmd_pending_q); + /* Initialize scan_pending_q */ + INIT_LIST_HEAD(&adapter->scan_pending_q); + + spin_lock_init(&adapter->cmd_free_q_lock); + spin_lock_init(&adapter->cmd_pending_q_lock); + spin_lock_init(&adapter->scan_pending_q_lock); + + skb_queue_head_init(&adapter->rx_mlme_q); + skb_queue_head_init(&adapter->rx_data_q); + skb_queue_head_init(&adapter->tx_data_q); + + for (i = 0; i < adapter->priv_num; ++i) { + INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); + spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); + } + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) + INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); + INIT_LIST_HEAD(&priv->sta_list); + skb_queue_head_init(&priv->bypass_txq); + + spin_lock_init(&priv->tx_ba_stream_tbl_lock); + spin_lock_init(&priv->rx_reorder_tbl_lock); + + spin_lock_init(&priv->ack_status_lock); + idr_init(&priv->ack_status_frames); + } +} + +/* This function initializes the firmware. + * + * The following operations are performed sequentially - + * - Allocate adapter structure + * - Initialize the adapter structure + * - Initialize the private structure + * - Add BSS priority tables to the adapter structure + * - For each interface, send the init commands to firmware + * - Send the first command in command pending queue, if available + */ +int nxpwifi_init_fw(struct nxpwifi_adapter *adapter) +{ + int ret; + struct nxpwifi_private *priv; + u8 i; + bool first_sta = true; + int is_cmd_pend_q_empty; + + adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING; + + /* Allocate memory for member of adapter structure */ + ret = nxpwifi_allocate_adapter(adapter); + if (ret) + return ret; + + /* Initialize adapter structure */ + nxpwifi_init_adapter(adapter); + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + /* Initialize private structure */ + ret = nxpwifi_init_priv(priv); + if (ret) + return ret; + } + + for (i = 0; i < adapter->priv_num; i++) { + ret = nxpwifi_sta_init_cmd(adapter->priv[i], + first_sta, true); + if (ret && ret != -EINPROGRESS) + return ret; + + first_sta = false; + } + + spin_lock_bh(&adapter->cmd_pending_q_lock); + is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + if (!is_cmd_pend_q_empty) + /* Send the first command in queue and return */ + nxpwifi_main_process(adapter); + else + adapter->hw_status = NXPWIFI_HW_STATUS_READY; + + return ret; +} + +/* This function deletes the BSS priority tables. + * + * The function traverses through all the allocated BSS priority nodes + * in every BSS priority table and frees them. + */ +static void nxpwifi_delete_bss_prio_tbl(struct nxpwifi_private *priv) +{ + int i; + struct nxpwifi_adapter *adapter = priv->adapter; + struct nxpwifi_bss_prio_node *bssprio_node, *tmp_node; + struct list_head *head; + spinlock_t *lock; /* bss priority lock */ + + for (i = 0; i < adapter->priv_num; ++i) { + head = &adapter->bss_prio_tbl[i].bss_prio_head; + lock = &adapter->bss_prio_tbl[i].bss_prio_lock; + nxpwifi_dbg(adapter, INFO, + "info: delete BSS priority table,\t" + "bss_type = %d, bss_num = %d, i = %d,\t" + "head = %p\n", + priv->bss_type, priv->bss_num, i, head); + + { + spin_lock_bh(lock); + list_for_each_entry_safe(bssprio_node, tmp_node, head, + list) { + if (bssprio_node->priv == priv) { + nxpwifi_dbg(adapter, INFO, + "info: Delete\t" + "node %p, next = %p\n", + bssprio_node, tmp_node); + list_del(&bssprio_node->list); + kfree(bssprio_node); + } + } + spin_unlock_bh(lock); + } + } +} + +/* This function frees the private structure, including cleans + * up the TX and RX queues and frees the BSS priority tables. + */ +void nxpwifi_free_priv(struct nxpwifi_private *priv) +{ + nxpwifi_clean_txrx(priv); + nxpwifi_delete_bss_prio_tbl(priv); + nxpwifi_free_curr_bcn(priv); +} + +/* This function is used to shutdown the driver. + * + * The following operations are performed sequentially - + * - Check if already shut down + * - Make sure the main process has stopped + * - Clean up the Tx and Rx queues + * - Delete BSS priority tables + * - Free the adapter + * - Notify completion + */ +void +nxpwifi_shutdown_drv(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + s32 i; + struct sk_buff *skb; + + /* nxpwifi already shutdown */ + if (adapter->hw_status == NXPWIFI_HW_STATUS_NOT_READY) + return; + + /* cancel current command */ + if (adapter->curr_cmd) { + nxpwifi_dbg(adapter, WARN, + "curr_cmd is still in processing\n"); + del_timer_sync(&adapter->cmd_timer); + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + adapter->curr_cmd = NULL; + } + + /* shut down nxpwifi */ + nxpwifi_dbg(adapter, MSG, + "info: shutdown nxpwifi...\n"); + + /* Clean up Tx/Rx queues and delete BSS priority table */ + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + nxpwifi_abort_cac(priv); + nxpwifi_free_priv(priv); + } + + atomic_set(&adapter->tx_queued, 0); + while ((skb = skb_dequeue(&adapter->tx_data_q))) + nxpwifi_write_data_complete(adapter, skb, 0, 0); + + tasklet_disable(&adapter->rx_task); + + while ((skb = skb_dequeue(&adapter->rx_mlme_q))) + dev_kfree_skb_any(skb); + + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + struct nxpwifi_rxinfo *rx_info = NXPWIFI_SKB_RXCB(skb); + + priv = adapter->priv[rx_info->bss_num]; + if (priv) + priv->stats.rx_dropped++; + + dev_kfree_skb_any(skb); + } + + nxpwifi_adapter_cleanup(adapter); + + adapter->hw_status = NXPWIFI_HW_STATUS_NOT_READY; +} + +/* This function downloads the firmware to the card. + * + * The actual download is preceded by two sanity checks - + * - Check if firmware is already running + * - Check if the interface is the winner to download the firmware + * + * ...and followed by another - + * - Check if the firmware is downloaded successfully + * + * After download is successfully completed, the host interrupts are enabled. + */ +int nxpwifi_dnld_fw(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *pmfw) +{ + int ret; + u32 poll_num = 1; + + /* check if firmware is already running */ + ret = adapter->if_ops.check_fw_status(adapter, poll_num); + if (!ret) { + nxpwifi_dbg(adapter, MSG, + "WLAN FW already running! Skip FW dnld\n"); + return 0; + } + + /* check if we are the winner for downloading FW */ + if (adapter->if_ops.check_winner_status) { + adapter->winner = 0; + ret = adapter->if_ops.check_winner_status(adapter); + + poll_num = MAX_FIRMWARE_POLL_TRIES; + if (ret) { + nxpwifi_dbg(adapter, MSG, + "WLAN read winner status failed!\n"); + return ret; + } + + if (!adapter->winner) { + nxpwifi_dbg(adapter, MSG, + "WLAN is not the winner! Skip FW dnld\n"); + goto poll_fw; + } + } + + if (pmfw) { + /* Download firmware with helper */ + ret = adapter->if_ops.prog_fw(adapter, pmfw); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "prog_fw failed ret=%#x\n", ret); + return ret; + } + } + +poll_fw: + /* Check if the firmware is downloaded successfully or not */ + ret = adapter->if_ops.check_fw_status(adapter, poll_num); + if (ret) + nxpwifi_dbg(adapter, ERROR, + "FW failed to be active in time\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(nxpwifi_dnld_fw); From patchwork Mon Sep 30 06:36:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815435 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2044.outbound.protection.outlook.com [40.107.21.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AB7F7185B68; Mon, 30 Sep 2024 06:38:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.44 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678330; cv=fail; b=VMuJjGvuf2g/2WeNIqwas4Dn5GpzRRQE17cfhDzOPJ37S2UZ37ruo9uo+D8FhNCr5FftrciyitgcxwYkH9Gl8e6ZymrvZJMt3f8hachqShoYtB+ZOLPhWOJwtqnpfPUPbcN5+cUCpYukt2lbOa5tydG/qDODL4nn/vk87shkJP0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678330; c=relaxed/simple; bh=A3qr9mVfXPCTBCZAGlHzSxQNgJgTlmapZCTTaMoWEJY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=Vm3oVfimk8s1Hvzn5S2QaWLNBBzSkEBTX+HQoOUJ1bfCkA5ShI0FeVj9DuANm50YL7XQCMFCX/3R4u7wDJUWeTzD9G2hpbEb/6tyHcE4rK7YG5yQWMaUBfj60qGexeBnuPp81u0uvDhWLmXlEvGJ7b8qN7OoF2I9i5+mhetWBwU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=JJ/Cg27p; arc=fail smtp.client-ip=40.107.21.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="JJ/Cg27p" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=pqsnBThvEUeFd2nivuIE0cotHzZclXmClN3mRXccprVqWt76c7DY9iPVMlsSEYgQu82Q4xARmN7gAEPd25L8bZCZr9oZDUV5k5xSCyhZajrzKIYrpEzSX+ccMwYvlpjRtlWrGa5p3z3PP8eUrkyrADiB+KMNX+KlhYzG2JXd5rFkR6L4doolkU08qEql78Kddcgjxunk6HoPejt757ekQiazcQKvOt+hArG7jwS0S3A2as6d0i5vac4xnMr2oAG+OfV3Hg5BxYR4igQPZzzEVqe6/giUl53516JH+3xy60O03LswjDHOMBhyVf4FV+JcDkb6hXAUd9wtD69QzQX08A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ZygSVhNTzduE8v66tIgkt5JbxBJdw0Ggkm6Uo5RNHuo=; b=yPqur2XW2TTvOpHgu0hu0HYJ8UIdiEZcuuzS9LYSSk0blAZ1DiBWDsKA2YEtq08buVCy8lRTZedW5D+BXd7HoFpOmj9Y8oSLWpUCaGI9sF7DZy2Et2Q7YJMaLxWzlXt8kajVRIprszJiHn76Q4zH8I4XWSs/SKwV8ubMcgTalQNYTXdlXFKxCMXn20LGAKr4H4MW4p5r8dklj/bAuSqS1qXk0thgKUUSe/0OUJUxbD+v9cRl2hbpzICBFq6GhdkWDJJogy5/WvSFQ4vuG2scva/J7etJAMudV7zFKj//drSK+Yc1RQQyl7cJjIPy0dMqLSlJ8OMeeTsrfj7MABOngg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ZygSVhNTzduE8v66tIgkt5JbxBJdw0Ggkm6Uo5RNHuo=; b=JJ/Cg27pv3JkXFBXTS5Pqxfhes/n1/BjpGpBUJfNTxQnh3LnfEb+4akuAqUlYcKrF4ryM20M/EIZZAUmeNjSF5uMRXwhgfnwj+8aihodC8xtKOWTU9lc1s8wXOfcqAaeDoAuEVfpjzrx169MnkxlEtXv96uM/B3eVdsx/RrZ3+EdN/5G5tFLzfkFv0tTDHsZDDoSLMiK94kbPpeWsuo7/XxS0cB22dsdzRDGxfxBJAd6kNPbpLeURgnD/QcDLTianKeSfCOa09+KbFqTaG2Kz+ll1psJkGrPyOppj+Mk6mQM8NxdGR6/P0MwupbKiJFBTRLTXdQ8wQEK20BKIyB4pw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:24 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:24 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 18/22] wifi: nxpwifi: add core files Date: Mon, 30 Sep 2024 14:36:57 +0800 Message-Id: <20240930063701.2566520-19-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 6dce7a76-8d58-4e73-2201-08dce11a7b7a X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: AAhEIsKpdvPsDpR5rcf1Ec1YypP19+3p7n5ZzwvXIoaQWRWhHvE5L7Lf7fb864Ff74roSql/eYtlDPbwv/BsFAuF1sbUCVaKHSjFFjiw7euOmhypOrB0yQpT6W9Pcthes7QXMF+BPuI+9OPEySN7TM5E7AC0wi/apwqfc6AjlZaWDFs7xEEN9vEF49Qu6lnO9qHZVBh/ThJ3sNgJO+J7deFUynZOPZYkS1J36rAKW9g8O7T5++dgLjVIa32K5zxw09gWRTKNFRWvU6GN0n1CsJ97B9pXU03kFU1dLAJz/6Ybw2mwDDraEuugJ+3pP+yp5gTvyK24k1T2Q8Mb4LvXf1ZAM7mRbplriFgd7FwXZSh+XlOZniwaYD6GwUmBIvdTFRWycTn+e6CS0+HLoP1m78i9JMIApjySteCXrQ0DcBu4HrvyH+g9eL44NU/FyPEL0zAC90uvO08mR17rQ4YcqLHwktFf6VEfKVCMS5De+gXzwIbI7MR5rdfbyJFuiRWHlI/3KJ5ECFwjdOPe1X4C4wjDPAGesgBZzE4nWFuHs+gfeCKC9W9+SslJ5/jMv96zesgInCYBBTN1K5qWWjrtunDp7VQxLr5fKLnx/395CNtWbF24L8csOd8xpK/p3HWBxBfYBbup/VIEKby7jTrEUD4SgJTtq0bQ/BqwBQ/cOVtnGY/52UI8Cj3kt8/1gOvCm4fYM4cFaggHtAplFfq8m1avVSfOLtDSvmggQBGz0Nbl+dzi8zdvdC29GArIdobkaK8Tcm9kHu91QZhoKmdqIGTg5WAiq/DOpCw0zMszwq1cmNqjHlLUg2lDDrqBm0Ay4rubwTdC3E5rjz/D3JIBJz34X4iLObzyQC9NjwfoxEQH/nD2HJKYRIe42WQ1Nf4heWst6dYmns1yWEQEbnMOe7NXHFnPbFZrJja4MpOps1h6gLaCZBz8GEWwG/QGfMcWVnRZFwLe4WwJsBNb4MoM+PMhbzSIpJm83jGC/Nk7xNhCTsxlpCmbsLvBzp8P6T/Lob3L1N6DL6u+PVOIrS5c3NusA6hDG/lEz6eqLSyYFhTc+ulI+j0EKjGVbv572wtc/wdH4mg5AevexjbF3ftYPwPZaQVDFuV83mCTaqK0uU6Y5gApioMmSbv3c0K4mhhZX0u+HD2QXS1u20UsJrBSScmRM+WVX4zNLCUxLKvbhSSH4zYPU09SeibjOmo32KhxTxlGA98YWGsNa3meM4k8GbZkIR4xsr0jBxM8ugEmE5maFtRmDx/qawqBjmsfj0RvIzPg7scmtH+OQF6SPcVEa7A4HOYpc7bu/YhCP/58uFkaZlKM+6Eccxkd6k8Q7glLiNegRpg1FblX8MzP1ZZ9LcgN9iL6rZ3zwsNv/WnKCADotdq1ssb3wBG4MEv4EYbzmvvT4C9VzNvrQDusdlg0uQ== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: BqkAYSvmCqPVgrssi6L07cVrfL2LJlPdGuLygaswABLafK+usI/AFZxMiHaq9rphPwAvjy1krYjqu0e3WUgGd1zBs/waEiRlICCR3ajRkOkqaMHhBdctAP+CkupiBVaoQ1hvZ6SQIPzP0SmRKhbgVl5Gr5wQnv8DNIAImy9e1liZQPnUwFsNclM7hAXeZthoDVhSO7HwZ6HqRUehE0C5w/FMie5bxuM2LBU4LfQ7BhRUcTntJRInCDyR9AxMsfbLFIScnFQaxT8sN2PQxTOlFvgNqofit1SZvqnvRdWgRIsh2JUBBm1iwzDycovZ9CRI+AP48j2ohp8JgaJPhorgeTGw30fba/75vsEuLXRwj/dQhsX6kHeiEsZgvvCyMafLh0DNf8uJaMhQQbD0IjVettgpChAXnRTEvawKbZ9KYmV/lh7uy7N1Ewpy6j0LwTjd2X0UtUZCM4e2T+hfv91MmBsQ4D0HlSzH7vWx0M3iWYovWbTyeCDtT3kmnjSvxRVOcvP16hEMdI0bUsQIJMFt1NaLkOmd/UyIyFsU5vzjwnoTx67X+noNqWwpHyrTfIJ++Ta2o1kICzoTdWUPvghLBytOFLjWGCsdV2x+BdYDj3wgKijwb/+si8/t689Z688FYBf7K3WOjKP0SqNE7oTGaa1vIPXkQ//HPzIGA4n2+YaXGUjsvyssvtRDk+Sx2OnNoY5Yx0Dkzl2q8AWkspaUDIxkitLM+g5DCiwSNeexrVMDvaobQUmjgr16hHpIiIuf2D+4P2cSyYrpl6qnCC5kSfG51grgrjmiyAVnD0OGhmo4mQhwu0s3C1KKpZVzpl3Im5CQxAHWAZYQdNnPOF4DRqH5tUzBaxj3yTHPPXiJiP/ABA8LaY/RwEVQcQo/AC3kXFSw3zuRp1SEpJTh9IcA1K0CHWe78Z6h305o9p/1WgHwX+kOyVVQCq+2ZNB5ekV2c5vL1ABCVshgC8BnDp9/AwRdFOyIp5YCkh8lI/bOda342u0clYl8NFpmq+HLgv/rxKaZmq8nmi/BY9nLBgEV2xSHJTASTI0mCQWqg5hngPNmgaLfH6vk8uv6jfWNgX/HwqZcy1CfArJFpHH/bp1cQyETnE/xuVZvLhJKZM0z4cXMFmgEbLuOaA8ouGGGJvQRQT3104ReRFtGxEzqQtWwJCC7g+7K78eoXqTWdmPEru6NJ0AvZmh+GAaJK52ANcumPfIUln6sJxFD1AjEr+bsAJWcIM5Na4ckQnvU5btU0xBLpqfaVukQ32lNxbzbdOYTyhKRZg+wreaIvmn76sJaRdohRIBi3W3G1/KmC4SEb6IFzu7i73Q8mdHeEuj0ppCJM0SMHu4LY5pvm1U4OTfwDbPGTsYdMbmv5Y5dG4lBZIjZ1dj5J22xgvhHpvGw7AUpGbzhAh+cracgBZynXneVQexKD2WOQW+KOuIHFUQZ7C/E3z7841/qMTeluts+SAPUncIERxBdp5PMHs7ktzq/mcG5DFGHQqXCqmr4eOF2yVhRYwTZaEHdU3VeAixj99S4/eT3WS8EHlnXdYQBmquT/A86OAPbnbTX8yDvFJDJ2SrOGUdcojeTyhz/XLEE73rl X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6dce7a76-8d58-4e73-2201-08dce11a7b7a X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:24.4680 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: J07u+M0PNmhyD9B/ZpTbD0xOEbTR/lwo3SdIjOgycKWt3VmFPiVqSA9QZVldcT4T66CcPsYdNaI6CqVFjQ1kZQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Nxpwifi is a two modules driver: one for core layer and another one is for bus driver. Core layer supports needed functions to allow bus driver to create an working instance for the bus driver. Struct nxpwifi_if_ops is defined to allow bus driver to hook callback functions to core layer for the operations of bus related functions. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/main.c | 1649 +++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/main.h | 1477 ++++++++++++++++++++ 2 files changed, 3126 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/main.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/main.h diff --git a/drivers/net/wireless/nxp/nxpwifi/main.c b/drivers/net/wireless/nxp/nxpwifi/main.c new file mode 100644 index 000000000000..6a26a5248b3a --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/main.c @@ -0,0 +1,1649 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: major functions + * + * Copyright 2011-2024 NXP + */ + +#include + +#include "main.h" +#include "cmdevt.h" +#include "wmm.h" +#include "cfg80211.h" +#include "11n.h" + +#define VERSION "1.0" + +static unsigned int debug_mask = NXPWIFI_DEFAULT_DEBUG_MASK; + +char driver_version[] = "nxpwifi " VERSION " (%s) "; + +const u16 nxpwifi_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + +/* This function registers the device and performs all the necessary + * initializations. + * + * The following initialization operations are performed - + * - Allocate adapter structure + * - Save interface specific operations table in adapter + * - Call interface specific initialization routine + * - Allocate private structures + * - Set default adapter structure parameters + * - Initialize locks + * + * In case of any errors during initialization, this function also ensures + * proper cleanup before exiting. + */ +static struct nxpwifi_adapter *nxpwifi_register(void *card, struct device *dev, + struct nxpwifi_if_ops *if_ops) +{ + struct nxpwifi_adapter *adapter; + int ret = 0; + int i; + + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); + if (!adapter) + return ERR_PTR(-ENOMEM); + + adapter->dev = dev; + adapter->card = card; + + /* Save interface specific operations in adapter */ + memmove(&adapter->if_ops, if_ops, sizeof(struct nxpwifi_if_ops)); + adapter->debug_mask = debug_mask; + + /* card specific initialization has been deferred until now .. */ + if (adapter->if_ops.init_if) { + ret = adapter->if_ops.init_if(adapter); + if (ret) + goto error; + } + + adapter->priv_num = 0; + + for (i = 0; i < NXPWIFI_MAX_BSS_NUM; i++) { + /* Allocate memory for private structure */ + adapter->priv[i] = + kzalloc(sizeof(struct nxpwifi_private), GFP_KERNEL); + if (!adapter->priv[i]) { + ret = -ENOMEM; + goto error; + } + + adapter->priv[i]->adapter = adapter; + adapter->priv_num++; + } + nxpwifi_init_lock_list(adapter); + + timer_setup(&adapter->cmd_timer, nxpwifi_cmd_timeout_func, 0); + + if (ret) + return ERR_PTR(ret); + else + return adapter; + +error: + nxpwifi_dbg(adapter, ERROR, + "info: leave %s with error\n", __func__); + + for (i = 0; i < adapter->priv_num; i++) + kfree(adapter->priv[i]); + + kfree(adapter); + + return ERR_PTR(ret); +} + +/* This function unregisters the device and performs all the necessary + * cleanups. + * + * The following cleanup operations are performed - + * - Free the timers + * - Free beacon buffers + * - Free private structures + * - Free adapter structure + */ +static void nxpwifi_unregister(struct nxpwifi_adapter *adapter) +{ + s32 i; + + if (adapter->if_ops.cleanup_if) + adapter->if_ops.cleanup_if(adapter); + + del_timer_sync(&adapter->cmd_timer); + + /* Free private structures */ + for (i = 0; i < adapter->priv_num; i++) { + nxpwifi_free_curr_bcn(adapter->priv[i]); + kfree(adapter->priv[i]); + } + + if (adapter->nd_info) { + for (i = 0 ; i < adapter->nd_info->n_matches ; i++) + kfree(adapter->nd_info->matches[i]); + kfree(adapter->nd_info); + adapter->nd_info = NULL; + } + + kfree(adapter->regd); + + kfree(adapter); +} + +static void nxpwifi_process_rx(struct nxpwifi_adapter *adapter) +{ + struct sk_buff *skb; + struct nxpwifi_rxinfo *rx_info; + + /* Check for Rx data */ + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + rx_info = NXPWIFI_SKB_RXCB(skb); + if (rx_info->buf_type == NXPWIFI_TYPE_AGGR_DATA) { + if (adapter->if_ops.deaggr_pkt) + adapter->if_ops.deaggr_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } else { + nxpwifi_handle_rx_packet(adapter, skb); + } + } +} + +static void maybe_quirk_fw_disable_ds(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + struct nxpwifi_ver_ext ver_ext; + + if (test_and_set_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &adapter->work_flags)) + return; + + memset(&ver_ext, 0, sizeof(ver_ext)); + ver_ext.version_str_sel = 1; + if (nxpwifi_send_cmd(priv, HOST_CMD_VERSION_EXT, + HOST_ACT_GEN_GET, 0, &ver_ext, false)) { + nxpwifi_dbg(priv->adapter, MSG, + "Checking hardware revision failed.\n"); + } +} + +/* The main process. + * + * This function is the main procedure of the driver and handles various driver + * operations. It runs in a loop and provides the core functionalities. + * + * The main responsibilities of this function are - + * - Ensure concurrency control + * - Handle pending interrupts and call interrupt handlers + * - Wake up the card if required + * - Handle command responses and call response handlers + * - Handle events and call event handlers + * - Execute pending commands + * - Transmit pending data packets + */ +void nxpwifi_main_process(struct nxpwifi_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + + /* Check if already processing */ + if (adapter->nxpwifi_processing || adapter->main_locked) { + adapter->more_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + return; + } + + adapter->nxpwifi_processing = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + +process_start: + do { + if (adapter->hw_status == NXPWIFI_HW_STATUS_NOT_READY) + break; + + /* Handle pending interrupt if any */ + if (adapter->int_status) { + if (adapter->hs_activated) + nxpwifi_process_hs_config(adapter); + if (adapter->if_ops.process_int_status) + adapter->if_ops.process_int_status(adapter); + } + + /* Need to wake up the card ? */ + if (adapter->ps_state == PS_STATE_SLEEP && + (adapter->pm_wakeup_card_req && + !adapter->pm_wakeup_fw_try) && + (is_command_pending(adapter) || + !skb_queue_empty(&adapter->tx_data_q) || + !nxpwifi_bypass_txlist_empty(adapter) || + !nxpwifi_wmm_lists_empty(adapter))) { + adapter->pm_wakeup_fw_try = true; + mod_timer(&adapter->wakeup_timer, jiffies + (HZ * 3)); + adapter->if_ops.wakeup(adapter); + continue; + } + + if (IS_CARD_RX_RCVD(adapter)) { + adapter->data_received = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + if (adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + } else { + /* We have tried to wakeup the card already */ + if (adapter->pm_wakeup_fw_try) + break; + if (adapter->ps_state == PS_STATE_PRE_SLEEP) + nxpwifi_check_ps_cond(adapter); + + if (adapter->ps_state != PS_STATE_AWAKE) + break; + if (adapter->tx_lock_flag) + break; + + if ((!adapter->scan_chan_gap_enabled && + adapter->scan_processing) || adapter->data_sent || + (nxpwifi_wmm_lists_empty(adapter) && + nxpwifi_bypass_txlist_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { + if (adapter->cmd_sent || adapter->curr_cmd || + (!is_command_pending(adapter))) + break; + } + } + + /* Check for event */ + if (adapter->event_received) { + adapter->event_received = false; + nxpwifi_process_event(adapter); + } + + /* Check for Cmd Resp */ + if (adapter->cmd_resp_received) { + adapter->cmd_resp_received = false; + nxpwifi_process_cmdresp(adapter); + + /* call nxpwifi back when init_fw is done */ + if (adapter->hw_status == NXPWIFI_HW_STATUS_INIT_DONE) { + adapter->hw_status = NXPWIFI_HW_STATUS_READY; + nxpwifi_init_fw_complete(adapter); + maybe_quirk_fw_disable_ds(adapter); + } + } + + /* Check if we need to confirm Sleep Request + * received previously + */ + if (adapter->ps_state == PS_STATE_PRE_SLEEP) + nxpwifi_check_ps_cond(adapter); + + /* * The ps_state may have been changed during processing of + * Sleep Request event. + */ + if (adapter->ps_state == PS_STATE_SLEEP || + adapter->ps_state == PS_STATE_PRE_SLEEP || + adapter->ps_state == PS_STATE_SLEEP_CFM) { + continue; + } + + if (adapter->tx_lock_flag) + continue; + + if (!adapter->cmd_sent && + adapter->vdll_ctrl.pending_block) { + struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl; + + nxpwifi_download_vdll_block(adapter, ctrl->pending_block, + ctrl->pending_block_len); + ctrl->pending_block = NULL; + } + + if (!adapter->cmd_sent && !adapter->curr_cmd) { + if (nxpwifi_exec_next_cmd(adapter)) + break; + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && + !skb_queue_empty(&adapter->tx_data_q)) { + if (adapter->hs_activated_manually) { + nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY), + NXPWIFI_ASYNC_CMD); + adapter->hs_activated_manually = false; + } + + nxpwifi_process_tx_queue(adapter); + if (adapter->hs_activated) { + clear_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + nxpwifi_hs_activated_event + (nxpwifi_get_priv + (adapter, NXPWIFI_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && + !nxpwifi_bypass_txlist_empty(adapter)) { + if (adapter->hs_activated_manually) { + nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY), + NXPWIFI_ASYNC_CMD); + adapter->hs_activated_manually = false; + } + + nxpwifi_process_bypass_tx(adapter); + if (adapter->hs_activated) { + clear_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + nxpwifi_hs_activated_event + (nxpwifi_get_priv + (adapter, NXPWIFI_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && !nxpwifi_wmm_lists_empty(adapter)) { + if (adapter->hs_activated_manually) { + nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY), + NXPWIFI_ASYNC_CMD); + adapter->hs_activated_manually = false; + } + + nxpwifi_wmm_process_tx(adapter); + if (adapter->hs_activated) { + clear_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + nxpwifi_hs_activated_event + (nxpwifi_get_priv + (adapter, NXPWIFI_BSS_ROLE_ANY), + false); + } + } + + if (adapter->delay_null_pkt && !adapter->cmd_sent && + !adapter->curr_cmd && !is_command_pending(adapter) && + (nxpwifi_wmm_lists_empty(adapter) && + nxpwifi_bypass_txlist_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { + if (!nxpwifi_send_null_packet + (nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA), + NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET | + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET)) { + adapter->delay_null_pkt = false; + adapter->ps_state = PS_STATE_SLEEP; + } + break; + } + } while (true); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->more_task_flag) { + adapter->more_task_flag = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + goto process_start; + } + adapter->nxpwifi_processing = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); +} +EXPORT_SYMBOL_GPL(nxpwifi_main_process); + +/* This function frees the adapter structure. + * + * Additionally, this closes the netlink socket, frees the timers + * and private structures. + */ +static void nxpwifi_free_adapter(struct nxpwifi_adapter *adapter) +{ + if (!adapter) { + pr_err("%s: adapter is NULL\n", __func__); + return; + } + + nxpwifi_unregister(adapter); + pr_debug("info: %s: free adapter\n", __func__); +} + +/* This function cancels all works in the queue and destroys + * the main workqueue. + */ +static void nxpwifi_terminate_workqueue(struct nxpwifi_adapter *adapter) +{ + if (adapter->workqueue) { + destroy_workqueue(adapter->workqueue); + adapter->workqueue = NULL; + } +} + +/* This function gets firmware and initializes it. + * + * The main initialization steps followed are - + * - Download the correct firmware to card + * - Issue the init commands to firmware + */ +static int _nxpwifi_fw_dpc(const struct firmware *firmware, void *context) +{ + int ret = 0; + char fmt[64]; + struct nxpwifi_adapter *adapter = context; + struct nxpwifi_fw_image fw; + bool init_failed = false; + struct wireless_dev *wdev; + struct completion *fw_done = adapter->fw_done; + + if (!firmware) { + nxpwifi_dbg(adapter, ERROR, + "Failed to get firmware %s\n", adapter->fw_name); + ret = -EINVAL; + goto err_dnld_fw; + } + + memset(&fw, 0, sizeof(struct nxpwifi_fw_image)); + adapter->firmware = firmware; + fw.fw_buf = (u8 *)adapter->firmware->data; + fw.fw_len = adapter->firmware->size; + + if (adapter->if_ops.dnld_fw) + ret = adapter->if_ops.dnld_fw(adapter, &fw); + else + ret = nxpwifi_dnld_fw(adapter, &fw); + + if (ret) + goto err_dnld_fw; + + nxpwifi_dbg(adapter, MSG, "WLAN FW is active\n"); + + /* enable host interrupt after fw dnld is successful */ + if (adapter->if_ops.enable_int) { + ret = adapter->if_ops.enable_int(adapter); + if (ret) + goto err_dnld_fw; + } + + adapter->init_wait_q_woken = false; + ret = nxpwifi_init_fw(adapter); + if (ret != -EINPROGRESS) { + goto err_init_fw; + } else if (!ret) { + adapter->hw_status = NXPWIFI_HW_STATUS_READY; + goto done; + } + /* Wait for nxpwifi_init to complete */ + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + if (adapter->hw_status != NXPWIFI_HW_STATUS_READY) + goto err_init_fw; + + if (!adapter->wiphy) { + if (nxpwifi_register_cfg80211(adapter)) { + nxpwifi_dbg(adapter, ERROR, + "cannot register with cfg80211\n"); + goto err_init_fw; + } + } + + if (nxpwifi_init_channel_scan_gap(adapter)) { + nxpwifi_dbg(adapter, ERROR, + "could not init channel stats table\n"); + goto err_init_chan_scan; + } + + rtnl_lock(); + wiphy_lock(adapter->wiphy); + /* Create station interface by default */ + wdev = nxpwifi_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, + NL80211_IFTYPE_STATION, NULL); + if (IS_ERR(wdev)) { + nxpwifi_dbg(adapter, ERROR, + "cannot create default STA interface\n"); + wiphy_unlock(adapter->wiphy); + rtnl_unlock(); + goto err_add_intf; + } + + wdev = nxpwifi_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, + NL80211_IFTYPE_AP, NULL); + if (IS_ERR(wdev)) { + nxpwifi_dbg(adapter, ERROR, + "cannot create AP interface\n"); + wiphy_unlock(adapter->wiphy); + rtnl_unlock(); + goto err_add_intf; + } + + wiphy_unlock(adapter->wiphy); + rtnl_unlock(); + + nxpwifi_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); + nxpwifi_dbg(adapter, MSG, "driver_version = %s\n", fmt); + adapter->is_up = true; + goto done; + +err_add_intf: + vfree(adapter->chan_stats); +err_init_chan_scan: + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); +err_init_fw: + if (adapter->if_ops.disable_int) + adapter->if_ops.disable_int(adapter); +err_dnld_fw: + nxpwifi_dbg(adapter, ERROR, + "info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + + set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + tasklet_kill(&adapter->rx_task); + nxpwifi_terminate_workqueue(adapter); + + if (adapter->hw_status == NXPWIFI_HW_STATUS_READY) { + pr_debug("info: %s: shutdown nxpwifi\n", __func__); + nxpwifi_shutdown_drv(adapter); + nxpwifi_free_cmd_buffers(adapter); + } + + init_failed = true; +done: + if (adapter->cal_data) { + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } + if (adapter->firmware) { + release_firmware(adapter->firmware); + adapter->firmware = NULL; + } + if (init_failed) { + if (adapter->irq_wakeup >= 0) + device_init_wakeup(adapter->dev, false); + nxpwifi_free_adapter(adapter); + } + /* Tell all current and future waiters we're finished */ + complete_all(fw_done); + + return ret; +} + +static void nxpwifi_fw_dpc(const struct firmware *firmware, void *context) +{ + _nxpwifi_fw_dpc(firmware, context); +} + +/* This function gets the firmware and (if called asynchronously) kicks off the + * HW init when done. + */ +static int nxpwifi_init_hw_fw(struct nxpwifi_adapter *adapter, + bool req_fw_nowait) +{ + int ret; + + if (req_fw_nowait) { + ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, + adapter->dev, GFP_KERNEL, adapter, + nxpwifi_fw_dpc); + } else { + ret = request_firmware(&adapter->firmware, + adapter->fw_name, + adapter->dev); + } + + if (ret < 0) + nxpwifi_dbg(adapter, ERROR, "request_firmware%s error %d\n", + req_fw_nowait ? "_nowait" : "", ret); + return ret; +} + +/* CFG802.11 network device handler for open. + * + * Starts the data queue. + */ +static int +nxpwifi_open(struct net_device *dev) +{ + netif_carrier_off(dev); + + return 0; +} + +/* CFG802.11 network device handler for close. + */ +static int +nxpwifi_close(struct net_device *dev) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + + if (priv->scan_request) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + nxpwifi_dbg(priv->adapter, INFO, + "aborting scan on ndo_stop\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request = NULL; + priv->scan_aborting = true; + } + + if (priv->sched_scanning) { + nxpwifi_dbg(priv->adapter, INFO, + "aborting bgscan on ndo_stop\n"); + nxpwifi_stop_bg_scan(priv); + cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0); + } + + return 0; +} + +static bool +nxpwifi_bypass_tx_queue(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + + if (eth_hdr->h_proto == htons(ETH_P_PAE) || + nxpwifi_is_skb_mgmt_frame(skb)) { + nxpwifi_dbg(priv->adapter, DATA, + "bypass txqueue; eth type %#x, mgmt %d\n", + ntohs(eth_hdr->h_proto), + nxpwifi_is_skb_mgmt_frame(skb)); + if (eth_hdr->h_proto == htons(ETH_P_PAE)) + nxpwifi_dbg(priv->adapter, MSG, + "key: send EAPOL to %pM\n", + eth_hdr->h_dest); + return true; + } + + return false; +} + +/* Add buffer into wmm tx queue and queue work to transmit it. + */ +void nxpwifi_queue_tx_pkt(struct nxpwifi_private *priv, struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter = priv->adapter; + struct netdev_queue *txq; + int index = nxpwifi_1d_to_wmm_queue[skb->priority]; + + if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (!netif_tx_queue_stopped(txq)) { + netif_tx_stop_queue(txq); + nxpwifi_dbg(adapter, DATA, + "stop queue: %d\n", index); + } + } + + if (nxpwifi_bypass_tx_queue(priv, skb)) { + atomic_inc(&adapter->tx_pending); + atomic_inc(&adapter->bypass_tx_pending); + nxpwifi_wmm_add_buf_bypass_txqueue(priv, skb); + } else { + atomic_inc(&adapter->tx_pending); + nxpwifi_wmm_add_buf_txqueue(priv, skb); + } + + nxpwifi_queue_work(adapter, &adapter->main_work); +} + +struct sk_buff * +nxpwifi_clone_skb_for_tx_status(struct nxpwifi_private *priv, + struct sk_buff *skb, u8 flag, u64 *cookie) +{ + struct sk_buff *orig_skb = skb; + struct nxpwifi_txinfo *tx_info, *orig_tx_info; + + skb = skb_clone(skb, GFP_ATOMIC); + if (skb) { + int id; + + spin_lock_bh(&priv->ack_status_lock); + id = idr_alloc(&priv->ack_status_frames, orig_skb, + 1, 0x10, GFP_ATOMIC); + spin_unlock_bh(&priv->ack_status_lock); + + if (id >= 0) { + tx_info = NXPWIFI_SKB_TXCB(skb); + tx_info->ack_frame_id = id; + tx_info->flags |= flag; + orig_tx_info = NXPWIFI_SKB_TXCB(orig_skb); + orig_tx_info->ack_frame_id = id; + orig_tx_info->flags |= flag; + + if (flag == NXPWIFI_BUF_FLAG_ACTION_TX_STATUS && cookie) + orig_tx_info->cookie = *cookie; + + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb = orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb = orig_skb; + } + + return skb; +} + +/* CFG802.11 network device handler for data transmission. + */ +static netdev_tx_t +nxpwifi_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct sk_buff *new_skb; + struct nxpwifi_txinfo *tx_info; + bool multicast; + + nxpwifi_dbg(priv->adapter, DATA, + "data: %lu BSS(%d-%d): Data <= kernel\n", + jiffies, priv->bss_type, priv->bss_num); + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &priv->adapter->work_flags)) { + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + if (!skb->len || skb->len > ETH_FRAME_LEN) { + nxpwifi_dbg(priv->adapter, ERROR, + "Tx: bad skb len %d\n", skb->len); + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) { + nxpwifi_dbg(priv->adapter, DATA, + "data: Tx: insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = + skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN); + if (unlikely(!new_skb)) { + nxpwifi_dbg(priv->adapter, ERROR, + "Tx: cannot alloca new_skb\n"); + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + kfree_skb(skb); + skb = new_skb; + nxpwifi_dbg(priv->adapter, INFO, + "info: new skb headroomd %d\n", + skb_headroom(skb)); + } + + tx_info = NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = skb->len; + + multicast = is_multicast_ether_addr(skb->data); + + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && + priv->adapter->fw_api_ver == NXPWIFI_FW_V15)) + skb = nxpwifi_clone_skb_for_tx_status(priv, + skb, + NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS, NULL); + + /* Record the current time the packet was queued; used to + * determine the amount of time the packet was queued in + * the driver before it was sent to the firmware. + * The delay is then sent along with the packet to the + * firmware for aggregate delay calculation for stats and + * MSDU lifetime expiry. + */ + __net_timestamp(skb); + + nxpwifi_queue_tx_pkt(priv, skb); + + return 0; +} + +int nxpwifi_set_mac_address(struct nxpwifi_private *priv, + struct net_device *dev, bool external, + u8 *new_mac) +{ + int ret; + u64 mac_addr, old_mac_addr; + + old_mac_addr = ether_addr_to_u64(priv->curr_addr); + + if (external) { + mac_addr = ether_addr_to_u64(new_mac); + } else { + /* Internal mac address change */ + if (priv->bss_type == NXPWIFI_BSS_TYPE_ANY) + return -EOPNOTSUPP; + + mac_addr = old_mac_addr; + + if (priv->adapter->priv[0] != priv) { + /* Set mac address based on bss_type/bss_num */ + mac_addr ^= BIT_ULL(priv->bss_type + 8); + mac_addr += priv->bss_num; + } + } + + u64_to_ether_addr(mac_addr, priv->curr_addr); + + /* Send request to firmware */ + ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_MAC_ADDRESS, + HOST_ACT_GEN_SET, 0, NULL, true); + + if (ret) { + u64_to_ether_addr(old_mac_addr, priv->curr_addr); + nxpwifi_dbg(priv->adapter, ERROR, + "set mac address failed: ret=%d\n", ret); + return ret; + } + + eth_hw_addr_set(dev, priv->curr_addr); + return 0; +} + +/* CFG802.11 network device handler for setting MAC address. + */ +static int +nxpwifi_ndo_set_mac_address(struct net_device *dev, void *addr) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct sockaddr *hw_addr = addr; + + return nxpwifi_set_mac_address(priv, dev, true, hw_addr->sa_data); +} + +/* CFG802.11 network device handler for setting multicast list. + */ +static void nxpwifi_set_multicast_list(struct net_device *dev) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + struct nxpwifi_multicast_list mcast_list; + + if (dev->flags & IFF_PROMISC) { + mcast_list.mode = NXPWIFI_PROMISC_MODE; + } else if (dev->flags & IFF_ALLMULTI || + netdev_mc_count(dev) > NXPWIFI_MAX_MULTICAST_LIST_SIZE) { + mcast_list.mode = NXPWIFI_ALL_MULTI_MODE; + } else { + mcast_list.mode = NXPWIFI_MULTICAST_MODE; + mcast_list.num_multicast_addr = + nxpwifi_copy_mcast_addr(&mcast_list, dev); + } + nxpwifi_request_set_multicast_list(priv, &mcast_list); +} + +/* CFG802.11 network device handler for transmission timeout. + */ +static void +nxpwifi_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + + priv->num_tx_timeout++; + priv->tx_timeout_cnt++; + nxpwifi_dbg(priv->adapter, ERROR, + "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n", + jiffies, priv->tx_timeout_cnt, priv->bss_type, + priv->bss_num); + nxpwifi_set_trans_start(dev); + + if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD && + priv->adapter->if_ops.card_reset) { + nxpwifi_dbg(priv->adapter, ERROR, + "tx_timeout_cnt exceeds threshold.\t" + "Triggering card reset!\n"); + priv->adapter->if_ops.card_reset(priv->adapter); + } +} + +void nxpwifi_upload_device_dump(struct nxpwifi_adapter *adapter) +{ + /* Dump all the memory data into single file, a userspace script will + * be used to split all the memory data to multiple files + */ + nxpwifi_dbg(adapter, MSG, + "== nxpwifi dump information to /sys/class/devcoredump start\n"); + dev_coredumpv(adapter->dev, adapter->devdump_data, adapter->devdump_len, + GFP_KERNEL); + nxpwifi_dbg(adapter, MSG, + "== nxpwifi dump information to /sys/class/devcoredump end\n"); + + /* Device dump data will be freed in device coredump release function + * after 5 min. Here reset adapter->devdump_data and ->devdump_len + * to avoid it been accidentally reused. + */ + adapter->devdump_data = NULL; + adapter->devdump_len = 0; +} +EXPORT_SYMBOL_GPL(nxpwifi_upload_device_dump); + +void nxpwifi_drv_info_dump(struct nxpwifi_adapter *adapter) +{ + char *p; + char drv_version[64]; + struct sdio_mmc_card *sdio_card; + struct nxpwifi_private *priv; + int i, idx; + struct netdev_queue *txq; + struct nxpwifi_debug_info *debug_info; + + nxpwifi_dbg(adapter, MSG, "===nxpwifi driverinfo dump start===\n"); + + p = adapter->devdump_data; + strscpy(p, "========Start dump driverinfo========\n", NXPWIFI_FW_DUMP_SIZE); + p += strlen("========Start dump driverinfo========\n"); + p += sprintf(p, "driver_name = "); + p += sprintf(p, "\"nxpwifi\"\n"); + + nxpwifi_drv_get_driver_version(adapter, drv_version, + sizeof(drv_version) - 1); + p += sprintf(p, "driver_version = %s\n", drv_version); + + p += sprintf(p, "tx_pending = %d\n", + atomic_read(&adapter->tx_pending)); + + if (adapter->iface_type == NXPWIFI_SDIO) { + sdio_card = (struct sdio_mmc_card *)adapter->card; + p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); + p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + p += sprintf(p, "\n[interface : \"%s\"]\n", + priv->netdev->name); + p += sprintf(p, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + p += sprintf(p, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + p += sprintf(p, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + p += sprintf(p, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? + "Disconnected" : "Connected"); + p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) + ? "on" : "off")); + for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { + txq = netdev_get_tx_queue(priv->netdev, idx); + p += sprintf(p, "tx queue %d:%s ", idx, + netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (adapter->iface_type == NXPWIFI_SDIO) { + p += sprintf(p, "\n=== %s register dump===\n", "SDIO"); + if (adapter->if_ops.reg_dump) + p += adapter->if_ops.reg_dump(adapter, p); + } + p += sprintf(p, "\n=== more debug information\n"); + debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); + if (debug_info) { + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + nxpwifi_get_debug_info(priv, debug_info); + p += nxpwifi_debug_info_to_buffer(priv, p, debug_info); + break; + } + kfree(debug_info); + } + + p += sprintf(p, "\n========End dump========\n"); + nxpwifi_dbg(adapter, MSG, "===nxpwifi driverinfo dump end===\n"); + adapter->devdump_len = p - (char *)adapter->devdump_data; +} +EXPORT_SYMBOL_GPL(nxpwifi_drv_info_dump); + +void nxpwifi_prepare_fw_dump_info(struct nxpwifi_adapter *adapter) +{ + u8 idx; + char *fw_dump_ptr; + u32 dump_len = 0; + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + dump_len += (strlen("========Start dump ") + + strlen(entry->mem_name) + + strlen("========\n") + + (entry->mem_size + 1) + + strlen("\n========End dump========\n")); + } + } + + if (dump_len + 1 + adapter->devdump_len > NXPWIFI_FW_DUMP_SIZE) { + /* Realloc in case buffer overflow */ + fw_dump_ptr = vzalloc(dump_len + 1 + adapter->devdump_len); + nxpwifi_dbg(adapter, MSG, "Realloc device dump data.\n"); + if (!fw_dump_ptr) { + vfree(adapter->devdump_data); + nxpwifi_dbg(adapter, ERROR, + "vzalloc devdump data failure!\n"); + return; + } + + memmove(fw_dump_ptr, adapter->devdump_data, + adapter->devdump_len); + vfree(adapter->devdump_data); + adapter->devdump_data = fw_dump_ptr; + } + + fw_dump_ptr = (char *)adapter->devdump_data + adapter->devdump_len; + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + fw_dump_ptr += sprintf(fw_dump_ptr, "========Start dump "); + fw_dump_ptr += sprintf(fw_dump_ptr, "%s", entry->mem_name); + fw_dump_ptr += sprintf(fw_dump_ptr, "========\n"); + memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); + fw_dump_ptr += entry->mem_size; + fw_dump_ptr += sprintf(fw_dump_ptr, "\n========End dump========\n"); + } + } + + adapter->devdump_len = fw_dump_ptr - (char *)adapter->devdump_data; + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + entry->mem_size = 0; + } +} +EXPORT_SYMBOL_GPL(nxpwifi_prepare_fw_dump_info); + +/* CFG802.11 network device handler for statistics retrieval. + */ +static struct net_device_stats *nxpwifi_get_stats(struct net_device *dev) +{ + struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev); + + return &priv->stats; +} + +static u16 +nxpwifi_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + skb->priority = cfg80211_classify8021d(skb, NULL); + return nxpwifi_1d_to_wmm_queue[skb->priority]; +} + +/* Network device handlers */ +static const struct net_device_ops nxpwifi_netdev_ops = { + .ndo_open = nxpwifi_open, + .ndo_stop = nxpwifi_close, + .ndo_start_xmit = nxpwifi_hard_start_xmit, + .ndo_set_mac_address = nxpwifi_ndo_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = nxpwifi_tx_timeout, + .ndo_get_stats = nxpwifi_get_stats, + .ndo_set_rx_mode = nxpwifi_set_multicast_list, + .ndo_select_queue = nxpwifi_netdev_select_wmm_queue, +}; + +/* This function initializes the private structure parameters. + * + * The following wait queues are initialized - + * - IOCTL wait queue + * - Command wait queue + * - Statistics wait queue + * + * ...and the following default parameters are set - + * - Current key index : Set to 0 + * - Rate index : Set to auto + * - Media connected : Set to disconnected + * - Nick name : Set to null + * - Number of Tx timeout : Set to 0 + * - Device address : Set to current address + * - Rx histogram statistc : Set to 0 + * + * In addition, the CFG80211 work queue is also created. + */ +void nxpwifi_init_priv_params(struct nxpwifi_private *priv, + struct net_device *dev) +{ + dev->netdev_ops = &nxpwifi_netdev_ops; + dev->needs_free_netdev = true; + /* Initialize private structure */ + priv->current_key_index = 0; + priv->media_connected = false; + memset(priv->mgmt_ie, 0, + sizeof(struct nxpwifi_ie) * MAX_MGMT_IE_INDEX); + priv->beacon_idx = NXPWIFI_AUTO_IDX_MASK; + priv->proberesp_idx = NXPWIFI_AUTO_IDX_MASK; + priv->assocresp_idx = NXPWIFI_AUTO_IDX_MASK; + priv->gen_idx = NXPWIFI_AUTO_IDX_MASK; + priv->num_tx_timeout = 0; + if (is_valid_ether_addr(dev->dev_addr)) + ether_addr_copy(priv->curr_addr, dev->dev_addr); + else + ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); + + if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { + priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); + if (priv->hist_data) + nxpwifi_hist_data_reset(priv); + } +} + +/* This function check if command is pending. + */ +int is_command_pending(struct nxpwifi_adapter *adapter) +{ + int is_cmd_pend_q_empty; + + spin_lock_bh(&adapter->cmd_pending_q_lock); + is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + + return !is_cmd_pend_q_empty; +} + +/* This is the RX tasklet function. + * + * It handles the RX operations. + */ +static void nxpwifi_rx_recv(unsigned long data) +{ + struct nxpwifi_adapter *adapter = (struct nxpwifi_adapter *)data; + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) + return; + + nxpwifi_process_rx(adapter); +} + +/* This is the main work function. + * + * It handles the main process, which in turn handles the complete + * driver operations. + */ +static void nxpwifi_main_work(struct work_struct *work) +{ + struct nxpwifi_adapter *adapter = + container_of(work, struct nxpwifi_adapter, main_work); + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) + return; + + nxpwifi_main_process(adapter); +} + +/* This is the host mlme work function. + * It handles the host mlme operations. + */ +static void nxpwifi_host_mlme_work(struct work_struct *work) +{ + struct nxpwifi_adapter *adapter = + container_of(work, struct nxpwifi_adapter, host_mlme_work); + struct sk_buff *skb; + struct nxpwifi_rxinfo *rx_info; + struct nxpwifi_private *priv; + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) + return; + + while ((skb = skb_dequeue(&adapter->rx_mlme_q))) { + rx_info = NXPWIFI_SKB_RXCB(skb); + priv = adapter->priv[rx_info->bss_num]; + wiphy_lock(priv->wdev.wiphy); + cfg80211_rx_mlme_mgmt(priv->netdev, + skb->data, + rx_info->pkt_len); + wiphy_unlock(priv->wdev.wiphy); + } + + /* Check for host mlme disconnection */ + if (adapter->host_mlme_link_lost) { + if (adapter->priv_link_lost) { + nxpwifi_reset_connect_state(adapter->priv_link_lost, + WLAN_REASON_DEAUTH_LEAVING, + true); + adapter->priv_link_lost = NULL; + } + adapter->host_mlme_link_lost = false; + } + + /* Check for host mlme Assoc Resp */ + if (adapter->assoc_resp_received) { + nxpwifi_process_assoc_resp(adapter); + adapter->assoc_resp_received = false; + } +} + +/* Common teardown code used for both device removal and reset */ +static void nxpwifi_uninit_sw(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + int i; + + /* We can no longer handle interrupts once we start doing the teardown + * below. + */ + if (adapter->if_ops.disable_int) + adapter->if_ops.disable_int(adapter); + + set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + tasklet_kill(&adapter->rx_task); + nxpwifi_terminate_workqueue(adapter); + adapter->int_status = 0; + + /* Stop data */ + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv->netdev) { + nxpwifi_stop_net_dev_queue(priv->netdev, adapter); + netif_carrier_off(priv->netdev); + netif_device_detach(priv->netdev); + } + } + + nxpwifi_dbg(adapter, CMD, "cmd: calling nxpwifi_shutdown_drv...\n"); + nxpwifi_shutdown_drv(adapter); + nxpwifi_dbg(adapter, CMD, "cmd: nxpwifi_shutdown_drv done\n"); + + if (atomic_read(&adapter->tx_pending) || + atomic_read(&adapter->cmd_pending)) { + nxpwifi_dbg(adapter, ERROR, + "tx_pending=%d,cmd_pending=%d\n", + atomic_read(&adapter->tx_pending), + atomic_read(&adapter->cmd_pending)); + } + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + rtnl_lock(); + if (priv->netdev && + priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) { + /* Close the netdev now, because if we do it later, the + * netdev notifiers will need to acquire the wiphy lock + * again --> deadlock. + */ + dev_close(priv->wdev.netdev); + wiphy_lock(adapter->wiphy); + nxpwifi_del_virtual_intf(adapter->wiphy, &priv->wdev); + wiphy_unlock(adapter->wiphy); + } + rtnl_unlock(); + } + + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); + adapter->wiphy = NULL; + + vfree(adapter->chan_stats); + nxpwifi_free_cmd_buffers(adapter); +} + +/* This function can be used for shutting down the adapter SW. + */ +void nxpwifi_shutdown_sw(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + + if (!adapter) + return; + + wait_for_completion(adapter->fw_done); + /* Caller should ensure we aren't suspending while this happens */ + reinit_completion(adapter->fw_done); + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + nxpwifi_deauthenticate(priv, NULL); + + nxpwifi_init_shutdown_fw(priv, NXPWIFI_FUNC_SHUTDOWN); + + nxpwifi_uninit_sw(adapter); + adapter->is_up = false; +} +EXPORT_SYMBOL_GPL(nxpwifi_shutdown_sw); + +/* This function can be used for reinitting the adapter SW. Required + * code is extracted from nxpwifi_add_card() + */ +int +nxpwifi_reinit_sw(struct nxpwifi_adapter *adapter) +{ + int ret = 0; + + nxpwifi_init_lock_list(adapter); + if (adapter->if_ops.up_dev) + adapter->if_ops.up_dev(adapter); + + adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING; + clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + init_waitqueue_head(&adapter->init_wait_q); + clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + adapter->hs_activated = false; + clear_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags); + init_waitqueue_head(&adapter->hs_activate_wait_q); + init_waitqueue_head(&adapter->cmd_wait_q.wait); + adapter->cmd_wait_q.status = 0; + adapter->scan_wait_q_woken = false; + + tasklet_init(&adapter->rx_task, + (void *)nxpwifi_rx_recv, (unsigned long)adapter); + tasklet_disable(&adapter->rx_task); + + adapter->workqueue = + alloc_workqueue("NXPWIFI_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0); + if (!adapter->workqueue) { + ret = -ENOMEM; + goto err_kmalloc; + } + + INIT_WORK(&adapter->main_work, nxpwifi_main_work); + INIT_WORK(&adapter->host_mlme_work, nxpwifi_host_mlme_work); + + /* Register the device. Fill up the private data structure with + * relevant information from the card. Some code extracted from + * nxpwifi_register_dev() + */ + nxpwifi_dbg(adapter, INFO, "%s, nxpwifi_init_hw_fw()...\n", __func__); + + ret = nxpwifi_init_hw_fw(adapter, false); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: firmware init failed\n", __func__); + goto err_init_fw; + } + + /* _nxpwifi_fw_dpc() does its own cleanup */ + ret = _nxpwifi_fw_dpc(adapter->firmware, adapter); + if (ret) { + pr_err("Failed to bring up adapter: %d\n", ret); + return ret; + } + nxpwifi_dbg(adapter, INFO, "%s, successful\n", __func__); + + tasklet_enable(&adapter->rx_task); + + return ret; + +err_init_fw: + nxpwifi_dbg(adapter, ERROR, "info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + +err_kmalloc: + set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + tasklet_kill(&adapter->rx_task); + nxpwifi_terminate_workqueue(adapter); + if (adapter->hw_status == NXPWIFI_HW_STATUS_READY) { + nxpwifi_dbg(adapter, ERROR, + "info: %s: shutdown nxpwifi\n", __func__); + nxpwifi_shutdown_drv(adapter); + nxpwifi_free_cmd_buffers(adapter); + } + + complete_all(adapter->fw_done); + nxpwifi_dbg(adapter, INFO, "%s, error\n", __func__); + + return ret; +} +EXPORT_SYMBOL_GPL(nxpwifi_reinit_sw); + +static irqreturn_t nxpwifi_irq_wakeup_handler(int irq, void *priv) +{ + struct nxpwifi_adapter *adapter = priv; + + dev_dbg(adapter->dev, "%s: wake by wifi", __func__); + adapter->wake_by_wifi = true; + disable_irq_nosync(irq); + + /* Notify PM core we are wakeup source */ + pm_wakeup_event(adapter->dev, 0); + pm_system_wakeup(); + + return IRQ_HANDLED; +} + +static void nxpwifi_probe_of(struct nxpwifi_adapter *adapter) +{ + int ret; + struct device *dev = adapter->dev; + + if (!dev->of_node) + goto err_exit; + + adapter->dt_node = dev->of_node; + adapter->irq_wakeup = irq_of_parse_and_map(adapter->dt_node, 0); + if (!adapter->irq_wakeup) { + dev_dbg(dev, "fail to parse irq_wakeup from device tree\n"); + goto err_exit; + } + + ret = devm_request_irq(dev, adapter->irq_wakeup, + nxpwifi_irq_wakeup_handler, + IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN, + "wifi_wake", adapter); + if (ret) { + dev_err(dev, "Failed to request irq_wakeup %d (%d)\n", + adapter->irq_wakeup, ret); + goto err_exit; + } + + if (device_init_wakeup(dev, true)) { + dev_err(dev, "fail to init wakeup for nxpwifi\n"); + goto err_exit; + } + return; + +err_exit: + adapter->irq_wakeup = -1; +} + +/* This function adds the card. + * + * This function follows the following major steps to set up the device - + * - Initialize software. This includes probing the card, registering + * the interface operations table, and allocating/initializing the + * adapter structure + * - Set up the netlink socket + * - Create and start the main work queue + * - Register the device + * - Initialize firmware and hardware + * - Add logical interfaces + */ +int +nxpwifi_add_card(void *card, struct completion *fw_done, + struct nxpwifi_if_ops *if_ops, u8 iface_type, + struct device *dev) +{ + struct nxpwifi_adapter *adapter; + int ret = 0; + + adapter = nxpwifi_register(card, dev, if_ops); + if (IS_ERR(adapter)) { + ret = PTR_ERR(adapter); + pr_err("%s: adapter register failed %d\n", __func__, ret); + goto err_init_sw; + } + + nxpwifi_probe_of(adapter); + + adapter->iface_type = iface_type; + adapter->fw_done = fw_done; + + adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING; + clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + init_waitqueue_head(&adapter->init_wait_q); + clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + adapter->hs_activated = false; + init_waitqueue_head(&adapter->hs_activate_wait_q); + init_waitqueue_head(&adapter->cmd_wait_q.wait); + adapter->cmd_wait_q.status = 0; + adapter->scan_wait_q_woken = false; + + tasklet_init(&adapter->rx_task, + (void *)nxpwifi_rx_recv, (unsigned long)adapter); + tasklet_disable(&adapter->rx_task); + + adapter->workqueue = + alloc_workqueue("NXPWIFI_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0); + if (!adapter->workqueue) { + ret = -ENOMEM; + goto err_kmalloc; + } + + INIT_WORK(&adapter->main_work, nxpwifi_main_work); + INIT_WORK(&adapter->host_mlme_work, nxpwifi_host_mlme_work); + + /* Register the device. Fill up the private data structure with relevant + * information from the card. + */ + ret = adapter->if_ops.register_dev(adapter); + if (ret) { + pr_err("%s: failed to register nxpwifi device\n", __func__); + goto err_registerdev; + } + + ret = nxpwifi_init_hw_fw(adapter, true); + if (ret) { + pr_err("%s: firmware init failed\n", __func__); + goto err_init_fw; + } + + tasklet_enable(&adapter->rx_task); + + return ret; + +err_init_fw: + pr_debug("info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); +err_registerdev: + set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + tasklet_kill(&adapter->rx_task); + nxpwifi_terminate_workqueue(adapter); + if (adapter->hw_status == NXPWIFI_HW_STATUS_READY) { + pr_debug("info: %s: shutdown nxpwifi\n", __func__); + nxpwifi_shutdown_drv(adapter); + nxpwifi_free_cmd_buffers(adapter); + } +err_kmalloc: + if (adapter->irq_wakeup >= 0) + device_init_wakeup(adapter->dev, false); + nxpwifi_free_adapter(adapter); + +err_init_sw: + + return ret; +} +EXPORT_SYMBOL_GPL(nxpwifi_add_card); + +/* This function removes the card. + * + * This function follows the following major steps to remove the device - + * - Stop data traffic + * - Shutdown firmware + * - Remove the logical interfaces + * - Terminate the work queue + * - Unregister the device + * - Free the adapter structure + */ +void nxpwifi_remove_card(struct nxpwifi_adapter *adapter) +{ + if (!adapter) + return; + + if (adapter->is_up) + nxpwifi_uninit_sw(adapter); + + if (adapter->irq_wakeup >= 0) + device_init_wakeup(adapter->dev, false); + + /* Unregister device */ + nxpwifi_dbg(adapter, INFO, + "info: unregister device\n"); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + /* Free adapter structure */ + nxpwifi_dbg(adapter, INFO, + "info: free adapter\n"); + nxpwifi_free_adapter(adapter); +} +EXPORT_SYMBOL_GPL(nxpwifi_remove_card); + +void _nxpwifi_dbg(const struct nxpwifi_adapter *adapter, int mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!(adapter->debug_mask & mask)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (adapter->dev) + dev_info(adapter->dev, "%pV", &vaf); + else + pr_info("%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(_nxpwifi_dbg); + +/* This function initializes the module. + * + * The debug FS is also initialized if configured. + */ +static int +nxpwifi_init_module(void) +{ +#ifdef CONFIG_DEBUG_FS + nxpwifi_debugfs_init(); +#endif + return 0; +} + +/* This function cleans up the module. + * + * The debug FS is removed if available. + */ +static void +nxpwifi_cleanup_module(void) +{ +#ifdef CONFIG_DEBUG_FS + nxpwifi_debugfs_remove(); +#endif +} + +module_init(nxpwifi_init_module); +module_exit(nxpwifi_cleanup_module); + +MODULE_AUTHOR("NXP International Ltd."); +MODULE_DESCRIPTION("NXP WiFi Driver version " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/nxp/nxpwifi/main.h b/drivers/net/wireless/nxp/nxpwifi/main.h new file mode 100644 index 000000000000..5b27e2baba26 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/main.h @@ -0,0 +1,1477 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: major data structures and prototypes + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_MAIN_H_ +#define _NXPWIFI_MAIN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "sdio.h" + +extern char driver_version[]; + +struct nxpwifi_adapter; +struct nxpwifi_private; + +enum { + NXPWIFI_ASYNC_CMD, + NXPWIFI_SYNC_CMD +}; + +#define NXPWIFI_MAX_AP 64 + +#define NXPWIFI_MAX_PKTS_TXQ 16 + +#define NXPWIFI_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) + +#define NXPWIFI_TIMER_10S 10000 +#define NXPWIFI_TIMER_1S 1000 + +#define MAX_TX_PENDING 400 +#define LOW_TX_PENDING 380 + +#define HIGH_RX_PENDING 50 +#define LOW_RX_PENDING 20 + +#define NXPWIFI_UPLD_SIZE (2312) + +#define MAX_EVENT_SIZE 2048 + +#define NXPWIFI_FW_DUMP_SIZE (2 * 1024 * 1024) + +#define ARP_FILTER_MAX_BUF_SIZE 68 + +#define NXPWIFI_KEY_BUFFER_SIZE 16 +#define NXPWIFI_DEFAULT_LISTEN_INTERVAL 10 +#define NXPWIFI_MAX_REGION_CODE 9 + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 + +#define FIRST_VALID_CHANNEL 0xff + +#define DEFAULT_BCN_MISS_TIMEOUT 5 + +#define MAX_SCAN_BEACON_BUFFER 8000 + +#define SCAN_BEACON_ENTRY_PAD 6 + +#define NXPWIFI_PASSIVE_SCAN_CHAN_TIME 110 +#define NXPWIFI_ACTIVE_SCAN_CHAN_TIME 40 +#define NXPWIFI_SPECIFIC_SCAN_CHAN_TIME 40 +#define NXPWIFI_DEF_SCAN_CHAN_GAP_TIME 50 + +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define NXPWIFI_MAX_TOTAL_SCAN_TIME (NXPWIFI_TIMER_10S - NXPWIFI_TIMER_1S) + +#define WPA_GTK_OUI_OFFSET 2 +#define RSN_GTK_OUI_OFFSET 2 + +#define NXPWIFI_OUI_NOT_PRESENT 0 +#define NXPWIFI_OUI_PRESENT 1 + +#define PKT_TYPE_MGMT 0xE5 + +/* Do not check for data_received for USB, as data_received + * is handled in nxpwifi_usb_recv for USB + */ +#define IS_CARD_RX_RCVD(adapter) ({ \ + typeof(adapter) (_adapter) = adapter; \ + ((_adapter)->cmd_resp_received || \ + (_adapter)->event_received || \ + (_adapter)->data_received); \ + }) + +#define NXPWIFI_TYPE_DATA 0 +#define NXPWIFI_TYPE_CMD 1 +#define NXPWIFI_TYPE_EVENT 3 +#define NXPWIFI_TYPE_VDLL 4 +#define NXPWIFI_TYPE_AGGR_DATA 10 + +#define MAX_BITMAP_RATES_SIZE 18 + +#define MAX_CHANNEL_BAND_BG 14 +#define MAX_CHANNEL_BAND_A 165 + +#define MAX_FREQUENCY_BAND_BG 2484 + +#define NXPWIFI_EVENT_HEADER_LEN 4 +#define NXPWIFI_UAP_EVENT_EXTRA_HEADER 2 + +#define NXPWIFI_TYPE_LEN 4 +#define NXPWIFI_USB_TYPE_CMD 0xF00DFACE +#define NXPWIFI_USB_TYPE_DATA 0xBEADC0DE +#define NXPWIFI_USB_TYPE_EVENT 0xBEEFFACE + +/* Threshold for tx_timeout_cnt before we trigger a card reset */ +#define TX_TIMEOUT_THRESHOLD 6 + +#define NXPWIFI_DRV_INFO_SIZE_MAX 0x40000 + +/* Address alignment */ +#define NXPWIFI_ALIGN_ADDR(p, a) ({ \ + typeof(a) (_a) = a; \ + (((long)(p) + (_a) - 1) & ~((_a) - 1)); \ + }) + +#define NXPWIFI_MAC_LOCAL_ADMIN_BIT 41 + +/** + *enum nxpwifi_debug_level - nxp wifi debug level + */ +enum NXPWIFI_DEBUG_LEVEL { + NXPWIFI_DBG_MSG = 0x00000001, + NXPWIFI_DBG_FATAL = 0x00000002, + NXPWIFI_DBG_ERROR = 0x00000004, + NXPWIFI_DBG_DATA = 0x00000008, + NXPWIFI_DBG_CMD = 0x00000010, + NXPWIFI_DBG_EVENT = 0x00000020, + NXPWIFI_DBG_INTR = 0x00000040, + NXPWIFI_DBG_IOCTL = 0x00000080, + + NXPWIFI_DBG_MPA_D = 0x00008000, + NXPWIFI_DBG_DAT_D = 0x00010000, + NXPWIFI_DBG_CMD_D = 0x00020000, + NXPWIFI_DBG_EVT_D = 0x00040000, + NXPWIFI_DBG_FW_D = 0x00080000, + NXPWIFI_DBG_IF_D = 0x00100000, + + NXPWIFI_DBG_ENTRY = 0x10000000, + NXPWIFI_DBG_WARN = 0x20000000, + NXPWIFI_DBG_INFO = 0x40000000, + NXPWIFI_DBG_DUMP = 0x80000000, + + NXPWIFI_DBG_ANY = 0xffffffff +}; + +#define NXPWIFI_DEFAULT_DEBUG_MASK (NXPWIFI_DBG_MSG | \ + NXPWIFI_DBG_FATAL | \ + NXPWIFI_DBG_ERROR) + +__printf(3, 4) +void _nxpwifi_dbg(const struct nxpwifi_adapter *adapter, int mask, + const char *fmt, ...); +#define nxpwifi_dbg(adapter, mask, fmt, ...) \ + _nxpwifi_dbg(adapter, NXPWIFI_DBG_##mask, fmt, ##__VA_ARGS__) + +#define DEBUG_DUMP_DATA_MAX_LEN 128 +#define nxpwifi_dbg_dump(adapter, dbg_mask, str, buf, len) \ +do { \ + if ((adapter)->debug_mask & NXPWIFI_DBG_##dbg_mask) \ + print_hex_dump(KERN_DEBUG, str, \ + DUMP_PREFIX_OFFSET, 16, 1, \ + buf, len, false); \ +} while (0) + +/** Min BGSCAN interval 15 second */ +#define NXPWIFI_BGSCAN_INTERVAL 15000 +/** default repeat count */ +#define NXPWIFI_BGSCAN_REPEAT_COUNT 6 + +struct nxpwifi_dbg { + u32 num_cmd_host_to_card_failure; + u32 num_cmd_sleep_cfm_host_to_card_failure; + u32 num_tx_host_to_card_failure; + u32 num_event_deauth; + u32 num_event_disassoc; + u32 num_event_link_lost; + u32 num_cmd_deauth; + u32 num_cmd_assoc_success; + u32 num_cmd_assoc_failure; + u32 num_tx_timeout; + u16 timeout_cmd_id; + u16 timeout_cmd_act; + u16 last_cmd_id[DBG_CMD_NUM]; + u16 last_cmd_act[DBG_CMD_NUM]; + u16 last_cmd_index; + u16 last_cmd_resp_id[DBG_CMD_NUM]; + u16 last_cmd_resp_index; + u16 last_event[DBG_CMD_NUM]; + u16 last_event_index; + u32 last_mp_wr_bitmap[NXPWIFI_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_ports[NXPWIFI_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_len[NXPWIFI_DBG_SDIO_MP_NUM]; + u32 last_mp_curr_wr_port[NXPWIFI_DBG_SDIO_MP_NUM]; + u8 last_sdio_mp_index; +}; + +enum NXPWIFI_HARDWARE_STATUS { + NXPWIFI_HW_STATUS_READY, + NXPWIFI_HW_STATUS_INITIALIZING, + NXPWIFI_HW_STATUS_INIT_DONE, + NXPWIFI_HW_STATUS_RESET, + NXPWIFI_HW_STATUS_NOT_READY +}; + +enum NXPWIFI_802_11_POWER_MODE { + NXPWIFI_802_11_POWER_MODE_CAM, + NXPWIFI_802_11_POWER_MODE_PSP +}; + +struct nxpwifi_tx_param { + u32 next_pkt_len; +}; + +enum NXPWIFI_PS_STATE { + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP_CFM, + PS_STATE_SLEEP +}; + +enum nxpwifi_iface_type { + NXPWIFI_SDIO +}; + +struct nxpwifi_add_ba_param { + u32 tx_win_size; + u32 rx_win_size; + u32 timeout; + u8 tx_amsdu; + u8 rx_amsdu; +}; + +struct nxpwifi_tx_aggr { + u8 ampdu_user; + u8 ampdu_ap; + u8 amsdu; +}; + +enum nxpwifi_ba_status { + BA_SETUP_NONE = 0, + BA_SETUP_INPROGRESS, + BA_SETUP_COMPLETE +}; + +struct nxpwifi_ra_list_tbl { + struct list_head list; + struct sk_buff_head skb_head; + u8 ra[ETH_ALEN]; + u32 is_11n_enabled; + u16 max_amsdu; + u16 ba_pkt_count; + u8 ba_packet_thr; + enum nxpwifi_ba_status ba_status; + u8 amsdu_in_ampdu; + u16 total_pkt_count; + bool tx_paused; +}; + +struct nxpwifi_tid_tbl { + struct list_head ra_list; +}; + +#define WMM_HIGHEST_PRIORITY 7 +#define HIGH_PRIO_TID 7 +#define LOW_PRIO_TID 0 +#define NO_PKT_PRIO_TID -1 +#define NXPWIFI_WMM_DRV_DELAY_MAX 510 + +struct nxpwifi_wmm_desc { + struct nxpwifi_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; + u32 packets_out[MAX_NUM_TID]; + u32 pkts_paused[MAX_NUM_TID]; + /* spin lock to protect ra_list */ + spinlock_t ra_list_spinlock; + struct nxpwifi_wmm_ac_status ac_status[IEEE80211_NUM_ACS]; + enum nxpwifi_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS]; + u32 drv_pkt_delay_max; + u8 queue_priority[IEEE80211_NUM_ACS]; + u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ + /* Number of transmit packets queued */ + atomic_t tx_pkts_queued; + /* Tracks highest priority with a packet queued */ + atomic_t highest_queued_prio; +}; + +struct nxpwifi_802_11_security { + u8 wpa_enabled; + u8 wpa2_enabled; + u8 wep_enabled; + u32 authentication_mode; + u8 is_authtype_auto; + u32 encryption_mode; +}; + +struct ieee_types_vendor_specific { + struct ieee80211_vendor_ie vend_hdr; + u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee80211_vendor_ie)]; +} __packed; + +struct nxpwifi_bssdescriptor { + u8 mac_address[ETH_ALEN]; + struct cfg80211_ssid ssid; + u32 privacy; + s32 rssi; + u32 channel; + u32 freq; + u16 beacon_period; + u8 erp_flags; + u32 bss_mode; + u8 supported_rates[NXPWIFI_SUPPORTED_RATES]; + u8 data_rates[NXPWIFI_SUPPORTED_RATES]; + u16 bss_band; + u64 fw_tsf; + u64 timestamp; + union ieee_types_phy_param_set phy_param_set; + struct ieee_types_cf_param_set cf_param_set; + u16 cap_info_bitmap; + struct ieee80211_wmm_param_ie wmm_ie; + u8 disable_11n; + struct ieee80211_ht_cap *bcn_ht_cap; + u16 ht_cap_offset; + struct ieee80211_ht_operation *bcn_ht_oper; + u16 ht_info_offset; + u8 *bcn_bss_co_2040; + u16 bss_co_2040_offset; + u8 *bcn_ext_cap; + u16 ext_cap_offset; + struct ieee80211_vht_cap *bcn_vht_cap; + u16 vht_cap_offset; + struct ieee80211_vht_operation *bcn_vht_oper; + u16 vht_info_offset; + struct ieee_types_oper_mode_ntf *oper_mode; + u16 oper_mode_offset; + u8 disable_11ac; + struct ieee80211_he_cap_elem *bcn_he_cap; + u16 he_cap_offset; + struct ieee80211_he_operation *bcn_he_oper; + u16 he_info_offset; + u8 disable_11ax; + struct ieee_types_vendor_specific *bcn_wpa_ie; + u16 wpa_offset; + struct element *bcn_rsn_ie; + u16 rsn_offset; + struct element *bcn_rsnx_ie; + u16 rsnx_offset; + u8 *beacon_buf; + u32 beacon_buf_size; + u8 sensed_11h; + u8 local_constraint; + u8 chan_sw_ie_present; +}; + +struct nxpwifi_current_bss_params { + struct nxpwifi_bssdescriptor bss_descriptor; + u8 wmm_enabled; + u8 wmm_uapsd_enabled; + u8 band; + u32 num_of_rates; + u8 data_rates[NXPWIFI_SUPPORTED_RATES]; +}; + +struct nxpwifi_sleep_period { + u16 period; + u16 reserved; +}; + +struct nxpwifi_wep_key { + u32 length; + u32 key_index; + u32 key_length; + u8 key_material[NXPWIFI_KEY_BUFFER_SIZE]; +}; + +#define MAX_REGION_CHANNEL_NUM 2 + +struct nxpwifi_chan_freq_power { + u16 channel; + u32 freq; + u16 max_tx_power; + u8 unsupported; +}; + +enum state_11d_t { + DISABLE_11D = 0, + ENABLE_11D = 1, +}; + +#define NXPWIFI_MAX_TRIPLET_802_11D 83 + +struct nxpwifi_802_11d_domain_reg { + u8 dfs_region; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 no_of_triplet; + struct ieee80211_country_ie_triplet + triplet[NXPWIFI_MAX_TRIPLET_802_11D]; +}; + +struct nxpwifi_vendor_spec_cfg_ie { + u16 mask; + u16 flag; + u8 ie[NXPWIFI_MAX_VSIE_LEN]; +}; + +struct wps { + u8 session_enable; +}; + +struct nxpwifi_roc_cfg { + u64 cookie; + struct ieee80211_channel chan; +}; + +enum nxpwifi_iface_work_flags { + NXPWIFI_IFACE_WORK_DEVICE_DUMP, + NXPWIFI_IFACE_WORK_CARD_RESET, +}; + +enum nxpwifi_adapter_work_flags { + NXPWIFI_SURPRISE_REMOVED, + NXPWIFI_IS_CMD_TIMEDOUT, + NXPWIFI_IS_SUSPENDED, + NXPWIFI_IS_HS_CONFIGURED, + NXPWIFI_IS_HS_ENABLING, + NXPWIFI_IS_REQUESTING_FW_VEREXT, +}; + +struct nxpwifi_band_config { + u8 chan_band:2; + u8 chan_width:2; + u8 chan2_offset:2; + u8 scan_mode:2; +} __packed; + +struct nxpwifi_channel_band { + struct nxpwifi_band_config band_config; + u8 channel; +}; + +struct nxpwifi_private { + struct nxpwifi_adapter *adapter; + u8 bss_type; + u8 bss_role; + u8 bss_priority; + u8 bss_num; + u8 bss_started; + u8 auth_flag; + u16 auth_alg; + u8 frame_type; + u8 curr_addr[ETH_ALEN]; + u8 media_connected; + u8 port_open; + u8 usb_port; + u32 num_tx_timeout; + /* track consecutive timeout */ + u8 tx_timeout_cnt; + struct net_device *netdev; + struct net_device_stats stats; + u32 curr_pkt_filter; + u32 bss_mode; + u32 pkt_tx_ctrl; + u16 tx_power_level; + u8 max_tx_power_level; + u8 min_tx_power_level; + u32 tx_ant; + u32 rx_ant; + u8 tx_rate; + u8 tx_htinfo; + u8 rxpd_htinfo; + u8 rxpd_rate; + u16 rate_bitmap; + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + u32 data_rate; + u8 is_data_rate_auto; + u16 bcn_avg_factor; + u16 data_avg_factor; + s16 data_rssi_last; + s16 data_nf_last; + s16 data_rssi_avg; + s16 data_nf_avg; + s16 bcn_rssi_last; + s16 bcn_nf_last; + s16 bcn_rssi_avg; + s16 bcn_nf_avg; + struct nxpwifi_bssdescriptor *attempted_bss_desc; + struct cfg80211_ssid prev_ssid; + u8 prev_bssid[ETH_ALEN]; + struct nxpwifi_current_bss_params curr_bss_params; + u16 beacon_period; + u8 dtim_period; + u16 listen_interval; + u16 atim_window; + struct nxpwifi_802_11_security sec_info; + struct nxpwifi_wep_key wep_key[NUM_WEP_KEYS]; + u16 wep_key_curr_index; + u8 wpa_ie[256]; + u16 wpa_ie_len; + u8 wpa_is_gtk_set; + struct host_cmd_ds_802_11_key_material aes_key; + u8 *wps_ie; + u16 wps_ie_len; + u8 wmm_required; + u8 wmm_enabled; + u8 wmm_qosinfo; + struct nxpwifi_wmm_desc wmm; + atomic_t wmm_tx_pending[IEEE80211_NUM_ACS]; + struct list_head sta_list; + /* spin lock for associated station list */ + spinlock_t sta_list_spinlock; + struct list_head tx_ba_stream_tbl_ptr; + /* spin lock for tx_ba_stream_tbl_ptr queue */ + spinlock_t tx_ba_stream_tbl_lock; + struct nxpwifi_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; + struct nxpwifi_add_ba_param add_ba_param; + u16 rx_seq[MAX_NUM_TID]; + u8 tos_to_tid_inv[MAX_NUM_TID]; + struct list_head rx_reorder_tbl_ptr; + /* spin lock for rx_reorder_tbl_ptr queue */ + spinlock_t rx_reorder_tbl_lock; +#define NXPWIFI_ASSOC_RSP_BUF_SIZE 500 + u8 assoc_rsp_buf[NXPWIFI_ASSOC_RSP_BUF_SIZE]; + u32 assoc_rsp_size; + struct cfg80211_bss *req_bss; + +#define NXPWIFI_GENIE_BUF_SIZE 256 + u8 gen_ie_buf[NXPWIFI_GENIE_BUF_SIZE]; + u8 gen_ie_buf_len; + + struct nxpwifi_vendor_spec_cfg_ie vs_ie[NXPWIFI_MAX_VSIE_NUM]; + +#define NXPWIFI_ASSOC_TLV_BUF_SIZE 256 + u8 assoc_tlv_buf[NXPWIFI_ASSOC_TLV_BUF_SIZE]; + u8 assoc_tlv_buf_len; + + u8 *curr_bcn_buf; + u32 curr_bcn_size; + /* spin lock for beacon buffer */ + spinlock_t curr_bcn_buf_lock; + struct wireless_dev wdev; + struct nxpwifi_chan_freq_power cfp; + u32 versionstrsel; + char version_str[NXPWIFI_VERSION_STR_LENGTH]; +#ifdef CONFIG_DEBUG_FS + struct dentry *dfs_dev_dir; +#endif + u16 current_key_index; + struct cfg80211_scan_request *scan_request; + u8 cfg_bssid[6]; + struct wps wps; + u8 scan_block; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; + u8 subsc_evt_rssi_state; + struct nxpwifi_ds_misc_subsc_evt async_subsc_evt_storage; + struct nxpwifi_ie mgmt_ie[MAX_MGMT_IE_INDEX]; + u16 beacon_idx; + u16 proberesp_idx; + u16 assocresp_idx; + u16 gen_idx; + u8 ap_11n_enabled; + u8 ap_11ac_enabled; + u8 ap_11ax_enabled; + u16 config_bands; + /* 11AX */ + u8 user_he_cap_len; + u8 user_he_cap[HE_CAP_MAX_SIZE]; + u8 user_2g_he_cap_len; + u8 user_2g_he_cap[HE_CAP_MAX_SIZE]; + bool host_mlme_reg; + u32 mgmt_frame_mask; + struct nxpwifi_roc_cfg roc_cfg; + bool scan_aborting; + u8 sched_scanning; + u8 csa_chan; + unsigned long csa_expire_time; + u8 del_list_idx; + bool hs2_enabled; + struct nxpwifi_uap_bss_param bss_cfg; + struct cfg80211_chan_def bss_chandef; + struct station_parameters *sta_params; + struct idr ack_status_frames; + /* spin lock for ack status */ + spinlock_t ack_status_lock; + /** rx histogram data */ + struct nxpwifi_histogram_data *hist_data; + struct cfg80211_chan_def dfs_chandef; + struct delayed_work dfs_cac_work; + struct delayed_work dfs_chan_sw_work; + bool uap_stop_tx; + struct cfg80211_beacon_data beacon_after; + struct nxpwifi_11h_intf_state state_11h; + struct nxpwifi_ds_mem_rw mem_rw; + struct sk_buff_head bypass_txq; + struct nxpwifi_user_scan_chan hidden_chan[NXPWIFI_USER_SCAN_CHAN_MAX]; + u8 assoc_resp_ht_param; + bool ht_param_present; +}; + +struct nxpwifi_tx_ba_stream_tbl { + struct list_head list; + int tid; + u8 ra[ETH_ALEN]; + enum nxpwifi_ba_status ba_status; + u8 amsdu; +}; + +struct nxpwifi_rx_reorder_tbl; + +struct reorder_tmr_cnxt { + struct timer_list timer; + struct nxpwifi_rx_reorder_tbl *ptr; + struct nxpwifi_private *priv; + u8 timer_is_set; +}; + +struct nxpwifi_rx_reorder_tbl { + struct list_head list; + int tid; + u8 ta[ETH_ALEN]; + int init_win; + int start_win; + int win_size; + void **rx_reorder_ptr; + struct reorder_tmr_cnxt timer_context; + u8 amsdu; + u8 flags; +}; + +struct nxpwifi_bss_prio_node { + struct list_head list; + struct nxpwifi_private *priv; +}; + +struct nxpwifi_bss_prio_tbl { + struct list_head bss_prio_head; + /* spin lock for bss priority */ + spinlock_t bss_prio_lock; + struct nxpwifi_bss_prio_node *bss_prio_cur; +}; + +struct cmd_ctrl_node { + struct list_head list; + struct nxpwifi_private *priv; + u32 cmd_no; + u32 cmd_flag; + struct sk_buff *cmd_skb; + struct sk_buff *resp_skb; + void *data_buf; + u32 wait_q_enabled; + struct sk_buff *skb; + u8 *condition; + u8 cmd_wait_q_woken; + int (*cmd_resp)(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf); +}; + +struct nxpwifi_bss_priv { + u16 band; + u64 fw_tsf; +}; + +struct nxpwifi_station_stats { + u64 last_rx; + s8 rssi; + u64 rx_bytes; + u64 tx_bytes; + u32 rx_packets; + u32 tx_packets; + u32 tx_failed; + u8 last_tx_rate; + u8 last_tx_htinfo; +}; + +/* This is AP specific structure which stores information + * about associated/peer STA + */ +struct nxpwifi_sta_node { + struct list_head list; + u8 mac_addr[ETH_ALEN]; + u8 is_wmm_enabled; + u8 is_11n_enabled; + u8 is_11ac_enabled; + u8 is_11ax_enabled; + u8 ampdu_sta[MAX_NUM_TID]; + u16 rx_seq[MAX_NUM_TID]; + u16 max_amsdu; + struct nxpwifi_station_stats stats; + u8 tx_pause; +}; + +#define NXPWIFI_TYPE_AGGR_DATA_V2 11 +#define NXPWIFI_BUS_AGGR_MODE_LEN_V2 (2) +#define NXPWIFI_BUS_AGGR_MAX_LEN 16000 +#define NXPWIFI_BUS_AGGR_MAX_NUM 10 +struct bus_aggr_params { + u16 enable; + u16 mode; + u16 tx_aggr_max_size; + u16 tx_aggr_max_num; + u16 tx_aggr_align; +}; + +struct vdll_dnld_ctrl { + u8 *pending_block; + u16 pending_block_len; + u8 *vdll_mem; + u32 vdll_len; + struct sk_buff *skb; +}; + +struct nxpwifi_if_ops { + int (*init_if)(struct nxpwifi_adapter *adapter); + void (*cleanup_if)(struct nxpwifi_adapter *adapter); + int (*check_fw_status)(struct nxpwifi_adapter *adapter, u32 poll_num); + int (*check_winner_status)(struct nxpwifi_adapter *adapter); + int (*prog_fw)(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw); + int (*register_dev)(struct nxpwifi_adapter *adapter); + void (*unregister_dev)(struct nxpwifi_adapter *adapter); + int (*enable_int)(struct nxpwifi_adapter *adapter); + void (*disable_int)(struct nxpwifi_adapter *adapter); + int (*process_int_status)(struct nxpwifi_adapter *adapter); + int (*host_to_card)(struct nxpwifi_adapter *adapter, u8 type, + struct sk_buff *skb, + struct nxpwifi_tx_param *tx_param); + int (*wakeup)(struct nxpwifi_adapter *adapter); + int (*wakeup_complete)(struct nxpwifi_adapter *adapter); + + /* Interface specific functions */ + void (*update_mp_end_port)(struct nxpwifi_adapter *adapter, u16 port); + void (*cleanup_mpa_buf)(struct nxpwifi_adapter *adapter); + int (*cmdrsp_complete)(struct nxpwifi_adapter *adapter, + struct sk_buff *skb); + int (*event_complete)(struct nxpwifi_adapter *adapter, + struct sk_buff *skb); + int (*dnld_fw)(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw); + void (*card_reset)(struct nxpwifi_adapter *adapter); + int (*reg_dump)(struct nxpwifi_adapter *adapter, char *drv_buf); + void (*device_dump)(struct nxpwifi_adapter *adapter); + void (*deaggr_pkt)(struct nxpwifi_adapter *adapter, + struct sk_buff *skb); + void (*up_dev)(struct nxpwifi_adapter *adapter); +}; + +struct nxpwifi_adapter { + u8 iface_type; + unsigned int debug_mask; + struct nxpwifi_iface_comb iface_limit; + struct nxpwifi_iface_comb curr_iface_comb; + struct nxpwifi_private *priv[NXPWIFI_MAX_BSS_NUM]; + u8 priv_num; + const struct firmware *firmware; + char fw_name[32]; + int winner; + struct device *dev; + struct wiphy *wiphy; + u8 perm_addr[ETH_ALEN]; + unsigned long work_flags; + u32 fw_release_number; + u8 intf_hdr_len; + u16 init_wait_q_woken; + wait_queue_head_t init_wait_q; + void *card; + struct nxpwifi_if_ops if_ops; + atomic_t bypass_tx_pending; + atomic_t tx_pending; + atomic_t cmd_pending; + atomic_t tx_hw_pending; + struct workqueue_struct *workqueue; + struct work_struct main_work; + struct work_struct host_mlme_work; + struct tasklet_struct rx_task; + /* spin lock for following variables which + * are used to synchronize main process function + */ + spinlock_t main_proc_lock; + /* avoid execution of main process function */ + bool main_locked; + /* indicate if main process function is running */ + u32 nxpwifi_processing; + /* indicate if there are more tasks should be done + * by main process function + */ + u8 more_task_flag; + struct nxpwifi_bss_prio_tbl bss_prio_tbl[NXPWIFI_MAX_BSS_NUM]; + u16 tx_buf_size; + u16 curr_tx_buf_size; + /* sdio single port rx aggregation capability */ + bool host_disable_sdio_rx_aggr; + bool sdio_rx_aggr_enable; + u16 sdio_rx_block_size; + u32 ioport; + enum NXPWIFI_HARDWARE_STATUS hw_status; + u16 number_of_antenna; + u32 fw_cap_info; + u32 fw_cap_ext; + u16 user_htstream; + u64 uuid_lo; + u64 uuid_hi; + /* spin lock for interrupt handling */ + spinlock_t int_lock; + u8 int_status; + u32 event_cause; + struct sk_buff *event_skb; + u8 upld_buf[NXPWIFI_UPLD_SIZE]; + u8 data_sent; + u8 cmd_sent; + u8 cmd_resp_received; + u8 event_received; + u8 data_received; + u8 assoc_resp_received; + struct nxpwifi_private *priv_link_lost; + u8 host_mlme_link_lost; + u16 seq_num; + struct cmd_ctrl_node *cmd_pool; + struct cmd_ctrl_node *curr_cmd; + /* spin lock for command */ + spinlock_t nxpwifi_cmd_lock; + u16 last_init_cmd; + struct timer_list cmd_timer; + struct list_head cmd_free_q; + /* spin lock for cmd_free_q */ + spinlock_t cmd_free_q_lock; + struct list_head cmd_pending_q; + /* spin lock for cmd_pending_q */ + spinlock_t cmd_pending_q_lock; + struct list_head scan_pending_q; + /* spin lock for scan_pending_q */ + spinlock_t scan_pending_q_lock; + struct sk_buff_head tx_data_q; + atomic_t tx_queued; + u32 scan_processing; + u16 region_code; + struct nxpwifi_802_11d_domain_reg domain_reg; + u16 scan_probes; + u32 scan_mode; + u16 specific_scan_time; + u16 active_scan_time; + u16 passive_scan_time; + u16 scan_chan_gap_time; + u16 fw_bands; + u8 tx_lock_flag; + struct nxpwifi_sleep_period sleep_period; + u16 ps_mode; + u32 ps_state; + u8 need_to_wakeup; + u16 multiple_dtim; + u16 local_listen_interval; + u16 null_pkt_interval; + struct sk_buff *sleep_cfm; + u16 bcn_miss_time_out; + u8 is_deep_sleep; + u8 delay_null_pkt; + u16 delay_to_ps; + u16 enhanced_ps_mode; + u8 pm_wakeup_card_req; + u16 gen_null_pkt; + u16 pps_uapsd_mode; + u32 pm_wakeup_fw_try; + struct timer_list wakeup_timer; + struct nxpwifi_hs_config_param hs_cfg; + u8 hs_activated; + u8 hs_activated_manually; + u16 hs_activate_wait_q_woken; + wait_queue_head_t hs_activate_wait_q; + u8 event_body[MAX_EVENT_SIZE]; + u32 hw_dot_11n_dev_cap; + u8 hw_dev_mcs_support; + u8 user_dev_mcs_support; + u8 sec_chan_offset; + struct nxpwifi_dbg dbg; + u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; + u32 arp_filter_size; + struct nxpwifi_wait_queue cmd_wait_q; + u8 scan_wait_q_woken; + spinlock_t queue_lock; /* lock for tx queues */ + u8 dfs_region; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u16 max_mgmt_ie_index; + const struct firmware *cal_data; + struct device_node *dt_node; + /* 11AC */ + u32 is_hw_11ac_capable; + u32 hw_dot_11ac_dev_cap; + u32 hw_dot_11ac_mcs_support; + u32 usr_dot_11ac_dev_cap_bg; + u32 usr_dot_11ac_dev_cap_a; + u32 usr_dot_11ac_mcs_support; + /* 11AX */ + u8 is_hw_11ax_capable; + u8 hw_he_cap_len; + u8 hw_he_cap[HE_CAP_MAX_SIZE]; + u8 hw_2g_he_cap_len; + u8 hw_2g_he_cap[HE_CAP_MAX_SIZE]; + atomic_t pending_bridged_pkts; + /* For synchronizing FW initialization with device lifecycle. */ + struct completion *fw_done; + bool is_up; + bool ext_scan; + u8 fw_api_ver; + u8 fw_hotfix_ver; + u8 key_api_major_ver, key_api_minor_ver; + u8 max_sta_conn; + struct memory_type_mapping *mem_type_mapping_tbl; + u8 num_mem_types; + bool scan_chan_gap_enabled; + struct sk_buff_head rx_mlme_q; + struct sk_buff_head rx_data_q; + struct nxpwifi_chan_stats *chan_stats; + u32 num_in_chan_stats; + int survey_idx; + u8 coex_scan; + u8 coex_min_scan_time; + u8 coex_max_scan_time; + u8 coex_win_size; + u8 coex_tx_win_size; + u8 coex_rx_win_size; + u8 active_scan_triggered; + bool usb_mc_status; + bool usb_mc_setup; + struct cfg80211_wowlan_nd_info *nd_info; + struct ieee80211_regdomain *regd; + /* Wake-on-WLAN (WoWLAN) */ + int irq_wakeup; + bool wake_by_wifi; + /* Aggregation parameters*/ + struct bus_aggr_params bus_aggr; + /* Device dump data/length */ + void *devdump_data; + int devdump_len; + bool ignore_btcoex_events; + struct vdll_dnld_ctrl vdll_ctrl; + u64 roc_cookie_counter; +}; + +void nxpwifi_process_tx_queue(struct nxpwifi_adapter *adapter); + +void nxpwifi_init_lock_list(struct nxpwifi_adapter *adapter); + +void nxpwifi_set_trans_start(struct net_device *dev); + +void nxpwifi_stop_net_dev_queue(struct net_device *netdev, + struct nxpwifi_adapter *adapter); + +void nxpwifi_wake_up_net_dev_queue(struct net_device *netdev, + struct nxpwifi_adapter *adapter); + +int nxpwifi_init_priv(struct nxpwifi_private *priv); +void nxpwifi_free_priv(struct nxpwifi_private *priv); + +int nxpwifi_init_fw(struct nxpwifi_adapter *adapter); + +void nxpwifi_init_fw_complete(struct nxpwifi_adapter *adapter); + +void nxpwifi_shutdown_drv(struct nxpwifi_adapter *adapter); + +int nxpwifi_dnld_fw(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw); + +int nxpwifi_recv_packet(struct nxpwifi_private *priv, struct sk_buff *skb); +int nxpwifi_uap_recv_packet(struct nxpwifi_private *priv, + struct sk_buff *skb); + +void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv, + u16 reason_code, u8 *sa); + +int nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv, + struct sk_buff *skb); + +int nxpwifi_complete_cmd(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +void nxpwifi_cmd_timeout_func(struct timer_list *t); + +int nxpwifi_get_debug_info(struct nxpwifi_private *priv, + struct nxpwifi_debug_info *info); + +int nxpwifi_alloc_cmd_buffer(struct nxpwifi_adapter *adapter); +void nxpwifi_free_cmd_buffer(struct nxpwifi_adapter *adapter); +void nxpwifi_free_cmd_buffers(struct nxpwifi_adapter *adapter); +void nxpwifi_cancel_all_pending_cmd(struct nxpwifi_adapter *adapter); +void nxpwifi_cancel_pending_scan_cmd(struct nxpwifi_adapter *adapter); +void nxpwifi_cancel_scan(struct nxpwifi_adapter *adapter); + +void nxpwifi_recycle_cmd_node(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +void nxpwifi_insert_cmd_to_pending_q(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +int nxpwifi_exec_next_cmd(struct nxpwifi_adapter *adapter); +int nxpwifi_process_cmdresp(struct nxpwifi_adapter *adapter); +void nxpwifi_process_assoc_resp(struct nxpwifi_adapter *adapter); +int nxpwifi_handle_rx_packet(struct nxpwifi_adapter *adapter, + struct sk_buff *skb); +int nxpwifi_process_tx(struct nxpwifi_private *priv, struct sk_buff *skb, + struct nxpwifi_tx_param *tx_param); +int nxpwifi_send_null_packet(struct nxpwifi_private *priv, u8 flags); +int nxpwifi_write_data_complete(struct nxpwifi_adapter *adapter, + struct sk_buff *skb, int aggr, int status); +void nxpwifi_clean_txrx(struct nxpwifi_private *priv); +u8 nxpwifi_check_last_packet_indication(struct nxpwifi_private *priv); +void nxpwifi_check_ps_cond(struct nxpwifi_adapter *adapter); +void nxpwifi_process_sleep_confirm_resp(struct nxpwifi_adapter *adapter, + u8 *pbuf, u32 upld_len); +void nxpwifi_process_hs_config(struct nxpwifi_adapter *adapter); +void nxpwifi_hs_activated_event(struct nxpwifi_private *priv, + u8 activated); +int nxpwifi_set_hs_params(struct nxpwifi_private *priv, u16 action, + int cmd_type, struct nxpwifi_ds_hs_cfg *hs_cfg); +int nxpwifi_ret_802_11_hs_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp); +int nxpwifi_process_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb); +int nxpwifi_process_sta_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb); +int nxpwifi_process_uap_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb); +int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv, + struct sk_buff *skb); +void nxpwifi_delete_all_station_list(struct nxpwifi_private *priv); +void nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private *priv, + const u8 *ra_addr); +void nxpwifi_process_sta_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb); +void nxpwifi_process_uap_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb); +int nxpwifi_cmd_802_11_scan(struct host_cmd_ds_command *cmd, + struct nxpwifi_scan_cmd_config *scan_cfg); +void nxpwifi_queue_scan_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node); +int nxpwifi_ret_802_11_scan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp); +int nxpwifi_associate(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc); +int nxpwifi_cmd_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + struct nxpwifi_bssdescriptor *bss_desc); +int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp); +u8 nxpwifi_band_to_radio_type(u16 config_bands); +int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac); +void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter); +int nxpwifi_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); +struct nxpwifi_chan_freq_power *nxpwifi_get_cfp(struct nxpwifi_private *priv, + u8 band, u16 channel, u32 freq); +u32 nxpwifi_index_to_data_rate(struct nxpwifi_private *priv, + u8 index, u8 ht_info); +u32 nxpwifi_index_to_acs_data_rate(struct nxpwifi_private *priv, + u8 index, u8 ht_info); +int nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private *priv, u16 vsie_mask, + u8 **buffer); +u32 nxpwifi_get_active_data_rates(struct nxpwifi_private *priv, + u8 *rates); +u32 nxpwifi_get_supported_rates(struct nxpwifi_private *priv, u8 *rates); +u32 nxpwifi_get_rates_from_cfg80211(struct nxpwifi_private *priv, + u8 *rates, u8 radio_type); +u8 nxpwifi_is_rate_auto(struct nxpwifi_private *priv); +extern u16 region_code_index[NXPWIFI_MAX_REGION_CODE]; +void nxpwifi_save_curr_bcn(struct nxpwifi_private *priv); +void nxpwifi_free_curr_bcn(struct nxpwifi_private *priv); +int is_command_pending(struct nxpwifi_adapter *adapter); +void nxpwifi_init_priv_params(struct nxpwifi_private *priv, + struct net_device *dev); +void nxpwifi_set_ba_params(struct nxpwifi_private *priv); +void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *pmadapter); +void nxpwifi_set_11ac_ba_params(struct nxpwifi_private *priv); +int nxpwifi_cmd_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp); +int nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private *priv, + void *buf); +int nxpwifi_cmd_802_11_bg_scan_config(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int nxpwifi_stop_bg_scan(struct nxpwifi_private *priv); + +/* This function checks if the queuing is RA based or not. + */ +static inline u8 +nxpwifi_queuing_ra_based(struct nxpwifi_private *priv) +{ + /* Currently we assume if we are in Infra, then DA=RA. This might not be + * true in the future + */ + if (priv->bss_mode == NL80211_IFTYPE_STATION && + (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)) + return false; + + return true; +} + +/* This function copies rates. + */ +static inline u32 +nxpwifi_copy_rates(u8 *dest, u32 pos, u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= NXPWIFI_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/* This function returns the correct private structure pointer based + * upon the BSS type and BSS number. + */ +static inline struct nxpwifi_private * +nxpwifi_get_priv_by_id(struct nxpwifi_adapter *adapter, + u8 bss_num, u8 bss_type) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED) + continue; + if (adapter->priv[i]->bss_num == bss_num && + adapter->priv[i]->bss_type == bss_type) + break; + } + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* This function returns the first available private structure pointer + * based upon the BSS role. + */ +static inline struct nxpwifi_private * +nxpwifi_get_priv(struct nxpwifi_adapter *adapter, + enum nxpwifi_bss_role bss_role) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (bss_role == NXPWIFI_BSS_ROLE_ANY || + GET_BSS_ROLE(adapter->priv[i]) == bss_role) + break; + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* This function checks available bss_num when adding new interface or + * changing interface type. + */ +static inline u8 +nxpwifi_get_unused_bss_num(struct nxpwifi_adapter *adapter, u8 bss_type) +{ + u8 i, j; + int index[NXPWIFI_MAX_BSS_NUM]; + + memset(index, 0, sizeof(index)); + for (i = 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->bss_type == bss_type && + !(adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED)) { + index[adapter->priv[i]->bss_num] = 1; + } + for (j = 0; j < NXPWIFI_MAX_BSS_NUM; j++) + if (!index[j]) + return j; + return -ENOENT; +} + +/* This function returns the first available unused private structure pointer. + */ +static inline struct nxpwifi_private * +nxpwifi_get_unused_priv_by_bss_type(struct nxpwifi_adapter *adapter, + u8 bss_type) +{ + u8 i; + + for (i = 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED) { + adapter->priv[i]->bss_num = + nxpwifi_get_unused_bss_num(adapter, bss_type); + break; + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* This function returns the driver private structure of a network device. + */ +static inline struct nxpwifi_private * +nxpwifi_netdev_get_priv(struct net_device *dev) +{ + return (struct nxpwifi_private *)(*(unsigned long *)netdev_priv(dev)); +} + +/* This function checks if a skb holds a management frame. + */ +static inline bool nxpwifi_is_skb_mgmt_frame(struct sk_buff *skb) +{ + return (get_unaligned_le32(skb->data) == PKT_TYPE_MGMT); +} + +/* This function retrieves channel closed for operation by Channel + * Switch Announcement. + */ +static inline u8 +nxpwifi_11h_get_csa_closed_channel(struct nxpwifi_private *priv) +{ + if (!priv->csa_chan) + return 0; + + /* Clear csa channel, if DFS channel move time has passed */ + if (time_after(jiffies, priv->csa_expire_time)) { + priv->csa_chan = 0; + priv->csa_expire_time = 0; + } + + return priv->csa_chan; +} + +static inline u8 nxpwifi_is_any_intf_active(struct nxpwifi_private *priv) +{ + struct nxpwifi_private *priv_tmp; + int i; + + for (i = 0; i < priv->adapter->priv_num; i++) { + priv_tmp = priv->adapter->priv[i]; + if ((GET_BSS_ROLE(priv_tmp) == NXPWIFI_BSS_ROLE_UAP && + priv_tmp->bss_started) || + (GET_BSS_ROLE(priv_tmp) == NXPWIFI_BSS_ROLE_STA && + priv_tmp->media_connected)) + return 1; + } + + return 0; +} + +/* Disable platform specific wakeup interrupt */ +static inline void nxpwifi_disable_wake(struct nxpwifi_adapter *adapter) +{ + if (adapter->irq_wakeup >= 0) { + disable_irq_wake(adapter->irq_wakeup); + disable_irq(adapter->irq_wakeup); + if (adapter->wake_by_wifi) + /* Undo our disable, since interrupt handler already + * did this. + */ + enable_irq(adapter->irq_wakeup); + } +} + +/* Enable platform specific wakeup interrupt */ +static inline void nxpwifi_enable_wake(struct nxpwifi_adapter *adapter) +{ + /* Enable platform specific wakeup interrupt */ + if (adapter->irq_wakeup >= 0) { + adapter->wake_by_wifi = false; + enable_irq(adapter->irq_wakeup); + enable_irq_wake(adapter->irq_wakeup); + } +} + +int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv, + u32 func_init_shutdown); + +int nxpwifi_add_card(void *card, struct completion *fw_done, + struct nxpwifi_if_ops *if_ops, u8 iface_type, + struct device *dev); +void nxpwifi_remove_card(struct nxpwifi_adapter *adapter); + +void nxpwifi_get_version(struct nxpwifi_adapter *adapter, char *version, + int maxlen); +int +nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv, + struct nxpwifi_multicast_list *mcast_list); +int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist, + struct net_device *dev); +int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_queued); +int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss, + struct cfg80211_ssid *req_ssid); +int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type); +bool nxpwifi_enable_hs(struct nxpwifi_adapter *adapter); +int nxpwifi_disable_auto_ds(struct nxpwifi_private *priv); +int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate); + +int nxpwifi_scan_networks(struct nxpwifi_private *priv, + const struct nxpwifi_user_scan_cfg *user_scan_in); +int nxpwifi_set_radio(struct nxpwifi_private *priv, u8 option); + +int nxpwifi_set_encode(struct nxpwifi_private *priv, struct key_params *kp, + const u8 *key, int key_len, u8 key_index, + const u8 *mac_addr, int disable); + +int nxpwifi_set_gen_ie(struct nxpwifi_private *priv, const u8 *ie, int ie_len); + +int nxpwifi_get_ver_ext(struct nxpwifi_private *priv, u32 version_str_sel); + +int nxpwifi_remain_on_chan_cfg(struct nxpwifi_private *priv, u16 action, + struct ieee80211_channel *chan, + unsigned int duration); + +int nxpwifi_get_stats_info(struct nxpwifi_private *priv, + struct nxpwifi_ds_get_stats *log); + +int nxpwifi_reg_write(struct nxpwifi_private *priv, u32 reg_type, + u32 reg_offset, u32 reg_value); + +int nxpwifi_reg_read(struct nxpwifi_private *priv, u32 reg_type, + u32 reg_offset, u32 *value); + +int nxpwifi_eeprom_read(struct nxpwifi_private *priv, u16 offset, u16 bytes, + u8 *value); + +int nxpwifi_set_11n_httx_cfg(struct nxpwifi_private *priv, int data); + +int nxpwifi_get_11n_httx_cfg(struct nxpwifi_private *priv, int *data); + +int nxpwifi_set_tx_rate_cfg(struct nxpwifi_private *priv, int tx_rate_index); + +int nxpwifi_get_tx_rate_cfg(struct nxpwifi_private *priv, int *tx_rate_index); + +int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode); + +int nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter, + char *version, int max_len); + +int nxpwifi_set_tx_power(struct nxpwifi_private *priv, + struct nxpwifi_power_cfg *power_cfg); + +void nxpwifi_main_process(struct nxpwifi_adapter *adapter); + +void nxpwifi_queue_tx_pkt(struct nxpwifi_private *priv, struct sk_buff *skb); + +int nxpwifi_get_bss_info(struct nxpwifi_private *priv, + struct nxpwifi_bss_info *info); +int nxpwifi_fill_new_bss_desc(struct nxpwifi_private *priv, + struct cfg80211_bss *bss, + struct nxpwifi_bssdescriptor *bss_desc); +int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter, + struct nxpwifi_bssdescriptor *bss_entry); +int nxpwifi_check_network_compatibility(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc); + +u8 nxpwifi_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); +u8 nxpwifi_get_chan_type(struct nxpwifi_private *priv); + +struct wireless_dev *nxpwifi_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + struct vif_params *params); +int nxpwifi_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); + +int nxpwifi_add_wowlan_magic_pkt_filter(struct nxpwifi_adapter *adapter); + +int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *data); +int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv); +u8 *nxpwifi_11d_code_2_region(u8 code); +void nxpwifi_init_11h_params(struct nxpwifi_private *priv); +int nxpwifi_is_11h_active(struct nxpwifi_private *priv); +int nxpwifi_11h_activate(struct nxpwifi_private *priv, bool flag); +void nxpwifi_11h_process_join(struct nxpwifi_private *priv, u8 **buffer, + struct nxpwifi_bssdescriptor *bss_desc); +int nxpwifi_11h_handle_event_chanswann(struct nxpwifi_private *priv); +void nxpwifi_dnld_txpwr_table(struct nxpwifi_private *priv); + +extern const struct ethtool_ops nxpwifi_ethtool_ops; + +void nxpwifi_del_all_sta_list(struct nxpwifi_private *priv); +void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac); +void +nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies, + int ies_len, struct nxpwifi_sta_node *node); +struct nxpwifi_sta_node * +nxpwifi_add_sta_entry(struct nxpwifi_private *priv, const u8 *mac); +struct nxpwifi_sta_node * +nxpwifi_get_sta_entry(struct nxpwifi_private *priv, const u8 *mac); +int nxpwifi_init_channel_scan_gap(struct nxpwifi_adapter *adapter); + +int nxpwifi_cmd_issue_chan_report_request(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int nxpwifi_11h_handle_chanrpt_ready(struct nxpwifi_private *priv, + struct sk_buff *skb); + +void nxpwifi_parse_tx_status_event(struct nxpwifi_private *priv, + void *event_body); + +struct sk_buff * +nxpwifi_clone_skb_for_tx_status(struct nxpwifi_private *priv, + struct sk_buff *skb, u8 flag, u64 *cookie); +void nxpwifi_dfs_cac_work(struct work_struct *work); +void nxpwifi_dfs_chan_sw_work(struct work_struct *work); +void nxpwifi_abort_cac(struct nxpwifi_private *priv); +int nxpwifi_stop_radar_detection(struct nxpwifi_private *priv, + struct cfg80211_chan_def *chandef); +int nxpwifi_11h_handle_radar_detected(struct nxpwifi_private *priv, + struct sk_buff *skb); + +void nxpwifi_hist_data_set(struct nxpwifi_private *priv, u8 rx_rate, s8 snr, + s8 nflr); +void nxpwifi_hist_data_reset(struct nxpwifi_private *priv); +void nxpwifi_hist_data_add(struct nxpwifi_private *priv, + u8 rx_rate, s8 snr, s8 nflr); +u8 nxpwifi_adjust_data_rate(struct nxpwifi_private *priv, + u8 rx_rate, u8 ht_info); + +void nxpwifi_drv_info_dump(struct nxpwifi_adapter *adapter); +void nxpwifi_prepare_fw_dump_info(struct nxpwifi_adapter *adapter); +void nxpwifi_upload_device_dump(struct nxpwifi_adapter *adapter); +void *nxpwifi_alloc_dma_align_buf(int rx_len, gfp_t flags); +void nxpwifi_fw_dump_event(struct nxpwifi_private *priv); +int nxpwifi_get_wakeup_reason(struct nxpwifi_private *priv, u16 action, + int cmd_type, + struct nxpwifi_ds_wakeup_reason *wakeup_reason); +int nxpwifi_get_chan_info(struct nxpwifi_private *priv, + struct nxpwifi_channel_band *channel_band); +void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter); +void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid); +int nxpwifi_send_domain_info_cmd_fw(struct wiphy *wiphy, enum nl80211_band band); +int nxpwifi_set_mac_address(struct nxpwifi_private *priv, + struct net_device *dev, + bool external, u8 *new_mac); +void nxpwifi_devdump_tmo_func(unsigned long function_context); + +#ifdef CONFIG_DEBUG_FS +void nxpwifi_debugfs_init(void); +void nxpwifi_debugfs_remove(void); + +void nxpwifi_dev_debugfs_init(struct nxpwifi_private *priv); +void nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv); +#endif +int nxpwifi_reinit_sw(struct nxpwifi_adapter *adapter); +void nxpwifi_shutdown_sw(struct nxpwifi_adapter *adapter); +#endif /* !_NXPWIFI_MAIN_H_ */ From patchwork Mon Sep 30 06:36:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815436 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B797186614; Mon, 30 Sep 2024 06:38:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678330; cv=fail; b=KB5P5rFvcg8JvappWcO/faIH2lSZoKgJVN/t1+F5TqDMish+iiHSL4nsUagYyxmln1VML8ok7JJsBURhjtERBUx2mW83N/PZggrJz8Telr+0efeAMXLU/9gMdwJplq2K7UxuzUx/ltZgMN0xJUq2TYL0WlOYYSgpi+VCP5tyxeQ= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678330; c=relaxed/simple; bh=jRypZywkFgEJFPu8Ra4XaUQVgmsqdNNQEuW9XiUkSpI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=RDksJAcdwbufrFIGrIfSsxvljZ/VQz0p/PC+cahjlAxbWd1VHQmGs3v7C+2RyQrLANWIuJc9UZ1g9xWmnM/+2+6Dovvq8ID6wOO48nAFKX2p0U4NI1CnISEPsL5pnCIUd8LBt42cq2nWeisghdoGK/2B1YzG9/TAsFe3gOuuF3g= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=jTj1Vh5h; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="jTj1Vh5h" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=RV+kKAql2M8KU7bKEZQJcXnIFcfaOZZfosMZtaWvKPfpd8eAahm2YQpRyBEyx5pYaRH/KxNqjgmLz1woPg8DGYPYfBH1VaiMr9+RHfUEBXPTCovIUbYspox7nlIjP8zzMaaclmRwYIpGE6qR+hRsAVs1yu4VYyFV2BWW3rjT4gm57Mdp+ElgHDOrx5ecfawR8+PQ7zKgc6RxXBK6qWIOmhbGgWQW0wuVAbzRbaoiSwcIB6aODWh4x+nhklRqoqGGdfVuLthgRvUr887SJQkH0YobAdnJnMNgTgKa/YNlaK5C293SqtOCH3XqqPG/CoHDbp17QdOsUdMcd+FvBZ1zKw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=R/CQvkbvfADEuicqSHO5DV+1fL3hetQU6MC7llE1CRQ=; b=mQVVgZ51H2nd8LZz7KR8EUujOuG6zy69M+M1JYP30+On5FlMVvep84qg6zsxx+Zw82O6lZ+PaS+65G6mBtEJ56Hf9pF3wTFaGH4hwDiM6TI8lRcD6/CHTJ50qEL3YrRATtNlWweXp73LW7CKF8jE1aaUXNzjCsBwuYt/P+ONaf3w7kS/pnD80BAxzZeVMrvsl/AkTE/v0GtmyC4Mxo/NOaZi5S8Q0dHzbctA9bwsQeOUD3ZEtT4sTFiD3Ff4iOCf2AyJw2YgBtHgYzIjwxmdweFZzwBbhqOBVQi/ohiETU7qNHgccW98qg7xZmWsCr8HThfTzMfdCoq26p5PGoFiLQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=R/CQvkbvfADEuicqSHO5DV+1fL3hetQU6MC7llE1CRQ=; b=jTj1Vh5hhvWpa0xQ7MEplP6hM2G6FLz4F2uaodqsrKvgy0XFoESxpAJaSYzRxytxNC0YfRNz3aQhcbtq1LrXX9qEGhptcl6OPYW4n2ls3wcR/n7C52kmFYvzhlWFc4fygj2ym0GpmxQHvDHDZzO8OZ2+zWXxbl9j2xHYA/3fImFX4G7ZtXta4jBzULUIXRZi9pJC08gBe++dpI0Vu7bEtMbrNJAWUsW8Qt1WufErzvmvJfYv9I34QpZMfDof+/rujHaZRqKR/nJa+6tA0m5SKOMJFPshgcKQCzB4rEE7FEEi4VXtJBZwAB3eJ06JQ1E3wrIxFvvPWhrGVcG8glhq0Q== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:28 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:27 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 19/22] wifi: nxpwifi: add sdio bus driver files Date: Mon, 30 Sep 2024 14:36:58 +0800 Message-Id: <20240930063701.2566520-20-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 93c544ee-e297-4717-ced4-08dce11a7db1 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: 6o83+032YnZ1G7pL3lc2h7TWtQ6Cnmk/b/I97JV3uw29x4oAYk0jtwaZ5RMYQDwKDtf/gNxBADO8wa1YChPj8grbfJrJ00/scXVF8UdVPVw5FJyxc8Jln6ItzCRX1qUaamlGCXGoFfWsSAmQ/JjFcGBWBVqVxnAAl2BTtPhGL9Z8vGw5ooE93mFAgcALE1r7E1lwYRjbTW2Y44JINTcWd0yVJTTB4quC6gu5JvHMXevQJE1eZrNxsZJSqgymaT+49mKw3h2hfyo1yrshL8HAFcgDXWrt8k3KS7btLojuYEEGHVJ6kZauwktNypF1zC2sUblUz9gVEAxWLNj1yFJ3M+F/LnMU70tuIm6cqN2yzk3hnU8XiBShHLdUapUVvdaWVIpe/lACQFAp99ykWX1WDvkaN3fMbJjjGZ2OfFWI7fOUhqsPmBqwjesCAtvZj9R6jaijDj0LdWsKdeN5/veex/TTWFqvqSxzPgElsEQJJg2x6Vx2AVoDNd6YocvrIiYME0+kTVOPdi+4VMKt8m/ptQtN8W9f/bnswHoPkuv9T4wfew4Wsd6SUKpF44qp+xZHgpLHtvSVqOMM4d9OKHsvR4w0ts76+GZZplV2d/lhh4iCGoib8mvdgBnZtDeX0zF0uis4VvS1HHToYRdZxS+gfDTrc8m5lWIwRXgHNT8JNYE/HZCzePW27SFeVFkWPwF42tjujrSkvxhdoW5Wy7JtPHKu9zsM5OSBj15ZQSAi+NOIq2dbLqFgtK5IWfjj5IyDz0Uuil5ea1zYG8EdinUoMLBu6+uUwiRLyzizaniL1WKwH9RE8m9+A0F0GZKCcJBfwjMQOulVYbvCXE7QbClNszCssJEL1F2XHsF2fj6hKOh0uagQrlx5FjcAxQCKryE0aFLegGeBJ/K8PnvpUWUfJYO3Rk/Hfk4RQlgbHr9KeybDjCJRuof16WIZkAoXe2jWC7utUs69NpQV7K5H38FNQJWoZ0bQGfubLAQdBljf5tCWUd1suosfxWtwM/SsrIssM1+507QPrdYJK75F+23ozV52QwckgJ4zVnluT0i/L3Xxt8W0kmcFUHyx8ttaMV39r/gEnJn5L/SWIgot1hLGQ+rGGJrNwUdm5+ZHKOW9tYTy2gov0E/nXXUbAp7pbjPzBkGaHdvqtKpl8nEacxdSIIDV9FnB9t4VYqDlpWdkSEjojR0DelDzCOSNJ5quIgn0k2kCJAsTL+Z4BihpkQjEK3TXPbUF2IuyImd6vjWWbfR2mDkAtnBUHDW2tt3MRte42uKU3SEfM5GldYz0ywwU2n6rVFLV11wosBK2e3XeCmUJtaoqWn4Rfr53EfXC8JjHS4hzcmnu01+hBOTwMjk00CSNKo0FTJ7HMpHI4ITJRRy77/f+CCg2/PZPvizXDKIB89QFq4T0HwDv95CGh1YXow== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: SC7dUpBHMHvyupaZ0SC5WmUbw5fuzfjvnKrrYmH/A5u3vmG4VlBhJcXvSgBUB7c5V+36/pZ7K2fNW+dNQc1k07AoMI2Rw3QzDNGa9v9MyRB2Hwt06USvQsSRI1PtxM1ixipsGxF4dGQXQ16TpXWFvltpaeUDfQRXHvYjVFG17ROrdV78FEg6M3Y21tBfzXmUf/Tl3zh1aGPZw0gZXqfWuo1MldhTzvFmpiy7Yp0MH4Ky6KNzc/lmSAcmSBbxCz6pWMam1LPqwE49VAM20/G3Vtx2cdu8R/1utOuaH6ePwpgRWyB7Az1AlcVKVdfkPg36/w7x+H+i7kT4v1gpgHN08wdVfrbqJlxhlMP8u3SACT7DBWPNQSfKD3VGeeenaxUqSaJulri8SSPiGpywsCFpwRJsbC3RAC9/4QqJvJ+sqU3YncXKnK26Gim6KMoBxSor6PyYlspdsE/88O/Psq1M3EUM+GtN30aT95hMfwK03QX/DHhOuIr3xyK9vUaDP/u8+1/SctLsXJi7OVKOpbH2nQ7nfyFIH4c5/azjCMRrzyJ8cLCzZuFxGURo8kUhZriHHkDvxX+Pb/xL0sYwK85Le4pegl8Nb6ovd2OPazMmTOBTaRMyXxrlxpCoW0d/QYGG+Jg17qhFiKE8fwd7wU1m4siawGVX+fbPAoPQBWWsaa9uIRPF5wo+mGTqp9DwVpdTBn9XGhBMHXsj5OdA3Wm0F7v7/hrhBkqC+JEl3AYZ/kcQ1eNfHpTzPtgO17oNJyctNXIeDID8V3Ctf2vfW0r3lWItYUS2rnajuutDX+cck0QMSnsPWsYyKDFSEEbp3GRT3l6iejlycMcMcV/mt1etrai/R4YKTLDvFy/zqyw4fNwG3EpzmcUjFedkhCXgWT4ipwaO4gsWnk5ROfcqJBOK1bQ6aSHaGe3/2WlgPo80h/4wns/CBzJjbxIWMoPabJEG0EWsAyqmcvcsex3YaQjq6MqSDnB5huzLPeluwsMXUEE56GKj8/j/eLdtYzRkZR0LD7X5KFaPji+d3DNulT95zDcHpLcarEN5SRyNjgbwKkAdvDq5GtVAlQl/4ab3ux2TxvJOV/ejLrJJ9uuZiQUzkwNyzIQmgEit8GaygYCWcTyHy3dMrkdFqZKtouxaDgYhLvDqhfGuSd+j2snl5zirRLyVg/wGUHWm2zHAKSmMjP33nRccQuvQj6yNBdBuHrQkZ3IFrbSPYP9Uaw5ieuwtmRUJb7eYumgO/BM+foMZLSiLBINDlNbkRdnCZd9TogVa3Jp1lcvW+c970pxo2Yb4kK67soYXr29zuy/tlpfB7qJh62I24hKmQLNbmY5AIoFRTNhNZrkYUDVL7Vh+J52k6hp7HAhGyTFJml8gjmHZWFyqONxr2nislqNKDfMum5gwN5Qy/fKmqd4ONVsYhwN7JlL9/TLOnWZTctdniZ6JW2GaaJb0PW4UYYIlsHZDItktGVCtR7c9bhdZTmZ51Lff0Bconkq6CzrT9FTA7KxBgvj1O4Qxfnok5r52hysON81D+tL1Sgyi8EgaG+R5ogHBcxhnmGSh34z3hT8d5DKritv//JGKJ0qJczszehRH2cPH X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 93c544ee-e297-4717-ced4-08dce11a7db1 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:27.9344 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: kg8gnzslFib+TgRRLgF4ptFQmiv/zZrdK72RSg0nxQRycqXZ2b0Rf/oxNtHbGps7fYxIyvpw71iA7HOyTvEd/Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Implement SDIO bus driver. This driver will create a working instance on core layer in order to support client and AP modes for supported SDIO NXP WiFi chips. Signed-off-by: David Lin --- drivers/net/wireless/nxp/nxpwifi/sdio.c | 2646 +++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/sdio.h | 340 +++ 2 files changed, 2986 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/sdio.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/sdio.h diff --git a/drivers/net/wireless/nxp/nxpwifi/sdio.c b/drivers/net/wireless/nxp/nxpwifi/sdio.c new file mode 100644 index 000000000000..c9c981134393 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sdio.c @@ -0,0 +1,2646 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: SDIO specific handling + * + * Copyright 2011-2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "sdio.h" + +#define SDIO_VERSION "1.0" + +static void nxpwifi_sdio_work(struct work_struct *work); + +static struct nxpwifi_if_ops sdio_ops; + +static const struct nxpwifi_sdio_card_reg nxpwifi_reg_iw61x = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0xF8, + .base_1_reg = 0xF9, + .poll_reg = 0x5C, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_rsr_reg = 0x4, + .host_int_status_reg = 0x0C, + .host_int_mask_reg = 0x08, + .host_strap_reg = 0xF4, + .host_strap_mask = 0x01, + .host_strap_value = 0x00, + .status_reg_0 = 0xE8, + .status_reg_1 = 0xE9, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .max_mp_regs = 196, + .rd_bitmap_l = 0x10, + .rd_bitmap_u = 0x11, + .rd_bitmap_1l = 0x12, + .rd_bitmap_1u = 0x13, + .wr_bitmap_l = 0x14, + .wr_bitmap_u = 0x15, + .wr_bitmap_1l = 0x16, + .wr_bitmap_1u = 0x17, + .rd_len_p0_l = 0x18, + .rd_len_p0_u = 0x19, + .card_misc_cfg_reg = 0xd8, + .card_cfg_2_1_reg = 0xd9, + .cmd_rd_len_0 = 0xc0, + .cmd_rd_len_1 = 0xc1, + .cmd_rd_len_2 = 0xc2, + .cmd_rd_len_3 = 0xc3, + .cmd_cfg_0 = 0xc4, + .cmd_cfg_1 = 0xc5, + .cmd_cfg_2 = 0xc6, + .cmd_cfg_3 = 0xc7, + .fw_dump_host_ready = 0xcc, + .fw_dump_ctrl = 0xf9, + .fw_dump_start = 0xf1, + .fw_dump_end = 0xf8, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0xE8, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x64, 0x65, 0x66, + 0x68, 0x69, 0x6a}, +}; + +static const struct nxpwifi_sdio_device nxpwifi_sdio_iw61x = { + .firmware = IW61X_SDIO_FW_NAME, + .reg = &nxpwifi_reg_iw61x, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = NXPWIFI_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size = NXPWIFI_MP_AGGR_BSIZE_MAX, + .mp_rx_agg_buf_size = NXPWIFI_MP_AGGR_BSIZE_MAX, + .can_dump_fw = true, + .fw_dump_enh = true, + .can_ext_scan = true, +}; + +static struct memory_type_mapping generic_mem_type_map[] = { + {"DUMP", NULL, 0, 0xDD}, +}; + +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"APU", NULL, 0, 0xF3}, + {"CIU", NULL, 0, 0xF4}, + {"ICU", NULL, 0, 0xF5}, + {"MAC", NULL, 0, 0xF6}, + {"EXT7", NULL, 0, 0xF7}, + {"EXT8", NULL, 0, 0xF8}, + {"EXT9", NULL, 0, 0xF9}, + {"EXT10", NULL, 0, 0xFA}, + {"EXT11", NULL, 0, 0xFB}, + {"EXT12", NULL, 0, 0xFC}, + {"EXT13", NULL, 0, 0xFD}, + {"EXTLAST", NULL, 0, 0xFE}, +}; + +static const struct of_device_id nxpwifi_sdio_of_match_table[] __maybe_unused = { + { } +}; + +/* This function parse device tree node using mmc subnode devicetree API. + * The device node is saved in card->plt_of_node. + * if the device tree node exist and include interrupts attributes, this + * function will also request platform specific wakeup interrupt. + */ +static int nxpwifi_sdio_probe_of(struct device *dev) +{ + if (!of_match_node(nxpwifi_sdio_of_match_table, dev->of_node)) { + dev_err(dev, "required compatible string missing\n"); + return -EINVAL; + } + + return 0; +} + +/* SDIO probe. + * + * This function probes an nxpwifi device and registers it. It allocates + * the card structure, enables SDIO function number and initiates the + * device registration and initialization procedure by adding a logical + * interface. + */ +static int +nxpwifi_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret; + struct sdio_mmc_card *card = NULL; + + pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", + func->vendor, func->device, func->class, func->num); + + card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + init_completion(&card->fw_done); + + card->func = func; + + func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; + + if (id->driver_data) { + struct nxpwifi_sdio_device *data = (void *)id->driver_data; + + card->firmware = data->firmware; + card->firmware_sdiouart = data->firmware_sdiouart; + card->reg = data->reg; + card->max_ports = data->max_ports; + card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; + card->tx_buf_size = data->tx_buf_size; + card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; + card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; + card->can_dump_fw = data->can_dump_fw; + card->fw_dump_enh = data->fw_dump_enh; + card->can_ext_scan = data->can_ext_scan; + INIT_WORK(&card->work, nxpwifi_sdio_work); + } + + sdio_claim_host(func); + ret = sdio_enable_func(func); + sdio_release_host(func); + + if (ret) { + dev_err(&func->dev, "failed to enable function\n"); + return ret; + } + + /* device tree node parsing and platform specific configuration*/ + if (func->dev.of_node) { + ret = nxpwifi_sdio_probe_of(&func->dev); + if (ret) + goto err_disable; + } + + ret = nxpwifi_add_card(card, &card->fw_done, &sdio_ops, + NXPWIFI_SDIO, &func->dev); + if (ret) { + dev_err(&func->dev, "add card failed\n"); + goto err_disable; + } + + return 0; + +err_disable: + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + + return ret; +} + +/* SDIO resume. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a host sleep cancel request to the firmware. + */ +static int nxpwifi_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct nxpwifi_adapter *adapter; + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + dev_err(dev, "resume: invalid card or adapter\n"); + return 0; + } + + adapter = card->adapter; + + if (!test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) { + nxpwifi_dbg(adapter, WARN, + "device already resumed\n"); + return 0; + } + + clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + + /* Disable Host Sleep */ + nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA), + NXPWIFI_SYNC_CMD); + + nxpwifi_disable_wake(adapter); + + return 0; +} + +/* Write data into SDIO card register. Caller claims SDIO device. */ +static int +nxpwifi_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) +{ + int ret; + + sdio_writeb(func, data, reg, &ret); + return ret; +} + +/* This function writes data into SDIO card register. + */ +static int +nxpwifi_write_reg(struct nxpwifi_adapter *adapter, u32 reg, u8 data) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + + sdio_claim_host(card->func); + ret = nxpwifi_write_reg_locked(card->func, reg, data); + sdio_release_host(card->func); + + return ret; +} + +/* This function reads data from SDIO card register. + */ +static int +nxpwifi_read_reg(struct nxpwifi_adapter *adapter, u32 reg, u8 *data) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 val; + + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + + *data = val; + + return ret; +} + +/* This function writes multiple data into SDIO card memory. + * + * This does not work in suspended mode. + */ +static int +nxpwifi_write_data_sync(struct nxpwifi_adapter *adapter, + u8 *buffer, u32 pkt_len, u32 port) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 blk_mode = + (port & NXPWIFI_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + u32 blk_size = (blk_mode == BLOCK_MODE) ? NXPWIFI_SDIO_BLOCK_SIZE : 1; + u32 blk_cnt = + (blk_mode == + BLOCK_MODE) ? (pkt_len / + NXPWIFI_SDIO_BLOCK_SIZE) : pkt_len; + u32 ioport = (port & NXPWIFI_SDIO_IO_PORT_MASK); + + if (test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) { + nxpwifi_dbg(adapter, ERROR, + "%s: not allowed while suspended\n", __func__); + return -EPERM; + } + + sdio_claim_host(card->func); + + ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); + + sdio_release_host(card->func); + + return ret; +} + +/* This function reads multiple data from SDIO card memory. + */ +static int nxpwifi_read_data_sync(struct nxpwifi_adapter *adapter, u8 *buffer, + u32 len, u32 port, u8 claim) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 blk_mode = (port & NXPWIFI_SDIO_BYTE_MODE_MASK) ? BYTE_MODE + : BLOCK_MODE; + u32 blk_size = (blk_mode == BLOCK_MODE) ? NXPWIFI_SDIO_BLOCK_SIZE : 1; + u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / NXPWIFI_SDIO_BLOCK_SIZE) + : len; + u32 ioport = (port & NXPWIFI_SDIO_IO_PORT_MASK); + + if (claim) + sdio_claim_host(card->func); + + ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); + + if (claim) + sdio_release_host(card->func); + + return ret; +} + +/* This function reads the firmware status. + */ +static int +nxpwifi_sdio_read_fw_status(struct nxpwifi_adapter *adapter, u16 *dat) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + u8 fws0, fws1; + int ret; + + ret = nxpwifi_read_reg(adapter, reg->status_reg_0, &fws0); + if (ret) + return ret; + + ret = nxpwifi_read_reg(adapter, reg->status_reg_1, &fws1); + if (ret) + return ret; + + *dat = (u16)((fws1 << 8) | fws0); + return ret; +} + +/* This function checks the firmware status in card. + */ +static int nxpwifi_check_fw_status(struct nxpwifi_adapter *adapter, + u32 poll_num) +{ + int ret = 0; + u16 firmware_stat = 0; + u32 tries; + + for (tries = 0; tries < poll_num; tries++) { + ret = nxpwifi_sdio_read_fw_status(adapter, &firmware_stat); + if (ret) + continue; + if (firmware_stat == FIRMWARE_READY_SDIO) { + ret = 0; + break; + } + + msleep(100); + ret = -EPERM; + } + + if (firmware_stat == FIRMWARE_READY_SDIO) + /* firmware might pretend to be ready, when it's not. + * Wait a little bit more as a workaround. + */ + msleep(100); + + return ret; +} + +/* This function checks if WLAN is the winner. + */ +static int nxpwifi_check_winner_status(struct nxpwifi_adapter *adapter) +{ + int ret; + u8 winner = 0; + struct sdio_mmc_card *card = adapter->card; + + ret = nxpwifi_read_reg(adapter, card->reg->status_reg_0, &winner); + if (ret) + return ret; + + if (winner) + adapter->winner = 0; + else + adapter->winner = 1; + + return ret; +} + +/* SDIO remove. + * + * This function removes the interface and frees up the card structure. + */ +static void +nxpwifi_sdio_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + struct nxpwifi_adapter *adapter; + struct nxpwifi_private *priv; + int ret = 0; + u16 firmware_stat; + + card = sdio_get_drvdata(func); + if (!card) + return; + + wait_for_completion(&card->fw_done); + + adapter = card->adapter; + if (!adapter || !adapter->priv_num) + return; + + nxpwifi_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); + + ret = nxpwifi_sdio_read_fw_status(adapter, &firmware_stat); + if (!ret && firmware_stat == FIRMWARE_READY_SDIO) { + nxpwifi_deauthenticate_all(adapter); + + priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + nxpwifi_disable_auto_ds(priv); + nxpwifi_init_shutdown_fw(priv, NXPWIFI_FUNC_SHUTDOWN); + } + + nxpwifi_remove_card(adapter); +} + +/* SDIO suspend. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a host + * sleep activate request to the firmware and turns off the traffic. + */ +static int nxpwifi_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct nxpwifi_adapter *adapter; + mmc_pm_flag_t pm_flag = 0; + int ret = 0; + + pm_flag = sdio_get_host_pm_caps(func); + pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", + sdio_func_id(func), pm_flag); + if (!(pm_flag & MMC_PM_KEEP_POWER)) { + dev_err(dev, + "%s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -EPERM; + } + + card = sdio_get_drvdata(func); + if (!card) { + dev_err(dev, "suspend: invalid card\n"); + return 0; + } + + /* Might still be loading firmware */ + wait_for_completion(&card->fw_done); + + adapter = card->adapter; + if (!adapter) { + dev_err(dev, "adapter is not valid\n"); + return 0; + } + + if (!adapter->is_up) + return -EBUSY; + + nxpwifi_enable_wake(adapter); + + /* Enable the Host Sleep */ + if (!nxpwifi_enable_hs(adapter)) { + nxpwifi_dbg(adapter, ERROR, + "cmd: failed to suspend\n"); + clear_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags); + nxpwifi_disable_wake(adapter); + return -EPERM; + } + + nxpwifi_dbg(adapter, INFO, + "cmd: suspend with MMC_PM_KEEP_POWER\n"); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + + /* Indicate device suspended */ + set_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + clear_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags); + + return ret; +} + +static void nxpwifi_sdio_coredump(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + + card = sdio_get_drvdata(func); + if (!test_and_set_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, + &card->work_flags)) + nxpwifi_queue_work(card->adapter, &card->work); +} + +/* WLAN IDs */ +static const struct sdio_device_id nxpwifi_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_NXP, SDIO_DEVICE_ID_NXP_IW61X), + .driver_data = (unsigned long)&nxpwifi_sdio_iw61x}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, nxpwifi_ids); + +static const struct dev_pm_ops nxpwifi_sdio_pm_ops = { + .suspend = nxpwifi_sdio_suspend, + .resume = nxpwifi_sdio_resume, +}; + +static struct sdio_driver nxpwifi_sdio = { + .name = "nxpwifi_sdio", + .id_table = nxpwifi_ids, + .probe = nxpwifi_sdio_probe, + .remove = nxpwifi_sdio_remove, + .drv = { + .owner = THIS_MODULE, + .coredump = nxpwifi_sdio_coredump, + .pm = &nxpwifi_sdio_pm_ops, + } +}; + +/* This function wakes up the card. + * + * A host power up command is written to the card configuration + * register to wake up the card. + */ +static int nxpwifi_pm_wakeup_card(struct nxpwifi_adapter *adapter) +{ + nxpwifi_dbg(adapter, EVENT, + "event: wakeup device...\n"); + + return nxpwifi_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); +} + +/* This function is called after the card has woken up. + * + * The card configuration register is reset. + */ +static int nxpwifi_pm_wakeup_card_complete(struct nxpwifi_adapter *adapter) +{ + nxpwifi_dbg(adapter, EVENT, + "cmd: wakeup device completed\n"); + + return nxpwifi_write_reg(adapter, CONFIGURATION_REG, 0); +} + +static int nxpwifi_sdio_dnld_fw(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + + sdio_claim_host(card->func); + ret = nxpwifi_dnld_fw(adapter, fw); + sdio_release_host(card->func); + + return ret; +} + +/* This function is used to initialize IO ports for the + * chipsets supporting SDIO new mode. + */ +static int nxpwifi_init_sdio_new_mode(struct nxpwifi_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card = adapter->card; + int ret; + + adapter->ioport = MEM_PORT; + + /* enable sdio new mode */ + ret = nxpwifi_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®); + if (ret) + return ret; + ret = nxpwifi_write_reg(adapter, card->reg->card_cfg_2_1_reg, + reg | CMD53_NEW_MODE); + if (ret) + return ret; + + /* Configure cmd port and enable reading rx length from the register */ + ret = nxpwifi_read_reg(adapter, card->reg->cmd_cfg_0, ®); + if (ret) + return ret; + ret = nxpwifi_write_reg(adapter, card->reg->cmd_cfg_0, + reg | CMD_PORT_RD_LEN_EN); + if (ret) + return ret; + + /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is + * completed + */ + ret = nxpwifi_read_reg(adapter, card->reg->cmd_cfg_1, ®); + if (ret) + return ret; + ret = nxpwifi_write_reg(adapter, card->reg->cmd_cfg_1, + reg | CMD_PORT_AUTO_EN); + + return ret; +} + +/* This function initializes the IO ports. + * + * The following operations are performed - + * - Read the IO ports (0, 1 and 2) + * - Set host interrupt Reset-To-Read to clear + * - Set auto re-enable interrupt + */ +static int nxpwifi_init_sdio_ioport(struct nxpwifi_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card = adapter->card; + int ret; + + ret = nxpwifi_init_sdio_new_mode(adapter); + if (ret) + return ret; + + nxpwifi_dbg(adapter, INFO, + "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); + + /* Set Host interrupt reset to read to clear */ + ret = nxpwifi_read_reg(adapter, card->reg->host_int_rsr_reg, ®); + if (ret) + return ret; + ret = nxpwifi_write_reg(adapter, card->reg->host_int_rsr_reg, + reg | card->reg->sdio_int_mask); + if (ret) + return ret; + + /* Dnld/Upld ready set to auto reset */ + ret = nxpwifi_read_reg(adapter, card->reg->card_misc_cfg_reg, ®); + if (ret) + return ret; + ret = nxpwifi_write_reg(adapter, card->reg->card_misc_cfg_reg, + reg | AUTO_RE_ENABLE_INT); + + return ret; +} + +/* This function sends data to the card. + */ +static int nxpwifi_write_data_to_card(struct nxpwifi_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port) +{ + u32 i = 0; + int ret; + + do { + ret = nxpwifi_write_data_sync(adapter, payload, pkt_len, port); + if (ret) { + i++; + nxpwifi_dbg(adapter, ERROR, + "host_to_card, write iomem\t" + "(%d) failed: %d\n", i, ret); + if (nxpwifi_write_reg(adapter, CONFIGURATION_REG, 0x04)) + nxpwifi_dbg(adapter, ERROR, + "write CFG reg failed\n"); + + if (i > MAX_WRITE_IOMEM_RETRY) + return ret; + } + } while (ret); + + return ret; +} + +/* This function gets the read port. + * + * If control port bit is set in MP read bitmap, the control port + * is returned, otherwise the current read port is returned and + * the value is increased (provided it does not reach the maximum + * limit, in which case it is reset to 1) + */ +static int nxpwifi_get_rd_port(struct nxpwifi_adapter *adapter, u8 *port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + u32 rd_bitmap = card->mp_rd_bitmap; + + nxpwifi_dbg(adapter, DATA, + "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (!(rd_bitmap & reg->data_port_mask)) + return -EINVAL; + + if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) + return -EINVAL; + + /* We are now handling the SDIO data ports */ + card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); + *port = card->curr_rd_port; + + if (++card->curr_rd_port == card->max_ports) + card->curr_rd_port = reg->start_rd_port; + + nxpwifi_dbg(adapter, DATA, + "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", + *port, rd_bitmap, card->mp_rd_bitmap); + + return 0; +} + +/* This function gets the write port for data. + * + * The current write port is returned if available and the value is + * increased (provided it does not reach the maximum limit, in which + * case it is reset to 1) + */ +static int nxpwifi_get_wr_port_data(struct nxpwifi_adapter *adapter, u32 *port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + u32 wr_bitmap = card->mp_wr_bitmap; + + nxpwifi_dbg(adapter, DATA, + "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); + + if (!(wr_bitmap & card->mp_data_port_mask)) { + adapter->data_sent = true; + return -EBUSY; + } + + if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { + card->mp_wr_bitmap &= (u32)(~(1 << card->curr_wr_port)); + *port = card->curr_wr_port; + if (++card->curr_wr_port == card->mp_end_port) + card->curr_wr_port = reg->start_wr_port; + } else { + adapter->data_sent = true; + return -EBUSY; + } + + nxpwifi_dbg(adapter, DATA, + "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *port, wr_bitmap, card->mp_wr_bitmap); + + return 0; +} + +/* This function polls the card status. + */ +static int +nxpwifi_sdio_poll_card_status(struct nxpwifi_adapter *adapter, u8 bits) +{ + struct sdio_mmc_card *card = adapter->card; + u32 tries; + u8 cs; + int ret; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = nxpwifi_read_reg(adapter, card->reg->poll_reg, &cs); + if (ret) + break; + else if ((cs & bits) == bits) + return 0; + + usleep_range(10, 20); + } + + nxpwifi_dbg(adapter, ERROR, + "poll card status failed, tries = %d\n", tries); + + return ret; +} + +/* This function disables the host interrupt. + * + * The host interrupt mask is read, the disable bit is reset and + * written back to the card host interrupt mask register. + */ +static void nxpwifi_sdio_disable_host_int(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + + sdio_claim_host(func); + nxpwifi_write_reg_locked(func, card->reg->host_int_mask_reg, 0); + sdio_release_irq(func); + sdio_release_host(func); +} + +/* This function reads the interrupt status from card. + */ +static void nxpwifi_interrupt_status(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + u8 sdio_ireg; + unsigned long flags; + + if (nxpwifi_read_data_sync(adapter, card->mp_regs, + card->reg->max_mp_regs, + REG_PORT | NXPWIFI_SDIO_BYTE_MODE_MASK, 0)) { + nxpwifi_dbg(adapter, ERROR, "read mp_regs failed\n"); + return; + } + + sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; + if (sdio_ireg) { + nxpwifi_dbg(adapter, INTR, + "int: sdio_ireg = %#x\n", sdio_ireg); + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= sdio_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + } +} + +/* SDIO interrupt handler. + * + * This function reads the interrupt status from firmware and handles + * the interrupt in current thread (ksdioirqd) right away. + */ +static void +nxpwifi_sdio_interrupt(struct sdio_func *func) +{ + struct nxpwifi_adapter *adapter; + struct sdio_mmc_card *card; + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_err("int: func=%p card=%p adapter=%p\n", + func, card, card ? card->adapter : NULL); + return; + } + adapter = card->adapter; + + if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + + nxpwifi_interrupt_status(adapter); + nxpwifi_main_process(adapter); +} + +/* This function enables the host interrupt. + * + * The host interrupt enable mask is written to the card + * host interrupt mask register. + */ +static int nxpwifi_sdio_enable_host_int(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + int ret; + + sdio_claim_host(func); + + /* Request the SDIO IRQ */ + ret = sdio_claim_irq(func, nxpwifi_sdio_interrupt); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "claim irq failed: ret=%d\n", ret); + goto done; + } + + /* Simply write the mask to the register */ + ret = nxpwifi_write_reg_locked(func, card->reg->host_int_mask_reg, + card->reg->host_int_enable); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "enable host interrupt failed\n"); + sdio_release_irq(func); + } + +done: + sdio_release_host(func); + return ret; +} + +/* This function gets a data buffer from the card. + */ +static int nxpwifi_sdio_card_to_host(struct nxpwifi_adapter *adapter, + u32 *type, u8 *buffer, + u32 npayload, u32 ioport) +{ + int ret; + u32 nb; + + if (!buffer) { + nxpwifi_dbg(adapter, ERROR, + "%s: buffer is NULL\n", __func__); + return -EINVAL; + } + + ret = nxpwifi_read_data_sync(adapter, buffer, npayload, ioport, 1); + + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: read iomem failed: %d\n", __func__, + ret); + return ret; + } + + nb = get_unaligned_le16((buffer)); + if (nb > npayload) { + nxpwifi_dbg(adapter, ERROR, + "%s: invalid packet, nb=%d npayload=%d\n", + __func__, nb, npayload); + return -EINVAL; + } + + *type = get_unaligned_le16((buffer + 2)); + + return ret; +} + +/* This function downloads the firmware to the card. + * + * Firmware is downloaded to the card in blocks. Every block download + * is tested for CRC errors, and retried a number of times before + * returning failure. + */ +static int nxpwifi_prog_fw_w_helper(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + int ret; + u8 *firmware = fw->fw_buf; + u32 firmware_len = fw->fw_len; + u32 offset = 0; + u8 base0, base1; + u8 *fwbuf; + u16 len = 0; + u32 txlen, tx_blocks = 0, tries; + u32 i = 0; + + if (!firmware_len) { + nxpwifi_dbg(adapter, ERROR, + "firmware image not found! Terminating download\n"); + return -EINVAL; + } + + nxpwifi_dbg(adapter, INFO, + "info: downloading FW image (%d bytes)\n", + firmware_len); + + /* Assume that the allocated buffer is 8-byte aligned */ + fwbuf = kzalloc(NXPWIFI_UPLD_SIZE, GFP_KERNEL); + if (!fwbuf) + return -ENOMEM; + + sdio_claim_host(card->func); + + /* Perform firmware data transfer */ + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY + * bits + */ + ret = nxpwifi_sdio_poll_card_status(adapter, CARD_IO_READY | + DN_LD_CARD_RDY); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "FW download with helper:\t" + "poll status timeout @ %d\n", offset); + goto done; + } + + /* More data? */ + if (offset >= firmware_len) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = nxpwifi_read_reg(adapter, reg->base_0_reg, + &base0); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "dev BASE0 register read failed:\t" + "base0=%#04X(%d). Terminating dnld\n", + base0, base0); + goto done; + } + ret = nxpwifi_read_reg(adapter, reg->base_1_reg, + &base1); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "dev BASE1 register read failed:\t" + "base1=%#04X(%d). Terminating dnld\n", + base1, base1); + goto done; + } + len = (u16)(((base1 & 0xff) << 8) | (base0 & 0xff)); + + if (len) + break; + + usleep_range(10, 20); + } + + if (!len) { + break; + } else if (len > NXPWIFI_UPLD_SIZE) { + nxpwifi_dbg(adapter, ERROR, + "FW dnld failed @ %d, invalid length %d\n", + offset, len); + ret = -EINVAL; + goto done; + } + + txlen = len; + + if (len & BIT(0)) { + i++; + if (i > MAX_WRITE_IOMEM_RETRY) { + nxpwifi_dbg(adapter, ERROR, + "FW dnld failed @ %d, over max retry\n", + offset); + ret = -EIO; + goto done; + } + nxpwifi_dbg(adapter, ERROR, + "CRC indicated by the helper:\t" + "len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last + * block + */ + if (firmware_len - offset < txlen) + txlen = firmware_len - offset; + + tx_blocks = (txlen + NXPWIFI_SDIO_BLOCK_SIZE - 1) + / NXPWIFI_SDIO_BLOCK_SIZE; + + /* Copy payload to buffer */ + memmove(fwbuf, &firmware[offset], txlen); + } + + ret = nxpwifi_write_data_sync(adapter, fwbuf, tx_blocks * + NXPWIFI_SDIO_BLOCK_SIZE, + adapter->ioport); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "FW download, write iomem (%d) failed @ %d\n", + i, offset); + if (nxpwifi_write_reg(adapter, CONFIGURATION_REG, 0x04)) + nxpwifi_dbg(adapter, ERROR, + "write CFG reg failed\n"); + + goto done; + } + + offset += txlen; + } while (true); + + nxpwifi_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", offset); + + ret = 0; +done: + sdio_release_host(card->func); + kfree(fwbuf); + return ret; +} + +/* This function decodes sdio aggregation pkt. + * + * Based on the data block size and pkt_len, + * skb data will be decoded to few packets. + */ +static void nxpwifi_deaggr_sdio_pkt(struct nxpwifi_adapter *adapter, + struct sk_buff *skb) +{ + u32 total_pkt_len, pkt_len; + struct sk_buff *skb_deaggr; + u16 blk_size; + u8 blk_num; + u8 *data; + + data = skb->data; + total_pkt_len = skb->len; + + while (total_pkt_len >= (SDIO_HEADER_OFFSET + adapter->intf_hdr_len)) { + if (total_pkt_len < adapter->sdio_rx_block_size) + break; + blk_num = *(data + BLOCK_NUMBER_OFFSET); + blk_size = adapter->sdio_rx_block_size * blk_num; + if (blk_size > total_pkt_len) { + nxpwifi_dbg(adapter, ERROR, + "%s: error in blk_size,\t" + "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", + __func__, blk_num, blk_size, total_pkt_len); + break; + } + pkt_len = get_unaligned_le16((data + + SDIO_HEADER_OFFSET)); + if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { + nxpwifi_dbg(adapter, ERROR, + "%s: error in pkt_len,\t" + "pkt_len=%d, blk_size=%d\n", + __func__, pkt_len, blk_size); + break; + } + + skb_deaggr = nxpwifi_alloc_dma_align_buf(pkt_len, GFP_KERNEL); + if (!skb_deaggr) + break; + skb_put(skb_deaggr, pkt_len); + memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); + skb_pull(skb_deaggr, adapter->intf_hdr_len); + + nxpwifi_handle_rx_packet(adapter, skb_deaggr); + data += blk_size; + total_pkt_len -= blk_size; + } +} + +/* This function decodes a received packet. + * + * Based on the type, the packet is treated as either a data, or + * a command response, or an event, and the correct handler + * function is invoked. + */ +static void nxpwifi_decode_rx_packet(struct nxpwifi_adapter *adapter, + struct sk_buff *skb, u32 upld_typ) +{ + u8 *cmd_buf; + u16 pkt_len; + struct nxpwifi_rxinfo *rx_info; + + pkt_len = get_unaligned_le16(skb->data); + + if (upld_typ != NXPWIFI_TYPE_AGGR_DATA) { + skb_trim(skb, pkt_len); + skb_pull(skb, adapter->intf_hdr_len); + } + + switch (upld_typ) { + case NXPWIFI_TYPE_AGGR_DATA: + nxpwifi_dbg(adapter, INFO, + "info: --- Rx: Aggr Data packet ---\n"); + rx_info = NXPWIFI_SKB_RXCB(skb); + rx_info->buf_type = NXPWIFI_TYPE_AGGR_DATA; + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + tasklet_schedule(&adapter->rx_task); + break; + + case NXPWIFI_TYPE_DATA: + nxpwifi_dbg(adapter, DATA, + "info: --- Rx: Data packet ---\n"); + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + tasklet_schedule(&adapter->rx_task); + break; + + case NXPWIFI_TYPE_CMD: + nxpwifi_dbg(adapter, CMD, + "info: --- Rx: Cmd Response ---\n"); + /* take care of curr_cmd = NULL case */ + if (!adapter->curr_cmd) { + cmd_buf = adapter->upld_buf; + + if (adapter->ps_state == PS_STATE_SLEEP_CFM) + nxpwifi_process_sleep_confirm_resp(adapter, + skb->data, + skb->len); + + memcpy(cmd_buf, skb->data, + min_t(u32, NXPWIFI_SIZE_OF_CMD_BUFFER, + skb->len)); + + dev_kfree_skb_any(skb); + } else { + adapter->cmd_resp_received = true; + adapter->curr_cmd->resp_skb = skb; + } + break; + + case NXPWIFI_TYPE_EVENT: + nxpwifi_dbg(adapter, EVENT, + "info: --- Rx: Event ---\n"); + adapter->event_cause = get_unaligned_le32(skb->data); + + if (skb->len > 0 && skb->len < MAX_EVENT_SIZE) + memcpy(adapter->event_body, + skb->data + NXPWIFI_EVENT_HEADER_LEN, + skb->len); + + /* event cause has been saved to adapter->event_cause */ + adapter->event_received = true; + adapter->event_skb = skb; + + break; + + default: + nxpwifi_dbg(adapter, ERROR, + "unknown upload type %#x\n", upld_typ); + dev_kfree_skb_any(skb); + break; + } +} + +/* This function transfers received packets from card to driver, performing + * aggregation if required. + * + * For data received on control port, or if aggregation is disabled, the + * received buffers are uploaded as separate packets. However, if aggregation + * is enabled and required, the buffers are copied onto an aggregation buffer, + * provided there is space left, processed and finally uploaded. + */ +static int nxpwifi_sdio_card_to_host_mp_aggr(struct nxpwifi_adapter *adapter, + u16 rx_len, u8 port) +{ + struct sdio_mmc_card *card = adapter->card; + s32 f_do_rx_aggr = 0; + s32 f_do_rx_cur = 0; + s32 f_aggr_cur = 0; + s32 f_post_aggr_cur = 0; + struct sk_buff *skb_deaggr; + struct sk_buff *skb = NULL; + u32 pkt_len, pkt_type, mport, pind; + u8 *curr_ptr; + int ret = 0; + + if (!card->mpa_rx.enabled) { + nxpwifi_dbg(adapter, WARN, + "info: %s: rx aggregation disabled\n", + __func__); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + if (card->mp_rd_bitmap & card->reg->data_port_mask) { + /* Some more data RX pending */ + nxpwifi_dbg(adapter, INFO, + "info: %s: not last packet\n", __func__); + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { + f_aggr_cur = 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_aggr = 1; + f_post_aggr_cur = 1; + } + } else { + /* Rx aggr not in progress */ + f_aggr_cur = 1; + } + + } else { + /* No more data RX pending */ + nxpwifi_dbg(adapter, INFO, + "info: %s: last packet\n", __func__); + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + f_do_rx_aggr = 1; + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) + f_aggr_cur = 1; + else + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_cur = 1; + } else { + f_do_rx_cur = 1; + } + } + + if (f_aggr_cur) { + nxpwifi_dbg(adapter, INFO, + "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + + if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || + mp_rx_aggr_port_limit_reached(card)) { + nxpwifi_dbg(adapter, INFO, + "info: %s: aggregated packet\t" + "limit reached\n", __func__); + /* No more pkts allowed in Aggr buf, rx it */ + f_do_rx_aggr = 1; + } + } + + if (f_do_rx_aggr) { + u32 port_count; + int i; + + /* do aggr RX now */ + nxpwifi_dbg(adapter, DATA, + "info: do_rx_aggr: num of packets: %d\n", + card->mpa_rx.pkt_cnt); + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_rx.ports & BIT(i)) + port_count++; + + /* Reading data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_rx.start_port; + + if (card->mpa_rx.pkt_cnt == 1) + mport = adapter->ioport + card->mpa_rx.start_port; + + ret = nxpwifi_read_data_sync(adapter, card->mpa_rx.buf, + card->mpa_rx.buf_len, mport, 1); + if (ret) + goto error; + + curr_ptr = card->mpa_rx.buf; + + for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { + u32 *len_arr = card->mpa_rx.len_arr; + + /* get curr PKT len & type */ + pkt_len = get_unaligned_le16(&curr_ptr[0]); + pkt_type = get_unaligned_le16(&curr_ptr[2]); + + /* copy pkt to deaggr buf */ + skb_deaggr = nxpwifi_alloc_dma_align_buf(len_arr[pind], + GFP_KERNEL); + if (!skb_deaggr) { + nxpwifi_dbg(adapter, ERROR, "skb allocation failure\t" + "drop pkt len=%d type=%d\n", + pkt_len, pkt_type); + curr_ptr += len_arr[pind]; + continue; + } + + skb_put(skb_deaggr, len_arr[pind]); + + if ((pkt_type == NXPWIFI_TYPE_DATA || + (pkt_type == NXPWIFI_TYPE_AGGR_DATA && + adapter->sdio_rx_aggr_enable)) && + pkt_len <= len_arr[pind]) { + memcpy(skb_deaggr->data, curr_ptr, pkt_len); + + skb_trim(skb_deaggr, pkt_len); + + /* Process de-aggr packet */ + nxpwifi_decode_rx_packet(adapter, skb_deaggr, + pkt_type); + } else { + nxpwifi_dbg(adapter, ERROR, + "drop wrong aggr pkt:\t" + "sdio_single_port_rx_aggr=%d\t" + "type=%d len=%d max_len=%d\n", + adapter->sdio_rx_aggr_enable, + pkt_type, pkt_len, len_arr[pind]); + dev_kfree_skb_any(skb_deaggr); + } + curr_ptr += len_arr[pind]; + } + MP_RX_AGGR_BUF_RESET(card); + } + +rx_curr_single: + if (f_do_rx_cur) { + nxpwifi_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", + port, rx_len); + + skb = nxpwifi_alloc_dma_align_buf(rx_len, GFP_KERNEL); + if (!skb) { + nxpwifi_dbg(adapter, ERROR, + "single skb allocated fail,\t" + "drop pkt port=%d len=%d\n", port, rx_len); + ret = nxpwifi_sdio_card_to_host(adapter, &pkt_type, + card->mpa_rx.buf, + rx_len, + adapter->ioport + port); + if (ret) + goto error; + return 0; + } + + skb_put(skb, rx_len); + + ret = nxpwifi_sdio_card_to_host(adapter, &pkt_type, + skb->data, skb->len, + adapter->ioport + port); + if (ret) + goto error; + if (!adapter->sdio_rx_aggr_enable && + pkt_type == NXPWIFI_TYPE_AGGR_DATA) { + nxpwifi_dbg(adapter, ERROR, "drop wrong pkt type %d\t" + "current SDIO RX Aggr not enabled\n", + pkt_type); + dev_kfree_skb_any(skb); + return 0; + } + + nxpwifi_decode_rx_packet(adapter, skb, pkt_type); + } + if (f_post_aggr_cur) { + nxpwifi_dbg(adapter, INFO, + "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + } + + return 0; +error: + if (MP_RX_AGGR_IN_PROGRESS(card)) + MP_RX_AGGR_BUF_RESET(card); + + if (f_do_rx_cur && skb) + /* Single transfer pending. Free curr buff also */ + dev_kfree_skb_any(skb); + + return ret; +} + +/* This function checks the current interrupt status. + * + * The following interrupts are checked and handled by this function - + * - Data sent + * - Command sent + * - Packets received + * + * Since the firmware does not generate download ready interrupt if the + * port updated is command port only, command sent interrupt checking + * should be done manually, and for every SDIO interrupt. + * + * In case of Rx packets received, the packets are uploaded from card to + * host and processed accordingly. + */ +static int nxpwifi_process_int_status(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + int ret = 0; + u8 sdio_ireg; + struct sk_buff *skb; + u8 port; + u32 len_reg_l, len_reg_u; + u32 rx_blocks; + u16 rx_len; + unsigned long flags; + u32 bitmap; + u8 cr; + + spin_lock_irqsave(&adapter->int_lock, flags); + sdio_ireg = adapter->int_status; + adapter->int_status = 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + if (!sdio_ireg) + return ret; + + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) + adapter->cmd_sent = false; + + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + u32 pkt_type; + + /* read the len of control packet */ + rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; + rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; + rx_blocks = DIV_ROUND_UP(rx_len, NXPWIFI_SDIO_BLOCK_SIZE); + if (rx_len <= adapter->intf_hdr_len || + (rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE) > + NXPWIFI_RX_DATA_BUF_SIZE) + return -EINVAL; + rx_len = (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE); + nxpwifi_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); + + skb = nxpwifi_alloc_dma_align_buf(rx_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_put(skb, rx_len); + + ret = nxpwifi_sdio_card_to_host(adapter, &pkt_type, skb->data, + skb->len, adapter->ioport | + CMD_PORT_SLCT); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: failed to card_to_host", __func__); + dev_kfree_skb_any(skb); + goto term_cmd; + } + + if (pkt_type != NXPWIFI_TYPE_CMD && + pkt_type != NXPWIFI_TYPE_EVENT) + nxpwifi_dbg(adapter, ERROR, + "%s:Received wrong packet on cmd port", + __func__); + + nxpwifi_decode_rx_packet(adapter, skb, pkt_type); + } + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + bitmap = (u32)card->mp_regs[reg->wr_bitmap_l]; + bitmap |= ((u32)card->mp_regs[reg->wr_bitmap_u]) << 8; + bitmap |= ((u32)card->mp_regs[reg->wr_bitmap_1l]) << 16; + bitmap |= ((u32)card->mp_regs[reg->wr_bitmap_1u]) << 24; + card->mp_wr_bitmap = bitmap; + + nxpwifi_dbg(adapter, INTR, + "int: DNLD: wr_bitmap=0x%x\n", + card->mp_wr_bitmap); + if (adapter->data_sent && + (card->mp_wr_bitmap & card->mp_data_port_mask)) { + nxpwifi_dbg(adapter, INTR, + "info: <--- Tx DONE Interrupt --->\n"); + adapter->data_sent = false; + } + } + + nxpwifi_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", + adapter->cmd_sent, adapter->data_sent); + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + bitmap = (u32)card->mp_regs[reg->rd_bitmap_l]; + bitmap |= ((u32)card->mp_regs[reg->rd_bitmap_u]) << 8; + bitmap |= ((u32)card->mp_regs[reg->rd_bitmap_1l]) << 16; + bitmap |= ((u32)card->mp_regs[reg->rd_bitmap_1u]) << 24; + card->mp_rd_bitmap = bitmap; + nxpwifi_dbg(adapter, INTR, + "int: UPLD: rd_bitmap=0x%x\n", + card->mp_rd_bitmap); + + while (true) { + ret = nxpwifi_get_rd_port(adapter, &port); + if (ret) { + nxpwifi_dbg(adapter, INFO, + "info: no more rd_port available\n"); + break; + } + len_reg_l = reg->rd_len_p0_l + (port << 1); + len_reg_u = reg->rd_len_p0_u + (port << 1); + rx_len = ((u16)card->mp_regs[len_reg_u]) << 8; + rx_len |= (u16)card->mp_regs[len_reg_l]; + nxpwifi_dbg(adapter, INFO, + "info: RX: port=%d rx_len=%u\n", + port, rx_len); + rx_blocks = + (rx_len + NXPWIFI_SDIO_BLOCK_SIZE - + 1) / NXPWIFI_SDIO_BLOCK_SIZE; + if (rx_len <= adapter->intf_hdr_len || + (card->mpa_rx.enabled && + ((rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE) > + card->mpa_rx.buf_size))) { + nxpwifi_dbg(adapter, ERROR, + "invalid rx_len=%d\n", + rx_len); + return -EINVAL; + } + + rx_len = (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE); + nxpwifi_dbg(adapter, INFO, "info: rx_len = %d\n", + rx_len); + + ret = nxpwifi_sdio_card_to_host_mp_aggr(adapter, rx_len, + port); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "card_to_host_mpa failed: int status=%#x\n", + sdio_ireg); + goto term_cmd; + } + } + } + + return 0; + +term_cmd: + /* terminate cmd */ + if (nxpwifi_read_reg(adapter, CONFIGURATION_REG, &cr)) + nxpwifi_dbg(adapter, ERROR, "read CFG reg failed\n"); + else + nxpwifi_dbg(adapter, INFO, + "info: CFG reg val = %d\n", cr); + + if (nxpwifi_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) + nxpwifi_dbg(adapter, ERROR, + "write CFG reg failed\n"); + else + nxpwifi_dbg(adapter, INFO, "info: write success\n"); + + if (nxpwifi_read_reg(adapter, CONFIGURATION_REG, &cr)) + nxpwifi_dbg(adapter, ERROR, + "read CFG reg failed\n"); + else + nxpwifi_dbg(adapter, INFO, + "info: CFG reg val =%x\n", cr); + + return ret; +} + +/* This function aggregates transmission buffers in driver and downloads + * the aggregated packet to card. + * + * The individual packets are aggregated by copying into an aggregation + * buffer and then downloaded to the card. Previous unsent packets in the + * aggregation buffer are pre-copied first before new packets are added. + * Aggregation is done till there is space left in the aggregation buffer, + * or till new packets are available. + * + * The function will only download the packet to the card when aggregation + * stops, otherwise it will just aggregate the packet in aggregation buffer + * and return. + */ +static int nxpwifi_host_to_card_mp_aggr(struct nxpwifi_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port, + u32 next_pkt_len) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + s32 f_send_aggr_buf = 0; + s32 f_send_cur_buf = 0; + s32 f_precopy_cur_buf = 0; + s32 f_postcopy_cur_buf = 0; + u32 mport; + int index; + + if (!card->mpa_tx.enabled || port == CMD_PORT_SLCT) { + nxpwifi_dbg(adapter, WARN, + "info: %s: tx aggregation disabled\n", + __func__); + + f_send_cur_buf = 1; + goto tx_curr_single; + } + + if (next_pkt_len) { + /* More pkt in TX queue */ + nxpwifi_dbg(adapter, INFO, + "info: %s: more packets in queue.\n", + __func__); + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { + f_precopy_cur_buf = 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port)) || + !MP_TX_AGGR_BUF_HAS_ROOM + (card, pkt_len + next_pkt_len)) + f_send_aggr_buf = 1; + } else { + /* No room in Aggr buf, send it */ + f_send_aggr_buf = 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port))) + f_send_cur_buf = 1; + else + f_postcopy_cur_buf = 1; + } + } else { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && + (card->mp_wr_bitmap & (1 << card->curr_wr_port))) + f_precopy_cur_buf = 1; + else + f_send_cur_buf = 1; + } + } else { + /* Last pkt in TX queue */ + nxpwifi_dbg(adapter, INFO, + "info: %s: Last packet in Tx Queue.\n", + __func__); + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + /* some packs in Aggr buf already */ + f_send_aggr_buf = 1; + + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) + f_precopy_cur_buf = 1; + else + /* No room in Aggr buf, send it */ + f_send_cur_buf = 1; + } else { + f_send_cur_buf = 1; + } + } + + if (f_precopy_cur_buf) { + nxpwifi_dbg(adapter, DATA, + "data: %s: precopy current buffer\n", + __func__); + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + + if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || + mp_tx_aggr_port_limit_reached(card)) + /* No more pkts allowed in Aggr buf, send it */ + f_send_aggr_buf = 1; + } + + if (f_send_aggr_buf) { + u32 port_count; + int i; + + nxpwifi_dbg(adapter, DATA, + "data: %s: send aggr buffer: %d %d\n", + __func__, card->mpa_tx.start_port, + card->mpa_tx.ports); + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_tx.ports & BIT(i)) + port_count++; + + /* Writing data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_tx.start_port; + + if (card->mpa_tx.pkt_cnt == 1) + mport = adapter->ioport + card->mpa_tx.start_port; + + ret = nxpwifi_write_data_to_card(adapter, card->mpa_tx.buf, + card->mpa_tx.buf_len, mport); + + /* Save the last multi port tx aggregation info to debug log */ + index = adapter->dbg.last_sdio_mp_index; + index = (index + 1) % NXPWIFI_DBG_SDIO_MP_NUM; + adapter->dbg.last_sdio_mp_index = index; + adapter->dbg.last_mp_wr_ports[index] = mport; + adapter->dbg.last_mp_wr_bitmap[index] = card->mp_wr_bitmap; + adapter->dbg.last_mp_wr_len[index] = card->mpa_tx.buf_len; + adapter->dbg.last_mp_curr_wr_port[index] = card->curr_wr_port; + + MP_TX_AGGR_BUF_RESET(card); + } + +tx_curr_single: + if (f_send_cur_buf) { + nxpwifi_dbg(adapter, DATA, + "data: %s: send current buffer %d\n", + __func__, port); + ret = nxpwifi_write_data_to_card(adapter, payload, pkt_len, + adapter->ioport + port); + } + + if (f_postcopy_cur_buf) { + nxpwifi_dbg(adapter, DATA, + "data: %s: postcopy current buffer\n", + __func__); + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + } + + return ret; +} + +/* This function downloads data from driver to card. + * + * Both commands and data packets are transferred to the card by this + * function. + * + * This function adds the SDIO specific header to the front of the buffer + * before transferring. The header contains the length of the packet and + * the type. The firmware handles the packets based upon this set type. + */ +static int nxpwifi_sdio_host_to_card(struct nxpwifi_adapter *adapter, + u8 type, struct sk_buff *skb, + struct nxpwifi_tx_param *tx_param) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u32 buf_block_len; + u32 blk_size; + u32 port; + u8 *payload = (u8 *)skb->data; + u32 pkt_len = skb->len; + + /* Allocate buffer and copy payload */ + blk_size = NXPWIFI_SDIO_BLOCK_SIZE; + buf_block_len = (pkt_len + blk_size - 1) / blk_size; + put_unaligned_le16((u16)pkt_len, payload + 0); + put_unaligned_le16((u16)type, payload + 2); + + /* This is SDIO specific header + * u16 length, + * u16 type (NXPWIFI_TYPE_DATA = 0, NXPWIFI_TYPE_CMD = 1, + * NXPWIFI_TYPE_EVENT = 3) + */ + if (type == NXPWIFI_TYPE_DATA) { + ret = nxpwifi_get_wr_port_data(adapter, &port); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: no wr_port available\n", + __func__); + return ret; + } + } else { + adapter->cmd_sent = true; + + if (pkt_len <= adapter->intf_hdr_len || + pkt_len > NXPWIFI_UPLD_SIZE) + nxpwifi_dbg(adapter, ERROR, + "%s: payload=%p, nb=%d\n", + __func__, payload, pkt_len); + + port = CMD_PORT_SLCT; + } + + /* Transfer data to card */ + pkt_len = buf_block_len * blk_size; + + if (tx_param) + ret = nxpwifi_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, tx_param->next_pkt_len + ); + else + ret = nxpwifi_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, 0); + + if (ret) { + if (type == NXPWIFI_TYPE_CMD || + type == NXPWIFI_TYPE_VDLL) + adapter->cmd_sent = false; + if (type == NXPWIFI_TYPE_DATA) { + adapter->data_sent = false; + /* restore curr_wr_port in error cases */ + card->curr_wr_port = port; + card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); + } + } else { + if (type == NXPWIFI_TYPE_DATA) { + if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) + adapter->data_sent = true; + else + adapter->data_sent = false; + } + } + + return ret; +} + +/* This function allocates the MPA Tx and Rx buffers. + */ +static int nxpwifi_alloc_sdio_mpa_buffers(struct nxpwifi_adapter *adapter, + u32 mpa_tx_buf_size, + u32 mpa_rx_buf_size) +{ + struct sdio_mmc_card *card = adapter->card; + u32 rx_buf_size; + int ret = 0; + + card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); + if (!card->mpa_tx.buf) { + ret = -ENOMEM; + goto error; + } + + card->mpa_tx.buf_size = mpa_tx_buf_size; + + rx_buf_size = max_t(u32, mpa_rx_buf_size, + (u32)SDIO_MAX_AGGR_BUF_SIZE); + card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!card->mpa_rx.buf) { + ret = -ENOMEM; + goto error; + } + + card->mpa_rx.buf_size = rx_buf_size; + +error: + if (ret) { + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); + card->mpa_tx.buf_size = 0; + card->mpa_rx.buf_size = 0; + card->mpa_tx.buf = NULL; + card->mpa_rx.buf = NULL; + } + + return ret; +} + +/* This function unregisters the SDIO device. + * + * The function is disabled and driver + * data is set to null. + */ +static void +nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + if (adapter->card) { + card->adapter = NULL; + sdio_claim_host(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + } +} + +/* This function registers the SDIO device. + * + * SDIO IRQ is claimed, block size is set and driver data is initialized. + */ +static int nxpwifi_register_dev(struct nxpwifi_adapter *adapter) +{ + int ret; + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + const char *firmware = card->firmware; + + /* save adapter pointer in card */ + card->adapter = adapter; + adapter->tx_buf_size = card->tx_buf_size; + + sdio_claim_host(func); + + /* Set block size */ + ret = sdio_set_block_size(card->func, NXPWIFI_SDIO_BLOCK_SIZE); + sdio_release_host(func); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "cannot set SDIO block size\n"); + return ret; + } + + /* Select correct firmware (sdsd or sdiouart) firmware based on the strapping + * option + */ + if (card->firmware_sdiouart) { + u8 val; + + nxpwifi_read_reg(adapter, card->reg->host_strap_reg, &val); + if ((val & card->reg->host_strap_mask) == card->reg->host_strap_value) + firmware = card->firmware_sdiouart; + } + strscpy(adapter->fw_name, firmware, sizeof(adapter->fw_name)); + + if (card->fw_dump_enh) { + adapter->mem_type_mapping_tbl = generic_mem_type_map; + adapter->num_mem_types = 1; + } else { + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); + } + + return 0; +} + +/* This function initializes the SDIO driver. + * + * The following initializations steps are followed - + * - Read the Host interrupt status register to acknowledge + * the first interrupt got from bootloader + * - Disable host interrupt mask register + * - Get SDIO port + * - Initialize SDIO variables in card + * - Allocate MP registers + * - Allocate MPA Tx and Rx buffers + */ +static int nxpwifi_init_sdio(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + int ret; + u8 sdio_ireg; + + sdio_set_drvdata(card->func, card); + + /* Read the host_int_status_reg for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + nxpwifi_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); + + /* Get SDIO ioport */ + if (nxpwifi_init_sdio_ioport(adapter)) + return -EIO; + + /* Initialize SDIO variables in card */ + card->mp_rd_bitmap = 0; + card->mp_wr_bitmap = 0; + card->curr_rd_port = reg->start_rd_port; + card->curr_wr_port = reg->start_wr_port; + + card->mp_data_port_mask = reg->data_port_mask; + + card->mpa_tx.buf_len = 0; + card->mpa_tx.pkt_cnt = 0; + card->mpa_tx.start_port = 0; + + card->mpa_tx.enabled = 1; + card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; + + card->mpa_rx.buf_len = 0; + card->mpa_rx.pkt_cnt = 0; + card->mpa_rx.start_port = 0; + + card->mpa_rx.enabled = 1; + card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; + + /* Allocate buffers for SDIO MP-A */ + card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); + if (!card->mp_regs) + return -ENOMEM; + + card->mpa_rx.len_arr = kcalloc(card->mp_agg_pkt_limit, + sizeof(*card->mpa_rx.len_arr), + GFP_KERNEL); + if (!card->mpa_rx.len_arr) { + kfree(card->mp_regs); + return -ENOMEM; + } + + ret = nxpwifi_alloc_sdio_mpa_buffers(adapter, + card->mp_tx_agg_buf_size, + card->mp_rx_agg_buf_size); + + /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ + if (ret && (card->mp_tx_agg_buf_size == NXPWIFI_MP_AGGR_BSIZE_MAX || + card->mp_rx_agg_buf_size == NXPWIFI_MP_AGGR_BSIZE_MAX)) { + /* Disable rx single port aggregation */ + adapter->host_disable_sdio_rx_aggr = true; + + ret = nxpwifi_alloc_sdio_mpa_buffers(adapter, + NXPWIFI_MP_AGGR_BSIZE_32K, + NXPWIFI_MP_AGGR_BSIZE_32K); + if (ret) { + /* Disable multi port aggregation */ + card->mpa_tx.enabled = 0; + card->mpa_rx.enabled = 0; + } + } + + adapter->ext_scan = card->can_ext_scan; + return ret; +} + +/* This function resets the MPA Tx and Rx buffers. + */ +static void nxpwifi_cleanup_mpa_buf(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + MP_TX_AGGR_BUF_RESET(card); + MP_RX_AGGR_BUF_RESET(card); +} + +/* This function cleans up the allocated card buffers. + * + * The following are freed by this function - + * - MP registers + * - MPA Tx buffer + * - MPA Rx buffer + */ +static void nxpwifi_cleanup_sdio(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + cancel_work_sync(&card->work); + + kfree(card->mp_regs); + kfree(card->mpa_rx.len_arr); + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); +} + +/* This function updates the MP end port in card. + */ +static void +nxpwifi_update_mp_end_port(struct nxpwifi_adapter *adapter, u16 port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct nxpwifi_sdio_card_reg *reg = card->reg; + int i; + + card->mp_end_port = port; + + card->mp_data_port_mask = reg->data_port_mask; + + if (reg->start_wr_port) { + for (i = 1; i <= card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &= + ~(1 << (card->max_ports - i)); + } + + card->curr_wr_port = reg->start_wr_port; + + nxpwifi_dbg(adapter, CMD, + "cmd: mp_end_port %d, data port mask 0x%x\n", + port, card->mp_data_port_mask); +} + +static void nxpwifi_sdio_card_reset_work(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + int ret; + + /* Prepare the adapter for the reset. */ + nxpwifi_shutdown_sw(adapter); + clear_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, &card->work_flags); + clear_bit(NXPWIFI_IFACE_WORK_CARD_RESET, &card->work_flags); + + /* Run a HW reset of the SDIO interface. */ + sdio_claim_host(func); + ret = mmc_hw_reset(func->card); + sdio_release_host(func); + + switch (ret) { + case 1: + dev_dbg(&func->dev, "SDIO HW reset asynchronous\n"); + complete_all(adapter->fw_done); + break; + case 0: + ret = nxpwifi_reinit_sw(adapter); + if (ret) + dev_err(&func->dev, "reinit failed: %d\n", ret); + break; + default: + dev_err(&func->dev, "SDIO HW reset failed: %d\n", ret); + break; + } +} + +/* This function read/write firmware */ +static enum +rdwr_status nxpwifi_sdio_rdwr_firmware(struct nxpwifi_adapter *adapter, + u8 doneflag) +{ + struct sdio_mmc_card *card = adapter->card; + int ret, tries; + u8 ctrl_data = 0; + + sdio_writeb(card->func, card->reg->fw_dump_host_ready, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, + &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO read err\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == FW_DUMP_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != card->reg->fw_dump_host_ready) { + nxpwifi_dbg(adapter, WARN, + "The ctrl reg was changed, re-try again\n"); + sdio_writeb(card->func, card->reg->fw_dump_host_ready, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + if (ctrl_data == card->reg->fw_dump_host_ready) { + nxpwifi_dbg(adapter, ERROR, + "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/* This function dump firmware memory to file */ +static void nxpwifi_sdio_fw_dump(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + enum rdwr_status stat; + u32 memory_size; + + if (!card->can_dump_fw) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + nxpwifi_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump start ==\n"); + + stat = nxpwifi_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg = card->reg->fw_dump_start; + /* Read the number of the memories which will dump */ + dump_num = sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO read memory length err\n"); + goto done; + } + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = nxpwifi_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + memory_size = 0; + reg = card->reg->fw_dump_start; + for (i = 0; i < 4; i++) { + read_reg = sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO read err\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + + if (memory_size == 0) { + nxpwifi_dbg(adapter, DUMP, "Firmware dump Finished!\n"); + ret = nxpwifi_write_reg(adapter, + card->reg->fw_dump_ctrl, + FW_DUMP_READ_DONE); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO write err\n"); + return; + } + break; + } + + nxpwifi_dbg(adapter, DUMP, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) + goto done; + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + nxpwifi_dbg(adapter, DUMP, + "Start %s output, please wait...\n", + entry->mem_name); + + do { + stat = nxpwifi_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + nxpwifi_dbg(adapter, ERROR, + "Allocated buf not enough\n"); + } + + if (stat != RDWR_STATUS_DONE) + continue; + + nxpwifi_dbg(adapter, DUMP, "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (1); + } + nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump end ==\n"); + +done: + sdio_release_host(card->func); +} + +static void nxpwifi_sdio_generic_fw_dump(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct memory_type_mapping *entry = &generic_mem_type_map[0]; + unsigned int reg, reg_start, reg_end; + u8 start_flag = 0, done_flag = 0; + u8 *dbg_ptr, *end_ptr; + enum rdwr_status stat; + int ret = -EPERM, tries; + + if (!card->fw_dump_enh) + return; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + + nxpwifi_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump start ==\n"); + + stat = nxpwifi_sdio_rdwr_firmware(adapter, done_flag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + start_flag = sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + if (start_flag == 0) + break; + if (tries == MAX_POLL_TRIES) { + nxpwifi_dbg(adapter, ERROR, + "FW not ready to dump\n"); + ret = -EPERM; + goto done; + } + } + usleep_range(100, 200); + } + + entry->mem_ptr = vmalloc(0xf0000 + 1); + if (!entry->mem_ptr) { + ret = -ENOMEM; + goto done; + } + dbg_ptr = entry->mem_ptr; + entry->mem_size = 0xf0000; + end_ptr = dbg_ptr + entry->mem_size; + + done_flag = entry->done_flag; + nxpwifi_dbg(adapter, DUMP, + "Start %s output, please wait...\n", entry->mem_name); + + while (true) { + stat = nxpwifi_sdio_rdwr_firmware(adapter, done_flag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >= end_ptr) { + u8 *tmp_ptr; + + tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); + if (!tmp_ptr) + goto done; + + memcpy(tmp_ptr, entry->mem_ptr, + entry->mem_size); + vfree(entry->mem_ptr); + entry->mem_ptr = tmp_ptr; + tmp_ptr = NULL; + dbg_ptr = entry->mem_ptr + entry->mem_size; + entry->mem_size += 0x4000; + end_ptr = entry->mem_ptr + entry->mem_size; + } + } + if (stat == RDWR_STATUS_DONE) { + entry->mem_size = dbg_ptr - entry->mem_ptr; + nxpwifi_dbg(adapter, DUMP, "dump %s done size=0x%x\n", + entry->mem_name, entry->mem_size); + ret = 0; + break; + } + } + nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump end ==\n"); + +done: + if (ret) { + nxpwifi_dbg(adapter, ERROR, "firmware dump failed\n"); + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + sdio_release_host(card->func); +} + +static void nxpwifi_sdio_device_dump_work(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + adapter->devdump_data = vzalloc(NXPWIFI_FW_DUMP_SIZE); + if (!adapter->devdump_data) + return; + + nxpwifi_drv_info_dump(adapter); + if (card->fw_dump_enh) + nxpwifi_sdio_generic_fw_dump(adapter); + else + nxpwifi_sdio_fw_dump(adapter); + nxpwifi_prepare_fw_dump_info(adapter); + nxpwifi_upload_device_dump(adapter); +} + +static void nxpwifi_sdio_work(struct work_struct *work) +{ + struct sdio_mmc_card *card = + container_of(work, struct sdio_mmc_card, work); + + if (test_and_clear_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, + &card->work_flags)) + nxpwifi_sdio_device_dump_work(card->adapter); + if (test_and_clear_bit(NXPWIFI_IFACE_WORK_CARD_RESET, + &card->work_flags)) + nxpwifi_sdio_card_reset_work(card->adapter); +} + +/* This function resets the card */ +static void nxpwifi_sdio_card_reset(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + if (!test_and_set_bit(NXPWIFI_IFACE_WORK_CARD_RESET, &card->work_flags)) + nxpwifi_queue_work(adapter, &card->work); +} + +/* This function dumps FW information */ +static void nxpwifi_sdio_device_dump(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + if (!test_and_set_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, + &card->work_flags)) + nxpwifi_queue_work(adapter, &card->work); +} + +/* Function to dump SDIO function registers and SDIO scratch registers in case + * of FW crash + */ +static int +nxpwifi_sdio_reg_dump(struct nxpwifi_adapter *adapter, char *drv_buf) +{ + char *p = drv_buf; + struct sdio_mmc_card *cardp = adapter->card; + int ret = 0; + u8 count, func, data, index = 0, size = 0; + u8 reg, reg_start, reg_end; + char buf[256], *ptr; + + if (!p) + return 0; + + nxpwifi_dbg(adapter, MSG, "SDIO register dump start\n"); + + nxpwifi_pm_wakeup_card(adapter); + + sdio_claim_host(cardp->func); + + for (count = 0; count < 5; count++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + + switch (count) { + case 0: + /* Read the registers of SDIO function0 */ + func = count; + reg_start = 0; + reg_end = 9; + break; + case 1: + /* Read the registers of SDIO function1 */ + func = count; + reg_start = cardp->reg->func1_dump_reg_start; + reg_end = cardp->reg->func1_dump_reg_end; + break; + case 2: + index = 0; + func = 1; + reg_start = cardp->reg->func1_spec_reg_table[index++]; + size = cardp->reg->func1_spec_reg_num; + reg_end = cardp->reg->func1_spec_reg_table[size - 1]; + break; + default: + /* Read the scratch registers of SDIO function1 */ + if (count == 4) + mdelay(100); + func = 1; + reg_start = cardp->reg->func1_scratch_reg; + reg_end = reg_start + NXPWIFI_SDIO_SCRATCH_SIZE; + } + + if (count != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", + func, reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(cardp->func, reg, &ret); + else + data = sdio_readb(cardp->func, reg, &ret); + + if (count == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) { + ptr += sprintf(ptr, "%02x ", data); + } else { + ptr += sprintf(ptr, "ERR"); + break; + } + + if (count == 2 && reg < reg_end) + reg = cardp->reg->func1_spec_reg_table[index++]; + else + reg++; + } + + nxpwifi_dbg(adapter, MSG, "%s\n", buf); + p += sprintf(p, "%s\n", buf); + } + + sdio_release_host(cardp->func); + + nxpwifi_dbg(adapter, MSG, "SDIO register dump end\n"); + + return p - drv_buf; +} + +/* sdio device/function initialization, code is extracted + * from init_if handler and register_dev handler. + */ +static void nxpwifi_sdio_up_dev(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + u8 sdio_ireg; + + sdio_claim_host(card->func); + sdio_enable_func(card->func); + sdio_set_block_size(card->func, NXPWIFI_SDIO_BLOCK_SIZE); + sdio_release_host(card->func); + + /* tx_buf_size might be changed to 3584 by firmware during + * data transfer, we will reset to default size. + */ + adapter->tx_buf_size = card->tx_buf_size; + + /* Read the host_int_status_reg for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + nxpwifi_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); + + if (nxpwifi_init_sdio_ioport(adapter)) + dev_err(&card->func->dev, "error enabling SDIO port\n"); +} + +static struct nxpwifi_if_ops sdio_ops = { + .init_if = nxpwifi_init_sdio, + .cleanup_if = nxpwifi_cleanup_sdio, + .check_fw_status = nxpwifi_check_fw_status, + .check_winner_status = nxpwifi_check_winner_status, + .prog_fw = nxpwifi_prog_fw_w_helper, + .register_dev = nxpwifi_register_dev, + .unregister_dev = nxpwifi_unregister_dev, + .enable_int = nxpwifi_sdio_enable_host_int, + .disable_int = nxpwifi_sdio_disable_host_int, + .process_int_status = nxpwifi_process_int_status, + .host_to_card = nxpwifi_sdio_host_to_card, + .wakeup = nxpwifi_pm_wakeup_card, + .wakeup_complete = nxpwifi_pm_wakeup_card_complete, + + /* SDIO specific */ + .update_mp_end_port = nxpwifi_update_mp_end_port, + .cleanup_mpa_buf = nxpwifi_cleanup_mpa_buf, + .cmdrsp_complete = nxpwifi_sdio_cmdrsp_complete, + .event_complete = nxpwifi_sdio_event_complete, + .dnld_fw = nxpwifi_sdio_dnld_fw, + .card_reset = nxpwifi_sdio_card_reset, + .reg_dump = nxpwifi_sdio_reg_dump, + .device_dump = nxpwifi_sdio_device_dump, + .deaggr_pkt = nxpwifi_deaggr_sdio_pkt, + .up_dev = nxpwifi_sdio_up_dev, +}; + +module_sdio_driver(nxpwifi_sdio); + +MODULE_AUTHOR("NXP International Ltd."); +MODULE_DESCRIPTION("NXP WiFi SDIO Driver version " SDIO_VERSION); +MODULE_VERSION(SDIO_VERSION); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(IW61X_SDIO_FW_NAME); diff --git a/drivers/net/wireless/nxp/nxpwifi/sdio.h b/drivers/net/wireless/nxp/nxpwifi/sdio.h new file mode 100644 index 000000000000..de5c884a5b14 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sdio.h @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NXP Wireless LAN device driver: SDIO specific definitions + * + * Copyright 2011-2024 NXP + */ + +#ifndef _NXPWIFI_SDIO_H +#define _NXPWIFI_SDIO_H + +#include "main.h" + +#define IW61X_SDIO_FW_NAME "nxp/sd_w61x_v1.bin.se" + +#define BLOCK_MODE 1 +#define BYTE_MODE 0 + +#define NXPWIFI_SDIO_IO_PORT_MASK 0xfffff + +#define NXPWIFI_SDIO_BYTE_MODE_MASK 0x80000000 + +#define NXPWIFI_MAX_FUNC2_REG_NUM 13 +#define NXPWIFI_SDIO_SCRATCH_SIZE 10 + +#define SDIO_MPA_ADDR_BASE 0x1000 + +#define CMD_PORT_UPLD_INT_MASK (0x1U << 6) +#define CMD_PORT_DNLD_INT_MASK (0x1U << 7) +#define HOST_TERM_CMD53 (0x1U << 2) +#define REG_PORT 0 +#define MEM_PORT 0x10000 + +#define CMD53_NEW_MODE (0x1U << 0) +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +#define CMD_PORT_AUTO_EN (0x1U << 0) +#define CMD_PORT_SLCT 0x8000 +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) + +#define NXPWIFI_MP_AGGR_BSIZE_32K (32768) +/* we leave one block of 256 bytes for DMA alignment*/ +#define NXPWIFI_MP_AGGR_BSIZE_MAX (65280) + +/* Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT BIT(4) + +/* Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/* Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) + +/* Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/* Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) + +/* Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/* Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/* Host Control Registers : Host interrupt status */ +#define CARD_INT_STATUS_REG 0x28 + +/* Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/* Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/* Max retry number of CMD53 write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/* SDIO Tx aggregation in progress ? */ +#define MP_TX_AGGR_IN_PROGRESS(a) ((a)->mpa_tx.pkt_cnt > 0) + +/* SDIO Tx aggregation buffer room for next packet ? */ +#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ({ \ + typeof(a) (_a) = a; \ + (((_a)->mpa_tx.buf_len + (len)) <= (_a)->mpa_tx.buf_size); \ + }) + +/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ +#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ + typeof(a) (_a) = (a); \ + typeof(pkt_len) (_pkt_len) = pkt_len; \ + typeof(port) (_port) = port; \ + memmove(&(_a)->mpa_tx.buf[(_a)->mpa_tx.buf_len], \ + payload, (_pkt_len)); \ + (_a)->mpa_tx.buf_len += (_pkt_len); \ + if (!(_a)->mpa_tx.pkt_cnt) \ + (_a)->mpa_tx.start_port = (_port); \ + if ((_a)->mpa_tx.start_port <= (_port)) \ + (_a)->mpa_tx.ports |= (1 << ((_a)->mpa_tx.pkt_cnt)); \ + else \ + (_a)->mpa_tx.ports |= (1 << ((_a)->mpa_tx.pkt_cnt + 1 + \ + ((_a)->max_ports - \ + (_a)->mp_end_port))); \ + (_a)->mpa_tx.pkt_cnt++; \ +} while (0) + +/* SDIO Tx aggregation limit ? */ +#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) ({ \ + typeof(a) (_a) = a; \ + ((_a)->mpa_tx.pkt_cnt == (_a)->mpa_tx.pkt_aggr_limit); \ + }) + +/* Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) do { \ + typeof(a) (_a) = (a); \ + (_a)->mpa_tx.pkt_cnt = 0; \ + (_a)->mpa_tx.buf_len = 0; \ + (_a)->mpa_tx.ports = 0; \ + (_a)->mpa_tx.start_port = 0; \ +} while (0) + +/* SDIO Rx aggregation limit ? */ +#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) ({ \ + typeof(a) (_a) = a; \ + ((_a)->mpa_rx.pkt_cnt == (_a)->mpa_rx.pkt_aggr_limit); \ + }) + +/* SDIO Rx aggregation in progress ? */ +#define MP_RX_AGGR_IN_PROGRESS(a) ((a)->mpa_rx.pkt_cnt > 0) + +/* SDIO Rx aggregation buffer room for next packet ? */ +#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) ({ \ + typeof(a) (_a) = a; \ + ((((_a)->mpa_rx.buf_len + (rx_len))) <= (_a)->mpa_rx.buf_size); \ + }) + +/* Reset SDIO Rx aggregation buffer parameters */ +#define MP_RX_AGGR_BUF_RESET(a) do { \ + typeof(a) (_a) = (a); \ + (_a)->mpa_rx.pkt_cnt = 0; \ + (_a)->mpa_rx.buf_len = 0; \ + (_a)->mpa_rx.ports = 0; \ + (_a)->mpa_rx.start_port = 0; \ +} while (0) + +/* data structure for SDIO MPA TX */ +struct nxpwifi_sdio_mpa_tx { + /* multiport tx aggregation buffer pointer */ + u8 *buf; + u32 buf_len; + u32 pkt_cnt; + u32 ports; + u16 start_port; + u8 enabled; + u32 buf_size; + u32 pkt_aggr_limit; +}; + +struct nxpwifi_sdio_mpa_rx { + u8 *buf; + u32 buf_len; + u32 pkt_cnt; + u32 ports; + u16 start_port; + u32 *len_arr; + u8 enabled; + u32 buf_size; + u32 pkt_aggr_limit; +}; + +int nxpwifi_bus_register(void); +void nxpwifi_bus_unregister(void); + +struct nxpwifi_sdio_card_reg { + u8 start_rd_port; + u8 start_wr_port; + u8 base_0_reg; + u8 base_1_reg; + u8 poll_reg; + u8 host_int_enable; + u8 host_int_rsr_reg; + u8 host_int_status_reg; + u8 host_int_mask_reg; + u8 host_strap_reg; + u8 host_strap_mask; + u8 host_strap_value; + u8 status_reg_0; + u8 status_reg_1; + u8 sdio_int_mask; + u32 data_port_mask; + u8 io_port_0_reg; + u8 io_port_1_reg; + u8 io_port_2_reg; + u8 max_mp_regs; + u8 rd_bitmap_l; + u8 rd_bitmap_u; + u8 rd_bitmap_1l; + u8 rd_bitmap_1u; + u8 wr_bitmap_l; + u8 wr_bitmap_u; + u8 wr_bitmap_1l; + u8 wr_bitmap_1u; + u8 rd_len_p0_l; + u8 rd_len_p0_u; + u8 card_misc_cfg_reg; + u8 card_cfg_2_1_reg; + u8 cmd_rd_len_0; + u8 cmd_rd_len_1; + u8 cmd_rd_len_2; + u8 cmd_rd_len_3; + u8 cmd_cfg_0; + u8 cmd_cfg_1; + u8 cmd_cfg_2; + u8 cmd_cfg_3; + u8 fw_dump_host_ready; + u8 fw_dump_ctrl; + u8 fw_dump_start; + u8 fw_dump_end; + u8 func1_dump_reg_start; + u8 func1_dump_reg_end; + u8 func1_scratch_reg; + u8 func1_spec_reg_num; + u8 func1_spec_reg_table[NXPWIFI_MAX_FUNC2_REG_NUM]; +}; + +struct sdio_mmc_card { + struct sdio_func *func; + struct nxpwifi_adapter *adapter; + + struct completion fw_done; + const char *firmware; + const char *firmware_sdiouart; + const struct nxpwifi_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; + + u32 mp_rd_bitmap; + u32 mp_wr_bitmap; + + u16 mp_end_port; + u32 mp_data_port_mask; + + u8 curr_rd_port; + u8 curr_wr_port; + + u8 *mp_regs; + bool can_dump_fw; + bool fw_dump_enh; + bool can_ext_scan; + + struct nxpwifi_sdio_mpa_tx mpa_tx; + struct nxpwifi_sdio_mpa_rx mpa_rx; + + struct work_struct work; + unsigned long work_flags; +}; + +struct nxpwifi_sdio_device { + const char *firmware; + const char *firmware_sdiouart; + const struct nxpwifi_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; + bool can_dump_fw; + bool fw_dump_enh; + bool can_ext_scan; +}; + +/* .cmdrsp_complete handler + */ +static inline int nxpwifi_sdio_cmdrsp_complete(struct nxpwifi_adapter *adapter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +/* .event_complete handler + */ +static inline int nxpwifi_sdio_event_complete(struct nxpwifi_adapter *adapter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +static inline bool +mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u8 tmp; + + if (card->curr_rd_port < card->mpa_rx.start_port) { + tmp = card->mp_end_port >> 1; + + if (((card->max_ports - card->mpa_rx.start_port) + + card->curr_rd_port) >= tmp) + return true; + } + + if ((card->curr_rd_port - card->mpa_rx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +static inline bool +mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u16 tmp; + + if (card->curr_wr_port < card->mpa_tx.start_port) { + tmp = card->mp_end_port >> 1; + + if (((card->max_ports - card->mpa_tx.start_port) + + card->curr_wr_port) >= tmp) + return true; + } + + if ((card->curr_wr_port - card->mpa_tx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ +static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, + u16 rx_len, u8 port) +{ + card->mpa_rx.buf_len += rx_len; + + if (!card->mpa_rx.pkt_cnt) + card->mpa_rx.start_port = port; + + card->mpa_rx.ports |= (1 << port); + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len; + card->mpa_rx.pkt_cnt++; +} +#endif /* _NXPWIFI_SDIO_H */ From patchwork Mon Sep 30 06:36:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815437 X-Patchwork-Delegate: johannes@sipsolutions.net Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2044.outbound.protection.outlook.com [40.107.21.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0EFA16F8EF; Mon, 30 Sep 2024 06:38:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.44 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678333; cv=fail; b=uiY9loJnolzMX9/t/HTnBvB2BC23sm3z/VJ6/yyvmZp4Rv+QtwxE8dkrpFLrsIA6xM0fPWXouejInK3CtgdoeyaeMF8i2v9rpC9wWT6nLvkDfC/qSsWR84V/5cTNNHbQdqyIiGSMfcr599JQSgTaqEfBgY8QFfSgRXJrP+z6RGo= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678333; c=relaxed/simple; bh=K/PWdCIOFjYl48F4wUAEUoIRnXjZgwJwIzMptF08IcU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=tGF5dExvZYMmD3h96RYJUJAzvey2+FIHbTJsW6OW3DlhVCNFP9q/ZGqN2/wn5gt44+ZcGywQRyGzs2hj55uu6PSC1Z1VYNYeeY3zSvXP9bUkw//hJFyb/gpp6WVOQuJ1E67KnbBFRMDGaLim7dTW25UWxYnpHIgbOhb9ppqRzEQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=YFNCmXU2; arc=fail smtp.client-ip=40.107.21.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="YFNCmXU2" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=IiQ+ebtM3l7bh5n9uFdOfByv6eZJFTQN6VZSp7Wjrj9VHObCbXADfaRIdO4VobDd0iMc/ZSFUv4pD2+bHRruZJHPbV7Z3RTxnQNHzR7+cVhQteS0z9PWQgr3ilRLNQYNG91K0bhvCFC6LB+lD1mEstD+CESOyy4lBCsAaGbH8hTZE3/A6HlD4iy9DHELExaX/IJnFArpWEPZafGjVhDhbPiHSrJrnjIl2bWqrua2Ve4JkIPdvPBJEEgEJtbhBV9VQeDOGpYpIUXpuIk/23udZxm6ih2NgiTy4+XDe27mFCbSUzRdz8w60p+GMKB/cp4SIaPaT2RIW+qkk4adjVanpQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=M7F/5uCs4d57RrRXy5qSG/FWyRdjhvfQpSinluQft4I=; b=l5i8j5tTHQTOe9teqZT1thWL2UK+t+SMlTbgGr31YrUNFRt/S3cO//rA41/l+tRSdr1S/wSZqVFlusHOa3leFo3vdPc9AHazub8QjPO5X6Fi+wQu4e/GE20zQMsIFOFoyTwU15qO1mpxAAjtHr7LCuvClucLurWq0/bxgksm0s0qoFl/z86PZPpXEw+icFGxRhELXzGxYEWtr0Ilb3b9mMPoEl3CO0G+MpC1WLT6dsHGdIcGxTf3Fr9N4cJSIAiz9rTNEKs14LHXAlPO4me5KjfJCTXN/u0YECH9DZQnEf/NeUV1thtbN13KbrungdvM9M0CQery7kuyzFV7iwpa7w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=M7F/5uCs4d57RrRXy5qSG/FWyRdjhvfQpSinluQft4I=; b=YFNCmXU2k3JxOt9tbaH2jbZ3MmEuR1q6dAwF370xnlV0nT8NiQNnsumFSeke5khz0RPqm2HJp+NfjiI+Y8TBClsTNa1J+6O08MOtxijbsxtXa3SxzjsqsWVlSRiTxGJuvT2HGjSVwE/lP1cWvWX3H6kS8OvrBUSsmFDd87PL2/O/FcEz/8TgQ5dYRJVrsF4KG7i2BKBnom+DuPMnpqvATT+WB3IjVwOsF5odt+hA6VlqquOZc4yL3ByAGAOJRd1amhUAp4F65F2elVpD9RQVr+OEMyLAiHIk+2HB+BeN3BEJ46YnVJr/fJa630+NzpBP+5RGE5JRB68sGik2fpsRUg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:30 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:30 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 20/22] wifi: nxpwifi: modify sdio_ids.h Date: Mon, 30 Sep 2024 14:36:59 +0800 Message-Id: <20240930063701.2566520-21-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: 63b8d190-e32b-4ea9-82c7-08dce11a7f82 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: U2YUwzOnqzinWFLmqaw+JGJtWv9Ge5vXiSFemVOY/bxhauWrzVM+4N8EJrHRBtBzjbVDLXobERcTPPxY55k7sMuDw8cmxB9VPKEs50bvspmIcP3HSypO/+eTWxyIcZpM/ZzIdDqH7YImx57esGsHaIjiZBNivErEalVdfhmVqx6JrLUM5AjTQrIZ62il4AVvHPbrfAJgHHIfo13J8WCaYfjmyuUM2OrIccR4+x09HAiDTeWxXovmq7ihJmc6J1tgdPJZ6IQKkRxAcWJziawn2Khcbwuwuqjri2L5jSyG0KSATN10m7Jj4SARo4XGp64GW/0JID0nYgadQ3a6+4X02ylVPkSbnXFoNca/ikTYwahF2UyWWurxYrKp/HVpiADv3PBrJTaXrb/oHPjHmIUsznUwQMvIZAjvX5kl3F4FBof3bP/fNxvn+xFDfGnvBBMUPPhC1nH9ki8fvoXJJCOpSNPpTmF5tyg5JqXsuUFJKtdiCFxnoIf8wXoUGCOduRunLgV4tOfmY02t/AQafoI038Vy8Pp4g4apxmYM9QVv4S6IoqyUsPYJqzsmnvdsDjeYUBc1MhiFv66EX9+/zmCOTDHaGDx8FFkYt+7p5oIxyAcWsPbb1MH0YOF8TxyoYhAvT1/P5+T3eCH63HzrmjNU6G7AcWDAcKVv3loOJHIUMpTEdfIXjpuCCy/0FFJ/nHA5ZiKUelxwdNrKqe+GfH2m8PByt0vb/p9PrYPYGREx0ekrcWFdzrNHse5mTHq6zkNoeS3cx8m7ttnSGgzrLyOpfddR23ito2Zbudh75LLlaSTe9wHdBI9GibZB2B5xplOSKI1+P6YTVu8yNUti02bf+3bNP1AhdwrfonkH9Roat+CUq04RsSUjKfOK5qg3a0/I50hIxlfVjerfE30SYZ4OvSzWFmGrOQVZBzAHfpMhb6Tm2We54Blyr2NaxOiWO77BTWmAn/K3DRKnS5hq93Xe8x0SzkcUpQls61xPl3Xga8bkRI0buxmoPp7iqlC942poFpFb8+IVOR4PPJV+1yzRUPt3OaDXIMHOQ/2BlBNqW3vCizocMZ1W5qMPeb7in9BQlnYtsVtsc+75/o1Mm8xLBDVCKGVk2sUZ24ISIW7FgYLZpy+29qsdr0SAXxk5nZxht/n0MBZw907EngGBuiWvRrfiq1T5WFgjYr+lLJvtrpEfcUdZ/QWox7er3e6nGTXbMMkKNJS5gNSvj4WXq5sKhXr2yoWcZaTHVjd9Vvn8MUgV5iEImNmfNYc/AkU9dVS3CnFNEYfkbJzOsytFfajk2PZeQNKQtVWEPekDLi3JGQ33WLToLxkqfdDcdXus2T3Et4bP0RDs1tsK3/BGkFx87aIRJRar50msrVLqGH85LFd/St1m3HILLDyvxxX8gh4v8gN2MdOa4TugatFL+mwr6Q== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: QqOIzQGMlFnOl7bjiC5P1UhrmbycsjytoWuZoBtWUK15d2HTnSS11f83lRWwgK0jadjMjmE0v/nvc1mBvRHRq1W1wJgUmtu0Y7rRaRUUP/DiCrZ6FuMlC1QK5/kW0SxaAV/Pc4cmL/j9VvTn3GfhW3HBOeYeeYORK57h8ocsmMjjQutubwmp8RsZ7Eot2oTjfVrYqReLIAruxCRXunSUAaqTkeUaj45hA17zhs268jyc60u2KxD5ShyMJbX6cYYqKiHMJjJci+fU1wPkyUvilFtkXqnGkrMwJlQw5cltv5Zj3e/XypEOOZSg6kYgONGg617XQi2xsqMZBaGCpkbySnloTiDv4ZRoO6QLYEvfLxIH3saE1AKFLVCqrFDyxijOQbYGSPkpdoD3OtPp53rG9sSgT0/ypqDxvvlHxnMU4mQDOykSjRcrc5Yv+rB3XEqYybqKUe4s8uNCvGrP9Pdk+hMABLotcDyohUQf0zhzsyBn73pDD5nDxUWDp7LHFHIf+3DlOeh6SWrVoo090HxBAQ7cMJMn6VSvQe71k6YrwRgx8vOHJhY82kh5wDb0snOpyOvXQ35s2gqiiJLAnAqmESg7eKm1pHvWylVDyysnHjSPsq6Fk130tKqBnPPrLLiWiqL1dr/ZHXlqEhTbIWTwbbnPKojSGuvFkkVkjGUJ6ZgB5peKMmRd5PQYmo65jtMTujJWnlAshBD4NilrrRrAWyvBBhOe24XmRdg662yKEZcO9YgotnZyyp8BxKPt1/nQvOs+lBp80cAG+21aEZjc99Lfb2gwJqZbjaO+6NJWZiDSX7/FeM2TV54yPGabsqnTCthcBIle8YyZtRw4nkE0EFCZraNL50S8Hj0A0CLGphfOVWwDLp3fIdADzkljdnzuaNm0XUId4wF9lsJL/9vdXM59BNYYNLMi7ITWVPoJTsq2dUrPlK+tfq3Bn4jmEpA5d3D05xs/m03krVx1OutUTPkWwCjrk6EAK0AGpe/FpbYoRg6fFjkZ4sEWeZ5lhYqxBsBCpCjb7eFDjksjH/NixQZc37DQD+7UdbsQiqFQU0ZsVzWqczTPnrEnyHhY33rxHb5sKNG1WJq7qy7Ms9LsGIFhsnCl6TPjUN8RvczYNA3jAUa9uenJALuk6K3stYE2tXLQX5qqs8VEl0W4UYwvBVlBYSrAgrHnxc6bAyUq7HnxWTBlNCstTFoPJrmeyDtml8Ri60Pb4qYyr9tIataoh/xfMeGswsQcJz3Gp8MGJjTcPFm5TuRW2GdkQJlOYG13JDzHgpOnbZyRryYcXPq5LzZ0Ex2Hln8X5iRIh5LnEWWxjh4asPQhBNvRCQvQscrMtvcMgw3tMYDzVuvN8bV9YCAtxci3PvK01jOC8kUe8nSRhnNYSLBJsTbe8YP+Kio2rFkXCcrlAInuzGrKwe6WKpiiIzaDBRJaP7xRmdUkTbtyZMi7GZm6CbPli93zAesaHwTjHkSoOjovBeYs1Qf8NwhrL7nxInPt3YMFdu38coxN3/zYuUrzDN8cHyadFAIUasF5dVhpxMHS8fJuo0ptI4slVjdOEX/GPI1eIVxCpPk3lC4gv6jw/BEjsonIbCCS X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 63b8d190-e32b-4ea9-82c7-08dce11a7f82 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:30.7939 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: CgTNlU3KJfBC/1TBWXv2UfMXaewDfkyLWjxU0+eMZT/rMoCqW5D4Aebv/E7rNivD77d6URSIyIjMG4sX4xjvSQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Add nxp sdio vendor id and iw61x device id. Signed-off-by: David Lin --- include/linux/mmc/sdio_ids.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 7cddfdac2f57..a3bd88383c31 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -115,6 +115,9 @@ #define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296 #define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347 +#define SDIO_VENDOR_ID_NXP 0x0471 +#define SDIO_DEVICE_ID_NXP_IW61X 0x0205 + #define SDIO_VENDOR_ID_REALTEK 0x024c #define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723 #define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821 From patchwork Mon Sep 30 06:37:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815438 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on2074.outbound.protection.outlook.com [40.107.21.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 585E9187339; Mon, 30 Sep 2024 06:38:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.21.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678334; cv=fail; b=CAG+6u7gnfMClw1L1frL0PP/9Yl3rbcEQowbVPhGcGV64tqwVTT167oDVI887zRapyd6ztFnpFrqVxAyumKRP36ggcYo9lZxnys5g0cPniylWLfroDATUnfGex1a9OpZfQbNN9KVcgfXVOrNvaNcgwXC2849kKwjSAbXor2uUgc= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678334; c=relaxed/simple; bh=xllh9g36ue1/224g8nKYO6OCUHsEhg9Z5CXDr5IINEo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=bRF1CjuPzAkvP0dNCKCvkD24+5Owq+FnW0SZhiI8Kk3eJma4o+3QUo50agDnXZ9kxupGc/woRXuQvD9ZMFoNpJxc2GaWDEMMgAJkKBVFedpT9eozrP/iUvcFFkN9lYucphGdVHLUOOqKoD1Z6Yycuy80rdDmZ9rVF+wYP1WSkEo= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=BNCl9gU9; arc=fail smtp.client-ip=40.107.21.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="BNCl9gU9" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=upCSEo2bXblFwFtKIDSNLRHNerrXWxw2/5Ab8RYuvOqe8xVG2PyJ/NxgxN62a1HwM1mXc/RbyN2HilHzDohxMK1g6JGqnD56twT4fh4oc1g8LntTIakiDuep1IFNlRwuW0DFzI1o3NSGabCn7N7tL4XbFRbBBVpNAsJS62WiqIufeKMTcQh1uwDKTUNrP3gJol0QE9bvoTRgOvy3VNwr9Th/AYIILO4SNFngQuW0oMO87ywccyK1vRPwZQwDGqwDykjBxUjpJyP/N/YTgmFvWLmxyMGDOcgrCuS//rVY5CzHOuOymONqwo7TmB8z/rjqVk7JV2khOnUPN/cDFLuaIg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=1C+fZ+kLlXQ3hs4tcRj/7DLpQ0te3mfxO8sC9JlFSvg=; b=MkK+DHV1SJ7XTZrAKtfzhFml4LcTT29iFmffDwoFsO3SitCT1zkam8NH5VxSphTWJvsjpR8/vGLJBxQykCTJtBUmrNGa2fH2bGF8+c2oQ8NdcWDOb7D9lzq6u9XxXxOwO2vC86QzpO3Pd0FjEvT9l0IJBvbtFwAYLxMhf5uwDGPb5dl+8Tku0wafmRYuZu2/GGZtcQqsmUUVUIlKsSYXQ8dG13OFjU7qlXv+QpfgHBXgtCZr6o71qkQGlqQXNXj/M3iFCO+d9NO4AuiFYFxZ8WIavkVAUrEf09tDUR8OttX4RBAuX0rjEE4V/I+TxVcoaztZ71HajBWXtb4r9e2dIw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=1C+fZ+kLlXQ3hs4tcRj/7DLpQ0te3mfxO8sC9JlFSvg=; b=BNCl9gU96TiClRHe9ncNxHOFidW6KvrZEoYI3sNutfkx6Rw+Do83MlkfpDpon3I+BwiVFP+5rrrw1XwBheT8VMXV81vrvfnQcOvlGEq4g7/vEBVjtC+xY6qnRLLe4KfnetSqVD5Ez7/PTEM2p2y2zIFkL0kAYc0KBWO1w4Zx3PKkiSHT/fxq/ngBJbD0GH28nZ0fTvfmTwIb9OamfsyM/i+Qrrr2Z7LwoWu0bP3QsyaMThQ/FV9JAFBeDE5PAV9pqm9r8vQf7XexK/Pfg23AzNl5ifUX5A7Pxds5UFcut5A7H/VMSGajwkMWoiyZYlZZa/l4IejXlOWT6N0qJ7ZvKw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by PAXPR04MB9154.eurprd04.prod.outlook.com (2603:10a6:102:22d::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7939.23; Mon, 30 Sep 2024 06:38:37 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:34 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 21/22] wifi: nxpwifi: add makefile and kconfig files Date: Mon, 30 Sep 2024 14:37:00 +0800 Message-Id: <20240930063701.2566520-22-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|PAXPR04MB9154:EE_ X-MS-Office365-Filtering-Correlation-Id: d8d30ffe-24f6-40a1-452d-08dce11a8180 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|376014|366016|38350700014; X-Microsoft-Antispam-Message-Info: Ie2K9nOrkkaB3P5hrgsmxeEmO5aW2VsVlrY9wGzq7wrWvn4c7x5kxlo8MFUF50pXJWzDWj96hkFzs0V7CGIK+T/Z/K8K1LDJ67fpj962VjRtd3WXhp3aLc6hy9d6nVpElnBD8a+uCM7BspSdq3OgtJ1JkKlmaQDp+CJMrD+1FcRGjqe8a2MBCahhp/iHW1EcHW9Fv/pwedbTe+nz+a9km03tcdDNnTomaCPC9SW0b/1/COs6UREFIGVGhC2cJLAS6QUVMboDGVqS77KLC7UQdLYx9RSBj5xHazmUO+ZYpyaZBr6gV8nEEWnyDCMpCGwby6Mc1I5Pt1ADtKVzAibdZlTNg0RtvNrNSm0+e+yeF/Z9YJ5jI1OeFhMDx2G96osttuKiPXtyIqlqqq5/YLKLboXO5TrkjP+fxV/f2vg5UFHxGZ+xgjmpDVLCJg7oAyXjOsx3WTVA0lT/j5RK/rMLe1P0pcxmk4wne1rduXhAq1R4yYgxRzjJeqDbr0SrvyJ0p5iOxs4QrX6WzajJicwMEOfVwHppuwHsbPxpTACY3oC2nWfO9mlVi8sC/Th7kG4caiL+M2Ol29vOrxuNRuoWXCWSy+/Z9N0lD/HZTOOxaAuGSHAzotdfbhf+9M1MdnWfZSR6EQCw7UfoYXXyPIAZHyHzQ18N31IhszsqLVAlToO7d/UYkKwMxIJMAPhkOHoXLGOxCrXwhNquUORNawq+7JW6VfRIqYubSLRSXwPjZH9YuJPgMTF/2FdQdh5oFzc53K0FaebAfWawJaYpFDhfN8symKxD3mJC4fhPStA3uVkG5a3SmACw4wOhUXfDthwYaPL0W33VFLrIKlnKO8gNDsG2J+VuvJ+Te7SvhjpXfKaI5fE3PsEfLRCUm9yCU6mqFu65fmplmyNRXFEZ+yjuxcTBbRg97Tb+3VTfLigdUHcMoNnxskmrOn7G/vtpmo6TP0dsJwn8GmcJR7qFlgyKktPGkgAHb3rXYnHVM8DP8iFsrkwam25mu4f1XJe2tbWWgqTpoc5spK7MmpYnw6ydjdyoAixrRbQzvXOo5gFlrNvEXcSIK6BEXBZivroaw4/+QkIc74mZT1keElQxt1QDDieKswVux9djPQVpYb5KkOyIA2364MOzOe7oTCYu4QxBQQyLIEDzFi16xdfJjj3fnx+RsMcuUYO+op/QLCRxjtt5ZBs99oyPJxfBRUs5vBOl8gl/rLaSlOuZK551Cq5lcrl7VlchCQ5A8Q9RKEqCSaXmxG1wn7pPBJPKKO2CmPK/VdGGNDjYnDbHUBvVclA7F4Ubn0Lk4gY57a0nWBfgRbWJqoqog1jo2zFefBZUP3GPjADYyiBNQSMXXJIVSvzEFQSCmd9F9dcgFbOMCbF71RbHjumPgzXyCkcLX4wGSVS9Gmy+eaAoeH7t6AMeVSCruQ== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(376014)(366016)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: MzAJWKYcT6lacLBR536N+9UI2P0LNUdVRM0pKAVAB6a4B7wb6TmNFUKYOKQG15XDOUwX90LSxBC28Ah0ZHK5fRFrsfXcKKyYHK0Qn8VBVfpdKtEeUZRhndMx2dZ4QAdCfJk/ls3iTDcaeSRE1PvFL1CZaayXxsFUDe9bSiFUaIvDIErzrxuNxNayx7yzVYoRsXlxlN0M5nZgA97pY47999SSK01XTDgecnRZR138OZrsO68hey65nVq1cCgVtC5dEx0CPRVnnWq5Un6O8kr7teJPuAW72s4tUBU0hLKqW7He1BZnZR/LLvwX314Iv74uqNeFt7I7tHDfSxbWWB45Ej1sXd9uqrf//grmfUMh35ObUzP3ExUWbFIjENox6iZCi/mX+O+g4AsdK8YwQFB8D4BvOhCcmjFRl755+NZy/Hkpgkg+tKxCN8d+ikOPgXHJ0vD2VQzSBA6pLjFJ5/s8fwrFx5E4E6u0WuFBO+UOQWqAcATULqFNCH5YKe+PE5NzXyw88qoknR4RcdZe4toTfaMc+z6ry6BuXkdm7kD/9sWuCPuJ1xsSTR7tbwvZ3eGUyu84QGy5kRhZva9jgKI5pnpyEUcDEhawt//mrXUEYDlUfS822gwyihMAMjTJSXWZFE4xAJZBZZKSR0O1s9RpQ2vlvbhHe98dL8N+vGc765mjRBuY+qc4FJOPPumFswixhxxsikSRtEeSZUmyBOCeMalve9IYFURHBVvkHfrLdVj8tqMWcJSg1FSzzhzQl5f4dgukz9QS96KfUYP4HWirUlO7Xl+JBxR9QKgIha++RRJoUGW4LztmcbO/XDUYWwNQNLeP+OUXBPxE0c6JqKv++DkpeBHg4vBT043iyIyWwPA+nim5GLJShjcecQWnXjvfZpiwxIYqM+eqjGO4/VD0DUzWe/LVXruXhxed9HQ/j6MivtCwItDvjsgGvxyukWecubsHxtcDVIHCYa72ya+rXOKPjy1DG0UDsrivD2funSG2waDkjVw9Cqm4KzuppnN6zz/IK29xafGkGXx8SVs2u0tLsa9QQ2FdY3YwcwUENMRVcavRFeQCg+KPAqIrdXn7DgwVw5NBBiNpey6gDRlao6KDIIxB4ijXB0fU1lszDtNPJNbNI/1tGG9VfLU9ja4LyRhqyS9X4mdp8wNCqhdFhjK0JrE1878coENYtSkFNObnoH2XMcZuEa862+R/fR+DttvOqEAm0lOjTqy2fiVP2NSw23AH1xh+4M0xlUET9/xsAh6uonAJYrsBBr/wnLW+mbX9EJ+hk6X/nRAiWSJEg64Q0NIqyC8kk4cPZoX08/PxZfEk5NoZWxx9GJmS+R3wkRcK7Lz5chahXu7ZbXPAkxF4Ny2sERZ4WfFXTQgxpbVjFFoSaiovbPolvriApeOCU/fyhXziz1s9htZtH3AMw6TLtUUueGYpp7HqaA7Fxxl0b3AgvN9w4ZPU3vYjmfMYkTeqNLu4bkroLv23vHlz2F+uOaFKUO6yzZj9ghg0va6n1Hg8Oz2IXANcUtHJ0eZwyHhqCW4gHotwFU63e6axzUga5SCcjOmBprhriDuT+RVBEQyiG9E035VQ8nFNboB8 X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: d8d30ffe-24f6-40a1-452d-08dce11a8180 X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:34.1051 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: vvO9dvlYKtFZ1JO6Jv0FK3ji3YYTg+4jPmq8mbutyBBzfcj5WrW3y5JE8HMUFXhtNpbzymGRLpLbMmMoSqXuJA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB9154 Let nxpwifi can be configured and compiled under kernel source tree. Signed-off-by: David Lin --- drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 1 + drivers/net/wireless/nxp/Kconfig | 17 ++++++++++ drivers/net/wireless/nxp/Makefile | 3 ++ drivers/net/wireless/nxp/nxpwifi/Kconfig | 22 +++++++++++++ drivers/net/wireless/nxp/nxpwifi/Makefile | 39 +++++++++++++++++++++++ 6 files changed, 83 insertions(+) create mode 100644 drivers/net/wireless/nxp/Kconfig create mode 100644 drivers/net/wireless/nxp/Makefile create mode 100644 drivers/net/wireless/nxp/nxpwifi/Kconfig create mode 100644 drivers/net/wireless/nxp/nxpwifi/Makefile diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c6599594dc99..4d7b81182925 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -27,6 +27,7 @@ source "drivers/net/wireless/intersil/Kconfig" source "drivers/net/wireless/marvell/Kconfig" source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/microchip/Kconfig" +source "drivers/net/wireless/nxp/Kconfig" source "drivers/net/wireless/purelifi/Kconfig" source "drivers/net/wireless/ralink/Kconfig" source "drivers/net/wireless/realtek/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index e1c4141c6004..0c6b3cc719db 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/ obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/ +obj-$(CONFIG_WLAN_VENDOR_NXP) += nxp/ obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/ obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/ obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/ diff --git a/drivers/net/wireless/nxp/Kconfig b/drivers/net/wireless/nxp/Kconfig new file mode 100644 index 000000000000..68b32d4536e5 --- /dev/null +++ b/drivers/net/wireless/nxp/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +config WLAN_VENDOR_NXP + bool "NXP devices" + default y + help + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + questions about these cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_NXP + +source "drivers/net/wireless/nxp/nxpwifi/Kconfig" + +endif # WLAN_VENDOR_NXP diff --git a/drivers/net/wireless/nxp/Makefile b/drivers/net/wireless/nxp/Makefile new file mode 100644 index 000000000000..27b41a0afdd2 --- /dev/null +++ b/drivers/net/wireless/nxp/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_NXPWIFI) += nxpwifi/ diff --git a/drivers/net/wireless/nxp/nxpwifi/Kconfig b/drivers/net/wireless/nxp/nxpwifi/Kconfig new file mode 100644 index 000000000000..3637068574b8 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NXPWIFI + tristate "NXP WiFi Driver" + depends on CFG80211 + help + This adds support for wireless adapters based on NXP + 802.11n/ac chipsets. + + If you choose to build it as a module, it will be called + nxpwifi. + +config NXPWIFI_SDIO + tristate "NXP WiFi Driver for IW61x" + depends on NXPWIFI && MMC + select FW_LOADER + select WANT_DEV_COREDUMP + help + This adds support for wireless adapters based on NXP + IW61x interface. + + If you choose to build it as a module, it will be called + nxpwifi_sdio. diff --git a/drivers/net/wireless/nxp/nxpwifi/Makefile b/drivers/net/wireless/nxp/nxpwifi/Makefile new file mode 100644 index 000000000000..8f581429f28d --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/Makefile @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright 2011-2020 NXP +# + + +nxpwifi-y += main.o +nxpwifi-y += init.o +nxpwifi-y += cfp.o +nxpwifi-y += cmdevt.o +nxpwifi-y += util.o +nxpwifi-y += txrx.o +nxpwifi-y += wmm.o +nxpwifi-y += 11n.o +nxpwifi-y += 11ac.o +nxpwifi-y += 11ax.o +nxpwifi-y += 11n_aggr.o +nxpwifi-y += 11n_rxreorder.o +nxpwifi-y += scan.o +nxpwifi-y += join.o +nxpwifi-y += sta_cfg.o +nxpwifi-y += sta_cmd.o +nxpwifi-y += uap_cmd.o +nxpwifi-y += ie.o +nxpwifi-y += sta_event.o +nxpwifi-y += uap_event.o +nxpwifi-y += sta_tx.o +nxpwifi-y += sta_rx.o +nxpwifi-y += uap_txrx.o +nxpwifi-y += cfg80211.o +nxpwifi-y += ethtool.o +nxpwifi-y += 11h.o +nxpwifi-$(CONFIG_DEBUG_FS) += debugfs.o +obj-$(CONFIG_NXPWIFI) += nxpwifi.o + +nxpwifi_sdio-y += sdio.o +obj-$(CONFIG_NXPWIFI_SDIO) += nxpwifi_sdio.o + +ccflags-y += -D__CHECK_ENDIAN From patchwork Mon Sep 30 06:37:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Lin X-Patchwork-Id: 13815433 X-Patchwork-Delegate: kvalo@adurom.com Received: from EUR02-DB5-obe.outbound.protection.outlook.com (mail-db5eur02on2074.outbound.protection.outlook.com [40.107.249.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC02D183CC7; Mon, 30 Sep 2024 06:38:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.249.74 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678323; cv=fail; b=hLiQcplL86ksUTteM+ccLNO0llHrHKwk56+hYFPKfb6pNM+dFPvhFIsKM4827rFT/l8wQamNJfEiO1ynfLOYaZS4fqGinlMPiswGNrdAlf6fmPhyhvcY7ZrD8ukl9lJnKitBLcSQIAHC+pQDOXmAmw0gwgTYfnLYGIftq9xfumY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1727678323; c=relaxed/simple; bh=phzpkL3hvWwhFAZK9c6zhaK0kDiACcLy4HEF+9VaZBs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=ceZpJ7YxZ1wmBcR9Z9XkFjWr7KLESkimQsK5tu2RcJzJsIoPYpVYp9mDfW5ycMgQ0+nXrW+lHQek54h/IiC5lYqRD5wqGH5DSvaZj76DDHQRQmENkQ7+Pil+hNBykNkI2YxbNCDpBX9HTz+lgi+4ynudXpsE+I8VJxv02jSSVUA= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b=Ra0Cg1i1; arc=fail smtp.client-ip=40.107.249.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nxp.com header.i=@nxp.com header.b="Ra0Cg1i1" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=FbKpSCQdkn3Dv8ZIg8ddfL2cet8yjsApLoly6mT/amrEDPZgJHIBXRsqOWAqKTVPVxQny4VxqZyw8io7qZKzbKDud8bpVKmfYO/zbE2vF8Na2y+AEugc5vkTGUH2urYNhFe4gWE8nJCp54VKj8MdNiJ702eWneXONbxJ5NwjLZPQh0ptgACrhvo1eqJZz9vsw5LAD7BAJrZ99L//e1o8KnNyIrzGHqwSo5kBfNNxYICn+NzK5CUvBEzcya5/RiQYKANvE6/BdFuOxnSqtA56c1mza2LeNlKO98Uj8K1sOqcSeeuS5a4moGmXiBklFUJAn0mLHa5kx8VWqvoXGHvWUg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=97/Xe14j3xdA5vSNX4xd+D6m5GBm6YuqYlLUoXjUL/s=; b=B11IMPtCaz6togZ2aVOolobSfpwX/LEQEy4D8bo+5Mbib9sqXCWGRGOqbiVYikrzD1REITfvX1Z/HnrW+DuyI5umB8IFGe0BzYdg6JoxjqFrHDjhcoPxI5Q9HcFsrbOBLoqOJs7dmR/54gjV7wCF8jOxc4Z9K6WK+FMwdrMr389avD97/kKtTyIOZ8iXQXhVZNE0nO5nxEvWI/WIBfIHJJG5NfAHZaWljzu889ELIqBPxD/NAsSEfFGf80E2NMrKJmMrGGe1KXl+PXTU7Suk0VQ1c/0iBy6SrUHMOEQ31ihqDDy9jzawG2PaejKnQdtKap00EWtnDxkveHTrdAGyzw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=97/Xe14j3xdA5vSNX4xd+D6m5GBm6YuqYlLUoXjUL/s=; b=Ra0Cg1i1baQzU8u1wOWs4G+RsPm9+8THCP/f5XjTHAxDNFd2I46sf1ck5odTiikT+pJrAo6FsMr5E/JnDZ20G0M12Pr8m4eK0a5mr15T3nOC00BVC8k2nitXBS5Hj5haYePUFeMRu4pYhwOvp85IGKrt1yeZo2uY8B9p/iqA9vN45XCK/s+dj2LzY4X/bzCouyA2pBonDKq2MdQ0TSf3lhfXBeTKU//xRTMlELgVE5evCT9vW83LZUClNQ/aBECsoQKM5npeQyIMP+Cgwgitu992bwDSNm1bIkiTbY2oV44dP5Xh8XFNE2UJOPOTyhSKAJf68lwsVF36ephtA3Wghw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) by VI1PR04MB7056.eurprd04.prod.outlook.com (2603:10a6:800:12c::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8005.26; Mon, 30 Sep 2024 06:38:37 +0000 Received: from PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257]) by PA4PR04MB9638.eurprd04.prod.outlook.com ([fe80::f950:3bb6:6848:2257%3]) with mapi id 15.20.8005.024; Mon, 30 Sep 2024 06:38:37 +0000 From: David Lin To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, David Lin Subject: [PATCH v3 22/22] wifi: nxpwifi: modify MAINTAINERS file Date: Mon, 30 Sep 2024 14:37:01 +0800 Message-Id: <20240930063701.2566520-23-yu-hao.lin@nxp.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240930063701.2566520-1-yu-hao.lin@nxp.com> References: <20240930063701.2566520-1-yu-hao.lin@nxp.com> X-ClientProxiedBy: AM8P190CA0001.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:219::6) To PA4PR04MB9638.eurprd04.prod.outlook.com (2603:10a6:102:273::20) Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PA4PR04MB9638:EE_|VI1PR04MB7056:EE_ X-MS-Office365-Filtering-Correlation-Id: 5efd967f-7d11-4781-1e18-08dce11a832f X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|52116014|366016|1800799024|376014|38350700014; X-Microsoft-Antispam-Message-Info: Sr8b8UoKZllS2lnRF+VWoP9jKOKjN+Hg645oT4YdqsFU5KvscpFTwA+Z6E1yXQ21Hb396T1DGayG0v5UzbJKu7alsDVZZItb2SsiWxw9lOmuOxgyfv2+Or7KUhG5i1TSVJmfgU4fH57Kvyf3RT27+YMuHca9vJOAuW0oupkK8ZPqvjcM0Y0ZWtDqyc9g3BFtw+Jqks19x/m89PNPAhHFVD830nLiyIOEEtIYhcAkMWuYi2JL2XD4Fie8FdpfFPCc8LDPSm0C8l+JRzctRtX+Vv64lVHTuGrQzuLOx7ycYUHN8gpeqABpdbNI8WkQdarmZhNZlW9Xvyse8iSs8/CecuL4k2yKh0cY0l6fvJv+F7459E4YapbxoGUIppdCldTRNz3FkRUWvlpaVwklFwOUTkVqF3YF30NEBcdv5JPbdsRpiOyf25jEdUh/5iFQ5xEU5S1fNtIhLLF905BJhHUBIv455mxwji4u5eaN4Ywme3zEXZvxP2cBzEUwXBeonVH4etPEf9yplx/bUULYKktPfUJnwWz+3bcJZqCvcK61bEIgbFj5Xrz39WM7RM5uFanB0KHezJqRMM3Xt57m3qLzZ7SD8MgIGWus2xFxoglepDSMElHdGEhckzVek7++fyB5KerMTCHZfQCCUaHQOl6w2OWNZDAEABld2dK0zk7INGVNY29Gkkj8g3vCoZslhOHORjSJVe573tF6xLMWLkKzJ7ZRTU4a9bzOOR6RqeZLXRSJEYa3A7krZapOosmpQ55BU4JWEsgA6nPyw8Xmmn48V4p1EFJxLPbQtoJ+yRCMtOmFYfyCwZyAy4lxr88CZPDDCmk/Km8CReB3SGSbhnpxX3KVC+6W0K6T1Gwot3i78knBWtanjtb2azLBEO73W+3exNBMy96I6iGFxbUSH3Dcw4R/ihfrJLdY6tr8WC2eHtSJznCqMLmVPBkJoWj1O4xgoRz1A85XOza0nt1OWmvKvlJj2uentcA8Fo798VDVZT+vEEezM4bH7+TnBtoqCzpTC7uRbkzHohzB4VTZt46GCWToediqJQPr7O97hZN1WPwWP1QSF7m/6Ljl5NBArVhEA16Ew7M2rjYVtjNtkkhHMYUSl4TiLF/p5Lxb+XQfpQlv9X3ncHmqVD7miWzrSZqvLp3U6GHmZt6vJ2tKVrEu1zgbrtKe7Uo3m6lwnvTy0U5S5Bc7dDrlzEWpTL4U8kGVfufc6BlpH1VD7/+7lvSX1PAnMyNnKkvlKS3bhZSm4B4kiEMMKjIs5JacbZETVGtvO7Hp2WMdmrVeqv/Eskae/hImNHTK+CvXvJbKqQQmFJEGQjudfvbzLpvA7U7uXqLcKnkGlsHkPjClhKMXsdprgA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PA4PR04MB9638.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(52116014)(366016)(1800799024)(376014)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: QfuMISdBeeDP8zEr0pI3S5+ibnk6RkLNIXxSLVyeiXM6DUXW78rn6cKauPge7VdZsF9NYkrasx7fPKQrrN4m82zVJM7vS2LDXm3i9tm+0O7ywg7M0aVlhXBtcB9m8VJLDqg+rB9HSipTK2cE78Amd807lNoly1OJmIH0B+RYol0wopzBw0JfOBAoOqFBsLqa6yxQwBzmIQWoTAhVv/Qy80SAwZSvGMPw3Xz1sWyN/Y8jKA1VNwrAuZdjQ5K2U1Hnz+f/poqIVYzWc81olQ7w/EQ3aRPGfGTg3iUi/V57IoxjqD85V2m9dUpC5Yl7qJLAH8JWWwtcApnpi5Yn31jVVCok1AroLym/1eWbyOkSClxSjQcp+qTWr+8nrCnzm2PC6ewBkfnZ0G0YLIsbymT1KUbjwOFyA2/w50f86yao7YiTxzTRhiBrTKFjEQrGhU2BjuHLPQ/vjDjDuasEAl4jxycd5Ly9+nK90l8cMtvn7ootMYrW1NC943zS+Xs89Go/jnHza4ehsMZqqNcFRplO3Gp0EInEivIoe0NruqxPMoU44gUPPjn6mWxrvxyIP655whBfYZTXJf2O99OxwOXnywSHgdxl0Ak1GTxMzx7E6RoazwiAsudQlBQ0hAvjMHfzEQrSMP1PEwMOkeI5KEkQh30r9L0UgCaVKk8x8cL26jwJnR0mW7LH0S98uS+UM1bbQqRLcm+SkKr33q3wtRYBo6ulEvPtryyJOGUWXwPeCwlekDznA78yPJ7yuswFhAvRHjR/Zu4GnUPvzGj9K03DXQnumF2MiAl1RVVi/ni7pbztfIM50ia/zchmhUOc9PYwFuYDBk82CSqUqhVQFYTP2TcJBUv9LsMfuW5tckmv/4Ljd/ZtU7skhD/CbNzx2G89pL1RGer0qKCeeEjYpiwUj2eqcCBzHXmUJWZK7hKPAKXvfzusjUb106dHX2dWWB6S+iipFfO2iWX1+tBVqWdFyNfNb2Q2+AVUpCdgIuG9DEjq2cHOneNrd6EAAzhyAVfYDCcD6rCYJwmZr38DsCLEsLMhqm/RjD04jk1POHsV8hg2IOC9wXCp0vWdOB7W0zzS1ANoCWCdhGIABchm6caaSC+cthyOtwqZv9Ubyd/zVEA+nvRn6p9DrvxI05hRF31AtUcyaKB8nr6Na2rDi71YA9+R3yjtjb4PVtlD51xoYEydVyWBokuvmH3y9yqUIZVFUNOJNJM6/Ml7/NzQZn8JeHikTyaP5X9fSZqpagBrGawdxxRRJg18LBaP1WIgi0a2xawTKGDMaDFZjbQFOpsF+M38NR9P7NPc+OHBOdILfefQKQOmJFWI2vWr5MAIeXS6pUnHwf4fXWulezHUUCCQFU504wdhDJmJzVZdK6sqJiTf5YZ4a5pWC/kOH0JtdM/a94KZar1y2FefIOYrcpaEDAcVeBRkgnc8I/wo/1UozmYnoGaH804MezC4iX4sNDP7IUuRdF3tTSNZxDFOX5rbV2MlDMsCDm+CL+67J9JSl0nCgGmY2c7cQEtRxnV44YEN0iOQ8X4Q+gr3TmBAsIqSuqIeAnTyBCtgkY33nfDinBn3s2nNrRigibm8U9/oQU9h X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 5efd967f-7d11-4781-1e18-08dce11a832f X-MS-Exchange-CrossTenant-AuthSource: PA4PR04MB9638.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Sep 2024 06:38:36.9212 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: 3pHQBWFmF/32i+caYXvZKRGFaIY9jNc05pLEnZQ8sXubrNm0B1sy6GcFuhVRQGSiwKt+CI6OApVH2x5v7tMADQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR04MB7056 Add nxpwifi related information to MAINTAINERS file. Signed-off-by: David Lin --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 77fcd6f802a5..9846759b0c99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16526,6 +16526,13 @@ F: Documentation/devicetree/bindings/clock/imx* F: drivers/clk/imx/ F: include/dt-bindings/clock/imx* +NXP NXPWIFI WIRELESS DRIVER +M: David Lin +R: Pete Hsieh +L: linux-wireless@vger.kernel.org +S: Maintained +F: drivers/net/wireless/nxp/nxpwifi + NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER M: Jagan Teki S: Maintained