From nobody Sat Feb 7 06:20:54 2026 Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazon11011067.outbound.protection.outlook.com [52.101.65.67]) (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 1A44B34B68C; Wed, 4 Feb 2026 18:05:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.65.67 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228315; cv=fail; b=u7TMCzSpDsJ7UeUS+lkAJmfzAbfY/Y1KD9hMwGO8krcBblfHCmURT+Gz+Dy8fR438KDVKpc6sJUeZBt0096VbOlGv2kAJtaCkSMsdNBsmVuJsn1EhP3MDhvglj0NOUqkJG6AFoOpP8hkKR+rF7DIUbORZU+B9nrD1NrUB8lQkpE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228315; c=relaxed/simple; bh=JIZKUTeu4HfFH5aGUZJyQ8R+Nku2kVozAY8fM9Q6v64=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=pJjvI2hYNb8TPWsKFkk6OdNMsXfcAE4whSuboHKt8FZMLMhOCBmsu7KAZthvODBAp87gRFKlZJyxMBP4eyiyHgkRV+TtqVzDSJ4FF6mZjrtTLinzOaSKlvjlwcZ6oaL0sACJi08nSnyzPOvBwNxkXThMZap+v3fCUbvK35NQ8nc= 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=EoZ7XrHh; arc=fail smtp.client-ip=52.101.65.67 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="EoZ7XrHh" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=CtzFPUxNRz+6smqBruyqK5GDAStfjup5+bI2vUT6Zeu/VIi4jRmrmBVAFJahzOS/04Ec1E3CV3+bnYrYFcK/S59hKpddzoNyrMJgKmu4w/KxrhkAO4K51glSRAL0ikk4vdi+/b3JsShtXF2GrNdkaqdcOnNfa5J03x/nUUjTSKF1dzKyCb/1GetQO0gsyEwxUNoDUa3wU12Tazqp0+fTheXc7cs9+XC4pSAK921qOWaopQ0lpmLJpYvTCvd7Iopze06LIe+DiteI+bH1bbCdI0Y2vrpTRaq4dFCE20ZbgZDQRvugLoiO/m3rSmn6Ik4plOPfBugkbWtkuN5TsCCZ4Q== 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=SYLzKwZFQmbpCxNuOUMiUvkcescrFkHPyUWXdatLvUU=; b=UXwHhplaj/QLntmO96WBFTO5BTfm4rHcVDNM0N9RQhZ4qIR9ppoQXibBL2s9BkOPYmik8rAo0CBWOKXL19WnLon5U5lAn9ZFx6MMZfzUTpnPc8OTNOezBujDaGbAvT/nI0b+oaaCbmC6hBCgp3YxnzFaIvAfWKJQfySp5thyD7Fu2+H33AjexPILBLlkh3J7MhdPLDFYam376VJV6wvkeVBX5zXufNtu/jk9HMvlOEG7cCQS3Lc7cbv888lij36E6s6/BMQmpM0C+12yhkIAH1DuUFyoe609PZBrigxKc64v1Z3ki90yWCwjGV9k53SyG+KVTt4ygaEBdGNVeNousg== 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=SYLzKwZFQmbpCxNuOUMiUvkcescrFkHPyUWXdatLvUU=; b=EoZ7XrHh27t5t3+Vw2e68OzhLu+Yid438I1N9sUH04E8ru5e/CMEnWidRqXegIy5eiIeNA1HWroGMdo02KpNVHIUYnl5uedmJCkGhNJrqNpKdWCRGtyJby91aeuqQEjQ2oF6VZdO7l0+oXcfR5Lfz518m2JRnNwDkOiiEcSu0b7klB2HGznSppnJcRwPb/XCLWyqt3nzKQ/ljgNdedGHeQRqTS7iV4nFMxBwj/7aU3KS08GkpbTM3zYREoTBdLd4vktaReRjDlO8Y/DIM3P1mSlfqnqZhmt4uHLOhLnnwokxuuPNthDtkko2bMtAGCV2TdDhOumpgZK2tN7xFeZWsg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:13 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:13 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 01/21] wifi: nxpwifi: Add 802.11n support for client and AP modes Date: Thu, 5 Feb 2026 02:03:38 +0800 Message-Id: <20260204180358.632281-2-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 6d1c908b-d728-465b-7342-08de6417f166 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?B?TDQyWTArUmZpVGRLSlRhYlpXeGM3OGVDeUxGM3JsZ3J1aWhVNU1RbzJxYlNH?= =?utf-8?B?anExeFFqUEc4cFNvU0NqN3hvd3djK25jU1hTOTBjbzdBS2pmT2hZcXhRT2p4?= =?utf-8?B?cGE1cDBzV0dNNTdwcTI0UjY3TmhFU0w0dUVuangybHFNTXdzWjN1MkJDZ0NR?= =?utf-8?B?c3EvNHpCS0IyOWo3M2FiTldzellWeVZLcm5CWC9BVENLc3gzZ1RvNFRMT2hQ?= =?utf-8?B?T3ZDeWlCZ0VJNmNjcUVpbDFTUFd5bkhrempiNDJpOEVPYmNNKzlrblNqTVBP?= =?utf-8?B?RjZxVUVoNjlxdC9QdWc3QmhDRk5aVDdiRlpmVy82WHhhVHZwdU5GZFJIQitj?= =?utf-8?B?OG1EcmhrSW95RS9JMXBKOGl4dnJMMjdNdEc4eHlkSm82d1A4UkhkMXh1Uitk?= =?utf-8?B?RldSMUVUdDNFa1p1enVXRFI0dXVWSTF3Z0hUVnhTckFKSUFqMmt3dXR0M2Z3?= =?utf-8?B?Tm95OTNnUGRXOWZURWV0K0NiZEF1ZlZoYTNzb1E0Rk1FM1BGaEJNM1FKZVFL?= =?utf-8?B?T3VmaXgvTkViVFhZbE0zR0dxWUtYbER4QUx2UHdXbUwvV2hiM1dyck14a2xa?= =?utf-8?B?Y3Y4V2lCdzZzU2N2b0RKaXU2UHMrc0lmMDBkZXhlMU05aWRIaWtOVXQrdGpq?= =?utf-8?B?WUx2RGlpTE56SXY0RktiWVJrVERGaEh6WERQemtqdEdDdTVFMGFFNGwrVVln?= =?utf-8?B?cGxXTHNjWERZV054cTlyRURQRSsrYlo3OS9QVENEZkhlWkh1TUJOQ0xvUTN1?= =?utf-8?B?c0dHOFVmNE9RSjB3NWJjQm00ZW5GcjJYTWQ5WnJndkFWUy9TZ21ub1gxUFNO?= =?utf-8?B?QXNlQzE3ejdtdklNSVljQmJhcXIzZFpEREpuUTNWRE56TnJJZWdhZVBsOHF3?= =?utf-8?B?b2RSV0prMUFiL0xyWUV0Q2FrbkhrRWg3cDhJRENqakxjRjBZNWdMSDFIa0Fh?= =?utf-8?B?UGk1V1V4Zk5lTDAvUGJyV0VndUtQdmxybGlnMytQYTE0Q09rQlpEVE9lUkFt?= =?utf-8?B?N3NOTkhkVWxWMEc1WVMzM1NUcG5ZUTY4VEZEb041NUNyZnZYZTA3Z3Rjc29Q?= =?utf-8?B?WmxOTDhJczBMdGdTYkNNbXZNcENjV3BzMzFXT2hEQ1MrOUQ4SzZaYUdMUWho?= =?utf-8?B?bjVjMlpyb3ZnSE9JTTUzOUdpQ2w0eDY1cGw5L3hWOXZaa0o5VElHSk1Qcm5Y?= =?utf-8?B?bE8rQVlpZ2hzZ1pnSDNiam1MczVTN1I1eVFyc29GR3VaQkdQYzd3MUVMenhm?= =?utf-8?B?cjBHZUV5Z2k2Smx3cVE4ekdkYkVtSVJaa2FxQkFWMnpEYXM0bW0yeTZyK1ZJ?= =?utf-8?B?b3BkZTlkckpkT2Z0NFZiR25jWXRSTGRNdkJ4QVI2QTNFb0hBNlMydHU2MHAr?= =?utf-8?B?SGFVMUI2dk41YTJld1BaQ1o1QnVVSS9TRHBraEFIK2R5T2FMemY2WTM3WGJE?= =?utf-8?B?WktsWFdaSHpsTEJyTDNHU0Q0ejk3aGlVWUhnTDhiZ0pkQ3VCeWZ0emVmZWFZ?= =?utf-8?B?YXlTRllJNXFrVnIyeE41dmFpZXQvenVGazZWYnlVbUI0RWhwSFRzeWd5SExL?= =?utf-8?B?dlRHV3V1TzJ2QzFsT1NjbC9ZblhYYU1ONDcreDk0SnQ5OW1YSmhUR1hSQTBh?= =?utf-8?B?cStlY0Z1UEMvVzBSbHdiUVBnRGFUdUJSNGNFcEpGWHh1QkRodkNSVHF4bzJR?= =?utf-8?B?YVhtNFQ1N01ZaFRRUSttRG0vYkRmQ3NVMklCdTdCWmxLSmpYVlMxUFlDS1Zk?= =?utf-8?B?MmZseEhpb2V6MTZCNndHeUZabGJETUpHVVJBVXZUVkdtVzREdE1tTmp1dDNY?= =?utf-8?B?eVF1MS8vQWRMN0pTQlZGaVdETVhzNzZyektxcGxDM2lyVDlGRGZVVXdOdHBM?= =?utf-8?B?aDhTbCswMU4xeWpSWk5KcjRzbi9yQ2dIL2sxWU1MUUZPZGxFQkcvN2VqVEoy?= =?utf-8?B?NVcrbDZ5OHlqYTlGWm94TE5jUTRTUExyMDFqa21DTDVDSk0vdWhLdlJsOUEx?= =?utf-8?B?bCs2a3RQYzRZY3VpQUtKdlNva2VqVlIydWlST3p1M2VFMjVqRHkxVHhPN2lJ?= =?utf-8?B?WG5YZWF2djIxZGFGVFpYSm9OZU5YVXNTUzh2N3h3UEFVcnUvUTJ5SVZPL0Zk?= =?utf-8?B?aUpWbzZ6MFlnM1BsSGxvMGFnUWErRk1SQXNsNnEzUFk3UzRQVmdsTU1sTEc1?= =?utf-8?Q?qyZ0WXXMuhg28LqlBB9/z8g=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?eVZab3prcU9PYndaOTB1Y3VEbkR5V1UzVTJHN3ZEWFU3VzNLbW5TYVVRYXZ1?= =?utf-8?B?ODRXQWZxNWYrMmNQRm45YXdQQ2l4WjBwRkVRa1dpemlHcWQ0bGFuTmtnU1hs?= =?utf-8?B?amF2UVMvYlNLbHBkbVFXUkRNbkFPTHQxd0Q2NzNvL3RVdGF6RFhndmQxTVpa?= =?utf-8?B?ZlphYkxDc25KVkR1VnljTlBIZGduejNEalp5RnRRWUsrak5RNllwT0FtbFFK?= =?utf-8?B?Si9Nb0paYm1NRzBYWXl6QWJwYi91UHdtcmxTRHZLM0N0c0VENCtScFJwTkxB?= =?utf-8?B?ZG9rTHdld0twbStTUndOZmVWMUQrR09CZzFQdnBWNm5wVTFlRnpXR2g2QTZu?= =?utf-8?B?cno0amRBdEMrdjRYNUFzR2ZCcUhYSUFlMTBoRFRjUmQxTklGdEtMK09aMVpN?= =?utf-8?B?L2c4TTc4NlRKR3VMRnNNaWRuSkVKWGNHa3dpQlIvckNLOXpXc2t5SEFVM1po?= =?utf-8?B?NUNmREQrNDB6bnVPM1I1eUFGUEtMaHNJeFI1SmtlbW9zMHd1ZVFWUEVUN0lG?= =?utf-8?B?MmNCdFhWYUZ6SUgrSEc2MFZZblJyYW9kTlgwREx5a0swYWdxUHNiZDYxYncv?= =?utf-8?B?L2lNMmwrVzNOQ05mT0pLQ056Zmgzc1hoR3VWdlBFdUVGN0Q0bjZBWXNBZTgw?= =?utf-8?B?TDAvZWtCYkZnZ1NHYUlqaU5icFNyMVN0YkpnQ2l2dXdFejhJRHhkV2YzSHU1?= =?utf-8?B?Y2JKeml6TWhBdDFrakw2YmRvSHkzQmh2TDRIUnR2UTNxU2NPem1lSFVZRHZP?= =?utf-8?B?RnR5a1dlRFZKcm53THlNMzd1MExTYTBVajEycE9sZjNPMFJBaEhQY2hTcFBT?= =?utf-8?B?cGFML0hxZ1N4ZllsWmlxYndNZ2lxU1BRUGk2NjFIaWZnS21mUGZQdkJUU3cw?= =?utf-8?B?OGxYTmZKcWxUaVR3K0t3MGtrdTJjNE5uVzNPQXFKblpjdktMdTJ6cDFHcGRs?= =?utf-8?B?U1lpTlVvbDN6dHZqN1ViNnlFZXNYcjdkQ2hKaVZOMElnUEVCaTU1MzFYRUZr?= =?utf-8?B?UTVsMlVaMWNtUjU0VWxGeXV2QXE4TW5KVnVRZ0VyUDVDYWxuQWRuRGF6dW9U?= =?utf-8?B?QnJwTjQrdjk3Q1RyWjhTbktUVmxiV0lRUHA2LzVIUGpUUU9HanliMzRxMkZ3?= =?utf-8?B?ZVM4azU5Z1krS1dmM2o1MVNqbnU0Skk1UXZRY3JYNWJjTUxmTWlobTF3Y1E1?= =?utf-8?B?UnhoaHkwdVVQakgzZEtRZXovbUViRExhRmc0SUZmcDhmZmFxYWFmcHN5Mnov?= =?utf-8?B?OXBhK3dKT1RJekNuNkYzNWVNUkFRM0FmK3gzMTExdkdtN3ZiN3VPbkR2aUN2?= =?utf-8?B?Q1RGMGErSDhCaDFhVFhER0NBMjdYZk4xU2M4K1JzWk50YXFTNlVmRStHbEQv?= =?utf-8?B?Z2had3hXbVcrWTl2M3Z1UC9OcFAvblZrWllHb1BiNlExVzN3cUE5ams3aEJK?= =?utf-8?B?eEdTUVd4RWdXbmd2d0lMaUxBWkdLNzVqVVZYeDZFV0NFRzdKVnlwdGM4dlg2?= =?utf-8?B?WlFCWkc5ZWUzdlR1YkVUcHBLOFpaQURBMmNldXEzZ3ZsVHRqWVk1djFudzlP?= =?utf-8?B?NVp4Zm1BNERpM29rZmdwcmxna3pUcGk4MmNtdHg0V09oZ0htc1JuaGRlR1dO?= =?utf-8?B?TFBtYTZxNnA0SzZ2Y0JCNVRwcjBZUkw5TlQ1dEZ3cm1FY3VURnQrQklQZzNQ?= =?utf-8?B?bGJhWmRSSG5mZ3VxdzdhRlRhWWFUNHdQbHZVRUt3R3ZQTDZJdjFySllMK1Ri?= =?utf-8?B?Wk13c01tdUQyckJlWU81RTBjOEtkYytEZlZ4ZWVRWGxUd0ZwYURtc1hJbTdv?= =?utf-8?B?czZQbE1HU1BLNkFxUHhtTkRqbUphUHZ2b2lMSFhYdGVFV0ZPa1ZkcGV5cW45?= =?utf-8?B?RndOYm9iUVY3Zk9ZUSszV1Y4RU5oWkxnSUVwSkMzVFp5TnhhbStDVUxUVW5t?= =?utf-8?B?blM5RzJPOFpXeFhYTGwxZ2xUZVUwYzBHRW5hOXhoMlU2cExqUVU2V3ZjMFhN?= =?utf-8?B?Q05DV0k3akZYNGdkRHRQK3pZYWpjMVg4UUFHSXV6ZENmUE42T0lQWjdpdFhN?= =?utf-8?B?NHJHa1I0bFRaMTU1Mzl0Q25YR042M0Y0MTVkREFXQThlM3ByVjlTbWtpdS9y?= =?utf-8?B?RDdmdG9xNFUvNDR2TS90em9FSHNtTFl0OHJwSjBUN0JITmJxbnhvVDNQOUhQ?= =?utf-8?B?TCtqRTkvdUpJa1A0aDBpSUtYM1FDRGh4WGdEZzgyRDFwaXEzL3VtU0IrUUd0?= =?utf-8?B?Z0xUWVdYWGVlUUV3YmFFVkJSOVVvN0tkUWY5UHhQTnpTNG5LQ2xWWkhHZUVC?= =?utf-8?B?OVQvenNBWGl1aEU5Z21SamphUGkwT3l1MW5kY0dBYVBIc0lPaE1jZz09?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6d1c908b-d728-465b-7342-08de6417f166 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:13.4661 (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: vzYvTqYK+u0dOHroy7ICfUqJNzw6iq5bqgFtswW3G9kubsSe9nPoZQyWlSSXenbaPldwBsybGVUrG3VmM15nhQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 This patch adds 802.11n feature support for both client and AP modes in the NXP Wi-Fi driver. - In client mode, the firmware assists the association process via the HOST_CMD_802_11_ASSOCIATE command. 802.11n IEs are converted from cfg80211 parameters to TLVs and appended to the host command. - In AP mode, 802.11n IEs are similarly converted and appended to the HOST_CMD_UAP_SYS_CONFIG command. - Due to firmware resource limitations, AMSDU aggregation and BA Rx buffer management (including reordering) are handled by the driver. This commit introduces the necessary files and logic to support 802.11n capabilities accordingly. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/11n.c | 837 ++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/11n.h | 158 ++++ drivers/net/wireless/nxp/nxpwifi/11n_aggr.c | 251 ++++++ drivers/net/wireless/nxp/nxpwifi/11n_aggr.h | 21 + .../net/wireless/nxp/nxpwifi/11n_rxreorder.c | 826 +++++++++++++++++ .../net/wireless/nxp/nxpwifi/11n_rxreorder.h | 71 ++ 6 files changed, 2164 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..2c2512712e63 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n.c @@ -0,0 +1,837 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi 802.11n helpers + * 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 "11ax.h" + +/* + * Fills HT capability information field, AMPDU Parameters field, HT exten= ded + * 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 =3D le16_to_cpu(ht_cap->cap_info); + u16 ht_ext_cap =3D le16_to_cpu(ht_cap->extended_ht_cap_info); + struct ieee80211_supported_band *sband =3D + 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 =3D + (AMPDU_FACTOR_64K & IEEE80211_HT_AMPDU_PARM_FACTOR) | + ((priv->adapter->hw_mpdu_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 =3D=3D NL80211_IFTYPE_STATION || + (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + priv->adapter->sec_chan_offset !=3D 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 &=3D ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; + + ht_cap_info =3D sband->ht_cap.cap; + if (bcn_ht_cap) { + if (!(bcn_ht_cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + ht_cap_info &=3D ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if (!(bcn_ht_cap & IEEE80211_HT_CAP_SGI_40)) + ht_cap_info &=3D ~IEEE80211_HT_CAP_SGI_40; + if (!(bcn_ht_cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) + ht_cap_info &=3D ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + } + ht_cap->cap_info =3D cpu_to_le16(ht_cap_info); + ht_cap->extended_ht_cap_info =3D cpu_to_le16(ht_ext_cap); + + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + ht_cap->tx_BF_cap_info =3D cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP); + + return 0; +} + +/* Return BA stream entry that matches the requested status. */ +static struct nxpwifi_tx_ba_stream_tbl * +nxpwifi_get_ba_status(struct nxpwifi_private *priv, int tid, + enum nxpwifi_ba_status ba_status) +{ + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl, *found =3D NULL; + + guard(rcu)(); + list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], = list) { + if (tx_ba_tsr_tbl->ba_status =3D=3D ba_status) { + found =3D tx_ba_tsr_tbl; + break; + } + } + return found; +} + +/* Handle DELBA command response (recreate or continue ADDBA as needed). */ +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 =3D &resp->params.del_ba; + u16 del_ba_param_set =3D le16_to_cpu(del_ba->del_ba_param_set); + + tid =3D del_ba_param_set >> DELBA_TID_POS; + if (del_ba->del_result =3D=3D 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 =3D nxpwifi_get_ba_status(priv, tid, 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 =3D nxpwifi_get_ba_status(priv, tid, 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; +} + +/* Handle ADDBA response; delete BA stream on 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 =3D &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 =3D le16_to_cpu(add_ba_rsp->block_ack_param_set); + + add_ba_rsp->ssn =3D cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) + & SSN_MASK); + + tid =3D u16_get_bits(block_ack_param_set, IEEE80211_ADDBA_PARAM_TID_MASK); + + tid_down =3D nxpwifi_wmm_downgrade_tid(priv, tid); + ra_list =3D nxpwifi_wmm_get_ralist_node(priv, tid_down, + add_ba_rsp->peer_mac_addr); + if (le16_to_cpu(add_ba_rsp->status_code) !=3D BA_RESULT_SUCCESS) { + if (ra_list) { + ra_list->ba_status =3D BA_SETUP_NONE; + ra_list->amsdu_in_ampdu =3D false; + } + nxpwifi_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, true); + if (add_ba_rsp->add_rsp_result !=3D BA_RESULT_TIMEOUT) + priv->aggr_prio_tbl[tid].ampdu_ap =3D + BA_STREAM_NOT_ALLOWED; + return 0; + } + + guard(rcu)(); + tx_ba_tbl =3D 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 =3D BA_SETUP_COMPLETE; + if ((block_ack_param_set & IEEE80211_ADDBA_PARAM_AMSDU_MASK) && + priv->add_ba_param.tx_amsdu && + priv->aggr_prio_tbl[tid].amsdu !=3D BA_STREAM_NOT_ALLOWED) + tx_ba_tbl->amsdu =3D true; + else + tx_ba_tbl->amsdu =3D false; + if (ra_list) { + ra_list->amsdu_in_ampdu =3D tx_ba_tbl->amsdu; + ra_list->ba_status =3D BA_SETUP_COMPLETE; + } + } else { + nxpwifi_dbg(priv->adapter, ERROR, "BA stream not created\n"); + } + + return 0; +} + +/* + * Reconfigure Tx buffer command. + * Set command ID/action/size; set Tx buffer size on SET; ensure little-en= dian. + */ +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 =3D &cmd->params.tx_buf; + u16 action =3D (u16)cmd_action; + + cmd->command =3D cpu_to_le16(HOST_CMD_RECONFIGURE_TX_BUFF); + cmd->size =3D + cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); + tx_buf->action =3D cpu_to_le16(action); + switch (action) { + case HOST_ACT_GEN_SET: + nxpwifi_dbg(priv->adapter, CMD, + "cmd: set tx_buf=3D%d\n", *buf_size); + tx_buf->buff_size =3D cpu_to_le16(*buf_size); + break; + case HOST_ACT_GEN_GET: + default: + tx_buf->buff_size =3D 0; + break; + } + return 0; +} + +/* + * AMSDU aggregation control command. + * Set ID/action/size; set AMSDU params on SET; ensure little-endian. + */ +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 =3D + &cmd->params.amsdu_aggr_ctrl; + u16 action =3D (u16)cmd_action; + + cmd->command =3D cpu_to_le16(HOST_CMD_AMSDU_AGGR_CTRL); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) + + S_DS_GEN); + amsdu_ctrl->action =3D cpu_to_le16(action); + switch (action) { + case HOST_ACT_GEN_SET: + amsdu_ctrl->enable =3D cpu_to_le16(aa_ctrl->enable); + amsdu_ctrl->curr_buf_size =3D 0; + break; + case HOST_ACT_GEN_GET: + default: + amsdu_ctrl->curr_buf_size =3D 0; + break; + } + return 0; +} + +/* + * 11n configuration command. + * Set action, HT Tx capability/info, and misc config when 11ac HW is pres= ent. + */ +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 =3D &cmd->params.htcfg; + + cmd->command =3D cpu_to_le16(HOST_CMD_11N_CFG); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); + htcfg->action =3D cpu_to_le16(cmd_action); + htcfg->ht_tx_cap =3D cpu_to_le16(txcfg->tx_htcap); + htcfg->ht_tx_info =3D cpu_to_le16(txcfg->tx_htinfo); + + if (priv->adapter->is_hw_11ac_capable) + htcfg->misc_config =3D cpu_to_le16(txcfg->misc_config); + + return 0; +} + +/* + * Append 11n TLVs to the caller-owned buffer. + * Caller allocates space; no size checks here. + * May add: HT Cap, HT Operation + channel list, 20/40 BSS Coexistence, + * and Extended Capabilities (HS2/TWT bits when applicable). + */ +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_chan_scan_param_set *chan_param; + struct nxpwifi_ie_types_2040bssco *bss_co_2040; + struct nxpwifi_ie_types_extcap *ext_cap; + int ret_len =3D 0; + struct ieee80211_supported_band *sband; + struct element *hdr; + u8 radio_type; + + if (!buffer || !*buffer) + return ret_len; + + radio_type =3D nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + sband =3D priv->wdev.wiphy->bands[radio_type]; + + if (bss_desc->bcn_ht_cap) { + ht_cap =3D (struct nxpwifi_ie_types_htcap *)*buffer; + memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap)); + ht_cap->header.type =3D cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len =3D + 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. */ + if (bss_desc->bcn_ht_oper) { + u8 ht_param =3D bss_desc->bcn_ht_oper->ht_param; + u8 radio =3D + nxpwifi_band_to_radio_type(bss_desc->bss_band); + int freq =3D + ieee80211_channel_to_frequency(bss_desc->channel, + radio); + struct ieee80211_channel *chan =3D + 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 &=3D + cpu_to_le16 + (~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + ht_cap->ht_cap.cap_info &=3D + 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 &=3D + cpu_to_le16 + (~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + ht_cap->ht_cap.cap_info &=3D + cpu_to_le16(~IEEE80211_HT_CAP_SGI_40); + } + break; + } + } + + *buffer +=3D sizeof(struct nxpwifi_ie_types_htcap); + ret_len +=3D sizeof(struct nxpwifi_ie_types_htcap); + } + + if (bss_desc->bcn_ht_oper) { + chan_list =3D + (struct nxpwifi_ie_types_chan_list_param_set *)*buffer; + chan_param =3D chan_list->chan_scan_param; + memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1)); + chan_list->header.type =3D cpu_to_le16(TLV_TYPE_CHANLIST); + chan_list->header.len =3D cpu_to_le16(sizeof(*chan_param)); + chan_param->chan_number =3D bss_desc->bcn_ht_oper->primary_chan; + chan_param->band_cfg =3D + nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + bss_desc->bcn_vht_oper && + bss_desc->bcn_vht_oper->chan_width =3D=3D + IEEE80211_VHT_CHANWIDTH_80MHZ) { + SET_SECONDARYCHAN(chan_param->band_cfg, + (bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); + chan_param->band_cfg |=3D + ((CHAN_BW_80MHZ << + BAND_CFG_CHAN_WIDTH_SHIFT_BIT) & + BAND_CFG_CHAN_WIDTH_MASK); + } else 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_param->band_cfg, + (bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); + chan_param->band_cfg |=3D + ((CHAN_BW_40MHZ << + BAND_CFG_CHAN_WIDTH_SHIFT_BIT) & + BAND_CFG_CHAN_WIDTH_MASK); + } + + *buffer +=3D struct_size(chan_list, chan_scan_param, 1); + ret_len +=3D struct_size(chan_list, chan_scan_param, 1); + } + + if (bss_desc->bcn_bss_co_2040) { + bss_co_2040 =3D (struct nxpwifi_ie_types_2040bssco *)*buffer; + memset(bss_co_2040, 0, + sizeof(struct nxpwifi_ie_types_2040bssco)); + bss_co_2040->header.type =3D cpu_to_le16(WLAN_EID_BSS_COEX_2040); + bss_co_2040->header.len =3D + 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 +=3D sizeof(struct nxpwifi_ie_types_2040bssco); + ret_len +=3D sizeof(struct nxpwifi_ie_types_2040bssco); + } + + if (bss_desc->bcn_ext_cap) { + u8 *ext_capab; + + hdr =3D (void *)bss_desc->bcn_ext_cap; + + ext_capab =3D (u8 *)cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, priv->gen_= ie_buf, + priv->gen_ie_buf_len); + if (ext_capab) { + ext_capab +=3D 2; + } else { + ext_cap =3D (struct nxpwifi_ie_types_extcap *)*buffer; + memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap) + hdr->datale= n); + ext_cap->header.type =3D cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + ext_cap->header.len =3D cpu_to_le16(hdr->datalen); + ext_capab =3D ext_cap->ext_capab; + *buffer +=3D sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen; + ret_len +=3D sizeof(struct nxpwifi_ie_types_extcap) + hdr->datalen; + } + + if (hdr->datalen > 3 && + ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) + priv->hs2_enabled =3D true; + else + priv->hs2_enabled =3D false; + + if (nxpwifi_is_11ax_twt_supported(priv, bss_desc)) + ext_capab[9] |=3D + WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT; + } + return ret_len; +} + +/* Check if pointer is a valid Tx BA stream entry. */ +static bool +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; + bool ret =3D false; + int tid; + + tid =3D tx_tbl_ptr->tid; + guard(rcu)(); + list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], = list) { + if (tx_ba_tsr_tbl =3D=3D tx_tbl_ptr) { + ret =3D true; + break; + } + } + return ret; +} + +/* Delete a Tx BA stream entry (after validating pointer). */ +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_rcu(&tbl->list); + kfree_rcu(tbl, rcu); +} + +/* Delete all 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; + + for (i =3D 0; i < MAX_NUM_TID; i++) { + spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->tx_ba_stream_tbl_ptr[i], list) + nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[i]); + + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr[i]); + + priv->aggr_prio_tbl[i].ampdu_ap =3D + priv->aggr_prio_tbl[i].ampdu_user; + } +} + +/* Return BA stream entry for given RA/TID. */ +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 =3D NULL; + + list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[tid], = list) { + if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && + tx_ba_tsr_tbl->tid =3D=3D tid) + return tx_ba_tsr_tbl; + } + return NULL; +} + +/* Create Tx BA stream entry for given RA/TID. */ +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; + struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl; + + guard(rcu)(); + tx_ba_tbl =3D nxpwifi_get_ba_tbl(priv, tid, ra); + + if (!tx_ba_tbl) { + new_node =3D kzalloc(sizeof(*new_node), GFP_ATOMIC); + if (!new_node) + return; + + tid_down =3D nxpwifi_wmm_downgrade_tid(priv, tid); + ra_list =3D nxpwifi_wmm_get_ralist_node(priv, tid_down, ra); + if (ra_list) { + ra_list->ba_status =3D ba_status; + ra_list->amsdu_in_ampdu =3D false; + } + INIT_LIST_HEAD(&new_node->list); + + new_node->tid =3D tid; + new_node->ba_status =3D ba_status; + memcpy(new_node->ra, ra, ETH_ALEN); + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock[tid]); + list_add_tail_rcu(&new_node->list, &priv->tx_ba_stream_tbl_ptr[tid]); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[tid]); + } +} + +/* Send ADDBA request to the given TID/RA. */ +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 =3D priv->add_ba_param.tx_win_size; + static u8 dialog_tok; + 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 =3D (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 !=3D BA_STREAM_NOT_ALLOWED) + block_ack_param_set |=3D IEEE80211_ADDBA_PARAM_AMSDU_MASK; + + add_ba_req.block_ack_param_set =3D cpu_to_le16(block_ack_param_set); + add_ba_req.block_ack_tmo =3D cpu_to_le16((u16)priv->add_ba_param.timeout); + + ++dialog_tok; + + if (dialog_tok =3D=3D 0) + dialog_tok =3D 1; + + add_ba_req.dialog_token =3D dialog_tok; + memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + return nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_REQ, + 0, 0, &add_ba_req, false); +} + +/* Send DELBA request to the given TID/RA. */ +int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac, + int initiator) +{ + struct host_cmd_ds_11n_delba delba; + u16 del_ba_param_set; + + memset(&delba, 0, sizeof(delba)); + + del_ba_param_set =3D tid << DELBA_TID_POS; + + if (initiator) + del_ba_param_set |=3D IEEE80211_DELBA_PARAM_INITIATOR_MASK; + else + del_ba_param_set &=3D ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; + + delba.del_ba_param_set =3D 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 */ + return nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, + HOST_ACT_GEN_SET, 0, &delba, false); +} + +/* Send DELBA to specific TID. */ +void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid) +{ + struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr; + u8 ta[ETH_ALEN]; + bool found =3D false; + + rcu_read_lock(); + list_for_each_entry_rcu(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr[tid], = list) { + if (rx_reor_tbl_ptr->tid =3D=3D tid) { + memcpy(ta, rx_reor_tbl_ptr->ta, ETH_ALEN); + found =3D true; + break; + } + } + rcu_read_unlock(); + + if (found) { + nxpwifi_dbg(priv->adapter, INFO, + "Send delba to tid=3D%d, %pM\n", tid, ta); + nxpwifi_send_delba(priv, tid, ta, 0); + } +} + +/* Handle DELBA event; remove BA stream. */ +void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba) +{ + struct host_cmd_ds_11n_delba *cmd_del_ba =3D + (struct host_cmd_ds_11n_delba *)del_ba; + u16 del_ba_param_set =3D le16_to_cpu(cmd_del_ba->del_ba_param_set); + int tid; + + tid =3D 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)); +} + +/* Retrieve Rx reordering table. */ +int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv, + struct nxpwifi_ds_rx_reorder_tbl *buf) +{ + int i, j; + struct nxpwifi_ds_rx_reorder_tbl *rx_reo_tbl =3D buf; + struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr; + int count =3D 0; + + guard(rcu)(); + for (j =3D 0; j < MAX_NUM_TID; j++) { + list_for_each_entry_rcu(rx_reorder_tbl_ptr, + &priv->rx_reorder_tbl_ptr[j], + list) { + rx_reo_tbl->tid =3D (u16)rx_reorder_tbl_ptr->tid; + memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); + rx_reo_tbl->start_win =3D rx_reorder_tbl_ptr->start_win; + rx_reo_tbl->win_size =3D rx_reorder_tbl_ptr->win_size; + for (i =3D 0; i < rx_reorder_tbl_ptr->win_size; ++i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + rx_reo_tbl->buffer[i] =3D true; + else + rx_reo_tbl->buffer[i] =3D false; + } + rx_reo_tbl++; + count++; + + if (count >=3D NXPWIFI_MAX_RX_BASTREAM_SUPPORTED) + return count; + } + } + + return count; +} + +/* Retrieve 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 =3D buf; + int count =3D 0; + int i; + + guard(rcu)(); + for (i =3D 0; i < MAX_NUM_TID; i++) { + list_for_each_entry_rcu(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr[i], l= ist) { + rx_reo_tbl->tid =3D (u16)tx_ba_tsr_tbl->tid; + nxpwifi_dbg(priv->adapter, DATA, "data: %s tid=3D%d\n", + __func__, rx_reo_tbl->tid); + memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); + rx_reo_tbl->amsdu =3D tx_ba_tsr_tbl->amsdu; + rx_reo_tbl++; + count++; + if (count >=3D NXPWIFI_MAX_TX_BASTREAM_SUPPORTED) + return count; + } + } + + return count; +} + +/* Delete Tx BA stream entry by RA. */ +void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *= ra) +{ + struct nxpwifi_tx_ba_stream_tbl *tbl; + int i; + + if (!ra) + return; + + for (i =3D 0; i < MAX_NUM_TID; i++) { + spin_lock_bh(&priv->tx_ba_stream_tbl_lock[i]); + list_for_each_entry_rcu(tbl, &priv->tx_ba_stream_tbl_ptr[i], 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[i]); + } +} + +/* Initialize BlockAck parameters. */ +void nxpwifi_set_ba_params(struct nxpwifi_private *priv) +{ + priv->add_ba_param.timeout =3D NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size =3D + NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size =3D + NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size =3D + NXPWIFI_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size =3D + NXPWIFI_STA_AMPDU_DEF_RXWINSIZE; + } + + priv->add_ba_param.tx_amsdu =3D true; + priv->add_ba_param.rx_amsdu =3D 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: + case 173: + sec_offset =3D 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: + case 169: + case 177: + sec_offset =3D IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case 165: + default: + sec_offset =3D IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + + return sec_offset; +} + +/* Send DELBA to entries in the Tx BA stream table. */ +static void +nxpwifi_send_delba_txbastream_tbl(struct nxpwifi_private *priv, u8 tid) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr; + + guard(rcu)(); + list_for_each_entry_rcu(tx_ba_stream_tbl_ptr, + &priv->tx_ba_stream_tbl_ptr[tid], list) { + if (tx_ba_stream_tbl_ptr->ba_status =3D=3D BA_SETUP_COMPLETE) { + if (tid =3D=3D tx_ba_stream_tbl_ptr->tid) { + nxpwifi_dbg(adapter, INFO, + "Tx:Send delba to tid=3D%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); + break; + } + } + } +} + +/* + * Update tx_win_size for all interfaces and send DELBA when it changes. + */ +void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *adapter) +{ + u8 i, j; + u32 tx_win_size; + struct nxpwifi_private *priv; + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + tx_win_size =3D priv->add_ba_param.tx_win_size; + + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_STA) + priv->add_ba_param.tx_win_size =3D + NXPWIFI_STA_AMPDU_DEF_TXWINSIZE; + + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_UAP) + priv->add_ba_param.tx_win_size =3D + NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE; + + if (adapter->coex_win_size) { + if (adapter->coex_tx_win_size) + priv->add_ba_param.tx_win_size =3D + adapter->coex_tx_win_size; + } + + if (tx_win_size !=3D priv->add_ba_param.tx_win_size) { + if (!priv->media_connected) + continue; + for (j =3D 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..fb54230cf83a --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: 802.11n support + * + * 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 *pri= v, + 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 bool +nxpwifi_is_station_ampdu_allowed(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, int tid) +{ + struct nxpwifi_sta_node *node; + + guard(rcu)(); + node =3D nxpwifi_get_sta_entry(priv, ptr->ra); + if (unlikely(!node)) + return false; + + if (node->ampdu_sta[tid] =3D=3D BA_STREAM_NOT_ALLOWED) + return false; + + return true; +} + +/* Check if AMPDU is allowed for the given TID. */ +static inline bool +nxpwifi_is_ampdu_allowed(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ptr, int tid) +{ + if (is_broadcast_ether_addr(ptr->ra)) + return false; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) + return nxpwifi_is_station_ampdu_allowed(priv, ptr, tid); + + return priv->aggr_prio_tbl[tid].ampdu_ap !=3D BA_STREAM_NOT_ALLOWED; +} + +/* Check if AMSDU is allowed for the given TID. */ +static inline bool +nxpwifi_is_amsdu_allowed(struct nxpwifi_private *priv, int tid) +{ + bool amsdu_enabled =3D priv->aggr_prio_tbl[tid].amsdu !=3D BA_STREAM_NOT_= ALLOWED; + bool rate_ok =3D priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x0= 3); + + return amsdu_enabled && rate_ok; +} + +/* Check if there is available space for a new BA stream. */ +static inline bool +nxpwifi_space_avail_for_new_ba_stream(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + u8 i, j; + size_t ba_stream_num =3D 0; + size_t ba_stream_max =3D NXPWIFI_MAX_TX_BASTREAM_SUPPORTED; + + if (adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) { + ba_stream_max =3D GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); + if (!ba_stream_max) + ba_stream_max =3D NXPWIFI_MAX_TX_BASTREAM_SUPPORTED; + } + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + for (j =3D 0; j < MAX_NUM_TID; j++) + ba_stream_num +=3D list_count_nodes(&priv->tx_ba_stream_tbl_ptr[j]); + } + + return ba_stream_num < ba_stream_max; +} + +/* Find the Tx BA stream to delete and return its TID and RA. */ +static inline bool +nxpwifi_find_stream_to_delete(struct nxpwifi_private *priv, int ptr_tid, + int *ptid, u8 *ra) +{ + int search_tid =3D priv->aggr_prio_tbl[ptr_tid].ampdu_user; + bool found =3D false; + struct nxpwifi_tx_ba_stream_tbl *tx_tbl; + int candidate_tid; + + spin_lock_bh(&priv->tx_ba_stream_tbl_lock[ptr_tid]); + + list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr[ptr_tid], list) { + candidate_tid =3D priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; + + if (search_tid > candidate_tid) { + search_tid =3D candidate_tid; + *ptid =3D tx_tbl->tid; + memcpy(ra, tx_tbl->ra, ETH_ALEN); + found =3D true; + } + } + + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[ptr_tid]); + + return found; +} + +/* Check whether the associated station is 11n=E2=80=91enabled. */ +static inline int nxpwifi_is_sta_11n_enabled(struct nxpwifi_private *priv, + struct nxpwifi_sta_node *node) +{ + if (!node || (priv->bss_role =3D=3D 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/wire= less/nxp/nxpwifi/11n_aggr.c new file mode 100644 index 000000000000..dea677596767 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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" + +/* + * Build an AMSDU subframe for aggregation, with fields + * (DA | SA | Length | SNAP header | MSDU), and compute padding + * to align the subframe to a 4=E2=80=91byte boundary. + */ +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 =3D { + 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 =3D skb_put(skb_aggr, sizeof(*tx_header)); + + /* Copy DA and SA */ + dt_offset =3D 2 * ETH_ALEN; + memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); + + /* Copy SNAP header */ + snap.snap_type =3D ((struct ethhdr *)skb_src->data)->h_proto; + + dt_offset +=3D 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 =3D 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 =3D (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 t= he + * 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 =3D (struct txpd *)skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + + /* Original priority has been overwritten */ + local_tx_pd->priority =3D (u8)skb->priority; + local_tx_pd->pkt_delay_2ms =3D + nxpwifi_wmm_compute_drv_pkt_delay(priv, skb); + local_tx_pd->bss_num =3D priv->bss_num; + local_tx_pd->bss_type =3D priv->bss_type; + /* Always zero as the data is followed by struct txpd */ + local_tx_pd->tx_pkt_offset =3D cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->tx_pkt_type =3D cpu_to_le16(PKT_TYPE_AMSDU); + local_tx_pd->tx_pkt_length =3D cpu_to_le16(skb->len - + sizeof(*local_tx_pd)); + + if (local_tx_pd->tx_control =3D=3D 0) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control =3D cpu_to_le32(priv->pkt_tx_ctrl); + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA && + priv->adapter->pps_uapsd_mode) { + if (nxpwifi_check_last_packet_indication(priv)) { + priv->adapter->tx_lock_flag =3D true; + local_tx_pd->flags =3D + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET; + } + } +} + +/* + * Build an aggregated MSDU packet by encapsulating buffers from the RA + * list as AMSDU subframes and concatenating them. A TxPD is prepended + * before transmission to form the final AMSDU packet. + */ +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 =3D priv->adapter; + struct sk_buff *skb_aggr, *skb_src; + struct nxpwifi_txinfo *tx_info_aggr, *tx_info_src; + int pad =3D 0, aggr_num =3D 0, ret; + struct nxpwifi_tx_param tx_param; + struct txpd *ptx_pd =3D NULL; + int headroom =3D adapter->intf_hdr_len; + + skb_src =3D skb_peek(&pra_list->skb_head); + if (!skb_src) { + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + return 0; + } + + tx_info_src =3D NXPWIFI_SKB_TXCB(skb_src); + skb_aggr =3D 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 =3D NXPWIFI_SKB_TXCB(skb_aggr); + + memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); + tx_info_aggr->bss_type =3D tx_info_src->bss_type; + tx_info_aggr->bss_num =3D tx_info_src->bss_num; + + tx_info_aggr->flags |=3D NXPWIFI_BUF_FLAG_AGGR_PKT; + skb_aggr->priority =3D skb_src->priority; + skb_aggr->tstamp =3D 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 =3D 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 =3D 0; + break; + } + skb_put(skb_aggr, pad); + + skb_src =3D 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) =3D=3D NXPWIFI_BSS_ROLE_STA) + ptx_pd =3D (struct txpd *)skb_aggr->data; + + skb_push(skb_aggr, headroom); + tx_info_aggr->aggr_num =3D 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 =3D skb_src->len + sizeof(struct txpd); + else + tx_param.next_pkt_len =3D 0; + + ret =3D 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) =3D=3D NXPWIFI_BSS_ROLE_STA && + adapter->pps_uapsd_mode && adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag =3D false; + if (ptx_pd) + ptx_pd->flags =3D 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 |=3D 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 !=3D -EBUSY) + nxpwifi_rotate_priolists(priv, pra_list, ptrindex); + + return 0; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h b/drivers/net/wire= less/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..c925a2485dcc --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c @@ -0,0 +1,826 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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" +/* Dispatch A-MSDU to stack. */ +static int nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct rxpd *local_rx_pd =3D (struct rxpd *)(skb->data); + int ret; + + if (le16_to_cpu(local_rx_pd->rx_pkt_type) =3D=3D 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 =3D __skb_dequeue(&list); + + if (priv->bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP) + ret =3D nxpwifi_uap_recv_packet(priv, rx_skb); + else + ret =3D nxpwifi_recv_packet(priv, rx_skb); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "Rx of A-MSDU failed"); + } + return 0; + } + + return -EINVAL; +} + +/* Process RX packet and forward to stack. */ +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 =3D nxpwifi_11n_dispatch_amsdu_pkt(priv, payload); + if (!ret) + return 0; + + if (priv->bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP) + return nxpwifi_handle_uap_rx_forward(priv, payload); + + return nxpwifi_process_rx_packet(priv, payload); +} + +/* Dispatch packets up to start_win. */ +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, tid; + + tid =3D tbl->tid; + __skb_queue_head_init(&list); + spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]); + + pkt_to_send =3D (start_win > tbl->start_win) ? + min((start_win - tbl->start_win), tbl->win_size) : + tbl->win_size; + + for (i =3D 0; i < pkt_to_send; ++i) { + if (tbl->rx_reorder_ptr[i]) { + skb =3D tbl->rx_reorder_ptr[i]; + __skb_queue_tail(&list, skb); + tbl->rx_reorder_ptr[i] =3D NULL; + } + } + + /* Simulate circular buffer via rotation. */ + for (i =3D 0; i < tbl->win_size - pkt_to_send; ++i) { + tbl->rx_reorder_ptr[i] =3D tbl->rx_reorder_ptr[pkt_to_send + i]; + tbl->rx_reorder_ptr[pkt_to_send + i] =3D NULL; + } + + tbl->start_win =3D start_win; + spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]); + + while ((skb =3D __skb_dequeue(&list))) + nxpwifi_11n_dispatch_pkt(priv, skb); +} + +/* Dispatch packets until a hole is found. */ +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, tid; + + tid =3D tbl->tid; + __skb_queue_head_init(&list); + spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]); + + for (i =3D 0; i < tbl->win_size; ++i) { + if (!tbl->rx_reorder_ptr[i]) + break; + skb =3D tbl->rx_reorder_ptr[i]; + __skb_queue_tail(&list, skb); + tbl->rx_reorder_ptr[i] =3D NULL; + } + + /* Simulate circular buffer via rotation. */ + if (i > 0) { + xchg =3D tbl->win_size - i; + for (j =3D 0; j < xchg; ++j) { + tbl->rx_reorder_ptr[j] =3D tbl->rx_reorder_ptr[i + j]; + tbl->rx_reorder_ptr[i + j] =3D NULL; + } + } + tbl->start_win =3D (tbl->start_win + i) & (MAX_TID_VALUE - 1); + + spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]); + + while ((skb =3D __skb_dequeue(&list))) + nxpwifi_11n_dispatch_pkt(priv, skb); +} + +/* Delete RX reorder entry and flush pending packets. */ +static void +nxpwifi_del_rx_reorder_entry(struct nxpwifi_private *priv, + struct nxpwifi_rx_reorder_tbl *tbl) +{ + int start_win, tid; + + if (!tbl) + return; + + tid =3D tbl->tid; + + atomic_set(&priv->adapter->rx_ba_teardown_pending, 1); + flush_workqueue(priv->adapter->rx_workqueue); + + start_win =3D (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); + nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + + timer_delete_sync(&tbl->timer_context.timer); + tbl->timer_context.timer_is_set =3D false; + + spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]); + list_del_rcu(&tbl->list); + spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]); + + kfree(tbl->rx_reorder_ptr); + kfree_rcu(tbl, rcu); + + atomic_set(&priv->adapter->rx_ba_teardown_pending, 0); +} + +/* Lookup RX reorder entry by TID/TA. */ +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, *found =3D NULL; + + guard(rcu)(); + + list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[tid], list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid =3D=3D tid) { + found =3D tbl; + break; + } + } + + return found; +} + +/* Delete RX reorder entries by TA. */ +void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8= *ta) +{ + struct nxpwifi_rx_reorder_tbl *tbl, *tmp; + LIST_HEAD(to_delete); + int i; + + if (!ta) + return; + + for (i =3D 0; i < MAX_NUM_TID; i++) { + guard(rcu)(); + list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[i], list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN)) { + INIT_LIST_HEAD(&tbl->tmp_list); + list_add_tail(&tbl->tmp_list, &to_delete); + } + } + + list_for_each_entry_safe(tbl, tmp, &to_delete, tmp_list) + nxpwifi_del_rx_reorder_entry(priv, tbl); + + INIT_LIST_HEAD(&to_delete); + } +} + +/* Find last buffered sequence index. */ +static int +nxpwifi_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) +{ + struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr =3D ctx->ptr; + int i; + + guard(rcu)(); + for (i =3D rx_reorder_tbl_ptr->win_size - 1; i >=3D 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + return i; + } + + return -EINVAL; +} + +/* Flush and dispatch buffered packets on timer. */ +static void +nxpwifi_flush_data(struct timer_list *t) +{ + struct reorder_tmr_cnxt *ctx =3D + timer_container_of(ctx, t, timer); + int start_win, seq_num; + + ctx->timer_is_set =3D false; + seq_num =3D 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 =3D (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); + nxpwifi_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, + start_win); +} + +/* Create RX reorder entry (TID/TA, SSN, winsize, timer). */ +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 =3D 0; + struct nxpwifi_sta_node *node; + + /* Existing TID/TA: flush and move window to SSN. */ + tbl =3D 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 =3D kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) + return; + + INIT_LIST_HEAD(&new_node->list); + new_node->tid =3D tid; + memcpy(new_node->ta, ta, ETH_ALEN); + new_node->start_win =3D seq_num; + new_node->init_win =3D seq_num; + new_node->flags =3D 0; + + if (nxpwifi_queuing_ra_based(priv)) { + if (priv->bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP) { + guard(rcu)(); + node =3D nxpwifi_get_sta_entry(priv, ta); + if (node) + last_seq =3D node->rx_seq[tid]; + } + } else { + guard(rcu)(); + node =3D nxpwifi_get_sta_entry(priv, ta); + if (node) + last_seq =3D node->rx_seq[tid]; + else + last_seq =3D priv->rx_seq[tid]; + } + + nxpwifi_dbg(priv->adapter, INFO, + "info: last_seq=3D%d start_win=3D%d\n", + last_seq, new_node->start_win); + + if (last_seq !=3D NXPWIFI_DEF_11N_RX_SEQ_NUM && + last_seq >=3D new_node->start_win) { + new_node->start_win =3D last_seq + 1; + new_node->flags |=3D RXREOR_INIT_WINDOW_SHIFT; + } + + new_node->win_size =3D win_size; + + new_node->rx_reorder_ptr =3D 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 =3D new_node; + new_node->timer_context.priv =3D priv; + new_node->timer_context.timer_is_set =3D false; + + timer_setup(&new_node->timer_context.timer, nxpwifi_flush_data, 0); + + for (i =3D 0; i < win_size; ++i) + new_node->rx_reorder_ptr[i] =3D NULL; + + spin_lock_bh(&priv->rx_reorder_tbl_lock[tid]); + list_add_tail_rcu(&new_node->list, &priv->rx_reorder_tbl_ptr[tid]); + spin_unlock_bh(&priv->rx_reorder_tbl_lock[tid]); +} + +static void +nxpwifi_11n_rxreorder_timer_restart(struct nxpwifi_rx_reorder_tbl *tbl) +{ + u32 min_flush_time; + + if (tbl->win_size >=3D NXPWIFI_BA_WIN_SIZE_32) + min_flush_time =3D MIN_FLUSH_TIMER_15_MS; + else + min_flush_time =3D 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 =3D true; +} + +/* Prepare ADDBA request. */ +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 =3D &cmd->params.add_ba_req; + + cmd->command =3D cpu_to_le16(HOST_CMD_11N_ADDBA_REQ); + cmd->size =3D cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); + memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); + + return 0; +} + +/* Prepare ADDBA response and create RX reorder table. */ +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 =3D &cmd->params.add_ba_rsp; + u32 rx_win_size =3D priv->add_ba_param.rx_win_size; + u8 tid; + int win_size; + u16 block_ack_param_set; + + cmd->command =3D cpu_to_le16(HOST_CMD_11N_ADDBA_RSP); + cmd->size =3D 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 =3D cmd_addba_req->dialog_token; + add_ba_rsp->block_ack_tmo =3D cmd_addba_req->block_ack_tmo; + add_ba_rsp->ssn =3D cmd_addba_req->ssn; + + block_ack_param_set =3D le16_to_cpu(cmd_addba_req->block_ack_param_set); + tid =3D (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + add_ba_rsp->status_code =3D cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); + block_ack_param_set &=3D ~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 =3D=3D BA_STREAM_NOT_ALLOWED) + block_ack_param_set &=3D ~IEEE80211_ADDBA_PARAM_AMSDU_MASK; + block_ack_param_set |=3D rx_win_size << BLOCKACKPARAM_WINSIZE_POS; + add_ba_rsp->block_ack_param_set =3D cpu_to_le16(block_ack_param_set); + win_size =3D (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 =3D 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; +} + +/* Prepare DELBA command. */ +int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_delba *del_ba =3D &cmd->params.del_ba; + + cmd->command =3D cpu_to_le16(HOST_CMD_11N_DELBA); + cmd->size =3D cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); + memcpy(del_ba, data_buf, sizeof(*del_ba)); + + return 0; +} + +/* Decide and perform RX reordering for a packet. */ +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 =3D false; + int ret =3D 0; + + tbl =3D nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta); + if (!tbl) { + if (pkt_type !=3D PKT_TYPE_BAR) + nxpwifi_11n_dispatch_pkt(priv, payload); + return ret; + } + + if (pkt_type =3D=3D PKT_TYPE_AMSDU && !tbl->amsdu) { + nxpwifi_11n_dispatch_pkt(priv, payload); + return ret; + } + + start_win =3D tbl->start_win; + prev_start_win =3D start_win; + win_size =3D tbl->win_size; + end_win =3D ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { + init_window_shift =3D true; + tbl->flags &=3D ~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 &=3D ~RXREOR_FORCE_NO_DROP; + } else if (init_window_shift && seq_num < start_win && + seq_num >=3D 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 =3D seq_num; + tbl->start_win =3D start_win; + end_win =3D ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + } else { + /* Drop packet if seq_num < start_win. */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >=3D ((start_win + TWOPOW11) & + (MAX_TID_VALUE - 1)) && + seq_num < start_win) { + ret =3D -EINVAL; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num >=3D (start_win + TWOPOW11))) { + ret =3D -EINVAL; + goto done; + } + } + + /* Adjust seq_num for BAR (WinStart =3D seq_num). */ + if (pkt_type =3D=3D PKT_TYPE_BAR) + seq_num =3D ((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 =3D seq_num; + if (((end_win - win_size) + 1) >=3D 0) + start_win =3D (end_win - win_size) + 1; + else + start_win =3D (MAX_TID_VALUE - (win_size - end_win)) + 1; + nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + } + + if (pkt_type !=3D PKT_TYPE_BAR) { + if (seq_num >=3D start_win) + pkt_index =3D seq_num - start_win; + else + pkt_index =3D (seq_num + MAX_TID_VALUE) - start_win; + + if (tbl->rx_reorder_ptr[pkt_index]) { + ret =3D -EINVAL; + goto done; + } + + tbl->rx_reorder_ptr[pkt_index] =3D payload; + } + + /* Dispatch sequentially until a hole; update start_win. */ + nxpwifi_11n_scan_and_dispatch(priv, tbl); + +done: + if (!tbl->timer_context.timer_is_set || + prev_start_win !=3D tbl->start_win) + nxpwifi_11n_rxreorder_timer_restart(tbl); + return ret; +} + +/* Delete BA entry for TID/TA. */ +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 =3D=3D TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl =3D (initiator) ? true : false; + else + cleanup_rx_reorder_tbl =3D (initiator) ? false : true; + + nxpwifi_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=3D%d initiator= =3D%d\n", + peer_mac, tid, initiator); + + if (cleanup_rx_reorder_tbl) { + tbl =3D 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 { + guard(rcu)(); + ptx_tbl =3D 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 =3D nxpwifi_wmm_downgrade_tid(priv, tid); + ra_list =3D nxpwifi_wmm_get_ralist_node(priv, tid_down, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu =3D false; + ra_list->ba_status =3D BA_SETUP_NONE; + } + spin_lock_bh(&priv->tx_ba_stream_tbl_lock[tid]); + nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock[tid]); + } +} + +/* Handle ADDBA response. */ +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 =3D &resp->params.add_ba_rsp; + int tid, win_size; + struct nxpwifi_rx_reorder_tbl *tbl; + u16 block_ack_param_set; + + block_ack_param_set =3D le16_to_cpu(add_ba_rsp->block_ack_param_set); + + tid =3D (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 stre= am */ + if (le16_to_cpu(add_ba_rsp->status_code) !=3D BA_RESULT_SUCCESS) { + nxpwifi_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=3D%d)\n", + add_ba_rsp->peer_mac_addr, tid); + + tbl =3D 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 =3D (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + tbl =3D nxpwifi_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) { + if ((block_ack_param_set & IEEE80211_ADDBA_PARAM_AMSDU_MASK) && + priv->add_ba_param.rx_amsdu && + priv->aggr_prio_tbl[tid].amsdu !=3D BA_STREAM_NOT_ALLOWED) + tbl->amsdu =3D true; + else + tbl->amsdu =3D false; + } + + nxpwifi_dbg(priv->adapter, CMD, + "cmd: ADDBA RSP: %pM tid=3D%d ssn=3D%d win_size=3D%d\n", + add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); + + return 0; +} + +/* Handle BA stream timeout: send DELBA. */ +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 |=3D + cpu_to_le16((u16)event->tid << DELBA_TID_POS); + delba.del_ba_param_set |=3D + cpu_to_le16((u16)event->origninator << DELBA_INITIATOR_POS); + delba.reason_code =3D cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); + nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, 0, 0, &delba, false); +} + +/* Cleanup all RX reorder entries. */ +void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv) +{ + struct nxpwifi_rx_reorder_tbl *del_tbl_ptr, *tmp_node; + LIST_HEAD(to_delete_list); + int i; + + for (i =3D 0; i < MAX_NUM_TID; i++) { + spin_lock_bh(&priv->rx_reorder_tbl_lock[i]); + list_splice_init(&priv->rx_reorder_tbl_ptr[i], &to_delete_list); + spin_unlock_bh(&priv->rx_reorder_tbl_lock[i]); + + list_for_each_entry_safe(del_tbl_ptr, tmp_node, &to_delete_list, list) + nxpwifi_del_rx_reorder_entry(priv, del_tbl_ptr); + + INIT_LIST_HEAD(&to_delete_list); + } + + nxpwifi_reset_11n_rx_seq_num(priv); +} + +/* Update flags for all RX reorder tables. */ +void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags) +{ + struct nxpwifi_private *priv; + struct nxpwifi_rx_reorder_tbl *tbl; + int i, j; + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + + for (j =3D 0; j < MAX_NUM_TID; j++) { + spin_lock_bh(&priv->rx_reorder_tbl_lock[j]); + list_for_each_entry_rcu(tbl, &priv->rx_reorder_tbl_ptr[j], list) + tbl->flags =3D flags; + spin_unlock_bh(&priv->rx_reorder_tbl_lock[j]); + } + } +} + +/* Update RX window size based on coex flag. */ +static void nxpwifi_update_ampdu_rxwinsize(struct nxpwifi_adapter *adapter, + bool coex_flag) +{ + u8 i, j; + u32 rx_win_size; + struct nxpwifi_private *priv; + + nxpwifi_dbg(adapter, INFO, "Update rxwinsize %d\n", coex_flag); + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + rx_win_size =3D priv->add_ba_param.rx_win_size; + if (coex_flag) { + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size =3D + NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size =3D + NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE; + } else { + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size =3D + NXPWIFI_STA_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size =3D + NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE; + } + + if (adapter->coex_win_size && adapter->coex_rx_win_size) + priv->add_ba_param.rx_win_size =3D + adapter->coex_rx_win_size; + + if (rx_win_size !=3D priv->add_ba_param.rx_win_size) { + if (!priv->media_connected) + continue; + for (j =3D 0; j < MAX_NUM_TID; j++) + nxpwifi_11n_delba(priv, j); + } + } +} + +/* Check coex for RX BA. */ +void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter) +{ + u8 i; + struct nxpwifi_private *priv; + u8 count =3D 0; + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) { + if (priv->media_connected) + count++; + } + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + if (priv->bss_started) + count++; + } + if (count >=3D NXPWIFI_BSS_COEX_COUNT) + break; + } + if (count >=3D NXPWIFI_BSS_COEX_COUNT) + nxpwifi_update_ampdu_rxwinsize(adapter, true); + else + nxpwifi_update_ampdu_rxwinsize(adapter, false); +} + +/* Handle 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 =3D (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 =3D 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 =3D le16_to_cpu(tlv_rxba->header.type); + tlv_len =3D 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=3D%d\n", + size_add(sizeof(tlv_rxba->header), tlv_len), + tlv_buf_left); + return; + } + + if (tlv_type !=3D TLV_TYPE_RXBA_SYNC) { + nxpwifi_dbg(priv->adapter, ERROR, + "Wrong TLV id=3D0x%x\n", tlv_type); + return; + } + + tlv_seq_num =3D le16_to_cpu(tlv_rxba->seq_num); + tlv_bitmap_len =3D 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=3D%d\n", + size_add(sizeof(*tlv_rxba), tlv_bitmap_len), + tlv_buf_left); + return; + } + + nxpwifi_dbg(priv->adapter, INFO, + "%pM tid=3D%d seq_num=3D%d bitmap_len=3D%d\n", + tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num, + tlv_bitmap_len); + + rx_reor_tbl_ptr =3D + 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 =3D 0; i < tlv_bitmap_len; i++) { + for (j =3D 0 ; j < 8; j++) { + if (tlv_rxba->bitmap[i] & (1 << j)) { + seq_num =3D (MAX_TID_VALUE - 1) & + (tlv_seq_num + i * 8 + j); + + nxpwifi_dbg(priv->adapter, ERROR, + "drop packet,seq=3D%d\n", + seq_num); + + ret =3D 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 -=3D (sizeof(tlv_rxba->header) + tlv_len); + tmp =3D (u8 *)tlv_rxba + sizeof(tlv_rxba->header) + tlv_len; + tlv_rxba =3D (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..db95d9db5d1f --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h @@ -0,0 +1,71 @@ +/* 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_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 =3D 1 << 0, + RXREOR_INIT_WINDOW_SHIFT =3D 1 << 1, +}; + +static inline void nxpwifi_reset_11n_rx_seq_num(struct nxpwifi_private *pr= iv) +{ + memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); +} + +int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *priv, + u16 seq_num, + 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 *t= a); +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_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from MRWPR03CU001.outbound.protection.outlook.com (mail-francesouthazon11011033.outbound.protection.outlook.com [40.107.130.33]) (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 91D4C421EFC; Wed, 4 Feb 2026 18:05:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.130.33 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228319; cv=fail; b=CGajtyxA/joyuStXdNPjjT5PjBaFAf3/y94dmDgN56gVsM0QlPEqsl8RT6tiYvdDn+6GYEAUC7EjM+YdEJiDdUyEoPtSbAE1jslzf3HRqZHzqG3SPYRZc9mGPkPaqghQGksNGve0KiIMh0Z3oNGMAo9cvvaxmfoYuWGL3nRIRiA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228319; c=relaxed/simple; bh=ZWshfbi43yjdwEqaeDxMmbRRb3vhW4iPB0kG7MvOvDg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=kxB7M/4kqBo2EK9I/t3EMfloOUSEARKABmCb/2pPLThjQCehOETZjLAoYEJxh+TUvN/+bG6GT5z2abPnfkCoqcODQ8gBPOFQ3kVXAQPbbeYJ7G+lMcB9JGCbD9oX5h0W1mnAJcDxAFDI/srGj0zim4k3jd8UtQVoNXw99ESPycw= 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=Zxsu+lAP; arc=fail smtp.client-ip=40.107.130.33 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="Zxsu+lAP" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=mrDW7AoOsMzar4we56BgMTXskcngC8d4ebe5VpisSC6SmwKyMpaJ7y2S+l5tCTJ8P6iB3jEe3GL/7/q7bq60VF7EGXeHHS2o2dyuBbBp0O5HVtDYTzXpYFnbYI3wSQirMLOr0oWhVCR0cWMchNJDTcysV98w11SM48mE+0Wp19sOazp8mfMT9za79jJuwFlthSWhiL8fx8xjKHVqi/4YoQTu6iMi4aWNG5SkIUoTG9Oiz6XtR3/kmed+0DuE38y7RNDvdkXu0Xqo3fTkyLBrbQfYfdPhjxzWpCx10wpQ9/PXMXvLV6pGmWLSo2xhBdpDuz0Zxc0DcBp60ACX+Mu6Jg== 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=1ntHaU9qT3CKzeeyxQ03aJ6OMynI6/Hf+dGcjLG/3N4=; b=DcCaSbTK1rajaolCnqc5yB+P3wzBGc/29wmrS/ry87QO3yxk1EgfC8AXji8QVqTI+wHhfa9Nnp+ZovKqSk3UC/NDvh6J6LmSKBWYjHTT/3edANnQs9IrdkV87DcCSD/3RPvfEMhCr263KNVu22pqM13KQvyFPoYdxxvT7ZCjzIdBtARtCm05A738LopNumeci8kMterzfuL4zHjYcK6kyJcTsAwoOyghSrQeB2DaUBmMghImTUZrfkYeY//tRJ7fmQqazfKJxAs23tCkcQco5JtC3t+UKZlb8vWHjt1mQaE70GzBHPfqwbKwfNjgGc+9cI2QsdrTTj76OblY4Bv4iQ== 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=1ntHaU9qT3CKzeeyxQ03aJ6OMynI6/Hf+dGcjLG/3N4=; b=Zxsu+lAPpwL8hwKYTmA5kLcOpBZbKWDaNmENERsRlD1JSSBTX3kjzJ8sqMQgfs9TNLbcw4LHyyamyXPg+YiUhAphBPaxGaJEmLyax8FXzRCmC4xZ7aTTPXc1fHiiHcH9sppu8PlPuXiOOGJSUasNPosDY8uS8qoM2BXiEWxCV5q3KUQUX0BJfMxBIg1hIlX3gSikR8xjs5NQRer+ipCfBc4yS2iWCuXtg+3+lGrlzVNC0nI3DQ6Vl+3aqPZGfVfFX6YOzCh+x7HwV9fYC/sNSdjN8yRFaQUP/wLlB0vt8iV8svEYs5in8ah205mXY/e83WzCWnXyG0jlGZjbgICo3w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:15 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:15 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 02/21] wifi: nxpwifi: add initial support for 802.11ac Date: Thu, 5 Feb 2026 02:03:39 +0800 Message-Id: <20260204180358.632281-3-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 2f998b36-45cc-4275-5c19-08de6417f2f7 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?RTO+pphZciSBE0xal6KF1x0yd3L2kbg+A+I1hp1Bd2ACMLYnczRauUE2efij?= =?us-ascii?Q?LiiUGiO2fAIPHP9e5bLlfGp0z8aig9Mi5PquPzFIY46GED81ueQtGNwZstkA?= =?us-ascii?Q?4pJLRge9nY6O2XrjNSk33ttw3plL+axJxE8nUVOW/qnT7brDvDPYVNJ2QhIp?= =?us-ascii?Q?ezGw6Sq9d/BKtN2IMgKjkVg47ufXiVgQ1GCmL2hwypYf9J8diud5YIo/xPkT?= =?us-ascii?Q?fgFTMYmAaxiLvP/3wz+vuEtv8ELovV3KKbQvT6MBe/63EHDg6AqAYpKiwtdp?= =?us-ascii?Q?g//fBsKyBaVsrA2kuk4H3OF00i9HJJNN+GYUMYnPdQzBMp0RQks3IHej5C9N?= =?us-ascii?Q?Lr3mh5B90/wpbzW9JQiqTMZmzDA+QsPlWZg49CBukGTI44EeqaAtEGH8kyjx?= =?us-ascii?Q?sYdhk+S4MffoY+3sDzlkM9M3wWd7S/IH8DQa7eoV0gLIT/tnnAAwZ3K4g+54?= =?us-ascii?Q?JgVqBN6D86vlIOwWt0mbAPKCJcEJKMk+jT8zKbAO3YMUp04DzVs9AFOcs15v?= =?us-ascii?Q?C4mr0JmtULf4T6Nd1fUgyu5JjcRz8daJ1T9jwBXczHYSlQzFkQGmj9/X+mUm?= =?us-ascii?Q?J8qJ50RGJMaoBPODOojGDG7ZT8/hlcb9+uu1Xym//ZYsevgw+X47d7r0DxJC?= =?us-ascii?Q?msqW9Ej0CIhgtmFp27RjFFd8jSa7uT0/9d5VYPavbtoVIXErolRaFNBHhPrq?= =?us-ascii?Q?0hVwB0GZ4S1NU5YAwYdaOLXlcc0+QSDCiQXYV46VGtPfpvMNpvxaZ0dfm/7X?= =?us-ascii?Q?CjuEg2XRLmPY3Cm/BQ0a2tFzRSlk9UjcXPOh/HsyfAgz1+Ka89KyYFkj8ubP?= =?us-ascii?Q?uV5H1XE/ILxPKY71+ovUqpDC25UssrrL9c+/0QfGCzYDYoHEB0EK0VsEGs0f?= =?us-ascii?Q?+XprGB5HmBGgK3F8JVGADjGGolEvcLi1HS8aGEzAeYnf/pnPBXVifpX5/XpK?= =?us-ascii?Q?ixk4vgUpXBq3ZLD9ywPcy0N6RndXnFqvNL5WHxkNv0qvdFxZ2lyrtUzCu1Hc?= =?us-ascii?Q?dkEdj3cta7klflCou0MExuRZOjCZL53LvwPjeVOVOFuDS6w1IDitb3A0XGDM?= =?us-ascii?Q?kaX8EkWmedV8zoT9n0GMiP/Q6bhIC7o4fo5NCWFhuOvsZAPuEsZJ8uxd26ZW?= =?us-ascii?Q?zAcTnpb9ulVt3AebS7QF8ZmNhDVYq+Wsa5y0nzcP5wbOij/lJKtJTeZlBekU?= =?us-ascii?Q?YH+6ASpRVpE+CDjOleH78MpLei8hxVPlJrKg9KRxOYTnj8oZbUIOLHK9eXMJ?= =?us-ascii?Q?E2ggjKezSV9HRhyB6D/6toaB8WmLZVdKRRvK5eV6Yf3MF04tN60XdIp37vz7?= =?us-ascii?Q?FPC3nlkARL4xkPwRQTCD1wBl3Ddgn/ZNsvQ7iNURlR9+9r74+4h5xA2CbwwA?= =?us-ascii?Q?4JX4g/dsYTgXoOdo4eo/SWlwl7n/TEKIovwAQff3xa4xXYtPFNwEH5i4WtzN?= =?us-ascii?Q?7Q67Au+icBWA90bFSSlZatg4cVGX0AsH6DTxhgDWP25Yw1Pq2QPzI28fhK16?= =?us-ascii?Q?5Xn851+Y5Z1xXyGCkDVvkfjfE1FJshPK+72/JFxKydS/3tB2ZCLgayHOhE+w?= =?us-ascii?Q?Fs+oj43L78l2WbYAHPR9pia1QFhq2U4IgJH0/mdIAhgSC7ZZfwANPCjHoboc?= =?us-ascii?Q?rpUxlENmK0O+6anTCy/rtXo=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?OlnZowyp8Hpa75R/p5zoLcGOESLNsT6ci06isLz9EOFNHB2rHOJftLoSt4A0?= =?us-ascii?Q?YwuYNpdtv3J9qhnqPIm5l6ikNvfEkTyArsnIlSH8v/dBjAHZfAEAzbNAT3Yx?= =?us-ascii?Q?IMtsEaUuEBMZUiwDwMqBOqY7rNGxJv2HbrRxKDTBCstIBTZvnFkdJ7R/Poho?= =?us-ascii?Q?8rIQ2N6x7NVw1fJKjcMCjPkk3q/cadbFOvyeapI9QPKoEPNrqGPEeENOyUin?= =?us-ascii?Q?3MEP5eFWzVEpcf6/+7fvpT7vBIvRdlBrmSmFbgtTTKMA9KiCOyBGsNEpE39/?= =?us-ascii?Q?/CCVQa5AzRBjh5ghnFnbcZXJk03BXOjIym5Yy4gWBtgr6Fs4tyjG2FtcVBRc?= =?us-ascii?Q?46WgbpdT80x4wBA94qAoJH+usBsWFV0fBzQatqobyPXGvolEO122y+0a5cgj?= =?us-ascii?Q?CoRu995+Ymo6fKXgLpmvIrja4KkxtBj7yTnjFDhMDlindvGRHaeh9HSGvhSc?= =?us-ascii?Q?TcoThxqZ9qsEGmhWN1XJhQZqSN2KuX/k/55ypPQ/6JafCVvmPP6JMUW2bkkR?= =?us-ascii?Q?HREwHguZgyiSxz7f7TA74+KGMqwq1rNOjA+A/HLiuaA32t0RgVJgUOuoTVKK?= =?us-ascii?Q?TE5WXnc28w6xMDmN9oMxGODDNJ37q3lzk56dfHx5BTVywuSkfdnpSPmH/Sza?= =?us-ascii?Q?wzQYW57WYLlau6RLcOjFQ4veI7821zJeJgJgs9mAY+w0Dt6kMIPBaBVUL0kU?= =?us-ascii?Q?kOakJW2GvTnAomE+tuWq1se83vllfA9Sm1GpRSdrbtzx+yELqsQM9cnLY/oC?= =?us-ascii?Q?jn4KCzoi0MQP8VgZY74QrG2YFAIpovpxmwgggSaAgP9ErTRDG4L2pF7CNrij?= =?us-ascii?Q?nvwCvYlMFv8NBWEDv5Wra14jyBYwMx6NZe3WV1wv9lU7pA/LAFKLlnqq4S/G?= =?us-ascii?Q?MeNM4pnnEXKJd+GmRhViVqLq1iymXwspHlmVYj6gBNylsPLe54uGA/t2oReq?= =?us-ascii?Q?w3ZyqGd295nIJMk6WQXa+uE1YLGs1f/HCwKkgMk/stYjYmdwwREvBYXB5mjl?= =?us-ascii?Q?64o+5JdJY0zaUtqn9wab7iUrd5v2Pl5MXil1UcqctX1q8MWREV8YwXE5ZAio?= =?us-ascii?Q?SLvj5Kcpy7Obvo+rkgVkSLzhEYq41HB5Jnq6FekmNLEJAiId//dhAt3t+g1U?= =?us-ascii?Q?dAWd65EdU3NlMML8NgOLfCCO9FS+9w0xrJmxcXssm+oVQIPySmZObSLZXeBx?= =?us-ascii?Q?KqhlMq3iDEACtNomaeiHc9Kr1O4Dr4M7Rw/1E3oJt0vFLSybcNSgdrJPDiQB?= =?us-ascii?Q?HpuDJ+svZOM1ZE0uDhGykbybbZ1Np/DlT6hdBhmFGPK7Cbp7pfzUpMLUETnP?= =?us-ascii?Q?lcMfojiMB/EttnaO0eot0q+v6lGaf1k/fzLbN3lcnD6gYjtjCI6dkiGMijTq?= =?us-ascii?Q?LqYXXs4jgbCi1MIsw2TnR/+xpwfEnJIhlml56zu7twIew/Dy7dp5zsJS+VYo?= =?us-ascii?Q?/yfGaqLGYCRAZB1aGZkhhgwMgjce/GqYCfLdS8fizD8yObQYS8Gye4K1QSJv?= =?us-ascii?Q?5AHIvwRK627vDyn2y1qcsMqp1vIjSZfxthbU25tQ4ES5R14HAJKDJL9454A9?= =?us-ascii?Q?gdm3A4GzmGi5v45kdEY59/dWLdWiHwgjvlJEryGxjOsFs1EnjfWNpU5yji/n?= =?us-ascii?Q?8u/UV/7y4dUrhWXFVZ+e6EPBS7oGU2x/2wJBp/6gDz5Ekz3gKndKpiTm1CNW?= =?us-ascii?Q?jCjOAFUgGPDG5u38x/Ycc83ZGeemvLrFUCzC82i4ci18YEJF21XJlLCfGZp2?= =?us-ascii?Q?7gglbsa4Ow=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2f998b36-45cc-4275-5c19-08de6417f2f7 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:15.8089 (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: dmcXj+AdcALcEOErJfTLXR2JbJmQf+SI5iR2QVqLkoqtcbizZrSITQVEbnDJFYe1Y4AZiWbVmDeD0zWtmyeWvg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce 802.11ac feature support for both client and AP modes, with coordination between host driver and NXP firmware. - In client mode, the firmware assists the association process via HOST_CMD_802_11_ASSOCIATE. The driver converts 802.11ac IEs from cfg80211 parameters into TLVs and appends them to the host command. - In AP mode, the driver converts 802.11ac IEs into parameters for HOST_CMD_11AC_CFG, which are then passed to the firmware for configuration. This patch adds logic to handle VHT capabilities, operations, and operating mode notifications, enabling proper negotiation and setup of 802.11ac features through firmware interaction. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/11ac.c | 280 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/11ac.h | 33 +++ 2 files changed, 313 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..117d06c35401 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ac.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi 802.11ac helpers + * Copyright 2011-2024 NXP + */ + +#include "cfg.h" +#include "fw.h" +#include "main.h" +#include "11ac.h" + +/* Map VHT MCS/NSS to highest data rate (Mbps), long GI. */ +static const u16 max_rate_lgi_80MHZ[8][3] =3D { + {0x124, 0x15F, 0x186}, /* NSS =3D 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS =3D 2 */ + {0x36D, 0x41D, 0x492}, /* NSS =3D 3 */ + {0x492, 0x57C, 0x618}, /* NSS =3D 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS =3D 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS =3D 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS =3D 7 */ + {0x924, 0xAF8, 0xC30} /* NSS =3D 8 */ +}; + +static const u16 max_rate_lgi_160MHZ[8][3] =3D { + {0x249, 0x2BE, 0x30C}, /* NSS =3D 1 */ + {0x492, 0x57C, 0x618}, /* NSS =3D 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS =3D 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS =3D 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS =3D 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS =3D 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS =3D 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS =3D 8 */ +}; + +/* Convert 2-bit MCS map to 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 =3D 0; + u32 usr_vht_cap_info =3D 0; + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (bands & BAND_AAC) + usr_vht_cap_info =3D adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info =3D adapter->usr_dot_11ac_dev_cap_bg; + + /* Find max supported NSS. */ + nss =3D 1; + for (i =3D 1; i <=3D 8; i++) { + mcs =3D GET_VHTNSSMCS(mcs_map, i); + if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) + nss =3D i; + } + mcs =3D GET_VHTNSSMCS(mcs_map, nss); + + /* If not supported, fall back to 0-9. */ + if (mcs =3D=3D IEEE80211_VHT_MCS_NOT_SUPPORTED) + mcs =3D IEEE80211_VHT_MCS_SUPPORT_0_9; + + if (u32_get_bits(usr_vht_cap_info, IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK= )) { + /* Support 160 MHz. */ + max_rate =3D max_rate_lgi_160MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 not supported in NSS6. */ + max_rate =3D max_rate_lgi_160MHZ[nss - 1][mcs - 1]; + } else { + max_rate =3D max_rate_lgi_80MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 not supported in NSS3. */ + max_rate =3D 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 =3D priv->adapter; + + if (bands & BAND_A) + vht_cap->vht_cap_info =3D + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); + else + vht_cap->vht_cap_info =3D + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); +} + +void +nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv, + struct ieee80211_vht_cap *vht_cap, u16 bands) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss, tmp; + + /* Fill VHT capability info. */ + nxpwifi_fill_vht_cap_info(priv, vht_cap, bands); + + /* RX MCS set: min(user, AP). */ + mcs_map_user =3D GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp =3D le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); + mcs_map_result =3D 0; + + for (nss =3D 1; nss <=3D 8; nss++) { + mcs_user =3D GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp =3D GET_VHTNSSMCS(mcs_map_resp, nss); + + if (mcs_user =3D=3D IEEE80211_VHT_MCS_NOT_SUPPORTED || + mcs_resp =3D=3D 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 =3D cpu_to_le16(mcs_map_result); + + tmp =3D nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.rx_highest =3D cpu_to_le16(tmp); + + /* TX MCS set: min(user, AP). */ + mcs_map_user =3D GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp =3D le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); + mcs_map_result =3D 0; + + for (nss =3D 1; nss <=3D 8; nss++) { + mcs_user =3D GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp =3D GET_VHTNSSMCS(mcs_map_resp, nss); + if (mcs_user =3D=3D IEEE80211_VHT_MCS_NOT_SUPPORTED || + mcs_resp =3D=3D 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 =3D cpu_to_le16(mcs_map_result); + + tmp =3D nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.tx_highest =3D 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 =3D priv->adapter; + u8 supp_chwd_set; + u32 usr_vht_cap_info; + int ret_len =3D 0; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info =3D adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info =3D adapter->usr_dot_11ac_dev_cap_bg; + + /* VHT Capabilities element. */ + if (bss_desc->bcn_vht_cap) { + vht_cap =3D (struct nxpwifi_ie_types_vhtcap *)*buffer; + memset(vht_cap, 0, sizeof(*vht_cap)); + vht_cap->header.type =3D cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_cap->header.len =3D + 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 +=3D sizeof(*vht_cap); + ret_len +=3D sizeof(*vht_cap); + } + + /* VHT Operation element. */ + if (bss_desc->bcn_vht_oper) { + if (priv->bss_mode =3D=3D NL80211_IFTYPE_STATION) { + vht_op =3D (struct nxpwifi_ie_types_vht_oper *)*buffer; + memset(vht_op, 0, sizeof(*vht_op)); + vht_op->header.type =3D + cpu_to_le16(WLAN_EID_VHT_OPERATION); + vht_op->header.len =3D 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 channel width; keep peer's center freq. */ + supp_chwd_set =3D u32_get_bits(usr_vht_cap_info, + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK); + + switch (supp_chwd_set) { + case 0: + vht_op->chan_width =3D + min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 1: + vht_op->chan_width =3D + min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 2: + vht_op->chan_width =3D + min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + default: + vht_op->chan_width =3D + IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + *buffer +=3D sizeof(*vht_op); + ret_len +=3D sizeof(*vht_op); + } + } + + /* Operating Mode Notification element. */ + if (bss_desc->oper_mode) { + ieee_oper_ntf =3D bss_desc->oper_mode; + oper_ntf =3D (void *)*buffer; + memset(oper_ntf, 0, sizeof(*oper_ntf)); + oper_ntf->header.type =3D cpu_to_le16(WLAN_EID_OPMODE_NOTIF); + oper_ntf->header.len =3D cpu_to_le16(sizeof(u8)); + oper_ntf->oper_mode =3D ieee_oper_ntf->oper_mode; + *buffer +=3D sizeof(*oper_ntf); + ret_len +=3D 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 =3D &cmd->params.vht_cfg; + + cmd->command =3D cpu_to_le16(HOST_CMD_11AC_CFG); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) + + S_DS_GEN); + vhtcfg->action =3D cpu_to_le16(cmd_action); + vhtcfg->band_config =3D cfg->band_config; + vhtcfg->misc_config =3D cfg->misc_config; + vhtcfg->cap_info =3D cpu_to_le32(cfg->cap_info); + vhtcfg->mcs_tx_set =3D cpu_to_le32(cfg->mcs_tx_set); + vhtcfg->mcs_rx_set =3D cpu_to_le32(cfg->mcs_rx_set); + + return 0; +} + +/* Initialize BlockAck parameters for 11ac. */ +void nxpwifi_set_11ac_ba_params(struct nxpwifi_private *priv) +{ + priv->add_ba_param.timeout =3D NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size =3D + NXPWIFI_11AC_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size =3D + NXPWIFI_11AC_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size =3D + NXPWIFI_11AC_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size =3D + 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..edc01b35d5b8 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ac.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: 802.11ac (VHT) definitions + * + * 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 =3D 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); +void nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv, + struct ieee80211_vht_cap *vht_cap, u16 bands); +#endif /* _NXPWIFI_11AC_H_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from MRWPR03CU001.outbound.protection.outlook.com (mail-francesouthazon11011055.outbound.protection.outlook.com [40.107.130.55]) (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 13E632F5A34; Wed, 4 Feb 2026 18:05:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.130.55 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228321; cv=fail; b=ASPGPqEOCRU2cHZan85gave5ehxO3CwCksWgKfsrH4uNucAy1yMZh0sSDDyQOh1FEunB5+3qsjquSQ2h9Z7Vu1ZofWg0lKQ94zqP0UuEoTiUa6Q/ThhGZTPn+78ymYnzF7SmpnROSHpyI5UBcuTPHS9Lg077RMXkkOdl9tlhtBI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228321; c=relaxed/simple; bh=CbYz/ZeIINRUKnQ9ekC0du+IXVhFM4urc2vtYyjEzWU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=f4WbFtJoe7r2b2w5ilvmZRE12DzPfbnnJZZNtkbS/Eb62284WziJLDvqE2pjbnfLiUj3QwVjhQxxsAsZW2mqPN89mYjuSHP+5Dq0zrZsuMt+t1Om0kMapE2uzfmQslLx52QkCYXYgK0geyPNyTJDLPJC86w0ejEJciMBjMxbBAU= 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=bDokfarA; arc=fail smtp.client-ip=40.107.130.55 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="bDokfarA" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=wkL+x4rexK/Z+hZ3r6ccWmz1dv3SEAx06Ax0aWAQHb4CqhDZrCZvrVxeYZRrQCt9+f1MXSJZDuqUWiMS+MqJVoyv99DAg67865j36WhFx/uCEQ9V1vcb9dC/1Xe96/2M2rfOiwrFxCnDxHLAcWljy2nfEjLogbjRoySlEOzvmC9elWULNMlDsI51xqpnmUSNAsZta2uCZKijHousaRRg7FvBQWwKJ0Y/admhNDo/qcwSxzm5sORJRW5MqIWRZZIMrj5Z2k8A51JSW0QY/V8kRVnneJVa7OLH94vsHnld03yW6ADVldCoGkEFe/s/ecQfpATlSz4d/z49FX/cKMvAXw== 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=PPmSvMu8tHEYTCLFXeCjKiueukdBN+EQYEAOZqyURZw=; b=pwGJhiIQpT/mD/9PVhWTqoD2n81XXjc4foF+CqyP59X3kalT4ZLFJsrn7kEELVCGD7ps9Kebzr16PC+Ldm8ACpvwC19tUw9lzwAKvsWsQslloV+y/qSqy/04uO+hmPWxfBVTl3Kp+ZfQgXPXw4VRxQkkbdouSWsM6x9+jTNitL2CwJBktZdCY5DQ9YxQ84uaxCf139pIQYZ0uTPakIj0JSTCXkvHq6+bblnkg/Rz6J2C4JJ1nNRmDl0P0xCQZGTm6+yxfVXSwCajkiVyV6DbYoGlLyNwIGPbR3e9hV+2yTYh3nG2TJV2WsD0N6lcSnjpbJJ6QzHxu8QQwHzjMPAZVA== 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=PPmSvMu8tHEYTCLFXeCjKiueukdBN+EQYEAOZqyURZw=; b=bDokfarAxuoyi+bi1sX1NleVTvXTgv6YTa6n90l2y80KGZReRPThzDYIVS/yZYiOwY0K7cY1xEUDxFTGu8ORAvBqButRNNaSDtf8Ze9hp2yM008Iu4RJAcjl7Ip7ZpWCBUkhepGCsqJF74dQb/bUSsEhYa5kgYfjxbvVr4qE6CJSeamtcnCobgEJhAGq3WjVNUtRq+w4julSlDeVCi0sFa4WQAu/7kuabtDhOjnDpe2q2ngY54lZljEq2jsl28YfNjzWA4kMBVQdaHpzeqbHCs+4a52fQYzkounly3iAI2NBsb3LHpK28bGN5Mm/elxGAl/L7M6vzt3pljBDCqumFg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:18 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:18 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 03/21] wifi: nxpwifi: add initial support for 802.11ax Date: Thu, 5 Feb 2026 02:03:40 +0800 Message-Id: <20260204180358.632281-4-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 3ff45faa-e27c-4b6a-6dda-08de6417f466 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?sPIn7VPFCyqe3S+5c1c6pYuOy3A6ZBFGHRMyu1WZYK63XVPENmkFj1tIzZdZ?= =?us-ascii?Q?Xymkk/awS7pIuJSoycfl1iSZjwIB7FMniXbO2IXFwP9AMClx+FBSGThAwivV?= =?us-ascii?Q?s2QYO2cE024IKwRAg8HoI+4GWDZJeKNYTvapNEumHwhxDD1xbdglwk/BlsHq?= =?us-ascii?Q?xmGTsry7rGPU9hhpOnIrhVvIyN1oqWTUf7mxRiRKsaw4nFwZI3pgkzubnphY?= =?us-ascii?Q?b6sx91S1p3VU8B68EfAzwABi/qqos8eZiFSLGW+CSPNEdZU1Pc/vMwO+0Rbw?= =?us-ascii?Q?KEoIeG6MQ/lla3yuZxhDjBV9c3/ymckEnn2OrjLhuQQtO0guRUr+qxIeB/in?= =?us-ascii?Q?xaQZl+Z1Gqlild1yzvZs/VH1IOIEw29BnY7x6+FtsZa8GE5H0Y2GXqgMGRXP?= =?us-ascii?Q?fnsmQylhs/wVRESGx091kx30k/pZA7L38jTHOca1UBm03EdSeIriUf6GwHG2?= =?us-ascii?Q?WAZavvCMCEF2gQXuzTNcaSLko3V11jX0hxvnkdK28knJnN6rdzy3sSiRgsWL?= =?us-ascii?Q?Ak4pbP+1jW6XxD0L2TofFgQkRBHqLT49IL7Z/YciQAkAEL+SR9jzHC7s3cHV?= =?us-ascii?Q?RHQYJO3CfDkImvwY3kGzxOzm2/G+t5aPHtc7s4u4DFfIIpUKkaKGZ8eMMl+5?= =?us-ascii?Q?kYnm4hJUaLBGBPMA6S7I5GT7PrtJqDdh2yj4hUw90z4kYs8/4ePm7u7TYj8Z?= =?us-ascii?Q?pTqG5hMOQEyGLrtIq9ro/MaN1cxRO99GsYYQKrcqrN6fKav/8TPbxv7oXiho?= =?us-ascii?Q?iqnAsVXWBRbnXsYYecAr1pkNjTB/QPw8MfsdR5aEvYyMhtREZAlfHYajcq+Q?= =?us-ascii?Q?kmQHd1RvKxZmxOdpVc58Dzz7p8GYFTCQGAzvE17m0k8KjzqDh1g48FAVD0Ac?= =?us-ascii?Q?1ArqqsYeV70BsvSzsf1k6iX0JPvxElaKaEbMCZHYG7UDra9HtH5jJ3ycbNi5?= =?us-ascii?Q?UrZI66uq3nJrwogIAtugdVb0mEpgPSxTSsTqJgOVJvo6xff6NIRNV/ChvGry?= =?us-ascii?Q?CiAWKr8snEU9elwU7Obo7Qyy+8sGme8z5um/nncZMzyQE4Jda8jSPtAHP/Dy?= =?us-ascii?Q?CHrjHTp14KbAUphZi3WMNTnJYNe0/x7V0Mj+stJjNbOHXcMH8SLI2xN5JQj4?= =?us-ascii?Q?zBu0SE/7pA8zhkZFH9WDJGtRK0QMDNg4uG9MnwqhURJWPwKle4SeRv+PKpgT?= =?us-ascii?Q?Scv2gmvHe+N1d6Vy/rR3Kir7ptS6/QxobP/jVbb4zgtGsCQQxjksBg7Gu/Bx?= =?us-ascii?Q?NkEraZWPFvdPod2XTwFuKqpg7nl8hudB/UaK0kaNAeIQfyWNnCWejScj9PHA?= =?us-ascii?Q?8SV7w9zR9mPC1jGIublkrfALA4M3TaZalk769gfxCRkxax+S617AYpQccyGG?= =?us-ascii?Q?4GkOpQVgJDV2uDC21KyMvc9TKbzQOiZZ7H3QAMPhFlrO7oZGAwNcJgyhQ9WH?= =?us-ascii?Q?HUVvq9VaEuFurndnzrTYDRs4mCC6+zIBCoEJb8hGFKx6ZZ2AgAov7HuBJ+p3?= =?us-ascii?Q?efAdd3PjyuNJuylDAKMRgNAERW83cx4lZD5z+knmqklVj1YgDphMt/NoTIb6?= =?us-ascii?Q?K70zcGEaBTCv7qHm0W7aSW712QtHB3xG/Ml6L74UWio8nrROAvlEUL6iMxBN?= =?us-ascii?Q?ZLXctN/cz7sCFip7GSeeQ+Q=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?o3P31ObcappxmdGsxgpHgw3OHK9mWU06HZD8AA+XPYGDUV7938t4uZyrlipD?= =?us-ascii?Q?6/RgqJ23eUwGMCk870Ief1DlHPW2e2CNOLhx6Mj8wJmX1FWCTM8TLXG9Cqnw?= =?us-ascii?Q?kwGoJVX6DgJ+ay4pwZKqJ3RLBc0l+NMAfT76QPKpwt/tFlmkGEF4JHRAxj9/?= =?us-ascii?Q?NB+dAKaArW4DpFlPktI6mg5ZiD/lZF3cY91RPCipNWuWY3dUI56g1qd3JbBM?= =?us-ascii?Q?Kja9ONme4ThRHINeb8tgb0cJohAoAS+4Efq4gyLYfQKiktDZsH3k84oWS7pf?= =?us-ascii?Q?mVjcjEAPDmqiwCqcuyuN4k3ffr+xKU7lYrCQevZlQ3MreGdjxglIO7jSdT/c?= =?us-ascii?Q?HHKw3wB5neVtUQkMrJLl9Y/pja0zqSID+Z6p7y8xjSCI2V4SEP+Fbj5vWid/?= =?us-ascii?Q?8e4mbvdq8ZXM6nfPi4FeO8RxAfOw+NxxHBEvA7v2VQkThAW3ukiaDtIetzzY?= =?us-ascii?Q?7eWSKUiIFUTwWOHG1L6QfbwAmd4HEeH0JUrH1bw+oMuEzVaYeAOgyYw/AJOm?= =?us-ascii?Q?DwxFNX3lUHNES+01g+ImT1DKQqunjrVZd6PMTGPLQxABLh2fAm+2AwlgoYh7?= =?us-ascii?Q?BfXqHrBEZ6SSvDkIKSMya95roQIJC+PaUbm0X/AIXxAuxBggvXoBrxhaXQAV?= =?us-ascii?Q?GZmeaEB3zUQSP3FzBjpum0Talx152sjmd5p49o1Xt8MnGWwGA6FuyzXIN7VB?= =?us-ascii?Q?PGwBUZn9ir4YOrp0ljFBx10h805XsAwu9XduWtcOOx3HPnlfEk7nJbLk+crb?= =?us-ascii?Q?6+IQ5lLNdgZwBBFFh/st09A0Cw13aJNqVVO5BApFk/N7O40IqDMC5yQM/hGt?= =?us-ascii?Q?ENnm4Ere2TZ7q5w1sItF1H52Ln5Td2PA7heCMRi+D/KkFhVm1ftZ95lpkCOQ?= =?us-ascii?Q?iIDeVYtuofjz7CVMK65Qcnl+nq4i1jWjKDqam6RXBbDJY0ujSbmqNKW6LR3j?= =?us-ascii?Q?bsLISe0KfdOZjhO7ko1ECn1/mSEqAuLatEo1sU4iR/9df3vh08KH/aW2WuGh?= =?us-ascii?Q?Lcd50sAtbJxRTe9dG+G/QeBV9LhNDYdcjC5CwfWm2+m8XVZtAvNx5dgcRsV1?= =?us-ascii?Q?x6x6duawK4p48D5tZ3jbmfoBz0k7elUgCJMQDMsoVS9YHaPdbbJfKpa5OviQ?= =?us-ascii?Q?GseX3hOqKnxcsbSdtPijlyKSb+SKFI8kuKpAzUpB2oKfE1ulzB8BQ9h8nrMq?= =?us-ascii?Q?RPtiFsjjQMdBowJ9a5iLvB9OwcKc9Jvq1smOOFAGjArwW/vgtK2OaqxKnTt1?= =?us-ascii?Q?U6R9Di7aPWuKEAngYEO6t61TiO9HwBW0ja07/JLTL5fCKhSWL/5qLkmG1NYi?= =?us-ascii?Q?dd9VWjuRA+GKGDVCyLXDTcCjK9sNJcEHZ2SgaFi2Svnva6CtU9OpZBiCvP9z?= =?us-ascii?Q?GK+F10/bxziQhmEBIao9OUcBllxmkamHKAAEVa933PwUzWdA75TBI5fLrJ8s?= =?us-ascii?Q?94XDw3oC79AGckkmo9sMEaQhudmXuhos8B+gJPEygw/anHduoXprUC1lvuBr?= =?us-ascii?Q?/oW1IRNCZK2GyY0fg8u/h4f1T79eYq9Px5LRdefeyfnWiuu0McS2Of4ZlThn?= =?us-ascii?Q?v34m7qk3qxpcjVooCegxyG27iRiV9MclE9pX9YS8ti7b9qGky8cLxUOhG6gx?= =?us-ascii?Q?+rtOSLRba2zzXP4s4rFhKatrfTqiBW0TOKfdP7Kj2FCOcsXRGCnu3/IEf6M/?= =?us-ascii?Q?3Sl49Fv+f7bt2gLkkTDpRzGYnJX7OKIPqhVqyN2DAiVkD6KpD2gtMSpYXzu5?= =?us-ascii?Q?qPy5YaY/Mg=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3ff45faa-e27c-4b6a-6dda-08de6417f466 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:18.1899 (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: 20co8UwZ3qTYiktVvNxv63Z6EabnGPTJdlGEOZV8zqrGSQNnHA+SPUlLSrr8B65OswEuG+fpbv+egn4Z+cjUIw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce 802.11ax feature support for both client and AP modes, with coordination between the driver and NXP firmware. - In client mode, the firmware assists the association process via HOST_CMD_802_11_ASSOCIATE. The driver converts 802.11ax IEs from cfg80211 parameters into TLVs and appends them to the host command. - In AP mode, the driver converts 802.11ax IEs into parameters for HOST_CMD_11AX_CFG, which are passed to the firmware for configuration. This patch adds logic to handle HE (High Efficiency) capabilities, including MAC/PHY features, MCS maps, and TWT (Target Wake Time) negotiation. It also includes support for various 11ax-specific firmware commands such as OBSS PD, beamforming, TXOMI, and BTWT. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/11ax.c | 594 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/11ax.h | 73 +++ 2 files changed, 667 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..cc47c435eb70 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ax.c @@ -0,0 +1,594 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* nxpwifi: 802.11ax (HE) support + * Copyright (C) 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 =3D NULL; + struct nxpwifi_ie_types_he_cap *user_he_cap =3D NULL; + u8 header_len =3D sizeof(struct nxpwifi_ie_types_header); + u16 data_len =3D le16_to_cpu(hw_he_cap->header.len); + bool he_cap_2g =3D false; + int i; + + if ((data_len + header_len) > sizeof(adapter->hw_he_cap)) { + nxpwifi_dbg(adapter, ERROR, + "hw_he_cap too big, len=3D%d\n", + data_len); + return; + } + + he_cap =3D (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 =3D data_len + header_len; + memcpy(adapter->hw_2g_he_cap, (u8 *)hw_he_cap, + adapter->hw_2g_he_cap_len); + adapter->fw_bands |=3D BAND_GAX; + he_cap_2g =3D true; + nxpwifi_dbg_dump(adapter, CMD_D, "2.4G HE capability element ", + adapter->hw_2g_he_cap, + adapter->hw_2g_he_cap_len); + } else { + adapter->hw_he_cap_len =3D data_len + header_len; + memcpy(adapter->hw_he_cap, (u8 *)hw_he_cap, + adapter->hw_he_cap_len); + adapter->fw_bands |=3D BAND_AAX; + nxpwifi_dbg_dump(adapter, CMD_D, "5G HE capability element ", + adapter->hw_he_cap, + adapter->hw_he_cap_len); + } + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + + if (he_cap_2g) { + priv->user_2g_he_cap_len =3D 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 =3D (struct nxpwifi_ie_types_he_cap *) + priv->user_2g_he_cap; + } else { + priv->user_he_cap_len =3D adapter->hw_he_cap_len; + memcpy(priv->user_he_cap, adapter->hw_he_cap, + sizeof(adapter->hw_he_cap)); + user_he_cap =3D (struct nxpwifi_ie_types_he_cap *) + priv->user_he_cap; + } + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) + user_he_cap->he_mac_cap[0] &=3D + ~HE_MAC_CAP_TWT_RESP_SUPPORT; + else + user_he_cap->he_mac_cap[0] &=3D + ~HE_MAC_CAP_TWT_REQ_SUPPORT; + } + + adapter->is_hw_11ax_capable =3D true; +} + +bool nxpwifi_11ax_bandconfig_allowed(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + u16 bss_band =3D 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_fill_he_cap_tlv(struct nxpwifi_private *priv, + struct nxpwifi_ie_types_he_cap *he_cap, + u16 bands) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_ie_types_he_cap *hw_he_cap =3D NULL; + u16 rx_nss, tx_nss; + u8 nss; + u16 cfg_value; + u16 hw_value; + int ret_len; + + if (bands & BAND_A) { + memcpy(he_cap, priv->user_he_cap, priv->user_he_cap_len); + hw_he_cap =3D (struct nxpwifi_ie_types_he_cap *)adapter->hw_he_cap; + ret_len =3D priv->user_he_cap_len; + } else { + memcpy(he_cap, priv->user_2g_he_cap, priv->user_2g_he_cap_len); + hw_he_cap =3D (struct nxpwifi_ie_types_he_cap *)adapter->hw_2g_he_cap; + ret_len =3D priv->user_2g_he_cap_len; + } + + if (bands & BAND_A) { + rx_nss =3D GET_RXMCSSUPP(adapter->user_htstream >> 8); + tx_nss =3D GET_TXMCSSUPP(adapter->user_htstream >> 8) & 0x0f; + } else { + rx_nss =3D GET_RXMCSSUPP(adapter->user_htstream); + tx_nss =3D GET_TXMCSSUPP(adapter->user_htstream) & 0x0f; + } + + for (nss =3D 1; nss <=3D 8; nss++) { + cfg_value =3D nxpwifi_get_he_nss_mcs(he_cap->rx_mcs_80, nss); + hw_value =3D nxpwifi_get_he_nss_mcs(hw_he_cap->rx_mcs_80, nss); + if (rx_nss !=3D 0 && nss > rx_nss) + cfg_value =3D NO_NSS_SUPPORT; + if (hw_value =3D=3D NO_NSS_SUPPORT || cfg_value =3D=3D 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 =3D 1; nss <=3D 8; nss++) { + cfg_value =3D nxpwifi_get_he_nss_mcs(he_cap->tx_mcs_80, nss); + hw_value =3D nxpwifi_get_he_nss_mcs(hw_he_cap->tx_mcs_80, nss); + if (tx_nss !=3D 0 && nss > tx_nss) + cfg_value =3D NO_NSS_SUPPORT; + if (hw_value =3D=3D NO_NSS_SUPPORT || cfg_value =3D=3D 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_append_11ax_tlv(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct nxpwifi_ie_types_he_cap *he_cap =3D NULL; + int ret_len; + + if (!bss_desc->bcn_he_cap) + return -EOPNOTSUPP; + + he_cap =3D (struct nxpwifi_ie_types_he_cap *)*buffer; + ret_len =3D nxpwifi_fill_he_cap_tlv(priv, he_cap, bss_desc->bss_band); + *buffer +=3D ret_len; + + 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 =3D &cmd->params.ax_cfg; + u16 cmd_size; + struct nxpwifi_ie_types_header *header; + + cmd->command =3D cpu_to_le16(HOST_CMD_11AX_CFG); + cmd_size =3D sizeof(struct host_cmd_11ax_cfg) + S_DS_GEN; + + he_cfg->action =3D cpu_to_le16(cmd_action); + he_cfg->band_config =3D ax_cfg->band; + + if (ax_cfg->he_cap_cfg.len && + ax_cfg->he_cap_cfg.ext_id =3D=3D WLAN_EID_EXT_HE_CAPABILITY) { + header =3D (struct nxpwifi_ie_types_header *)he_cfg->tlv; + header->type =3D cpu_to_le16(ax_cfg->he_cap_cfg.id); + header->len =3D 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 +=3D (sizeof(*header) + ax_cfg->he_cap_cfg.len); + } + + cmd->size =3D 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 =3D &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 =3D &ax_cfg->he_cap_cfg; + + left_len =3D le16_to_cpu(resp->size) - sizeof(*he_cfg) - S_DS_GEN; + header =3D (struct nxpwifi_ie_types_header *)he_cfg->tlv; + + while (left_len > sizeof(*header)) { + tlv_type =3D le16_to_cpu(header->type); + tlv_len =3D le16_to_cpu(header->len); + + if (tlv_type =3D=3D TLV_TYPE_EXTENSION_ID) { + ext_id =3D *((u8 *)header + sizeof(*header) + 1); + if (ext_id =3D=3D WLAN_EID_EXT_HE_CAPABILITY) { + he_cap->id =3D tlv_type; + he_cap->len =3D 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 =3D + sizeof(*header) + tlv_len; + } else { + memcpy(priv->user_2g_he_cap, + (u8 *)header, + sizeof(*header) + tlv_len); + priv->user_2g_he_cap_len =3D + sizeof(*header) + tlv_len; + } + } + } + + left_len -=3D (sizeof(*header) + tlv_len); + header =3D (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 =3D priv->adapter; + struct host_cmd_11ax_cmd *he_cmd =3D &cmd->params.ax_cmd; + u16 cmd_size; + struct nxpwifi_11ax_sr_cmd *sr_cmd; + struct nxpwifi_ie_types_data *tlv; + struct nxpwifi_11ax_beam_cmd *beam_cmd; + struct nxpwifi_11ax_htc_cmd *htc_cmd; + struct nxpwifi_11ax_txomi_cmd *txmoi_cmd; + struct nxpwifi_11ax_toltime_cmd *toltime_cmd; + struct nxpwifi_11ax_txop_cmd *txop_cmd; + struct nxpwifi_11ax_set_bsrp_cmd *set_bsrp_cmd; + struct nxpwifi_11ax_llde_cmd *llde_cmd; + + cmd->command =3D cpu_to_le16(HOST_CMD_11AX_CMD); + cmd_size =3D sizeof(struct host_cmd_11ax_cmd) + S_DS_GEN; + + he_cmd->action =3D cpu_to_le16(cmd_action); + he_cmd->sub_id =3D cpu_to_le16(ax_cmd->sub_id); + + switch (ax_cmd->sub_command) { + case NXPWIFI_11AXCMD_SR_SUBID: + sr_cmd =3D (struct nxpwifi_11ax_sr_cmd *)&ax_cmd->param; + + tlv =3D (struct nxpwifi_ie_types_data *)he_cmd->val; + tlv->header.type =3D cpu_to_le16(sr_cmd->type); + tlv->header.len =3D cpu_to_le16(sr_cmd->len); + memcpy(tlv->data, sr_cmd->param.obss_pd_offset.offset, + sr_cmd->len); + cmd_size +=3D (sizeof(tlv->header) + sr_cmd->len); + break; + case NXPWIFI_11AXCMD_BEAM_SUBID: + beam_cmd =3D (struct nxpwifi_11ax_beam_cmd *)&ax_cmd->param; + + he_cmd->val[0] =3D beam_cmd->value; + cmd_size +=3D sizeof(*beam_cmd); + break; + case NXPWIFI_11AXCMD_HTC_SUBID: + htc_cmd =3D (struct nxpwifi_11ax_htc_cmd *)&ax_cmd->param; + + he_cmd->val[0] =3D htc_cmd->value; + cmd_size +=3D sizeof(*htc_cmd); + break; + case NXPWIFI_11AXCMD_TXOMI_SUBID: + txmoi_cmd =3D (struct nxpwifi_11ax_txomi_cmd *)&ax_cmd->param; + + memcpy((void *)he_cmd->val, txmoi_cmd, sizeof(*txmoi_cmd)); + cmd_size +=3D sizeof(*txmoi_cmd); + break; + case NXPWIFI_11AXCMD_OBSS_TOLTIME_SUBID: + toltime_cmd =3D (struct nxpwifi_11ax_toltime_cmd *)&ax_cmd->param; + + memcpy(he_cmd->val, &toltime_cmd->tol_time, + sizeof(toltime_cmd->tol_time)); + cmd_size +=3D sizeof(*toltime_cmd); + break; + case NXPWIFI_11AXCMD_TXOPRTS_SUBID: + txop_cmd =3D (struct nxpwifi_11ax_txop_cmd *)&ax_cmd->param; + + memcpy(he_cmd->val, &txop_cmd->rts_thres, + sizeof(txop_cmd->rts_thres)); + cmd_size +=3D sizeof(*txop_cmd); + break; + case NXPWIFI_11AXCMD_SET_BSRP_SUBID: + set_bsrp_cmd =3D (struct nxpwifi_11ax_set_bsrp_cmd *)&ax_cmd->param; + + he_cmd->val[0] =3D set_bsrp_cmd->value; + cmd_size +=3D sizeof(*set_bsrp_cmd); + break; + case NXPWIFI_11AXCMD_LLDE_SUBID: + llde_cmd =3D (struct nxpwifi_11ax_llde_cmd *)&ax_cmd->param; + + memcpy((void *)he_cmd->val, llde_cmd, sizeof(*llde_cmd)); + cmd_size +=3D sizeof(*llde_cmd); + break; + default: + nxpwifi_dbg(adapter, ERROR, + "%s: Unknown sub command: %d\n", + __func__, ax_cmd->sub_command); + return -EINVAL; + } + + cmd->size =3D 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 =3D priv->adapter; + struct host_cmd_11ax_cmd *he_cmd =3D &resp->params.ax_cmd; + struct nxpwifi_ie_types_data *tlv; + + ax_cmd->sub_id =3D le16_to_cpu(he_cmd->sub_id); + + switch (ax_cmd->sub_command) { + case NXPWIFI_11AXCMD_SR_SUBID: + tlv =3D (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 =3D *he_cmd->val; + break; + case NXPWIFI_11AXCMD_HTC_SUBID: + ax_cmd->param.htc_cfg.value =3D *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 =3D *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; +} + +static u8 nxpwifi_is_ap_11ax_twt_supported(struct nxpwifi_bssdescriptor *b= ss_desc) +{ + struct element *ext_cap; + + if (!bss_desc->bcn_he_cap) + return false; + if (!(bss_desc->bcn_he_cap->mac_cap_info[0] & HE_MAC_CAP_TWT_RESP_SUPPORT= )) + return false; + if (!bss_desc->bcn_ext_cap) + return false; + ext_cap =3D (struct element *)bss_desc->bcn_ext_cap; + + if (!(ext_cap->data[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT)) + return false; + return true; +} + +bool nxpwifi_is_11ax_twt_supported(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + struct nxpwifi_ie_types_he_cap *user_he_cap; + struct nxpwifi_ie_types_he_cap *hw_he_cap; + + if (bss_desc && (!nxpwifi_is_ap_11ax_twt_supported(bss_desc))) { + nxpwifi_dbg(priv->adapter, MSG, + "AP don't support twt feature\n"); + return false; + } + + if (bss_desc->bss_band & BAND_A) { + hw_he_cap =3D (struct nxpwifi_ie_types_he_cap *) + priv->adapter->hw_he_cap; + user_he_cap =3D (struct nxpwifi_ie_types_he_cap *) + priv->user_he_cap; + } else { + hw_he_cap =3D (struct nxpwifi_ie_types_he_cap *) + priv->adapter->hw_2g_he_cap; + user_he_cap =3D (struct nxpwifi_ie_types_he_cap *) + priv->user_2g_he_cap; + } + + if (!(hw_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) { + nxpwifi_dbg(priv->adapter, MSG, + "FW don't support TWT\n"); + return false; + } + + if (!(user_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) { + nxpwifi_dbg(priv->adapter, MSG, + "USER HE_MAC_CAP don't support TWT\n"); + return false; + } + + return true; +} + +u8 nxpwifi_is_sta_11ax_twt_req_supported(struct nxpwifi_private *priv) +{ + struct nxpwifi_ie_types_he_cap *user_he_cap; + u8 ret =3D 0; + + if (ISSUPP_11AXENABLED(priv->adapter->fw_cap_ext) && + (priv->config_bands & BAND_GAX || priv->config_bands & BAND_AAX)) { + if (priv->config_bands & BAND_AAX) + user_he_cap =3D (struct nxpwifi_ie_types_he_cap *)priv->user_he_cap; + else + user_he_cap =3D (struct nxpwifi_ie_types_he_cap *)priv->user_2g_he_cap; + ret =3D user_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT; + } + + return ret; +} + +int nxpwifi_cmd_twt_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_twt_cfg *twt_cfg) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct host_cmd_twt_cfg *twt_cfg_cmd =3D &cmd->params.twt_cfg; + struct nxpwifi_twt_setup *twt_setup; + struct nxpwifi_twt_teardown *twt_teardown; + struct nxpwifi_twt_report *twt_report; + struct nxpwifi_twt_information *twt_information; + struct nxpwifi_btwt_ap_config *btwt_ap_config; + u8 i; + u16 cmd_size; + + cmd->command =3D cpu_to_le16(HOST_CMD_TWT_CFG); + cmd_size =3D sizeof(struct host_cmd_twt_cfg) + S_DS_GEN; + + twt_cfg_cmd->action =3D cpu_to_le16(cmd_action); + twt_cfg_cmd->sub_id =3D cpu_to_le16(twt_cfg->sub_id); + + switch (twt_cfg->sub_id) { + case NXPWIFI_11AX_TWT_SETUP_SUBID: + twt_setup =3D (struct nxpwifi_twt_setup *) + twt_cfg_cmd->val; + + memset(twt_setup, 0x00, sizeof(struct nxpwifi_twt_setup)); + twt_setup->implicit =3D twt_cfg->param.twt_setup.implicit; + twt_setup->announced =3D twt_cfg->param.twt_setup.announced; + twt_setup->trigger_enabled =3D twt_cfg->param.twt_setup.trigger_enabled; + twt_setup->twt_info_disabled =3D twt_cfg->param.twt_setup.twt_info_disab= led; + twt_setup->negotiation_type =3D twt_cfg->param.twt_setup.negotiation_typ= e; + twt_setup->twt_wakeup_duration =3D + twt_cfg->param.twt_setup.twt_wakeup_duration; + twt_setup->flow_identifier =3D twt_cfg->param.twt_setup.flow_identifier; + twt_setup->hard_constraint =3D twt_cfg->param.twt_setup.hard_constraint; + twt_setup->twt_exponent =3D twt_cfg->param.twt_setup.twt_exponent; + twt_setup->twt_mantissa =3D twt_cfg->param.twt_setup.twt_mantissa; + twt_setup->twt_request =3D twt_cfg->param.twt_setup.twt_request; + twt_setup->bcn_miss_threshold =3D twt_cfg->param.twt_setup.bcn_miss_thre= shold; + cmd_size +=3D sizeof(struct nxpwifi_twt_setup); + break; + case NXPWIFI_11AX_TWT_TEARDOWN_SUBID: + twt_teardown =3D (struct nxpwifi_twt_teardown *) + twt_cfg_cmd->val; + memset(twt_teardown, 0x00, + sizeof(struct nxpwifi_twt_teardown)); + twt_teardown->flow_identifier =3D + twt_cfg->param.twt_teardown.flow_identifier; + twt_teardown->negotiation_type =3D + twt_cfg->param.twt_teardown.negotiation_type; + twt_teardown->teardown_all_twt =3D + twt_cfg->param.twt_teardown.teardown_all_twt; + cmd_size +=3D sizeof(struct nxpwifi_twt_teardown); + break; + case NXPWIFI_11AX_TWT_REPORT_SUBID: + twt_report =3D (struct nxpwifi_twt_report *) + twt_cfg_cmd->val; + memset(twt_report, 0x00, sizeof(struct nxpwifi_twt_report)); + twt_report->type =3D twt_cfg->param.twt_report.type; + cmd_size +=3D sizeof(struct nxpwifi_twt_report); + break; + case NXPWIFI_11AX_TWT_INFORMATION_SUBID: + twt_information =3D (struct nxpwifi_twt_information *) + twt_cfg_cmd->val; + memset(twt_information, 0x00, + sizeof(struct nxpwifi_twt_information)); + twt_information->flow_identifier =3D + twt_cfg->param.twt_information.flow_identifier; + twt_information->suspend_duration =3D + twt_cfg->param.twt_information.suspend_duration; + cmd_size +=3D sizeof(struct nxpwifi_twt_information); + break; + case NXPWIFI_11AX_BTWT_AP_CONFIG_SUBID: + btwt_ap_config =3D (struct nxpwifi_btwt_ap_config *) + twt_cfg_cmd->val; + memset(btwt_ap_config, 0x00, + sizeof(struct nxpwifi_btwt_ap_config)); + btwt_ap_config->ap_bcast_bet_sta_wait =3D + twt_cfg->param.btwt_ap_config.ap_bcast_bet_sta_wait; + btwt_ap_config->ap_bcast_offset =3D + twt_cfg->param.btwt_ap_config.ap_bcast_offset; + btwt_ap_config->bcast_twtli =3D + twt_cfg->param.btwt_ap_config.bcast_twtli; + btwt_ap_config->count =3D + twt_cfg->param.btwt_ap_config.count; + for (i =3D 0; i < BTWT_AGREEMENT_MAX; i++) { + btwt_ap_config->btwt_sets[i].btwt_id =3D + twt_cfg->param.btwt_ap_config.btwt_sets[i].btwt_id; + btwt_ap_config->btwt_sets[i].ap_bcast_mantissa =3D + twt_cfg->param.btwt_ap_config.btwt_sets[i].ap_bcast_mantissa; + btwt_ap_config->btwt_sets[i].ap_bcast_exponent =3D + twt_cfg->param.btwt_ap_config.btwt_sets[i].ap_bcast_exponent; + btwt_ap_config->btwt_sets[i].nominalwake =3D + twt_cfg->param.btwt_ap_config.btwt_sets[i].nominalwake; + } + + cmd_size +=3D sizeof(struct nxpwifi_btwt_ap_config); + break; + default: + nxpwifi_dbg(adapter, ERROR, + "Unknown sub id: %d\n", twt_cfg->sub_id); + return -EINVAL; + } + + cmd->size =3D cpu_to_le16(cmd_size); + + return 0; +} + +int nxpwifi_ret_twt_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + struct nxpwifi_twt_cfg *twt_cfg) +{ + struct host_cmd_twt_cfg *twt_cfg_cmd =3D &resp->params.twt_cfg; + u16 action; + + action =3D le16_to_cpu(twt_cfg_cmd->action); + twt_cfg->sub_id =3D le16_to_cpu(twt_cfg_cmd->sub_id); + + if (action =3D=3D HOST_ACT_GEN_GET && + twt_cfg->sub_id =3D=3D NXPWIFI_11AX_TWT_REPORT_SUBID) { + struct nxpwifi_twt_report *twt_report =3D + (struct nxpwifi_twt_report *)twt_cfg_cmd->val; + + memcpy(&twt_cfg->param.twt_report, twt_report, sizeof(struct nxpwifi_twt= _report)); + } + + 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..2eda69f19763 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11ax.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: 802.11ax support + * + * 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) + +/* Get HE MCS map code for n spatial streams (0..3). */ +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 =3D le16_to_cpu(*mcs_map_set); + temp |=3D ((value & 0x3) << (2 * (nss - 1))); + *mcs_map_set =3D cpu_to_le16(temp); +} + +bool nxpwifi_is_11ax_twt_supported(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc); + +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_fill_he_cap_tlv(struct nxpwifi_private *priv, + struct nxpwifi_ie_types_he_cap *he_cap, + u16 bands); +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); + +int nxpwifi_cmd_twt_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct nxpwifi_twt_cfg *twt_cfg); + +int nxpwifi_ret_twt_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + struct nxpwifi_twt_cfg *twt_cfg); + +u8 nxpwifi_is_sta_11ax_twt_req_supported(struct nxpwifi_private *priv); + +#endif /* _NXPWIFI_11AX_H_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazon11011009.outbound.protection.outlook.com [52.101.65.9]) (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 438202D978A; Wed, 4 Feb 2026 18:05:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.65.9 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228323; cv=fail; b=JR5jk1eZS2a+meDn9a+04oHKPdJitXpazQW3VeFn37L/OON6RyoPiJnh54JdxcpHagW6TRLUolGbkBs4ZD6IirRt8Y8zR+2cxcGtIOteL2GJmA2r4DyLFvWwPvNa8KATOmYK4IonV5krdd/+/9phn3Qec6LSshm9YBF5VVaL2aI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228323; c=relaxed/simple; bh=rmmyynuVxgw+u7YgPUYnQrliaMWNQ3gGnmDMj06eZas=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=devV2JfJ5sfoG33kCEdt8AeKAdpYGbpdOSJz5iGruYJpi7x12UDaHQyzx1+096zn3hScM0SiDkTnjni5qywAQbKAl11ZVin/Phu8OzP6SGuGwO+5LCpThU7a1lmfzd3kP3TwYl2rGhrKHwW+fWGPbaM7r0u18cEgzkwmsAxN/Uw= 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=YlYLF+L/; arc=fail smtp.client-ip=52.101.65.9 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="YlYLF+L/" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=zHxxtxv3uUFevrXSUV7PSfEEmib7fRWIEWGtevC14D8sVm61nSS0+HxyUG6RFK0zBR1thmSC2k2YrtBK4are+66W5ZbqNqu/OJwgfN7oQfVYKuyPx7fFyj3vOaQhObPS1jkNoNgCdl4zuUIJvINKaEFJ/k1caK5Sem+SV/I6vDFbUMW/DxuyRnAkyoDz6RKAuTNX+1HiSpNX1HwHfsZAL3FzQguROGgpGCaarS7HhsS1+AXDvtdTC20iUjbp0w/olB0iNCAWpoCbLPiB7dA/BzqdsM8fzjt2w1bV4D6jidfBhRXafF9IMc1GXlx84C3u1GL+Fcm0TdGgk34ePjWgCQ== 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=6XzHrle+3IjyH2AS1V25QqZZEOcU9FOUH2EwDV9YSHQ=; b=cVz/MgIe+r/K583SlO6pA3Szox8zY8+mw4vs9thC58pmaU2Lxu18qjA0WKWL2LceOHphc8h5AnnErYu5ppAyzpSMp+cnOdmAL2okyu4n3lE6PUm7+4eHaLSsH6ASp33FkuCYHn/8z1XOCwHj3c0vnW+sTY3liI2MaxiG3xVcEB5ERAFuUbqPCwVCir7TzmIf1l5nizNd3wcRK3hOgFYDAWpjrdU/GhH1yH0nANuoqkcQM2hMj343KA1t3RBf8xIsjXtlgpDkjH7RqIHE+WkJKKbBSAZc7phgh5OEIje9KGeFOm6klvR9YzkLRPTMJMkaRLkv3pKaIHzhqaffT88HsQ== 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=6XzHrle+3IjyH2AS1V25QqZZEOcU9FOUH2EwDV9YSHQ=; b=YlYLF+L/srxKtXftvZraxyUQZbE98DNIFFqGRtfD+4CCEQkFDq0oPBrKa07jJ/bwteJjgEeZ5h2K1E6ivB/cp7eqEoaARZxCQxSge2vesV9MoijZUol5mPE5sdiPgPyJ07G540YugPtkzhbNMH8pp22DwlJSzIifv5Q6eOg3xLSkrZbtRweADvwJXeqGnTn6/Q6tX3sTvXWzeTyPqx0X9n87uD6gDh7N63zHLJo5C7MfT3/NU8IKFKvhkRe+YywtW3tYVhFv/ALBLVwUIDEeKo731wUBq9U5xQZmALCGZ6qc34ISAIulkwT0x7wKf/78++oMiU5zLFZkwayhuz4Khg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:20 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:20 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 04/21] wifi: nxpwifi: add support for 802.11h (DFS and TPC) Date: Thu, 5 Feb 2026 02:03:41 +0800 Message-Id: <20260204180358.632281-5-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 4c9c1317-8491-4f50-05a7-08de6417f5bb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?lQ8EXixbxA9gSef3kw1+bltBXiHI8XcZJKboHsFInLUIG8UDkK5SDlBKQ71t?= =?us-ascii?Q?xokwzPzVDFTxoE3zuH18MPaVLfVBwuu+BD2SWGnBguHBJlqj225j56qex6p0?= =?us-ascii?Q?+0G9uVS3inbIlCWOUADW0gkhjQFSpsRyHV0iCox1lazprli9eYD7vATvRR6H?= =?us-ascii?Q?Nx8RzG2eOBt13686JwTmfF5miQYeTlNOFcjWymPL7mrGaO8FsuYwqhmUv+KD?= =?us-ascii?Q?MSDPjAmIFSgkxO/Uuas15eM66msaAbTLNQIzG7ysa6SaWXbIz0j7E2YAeAZ5?= =?us-ascii?Q?06vGzOoXHCKaMbK43rRRot/Oy2AuoncLLoCZ31A3o31q2DsaVqNTYrhp9OeJ?= =?us-ascii?Q?y1yT9D1p/CnA/a60/ji+M5KNfpUBxl1gIzfrqlmUCMEHrWOPdTpMsO36ZjXW?= =?us-ascii?Q?SzV6g9zHnLwNq99nHWaL2eSIZUQ79Inma7Ewo+bxXG9N/TcgiA9F+pgui7Zz?= =?us-ascii?Q?w4caV+h9XtnG9O14dsxV271pM1YrF6XH5d7h00nOB2Cdm+oS0OJY7abmZkL3?= =?us-ascii?Q?GpxjkOw62tGyF7SCIxZbhs7ljVBlLQHweXY+/LU9hstUZxWo102Xn55sk3S5?= =?us-ascii?Q?diH/f//hzjpMX50oPXjq1jUK7+Wh7F983qOFHeqEFO2y5Hclx3E5OcWQAZaO?= =?us-ascii?Q?LfQs393yate9ZK07LcJ9mVZejs49ubOUGDlq1G2azGqInrA6B6J3PNCsLOt/?= =?us-ascii?Q?Qw4VAaSVIoHpK8ev9UWAqk7cFQ3+D85hvEFWz+lDYTyZn86pkoZr6kLuJk29?= =?us-ascii?Q?F/bDZOxt2Gmk5n9egWbJ2wUVVTdi+9bu4x9EHdOdwOJi0Y0dlfgASGUsFW7R?= =?us-ascii?Q?KUODPdujzmnjAryPhqn1MpYMjhPja8z1HmaOljhjyXxsrF1110//6Rb7BOSG?= =?us-ascii?Q?EVgTiL7HlNi6I547m1IWgTT53WAMHWOIch0Rblhm37NLUoXebdGPG1saoSSD?= =?us-ascii?Q?SdoPYUsQlT5Fp85Q4eShckQtDTRpqMPxasPWMr+KLSN+MMEiFj2Ru72Qtu4b?= =?us-ascii?Q?doubuxRZqpZaD+Iju96iu9lMO51NmNtkzM9nGXCkVmlJqjHqVAKVDVFwURoq?= =?us-ascii?Q?W8TZGpMuTNnFLHjdoz68/32ZPnmtuyGz0W6E6o3oarZnedGw7lp4PCzCVlOX?= =?us-ascii?Q?F+60UUIO4R9g+uuYJISAgLxv10oaHpxgTgz76ogCt137jVPqrG2O83pIBjqQ?= =?us-ascii?Q?ifEGo5px54O9sB5kAi9A5SG17BDzasdhvd2uUfegn0hqBIr8mcVHmeaWG6zg?= =?us-ascii?Q?niq7Oa8fZyMxgUzUbtDT5FLbZKfP3xOnwe4YuYjlyMJ+Ov5TxldXjiyZQ0cB?= =?us-ascii?Q?uB1qXo0pqGXAl5FuQP0KTqMccBHmtuZnLhmPgDMm8r5e5gp8Ix32vvYOxoRv?= =?us-ascii?Q?LrzhC/Pn8n//3IXsR4RnImI6BdNzcpEEJYRpx+qaAONBK3VSUQBNq6kCSpmY?= =?us-ascii?Q?CJVI5yStIuLWRC3rGukEF+hpgyzK4E/MC1GkkQ6rQXhDtOkbDiTXPl9GVAr7?= =?us-ascii?Q?iiBZeviK4OaMn39AM82yZI4Ubpe0/ecJ2h1+5HmJWyZA6afnv8udXoKibqLE?= =?us-ascii?Q?3tzv7n0LAhTyVNRE65KNR55g9IfqVdNeCMdvpsxJ9i6k5KAAsBbOmqTIK2nj?= =?us-ascii?Q?L9XnDfcUVRVXsPNnfcz/L2k=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?fpvHmASPnacKyAE2F7XZpCETNAfne5JedqaE980+BhDMpbXfB/qodX/IfJby?= =?us-ascii?Q?8GLGIOkfeP6qrUbYlYDDvJCNj8GbCoVZU6xsAps0SyK4swVfoavhQwmGdn2Z?= =?us-ascii?Q?kcb4RIUXccCpw5RU213hG62Wg5j33YH86LV7QlRK3kqhilHPGg2M7a4V/L0m?= =?us-ascii?Q?aWP+WCPV9lWyYhPRU9Y5M/x1Al7Pul5k/zBuIXT2a1MbNKfIXAxjBmxuwWNi?= =?us-ascii?Q?QnJLoZZVlvVEDPBp1XWTRiS3wxLNzJTCbUg0CYlAN7sXZEzSMlG7o7lcfKYJ?= =?us-ascii?Q?yHpfLP1kJxgg0Ef4FfQkg2cu1QDnlBhD0DEyiOQKvQAqjlO6Id5n/4dRQa9X?= =?us-ascii?Q?a3QHqgZ/tUJuR/y8XndVPX9VNVLzeURjd6GXTtQgv1aRiGrSzK+apgwo3Pw/?= =?us-ascii?Q?efzbNwYrEMoCMcBKmZkkxUK5cErEVMCxlmGX9CDHaSHenlZlK3Fk0frDMFVZ?= =?us-ascii?Q?5dwjYoRuDyoY3mnqlb9N1Aam1b/8PQCZjjuY6Nz/0tQtT6/+U0qlgtweiqmV?= =?us-ascii?Q?1MMi67zSl2KtMOuKMo82PAZru36vwySqPKglQDejeVjspvk0yosvaidrKUd7?= =?us-ascii?Q?gby7n5Izbi2OLDfgObNrbYyT9wpjtNuzy1QhaimaFFSD7bTiYvlw+IB10aEd?= =?us-ascii?Q?A+k48f0jcLpsTXy7ZXK09SucT0OgIi4uIoFFP2CJpNbESv2GPZ0FpxYj9cQz?= =?us-ascii?Q?Q86l971u/TSnZsE3ZpcWq0R7fzjGIGQ5cQkvavFj2ce852uKw1796x3oGP1S?= =?us-ascii?Q?o4AM940vioDzxTXTkV2ou57HJJHEzmU6vyaFZUGjIOOjyjhbTD84I97G/aTq?= =?us-ascii?Q?10UJTwqC3qM/TRg5uFld2YZQkAUxjArmUHCzXPH5CeO8XyOqaccKoBNk3hMO?= =?us-ascii?Q?YdYh5cY8ulsCMwBy7OTbcqmXoAizcRM1tn7ngms9d5Px6FwEn3CiexGmtw41?= =?us-ascii?Q?uuRGkbejbw031jZlhH5H+OcNN4ZxjxnSdNjUv5lpLgE51yTKj7M9C2bcEFlc?= =?us-ascii?Q?2Wg87r7riYX7ualLQwhznvCmXvu+gq+Wyuu5qlrAWMo0mWmnkXq4efxFjbAp?= =?us-ascii?Q?SbbCxZ57vAe7qnHqLF382iU1lKyLd+Zgi5qdea+azmfbnQ7x2gBkmLwO16pI?= =?us-ascii?Q?PGF22CQGGldOwll2FxXUeafrhFm1/67Osm9QEFpezFs2GMdosJjX32MbRk7n?= =?us-ascii?Q?G1ws4CC4GEpUmCOqIu0DjbJ2NykB//K3deTzDahl2EbG/vGYIttzNMIHL6Ez?= =?us-ascii?Q?79YhOUQ3sjHWKBOYDBOtAuxNEr2d/2A4rHcaL0V0gaCTTnR7Q97u7UWhWa48?= =?us-ascii?Q?0TLpa0VqunFm3QlYJnqOIkuv+incqu8qDHhDP9qQSrSTQwEACvfv0Wtq+HT1?= =?us-ascii?Q?tP7+JAFCLn3QqOf3YnVFlvda43DfrZgIYEHEErZoL9fobFNIR3uWAtIKtCyJ?= =?us-ascii?Q?TrmfJ/Yrz6Dgs7OoK6LzQQ9pEUpOcB3R1sDU4bpoN3hol+wHeUpio+tteZCB?= =?us-ascii?Q?0TqNBiPwDF8KAo0TcE0geKR6xn924lplKkIJ9zavEgRZ+k45BD9GKW+BsLjs?= =?us-ascii?Q?OoUapLjlCsD18RNWD/ab3mAR5Cl+8HxM6OMvIlbsIhsQ00haosNr6bUs7hp1?= =?us-ascii?Q?kmMsQ3z6+jWYzNv2c652NMy4GQ9r1HrYi37gfPBDTIlZLlGiGY5GgSZd/4xB?= =?us-ascii?Q?T+i5ZCnc37p8oxO8nef5M8Hphhgj2aCYj+Y0hZerW+m4iirmxANuU1w1oXeM?= =?us-ascii?Q?jyh3QlUTvA=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4c9c1317-8491-4f50-05a7-08de6417f5bb X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:20.4149 (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: TTYoiGpNG+M6lLExizszfrST45/6Wu6rAdY78eU/0aZB6k7/996lm/7OznbPKI03ofPeiD2l0EGvDCZ/q1hobg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce 802.11h functionality to the nxpwifi driver, enabling Dynamic Frequency Selection (DFS) and Transmit Power Control (TPC) features required for regulatory compliance in 5GHz bands. - Adds logic to handle DFS CAC (Channel Availability Check) procedures, including radar detection, CAC timeout, and abort handling. - Implements firmware interaction via HOST_CMD_CHAN_REPORT_REQUEST and radar event handling. - Supports TPC through power capability and local power constraint IEs during association. - Enables channel switch operations with AP restart and beacon updates. Firmware is responsible for radar detection and reporting, while the driver manages TLV construction, event handling, and cfg80211 integration. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/11h.c | 338 +++++++++++++++++++++++++ 1 file changed, 338 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..2189b5242fc9 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/11h.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 802.11h helpers + * + * 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 =3D true; + priv->state_11h.is_11h_active =3D false; +} + +inline int nxpwifi_is_11h_active(struct nxpwifi_private *priv) +{ + return priv->state_11h.is_11h_active; +} + +/* 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 =3D nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + sband =3D priv->wdev.wiphy->bands[radio_type]; + + cap =3D (struct nxpwifi_ie_types_pwr_capability *)*buffer; + cap->header.type =3D cpu_to_le16(WLAN_EID_PWR_CAPABILITY); + cap->header.len =3D cpu_to_le16(2); + cap->min_pwr =3D 0; + cap->max_pwr =3D 0; + *buffer +=3D sizeof(*cap); + + constraint =3D (struct nxpwifi_ie_types_local_pwr_constraint *)*buffer; + constraint->header.type =3D cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); + constraint->header.len =3D cpu_to_le16(2); + constraint->chan =3D bss_desc->channel; + constraint->constraint =3D bss_desc->local_constraint; + *buffer +=3D sizeof(*constraint); + + ie_header =3D (struct nxpwifi_ie_types_header *)*buffer; + ie_header->type =3D cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header->len =3D cpu_to_le16(2 * sband->n_channels + 2); + *buffer +=3D sizeof(*ie_header); + *(*buffer)++ =3D WLAN_EID_SUPPORTED_CHANNELS; + *(*buffer)++ =3D 2 * sband->n_channels; + for (i =3D 0; i < sband->n_channels; i++) { + u32 center_freq; + + center_freq =3D sband->channels[i].center_freq; + *(*buffer)++ =3D ieee80211_frequency_to_channel(center_freq); + *(*buffer)++ =3D 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 =3D flag; + + /* enable master mode radar detection on AP interface */ + if ((GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) && enable) + enable |=3D NXPWIFI_MASTER_RADAR_DET_MASK; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_SET, DOT11H_I, &enable, true); +} + +/* + * Process TLV buffer for a pending BSS join. Enable 11h in firmware when = the + * network advertises spectrum management, and add required TLVs based on = the + * BSS'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 =3D true; + bss_desc->cap_info_bitmap |=3D 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 =3D false; + bss_desc->cap_info_bitmap &=3D ~WLAN_CAPABILITY_SPECTRUM_MGMT; + } +} + +/* + * DFS CAC work function. This delayed work emits CAC finished event for c= fg80211 + * if CAC was started earlier + */ +void nxpwifi_dfs_cac_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct cfg80211_chan_def chandef; + struct wiphy_delayed_work *delayed_work =3D + container_of(work, struct wiphy_delayed_work, work); + struct nxpwifi_private *priv =3D container_of(delayed_work, + struct nxpwifi_private, + dfs_cac_work); + + chandef =3D 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); + } +} + +/* prepares channel report request command to FW for starting radar detect= ion */ +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 =3D &cmd->params.chan_rpt_req; + struct nxpwifi_radar_params *radar_params =3D (void *)data_buf; + u16 size; + + cmd->command =3D cpu_to_le16(HOST_CMD_CHAN_REPORT_REQUEST); + size =3D S_DS_GEN; + + cr_req->chan_desc.start_freq =3D cpu_to_le16(NXPWIFI_A_BAND_START_FREQ); + nxpwifi_convert_chan_to_band_cfg(priv, + &cr_req->chan_desc.band_cfg, + radar_params->chandef); + cr_req->chan_desc.chan_num =3D radar_params->chandef->chan->hw_value; + cr_req->msec_dwell_time =3D cpu_to_le32(radar_params->cac_time_ms); + size +=3D sizeof(*cr_req); + + if (radar_params->cac_time_ms) { + struct nxpwifi_ie_types_chan_rpt_data *rpt; + + rpt =3D (struct nxpwifi_ie_types_chan_rpt_data *)((u8 *)cmd + size); + rpt->header.type =3D cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC); + rpt->header.len =3D cpu_to_le16(sizeof(u8)); + rpt->meas_rpt_map =3D 1 << MEAS_RPT_MAP_RADAR_SHIFT_BIT; + size +=3D sizeof(*rpt); + + nxpwifi_dbg(priv->adapter, MSG, + "11h: issuing DFS Radar check for channel=3D%d\n", + radar_params->chandef->chan->hw_value); + } else { + nxpwifi_dbg(priv->adapter, MSG, "cancelling CAC\n"); + } + + cmd->size =3D 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 =3D chandef; + radar_params.cac_time_ms =3D 0; + + return nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST, + HOST_ACT_GEN_SET, 0, &radar_params, true); +} + +/* Abort ongoing CAC when 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"); + wiphy_delayed_work_cancel(priv->adapter->wiphy, &priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); + } +} + +/* + * handles channel report event from FW during CAC period. If radar is det= ected + * during CAC, driver indicates the same to cfg80211 and also cancels ongo= ing + * 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 =3D (void *)(skb->data + sizeof(u32)); + event_len =3D skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event) + + sizeof(u32)); + + if (le32_to_cpu(rpt_event->result) !=3D HOST_RESULT_OK) { + nxpwifi_dbg(priv->adapter, ERROR, + "Error in channel report event\n"); + return -EINVAL; + } + + while (event_len >=3D sizeof(struct nxpwifi_ie_types_header)) { + rpt =3D (void *)&rpt_event->tlvbuf; + tlv_len =3D 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); + + wiphy_delayed_work_cancel(priv->adapter->wiphy, + &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 -=3D (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 =3D (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"); + wiphy_delayed_work_cancel(priv->adapter->wiphy, &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; +} + +/* + * work function for channel switch handling. takes care of updating new c= hannel + * definitin to bss config structure, restart AP and indicate channel swit= ch + * success to cfg80211 + */ +void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct nxpwifi_uap_bss_param *bss_cfg; + struct wiphy_delayed_work *delayed_work =3D + container_of(work, struct wiphy_delayed_work, work); + struct nxpwifi_private *priv =3D container_of(delayed_work, + struct nxpwifi_private, + dfs_chan_sw_work); + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (nxpwifi_del_mgmt_ies(priv)) + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + bss_cfg =3D &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(adapter->wiphy, priv->netdev, + &priv->ap_update_info)) { + 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"); + + cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0); + + if (priv->uap_stop_tx) { + netif_carrier_on(priv->netdev); + nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter); + priv->uap_stop_tx =3D false; + } +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazon11011025.outbound.protection.outlook.com [52.101.65.25]) (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 E89B1421EFC; Wed, 4 Feb 2026 18:05:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.65.25 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228326; cv=fail; b=pxaHNhXzFpbzle9FQnUeIb34oI67AsdeXntLpVr7iCv6KqHKsMP0LlN3b/8XNP0qiPwfqgNJfyqQ0q1il9lUw4JDoLAQfuGKSPJZwZFmG40Mejn0exTdM8NSV6mf9hP9p54Rl4lRxvGUFElpAZxnMZaGQBUFpTgYPEPRgofgCAY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228326; c=relaxed/simple; bh=pdo2LorXrOU0PPChjW/r8y28/1zPVcGrdPpntI3d+8s=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=WQIn2H+mfDYzgm/qFgiPs0clkUo3INpX0/IW0zZt4bYyoETRcZ4WttoY4+MK32sljTENzzpid50MYiEobDlCLE2zvJZQfNKSfxin7YgXHEC/ZVu8rpekTcRvewV1ocjlt2GQ1bYPo2Q2pD16SqCPVNNsy2xhzJGdXn6FCwuafUE= 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=WJJJj3Bf; arc=fail smtp.client-ip=52.101.65.25 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="WJJJj3Bf" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=JrUJv1n4q1qTpf8dMdJBUvaUtU/znicNJVvjUjdeDoJZYUpDULwk9MMo4f7XTyMICy/Y+VJJIBdm4CrW9Mz8Msk8Kgk8V+lkqjWNvfGysyiRkdRclZTTGyT7ypfDkxhdLg6nsrvyHgHcMcv505JAg08DwVoH9mxqaLI0hbh2Oc3GSK1yZe06DaaTBSt8krdoerqa47lHYpyOmshPtYRwmN+97ajS5dMzy7E/8Kz1y34FY5oDX0JS+uYl3U3M55EFjlu6K9lMGvgr55DatafqO1QixxM7/3HOnIdjtG3Zzq0jwfo+aPcjxjxo4BsTkgrSEcQaQnGxSO315/IwvHwO0w== 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=wqAscNb0Lh/j505A3N3B2cPGiYfQjC9fzKCznfAkH5A=; b=kTzl7+J9x8oFyURAvEUQTpk99r9KYM/+/Cg1PIHNArAqy7PZ1fBEUAesUxJGUG3rW41sKrt2z8PTzGORptQ6aXZHI4A5cW7PTxIivfMo15VEkQfbDfJ7PerTASuFjruYEf3M3Ifa8vhYoiInjUKieSl69XLLja+bXhRuP9a0LOjc7izJeGMLk4bOsUEMhmH1Ppgwytz0/9lA9VHVwUvPOJB2CaysHBkTBGnjEkeeDW0mWppljNYvQCnxDSIwzU8Wxfjx0r66gHc4g2CLK3uibFi/CLiRlVO/h0pe702Uc3UqylBgvwnF25Izc+MEcoCUECyqbrhppeaj4S+W/LJu/Q== 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=wqAscNb0Lh/j505A3N3B2cPGiYfQjC9fzKCznfAkH5A=; b=WJJJj3Bf/9hAxvKNpm/4glbc790HhcZwmIswOBikZTQ+u/HuN0+54d435TJJe7WpkymJge/ccF5B2Y6rcaPVuwwl39v/G7ynr1RvWxA68+Nljhrqbxpy/QsC7ZLO/Ar/2XOc7BHH+ZzV518Y+F1oQwcpEkI1ybzZdyRPqLcluiEXmnKJOUA6Mp6xkrkis/G+728EXR+7m18kHF5yC8O6L6AccJSu4/9uK4NDQEPZHcj1dBY3cYC8yEF6l/lXVN1wFZ3wfPLkxIakoUg0N3ibyvlDGOO6QDJNcQ7ghPd5LVgscitBloijZUvf0LK1/CfnYK651stXBSVotBVh3ipOHA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:22 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:22 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 05/21] wifi: nxpwifi: add support for WMM Date: Thu, 5 Feb 2026 02:03:42 +0800 Message-Id: <20260204180358.632281-6-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: d953f654-b1c6-417c-2d91-08de6417f714 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?1ngfbo32pnl3NoEK95GHrdIL5XwQ0rh/nkAKXqkN3OduCaeG1MTlJIYtBWJ4?= =?us-ascii?Q?coJ35FmLtXDWPAkYjdNdIO+ajtB4L9N2MLsYbSWPpsJw2eJ+NhQqIMn/nfZ0?= =?us-ascii?Q?SzaT1wdgUEBUCnz7GccyFfutRX7qbzHiTTeqr7pJGnBNGp26/T7J8d1izTaP?= =?us-ascii?Q?LAlVJ46qpp+WJ0gXOpmpKM14TfH/bR+6HZL5nqk8HOkFFQ+F940WTzzL9Rlf?= =?us-ascii?Q?sby2rWmTWU04DLHyZxnlNGyVHN+9PGdjAttu7jQr6cLgo9OxRYS6N/3bTSRA?= =?us-ascii?Q?zgWzMRSa3QXF/33fd4ZDUGwywtljTrgxw7xEsa8mFSwoeJ8H/XWJEPOXjLLz?= =?us-ascii?Q?8FbuzYKNk45hikko4scHXDBYhFSUW14JXXVuuJzm/sL7VA1HbFAoLNosztGM?= =?us-ascii?Q?4rVrEHLsqMTISvXKbF2gKarVBdfTtujH264u5/xz/RJmjyemWWm13QWcllMr?= =?us-ascii?Q?zLpJwKgaG6g78igUj9E7KotcOPXZsyzzWzcYx+qHCavlSGAYVSg1CG6KQb9P?= =?us-ascii?Q?og+YVFIvkMP0moGeZq83h7Bkp8g6LW9GuO5+aJ4IZAjdoB6p2WvuN9OEM6qf?= =?us-ascii?Q?cFUHf1H+7+x1XcT3WGXpuG2g4/d/tvtyKzEzigXVWDDyfzrkrsNb+UV92ymg?= =?us-ascii?Q?3IV+3SbREtO81M0nrll+B/7i78CWbhJuvmMiKmEniJjQ7XK8a+oEDmI0E7ui?= =?us-ascii?Q?pUhHQ0kKs1Jz8WZ4UuiJvFmS32ex0kpPbQFbeHxfwvQTwgHI8tCXMudKBvZT?= =?us-ascii?Q?XMUYf6PAxFqps3sqYZjuKCVZsgxpjju0O7N84S8B7XxUQcbrXR+RQTqIt/yt?= =?us-ascii?Q?dNbsyHfBjqTVf+vZb2THZJm1Gv7MwNXExGPEph3Ms3jl/65+8UCueWVz+Fwj?= =?us-ascii?Q?PKRj3AiN7ue5WQO2+zVUGaAc89UKS/sb1yx+3wtFqXozZVx0DNEm6DbnxohI?= =?us-ascii?Q?fF7OQNlJbIfjWWVyzBnsv6MxJ/9GNN3OAxOyNduH3/giv5Xy9z8tFZggTXc6?= =?us-ascii?Q?f5ckcMoiHelY9sHMh3OEXFYW+T/wFRdiRiVgdhMoFrhdlvNTGRcYV7uwvw3y?= =?us-ascii?Q?XVPYsFAmtfoD84NR9Np6DfnNBAvuaS42sLPYSKPQMZnA+LlVQc30AymW6Jmi?= =?us-ascii?Q?NXGHntBAuFILqOaikWTlEZSUho9xkwh1K8n1Bm9EGTT2IuV/F9+JhcKuVHpo?= =?us-ascii?Q?JPehDs2Ahqs67enTP8hG8yKlfNFmSDmOjrFZm5ZNcBsA3j3DulusZjYmC9Kt?= =?us-ascii?Q?M7yhgNkkZcMBPojcTEDEfVLb4GtmGy5MUtF1exdr1HcnSdU1astnqIqymMyL?= =?us-ascii?Q?joPA2I+QyYxPjVsyuaylATX3EoUL2xyu5fTEYqde6Bfl9bFIXYDQwqve0F+W?= =?us-ascii?Q?uv6apWU6qLycoHgouk1nIUBJwRJ8JTX//xtR74leOsyo/dwYGQeP83q4hA4j?= =?us-ascii?Q?5FyK8p62uFiQElesOwRN1F7kJnKtt9LK0Bjc0NCBwmUF+cjVoPxl3U1uuoX4?= =?us-ascii?Q?zWqlLPYZvLP4BOmDdCX4IGDTQLeRSQV5XObFaedctdYKL0VGVUY8ziMTVQTW?= =?us-ascii?Q?7N5UZLXpMIrTQ0y0yvLHZ9uUJHP3VTXOMk94MxgVFur+mDBAM0r0t9Vg6d79?= =?us-ascii?Q?LiOXLjTz64lvTWDgvQuJ/Ac=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?svrRFWTwdP+ysBmfmSj4jVcTy0Vf84I5ohWd3mI1peC9cD/7NniH72X/OwNM?= =?us-ascii?Q?gn47xLaEJV/RNwkQLbZZRxuRBgK/ZZo4ZqNlv0S34k4Gwz5R1Cu66Qr3zgPx?= =?us-ascii?Q?fwF7NKbXM/cLokSnXJ4ibhG7KdvK+LxLuJ+TLB9wsKb07sRjjSBR2rW9X4Sx?= =?us-ascii?Q?yfOIjZj5/rpH396Jzg9sh8zTsep5P9XlqyPePvUdxt51EvSDAYH5si3Huisu?= =?us-ascii?Q?YQbJfcYsY1zCudN3yRAfL+v00358Sl1qu6+so7UP3CEUFbc7ndPEHX9uUj/1?= =?us-ascii?Q?JG/i2bGQd4SKENwp8cPv0DL9yGGw0EouzSDWuA7CbtoDqVp4MUEm9rOivcv/?= =?us-ascii?Q?1fRoSm0UoM+0p+3K1fOcvvtBRB/ol33Wstd4ZDwQAOgkJBi0ixgX2zdvrsmn?= =?us-ascii?Q?Ef4uhgcv6cchADOYhqOi0C4Y+fqsmFUlZepykYycN4w1PgpMZWYbxvdlcbxx?= =?us-ascii?Q?nImmxE5cY7uPTwXsjNoa3k7T9n+2GhKypRrnmQuLcObOuO9Wef5/v0b/G4cI?= =?us-ascii?Q?Md6kSZ+oBkEGQ1tQy7Su8v53T0rykA2H1pNfK+ddjLDMbvNAAZZ9MVIN/fQa?= =?us-ascii?Q?zWyguqv1RpyveTSZq5b/9C3TvwIOtAWvYGLn1pYn6BNJlEoHW7pV58d3IwoY?= =?us-ascii?Q?sAh2MKs2TQ91XKGO614l29J65zpLCUZbcCBPJlK2ZXLV/fVJe3FPkM8N6SZP?= =?us-ascii?Q?StD9ImzMfbKni1vo8UgK9xfK/mRyF97tdBKwWNkn2be2CTAeKUCmdvxDRV8E?= =?us-ascii?Q?RwJZlio+1fm8WOlR3vi8yWxn+9LGdyUf7zN+WMAW5rJQhMPyojqkxewyBVul?= =?us-ascii?Q?rRON6hS6NqdWB+dgR4tgj5aXb0ULqgCY8nWHmki1IbDAxdogpbqGUaW7BMCK?= =?us-ascii?Q?ie+QCGBR2gt1+dNVWMMTadHRDF595G0p5wEhQ9Gw7JZVLO/C/rMLJMSTe6Wz?= =?us-ascii?Q?c2HCja5qjgZCTkcnGh0zRKPaEjGKzoD4RLgvd0fyY1qF+wtkTUd0XHC2U+0w?= =?us-ascii?Q?imqieQ3DfduK2D94OugGxxTRWBIRUAv1UfGIBu+awg8r1WH+eVKLwsi6KMye?= =?us-ascii?Q?Rzn658smf9JpwFCUzGFnOfkYzr61PcOyntUhon2LdsWmlidBnBgYV4Cgqn+y?= =?us-ascii?Q?j0s0XXv7EXPbqV6wVIprbvB3w1QAIWCjas8VrFs/lfG/zqouB+plL34/QX9l?= =?us-ascii?Q?YL1AnrRLxr+IJEp792ADHBiRDHowUUt6NQGjxfU3rUXvzVAlCpYFEukzKpev?= =?us-ascii?Q?Y2IAgu1/s/GrknX8/H73Ip55+oUwbVdoGoq7hzpmOoeNU5dIL0dpbQ7Bk03i?= =?us-ascii?Q?GtHIE8qDmkHs6wuZ3Iuxx3AUUveIc2EqDjyea5S8CoJj1d54JazPtthKI7Rx?= =?us-ascii?Q?ErNNIz3GEMALilkhlNHPexgh+ZxSDaKXueykI7w1JD5ZkHTHos+79YlBO3ad?= =?us-ascii?Q?ztR11e1BeN+iXG6ogUYHxCh74e1Vsa5wKnsHjB5GvOr8oDm9+4GOsF5Vw01G?= =?us-ascii?Q?IoPPzTmY8XlsL7Zp3pVLVnITMplNRfdrXr5ljQdTggXch3gsyIXJi16A5l9x?= =?us-ascii?Q?/1WHNp9hXTNkGWx4qTvOVBGiFct7GxIIpc2eG6hgZ4X9gfYUWdZvP1sJ8dEr?= =?us-ascii?Q?ik2W2P+AaldvMXVLJCPtCv0yWuHviUgvdkBxi5G5y3oOVLDjhLspAv3EZAgj?= =?us-ascii?Q?F85yeipNTv94FvU8VxjC3/5P2TmTUkQ/PLZzM0k8v5KzbZsHOqLvvc2EM2g3?= =?us-ascii?Q?zG18/cYVDw=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: d953f654-b1c6-417c-2d91-08de6417f714 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:22.7635 (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: KJfeB4cSKkX7i6S+giCtC1hQP+lwqGLBJtQFVFio3XWIhtX8TzujI8XhW1SrI9mr3BI9Z8L7lgrMKUE/LpLBLg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce WMM functionality to the nxpwifi driver, enabling QoS-based traffic prioritization and queue management for multimedia applications. - Implements WMM AC queue setup and priority mapping based on WMM Parameter IE from AP. - Adds support for TID-to-AC conversion, admission control handling, and downgrade logic for non-admitted traffic. - Integrates AMSDU/AMPDU aggregation logic with per-TID RA list queues. - Handles firmware interaction for WMM status reporting and association request TLV construction. - Supports packet queuing, reordering, and transmission scheduling based on priority and aggregation eligibility. This patch enhances traffic handling efficiency and ensures compliance with WMM specifications for both STA and AP modes. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/wmm.c | 1308 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/wmm.h | 77 ++ 2 files changed, 1385 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..e277b23bfc42 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/wmm.c @@ -0,0 +1,1308 @@ +// 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[] =3D { + 0x02, /* from tos_to_tid[2] =3D 0 */ + 0x00, /* from tos_to_tid[0] =3D 1 */ + 0x01, /* from tos_to_tid[1] =3D 2 */ + 0x03, + 0x04, + 0x05, + 0x06, + 0x07 +}; + +/* WMM information element */ +static const u8 wmm_info_ie[] =3D { WLAN_EID_VENDOR_SPECIFIC, 0x07, + 0x00, 0x50, 0xf2, 0x02, + 0x00, 0x01, 0x00 +}; + +static const u8 wmm_aci_to_qidx_map[] =3D { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VO +}; + +static u8 tos_to_tid[] =3D { + /* 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] =3D { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; + +/* 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[] =3D { "BK", "BE", "VI", "VO" }; + + pr_debug("info: WMM AC_%s: ACI=3D%d, ACM=3D%d, Aifsn=3D%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=3D%d, EcwMax=3D%d, TxopLimit=3D%d\n", + ac_param->cw & NXPWIFI_ECW_MIN, + (ac_param->cw & NXPWIFI_ECW_MAX) >> 4, + le16_to_cpu(ac_param->txop_limit)); +} + +/* Allocates a route address list. */ +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 =3D 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 =3D 0; + + nxpwifi_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); + + return ra_list; +} + +/* + * 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 =3D ktime_get_ns(); + ns +=3D (ns >> 32) + (ns >> 16); + + return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; +} + +/* 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 =3D priv->adapter; + struct nxpwifi_sta_node *node; + + for (i =3D 0; i < MAX_NUM_TID; ++i) { + ra_list =3D 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 =3D 0; + ra_list->ba_status =3D BA_SETUP_NONE; + ra_list->amsdu_in_ampdu =3D false; + if (!nxpwifi_queuing_ra_based(priv)) { + ra_list->is_11n_enabled =3D IS_11N_ENABLED(priv); + } else { + rcu_read_lock(); + node =3D nxpwifi_get_sta_entry(priv, ra); + if (node) + ra_list->tx_paused =3D node->tx_pause; + ra_list->is_11n_enabled =3D + nxpwifi_is_sta_11n_enabled(priv, node); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu =3D node->max_amsdu; + rcu_read_unlock(); + } + + nxpwifi_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=3D%d\n", + ra_list, ra_list->is_11n_enabled); + + if (ra_list->is_11n_enabled) { + ra_list->ba_pkt_count =3D 0; + ra_list->ba_packet_thr =3D + nxpwifi_get_random_ba_threshold(); + } + list_add_tail(&ra_list->list, + &priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +/* Sets the WMM queue priorities to their default values. */ +static void nxpwifi_wmm_default_queue_priorities(struct nxpwifi_private *p= riv) +{ + /* Default queue priorities: VO->VI->BE->BK */ + priv->wmm.queue_priority[0] =3D WMM_AC_VO; + priv->wmm.queue_priority[1] =3D WMM_AC_VI; + priv->wmm.queue_priority[2] =3D WMM_AC_BE; + priv->wmm.queue_priority[3] =3D WMM_AC_BK; +} + +/* Map ACs to TIDs. */ +static void +nxpwifi_wmm_queue_priorities_tid(struct nxpwifi_private *priv) +{ + struct nxpwifi_wmm_desc *wmm =3D &priv->wmm; + u8 *queue_priority =3D wmm->queue_priority; + int i; + + for (i =3D 0; i < 4; ++i) { + tos_to_tid[7 - (i * 2)] =3D ac_to_tid[queue_priority[i]][1]; + tos_to_tid[6 - (i * 2)] =3D ac_to_tid[queue_priority[i]][0]; + } + + for (i =3D 0; i < MAX_NUM_TID; ++i) + priv->tos_to_tid_inv[tos_to_tid[i]] =3D (u8)i; + + atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); +} + +/* 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 element: version=3D%d,\t" + "qos_info Parameter Set Count=3D%d, Reserved=3D%#x\n", + wmm_ie->version, wmm_ie->qos_info & + IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, + wmm_ie->reserved); + + for (num_ac =3D 0; num_ac < ARRAY_SIZE(wmm_ie->ac); num_ac++) { + u8 ecw =3D wmm_ie->ac[num_ac].cw; + u8 aci_aifsn =3D wmm_ie->ac[num_ac].aci_aifsn; + + cw_min =3D (1 << (ecw & NXPWIFI_ECW_MIN)) - 1; + avg_back_off =3D (cw_min >> 1) + (aci_aifsn & NXPWIFI_AIFSN); + + ac_idx =3D wmm_aci_to_qidx_map[(aci_aifsn & NXPWIFI_ACI) >> 5]; + priv->wmm.queue_priority[ac_idx] =3D ac_idx; + tmp[ac_idx] =3D avg_back_off; + + nxpwifi_dbg(priv->adapter, INFO, + "info: WMM: CWmax=3D%d CWmin=3D%d Avg Back-off=3D%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 =3D 0; i < num_ac; i++) { + for (j =3D 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] =3D=3D 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); +} + +/* Evaluates whether or not an AC is to be downgraded. */ +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 =3D &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 =3D 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 =3D WMM_AC_BK; down_ac < eval_ac; down_ac++) { + ac_status =3D &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 =3D (enum nxpwifi_wmm_ac_e)down_ac; + } + + return ret_ac; +} + +/* 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 =3D WMM_AC_BK; ac_val <=3D WMM_AC_VO; ac_val++) + priv->wmm.ac_down_graded_vals[ac_val] =3D + (enum nxpwifi_wmm_ac_e)ac_val; + } else { + for (ac_val =3D WMM_AC_BK; ac_val <=3D WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] =3D + 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]); + } + } +} + +/* 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[] =3D { + 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 >=3D ARRAY_SIZE(tos_to_ac)) + return WMM_AC_BE; + + return tos_to_ac[tos]; +} + +/* + * Evaluates a given TID and downgrades it to a lower TID if the WMM Param= eter + * element received from the AP indicates that the AP is disabled (due to = call + * admission control (ACM bit). + */ +u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid) +{ + enum nxpwifi_wmm_ac_e ac, ac_down; + u8 new_tid; + + ac =3D nxpwifi_wmm_convert_tos_to_ac(priv->adapter, tid); + ac_down =3D 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 =3D ac_to_tid[ac_down][tid % 2]; + + return new_tid; +} + +/* 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 =3D 0; j < adapter->priv_num; ++j) { + priv =3D adapter->priv[j]; + + for (i =3D 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 =3D + priv->tos_to_tid_inv[i]; + else + priv->aggr_prio_tbl[i].amsdu =3D + BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[i].ampdu_ap =3D + priv->tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_user =3D + priv->tos_to_tid_inv[i]; + } + + priv->aggr_prio_tbl[6].amsdu =3D + priv->aggr_prio_tbl[6].ampdu_ap =3D + priv->aggr_prio_tbl[6].ampdu_user =3D + BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].amsdu =3D + priv->aggr_prio_tbl[7].ampdu_ap =3D + priv->aggr_prio_tbl[7].ampdu_user =3D + BA_STREAM_NOT_ALLOWED; + + nxpwifi_set_ba_params(priv); + nxpwifi_reset_11n_rx_seq_num(priv); + + priv->wmm.drv_pkt_delay_max =3D 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 =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + if (!skb_queue_empty(&priv->bypass_txq)) + return false; + } + + return true; +} + +/* Checks if WMM Tx queue is empty. */ +bool nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter) +{ + int i; + struct nxpwifi_private *priv; + + for (i =3D 0; i < adapter->priv_num; ++i) { + priv =3D adapter->priv[i]; + if (!priv->port_open) + continue; + if (atomic_read(&priv->wmm.tx_pkts_queued)) + return false; + } + + return true; +} + +/* Deletes all packets in an RA list node. */ +static void +nxpwifi_wmm_del_pkts_in_ralist_node(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra_list) +{ + struct nxpwifi_adapter *adapter =3D 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); + } +} + +/* Deletes all packets in an RA list. */ +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); +} + +/* Deletes all packets in all RA lists. */ +static void nxpwifi_wmm_cleanup_queues(struct nxpwifi_private *priv) +{ + int i; + + for (i =3D 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); +} + +/* 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 =3D 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; +} + +/* Cleans up the Tx and Rx queues. */ +void +nxpwifi_clean_txrx(struct nxpwifi_private *priv) +{ + struct sk_buff *skb, *tmp; + unsigned long index; + void *entry; + + 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); + + xa_for_each(&priv->ack_status_frames, index, entry) { + nxpwifi_free_ack_frame(index, entry, NULL); + xa_erase(&priv->ack_status_frames, index); + } + + xa_destroy(&priv->ack_status_frames); +} + +/* 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 =3D 0, tx_pkts_queued; + int i; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + for (i =3D 0; i < MAX_NUM_TID; ++i) { + ra_list =3D nxpwifi_wmm_get_ralist_node(priv, i, mac); + if (ra_list && ra_list->tx_paused !=3D tx_pause) { + pkt_cnt +=3D ra_list->total_pkt_count; + ra_list->tx_paused =3D tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] +=3D + ra_list->total_pkt_count; + else + priv->wmm.pkts_paused[i] -=3D + ra_list->total_pkt_count; + } + } + + if (pkt_cnt) { + tx_pkts_queued =3D atomic_read(&priv->wmm.tx_pkts_queued); + if (tx_pause) + tx_pkts_queued -=3D pkt_cnt; + else + tx_pkts_queued +=3D 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); +} + +/* Retrieves an RA list node for a given TID and RA address pair. */ +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 =3D 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); +} + +/* Deletes RA list nodes for given mac for all TIDs. */ +void +nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private *priv, const u8 *ra_ad= dr) +{ + struct nxpwifi_ra_list_tbl *ra_list; + int i; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + for (i =3D 0; i < MAX_NUM_TID; ++i) { + ra_list =3D 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] -=3D 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); +} + +/* 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 =3D=3D ra_list) + return true; + } + + return false; +} + +/* Adds a packet to bypass TX queue. */ +void +nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + skb_queue_tail(&priv->bypass_txq, skb); +} + +/* Adds a packet to WMM queue. */ +void +nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u32 tid; + struct nxpwifi_ra_list_tbl *ra_list =3D NULL; + struct list_head list_head; + u8 ra[ETH_ALEN], tid_down; + struct ethhdr *eth_hdr =3D (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 =3D skb->priority; + + spin_lock_bh(&priv->wmm.ra_list_spinlock); + + tid_down =3D 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 + */ + if (!nxpwifi_queuing_ra_based(priv) && + !nxpwifi_is_skb_mgmt_frame(skb)) { + list_head =3D priv->wmm.tid_tbl_ptr[tid_down].ra_list; + ra_list =3D list_first_entry_or_null(&list_head, + struct nxpwifi_ra_list_tbl, + list); + } else { + memcpy(ra, skb->data, ETH_ALEN); + if (is_multicast_ether_addr(ra) || + nxpwifi_is_skb_mgmt_frame(skb)) + eth_broadcast_addr(ra); + ra_list =3D 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); +} + +/* Processes the get WMM status command response from firmware. */ +int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv, + const struct host_cmd_ds_command *resp) +{ + u8 *curr =3D (u8 *)&resp->params.get_wmm_status; + u16 resp_len =3D le16_to_cpu(resp->size), tlv_len; + int mask =3D IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + bool valid =3D 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 =3D 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 >=3D sizeof(tlv_hdr->header)) && valid) { + tlv_hdr =3D (struct nxpwifi_ie_types_data *)curr; + tlv_len =3D 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 =3D (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 =3D &priv->wmm.ac_status[wmm_qs->queue_index]; + ac_status->disabled =3D wmm_qs->disabled; + ac_status->flow_required =3D wmm_qs->flow_required; + ac_status->flow_created =3D wmm_qs->flow_created; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* + * Point the regular IEEE element 2 bytes into the NXP element + * and setup the IEEE element type and length byte fields + */ + + wmm_param_ie =3D + (struct ieee80211_wmm_param_ie *)(curr + 2); + wmm_param_ie->len =3D (u8)tlv_len; + wmm_param_ie->element_id =3D 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 =3D false; + break; + } + + curr +=3D (tlv_len + sizeof(tlv_hdr->header)); + resp_len -=3D (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 TL= V. + * + * If the BSS we are associating to supports WMM, this function adds the + * required WMM Information element to the association request command buf= fer in + * the form of a NXP extended IEEE element. + */ +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 =3D 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=3D%#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 =3D=3D WLAN_EID_VENDOR_SPECIFIC) { + wmm_tlv =3D (struct nxpwifi_ie_types_wmm_param_set *)*assoc_buf; + wmm_tlv->header.type =3D cpu_to_le16((u16)wmm_info_ie[0]); + wmm_tlv->header.len =3D 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 =3D sizeof(wmm_tlv->header) + + le16_to_cpu(wmm_tlv->header.len); + + *assoc_buf +=3D ret_len; + } + + return ret_len; +} + +/* Computes the time delay in the driver queues for a given packet. */ +u8 +nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv, + const struct sk_buff *skb) +{ + u32 queue_delay =3D 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 =3D (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; +} + +/* 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 =3D adapter->priv_num - 1; j >=3D 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 =3D adapter->bss_prio_tbl[j].bss_prio_cur->priv; + + if (!priv_tmp->port_open || + (atomic_read(&priv_tmp->wmm.tx_pkts_queued) =3D=3D 0)) + continue; + + /* iterate over the WMM queues of the BSS */ + hqp =3D &priv_tmp->wmm.highest_queued_prio; + for (i =3D atomic_read(hqp); i >=3D LOW_PRIO_TID; --i) { + spin_lock_bh(&priv_tmp->wmm.ra_list_spinlock); + + to_tid =3D tos_to_tid[i]; + tid_ptr =3D &(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) !=3D 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 =3D priv_tmp; + *tid =3D tos_to_tid[i]; + + return ptr; +} + +/* Rotates ra and bss lists so packets are picked round robin. */ +void nxpwifi_rotate_priolists(struct nxpwifi_private *priv, + struct nxpwifi_ra_list_tbl *ra, + int tid) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_bss_prio_tbl *tbl =3D adapter->bss_prio_tbl; + struct nxpwifi_tid_tbl *tid_ptr =3D &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); +} + +/* 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 =3D 0, total_size =3D 0; + struct sk_buff *skb, *tmp; + int max_amsdu_size; + + if (priv->bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP && priv->ap_11n_enabled && + ptr->is_11n_enabled) + max_amsdu_size =3D min_t(int, ptr->max_amsdu, max_buf_size); + else + max_amsdu_size =3D max_buf_size; + + skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { + total_size +=3D skb->len; + if (total_size >=3D max_amsdu_size) + break; + if (++count >=3D MIN_NUM_AMSDU) + return true; + } + + return false; +} + +/* 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 =3D 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 =3D skb_dequeue(&ptr->skb_head); + + tx_info =3D 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 =3D skb_peek(&ptr->skb_head); + else + skb_next =3D NULL; + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + + tx_param.next_pkt_len =3D ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + + if (nxpwifi_process_tx(priv, skb, &tx_param) =3D=3D -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 |=3D 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); + } +} + +/* 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 =3D skb_peek(&ptr->skb_head); + + tx_info =3D NXPWIFI_SKB_TXCB(skb); + if (tx_info->flags & NXPWIFI_BUF_FLAG_REQUEUED_PKT) + return true; + + return false; +} + +/* 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 =3D 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 =3D 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 =3D skb_peek(&ptr->skb_head); + else + skb_next =3D NULL; + + tx_info =3D NXPWIFI_SKB_TXCB(skb); + + spin_unlock_bh(&priv->wmm.ra_list_spinlock); + + tx_param.next_pkt_len =3D + ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + + ret =3D 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 |=3D 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 !=3D -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); + } +} + +/* 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 =3D NULL; + int ptr_index =3D 0; + u8 ra[ETH_ALEN]; + int tid_del =3D 0, tid =3D 0; + + ptr =3D nxpwifi_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); + if (!ptr) + return -ENOENT; + + tid =3D nxpwifi_get_tid(ptr); + + nxpwifi_dbg(adapter, DATA, "data: tid=3D%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 =3D 0; i < adapter->priv_num; ++i) { + priv =3D adapter->priv[i]; + + if (skb_queue_empty(&priv->bypass_txq)) + continue; + + skb =3D skb_dequeue(&priv->bypass_txq); + tx_info =3D NXPWIFI_SKB_TXCB(skb); + + /* no aggregation for bypass packets */ + tx_param.next_pkt_len =3D 0; + + if (nxpwifi_process_tx(priv, skb, &tx_param) =3D=3D -EBUSY) { + skb_queue_head(&priv->bypass_txq, skb); + tx_info->flags |=3D NXPWIFI_BUF_FLAG_REQUEUED_PKT; + } else { + atomic_dec(&adapter->bypass_tx_pending); + } + } +} + +/* 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 !=3D NXPWIFI_SDIO) { + if (adapter->data_sent || + adapter->tx_lock_flag) + break; + } else { + if (atomic_read(&adapter->tx_queued) >=3D + 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..6241c2c5fcc4 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/wmm.h @@ -0,0 +1,77 @@ +/* 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 =3D (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + NXPWIFI_ACM =3D BIT(4), + NXPWIFI_ACI =3D (BIT(5) | BIT(6)), +}; + +enum ieee_types_wmm_ecw_bitmasks { + NXPWIFI_ECW_MIN =3D (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + NXPWIFI_ECW_MAX =3D (BIT(4) | BIT(5) | BIT(6) | BIT(7)), +}; + +extern const u16 nxpwifi_1d_to_wmm_queue[]; +extern const u8 tos_to_tid_inv[]; + +/* Retrieve 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 =3D 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_pri= vate + *priv, u8 tid, const u8 *ra_addr); +#endif /* !_NXPWIFI_WMM_H_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazon11011025.outbound.protection.outlook.com [52.101.65.25]) (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 B28AD423A7B; Wed, 4 Feb 2026 18:05:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.65.25 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228327; cv=fail; b=Zeklp2VZWo4iZomIGoU09NLb1ZPQoeQTUhfSu0YeLi4xpFxkM6nDZUH8DChNUeQgzsJC3G6AtNCAjb4R5mxbfQBlJIgrTmSaGjezuFuAOMd0gUi8yAxGEODTGhz5QHqi2iaCQ5gKE0BzsA9N4NmZtAV6/bAWEstcKiWIIzOyKOc= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228327; c=relaxed/simple; bh=vGbqBLW2XNZYcm1SQIR7Iihbzk4+yUVNHIhW0Y1ljPk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=u07njFyGvjJ20UXXWfLpo0kGkayN2qJDQBD8jCrsH6tixigJVeZY1yVBgET/q3uEU5l5WnF2scV0oPgLUm2Q/+7HyuLoXwHVFLPlVuIChlg1KCS1AsVPy6bDiSuvq9fz2M09U4Vumh+iWhkyPLysajQNpfa9b94ruqz3n/3IXFg= 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=Kg9XJe3H; arc=fail smtp.client-ip=52.101.65.25 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="Kg9XJe3H" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=wOPFlNoKyNf09/Z7rhYW8Q68qJvkRowW7d6IIsQUlj4AfSsc//SfnmOWSbQjqdKMMypahGmN+U+ZrveqJizyzFfVxsnziuYPT0DGbLlVHD2JY+MxNsYmZ3uoKCPw0lcvuPaSyyAr6GMHf8Nkw45U8ko7hvdPz+WlZgkhNmFvgdVAltwEFyTmj0M+nizY5fN+ZWv3z6o+PSlnWZKMmMPGCVCjtQj9zD0QBru4QLODn8j7bD1X8LUKuBJIP10WkSxJ/lvOvERAB7mk8mE4CUZMccncdQyupDiouAcL0TvQTQWv2BV70SnONoEPnKn2MbZ4QqZ+coUmlBaIs/Fjo7by2A== 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=8oe1woDDU98x9ArUlYcdFMYrGUPieU2d6VPYKI5ZCEw=; b=jouiCjyb3hk6Z7LKFSQ6cGf25v980frzsx0PW9xMFFg2Qk1gibNrC//cPQfecQhXi4ip5fxNPeQFWjgUVcKt2NUOEzvugHNlAF1sejLSiOt7i7hqaIrfOND6DEKSyDbtFdnPGbmUJTanTrqXUqC/LV2ud2Os9bo3w0U0HixiQfijnlJcU8DVY7Rewr5ZQmgX7MeguiWN833nvsG93u0yrH63iKbOE+wO2Knf79pU2ua6cdAP+z6l2SejXSaHBxUlUl3DJsnAQWYGz01NcTCpAZ096yM/CiQ1PPrnHHpkxBBsPe3FmZKgRL8/UTGWff4k8PZ6/YFTCY+qNk7ZxBNXWw== 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=8oe1woDDU98x9ArUlYcdFMYrGUPieU2d6VPYKI5ZCEw=; b=Kg9XJe3Hr+8UFBLnEVxaX6UOminUkgyqPt7mqfUgIKtK0beAVPqrI/fHVAObstvXELYjB0ulUbTy08NcEfRP8Fi/HkddM3SXbph0SepW9lPBtEFLsfWBkjZP+LiZT44cqRt02WjV1VgOyA2MNbb/6cbUxn25s3M73B9b9Ed9Q3DIJC77J588rMxCsqprvwBxu+ypTadUM3j9EJvL0ajjX1fIyEZeUYhHh93ul3JI+tcq1B0ZX6vfUhxT39v8EfLB63B9lXsX3nf9Mmra6LgO30kYN6kCYKxEuZvB1abCN9oWs0ErxUWGJbF3aB6FIdGt2UlU+u03xdR8esWbK16HJg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:25 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:25 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 06/21] wifi: nxpwifi: add scan support Date: Thu, 5 Feb 2026 02:03:43 +0800 Message-Id: <20260204180358.632281-7-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 473c9946-33fe-4180-6834-08de6417f87c X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?72qMbtTgD9wexCOzU+X4XpQAvyx7S669IGNFbtGf7iY+PXi8iB/WpOTT2BsO?= =?us-ascii?Q?cItqGBpYKOCEfXwRXNgwzc01CHKfQOk2YNwJct2r2uEWmOzs/DnwEG1itRPt?= =?us-ascii?Q?8Q8xUI2LoaAwIl+jWEHGOmLGMSaA4qYx4TvN8caqHep9Ji2KTm8gSGOxjwxS?= =?us-ascii?Q?EjboICykkFBlh2XirdU6XIRaDwniG2Q3b+dFNldiZhPrXfC87qR8le6aBskB?= =?us-ascii?Q?i07B9+OouxyPN97KT5Fj14hjunTaXYh0PShpEULuCAAMmBfIf3hF1XSoJadf?= =?us-ascii?Q?QLB8wAE1+wqLy6TYwLPwy2KOOAf+wU4iqcb4EnE5VNuQN9izyIaJkPwoRuho?= =?us-ascii?Q?U2Y5oCrekaomheyX9vDg4q5tqFeLjF3N/whqDS5w1916kUwPEocstnjrsP8Z?= =?us-ascii?Q?lm0lEkaXaEfpBI2MNwbQJXOWw926a8GvtFsm7Vlri3gu+nIEL7cDx0HGes8R?= =?us-ascii?Q?mcQcJGTni5iJMvU/X/Pp0/ybTg5Cky1IAzilLJ4+g1/ysnarWqJBQMzI8a8d?= =?us-ascii?Q?1yKuRs0icOggqPesrMrgpAD0WQsN9v9XRNOwtUkhRDoTYzDX1We2mbsCg3WB?= =?us-ascii?Q?jHlNVgPhbyaThIw7ManYkYuOi19FTTjh7wwelY0T6oHJXUJP2xrjGv/XA2Oa?= =?us-ascii?Q?nUrF26KAXlZh11cqa9rgcgbDREGxZh+Yvqm7zuG41Rj0YNAHKGmDMSgvY7kc?= =?us-ascii?Q?/HZ25wzq+xoS2HFIj4WVokmwZhjiHyPQarfigKICH130rdN2txqU2+0x7ici?= =?us-ascii?Q?00dZ00//vgbbwHgF50P88WpYvIBfbmcH7frLSWwPUT6gloFgEAigg0P2d5oL?= =?us-ascii?Q?cSdOvWe76RMS3vtb0FJvdN0Il36NDAgBhs0ZCEu3mDEZaeF2R75+ii0teyR3?= =?us-ascii?Q?qOr4/BXFnM/tYi7LRMfTzVKyMzbqQ6AIWSPTz8015CKYrsvQRerFu/8IRjKz?= =?us-ascii?Q?xm7/0aakO5hxlhc0oZ0lnndU+9iFT0JTaGtZVRlZYlrc2aVGcURTUnFg8cOi?= =?us-ascii?Q?UzDSfEJ0C6/dfBqFU4wLOk29Yndu+z2j0oFAJ3Pa3u2RrX/AuTbJ4I9KxJfu?= =?us-ascii?Q?O4gHMEdOqITWK7tnkdL/fXL/pyYV4vXNA0WteGBwU71UoaDcYHG4goSQf9IB?= =?us-ascii?Q?14Dsurm2qNQtkX/4VaqA078TnDhH2SWNmkyoGN8Uj/1DDZMxVmCOoduYe0IK?= =?us-ascii?Q?Trae3C+X7uoeqmLVrQ9ifrBkALGZFDWowc9GphvHFth6CSJ36XpTHb+Qm6hD?= =?us-ascii?Q?wCjZO0twuHCnIIyK700v+hotO4DeN9he1gJ8JZOlprnU7TR9HHqNJDr9jiwc?= =?us-ascii?Q?7kc8aA5EZOxxfjvknkZc9m8osKFHn24EiOOCb+WyfQWHmhNiV8d7+8/Yo+84?= =?us-ascii?Q?SBmEFmGLu9J9MYL2466uk+3RPTExwwSuHowRzp13iaN7HkWVvtJjLs3Kd4aN?= =?us-ascii?Q?ep7tpG8nFjekO5B0W9ZLoIVQmTlC0gam3YlWmUpcEhGY7Vh9xN2MgK9n6o0O?= =?us-ascii?Q?49FJE4CwYAm5+1+WF7WCcJnePkvfujuOmK5TgtV5lx87wrMB9JR0+WFT8qTL?= =?us-ascii?Q?TzN9669gj0rYoX9sTtz9u737RI+x6aSdqVddSF2DDYLBAa2iEsFBACl57c/Z?= =?us-ascii?Q?hxab+J0Z7h14Pc4b9hyXlTo=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?9MahXR7FnKmr1hUrduJPnOpkjoEvW3hpsrUyjReZoMn+ASIQPWS8eYmCxnMl?= =?us-ascii?Q?5cityDQImKNj8KXYMIw/PwAV/j1l41dZCMEt6dvYdvq5s5dbC7oo1s2vZCSg?= =?us-ascii?Q?ZTJ2CmZg7QMKJ4zTwqamG7DYH3z8EoU2fPg9iCEWIOpcdl1GKTpZny5SKH4c?= =?us-ascii?Q?PohEo2OUgKaEfsBsK97sGpcIsqu1TkvxI/M+3qFw8JMLM2iMBqw3CBQz2uwz?= =?us-ascii?Q?Ww4q3YYlP6MgjrZ1EMKJcuwlhSfnh6y5KdEbsVrhnhxb66wUu2cH1ap0q6dW?= =?us-ascii?Q?RgmDqD9JNt3dYRPzrfoFbFbSZedKe/JAco0ly3LOMoblwIlWNXu6hXmfkiNl?= =?us-ascii?Q?4iUD8gcqoguopQzdiREwVcBI8vNu+lWgRVe3V2yfWy5gyWcHCPNc6MSofhBR?= =?us-ascii?Q?f5nRx+/d/3xvJyCjRRzmLD2fZgUTFPF10gN9ExfDn70qZetq2NdJop/JAz3B?= =?us-ascii?Q?UCM605oy3xFHPrej8mJ8FX0/xM4y6gBl4bZ9Coi9nqX3RmkU8WK1yWBvfglz?= =?us-ascii?Q?otdDx/NHgd88+gkfhAX+dQy9qtLdDueFgQfypQOXUp+Z8SnFoe2MiOlJ/edt?= =?us-ascii?Q?NG2l/Db9YZIGTRqHFVgmHlEkTcTH7b/7oezNcHl5g8tv4gt6VU8kqBRKKiyF?= =?us-ascii?Q?Jp+SI/j09U5jiY9+S6e9/ZpmzZOle/mJVRJWf33Ke9ZZSf30ZprRE3LAf6Il?= =?us-ascii?Q?9BDhmNPVNSY9AoZAYAD52Dh0ppuEpEcUEDQc/9TU3I6fzQf5KCszpD/3oC0h?= =?us-ascii?Q?owyztkBNkxOqH4+07ct21IGTs6J46nMAE96erLSzo6peWNbBdLIRRsYkw6je?= =?us-ascii?Q?3eeQJrDpvUhyQzBs3uQPxkl/T5Y+FkR6CAgm+6MwTQamEB5g5rECMRUlF2f0?= =?us-ascii?Q?xAZ0tU5pYfOPr1jREE95r5ddYdCU59ET1ihk6ebOAMJ7dJdVh5qNKHLa0JU3?= =?us-ascii?Q?Cl8+/uSuC3wTwGhCF65dKUs38UlHq0eTzdXACwKYf91O1335xcibt3Ieo5xy?= =?us-ascii?Q?K40yRGl9+eDs7GcXjQC/Ogd+WKJc80a17oIMvGAXTWhSUQaTLiCAK3OY5/PJ?= =?us-ascii?Q?lv7EIiszAjVWAvN7VbKM9isrvIjgqMN2dOInjWylauoKOTcJkYWaltdzs/62?= =?us-ascii?Q?wbUcyXflTzT47GG7/MW7+EcnYGSygy8IY81eS4IQovJLfEKxJGAz0SCcnGLA?= =?us-ascii?Q?xOGU8I+DOjYee97sV+tPEPepLPT6147LIs3dbgKl/AR8zfqcvybsSiWO5/75?= =?us-ascii?Q?bN2TyXLyLrlydhxyPummBDxhTSi/f5ivRXKLcM6ra1tqMNsngvggerr4YxVi?= =?us-ascii?Q?LmOs4Zg44gf4m2DV7BoRDjd2hcggrY5WIxSbdEbYXOs1nriMO/44V3SqgaUr?= =?us-ascii?Q?2sZPHaSDo1VtfummqRgL3sPICpZflV0i2bW8W1DTuI9XGI1Cs2MYqh/sO3Md?= =?us-ascii?Q?N9siUmIRnhhGnHkUnYoDZq/d6oKUbC59qhBsQA0ooCKZSa7C99HfZd9n+bfR?= =?us-ascii?Q?llpjRwQurv63ku5VEJfAU44JalpFnc+1j3yTzNhoKU7aMOySp8n8ozs9RCkI?= =?us-ascii?Q?RpKdY/ewvIxr6XUyDryJ8Y/3NcS3dybCERixOGBkS48xVstmwZs8cXrYYWnJ?= =?us-ascii?Q?D2dzm7mBbwa896Cb03jmgO5nR1+60Ug6sbo0hXofsi/NOY1J9LoY5M6dh+EJ?= =?us-ascii?Q?50g+3NBDDYYmJr5lfViUAIwyMBfqVc5I5zvqCQrj08NFQbVB8CLPtxbvx9Fa?= =?us-ascii?Q?3OxdgnTzzw=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 473c9946-33fe-4180-6834-08de6417f87c X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:25.2527 (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: V3EnvVuHIvXBKEMcFqvauz5XAx2SH2jXP1PJb2MxJDIGFPZi3ewQUZNZY7dRRSonDKy7TgjQmxsKs5YUDO50/A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce full scan functionality to the nxpwifi driver, enabling active/passive scans, background scans, and extended scan handling. - Implements scan command construction with SSID/BSSID filters, probe count, channel list, and vendor-specific IEs. - Adds support for parsing scan responses and updating BSS descriptors, including WPA/WPA2/WMM/HT/VHT/HE capabilities. - Integrates with cfg80211 for scan result reporting and hidden SSID handling. - Supports background scan configuration and query, including RSSI thresholds, repeat count, and scan intervals. - Handles scan abort, scan completion, and scan queue management. This patch provides robust scanning capabilities for both STA and AP modes, ensuring compatibility with modern Wi-Fi standards and regulatory requirements. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/scan.c | 2765 +++++++++++++++++++++++ 1 file changed, 2765 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..0762788d1140 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/scan.c @@ -0,0 +1,2765 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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 "11ac.h" +#include "11ax.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_se= t) \ + + HOSTCMD_SUPPORTED_RATES) + +/* Memory needed to store a max number/size WildCard SSID TLV for a firmwa= re 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 ma= x */ +#define MAX_SCAN_CFG_ALLOC (sizeof(struct nxpwifi_scan_cmd_config) \ + + sizeof(struct nxpwifi_ie_types_num_probes) \ + + sizeof(struct nxpwifi_ie_types_htcap) \ + + sizeof(struct nxpwifi_ie_types_vhtcap) \ + + sizeof(struct nxpwifi_ie_types_he_cap) \ + + 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]; +}; + +#define NXPWIFI_WPA_CIPHER_SUITE_TKIP SUITE(WLAN_OUI_MICROSOFT, 2) +#define NXPWIFI_WPA_CIPHER_SUITE_CCMP SUITE(WLAN_OUI_MICROSOFT, 4) + +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=3D%#x wpa2_ie=3D%#x WEP=3D%s WPA=3D%s WPA2= =3D%s\tEncMode=3D%#x privacy=3D%#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, __func__, desc, priv, bss_desc) + +/* Parse a WPA/RSN element and check whether its PTK list contains the OUI= */ +static u8 +nxpwifi_search_oui_in_ie(struct ie_body *iebody, u8 *oui) +{ + u8 count; + + count =3D iebody->ptk_cnt[0]; + + /* + * PTK may contain multiple OUIs; iterate through the list and compare + * each one + */ + while (count) { + if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) + return NXPWIFI_OUI_PRESENT; + + --count; + if (count) + iebody =3D (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; +} + +/* Check whether the RSN IE is present and if its PTK list contains the OU= I */ +static u8 +nxpwifi_is_rsn_oui_present(struct nxpwifi_bssdescriptor *bss_desc, + u32 cipher) +{ + struct ie_body *iebody; + u8 ret =3D NXPWIFI_OUI_NOT_PRESENT; + __be32 oui =3D cpu_to_be32(cipher); + + if (bss_desc->bcn_rsn_ie) { + iebody =3D (struct ie_body *) + (((u8 *)bss_desc->bcn_rsn_ie->data) + + RSN_GTK_OUI_OFFSET); + ret =3D nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui); + if (ret) + return ret; + } + return ret; +} + +/* Check if the WPA IE exists and whether its PTK list contains the OUI */ +static u8 +nxpwifi_is_wpa_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cip= her) +{ + struct ie_body *iebody; + u8 ret =3D NXPWIFI_OUI_NOT_PRESENT; + __be32 oui =3D cpu_to_be32(cipher); + + if (bss_desc->bcn_wpa_ie) { + iebody =3D (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data + + WPA_GTK_OUI_OFFSET); + ret =3D nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui); + if (ret) + return ret; + } + return ret; +} + +/* Check whether both driver and BSS operate with no security */ +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 && + !bss_desc->bcn_rsn_ie && + !bss_desc->bcn_wpa_ie && + !priv->sec_info.encryption_mode && !bss_desc->privacy) { + return true; + } + return false; +} + +/* Check whether static WEP is enabled and the BSS privacy setting matches= */ +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; +} + +/* Check whether WPA is enabled and the BSS contains a WPA IE */ +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 && + bss_desc->bcn_wpa_ie) { + dbg_security_flags(INFO, "WPA", priv, bss_desc); + return true; + } + return false; +} + +/* Check whether WPA2 is enabled and the BSS includes an RSN IE */ +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 && + bss_desc->bcn_rsn_ie) { + /* + * Some APs (e.g., WRT54G) may omit the privacy bit even when + * using WPA2 + */ + dbg_security_flags(ERROR, "WPA2", priv, bss_desc); + return true; + } + return false; +} + +/* Check dynamic WEP: enabled in driver, privacy set, and no WPA/RSN IE pr= esent */ +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 && + !bss_desc->bcn_wpa_ie && + !bss_desc->bcn_rsn_ie && + priv->sec_info.encryption_mode && bss_desc->privacy) { + dbg_security_flags(INFO, "dynamic", priv, bss_desc); + return true; + } + return false; +} + +/* + * Check whether a scanned network is compatible with the driver's security + * configuration. The decision considers WEP, WPA, WPA2, privacy settings, + * and whether HT must be disabled when required (e.g., no AES). + * + * General rules: + * - Open networks: always compatible. + * - WPA-only: compatible; HT disabled if AES is not supported. + * - WPA2-only: compatible; HT disabled if AES is not supported. + * - Static WEP: compatible; HT disabled. + * - Dynamic WEP: compatible when privacy is enabled. + * + * Note: Compatibility is not enforced during roaming except for security = mode. + */ +static int +nxpwifi_is_network_compatible(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc, u32 mode) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + bss_desc->disable_11n =3D false; + + /* Skip compatibility checks while roaming */ + if (priv->media_connected && + priv->bss_mode =3D=3D NL80211_IFTYPE_STATION && + bss_desc->bss_mode =3D=3D 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 =3D=3D mode) { + if (nxpwifi_is_bss_no_sec(priv, bss_desc)) { + return 0; + } else if (nxpwifi_is_bss_static_wep(priv, bss_desc)) { + nxpwifi_dbg(adapter, INFO, + "info: Disable 11n in WEP mode.\n"); + bss_desc->disable_11n =3D true; + return 0; + } else if (nxpwifi_is_bss_wpa(priv, bss_desc)) { + if (((priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !nxpwifi_is_wpa_oui_present(bss_desc, + NXPWIFI_WPA_CIPHER_SUITE_CCMP)) { + if (nxpwifi_is_wpa_oui_present + (bss_desc, NXPWIFI_WPA_CIPHER_SUITE_TKIP)) { + nxpwifi_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n =3D true; + } else { + return -EINVAL; + } + } + return 0; + } else if (nxpwifi_is_bss_wpa2(priv, bss_desc)) { + if (((priv->config_bands & BAND_GN || + priv->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !nxpwifi_is_rsn_oui_present(bss_desc, + WLAN_CIPHER_SUITE_CCMP)) { + if (nxpwifi_is_rsn_oui_present + (bss_desc, WLAN_CIPHER_SUITE_TKIP)) { + nxpwifi_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n =3D true; + } else if (nxpwifi_is_rsn_oui_present + (bss_desc, WLAN_CIPHER_SUITE_GCMP_256) || + nxpwifi_is_rsn_oui_present + (bss_desc, WLAN_CIPHER_SUITE_CCMP_256)) { + return 0; + } else { + return -EINVAL; + } + } + return 0; + } else if (nxpwifi_is_bss_dynamic_wep(priv, bss_desc)) { + return 0; + } + + /* Security mismatch */ + dbg_security_flags(ERROR, "failed", priv, bss_desc); + return -EINVAL; + } + + return -EINVAL; +} + +/* + * Build the channel list for scanning based on region and band settings. + * Used when a scan request does not specify its own channel list. + */ +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 =3D priv->adapter; + int chan_idx =3D 0, i; + u16 scan_time =3D 0; + + if (user_scan_in) + scan_time =3D (u16)user_scan_in->chan_list[0].scan_time; + + for (band =3D 0; (band < NUM_NL80211_BANDS) ; band++) { + if (!priv->wdev.wiphy->bands[band]) + continue; + + sband =3D priv->wdev.wiphy->bands[band]; + + for (i =3D 0; (i < sband->n_channels) ; i++) { + ch =3D &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_chan_list[chan_idx].band_cfg =3D band; + + if (scan_time) + scan_chan_list[chan_idx].max_scan_time =3D + 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 =3D + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time =3D + cpu_to_le16(adapter->active_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bmap |=3D + (NXPWIFI_PASSIVE_SCAN | NXPWIFI_HIDDEN_SSID_REPORT); + else + scan_chan_list[chan_idx].chan_scan_mode_bmap &=3D + ~NXPWIFI_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_number =3D (u32)ch->hw_value; + scan_chan_list[chan_idx].chan_scan_mode_bmap |=3D + 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 =3D + cpu_to_le16(adapter->specific_scan_time); + + chan_idx++; + } + } + return chan_idx; +} + +/* + * Build the channel-list TLV for bgscan based on region and band settings. + */ +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 =3D priv->adapter; + int chan_idx =3D 0, i; + u16 scan_time =3D 0, specific_scan_time =3D adapter->specific_scan_time; + + if (bgscan_cfg_in) + scan_time =3D (u16)bgscan_cfg_in->chan_list[0].scan_time; + + for (band =3D 0; (band < NUM_NL80211_BANDS); band++) { + if (!priv->wdev.wiphy->bands[band]) + continue; + + sband =3D priv->wdev.wiphy->bands[band]; + + for (i =3D 0; (i < sband->n_channels) ; i++) { + ch =3D &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_chan_list[chan_idx].band_cfg =3D band; + + if (scan_time) + scan_chan_list[chan_idx].max_scan_time =3D + cpu_to_le16(scan_time); + else if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].max_scan_time =3D + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time =3D + cpu_to_le16(specific_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bmap |=3D + NXPWIFI_PASSIVE_SCAN; + else + scan_chan_list[chan_idx].chan_scan_mode_bmap &=3D + ~NXPWIFI_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_number =3D (u32)ch->hw_value; + chan_idx++; + } + } + return chan_idx; +} + +/* Append the rate TLV to the scan configuration 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 =3D (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; + + if (priv->scan_request) + rates_size =3D nxpwifi_get_rates_from_cfg80211(priv, rates, + radio); + else + rates_size =3D nxpwifi_get_supported_rates(priv, rates); + + nxpwifi_dbg(priv->adapter, CMD, + "info: SCAN_CMD: Rates size =3D %d\n", + rates_size); + rates_tlv =3D (struct nxpwifi_ie_types_rates_param_set *)tlv_pos; + rates_tlv->header.type =3D cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len =3D cpu_to_le16((u16)rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + scan_cfg_out->tlv_buf_len +=3D sizeof(rates_tlv->header) + rates_size; + + return rates_size; +} + +/* + * Build and send multiple scan commands by chunking channel TLVs per scan + * limit. + */ +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 =3D priv->adapter; + int ret =3D 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 =3D cpu_to_le16(TLV_TYPE_CHANLIST); + + tmp_chan_list =3D scan_chan_list; + + /* + * Iterate through the channel list and send a firmware scan command for + * each group of max_chan_per_scan channels, or individually for + * channels 1, 6, and 11 when configured. + */ + while (tmp_chan_list->chan_number) { + tlv_idx =3D 0; + total_scan_time =3D 0; + radio_type =3D 0; + tlv_o->header.len =3D 0; + done_early =3D false; + + /* + * Build the channel TLV for the scan command. Continue adding + * channel TLVs until one of the following conditions is met: + * - tlv_idx reaches the maximum allowed per scan command + * - the next channel is 0 (end of the desired channel list) + * - done_early is set (used for per-channel scanning of 1, 6, + * and 11) + */ + while (tlv_idx < max_chan_per_scan && + tmp_chan_list->chan_number && !done_early) { + if (tmp_chan_list->chan_number =3D=3D priv->csa_chan) { + tmp_chan_list++; + continue; + } + + radio_type =3D tmp_chan_list->band_cfg; + nxpwifi_dbg(priv->adapter, INFO, + "info: Scan: Chan(%3d), Band(%d),\t" + "Mode(%d, %d), Dur(%d)\n", + tmp_chan_list->chan_number, + tmp_chan_list->band_cfg, + 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 into 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 =3D + (u32)((u8 *)tlv_o - scan_cfg_out->tlv_buf); + + scan_cfg_out->tlv_buf_len +=3D + (sizeof(tlv_o->header) + + le16_to_cpu(tlv_o->header.len)); + + /* Advance the index for the channel TLV being constructed. */ + tlv_idx++; + + /* Count the total scan time per command */ + total_scan_time +=3D + le16_to_cpu(tmp_chan_list->max_scan_time); + + done_early =3D false; + + /* + * Stop the loop if the current channel is one of 1, 6, + * or 11 and no SSID or BSSID filter is applied. + */ + if (!filtered_scan && + (tmp_chan_list->chan_number =3D=3D 1 || + tmp_chan_list->chan_number =3D=3D 6 || + tmp_chan_list->chan_number =3D=3D 11)) + done_early =3D true; + + /* Advance the tmp pointer to the next channel to be scanned. */ + tmp_chan_list++; + + /* + * Stop the loop if the next channel is one of 1, 6, + * or 11. This causes that channel to be scanned alone + * in the next iteration. + */ + if (!filtered_scan && + (tmp_chan_list->chan_number =3D=3D 1 || + tmp_chan_list->chan_number =3D=3D 6 || + tmp_chan_list->chan_number =3D=3D 11)) + done_early =3D true; + } + + /* Ensure the total scan time does not exceed the scan-command timeout. = */ + 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 =3D -EINVAL; + break; + } + + rates_size =3D nxpwifi_append_rate_tlv(priv, scan_cfg_out, + radio_type); + + if (priv->adapter->ext_scan) + cmd_no =3D HOST_CMD_802_11_SCAN_EXT; + else + cmd_no =3D HOST_CMD_802_11_SCAN; + + ret =3D nxpwifi_send_cmd(priv, cmd_no, HOST_ACT_GEN_SET, + 0, scan_cfg_out, false); + + /* + * The rate element is updated for each scan command, but the + * same starting pointer is reused, so the previous rate element + * in scan_cfg_out->buf is overwritten. + */ + scan_cfg_out->tlv_buf_len -=3D + sizeof(struct nxpwifi_ie_types_header) + rates_size; + + if (ret) { + nxpwifi_cancel_pending_scan_cmd(adapter); + break; + } + } + + return ret; +} + +/* + * Build final scan config from user params, disabling missing filters and= using + * defaults. + */ +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 =3D 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; + struct nxpwifi_ie_types_extcap *ext_cap; + u8 *ext_capab; + 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, vsid; + u8 ssid_filter; + struct nxpwifi_ie_types_htcap *ht_cap; + struct nxpwifi_ie_types_bss_mode *bss_mode; + struct nxpwifi_ie_types_vhtcap *vht_cap; + struct nxpwifi_ie_types_he_cap *he_cap; + + /* + * tlv_buf_len is recalculated for each scan command. TLVs added in this + * routine are preserved because the send routine appends channel TLVs + * at chan_list_out. The difference between chan_list_out and the start + * of the TLV buffer determines the size of the TLVs added here. + */ + scan_cfg_out->tlv_buf_len =3D 0; + + /* + * Running TLV pointer. It is assigned to chan_list_out at the end of + * the function so later routines know where channel TLVs can be + * appended in the command buffer. + */ + tlv_pos =3D 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 =3D 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 =3D 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 =3D true; + + /* + * Set the BSS type scan filter, use Adapter setting if + * unset + */ + scan_cfg_out->bss_mode =3D + (u8)(user_scan_in->bss_mode ?: adapter->scan_mode); + + /* + * Set the number of probes to send, use Adapter setting + * if unset + */ + num_probes =3D 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 =3D + (struct nxpwifi_ie_types_bssid_list *)tlv_pos; + bssid_tlv->header.type =3D cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len =3D cpu_to_le16(ETH_ALEN); + memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, + ETH_ALEN); + tlv_pos +=3D sizeof(struct nxpwifi_ie_types_bssid_list); + } + + for (i =3D 0; i < user_scan_in->num_ssids; i++) { + ssid_len =3D user_scan_in->ssid_list[i].ssid_len; + + wildcard_ssid_tlv =3D + (struct nxpwifi_ie_types_wildcard_ssid_params *) + tlv_pos; + wildcard_ssid_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len =3D + cpu_to_le16((u16)(ssid_len + sizeof(u8))); + + /* + * max_ssid_length =3D 0 tells firmware to perform + * specific scan for the SSID filled, whereas + * max_ssid_length =3D IEEE80211_MAX_SSID_LEN is for + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length =3D 0; + else + wildcard_ssid_tlv->max_ssid_length =3D + IEEE80211_MAX_SSID_LEN; + + if (!memcmp(user_scan_in->ssid_list[i].ssid, + "DIRECT-", 7)) + wildcard_ssid_tlv->max_ssid_length =3D 0xfe; + + memcpy(wildcard_ssid_tlv->ssid, + user_scan_in->ssid_list[i].ssid, ssid_len); + + tlv_pos +=3D (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 =3D=3D 32), therefore do + * not treat the scan as + * filtered. + */ + if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) + ssid_filter =3D 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 =3D true; + + if (user_scan_in->scan_chan_gap) { + nxpwifi_dbg(adapter, INFO, + "info: scan: channel gap =3D %d\n", + user_scan_in->scan_chan_gap); + *max_chan_per_scan =3D + NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN; + + chan_gap_tlv =3D (void *)tlv_pos; + chan_gap_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + chan_gap_tlv->header.len =3D + cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); + chan_gap_tlv->chan_gap =3D + cpu_to_le16((user_scan_in->scan_chan_gap)); + tlv_pos +=3D + sizeof(struct nxpwifi_ie_types_scan_chan_gap); + } + + if (!is_zero_ether_addr(user_scan_in->random_mac)) { + random_mac_tlv =3D (void *)tlv_pos; + random_mac_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_RANDOM_MAC); + random_mac_tlv->header.len =3D + cpu_to_le16(sizeof(random_mac_tlv->mac)); + ether_addr_copy(random_mac_tlv->mac, + user_scan_in->random_mac); + tlv_pos +=3D + sizeof(struct nxpwifi_ie_types_random_mac); + } + } else { + scan_cfg_out->bss_mode =3D (u8)adapter->scan_mode; + num_probes =3D 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 =3D NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN; + } else { + if (!priv->media_connected) + *max_chan_per_scan =3D NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD; + else + *max_chan_per_scan =3D + NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD / 2; + } + + if (adapter->ext_scan) { + bss_mode =3D (struct nxpwifi_ie_types_bss_mode *)tlv_pos; + bss_mode->header.type =3D cpu_to_le16(TLV_TYPE_BSS_MODE); + bss_mode->header.len =3D cpu_to_le16(sizeof(bss_mode->bss_mode)); + bss_mode->bss_mode =3D scan_cfg_out->bss_mode; + tlv_pos +=3D 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 =3D %d\n", + num_probes); + + num_probes_tlv =3D (struct nxpwifi_ie_types_num_probes *)tlv_pos; + num_probes_tlv->header.type =3D cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len =3D + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes =3D cpu_to_le16((u16)num_probes); + + tlv_pos +=3D 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 =3D (struct nxpwifi_ie_types_htcap *)tlv_pos; + memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap)); + ht_cap->header.type =3D cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len =3D + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + radio_type =3D + nxpwifi_band_to_radio_type(priv->config_bands); + nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + tlv_pos +=3D sizeof(struct nxpwifi_ie_types_htcap); + } + + if (ISSUPP_11ACENABLED(adapter->fw_cap_info) && + (priv->config_bands & BAND_AAC)) { + vht_cap =3D (struct nxpwifi_ie_types_vhtcap *)tlv_pos; + memset(vht_cap, 0, sizeof(struct nxpwifi_ie_types_vhtcap)); + vht_cap->header.type =3D cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_cap->header.len =3D cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + nxpwifi_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, priv->config_bands); + tlv_pos +=3D sizeof(*vht_cap); + } + + if (ISSUPP_11AXENABLED(adapter->fw_cap_ext) && + (priv->config_bands & BAND_GAX || + priv->config_bands & BAND_AAX)) { + he_cap =3D (struct nxpwifi_ie_types_he_cap *)tlv_pos; + memset(he_cap, 0, sizeof(struct nxpwifi_ie_types_he_cap)); + tlv_pos +=3D nxpwifi_fill_he_cap_tlv(priv, he_cap, priv->config_bands); + } + + if (nxpwifi_is_sta_11ax_twt_req_supported(priv)) { + for (vsid =3D 0; vsid < NXPWIFI_MAX_VSIE_NUM; vsid++) { + if (priv->vs_ie[vsid].mask & NXPWIFI_VSIE_MASK_SCAN) { + ext_capab =3D (u8 *)cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, + priv->vs_ie[vsid].ie, + sizeof(priv->vs_ie[vsid].ie)); + break; + } + } + + if (ext_capab) { + ext_capab +=3D 2; + } else { + ext_cap =3D (struct nxpwifi_ie_types_extcap *)tlv_pos; + memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap) + + NXPWIFI_EXT_CAPAB_IE_LEN); + ext_cap->header.type =3D cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + ext_cap->header.len =3D cpu_to_le16(NXPWIFI_EXT_CAPAB_IE_LEN); + ext_capab =3D ext_cap->ext_capab; + tlv_pos +=3D sizeof(struct nxpwifi_ie_types_extcap) + + le16_to_cpu(ext_cap->header.len); + } + + ext_capab[9] |=3D WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT; + } + + /* Append vendor specific element TLV */ + nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_SCAN, &tlv_pos); + + /* + * Set the channel TLV output pointer to the end of the newly added TLVs + * (SSID, num_probes). Channel TLVs for each scan will be appended after + * these, preserving previously added TLVs. + */ + *chan_list_out =3D + (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 =3D 0; + chan_idx < NXPWIFI_USER_SCAN_CHAN_MAX && + user_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + channel =3D user_scan_in->chan_list[chan_idx].chan_number; + scan_chan_list[chan_idx].chan_number =3D channel; + + radio_type =3D + user_scan_in->chan_list[chan_idx].radio_type; + scan_chan_list[chan_idx].band_cfg =3D radio_type; + + scan_type =3D user_scan_in->chan_list[chan_idx].scan_type; + + if (scan_type =3D=3D NXPWIFI_SCAN_TYPE_PASSIVE) + scan_chan_list[chan_idx].chan_scan_mode_bmap |=3D + (NXPWIFI_PASSIVE_SCAN | + NXPWIFI_HIDDEN_SSID_REPORT); + else + scan_chan_list[chan_idx].chan_scan_mode_bmap &=3D + ~NXPWIFI_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_scan_mode_bmap |=3D + NXPWIFI_DISABLE_CHAN_FILT; + + scan_time =3D user_scan_in->chan_list[chan_idx].scan_time; + + if (scan_time) { + scan_dur =3D (u16)scan_time; + } else { + if (scan_type =3D=3D NXPWIFI_SCAN_TYPE_PASSIVE) + scan_dur =3D adapter->passive_scan_time; + else if (*filtered_scan) + scan_dur =3D adapter->specific_scan_time; + else + scan_dur =3D adapter->active_scan_time; + } + + scan_chan_list[chan_idx].min_scan_time =3D + cpu_to_le16(scan_dur); + scan_chan_list[chan_idx].max_scan_time =3D + cpu_to_le16(scan_dur); + } + + /* Check if we are only scanning the current channel */ + if (chan_idx =3D=3D 1 && + user_scan_in->chan_list[0].chan_number =3D=3D + priv->curr_bss_params.bss_descriptor.channel) { + *scan_current_only =3D 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); + } +} + +/* + * Parse the scan response buffer for expected TLV pointers. + * TLVs may be appended after the BSS info and can be returned if found. + */ +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 =3D tlv; + tlv_buf_left =3D tlv_buf_size; + *tlv_data =3D NULL; + + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: tlv_buf_size =3D %d\n", + tlv_buf_size); + + while (tlv_buf_left >=3D sizeof(struct nxpwifi_ie_types_header)) { + tlv_type =3D le16_to_cpu(current_tlv->header.type); + tlv_len =3D 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 =3D=3D tlv_type) { + switch (tlv_type) { + case TLV_TYPE_TSFTIMESTAMP: + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: TSF\t" + "timestamp TLV, len =3D %d\n", + tlv_len); + *tlv_data =3D current_tlv; + break; + case TLV_TYPE_CHANNELBANDLIST: + nxpwifi_dbg(adapter, INFO, + "info: SCAN_RESP: channel\t" + "band list TLV, len =3D %d\n", + tlv_len); + *tlv_data =3D current_tlv; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "SCAN_RESP: unhandled TLV =3D %d\n", + tlv_type); + /* Give up, this seems corrupted */ + return; + } + } + + if (*tlv_data) + break; + + tlv_buf_left -=3D (sizeof(tlv->header) + tlv_len); + current_tlv =3D + (struct nxpwifi_ie_types_data *)(current_tlv->data + + tlv_len); + } /* while */ +} + +/* Parse the beacon buffer and update the BSS descriptor fields. */ +int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter, + struct nxpwifi_bssdescriptor *bss_entry) +{ + u8 element_id; + u16 elem_size =3D 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] =3D { 0x00, 0x50, 0xf2, 0x01 }; + const u8 wmm_oui[4] =3D { 0x00, 0x50, 0xf2, 0x02 }; + struct element *elem; + + found_data_rate_ie =3D false; + rate_size =3D 0; + current_ptr =3D bss_entry->beacon_buf; + bytes_left =3D bss_entry->beacon_buf_size; + + /* Process variable element */ + while (bytes_left >=3D 2) { + element_id =3D *current_ptr; + element_len =3D *(current_ptr + 1); + total_ie_len =3D element_len + elem_size; + + if (bytes_left < total_ie_len) { + nxpwifi_dbg(adapter, ERROR, + "err: InterpretIE: in processing\t" + "element, bytes left < element 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 =3D 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 =3D element_len; + found_data_rate_ie =3D true; + break; + + case WLAN_EID_FH_PARAMS: + if (total_ie_len < sizeof(*fh_param_set)) + return -EINVAL; + fh_param_set =3D + (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 =3D + (struct ieee_types_ds_param_set *)current_ptr; + + bss_entry->channel =3D 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 =3D + (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 =3D *(current_ptr + 2); + break; + + case WLAN_EID_PWR_CONSTRAINT: + if (!element_len) + return -EINVAL; + bss_entry->local_constraint =3D *(current_ptr + 2); + bss_entry->sensed_11h =3D true; + break; + + case WLAN_EID_CHANNEL_SWITCH: + bss_entry->chan_sw_ie_present =3D true; + fallthrough; + case WLAN_EID_PWR_CAPABILITY: + case WLAN_EID_TPC_REPORT: + case WLAN_EID_QUIET: + bss_entry->sensed_11h =3D true; + break; + + case WLAN_EID_EXT_SUPP_RATES: + /* + * Only process extended supported rate + * if data rate is already found. + * Data rate element should come before + * extended supported rate element + */ + if (found_data_rate_ie) { + if ((element_len + rate_size) > + NXPWIFI_SUPPORTED_RATES) + bytes_to_copy =3D + (NXPWIFI_SUPPORTED_RATES - + rate_size); + else + bytes_to_copy =3D element_len; + + rate =3D (u8 *)bss_entry->data_rates; + rate +=3D rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + + rate =3D (u8 *)bss_entry->supported_rates; + rate +=3D rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + } + break; + + case WLAN_EID_VENDOR_SPECIFIC: + vendor_ie =3D (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 =3D + (struct ieee_types_vendor_specific *) + current_ptr; + bss_entry->wpa_offset =3D + (u16)(current_ptr - + bss_entry->beacon_buf); + } else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui))) { + if (total_ie_len =3D=3D + sizeof(struct ieee80211_wmm_param_ie) || + total_ie_len =3D=3D + sizeof(struct ieee_types_wmm_info)) + /* + * Only accept and copy the WMM element if + * it matches the size expected for the + * WMM Info element or the WMM Parameter element. + */ + memcpy((u8 *)&bss_entry->wmm_ie, + current_ptr, total_ie_len); + } + break; + case WLAN_EID_RSN: + bss_entry->bcn_rsn_ie =3D + (struct element *)current_ptr; + bss_entry->rsn_offset =3D + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_RSNX: + bss_entry->bcn_rsnx_ie =3D + (struct element *)current_ptr; + bss_entry->rsnx_offset =3D + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_HT_CAPABILITY: + bss_entry->bcn_ht_cap =3D + (struct ieee80211_ht_cap *)(current_ptr + + elem_size); + bss_entry->ht_cap_offset =3D + (u16)(current_ptr + elem_size - + bss_entry->beacon_buf); + break; + case WLAN_EID_HT_OPERATION: + bss_entry->bcn_ht_oper =3D + (struct ieee80211_ht_operation *)(current_ptr + + elem_size); + bss_entry->ht_info_offset =3D + (u16)(current_ptr + elem_size - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_CAPABILITY: + bss_entry->disable_11ac =3D false; + bss_entry->bcn_vht_cap =3D (void *)(current_ptr + + elem_size); + bss_entry->vht_cap_offset =3D + (u16)((u8 *)bss_entry->bcn_vht_cap - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_OPERATION: + bss_entry->bcn_vht_oper =3D + (void *)(current_ptr + elem_size); + bss_entry->vht_info_offset =3D + (u16)((u8 *)bss_entry->bcn_vht_oper - + bss_entry->beacon_buf); + break; + case WLAN_EID_BSS_COEX_2040: + bss_entry->bcn_bss_co_2040 =3D current_ptr; + bss_entry->bss_co_2040_offset =3D + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_EXT_CAPABILITY: + bss_entry->bcn_ext_cap =3D current_ptr; + bss_entry->ext_cap_offset =3D + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_OPMODE_NOTIF: + bss_entry->oper_mode =3D (void *)current_ptr; + bss_entry->oper_mode_offset =3D + (u16)(current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_EXTENSION: + elem =3D (struct element *)current_ptr; + + switch (elem->data[0]) { + case WLAN_EID_EXT_HE_CAPABILITY: + bss_entry->disable_11ax =3D false; + bss_entry->bcn_he_cap =3D + (void *)(current_ptr + elem_size + 1); + bss_entry->he_cap_offset =3D + (u16)((u8 *)bss_entry->bcn_he_cap - + bss_entry->beacon_buf); + break; + case WLAN_EID_EXT_HE_OPERATION: + bss_entry->bcn_he_oper =3D + (void *)(current_ptr + elem_size + 1); + bss_entry->he_info_offset =3D + (u16)((u8 *)bss_entry->bcn_he_oper - + bss_entry->beacon_buf); + break; + default: + break; + } + break; + default: + break; + } + + current_ptr +=3D total_ie_len; + bytes_left -=3D total_ie_len; + + } /* while (bytes_left > 2) */ + return 0; +} + +/* Convert the radio-type scan parameter to the join command's band config= . */ +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; + } +} + +/* Internal helper to start a scan using the given configuration. */ +int nxpwifi_scan_networks(struct nxpwifi_private *priv, + const struct nxpwifi_user_scan_cfg *user_scan_in) +{ + int ret; + struct nxpwifi_adapter *adapter =3D 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 =3D true; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + scan_cfg_out =3D kzalloc(sizeof(union nxpwifi_scan_cmd_config_tlv), + GFP_KERNEL); + if (!scan_cfg_out) { + ret =3D -ENOMEM; + goto done; + } + + scan_chan_list =3D kcalloc(NXPWIFI_USER_SCAN_CHAN_MAX, + sizeof(struct nxpwifi_chan_scan_param_set), + GFP_KERNEL); + if (!scan_chan_list) { + kfree(scan_cfg_out); + ret =3D -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 =3D 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 =3D 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 =3D false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + } + return ret; +} + +/* + * Build the firmware scan command from the given configuration, including + * fixed fields and TLVs, and set the command ID, size, and endianness. + */ +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 =3D &cmd->params.scan; + + /* Set fixed field variables in scan command */ + scan_cmd->bss_mode =3D 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 =3D cpu_to_le16(HOST_CMD_802_11_SCAN); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size =3D cpu_to_le16((u16)(sizeof(scan_cmd->bss_mode) + + sizeof(scan_cmd->bssid) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* Check compatibility of the requested network with current driver settin= gs. */ +int nxpwifi_check_network_compatibility(struct nxpwifi_private *priv, + struct nxpwifi_bssdescriptor *bss_desc) +{ + int ret =3D 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 =3D nxpwifi_is_network_compatible(priv, bss_desc, + priv->bss_mode); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "Incompatible network settings\n"); + break; + default: + ret =3D 0; + } + } + + return ret; +} + +/* Check if the SSID length is zero or all bytes are zero. */ +static bool nxpwifi_is_hidden_ssid(struct cfg80211_ssid *ssid) +{ + int idx; + + for (idx =3D 0; idx < ssid->ssid_len; idx++) { + if (ssid->ssid[idx]) + return false; + } + + return true; +} + +/* Find hidden SSIDs on passive channels and save those channels for activ= e 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 =3D kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret =3D 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 =3D 0 ; chid < NXPWIFI_USER_SCAN_CHAN_MAX; chid++) { + if (priv->hidden_chan[chid].chan_number =3D=3D + bss->channel->hw_value) + break; + + if (!priv->hidden_chan[chid].chan_number) { + priv->hidden_chan[chid].chan_number =3D + bss->channel->hw_value; + priv->hidden_chan[chid].radio_type =3D + bss->channel->band; + priv->hidden_chan[chid].scan_type =3D + NXPWIFI_SCAN_TYPE_ACTIVE; + break; + } + } + } + +done: + /* Free beacon_ie allocated by nxpwifi_fill_new_bss_desc(). */ + 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 =3D kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret =3D nxpwifi_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + ret =3D 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)); + + /* 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: + /* Free beacon_ie allocated by nxpwifi_fill_new_bss_desc(). */ + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return ret; +} + +static int +nxpwifi_parse_single_response_buf(struct nxpwifi_private *priv, u8 **bss_i= nfo, + u32 *bytes_left, u64 fw_tsf, u8 *radio_type, + bool ext_scan, s32 rssi_val) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D 0; + u16 beacon_size =3D 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 >=3D sizeof(beacon_size)) { + /* Extract & convert beacon size from command buffer */ + beacon_size =3D get_unaligned_le16((*bss_info)); + *bytes_left -=3D sizeof(beacon_size); + *bss_info +=3D sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + *bss_info +=3D *bytes_left; + *bytes_left =3D 0; + return -EINVAL; + } + + /* + * Initialize the current working beacon pointer for this BSS + * iteration + */ + current_ptr =3D *bss_info; + + /* Advance the return beacon pointer past the current beacon */ + *bss_info +=3D beacon_size; + *bytes_left -=3D beacon_size; + + curr_bcn_bytes =3D 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 +=3D ETH_ALEN; + curr_bcn_bytes -=3D ETH_ALEN; + + if (!ext_scan) { + rssi =3D (s32)*current_ptr; + rssi =3D (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr +=3D sizeof(u8); + curr_bcn_bytes -=3D sizeof(u8); + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: RSSI=3D%d\n", rssi); + } else { + rssi =3D rssi_val; + } + + bcn_param =3D (struct nxpwifi_fixed_bcn_param *)current_ptr; + current_ptr +=3D sizeof(*bcn_param); + curr_bcn_bytes -=3D sizeof(*bcn_param); + + timestamp =3D le64_to_cpu(bcn_param->timestamp); + beacon_period =3D le16_to_cpu(bcn_param->beacon_period); + + cap_info_bitmap =3D le16_to_cpu(bcn_param->cap_info_bitmap); + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: capabilities=3D0x%X\n", + cap_info_bitmap); + + /* Rest of the current buffer are element's */ + ie_buf =3D current_ptr; + ie_len =3D curr_bcn_bytes; + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: IELength for this AP =3D %d\n", + curr_bcn_bytes); + + while (curr_bcn_bytes >=3D sizeof(struct element)) { + u8 element_id, element_len; + + element_id =3D *current_ptr; + element_len =3D *(current_ptr + 1); + if (curr_bcn_bytes < element_len + + sizeof(struct element)) { + nxpwifi_dbg(adapter, ERROR, + "%s: bytes left < element length\n", __func__); + return -EFAULT; + } + if (element_id =3D=3D WLAN_EID_DS_PARAMS) { + channel =3D *(current_ptr + + sizeof(struct element)); + break; + } + + current_ptr +=3D element_len + sizeof(struct element); + curr_bcn_bytes -=3D 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 =3D=3D priv->csa_chan) { + nxpwifi_dbg(adapter, WARN, + "Dropping entry on csa closed channel\n"); + return 0; + } + + band =3D BAND_G; + if (radio_type) + band =3D nxpwifi_radio_type_to_band(*radio_type & + (BIT(0) | BIT(1))); + + cfp =3D nxpwifi_get_cfp(priv, band, channel, 0); + + freq =3D cfp ? cfp->freq : 0; + + chan =3D ieee80211_get_channel(priv->wdev.wiphy, freq); + + if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { + bss =3D 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 =3D (struct nxpwifi_bss_priv *)bss->priv; + bss_priv->band =3D band; + bss_priv->fw_tsf =3D fw_tsf; + bss_desc =3D + &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 element\n"); + } + + return 0; +} + +static void nxpwifi_complete_scan(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + adapter->survey_idx =3D 0; + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status =3D 0; + if (!priv->scan_request) { + nxpwifi_dbg(adapter, INFO, + "complete internal scan\n"); + nxpwifi_complete_cmd(adapter, adapter->curr_cmd); + } + } +} + +/* Find hidden SSIDs on passive channels and run active scans on them. */ +static int +nxpwifi_active_scan_req_for_passive_chan(struct nxpwifi_private *priv) +{ + int ret; + struct nxpwifi_adapter *adapter =3D priv->adapter; + u8 id =3D 0; + struct nxpwifi_user_scan_cfg *user_scan_cfg; + + if (adapter->active_scan_triggered || !priv->scan_request || + priv->scan_aborting) { + adapter->active_scan_triggered =3D false; + return 0; + } + + if (!priv->hidden_chan[0].chan_number) { + nxpwifi_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channel= s\n"); + return 0; + } + user_scan_cfg =3D kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + + if (!user_scan_cfg) + return -ENOMEM; + + for (id =3D 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 =3D 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 =3D priv->scan_request->n_ssids; + user_scan_cfg->ssid_list =3D priv->scan_request->ssids; + + ret =3D nxpwifi_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + + memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); + + if (ret) + nxpwifi_dbg(adapter, ERROR, "scan failed: %d\n", ret); + + return ret; +} + +static void nxpwifi_check_next_scan_command(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D 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 =3D { + .aborted =3D false, + }; + + nxpwifi_dbg(adapter, INFO, + "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request =3D NULL; + priv->scan_aborting =3D false; + } else { + priv->scan_aborting =3D 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 =3D false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + if (!adapter->active_scan_triggered) { + if (priv->scan_request) { + struct cfg80211_scan_info info =3D { + .aborted =3D true, + }; + + nxpwifi_dbg(adapter, INFO, + "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request =3D NULL; + priv->scan_aborting =3D false; + } else { + priv->scan_aborting =3D false; + nxpwifi_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } + } else { + /* Move a scan command from scan_pending_q to cmd_pending_q. */ + cmd_node =3D 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 =3D false; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + if (priv->scan_request) { + struct cfg80211_scan_info info =3D { + .aborted =3D true, + }; + + nxpwifi_dbg(adapter, INFO, + "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request =3D NULL; + priv->scan_aborting =3D false; + } + } + } +} + +/* + * Handle the scan command response. + * + * The scan response buffer has the following layout: + * + * ------------------------------------------------------------- + * | Header (4 * t_u16): standard command response header | + * ------------------------------------------------------------- + * | BufSize (t_u16): size of the BSS description data | + * ------------------------------------------------------------- + * | NumOfSet (t_u8): number of returned BSS descriptions | + * ------------------------------------------------------------- + * | BSS description data (variable, size =3D BufSize) | + * ------------------------------------------------------------- + * | TLV data (variable, size =3D cmd_size - fixed fields) | + * ------------------------------------------------------------- + */ +int nxpwifi_ret_802_11_scan(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + int ret =3D 0; + struct nxpwifi_adapter *adapter =3D 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 =3D 0; + u8 *radio_type; + struct cfg80211_wowlan_nd_match *pmatch; + struct cfg80211_sched_scan_request *nd_config =3D NULL; + + is_bgscan_resp =3D (le16_to_cpu(resp->command) + =3D=3D HOST_CMD_802_11_BG_SCAN_QUERY); + if (is_bgscan_resp) + scan_rsp =3D &resp->params.bg_scan_query_resp.scan_resp; + else + scan_rsp =3D &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 =3D -EINVAL; + goto check_next_scan; + } + + /* Check csa channel expiry before parsing scan response */ + nxpwifi_11h_get_csa_closed_channel(priv); + + bytes_left =3D 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 =3D 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 =3D scan_rsp->bss_desc_and_tlv_buffer; + + /* + * TLV buffer size =3D scan_resp_size minus the fixed fields, BSS + * description data, and the command response header (S_DS_GEN). + */ + tlv_buf_size =3D scan_resp_size - (bytes_left + + sizeof(scan_rsp->bss_descript_size) + + sizeof(scan_rsp->number_of_sets) + + S_DS_GEN); + + tlv_data =3D (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 =3D priv->wdev.wiphy->wowlan_config->nd_config; +#endif + + if (nd_config) { + adapter->nd_info =3D + kzalloc(struct_size(adapter->nd_info, matches, + scan_rsp->number_of_sets), + GFP_ATOMIC); + + if (adapter->nd_info) + adapter->nd_info->n_matches =3D scan_rsp->number_of_sets; + } + + for (idx =3D 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { + /* + * If a TSF TLV is present, save its TSF value in fw_tsf. This + * is the firmware TSF 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 =3D &chan_band_tlv->chan_band_param[idx]; + radio_type =3D &chan_band->radio_type; + } else { + radio_type =3D NULL; + } + + if (chan_band_tlv && adapter->nd_info) { + adapter->nd_info->matches[idx] =3D + kzalloc(sizeof(*pmatch) + sizeof(u32), + GFP_ATOMIC); + + pmatch =3D adapter->nd_info->matches[idx]; + + if (pmatch) { + pmatch->n_channels =3D 1; + pmatch->channels[0] =3D chan_band->chan_number; + } + } + + ret =3D 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; +} + +/* + * Prepare the extended scan command using the provided scan configuration + * and build the structure to be sent 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 =3D &cmd->params.ext_scan; + struct nxpwifi_scan_cmd_config *scan_cfg =3D data_buf; + + memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size =3D cpu_to_le16((u16)(sizeof(ext_scan->reserved) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* Prepare the background scan config command to send to 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 =3D + &cmd->params.bg_scan_config; + struct nxpwifi_bg_scan_cfg *bgscan_cfg_in =3D data_buf; + u8 *tlv_pos =3D 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 =3D cpu_to_le16(HOST_CMD_802_11_BG_SCAN_CONFIG); + cmd->size =3D cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN); + + bgscan_config->action =3D cpu_to_le16(bgscan_cfg_in->action); + bgscan_config->enable =3D bgscan_cfg_in->enable; + bgscan_config->bss_type =3D bgscan_cfg_in->bss_type; + bgscan_config->scan_interval =3D + cpu_to_le32(bgscan_cfg_in->scan_interval); + bgscan_config->report_condition =3D + cpu_to_le32(bgscan_cfg_in->report_condition); + + /* stop sched scan */ + if (!bgscan_config->enable) + return 0; + + bgscan_config->chan_per_scan =3D bgscan_cfg_in->chan_per_scan; + + num_probes =3D (bgscan_cfg_in->num_probes ? + bgscan_cfg_in->num_probes : priv->adapter->scan_probes); + + if (num_probes) { + num_probes_tlv =3D (struct nxpwifi_ie_types_num_probes *)tlv_pos; + num_probes_tlv->header.type =3D cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len =3D + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes =3D cpu_to_le16((u16)num_probes); + + tlv_pos +=3D sizeof(num_probes_tlv->header) + + le16_to_cpu(num_probes_tlv->header.len); + } + + if (bgscan_cfg_in->repeat_count) { + repeat_count_tlv =3D + (struct nxpwifi_ie_types_repeat_count *)tlv_pos; + repeat_count_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + repeat_count_tlv->header.len =3D + cpu_to_le16(sizeof(repeat_count_tlv->repeat_count)); + repeat_count_tlv->repeat_count =3D + cpu_to_le16(bgscan_cfg_in->repeat_count); + + tlv_pos +=3D sizeof(repeat_count_tlv->header) + + le16_to_cpu(repeat_count_tlv->header.len); + } + + if (bgscan_cfg_in->rssi_threshold) { + rssi_threshold_tlv =3D + (struct nxpwifi_ie_types_min_rssi_threshold *)tlv_pos; + rssi_threshold_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_threshold_tlv->header.len =3D + cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold)); + rssi_threshold_tlv->rssi_threshold =3D + cpu_to_le16(bgscan_cfg_in->rssi_threshold); + + tlv_pos +=3D sizeof(rssi_threshold_tlv->header) + + le16_to_cpu(rssi_threshold_tlv->header.len); + } + + for (i =3D 0; i < bgscan_cfg_in->num_ssids; i++) { + ssid_len =3D bgscan_cfg_in->ssid_list[i].ssid.ssid_len; + + wildcard_ssid_tlv =3D + (struct nxpwifi_ie_types_wildcard_ssid_params *)tlv_pos; + wildcard_ssid_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len =3D + cpu_to_le16((u16)(ssid_len + sizeof(u8))); + + /* + * max_ssid_length =3D 0 tells firmware to scan only for the given + * SSID. max_ssid_length =3D IEEE80211_MAX_SSID_LEN triggers a + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length =3D 0; + else + wildcard_ssid_tlv->max_ssid_length =3D + IEEE80211_MAX_SSID_LEN; + + memcpy(wildcard_ssid_tlv->ssid, + bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len); + + tlv_pos +=3D (sizeof(wildcard_ssid_tlv->header) + + le16_to_cpu(wildcard_ssid_tlv->header.len)); + } + + tlv_l =3D (struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos; + + if (bgscan_cfg_in->chan_list[0].chan_number) { + nxpwifi_dbg(priv->adapter, INFO, "info: bgscan: Using supplied channel l= ist\n"); + + tlv_l->header.type =3D cpu_to_le16(TLV_TYPE_CHANLIST); + + for (chan_idx =3D 0; + chan_idx < NXPWIFI_BG_SCAN_CHAN_MAX && + bgscan_cfg_in->chan_list[chan_idx].chan_number; + chan_idx++) { + temp_chan =3D &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 =3D + bgscan_cfg_in->chan_list[chan_idx].chan_number; + temp_chan->band_cfg =3D + bgscan_cfg_in->chan_list[chan_idx].radio_type; + + scan_type =3D + bgscan_cfg_in->chan_list[chan_idx].scan_type; + + if (scan_type =3D=3D NXPWIFI_SCAN_TYPE_PASSIVE) + temp_chan->chan_scan_mode_bmap |=3D + NXPWIFI_PASSIVE_SCAN; + else + temp_chan->chan_scan_mode_bmap &=3D + ~NXPWIFI_PASSIVE_SCAN; + + scan_time =3D bgscan_cfg_in->chan_list[chan_idx].scan_time; + + if (scan_time) { + scan_dur =3D (u16)scan_time; + } else { + scan_dur =3D (scan_type =3D=3D + NXPWIFI_SCAN_TYPE_PASSIVE) ? + priv->adapter->passive_scan_time : + priv->adapter->specific_scan_time; + } + + temp_chan->min_scan_time =3D cpu_to_le16(scan_dur); + temp_chan->max_scan_time =3D cpu_to_le16(scan_dur); + } + } else { + nxpwifi_dbg(priv->adapter, INFO, + "info: bgscan: Creating full region channel list\n"); + chan_num =3D + 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 +=3D (sizeof(tlv_l->header) + + le16_to_cpu(tlv_l->header.len)); + + if (bgscan_cfg_in->start_later) { + start_later_tlv =3D + (struct nxpwifi_ie_types_bgscan_start_later *)tlv_pos; + start_later_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER); + start_later_tlv->header.len =3D + cpu_to_le16(sizeof(start_later_tlv->start_later)); + start_later_tlv->start_later =3D + cpu_to_le16(bgscan_cfg_in->start_later); + + tlv_pos +=3D sizeof(start_later_tlv->header) + + le16_to_cpu(start_later_tlv->header.len); + } + + /* Append vendor specific element 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) { + nxpwifi_dbg(priv->adapter, MSG, "bgscan already stopped!\n"); + return 0; + } + + bgscan_cfg =3D kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); + if (!bgscan_cfg) + return -ENOMEM; + + bgscan_cfg->bss_type =3D NXPWIFI_BSS_MODE_INFRA; + bgscan_cfg->action =3D NXPWIFI_BGSCAN_ACT_SET; + bgscan_cfg->enable =3D false; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG, + HOST_ACT_GEN_SET, 0, bgscan_cfg, true); + if (!ret) + priv->sched_scanning =3D 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 =3D priv->adapter; + u8 i, num_chan; + struct nxpwifi_fw_chan_stats *fw_chan_stats; + struct nxpwifi_chan_stats chan_stats; + + fw_chan_stats =3D (void *)((u8 *)tlv_stat + + sizeof(struct nxpwifi_ie_types_header)); + num_chan =3D le16_to_cpu(tlv_stat->header.len) / + sizeof(struct nxpwifi_chan_stats); + + for (i =3D 0 ; i < num_chan; i++) { + if (adapter->survey_idx >=3D 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 =3D fw_chan_stats->chan_num; + chan_stats.bandcfg =3D fw_chan_stats->bandcfg; + chan_stats.flags =3D fw_chan_stats->flags; + chan_stats.noise =3D fw_chan_stats->noise; + chan_stats.total_bss =3D le16_to_cpu(fw_chan_stats->total_bss); + chan_stats.cca_scan_dur =3D + le16_to_cpu(fw_chan_stats->cca_scan_dur); + chan_stats.cca_busy_dur =3D + le16_to_cpu(fw_chan_stats->cca_busy_dur); + nxpwifi_dbg(adapter, INFO, + "chan=3D%d, noise=3D%d, total_network=3D%d scan_duration=3D%d, busy= _duration=3D%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++; + } +} + +/* Handle the extended scan command response. */ +int nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D false; + + nxpwifi_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); + + ext_scan_resp =3D &resp->params.ext_scan; + + tlv =3D (void *)ext_scan_resp->tlv_buffer; + buf_left =3D le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN= ); + + while (buf_left >=3D sizeof(struct nxpwifi_ie_types_header)) { + type =3D le16_to_cpu(tlv->type); + len =3D 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 =3D (void *)tlv; + nxpwifi_update_chan_statistics(priv, tlv_stat); + break; + default: + break; + } + + buf_left -=3D len + sizeof(struct nxpwifi_ie_types_header); + tlv =3D (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 =3D true; + list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { + cmd_ptr =3D (void *)cmd_node->cmd_skb->data; + if (le16_to_cpu(cmd_ptr->command) =3D=3D + HOST_CMD_802_11_SCAN_EXT) { + nxpwifi_dbg(adapter, INFO, + "Scan pending in command pending list"); + complete_scan =3D 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; +} + +/* + * Handle the extended scan report event: parse the results and notify + * cfg80211. + */ +int nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private *priv, + void *buf) +{ + int ret =3D 0; + struct nxpwifi_adapter *adapter =3D 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 =3D 0; + s32 rssi =3D 0; + struct nxpwifi_event_scan_result *event_scan =3D buf; + u8 num_of_set =3D event_scan->num_of_set; + u8 *scan_resp =3D buf + sizeof(struct nxpwifi_event_scan_result); + u16 scan_resp_size =3D 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 =3D -EINVAL; + goto check_next_scan; + } + + bytes_left =3D 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 =3D (struct nxpwifi_ie_types_data *)scan_resp; + + for (idx =3D 0; idx < num_of_set && bytes_left; idx++) { + type =3D le16_to_cpu(tlv->header.type); + len =3D 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 =3D NULL; + scan_info_tlv =3D NULL; + bytes_left_for_tlv =3D bytes_left; + + /* + * BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor + */ + if (type !=3D TLV_TYPE_BSS_SCAN_RSP) + break; + + bss_info =3D (u8 *)tlv; + scan_rsp_tlv =3D (struct nxpwifi_ie_types_scan_rsp *)tlv; + tlv =3D (struct nxpwifi_ie_types_data *)(tlv->data + len); + bytes_left_for_tlv -=3D + (len + sizeof(struct nxpwifi_ie_types_header)); + + while (bytes_left_for_tlv >=3D + sizeof(struct nxpwifi_ie_types_header) && + le16_to_cpu(tlv->header.type) !=3D TLV_TYPE_BSS_SCAN_RSP) { + type =3D le16_to_cpu(tlv->header.type); + len =3D 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 =3D NULL; + bytes_left_for_tlv =3D 0; + continue; + } + switch (type) { + case TLV_TYPE_BSS_SCAN_INFO: + scan_info_tlv =3D + (struct nxpwifi_ie_types_scan_inf *)tlv; + if (len !=3D + sizeof(struct nxpwifi_ie_types_scan_inf) - + sizeof(struct nxpwifi_ie_types_header)) { + bytes_left_for_tlv =3D 0; + continue; + } + break; + default: + break; + } + tlv =3D (struct nxpwifi_ie_types_data *)(tlv->data + len); + bytes_left -=3D + (len + sizeof(struct nxpwifi_ie_types_header)); + bytes_left_for_tlv -=3D + (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 +=3D sizeof(u16); + bytes_left -=3D sizeof(u16); + + if (scan_info_tlv) { + rssi =3D (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); + rssi *=3D 100; /* Convert dBm to mBm */ + nxpwifi_dbg(adapter, INFO, + "info: InterpretIE: RSSI=3D%d\n", rssi); + fw_tsf =3D le64_to_cpu(scan_info_tlv->tsf); + radio_type =3D &scan_info_tlv->radio_type; + } else { + radio_type =3D NULL; + } + ret =3D 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; +} + +/* + * Prepare the background scan query command. Sets the command ID, size, + * flush parameter, and fixes endianness. + */ +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 =3D + &cmd->params.bg_scan_query; + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_BG_SCAN_QUERY); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) + + S_DS_GEN); + + bg_query->flush =3D 1; + + return 0; +} + +/* Insert a scan command node into the scan_pending_q. */ +void +nxpwifi_queue_scan_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + cmd_node->wait_q_enabled =3D true; + cmd_node->condition =3D &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); +} + +/* Append a vendor-specific element TLV to the buffer. */ +int +nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private *priv, + u16 vsie_mask, u8 **buffer) +{ + int id, ret_len =3D 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 element array and append + * the selected(scan/assoc) element as TLV to the command + */ + for (id =3D 0; id < NXPWIFI_MAX_VSIE_NUM; id++) { + if (priv->vs_ie[id].mask & vsie_mask) { + vs_param_set =3D + (struct nxpwifi_ie_types_vendor_param_set *) + *buffer; + vs_param_set->header.type =3D + cpu_to_le16(TLV_TYPE_PASSTHROUGH); + vs_param_set->header.len =3D + 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 +=3D le16_to_cpu(vs_param_set->header.len) + + sizeof(struct nxpwifi_ie_types_header); + ret_len +=3D le16_to_cpu(vs_param_set->header.len) + + sizeof(struct nxpwifi_ie_types_header); + } + } + return ret_len; +} + +/* + * Save the beacon buffer of the current BSS descriptor. + * + * The buffer is preserved so it can be restored when the current SSID's + * beacon is missing, such as when: + * - the SSID was not found in the latest scan, or + * - the SSID was the last entry in the scan table and was overwritten. + */ +void +nxpwifi_save_curr_bcn(struct nxpwifi_private *priv) +{ + struct nxpwifi_bssdescriptor *curr_bss =3D + &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 !=3D curr_bss->beacon_buf_size) { + priv->curr_bcn_size =3D curr_bss->beacon_buf_size; + + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf =3D 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 =3D priv->curr_bcn_buf; + + /* adjust the pointers in the current BSS descriptor */ + if (curr_bss->bcn_wpa_ie) + curr_bss->bcn_wpa_ie =3D + (struct ieee_types_vendor_specific *) + (curr_bss->beacon_buf + + curr_bss->wpa_offset); + + if (curr_bss->bcn_rsn_ie) + curr_bss->bcn_rsn_ie =3D + (struct element *)(curr_bss->beacon_buf + + curr_bss->rsn_offset); + + if (curr_bss->bcn_ht_cap) + curr_bss->bcn_ht_cap =3D (struct ieee80211_ht_cap *) + (curr_bss->beacon_buf + + curr_bss->ht_cap_offset); + + if (curr_bss->bcn_ht_oper) + curr_bss->bcn_ht_oper =3D (struct ieee80211_ht_operation *) + (curr_bss->beacon_buf + + curr_bss->ht_info_offset); + + if (curr_bss->bcn_vht_cap) + curr_bss->bcn_vht_cap =3D (void *)(curr_bss->beacon_buf + + curr_bss->vht_cap_offset); + + if (curr_bss->bcn_vht_oper) + curr_bss->bcn_vht_oper =3D (void *)(curr_bss->beacon_buf + + curr_bss->vht_info_offset); + + if (curr_bss->bcn_he_cap) + curr_bss->bcn_he_cap =3D (void *)(curr_bss->beacon_buf + + curr_bss->he_cap_offset); + + if (curr_bss->bcn_he_oper) + curr_bss->bcn_he_oper =3D (void *)(curr_bss->beacon_buf + + curr_bss->he_info_offset); + + if (curr_bss->bcn_bss_co_2040) + curr_bss->bcn_bss_co_2040 =3D + (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); + + if (curr_bss->bcn_ext_cap) + curr_bss->bcn_ext_cap =3D curr_bss->beacon_buf + + curr_bss->ext_cap_offset; + + if (curr_bss->oper_mode) + curr_bss->oper_mode =3D (void *)(curr_bss->beacon_buf + + curr_bss->oper_mode_offset); +} + +/* Free the beacon buffer in the current BSS descriptor. */ +void +nxpwifi_free_curr_bcn(struct nxpwifi_private *priv) +{ + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf =3D NULL; +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazon11011040.outbound.protection.outlook.com [52.101.65.40]) (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 D11A22D978A; Wed, 4 Feb 2026 18:05:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.65.40 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228331; cv=fail; b=X6uTkPo1LPsW85qaQwWuHdfG/IwvHk3IkqyTRk80xS8rBmbC2fOb+EIZqcgqCTXTjhuLvJkff6hC3fkTnFYy8dOOkERlso4U9dCn5js12NvvJu4CUN91sj4KwSlCv+OCRmvtIoG6GYgpgGoYwtx+LJ4OJE5/8MRe0PPr6ims2GI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228331; c=relaxed/simple; bh=JIDL9n28bDqZVbnWTGpjgZRmjE/LuCPMsEl0r+uuqf4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=gi4OzAOJ3WvRw89mnkOWO9H8gow8GYowt7sSZbL5NVcqSHdGJ/ULpp0qU42dbnME2JArqKE5zWGA9urKZJXZrUupsy3LbAQkCvQdxyB9xTN6VHqEQO6ZhKH+8nHj/lR8L2y/RC7x6Hr8mIKG46bAYlizrnlZc9V2zVwSUJh84vM= 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=cr939Ksb; arc=fail smtp.client-ip=52.101.65.40 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="cr939Ksb" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Vg26Ce2+O4t9ZPKyhQsUYBJTuVDB7jATIgTKJPdX5peCcCXMA6cmyWmhoIoomEJti++RNWvSSREISFYIOFrskjTKvHcHpaUvck0p7Z7n7yPHdEKerLdmJ19zKQOKRduzDAItgEfld+ZfhqGP0FcRWjsWWQMlL2YfkzMd5FrJreA8Zhjas5/tSzS2gl1aBXkBvhMWLTC9hgNG+bpH+6+dw6cB0rnJopgXARpewCTlpV0JxuUhT3N5eCC5D3RxjB8qfVFhwAkAR48EOUzoOVm8A2ycWccBVcw4D77wvTxFEKkpOfA1JW3F8wIiEGcTHPoyekOIt8+oZzQckr5X9yBmAw== 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=XAFdoTVmo0O/JyWK7stabIkmM5Z/3eaM68X784VjmeM=; b=Y98FG/iv9HAw9GHmLKaXOtw0jLEdRIzAY0FxNTCNDp30HYDCtLY4YjQpK6GhZ/ClOjwvVpBpHukF0XgbQ/1jufX+DGlWCkW5C4Z0dwVnHZU+1RIdsQyVtvmVg/CnoSKHC8EbY6T4DWeV/TxeRGHqiyYNhkZNHLG6+1+pthWm5mYgSba+bkTgPUDrOQFXtzeRsde+FrKTYympje0A4j0HycG8wK42HAsPcRg60iyfu+5IVKwwdhA1aWdi8w+e99uTa6sdRyJ/4deOh3yR+CvCxBtBOQiLb9GVaLVqFOhf1/VpG+rUsnq4J24/ocT9fE0q+fKrE5sJ7si8G7r4CeSmFw== 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=XAFdoTVmo0O/JyWK7stabIkmM5Z/3eaM68X784VjmeM=; b=cr939Ksbby1RZVepLF2Xd+OzKBJ/PkQfzs7StcHjoK/OqMsmuiWihbbq5IT7uqmcn7/lSzzmWBSeB8++5rz6j3kGR4vBFrgID+ALgvd/qspI14qGVk0vVpXvEy9gmqWrl3zOLvTuURGFwSr6PIlnMeMNcGC61r8pEafvFt3NYCkL/yXKbOetljizROZmukqO7S2s5TKfg6DmUu3eu2+yvM4cSrhWYJ/8tOMkIzeYP69JPNixO4jGBKC2WNx4EiGRgdh8LK+BBaZdFfr+5knXeTITGC33g0ae/I2/c2Q19vEhkqAVipkuOyA0ZFRuxsWjwUNOTtD292iabfNhVGAkTA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:28 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:28 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 07/21] wifi: nxpwifi: add join and association support Date: Thu, 5 Feb 2026 02:03:44 +0800 Message-Id: <20260204180358.632281-8-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 134015ab-d5da-45dc-3c80-08de6417fa0d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?Sqv0+vKaq8+ZUqHpIfvx6yNTzOnuVsaZB2LCbH+xTerzeONsXyrQOHxyV8Gh?= =?us-ascii?Q?96CpKB3BBW2qwPm3mnwBzWlxdXb+xnCOCDBx2B1Wpf6/3cuy9hm/b3H8jvak?= =?us-ascii?Q?c0qM5wWsJwMSzDgMIeTG0xGCFJ5mKJKxLmbF+4qSC/FZ+/F5BYQyOah3Qfyn?= =?us-ascii?Q?RdXk4g5UsASO/k5xlb16Q4JqYzZK8ALGv+vsKxyLU1llErRTvx/l1VTljLAD?= =?us-ascii?Q?Y0BFRJUqrv6dRQbkmrx+e/wjNovhvZUX4z4csZMKFgTyfpWMdAKMu9eONyiC?= =?us-ascii?Q?a2vw11DUeIintnD9S1bRm/GrEaGWZCDf+r8BXKNhnnuKi9aIdkBPqVzONlC/?= =?us-ascii?Q?PB55h2AWEwpg4JSNfXJeMHe9AsJ2A1nA+XFimza48Y1FHEgF/swacCcyN/8B?= =?us-ascii?Q?bcE9RlYsSb3plUWLfe+tf/xE1LdvNK3RBBUXoS426ZaBDrfT4D8dRpcPz8vb?= =?us-ascii?Q?KGRld8fiaiuoIHDQl48HqSL5Xwiw2XD7Q43zlcUVPh26wzIyuH6NFYlIjzdf?= =?us-ascii?Q?CHAbyTSCFlg5RLBffuC2/PaStbFWfxk7UJBICQJoZF4Pk8Pzi6U4VV0lMJXk?= =?us-ascii?Q?4kJezllpoaaMYWklRLyYRWQtxfxZIV88YgShXoIWs9m4os50u2hx9FdDYDWF?= =?us-ascii?Q?8/wfarSTbQ7rcUH+HJhVXeUyzq0ideFbNK3LqXCvltvROM1wXQuCel0eKHED?= =?us-ascii?Q?/yiLB8vaUTpZC6X2bJAlKJhADuGmQKj3brciL8TwxNzEkSXge4wq8HGIg8T8?= =?us-ascii?Q?XMLtr2o9yi8TTiBuIlzDcOsNvTHUxJ3QdbdO0ksh9sFNFPY/fNbEMwfFcy0r?= =?us-ascii?Q?JdluYnLYukHTnV0g2NMoyEaa3SvusSGrPITiztrgSydn6HUARBFKPEV5UqZr?= =?us-ascii?Q?qeRPcVF1VcXL9tex/XbvXIOkiiPDEVuFti0Tscixq9fKFgH3vHxO5WyqM1rs?= =?us-ascii?Q?k9dfAq34jbdz8aWlcHkSK0beEliVPItC6yB+0FKVnc13+5vcvmI686KskL4u?= =?us-ascii?Q?FzkEh6efj6pmYeiw9OPNCVYwcrpR+F/EcAXPdqOFVodcYRjIYNxnvQXnXT3g?= =?us-ascii?Q?7Kpf6Zh32ALLqxT50oULQ7I6VCqP9qzhsNai7GUkJL0VEUUFRSZzw/DwGdt0?= =?us-ascii?Q?RgetT822S/86S3N5i/gh+x6yAz3Sc6kQfd6caBd5CAJonB8Q0uSq7LkETh0d?= =?us-ascii?Q?LjL5sEi5BZVIF2Yt+j8mN1YXOMhCLxVUlQLgnFIlh4hg+2OJDr+k/j6gHvmI?= =?us-ascii?Q?ES0XOICcWFrSVHAEBdqUVEVDLAVUCl5RDhCoQ05VF8dMRF/Jq8iv2oWl5k3j?= =?us-ascii?Q?WtvxVyMoVbt/2YP+1auNhz62Jfa6IytP99F5mk+kqNJRDU9PBmpEHRUHeVUT?= =?us-ascii?Q?b2g5xFaK3dtKFtCbBRZolhGVeupG7L8z9xYgAYTyvjzM8Bo6zr6/6vF8A854?= =?us-ascii?Q?qgEi4dSRcXbJNKZO84F677tVac9zsLY/9BbO7liP6ab6YvONejpkJfSC4uRv?= =?us-ascii?Q?42pAcXm6XewAy5M4NkLrRP+ApzIbPuWGVQtgcAte4exdoW89Nfp/svyjh8CN?= =?us-ascii?Q?2Bbl3cc0d+sEeLRlmJZl5NFGpzA8m4IndG46Xd0x2kWJCwGrFj25SdnkGbVU?= =?us-ascii?Q?bDFnzqCRQPVGnsaqNTU9wjc=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?lYDQMfSKga4xoQe6gIvwh0PtzVNGgBgvesDlcGT3gkt1ejnj64Ju+SzCSFKh?= =?us-ascii?Q?H4QCJM+z9HnceWYVxawh42TPZATBRvs36iXPq3pfHJF47bR9CuQyfmt8x3CW?= =?us-ascii?Q?f09H7YAqof+M5Edgm5cfPLTXOCgN1F6qRugMxza7m6MMjWqtZoryNvRbDgCs?= =?us-ascii?Q?aBcOlaVe6cJs6XwG5lg9uSZG84YMWN10R+G71T6KHO1fAc1x4gvRiDhpV9Bs?= =?us-ascii?Q?TBeVTLaupKl/0girZb/QriW+8k95AKLJTBg//nAfQS1CZppwGRk5Mcimvm6K?= =?us-ascii?Q?P1rlIg7dhFLA4V+JzCB8Q1STSlWGXesSR7cqGnLAFF8CCNo59suHIwOYBYdh?= =?us-ascii?Q?Kbh12v5+PAWe3CesxFgv88o9IMb7GyQSJ2aeTOJXs4HOTOygmWE1W1QhusCG?= =?us-ascii?Q?CYzghLXjGzb5247xUf9E5p1Dx9URcagqOOEdO+bcxamXIhd1Q7X+P2lxOARr?= =?us-ascii?Q?X+LMu32m1d/QJUMvOYM+PTK1va9HyAkb1Ec8dxpVI9l6BM7splAtjnbLnh3U?= =?us-ascii?Q?ObJvrscVIu2vwD86lRZ/Rmu1AIxaX89BW9Zn1CHzgT6kanGvgGnViFnlpOEC?= =?us-ascii?Q?ErHzbTMYmCD0pqsArKz/Bz/8NIw6d0Hu7obrMMuYg6UrEsAE7M+4Lg37mdqk?= =?us-ascii?Q?Lpkh4bg0KUseGXMFINSdtqkFapdC6x6UOoviaD2c5dGwt5DSv8l3nUE080yL?= =?us-ascii?Q?WXmFXpDYCdAqET8fK1lyOTloBDWrMuWwalvcEozDg+CnnCyUz51zj3x/z5b5?= =?us-ascii?Q?pza4pCmvNWQAQG/WhdfL1/B/XLJGMFne4AdWcJhjNOxVUGSb241b+/13mU9s?= =?us-ascii?Q?vN9dkfCOSlY1akzGUqxvk5Z7/xENy0TPw79EmvP0lKiJT73RIpYm+K10/NqK?= =?us-ascii?Q?/+Cgv4U2pB5bXV2nyPlvw4Pi1PZLzvOmL+r3WGmT33U99fdLzAJ/TVUXaPbx?= =?us-ascii?Q?9F2zCRsgDCsFeLbpqXVuU7GTvTMzuI0B4rYL9YefKdbECxYvx4poCSuwLQNH?= =?us-ascii?Q?GCr5bD20zvNFg/0wgZsAzAFNKIlZ+1BnbROVR1eATBVm2OOUiHQEyc1uiPpG?= =?us-ascii?Q?6EAEpKlv1BiH5dnDfHs7xT5KhBLEpD1l3G1ecWj48ko5tPWKIzt6Dq/pctGn?= =?us-ascii?Q?Rhyx1tLV5R+9L2Ma2PXrxBA/jKPBs/yHxDqEE71La5ddC+wH/4FLSR1Eiwla?= =?us-ascii?Q?ooYPZVD4aoCiDrsedSCT9eaD+iKvmk/kGE6xyGv4Gd+1l/oza8enYaApioqh?= =?us-ascii?Q?GcVPV8IGelGGLSAuDWb4/dRVK8nrwQKv7bMhoqi8UJnBrvDY2bGi3wMGui/p?= =?us-ascii?Q?tw51xYTMZ+Rm5R1KRf2W07VQOEgtWToYWFyYcl3STZj8DeQKUfCexc+U+XqU?= =?us-ascii?Q?LdNRhQ2ZeYHbCjhYGVTau+tBpDd5l228qmPmtqstJp7Np6Coa1pkGQVARRGW?= =?us-ascii?Q?6xQKNXY5H1YFHExNqVO138xVci0UnMkt3PvkBsmlctSECh9nOx4Rg/+gC8iD?= =?us-ascii?Q?IiXnKyOKKsEDcsLXAgbSnLcigmKATIGvYUZZtpr46fq9lcdYZjaUzwgao1ad?= =?us-ascii?Q?OptHfhJxWug0zJPo8Hw7wNaqksgt8s4RVanrqkxKq7IZw52m5cHblQ+cSpS3?= =?us-ascii?Q?k+YMLDJx/lQn+DwQ0DGE5GySAHe2KijAu9MiIfrHi0XGCTyZMY8Mq/vP/SYM?= =?us-ascii?Q?vJtULExfTaA+v5w4zTyTM8Jfy3Mj4POvPGX2EjPafqd2SSlCLtARF1ayI7hd?= =?us-ascii?Q?HKMtkb8XlQ=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 134015ab-d5da-45dc-3c80-08de6417fa0d X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:27.8938 (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: JlJ5Pmd94LCHmoebHd/flBsIdfgsj1ccepBEp3C+XxdSysSzSA41QJepv/Wun6N3m4PfjqASesUEsmJKSW/voQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce join.c to implement infrastructure mode association and deauthentication logic for the nxpwifi driver. - Implements association command construction, including: - SSID, channel, rates, authentication, WPA/WPA2/RSN IEs - 802.11n/ac/ax capability TLVs - WMM and vendor-specific IEs - TSF synchronization and scan result integration - Handles association response parsing and connection state updates - Adds support for deauthentication (infra, AP, adhoc) - Updates current BSS parameters and enables data path upon success This patch enables full STA-mode association flow with cfg80211 integration. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/join.c | 788 ++++++++++++++++++++++++ 1 file changed, 788 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..e1aaa0424c25 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/join.c @@ -0,0 +1,788 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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 generic IE as pass-through TLV for join */ +static int +nxpwifi_cmd_append_generic_ie(struct nxpwifi_private *priv, u8 **buffer) +{ + int ret_len =3D 0; + struct nxpwifi_ie_types_header ie_header; + + /* Null Checks */ + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* + * If there is a generic element 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 element len %d to %p\n", + __func__, priv->gen_ie_buf_len, *buffer); + + /* Wrap the generic element buffer with a pass through TLV type */ + ie_header.type =3D cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len =3D 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 +=3D sizeof(ie_header); + ret_len +=3D sizeof(ie_header); + + /* + * Copy the generic element 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 +=3D priv->gen_ie_buf_len; + ret_len +=3D priv->gen_ie_buf_len; + + /* Reset the generic element buffer */ + priv->gen_ie_buf_len =3D 0; + } + + /* return the length appended to the buffer */ + return ret_len; +} + +/* Append TSF timestamp (AP TSF and local RX TSF) for reassoc */ +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 =3D cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); + tsf_tlv.header.len =3D cpu_to_le16(2 * sizeof(tsf_val)); + + memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); + *buffer +=3D sizeof(tsf_tlv.header); + + /* TSF at the time when beacon/probe_response was received */ + tsf_val =3D cpu_to_le64(bss_desc->fw_tsf); + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer +=3D sizeof(tsf_val); + + tsf_val =3D 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 +=3D sizeof(tsf_val); + + return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); +} + +/* Compute intersection of two rate sets; rate1 updated in-place */ +static int nxpwifi_get_common_rates(struct nxpwifi_private *priv, u8 *rate= 1, + u32 rate1_size, u8 *rate2, u32 rate2_size) +{ + int ret; + u8 *ptr =3D rate1, *tmp; + u32 i, j; + + tmp =3D kmemdup(rate1, rate1_size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + memset(rate1, 0, rate1_size); + + for (i =3D 0; i < rate2_size && rate2[i]; i++) { + for (j =3D 0; j < rate1_size && tmp[j]; j++) { + /* + * Check common rate, excluding the bit for + * basic rate + */ + if ((rate2[i] & 0x7F) =3D=3D (tmp[j] & 0x7F)) { + *rate1++ =3D 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) =3D=3D priv->data_rate) { + ret =3D 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 =3D -EPERM; + goto done; + } + + ret =3D 0; +done: + kfree(tmp); + return ret; +} + +/* Build common rates from BSS descriptor into out_rates */ +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 =3D nxpwifi_get_active_data_rates(priv, card_rates); + /* Get the common rates between AP and STA supported rates */ + ret =3D nxpwifi_get_common_rates(priv, out_rates, NXPWIFI_SUPPORTED_RATES, + card_rates, card_rates_size); + if (ret) { + *out_rates_size =3D 0; + nxpwifi_dbg(priv->adapter, ERROR, + "%s: cannot get common rates\n", + __func__); + } else { + *out_rates_size =3D + min_t(size_t, strlen(out_rates), NXPWIFI_SUPPORTED_RATES); + } + + return ret; +} + +/* Append WPS IE as pass-through TLV for join */ +static int +nxpwifi_cmd_append_wps_ie(struct nxpwifi_private *priv, u8 **buffer) +{ + int ret_len =3D 0; + struct nxpwifi_ie_types_header ie_header; + + if (!buffer || !*buffer) + return 0; + + /* + * If there is a wps element buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wps_ie_len) { + nxpwifi_dbg(priv->adapter, CMD, + "cmd: append wps element %d to %p\n", + priv->wps_ie_len, *buffer); + + /* Wrap the generic element buffer with a pass through TLV type */ + ie_header.type =3D cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len =3D cpu_to_le16(priv->wps_ie_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + *buffer +=3D sizeof(ie_header); + ret_len +=3D sizeof(ie_header); + + memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); + *buffer +=3D priv->wps_ie_len; + ret_len +=3D priv->wps_ie_len; + } + + kfree(priv->wps_ie); + priv->wps_ie_len =3D 0; + return ret_len; +} + +/* Append WPA/WPA2 RSN IE TLV */ +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 =3D (struct nxpwifi_ie_types_rsn_param_set *)(*buffer); + rsn_ie_tlv->header.type =3D cpu_to_le16((u16)priv->wpa_ie[0]); + rsn_ie_tlv->header.type =3D + cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); + rsn_ie_tlv->header.len =3D cpu_to_le16((u16)priv->wpa_ie[1]); + rsn_ie_tlv->header.len =3D cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) + & 0x00FF); + if (le16_to_cpu(rsn_ie_tlv->header.len) <=3D (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 =3D sizeof(rsn_ie_tlv->header) + + le16_to_cpu(rsn_ie_tlv->header.len); + *buffer +=3D rsn_ie_len; + + return rsn_ie_len; +} + +/* Build 802.11 association command and required TLVs */ +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 =3D priv->adapter; + struct host_cmd_ds_802_11_associate *assoc =3D &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 =3D 0; + int ret; + + pos =3D (u8 *)assoc; + + cmd->command =3D 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 =3D bss_desc; + + memcpy(assoc->peer_sta_addr, + bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); + pos +=3D sizeof(assoc->peer_sta_addr); + + /* Set the listen interval */ + assoc->listen_interval =3D cpu_to_le16(priv->listen_interval); + /* Set the beacon period */ + assoc->beacon_period =3D cpu_to_le16(bss_desc->beacon_period); + + pos +=3D sizeof(assoc->cap_info_bitmap); + pos +=3D sizeof(assoc->listen_interval); + pos +=3D sizeof(assoc->beacon_period); + pos +=3D sizeof(assoc->dtim_period); + + host_mlme_tlv =3D (struct nxpwifi_ie_types_host_mlme *)pos; + host_mlme_tlv->header.type =3D cpu_to_le16(TLV_TYPE_HOST_MLME); + host_mlme_tlv->header.len =3D cpu_to_le16(sizeof(host_mlme_tlv->host_mlme= )); + host_mlme_tlv->host_mlme =3D 1; + pos +=3D sizeof(host_mlme_tlv->header) + sizeof(host_mlme_tlv->host_mlme); + + ssid_tlv =3D (struct nxpwifi_ie_types_ssid_param_set *)pos; + ssid_tlv->header.type =3D cpu_to_le16(WLAN_EID_SSID); + ssid_tlv->header.len =3D 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 +=3D sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); + + phy_tlv =3D (struct nxpwifi_ie_types_phy_param_set *)pos; + phy_tlv->header.type =3D cpu_to_le16(WLAN_EID_DS_PARAMS); + phy_tlv->header.len =3D 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 +=3D sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); + + ss_tlv =3D (struct nxpwifi_ie_types_ss_param_set *)pos; + ss_tlv->header.type =3D cpu_to_le16(WLAN_EID_CF_PARAMS); + ss_tlv->header.len =3D cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); + pos +=3D sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); + + /* Get the common rates supported between the driver and the BSS Desc */ + ret =3D 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 =3D rates_size; + memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); + + /* Setup the Rates TLV in the association command */ + rates_tlv =3D (struct nxpwifi_ie_types_rates_param_set *)pos; + rates_tlv->header.type =3D cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len =3D cpu_to_le16((u16)rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + pos +=3D sizeof(rates_tlv->header) + rates_size; + nxpwifi_dbg(adapter, INFO, "info: ASSOC_CMD: rates size =3D %d\n", + rates_size); + + /* Add the Authentication type */ + auth_tlv =3D (struct nxpwifi_ie_types_auth_type *)pos; + auth_tlv->header.type =3D cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_tlv->header.len =3D cpu_to_le16(sizeof(auth_tlv->auth_type)); + if (priv->sec_info.wep_enabled) + auth_tlv->auth_type =3D + cpu_to_le16((u16)priv->sec_info.authentication_mode); + else + auth_tlv->auth_type =3D cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); + + pos +=3D sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); + + if (priv->sec_info.authentication_mode =3D=3D WLAN_AUTH_SAE) { + auth_tlv->auth_type =3D 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 =3D + (struct nxpwifi_ie_types_sae_pwe_mode *)pos; + sae_pwe_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_SAE_PWE_MODE); + sae_pwe_tlv->header.len =3D + cpu_to_le16(sizeof(sae_pwe_tlv->pwe[0])); + sae_pwe_tlv->pwe[0] =3D bss_desc->bcn_rsnx_ie->data[0]; + pos +=3D 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 =3D (struct nxpwifi_ie_types_chan_list_param_set *)pos; + chan_tlv->header.type =3D cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len =3D + 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 =3D + (bss_desc->phy_param_set.ds_param_set.current_chan); + nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Chan =3D %d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].band_cfg =3D + nxpwifi_band_to_radio_type((u8)bss_desc->bss_band); + + nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Band =3D %d\n", + chan_tlv->chan_scan_param[0].band_cfg); + pos +=3D 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 =3D nxpwifi_append_rsn_ie_wpa_wpa2(priv, &pos); + + if (rsn_ie_len =3D=3D -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_GAC || + 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 element 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 =3D cpu_to_le16((u16)(pos - (u8 *)assoc) + S_DS_GEN); + + /* Set the Capability info at last */ + tmp_cap =3D bss_desc->cap_info_bitmap; + + if (priv->config_bands =3D=3D BAND_B) + tmp_cap &=3D ~WLAN_CAPABILITY_SHORT_SLOT_TIME; + + tmp_cap &=3D CAPINFO_MASK; + nxpwifi_dbg(adapter, INFO, + "info: ASSOC_CMD: tmp_cap=3D%4X CAPINFO_MASK=3D%4lX\n", + tmp_cap, CAPINFO_MASK); + assoc->cap_info_bitmap =3D 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"; +} + +/* + * Handle association command response. + * Parse cap_info/status_code/AID and copy IEs, update connection state, + * WMM and HT/HE parameters, queues and filters. On failure, return the + * IEEE status code or a mapped timeout error. + */ +int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + int ret =3D 0; + struct ieee_types_assoc_rsp *assoc_rsp; + struct nxpwifi_bssdescriptor *bss_desc; + bool enable_data =3D 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 =3D (struct ieee80211_mgmt *)&resp->params; + if (!memcmp(hdr->bssid, priv->attempted_bss_desc->mac_address, + ETH_ALEN)) + assoc_rsp =3D (struct ieee_types_assoc_rsp *)&hdr->u.assoc_resp; + else + assoc_rsp =3D (struct ieee_types_assoc_rsp *)&resp->params; + + cap_info =3D le16_to_cpu(assoc_rsp->cap_info_bitmap); + status_code =3D le16_to_cpu(assoc_rsp->status_code); + aid =3D le16_to_cpu(assoc_rsp->a_id); + + if ((aid & (BIT(15) | BIT(14))) !=3D (BIT(15) | BIT(14))) + nxpwifi_dbg(adapter, ERROR, + "invalid AID value 0x%x; bits 15:14 not set\n", aid); + + aid &=3D ~(BIT(15) | BIT(14)); + + priv->assoc_rsp_size =3D min(le16_to_cpu(resp->size) - S_DS_GEN, + sizeof(priv->assoc_rsp_buf)); + + assoc_rsp->a_id =3D 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=3D%d err=3D%#x a_id=3D%#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 =3D=3D CONNECT_ERR_ASSOC_ERR_TIMEOUT) { + if (status_code =3D=3D NXPWIFI_ASSOC_CMD_FAILURE_AUTH) { + ret =3D WLAN_STATUS_AUTH_TIMEOUT; + nxpwifi_dbg(adapter, ERROR, + "ASSOC_RESP: AUTH timeout\n"); + } else { + ret =3D WLAN_STATUS_UNSPECIFIED_FAILURE; + nxpwifi_dbg(adapter, ERROR, + "ASSOC_RESP: UNSPECIFIED failure\n"); + } + + priv->assoc_rsp_size =3D 0; + } else { + ret =3D status_code; + } + + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + priv->media_connected =3D true; + + adapter->ps_state =3D PS_STATE_AWAKE; + adapter->pps_uapsd_mode =3D false; + adapter->tx_lock_flag =3D false; + + /* Set the attempted BSSID Index to current */ + bss_desc =3D 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 =3D + bss_desc->phy_param_set.ds_param_set.current_chan; + + priv->curr_bss_params.band =3D (u8)bss_desc->bss_band; + + if (bss_desc->wmm_ie.element_id =3D=3D WLAN_EID_VENDOR_SPECIFIC) + priv->curr_bss_params.wmm_enabled =3D true; + else + priv->curr_bss_params.wmm_enabled =3D false; + + if ((priv->wmm_required || bss_desc->bcn_ht_cap) && + priv->curr_bss_params.wmm_enabled) + priv->wmm_enabled =3D true; + else + priv->wmm_enabled =3D false; + + priv->curr_bss_params.wmm_uapsd_enabled =3D false; + + if (priv->wmm_enabled) + priv->curr_bss_params.wmm_uapsd_enabled =3D + ((bss_desc->wmm_ie.qos_info & + IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); + + /* Store the bandwidth information from assoc response */ + ie_ptr =3D 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 =3D (struct ieee80211_ht_operation *)(ie_ptr + + sizeof(struct element)); + priv->assoc_resp_ht_param =3D assoc_resp_ht_oper->ht_param; + priv->ht_param_present =3D true; + } else { + priv->ht_param_present =3D 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 =3D false; + + if (priv->wmm_enabled) { + /* Don't re-enable carrier until we get the WMM_GET_STATUS event */ + enable_data =3D 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 =3D 0; + priv->data_nf_last =3D 0; + priv->data_rssi_avg =3D 0; + priv->data_nf_avg =3D 0; + priv->bcn_rssi_last =3D 0; + priv->bcn_nf_last =3D 0; + priv->bcn_rssi_avg =3D 0; + priv->bcn_nf_avg =3D 0; + priv->rxpd_rate =3D 0; + priv->rxpd_htinfo =3D 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 =3D true; + else + priv->port_open =3D true; + +done: + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + if (ret) + adapter->cmd_wait_q.status =3D -1; + else + adapter->cmd_wait_q.status =3D 0; + } + + return ret; +} + +/* Associate to the specified BSS (STA only) */ +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) !=3D NXPWIFI_BSS_ROLE_STA) || + bss_desc->bss_mode !=3D 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 =3D 0; + + return nxpwifi_send_cmd(priv, HOST_CMD_802_11_ASSOCIATE, + HOST_ACT_GEN_SET, 0, bss_desc, true); +} + +/* Send deauth to disconnect from an infrastructure BSS */ +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 =3D nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE, + HOST_ACT_GEN_SET, 0, mac_address, true); + + return ret; +} + +/* Disconnect from the current BSS (STA/P2P/AP) */ +int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac) +{ + int ret =3D 0; + + if (!priv->media_connected) + return 0; + + priv->auth_flag =3D 0; + priv->auth_alg =3D WLAN_AUTH_NONE; + priv->host_mlme_reg =3D false; + priv->mgmt_frame_mask =3D 0; + + ret =3D 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 =3D nxpwifi_deauthenticate_infra(priv, mac); + if (ret) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, + true, GFP_KERNEL); + break; + case NL80211_IFTYPE_AP: + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP, + HOST_ACT_GEN_SET, 0, NULL, true); + default: + break; + } + + return ret; +} + +/* Deauthenticate/disconnect from all BSS. */ +void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + int i; + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + nxpwifi_deauthenticate(priv, NULL); + } +} +EXPORT_SYMBOL_GPL(nxpwifi_deauthenticate_all); + +/* Convert 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; +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DU2PR03CU002.outbound.protection.outlook.com (mail-northeuropeazon11011024.outbound.protection.outlook.com [52.101.65.24]) (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 124E642316D; Wed, 4 Feb 2026 18:05:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.65.24 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228333; cv=fail; b=K6ss9C4g8brdI6gUZQOoEpIzQzdhQDEHT77NR7epyVRZrwBLQj3nT9AzrExepVSx/s2K4H94NwpGrn71GgMHLs1ozjB+O4Xzu5PAW33DOBy8tB8GV8ja1FL0qzGx8N1ydZVVq3dFLpXhXK5akhUPFT3Y9ZKsdAadYoFyq/Xf33s= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228333; c=relaxed/simple; bh=HVEFDZf8EwBk0JvLvJTj2fFDiCE+LBK7RfovK1SONKY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=Oq5eAipH/2IhFHKllGFqbZbm3VuPpkQmQEUAX4SrlKKhcjk0LoPmuxAnSRfugmXNb3F8ukOh9AVHxsc23mY3zqBKRmsPXbVvAWMo0ZGB+4qdo/GoPK6rNVNAnPFhUi2JZQIF57tx/ABnWqwf4s8TqPPS46EInxA5WlE6zODmTG8= 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=oVfgw6ZW; arc=fail smtp.client-ip=52.101.65.24 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="oVfgw6ZW" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Oeb0y8u0LMIBz3SB7KCfWcC1bGmRtuPAMxjOFlaDwriKmZWPxgsfr7gOxj/KjA7Lj6zA2OAcW6+Op+YOdwZKBPq1CFbD48w8PvL4D+0z9iAXGAQyXhjHgODcBV/0gbN3xrN9a5wETDAscEw82JcXM/F1eMHYcBQmJ5ODH5zfHg2v2V7wYdQjzUZ1LWLievyS2NJeyEzz7u263Sdt+fvZOj/bXAxJkD+wG7Xh/h2EIfHxSqRo2+Dky38Og9Yrr+ummnl9Dmdg0dKC5AfG981NPu8QfAjS+EQDrqnbyp2qVc/hzzbZU8dQaLbmkvcRmHI6QwmjFi1EJb0LzOWVKiv74g== 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=TcSPYBv7x2jsowhnfO/nfbNx5GB5FOGBDfK5Yoad+eA=; b=l1XQekPSJftR/np3NcaL3OwS9tuWk3UEttpsBL/TrbZBTACNUc+P8+TdKg0T87fb8AJnuZZ+S8RTjgGMCd3OzJHnSpVUMltBYjx5htJSl3JFHJObgl5NeXfuwabudXhWnznGZHF43b+BS35DedrI6kEP+rpTlSV4mXJ18XtpbXLIAP4XgjlZ3ZPxli92fTJrbWENdrl13Kd5ZsKHbmSwRZSXAA7buoSKGNne4/Ab+MoJ7xd/RllGM/OajsOBtY0cWixSvq0GkyJjigb+hInj7KyWb2i2ZXXfMJGpjq5n2GFkGI2L8cj1eg1ZPVWzWwluexvApNawJ2+iOQHg/G1oMg== 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=TcSPYBv7x2jsowhnfO/nfbNx5GB5FOGBDfK5Yoad+eA=; b=oVfgw6ZWMWkHKUdk87YVk8+7bVHem4QLz36887nM4cikY5o7HfwgAKJ9YhLmLkSCCQYZuW5MPQqV9QLTEbBkBJAhL58TOxFuNIme9Uz9LkqhHFe1kbSSU2kUzTsUI0Hm4LMbwM1D+3mu9Nf5v/9GC/2nywnhDIQpRfmXJklP3xgXPCZ1V1LZfz10HFDps0cWtHBVtb4K7RHkI9NM/q+dtxdL7OQJqNI3PwHlsIfDobaxq5Eq/r5vtlRlJTISE4Wr8C9/16w2WDf9AgS87R9mfxeVvc0LTHDIE3ve8+x/ORY06SqgLSVDeNQ8nzqS2sZ1gs2tfm1OxnhZ65Pi2Wc2QQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GVXPR04MB12314.eurprd04.prod.outlook.com (2603:10a6:150:30f::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Wed, 4 Feb 2026 18:05:30 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:30 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 08/21] wifi: nxpwifi: add channel/frequency/power (cfp) support Date: Thu, 5 Feb 2026 02:03:45 +0800 Message-Id: <20260204180358.632281-9-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GVXPR04MB12314:EE_ X-MS-Office365-Filtering-Correlation-Id: 354097b2-28e8-4bec-0a42-08de6417fb96 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|52116014|366016|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?mXP4GqXlYQOM/TaTY9hMpwgV3F4mNuJ70P8/6xHjxsETa6kf88OGByNp3gzC?= =?us-ascii?Q?hPanLuQxpRQ52o+97/K9UHxDRyVUibuozZz/6fd5N6oYj6HUUeSy0f0CGrRF?= =?us-ascii?Q?E5MPgbfjXShRv1mEoddqGhSH7hDNJ9SDN9W2rRfGxhQj9SAtynywIvGq4Xn9?= =?us-ascii?Q?h/h8CCvv97NEz3L1vH3Fv9U8XgdPF9/iiu8qokdBTLAp7DdkMjWRpasvSmVk?= =?us-ascii?Q?PxxkipKirD+0hKgE8BI24Le6cH+S0KRPNXIHcgk6+P7EgQLwxwRkAdXwYbhy?= =?us-ascii?Q?aqhBQNPzcLg/Adq8zgrdZlBHdAY4jvh5TFvBcsyAf2BZvUw7k4BIiqEwaSKs?= =?us-ascii?Q?Z4Ip6Auo7/FC2I6jt/3CoF2gyRccIPnCM6xLrnFzbBli5WouOkBDXWNY8l8q?= =?us-ascii?Q?o8vKUSv64DzfXu9nrIVw2Q2yRpl0TW6wgruEaAA1y2oS3I7YOdubDZnD+FnG?= =?us-ascii?Q?t+8ZByoyJqOVwS9ZiIyzH/yK9bWmM5R5KkBm77ahIXmfvGQNUioS8E041e58?= =?us-ascii?Q?WPqY6ZlHEQwqIlw9WQAKEzVcOKl21lCUM0k7JDpDbOXy7Nc/fbXJNn1XNfIN?= =?us-ascii?Q?oxK2/65cnPg+aJYFe33sagyUHMhmLPtejNip4/YO4E7fOAPwjCT397Qvy1SR?= =?us-ascii?Q?xs7f/Dj1IJ934XtLB/WyHlDP5NjH0yM1uJF064s7NnEAwHPR339cdq3N4452?= =?us-ascii?Q?NGULIjUjHBxx00FD8GYTfcrbLHZOtRIN0nTgMXPMAheqXz91Nz22Jeov6PCm?= =?us-ascii?Q?JKNtFc+0jXmEWi2Dc8cGstu0JW4qgxqpAaD7ZIWU3oRX+kzJfZpl5e/SWqnp?= =?us-ascii?Q?7o2NV3wBCc6ZlCT0Vl8CZ+8WKOcnUFsuvTgjLqCf0GjLchapvUvM5iqQ3WtA?= =?us-ascii?Q?phH0AlGPYCSzzjkhbpF+ifYsusKbQ+f6BgY0yykBiaFxfPGVgtJdrIbUsntc?= =?us-ascii?Q?T4zm9u25nKLnC19+Im8bBvoztXWlENwvOfLOKulZZ/CfpvwfroejbyseuoEq?= =?us-ascii?Q?GAupuehBeFYuZGTrCBMI+JgCGrS4xbynW4TAZiYqjKVwZYUzQoguOtpQY3Fo?= =?us-ascii?Q?whRK9QrKGKAHDzJj7ulvdANJMgbh+zXYOIwRBSoqfNyP3f8mOR32A34K2Dd1?= =?us-ascii?Q?gC3X8UpThAl6FQ2vKTE0Wtx4TDPjkwSBVExG5qgIz91V5Q82tJYsZaXhdc4X?= =?us-ascii?Q?FoMLGFCsBPnl8SbWWYrjw5ec0TtHJADyk3AMyFGjKxsqsdeRkLlRLGXZKTt7?= =?us-ascii?Q?NfTfF9pv2YyN1LZRw4Vc3VsF7T/BsY0WDVXNA7jtRsrc4/GqWusDazEF78WS?= =?us-ascii?Q?rNvJzBuZq/luBKtCvx1x/EoI2jF+a+IG/5jJ73blopHd5l88lJvOYUurj3BS?= =?us-ascii?Q?gNFum9sCzZStLzw+0uiExDSparSkAy0BAxrVvadgI6AFpqjrZkEzTFiklm/5?= =?us-ascii?Q?xA7S6nWnVhuTpqb+m9kU8LmhRQ6+ohc9z+p9a1pb//AEifSMkN73DCm3ay2f?= =?us-ascii?Q?StBOyPsAeDP8BfVJjLDkpG7cIQfnndHC02vK0B2f5FxywdAONkzikhHXgT3x?= =?us-ascii?Q?x911m0ELv8tYHfILGCLm8kTicAdBsa/8FL5QEfBmKkEGcYaljQ5A+q6k8nkD?= =?us-ascii?Q?Irk6uN6A40uDDne7OrR2ZfE=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(52116014)(366016)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?sWACDKeVrpW/OHAdHAb2HpW7Nno3ZJO8NXoadC2dR8JzYwbRwmQd7wOwQmTo?= =?us-ascii?Q?pv8UcJGKmTQ3mdUtR0BpfqNAnM649pc26bRRqG97it+1gsmOi7uElcysPNt5?= =?us-ascii?Q?MFT2I/K8nTM0hP6nxwuumGfqS1C+SVcXyxUdS3En1c2P1vQLZCi2jy2byYrM?= =?us-ascii?Q?e/I9uMxGcTTs/s2Ot1M/YGYl8PCTDpauWnJIrl9AiIIIURWHMtrpJoy7ZpZQ?= =?us-ascii?Q?cxcy+kFc3i76Y9A6X5FNMahG0q1rnUbUn3nm3qOQ+Bd10fafgB1DFh1CANOm?= =?us-ascii?Q?OLWDBU/7L+XJdt/m7hJmKPdxEdCD4JwJ5hv96fegPY2A6A8fOImkJg7q02iM?= =?us-ascii?Q?GzNklQLOZiBMc95/R1+YKKUYOlx5CYvwaFha21LrIagQa5WTH7yD/7k2HPL+?= =?us-ascii?Q?CI50vzr0pzLWXAGKMpzeUFSyV1WpLR5IaULScU3CrSox3RXKtaSNMFSZf4ve?= =?us-ascii?Q?o/Opc+8uBYMW81HDzHay+Pf9pDWb23nlRHph1fSsfL34uP2Ns1/5exbGUZ1p?= =?us-ascii?Q?d7fIU1uy6ZIaYBCgxFqXcD1+DPXAtD84aro5nRGM9Y97pxiTfVYLF7WpcTmz?= =?us-ascii?Q?v5P8ZRuz/TjsblFFMgTY3Ztb9Z/wvaUYiUU+7X9noWZ0huKXSjbzI/90WgIX?= =?us-ascii?Q?LKjOKsOJ0vC1q3mLOkVJhkskZ+tRv52KuqyBT7fuMb0IGu+tlLRemzDpDk6u?= =?us-ascii?Q?MJ2UDD17kR1cnnjlCk+w7rujRp4znyU61SmDjYQpt72xzRx1n/b6muOW2fIH?= =?us-ascii?Q?yZdg1M2U2xfTRm3c6gaiaD0SuB/DBMOHInenihnfV+2ti3S7oq4gfHHEi/CL?= =?us-ascii?Q?3Uv0GAe1ZVP1xVpkJ7NCduiEPA2giZngLeFQuyf2LipSgb7PYNEYOAo5yp3a?= =?us-ascii?Q?f5iOoY7DlozSAA7k24YcCTlpTvKstMk8tdTsiSfy4QKRo5/yqC0k9Lc5/1I2?= =?us-ascii?Q?Ly9m9gjUqtm5Hh5y1DYctW9V3KQeBEfPSLPDiPRL5NdflZ/ENc2RcQ763txz?= =?us-ascii?Q?1CFBZ5bikUApjgB1na2dlW0Kh+ZYrHhfkCGUK18T1x1Vy7T/LML2je5hE1mc?= =?us-ascii?Q?pGAgFVa8QQqhgSKBs5QlmogefqsHs6ycf1tvRK0sCeIbEjzGglKnYpSsEE0l?= =?us-ascii?Q?HxlGCP4UOkDhITteaDK6oZv1W19y8qX2++CVErBA3QS9zJX8b0bz/2o9eH4+?= =?us-ascii?Q?vO5gqYKoZf0TBSR/+XXDtW/VYSx0+wDYp8bl5RzSs4/QWEeh6+mYcSDLUEAy?= =?us-ascii?Q?hlY5WMGSBjJiZavZvuylbrbF3CfAIXbJITipAqbm0jh1Oca5c9LLbeOrlhra?= =?us-ascii?Q?Ol8LOwQBWzJmxtPs1kqSNfVH2RBzi5BqpollEqQ4UglGIEWhVPJThgGAU9h5?= =?us-ascii?Q?moUhN3W4niZZuedmNLraZo5O9DjbVTPZ9XLeEADyllUQl4mZRjboVUvgSTIb?= =?us-ascii?Q?rkd32KmOg+blzQUXnHDD/LLiU5/iaK4dIMnG4u+KWT8N/3FEIR2jO5O/QAZp?= =?us-ascii?Q?c/rgp0xolVhMJn8IsXREePPoB0E25pd3aPyJ3yz0cNfvV55BWA3KcTqt2LiU?= =?us-ascii?Q?JtGpGEaIjiWLDBUpjllgfw4grJmVqQjI0QGmwrtsw6WgwNtDRpieACXlj0mz?= =?us-ascii?Q?LkPDWTFX4c0047xOQkhA14akesXgt14jAN+lQggVp7ZNGQ926X8fIWCSB4vv?= =?us-ascii?Q?7rHJL/xoii0SSJCr7sNOOxYuHIcLdR8W48qUkRSNpwOrMIJWNkKFMXobrA5A?= =?us-ascii?Q?EKudVPmWGw=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 354097b2-28e8-4bec-0a42-08de6417fb96 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:30.2651 (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: DUjxJXpEmcfxTy9ufAP6vMpjNSP+EZ7twY0+Liv3D3KnCZBEVZ0W/75ofhnTCFRwfl4HoM0TEFUtnkmZDT2r/A== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GVXPR04MB12314 Content-Type: text/plain; charset="utf-8" Introduce cfp.c to provide channel, frequency, and power-related utilities for the nxpwifi driver. - Defines supported data rates for 802.11a/b/g/n/ac - Implements rate-to-index and index-to-rate mapping for HT/VHT formats - Provides region code to country mapping for 802.11d support - Adds helpers for: - Determining active/supported data rates - Mapping channel/frequency/power (CFP) triplets - Adjusting RX data rate index - Extracting rates from cfg80211 scan requests This module serves as a foundational component for rate control, regulatory compliance, and scan/join operations. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/cfp.c | 458 +++++++++++++++++++++++++ 1 file changed, 458 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..e4adbeb6a09c --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfp.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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] =3D { 0x0c, 0x12, 0x18, 0x2= 4, + 0xb0, 0x48, 0x60, 0x6c, 0 }; +static u16 nxpwifi_data_rates[NXPWIFI_SUPPORTED_RATES_EXT] =3D { 0x02, 0x0= 4, + 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] =3D { 0x02, 0x04, 0x0b, 0x1= 6, 0 }; + +static u8 supported_rates_g[G_SUPPORTED_RATES] =3D { 0x0c, 0x12, 0x18, 0x2= 4, + 0x30, 0x48, 0x60, 0x6c, 0 }; + +static u8 supported_rates_bg[BG_SUPPORTED_RATES] =3D { 0x02, 0x04, 0x0b, 0= x0c, + 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0 }; + +u16 region_code_index[NXPWIFI_MAX_REGION_CODE] =3D { 0x00, 0x10, 0x20, 0x3= 0, + 0x31, 0x32, 0x40, 0x41, 0x50 }; + +/* mcs_rate: first 8 entries for 1x1; all 16 for 2x2. */ +static const u16 mcs_rate[4][16] =3D { + /* 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] =3D { + /* 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] =3D { + /* 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[] =3D { + { 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 */ +}; + +/* Convert 11d country code to region string. */ +u8 *nxpwifi_11d_code_2_region(u8 code) +{ + u8 i; + + /* Look for code in mapping table */ + for (i =3D 0; i < ARRAY_SIZE(region_code_mapping_t); i++) + if (region_code_mapping_t[i].code =3D=3D code) + return region_code_mapping_t[i].region; + + return NULL; +} + +/* Map supported rate index to AC/VHT data rate. */ +u32 nxpwifi_index_to_acs_data_rate(struct nxpwifi_private *priv, + u8 index, u8 ht_info) +{ + u32 rate =3D 0; + u8 mcs_index =3D 0; + u8 bw =3D 0; + u8 gi =3D 0; + + if ((ht_info & 0x3) =3D=3D NXPWIFI_RATE_FORMAT_VHT) { + mcs_index =3D min(index & 0xF, 9); + + /* 20M: bw=3D0, 40M: bw=3D1, 80M: bw=3D2, 160M: bw=3D3 */ + bw =3D (ht_info & 0xC) >> 2; + + /* LGI: gi =3D0, SGI: gi =3D 1 */ + gi =3D (ht_info & 0x10) >> 4; + + if ((index >> 4) =3D=3D 1) /* NSS =3D 2 */ + rate =3D ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index]; + else /* NSS =3D 1 */ + rate =3D ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index]; + } else if ((ht_info & 0x3) =3D=3D NXPWIFI_RATE_FORMAT_HT) { + /* 20M: bw=3D0, 40M: bw=3D1 */ + bw =3D (ht_info & 0xC) >> 2; + + /* LGI: gi =3D0, SGI: gi =3D 1 */ + gi =3D (ht_info & 0x10) >> 4; + + if (index =3D=3D NXPWIFI_RATE_BITMAP_MCS0) { + if (gi =3D=3D 1) + rate =3D 0x0D; /* MCS 32 SGI rate */ + else + rate =3D 0x0C; /* MCS 32 LGI rate */ + } else if (index < 16) { + if (bw =3D=3D 1 || bw =3D=3D 0) + rate =3D mcs_rate[2 * (1 - bw) + gi][index]; + else + rate =3D nxpwifi_data_rates[0]; + } else { + rate =3D nxpwifi_data_rates[0]; + } + } else { + /* 11n non-HT rates */ + if (index >=3D NXPWIFI_SUPPORTED_RATES_EXT) + index =3D 0; + rate =3D nxpwifi_data_rates[index]; + } + + return rate; +} + +/* Map supported rate index to data rate. */ +u32 nxpwifi_index_to_data_rate(struct nxpwifi_private *priv, + u8 index, u8 ht_info) +{ + u32 mcs_num_supp =3D + (priv->adapter->user_dev_mcs_support =3D=3D 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 =3D=3D NXPWIFI_RATE_BITMAP_MCS0) { + if (ht_info & BIT(2)) + rate =3D 0x0D; /* MCS 32 SGI rate */ + else + rate =3D 0x0C; /* MCS 32 LGI rate */ + } else if (index < mcs_num_supp) { + if (ht_info & BIT(1)) { + if (ht_info & BIT(2)) + /* SGI, 40M */ + rate =3D mcs_rate[1][index]; + else + /* LGI, 40M */ + rate =3D mcs_rate[0][index]; + } else { + if (ht_info & BIT(2)) + /* SGI, 20M */ + rate =3D mcs_rate[3][index]; + else + /* LGI, 20M */ + rate =3D mcs_rate[2][index]; + } + } else { + rate =3D nxpwifi_data_rates[0]; + } + } else { + if (index >=3D NXPWIFI_SUPPORTED_RATES_EXT) + index =3D 0; + rate =3D nxpwifi_data_rates[index]; + } + return rate; +} + +/* Return current active data rates (depends on connection). */ +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); +} + +/* Find Channel/Frequency/Power by band and channel or frequency. */ +struct nxpwifi_chan_freq_power * +nxpwifi_get_cfp(struct nxpwifi_private *priv, u8 band, u16 channel, u32 fr= eq) +{ + struct nxpwifi_chan_freq_power *cfp =3D NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch =3D NULL; + int i; + + if (!channel && !freq) + return cfp; + + if (nxpwifi_band_to_radio_type(band) =3D=3D HOST_SCAN_RADIO_TYPE_BG) + sband =3D priv->wdev.wiphy->bands[NL80211_BAND_2GHZ]; + else + sband =3D 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 =3D 0; i < sband->n_channels; i++) { + ch =3D &sband->channels[i]; + + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (freq) { + if (ch->center_freq =3D=3D freq) + break; + } else { + /* Find by valid channel. */ + if (ch->hw_value =3D=3D channel || + channel =3D=3D FIRST_VALID_CHANNEL) + break; + } + } + if (i =3D=3D sband->n_channels) { + nxpwifi_dbg(priv->adapter, WARN, + "%s: cannot find cfp by band %d\t" + "& channel=3D%d freq=3D%d\n", + __func__, band, channel, freq); + } else { + if (!ch) + return cfp; + + priv->cfp.channel =3D ch->hw_value; + priv->cfp.freq =3D ch->center_freq; + priv->cfp.max_tx_power =3D ch->max_power; + cfp =3D &priv->cfp; + } + + return cfp; +} + +/* Return true if data rate is set to auto. */ +u8 +nxpwifi_is_rate_auto(struct nxpwifi_private *priv) +{ + u32 i; + int rate_num =3D 0; + + for (i =3D 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) + if (priv->bitmap_rates[i]) + rate_num++; + + if (rate_num > 1) + return true; + else + return false; +} + +/* Extract supported rates from cfg80211_scan_request bitmask. */ +u32 nxpwifi_get_rates_from_cfg80211(struct nxpwifi_private *priv, + u8 *rates, u8 radio_type) +{ + struct wiphy *wiphy =3D priv->adapter->wiphy; + struct cfg80211_scan_request *request =3D priv->scan_request; + u32 num_rates, rate_mask; + struct ieee80211_supported_band *sband; + int i; + + if (radio_type) { + sband =3D wiphy->bands[NL80211_BAND_5GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask =3D request->rates[NL80211_BAND_5GHZ]; + } else { + sband =3D wiphy->bands[NL80211_BAND_2GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask =3D request->rates[NL80211_BAND_2GHZ]; + } + + num_rates =3D 0; + for (i =3D 0; i < sband->n_bitrates; i++) { + if ((BIT(i) & rate_mask) =3D=3D 0) + continue; /* skip rate */ + rates[num_rates++] =3D (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 =3D 0; + + if (config_bands & BAND_B) + bands |=3D BAND_B; + if (config_bands & BAND_G || config_bands & BAND_GN || + config_bands & BAND_GAC || config_bands & BAND_GAX) + bands |=3D BAND_G; + if (config_bands & BAND_A || config_bands & BAND_AN || + config_bands & BAND_AAC || config_bands & BAND_AAX) + bands |=3D BAND_A; + + return bands; +} + +/* Get supported rates in infrastructure (STA/P2P client) mode. */ +u32 nxpwifi_get_supported_rates(struct nxpwifi_private *priv, u8 *rates) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u32 k =3D 0; + u16 bands =3D 0; + + bands =3D nxpwifi_convert_config_bands(adapter->fw_bands); + + if (priv->bss_mode =3D=3D NL80211_IFTYPE_STATION) { + if (bands =3D=3D BAND_B) { + /* B only */ + nxpwifi_dbg(adapter, INFO, "info: infra band=3D%d\t" + "supported_rates_b\n", + priv->config_bands); + k =3D nxpwifi_copy_rates(rates, k, supported_rates_b, + sizeof(supported_rates_b)); + } else if (bands =3D=3D BAND_G) { + /* G only */ + nxpwifi_dbg(adapter, INFO, "info: infra band=3D%d\t" + "supported_rates_g\n", + priv->config_bands); + k =3D 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=3D%d\t" + "supported_rates_bg\n", + priv->config_bands); + k =3D 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=3D%d\t" + "supported_rates_a\n", + priv->config_bands); + k =3D 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 =3D 0; + + /* HT40 */ + if ((rate_info & BIT(0)) && (rate_info & BIT(1))) + rate_index =3D NXPWIFI_RATE_INDEX_MCS0 + + NXPWIFI_BW20_MCS_NUM + rx_rate; + else if (rate_info & BIT(0)) /* HT20 */ + rate_index =3D NXPWIFI_RATE_INDEX_MCS0 + rx_rate; + else + rate_index =3D (rx_rate > NXPWIFI_RATE_INDEX_OFDM0) ? + rx_rate - 1 : rx_rate; + + if (rate_index >=3D NXPWIFI_MAX_AC_RX_RATES) + rate_index =3D NXPWIFI_MAX_AC_RX_RATES - 1; + + return rate_index; +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010069.outbound.protection.outlook.com [52.101.69.69]) (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 3EB4A423A70; Wed, 4 Feb 2026 18:05:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.69 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228339; cv=fail; b=JjxDUTOQIAQr2Mk/zQaUs8SPFJtodKSWJYgKKwFJ1+oP+QJWrednEqq9Ja1ZuusOsAJdq7U9fTWSw77vO0Fy/7l7n9nNhEmIsuMjEjOMSpv3X/IF3rLn3uQ/wU8ddLdmCSPIqlEcHvDb/Ic1qfoj+SmdZ87tql1WcYdcHYD/zwc= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228339; c=relaxed/simple; bh=NEYTCVFyj4PVzzYwjD1CwXFsKV8sdAS4KVc0zgTZUf4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=LUSwBUXEMrLANUL6rbda9dgNJkmhFS4X3d4pYv5lh7b19Hm8aXGY6lGdyWpKBKEvv9JqJcjlEUiM6jNN/Qri7KjzrPLs0pX10W6frwfTOYeGPwOHKa0SloYu5/XOqC78HCOzr24VxeZRHZ5iKiICUTIGc0JzS8RK18cMP3QZR/o= 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=Dt0T5xEs; arc=fail smtp.client-ip=52.101.69.69 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="Dt0T5xEs" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=QPbRekyzv1dFUjL3V02oOs51cR7E2hbsDPu4S6/B/BCTRFhrSkQ44PwSKuQvMWyHY0kf2M62RW0k/oUiXRZ/1rn43/fkDfhNJTwzzbiWpZx4RDeTx8RJFyW1aCoVVvKq1CX1Eoorr3DwMCr3HaN6zwhJM1Ka5UWb7taiyyOCBMR9I1xQdmR7uVMwfGBuug/HzlLG//CSPG55xo0BJt7ey9iLw+0oXXddbKWsFwc/s6kpkaNVyfPV/XJ/H6K1HTPw3vVj3Ag0D27zUZcDN69h6F/l0wiWddri2T7gyY7Svv1ArJibOUSPbUzuv90UbDzXxLzKUrUKVpM0kzNNytrWvg== 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=PuCqCe02e9uGt0kAH/jJgv4yRBrf8FMAHylaymyfU68=; b=T51d0bWv5/FInsN8zm2BYnaDyLMvnZoe7mOXBBYqevmqmc4ph7WWZvWTOT0NGZZGiZR6X8A4b5ei0U2r72ybtqm5Vjkx3dBVYj9448EaDVdersYnTM893qCGQ97iaYb9h/cLFgU5cfNCrZ1gj06+Owf/Rk1UXt27O+lur44CbmSqZzXpNTMCAwqziOVwOHZ6mfLmrga2H3h71Ygl/VIPq1M8iYkvisK7OWLM2h/PNamb5C0qy8RgWdZmUCSx6oIKAtXcpi2lBw+/CyIGoh2BPQYDKXqAG+HcovoA81a9iMdeiofx9DslYUBNcEUDS8A9jegkUJHvY40E9/F9fVJybA== 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=PuCqCe02e9uGt0kAH/jJgv4yRBrf8FMAHylaymyfU68=; b=Dt0T5xEsCyuqo2PONCiXlH6vCE7CYoMprMCKXrvG+8O4ag4bnTZmhZpxMRL9+ZM9cFPrHd9xoVFTvrhPSpFjs2CJWWZOkrrgyjCU284uMC0qvFSlvQBjLA7aU99Ui7T0BtKeXFDH2/p1/gISvDNKn29WlQI21nST7niIa7O8JSF+uHQx2baddTDd+PnwCPjeAG681vB8aPM/wHEFY8Wl2a8xsNwTiFTFBUfGYk1t6QZkm4R1e4x8ExoRYHhgtA57F41DTiD5bPKszy/RZvldHIhcjUPic5SA5Fi3vOXB6D2zhmjXWHu7mpNhYfUsMwSy80o+Fdw8gan7UH+i/pfaWw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:33 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:33 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 09/21] wifi: nxpwifi: add configuration support Date: Thu, 5 Feb 2026 02:03:46 +0800 Message-Id: <20260204180358.632281-10-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 582f0b69-a19d-4334-a6a6-08de6417fd2c X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?PT2Mfs0jxrbZxE0O9V4VW0kJI1QCw2Nm0awnKAJii6YwMbPx9EYvwrfL5KWi?= =?us-ascii?Q?8Hx5+eHYchL8KjprHXCJ0gmACzWJr6/KEK/VfCo/Op3uGPsTkkes/at93vwc?= =?us-ascii?Q?m97owhjotGB4K8fPahPG8+Yr8uuDboqvH9fkBRuM0bD5Ko6TeBZAfp5WDAmK?= =?us-ascii?Q?Ol3s9J7/dtNavy8gMiTUpT4rAk1a74wgwjgyF8AoFRLe8vwLnmpKJB7WwZpd?= =?us-ascii?Q?opduHnow9NbPjiGQFofeVmRK5+YCsA518HHZEGCb1Ic4Zo6oDacOzWThShoR?= =?us-ascii?Q?b9KBem50MPiSY+nMzGHroT1n4FW6SydNKn/vhouQZUwXrnIxiwhEOB8foWZO?= =?us-ascii?Q?BtIJBg+j0WFoZ84ZZdn2ziA8nAbtqUasJ9sJFh4+14ogNtLnAzHqeRvHDezx?= =?us-ascii?Q?3dL7BkMf/wmZVPdjtT8FJBobpXPqPwPXGcgx6/Uyj0+KrsCvdnoA44DzQT3d?= =?us-ascii?Q?5bHbIvPfX3knjpwr+VjBKV471nGod8Ei3moUFwuCMK3XPgoWM+3Iud4OFXi8?= =?us-ascii?Q?x21NkezBcsqksRKgOrVlajbWBMC6Q15S63nYVFMQ4osRyxKYWQeBFUO1AKA2?= =?us-ascii?Q?5TlVOWPm1CR+cHcLDDMc9owhXqGqZelW/oxwo3QzOprMfsQ8EaXslhiCBHXD?= =?us-ascii?Q?68Dk6tgY3/E71jIHvf5JEZ/sk5awvx6lz0je0I/5KFyyVYOr8PXCLEfAAXU5?= =?us-ascii?Q?9wFq7KTgRPPYWz+VlCdmNkd3VAz0O8vJLDSNgWI7V2yGiZ2RXyr2vjnW0xW6?= =?us-ascii?Q?ZLf4NSxj5fKT0m7i6BSaRl72Xlx0MBRxzg8Whg+oVfzo6bYjnGCYKQDNBXrl?= =?us-ascii?Q?VsPSDXDRQw9dGGB56LfePy+5Bj9e8bIwMDrwIUMRYrrX0hqo4/kHOgDYuIc2?= =?us-ascii?Q?lwH0Vqzr+T7916pTOGjzikJ3r61VD+46WeOZc3wWpYZjavQQUNmJUIy2SSFG?= =?us-ascii?Q?jnVWEAroOjQNjyh1T29r/x20Q2p0ZNYJlbCDeY6f5yfnC/iflM7WC7W+uzJ2?= =?us-ascii?Q?oxTaQegnJ5ljW8HZkKnxjbUI9ZGV4ExLLhIK8ceUhvqfDB1ijsT3uOyGDuQn?= =?us-ascii?Q?nqJulNRrVSGP10/NU0w9OH20J1FGeWbk7vvRDrVzq/FDJeHiIPAssGzYQr/j?= =?us-ascii?Q?+IHZt1+5CoN1l8EYo0i73W6VvJ+pE3JMjZwDvDbfXJwMYTZ9C0hhG72AZiNl?= =?us-ascii?Q?PgwG762Hek7dtfJ01L5uIkDm/D4udPEg+gc07xUML9uXr4AD5fsOlWJ8nuf6?= =?us-ascii?Q?eeGlGr5Y/oftE8Zl6ZbxqjXk7Ac3C+qH4uhm/WJscJiuz6PEThZR1DgikNuH?= =?us-ascii?Q?DNkqUK9QIrISdto0knzxBPcEaHjnBx3SSrLMJrWcgyl4knlYeI5+/XF4SjYT?= =?us-ascii?Q?rz5hlgBXDPblTCy1EwqP9fSwzughYBfH/bjrIxw0Ty/JcDPOfFqK9b6gFNcE?= =?us-ascii?Q?cjru39ZEDtVjW0fx1r9wcEh4uykrMNbte0LhIOeCAf9mtbZJXmuftvuQD+AR?= =?us-ascii?Q?TVDFx1Jfo5hI78MK7FN3IuHGgx3Hy4smXqVDcyOvfsCn+3ju4N5jDWyBl3BJ?= =?us-ascii?Q?e3ZCRMVTYS3+gjM+BMDCP0P1YeFfHCRUElu7v6J0oLbMortfNZlNrj9ecu6n?= =?us-ascii?Q?hn248L1vSVyyr5zi+c3TmoE=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?sSgOVb0BzsGCMm1F4HIR1eUtk7PPmk38B6bdbw8d2cVeiiPY9x9utpLeJAWm?= =?us-ascii?Q?ETjmgGehU48a60VGu2DLlBfSTtJg5j+JRCKDiKMw5bgOM7cK+IfNdPj6exTY?= =?us-ascii?Q?AVXSCbjMi4J86hB7a40tJ7+lWA9UFgmlg4y/T8p8nTu1CvwVzkkovzjWkC5n?= =?us-ascii?Q?Sq/quGR90CI/vl+iU5VU+e1RH7ns9fp4QlDUtLD+sqmI4Ap6l8Vw1SthMAtB?= =?us-ascii?Q?zchF4yPSsj4dGCDNdQex6Nag6lqY8cXKCbrw8XPwDGXFlUqtqW3oZpobbKio?= =?us-ascii?Q?sLeqdH3xhGethLb8GW1p/sazBiX+N2mzDX/6Mx32oIRnJ+TKFL9KlEHQwz6Q?= =?us-ascii?Q?Vd5yFejEr0b+AjLSmOYxhFfuBu3DJklRINwsj3WTQZ/Htha1BRrJLNv2mAAP?= =?us-ascii?Q?8aHIa+WM7Nd/zOh2vE+hOJctCd4f0IQHp0lJJFU3UOg5Hq+a2GAoCu5JHdKn?= =?us-ascii?Q?65fnzPAqj0qYDi2GSxCgO0GVbYQm39WzB0liEJAakDyUJ1c1T+GlaMyRaMT4?= =?us-ascii?Q?F1UPVRNq0nt35TwwN5g6odMmNdtrVCxC/uj8rf1AxMuJNWlAzl2KYasgQNBV?= =?us-ascii?Q?C7YObx+sKe29Qj0mlIJgRUiPBQE/70bBSggkH6pbTe9IGoc8Uh0PxUSDoW2A?= =?us-ascii?Q?WPI37ngi0jj/Vu+EQPEUspDn6pnMKW0uRP5eJSbHcs/PSeDtLs6AcvEOK1j5?= =?us-ascii?Q?jhC8k0TfHNqXNFN9oVuNO6tBVVt3y05B3XgF0xouOIcn4dnjYwSq7YXsIn4c?= =?us-ascii?Q?3HNHjzWRINAF1MBXdNIOXpj0WdvuNVgglbGvbn7zRcz7qDtZouu9PZrCZfuh?= =?us-ascii?Q?M6GiRASHlg7e6VhVKa4UE0gt1BZbZ/hLeLig2Fg0mWtn0myKPRJQmXDzG74/?= =?us-ascii?Q?45cTgl+OdGAQerdJJR+UN6Ewv9kUkdV1jY5bKRYLVJe6QsDA/mPiNuCdqkUm?= =?us-ascii?Q?kOMM2Hbx8yrGBcm3EwRAc8gPf98g4J8UgOrGDQcNxOvC3bXbAYSjEsV9/ief?= =?us-ascii?Q?76ws72xUhFBxZ5n+dryxRTgMmIka0F7s3X02E+35/Xlkr2ulEYoJA/cUEPz+?= =?us-ascii?Q?MEA+PpSJzTnGnzV8Hyl1OplSUXhPl+JP7BnDWF+0KSABbKZVqEUEyWMFfCH+?= =?us-ascii?Q?LT0X0nHYyk+9kkX4KjdRzeAG6/vLpvWMXsrR2kXCymNGDK2xv3W+cfmL5M9v?= =?us-ascii?Q?2OJxv1TwtAYe98KzNA2y5VgpqlskLHZLf50qtB7wr9gAmBCAhaSMPhsyN8ka?= =?us-ascii?Q?5qWmEGN25/jeiqaVrAN4ZqVZhXmsu2ZZ2iqvlBA1HJt8DP8DPxWFFb4QZ5FQ?= =?us-ascii?Q?v2M0diz6UKuTeUIRGl+0ERZhzWKgjIq9RN+bN223vQQvCPge8qJJDbVpn43q?= =?us-ascii?Q?90+GQCcwo6ksP2uUho83zj1DwCcgmMD68Z1v+AiFRfZzwqJO1HcuIeT2LUuJ?= =?us-ascii?Q?zXPXRGejaIVKizDyJnoGsFH5jz9hLN8/Ipuf6qVHfkoWW6m7SgfV8AZSpH4u?= =?us-ascii?Q?QYff2ijNW8MsrUjeU9uFqDFec5hyfmFtOVpo9P1uoBcNgnwxEsZuCqcCTuwF?= =?us-ascii?Q?bMkI+jfSQgs7yNt5Mi8TiO3enBiJib+H6u7PxcUHUQ+gRfuDde9cta/6+E35?= =?us-ascii?Q?diM2DjEAIA0oNDCeXIlk0CN+Jyw6TP3JyYa3kJvUiu9yHhYr7m0JbEfhIT0b?= =?us-ascii?Q?ZUthY+2S6qgnn6Z6g/y8qUjNfujxy3Gx6mNpd6nTSVdMm0sdNmNWXKoki/UO?= =?us-ascii?Q?UJxuBn8oCQ=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 582f0b69-a19d-4334-a6a6-08de6417fd2c X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:33.1255 (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: oJD6rtBEIJEO9awTSiD0HVQxupbBBDRTMH0fylRMjrbNnSV53oK2WjRcZquNFUkQjSnuTrJ7CFWo5rl4JkEU1g== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" Introduce configuration infrastructure for the nxpwifi driver. - Add cfg.h to define constants, enums, and data structures for: - BSS roles and types - Power, channel, and rate control - WMM, 11n/ac/ax capabilities - Encryption (WEP/WPA/WPA2), WPS, and generic IEs - Host sleep, TWT, and coalescing filters - Register/memory/EEPROM access - Add sta_cfg.c to implement station-mode configuration and control: - Multicast list management - Association and deauthentication handling - Power save and deep sleep configuration - WPA/WEP key management and IE handling - TWT setup/teardown/report - IOCTL handlers for statistics, version, and register access These files provide the foundational configuration and control logic for the nxpwifi driver in STA mode. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/cfg.h | 993 +++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_cfg.c | 1177 ++++++++++++++++++++ 2 files changed, 2170 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..114e70cddf5c --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfg.h @@ -0,0 +1,993 @@ +/* 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 + +#define NUM_WEP_KEYS 4 + +#define NXPWIFI_BSS_COEX_COUNT 2 +#define NXPWIFI_MAX_BSS_NUM (3) + +#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=3DMCS0-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 +#define NXPWIFI_EXT_CAPAB_IE_LEN 10 + +enum nxpwifi_bss_type { + NXPWIFI_BSS_TYPE_STA =3D 0, + NXPWIFI_BSS_TYPE_UAP =3D 1, + NXPWIFI_BSS_TYPE_ANY =3D 0xff, +}; + +enum nxpwifi_bss_role { + NXPWIFI_BSS_ROLE_STA =3D 0, + NXPWIFI_BSS_ROLE_UAP =3D 1, + NXPWIFI_BSS_ROLE_ANY =3D 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 =3D 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 + +/* Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 +/* secondary channel is 80MHz bandwidth for 11ac */ +#define CHANNEL_BW_80MHZ 4 +#define CHANNEL_BW_160MHZ 5 + +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 =3D 0, + RDWR_STATUS_FAILURE =3D 1, + RDWR_STATUS_DONE =3D 2 +}; + +enum nxpwifi_chan_band { + BAND_2GHZ =3D 0, + BAND_5GHZ, + BAND_6GHZ, + BAND_4GHZ, +}; + +enum nxpwifi_chan_width { + CHAN_BW_20MHZ =3D 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, + CHAN_BW_80MHZ, + CHAN_BW_8080MHZ, + CHAN_BW_160MHZ, + CHAN_BW_5MHZ, +}; + +enum { + NXPWIFI_SCAN_TYPE_UNCHANGED =3D 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 =3D 1, + BAND_G =3D 2, + BAND_A =3D 4, + BAND_GN =3D 8, + BAND_AN =3D 16, + BAND_GAC =3D 32, + BAND_AAC =3D 64, + BAND_GAX =3D 256, + BAND_AAX =3D 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 0x03 +#define NXPWIFI_SEC_CHAN_ABOVE 0x01 +#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; + u32 key_cipher; + 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 =3D 0, + NXPWIFI_IE_TYPE_ARP_FILTER, +}; + +enum { + NXPWIFI_REG_MAC =3D 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 =3D 1, + NXPWIFI_FUNC_SHUTDOWN, +}; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ =3D 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST =3D 1, + PACKET_TYPE_MULTICAST =3D 2, + PACKET_TYPE_BROADCAST =3D 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 +#define NXPWIFI_11AX_TWT_INFORMATION_SUBID 0x119 +#define NXPWIFI_11AX_BTWT_AP_CONFIG_SUBID 0x120 +#define BTWT_AGREEMENT_MAX 5 + +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=3D1,disable=3D0 */ + u8 llde; + /* operation mode: default=3D0,carplay=3D1,gameplay=3D2 */ + u8 mode; + /* trigger frame rate: auto=3D0xff */ + u8 fixrate; + /* cap airtime limit index: auto=3D0xff */ + u8 trigger_limit; + /* cap peak UL rate */ + u8 peak_ul_rate; + /* Downlink LLDE: enable=3D1,disable=3D0 */ + u8 dl_llde; + /* Set trigger frame interval(us): auto=3D0 */ + 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; +}; + +struct nxpwifi_twt_setup { + /** Implicit, 0: TWT session is explicit, 1: Session is implicit */ + u8 implicit; + /** Announced, 0: Unannounced, 1: Announced TWT */ + u8 announced; + /** Trigger Enabled, 0: Non-Trigger enabled, 1: Trigger enabled TWT */ + u8 trigger_enabled; + /** TWT Information Disabled, 0: TWT info enabled, 1: TWT info disabled */ + u8 twt_info_disabled; + /* + * Negotiation Type, 0: Future Individual TWT SP start time, 1: + * Next Wake TBTT time + */ + u8 negotiation_type; + /* + * TWT Wakeup Duration, time after which the TWT requesting STA can + * transition to doze state + */ + u8 twt_wakeup_duration; + /** Flow Identifier. Range: [0-7]*/ + u8 flow_identifier; + /* + * Hard Constraint, 0: FW can tweak the TWT setup parameters if it is + * rejected by AP. + * 1: Firmware should not tweak any parameters. + */ + u8 hard_constraint; + /** TWT Exponent, Range: [0-63] */ + u8 twt_exponent; + /** TWT Mantissa Range: [0-sizeof(UINT16)] */ + __le16 twt_mantissa; + /** TWT Request Type, 0: REQUEST_TWT, 1: SUGGEST_TWT*/ + u8 twt_request; + /** TWT Setup State. Set to 0 by driver, filled by FW in response*/ + u8 twt_setup_state; + /** TWT link lost timeout threshold */ + __le16 bcn_miss_threshold; +} __packed; + +struct nxpwifi_twt_teardown { + /** TWT Flow Identifier. Range: [0-7] */ + u8 flow_identifier; + /* + * Negotiation Type. 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time + */ + u8 negotiation_type; + /** Tear down all TWT. 1: To teardown all TWT, 0 otherwise */ + u8 teardown_all_twt; + /** TWT Teardown State. Set to 0 by driver, filled by FW in response */ + u8 twt_teardown_state; + /** Reserved, set to 0. */ + u8 reserved[3]; +} __packed; + +#define NXPWIFI_BTWT_REPORT_LEN 9 +#define NXPWIFI_BTWT_REPORT_MAX_NUM 4 +struct nxpwifi_twt_report { + /** TWT report type, 0: BTWT id */ + u8 type; + /** TWT report length of value in data */ + u8 length; + u8 reserve[2]; + /** TWT report payload for FW response to fill */ + u8 data[NXPWIFI_BTWT_REPORT_LEN * NXPWIFI_BTWT_REPORT_MAX_NUM]; +} __packed; + +struct nxpwifi_twt_information { + /** TWT Flow Identifier. Range: [0-7] */ + u8 flow_identifier; + /* + * Suspend Duration. Range: [0-UINT32_MAX] + * 0:Suspend forever; + * Else:Suspend agreement for specific duration in milli seconds, + * after than resume the agreement and enter SP immediately + */ + __le32 suspend_duration; + /** TWT Information State. Set to 0 by driver, filled by FW in response */ + u8 twt_information_state; +} __packed; + +struct btwt_set { + u8 btwt_id; + __le16 ap_bcast_mantissa; + u8 ap_bcast_exponent; + u8 nominalwake; +} __packed; + +#define BTWT_AGREEMENT_MAX 5 +struct nxpwifi_btwt_ap_config { + u8 ap_bcast_bet_sta_wait; + __le16 ap_bcast_offset; + u8 bcast_twtli; + u8 count; + struct btwt_set btwt_sets[BTWT_AGREEMENT_MAX]; +} __packed; + +struct nxpwifi_twt_cfg { + u16 action; + u16 sub_id; + union { + struct nxpwifi_twt_setup twt_setup; + struct nxpwifi_twt_teardown twt_teardown; + struct nxpwifi_twt_report twt_report; + struct nxpwifi_twt_information twt_information; + struct nxpwifi_btwt_ap_config btwt_ap_config; + } param; +}; +#endif /* !_NXPWIFI_CFG_H_ */ diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c b/drivers/net/wirel= ess/nxp/nxpwifi/sta_cfg.c new file mode 100644 index 000000000000..229f026fa866 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c @@ -0,0 +1,1177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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 */ +int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist, + struct net_device *dev) +{ + int i =3D 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 */ +int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_queued) +{ + int status; + + /* Wait for completion */ + status =3D wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, + *cmd_queued->condition, + (12 * HZ)); + if (status <=3D 0) { + if (status =3D=3D 0) + status =3D -ETIMEDOUT; + nxpwifi_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", + status); + nxpwifi_cancel_all_pending_cmd(adapter); + return status; + } + + status =3D adapter->cmd_wait_q.status; + adapter->cmd_wait_q.status =3D 0; + + return status; +} + +/* Set multicast list by issuing the proper firmware command */ +int +nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv, + struct nxpwifi_multicast_list *mcast_list) +{ + int ret =3D 0; + u16 old_pkt_filter; + + old_pkt_filter =3D priv->curr_pkt_filter; + + if (mcast_list->mode =3D=3D NXPWIFI_PROMISC_MODE) { + nxpwifi_dbg(priv->adapter, INFO, + "info: Enable Promiscuous mode\n"); + priv->curr_pkt_filter |=3D HOST_ACT_MAC_PROMISCUOUS_ENABLE; + priv->curr_pkt_filter &=3D + ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + /* Multicast */ + priv->curr_pkt_filter &=3D ~HOST_ACT_MAC_PROMISCUOUS_ENABLE; + if (mcast_list->mode =3D=3D NXPWIFI_ALL_MULTI_MODE) { + nxpwifi_dbg(priv->adapter, INFO, + "info: Enabling All Multicast!\n"); + priv->curr_pkt_filter |=3D + HOST_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + priv->curr_pkt_filter &=3D + ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE; + nxpwifi_dbg(priv->adapter, INFO, + "info: Set multicast list=3D%d\n", + mcast_list->num_multicast_addr); + /* Send multicast addresses to firmware */ + ret =3D 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=3D%#x, curr_pkt_filter=3D%#x\n", + old_pkt_filter, priv->curr_pkt_filter); + if (old_pkt_filter !=3D priv->curr_pkt_filter) { + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL, + HOST_ACT_GEN_SET, + 0, &priv->curr_pkt_filter, false); + } + + return ret; +} + +/* Fill BSS descriptor from cfg80211_bss */ +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 =3D (void *)bss->priv; + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + ies =3D rcu_dereference(bss->ies); + beacon_ie =3D kmemdup(ies->data, ies->len, GFP_ATOMIC); + beacon_ie_len =3D ies->len; + bss_desc->timestamp =3D 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 =3D bss->signal; + /* The caller of this function will free beacon_ie */ + bss_desc->beacon_buf =3D beacon_ie; + bss_desc->beacon_buf_size =3D beacon_ie_len; + bss_desc->beacon_period =3D bss->beacon_interval; + bss_desc->cap_info_bitmap =3D bss->capability; + bss_desc->bss_band =3D bss_priv->band; + bss_desc->fw_tsf =3D 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 =3D NXPWIFI_802_11_PRIV_FILTER_8021X_WEP; + } else { + bss_desc->privacy =3D NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL; + } + bss_desc->bss_mode =3D NL80211_IFTYPE_STATION; + + /* Disable 11ac by default */ + bss_desc->disable_11ac =3D true; + /* Disable 11ax by default */ + bss_desc->disable_11ax =3D true; + + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) + bss_desc->sensed_11h =3D 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[] =3D {"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 =3D + &priv->adapter->domain_reg; + int ret; + + rcu_read_lock(); + country_ie =3D ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + rcu_read_unlock(); + return 0; + } + + country_ie_len =3D 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] =3D country_ie[2]; + domain_info->country_code[1] =3D country_ie[3]; + domain_info->country_code[2] =3D ' '; + + country_ie_len -=3D IEEE80211_COUNTRY_STRING_LEN; + + domain_info->no_of_triplet =3D + 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 =3D 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 *b= ss, + struct cfg80211_ssid *req_ssid) +{ + int ret; + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_bssdescriptor *bss_desc =3D NULL; + u16 config_bands; + + priv->scan_block =3D false; + + if (adapter->region_code =3D=3D 0x00 && + nxpwifi_process_country_ie(priv, bss)) + return -EINVAL; + + /* Allocate and fill new bss descriptor */ + bss_desc =3D kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret =3D nxpwifi_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + if (nxpwifi_band_to_radio_type(bss_desc->bss_band) =3D=3D + HOST_SCAN_RADIO_TYPE_BG) { + config_bands =3D BAND_B | BAND_G | BAND_GN; + if (adapter->fw_bands & BAND_GAC) + config_bands |=3D BAND_GAC; + if (adapter->fw_bands & BAND_GAX) + config_bands |=3D BAND_GAX; + } else { + config_bands =3D BAND_A | BAND_AN; + if (adapter->fw_bands & BAND_AAC) + config_bands |=3D BAND_AAC; + if (adapter->fw_bands & BAND_AAX) + config_bands |=3D BAND_AAX; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) + priv->config_bands =3D config_bands; + + ret =3D nxpwifi_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + if (nxpwifi_11h_get_csa_closed_channel(priv) =3D=3D (u8)bss_desc->channel= ) { + nxpwifi_dbg(adapter, ERROR, + "Attempt to reconnect on csa closed chan(%d)\n", + bss_desc->channel); + ret =3D -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 =3D 0; + ret =3D nxpwifi_associate(priv, bss_desc); + + /* + * If auth type is auto and association fails using open mode, try to con= nect + * using shared mode + */ + if (ret =3D=3D WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + priv->sec_info.is_authtype_auto && + priv->sec_info.wep_enabled) { + priv->sec_info.authentication_mode =3D + NL80211_AUTHTYPE_SHARED_KEY; + ret =3D nxpwifi_associate(priv, bss_desc); + } + +done: + /* beacon_ie buffer was allocated in function nxpwifi_fill_new_bss_desc()= */ + if (bss_desc) + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + + if (ret < 0) + priv->attempted_bss_desc =3D NULL; + + return ret; +} + +/* IOCTL request handler to set host sleep configuration */ +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 =3D priv->adapter; + int status =3D 0; + u32 prev_cond =3D 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 =3D -EPERM; + break; + } + if (hs_cfg->is_invoke_hostcmd) { + if (hs_cfg->conditions =3D=3D HS_CFG_CANCEL) { + if (!test_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags)) + /* Already cancelled */ + break; + /* Save previous condition */ + prev_cond =3D le32_to_cpu(adapter->hs_cfg + .conditions); + adapter->hs_cfg.conditions =3D + cpu_to_le32(hs_cfg->conditions); + } else if (hs_cfg->conditions) { + adapter->hs_cfg.conditions =3D + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio =3D (u8)hs_cfg->gpio; + if (hs_cfg->gap) + adapter->hs_cfg.gap =3D (u8)hs_cfg->gap; + } else if (adapter->hs_cfg.conditions =3D=3D + cpu_to_le32(HS_CFG_CANCEL)) { + status =3D -EINVAL; + break; + } + + status =3D nxpwifi_send_cmd(priv, + HOST_CMD_802_11_HS_CFG_ENH, + HOST_ACT_GEN_SET, 0, + &adapter->hs_cfg, + cmd_type =3D=3D NXPWIFI_SYNC_CMD); + + if (hs_cfg->conditions =3D=3D HS_CFG_CANCEL) + /* Restore previous condition */ + adapter->hs_cfg.conditions =3D + cpu_to_le32(prev_cond); + } else { + adapter->hs_cfg.conditions =3D + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio =3D (u8)hs_cfg->gpio; + adapter->hs_cfg.gap =3D (u8)hs_cfg->gap; + } + break; + case HOST_ACT_GEN_GET: + hs_cfg->conditions =3D le32_to_cpu(adapter->hs_cfg.conditions); + hs_cfg->gpio =3D adapter->hs_cfg.gpio; + hs_cfg->gap =3D adapter->hs_cfg.gap; + break; + default: + status =3D -EINVAL; + break; + } + + return status; +} + +/* Sends IOCTL request to cancel the existing Host Sleep configuration */ +int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type) +{ + struct nxpwifi_ds_hs_cfg hscfg; + + hscfg.conditions =3D HS_CFG_CANCEL; + hscfg.is_invoke_hostcmd =3D 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 */ +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 =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + nxpwifi_deauthenticate(priv, NULL); + } + } + + priv =3D 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 =3D false; + + memset(&hscfg, 0, sizeof(hscfg)); + hscfg.is_invoke_hostcmd =3D 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)) <=3D 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 */ +int nxpwifi_get_bss_info(struct nxpwifi_private *priv, + struct nxpwifi_bss_info *info) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_bssdescriptor *bss_desc; + + if (!info) + return -EINVAL; + + bss_desc =3D &priv->curr_bss_params.bss_descriptor; + + info->bss_mode =3D 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 =3D bss_desc->channel; + + memcpy(info->country_code, adapter->country_code, + IEEE80211_COUNTRY_STRING_LEN); + + info->media_connected =3D priv->media_connected; + + info->max_power_level =3D priv->max_tx_power_level; + info->min_power_level =3D priv->min_tx_power_level; + + info->bcn_nf_last =3D priv->bcn_nf_last; + + if (priv->sec_info.wep_enabled) + info->wep_status =3D true; + else + info->wep_status =3D false; + + info->is_hs_configured =3D test_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + info->is_deep_sleep =3D 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 =3D { + .auto_ds =3D 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 */ +int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate) +{ + int ret; + + ret =3D 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 =3D nxpwifi_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + else + *rate =3D priv->data_rate; + } + + return ret; +} + +/* IOCTL request handler to set tx power configuration */ +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 =3D 0; + + if (!power_cfg->is_power_auto) { + dbm =3D (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 =3D kzalloc(NXPWIFI_SIZE_OF_CMD_BUFFER, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + txp_cfg =3D (struct host_cmd_ds_txpwr_cfg *)buf; + txp_cfg->action =3D cpu_to_le16(HOST_ACT_GEN_SET); + if (!power_cfg->is_power_auto) { + u16 dbm_min =3D power_cfg->is_power_fixed ? + dbm : priv->min_tx_power_level; + + txp_cfg->mode =3D cpu_to_le32(1); + pg_tlv =3D (struct nxpwifi_types_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); + pg_tlv->type =3D cpu_to_le16(TLV_TYPE_POWER_GROUP); + pg_tlv->length =3D + cpu_to_le16(4 * sizeof(struct nxpwifi_power_group)); + pg =3D (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 =3D 0x00; + pg->last_rate_code =3D 0x03; + pg->modulation_class =3D MOD_CLASS_HR_DSSS; + pg->power_step =3D 0; + pg->power_min =3D (s8)dbm_min; + pg->power_max =3D (s8)dbm; + pg++; + /* Power group for modulation class OFDM */ + pg->first_rate_code =3D 0x00; + pg->last_rate_code =3D 0x07; + pg->modulation_class =3D MOD_CLASS_OFDM; + pg->power_step =3D 0; + pg->power_min =3D (s8)dbm_min; + pg->power_max =3D (s8)dbm; + pg++; + /* Power group for modulation class HTBW20 */ + pg->first_rate_code =3D 0x00; + pg->last_rate_code =3D 0x20; + pg->modulation_class =3D MOD_CLASS_HT; + pg->power_step =3D 0; + pg->power_min =3D (s8)dbm_min; + pg->power_max =3D (s8)dbm; + pg->ht_bandwidth =3D HT_BW_20; + pg++; + /* Power group for modulation class HTBW40 */ + pg->first_rate_code =3D 0x00; + pg->last_rate_code =3D 0x20; + pg->modulation_class =3D MOD_CLASS_HT; + pg->power_step =3D 0; + pg->power_min =3D (s8)dbm_min; + pg->power_max =3D (s8)dbm; + pg->ht_bandwidth =3D HT_BW_40; + } + ret =3D 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 */ +int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode) +{ + int ret; + struct nxpwifi_adapter *adapter =3D priv->adapter; + u16 sub_cmd; + + if (*ps_mode) + adapter->ps_mode =3D NXPWIFI_802_11_POWER_MODE_PSP; + else + adapter->ps_mode =3D NXPWIFI_802_11_POWER_MODE_CAM; + sub_cmd =3D (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH, + sub_cmd, BITMAP_STA_PS, NULL, true); + if (!ret && sub_cmd =3D=3D DIS_AUTO_PS) + ret =3D 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 element */ +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 element, too big\n"); + return -EINVAL; + } + memcpy(priv->wpa_ie, ie_data_ptr, ie_len); + priv->wpa_ie_len =3D ie_len; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: Set WPA element len=3D%d element=3D%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + + if (priv->wpa_ie[0] =3D=3D WLAN_EID_VENDOR_SPECIFIC) { + priv->sec_info.wpa_enabled =3D true; + } else if (priv->wpa_ie[0] =3D=3D WLAN_EID_RSN) { + priv->sec_info.wpa2_enabled =3D true; + } else { + priv->sec_info.wpa_enabled =3D false; + priv->sec_info.wpa2_enabled =3D false; + } + } else { + memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + priv->wpa_ie_len =3D 0; + nxpwifi_dbg(priv->adapter, INFO, + "info: reset WPA element len=3D%d element=3D%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + priv->sec_info.wpa_enabled =3D false; + priv->sec_info.wpa2_enabled =3D false; + } + + return 0; +} + +/* IOCTL request handler to set/reset WPS element */ +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 element, too big\n"); + return -EINVAL; + } + + priv->wps_ie =3D 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 =3D ie_len; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: Set WPS element len=3D%d element=3D%#x\n", + priv->wps_ie_len, priv->wps_ie[0]); + } else { + kfree(priv->wps_ie); + priv->wps_ie_len =3D ie_len; + nxpwifi_dbg(priv->adapter, INFO, + "info: Reset WPS element len=3D%d\n", priv->wps_ie_len); + } + return 0; +} + +/* IOCTL request handler to set WEP network key */ +static int +nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private *priv, + struct nxpwifi_ds_encrypt_key *encrypt_key) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + int ret; + struct nxpwifi_wep_key *wep_key; + int index; + + if (priv->wep_key_curr_index >=3D NUM_WEP_KEYS) + priv->wep_key_curr_index =3D 0; + wep_key =3D &priv->wep_key[priv->wep_key_curr_index]; + index =3D encrypt_key->key_index; + if (encrypt_key->key_disable) { + priv->sec_info.wep_enabled =3D 0; + } else if (!encrypt_key->key_len) { + /* Copy the required key as the current key */ + wep_key =3D &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 =3D wep_key->key_length; + + priv->wep_key_curr_index =3D (u16)index; + priv->sec_info.wep_enabled =3D 1; + } else { + wep_key =3D &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 =3D index; + wep_key->key_length =3D encrypt_key->key_len; + priv->sec_info.wep_enabled =3D 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 =3D encrypt_key; + + /* Send request to firmware */ + ret =3D 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 |=3D HOST_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &=3D ~HOST_ACT_MAC_WEP_ENABLE; + + ret =3D 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 */ +static int +nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private *priv, + struct nxpwifi_ds_encrypt_key *encrypt_key) +{ + int ret; + u8 remove_key =3D 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 =3D NXPWIFI_KEY_INDEX_UNICAST; + + if (remove_key) + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL, + HOST_ACT_GEN_SET, + !KEY_INFO_ENABLED, encrypt_key, true); + else + ret =3D 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 */ +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 =3D nxpwifi_sec_ioctl_set_wpa_key(priv, encrypt_key); + else + status =3D nxpwifi_sec_ioctl_set_wep_key(priv, encrypt_key); + + return status; +} + +/* Return driver version string */ +int +nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter, char *vers= ion, + int max_len) +{ + union { + __le32 l; + u8 c[4]; + } ver; + char fw_ver[32]; + + ver.l =3D 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 */ +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 =3D key_len; + encrypt_key.key_index =3D key_index; + + if (kp) { + encrypt_key.key_cipher =3D kp->cipher; + if (kp->cipher =3D=3D WLAN_CIPHER_SUITE_AES_CMAC || + kp->cipher =3D=3D WLAN_CIPHER_SUITE_BIP_GMAC_256) + encrypt_key.is_igtk_key =3D true; + } + + if (!disable) { + if (key_len) + memcpy(encrypt_key.key_material, key, key_len); + else + encrypt_key.is_current_wep_key =3D 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 =3D kp->seq_len; + encrypt_key.is_rx_seq_valid =3D true; + } + } else { + encrypt_key.key_disable =3D 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 */ +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 =3D 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 =3D cpu_to_le16(action); + if (action =3D=3D HOST_ACT_GEN_SET) { + roc_cfg.band_cfg =3D chan->band; + sc =3D nxpwifi_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); + roc_cfg.band_cfg |=3D (sc << 2); + + roc_cfg.channel =3D + ieee80211_frequency_to_channel(chan->center_freq); + roc_cfg.duration =3D cpu_to_le32(duration); + } + ret =3D 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 */ +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 */ +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 =3D HOST_CMD_MAC_REG_ACCESS; + break; + case NXPWIFI_REG_BBP: + cmd_no =3D HOST_CMD_BBP_REG_ACCESS; + break; + case NXPWIFI_REG_RF: + cmd_no =3D HOST_CMD_RF_REG_ACCESS; + break; + case NXPWIFI_REG_PMIC: + cmd_no =3D HOST_CMD_PMIC_REG_ACCESS; + break; + case NXPWIFI_REG_CAU: + cmd_no =3D 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 */ +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 =3D reg_type; + reg_rw.offset =3D reg_offset; + reg_rw.value =3D reg_value; + + return nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_SET); +} + +/* Sends IOCTL request to read from a register */ +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 =3D reg_type; + reg_rw.offset =3D reg_offset; + ret =3D nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_GET); + + if (!ret) + *value =3D reg_rw.value; + + return ret; +} + +/* Sends IOCTL request to read from EEPROM */ +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 =3D offset; + rd_eeprom.byte_count =3D bytes; + + /* Send request to firmware */ + ret =3D 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; +} + +/* Set generic IE(s); handle WPA/WPS specially */ +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[] =3D { 0x00, 0x50, 0xf2, 0x01 }; + static const u8 wps_oui[] =3D { 0x00, 0x50, 0xf2, 0x04 }; + u16 unparsed_len =3D ie_len, cur_ie_len; + + /* If the passed length is zero, reset the buffer */ + if (!ie_len) { + priv->gen_ie_buf_len =3D 0; + priv->wps.session_enable =3D false; + return 0; + } else if (!ie_data_ptr || + ie_len <=3D sizeof(struct element)) { + return -EINVAL; + } + pvendor_ie =3D (struct ieee80211_vendor_ie *)ie_data_ptr; + + while (pvendor_ie) { + cur_ie_len =3D pvendor_ie->len + sizeof(struct element); + + if (pvendor_ie->element_id =3D=3D WLAN_EID_RSN) { + /* element is a WPA/WPA2 element so call set_wpa function */ + nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len); + priv->wps.session_enable =3D false; + goto next_ie; + } + + if (pvendor_ie->element_id =3D=3D WLAN_EID_VENDOR_SPECIFIC) { + /* Test to see if it is a WPA element, if not, then it is a gen element= */ + if (!memcmp(&pvendor_ie->oui, wpa_oui, + sizeof(wpa_oui))) { + /* element is a WPA/WPA2 element so call set_wpa function */ + nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, + cur_ie_len); + priv->wps.session_enable =3D false; + goto next_ie; + } + + if (!memcmp(&pvendor_ie->oui, wps_oui, + sizeof(wps_oui))) { + /* + * Test to see if it is a WPS element, if so, enable wps session + * flag + */ + priv->wps.session_enable =3D 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 +=3D cur_ie_len; + } + +next_ie: + unparsed_len -=3D cur_ie_len; + + if (unparsed_len <=3D sizeof(struct element)) + pvendor_ie =3D NULL; + else + pvendor_ie =3D (struct ieee80211_vendor_ie *) + (((u8 *)pvendor_ie) + cur_ie_len); + } + + return 0; +} + +/* IOCTL request handler to set/get generic element */ +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 =3D priv->adapter; + + switch (gen_ie->type) { + case NXPWIFI_IE_TYPE_GEN_IE: + if (action =3D=3D HOST_ACT_GEN_GET) { + gen_ie->len =3D 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 =3D 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 =3D gen_ie->len; + break; + default: + nxpwifi_dbg(adapter, ERROR, "invalid element type\n"); + return -EINVAL; + } + return 0; +} + +/* Sends IOCTL request to set a generic element */ +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 =3D NXPWIFI_IE_TYPE_GEN_IE; + gen_ie.len =3D ie_len; + memcpy(gen_ie.ie_data, ie, ie_len); + + return nxpwifi_misc_ioctl_gen_ie(priv, &gen_ie, HOST_ACT_GEN_SET); +} + +/* Get Host Sleep wakeup 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 =3D=3D 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); +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010069.outbound.protection.outlook.com [52.101.69.69]) (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 3C837428848; Wed, 4 Feb 2026 18:05:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.69 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228340; cv=fail; b=MsmF9YLXuQQs3lTdkirVL0o9n8gCVUOj81qFjjayyhdnm3jDwv3XVPOT/ZTe32hjiEiw97yZg2f7OUw4pHDSTs+KsQGsJYNLJ6AH9PI7vumnBcUvNC/tq111dt7OxQ97XwXcXa2Ist0mGyVA56cOAG6LPxOkHeQjwZlraKDKa9Q= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228340; c=relaxed/simple; bh=7YE83o8+Nfdy7ylWGNxiigI1jJaF8cAuwF5ddMsKaCQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=kyI+1vtgwMcg7Vt+EsBnt4Yy2mQzBol3xDvtkKUeh905+9ulAgAdJQMFrRwFuxT8NUn2p/iYsrcDfzRPlt3B0QYZppBSYNehPNnEH7PybWo5van1D2CvbMoUI1jtnL9iAUKK7iXmOo4qm1hKFwrnwXzv61ngSjODsA3zYFxUhFY= 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=Y5qpBorS; arc=fail smtp.client-ip=52.101.69.69 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="Y5qpBorS" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=iYbbjYUHVYPxL8muP9j5EUm2iCqR9L2vPjhCwBBr8qUFx9Ca4VY6rGDB5mM9KfIhOGapu0qs7cUyr68Ruqso1vWZ1xxKycNufr2s11zx3qOIQ5GdYhM9+CSiP4MmfAT66it6BMoN09aLjVcCnH3ACsjsIpUIBUVWYx3BBUNGmI/qr606S51x85PwnjlTAWPAqjfrjwQBpOED1PqkHckV9PTQ4sLV1lZexerQaIXyv/uhwOtMIcaPjCMvPLvitzInqJl5pPWQJPG5sFlrpBcP8DEVgQr5UX3Zzyc0yn3ktO77/wmCIvvIeUwafXaeE7SMBzZJ2TDfD4IVvfBEWVPFpA== 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=aMtnbf2GNFNpFo5TcWdq3tSELQMWZy+4kAXxN3SloSY=; b=iaOxh497UbR46rTGgf6444pTAoE88gyf732kYTmEVpyiHbRenB/YLoQhDJa0rRxs3/KFUy8YaWMzGGqf+R3nI+cZPMCU4tgcAm1L0mC21ID9A4II4xNVpY01P6qtGslQ47VUZi1P9Nx9yc44WwjZW6ZTqxCzcMnWb4ml7iwGw1Mm//h80XGcJumB2cMpn645X9mGYR7KByrnxcdHKnDAbXxAWbr9K3pye/NVDDZcRfm4Smx7+gNlZFG4R3rXQ8zQiDC/2jcZLZbDffjaW8TPtaJY9/03AkBBwzfq1dBn3VNc1wheTcTv1y7av81I4pvHYIojPOOXwj8Ahyvq8bg1Lg== 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=aMtnbf2GNFNpFo5TcWdq3tSELQMWZy+4kAXxN3SloSY=; b=Y5qpBorS/h/niui1ZbFbW/XB/+7ZqKZKHQveU1nDhSvjIHUBX33tdeWeFdDM+lCHOjcVo11Va/TZI1llNKWzADtqO9uiiNHx51HVYZiUJw6YhJyuMC7rlw7p+CAtFTxzgtk+xzsqleamUS1841oxWwhKahN8+q9EcfmDNQAMlvmz1207JXaRmxZ+4tPLDSid6Ghh0RIp2MspayAjmJ+jDJKYmTcwhFOckSUbdBxyzNm/92NkvPKxMhz9Zav6gXBGGSnOt6DUlYsEO9641JK8AS3Aug/pRj+aYHCU78iZ/hhvp3czZvGFRS8+opz1WjbcJBP5jGqE/WgXaKTLdLqTag== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:36 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:35 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 10/21] wifi: nxpwifi: implement cfg80211 ops for STA and AP modes Date: Thu, 5 Feb 2026 02:03:47 +0800 Message-Id: <20260204180358.632281-11-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 6aa8553f-8fe2-45a5-5659-08de6417feb2 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|4022899009|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?B?a2F1VUJmeml1UHJkVGFDWkFJemduN2dWa2J3UnF1OFQxdWM5QjRSRU9wVDNi?= =?utf-8?B?blZ6ek1QOVh6cHRhYkRna0hETThpNElSRnQ3QUVsRGtyUmlnRDQzaFF5U3RY?= =?utf-8?B?dnd4ZUhoTUtHZFBDd2VEOXlFOW9Xekw2dWQ1M2NrUVZXaDlxSUtiRFVTczRl?= =?utf-8?B?dkVNSis5SzliZDJkZGNVOVhxUUYxRkh1SUx2VXZYTDRvUW5senVKSmI2VCtk?= =?utf-8?B?K1RJYkdaVDRPQmJPOHJKRllLOFU4RnQrRzRmbzRSRktmVU0zNGR2OUt6Zlpi?= =?utf-8?B?L2taU3poRklQb1lEeFRKL01uc0hLWEtVMEdlSU1SVjkybVJwK2lkS2RsT2tT?= =?utf-8?B?UjhFL0t1TlFjbG8zbXZjTW9RcUpzWGN2aDRTZ1RBZ3cyS3RqNmUyMDFrbkVN?= =?utf-8?B?TWJJd0hTQUJrMnU3bTdCOHVyajJ0VHJLYWNQWXY5MHF2QTB5V2RmN01yRk5j?= =?utf-8?B?MjNWaG45d0dvV1lITHRYdWFtYVMxU3NaZWpNaDlmUzhtMi9KTDhXYXNvdFF3?= =?utf-8?B?amNlRC9HOFZHNFlRUmc1cTBReVVJOEZMdGZWQXNHVWVobmtpN3I1bWc4dnFz?= =?utf-8?B?YWZmNUxCZHVsZk9SQmtWY1Q1Q1JReXZEZStVNTc2ZXhGc2FCbVJuVENlc25D?= =?utf-8?B?N0xVSyttRnAraVgyNVRFWXhYRWQ1bmVhRHpTMi9CSHVUb0ViS2ZNOEY1YW5G?= =?utf-8?B?ZkozQTFBN0xMbzU3SlljNEdLZVVUS0kwem05a204TC9kRys1RFlQNWxmTVBv?= =?utf-8?B?UlBnUnlpenlacjFCNkE1dWw5eEQ5Kzd4VUFjamNZUWhaaUxxdEJkUzR3Y2JW?= =?utf-8?B?bXNDSW1OdEI4b2xYWmlhOVNUL055RklCMTlYeDNJcHo3OHNkU2hSbXpGekc5?= =?utf-8?B?TTd4NE8vTG9RVW5ya051UkZrQ09NU1ZFbFVoU3IyTlc0elNYWVhoQ0JMZnh3?= =?utf-8?B?MSt1ZkZFQmMvd3FFZ0NCWFlKVWUwOG5Ya0JnYVgrTTl6bngzTU5aZkVRSmw3?= =?utf-8?B?QXhvWndnN0hWVnBWaVlHU2hyTjNQbnpzeVk3SGdZdjB3ckkxRk9RanBhZ3p3?= =?utf-8?B?YW1sa1dEYWNLcXdpSHIzQUVuRkRMaCtseEhhU3p6djFoR0VVT2pwM0JCR3Jl?= =?utf-8?B?YWF1ZVFuMzhhYTdMYVlucGtTekZjSEZnK1UxWWVIRjZUZGRRRm1HWUcrdEpz?= =?utf-8?B?czYyaEQ5R2N0VDl5UUVDVWxjajROU21Scnl1dVNUOHZtcFVWS0RhaUNjaGxC?= =?utf-8?B?RjVFakQ4ZHBEdDZZWDNTQk1lcCtrZ0xLeVFEa2UxNFlZTENmelRLY2s1Lzl5?= =?utf-8?B?K1g4a2lyQzZ4WFNXOG5BSExveHdvRDNieWdpTXJZWmRXRDNKMWUrZXJad2Ux?= =?utf-8?B?eG83WEdmLzY5WmRDR1EzdlhsNXdZTFAzZmNEQkFITjBXdHdVV0IyU0NWM0hX?= =?utf-8?B?WHAxRDVoS0dVZ3NqeXdTNkxJQzhBblJabitQbHBHSnJHUlVvRi9qVVdHMk9L?= =?utf-8?B?b3VJNkdQYXZ3UitOanN5ak1vOU9LeS96VUZ4V0dOY0RNeEFWOWl1QWlueXZS?= =?utf-8?B?TndrNTNteUFnc3ZLVFFVNjB5Nmo2WnNmSDJsS1FycmIzRi9wMzNIS2psMkZh?= =?utf-8?B?RXJvK3VMcEtzVFJ4VUFOOE5ueStSKy9uNWFqeGdTTlQyT1NFbjVBUDh1QXp4?= =?utf-8?B?OFZSVGtPMkNINGtlODA2Njl5VXV5Q054U09SSW9mblcrTUhHa3dRNlpQZU02?= =?utf-8?B?eTdiS0UwZTc3UlhOM3NCK0hqWFBJblhrSjNLWFN4bCtnRDNmLzdKSnFrKzlB?= =?utf-8?B?RHU1V2VHczU5bEs3TjVnaDNzeDVmZkJoQjE1TTU5NzZDOHpOZGVXa2pXSFY4?= =?utf-8?B?VWZNbk9iRGtCNVJ1b2haL1Bqem5iZStWdnJYc0JxR1UwbEdkcmg4V1h4V2l4?= =?utf-8?B?V2RoVS9SL2I5aExaQTFrMTEwN05hcEFob3hTYnRVYnRsV3I5VDAyMWZJL2dl?= =?utf-8?B?TUp4K0Y0Qk9ySzdCNzR3bVJ0ZjU5dm95akdOTGFSR2JsVVAzL205VFF2ekJ4?= =?utf-8?B?b2lZY21ITXp0dmRRb0pOMTk5aW4yc3JLUnM1c0NEYS80WTdrWGJKalRLS1Fk?= =?utf-8?B?d2p4a2ZhbEZNSjh4RERhOXBQVDRMMnJTTldBaUVka3VjVWlITUFiMjdxcUJQ?= =?utf-8?Q?7Y/+CVDksT4jF3udWpPKo/w=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(4022899009)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?R1ZPMEZlanhhbGxhdEYwMFNvS044ZktPaE9abmVpM2ordTVBbElJdUZXVm9n?= =?utf-8?B?S1FFeWlTamVBZEp5SlVhZDVvL2UyMmlDNFVTb1hlRkRwS0ZHQklKUktRb293?= =?utf-8?B?enJJYWNZSVhkVjIrSDk4TDhxNE9DcHp6c1MwVExYRE9iemVwdWRHSWZyTDFo?= =?utf-8?B?d21GRVU4ZHhYWVlJWnBTRlRhSDhtaVBScGhzblpuOFU0NUdrMkRGSy9OWEV2?= =?utf-8?B?ZEdXRk51eUZ4SWErWjk4NXUrVXpNdlU5OWVrVVArY2RXZjJKbS9aaE9MMmIr?= =?utf-8?B?eU0wTkdnRmcxa2ZtaHNnUmdROUZtOGQzZUtTd2lJUlQ0T3FjbnVoaERyRzNn?= =?utf-8?B?TnVQcHREdnZvTGFPTzBkNTlNRWpEY2UvVC9iYmdwY2FDTjF2Ykh4dnZmK2Nh?= =?utf-8?B?Y3p6b2ptRVV5R1BFRjFuRFdncS9hemM1UXB2b0dXNHRSdE1zL1ZpRWdqdHpl?= =?utf-8?B?R01WMGdLT0RDMG5vUmJRbFNSQzgwNHFmb3gydHFXVElocFMrSWdoZUhhUzJI?= =?utf-8?B?SEtLaE9sNVlIN29vVE4xUmNIWTV4Ti90dU1KMElNdFVkU3o0T09kZ2R2OVVH?= =?utf-8?B?TmJlWWxqai84UG9iWkhRZHVoUk8zamdJSWZrM09qZXpzWVRWdkxQMFVCc2NU?= =?utf-8?B?Wm1tMWlrbkJHM0NZYlFadUpGemMrM09pWjl1c0xHYml4TkZMbDNCUzBRNUxJ?= =?utf-8?B?anlnU1R3TjR4N2pjajdad0R2VDd4TXh3VFZweUVHMXdTYlVocFp2V2hPWVdK?= =?utf-8?B?SElHaDY3UTRnV0JmQVg0amoxUE9ycGhUQVRvYUZCbDF0bmkxV2Z4TzNHYXZN?= =?utf-8?B?T01leVhkV21mbDhkdnlvU2ZXT2dVRzkwaER5ZWp0ZGwvZUR3TFNqU0crdzVB?= =?utf-8?B?WHVEdGhrQk9kNzRHekcrRXcyaitKL3VmZ21uYjRVOHE3d1M1OC9yMUg5dGtC?= =?utf-8?B?MlBnQUFDVmNUMnA5NmtGajJmUmlMS2xnUThuQUtBYU9CYVF2bDg2OG9qcFBp?= =?utf-8?B?WS82dzRubGNGSWZKMkhqdWJLeGdxWU1FMHJlKzFLTzYvTit4L290OXhBdnpk?= =?utf-8?B?a0tzRGJhMVdYdEQweWxPRENjRkFERzFTT3J2cTE0akoralR2K21jdklqcXVq?= =?utf-8?B?QXFMVFB0OUlGYVRkUmwzSGQ2aUhmbHdWV3hEOHlVTzZWc1FZdDdVVVBvdjZh?= =?utf-8?B?WHIzSFdLQnFleXZIYzg5WjFTd3Q3dVNFajdZV040WWRWQ2pkQ3QzMjVFMzV5?= =?utf-8?B?MXNiRkQyWVIwbk5xQ3hQdk4xZEU0VDhTVnFpTUJkK2hTSzBmV0QzL05JOVh2?= =?utf-8?B?Q2xLdHpHZ3I4VUxhRGJxRURQSis3Y1ZsbG5KZWNab0R0bjJTbHdNd05IM2RB?= =?utf-8?B?d3V2TW5JVk1VK0Zrd01lbTFmMXNuMitKblhpZlcyWnY0S1Yvc2djNDlwNkh1?= =?utf-8?B?ZXlCUEhRR2Y1Q28ySkljbVFhd24yMUdJcjVuNlJGWTBQbmZQcmU2aFJIcEpQ?= =?utf-8?B?UG8rbG81QU05YlI2VkpjcUdKU1Eza1hUTGorYlJwb3RWaENDTVVTczJKV1RF?= =?utf-8?B?MWRYZGY1aEJ6b2h2ajFiWkRCSVBUK2JsaVUxcTVZN2RtR21FK3RDOXVobUFv?= =?utf-8?B?MWdTSWVrT3FWZm5GcmhFUjF1cVI0eU1lSkFmM1lnRDZFMGZqN3NubDBuZHVM?= =?utf-8?B?RjZHWXZxbUZtUHdpQ1QyV3d1YVdJbytoSnd5MExkZXgreU5iVVhoZm9VcFZW?= =?utf-8?B?RUpwTE9PN0orYjJjSFoyaS9RaktjeWNIWVRVUHdveFlQN2I0clhva29HU2Jz?= =?utf-8?B?U25Vajg5NTNGOTMydCtPRnZLSyt4MnNPYUVEZTNOUncrNUkxcUw4dzAzS0NR?= =?utf-8?B?Y2l6SDVsVjdwais3cjRwT2hVb0N1MTA0L0RkaU9NYU5Ha2ttMEdKVm14UEZ4?= =?utf-8?B?RFVVWjIvRU1Pb2xqNjRBNVpscENsTDJOUEo5d1QvTlZmdFJzTW1vUExkd1BZ?= =?utf-8?B?cXAxT1JIbkwxUkNHbFJ4T29aM3V0c2hrb2IzMlVpbUszUWJGTUtTWngxajRK?= =?utf-8?B?ZS9xalNrR3hNVFBQU2d6dU0wdkl3MW5hRWNkcnBNRlZCbjVFc2daYmhyQktH?= =?utf-8?B?L0doYTRjMVEvNUtYMFNNZ1JjM2ZwK05JMVNhYi9kZzlHd281UUl4NnE5NXBH?= =?utf-8?B?amRHVGR1SDViTmV5aXNHempWbElqYmZsdlB4STBCZGpqK2tyeUZGWTM2Rk9Q?= =?utf-8?B?SmpaUkJwbkxORFkySHJNMjFXZld5RXViNnJtcm1pUG1oTDdLV3lTdjlxS1Y5?= =?utf-8?B?c1VGTlhYcVlKcHlmdzhha1FuZCtZd2tGQWV3UzZtaFlmVkxCdVEzZz09?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6aa8553f-8fe2-45a5-5659-08de6417feb2 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:35.7075 (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: vNPy3/nQE7NCXnt0OHm7JoSdsTabC4WXVxlS6K3qaowmV6AsQrvfhHGT9GAjJgcbXpNoWySqFwRQdq3q1RR/Jw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Implement the required cfg80211_ops callbacks to support both client (STA) and access point (AP) modes on top of the NXP firmware. Due to the NXP firmware's involvement in the association process=E2=80=94wh= ere the driver must issue an association command to the firmware=E2=80=94nxpwifi cannot leverage mac80211 for this functionality. Instead, nxpwifi implements separate authentication and association handling to support SAE via wpa_supplicant and hostapd. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 3960 +++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/cfg80211.h | 19 + 2 files changed, 3979 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/wire= less/nxp/nxpwifi/cfg80211.c new file mode 100644 index 000000000000..69fe318fa84b --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -0,0 +1,3960 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: cfg80211 support + * + * 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[] =3D { + { + .max =3D NXPWIFI_MAX_BSS_NUM, + .types =3D BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MONITOR), + }, +}; + +static const struct ieee80211_iface_combination +nxpwifi_iface_comb_ap_sta =3D { + .limits =3D nxpwifi_ap_sta_limits, + .num_different_channels =3D 1, + .n_limits =3D ARRAY_SIZE(nxpwifi_ap_sta_limits), + .max_interfaces =3D NXPWIFI_MAX_BSS_NUM, + .beacon_int_infra_match =3D true, + .radar_detect_widths =3D 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 =3D { + .limits =3D nxpwifi_ap_sta_limits, + .num_different_channels =3D 1, + .n_limits =3D ARRAY_SIZE(nxpwifi_ap_sta_limits), + .max_interfaces =3D NXPWIFI_MAX_BSS_NUM, + .beacon_int_infra_match =3D true, + .radar_detect_widths =3D BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), +}; + +/* Map nl80211 channel types to secondary channel offsets */ +u8 nxpwifi_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_typ= e) +{ + 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; + } +} + +/* Map IEEE HT secondary=E2=80=91channel type to nl80211 channel type */ +u8 nxpwifi_get_chan_type(struct nxpwifi_private *priv) +{ + struct nxpwifi_channel_band channel_band; + int ret; + + ret =3D 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 =3D=3D + IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + return NL80211_CHAN_HT40PLUS; + else + return NL80211_CHAN_HT40MINUS; + default: + return NL80211_CHAN_HT20; + } + } + + return NL80211_CHAN_HT20; +} + +/* Retrieve the driver private data from the wiphy */ +static void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy) +{ + return (void *)(*(unsigned long *)wiphy_priv(wiphy)); +} + +/* cfg80211 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 =3D nxpwifi_netdev_get_priv(netdev); + static const u8 bc_mac[] =3D {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac =3D pairwise ? mac_addr : bc_mac; + int ret; + + ret =3D 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; +} + +/* Build an skb containing a management frame */ +static void +nxpwifi_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) +{ + u8 addr[ETH_ALEN]; + u16 pkt_len; + u32 tx_control =3D 0, pkt_type =3D PKT_TYPE_MGMT; + + eth_broadcast_addr(addr); + pkt_len =3D 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 =3D TC_PRIO_BESTEFFORT; + __net_timestamp(skb); +} + +/* cfg80211 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 =3D params->buf; + size_t len =3D params->len; + struct sk_buff *skb; + u16 pkt_len; + const struct ieee80211_mgmt *mgmt; + struct nxpwifi_txinfo *tx_info; + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(wdev->netdev); + + if (!buf || !len) { + nxpwifi_dbg(priv->adapter, ERROR, "invalid buffer and length\n"); + return -EINVAL; + } + + mgmt =3D (const struct ieee80211_mgmt *)buf; + if (GET_BSS_ROLE(priv) !=3D NXPWIFI_BSS_ROLE_STA && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Offloaded probe responses; skip TX in AP/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) =3D=3D 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 =3D len + ETH_ALEN; + skb =3D 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 =3D NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num =3D priv->bss_num; + tx_info->bss_type =3D priv->bss_type; + tx_info->pkt_len =3D pkt_len; + + nxpwifi_form_mgmt_frame(skb, buf, len); + *cookie =3D nxpwifi_roc_cookie(priv->adapter); + + if (ieee80211_is_action(mgmt->frame_control)) + skb =3D 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; +} + +/* cfg80211 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 =3D nxpwifi_netdev_get_priv(wdev->netdev); + u32 mask =3D upd->interface_stypes; + + if (mask !=3D priv->mgmt_frame_mask) { + priv->mgmt_frame_mask =3D mask; + if (priv->host_mlme_reg && + GET_BSS_ROLE(priv) !=3D NXPWIFI_BSS_ROLE_UAP) + priv->mgmt_frame_mask |=3D 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"); + } +} + +/* cfg80211 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 =3D nxpwifi_netdev_get_priv(wdev->netdev); + struct nxpwifi_adapter *adapter =3D 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 =3D 0x%llx\n", + priv->roc_cfg.cookie); + return -EBUSY; + } + + ret =3D nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_SET, chan, + duration); + + if (!ret) { + *cookie =3D nxpwifi_roc_cookie(adapter); + priv->roc_cfg.cookie =3D *cookie; + priv->roc_cfg.chan =3D *chan; + + cfg80211_ready_on_channel(wdev, *cookie, chan, + duration, GFP_ATOMIC); + + nxpwifi_dbg(adapter, INFO, + "info: ROC, cookie =3D 0x%llx\n", *cookie); + } + + return ret; +} + +/* cfg80211 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 =3D nxpwifi_netdev_get_priv(wdev->netdev); + int ret; + + if (cookie !=3D priv->roc_cfg.cookie) + return -ENOENT; + + ret =3D 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 =3D 0x%llx\n", cookie); + } + + return ret; +} + +/* cfg80211 operation handler to set Tx power. */ +static int +nxpwifi_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int radio_idx, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_power_cfg power_cfg; + int dbm =3D MBM_TO_DBM(mbm); + + switch (type) { + case NL80211_TX_POWER_FIXED: + power_cfg.is_power_auto =3D 0; + power_cfg.is_power_fixed =3D 1; + power_cfg.power_level =3D dbm; + break; + case NL80211_TX_POWER_LIMITED: + power_cfg.is_power_auto =3D 0; + power_cfg.is_power_fixed =3D 0; + power_cfg.power_level =3D dbm; + break; + case NL80211_TX_POWER_AUTOMATIC: + power_cfg.is_power_auto =3D 1; + break; + } + + priv =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + return nxpwifi_set_tx_power(priv, &power_cfg); +} + +/* cfg80211 operation handler to get Tx power. */ +static int +nxpwifi_cfg80211_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int radio_idx, + unsigned int link_id, + int *dbm) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv =3D nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY); + int ret =3D 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 =3D priv->tx_power_level; + + return 0; +} + +/* + * cfg80211 handler for setting IEEE 802.11 Power Save mode. + * + * The 'timeout' parameter is not supported and is ignored. + */ +static int +nxpwifi_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool enabled, int timeout) +{ + struct nxpwifi_private *priv =3D 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 =3D enabled; + + return nxpwifi_drv_set_power(priv, &ps_mode); +} + +/* + * cfg80211 handler for setting the default WEP key. + * + * Ignored if WEP is not enabled. + */ +static int +nxpwifi_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *n= etdev, + int link_id, u8 key_index, bool unicast, + bool multicast) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(netdev); + int ret =3D 0; + + /* Return if WEP key not configured */ + if (!priv->sec_info.wep_enabled) + return 0; + + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_UAP) { + priv->wep_key_curr_index =3D key_index; + } else { + ret =3D 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; +} + +/* cfg80211 handler for adding an 802.11 encryption 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 =3D nxpwifi_netdev_get_priv(netdev); + struct nxpwifi_wep_key *wep_key; + u8 bc_mac[ETH_ALEN]; + const u8 *peer_mac; + int ret; + + eth_broadcast_addr(bc_mac); + peer_mac =3D pairwise ? mac_addr : bc_mac; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP && + (params->cipher =3D=3D WLAN_CIPHER_SUITE_WEP40 || + params->cipher =3D=3D WLAN_CIPHER_SUITE_WEP104)) { + if (params->key && params->key_len) { + wep_key =3D &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 =3D key_index; + wep_key->key_length =3D params->key_len; + priv->sec_info.wep_enabled =3D 1; + } + return 0; + } + + ret =3D 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; +} + +/* cfg80211 handler for setting the default management key. */ +static int +nxpwifi_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + int link_id, + u8 key_index) +{ + return 0; +} + +/* + * Sends regulatory domain information to the firmware. + * + * Includes: + * - Country code + * - Sub-band definitions (first channel, channel count, max TX power) + */ +int nxpwifi_send_domain_info_cmd_fw(struct wiphy *wiphy, enum nl80211_band= band) +{ + u8 no_of_triplet =3D 0; + struct ieee80211_country_ie_triplet *t; + u8 no_of_parsed_chan =3D 0; + u8 first_chan =3D 0, next_chan =3D 0, max_pwr =3D 0; + u8 i, flag =3D 0; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_802_11d_domain_reg *domain_info =3D &adapter->domain_reg; + int ret; + + domain_info->dfs_region =3D adapter->dfs_region; + + /* Set country code */ + domain_info->country_code[0] =3D adapter->country_code[0]; + domain_info->country_code[1] =3D adapter->country_code[1]; + domain_info->country_code[2] =3D ' '; + + if (!wiphy->bands[band]) { + nxpwifi_dbg(adapter, ERROR, + "11D: setting domain info in FW\n"); + return -EINVAL; + } + + sband =3D wiphy->bands[band]; + + for (i =3D 0; i < sband->n_channels ; i++) { + ch =3D &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag =3D 1; + first_chan =3D (u32)ch->hw_value; + next_chan =3D first_chan; + max_pwr =3D ch->max_power; + no_of_parsed_chan =3D 1; + continue; + } + + if (ch->hw_value =3D=3D next_chan + 1 && + ch->max_power =3D=3D max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + t =3D &domain_info->triplet[no_of_triplet]; + t->chans.first_channel =3D first_chan; + t->chans.num_channels =3D no_of_parsed_chan; + t->chans.max_power =3D max_pwr; + no_of_triplet++; + first_chan =3D (u32)ch->hw_value; + next_chan =3D first_chan; + max_pwr =3D ch->max_power; + no_of_parsed_chan =3D 1; + } + } + + if (flag) { + t =3D &domain_info->triplet[no_of_triplet]; + t->chans.first_channel =3D first_chan; + t->chans.num_channels =3D no_of_parsed_chan; + t->chans.max_power =3D max_pwr; + no_of_triplet++; + } + + domain_info->no_of_triplet =3D no_of_triplet; + + priv =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + ret =3D 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 =3D wiphy->bands[NL80211_BAND_5GHZ]; + + for (i =3D 0; i < sband->n_channels; i++) { + chan =3D &sband->channels[i]; + if ((!(chan->flags & IEEE80211_CHAN_DISABLED)) && + (chan->flags & IEEE80211_CHAN_RADAR)) + chan->flags |=3D IEEE80211_CHAN_NO_IR; + } +} + +/* + * cfg80211 regulatory domain change callback. + * + * Invoked when the regulatory domain is updated by: + * - the driver + * - the system core + * - the user + * - a received Country IE + */ +static void nxpwifi_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv =3D 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; + } + + /* Skip world/unchanged regulatory domains. */ + 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 =3D 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); + } +} + +/* Set fragmentation threshold, clamped to the valid range. */ +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 =3D 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); +} + +/* Set RTS threshold, clamped to the valid range. */ +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 =3D 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); +} + +/* + * cfg80211 op: set wiphy parameters. + * Updates RTS/fragmentation thresholds and retry limits based on 'changed' + * flags. + */ +static int +nxpwifi_cfg80211_set_wiphy_params(struct wiphy *wiphy, int radio_idx, u32 = changed) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv; + struct nxpwifi_uap_bss_param *bss_cfg; + int ret =3D 0; + + priv =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + switch (priv->bss_role) { + case NXPWIFI_BSS_ROLE_UAP: + bss_cfg =3D kzalloc(sizeof(*bss_cfg), GFP_KERNEL); + if (!bss_cfg) { + ret =3D -ENOMEM; + break; + } + + nxpwifi_set_sys_config_invalid_data(bss_cfg); + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + bss_cfg->rts_threshold =3D wiphy->rts_threshold; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + bss_cfg->frag_threshold =3D wiphy->frag_threshold; + if (changed & WIPHY_PARAM_RETRY_LONG) + bss_cfg->retry_limit =3D wiphy->retry_long; + + ret =3D 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 =3D nxpwifi_set_rts(priv, + wiphy->rts_threshold); + if (ret) + break; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + ret =3D nxpwifi_set_frag(priv, + wiphy->frag_threshold); + break; + } + + return ret; +} + +static int nxpwifi_deinit_priv_params(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + int ret; + + priv->host_mlme_reg =3D false; + priv->mgmt_frame_mask =3D 0; + + ret =3D 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); + + atomic_set(&adapter->iface_changing, 1); + flush_workqueue(adapter->workqueue); + flush_workqueue(adapter->rx_workqueue); + nxpwifi_free_priv(priv); + priv->wdev.iftype =3D NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode =3D NL80211_IFTYPE_UNSPECIFIED; + priv->sec_info.authentication_mode =3D 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 =3D priv->adapter; + + nxpwifi_init_priv(priv); + + priv->bss_mode =3D type; + priv->wdev.iftype =3D type; + + nxpwifi_init_priv_params(priv, priv->netdev); + priv->bss_started =3D 0; + + switch (type) { + case NL80211_IFTYPE_STATION: + priv->bss_role =3D NXPWIFI_BSS_ROLE_STA; + break; + case NL80211_IFTYPE_AP: + priv->bss_role =3D NXPWIFI_BSS_ROLE_UAP; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + priv->bss_num =3D nxpwifi_get_unused_bss_num(adapter, priv->bss_type); + + flush_workqueue(adapter->workqueue); + atomic_set(&adapter->iface_changing, 0); + + 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 !=3D + 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 !=3D + 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 +=3D change; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf +=3D change; + break; + case NL80211_IFTYPE_MONITOR: + 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 =3D nxpwifi_netdev_get_priv(dev); + + if (!priv) + return -EINVAL; + + adapter =3D priv->adapter; + + nxpwifi_dbg(adapter, INFO, + "%s: changing role to station\n", dev->name); + + ret =3D nxpwifi_deinit_priv_params(priv); + if (ret) + goto done; + ret =3D 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 =3D type; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) + goto done; + + ret =3D 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 =3D nxpwifi_netdev_get_priv(dev); + + if (!priv) + return -EINVAL; + + adapter =3D priv->adapter; + + nxpwifi_dbg(adapter, INFO, + "%s: changing role to AP\n", dev->name); + + ret =3D nxpwifi_deinit_priv_params(priv); + if (ret) + goto done; + + ret =3D 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 =3D type; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) + goto done; + + ret =3D nxpwifi_sta_init_cmd(priv, false, false); + +done: + return ret; +} + +/* cfg80211 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 =3D nxpwifi_netdev_get_priv(dev); + enum nl80211_iftype curr_iftype =3D dev->ieee80211_ptr->iftype; + + if (priv->scan_request) { + nxpwifi_dbg(priv->adapter, ERROR, + "change virtual interface: scan in process\n"); + return -EBUSY; + } + + if (type =3D=3D 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 =3D=3D 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 =3D priv->adapter; + u8 rate_format; + u8 he_dcm; + u8 stbc; + u8 gi; + u8 bw; + /* Bitrates in multiples of 100kb/s. */ + static const int legacy_rates[] =3D { + [0] =3D 10, + [1] =3D 20, + [2] =3D 55, + [3] =3D 110, + [4] =3D 60, /* NXPWIFI_RATE_INDEX_OFDM0 */ + [5] =3D 60, + [6] =3D 90, + [7] =3D 120, + [8] =3D 180, + [9] =3D 240, + [10] =3D 360, + [11] =3D 480, + [12] =3D 540, + }; + + rate_format =3D htinfo & 0x3; + + switch (rate_format) { + case RATE_FORMAT_LG: + if (rateinfo < ARRAY_SIZE(legacy_rates)) + rate->legacy =3D legacy_rates[rateinfo]; + break; + case RATE_FORMAT_HT: + rate->mcs =3D rateinfo; + rate->flags |=3D RATE_INFO_FLAGS_MCS; + break; + case RATE_FORMAT_VHT: + rate->mcs =3D rateinfo & 0xF; + rate->flags |=3D RATE_INFO_FLAGS_VHT_MCS; + break; + case RATE_FORMAT_HE: + rate->mcs =3D rateinfo & 0xF; + rate->flags |=3D RATE_INFO_FLAGS_HE_MCS; + he_dcm =3D 0; /* ToDo: ext_rate_info */ + gi =3D (htinfo & BIT(4)) >> 4 | + (htinfo & BIT(7)) >> 6; + stbc =3D (htinfo & BIT(5)) >> 5; + if (gi > 3) { + nxpwifi_dbg(adapter, ERROR, "Invalid gi value\n"); + break; + } + if (gi =3D=3D 3 && stbc && he_dcm) { + gi =3D 0; + stbc =3D 0; + he_dcm =3D 0; + } + if (gi > 0) + gi -=3D 1; + rate->he_gi =3D gi; + rate->he_dcm =3D he_dcm; + break; + } + + bw =3D (htinfo & 0xC) >> 2; + + switch (bw) { + case 0: + rate->bw =3D RATE_INFO_BW_20; + break; + case 1: + rate->bw =3D RATE_INFO_BW_40; + break; + case 2: + rate->bw =3D RATE_INFO_BW_80; + break; + case 3: + rate->bw =3D RATE_INFO_BW_160; + break; + } + + if (rate_format !=3D RATE_FORMAT_HE && (htinfo & BIT(4))) + rate->flags |=3D RATE_INFO_FLAGS_SHORT_GI; + + if ((rateinfo >> 4) =3D=3D 1) + rate->nss =3D 2; + else + rate->nss =3D 1; +} + +/* + * Dump station statistics into station_info. + * Includes bytes/packets counters, signal level, and TX/RX rates. + */ +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 =3D 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) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + if (!node) + return -ENOENT; + + sinfo->filled |=3D BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) | + BIT_ULL(NL80211_STA_INFO_TX_FAILED); + sinfo->inactive_time =3D + jiffies_to_msecs(jiffies - node->stats.last_rx); + + sinfo->signal =3D node->stats.rssi; + sinfo->signal_avg =3D node->stats.rssi; + sinfo->rx_bytes =3D node->stats.rx_bytes; + sinfo->tx_bytes =3D node->stats.tx_bytes; + sinfo->rx_packets =3D node->stats.rx_packets; + sinfo->tx_packets =3D node->stats.tx_packets; + sinfo->tx_failed =3D node->stats.tx_failed; + + nxpwifi_parse_htinfo(priv, priv->tx_rate, + node->stats.last_tx_htinfo, + &sinfo->txrate); + sinfo->txrate.legacy =3D node->stats.last_tx_rate * 5; + + return 0; + } + + /* Get signal information from the firmware */ + ret =3D 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 =3D nxpwifi_drv_get_data_rate(priv, &rate); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "getting data rate error\n"); + goto done; + } + + /* Retrieve DTIM period value 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 =3D priv->bcn_rssi_avg; + sinfo->rx_bytes =3D priv->stats.rx_bytes; + sinfo->tx_bytes =3D priv->stats.tx_bytes; + sinfo->rx_packets =3D priv->stats.rx_packets; + sinfo->tx_packets =3D priv->stats.tx_packets; + sinfo->signal =3D priv->bcn_rssi_avg; + /* Convert bitrate from 500 kb/s units to 100 kb/s units. */ + sinfo->txrate.legacy =3D rate * 5; + + sinfo->filled |=3D BIT(NL80211_STA_INFO_RX_BITRATE); + nxpwifi_parse_htinfo(priv, priv->rxpd_rate, priv->rxpd_htinfo, + &sinfo->rxrate); + + if (priv->bss_mode =3D=3D NL80211_IFTYPE_STATION) { + sinfo->filled |=3D BIT_ULL(NL80211_STA_INFO_BSS_PARAM); + sinfo->bss_param.flags =3D 0; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |=3D + BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |=3D + BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period =3D priv->dtim_period; + sinfo->bss_param.beacon_interval =3D + priv->curr_bss_params.bss_descriptor.beacon_period; + } + +done: + return ret; +} + +/* + * cfg80211 op: get station information. + * Works only when connected and fills station_info with current stats. + */ +static int +nxpwifi_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_sta_node *node; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) { + if (!priv->media_connected || + memcmp(mac, priv->cfg_bssid, ETH_ALEN)) + return -ENOENT; + node =3D NULL; + } else { + rcu_read_lock(); + node =3D nxpwifi_get_sta_entry(priv, mac); + rcu_read_unlock(); + } + + return nxpwifi_dump_station_info(priv, node, sinfo); +} + +/* cfg80211 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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_sta_node *node; + struct nxpwifi_sta_node *found =3D NULL; + int i; + + if ((GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) && + priv->media_connected && idx =3D=3D 0) { + ether_addr_copy(mac, priv->cfg_bssid); + return nxpwifi_dump_station_info(priv, NULL, sinfo); + } else if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + nxpwifi_send_cmd(priv, HOST_CMD_APCMD_STA_LIST, + HOST_ACT_GEN_GET, 0, NULL, true); + + i =3D 0; + rcu_read_lock(); + list_for_each_entry_rcu(node, &priv->sta_list, list) { + if (i++ !=3D idx) + continue; + found =3D node; + break; + } + rcu_read_unlock(); + + if (found) { + 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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_chan_stats *pchan_stats =3D priv->adapter->chan_stats; + enum nl80211_band band; + u8 chan_num; + + nxpwifi_dbg(priv->adapter, DUMP, "dump_survey idx=3D%d\n", idx); + + memset(survey, 0, sizeof(struct survey_info)); + + if ((GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) && + priv->media_connected && idx =3D=3D 0) { + u8 curr_bss_band =3D priv->curr_bss_params.band; + u32 chan =3D priv->curr_bss_params.bss_descriptor.channel; + + band =3D nxpwifi_band_to_radio_type(curr_bss_band); + survey->channel =3D ieee80211_get_channel + (wiphy, + ieee80211_channel_to_frequency(chan, band)); + + if (priv->bcn_nf_last) { + survey->filled =3D SURVEY_INFO_NOISE_DBM; + survey->noise =3D priv->bcn_nf_last; + } + return 0; + } + + if (idx >=3D priv->adapter->num_in_chan_stats) + return -ENOENT; + + if (!pchan_stats[idx].cca_scan_dur) + return 0; + + band =3D pchan_stats[idx].bandcfg; + chan_num =3D pchan_stats[idx].chan_num; + survey->channel =3D ieee80211_get_channel + (wiphy, + ieee80211_channel_to_frequency(chan_num, band)); + survey->filled =3D SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->noise =3D pchan_stats[idx].noise; + survey->time =3D pchan_stats[idx].cca_scan_dur; + survey->time_busy =3D pchan_stats[idx].cca_busy_dur; + + return 0; +} + +/* Supported rates to be advertised to the cfg80211 */ +static struct ieee80211_rate nxpwifi_rates[] =3D { + {.bitrate =3D 10, .hw_value =3D 2, }, + {.bitrate =3D 20, .hw_value =3D 4, }, + {.bitrate =3D 55, .hw_value =3D 11, }, + {.bitrate =3D 110, .hw_value =3D 22, }, + {.bitrate =3D 60, .hw_value =3D 12, }, + {.bitrate =3D 90, .hw_value =3D 18, }, + {.bitrate =3D 120, .hw_value =3D 24, }, + {.bitrate =3D 180, .hw_value =3D 36, }, + {.bitrate =3D 240, .hw_value =3D 48, }, + {.bitrate =3D 360, .hw_value =3D 72, }, + {.bitrate =3D 480, .hw_value =3D 96, }, + {.bitrate =3D 540, .hw_value =3D 108, }, +}; + +/* Channel definitions to be advertised to cfg80211 */ +static struct ieee80211_channel nxpwifi_channels_2ghz[] =3D { + {.center_freq =3D 2412, .hw_value =3D 1, }, + {.center_freq =3D 2417, .hw_value =3D 2, }, + {.center_freq =3D 2422, .hw_value =3D 3, }, + {.center_freq =3D 2427, .hw_value =3D 4, }, + {.center_freq =3D 2432, .hw_value =3D 5, }, + {.center_freq =3D 2437, .hw_value =3D 6, }, + {.center_freq =3D 2442, .hw_value =3D 7, }, + {.center_freq =3D 2447, .hw_value =3D 8, }, + {.center_freq =3D 2452, .hw_value =3D 9, }, + {.center_freq =3D 2457, .hw_value =3D 10, }, + {.center_freq =3D 2462, .hw_value =3D 11, }, + {.center_freq =3D 2467, .hw_value =3D 12, }, + {.center_freq =3D 2472, .hw_value =3D 13, }, + {.center_freq =3D 2484, .hw_value =3D 14, }, +}; + +static struct ieee80211_supported_band nxpwifi_band_2ghz =3D { + .band =3D NL80211_BAND_2GHZ, + .channels =3D nxpwifi_channels_2ghz, + .n_channels =3D ARRAY_SIZE(nxpwifi_channels_2ghz), + .bitrates =3D nxpwifi_rates, + .n_bitrates =3D ARRAY_SIZE(nxpwifi_rates), +}; + +static struct ieee80211_channel nxpwifi_channels_5ghz[] =3D { + {.center_freq =3D 5040, .hw_value =3D 8, }, + {.center_freq =3D 5060, .hw_value =3D 12, }, + {.center_freq =3D 5080, .hw_value =3D 16, }, + {.center_freq =3D 5170, .hw_value =3D 34, }, + {.center_freq =3D 5190, .hw_value =3D 38, }, + {.center_freq =3D 5210, .hw_value =3D 42, }, + {.center_freq =3D 5230, .hw_value =3D 46, }, + {.center_freq =3D 5180, .hw_value =3D 36, }, + {.center_freq =3D 5200, .hw_value =3D 40, }, + {.center_freq =3D 5220, .hw_value =3D 44, }, + {.center_freq =3D 5240, .hw_value =3D 48, }, + {.center_freq =3D 5260, .hw_value =3D 52, }, + {.center_freq =3D 5280, .hw_value =3D 56, }, + {.center_freq =3D 5300, .hw_value =3D 60, }, + {.center_freq =3D 5320, .hw_value =3D 64, }, + {.center_freq =3D 5500, .hw_value =3D 100, }, + {.center_freq =3D 5520, .hw_value =3D 104, }, + {.center_freq =3D 5540, .hw_value =3D 108, }, + {.center_freq =3D 5560, .hw_value =3D 112, }, + {.center_freq =3D 5580, .hw_value =3D 116, }, + {.center_freq =3D 5600, .hw_value =3D 120, }, + {.center_freq =3D 5620, .hw_value =3D 124, }, + {.center_freq =3D 5640, .hw_value =3D 128, }, + {.center_freq =3D 5660, .hw_value =3D 132, }, + {.center_freq =3D 5680, .hw_value =3D 136, }, + {.center_freq =3D 5700, .hw_value =3D 140, }, + {.center_freq =3D 5745, .hw_value =3D 149, }, + {.center_freq =3D 5765, .hw_value =3D 153, }, + {.center_freq =3D 5785, .hw_value =3D 157, }, + {.center_freq =3D 5805, .hw_value =3D 161, }, + {.center_freq =3D 5825, .hw_value =3D 165, }, +}; + +static struct ieee80211_supported_band nxpwifi_band_5ghz =3D { + .band =3D NL80211_BAND_5GHZ, + .channels =3D nxpwifi_channels_5ghz, + .n_channels =3D ARRAY_SIZE(nxpwifi_channels_5ghz), + .bitrates =3D nxpwifi_rates + 4, + .n_bitrates =3D ARRAY_SIZE(nxpwifi_rates) - 4, +}; + +/* Supported crypto cipher suits to be advertised to cfg80211 */ +static const u32 nxpwifi_cipher_suites[] =3D { + 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, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_CCMP_256, + WLAN_CIPHER_SUITE_BIP_GMAC_256, + WLAN_CIPHER_SUITE_BIP_CMAC_256, +}; + +/* Supported mgmt frame types to be advertised to cfg80211 */ +static const struct ieee80211_txrx_stypes +nxpwifi_mgmt_stypes[NUM_NL80211_IFTYPES] =3D { + [NL80211_IFTYPE_STATION] =3D { + .tx =3D BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx =3D BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_AP] =3D { + .tx =3D 0xffff, + .rx =3D 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), + }, +}; + +/* + * cfg80211 op: set bitrate mask. + * Converts cfg80211 bitrate selections into firmware bitmap format. + */ +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 =3D nxpwifi_netdev_get_priv(dev); + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + enum nl80211_band band; + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (!priv->media_connected) { + nxpwifi_dbg(adapter, ERROR, + "Can not set Tx data rate in disconnected state\n"); + return -EINVAL; + } + + band =3D nxpwifi_band_to_radio_type(priv->curr_bss_params.band); + + memset(bitmap_rates, 0, sizeof(bitmap_rates)); + + /* Fill HR/DSSS legacy rates (2.4 GHz only). */ + if (band =3D=3D NL80211_BAND_2GHZ) + bitmap_rates[0] =3D mask->control[band].legacy & 0x000f; + + /* Fill OFDM legacy rates. */ + if (band =3D=3D NL80211_BAND_2GHZ) + bitmap_rates[1] =3D (mask->control[band].legacy & 0x0ff0) >> 4; + else + bitmap_rates[1] =3D mask->control[band].legacy; + + /* Fill HT MCS bitmap (1x1 or 2x2 depending on hardware). */ + bitmap_rates[2] =3D mask->control[band].ht_mcs[0]; + if (adapter->hw_dev_mcs_support =3D=3D HT_STREAM_2X2) + bitmap_rates[2] |=3D mask->control[band].ht_mcs[1] << 8; + + /* Fill VHT MCS bitmap if supported by firmware. */ + if (adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) { + bitmap_rates[10] =3D mask->control[band].vht_mcs[0]; + if (adapter->hw_dev_mcs_support =3D=3D HT_STREAM_2X2) + bitmap_rates[11] =3D mask->control[band].vht_mcs[1]; + } + + return nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG, + HOST_ACT_GEN_SET, 0, bitmap_rates, true); +} + +/* + * cfg80211 op: configure connection-quality monitoring. + * Subscribes or unsubscribes HIGH_RSSI and LOW_RSSI events to firmware. + */ +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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_ds_misc_subsc_evt subsc_evt; + int ret =3D 0; + + priv->cqm_rssi_thold =3D rssi_thold; + priv->cqm_rssi_hyst =3D rssi_hyst; + + memset(&subsc_evt, 0x00, sizeof(struct nxpwifi_ds_misc_subsc_evt)); + subsc_evt.events =3D BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + + /* Subscribe/unsubscribe low and high rssi events */ + if (rssi_thold && rssi_hyst) { + subsc_evt.action =3D HOST_ACT_BITWISE_SET; + subsc_evt.bcn_l_rssi_cfg.abs_value =3D abs(rssi_thold); + subsc_evt.bcn_h_rssi_cfg.abs_value =3D abs(rssi_thold); + subsc_evt.bcn_l_rssi_cfg.evt_freq =3D 1; + subsc_evt.bcn_h_rssi_cfg.evt_freq =3D 1; + ret =3D nxpwifi_send_cmd(priv, + HOST_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } else { + subsc_evt.action =3D HOST_ACT_BITWISE_CLR; + ret =3D 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(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_update *params) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct cfg80211_beacon_data *data =3D ¶ms->beacon; + int ret; + + nxpwifi_cancel_scan(adapter); + + if (GET_BSS_ROLE(priv) !=3D NXPWIFI_BSS_ROLE_UAP) { + nxpwifi_dbg(priv->adapter, ERROR, + "%s: bss_type mismatched\n", __func__); + return -EINVAL; + } + + ret =3D 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 del_station. + * Function deauthenticates station which value is provided in mac paramet= er. + * 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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_sta_node *sta_node; + u8 deauth_mac[ETH_ALEN]; + int ret =3D 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); + + sta_node =3D nxpwifi_get_sta_entry(priv, params->mac); + if (sta_node) + ether_addr_copy(deauth_mac, params->mac); + + if (is_valid_ether_addr(deauth_mac)) { + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_UAP_STA_DEAUTH, + HOST_ACT_GEN_SET, 0, + deauth_mac, true); + nxpwifi_del_sta_entry(priv, deauth_mac); + } + return ret; +} + +static int +nxpwifi_cfg80211_set_antenna(struct wiphy *wiphy, int radio_idx, u32 tx_an= t, u32 rx_ant) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv =3D 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 !=3D 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 !=3D rx_ant) + return -EOPNOTSUPP; + + if ((tx_ant & (tx_ant - 1)) && + (tx_ant !=3D BIT(adapter->number_of_antenna) - 1)) + return -EOPNOTSUPP; + + if ((tx_ant =3D=3D BIT(adapter->number_of_antenna) - 1) && + priv->adapter->number_of_antenna > 1) { + tx_ant =3D RF_ANTENNA_AUTO; + rx_ant =3D RF_ANTENNA_AUTO; + } + } else { + struct ieee80211_sta_ht_cap *ht_info; + int rx_mcs_supp; + enum nl80211_band band; + + if ((tx_ant =3D=3D 0x1 && rx_ant =3D=3D 0x1)) { + adapter->user_dev_mcs_support =3D HT_STREAM_1X1; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support =3D + NXPWIFI_11AC_MCS_MAP_1X1; + } else { + adapter->user_dev_mcs_support =3D HT_STREAM_2X2; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support =3D + NXPWIFI_11AC_MCS_MAP_2X2; + } + + for (band =3D 0; band < NUM_NL80211_BANDS; band++) { + if (!adapter->wiphy->bands[band]) + continue; + + ht_info =3D &adapter->wiphy->bands[band]->ht_cap; + rx_mcs_supp =3D + 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 =3D tx_ant; + ant_cfg.rx_ant =3D 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, int radio_idx, u32 *tx_a= nt, u32 *rx_ant) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_private *priv =3D nxpwifi_get_priv(adapter, + NXPWIFI_BSS_ROLE_ANY); + int ret; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_RF_ANTENNA, + HOST_ACT_GEN_GET, 0, NULL, true); + + if (!ret) { + *tx_ant =3D priv->tx_ant; + *rx_ant =3D priv->rx_ant; + } + + return ret; +} + +/* + * cfg80211 op: stop AP. + * Stops the BSS running on the uAP interface. + */ +static int nxpwifi_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device= *dev, + unsigned int link_id) +{ + struct nxpwifi_private *priv =3D 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 =3D 0; + memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); + + ret =3D 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 =3D 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 op: start AP. + * Applies beacon/DTIM/SSID/security settings to the uAP configuration and + * starts the BSS. + */ +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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter =3D priv->adapter; + int ret; + + if (GET_BSS_ROLE(priv) !=3D NXPWIFI_BSS_ROLE_UAP) + return -EINVAL; + + if (!nxpwifi_is_channel_setting_allowable(priv, params->chandef.chan)) + return -EOPNOTSUPP; + + bss_cfg =3D 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 =3D params->beacon_interval; + if (params->dtim_period) + bss_cfg->dtim_period =3D params->dtim_period; + + if (params->ssid && params->ssid_len) { + memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); + bss_cfg->ssid.ssid_len =3D 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 =3D 10 * params->inactivity_timeout; + bss_cfg->ps_sta_ao_timer =3D 10 * params->inactivity_timeout; + } + + switch (params->hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + bss_cfg->bcast_ssid_ctl =3D 1; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + bss_cfg->bcast_ssid_ctl =3D 0; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + bss_cfg->bcast_ssid_ctl =3D 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 =3D 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 =3D + 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 =3D 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 =3D false; + } + + nxpwifi_config_uap_11d(priv, ¶ms->beacon); + + ret =3D nxpwifi_set_mgmt_ies(priv, ¶ms->beacon); + if (ret) + goto done; + + ret =3D nxpwifi_config_start_uap(priv, bss_cfg); + if (ret) { + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to start AP\n"); + 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; +} + +/* + * cfg80211 op: handle scan request. + * Issues a firmware scan using the requested parameters and reports the + * results. + */ +static int +nxpwifi_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct net_device *dev =3D request->wdev->netdev; + struct nxpwifi_private *priv =3D 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 requests during active scanning or scan cleanup. + * Prevents new scans when the interface is disabled or teardown is in + * progress. + */ + 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 =3D false; + + if (!nxpwifi_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); + + user_scan_cfg =3D kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) + return -ENOMEM; + + priv->scan_request =3D 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 =3D request->n_ssids; + user_scan_cfg->ssid_list =3D request->ssids; + + if (request->ie && request->ie_len) { + offset =3D 0; + for (i =3D 0; i < NXPWIFI_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask !=3D NXPWIFI_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask =3D NXPWIFI_VSIE_MASK_SCAN; + ie =3D (struct element *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, + sizeof(*ie) + ie->datalen); + offset +=3D sizeof(*ie) + ie->datalen; + + if (offset >=3D request->ie_len) + break; + } + } + + for (i =3D 0; i < min_t(u32, request->n_channels, + NXPWIFI_USER_SCAN_CHAN_MAX); i++) { + chan =3D request->channels[i]; + user_scan_cfg->chan_list[i].chan_number =3D chan->hw_value; + user_scan_cfg->chan_list[i].radio_type =3D chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + user_scan_cfg->chan_list[i].scan_type =3D + NXPWIFI_SCAN_TYPE_PASSIVE; + else + user_scan_cfg->chan_list[i].scan_type =3D + NXPWIFI_SCAN_TYPE_ACTIVE; + + user_scan_cfg->chan_list[i].scan_time =3D 0; + } + + if (priv->adapter->scan_chan_gap_enabled && + nxpwifi_is_any_intf_active(priv)) + user_scan_cfg->scan_chan_gap =3D + priv->adapter->scan_chan_gap_time; + + ret =3D 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 =3D false; + priv->scan_request =3D NULL; + return ret; + } + + if (request->ie && request->ie_len) { + for (i =3D 0; i < NXPWIFI_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask =3D=3D NXPWIFI_VSIE_MASK_SCAN) { + priv->vs_ie[i].mask =3D NXPWIFI_VSIE_MASK_CLEAR; + memset(&priv->vs_ie[i].ie, 0, + NXPWIFI_MAX_VSIE_LEN); + } + } + } + return 0; +} + +/* + * cfg80211 sched_scan_start handler. + * + * Send a bgscan configuration request to the firmware based on the + * scheduled scan parameters. On success, the firmware later issues a + * BGSCAN_REPORT event, after which the driver should query the firmware + * for scan results. + */ +static int +nxpwifi_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct nxpwifi_private *priv =3D 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=3D%d n_match_sets=3D%d ", + request->n_ssids, request->n_match_sets); + wiphy_info(wiphy, "n_channels=3D%d interval=3D%d ie_len=3D%d\n", + request->n_channels, request->scan_plans->interval, + (int)request->ie_len); + + bgscan_cfg =3D kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); + if (!bgscan_cfg) + return -ENOMEM; + + if (priv->scan_request || priv->scan_aborting) + bgscan_cfg->start_later =3D true; + + bgscan_cfg->num_ssids =3D request->n_match_sets; + bgscan_cfg->ssid_list =3D request->match_sets; + + if (request->ie && request->ie_len) { + offset =3D 0; + for (i =3D 0; i < NXPWIFI_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask !=3D NXPWIFI_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask =3D NXPWIFI_VSIE_MASK_BGSCAN; + ie =3D (struct element *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, + sizeof(*ie) + ie->datalen); + offset +=3D sizeof(*ie) + ie->datalen; + + if (offset >=3D request->ie_len) + break; + } + } + + for (i =3D 0; i < min_t(u32, request->n_channels, + NXPWIFI_BG_SCAN_CHAN_MAX); i++) { + chan =3D request->channels[i]; + bgscan_cfg->chan_list[i].chan_number =3D chan->hw_value; + bgscan_cfg->chan_list[i].radio_type =3D chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + bgscan_cfg->chan_list[i].scan_type =3D + NXPWIFI_SCAN_TYPE_PASSIVE; + else + bgscan_cfg->chan_list[i].scan_type =3D + NXPWIFI_SCAN_TYPE_ACTIVE; + + bgscan_cfg->chan_list[i].scan_time =3D 0; + } + + bgscan_cfg->chan_per_scan =3D min_t(u32, request->n_channels, + NXPWIFI_BG_SCAN_CHAN_MAX); + + /* Minimum scan cycle duration: 15 seconds */ + bgscan_cfg->scan_interval =3D (request->scan_plans->interval > + NXPWIFI_BGSCAN_INTERVAL) ? + request->scan_plans->interval : + NXPWIFI_BGSCAN_INTERVAL; + + bgscan_cfg->repeat_count =3D NXPWIFI_BGSCAN_REPEAT_COUNT; + bgscan_cfg->report_condition =3D NXPWIFI_BGSCAN_SSID_MATCH | + NXPWIFI_BGSCAN_WAIT_ALL_CHAN_DONE; + bgscan_cfg->bss_type =3D NXPWIFI_BSS_MODE_INFRA; + bgscan_cfg->action =3D NXPWIFI_BGSCAN_ACT_SET; + bgscan_cfg->enable =3D true; + if (request->min_rssi_thold !=3D NL80211_SCAN_RSSI_THOLD_OFF) { + bgscan_cfg->report_condition |=3D NXPWIFI_BGSCAN_SSID_RSSI_MATCH; + bgscan_cfg->rssi_threshold =3D request->min_rssi_thold; + } + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG, + HOST_ACT_GEN_SET, 0, bgscan_cfg, true); + + if (!ret) + priv->sched_scanning =3D true; + + kfree(bgscan_cfg); + return ret; +} + +/* + * cfg80211 sched_scan_stop handler. + * + * Send a bgscan configuration command to disable the previous + * background scan settings in the firmware. + */ +static int nxpwifi_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev, u64 reqid) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + + wiphy_info(wiphy, "sched scan stop!"); + return nxpwifi_stop_bg_scan(priv); +} + +/* + * Set default cfg80211 HT capabilities. + */ +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 =3D (u8 *)&mcs_set; + struct nxpwifi_adapter *adapter =3D priv->adapter; + + ht_info->ht_supported =3D true; + ht_info->ampdu_factor =3D IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density =3D 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 |=3D IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |=3D IEEE80211_HT_CAP_SGI_20; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_SGI_20; + + if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |=3D IEEE80211_HT_CAP_SGI_40; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_SGI_40; + + if (adapter->user_dev_mcs_support =3D=3D HT_STREAM_2X2) + ht_info->cap |=3D 2 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + else + ht_info->cap |=3D 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + + if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |=3D IEEE80211_HT_CAP_TX_STBC; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_TX_STBC; + + if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |=3D IEEE80211_HT_CAP_GRN_FLD; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_GRN_FLD; + + if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |=3D IEEE80211_HT_CAP_40MHZ_INTOLERANT; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + + if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |=3D IEEE80211_HT_CAP_LDPC_CODING; + else + ht_info->cap &=3D ~IEEE80211_HT_CAP_LDPC_CODING; + + ht_info->cap &=3D ~IEEE80211_HT_CAP_MAX_AMSDU; + ht_info->cap |=3D IEEE80211_HT_CAP_SM_PS; + + rx_mcs_supp =3D 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 =3D=3D 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 =3D 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 =3D priv->adapter; + + vht_info->vht_supported =3D true; + + vht_info->cap =3D adapter->hw_dot_11ac_dev_cap; + /* Update MCS support for VHT */ + vht_info->vht_mcs.rx_mcs_map =3D + cpu_to_le16(adapter->hw_dot_11ac_mcs_support & 0xFFFF); + vht_info->vht_mcs.rx_highest =3D 0; + vht_info->vht_mcs.tx_mcs_map =3D + cpu_to_le16(adapter->hw_dot_11ac_mcs_support >> 16); + vht_info->vht_mcs.tx_highest =3D 0; +} + +/* + * 5 GHz HE capability masks for UAP mode. + * + * MAC: TWT requester/respondor, broadcast TWT, OMI control. + * + * PHY: 40/80 MHz width, puncturing, LDPC, NDP 4xLTF, STBC, + * Doppler, DCM, SU BF/BFe, STS, sounding dims, extended + * range, PPE present, 4xLTF 0.8us GI, Rx 1024-QAM. + */ +#define UAP_HE_MAC_CAP0_MASK (IEEE80211_HE_MAC_CAP0_TWT_REQ | \ + IEEE80211_HE_MAC_CAP0_TWT_RES) + +#define UAP_HE_MAC_CAP1_MASK 0 +#define UAP_HE_MAC_CAP2_MASK IEEE80211_HE_MAC_CAP2_BCAST_TWT +#define UAP_HE_MAC_CAP3_MASK IEEE80211_HE_MAC_CAP3_OMI_CONTROL +#define UAP_HE_MAC_CAP4_MASK 0 +#define UAP_HE_MAC_CAP5_MASK 0 + +#define UAP_HE_PHY_CAP0_MASK IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ= _80MHZ_IN_5G +#define UAP_HE_PHY_CAP1_MASK (IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD= | \ + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_20MHZ | \ + IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_40MHZ) +#define UAP_HE_PHY_CAP2_MASK (IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |= \ + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | \ + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | \ + IEEE80211_HE_PHY_CAP2_DOPPLER_TX | \ + IEEE80211_HE_PHY_CAP2_DOPPLER_RX) +#define UAP_HE_PHY_CAP3_MASK (IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK = | \ + IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | \ + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | \ + IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 | \ + IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER) +#define UAP_HE_PHY_CAP4_MASK (IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | \ + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8) +#define UAP_HE_PHY_CAP5_MASK IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_= UNDER_80MHZ_2 +#define UAP_HE_PHY_CAP6_MASK (IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |= \ + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) +#define UAP_HE_PHY_CAP7_MASK (IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AN= D_08_US_GI | \ + IEEE80211_HE_PHY_CAP7_MAX_NC_1) +#define UAP_HE_PHY_CAP8_MASK 0 +#define UAP_HE_PHY_CAP9_MASK IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_2= 42_TONE_RU +#define UAP_HE_PHY_CAP10_MASK 0 + +/* + * 2.4 GHz HE capability masks for UAP mode. + * + * MAC: HTC HE, OMI control (no UL OFDMA). + * PHY: 40 MHz, LDPC, NDP 4xLTF, STBC, Doppler, DCM, + * SU BF/BFe, STS/sounding dims, extended range, + * PPE present, 4xLTF 0.8us GI, Rx 1024-QAM. + */ +#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 IEEE80211_HE_MAC_CAP3_OMI_CONTROL +#define UAP_HE_2G_MAC_CAP4_MASK 0x00 +#define UAP_HE_2G_MAC_CAP5_MASK 0x00 + +#define UAP_HE_2G_PHY_CAP0_MASK IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40= MHZ_IN_2G +#define UAP_HE_2G_PHY_CAP1_MASK IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLO= AD +#define UAP_HE_2G_PHY_CAP2_MASK (IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2U= S | \ + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | \ + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | \ + IEEE80211_HE_PHY_CAP2_DOPPLER_TX | \ + IEEE80211_HE_PHY_CAP2_DOPPLER_RX) +#define UAP_HE_2G_PHY_CAP3_MASK (IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BP= SK | \ + IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 | \ + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK | \ + IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 | \ + IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER) +#define UAP_HE_2G_PHY_CAP4_MASK (IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | \ + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8) +#define UAP_HE_2G_PHY_CAP5_MASK IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_D= IM_UNDER_80MHZ_2 +#define UAP_HE_2G_PHY_CAP6_MASK (IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANG= E | \ + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) +#define UAP_HE_2G_PHY_CAP7_MASK (IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF= _AND_08_US_GI | \ + IEEE80211_HE_PHY_CAP7_MAX_NC_1) +#define UAP_HE_2G_PHY_CAP8_MASK 0x00 +#define UAP_HE_2G_PHY_CAP9_MASK IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THA= N_242_TONE_RU +#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 =3D=3D BAND_A) { + he_cap_cfg->cap_elem.mac_cap_info[0] &=3D UAP_HE_MAC_CAP0_MASK; + he_cap_cfg->cap_elem.mac_cap_info[1] &=3D UAP_HE_MAC_CAP1_MASK; + he_cap_cfg->cap_elem.mac_cap_info[2] &=3D UAP_HE_MAC_CAP2_MASK; + he_cap_cfg->cap_elem.mac_cap_info[3] &=3D UAP_HE_MAC_CAP3_MASK; + he_cap_cfg->cap_elem.mac_cap_info[4] &=3D UAP_HE_MAC_CAP4_MASK; + he_cap_cfg->cap_elem.mac_cap_info[5] &=3D UAP_HE_MAC_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[0] &=3D UAP_HE_PHY_CAP0_MASK; + he_cap_cfg->cap_elem.phy_cap_info[1] &=3D UAP_HE_PHY_CAP1_MASK; + he_cap_cfg->cap_elem.phy_cap_info[2] &=3D UAP_HE_PHY_CAP2_MASK; + he_cap_cfg->cap_elem.phy_cap_info[3] &=3D UAP_HE_PHY_CAP3_MASK; + he_cap_cfg->cap_elem.phy_cap_info[4] &=3D UAP_HE_PHY_CAP4_MASK; + he_cap_cfg->cap_elem.phy_cap_info[5] &=3D UAP_HE_PHY_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[6] &=3D UAP_HE_PHY_CAP6_MASK; + he_cap_cfg->cap_elem.phy_cap_info[7] &=3D UAP_HE_PHY_CAP7_MASK; + he_cap_cfg->cap_elem.phy_cap_info[8] &=3D UAP_HE_PHY_CAP8_MASK; + he_cap_cfg->cap_elem.phy_cap_info[9] &=3D UAP_HE_PHY_CAP9_MASK; + he_cap_cfg->cap_elem.phy_cap_info[10] &=3D UAP_HE_PHY_CAP10_MASK; + } else { + he_cap_cfg->cap_elem.mac_cap_info[0] &=3D UAP_HE_2G_MAC_CAP0_MASK; + he_cap_cfg->cap_elem.mac_cap_info[1] &=3D UAP_HE_2G_MAC_CAP1_MASK; + he_cap_cfg->cap_elem.mac_cap_info[2] &=3D UAP_HE_2G_MAC_CAP2_MASK; + he_cap_cfg->cap_elem.mac_cap_info[3] &=3D UAP_HE_2G_MAC_CAP3_MASK; + he_cap_cfg->cap_elem.mac_cap_info[4] &=3D UAP_HE_2G_MAC_CAP4_MASK; + he_cap_cfg->cap_elem.mac_cap_info[5] &=3D UAP_HE_2G_MAC_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[0] &=3D UAP_HE_2G_PHY_CAP0_MASK; + he_cap_cfg->cap_elem.phy_cap_info[1] &=3D UAP_HE_2G_PHY_CAP1_MASK; + he_cap_cfg->cap_elem.phy_cap_info[2] &=3D UAP_HE_2G_PHY_CAP2_MASK; + he_cap_cfg->cap_elem.phy_cap_info[3] &=3D UAP_HE_2G_PHY_CAP3_MASK; + he_cap_cfg->cap_elem.phy_cap_info[4] &=3D UAP_HE_2G_PHY_CAP4_MASK; + he_cap_cfg->cap_elem.phy_cap_info[5] &=3D UAP_HE_2G_PHY_CAP5_MASK; + he_cap_cfg->cap_elem.phy_cap_info[6] &=3D UAP_HE_2G_PHY_CAP6_MASK; + he_cap_cfg->cap_elem.phy_cap_info[7] &=3D UAP_HE_2G_PHY_CAP7_MASK; + he_cap_cfg->cap_elem.phy_cap_info[8] &=3D UAP_HE_2G_PHY_CAP8_MASK; + he_cap_cfg->cap_elem.phy_cap_info[9] &=3D UAP_HE_2G_PHY_CAP9_MASK; + he_cap_cfg->cap_elem.phy_cap_info[10] &=3D 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 =3D 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 =3D=3D NL80211_BAND_5GHZ) { + hw_he_cap_len =3D 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 =3D 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 =3D kmalloc(sizeof(*iftype_data), GFP_KERNEL); + if (!iftype_data) + return; + memset(iftype_data, 0, sizeof(*iftype_data)); + + iftype_data->types_mask =3D + BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + iftype_data->he_cap.has_he =3D 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 =3D 0; + /* Add 160 MHz MCS/NSS if supported */ + if (he_cap_cfg.cap_elem.phy_cap_info[0] & BIT(3)) + extra_mcs_size +=3D 4; + /* Add 80+80 MHz MCS/NSS if supported */ + if (he_cap_cfg.cap_elem.phy_cap_info[0] & BIT(4)) + extra_mcs_size +=3D 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); + + /* Parse PPE thresholds when present */ + ppe_threshold_len =3D 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] &=3D BIT(7); + } + + _ieee80211_set_sband_iftype_data(band, iftype_data, 1); +} + +/* 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 =3D 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 =3D=3D + adapter->iface_limit.sta_intf) { + nxpwifi_dbg(adapter, ERROR, + "cannot create multiple sta ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv =3D 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 =3D wiphy; + priv->wdev.iftype =3D NL80211_IFTYPE_STATION; + + if (type =3D=3D NL80211_IFTYPE_UNSPECIFIED) + priv->bss_mode =3D NL80211_IFTYPE_STATION; + else + priv->bss_mode =3D type; + + priv->bss_type =3D NXPWIFI_BSS_TYPE_STA; + priv->frame_type =3D NXPWIFI_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority =3D 0; + priv->bss_role =3D NXPWIFI_BSS_ROLE_STA; + + break; + case NL80211_IFTYPE_AP: + if (adapter->curr_iface_comb.uap_intf =3D=3D + adapter->iface_limit.uap_intf) { + nxpwifi_dbg(adapter, ERROR, + "cannot create multiple AP ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv =3D 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 =3D wiphy; + priv->wdev.iftype =3D NL80211_IFTYPE_AP; + + priv->bss_type =3D NXPWIFI_BSS_TYPE_UAP; + priv->frame_type =3D NXPWIFI_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority =3D 0; + priv->bss_role =3D NXPWIFI_BSS_ROLE_UAP; + priv->bss_started =3D 0; + priv->bss_mode =3D type; + + break; + case NL80211_IFTYPE_MONITOR: + priv =3D 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 =3D wiphy; + priv->wdev.iftype =3D NL80211_IFTYPE_MONITOR; + + priv->bss_type =3D NXPWIFI_BSS_TYPE_UAP; + priv->frame_type =3D NXPWIFI_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority =3D 0; + priv->bss_started =3D 0; + priv->bss_mode =3D type; + + break; + default: + nxpwifi_dbg(adapter, ERROR, "type not supported\n"); + return ERR_PTR(-EINVAL); + } + + dev =3D 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 =3D -ENOMEM; + goto err_alloc_netdev; + } + + nxpwifi_init_priv_params(priv, dev); + + priv->netdev =3D dev; + + nxpwifi_set_mac_address(priv, dev, false, NULL); + + if (type !=3D NL80211_IFTYPE_MONITOR) { + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE, + HOST_ACT_GEN_SET, 0, NULL, true); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: err_set_bss_mode\n", __func__); + goto err_set_bss_mode; + } + } + + ret =3D nxpwifi_sta_init_cmd(priv, false, false); + if (ret) + goto err_sta_init; + + dev_net_set(dev, wiphy_net(wiphy)); + dev->ieee80211_ptr =3D &priv->wdev; + dev->ieee80211_ptr->iftype =3D priv->bss_mode; + SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); + + dev->flags |=3D IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo =3D NXPWIFI_DEFAULT_WATCHDOG_TIMEOUT; + dev->needed_headroom =3D NXPWIFI_MIN_DATA_HEADER_LEN; + dev->ethtool_ops =3D &nxpwifi_ethtool_ops; + + mdev_priv =3D netdev_priv(dev); + *((unsigned long *)mdev_priv) =3D (unsigned long)priv; + + if (type =3D=3D NL80211_IFTYPE_MONITOR) + dev->type =3D ARPHRD_IEEE80211_RADIOTAP; + + SET_NETDEV_DEV(dev, adapter->dev); + + wiphy_work_init(&priv->reset_conn_state_work, nxpwifi_reset_conn_state_wo= rk); + + wiphy_delayed_work_init(&priv->dfs_cac_work, nxpwifi_dfs_cac_work); + + wiphy_delayed_work_init(&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 =3D -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 =3D NULL; +err_sta_init: +err_set_bss_mode: +err_alloc_netdev: + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype =3D NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode =3D 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 *wde= v) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(wdev->netdev); + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct sk_buff *skb, *tmp; + +#ifdef CONFIG_DEBUG_FS + nxpwifi_dev_debugfs_remove(priv); +#endif + if (priv->bss_mode =3D=3D NL80211_IFTYPE_MONITOR) { + struct nxpwifi_802_11_net_monitor netmon_cfg; + + memset(&netmon_cfg, 0, sizeof(struct nxpwifi_802_11_net_monitor)); + nxpwifi_send_cmd(priv, HOST_CMD_802_11_NET_MONITOR, + HOST_ACT_GEN_SET, 0, &netmon_cfg, true); + } + + if (priv->sched_scanning) + priv->sched_scanning =3D 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 =3D=3D NETREG_REGISTERED) + cfg80211_unregister_netdevice(wdev->netdev); + + /* Clear the priv in adapter */ + priv->netdev =3D NULL; + + update_vif_type_counter(adapter, priv->bss_mode, -1); + + priv->bss_mode =3D NL80211_IFTYPE_UNSPECIFIED; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) =3D=3D 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_se= q, + u8 max_byte_seq) +{ + int j, k, valid_byte_cnt =3D 0; + bool dont_care_byte =3D false; + + for (j =3D 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k =3D 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 =3D 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] =3D 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 =3D 0, num_ipv4 =3D 0; + struct in_device *in_dev; + struct in_ifaddr *ifa; + __be32 ips[NXPWIFI_MAX_SUPPORTED_IPADDR]; + struct nxpwifi_adapter *adapter =3D priv->adapter; + + mef_entry->mode =3D MEF_MODE_HOST_SLEEP; + mef_entry->action =3D MEF_ACTION_AUTO_ARP; + + /* Enable ARP offload feature */ + memset(ips, 0, sizeof(ips)); + for (i =3D 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]->netdev) { + in_dev =3D __in_dev_get_rtnl(adapter->priv[i]->netdev); + if (!in_dev) + continue; + ifa =3D rtnl_dereference(in_dev->ifa_list); + if (!ifa || !ifa->ifa_local) + continue; + ips[i] =3D ifa->ifa_local; + num_ipv4++; + } + } + + for (i =3D 0; i < num_ipv4; i++) { + if (!ips[i]) + continue; + mef_entry->filter[filt_num].repeat =3D 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] =3D + sizeof(ips[i]); + mef_entry->filter[filt_num].offset =3D 46; + mef_entry->filter[filt_num].filt_type =3D TYPE_EQ; + if (filt_num) { + mef_entry->filter[filt_num].filt_action =3D + TYPE_OR; + } + filt_num++; + } + + mef_entry->filter[filt_num].repeat =3D 1; + mef_entry->filter[filt_num].byte_seq[0] =3D 0x08; + mef_entry->filter[filt_num].byte_seq[1] =3D 0x06; + mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] =3D 2; + mef_entry->filter[filt_num].offset =3D 20; + mef_entry->filter[filt_num].filt_type =3D TYPE_EQ; + mef_entry->filter[filt_num].filt_action =3D 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 =3D 0, ret =3D 0; + bool first_pat =3D true; + u8 byte_seq[NXPWIFI_MEF_MAX_BYTESEQ + 1]; + + mef_entry->mode =3D MEF_MODE_HOST_SLEEP; + mef_entry->action =3D MEF_ACTION_ALLOW_AND_WAKEUP_HOST; + + for (i =3D 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] =3D=3D 1) { + mef_cfg->criteria |=3D NXPWIFI_CRITERIA_UNICAST; + continue; + } else if (is_broadcast_ether_addr(byte_seq)) { + mef_cfg->criteria |=3D NXPWIFI_CRITERIA_BROADCAST; + continue; + } else if ((!memcmp(byte_seq, "\x33\x33", 2) && + (byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] =3D=3D 2)) || + (!memcmp(byte_seq, "\x01\x00\x5e", 3) && + (byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] =3D=3D 3))) { + mef_cfg->criteria |=3D NXPWIFI_CRITERIA_MULTICAST; + continue; + } + } + mef_entry->filter[filt_num].repeat =3D 1; + mef_entry->filter[filt_num].offset =3D + 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 =3D TYPE_EQ; + + if (first_pat) { + first_pat =3D false; + nxpwifi_dbg(priv->adapter, INFO, "Wake on patterns\n"); + } else { + mef_entry->filter[filt_num].filt_action =3D TYPE_AND; + } + + filt_num++; + } + + if (wowlan->magic_pkt) { + mef_cfg->criteria |=3D NXPWIFI_CRITERIA_UNICAST; + mef_entry->filter[filt_num].repeat =3D 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] =3D + ETH_ALEN; + mef_entry->filter[filt_num].offset =3D 28; + mef_entry->filter[filt_num].filt_type =3D TYPE_EQ; + if (filt_num) + mef_entry->filter[filt_num].filt_action =3D TYPE_OR; + + filt_num++; + mef_entry->filter[filt_num].repeat =3D 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] =3D + ETH_ALEN; + mef_entry->filter[filt_num].offset =3D 56; + mef_entry->filter[filt_num].filt_type =3D TYPE_EQ; + mef_entry->filter[filt_num].filt_action =3D 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 =3D 0, num_entries =3D 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 =3D kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); + if (!mef_entry) + return -ENOMEM; + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.criteria |=3D NXPWIFI_CRITERIA_BROADCAST | + NXPWIFI_CRITERIA_UNICAST; + mef_cfg.num_entries =3D num_entries; + mef_cfg.mef_entry =3D mef_entry; + + nxpwifi_set_auto_arp_mef_entry(priv, &mef_entry[0]); + + if (wowlan->n_patterns || wowlan->magic_pkt) { + ret =3D nxpwifi_set_wowlan_mef_entry(priv, &mef_cfg, + &mef_entry[1], wowlan); + if (ret) + goto done; + } + + if (!mef_cfg.criteria) + mef_cfg.criteria =3D NXPWIFI_CRITERIA_BROADCAST | + NXPWIFI_CRITERIA_UNICAST | + NXPWIFI_CRITERIA_MULTICAST; + + ret =3D 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 =3D nxpwifi_cfg80211_get_adapter(wiphy); + struct nxpwifi_ds_hs_cfg hs_cfg; + int i, ret =3D 0, retry_num =3D 10; + struct nxpwifi_private *priv; + struct nxpwifi_private *sta_priv =3D + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + + sta_priv->scan_aborting =3D true; + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + nxpwifi_abort_cac(priv); + } + + nxpwifi_cancel_all_pending_cmd(adapter); + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + if (priv->netdev) + netif_device_detach(priv->netdev); + } + + for (i =3D 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 =3D 0; + goto done; + } + + if (!sta_priv->media_connected && !wowlan->nd_config) { + nxpwifi_dbg(adapter, ERROR, + "Can not configure WOWLAN in disconnected state\n"); + ret =3D 0; + goto done; + } + + ret =3D 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 =3D le32_to_cpu(adapter->hs_cfg.conditions); + + if (wowlan->nd_config) { + nxpwifi_dbg(adapter, INFO, "Wake on net detect\n"); + hs_cfg.conditions |=3D HS_CFG_COND_MAC_EVENT; + nxpwifi_cfg80211_sched_scan_start(wiphy, sta_priv->netdev, + wowlan->nd_config); + } + + if (wowlan->disconnect) { + hs_cfg.conditions |=3D HS_CFG_COND_MAC_EVENT; + nxpwifi_dbg(sta_priv->adapter, INFO, "Wake on device disconnect\n"); + } + + hs_cfg.is_invoke_hostcmd =3D false; + hs_cfg.gpio =3D adapter->hs_cfg.gpio; + hs_cfg.gap =3D adapter->hs_cfg.gap; + ret =3D 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 =3D false; + return ret; +} + +static int nxpwifi_cfg80211_resume(struct wiphy *wiphy) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D true; + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + if (priv->netdev) + netif_device_attach(priv->netdev); + } + + if (!wiphy->wowlan_config) + goto done; + + priv =3D 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 =3D -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 =3D true; + if (wiphy->wowlan_config->nd_config) + wakeup_report.net_detect =3D adapter->nd_info; + break; + case NON_MASKABLE_CONDITION_MATCHED: + break; + case MAGIC_PATTERN_MATCHED: + if (wiphy->wowlan_config->magic_pkt) + wakeup_report.magic_pkt =3D true; + if (wiphy->wowlan_config->n_patterns) + wakeup_report.pattern_idx =3D 1; + break; + case GTK_REKEY_FAILURE: + if (wiphy->wowlan_config->gtk_rekey_failure) + wakeup_report.gtk_rekey_failure =3D true; + break; + default: + report_wakeup_reason =3D false; + break; + } + + if (report_wakeup_reason) + cfg80211_report_wowlan_wakeup(&priv->wdev, &wakeup_report, + GFP_KERNEL); + +done: + if (adapter->nd_info) { + for (i =3D 0 ; i < adapter->nd_info->n_matches ; i++) + kfree(adapter->nd_info->matches[i]); + kfree(adapter->nd_info); + adapter->nd_info =3D NULL; + } + + return 0; +} + +static void nxpwifi_cfg80211_set_wakeup(struct wiphy *wiphy, + bool enabled) +{ + struct nxpwifi_adapter *adapter =3D nxpwifi_cfg80211_get_adapter(wiphy); + + device_set_wakeup_enable(adapter->dev, enabled); +} +#endif + +static int nxpwifi_get_coalesce_pkt_type(u8 *byte_seq) +{ + if ((byte_seq[0] & 0x01) && + byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] =3D=3D 1) + return PACKET_TYPE_UNICAST; + else if (is_broadcast_ether_addr(byte_seq)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, "\x33\x33", 2) && + byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] =3D=3D 2) || + (!memcmp(byte_seq, "\x01\x00\x5e", 3) && + byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] =3D=3D 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 =3D crule->delay; + + param =3D mrule->params; + + for (i =3D 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 =3D 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 =3D pkt_type; + continue; + } + } + + if (crule->condition =3D=3D NL80211_COALESCE_CONDITION_MATCH) + param->operation =3D RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation =3D RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len =3D byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset =3D 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 =3D nxpwifi_cfg80211_get_adapter(wiphy); + int i, ret; + struct nxpwifi_ds_coalesce_cfg coalesce_cfg; + struct nxpwifi_private *priv =3D + 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 =3D coalesce->n_rules; + for (i =3D 0; i < coalesce->n_rules; i++) { + ret =3D 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 *m= ac, + struct station_parameters *params) +{ + struct nxpwifi_sta_info add_sta; + int ret; + + memcpy(add_sta.peer_mac, mac, ETH_ALEN); + add_sta.params =3D params; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_ADD_NEW_STATION, + HOST_ACT_ADD_STA, 0, (void *)&add_sta, true); + + 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 =3D nxpwifi_netdev_get_priv(dev); + int ret =3D -EOPNOTSUPP; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) + ret =3D nxpwifi_cfg80211_uap_add_station(priv, mac, params); + + return ret; +} + +static int +nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *de= v, + struct cfg80211_csa_settings *params) +{ + struct nxpwifi_private *priv =3D 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 =3D true; + } + + ret =3D nxpwifi_del_mgmt_ies(priv); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + ret =3D 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->ap_update_info.beacon, ¶ms->beacon_after, + sizeof(priv->ap_update_info.beacon)); + + chsw_msec =3D max(params->count * priv->bss_cfg.beacon_period, 100); + + nxpwifi_queue_delayed_wiphy_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 =3D 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 =3D -ENODATA; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP && + cfg80211_chandef_valid(&priv->bss_chandef)) { + *chandef =3D priv->bss_chandef; + ret =3D 0; + } else if (priv->media_connected) { + curr_bss =3D &priv->curr_bss_params.bss_descriptor; + band =3D nxpwifi_band_to_radio_type(priv->curr_bss_params.band); + freq =3D ieee80211_channel_to_frequency(curr_bss->channel, band); + chan =3D ieee80211_get_channel(wiphy, freq); + + if (priv->ht_param_present) { + chan_type =3D nxpwifi_get_chan_type(priv); + cfg80211_chandef_create(chandef, chan, chan_type); + } else { + cfg80211_chandef_create(chandef, chan, + NL80211_CHAN_NO_HT); + } + ret =3D 0; + } + + return ret; +} + +#ifdef CONFIG_NL80211_TESTMODE + +enum nxpwifi_tm_attr { + __NXPWIFI_TM_ATTR_INVALID =3D 0, + NXPWIFI_TM_ATTR_CMD =3D 1, + NXPWIFI_TM_ATTR_DATA =3D 2, + + /* keep last */ + __NXPWIFI_TM_ATTR_AFTER_LAST, + NXPWIFI_TM_ATTR_MAX =3D __NXPWIFI_TM_ATTR_AFTER_LAST - 1, +}; + +static const struct nla_policy nxpwifi_tm_policy[NXPWIFI_TM_ATTR_MAX + 1] = =3D { + [NXPWIFI_TM_ATTR_CMD] =3D { .type =3D NLA_U32 }, + [NXPWIFI_TM_ATTR_DATA] =3D { .type =3D NLA_BINARY, + .len =3D NXPWIFI_SIZE_OF_CMD_BUFFER }, +}; + +enum nxpwifi_tm_command { + NXPWIFI_TM_CMD_HOSTCMD =3D 0, +}; + +static int nxpwifi_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len) +{ + struct nxpwifi_private *priv =3D 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 =3D 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 =3D kzalloc(sizeof(*hostcmd), GFP_KERNEL); + if (!hostcmd) + return -ENOMEM; + + hostcmd->len =3D 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)) { + nxpwifi_dbg(priv->adapter, ERROR, "Failed to process hostcmd\n"); + kfree(hostcmd); + return -EFAULT; + } + + /* process hostcmd response*/ + skb =3D cfg80211_testmode_alloc_reply_skb(wiphy, hostcmd->len); + if (!skb) { + kfree(hostcmd); + return -ENOMEM; + } + err =3D nla_put(skb, NXPWIFI_TM_ATTR_DATA, + hostcmd->len, hostcmd->cmd); + if (err) { + kfree(hostcmd); + kfree_skb(skb); + return -EMSGSIZE; + } + + err =3D 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 =3D 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 =3D true; + } + + memset(&radar_params, 0, sizeof(struct nxpwifi_radar_params)); + radar_params.chandef =3D chandef; + radar_params.cac_time_ms =3D cac_time_ms; + + memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST, + HOST_ACT_GEN_SET, 0, &radar_params, true); + if (!ret) + nxpwifi_queue_delayed_wiphy_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 *de= v, + 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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct sk_buff *skb; + u16 pkt_len, auth_alg; + int ret; + struct ieee80211_mgmt *mgmt; + struct nxpwifi_txinfo *tx_info; + u8 trans =3D 1, status_code =3D 0; + u8 *varptr =3D NULL; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + nxpwifi_dbg(adapter, ERROR, "Interface role is AP\n"); + return -EINVAL; + } + + if (priv->wdev.iftype !=3D 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 !=3D 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 =3D true; + priv->mgmt_frame_mask |=3D 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 =3D WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + auth_alg =3D WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + auth_alg =3D WLAN_AUTH_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + auth_alg =3D WLAN_AUTH_LEAP; + break; + case NL80211_AUTHTYPE_SAE: + auth_alg =3D WLAN_AUTH_SAE; + break; + default: + nxpwifi_dbg(adapter, ERROR, + "unsupported auth type=3D%d\n", req->auth_type); + return -EOPNOTSUPP; + } + + if (!priv->auth_flag) { + ret =3D nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_SET, + req->bss->channel, + AUTH_TX_DEFAULT_WAIT_TIME); + + if (!ret) { + priv->roc_cfg.cookie =3D + nxpwifi_roc_cookie(adapter); + priv->roc_cfg.chan =3D *req->bss->channel; + } else { + return -EPERM; + } + } + + priv->sec_info.authentication_mode =3D auth_alg; + + nxpwifi_cancel_scan(adapter); + + pkt_len =3D (u16)req->ie_len + req->auth_data_len + + NXPWIFI_MGMT_HEADER_LEN + NXPWIFI_AUTH_BODY_LEN; + + if (req->auth_data_len >=3D 4) + pkt_len -=3D 4; + + mgmt =3D kzalloc(pkt_len, GFP_KERNEL); + + skb =3D 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 =3D NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num =3D priv->bss_num; + tx_info->bss_type =3D priv->bss_type; + tx_info->pkt_len =3D pkt_len; + + memcpy(mgmt->da, req->bss->bssid, ETH_ALEN); + memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->bssid, req->bss->bssid, ETH_ALEN); + mgmt->frame_control =3D + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); + + if (req->auth_data_len >=3D 4) { + if (req->auth_type =3D=3D NL80211_AUTHTYPE_SAE) { + __le16 *pos =3D (__le16 *)req->auth_data; + + trans =3D le16_to_cpu(pos[0]); + status_code =3D le16_to_cpu(pos[1]); + } + memcpy((u8 *)(&mgmt->u.auth.variable), req->auth_data + 4, + req->auth_data_len - 4); + varptr =3D (u8 *)&mgmt->u.auth.variable + + (req->auth_data_len - 4); + } + + mgmt->u.auth.auth_alg =3D cpu_to_le16(auth_alg); + mgmt->u.auth.auth_transaction =3D cpu_to_le16(trans); + mgmt->u.auth.status_code =3D cpu_to_le16(status_code); + + if (req->ie && req->ie_len) { + if (!varptr) + varptr =3D (u8 *)&mgmt->u.auth.variable; + memcpy((u8 *)varptr, req->ie, req->ie_len); + } + + nxpwifi_form_mgmt_frame(skb, (const u8 *)mgmt, pkt_len); + kfree(mgmt); + priv->auth_flag =3D HOST_MLME_AUTH_PENDING; + priv->auth_alg =3D auth_alg; + skb->priority =3D 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 =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_adapter *adapter =3D priv->adapter; + int ret; + struct cfg80211_ssid req_ssid; + const u8 *ssid_ie; + + if (GET_BSS_ROLE(priv) !=3D 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 =3D=3D WLAN_AUTH_SAE) + priv->auth_flag =3D HOST_MLME_AUTH_DONE; + + if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE)) + return -EBUSY; + + if (priv->roc_cfg.cookie) { + ret =3D 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 =3D ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + + if (!ssid_ie) + goto ssid_err; + + req_ssid.ssid_len =3D 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 =3D false; + priv->sec_info.wpa2_enabled =3D false; + priv->wep_key_curr_index =3D 0; + priv->sec_info.encryption_mode =3D 0; + priv->sec_info.is_authtype_auto =3D 0; + ret =3D nxpwifi_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); + + if (req->crypto.n_ciphers_pairwise) + priv->sec_info.encryption_mode =3D + req->crypto.ciphers_pairwise[0]; + + if (req->crypto.cipher_group) + priv->sec_info.encryption_mode =3D req->crypto.cipher_group; + + if (req->ie) + ret =3D 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 =3D nxpwifi_bss_start(priv, req->bss, &req_ssid); + + if (ret) { + priv->auth_flag =3D 0; + priv->auth_alg =3D WLAN_AUTH_NONE; + eth_zero_addr(priv->cfg_bssid); + } + + if (ret >=3D 0) { + if (priv->assoc_rsp_size) { + priv->req_bss =3D req->bss; + adapter->assoc_resp_received =3D true; + nxpwifi_queue_wiphy_work(adapter, + &adapter->host_mlme_work); + } + ret =3D 0; + } + + 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 =3D nxpwifi_netdev_get_priv(dev); + int ret; + + if (!nxpwifi_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); + + ret =3D nxpwifi_deauthenticate(priv, NULL); + if (!ret) { + eth_zero_addr(priv->cfg_bssid); + priv->hs2_enabled =3D 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 =3D { + .add_virtual_intf =3D nxpwifi_add_virtual_intf, + .del_virtual_intf =3D nxpwifi_del_virtual_intf, + .change_virtual_intf =3D nxpwifi_cfg80211_change_virtual_intf, + .scan =3D nxpwifi_cfg80211_scan, + .auth =3D nxpwifi_cfg80211_authenticate, + .assoc =3D nxpwifi_cfg80211_associate, + .deauth =3D nxpwifi_cfg80211_deauthenticate, + .disassoc =3D nxpwifi_cfg80211_disassociate, + .probe_client =3D nxpwifi_cfg80211_probe_client, + .get_station =3D nxpwifi_cfg80211_get_station, + .dump_station =3D nxpwifi_cfg80211_dump_station, + .dump_survey =3D nxpwifi_cfg80211_dump_survey, + .set_wiphy_params =3D nxpwifi_cfg80211_set_wiphy_params, + .add_key =3D nxpwifi_cfg80211_add_key, + .del_key =3D nxpwifi_cfg80211_del_key, + .set_default_mgmt_key =3D nxpwifi_cfg80211_set_default_mgmt_key, + .mgmt_tx =3D nxpwifi_cfg80211_mgmt_tx, + .update_mgmt_frame_registrations =3D + nxpwifi_cfg80211_update_mgmt_frame_registrations, + .remain_on_channel =3D nxpwifi_cfg80211_remain_on_channel, + .cancel_remain_on_channel =3D nxpwifi_cfg80211_cancel_remain_on_channel, + .set_default_key =3D nxpwifi_cfg80211_set_default_key, + .set_power_mgmt =3D nxpwifi_cfg80211_set_power_mgmt, + .set_tx_power =3D nxpwifi_cfg80211_set_tx_power, + .get_tx_power =3D nxpwifi_cfg80211_get_tx_power, + .set_bitrate_mask =3D nxpwifi_cfg80211_set_bitrate_mask, + .start_ap =3D nxpwifi_cfg80211_start_ap, + .stop_ap =3D nxpwifi_cfg80211_stop_ap, + .change_beacon =3D nxpwifi_cfg80211_change_beacon, + .set_cqm_rssi_config =3D nxpwifi_cfg80211_set_cqm_rssi_config, + .set_antenna =3D nxpwifi_cfg80211_set_antenna, + .get_antenna =3D nxpwifi_cfg80211_get_antenna, + .del_station =3D nxpwifi_cfg80211_del_station, + .sched_scan_start =3D nxpwifi_cfg80211_sched_scan_start, + .sched_scan_stop =3D nxpwifi_cfg80211_sched_scan_stop, + .change_station =3D nxpwifi_cfg80211_change_station, +#ifdef CONFIG_PM + .suspend =3D nxpwifi_cfg80211_suspend, + .resume =3D nxpwifi_cfg80211_resume, + .set_wakeup =3D nxpwifi_cfg80211_set_wakeup, +#endif + .set_coalesce =3D nxpwifi_cfg80211_set_coalesce, + .add_station =3D nxpwifi_cfg80211_add_station, + CFG80211_TESTMODE_CMD(nxpwifi_tm_cmd) + .get_channel =3D nxpwifi_cfg80211_get_channel, + .start_radar_detection =3D nxpwifi_cfg80211_start_radar_detection, + .channel_switch =3D nxpwifi_cfg80211_channel_switch, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support nxpwifi_wowlan_support =3D { + .flags =3D WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_NET_DETECT | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + .n_patterns =3D NXPWIFI_MEF_MAX_FILTERS, + .pattern_min_len =3D 1, + .pattern_max_len =3D NXPWIFI_MAX_PATTERN_LEN, + .max_pkt_offset =3D NXPWIFI_MAX_OFFSET_LEN, + .max_nd_match_sets =3D NXPWIFI_MAX_ND_MATCH_SETS, +}; + +static const struct wiphy_wowlan_support nxpwifi_wowlan_support_no_gtk =3D= { + .flags =3D WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_NET_DETECT, + .n_patterns =3D NXPWIFI_MEF_MAX_FILTERS, + .pattern_min_len =3D 1, + .pattern_max_len =3D NXPWIFI_MAX_PATTERN_LEN, + .max_pkt_offset =3D NXPWIFI_MAX_OFFSET_LEN, + .max_nd_match_sets =3D NXPWIFI_MAX_ND_MATCH_SETS, +}; +#endif + +static const struct wiphy_coalesce_support nxpwifi_coalesce_support =3D { + .n_rules =3D NXPWIFI_COALESCE_MAX_RULES, + .max_delay =3D NXPWIFI_MAX_COALESCING_DELAY, + .n_patterns =3D NXPWIFI_COALESCE_MAX_FILTERS, + .pattern_min_len =3D 1, + .pattern_max_len =3D NXPWIFI_MAX_PATTERN_LEN, + .max_pkt_offset =3D NXPWIFI_MAX_OFFSET_LEN, +}; + +int nxpwifi_init_channel_scan_gap(struct nxpwifi_adapter *adapter) +{ + u32 n_channels_bg, n_channels_a =3D 0; + + n_channels_bg =3D nxpwifi_band_2ghz.n_channels; + + if (adapter->fw_bands & BAND_A) + n_channels_a =3D 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 =3D 2 * (n_channels_bg + n_channels_a); + adapter->chan_stats =3D vmalloc(array_size(sizeof(*adapter->chan_stats), + adapter->num_in_chan_stats)); + + if (!adapter->chan_stats) + return -ENOMEM; + + return 0; +} + +/* + * Register the device with cfg80211. + * + * Create and initialize the wiphy, fill in defaults and handlers, + * then register it with the cfg80211 subsystem. + */ +int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter) +{ + int ret; + void *wdev_priv; + struct wiphy *wiphy; + struct nxpwifi_private *priv =3D 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 =3D 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 =3D NXPWIFI_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len =3D NXPWIFI_MAX_VSIE_LEN; + + wiphy->mgmt_stypes =3D nxpwifi_mgmt_stypes; + wiphy->max_remain_on_channel_duration =3D 5000; + wiphy->interface_modes =3D BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MONITOR); + + wiphy->max_num_akm_suites =3D CFG80211_MAX_NUM_AKM_SUITES; + + wiphy->bands[NL80211_BAND_2GHZ] =3D + devm_kmemdup(adapter->dev, &nxpwifi_band_2ghz, + sizeof(nxpwifi_band_2ghz), GFP_KERNEL); + if (!wiphy->bands[NL80211_BAND_2GHZ]) { + ret =3D -ENOMEM; + goto err; + } + + if (adapter->fw_bands & BAND_A) { + wiphy->bands[NL80211_BAND_5GHZ] =3D + devm_kmemdup(adapter->dev, &nxpwifi_band_5ghz, + sizeof(nxpwifi_band_5ghz), GFP_KERNEL); + if (!wiphy->bands[NL80211_BAND_5GHZ]) { + ret =3D -ENOMEM; + goto err; + } + } else { + wiphy->bands[NL80211_BAND_5GHZ] =3D NULL; + } + + ht_cap =3D &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap; + nxpwifi_setup_ht_caps(priv, ht_cap); + + if (adapter->is_hw_11ac_capable) { + vht_cap =3D &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 =3D &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap; + nxpwifi_setup_ht_caps(priv, ht_cap); + + if (adapter->is_hw_11ac_capable) { + vht_cap =3D &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 =3D &nxpwifi_iface_comb_ap_sta_vht; + else + wiphy->iface_combinations =3D &nxpwifi_iface_comb_ap_sta; + wiphy->n_iface_combinations =3D 1; + + wiphy->max_ap_assoc_sta =3D adapter->max_sta_conn; + + /* Initialize cipher suits */ + wiphy->cipher_suites =3D nxpwifi_cipher_suites; + wiphy->n_cipher_suites =3D ARRAY_SIZE(nxpwifi_cipher_suites); + + if (adapter->regd) { + wiphy->regulatory_flags |=3D 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 =3D CFG80211_SIGNAL_TYPE_MBM; + wiphy->flags |=3D 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 =3D NXPWIFI_MAX_CSA_COUNTERS; + +#ifdef CONFIG_PM + if (ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info)) + wiphy->wowlan =3D &nxpwifi_wowlan_support; + else + wiphy->wowlan =3D &nxpwifi_wowlan_support_no_gtk; +#endif + + wiphy->coalesce =3D &nxpwifi_coalesce_support; + + wiphy->probe_resp_offload =3D NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; + + wiphy->max_sched_scan_reqs =3D 1; + wiphy->max_sched_scan_ssids =3D NXPWIFI_MAX_SSID_LIST_LENGTH; + wiphy->max_sched_scan_ie_len =3D NXPWIFI_MAX_VSIE_LEN; + wiphy->max_match_sets =3D NXPWIFI_MAX_SSID_LIST_LENGTH; + + wiphy->available_antennas_tx =3D BIT(adapter->number_of_antenna) - 1; + wiphy->available_antennas_rx =3D BIT(adapter->number_of_antenna) - 1; + + wiphy->features |=3D 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 |=3D NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + + if (adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) + wiphy->features |=3D NL80211_FEATURE_SK_TX_STATUS; + + /* Reserve space for nxpwifi specific private data for BSS */ + wiphy->bss_priv_size =3D sizeof(struct nxpwifi_bss_priv); + + wiphy->reg_notifier =3D nxpwifi_reg_notifier; + + /* Set struct nxpwifi_adapter pointer in wiphy_priv */ + wdev_priv =3D wiphy_priv(wiphy); + *(unsigned long *)wdev_priv =3D (unsigned long)adapter; + + set_wiphy_dev(wiphy, priv->adapter->dev); + + ret =3D 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 =3D=3D 0x00) { + nxpwifi_dbg(adapter, WARN, + "Ignore world regulatory domain\n"); + } else { + wiphy->regulatory_flags |=3D + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + country_code =3D + 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 =3D thr; + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, RTS_THRESH_I, &thr, true); + wiphy->rts_threshold =3D thr; + nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB, + HOST_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); + wiphy->retry_short =3D (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 =3D (u8)retry; + + adapter->wiphy =3D wiphy; + return ret; + +err: + wiphy_free(wiphy); + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.h b/drivers/net/wire= less/nxp/nxpwifi/cfg80211.h new file mode 100644 index 000000000000..3af0d94feae8 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: cfg80211 support + * + * 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(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_update *params); + +#endif --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010069.outbound.protection.outlook.com [52.101.69.69]) (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 009C042316D; Wed, 4 Feb 2026 18:05:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.69 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228341; cv=fail; b=InOL80JkzB+jw6Ul5LxP888DZzvspTA6sSmRoJUlk6GPaju2KHNxiXyjTiWKCSsCleP6veLjoe0tWYnKTio+5qqIXxj0o8fPF7DMSZ+rkdS9HXHV3HVFR/w8oDhj/1fLY76cXsO3aufdTltRNHIvxEs44pBiNZBTP7cuHMgV1SM= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228341; c=relaxed/simple; bh=IDe1FD2rRhOkNfZ56kunVZXYzVmndl3wmE+2YZgr8OQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=Q4yiUIHdRgv6E4rRA+CfrsXv0PxpfCXpcDzaPz4kN+8VK09YGY21bbawCS26eAJigJh9peDjPaFEQu3dHqbu1xK+AXoeQaafwqEU1l47J53+m9iStsn7InIM5mVSZe2L7Z/qhS/TjJQEzAEkAiYwj9l7pTQbtVebdfPbVevZCWE= 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=LjKyu7Qd; arc=fail smtp.client-ip=52.101.69.69 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="LjKyu7Qd" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=KMwsaBArf8QpnIBg/BCp5+vRWwFzRprN02CQRkWdo9DV1NuTtbdJMTnY1vv5TKSKTxId0jaDn0bV7P2E2nrriHqn0SqlM59u0jz76mfXGQMP5afuRgniKIZnEXn5/9lzOpixTCP4pmTKmRH6UAZgNPFF8Vrn/29oAtMAyJA6Pu/9OPMGtrhvs4MDhb8TgqGcoqwyJDInllH3t46YVj2MxXPHz4ir4xi/qhkx9PS7K6ELFnFl8YkuhpxjUqjvey4qVHWH9BvQnIM36w9w549Z8PNkxWrsgsk4Xbro8iqCnuo3Ou7lILOV5EIwkNXFdCQZf9uKUz9sDJ4T34H/WF36QA== 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=DlFuS0Kh9vhJMji5rTfHsFdJmW87hIciwP7HjuXBUYk=; b=gxcO72kl+Z7SfIYFBLxgxcy7lGAa7LBuq0zhqQpJdDZUP6dhkLXo1RkECzH4pffXtdlLT0/NxpeR1ctOysvejOlnOXvcheFTxPAWAfQPvkJu8vwdM3+SNAeFPvRn1PC+7TlWrQOsdKQIiAUk9ROb7mKDqzB3Zb/T7MssbN1atFpi/zf+SyZSDOBT/9zzhBjCZHNc8TeY+EKTmr2SYKKlkdCSZdkuAr7gWbILK2W1kjQh7NXs8+3gStGwrdVfNMPqZN3hn50BFF7xfgqP7xUqg0/JArXPiNqUIQOvjGb9yk1tA37XfcAsAv00w6Wd2DHnvjyeTfRH79cqq795wcn0lg== 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=DlFuS0Kh9vhJMji5rTfHsFdJmW87hIciwP7HjuXBUYk=; b=LjKyu7Qd3m2VCmA8NA/cCmsnBs8Y95DUWQrFAlVbHUKah2670741PWkymrXVballBg+8yRJtRWYCb2E1Hw9X5JKsT6pghcGQvKm4xAu59MRkauxq61XyVAxOIrVBaOzpOCVXkYWZxShpTb6kV0eOL1AoSlEEzAPrznUMaVmW9Q9LzvFoJpFT+6Ml9wT4ZV0sV+LzWjcJWLq/6Of6hIsdbL9Cvj+OFuDBGoE2cx4vdCJjtx6wAfcLoTbweHPobmOWCHYXux82ClvtuKuZFXGXhxnTIbNmy3h2I0YDpJWEJo+9kCkZRxfwsjYF0KhcsUsOB8flQjesfF9kFW6EEYO35g== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:38 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:38 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 11/21] wifi: nxpwifi: add firmware command and TLV definitions Date: Thu, 5 Feb 2026 02:03:48 +0800 Message-Id: <20260204180358.632281-12-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 92b7f689-bf0b-4429-f27d-08de64180061 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?vI2mRRrR+E6DjMhYxtgUmeaL57A1q86veFvG3k/D3kIOSMNXn+R6oZ3oxDTo?= =?us-ascii?Q?4zXOXKJRJ2mRoKfXCKC7Q92Zf1ex6pLcxeMgEEuKOdEYQFGHTO/4XNRF/tHu?= =?us-ascii?Q?wxFpki+J95qSAHFGZ2E8QH5YsygdaJCYczO0HYvnMhHQMQrTqu3L9aRSacwz?= =?us-ascii?Q?oS2PyY1qN/FKeHujE3LA3M30YacJMEBGqeeYfLTmqAJTrQLDkaa7yvhxF+L4?= =?us-ascii?Q?+TFhfjD4HWRyUam/kK7S4y55nw4NBIUuKjHOO/ahn7ct6D1Ik9rVX79Gw1Md?= =?us-ascii?Q?OmpEXELDIJFxZ3q1Od2Z94OJFvPKCFga98Lon9rwlLbcBNhJ1QXEUv+6wO+h?= =?us-ascii?Q?czdFJ+rhqR3DeuCZvB9kOTRyNdrcecbC94aD/E5uIxT0SOITEpsKK1nSoj6m?= =?us-ascii?Q?Fdd8WHfJUmX/6y+kPxxkX2qeHmNyeaFPzFZ24POZ5sjxBAd4CU2F2J2ucDsh?= =?us-ascii?Q?8C4iaS1w6+qQ4evxM8MR2szQ0OfLc8QB1fbAjK8aVu7c6GcO4f/kZWujIe9a?= =?us-ascii?Q?oU3Xf/Q/3b6UsqaenL22bMfY6vVP3NAMHoIHBhmDisMIMfcnioGq/Lf1zz7S?= =?us-ascii?Q?b7Zsat3J8PHiH5BCRlqxOAfh72YyDw2HauU0pMt9R/rvBogJyzgUcZ3RuVka?= =?us-ascii?Q?eDGOyzGawjLXDRktcjB4KlBfKHq2Qzoen0fSCKXaiaFOgbC9231P2iIQJXMl?= =?us-ascii?Q?cfvz2nNcQIHw8BGD7wQeu5bLfNMSJOX4agzuTKw9Y58hQDGelRLuaPm05oBY?= =?us-ascii?Q?v0o8rWEbkK7TUw3L5+P4qDDQybxZz5WuLrRtcpJp8rBVeu5esoRCwtzYzcap?= =?us-ascii?Q?/C4kzRu+2rYtpu05xymZhjB6MM7xVtPa1HDpGSw8qyS0dMV+iWFnxkVHN7ma?= =?us-ascii?Q?7+DN+kO0sqKShyJJEZc8naYVJ6dGqxRIQnC6UaukCKs/04yE/gJbhpYgdtqc?= =?us-ascii?Q?WtuI5gW7XEtuekG50B/N2ehUidwBbvCqxd/++wU5RCHo1yUeBz7MxqAZ5jnR?= =?us-ascii?Q?PCgersdQ+8euqaT2d6ZQ8RRulSmxTxkJZ0YaQ4Tjy8CYOV22+D7AMsItTsqd?= =?us-ascii?Q?rSKR/Sul3lU95l/Cmsx00PADHvMwy1RFXKJ5YV8jGkYzsNNKZFLo74pNKpLf?= =?us-ascii?Q?j650rs1UclSqb3Fr+V404mSuu3fApXbsgkFuMILYZw6oSXN0OcctM9wTy5T4?= =?us-ascii?Q?LfmPDOcVlYSW0S6gawcyYN2F7aJ3TjooKcO24U0O9Nd6nFw7NkVOcTurc0H+?= =?us-ascii?Q?hw3zPhRIG/cfpPMRZVOq8dKv8nW8Mx62S5pma5NhGFsJeSaNbQENKGz+LFap?= =?us-ascii?Q?JwSEGPDSYub98dP3uYPYdFptcLfzk0bP4CBwc2QHINwgnDhZUDfcI6628Dgf?= =?us-ascii?Q?FGiSqy1o9mq9uSspWkRaS4gq56rEK/rRwKrPNuwTirrnd/nflSbgPfaoXbyv?= =?us-ascii?Q?S2EvAsa2Fe1lZ4d3mCBcBlZrZ1zegGW/1MlI8p+Nfse4ks7wIsMpKBqEmXEQ?= =?us-ascii?Q?uqaFApqG17LPoAt/SH6ahIyi98vy+R4nv9gwiZF1rb8S9s6ymToVAtnKwLT8?= =?us-ascii?Q?TB8h35mDNTDl79WcSTE2zfwBy/gNjDzNewYYHrLgsYiidU41IOt8UouAb4CY?= =?us-ascii?Q?pV4gDoeUAOL3HIGw/dsIu5s=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?+WpgmAZVLYZV+YCx2Yfhv8qfme+XXsod2+qsk7yuMFo37SHj/gcfAXiMNE3e?= =?us-ascii?Q?q+LlUl9xAuVxYnF7AJ6saPHNUjgfutPAtiD1bigmApN3oreT/xI++wpImANg?= =?us-ascii?Q?XlRGt7XLBzM9q1sqaM+AMbHJlk0hmnY8vgoCz6ks7qzZ96JNwewjSrKriacE?= =?us-ascii?Q?HlqgI3JBWEyLmLyKGc3+nP9Mqg2IXXc7/uFiEAHinJyd1s2Q0DGwaERkCbqb?= =?us-ascii?Q?riDHnaCqCHaHIts/725VqlQ1LRel5NAjk8BC64HlhCaz8Z5SyK4FRU617YPA?= =?us-ascii?Q?ioe+6EzVSMNIiDRvl9KNS25OrMHRi5kmLLo9rDuvnV1qDR51SVZ2BsKXYrFX?= =?us-ascii?Q?d3+3OmGdqzXuRaapv6/bqWNZakg8iLOFGR6FSKdpJyob8NISxaoRj+S0UwTk?= =?us-ascii?Q?jT/kWGekamx08rBKFrbqRaazloQINgh/XV77cF05PpgUnWd29Ix+LtpMlJvP?= =?us-ascii?Q?koi4bq+mCGPEKC6B9896fTxm6OJV/CbP/teopgKL+nIf6w4tarDUq73SmUhi?= =?us-ascii?Q?MFYhUo7dRmCe07egBO4Vq+Ay/66ZzDatA2Ya9y+y7DXU1HmHVjDkXsBnVtKD?= =?us-ascii?Q?GyvrHLbjEa2MiB5sW67imdAzQ0f01aMPPME12+Qsxh26BCpGYACHbzhLElcl?= =?us-ascii?Q?tK2HNxDb+3jvUPOynUZWeE4FdE7z4SZLE1iIZ3+prIIW3k1HdRxwqSmFUVgr?= =?us-ascii?Q?75TWohlnSzYIjfNJzBUYQRmHFZuOMuy090IZ9aqPtzFs55R4ixFjmQ1kbeF+?= =?us-ascii?Q?BiDxLPJWr+UcPpbVpwAmKg1IX9U6osdrmq5j9mFj9v2lfOVSZEntDpEKq7fK?= =?us-ascii?Q?n7NPtfeHkN1swYQEgQL8aEJeJYCf2hx+cqKCC7klvX6T8gAWwzRYNGARJukr?= =?us-ascii?Q?9yDNSQZuQdjXUR3azBq11sihWIMm3RILtb1AvHAoou+UdMqXvEMe1eMsIBti?= =?us-ascii?Q?MQ+9neZVZO3pHpfql8rDFxQAum58P/xr90C/yEWkhl0P/pZx5tnbg4YNq02b?= =?us-ascii?Q?H3PRanicff6zPNPMTvqDkMCAHnXljtJyLZ4f89NFBjJF4ZOsrn7L3YfKsdBa?= =?us-ascii?Q?+vPKHEeAfEPsAr7EfWmCs1OSAod2qh2BUS8S3jsOp35luEEU1GI6axllhgLK?= =?us-ascii?Q?W/JfY05OgWWcCFQwGoXRdBc/+lwgJCNLqwI2Zi0dpoE5Ul6blvTDfORgLk6Q?= =?us-ascii?Q?kIu6cEysS9cmqAJ/C7xhOa3CiWyzNeI3YqfFsprgWa5Pw84qG4iJ2sJAEvNK?= =?us-ascii?Q?fUql9aXfN6GUJm+GmpmHH6LnAAb61wkejkiyA607BwpgXCjM4e09NqeZTEYq?= =?us-ascii?Q?sKe9adahjTU2LUqQc+GrslhoNHsx1mcr61N5ArIXrWaMeScSD16gw61vR4wN?= =?us-ascii?Q?SasCzZiDDgvCx0265EzUL12fhlQIZvzQmaW6bsEnLorj5fj0hBlF2vRNyjdG?= =?us-ascii?Q?oFGMZMw7S+5Jw1N+GqQ1mnUU1Wojj4us2HygZOuZaJZnRQ7HtkyerqUbrA+p?= =?us-ascii?Q?QG/VMmLT7nEUc3v2DdlPSCzs7aeIRL3K8PP0HoGZTTbpFzhK4OHTSpi3f1Ph?= =?us-ascii?Q?21shQMcb3GnoyWsmnNxjTTB6nwEgN19Leflv83HdI2NBf4KQY+twEEEdaKGb?= =?us-ascii?Q?1SitlaN4J1JiMEsE59QNqaKsRIoGJsTdS1rv2Jqef9Pw4V7xxPIqwQhv4MbS?= =?us-ascii?Q?BOk+N1Inm9IQB9q9h3S3htHfk1KqAcarh3DmZTo7k3JzfS79Fhe/GBfCMWKN?= =?us-ascii?Q?xaREbljvJw=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 92b7f689-bf0b-4429-f27d-08de64180061 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:38.4736 (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: 6u4hVCMU1Op6bObW4+rIKhnsvju/MEvOedaAE6UZmamRDrvsTrNKWiV/S4o+Xm7W4YrPDea7k3H2dX+cRIO0Mg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" Introduce a new header file (fw.h) that defines host command structures, NXP-specific TLVs (Type-Length-Value), and related constants used for communication between the driver and NXP firmware. This includes: - Host command IDs and result codes - TLV type definitions for various configuration and control parameters - Data structures for command payloads and firmware events - Macros for firmware capabilities and feature support These definitions are essential for implementing and extending firmware interactions in the nxpwifi driver. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/fw.h | 2373 +++++++++++++++++++++++++ 1 file changed, 2373 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/n= xp/nxpwifi/fw.h new file mode 100644 index 000000000000..0e5eeb115847 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/fw.h @@ -0,0 +1,2373 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: Firmware-specific macros and 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) + +/* + * Map fw_cap_info for default bands: shift 11ac flags so bits + * 11:GN, 12:AN, 13:GAC, 14:AAC match driver layout after >>8. + */ +#define GET_FW_DEFAULT_BANDS(adapter) ({\ + typeof(adapter) (_adapter) =3D 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 =3D 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES, + KEY_TYPE_ID_WAPI, + KEY_TYPE_ID_AES_CMAC, + KEY_TYPE_ID_GCMP, + KEY_TYPE_ID_GCMP_256, + KEY_TYPE_ID_CCMP_256, + KEY_TYPE_ID_BIP_GMAC_128, + KEY_TYPE_ID_BIP_GMAC_256, +}; + +#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 =3D 1, + NXPWIFI_USB_EP_DATA =3D 2, + NXPWIFI_USB_EP_DATA_CH2 =3D 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) =3D 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 + +#define RXPD_FLAG_EXTRA_HEADER BIT(1) +/* channel number at bit 5-13 */ +#define RXPD_CHAN_MASK 0x3FE0 +/* DCM at bit 16 */ +#define RXPD_DCM_MASK 0x10000 + +/* + * dot11n dev_cap bits: 17:20/40MHz, 23:SGI20, 24:SGI40, 25:TXSTBC, + * 26:RXSTBC, 29:Greenfield. + */ +#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) & B= IT(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) + +/* AMPDU factor size */ +#define AMPDU_FACTOR_64K 0x03 +/* hw_dev_cap : MPDU DENSITY */ +#define GET_MPDU_DENSITY(hw_dev_cap) ((hw_dev_cap) & 0x7) + +/* httxcfg bits: 1:20/40, 4:GF, 5:SGI20, 6:SGI40. */ +#define NXPWIFI_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) + +/* 11ac MCS map (1x1): stream0 supports 0-9, others not supported. */ +#define NXPWIFI_11AC_MCS_MAP_1X1 0xfffefffe + +/* 11ac MCS map (2x2): stream0/1 support 0-9, others not supported. */ +#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] |=3D 1) +#define HT_STREAM_1X1 0x11 +#define HT_STREAM_2X2 0x22 + +#define SET_SECONDARYCHAN(radio_type, sec_chan) \ + ((radio_type) |=3D ((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 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) |=3D ((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/MU beamformer/beamformee and sounding dimension 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_NET_MONITOR 0x0102 +#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 HOST_CMD_TWT_CFG 0x0270 + +#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 =3D 1, + DIS_PS =3D 2, + EN_AUTO_DS =3D 3, + DIS_AUTO_DS =3D 4, + SLEEP_CONFIRM =3D 5, + GET_PS =3D 0, + EN_AUTO_PS =3D 0xff, + DIS_AUTO_PS =3D 0xfe, +}; + +enum nxpwifi_channel_flags { + NXPWIFI_CHANNEL_PASSIVE =3D BIT(0), + NXPWIFI_CHANNEL_DFS =3D BIT(1), + NXPWIFI_CHANNEL_NOHT40 =3D BIT(2), + NXPWIFI_CHANNEL_NOHT80 =3D BIT(3), + NXPWIFI_CHANNEL_DISABLED =3D 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 =3D 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=3D0, HT=3D1 + * [Bit 1] HT Bandwidth: BW20 =3D 0, BW40 =3D 1 + * [Bit 2] HT Guard Interval: LGI =3D 0, SGI =3D 1 + * + * For: 802.11 AC cards + * [Bit 1] [Bit 0] RxRate format: legacy rate =3D 00 HT =3D 01 VHT =3D 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 =3D 00 BW40 =3D 01 + * BW80 =3D 10 BW160 =3D 11 + * [Bit 4] HT/VHT Guard interval LGI =3D 0 SGI =3D 1 + * [Bit 5] STBC support Enabled =3D 1 + * [Bit 6] LDPC support Enabled =3D 1 + * [Bit 7] Reserved + */ + u8 ht_info; + u8 reserved[3]; + u8 flags; + u8 antenna; + /* toa_tod_tstamps: [31:0] ToA, [63:32] ToD (ns). */ + __le64 toa_tod_tstamps; + /* rx info */ + __le32 rx_info; + /* Reserved */ + u8 reserved3[8]; + u8 ta_mac[6]; + u8 reserved4[2]; +} __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 =3D BIT(0), + NXPWIFI_DISABLE_CHAN_FILT =3D BIT(1), + NXPWIFI_HIDDEN_SSID_REPORT =3D BIT(4), +}; + +struct nxpwifi_chan_scan_param_set { + u8 band_cfg; + 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_gmac_aes_param { + u8 ipn[IGTK_PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_BIP_GMAC_256]; +} __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; + struct nxpwifi_gmac_aes_param gmac_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 =3D 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 =3D 1, + FW_API_VER_ID =3D 2, + UAP_FW_API_VER_ID =3D 3, + CHANRPT_API_VER_ID =3D 4, + FW_HOTFIX_VER_ID =3D 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 interfaces */ + __le16 mgmt_buf_count; /* mgmt element 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 =3D 0, + NXPWIFI_RATE_FORMAT_HT, + NXPWIFI_RATE_FORMAT_VHT, + NXPWIFI_RATE_FORMAT_HE, + NXPWIFI_RATE_FORMAT_AUTO =3D 0xFF, +}; + +struct host_cmd_ds_tx_rate_query { + u8 tx_rate; + /* + * Tx Rate Info: For 802.11 AC cards + * + * [Bit 0-1] tx rate format: LG =3D 0, HT =3D 1, VHT =3D 2 + * [Bit 2-3] HT/VHT Bandwidth: BW20 =3D 0, BW40 =3D 1, BW80 =3D 2, BW160 = =3D 3 + * [Bit 4] HT/VHT Guard Interval: LGI =3D 0, SGI =3D 1 + * + * For non-802.11 AC cards + * Ht Info [Bit 0] RxRate format: LG=3D0, HT=3D1 + * [Bit 1] HT Bandwidth: BW20 =3D 0, BW40 =3D 1 + * [Bit 2] HT Guard Interval: LGI =3D 0, SGI =3D 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 =3D 0x0001, + HS_ACTIVATE =3D 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 =3D 1, + DTIM_PERIOD_I =3D 3, + RTS_THRESH_I =3D 5, + SHORT_RETRY_LIM_I =3D 6, + LONG_RETRY_LIM_I =3D 7, + FRAG_THRESH_I =3D 8, + DOT11D_I =3D 9, + DOT11H_I =3D 10, +}; + +enum nxpwifi_assocmd_failurepoint { + NXPWIFI_ASSOC_CMD_SUCCESS =3D 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 interfaces */ + __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 element - 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 element */ +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=3Dfcc, 2=3Detsi, 3=3Dmic*/ + u8 det_type; /*0=3Dnone, 1=3Dpw(chirp), 2=3Dpri(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 nxpwifi_802_11_net_monitor { + u32 enable_net_mon; + u32 filter_flag; + u32 band; + u32 channel; + u32 chan_bandwidth; +}; + +struct band_config { + /* Band: 00=3D2.4, 01=3D5, 10=3D6 GHz */ + u8 chan_band : 2; + /* Width: 00=3D20, 10=3D40, 11=3D80 MHz */ + u8 chan_width : 2; + /* Sec offset: 00=3DNone, 01=3DAbove, 11=3DBelow */ + u8 chan_2O_ffset : 2; + /* Chan sel: 00=3Dmanual, 01=3DACS, 02=3DAdoption */ + u8 scan_mode : 2; +} __packed; + +struct chan_band_param { + struct band_config band_cfg; + u8 chan_number; +} __packed; + +struct nxpwifi_ie_types_chan_band_list { + struct nxpwifi_ie_types_header header; + struct chan_band_param chan_band_param[]; +} __packed; + +struct host_cmd_ds_802_11_net_monitor { + __le16 action; + __le16 enable_net_mon; + __le16 filter_flag; + struct nxpwifi_ie_types_chan_band_list monitor_chan; +} __packed; + +struct host_cmd_twt_cfg { + __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; + struct host_cmd_ds_802_11_net_monitor net_mon; + struct host_cmd_twt_cfg twt_cfg; + } 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_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from GVXPR05CU001.outbound.protection.outlook.com (mail-swedencentralazon11013020.outbound.protection.outlook.com [52.101.83.20]) (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 2985F429818; Wed, 4 Feb 2026 18:05:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.83.20 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228346; cv=fail; b=ojlFlETwUU/e9kzmIywbB5wxacf55rY+zSh+E02HGhd2eoKl4lqCztAgAOLC+aC6BeS58wVQ50oVI3pe8MejM5s7J4tXT1S5GZuPqIzJ5gn02nDe3qIXf3Ps0FEMMRYfcj1iRJGTj2qUXCYQtR/UbbD4n9HF44GWZ98WMeycj4Y= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228346; c=relaxed/simple; bh=hxP/0odjyK9/1/e7BETbFqdVU4ZAX4mc3v9TOTW+VtU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=cd6G710QsslhdSj7VqFAHGvvkBirjDPmCpVO9tYQU9tOJOdtrKI+MttdEPaXJfMJrvGTpcWJoVWZiZo8oYxwuciPn2lV4N9/FAqlYj3CL/bbGlGTFSm2GDmDJznu1RfMiyaLZiIHYSvP6OACviGxf1wokxnyW1HUDHgBnsY5+oA= 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=gAfc1LP1; arc=fail smtp.client-ip=52.101.83.20 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="gAfc1LP1" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=N32i2Izywka2gYxJN9xhNjtn4Ujo1Le2bpk0a2L6TY4rlpELvvFDIfSIaijM0htCj30XN8nJdB5bzV0FwiZTHvwp3PU+9HB0aASK4SQ1pj/5O1auuQIKR8Dl5QC9q76oon7ZxTQNgkgTHcYcS6yJ3YAltXKb7VuAZVkbiF5bu98afDNSmHDfCK94pZordLzkBELxueHfSYS9n6mcYjEhkmOmTrbeEGrUFciEJujf3t4m+yp+QtHblGwCMOpmCgN3BiXl9y4swceT4uV+fl39rWhWLC/lA3lPRCWWIP+k6tPgn6p0vOiDhuopMwgy+Qv+vv3jClbnydsMHs6U93RTsQ== 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=p5a7COfQehXzhGuAbttuFUuIwmU/cGXRpWGLdaT3g3c=; b=HHnYlbcEUN3zG9TYY8JAERILaHk/nUTUMtzRFgLY4ApVb8yxSJ9cW8yrEnhP3dD1eFf7U/HhTDYmcpTBgASDK1I6a29GaqC72CpTbKnryhY1CgEpYUj+gri9fzaYFH47Mv3aBYC1aK9sG11mXlZHiqsYImJFdjs5Gp6Wjzsxwt6fTFv+e23E3dsbb5Z3qzCGqcP9Tl4kuIok1NQShiK/zjTwHGCX+U05W/pwBNl04GEL9JZxuYzmr6jgjbpjjuu6EUBQXxamlatmU2DHwHOdWEK5F9kJ57pq70o6H10Iua9mFrjx6v9kkR8rSxYY+upinQ8OcIGdnrEAyUToQ4QKIg== 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=p5a7COfQehXzhGuAbttuFUuIwmU/cGXRpWGLdaT3g3c=; b=gAfc1LP1Fd2xkElOSWpSKcaDh5coHNGG9BxWY5Uj++cq/NV6YUG0ljMTV3H+Jag0F76OqUDg2VpBCP076J7/bCEbpLI/4p53BIWWOnbzYud4ufKW9ZJIb9xdxG3ZhXGX0zwLGs5H+2Ae1FiNFEJxuppccXgaGmpa/Xy9At+QV1TvomZ0c0zzxA/4R8Q5CHHgfdDwockX876wNe8S7yc8iJ12nsN5EtzuTSfugrS0XvLpvewrZw+AonrNq3gCfLtOzDG1cJo43VS0NUnZPsR/6BKyI0G40Hv1y5ZguZ9Iq96s3Hfoimf9kO7xiIruz01VGCnXGKN+5Bv3d3XV9Kv6mw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:42 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:41 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 12/21] wifi: nxpwifi: introduce command and event handling infrastructure Date: Thu, 5 Feb 2026 02:03:49 +0800 Message-Id: <20260204180358.632281-13-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: ac32581b-b946-4677-612f-08de64180209 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?B?dXlNZ2VjdGcyTHVMM0NyYU05TURJSmFTeWVDenBrUnJ4L0RZNHAxdTJuWjE0?= =?utf-8?B?b0VkY2hKM3NsRzZDKzcxWWt6OXFHOU13Y3MzamsxMFAvZ1BuYy9sWnZUSVJo?= =?utf-8?B?S0RybElKQVNhTS9rZkh1UFMvQ0Vic0NlMjZnbVZRWjNsZTVDcTkyY3hoeWJQ?= =?utf-8?B?TjJUc3ZYa3ZzVTlNVXVZTW5GM25JejRZNHByYW5lL2RnUmhrOFdQYzNTbXg1?= =?utf-8?B?N1V3Vk51YjVKUTdDWFRyZnE5d3ZVYW9zWGpQTyt2S3J0aVJWVGx5Y2F4US9T?= =?utf-8?B?WWVIRHlibDJ5S2dneWhuTUNxN0pqZGErd1NER0FtaEZmQ1c2OEFXTGN2V3Vp?= =?utf-8?B?RTZkMUhlQk1FNit0MEZwdmh4cmJreHF3b01Fcm9WdTlvbVdkS2I1UmlnMjl1?= =?utf-8?B?NkZpbTBiK0xxN09ZYnpDTGREV1NCZlYweGsvRml0Z0cra3hHT2VKNThxNDNO?= =?utf-8?B?WWNvWXE1Ykx5N0ttT3lCaDdmalA0UVkySXdtRTE4V3YvalJPWXViS0kweVhr?= =?utf-8?B?U0ZvR0JiYm52U3gzTzB1K3VSQzdXcmx0eDUvV1VNWWFTTEVWV0s0VlN5eXdI?= =?utf-8?B?bm03eUszb0VjY2pKcFB3VVJ6U2lBMXk0R2V5eEEvZ2hTRkZDM2pNWjdLVndY?= =?utf-8?B?eThDWGxOV3ZLZmludlo0SHd0SG5jWnk3WTg4RWJoOE5UVlVFeWxxbjFGbWFy?= =?utf-8?B?M1doUTkvdkZVVWV5ODMrNzNXZXlaOGtFNitwWUxlVC90Z3ZJY0MyaUVBVkdV?= =?utf-8?B?ZVhSNWdmZzJDUmVyNGZhNm1TeXpGYVVELzBoUEpJT2ROcmlsZFlaQ2VkRTA0?= =?utf-8?B?b1UrOWQ4ZmVXYkJ1UWpVOCtkV1FzRVNmZG14TG4wNVdLSVJsTkFVUS9UOUxP?= =?utf-8?B?UzhNMFQxbDk3RUFqM1Q0MCtxd2grZUxCZDIvWGpsaGJ2MDgxQlNlYVZwUVlt?= =?utf-8?B?T2lmK1IwWjNVRjhuN3hLZGJLRy91VzMrRlJhWEE2VnVRTkQ1WUN2VGMzWGpr?= =?utf-8?B?OEpEN0lldVJLbVNlSlhPdTZQUmNIY1NjbmJRTXA2UmZUdUFwcndQYk04SjlY?= =?utf-8?B?dVlYcnI2cnI4ZVdLWDkzdnhSNk5EeFdrVXFCeEVBSy9oemdod0U3Z0ZMcGty?= =?utf-8?B?NThjOUphb3c1NkFvSnFwSUpJVnY5cFNUYmEyNFk1YUw0bDNXV3J1K3ZKWit2?= =?utf-8?B?Y2ZwZGtucTZ6WmpBbTJnV0MwM2QyVWRxSElKV28zYkRtQXRoQzlabHlueVo2?= =?utf-8?B?V3QxSUs0aUV5N1ArcDJQY1ZoR3NTOGJRWVdSY1pHYTc0SDJLcXV1bExLdXV6?= =?utf-8?B?THFBS3lPejEwVlBrRzFBUmc3K1ZpeUhNeEkrMldsMlJZK3NURkVIRXhpbGYx?= =?utf-8?B?aUZDQVJpSzNWTDdrS0x4M0lmNWF4dllKaWcvUXBCNEFqU0FyZEtTZUV3czhO?= =?utf-8?B?VUw5c3hSWmExa29nSXc2V3czU0NKWHh1U0NXbkRLSk5aNk9seC9ZR1ZpeFBz?= =?utf-8?B?bDA1czZkR3VsR0tCT1RnVnFwblZHUUdYY0c2WDNuOG1rQ29wQ21JQi84Q2Iw?= =?utf-8?B?MWdIT05mczFGdGRlZWtWZEVVblJzLzF4NnNsMjN1TCtFYlp0SXJYajFRQ0h0?= =?utf-8?B?ZjRacHhxcUJhT3U1R2RjUUd0RWF0ekg1ekdicS8za0Z1cEU3U0JaVzRQcmtp?= =?utf-8?B?Zk5kb0piSW9iZDN4M0s3YWxsMG56S0ZUK213VXNzVmU3bEl5UXNGZTVRUDZL?= =?utf-8?B?RGhoSUpKcXcvT1FKR0krLzVMZzF4QTNJQWhnbytvU2V2SkJhMEk5VlNrbW1Z?= =?utf-8?B?ODhMd0J6NWlwY2NXY0Y0NEcyVWg1aFlhaDZRR3VISnREZzRjbFYzdk9yWkpm?= =?utf-8?B?WVRpK01KdnBvV0hXZkU4TUhvekRqd1pCeDhsM0ZVNS9xeWNpSXZwekJtSmxo?= =?utf-8?B?ZjR0REhEZnptK1pWNVBZa3BmbW02WGNnQ25zNEF2NzZNcHJBZ1pPQi9sL3lD?= =?utf-8?B?RS9pcWh2cU1xa0hzblpQc05Ed0Q1cStOMkxFYitIV0FwTnRFR0huQXB2eFdh?= =?utf-8?B?Um5mbnpLNCthVFgvTkNoNjlFUWJkdGtFbXo1WnRRaGhpbXdRdEZCMUsyWnV3?= =?utf-8?B?c1VSSmIvN3lxY2JvVWRrN2hodGc5Y0s1UjlxcTdPTGlrMmd3NkNQcDZwUkZE?= =?utf-8?Q?zZn+p5YWmgXj+HxktLdB9Ks=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?ekQzQlgzaFE1VWQvNkY4MkVnd2NxTk9COHY1aG0ydnZLRFNIR3NaQWprWVhT?= =?utf-8?B?QkEyOEIyR0NqOFZSZnJ0SzFWeFRYRnB2SXFnNnBjU1VoZnFQSE1KZ3V1YVlk?= =?utf-8?B?YzhNRitDNGZPZEJXRkViOHFlZUlUL1NHSFZyVkxRejg5R0kyczZlMGpWZVFK?= =?utf-8?B?cExsQWE3bnE0M1ArWG1rMnhmaXZGTGZudCtoc0hzSFdJRTFSdEptbU1WVmRJ?= =?utf-8?B?Z1BTQTJHbnVEWlNHQjlIVHlIQzRwWGl5N21IU3FLSHNSRzF3cjJLNmtLWTVa?= =?utf-8?B?QlE3UGQ1RU9ySVhJMWttSHZwR0xqRWFTZDQrSjlraUtOZHRidmtVa2J5T29x?= =?utf-8?B?eEdSY2kxcVgxWmkzYTdyZjFTQkU4VktCVjU3eSthS2pxUG1kTmlVUEJMdUdR?= =?utf-8?B?cWk2SUZJZ0IxSUtFUmlUUDFxbCtaTit4TVBmemVuaDQwVGtwTEhqVXY0dHhL?= =?utf-8?B?SWhZUmQyY2czWGZvRjZ5cVdza1NiaTZkbVJaQU1KdGY3eksxMEZEVk1PZ3B3?= =?utf-8?B?dk1vMFVUTVlNVUlKVm9MQkwxRVhDWGgxMjNjVzU4Mm1hTDI5OFNWNHJXamhZ?= =?utf-8?B?WmUySGgyYldlM2h5M3lWSUV1azJHbWFMVVl5SG9Tdk5XMnVZM05QZjdCM1ND?= =?utf-8?B?MVpqMlJjTldBdmRVY3Vxa1B0ckxHTWpmOW0wT2paaWNPRUZ5MnQ1dmFNajNh?= =?utf-8?B?L3lNeDJoWlg5ZTB4OHd3NmNpWWRxL0ZBZlJsYkJMV2dMZ0ZxM3lQelBzTEc2?= =?utf-8?B?UEZHQ2cra3laMGdvTG9ld05BcjQ5a2hPdXFJa3F5Z0FmM1NGekk2VTVxNmRU?= =?utf-8?B?Um1lcUxyNGhPcXgxbkJMa1VTaElhZFg2cXE0aS9QUUZjKyt2TjJPNWdaODRY?= =?utf-8?B?bjhaNXI1OUJtdVNpYjNucSt0a2FRZXFhajhxb1F6eHJqUXlyaEpQRzJldUpa?= =?utf-8?B?S0pTcXFUdWI1WUNMNmZNbDd5Ri96cXNoVTYvdU5oL3Jna1A2YUtyQURzVE5q?= =?utf-8?B?RzZWY2lqOGFxRlRQN0R0em9oK1BiOWdhbVNZakcrMHZFbTJ0Qkk0U25tak5E?= =?utf-8?B?WURJYlJINU5JV1NyUXkyUElRViswdmo4dmZuR0VybFJac2dnaHByS3JDZHV4?= =?utf-8?B?NVhjc1dwTDNndXZaNVdic1JGUnFPWGorZ0xsWVlRVmxneUdRU2lNemZqUW9q?= =?utf-8?B?SFF0eDVhMG1QOUtBbzhaRXpsem95VjBkcWhUNVB4c1cxMzdDb1ZOV2RMYVNX?= =?utf-8?B?LzNTUnNWbzBPMU0zcnlwS1pEOTZ1R0ljZHEvUTRROGFwSjN6WnJCdGR0cStP?= =?utf-8?B?N2pLS0tPOXlWWnpsUFdleEhtd2ZWTDBtTGI1MTBnczJMZ0Q0K1cwLzBVV1dt?= =?utf-8?B?a0FSanFLNHk4ZTZ5eEVUdmlvK1hza2FuZml1VkR6NVNKSkVBR2V4LythazBj?= =?utf-8?B?RmI4OEdWZXBwa3pNc3VaWUVhVzRiaksyQWRRTWFtdE5ES1VPZGd2NTE2NXlC?= =?utf-8?B?NkxuQUxjWU5Gc1RoZ0Fjc3NMVDFpem1MZ0MxUGl0cmtZSmJZYkVYckFBVWZk?= =?utf-8?B?bnh1Um9ldk5hcE5rRkVKbCtyWktLQ2hYYm9nVUE0akRHNTA1MTdmOUpSNm5l?= =?utf-8?B?TVJjeWNJUi84bXdtRlhZZmhFazVCbERjd3MzRDMybUdreEwwMkZTWW8rR3Vm?= =?utf-8?B?cUdqNGRaSHJSQm40N0kyYk5Cdlc4YlBFOUZtMmpyR0hMZis5aFpJSFNRVVFn?= =?utf-8?B?SEZrK2gzQXNkZEpWMG40WXhGU011RUZ6bkkxdjhCOENUcTZTdnIwbjhaeGNj?= =?utf-8?B?c3BSZFhYTUNGbm5QWUJUNVNEY1B6eGFtQ1BMaGtwNzh0cmFlaFUxODA4c3dB?= =?utf-8?B?c25pUXdRRUZZdkFJYlFIUW1MMy9YbnBHYVRIdFZvOUsrT0dkc09mNWVsTzds?= =?utf-8?B?UE1YbTRCOXg3NGJ5L0NMenF0MGFCMnNFZ2JqN1RJVWtPRHlzKzcyWUtadm1k?= =?utf-8?B?U1dZR3JFcUdKZjZKMmxVSUMwbFJ4aVVlQnh0WTRaM3ZscThQNVdCMzJXRGtL?= =?utf-8?B?bmdMbjk3ZjEvN1VjeDcvNUt4ZFY4NHlBZFd3K1RBcWFnZ2R5ZmFpbHNRMGVT?= =?utf-8?B?NitmR1I0VWczNWRxSUdDLzFnazdLQ0IxL29lYVoyM0pGZVdycWpNaGF0RFp4?= =?utf-8?B?MEV5aU9NZ2tnV2NLSVVVNHRXeDh2QWYyMGt5WVZDMFliSHlYOVh6RFdIQ2ZE?= =?utf-8?B?WTlVeUdNUCtEbEp0VXp2NUoyUnd5Nm53R2FEMzRPM2dQT0NQZGNZaWp6L3VY?= =?utf-8?B?bTBrdWQ5dCtqZFhUa3ZzejlJNlF5b1FtVStlYzRMUjZteW56OXQyQT09?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: ac32581b-b946-4677-612f-08de64180209 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:41.5822 (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: zFP+FwQQCy5CpqT4eC1QcwtkUjdOcAtCQL3Yi2HocOW+tX27jVuy83ItujkVATNRvEplAWJ5rLxX50dVHTvOPA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Add implementation of command and event handling for the nxpwifi driver to support communication with firmware. This patch introduces the following components: - Command infrastructure (cmdevt.c/h): * Command node lifecycle management (init, alloc, recycle, cleanup) * Command execution and response handling * Timeout and error handling * Power save and host sleep support * Event dispatching and processing - Function tables (cmdevt.h): * Command and event handler mappings for STA and UAP roles - STA command support (sta_cmd.c): * Commands: HW spec, scan, association, SNMP MIB, register/memory access, RF power/antenna config, key management, TX rate/power config, etc. * Support for 802.11n/ac/ax, WMM, TWT, MEF, GTK rekey offload, and more - STA event handling (sta_event.c): * Handles events such as link loss, deauth, PS transitions, scan reports, RSSI thresholds, radar detection, BT coexistence, etc. - UAP command/event support (uap_cmd.c, uap_event.c): * Commands: sys_config, BSS start/stop, STA deauth, new STA addition * Event handling for UAP-specific events Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/cmdevt.c | 1149 ++++++ drivers/net/wireless/nxp/nxpwifi/cmdevt.h | 98 + drivers/net/wireless/nxp/nxpwifi/sta_cmd.c | 3444 ++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_event.c | 862 +++++ drivers/net/wireless/nxp/nxpwifi/uap_cmd.c | 1198 ++++++ drivers/net/wireless/nxp/nxpwifi/uap_event.c | 488 +++ 6 files changed, 7239 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/wirele= ss/nxp/nxpwifi/cmdevt.c new file mode 100644 index 000000000000..52474664b833 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c @@ -0,0 +1,1149 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: commands and events + * + * 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 void nxpwifi_cancel_pending_ioctl(struct nxpwifi_adapter *adapter); + +/* Initialize command node; set defaults; buffers are supplied by caller. = */ +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 =3D priv; + cmd_node->cmd_no =3D cmd_no; + + if (sync) { + cmd_node->wait_q_enabled =3D true; + cmd_node->cmd_wait_q_woken =3D false; + cmd_node->condition =3D &cmd_node->cmd_wait_q_woken; + } + cmd_node->data_buf =3D data_buf; + cmd_node->cmd_skb =3D cmd_node->skb; + cmd_node->cmd_resp =3D NULL; +} + +/* Get a free command node from cmd_free_q; return NULL if none. */ +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 =3D 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; +} + +/* Reset cmd node state; trim cmd skb; complete and clear resp_skb if pres= ent. */ +static void +nxpwifi_clean_cmd_node(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + cmd_node->cmd_no =3D 0; + cmd_node->cmd_flag =3D 0; + cmd_node->data_buf =3D NULL; + cmd_node->wait_q_enabled =3D 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 =3D NULL; + } +} + +/* Optionally complete waiters, clean the node, and add it back to cmd_fre= e_q. */ +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); +} + +/* Reuse 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 =3D (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=3D%#x, cmd_pending=3D%d\n", + le16_to_cpu(host_cmd->command), + atomic_read(&adapter->cmd_pending)); +} + +/* Copy host command (userspace-provided) into the driver cmd buffer. */ +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 =3D (struct host_cmd_ds_command *)cmd_node->skb->data; + pcmd_ptr =3D (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 =3D %d\n", pcmd_ptr->len); + return 0; +} + +/* Send prepared command to FW: set seq no, adjust skb length, log, start = timer. */ +static int nxpwifi_dnld_cmd_to_fw(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data); + + /* Sanity test */ + if (host_cmd->size =3D=3D 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 =3D -1; + nxpwifi_recycle_cmd_node(adapter, cmd_node); + return -EINVAL; + } + + cmd_code =3D le16_to_cpu(host_cmd->command); + cmd_node->cmd_no =3D cmd_code; + cmd_size =3D le16_to_cpu(host_cmd->size); + + if (adapter->hw_status =3D=3D NXPWIFI_HW_STATUS_RESET && + cmd_code !=3D HOST_CMD_FUNC_SHUTDOWN && + cmd_code !=3D 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 =3D 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 =3D 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 element 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 =3D 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 =3D -1; + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd =3D 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 =3D + (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] =3D cmd_code; + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] =3D + 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 !=3D 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 &=3D HOST_CMD_ID_MASK; + + return 0; +} + +/* Send sleep-confirm command to FW; set seq no; resp may be skipped when = resp_ctrl=3D0. */ +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 =3D + (struct nxpwifi_opt_sleep_confirm *) + adapter->sleep_cfm->data; + + priv =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + adapter->seq_num++; + sleep_cfm_buf->seq_num =3D + 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 =3D 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 =3D PS_STATE_SLEEP; + else + adapter->ps_state =3D 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 =3D true; + nxpwifi_hs_activated_event(nxpwifi_get_priv + (adapter, NXPWIFI_BSS_ROLE_ANY), true); + } + + return ret; +} + +/* Allocate cmd pool and link all nodes to cmd_free_q (used/returned by cm= ds). */ +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 =3D kcalloc(NXPWIFI_NUM_OF_CMD_BUFFER, + sizeof(struct cmd_ctrl_node), GFP_KERNEL); + if (!cmd_array) + return -ENOMEM; + + adapter->cmd_pool =3D cmd_array; + + /* Allocate and initialize command buffers */ + for (i =3D 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) { + cmd_array[i].skb =3D dev_alloc_skb(NXPWIFI_SIZE_OF_CMD_BUFFER); + if (!cmd_array[i].skb) + return -ENOMEM; + } + + for (i =3D 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) + nxpwifi_insert_cmd_to_free_q(adapter, &cmd_array[i]); + + return 0; +} + +/* Free cmd pool; release any remaining resp skbs. */ +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 =3D adapter->cmd_pool; + + /* Release shared memory buffers */ + for (i =3D 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 =3D NULL; + } +} + +/* + * Handle FW event: select per-BSS priv, fill rxinfo, dispatch to STA/UAP = handler, complete. + */ +int nxpwifi_process_event(struct nxpwifi_adapter *adapter) +{ + int ret, i; + struct nxpwifi_private *priv =3D + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + struct sk_buff *skb =3D adapter->event_skb; + u32 eventcause; + struct nxpwifi_rxinfo *rx_info; + + if ((adapter->event_cause & EVENT_ID_MASK) =3D=3D EVENT_RADAR_DETECTED) { + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + if (nxpwifi_is_11h_active(priv)) { + adapter->event_cause |=3D + ((priv->bss_num & 0xff) << 16) | + ((priv->bss_type & 0xff) << 24); + break; + } + } + } + + eventcause =3D adapter->event_cause; + + /* Save the last event to debug log */ + adapter->dbg.last_event_index =3D + (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_event[adapter->dbg.last_event_index] =3D + (u16)eventcause; + + /* Get BSS number and corresponding priv */ + priv =3D nxpwifi_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (!priv) + priv =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + + /* Clear BSS_NO_BITS from event */ + eventcause &=3D EVENT_ID_MASK; + adapter->event_cause =3D eventcause; + + if (skb) { + rx_info =3D NXPWIFI_SKB_RXCB(skb); + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num =3D priv->bss_num; + rx_info->bss_type =3D 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 =3D=3D NXPWIFI_BSS_ROLE_UAP) + ret =3D nxpwifi_process_uap_event(priv); + else + ret =3D nxpwifi_process_sta_event(priv); + + adapter->event_cause =3D 0; + adapter->event_skb =3D NULL; + adapter->if_ops.event_complete(adapter, skb); + + return ret; +} + +/* + * Prepare and queue a command: sanity checks, get node, init, fill, and e= nqueue/dispatch. + */ +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 =3D 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 !=3D 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 =3D=3D NXPWIFI_HW_STATUS_RESET) { + if (cmd_no !=3D 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 !=3D HOST_CMD_802_11_HS_CFG_ENH) { + nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD); + priv->adapter->hs_activated_manually =3D false; + } + + /* Get a new command node */ + cmd_node =3D 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 =3D nxpwifi_uap_prepare_cmd(priv, cmd_node, + cmd_action, cmd_oid); + break; + default: + ret =3D nxpwifi_sta_prepare_cmd(priv, cmd_node, + cmd_action, cmd_oid); + break; + } + } else { + ret =3D nxpwifi_cmd_host_cmd(priv, cmd_node); + cmd_node->cmd_flag |=3D 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 =3D=3D HOST_CMD_802_11_SCAN || + cmd_no =3D=3D 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 =3D nxpwifi_wait_queue_complete(adapter, cmd_node); + } + + return ret; +} + +/* Queue command to cmd_pending_q; EXIT_PS and HS_ACTIVATE go to the head.= */ +void +nxpwifi_insert_cmd_to_pending_q(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + struct host_cmd_ds_command *host_cmd =3D NULL; + u16 command; + bool add_tail =3D true; + + host_cmd =3D (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 =3D le16_to_cpu(host_cmd->command); + + /* Exit_PS command needs to be queued in the header always. */ + if (command =3D=3D HOST_CMD_802_11_PS_MODE_ENH) { + struct host_cmd_ds_802_11_ps_mode_enh *pm =3D + &host_cmd->params.psmode_enh; + if ((le16_to_cpu(pm->action) =3D=3D DIS_PS) || + (le16_to_cpu(pm->action) =3D=3D DIS_AUTO_PS)) { + if (adapter->ps_state !=3D PS_STATE_AWAKE) + add_tail =3D false; + } + } + + /* Same with exit host sleep cmd, luckily that can't happen at the same t= ime as EXIT_PS */ + if (command =3D=3D HOST_CMD_802_11_HS_CFG_ENH) { + struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg =3D + &host_cmd->params.opt_hs_cfg; + + if (le16_to_cpu(hs_cfg->action) =3D=3D HS_ACTIVATE) + add_tail =3D 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=3D%#x, cmd_pending=3D%d\n", + command, atomic_read(&adapter->cmd_pending)); +} + +/* Dequeue next cmd and download to FW; if HS active (except HS_CFG), deac= tivate it. */ +int nxpwifi_exec_next_cmd(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_private *priv; + struct cmd_ctrl_node *cmd_node; + int ret =3D 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 -ENODATA; + } + cmd_node =3D list_first_entry(&adapter->cmd_pending_q, + struct cmd_ctrl_node, list); + + host_cmd =3D (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data); + priv =3D cmd_node->priv; + + if (adapter->ps_state !=3D 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 =3D nxpwifi_dnld_cmd_to_fw(priv, cmd_node); + priv =3D 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 !=3D + 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 =3D priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *pm; + + nxpwifi_dbg(adapter, ERROR, + "CMD_RESP: cmd %#x error, result=3D%#x\n", + resp->command, resp->result); + + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status =3D -1; + + switch (le16_to_cpu(resp->command)) { + case HOST_CMD_802_11_PS_MODE_ENH: + pm =3D &resp->params.psmode_enh; + nxpwifi_dbg(adapter, ERROR, + "PS_MODE_ENH cmd failed: result=3D0x%x action=3D0x%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 =3D NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); +} + +/* Handle command response: validate, cancel timer, dispatch, set status, = recycle node. */ +int nxpwifi_process_cmdresp(struct nxpwifi_adapter *adapter) +{ + struct host_cmd_ds_command *resp; + struct nxpwifi_private *priv =3D + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + int ret =3D 0; + u16 orig_cmdresp_no; + u16 cmdresp_no; + u16 cmdresp_result; + + if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { + resp =3D (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 =3D (struct host_cmd_ds_command *)adapter->curr_cmd->resp_skb->data; + orig_cmdresp_no =3D le16_to_cpu(resp->command); + cmdresp_no =3D (orig_cmdresp_no & HOST_CMD_ID_MASK); + + if (adapter->curr_cmd->cmd_no !=3D cmdresp_no) { + nxpwifi_dbg(adapter, ERROR, + "cmdresp error: cmd=3D0x%x cmd_resp=3D0x%x\n", + adapter->curr_cmd->cmd_no, cmdresp_no); + return -EINVAL; + } + /* Now we got response from FW, cancel the command timer */ + timer_delete_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 =3D le16_to_cpu(resp->size); + + nxpwifi_dbg(adapter, INFO, + "info: host cmd resp size =3D %d\n", size); + size =3D min_t(u16, size, NXPWIFI_SIZE_OF_CMD_BUFFER); + if (adapter->curr_cmd->data_buf) { + hostcmd =3D adapter->curr_cmd->data_buf; + hostcmd->len =3D size; + memcpy(hostcmd->cmd, resp, size); + } + } + + /* Get BSS number and corresponding priv */ + priv =3D 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 =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + /* Clear RET_BIT from HOST */ + resp->command =3D cpu_to_le16(orig_cmdresp_no & HOST_CMD_ID_MASK); + + cmdresp_no =3D le16_to_cpu(resp->command); + cmdresp_result =3D le16_to_cpu(resp->result); + + /* Save the last command response to debug log */ + adapter->dbg.last_cmd_resp_index =3D + (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =3D + 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 =3D -1; + + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd =3D NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + return -EINVAL; + } + + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + adapter->curr_cmd->cmd_flag &=3D ~CMD_F_HOSTCMD; + if (cmdresp_result =3D=3D HOST_RESULT_OK && + cmdresp_no =3D=3D HOST_CMD_802_11_HS_CFG_ENH) + ret =3D nxpwifi_ret_802_11_hs_cfg(priv, resp); + } else { + if (resp->result !=3D HOST_RESULT_OK) { + nxpwifi_process_cmdresp_error(priv, resp); + return -EFAULT; + } + if (adapter->curr_cmd->cmd_resp) { + void *data_buf =3D adapter->curr_cmd->data_buf; + + ret =3D adapter->curr_cmd->cmd_resp(priv, resp, + cmdresp_no, + data_buf); + } + } + + if (adapter->curr_cmd) { + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status =3D ret; + + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + adapter->curr_cmd =3D 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 =3D { + .uapsd_queues =3D -1, + }; + struct nxpwifi_private *priv =3D + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA); + + if (priv->assoc_rsp_size) { + assoc_resp.links[0].bss =3D priv->req_bss; + assoc_resp.buf =3D priv->assoc_rsp_buf; + assoc_resp.len =3D priv->assoc_rsp_size; + cfg80211_rx_assoc_resp(priv->netdev, + &assoc_resp); + priv->assoc_rsp_size =3D 0; + } +} + +/* + * Command timeout handler: mark timed out, cancel pending IOCTL, dump/res= et device if provided. + */ +void +nxpwifi_cmd_timeout_func(struct timer_list *t) +{ + struct nxpwifi_adapter *adapter =3D timer_container_of(adapter, t, cmd_ti= mer); + 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 =3D adapter->curr_cmd; + if (cmd_node) { + adapter->dbg.timeout_cmd_id =3D + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; + adapter->dbg.timeout_cmd_act =3D + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; + nxpwifi_dbg(adapter, MSG, + "%s: Timeout cmd id =3D %#x, act =3D %#x\n", __func__, + adapter->dbg.timeout_cmd_id, + adapter->dbg.timeout_cmd_act); + + nxpwifi_dbg(adapter, MSG, + "num_data_h2c_failure =3D %d\n", + adapter->dbg.num_tx_host_to_card_failure); + nxpwifi_dbg(adapter, MSG, + "num_cmd_h2c_failure =3D %d\n", + adapter->dbg.num_cmd_host_to_card_failure); + + nxpwifi_dbg(adapter, MSG, + "is_cmd_timedout =3D %d\n", + test_bit(NXPWIFI_IS_CMD_TIMEDOUT, + &adapter->work_flags)); + nxpwifi_dbg(adapter, MSG, + "num_tx_timeout =3D %d\n", + adapter->dbg.num_tx_timeout); + + nxpwifi_dbg(adapter, MSG, + "last_cmd_index =3D %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 =3D %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 =3D %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=3D%d cmd_sent=3D%d\n", + adapter->data_sent, adapter->cmd_sent); + + nxpwifi_dbg(adapter, MSG, + "ps_mode=3D%d ps_state=3D%d\n", + adapter->ps_mode, adapter->ps_state); + + if (cmd_node->wait_q_enabled) { + adapter->cmd_wait_q.status =3D -ETIMEDOUT; + nxpwifi_cancel_pending_ioctl(adapter); + } + } + + 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 =3D 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 =3D false; + nxpwifi_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_bh(&adapter->scan_pending_q_lock); +} + +/* + * Cancel current cmd (if waiting), all pending cmds, and pending scan cmd= s; complete with error. + */ +void +nxpwifi_cancel_all_pending_cmd(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node =3D 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 =3D -1; + nxpwifi_complete_cmd(adapter, adapter->curr_cmd); + adapter->curr_cmd->wait_q_enabled =3D 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 =3D -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); +} + +/* Cancel current/pending commands for the pending IOCTL; also cancel scan= cmds. */ +static void +nxpwifi_cancel_pending_ioctl(struct nxpwifi_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node =3D NULL; + + if (adapter->curr_cmd && + adapter->curr_cmd->wait_q_enabled) { + spin_lock_bh(&adapter->nxpwifi_cmd_lock); + cmd_node =3D adapter->curr_cmd; + /* + * Be careful when setting curr_cmd =3D NULL: + * nxpwifi_process_cmdresp expects a non-NULL pointer. + * This is safe here because only cmd_timeout calls this path + * and no response is expected at that point. + */ + adapter->curr_cmd =3D NULL; + spin_unlock_bh(&adapter->nxpwifi_cmd_lock); + + nxpwifi_recycle_cmd_node(adapter, cmd_node); + } + + nxpwifi_cancel_scan(adapter); +} + +/* If no cmd/event/tx is pending, send sleep-confirm to FW; otherwise defe= r. */ +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" : ""); +} + +/* Generate HS activated/deactivated event for userspace; update flags and= wake waiters. */ +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 =3D 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 =3D 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 =3D false; + } +} + +/* Handle HS_CFG response: update HS configured/activated flags and emit H= S events. */ +int nxpwifi_ret_802_11_hs_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =3D + &resp->params.opt_hs_cfg; + u32 conditions =3D le32_to_cpu(phs_cfg->params.hs_config.conditions); + + if (phs_cfg->action =3D=3D 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=3D%#x, conditions=3D0x%x gpio=3D0x%x gap=3D0x%x\n", + resp->result, conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap); + } + if (conditions !=3D 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; +} + +/* On power-up interrupt, wake device and cancel HS if armed; clear flags = and notify. */ +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 =3D false; + } + + adapter->hs_activated =3D 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); + +/* Handle sleep-confirm response; set ps_state and hs activation according= ly. */ +void +nxpwifi_process_sleep_confirm_resp(struct nxpwifi_adapter *adapter, + u8 *pbuf, u32 upld_len) +{ + struct host_cmd_ds_command *cmd =3D (struct host_cmd_ds_command *)pbuf; + u16 result =3D le16_to_cpu(cmd->result); + u16 command =3D le16_to_cpu(cmd->command); + u16 seq_num =3D 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 =3D HOST_GET_SEQ_NO(seq_num); + /* Clear RET_BIT from HOST */ + command &=3D HOST_CMD_ID_MASK; + + if (command !=3D HOST_CMD_802_11_PS_MODE_ENH) { + nxpwifi_dbg(adapter, ERROR, + "%s: rcvd unexpected resp for cmd %#x, result =3D %x\n", + __func__, command, result); + return; + } + + if (result) { + nxpwifi_dbg(adapter, ERROR, + "%s: sleep confirm cmd failed\n", + __func__); + adapter->pm_wakeup_card_req =3D false; + adapter->ps_state =3D PS_STATE_AWAKE; + return; + } + adapter->pm_wakeup_card_req =3D 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 =3D PS_STATE_SLEEP; + cmd->command =3D cpu_to_le16(command); + cmd->seq_num =3D 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/wirele= ss/nxp/nxpwifi/cmdevt.h new file mode 100644 index 000000000000..ec531fe4426f --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: 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 =3D cpu_to_le16(cmd_no); + cmd->size =3D 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 *con= fig); +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/wirel= ess/nxp/nxpwifi/sta_cmd.c new file mode 100644 index 000000000000..70085dd28264 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c @@ -0,0 +1,3444 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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 =3D &cmd->params.hw_spec; + + cmd->command =3D cpu_to_le16(HOST_CMD_GET_HW_SPEC); + cmd->size =3D 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 =3D &resp->params.hw_spec; + struct nxpwifi_adapter *adapter =3D 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 =3D 0; + + adapter->fw_cap_info =3D le32_to_cpu(hw_spec->fw_cap_info); + + if (IS_SUPPORT_MULTI_BANDS(adapter)) + adapter->fw_bands =3D GET_FW_DEFAULT_BANDS(adapter); + else + adapter->fw_bands =3D BAND_B; + + if ((adapter->fw_bands & BAND_A) && (adapter->fw_bands & BAND_GN)) + adapter->fw_bands |=3D BAND_AN; + if (!(adapter->fw_bands & BAND_G) && (adapter->fw_bands & BAND_GN)) + adapter->fw_bands &=3D ~BAND_GN; + + adapter->fw_release_number =3D le32_to_cpu(hw_spec->fw_release_number); + adapter->fw_api_ver =3D (adapter->fw_release_number >> 16) & 0xff; + adapter->number_of_antenna =3D + le16_to_cpu(hw_spec->number_of_antenna) & 0xf; + + if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { + adapter->is_hw_11ac_capable =3D true; + + /* Copy 11AC cap */ + adapter->hw_dot_11ac_dev_cap =3D + le32_to_cpu(hw_spec->dot_11ac_dev_cap); + adapter->usr_dot_11ac_dev_cap_bg =3D adapter->hw_dot_11ac_dev_cap + & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK; + adapter->usr_dot_11ac_dev_cap_a =3D adapter->hw_dot_11ac_dev_cap + & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK; + + /* Copy 11AC mcs */ + adapter->hw_dot_11ac_mcs_support =3D + le32_to_cpu(hw_spec->dot_11ac_mcs_support); + adapter->usr_dot_11ac_mcs_support =3D + adapter->hw_dot_11ac_mcs_support; + } else { + adapter->is_hw_11ac_capable =3D false; + } + + resp_size =3D 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 =3D resp_size - sizeof(struct host_cmd_ds_get_hw_spec); + while (left_len > sizeof(struct nxpwifi_ie_types_header)) { + tlv =3D (void *)&hw_spec->tlv + parsed_len; + switch (le16_to_cpu(tlv->type)) { + case TLV_TYPE_API_REV: + api_rev =3D (struct hw_spec_api_rev *)tlv; + api_id =3D le16_to_cpu(api_rev->api_id); + switch (api_id) { + case KEY_API_VER_ID: + adapter->key_api_major_ver =3D + api_rev->major_ver; + adapter->key_api_minor_ver =3D + 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 =3D + 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 =3D + 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 =3D (struct hw_spec_max_conn *)tlv; + adapter->max_sta_conn =3D 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 =3D (struct hw_spec_extension *)tlv; + if (hw_he_cap->ext_id =3D=3D + WLAN_EID_EXT_HE_CAPABILITY) + nxpwifi_update_11ax_cap(adapter, hw_he_cap); + break; + case TLV_TYPE_FW_CAP_INFO: + fw_cap =3D (struct hw_spec_fw_cap_info *)tlv; + adapter->fw_cap_info =3D + le32_to_cpu(fw_cap->fw_cap_info); + adapter->fw_cap_ext =3D + 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 =3D (struct hw_spec_secure_boot_uuid *)tlv; + adapter->uuid_lo =3D + le64_to_cpu(sb_uuid->uuid_lo); + adapter->uuid_hi =3D + 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 +=3D le16_to_cpu(tlv->len) + + sizeof(struct nxpwifi_ie_types_header); + left_len -=3D 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=3D%#x version=3D%#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 =3D le16_to_cpu(hw_spec->region_code); + + for (i =3D 0; i < NXPWIFI_MAX_REGION_CODE; i++) + /* Use the region code to search for the index */ + if (adapter->region_code =3D=3D region_code_index[i]) + break; + + /* If it's unidentified region code, use the default (world) */ + if (i >=3D NXPWIFI_MAX_REGION_CODE) { + adapter->region_code =3D 0x00; + nxpwifi_dbg(adapter, WARN, + "cmd: unknown region code, use default (USA)\n"); + } + + adapter->hw_dot_11n_dev_cap =3D le32_to_cpu(hw_spec->dot_11n_dev_cap); + adapter->hw_dev_mcs_support =3D hw_spec->dev_mcs_support; + adapter->hw_mpdu_density =3D GET_MPDU_DENSITY(le32_to_cpu(hw_spec->hw_dev= _cap)); + adapter->user_dev_mcs_support =3D adapter->hw_dev_mcs_support; + adapter->user_htstream =3D adapter->hw_dev_mcs_support; + if (adapter->fw_bands & BAND_A) + adapter->user_htstream |=3D (adapter->user_htstream << 8); + + if (adapter->if_ops.update_mp_end_port) { + u16 mp_end_port; + + mp_end_port =3D le16_to_cpu(hw_spec->mp_end_port); + adapter->if_ops.update_mp_end_port(adapter, mp_end_port); + } + + if (adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) + adapter->scan_chan_gap_enabled =3D true; + + for (i =3D 0; i < adapter->priv_num; i++) + adapter->priv[i]->config_bands =3D 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 =3D priv->adapter; + int ret; + + ret =3D nxpwifi_ret_802_11_scan(priv, resp); + adapter->curr_cmd->wait_q_enabled =3D 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 =3D cpu_to_le16(HOST_CMD_802_11_GET_LOG); + cmd->size =3D 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 =3D + &resp->params.get_log; + struct nxpwifi_ds_get_stats *stats =3D + (struct nxpwifi_ds_get_stats *)data_buf; + + if (stats) { + stats->mcast_tx_frame =3D le32_to_cpu(get_log->mcast_tx_frame); + stats->failed =3D le32_to_cpu(get_log->failed); + stats->retry =3D le32_to_cpu(get_log->retry); + stats->multi_retry =3D le32_to_cpu(get_log->multi_retry); + stats->frame_dup =3D le32_to_cpu(get_log->frame_dup); + stats->rts_success =3D le32_to_cpu(get_log->rts_success); + stats->rts_failure =3D le32_to_cpu(get_log->rts_failure); + stats->ack_failure =3D le32_to_cpu(get_log->ack_failure); + stats->rx_frag =3D le32_to_cpu(get_log->rx_frag); + stats->mcast_rx_frame =3D le32_to_cpu(get_log->mcast_rx_frame); + stats->fcs_error =3D le32_to_cpu(get_log->fcs_error); + stats->tx_frame =3D le32_to_cpu(get_log->tx_frame); + stats->wep_icv_error[0] =3D + le32_to_cpu(get_log->wep_icv_err_cnt[0]); + stats->wep_icv_error[1] =3D + le32_to_cpu(get_log->wep_icv_err_cnt[1]); + stats->wep_icv_error[2] =3D + le32_to_cpu(get_log->wep_icv_err_cnt[2]); + stats->wep_icv_error[3] =3D + le32_to_cpu(get_log->wep_icv_err_cnt[3]); + stats->bcn_rcv_cnt =3D le32_to_cpu(get_log->bcn_rcv_cnt); + stats->bcn_miss_cnt =3D 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 =3D &cmd->params.mc_addr; + struct nxpwifi_multicast_list *mcast_list =3D + (struct nxpwifi_multicast_list *)data_buf; + + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command =3D cpu_to_le16(HOST_CMD_MAC_MULTICAST_ADR); + + mcast_addr->action =3D cpu_to_le16(cmd_action); + mcast_addr->num_of_adrs =3D + 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 =3D &cmd->params.smib; + u16 *ul_temp =3D (u16 *)data_buf; + + nxpwifi_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: cmd_oid =3D 0x%x\n", cmd_type); + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_SNMP_MIB); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) + + S_DS_GEN); + + snmp_mib->oid =3D cpu_to_le16((u16)cmd_type); + if (cmd_action =3D=3D HOST_ACT_GEN_GET) { + snmp_mib->query_type =3D cpu_to_le16(HOST_ACT_GEN_GET); + snmp_mib->buf_size =3D cpu_to_le16(MAX_SNMP_BUF_SIZE); + le16_unaligned_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); + } else if (cmd_action =3D=3D HOST_ACT_GEN_SET) { + snmp_mib->query_type =3D cpu_to_le16(HOST_ACT_GEN_SET); + snmp_mib->buf_size =3D 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=3D0x%x, OID=3D0x%x,\t" + "OIDSize=3D0x%x, Value=3D0x%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 =3D &resp->params.smib; + u16 oid =3D le16_to_cpu(smib->oid); + u16 query_type =3D le16_to_cpu(smib->query_type); + u32 ul_temp; + + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: oid value =3D %#x,\t" + "query_type =3D %#x, buf size =3D %#x\n", + oid, query_type, le16_to_cpu(smib->buf_size)); + if (query_type =3D=3D HOST_ACT_GEN_GET) { + ul_temp =3D get_unaligned_le16(smib->value); + if (data_buf) + *(u32 *)data_buf =3D ul_temp; + switch (oid) { + case FRAG_THRESH_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: FragThsd =3D%u\n", + ul_temp); + break; + case RTS_THRESH_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: RTSThsd =3D%u\n", + ul_temp); + break; + case SHORT_RETRY_LIM_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: TxRetryCount=3D%u\n", + ul_temp); + break; + case DTIM_PERIOD_I: + nxpwifi_dbg(priv->adapter, INFO, + "info: SNMP_RESP: DTIM period=3D%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 =3D data_buf; + + cmd->command =3D 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 =3D cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); + mac_reg =3D &cmd->params.mac_reg; + mac_reg->action =3D cpu_to_le16(cmd_action); + mac_reg->offset =3D cpu_to_le16((u16)reg_rw->offset); + mac_reg->value =3D cpu_to_le32(reg_rw->value); + break; + } + case HOST_CMD_BBP_REG_ACCESS: + { + struct host_cmd_ds_bbp_reg_access *bbp_reg; + + cmd->size =3D cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); + bbp_reg =3D &cmd->params.bbp_reg; + bbp_reg->action =3D cpu_to_le16(cmd_action); + bbp_reg->offset =3D cpu_to_le16((u16)reg_rw->offset); + bbp_reg->value =3D (u8)reg_rw->value; + break; + } + case HOST_CMD_RF_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *rf_reg; + + cmd->size =3D cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); + rf_reg =3D &cmd->params.rf_reg; + rf_reg->action =3D cpu_to_le16(cmd_action); + rf_reg->offset =3D cpu_to_le16((u16)reg_rw->offset); + rf_reg->value =3D (u8)reg_rw->value; + break; + } + case HOST_CMD_PMIC_REG_ACCESS: + { + struct host_cmd_ds_pmic_reg_access *pmic_reg; + + cmd->size =3D cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); + pmic_reg =3D &cmd->params.pmic_reg; + pmic_reg->action =3D cpu_to_le16(cmd_action); + pmic_reg->offset =3D cpu_to_le16((u16)reg_rw->offset); + pmic_reg->value =3D (u8)reg_rw->value; + break; + } + case HOST_CMD_CAU_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *cau_reg; + + cmd->size =3D cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); + cau_reg =3D &cmd->params.rf_reg; + cau_reg->action =3D cpu_to_le16(cmd_action); + cau_reg->offset =3D cpu_to_le16((u16)reg_rw->offset); + cau_reg->value =3D (u8)reg_rw->value; + break; + } + case HOST_CMD_802_11_EEPROM_ACCESS: + { + struct nxpwifi_ds_read_eeprom *rd_eeprom =3D data_buf; + struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom =3D + &cmd->params.eeprom; + + cmd->size =3D cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); + cmd_eeprom->action =3D cpu_to_le16(cmd_action); + cmd_eeprom->offset =3D cpu_to_le16(rd_eeprom->offset); + cmd_eeprom->byte_count =3D cpu_to_le16(rd_eeprom->byte_count); + cmd_eeprom->value =3D 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 =3D data_buf; + eeprom =3D data_buf; + switch (cmdresp_no) { + case HOST_CMD_MAC_REG_ACCESS: + r.mac =3D &resp->params.mac_reg; + reg_rw->offset =3D (u32)le16_to_cpu(r.mac->offset); + reg_rw->value =3D le32_to_cpu(r.mac->value); + break; + case HOST_CMD_BBP_REG_ACCESS: + r.bbp =3D &resp->params.bbp_reg; + reg_rw->offset =3D (u32)le16_to_cpu(r.bbp->offset); + reg_rw->value =3D (u32)r.bbp->value; + break; + + case HOST_CMD_RF_REG_ACCESS: + r.rf =3D &resp->params.rf_reg; + reg_rw->offset =3D (u32)le16_to_cpu(r.rf->offset); + reg_rw->value =3D (u32)r.bbp->value; + break; + case HOST_CMD_PMIC_REG_ACCESS: + r.pmic =3D &resp->params.pmic_reg; + reg_rw->offset =3D (u32)le16_to_cpu(r.pmic->offset); + reg_rw->value =3D (u32)r.pmic->value; + break; + case HOST_CMD_CAU_REG_ACCESS: + r.rf =3D &resp->params.rf_reg; + reg_rw->offset =3D (u32)le16_to_cpu(r.rf->offset); + reg_rw->value =3D (u32)r.rf->value; + break; + case HOST_CMD_802_11_EEPROM_ACCESS: + r.eeprom =3D &resp->params.eeprom; + pr_debug("info: EEPROM read len=3D%x\n", + le16_to_cpu(r.eeprom->byte_count)); + if (eeprom->byte_count < le16_to_cpu(r.eeprom->byte_count)) { + eeprom->byte_count =3D 0; + pr_debug("info: EEPROM read length is too big\n"); + return -ENOMEM; + } + eeprom->offset =3D le16_to_cpu(r.eeprom->offset); + eeprom->byte_count =3D 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 =3D &cmd->params.txp; + + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + + S_DS_GEN); + cmd->command =3D cpu_to_le16(HOST_CMD_RF_TX_PWR); + txp->action =3D 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 =3D &resp->params.txp; + u16 action =3D le16_to_cpu(txp->action); + + priv->tx_power_level =3D le16_to_cpu(txp->cur_level); + + if (action =3D=3D HOST_ACT_GEN_GET) { + priv->max_tx_power_level =3D txp->max_power; + priv->min_tx_power_level =3D txp->min_power; + } + + nxpwifi_dbg(priv->adapter, INFO, + "Current TxPower Level=3D%d, Max Power=3D%d, Min Power=3D%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 =3D &cmd->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso =3D &cmd->params.ant_siso; + struct nxpwifi_ds_ant_cfg *ant_cfg =3D + (struct nxpwifi_ds_ant_cfg *)data_buf; + + cmd->command =3D cpu_to_le16(HOST_CMD_RF_ANTENNA); + + switch (cmd_action) { + case HOST_ACT_GEN_SET: + if (priv->adapter->hw_dev_mcs_support =3D=3D HT_STREAM_2X2) { + cmd->size =3D cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_mimo) + + S_DS_GEN); + ant_mimo->action_tx =3D cpu_to_le16(HOST_ACT_SET_TX); + ant_mimo->tx_ant_mode =3D + cpu_to_le16((u16)ant_cfg->tx_ant); + ant_mimo->action_rx =3D cpu_to_le16(HOST_ACT_SET_RX); + ant_mimo->rx_ant_mode =3D + cpu_to_le16((u16)ant_cfg->rx_ant); + } else { + cmd->size =3D cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_siso) + + S_DS_GEN); + ant_siso->action =3D cpu_to_le16(HOST_ACT_SET_BOTH); + ant_siso->ant_mode =3D cpu_to_le16((u16)ant_cfg->tx_ant); + } + break; + case HOST_ACT_GEN_GET: + if (priv->adapter->hw_dev_mcs_support =3D=3D HT_STREAM_2X2) { + cmd->size =3D cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_mimo) + + S_DS_GEN); + ant_mimo->action_tx =3D cpu_to_le16(HOST_ACT_GET_TX); + ant_mimo->action_rx =3D cpu_to_le16(HOST_ACT_GET_RX); + } else { + cmd->size =3D cpu_to_le16(sizeof(struct + host_cmd_ds_rf_ant_siso) + + S_DS_GEN); + ant_siso->action =3D 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 =3D &resp->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso =3D &resp->params.ant_siso; + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (adapter->hw_dev_mcs_support =3D=3D HT_STREAM_2X2) { + priv->tx_ant =3D le16_to_cpu(ant_mimo->tx_ant_mode); + priv->rx_ant =3D le16_to_cpu(ant_mimo->rx_ant_mode); + nxpwifi_dbg(adapter, INFO, + "RF_ANT_RESP: Tx action =3D 0x%x, Tx Mode =3D 0x%04x\t" + "Rx action =3D 0x%x, Rx Mode =3D 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 =3D le16_to_cpu(ant_siso->ant_mode); + priv->rx_ant =3D le16_to_cpu(ant_siso->ant_mode); + nxpwifi_dbg(adapter, INFO, + "RF_ANT_RESP: action =3D 0x%x, Mode =3D 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 =3D &cmd->params.deauth; + u8 *mac =3D (u8 *)data_buf; + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_DEAUTHENTICATE); + cmd->size =3D 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 =3D 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 =3D 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 =3D &cmd->params.mac_ctrl; + u32 *action =3D (u32 *)data_buf; + + if (cmd_action !=3D HOST_ACT_GEN_SET) { + nxpwifi_dbg(priv->adapter, ERROR, + "mac_control: only support set cmd\n"); + return -EINVAL; + } + + cmd->command =3D cpu_to_le16(HOST_CMD_MAC_CONTROL); + cmd->size =3D + cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); + mac_ctrl->action =3D 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 =3D cpu_to_le16(HOST_CMD_802_11_MAC_ADDRESS); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result =3D 0; + + cmd->params.mac_addr.action =3D cpu_to_le16(cmd_action); + + if (cmd_action =3D=3D 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 =3D &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 =3D priv->adapter; + struct host_cmd_ds_802_11d_domain_info *domain_info =3D + &cmd->params.domain_info; + struct nxpwifi_ietypes_domain_param_set *domain =3D + &domain_info->domain; + struct nxpwifi_ietypes_domain_code *domain_code; + u8 no_of_triplet =3D adapter->domain_reg.no_of_triplet; + int triplet_size; + + nxpwifi_dbg(adapter, INFO, + "info: 11D: no_of_triplet=3D0x%x\n", no_of_triplet); + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11D_DOMAIN_INFO); + cmd->size =3D cpu_to_le16(S_DS_GEN); + domain_info->action =3D cpu_to_le16(cmd_action); + le16_unaligned_add_cpu(&cmd->size, sizeof(domain_info->action)); + + if (cmd_action =3D=3D HOST_ACT_GEN_GET) + return 0; + + triplet_size =3D no_of_triplet * + sizeof(struct ieee80211_country_ie_triplet); + + domain->header.type =3D cpu_to_le16(WLAN_EID_COUNTRY); + domain->header.len =3D + 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 =3D (struct nxpwifi_ietypes_domain_code *)((u8 *)cmd + + le16_to_cpu(cmd->size)); + domain_code->header.type =3D cpu_to_le16(TLV_TYPE_REGION_DOMAIN_CODE); + domain_code->header.len =3D + 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 =3D + &resp->params.domain_info_resp; + struct nxpwifi_ietypes_domain_param_set *domain =3D &domain_info->domain; + u16 action =3D le16_to_cpu(domain_info->action); + u8 no_of_triplet; + + no_of_triplet =3D (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=3D%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 =3D priv->adapter; + u16 size, len =3D KEY_PARAMS_FIXED_LEN; + u8 key_type, key_type_igtk; + + if (enc_key->key_len =3D=3D WLAN_KEY_LEN_CCMP) { + key_type =3D KEY_TYPE_ID_AES; + key_type_igtk =3D KEY_TYPE_ID_AES_CMAC; + } else { + key_type =3D KEY_TYPE_ID_GCMP_256; + key_type_igtk =3D KEY_TYPE_ID_BIP_GMAC_256; + } + + if (enc_key->is_igtk_key) { + km->key_param_set.key_info &=3D cpu_to_le16(~KEY_MCAST); + km->key_param_set.key_info |=3D cpu_to_le16(KEY_IGTK); + km->key_param_set.key_type =3D key_type_igtk; + if (enc_key->key_len =3D=3D WLAN_KEY_LEN_CCMP) { + 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_params.cmac_aes.key_len =3D + 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 +=3D sizeof(struct nxpwifi_cmac_aes_param); + } else { + nxpwifi_dbg(adapter, INFO, + "%s: Set GMAC AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.gmac_aes.ipn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_params.gmac_aes.key_len =3D + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.gmac_aes.key, + enc_key->key_material, enc_key->key_len); + len +=3D sizeof(struct nxpwifi_gmac_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 =3D key_type_igtk; + km->key_param_set.key_idx =3D 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 =3D key_type; + km->key_param_set.key_params.aes.key_len =3D + 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 +=3D sizeof(struct nxpwifi_aes_param); + } + + km->key_param_set.len =3D cpu_to_le16(len); + size =3D len + sizeof(struct nxpwifi_ie_types_header) + + sizeof(km->action) + S_DS_GEN; + cmd->size =3D 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 =3D priv->adapter; + struct nxpwifi_ds_encrypt_key *enc_key =3D + (struct nxpwifi_ds_encrypt_key *)data_buf; + u8 *mac =3D enc_key->mac_addr; + u16 key_info, len =3D KEY_PARAMS_FIXED_LEN; + struct host_cmd_ds_802_11_key_material *km =3D + &cmd->params.key_material; + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_KEY_MATERIAL); + km->action =3D cpu_to_le16(cmd_action); + + if (cmd_action =3D=3D HOST_ACT_GEN_GET) { + nxpwifi_dbg(adapter, INFO, "%s: Get key\n", __func__); + km->key_param_set.key_idx =3D + enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type =3D cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len =3D cpu_to_le16(KEY_PARAMS_FIXED_LEN); + ether_addr_copy(km->key_param_set.mac_addr, mac); + + if (enc_key->key_index & NXPWIFI_KEY_INDEX_UNICAST) + key_info =3D KEY_UNICAST; + else + key_info =3D KEY_MCAST; + + if (enc_key->is_igtk_key) + key_info |=3D KEY_IGTK; + + km->key_param_set.key_info =3D cpu_to_le16(key_info); + + cmd->size =3D 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 =3D cpu_to_le16(HOST_ACT_GEN_REMOVE); + km->key_param_set.type =3D cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len =3D cpu_to_le16(KEY_PARAMS_FIXED_LEN); + km->key_param_set.key_idx =3D enc_key->key_index & KEY_INDEX_MASK; + key_info =3D KEY_MCAST | KEY_UNICAST; + km->key_param_set.key_info =3D cpu_to_le16(key_info); + ether_addr_copy(km->key_param_set.mac_addr, mac); + cmd->size =3D cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + km->action =3D cpu_to_le16(HOST_ACT_GEN_SET); + km->key_param_set.key_idx =3D enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type =3D cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + key_info =3D KEY_ENABLED; + ether_addr_copy(km->key_param_set.mac_addr, mac); + + if (enc_key->key_len <=3D WLAN_KEY_LEN_WEP104) { + nxpwifi_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); + len +=3D sizeof(struct nxpwifi_wep_param); + km->key_param_set.len =3D cpu_to_le16(len); + km->key_param_set.key_type =3D KEY_TYPE_ID_WEP; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + key_info |=3D KEY_MCAST | KEY_UNICAST; + } else { + if (enc_key->is_current_wep_key) { + key_info |=3D KEY_MCAST | KEY_UNICAST; + if (km->key_param_set.key_idx =3D=3D + (priv->wep_key_curr_index & KEY_INDEX_MASK)) + key_info |=3D KEY_DEFAULT; + } else { + if (is_broadcast_ether_addr(mac)) + key_info |=3D KEY_MCAST; + else + key_info |=3D KEY_UNICAST | KEY_DEFAULT; + } + } + km->key_param_set.key_info =3D cpu_to_le16(key_info); + + km->key_param_set.key_params.wep.key_len =3D + 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 =3D 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 |=3D KEY_MCAST | KEY_RX_KEY; + else + key_info |=3D KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; + + /* Enable default key for WPA/WPA2 */ + if (!priv->wpa_is_gtk_set) + key_info |=3D KEY_DEFAULT; + + km->key_param_set.key_info =3D cpu_to_le16(key_info); + + if (enc_key->key_cipher !=3D WLAN_CIPHER_SUITE_TKIP && + enc_key->key_len >=3D WLAN_KEY_LEN_CCMP) + return nxpwifi_set_aes_key(priv, cmd, enc_key, km); + + if (enc_key->key_len =3D=3D 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 =3D KEY_TYPE_ID_TKIP; + km->key_param_set.key_params.tkip.key_len =3D + 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 +=3D sizeof(struct nxpwifi_tkip_param); + km->key_param_set.len =3D cpu_to_le16(len); + cmd->size =3D 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 =3D &resp->params.key_material; + + len =3D 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) =3D=3D 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 =3D true; + priv->scan_block =3D false; + priv->port_open =3D true; + } + } + + if (key->key_param_set.key_type !=3D KEY_TYPE_ID_AES && + key->key_param_set.key_type !=3D KEY_TYPE_ID_GCMP_256) + 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 =3D 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 =3D priv->adapter; + int ret; + + ret =3D 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 =3D cpu_to_le16(HOST_CMD_WMM_GET_STATUS); + cmd->size =3D 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 =3D &cmd->params.subsc_evt; + struct nxpwifi_ds_misc_subsc_evt *subsc_evt_cfg =3D + (struct nxpwifi_ds_misc_subsc_evt *)data_buf; + struct nxpwifi_ie_types_rssi_threshold *rssi_tlv; + u16 event_bitmap; + u8 *pos; + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_SUBSCRIBE_EVENT); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + + S_DS_GEN); + + subsc_evt->action =3D 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 =3D=3D HOST_ACT_GEN_GET) + return 0; + + subsc_evt->events =3D cpu_to_le16(subsc_evt_cfg->events); + + event_bitmap =3D subsc_evt_cfg->events; + nxpwifi_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", + event_bitmap); + + if ((subsc_evt_cfg->action =3D=3D HOST_ACT_BITWISE_CLR || + subsc_evt_cfg->action =3D=3D HOST_ACT_BITWISE_SET) && + event_bitmap =3D=3D 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 =3D=3D HOST_ACT_BITWISE_CLR) + return 0; + + pos =3D ((u8 *)subsc_evt) + + sizeof(struct host_cmd_ds_802_11_subsc_evt); + + if (event_bitmap & BITMASK_BCN_RSSI_LOW) { + rssi_tlv =3D (struct nxpwifi_ie_types_rssi_threshold *)pos; + + rssi_tlv->header.type =3D cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len =3D + cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + rssi_tlv->abs_value =3D subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; + rssi_tlv->evt_freq =3D 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 +=3D 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 =3D (struct nxpwifi_ie_types_rssi_threshold *)pos; + + rssi_tlv->header.type =3D cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_tlv->header.len =3D + cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + rssi_tlv->abs_value =3D subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; + rssi_tlv->evt_freq =3D 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 +=3D 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 =3D + &resp->params.subsc_evt; + + /* + * For every subscribe event command (Get/Set/Clear), FW reports the curr= ent + * 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 =3D cpu_to_le16(HOST_CMD_802_11_TX_RATE_QUERY); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + + S_DS_GEN); + priv->tx_rate =3D 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 =3D resp->params.tx_rate.tx_rate; + priv->tx_htinfo =3D resp->params.tx_rate.ht_info; + if (!priv->is_data_rate_auto) + priv->data_rate =3D + 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 =3D + (struct nxpwifi_ds_mem_rw *)data_buf; + struct host_cmd_ds_mem_access *mem_access =3D (void *)&cmd->params.mem; + + cmd->command =3D cpu_to_le16(HOST_CMD_MEM_ACCESS); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + + S_DS_GEN); + + mem_access->action =3D cpu_to_le16(cmd_action); + mem_access->addr =3D cpu_to_le32(mem_rw->addr); + mem_access->value =3D 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 =3D (void *)&resp->params.mem; + + priv->mem_rw.addr =3D le32_to_cpu(mem->addr); + priv->mem_rw.value =3D le32_to_cpu(mem->value); + + return 0; +} + +static u32 nxpwifi_parse_cal_cfg(u8 *src, size_t len, u8 *dst) +{ + u8 *s =3D src, *d =3D dst; + + while (s - src < len) { + if (*s && (isspace(*s) || *s =3D=3D '\t')) { + s++; + continue; + } + if (isxdigit(*s)) { + if (kstrtou8(s, 16, d)) + return 0; + d++; + s +=3D 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 =3D priv->adapter; + struct property *prop =3D data_buf; + u32 len; + u8 *data =3D (u8 *)cmd + S_DS_GEN; + int ret; + + if (prop) { + len =3D prop->length; + ret =3D 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 =3D 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 =3D cpu_to_le16(HOST_CMD_CFG_DATA); + cmd->size =3D 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 !=3D 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 =3D cpu_to_le16(cmd_no); + cmd->params.verext.version_str_sel =3D + (u8)(get_unaligned((u32 *)data_buf)); + memcpy(&cmd->params, data_buf, sizeof(struct host_cmd_ds_version_ext)); + cmd->size =3D 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 =3D &resp->params.verext; + struct host_cmd_ds_version_ext *version_ext =3D + (struct host_cmd_ds_version_ext *)data_buf; + + if (test_and_clear_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &priv->adapter->w= ork_flags)) { + if (strncmp(ver_ext->version_str, "ChipRev:20, BB:9b(10.00), RF:40(21)", + NXPWIFI_VERSION_STR_LENGTH) =3D=3D 0) { + struct nxpwifi_ds_auto_ds auto_ds =3D { + .auto_ds =3D 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 =3D 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] =3D '\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 =3D mef_entry->filter; + int i, byte_len; + u8 *stack_ptr =3D *buffer; + + for (i =3D 0; i < NXPWIFI_MEF_MAX_FILTERS; i++) { + filter =3D &mef_entry->filter[i]; + if (!filter->filt_type) + break; + put_unaligned_le32((u32)filter->repeat, stack_ptr); + stack_ptr +=3D 4; + *stack_ptr =3D TYPE_DNUM; + stack_ptr +=3D 1; + + byte_len =3D filter->byte_seq[NXPWIFI_MEF_MAX_BYTESEQ]; + memcpy(stack_ptr, filter->byte_seq, byte_len); + stack_ptr +=3D byte_len; + *stack_ptr =3D byte_len; + stack_ptr +=3D 1; + *stack_ptr =3D TYPE_BYTESEQ; + stack_ptr +=3D 1; + put_unaligned_le32((u32)filter->offset, stack_ptr); + stack_ptr +=3D 4; + *stack_ptr =3D TYPE_DNUM; + stack_ptr +=3D 1; + + *stack_ptr =3D filter->filt_type; + stack_ptr +=3D 1; + + if (filter->filt_action) { + *stack_ptr =3D filter->filt_action; + stack_ptr +=3D 1; + } + + if (stack_ptr - *buffer > STACK_NBYTES) + return -ENOMEM; + } + + *buffer =3D 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 =3D &cmd->params.mef_cfg; + struct nxpwifi_ds_mef_cfg *mef =3D + (struct nxpwifi_ds_mef_cfg *)data_buf; + struct nxpwifi_fw_mef_entry *mef_entry =3D NULL; + u8 *pos =3D (u8 *)mef_cfg; + u16 i; + int ret =3D 0; + + cmd->command =3D cpu_to_le16(HOST_CMD_MEF_CFG); + + mef_cfg->criteria =3D cpu_to_le32(mef->criteria); + mef_cfg->num_entries =3D cpu_to_le16(mef->num_entries); + pos +=3D sizeof(*mef_cfg); + + for (i =3D 0; i < mef->num_entries; i++) { + mef_entry =3D (struct nxpwifi_fw_mef_entry *)pos; + mef_entry->mode =3D mef->mef_entry[i].mode; + mef_entry->action =3D mef->mef_entry[i].action; + pos +=3D sizeof(*mef_entry); + + ret =3D nxpwifi_cmd_append_rpn_expression(priv, + &mef->mef_entry[i], + &pos); + if (ret) + return ret; + + mef_entry->exprsize =3D + cpu_to_le16(pos - mef_entry->expr); + } + cmd->size =3D 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 =3D cpu_to_le16(HOST_CMD_RSSI_INFO); + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + + S_DS_GEN); + cmd->params.rssi_info.action =3D cpu_to_le16(cmd_action); + cmd->params.rssi_info.ndata =3D cpu_to_le16(priv->data_avg_factor); + cmd->params.rssi_info.nbcn =3D cpu_to_le16(priv->bcn_avg_factor); + + /* Reset SNR/NF/RSSI values in private structure */ + priv->data_rssi_last =3D 0; + priv->data_nf_last =3D 0; + priv->data_rssi_avg =3D 0; + priv->data_nf_avg =3D 0; + priv->bcn_rssi_last =3D 0; + priv->bcn_nf_last =3D 0; + priv->bcn_rssi_avg =3D 0; + priv->bcn_nf_avg =3D 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 =3D + &resp->params.rssi_info_rsp; + struct nxpwifi_ds_misc_subsc_evt *subsc_evt =3D + &priv->async_subsc_evt_storage; + + priv->data_rssi_last =3D le16_to_cpu(rssi_info_rsp->data_rssi_last); + priv->data_nf_last =3D le16_to_cpu(rssi_info_rsp->data_nf_last); + + priv->data_rssi_avg =3D le16_to_cpu(rssi_info_rsp->data_rssi_avg); + priv->data_nf_avg =3D le16_to_cpu(rssi_info_rsp->data_nf_avg); + + priv->bcn_rssi_last =3D le16_to_cpu(rssi_info_rsp->bcn_rssi_last); + priv->bcn_nf_last =3D le16_to_cpu(rssi_info_rsp->bcn_nf_last); + + priv->bcn_rssi_avg =3D le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); + priv->bcn_nf_avg =3D le16_to_cpu(rssi_info_rsp->bcn_nf_avg); + + if (priv->subsc_evt_rssi_state =3D=3D 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 =3D BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + subsc_evt->action =3D HOST_ACT_BITWISE_SET; + if (priv->subsc_evt_rssi_state =3D=3D RSSI_LOW_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value =3D abs(priv->bcn_rssi_avg - + priv->cqm_rssi_hyst); + subsc_evt->bcn_h_rssi_cfg.abs_value =3D abs(priv->cqm_rssi_thold); + } else if (priv->subsc_evt_rssi_state =3D=3D RSSI_HIGH_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value =3D abs(priv->cqm_rssi_thold); + subsc_evt->bcn_h_rssi_cfg.abs_value =3D abs(priv->bcn_rssi_avg + + priv->cqm_rssi_hyst); + } + subsc_evt->bcn_l_rssi_cfg.evt_freq =3D 1; + subsc_evt->bcn_h_rssi_cfg.evt_freq =3D 1; + + priv->subsc_evt_rssi_state =3D 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 =3D=3D NXPWIFI_HW_STATUS_RESET) + priv->adapter->hw_status =3D NXPWIFI_HW_STATUS_READY; + cmd->command =3D cpu_to_le16(cmd_no); + cmd->size =3D 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 =3D NXPWIFI_HW_STATUS_RESET; + cmd->command =3D cpu_to_le16(cmd_no); + cmd->size =3D 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 =3D &cmd->params.txp_cfg; + struct host_cmd_ds_txpwr_cfg *txp =3D + (struct host_cmd_ds_txpwr_cfg *)data_buf; + + cmd->command =3D cpu_to_le16(HOST_CMD_TXPWR_CFG); + cmd->size =3D + 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 =3D (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 =3D (struct nxpwifi_types_power_group *)((u8 *) + cmd_txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + cmd->size =3D 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 =3D cpu_to_le16(cmd_action); + break; + case HOST_ACT_GEN_GET: + cmd_txp_cfg->action =3D cpu_to_le16(cmd_action); + break; + } + + return 0; +} + +static int nxpwifi_get_power_level(struct nxpwifi_private *priv, void *dat= a_buf) +{ + int length, max_power =3D -1, min_power =3D -1; + struct nxpwifi_types_power_group *pg_tlv_hdr; + struct nxpwifi_power_group *pg; + + if (!data_buf) + return -ENOMEM; + + pg_tlv_hdr =3D (struct nxpwifi_types_power_group *)((u8 *)data_buf); + pg =3D (struct nxpwifi_power_group *) + ((u8 *)pg_tlv_hdr + sizeof(struct nxpwifi_types_power_group)); + length =3D 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 =3D pg->power_max; + min_power =3D pg->power_min; + length -=3D sizeof(struct nxpwifi_power_group); + + while (length >=3D sizeof(struct nxpwifi_power_group)) { + pg++; + if (max_power < pg->power_max) + max_power =3D pg->power_max; + + if (min_power > pg->power_min) + min_power =3D pg->power_min; + + length -=3D sizeof(struct nxpwifi_power_group); + } + priv->min_tx_power_level =3D (u8)min_power; + priv->max_tx_power_level =3D (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 =3D priv->adapter; + struct host_cmd_ds_txpwr_cfg *txp_cfg =3D &resp->params.txp_cfg; + struct nxpwifi_types_power_group *pg_tlv_hdr; + struct nxpwifi_power_group *pg; + u16 action =3D le16_to_cpu(txp_cfg->action); + u16 tlv_buf_left; + + pg_tlv_hdr =3D (struct nxpwifi_types_power_group *) + ((u8 *)txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + + pg =3D (struct nxpwifi_power_group *) + ((u8 *)pg_tlv_hdr + + sizeof(struct nxpwifi_types_power_group)); + + tlv_buf_left =3D 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 =3D=3D NXPWIFI_HW_STATUS_INITIALIZING) + nxpwifi_get_power_level(priv, pg_tlv_hdr); + + priv->tx_power_level =3D (u16)pg->power_min; + break; + + case HOST_ACT_GEN_SET: + if (!le32_to_cpu(txp_cfg->mode)) + break; + + if (pg->power_max =3D=3D pg->power_min) + priv->tx_power_level =3D (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 =3D %d, Max Power=3D%d, Min Power=3D%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 =3D &cmd->params.tx_rate_cfg; + u16 *pbitmap_rates =3D (u16 *)data_buf; + struct nxpwifi_rate_scope *rate_scope; + struct nxpwifi_rate_drop_pattern *rate_drop; + u32 i; + + cmd->command =3D cpu_to_le16(HOST_CMD_TX_RATE_CFG); + + rate_cfg->action =3D cpu_to_le16(cmd_action); + rate_cfg->cfg_index =3D 0; + + rate_scope =3D (struct nxpwifi_rate_scope *)((u8 *)rate_cfg + + sizeof(struct host_cmd_ds_tx_rate_cfg)); + rate_scope->type =3D cpu_to_le16(TLV_TYPE_RATE_SCOPE); + rate_scope->length =3D cpu_to_le16 + (sizeof(*rate_scope) - sizeof(struct nxpwifi_ie_types_header)); + if (pbitmap_rates) { + rate_scope->hr_dsss_rate_bitmap =3D cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap =3D cpu_to_le16(pbitmap_rates[1]); + for (i =3D 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] =3D + cpu_to_le16(pbitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) { + for (i =3D 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] =3D + cpu_to_le16(pbitmap_rates[10 + i]); + } + } else { + rate_scope->hr_dsss_rate_bitmap =3D + cpu_to_le16(priv->bitmap_rates[0]); + rate_scope->ofdm_rate_bitmap =3D + cpu_to_le16(priv->bitmap_rates[1]); + for (i =3D 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] =3D + cpu_to_le16(priv->bitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) { + for (i =3D 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] =3D + cpu_to_le16(priv->bitmap_rates[10 + i]); + } + } + + rate_drop =3D (struct nxpwifi_rate_drop_pattern *)((u8 *)rate_scope + + sizeof(struct nxpwifi_rate_scope)); + rate_drop->type =3D cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); + rate_drop->length =3D cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); + rate_drop->rate_drop_mode =3D 0; + + cmd->size =3D + 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_b= uf) +{ + struct nxpwifi_rate_scope *rate_scope; + int i; + + rate_scope =3D (struct nxpwifi_rate_scope *)tlv_buf; + priv->bitmap_rates[0] =3D + le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); + priv->bitmap_rates[1] =3D + le16_to_cpu(rate_scope->ofdm_rate_bitmap); + for (i =3D 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + priv->bitmap_rates[2 + i] =3D + le16_to_cpu(rate_scope->ht_mcs_rate_bitmap[i]); + + if (priv->adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15) { + for (i =3D 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + priv->bitmap_rates[10 + i] =3D + 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 =3D &resp->params.tx_rate_cfg; + struct nxpwifi_ie_types_header *head; + u16 tlv, tlv_buf_len, tlv_buf_left; + u8 *tlv_buf; + + tlv_buf =3D ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); + tlv_buf_left =3D le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); + + while (tlv_buf_left >=3D sizeof(*head)) { + head =3D (struct nxpwifi_ie_types_header *)tlv_buf; + tlv =3D le16_to_cpu(head->type); + tlv_buf_len =3D 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 +=3D (sizeof(*head) + tlv_buf_len); + tlv_buf_left -=3D (sizeof(*head) + tlv_buf_len); + } + + priv->is_data_rate_auto =3D nxpwifi_is_rate_auto(priv); + + if (priv->is_data_rate_auto) + priv->data_rate =3D 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 =3D priv->adapter; + + if (0xffff !=3D (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { + adapter->tx_buf_size =3D + (u16)le16_to_cpu(resp->params.tx_buf.buff_size); + adapter->tx_buf_size =3D + (adapter->tx_buf_size / NXPWIFI_SDIO_BLOCK_SIZE) * + NXPWIFI_SDIO_BLOCK_SIZE; + adapter->curr_tx_buf_size =3D adapter->tx_buf_size; + nxpwifi_dbg(adapter, CMD, "cmd: curr_tx_buf_size=3D%d\n", + adapter->curr_tx_buf_size); + + if (adapter->if_ops.update_mp_end_port) { + u16 mp_end_port; + + mp_end_port =3D + 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 =3D &cmd->params.coex; + bool *is_timeshare =3D (bool *)data_buf; + struct nxpwifi_ie_types_robust_coex *coex_tlv; + + cmd->command =3D cpu_to_le16(HOST_CMD_ROBUST_COEX); + cmd->size =3D cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); + + coex->action =3D cpu_to_le16(cmd_action); + coex_tlv =3D (struct nxpwifi_ie_types_robust_coex *) + ((u8 *)coex + sizeof(*coex)); + coex_tlv->header.type =3D cpu_to_le16(TLV_TYPE_ROBUST_COEX); + coex_tlv->header.len =3D cpu_to_le16(sizeof(coex_tlv->mode)); + + if (coex->action =3D=3D HOST_ACT_GEN_GET) + return 0; + + if (*is_timeshare) + coex_tlv->mode =3D cpu_to_le32(NXPWIFI_COEX_MODE_TIMESHARE); + else + coex_tlv->mode =3D 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 =3D &resp->params.coex; + bool *is_timeshare =3D (bool *)data_buf; + struct nxpwifi_ie_types_robust_coex *coex_tlv; + u16 action =3D le16_to_cpu(coex->action); + u32 mode; + + coex_tlv =3D (struct nxpwifi_ie_types_robust_coex + *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); + if (action =3D=3D HOST_ACT_GEN_GET) { + mode =3D le32_to_cpu(coex_tlv->mode); + if (mode =3D=3D NXPWIFI_COEX_MODE_TIMESHARE) + *is_timeshare =3D true; + else + *is_timeshare =3D 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 =3D + &cmd->params.psmode_enh; + u16 ps_bitmap =3D (u16)cmd_type; + struct nxpwifi_ds_auto_ds *auto_ds =3D + (struct nxpwifi_ds_auto_ds *)data_buf; + u8 *tlv; + u16 cmd_size =3D 0; + + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH); + if (cmd_action =3D=3D DIS_AUTO_PS) { + psmode_enh->action =3D cpu_to_le16(DIS_AUTO_PS); + psmode_enh->params.ps_bitmap =3D cpu_to_le16(ps_bitmap); + cmd->size =3D cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action =3D=3D GET_PS) { + psmode_enh->action =3D cpu_to_le16(GET_PS); + psmode_enh->params.ps_bitmap =3D cpu_to_le16(ps_bitmap); + cmd->size =3D cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action =3D=3D EN_AUTO_PS) { + psmode_enh->action =3D cpu_to_le16(EN_AUTO_PS); + psmode_enh->params.ps_bitmap =3D cpu_to_le16(ps_bitmap); + cmd_size =3D S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap); + tlv =3D (u8 *)cmd + cmd_size; + if (ps_bitmap & BITMAP_STA_PS) { + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_ie_types_ps_param *ps_tlv =3D + (struct nxpwifi_ie_types_ps_param *)tlv; + struct nxpwifi_ps_param *ps_mode =3D &ps_tlv->param; + + ps_tlv->header.type =3D cpu_to_le16(TLV_TYPE_PS_PARAM); + ps_tlv->header.len =3D cpu_to_le16(sizeof(*ps_tlv) - + sizeof(struct nxpwifi_ie_types_header)); + cmd_size +=3D sizeof(*ps_tlv); + tlv +=3D sizeof(*ps_tlv); + nxpwifi_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter PS\n"); + ps_mode->null_pkt_interval =3D + cpu_to_le16(adapter->null_pkt_interval); + ps_mode->multiple_dtims =3D + cpu_to_le16(adapter->multiple_dtim); + ps_mode->bcn_miss_timeout =3D + cpu_to_le16(adapter->bcn_miss_time_out); + ps_mode->local_listen_interval =3D + cpu_to_le16(adapter->local_listen_interval); + ps_mode->delay_to_ps =3D + cpu_to_le16(adapter->delay_to_ps); + ps_mode->mode =3D cpu_to_le16(adapter->enhanced_ps_mode); + } + if (ps_bitmap & BITMAP_AUTO_DS) { + struct nxpwifi_ie_types_auto_ds_param *auto_ds_tlv =3D + (struct nxpwifi_ie_types_auto_ds_param *)tlv; + u16 idletime =3D 0; + + auto_ds_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); + auto_ds_tlv->header.len =3D + cpu_to_le16(sizeof(*auto_ds_tlv) - + sizeof(struct nxpwifi_ie_types_header)); + cmd_size +=3D sizeof(*auto_ds_tlv); + tlv +=3D sizeof(*auto_ds_tlv); + if (auto_ds) + idletime =3D auto_ds->idle_time; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter Auto Deep Sleep\n"); + auto_ds_tlv->deep_sleep_timeout =3D cpu_to_le16(idletime); + } + cmd->size =3D 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 =3D priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =3D + &resp->params.psmode_enh; + struct nxpwifi_ds_pm_cfg *pm_cfg =3D + (struct nxpwifi_ds_pm_cfg *)data_buf; + u16 action =3D le16_to_cpu(ps_mode->action); + u16 ps_bitmap =3D le16_to_cpu(ps_mode->params.ps_bitmap); + u16 auto_ps_bitmap =3D + le16_to_cpu(ps_mode->params.ps_bitmap); + + nxpwifi_dbg(adapter, INFO, + "info: %s: PS_MODE cmd reply result=3D%#x action=3D%#X\n", + __func__, resp->result, action); + if (action =3D=3D 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 =3D 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 =3D=3D DIS_AUTO_PS) { + if (ps_bitmap & BITMAP_AUTO_DS) { + priv->adapter->is_deep_sleep =3D 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 =3D false; + adapter->tx_lock_flag =3D false; + adapter->pps_uapsd_mode =3D false; + } + } + } else if (action =3D=3D GET_PS) { + if (ps_bitmap & BITMAP_STA_PS) + adapter->ps_mode =3D NXPWIFI_802_11_POWER_MODE_PSP; + else + adapter->ps_mode =3D NXPWIFI_802_11_POWER_MODE_CAM; + + nxpwifi_dbg(adapter, CMD, + "cmd: ps_bitmap=3D%#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 =3D 1; + else + pm_cfg->param.ps_mode =3D 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 =3D priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg =3D &cmd->params.opt_hs_cfg; + struct nxpwifi_hs_config_param *hscfg_param =3D + (struct nxpwifi_hs_config_param *)data_buf; + u8 *tlv =3D (u8 *)hs_cfg + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); + struct nxpwifi_ps_param_in_hs *psparam_tlv =3D NULL; + bool hs_activate =3D false; + u16 size; + + if (!hscfg_param) + /* New Activate command */ + hs_activate =3D true; + cmd->command =3D cpu_to_le16(HOST_CMD_802_11_HS_CFG_ENH); + + if (!hs_activate && + hscfg_param->conditions !=3D cpu_to_le32(HS_CFG_CANCEL) && + (adapter->arp_filter_size > 0 && + adapter->arp_filter_size <=3D 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 =3D adapter->arp_filter_size + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + S_DS_GEN; + tlv =3D (u8 *)hs_cfg + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + adapter->arp_filter_size; + } else { + size =3D S_DS_GEN + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh); + } + if (hs_activate) { + hs_cfg->action =3D cpu_to_le16(HS_ACTIVATE); + hs_cfg->params.hs_activate.resp_ctrl =3D cpu_to_le16(RESP_NEEDED); + + adapter->hs_activated_manually =3D true; + nxpwifi_dbg(priv->adapter, CMD, + "cmd: Activating host sleep manually\n"); + } else { + hs_cfg->action =3D cpu_to_le16(HS_CONFIGURE); + hs_cfg->params.hs_config.conditions =3D hscfg_param->conditions; + hs_cfg->params.hs_config.gpio =3D hscfg_param->gpio; + hs_cfg->params.hs_config.gap =3D hscfg_param->gap; + + size +=3D sizeof(struct nxpwifi_ps_param_in_hs); + psparam_tlv =3D (struct nxpwifi_ps_param_in_hs *)tlv; + psparam_tlv->header.type =3D + cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS); + psparam_tlv->header.len =3D + cpu_to_le16(sizeof(struct nxpwifi_ps_param_in_hs) + - sizeof(struct nxpwifi_ie_types_header)); + psparam_tlv->hs_wake_int =3D cpu_to_le32(HS_DEF_WAKE_INTERVAL); + psparam_tlv->hs_inact_timeout =3D + 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 =3D 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 =3D cpu_to_le16(cmd_no); + if (priv->bss_mode =3D=3D NL80211_IFTYPE_STATION) + cmd->params.bss_mode.con_type =3D CONNECTION_TYPE_INFRA; + else if (priv->bss_mode =3D=3D NL80211_IFTYPE_AP) + cmd->params.bss_mode.con_type =3D CONNECTION_TYPE_AP; + cmd->size =3D cpu_to_le16(sizeof(struct host_cmd_ds_set_bss_mode) + + S_DS_GEN); + + return 0; +} + +static int +nxpwifi_cmd_sta_802_11_net_monitor(struct nxpwifi_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_no, void *data_buf, + u16 cmd_action, u32 cmd_type) +{ + struct nxpwifi_802_11_net_monitor *net_mon; + struct host_cmd_ds_802_11_net_monitor *cmd_net_mon =3D + &cmd->params.net_mon; + struct chan_band_param *chan_band =3D NULL; + u8 sec_chan_offset =3D 0; + u32 bw_offset =3D 0; + + net_mon =3D (struct nxpwifi_802_11_net_monitor *)data_buf; + + cmd->size =3D cpu_to_le16(S_DS_GEN + + sizeof(struct host_cmd_ds_802_11_net_monitor) + + sizeof(struct chan_band_param)); + cmd->command =3D cpu_to_le16(cmd_no); + cmd_net_mon->action =3D cpu_to_le16(cmd_action); + + if (cmd_action =3D=3D HOST_ACT_GEN_SET) { + if (net_mon->enable_net_mon) { + cmd_net_mon->enable_net_mon =3D cpu_to_le16(0x1); + cmd_net_mon->filter_flag =3D cpu_to_le16((u16) + net_mon->filter_flag); + } + + if (net_mon->enable_net_mon && net_mon->channel) { + chan_band =3D &cmd_net_mon->monitor_chan.chan_band_param[0]; + cmd_net_mon->monitor_chan.header.type =3D + cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + cmd_net_mon->monitor_chan.header.len =3D + cpu_to_le16(sizeof(struct chan_band_param)); + chan_band->chan_number =3D (u8)net_mon->channel; + chan_band->band_cfg.chan_band =3D + nxpwifi_band_to_radio_type((u16)net_mon->band); + + if (net_mon->band & BAND_GN || + net_mon->band & BAND_AN || + net_mon->band & BAND_GAC || + net_mon->band & BAND_AAC) { + bw_offset =3D net_mon->chan_bandwidth; + if (bw_offset =3D=3D CHANNEL_BW_40MHZ_ABOVE) { + chan_band->band_cfg.chan_2O_ffset =3D + NXPWIFI_SEC_CHAN_ABOVE; + chan_band->band_cfg.chan_width =3D + CHAN_BW_40MHZ; + } else if (bw_offset =3D=3D CHANNEL_BW_40MHZ_BELOW) { + chan_band->band_cfg.chan_2O_ffset =3D + NXPWIFI_SEC_CHAN_BELOW; + chan_band->band_cfg.chan_width =3D + CHAN_BW_40MHZ; + } else if (bw_offset =3D=3D CHANNEL_BW_80MHZ) { + sec_chan_offset =3D + nxpwifi_get_sec_chan_offset(net_mon->channel); + if (sec_chan_offset =3D=3D NXPWIFI_SEC_CHAN_ABOVE) + chan_band->band_cfg.chan_2O_ffset =3D + NXPWIFI_SEC_CHAN_ABOVE; + else if (sec_chan_offset =3D=3D NXPWIFI_SEC_CHAN_BELOW) + chan_band->band_cfg.chan_2O_ffset =3D + NXPWIFI_SEC_CHAN_BELOW; + chan_band->band_cfg.chan_width =3D CHAN_BW_80MHZ; + } + } + } + } + return 0; +} + +static int +nxpwifi_ret_sta_802_11_net_monitor(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + struct host_cmd_ds_802_11_net_monitor *cmd_net_mon =3D &resp->params.net_= mon; + + nxpwifi_dbg(priv->adapter, CMD, + "cmd: NET_MONITOR_CMD: action: %d, enable: %d, flag: %d ch: %d band:= %d bw: %d offset: %d\n", + le16_to_cpu(cmd_net_mon->action), + le16_to_cpu(cmd_net_mon->enable_net_mon), + le16_to_cpu(cmd_net_mon->filter_flag), + cmd_net_mon->monitor_chan.chan_band_param[0].chan_number, + cmd_net_mon->monitor_chan.chan_band_param[0].band_cfg.chan_band, + cmd_net_mon->monitor_chan.chan_band_param[0].band_cfg.chan_width, + cmd_net_mon->monitor_chan.chan_band_param[0].band_cfg.chan_2O_ffset); + priv->adapter->enable_net_mon =3D le16_to_cpu(cmd_net_mon->enable_net_mon= ); + 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 =3D priv->adapter; + int ret; + + ret =3D nxpwifi_ret_802_11_scan_ext(priv, resp); + adapter->curr_cmd->wait_q_enabled =3D 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 =3D + &cmd->params.coalesce_cfg; + struct nxpwifi_ds_coalesce_cfg *cfg =3D + (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 =3D cpu_to_le16(HOST_CMD_COALESCE_CFG); + cmd->size =3D cpu_to_le16(S_DS_GEN); + + coalesce_cfg->action =3D cpu_to_le16(cmd_action); + coalesce_cfg->num_of_rules =3D cpu_to_le16(cfg->num_of_rules); + rule =3D (void *)coalesce_cfg->rule_data; + + for (cnt =3D 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type =3D cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay =3D + cpu_to_le16(cfg->rule[cnt].max_coalescing_delay); + rule->pkt_type =3D cfg->rule[cnt].pkt_type; + rule->num_of_fields =3D cfg->rule[cnt].num_of_fields; + + length =3D 0; + + param =3D rule->params; + for (idx =3D 0; idx < cfg->rule[cnt].num_of_fields; idx++) { + param->operation =3D cfg->rule[cnt].params[idx].operation; + param->operand_len =3D + cfg->rule[cnt].params[idx].operand_len; + param->offset =3D + 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 +=3D 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 =3D 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 =3D (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 =3D cpu_to_le16(cmd_no); + cmd->params.reg_mask.action =3D cpu_to_le16(cmd_action); + cmd->params.reg_mask.mask =3D + cpu_to_le32(get_unaligned((u32 *)data_buf)); + cmd->size =3D 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 =3D cpu_to_le16(cmd_no); + memcpy(&cmd->params, data_buf, + sizeof(struct host_cmd_ds_remain_on_chan)); + cmd->size =3D 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 =3D &resp->params.roc_cfg; + struct host_cmd_ds_remain_on_chan *roc_cfg =3D + (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 =3D &cmd->params.rekey; + struct cfg80211_gtk_rekey_data *data =3D + (struct cfg80211_gtk_rekey_data *)data_buf; + u64 rekey_ctr; + + cmd->command =3D cpu_to_le16(HOST_CMD_GTK_REKEY_OFFLOAD_CFG); + cmd->size =3D cpu_to_le16(sizeof(*rekey) + S_DS_GEN); + + rekey->action =3D cpu_to_le16(cmd_action); + if (cmd_action =3D=3D HOST_ACT_GEN_SET) { + memcpy(rekey->kek, data->kek, NL80211_KEK_LEN); + memcpy(rekey->kck, data->kck, NL80211_KCK_LEN); + rekey_ctr =3D be64_to_cpup((__be64 *)data->replay_ctr); + rekey->replay_ctr_low =3D cpu_to_le32((u32)rekey_ctr); + rekey->replay_ctr_high =3D + 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 =3D cpu_to_le16(HOST_CMD_HS_WAKEUP_REASON); + cmd->size =3D 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 =3D + (struct host_cmd_ds_wakeup_reason *)data_buf; + wakeup_reason->wakeup_reason =3D + 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 =3D &cmd->params.mc_policy; + const u16 *drcs_info =3D data_buf; + + mc_pol->action =3D cpu_to_le16(cmd_action); + mc_pol->policy =3D cpu_to_le16(*drcs_info); + cmd->command =3D cpu_to_le16(HOST_CMD_MC_POLICY); + cmd->size =3D 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 =3D + &cmd->params.sdio_rx_aggr_cfg; + + cmd->command =3D cpu_to_le16(HOST_CMD_SDIO_SP_RX_AGGR_CFG); + cmd->size =3D + cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + + S_DS_GEN); + cfg->action =3D cmd_action; + if (cmd_action =3D=3D HOST_ACT_GEN_SET) + cfg->enable =3D *(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 =3D priv->adapter; + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =3D + &resp->params.sdio_rx_aggr_cfg; + + adapter->sdio_rx_aggr_enable =3D cfg->enable; + adapter->sdio_rx_block_size =3D 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 =3D &cmd->params.sta_cfg; + struct host_cmd_tlv_channel_band *tlv_band_channel =3D + (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer; + + cmd->command =3D cpu_to_le16(HOST_CMD_STA_CONFIGURE); + cmd->size =3D cpu_to_le16(sizeof(*sta_cfg_cmd) + + sizeof(*tlv_band_channel) + S_DS_GEN); + sta_cfg_cmd->action =3D cpu_to_le16(cmd_action); + memset(tlv_band_channel, 0, sizeof(*tlv_band_channel)); + tlv_band_channel->header.type =3D cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + tlv_band_channel->header.len =3D 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 =3D &resp->params.sta_cfg; + struct nxpwifi_channel_band *channel_band =3D + (struct nxpwifi_channel_band *)data_buf; + struct host_cmd_tlv_channel_band *tlv_band_channel; + + tlv_band_channel =3D + (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 =3D 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 =3D &cmd->params.reg_cfg; + + cmd->command =3D cpu_to_le16(HOST_CMD_CHAN_REGION_CFG); + cmd->size =3D cpu_to_le16(sizeof(*reg) + S_DS_GEN); + + if (cmd_action =3D=3D HOST_ACT_GEN_GET) + reg->action =3D 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 =3D buf_len / 2; + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *rule; + bool new_rule; + int idx, freq, prev_freq =3D 0; + u32 bw, prev_bw =3D 0; + u8 chflags, prev_chflags =3D 0, valid_rules =3D 0; + + if (WARN_ON_ONCE(num_chan > NL80211_MAX_SUPP_REG_RULES)) + return ERR_PTR(-EINVAL); + + regd =3D kzalloc(struct_size(regd, reg_rules, num_chan), GFP_KERNEL); + if (!regd) + return ERR_PTR(-ENOMEM); + + for (idx =3D 0; idx < num_chan; idx++) { + u8 chan; + enum nl80211_band band; + + chan =3D *buf++; + if (!chan) { + kfree(regd); + return NULL; + } + chflags =3D *buf++; + band =3D (chan <=3D 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + freq =3D ieee80211_channel_to_frequency(chan, band); + new_rule =3D false; + + if (chflags & NXPWIFI_CHANNEL_DISABLED) + continue; + + if (band =3D=3D NL80211_BAND_5GHZ) { + if (!(chflags & NXPWIFI_CHANNEL_NOHT80)) + bw =3D MHZ_TO_KHZ(80); + else if (!(chflags & NXPWIFI_CHANNEL_NOHT40)) + bw =3D MHZ_TO_KHZ(40); + else + bw =3D MHZ_TO_KHZ(20); + } else { + if (!(chflags & NXPWIFI_CHANNEL_NOHT40)) + bw =3D MHZ_TO_KHZ(40); + else + bw =3D MHZ_TO_KHZ(20); + } + + if (idx =3D=3D 0 || prev_chflags !=3D chflags || prev_bw !=3D bw || + freq - prev_freq > 20) { + valid_rules++; + new_rule =3D true; + } + + rule =3D ®d->reg_rules[valid_rules - 1]; + + rule->freq_range.end_freq_khz =3D MHZ_TO_KHZ(freq + 10); + + prev_chflags =3D chflags; + prev_freq =3D freq; + prev_bw =3D bw; + + if (!new_rule) + continue; + + rule->freq_range.start_freq_khz =3D MHZ_TO_KHZ(freq - 10); + rule->power_rule.max_eirp =3D DBM_TO_MBM(19); + + if (chflags & NXPWIFI_CHANNEL_PASSIVE) + rule->flags =3D NL80211_RRF_NO_IR; + + if (chflags & NXPWIFI_CHANNEL_DFS) + rule->flags =3D NL80211_RRF_DFS; + + rule->freq_range.max_bandwidth_khz =3D bw; + } + + regd->n_reg_rules =3D valid_rules; + regd->alpha2[0] =3D '9'; + regd->alpha2[1] =3D '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 =3D &resp->params.reg_cfg; + u16 action =3D 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 !=3D HOST_ACT_GEN_GET) + return 0; + + tlv_buf =3D (u8 *)reg + sizeof(*reg); + tlv_buf_left =3D le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*reg); + + while (tlv_buf_left >=3D sizeof(*head)) { + head =3D (struct nxpwifi_ie_types_header *)tlv_buf; + tlv =3D le16_to_cpu(head->type); + tlv_buf_len =3D 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 =3D nxpwifi_create_custom_regdomain(priv, (u8 *)head + + sizeof(*head), + tlv_buf_len); + if (!IS_ERR(regd)) + priv->adapter->regd =3D regd; + break; + } + + tlv_buf +=3D (sizeof(*head) + tlv_buf_len); + tlv_buf_left -=3D (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 =3D cpu_to_le16(cmd_no); + cmd->params.pkt_aggr_ctrl.action =3D cpu_to_le16(cmd_action); + cmd->params.pkt_aggr_ctrl.enable =3D cpu_to_le16(*(u16 *)data_buf); + cmd->size =3D 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 =3D + &resp->params.pkt_aggr_ctrl; + struct nxpwifi_adapter *adapter =3D priv->adapter; + + adapter->bus_aggr.enable =3D le16_to_cpu(pkt_aggr_ctrl->enable); + if (adapter->bus_aggr.enable) + adapter->intf_hdr_len =3D INTF_HEADER_LEN; + adapter->bus_aggr.mode =3D NXPWIFI_BUS_AGGR_MODE_LEN_V2; + adapter->bus_aggr.tx_aggr_max_size =3D + le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_size); + adapter->bus_aggr.tx_aggr_max_num =3D + le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_num); + adapter->bus_aggr.tx_aggr_align =3D + 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 int +nxpwifi_cmd_sta_twt_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_twt_cfg(priv, cmd, cmd_action, data_buf); +} + +static int +nxpwifi_ret_sta_twt_cfg(struct nxpwifi_private *priv, + struct host_cmd_ds_command *resp, + u16 cmdresp_no, + void *data_buf) +{ + return nxpwifi_ret_twt_cfg(priv, resp, data_buf); +} + +static const struct nxpwifi_cmd_entry cmd_table_sta[] =3D { + {.cmd_no =3D HOST_CMD_GET_HW_SPEC, + .prepare_cmd =3D nxpwifi_cmd_sta_get_hw_spec, + .cmd_resp =3D nxpwifi_ret_sta_get_hw_spec}, + {.cmd_no =3D HOST_CMD_802_11_SCAN, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_scan, + .cmd_resp =3D nxpwifi_ret_sta_802_11_scan}, + {.cmd_no =3D HOST_CMD_802_11_GET_LOG, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_get_log, + .cmd_resp =3D nxpwifi_ret_sta_802_11_get_log}, + {.cmd_no =3D HOST_CMD_MAC_MULTICAST_ADR, + .prepare_cmd =3D nxpwifi_cmd_sta_mac_multicast_adr, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_802_11_ASSOCIATE, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_associate, + .cmd_resp =3D nxpwifi_ret_sta_802_11_associate}, + {.cmd_no =3D HOST_CMD_802_11_SNMP_MIB, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_snmp_mib, + .cmd_resp =3D nxpwifi_ret_sta_802_11_snmp_mib}, + {.cmd_no =3D HOST_CMD_MAC_REG_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_reg_access, + .cmd_resp =3D nxpwifi_ret_sta_reg_access}, + {.cmd_no =3D HOST_CMD_BBP_REG_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_reg_access, + .cmd_resp =3D nxpwifi_ret_sta_reg_access}, + {.cmd_no =3D HOST_CMD_RF_REG_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_reg_access, + .cmd_resp =3D nxpwifi_ret_sta_reg_access}, + {.cmd_no =3D HOST_CMD_RF_TX_PWR, + .prepare_cmd =3D nxpwifi_cmd_sta_rf_tx_pwr, + .cmd_resp =3D nxpwifi_ret_sta_rf_tx_pwr}, + {.cmd_no =3D HOST_CMD_RF_ANTENNA, + .prepare_cmd =3D nxpwifi_cmd_sta_rf_antenna, + .cmd_resp =3D nxpwifi_ret_sta_rf_antenna}, + {.cmd_no =3D HOST_CMD_802_11_DEAUTHENTICATE, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_deauthenticate, + .cmd_resp =3D nxpwifi_ret_sta_802_11_deauthenticate}, + {.cmd_no =3D HOST_CMD_MAC_CONTROL, + .prepare_cmd =3D nxpwifi_cmd_sta_mac_control, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_802_11_MAC_ADDRESS, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_mac_address, + .cmd_resp =3D nxpwifi_ret_sta_802_11_mac_address}, + {.cmd_no =3D HOST_CMD_802_11_EEPROM_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_reg_access, + .cmd_resp =3D nxpwifi_ret_sta_reg_access}, + {.cmd_no =3D HOST_CMD_802_11D_DOMAIN_INFO, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11d_domain_info, + .cmd_resp =3D nxpwifi_ret_sta_802_11d_domain_info}, + {.cmd_no =3D HOST_CMD_802_11_KEY_MATERIAL, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_key_material, + .cmd_resp =3D nxpwifi_ret_sta_802_11_key_material}, + {.cmd_no =3D HOST_CMD_802_11_BG_SCAN_CONFIG, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_bg_scan_config, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_802_11_BG_SCAN_QUERY, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_bg_scan_query, + .cmd_resp =3D nxpwifi_ret_sta_802_11_bg_scan_query}, + {.cmd_no =3D HOST_CMD_WMM_GET_STATUS, + .prepare_cmd =3D nxpwifi_cmd_sta_wmm_get_status, + .cmd_resp =3D nxpwifi_ret_sta_wmm_get_status}, + {.cmd_no =3D HOST_CMD_802_11_SUBSCRIBE_EVENT, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_subsc_evt, + .cmd_resp =3D nxpwifi_ret_sta_subsc_evt}, + {.cmd_no =3D HOST_CMD_802_11_TX_RATE_QUERY, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_tx_rate_query, + .cmd_resp =3D nxpwifi_ret_sta_802_11_tx_rate_query}, + {.cmd_no =3D HOST_CMD_MEM_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_mem_access, + .cmd_resp =3D nxpwifi_ret_sta_mem_access}, + {.cmd_no =3D HOST_CMD_CFG_DATA, + .prepare_cmd =3D nxpwifi_cmd_sta_cfg_data, + .cmd_resp =3D nxpwifi_ret_sta_cfg_data}, + {.cmd_no =3D HOST_CMD_VERSION_EXT, + .prepare_cmd =3D nxpwifi_cmd_sta_ver_ext, + .cmd_resp =3D nxpwifi_ret_sta_ver_ext}, + {.cmd_no =3D HOST_CMD_MEF_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_mef_cfg, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_RSSI_INFO, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_rssi_info, + .cmd_resp =3D nxpwifi_ret_sta_802_11_rssi_info}, + {.cmd_no =3D HOST_CMD_FUNC_INIT, + .prepare_cmd =3D nxpwifi_cmd_sta_func_init, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_FUNC_SHUTDOWN, + .prepare_cmd =3D nxpwifi_cmd_sta_func_shutdown, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_PMIC_REG_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_reg_access, + .cmd_resp =3D nxpwifi_ret_sta_reg_access}, + {.cmd_no =3D HOST_CMD_11N_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_11n_cfg, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_11N_ADDBA_REQ, + .prepare_cmd =3D nxpwifi_cmd_sta_11n_addba_req, + .cmd_resp =3D nxpwifi_ret_sta_11n_addba_req}, + {.cmd_no =3D HOST_CMD_11N_ADDBA_RSP, + .prepare_cmd =3D nxpwifi_cmd_sta_11n_addba_rsp, + .cmd_resp =3D nxpwifi_ret_sta_11n_addba_rsp}, + {.cmd_no =3D HOST_CMD_11N_DELBA, + .prepare_cmd =3D nxpwifi_cmd_sta_11n_delba, + .cmd_resp =3D nxpwifi_ret_sta_11n_delba}, + {.cmd_no =3D HOST_CMD_TXPWR_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_tx_power_cfg, + .cmd_resp =3D nxpwifi_ret_sta_tx_power_cfg}, + {.cmd_no =3D HOST_CMD_TX_RATE_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_tx_rate_cfg, + .cmd_resp =3D nxpwifi_ret_sta_tx_rate_cfg}, + {.cmd_no =3D HOST_CMD_RECONFIGURE_TX_BUFF, + .prepare_cmd =3D nxpwifi_cmd_sta_reconfigure_rx_buff, + .cmd_resp =3D nxpwifi_ret_sta_reconfigure_rx_buff}, + {.cmd_no =3D HOST_CMD_CHAN_REPORT_REQUEST, + .prepare_cmd =3D nxpwifi_cmd_sta_chan_report_request, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_AMSDU_AGGR_CTRL, + .prepare_cmd =3D nxpwifi_cmd_sta_amsdu_aggr_ctrl, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_ROBUST_COEX, + .prepare_cmd =3D nxpwifi_cmd_sta_robust_coex, + .cmd_resp =3D nxpwifi_ret_sta_robust_coex}, + {.cmd_no =3D HOST_CMD_802_11_PS_MODE_ENH, + .prepare_cmd =3D nxpwifi_cmd_sta_enh_power_mode, + .cmd_resp =3D nxpwifi_ret_sta_enh_power_mode}, + {.cmd_no =3D HOST_CMD_802_11_HS_CFG_ENH, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_hs_cfg, + .cmd_resp =3D nxpwifi_ret_sta_802_11_hs_cfg}, + {.cmd_no =3D HOST_CMD_CAU_REG_ACCESS, + .prepare_cmd =3D nxpwifi_cmd_sta_reg_access, + .cmd_resp =3D nxpwifi_ret_sta_reg_access}, + {.cmd_no =3D HOST_CMD_SET_BSS_MODE, + .prepare_cmd =3D nxpwifi_cmd_sta_set_bss_mode, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_802_11_NET_MONITOR, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_net_monitor, + .cmd_resp =3D nxpwifi_ret_sta_802_11_net_monitor}, + {.cmd_no =3D HOST_CMD_802_11_SCAN_EXT, + .prepare_cmd =3D nxpwifi_cmd_sta_802_11_scan_ext, + .cmd_resp =3D nxpwifi_ret_sta_802_11_scan_ext}, + {.cmd_no =3D HOST_CMD_COALESCE_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_coalesce_cfg, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_MGMT_FRAME_REG, + .prepare_cmd =3D nxpwifi_cmd_sta_mgmt_frame_reg, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_REMAIN_ON_CHAN, + .prepare_cmd =3D nxpwifi_cmd_sta_remain_on_chan, + .cmd_resp =3D nxpwifi_ret_sta_remain_on_chan}, + {.cmd_no =3D HOST_CMD_GTK_REKEY_OFFLOAD_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_gtk_rekey_offload, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_11AC_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_11ac_cfg, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_HS_WAKEUP_REASON, + .prepare_cmd =3D nxpwifi_cmd_sta_hs_wakeup_reason, + .cmd_resp =3D nxpwifi_ret_sta_hs_wakeup_reason}, + {.cmd_no =3D HOST_CMD_MC_POLICY, + .prepare_cmd =3D nxpwifi_cmd_sta_mc_policy, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_FW_DUMP_EVENT, + .prepare_cmd =3D nxpwifi_cmd_fill_head_only, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_SDIO_SP_RX_AGGR_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_sdio_rx_aggr_cfg, + .cmd_resp =3D nxpwifi_ret_sta_sdio_rx_aggr_cfg}, + {.cmd_no =3D HOST_CMD_STA_CONFIGURE, + .prepare_cmd =3D nxpwifi_cmd_sta_get_chan_info, + .cmd_resp =3D nxpwifi_ret_sta_get_chan_info}, + {.cmd_no =3D HOST_CMD_CHAN_REGION_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_chan_region_cfg, + .cmd_resp =3D nxpwifi_ret_sta_chan_region_cfg}, + {.cmd_no =3D HOST_CMD_PACKET_AGGR_CTRL, + .prepare_cmd =3D nxpwifi_cmd_sta_pkt_aggr_ctrl, + .cmd_resp =3D nxpwifi_ret_sta_pkt_aggr_ctrl}, + {.cmd_no =3D HOST_CMD_11AX_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_11ax_cfg, + .cmd_resp =3D nxpwifi_ret_sta_11ax_cfg}, + {.cmd_no =3D HOST_CMD_11AX_CMD, + .prepare_cmd =3D nxpwifi_cmd_sta_11ax_cmd, + .cmd_resp =3D nxpwifi_ret_sta_11ax_cmd}, + {.cmd_no =3D HOST_CMD_TWT_CFG, + .prepare_cmd =3D nxpwifi_cmd_sta_twt_cfg, + .cmd_resp =3D nxpwifi_ret_sta_twt_cfg}, +}; + +/* + * Prepare a command before sending it to firmware by invoking the + * appropriate handler based on the command ID. + */ +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 =3D priv->adapter; + u16 cmd_no =3D cmd_node->cmd_no; + struct host_cmd_ds_command *cmd =3D + (struct host_cmd_ds_command *)cmd_node->skb->data; + void *data_buf =3D cmd_node->data_buf; + int i, ret =3D -EINVAL; + + for (i =3D 0; i < ARRAY_SIZE(cmd_table_sta); i++) { + if (cmd_no =3D=3D cmd_table_sta[i].cmd_no) { + if (cmd_table_sta[i].prepare_cmd) + ret =3D cmd_table_sta[i].prepare_cmd(priv, cmd, + cmd_no, + data_buf, + cmd_action, + cmd_oid); + cmd_node->cmd_resp =3D cmd_table_sta[i].cmd_resp; + break; + } + } + + if (i =3D=3D 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 =3D 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 <=3D NXPWIFI_SIZE_OF_CMD_BUFFER - S_DS_GEN) { + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_CFG_DATA, + HOST_ACT_GEN_SET, 0, + prop, true); + if (ret) + return ret; + } + } +#endif + return 0; +} + +/* + * Initialize firmware after download or during virtual interface + * reinitialization to bring the device to a working state. + */ +int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool = init) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D 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) =3D=3D 0) { + pr_debug("Wakeup pin =3D 0x%x\n", data); + adapter->hs_cfg.gpio =3D 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 =3D 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 =3D=3D NXPWIFI_SDIO && + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && + !priv->adapter->host_disable_sdio_rx_aggr) { + sdio_sp_rx_aggr_enable =3D true; + ret =3D 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 =3D false; + } + } + + /* Reconfigure tx buf size */ + ret =3D 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 !=3D NXPWIFI_BSS_TYPE_UAP) { + /* Enable IEEE PS by default */ + priv->adapter->ps_mode =3D NXPWIFI_802_11_POWER_MODE_PSP; + ret =3D 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 =3D nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG, + HOST_ACT_GEN_GET, 0, NULL, true); + if (ret) + return ret; + priv->data_rate =3D 0; + + /* get tx power */ + ret =3D 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 =3D true; + /* Send request to firmware */ + ret =3D 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 =3D 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 !=3D NXPWIFI_BSS_TYPE_UAP) { + /* Enable auto deep sleep */ + auto_ds.auto_ds =3D DEEP_SLEEP_ON; + auto_ds.idle_time =3D DEEP_SLEEP_IDLE_TIME; + ret =3D 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 !=3D NXPWIFI_BSS_TYPE_UAP) { + /* Send cmd to FW to enable/disable 11D function */ + state_11d =3D ENABLE_11D; + ret =3D 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 =3D NXPWIFI_FW_DEF_HTTXCFG; + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_11N_CFG, + HOST_ACT_GEN_SET, 0, &tx_cfg, true); + + return ret; +} diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_event.c b/drivers/net/wir= eless/nxp/nxpwifi/sta_event.c new file mode 100644 index 000000000000..355064b1d8f7 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_event.c @@ -0,0 +1,862 @@ +// 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 =3D priv->adapter; + + adapter->dbg.num_event_link_lost++; + if (priv->media_connected) { + adapter->priv_link_lost =3D priv; + adapter->host_mlme_link_lost =3D true; + nxpwifi_queue_wiphy_work(adapter, + &adapter->host_mlme_work); + } + + return 0; +} + +static int +nxpwifi_sta_event_link_sensed(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D priv->adapter; + + 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) { + priv->last_deauth_reason =3D + get_unaligned_le16(priv->adapter->event_body); + nxpwifi_queue_wiphy_work(priv->adapter, + &priv->reset_conn_state_work); + } + } + + return 0; +} + +static int +nxpwifi_sta_event_disassociated(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + 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) { + priv->last_deauth_reason =3D + get_unaligned_le16(priv->adapter->event_body); + nxpwifi_queue_wiphy_work(priv->adapter, + &priv->reset_conn_state_work); + } + } + + return 0; +} + +static int +nxpwifi_sta_event_ps_awake(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (!adapter->pps_uapsd_mode && + priv->port_open && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode =3D true; + nxpwifi_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag =3D false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (nxpwifi_check_last_packet_indication(priv)) { + if (adapter->data_sent) { + adapter->ps_state =3D PS_STATE_AWAKE; + adapter->pm_wakeup_card_req =3D false; + adapter->pm_wakeup_fw_try =3D false; + timer_delete(&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 =3D PS_STATE_SLEEP; + } + + return 0; + } + } + + adapter->ps_state =3D PS_STATE_AWAKE; + adapter->pm_wakeup_card_req =3D false; + adapter->pm_wakeup_fw_try =3D false; + timer_delete(&adapter->wakeup_timer); + + return 0; +} + +static int +nxpwifi_sta_event_ps_sleep(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + adapter->ps_state =3D 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 =3D priv->adapter; + + adapter->if_ops.wakeup_complete(adapter); + if (adapter->is_deep_sleep) + adapter->is_deep_sleep =3D 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 =3D 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 =3D 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 =3D true; + + return 0; +} + +static int +nxpwifi_sta_event_addba(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D 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 =3D priv->adapter; + struct host_cmd_ds_11n_batimeout *event =3D + (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 =3D priv->adapter; + u16 ctrl; + + ctrl =3D get_unaligned_le16(adapter->event_body); + adapter->tx_buf_size =3D 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 =3D &priv->curr_bss_params.bss_descriptor; + priv->csa_expire_time =3D jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); + priv->csa_chan =3D 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 =3D 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 =3D 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 =3D 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 =3D priv->adapter; + void *buf =3D adapter->event_skb->data; + int ret =3D 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 =3D 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 =3D 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 =3D 0; + priv->auth_alg =3D 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 =3D false; + + return 0; +} + +static int +nxpwifi_sta_event_multi_chan_info(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D 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 =3D 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 =3D priv->adapter; + + return nxpwifi_process_vdll_event(priv, adapter->event_skb); +} + +static const struct nxpwifi_evt_entry evt_table_sta[] =3D { + {.event_cause =3D EVENT_LINK_LOST, + .event_handler =3D nxpwifi_sta_event_link_lost}, + {.event_cause =3D EVENT_LINK_SENSED, + .event_handler =3D nxpwifi_sta_event_link_sensed}, + {.event_cause =3D EVENT_DEAUTHENTICATED, + .event_handler =3D nxpwifi_sta_event_deauthenticated}, + {.event_cause =3D EVENT_DISASSOCIATED, + .event_handler =3D nxpwifi_sta_event_disassociated}, + {.event_cause =3D EVENT_PS_AWAKE, + .event_handler =3D nxpwifi_sta_event_ps_awake}, + {.event_cause =3D EVENT_PS_SLEEP, + .event_handler =3D nxpwifi_sta_event_ps_sleep}, + {.event_cause =3D EVENT_MIC_ERR_MULTICAST, + .event_handler =3D nxpwifi_sta_event_mic_err_multicast}, + {.event_cause =3D EVENT_MIC_ERR_UNICAST, + .event_handler =3D nxpwifi_sta_event_mic_err_unicast}, + {.event_cause =3D EVENT_DEEP_SLEEP_AWAKE, + .event_handler =3D nxpwifi_sta_event_deep_sleep_awake}, + {.event_cause =3D EVENT_WMM_STATUS_CHANGE, + .event_handler =3D nxpwifi_sta_event_wmm_status_change}, + {.event_cause =3D EVENT_BG_SCAN_REPORT, + .event_handler =3D nxpwifi_sta_event_bs_scan_report}, + {.event_cause =3D EVENT_RSSI_LOW, + .event_handler =3D nxpwifi_sta_event_rssi_low}, + {.event_cause =3D EVENT_RSSI_HIGH, + .event_handler =3D nxpwifi_sta_event_rssi_high}, + {.event_cause =3D EVENT_PORT_RELEASE, + .event_handler =3D nxpwifi_sta_event_port_release}, + {.event_cause =3D EVENT_ADDBA, + .event_handler =3D nxpwifi_sta_event_addba}, + {.event_cause =3D EVENT_DELBA, + .event_handler =3D nxpwifi_sta_event_delba}, + {.event_cause =3D EVENT_BA_STREAM_TIEMOUT, + .event_handler =3D nxpwifi_sta_event_bs_stream_timeout}, + {.event_cause =3D EVENT_AMSDU_AGGR_CTRL, + .event_handler =3D nxpwifi_sta_event_amsdu_aggr_ctrl}, + {.event_cause =3D EVENT_HS_ACT_REQ, + .event_handler =3D nxpwifi_sta_event_hs_act_req}, + {.event_cause =3D EVENT_CHANNEL_SWITCH_ANN, + .event_handler =3D nxpwifi_sta_event_channel_switch_ann}, + {.event_cause =3D EVENT_RADAR_DETECTED, + .event_handler =3D nxpwifi_sta_event_radar_detected}, + {.event_cause =3D EVENT_CHANNEL_REPORT_RDY, + .event_handler =3D nxpwifi_sta_event_channel_report_rdy}, + {.event_cause =3D EVENT_TX_DATA_PAUSE, + .event_handler =3D nxpwifi_sta_event_tx_data_pause}, + {.event_cause =3D EVENT_EXT_SCAN_REPORT, + .event_handler =3D nxpwifi_sta_event_ext_scan_report}, + {.event_cause =3D EVENT_RXBA_SYNC, + .event_handler =3D nxpwifi_sta_event_rxba_sync}, + {.event_cause =3D EVENT_REMAIN_ON_CHAN_EXPIRED, + .event_handler =3D nxpwifi_sta_event_remain_on_chan_expired}, + {.event_cause =3D EVENT_BG_SCAN_STOPPED, + .event_handler =3D nxpwifi_sta_event_bg_scan_stopped}, + {.event_cause =3D EVENT_MULTI_CHAN_INFO, + .event_handler =3D nxpwifi_sta_event_multi_chan_info}, + {.event_cause =3D EVENT_TX_STATUS_REPORT, + .event_handler =3D nxpwifi_sta_event_tx_status_report}, + {.event_cause =3D EVENT_BT_COEX_WLAN_PARA_CHANGE, + .event_handler =3D nxpwifi_sta_event_bt_coex_wlan_para_change}, + {.event_cause =3D EVENT_VDLL_IND, + .event_handler =3D nxpwifi_sta_event_vdll_ind}, + {.event_cause =3D EVENT_DUMMY_HOST_WAKEUP_SIGNAL, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_MIB_CHANGED, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_INIT_DONE, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_SNR_LOW, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_MAX_FAIL, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_SNR_HIGH, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_DATA_RSSI_LOW, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_DATA_SNR_LOW, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_DATA_RSSI_HIGH, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_DATA_SNR_HIGH, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_LINK_QUALITY, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_PRE_BEACON_LOST, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_WEP_ICV_ERR, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_BW_CHANGE, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_HOSTWAKE_STAIE, + .event_handler =3D NULL}, + {.event_cause =3D EVENT_UNKNOWN_DEBUG, + .event_handler =3D 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 =3D (void *)tlv; + nxpwifi_dbg(priv->adapter, EVENT, + "uap tx_pause: %pM pause=3D%d, pkts=3D%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 =3D false; + else + priv->port_open =3D true; + } else if (is_multicast_ether_addr(tp->peermac)) { + nxpwifi_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); + } else { + rcu_read_lock(); + sta_ptr =3D nxpwifi_get_sta_entry(priv, tp->peermac); + if (sta_ptr && sta_ptr->tx_pause !=3D tp->tx_pause) { + sta_ptr->tx_pause =3D tp->tx_pause; + nxpwifi_update_ralist_tx_pause(priv, tp->peermac, + tp->tx_pause); + } + rcu_read_unlock(); + } +} + +static void nxpwifi_process_sta_tx_pause(struct nxpwifi_private *priv, + struct nxpwifi_ie_types_header *tlv) +{ + struct nxpwifi_tx_pause_tlv *tp; + + tp =3D (void *)tlv; + nxpwifi_dbg(priv->adapter, EVENT, + "sta tx_pause: %pM pause=3D%d, pkts=3D%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 =3D false; + else + priv->port_open =3D true; + } +} + +/* + * Reset connection state after a firmware-triggered disconnect. + * Clears link state, queues, RSSI/SNR and security settings, + * saves previous SSID/BSSID for possible reassociation, + * and notifies cfg80211. + */ +void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason_= code, + bool from_ap) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (!priv->media_connected) + return; + + nxpwifi_dbg(adapter, INFO, + "info: handles disconnect event\n"); + + priv->media_connected =3D false; + + priv->auth_flag =3D 0; + priv->auth_alg =3D WLAN_AUTH_NONE; + + priv->scan_block =3D false; + priv->port_open =3D false; + + /* Free Tx and Rx packets, report disconnect to upper layer */ + nxpwifi_clean_txrx(priv); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last =3D 0; + priv->data_nf_last =3D 0; + priv->data_rssi_avg =3D 0; + priv->data_nf_avg =3D 0; + priv->bcn_rssi_last =3D 0; + priv->bcn_nf_last =3D 0; + priv->bcn_rssi_avg =3D 0; + priv->bcn_nf_avg =3D 0; + priv->rxpd_rate =3D 0; + priv->rxpd_htinfo =3D 0; + priv->sec_info.wpa_enabled =3D false; + priv->sec_info.wpa2_enabled =3D false; + priv->wpa_ie_len =3D 0; + + priv->sec_info.encryption_mode =3D 0; + + /* Enable auto data rate */ + priv->is_data_rate_auto =3D true; + priv->data_rate =3D 0; + + priv->assoc_resp_ht_param =3D 0; + priv->ht_param_present =3D false; + + if ((GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) =3D=3D 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=3D%s, SSID len=3D%u\n", + priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); + + nxpwifi_dbg(adapter, INFO, + "info: current SSID=3D%s, SSID len=3D%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 =3D false; + adapter->pps_uapsd_mode =3D false; + + if (test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) && + adapter->curr_cmd) + return; + + priv->media_connected =3D false; + nxpwifi_dbg(adapter, MSG, + "info: successfully disconnected from %pM: reason code %d\n", + priv->cfg_bssid, reason_code); + + if (priv->bss_mode =3D=3D 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_reset_conn_state_work(struct wiphy *wiphy, struct wiphy_work = *work) +{ + struct nxpwifi_private *priv =3D container_of(work, + struct nxpwifi_private, + reset_conn_state_work); + + nxpwifi_reset_connect_state(priv, priv->last_deauth_reason, true); +} + +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 =3D 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 =3D event_skb->len - sizeof(u32); + chan_info =3D (void *)event_skb->data + sizeof(u32); + + if (le16_to_cpu(chan_info->header.type) !=3D 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 =3D le16_to_cpu(chan_info->status); + nxpwifi_dbg(adapter, EVENT, "multi chan operation %s\n", + adapter->usb_mc_status ? "started" : "over"); + + tlv_buf_left -=3D sizeof(struct nxpwifi_ie_types_multi_chan_info); + tlv =3D (struct nxpwifi_ie_types_header *)chan_info->tlv_buffer; + + while (tlv_buf_left >=3D (int)sizeof(struct nxpwifi_ie_types_header)) { + tlv_type =3D le16_to_cpu(tlv->type); + tlv_len =3D le16_to_cpu(tlv->len); + if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) > + tlv_buf_left) { + nxpwifi_dbg(adapter, ERROR, "wrong tlv: tlvLen=3D%d,\t" + "tlvBufLeft=3D%d\n", tlv_len, tlv_buf_left); + break; + } + if (tlv_type !=3D TLV_TYPE_MC_GROUP_INFO) { + nxpwifi_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", + tlv_type); + break; + } + + grp_info =3D (struct nxpwifi_ie_types_mc_group_info *)tlv; + intf_num =3D grp_info->intf_num; + for (i =3D 0; i < intf_num; i++) { + bss_type =3D grp_info->bss_type_numlist[i] >> 4; + bss_num =3D grp_info->bss_type_numlist[i] & BSS_NUM_MASK; + intf_priv =3D 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 -=3D sizeof(struct nxpwifi_ie_types_header) + + tlv_len; + tlv =3D (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=3D%d\n", + priv->bss_role); + return; + } + + tlv_buf_left =3D event_skb->len - sizeof(u32); + tlv =3D (void *)event_skb->data + sizeof(u32); + + while (tlv_buf_left >=3D (int)sizeof(struct nxpwifi_ie_types_header)) { + tlv_type =3D le16_to_cpu(tlv->type); + tlv_len =3D 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=3D%d, tlvBufLeft=3D%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type =3D=3D TLV_TYPE_TX_PAUSE) { + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) + nxpwifi_process_sta_tx_pause(priv, tlv); + else + nxpwifi_process_uap_tx_pause(priv, tlv); + } + + tlv_buf_left -=3D sizeof(struct nxpwifi_ie_types_header) + + tlv_len; + tlv =3D (void *)((u8 *)tlv + tlv_len + + sizeof(struct nxpwifi_ie_types_header)); + } +} + +/* + * Handle BT coexistence event. Parse TLVs and update + * coexistence aggregation window and scan timing parameters. + */ +void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv, + struct sk_buff *event_skb) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D event_skb->len - sizeof(u32); + u8 *cur_ptr =3D event_skb->data + sizeof(u32); + u16 tlv_type, tlv_len; + + while (len >=3D sizeof(struct nxpwifi_ie_types_header)) { + tlv =3D (struct nxpwifi_ie_types_header *)cur_ptr; + tlv_len =3D le16_to_cpu(tlv->len); + tlv_type =3D 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 =3D + (struct nxpwifi_ie_types_btcoex_aggr_win_size *)tlv; + adapter->coex_win_size =3D winsizetlv->coex_win_size; + adapter->coex_tx_win_size =3D + winsizetlv->tx_win_size; + adapter->coex_rx_win_size =3D + winsizetlv->rx_win_size; + nxpwifi_coex_ampdu_rxwinsize(adapter); + nxpwifi_update_ampdu_txwinsize(adapter); + break; + + case TLV_BTCOEX_WL_SCANTIME: + scantlv =3D + (struct nxpwifi_ie_types_btcoex_scan_time *)tlv; + adapter->coex_scan =3D scantlv->coex_scan; + adapter->coex_min_scan_time =3D le16_to_cpu(scantlv->min_scan_time); + adapter->coex_max_scan_time =3D le16_to_cpu(scantlv->max_scan_time); + break; + + default: + break; + } + + len -=3D tlv_len + sizeof(struct nxpwifi_ie_types_header); + cur_ptr +=3D tlv_len + + sizeof(struct nxpwifi_ie_types_header); + } + + nxpwifi_dbg(adapter, INFO, "coex_scan=3D%d min_scan=3D%d coex_win=3D%d, t= x_win=3D%d rx_win=3D%d\n", + adapter->coex_scan, adapter->coex_min_scan_time, + adapter->coex_win_size, adapter->coex_tx_win_size, + adapter->coex_rx_win_size); +} + +/* + * Dispatch station firmware event based on event_cause. + * Looks up the handler in the station event table and invokes it. + */ +int nxpwifi_process_sta_event(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u32 eventcause =3D adapter->event_cause; + int evt, ret =3D 0; + + for (evt =3D 0; evt < ARRAY_SIZE(evt_table_sta); evt++) { + if (eventcause =3D=3D evt_table_sta[evt].event_cause) { + if (evt_table_sta[evt].event_handler) + ret =3D evt_table_sta[evt].event_handler(priv); + break; + } + } + + if (evt =3D=3D 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/wirel= ess/nxp/nxpwifi/uap_cmd.c new file mode 100644 index 000000000000..037d8e76f9fa --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c @@ -0,0 +1,1198 @@ +// 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" + +/* Parse BSS params and append WPA/WPA2 TLVs to the 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 =3D cmd_buf; + u16 cmd_size =3D *param_size; + u8 *tlv =3D *tlv_buf; + + tlv_akmp =3D (struct host_cmd_tlv_akmp *)tlv; + tlv_akmp->header.type =3D cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->header.len =3D cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - + sizeof(struct nxpwifi_ie_types_header)); + tlv_akmp->key_mgmt_operation =3D cpu_to_le16(bss_cfg->key_mgmt_operation); + tlv_akmp->key_mgmt =3D cpu_to_le16(bss_cfg->key_mgmt); + cmd_size +=3D sizeof(struct host_cmd_tlv_akmp); + tlv +=3D sizeof(struct host_cmd_tlv_akmp); + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { + pwk_cipher =3D (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type =3D cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct nxpwifi_ie_types_header)); + pwk_cipher->proto =3D cpu_to_le16(PROTOCOL_WPA); + pwk_cipher->cipher =3D bss_cfg->wpa_cfg.pairwise_cipher_wpa; + cmd_size +=3D sizeof(struct host_cmd_tlv_pwk_cipher); + tlv +=3D sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { + pwk_cipher =3D (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type =3D cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct nxpwifi_ie_types_header)); + pwk_cipher->proto =3D cpu_to_le16(PROTOCOL_WPA2); + pwk_cipher->cipher =3D bss_cfg->wpa_cfg.pairwise_cipher_wpa2; + cmd_size +=3D sizeof(struct host_cmd_tlv_pwk_cipher); + tlv +=3D sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { + gwk_cipher =3D (struct host_cmd_tlv_gwk_cipher *)tlv; + gwk_cipher->header.type =3D cpu_to_le16(TLV_TYPE_GWK_CIPHER); + gwk_cipher->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - + sizeof(struct nxpwifi_ie_types_header)); + gwk_cipher->cipher =3D bss_cfg->wpa_cfg.group_cipher; + cmd_size +=3D sizeof(struct host_cmd_tlv_gwk_cipher); + tlv +=3D sizeof(struct host_cmd_tlv_gwk_cipher); + } + + if (bss_cfg->wpa_cfg.length) { + passphrase =3D (struct host_cmd_tlv_passphrase *)tlv; + passphrase->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); + passphrase->header.len =3D cpu_to_le16(bss_cfg->wpa_cfg.length); + memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, + bss_cfg->wpa_cfg.length); + cmd_size +=3D sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->wpa_cfg.length; + tlv +=3D sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->wpa_cfg.length; + } + + *param_size =3D cmd_size; + *tlv_buf =3D tlv; +} + +/* Parse BSS params and append WEP TLVs to the 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 =3D *param_size; + int i; + u8 *tlv =3D *tlv_buf; + struct nxpwifi_uap_bss_param *bss_cfg =3D cmd_buf; + + for (i =3D 0; i < NUM_WEP_KEYS; i++) { + if (bss_cfg->wep_cfg[i].length && + (bss_cfg->wep_cfg[i].length =3D=3D WLAN_KEY_LEN_WEP40 || + bss_cfg->wep_cfg[i].length =3D=3D WLAN_KEY_LEN_WEP104)) { + wep_key =3D (struct host_cmd_tlv_wep_key *)tlv; + wep_key->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + wep_key->header.len =3D + cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); + wep_key->key_index =3D bss_cfg->wep_cfg[i].key_index; + wep_key->is_default =3D 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 +=3D sizeof(struct nxpwifi_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + tlv +=3D sizeof(struct nxpwifi_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + } + } + + *param_size =3D cmd_size; + *tlv_buf =3D tlv; +} + +/* Parse BSS params and append TLVs to the 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 =3D cmd_buf; + int i; + u16 cmd_size =3D *param_size; + + mac_tlv =3D (struct host_cmd_tlv_mac_addr *)tlv; + mac_tlv->header.type =3D cpu_to_le16(TLV_TYPE_UAP_MAC_ADDRESS); + mac_tlv->header.len =3D cpu_to_le16(ETH_ALEN); + memcpy(mac_tlv->mac_addr, bss_cfg->mac_addr, ETH_ALEN); + cmd_size +=3D sizeof(struct host_cmd_tlv_mac_addr); + tlv +=3D sizeof(struct host_cmd_tlv_mac_addr); + + if (bss_cfg->ssid.ssid_len) { + ssid =3D (struct host_cmd_tlv_ssid *)tlv; + ssid->header.type =3D cpu_to_le16(TLV_TYPE_UAP_SSID); + ssid->header.len =3D cpu_to_le16((u16)bss_cfg->ssid.ssid_len); + memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); + cmd_size +=3D sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->ssid.ssid_len; + tlv +=3D sizeof(struct nxpwifi_ie_types_header) + + bss_cfg->ssid.ssid_len; + + bcast_ssid =3D (struct host_cmd_tlv_bcast_ssid *)tlv; + bcast_ssid->header.type =3D cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); + bcast_ssid->header.len =3D + cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); + bcast_ssid->bcast_ctl =3D bss_cfg->bcast_ssid_ctl; + cmd_size +=3D sizeof(struct host_cmd_tlv_bcast_ssid); + tlv +=3D sizeof(struct host_cmd_tlv_bcast_ssid); + } + if (bss_cfg->rates[0]) { + tlv_rates =3D (struct host_cmd_tlv_rates *)tlv; + tlv_rates->header.type =3D cpu_to_le16(TLV_TYPE_UAP_RATES); + + for (i =3D 0; i < NXPWIFI_SUPPORTED_RATES && bss_cfg->rates[i]; + i++) + tlv_rates->rates[i] =3D bss_cfg->rates[i]; + + tlv_rates->header.len =3D cpu_to_le16(i); + cmd_size +=3D sizeof(struct host_cmd_tlv_rates) + i; + tlv +=3D sizeof(struct host_cmd_tlv_rates) + i; + } + if (bss_cfg->channel && + (((bss_cfg->band_cfg & BIT(0)) =3D=3D BAND_CONFIG_BG && + bss_cfg->channel <=3D MAX_CHANNEL_BAND_BG) || + ((bss_cfg->band_cfg & BIT(0)) =3D=3D BAND_CONFIG_A && + bss_cfg->channel <=3D MAX_CHANNEL_BAND_A))) { + chan_band =3D (struct host_cmd_tlv_channel_band *)tlv; + chan_band->header.type =3D cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + chan_band->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - + sizeof(struct nxpwifi_ie_types_header)); + chan_band->band_config =3D bss_cfg->band_cfg; + chan_band->channel =3D bss_cfg->channel; + cmd_size +=3D sizeof(struct host_cmd_tlv_channel_band); + tlv +=3D sizeof(struct host_cmd_tlv_channel_band); + } + if (bss_cfg->beacon_period >=3D MIN_BEACON_PERIOD && + bss_cfg->beacon_period <=3D MAX_BEACON_PERIOD) { + beacon_period =3D (struct host_cmd_tlv_beacon_period *)tlv; + beacon_period->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); + beacon_period->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - + sizeof(struct nxpwifi_ie_types_header)); + beacon_period->period =3D cpu_to_le16(bss_cfg->beacon_period); + cmd_size +=3D sizeof(struct host_cmd_tlv_beacon_period); + tlv +=3D sizeof(struct host_cmd_tlv_beacon_period); + } + if (bss_cfg->dtim_period >=3D MIN_DTIM_PERIOD && + bss_cfg->dtim_period <=3D MAX_DTIM_PERIOD) { + dtim_period =3D (struct host_cmd_tlv_dtim_period *)tlv; + dtim_period->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + dtim_period->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - + sizeof(struct nxpwifi_ie_types_header)); + dtim_period->period =3D bss_cfg->dtim_period; + cmd_size +=3D sizeof(struct host_cmd_tlv_dtim_period); + tlv +=3D sizeof(struct host_cmd_tlv_dtim_period); + } + if (bss_cfg->rts_threshold <=3D NXPWIFI_RTS_MAX_VALUE) { + rts_threshold =3D (struct host_cmd_tlv_rts_threshold *)tlv; + rts_threshold->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); + rts_threshold->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + rts_threshold->rts_thr =3D cpu_to_le16(bss_cfg->rts_threshold); + cmd_size +=3D sizeof(struct host_cmd_tlv_frag_threshold); + tlv +=3D sizeof(struct host_cmd_tlv_frag_threshold); + } + if (bss_cfg->frag_threshold >=3D NXPWIFI_FRAG_MIN_VALUE && + bss_cfg->frag_threshold <=3D NXPWIFI_FRAG_MAX_VALUE) { + frag_threshold =3D (struct host_cmd_tlv_frag_threshold *)tlv; + frag_threshold->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); + frag_threshold->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - + sizeof(struct nxpwifi_ie_types_header)); + frag_threshold->frag_thr =3D cpu_to_le16(bss_cfg->frag_threshold); + cmd_size +=3D sizeof(struct host_cmd_tlv_frag_threshold); + tlv +=3D sizeof(struct host_cmd_tlv_frag_threshold); + } + if (bss_cfg->retry_limit <=3D NXPWIFI_RETRY_LIMIT) { + retry_limit =3D (struct host_cmd_tlv_retry_limit *)tlv; + retry_limit->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + retry_limit->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - + sizeof(struct nxpwifi_ie_types_header)); + retry_limit->limit =3D (u8)bss_cfg->retry_limit; + cmd_size +=3D sizeof(struct host_cmd_tlv_retry_limit); + tlv +=3D 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 <=3D WLAN_AUTH_SHARED_KEY || + bss_cfg->auth_mode =3D=3D NXPWIFI_AUTH_MODE_AUTO) { + auth_type =3D (struct host_cmd_tlv_auth_type *)tlv; + auth_type->header.type =3D cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_type->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - + sizeof(struct nxpwifi_ie_types_header)); + auth_type->auth_type =3D (u8)bss_cfg->auth_mode; + auth_type->pwe_derivation =3D 0; + auth_type->transition_disable =3D 0; + cmd_size +=3D sizeof(struct host_cmd_tlv_auth_type); + tlv +=3D sizeof(struct host_cmd_tlv_auth_type); + } + if (bss_cfg->protocol) { + encrypt_protocol =3D (struct host_cmd_tlv_encrypt_protocol *)tlv; + encrypt_protocol->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); + encrypt_protocol->header.len =3D + cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) + - sizeof(struct nxpwifi_ie_types_header)); + encrypt_protocol->proto =3D cpu_to_le16(bss_cfg->protocol); + cmd_size +=3D sizeof(struct host_cmd_tlv_encrypt_protocol); + tlv +=3D sizeof(struct host_cmd_tlv_encrypt_protocol); + } + + if (bss_cfg->ht_cap.cap_info) { + htcap =3D (struct nxpwifi_ie_types_htcap *)tlv; + htcap->header.type =3D cpu_to_le16(WLAN_EID_HT_CAPABILITY); + htcap->header.len =3D + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + htcap->ht_cap.cap_info =3D bss_cfg->ht_cap.cap_info; + htcap->ht_cap.ampdu_params_info =3D + 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 =3D + bss_cfg->ht_cap.extended_ht_cap_info; + htcap->ht_cap.tx_BF_cap_info =3D bss_cfg->ht_cap.tx_BF_cap_info; + htcap->ht_cap.antenna_selection_info =3D + bss_cfg->ht_cap.antenna_selection_info; + cmd_size +=3D sizeof(struct nxpwifi_ie_types_htcap); + tlv +=3D sizeof(struct nxpwifi_ie_types_htcap); + } + + if (bss_cfg->wmm_info.qos_info !=3D 0xFF) { + wmm_cap =3D (struct nxpwifi_ie_types_wmmcap *)tlv; + wmm_cap->header.type =3D cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); + wmm_cap->header.len =3D cpu_to_le16(sizeof(wmm_cap->wmm_info)); + memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, + sizeof(wmm_cap->wmm_info)); + cmd_size +=3D sizeof(struct nxpwifi_ie_types_wmmcap); + tlv +=3D sizeof(struct nxpwifi_ie_types_wmmcap); + } + + if (bss_cfg->sta_ao_timer) { + ao_timer =3D (struct host_cmd_tlv_ageout_timer *)tlv; + ao_timer->header.type =3D cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); + ao_timer->header.len =3D cpu_to_le16(sizeof(*ao_timer) - + sizeof(struct nxpwifi_ie_types_header)); + ao_timer->sta_ao_timer =3D cpu_to_le32(bss_cfg->sta_ao_timer); + cmd_size +=3D sizeof(*ao_timer); + tlv +=3D sizeof(*ao_timer); + } + + if (bss_cfg->power_constraint) { + pwr_ct =3D (void *)tlv; + pwr_ct->header.type =3D cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT); + pwr_ct->header.len =3D cpu_to_le16(sizeof(u8)); + pwr_ct->constraint =3D bss_cfg->power_constraint; + cmd_size +=3D sizeof(*pwr_ct); + tlv +=3D sizeof(*pwr_ct); + } + + if (bss_cfg->ps_sta_ao_timer) { + ps_ao_timer =3D (struct host_cmd_tlv_ageout_timer *)tlv; + ps_ao_timer->header.type =3D + cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); + ps_ao_timer->header.len =3D cpu_to_le16(sizeof(*ps_ao_timer) - + sizeof(struct nxpwifi_ie_types_header)); + ps_ao_timer->sta_ao_timer =3D + cpu_to_le32(bss_cfg->ps_sta_ao_timer); + cmd_size +=3D sizeof(*ps_ao_timer); + tlv +=3D sizeof(*ps_ao_timer); + } + + *param_size =3D cmd_size; + + return 0; +} + +/* Parse custom IEs and write them to the command buffer. */ +static int nxpwifi_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_s= ize) +{ + struct nxpwifi_ie_list *ap_ie =3D cmd_buf; + struct nxpwifi_ie_types_header *tlv_ie =3D (void *)tlv; + + if (!ap_ie || !ap_ie->len) + return -EINVAL; + + *ie_size +=3D le16_to_cpu(ap_ie->len) + + sizeof(struct nxpwifi_ie_types_header); + + tlv_ie->type =3D cpu_to_le16(TLV_TYPE_MGMT_IE); + tlv_ie->len =3D ap_ie->len; + tlv +=3D sizeof(struct nxpwifi_ie_types_header); + + memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); + + return 0; +} + +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 =3D 0; + + cmd->command =3D cpu_to_le16(HOST_CMD_UAP_SYS_CONFIG); + cmd_size =3D (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); + sys_cfg =3D &cmd->params.uap_sys_config; + sys_cfg->action =3D cpu_to_le16(cmd_action); + tlv =3D sys_cfg->tlv; + + switch (cmd_type) { + case UAP_BSS_PARAMS_I: + param_size =3D cmd_size; + ret =3D nxpwifi_uap_bss_param_prepare(tlv, data_buf, ¶m_size); + if (ret) + return ret; + cmd->size =3D cpu_to_le16(param_size); + break; + case UAP_CUSTOM_IE_I: + ie_size =3D cmd_size; + ret =3D nxpwifi_uap_custom_ie_prepare(tlv, data_buf, &ie_size); + if (ret) + return ret; + cmd->size =3D 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 =3D cpu_to_le16(HOST_CMD_UAP_BSS_START); + size =3D S_DS_GEN; + + tlv =3D (struct nxpwifi_ie_types_host_mlme *)((u8 *)cmd + size); + tlv->header.type =3D cpu_to_le16(TLV_TYPE_HOST_MLME); + tlv->header.len =3D cpu_to_le16(sizeof(tlv->host_mlme)); + tlv->host_mlme =3D 1; + size +=3D sizeof(struct nxpwifi_ie_types_host_mlme); + + cmd->size =3D 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 =3D priv->adapter; + + adapter->tx_lock_flag =3D false; + adapter->pps_uapsd_mode =3D false; + adapter->delay_null_pkt =3D false; + priv->bss_started =3D 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 =3D 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 =3D + &resp->params.sta_list; + struct nxpwifi_ie_types_sta_info *sta_info =3D (void *)&sta_list->tlv; + int i; + struct nxpwifi_sta_node *sta_node; + + rcu_read_lock(); + for (i =3D 0; i < (le16_to_cpu(sta_list->sta_count)); i++) { + sta_node =3D nxpwifi_get_sta_entry(priv, sta_info->mac); + if (unlikely(!sta_node)) + continue; + + sta_node->stats.rssi =3D sta_info->rssi; + sta_info++; + } + rcu_read_unlock(); + return 0; +} + +/* Build AP deauth command for the given MAC address. */ +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 =3D &cmd->params.sta_deauth; + u8 *mac =3D (u8 *)data_buf; + + cmd->command =3D cpu_to_le16(HOST_CMD_UAP_STA_DEAUTH); + memcpy(sta_deauth->mac, mac, ETH_ALEN); + sta_deauth->reason =3D cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + cmd->size =3D 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); +} + +/* Build AP 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 =3D &cmd->params.sta_info; + struct nxpwifi_sta_info *add_sta =3D (struct nxpwifi_sta_info *)data_buf; + struct station_parameters *params =3D 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 =3D cpu_to_le16(HOST_CMD_ADD_NEW_STATION); + new_sta->action =3D cpu_to_le16(cmd_action); + cmd_size =3D sizeof(struct host_cmd_ds_add_station) + S_DS_GEN; + + if (cmd_action =3D=3D HOST_ACT_ADD_STA) + sta_ptr =3D nxpwifi_add_sta_entry(priv, add_sta->peer_mac); + else + sta_ptr =3D nxpwifi_get_sta_entry_rcu(priv, add_sta->peer_mac); + + if (!sta_ptr) + return -EINVAL; + + memcpy(new_sta->peer_mac, add_sta->peer_mac, ETH_ALEN); + + if (cmd_action =3D=3D HOST_ACT_REMOVE_STA) { + cmd->size =3D cpu_to_le16(cmd_size); + return 0; + } + + new_sta->aid =3D cpu_to_le16(params->aid); + new_sta->listen_interval =3D cpu_to_le32(params->listen_interval); + new_sta->cap_info =3D cpu_to_le16(params->capability); + + pos =3D new_sta->tlv; + cmd_end =3D (u8 *)cmd; + cmd_end +=3D (NXPWIFI_SIZE_OF_CMD_BUFFER - 1); + + if (params->sta_flags_set & NL80211_STA_FLAG_WME) + sta_ptr->is_wmm_enabled =3D 1; + sta_flag =3D (struct nxpwifi_ie_types_sta_flag *)pos; + sta_flag->header.type =3D cpu_to_le16(TLV_TYPE_UAP_STA_FLAGS); + sta_flag->header.len =3D cpu_to_le16(sizeof(__le32)); + sta_flag->sta_flags =3D cpu_to_le32(params->sta_flags_set); + pos +=3D sizeof(struct nxpwifi_ie_types_sta_flag); + cmd_size +=3D sizeof(struct nxpwifi_ie_types_sta_flag); + + if (params->ext_capab_len) { + u8 *data =3D (u8 *)params->ext_capab; + u16 len =3D params->ext_capab_len; + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_EXT_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + } + + if (params->link_sta_params.supported_rates_len) { + u8 *data =3D (u8 *)params->link_sta_params.supported_rates; + u16 len =3D params->link_sta_params.supported_rates_len; + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_SUPP_RATES, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + } + + if (params->uapsd_queues || params->max_sp) { + u8 qos_capability =3D params->uapsd_queues | (params->max_sp << 5); + u8 *data =3D &qos_capability; + u16 len =3D sizeof(u8); + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_QOS_CAPA, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + sta_ptr->is_wmm_enabled =3D 1; + } + + if (params->link_sta_params.ht_capa) { + u8 *data =3D (u8 *)params->link_sta_params.ht_capa; + u16 len =3D sizeof(struct ieee80211_ht_cap); + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_HT_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + sta_ptr->is_11n_enabled =3D 1; + sta_ptr->max_amsdu =3D + 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 =3D (u8 *)params->link_sta_params.vht_capa; + u16 len =3D sizeof(struct ieee80211_vht_cap); + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_VHT_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + sta_ptr->is_11ac_enabled =3D 1; + } + + if (params->link_sta_params.opmode_notif_used) { + u8 *data =3D ¶ms->link_sta_params.opmode_notif; + u16 len =3D sizeof(u8); + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_OPMODE_NOTIF, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + } + + if (params->link_sta_params.he_capa_len) { + u8 *data =3D (u8 *)params->link_sta_params.he_capa; + u16 len =3D params->link_sta_params.he_capa_len; + + tlv_len =3D nxpwifi_append_data_tlv(WLAN_EID_EXT_HE_CAPABILITY, + data, len, pos, cmd_end); + if (!tlv_len) + return -EINVAL; + pos +=3D tlv_len; + cmd_size +=3D tlv_len; + sta_ptr->is_11ax_enabled =3D 1; + } + + for (i =3D 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled || sta_ptr->is_11ax_enabled) + sta_ptr->ampdu_sta[i] =3D + priv->aggr_prio_tbl[i].ampdu_user; + else + sta_ptr->ampdu_sta[i] =3D BA_STREAM_NOT_ALLOWED; + } + + memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + + cmd->size =3D cpu_to_le16(cmd_size); + + return 0; +} + +static const struct nxpwifi_cmd_entry cmd_table_uap[] =3D { + {.cmd_no =3D HOST_CMD_APCMD_SYS_RESET, + .prepare_cmd =3D nxpwifi_cmd_fill_head_only, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_UAP_SYS_CONFIG, + .prepare_cmd =3D nxpwifi_cmd_uap_sys_config, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_UAP_BSS_START, + .prepare_cmd =3D nxpwifi_cmd_uap_bss_start, + .cmd_resp =3D nxpwifi_ret_uap_bss_start}, + {.cmd_no =3D HOST_CMD_UAP_BSS_STOP, + .prepare_cmd =3D nxpwifi_cmd_fill_head_only, + .cmd_resp =3D nxpwifi_ret_uap_bss_stop}, + {.cmd_no =3D HOST_CMD_APCMD_STA_LIST, + .prepare_cmd =3D nxpwifi_cmd_fill_head_only, + .cmd_resp =3D nxpwifi_ret_apcmd_sta_list}, + {.cmd_no =3D HOST_CMD_UAP_STA_DEAUTH, + .prepare_cmd =3D nxpwifi_cmd_uap_sta_deauth, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_CHAN_REPORT_REQUEST, + .prepare_cmd =3D nxpwifi_cmd_uap_chan_report_request, + .cmd_resp =3D NULL}, + {.cmd_no =3D HOST_CMD_ADD_NEW_STATION, + .prepare_cmd =3D nxpwifi_cmd_uap_add_new_station, + .cmd_resp =3D NULL}, +}; + +/* Prepare AP commands and dispatch to per-cmd builders before sending to = firmware. */ +int nxpwifi_uap_prepare_cmd(struct nxpwifi_private *priv, + struct cmd_ctrl_node *cmd_node, + u16 cmd_action, u32 type) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u16 cmd_no =3D cmd_node->cmd_no; + struct host_cmd_ds_command *cmd =3D + (struct host_cmd_ds_command *)cmd_node->skb->data; + void *data_buf =3D cmd_node->data_buf; + int i, ret =3D -EINVAL; + + for (i =3D 0; i < ARRAY_SIZE(cmd_table_uap); i++) { + if (cmd_no =3D=3D cmd_table_uap[i].cmd_no) { + if (cmd_table_uap[i].prepare_cmd) + ret =3D cmd_table_uap[i].prepare_cmd(priv, cmd, + cmd_no, + data_buf, + cmd_action, + type); + cmd_node->cmd_resp =3D cmd_table_uap[i].cmd_resp; + break; + } + } + + if (i =3D=3D 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; +} + +/* Translate cfg80211_ap_settings security into bss_config for firmware. */ +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 =3D PROTOCOL_NO_SECURITY; + bss_config->key_mgmt =3D KEY_MGMT_NONE; + bss_config->wpa_cfg.length =3D 0; + priv->sec_info.wep_enabled =3D 0; + priv->sec_info.wpa_enabled =3D 0; + priv->sec_info.wpa2_enabled =3D 0; + + return 0; + } + + switch (params->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + bss_config->auth_mode =3D WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + bss_config->auth_mode =3D WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + bss_config->auth_mode =3D WLAN_AUTH_LEAP; + break; + default: + bss_config->auth_mode =3D NXPWIFI_AUTH_MODE_AUTO; + break; + } + + bss_config->key_mgmt_operation |=3D KEY_MGMT_ON_HOST; + + bss_config->protocol =3D 0; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->protocol |=3D PROTOCOL_WPA; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->protocol |=3D PROTOCOL_WPA2; + + bss_config->key_mgmt =3D 0; + for (i =3D 0; i < params->crypto.n_akm_suites; i++) { + switch (params->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + bss_config->key_mgmt |=3D KEY_MGMT_EAP; + break; + case WLAN_AKM_SUITE_PSK: + bss_config->key_mgmt |=3D KEY_MGMT_PSK; + break; + case WLAN_AKM_SUITE_PSK_SHA256: + bss_config->key_mgmt |=3D KEY_MGMT_PSK_SHA256; + break; + case WLAN_AKM_SUITE_OWE: + bss_config->key_mgmt |=3D KEY_MGMT_OWE; + break; + case WLAN_AKM_SUITE_SAE: + bss_config->key_mgmt |=3D KEY_MGMT_SAE; + break; + default: + break; + } + } + + for (i =3D 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 |=3D + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |=3D + CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |=3D + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |=3D + 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 =3D PROTOCOL_STATIC_WEP; + bss_config->key_mgmt =3D KEY_MGMT_NONE; + bss_config->wpa_cfg.length =3D 0; + + for (i =3D 0; i < NUM_WEP_KEYS; i++) { + wep_key =3D priv->wep_key[i]; + bss_config->wep_cfg[i].key_index =3D i; + + if (priv->wep_key_curr_index =3D=3D i) + bss_config->wep_cfg[i].is_default =3D 1; + else + bss_config->wep_cfg[i].is_default =3D 0; + + bss_config->wep_cfg[i].length =3D + 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 =3D CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + bss_config->wpa_cfg.group_cipher =3D CIPHER_AES_CCMP; + break; + default: + break; + } + + return 0; +} + +/* Update 11n HT params from beacon and fill bss_config. */ +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 =3D 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 =3D + cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP); + priv->ap_11n_enabled =3D 1; + } else { + memset(&bss_cfg->ht_cap, 0, sizeof(struct ieee80211_ht_cap)); + bss_cfg->ht_cap.cap_info =3D cpu_to_le16(NXPWIFI_DEF_HT_CAP); + bss_cfg->ht_cap.ampdu_params_info =3D NXPWIFI_DEF_AMPDU; + } +} + +/* Update 11ac VHT params from beacon and fill bss_config. */ +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 =3D 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 =3D 1; + } else { + priv->ap_11ac_enabled =3D 0; + } +} + +/* Extract TPC request from beacon and set power_constraint. */ +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 =3D cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail, + params->beacon.tail_len); + if (tpc_ie) + bss_cfg->power_constraint =3D *(tpc_ie + 2); + else + bss_cfg->power_constraint =3D 0; +} + +/* Enable VHT only when VHT IE is present; 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 =3D priv->adapter; + struct nxpwifi_11ac_vht_cfg vht_cfg; + + vht_cfg.band_config =3D VHT_CFG_5GHZ; + vht_cfg.cap_info =3D adapter->hw_dot_11ac_dev_cap; + + if (!ap_11ac_enable) { + vht_cfg.mcs_tx_set =3D DISABLE_VHT_MCS_SET; + vht_cfg.mcs_rx_set =3D DISABLE_VHT_MCS_SET; + } else { + vht_cfg.mcs_tx_set =3D DEFAULT_VHT_MCS_SET; + vht_cfg.mcs_rx_set =3D DEFAULT_VHT_MCS_SET; + } + + vht_cfg.misc_config =3D VHT_CAP_UAP_ONLY; + + if (ap_11ac_enable && width >=3D NL80211_CHAN_WIDTH_80) + vht_cfg.misc_config |=3D 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 =3D priv->adapter; + u8 band =3D bss_cfg->band_cfg & BAND_CFG_CHAN_BAND_MASK; + + if (band =3D=3D BAND_2GHZ && + !(adapter->fw_bands & BAND_GAX)) + return false; + + if (band =3D=3D 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 =3D bss_cfg->band_cfg & BAND_CFG_CHAN_BAND_MASK; + const struct element *he_cap; + int ret; + + if (band =3D=3D BAND_2GHZ) + ax_cfg.band =3D BIT(0); + else if (band =3D=3D BAND_5GHZ) + ax_cfg.band =3D BIT(1); + else + return -EINVAL; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_11AX_CFG, + HOST_ACT_GEN_GET, 0, &ax_cfg, true); + if (ret) + return ret; + + he_cap =3D 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 =3D he_cap->id; + ax_cfg.he_cap_cfg.len =3D he_cap->datalen; + if (params->twt_responder =3D=3D 0) { + struct nxpwifi_11ax_he_cap_cfg *he_cap_cfg =3D + (struct nxpwifi_11ax_he_cap_cfg *)he_cap; + + he_cap_cfg->cap_elem.mac_cap_info[0] &=3D + ~HE_MAC_CAP_TWT_RESP_SUPPORT; + } + 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 =3D=3D 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); +} + +/* Copy supported rates from beacon into bss_config. */ +void +nxpwifi_set_uap_rates(struct nxpwifi_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct element *rate_ie; + int var_offset =3D offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos =3D params->beacon.head + var_offset; + int len =3D params->beacon.head_len - var_offset; + u8 rate_len =3D 0; + + rate_ie =3D (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 =3D rate_ie->datalen; + } + + rate_ie =3D (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); + } +} + +/* Seed bss_config with sentinel values so firmware ignores unset fields. = */ +void nxpwifi_set_sys_config_invalid_data(struct nxpwifi_uap_bss_param *con= fig) +{ + config->bcast_ssid_ctl =3D 0x7F; + config->radio_ctl =3D 0x7F; + config->dtim_period =3D 0x7F; + config->beacon_period =3D 0x7FFF; + config->auth_mode =3D 0x7F; + config->rts_threshold =3D 0x7FFF; + config->frag_threshold =3D 0x7FFF; + config->retry_limit =3D 0x7F; + config->qos_info =3D 0xFF; +} + +/* Parse WMM params from cfg80211_ap_settings and update bss_config. */ +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[] =3D {0x00, 0x50, 0xf2, 0x02}; + + vendor_ie =3D cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + params->beacon.tail, + params->beacon.tail_len); + if (vendor_ie) { + wmm_ie =3D 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 =3D 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 =3D NXPWIFI_WMM_SUBTYPE; + bss_cfg->wmm_info.version =3D NXPWIFI_WMM_VERSION; + priv->wmm_enabled =3D 0; + } + + bss_cfg->qos_info =3D 0x00; +} + +/* Enable 11d when country IE is present. */ +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 =3D 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 =3D 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 =3D 0, old_bands =3D priv->config_bands; + + priv->bss_chandef =3D chandef; + + bss_cfg->channel =3D + ieee80211_frequency_to_channel(chandef.chan->center_freq); + + nxpwifi_convert_chan_to_band_cfg(priv, &bss_cfg->band_cfg, &chandef); + + /* Set appropriate bands */ + if (chandef.chan->band =3D=3D NL80211_BAND_2GHZ) { + config_bands =3D BAND_B | BAND_G; + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |=3D BAND_GN | BAND_GAX; + } else { + config_bands =3D BAND_A; + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |=3D BAND_AN; + if (chandef.width > NL80211_CHAN_WIDTH_40) + config_bands |=3D BAND_AAC | BAND_AAX; + } + + priv->config_bands =3D config_bands; + + if (old_bands !=3D config_bands) { + if (nxpwifi_band_to_radio_type(priv->config_bands) =3D=3D + 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 =3D 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 =3D 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 |=3D HOST_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &=3D ~HOST_ACT_MAC_WEP_ENABLE; + + ret =3D 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/wir= eless/nxp/nxpwifi/uap_event.c new file mode 100644 index 000000000000..b2a8e10de04a --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/uap_event.c @@ -0,0 +1,488 @@ +// 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 =3D priv->adapter; + + if (!adapter->pps_uapsd_mode && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode =3D true; + nxpwifi_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag =3D false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (nxpwifi_check_last_packet_indication(priv)) { + if (adapter->data_sent) { + adapter->ps_state =3D PS_STATE_AWAKE; + adapter->pm_wakeup_card_req =3D false; + adapter->pm_wakeup_fw_try =3D false; + } else { + if (!nxpwifi_send_null_packet + (priv, + NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET | + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state =3D PS_STATE_SLEEP; + } + + return 0; + } + } + + adapter->ps_state =3D PS_STATE_AWAKE; + adapter->pm_wakeup_card_req =3D false; + adapter->pm_wakeup_fw_try =3D false; + + return 0; +} + +static int +nxpwifi_uap_event_ps_sleep(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + adapter->ps_state =3D 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 =3D priv->adapter; + u8 *deauth_mac; + + deauth_mac =3D 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); + + return 0; +} + +static int +nxpwifi_uap_event_sta_assoc(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct station_info *sinfo; + struct nxpwifi_assoc_event *event; + struct nxpwifi_sta_node *node; + int len, i; + + sinfo =3D kzalloc(sizeof(*sinfo), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + + event =3D (struct nxpwifi_assoc_event *) + (adapter->event_body + NXPWIFI_UAP_EVENT_EXTRA_HEADER); + if (le16_to_cpu(event->type) =3D=3D TLV_TYPE_UAP_MGMT_FRAME) { + len =3D -1; + + if (ieee80211_is_assoc_req(event->frame_control)) + len =3D 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 =3D ETH_ALEN; + + if (len !=3D -1) { + sinfo->assoc_req_ies =3D &event->data[len]; + len =3D (u8 *)sinfo->assoc_req_ies - + (u8 *)&event->frame_control; + sinfo->assoc_req_ies_len =3D + le16_to_cpu(event->len) - (u16)len; + } + } + cfg80211_new_sta(priv->netdev, event->sta_addr, sinfo, + GFP_KERNEL); + + node =3D 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 =3D 0; i < MAX_NUM_TID; i++) { + if (node->is_11n_enabled || node->is_11ax_enabled) + node->ampdu_sta[i] =3D + priv->aggr_prio_tbl[i].ampdu_user; + else + node->ampdu_sta[i] =3D 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 =3D NULL; + int mask =3D IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + + priv->wmm_enabled =3D false; + skb_pull(event, NXPWIFI_BSS_START_EVT_FIX_SIZE); + evt_len =3D event->len; + curr =3D 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 >=3D sizeof(tlv_hdr->header))) { + tlv_hdr =3D (struct nxpwifi_ie_types_data *)curr; + tlv_len =3D 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 =3D true; + break; + + case WLAN_EID_VHT_CAPABILITY: + priv->ap_11ac_enabled =3D true; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* + * Point the regular IEEE element 2 bytes into the NXP element + * and setup the IEEE element type and length byte fields + */ + wmm_param_ie =3D (void *)(curr + 2); + wmm_param_ie->len =3D (u8)tlv_len; + wmm_param_ie->element_id =3D + 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 =3D true; + nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie); + break; + + default: + break; + } + + curr +=3D (tlv_len + sizeof(tlv_hdr->header)); + evt_len -=3D (tlv_len + sizeof(tlv_hdr->header)); + } + + return 0; +} + +static int +nxpwifi_uap_event_bss_start(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + priv->port_open =3D 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 =3D 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 =3D 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 =3D priv->adapter; + struct host_cmd_ds_11n_batimeout *ba_timeout; + + if (priv->media_connected) { + ba_timeout =3D (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 =3D priv->adapter; + u16 ctrl; + + ctrl =3D 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 =3D + 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 =3D false; + priv->port_open =3D 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 =3D true; + priv->port_open =3D 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 =3D 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 =3D 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 =3D 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 =3D priv->adapter; + void *buf =3D adapter->event_skb->data; + int ret =3D 0; + + if (adapter->ext_scan) + ret =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D priv->adapter; + + return nxpwifi_process_vdll_event(priv, adapter->event_skb); +} + +static const struct nxpwifi_evt_entry evt_table_uap[] =3D { + {.event_cause =3D EVENT_PS_AWAKE, + .event_handler =3D nxpwifi_uap_event_ps_awake}, + {.event_cause =3D EVENT_PS_SLEEP, + .event_handler =3D nxpwifi_uap_event_ps_sleep}, + {.event_cause =3D EVENT_UAP_STA_DEAUTH, + .event_handler =3D nxpwifi_uap_event_sta_deauth}, + {.event_cause =3D EVENT_UAP_STA_ASSOC, + .event_handler =3D nxpwifi_uap_event_sta_assoc}, + {.event_cause =3D EVENT_UAP_BSS_START, + .event_handler =3D nxpwifi_uap_event_bss_start}, + {.event_cause =3D EVENT_ADDBA, + .event_handler =3D nxpwifi_uap_event_addba}, + {.event_cause =3D EVENT_DELBA, + .event_handler =3D nxpwifi_uap_event_delba}, + {.event_cause =3D EVENT_BA_STREAM_TIEMOUT, + .event_handler =3D nxpwifi_uap_event_ba_stream_timeout}, + {.event_cause =3D EVENT_AMSDU_AGGR_CTRL, + .event_handler =3D nxpwifi_uap_event_amsdu_aggr_ctrl}, + {.event_cause =3D EVENT_UAP_BSS_IDLE, + .event_handler =3D nxpwifi_uap_event_bss_idle}, + {.event_cause =3D EVENT_UAP_BSS_ACTIVE, + .event_handler =3D nxpwifi_uap_event_bss_active}, + {.event_cause =3D EVENT_UAP_MIC_COUNTERMEASURES, + .event_handler =3D nxpwifi_uap_event_mic_countermeasures}, + {.event_cause =3D EVENT_RADAR_DETECTED, + .event_handler =3D nxpwifi_uap_event_radar_detected}, + {.event_cause =3D EVENT_CHANNEL_REPORT_RDY, + .event_handler =3D nxpwifi_uap_event_channel_report_rdy}, + {.event_cause =3D EVENT_TX_DATA_PAUSE, + .event_handler =3D nxpwifi_uap_event_tx_data_pause}, + {.event_cause =3D EVENT_EXT_SCAN_REPORT, + .event_handler =3D nxpwifi_uap_event_ext_scan_report}, + {.event_cause =3D EVENT_RXBA_SYNC, + .event_handler =3D nxpwifi_uap_event_rxba_sync}, + {.event_cause =3D EVENT_REMAIN_ON_CHAN_EXPIRED, + .event_handler =3D nxpwifi_uap_event_remain_on_chan_expired}, + {.event_cause =3D EVENT_MULTI_CHAN_INFO, + .event_handler =3D nxpwifi_uap_event_multi_chan_info}, + {.event_cause =3D EVENT_TX_STATUS_REPORT, + .event_handler =3D nxpwifi_uap_event_tx_status_report}, + {.event_cause =3D EVENT_BT_COEX_WLAN_PARA_CHANGE, + .event_handler =3D nxpwifi_uap_event_bt_coex_wlan_para_change}, + {.event_cause =3D EVENT_VDLL_IND, + .event_handler =3D nxpwifi_uap_event_vdll_ind}, +}; + +/* Handle AP=E2=80=91interface events by dispatching them to event=E2=80= =91specific routines. */ +int nxpwifi_process_uap_event(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u32 eventcause =3D adapter->event_cause; + int evt, ret =3D 0; + + for (evt =3D 0; evt < ARRAY_SIZE(evt_table_uap); evt++) { + if (eventcause =3D=3D evt_table_uap[evt].event_cause) { + if (evt_table_uap[evt].event_handler) + ret =3D evt_table_uap[evt].event_handler(priv); + break; + } + } + + if (evt =3D=3D 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; +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from GVXPR05CU001.outbound.protection.outlook.com (mail-swedencentralazon11013020.outbound.protection.outlook.com [52.101.83.20]) (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 A4A0A429830; Wed, 4 Feb 2026 18:05:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.83.20 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228346; cv=fail; b=p4PvmuIH7uAaYTYllcaXhNiZtTsLxTzzoMm8JYvJD+KMDg9SgMYdXZPNB13p8LSii7ccQpQHNPq5++fGnsVoEfNUTeE3LB4/4esqSdwUFqqS+yzDTPQDJuAbsQyf3Z8JKiWn3FPEoKUkvfblTbrHkGnTrzd4n6tes2dXjV4f/Fg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228346; c=relaxed/simple; bh=fQT0uZTO3am2JtwgZNbsRmDKxLW4DFrMu5qSlAKRIUs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=Gbk5m2L62nc6lX/XUYYFjV/uuSCKG8tIvYb8rgRGA48Mz3qGTG0ALcBQ91GC2pfq0rPdJDV03VwYpHjY9XNVZxm2xQZ++RdMv4fMisP4s3CVE4hNhQHD4z38txzqOf1pL9z3scshzT/u3CQeL29UNc3Yw81udXUSndxnrqhuLlY= 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=SnakFC35; arc=fail smtp.client-ip=52.101.83.20 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="SnakFC35" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=oXBe3up+Jlmr6L+TW4cc508i4yJgek7+qXKo7f+VNEI4Gdolt6AYtugWUhHUDrx15/wSUlHhNRanYqf1jhPMqZx+BJ0Bx51hCcFQxlL2cZpdy+cI++q1w2L2nwHAUdKubaDaIGHzwHV1W5dQFo5zo6Q24lkDw2jR7dsqkwxVbS7YaTL39F3o1Kay9CwCaiZ7GUMhC+kprcYHN7aXOSTASm9rPaMFm+V51DXSm2MWqnwaNGOi0UjLghgQ+6R7jKyFVs5WG85xoAbw+6k8LrUWZv8hcLLcFvNv9OWGI347058XV9/Dyw2bpgroZ/2KSDcYKeK7j9kQ4vxdci3nurPIKQ== 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=UB4/rAKKW3BV0l8WmriykKw2Apfie46z2GuxqxzHA5k=; b=S4NC5JprVCD3ZaU7HKS8rPjqUeXbT+LjZjRAZ5BYy6txwds2MtZDUm0MV90O2yKgJXMYHeYwi1WmO8AnljdIItYpUBJunKZA/gZgMOhG5XT3GyUHal3YJMMiG2kY8jbDrlUzQxiGmzuPMW4OsduuUwfCeuj8EcDXPyT8nUapXQ2fZizLke6+rS8sGxUkhorOkZmrlgXZD2VqsIPo+22EbUP/vZegwgzLsKQmli8OUS9TYvme6oFMYqY7de4L0d8QZqpDRgrS2pfjwYN8T+jDyx8gr9ZDJuA1tdleXB2xGdDTlPJsEuBhP59PptwxLGVTq/Z2NR4n04a08eWzRE8ZaQ== 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=UB4/rAKKW3BV0l8WmriykKw2Apfie46z2GuxqxzHA5k=; b=SnakFC35wzLqARv0mkvHff6r7PSLbdmPhKqNXM7xJkjqy9d4737cAv93ebx6kbOhEJSgtKZfuf0oD9dDqJ209VyzYuSxz3eSZT8Bw7huJoRTxKnvecKnsPwW7XqR9oy7/Rc4GJbJuW1h600c2v5YOU/KdzMpRuRVP9J7RCa4B7Ikac3KmWx/25ZJRxOfcax/pQEXev2fU9poW6oehfotY63+XwiWOXsQz7PCiDyl4Bfgzqh6oYgC//HMiKCAG4MrKlZJUYNIUt4uI4wWX6HRlJjVlV0PM/DI+6oBo4J2XHYRlcoVg5h7g7bAkI5WE2e6+Om5LBl3+jlZIObWh9461g== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:44 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:44 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 13/21] wifi: nxpwifi: add data path support for STA and AP modes Date: Thu, 5 Feb 2026 02:03:50 +0800 Message-Id: <20260204180358.632281-14-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: b7dcede0-ef66-4302-870d-08de641803bb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?B?WklkOFcveHlTYWRFYWY2SFpUVTdjSHFYU3pZMmtja1ZiWjEvc3JJOG91VFA1?= =?utf-8?B?dTNpdU1STGZkMHhpb2o5TEI5NzhGUHphZjBOcTdscXpuVkhYLzlOSWhYWmg0?= =?utf-8?B?ZkQ1TnVGc1lneGpwUTZWVXlhUHFma0JKbnpyWE5wTnpEWjRzcDZtZ3dBaWc5?= =?utf-8?B?VXhYbm5nMWNxaWFwUnVNTjJHYkpTUHpQeE9GSDd4SG0wTzA0eUE0S1NsQ1c5?= =?utf-8?B?MEhJT0ZCMFl0anBiY1lWVGlJaG9mV0VUNVJaaHh3S1QrOVN6OWx3Y3p2QVZL?= =?utf-8?B?YXdTNmVmUDE3Rkl5T2RwS1U4bWVCS09RY1BpODhiMElWejNiU0x3YUN1Q0FT?= =?utf-8?B?Z3llNkhBVTFTRmloSkNmUElHWGc1RkU0THl0LytRbjl3YU9kRm5TVCtVVGhi?= =?utf-8?B?dXVDOHlRSEQ5R0pGVmt2SldkY3R6aWNkUFVoNkNTNTBPYkN5bkc3c2hFRlV0?= =?utf-8?B?ZnJLdk9MSC9DTjFUMjZ4ZnFEVU5XTjM1cExsdDYzN3ZrVUgvNm16RS9oTTk2?= =?utf-8?B?ZVY5NEdjSk5DQkNkMlNXUE8rNzdvWmY3SURNSFdBeXFYanZ3aXlyNDN3UHZt?= =?utf-8?B?d29mUFBwMWp3WTlmR0ZyTm92TlE2RCtIOCtLVU0wdDFxZ2pFQlFRZXprMU1J?= =?utf-8?B?SVJjTEdFeDFCa0M5MjNyT1RNSEJQYy9HQVl5ZElCazFVczlQZzZUK3ZVeW9G?= =?utf-8?B?MEFhb1NITVI4SjhLQnBPNm55WGRHTDdZeFRobm54L3djVWxFRDJJbFNBUEtt?= =?utf-8?B?RytOZjA2Tm9ScFB5NkU5c3JraHpFRFpYTWViS21kemNFc3RObmdZY3pja3VT?= =?utf-8?B?cHVzZCtpZkI0bVF4YVpuN3JDNStFY25pVVE3dytaU2ZqaXljeWdoQWtyeE1I?= =?utf-8?B?S1AyUkdUR3BTZlNPc1hxODdPbGpXVjhvZmJ6SHBpRnBQL0N3ZEZTNzdXTFVE?= =?utf-8?B?VE5HT0YyRTYyM0hDSVBIMFp0TVZXMmdaMnhITTVrMCt4bC9IK2QxSUdpOTR4?= =?utf-8?B?Q0VVSXlTV0d0NDEyY3A3eW1HaEhBRUpqNFM0R1M5NE1EaG9iZWgxSkF6Nk52?= =?utf-8?B?WXlYd3JlOXZHQTlVS01qOUZoakV4MGM2eE1NVzZ3QloxK2l4a1hudjluSHlI?= =?utf-8?B?NHJwNnl1WUN1RGx1MjlrZWVJZWhJWFhjVFNmK3ZjRXUxVmZ5aDVsV3lYakpQ?= =?utf-8?B?dHowQlE0Q3BJRlZYZkQxWXNLT1BhYW5IT08yM3kyZ2xIZ014dzRYOTZFWFFL?= =?utf-8?B?eGREWW5SOUx3alNVRmJ6UHJuNHFJcW1JTXNPUW1CSEpPczJjWFhSODR0Ti9B?= =?utf-8?B?WVhnRk1NVmgxZm9oY1FTRWljSlUxOFR2NVRWTFNYM0k5eERjR3g2eU5xSnB1?= =?utf-8?B?RVlZekIrYkd4dzVjQjNKU1RDczlHS3VBZjRDbEkvdXZIa1ZQUndxK213aGZz?= =?utf-8?B?QUxzWklRaVQ0WTNEYmEwNFhTakN1TkZyb3h6ZmlNZUJUb3VBMU9UTG9qZ2FQ?= =?utf-8?B?c3FLc2RvT2VFdDdCWkZua1BMcXU5S2Rzd3dwYS9aM1NSNGFicDdmbjR4U2Jw?= =?utf-8?B?RGxsT2tDTEFZS3lnNGZoZzJsNXZxV1NkaUdYS2ZaWWNBTDRvcnh0RVZodVJz?= =?utf-8?B?dEgyaXVhSXpXb0t3bWlUaE14UzlxZ3JhOStaK01qRk5CelJmUTNvRzN1Q2VO?= =?utf-8?B?UXQyVWZvUmYrenA4QUxSekRVWkZsa2ZLZHF5eFI0U0Q0VU4vOWdNS1U0K01S?= =?utf-8?B?d2RrV1VJSmhaSW1tdysxeVNFbEhsSERpMVN2V1hTYlRGc2ptQXRYczdGVVFC?= =?utf-8?B?dy9QUU1OWmlrZlRQWkEvWUtIRDlydG90d0d5S21ZVVRSZFUxdzk0Vm5FRWM5?= =?utf-8?B?T1RCay9CZ1AxdURnWVBaZHdKOXJMeDl3NVgrSzBucVRQQm5rQ3E4M3BnanJy?= =?utf-8?B?cW5XNzNOUmtmYXVSbW1rSnhXTnhpb2VoMXZKd3NSVW9TazR5bFk2bmNHZ1Ex?= =?utf-8?B?NmpWSEFFdTdjWmdJZEF6RWZNZFVHL1JMTnhKRWtYV0hZMnRxU1IwVkdIWHZt?= =?utf-8?B?SUI4UENSejVHcU5ScWU1cTRub0ZtLzZKTnkxakRTckViQ0xBYnVsUnVieXlu?= =?utf-8?B?dWxrWTR1Mk5zUDB2ZVdJeVV2em1EaEZIcXhIZkg1V3YrWFp6bnlrRjE5M0Nl?= =?utf-8?Q?ojAyAZi8d8/RrU8Ppwmu5WM=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?cUYyRDBFUnlNanhVQkR6dXNnd2ZaT0hsblIvaXU3V21RQ2IyV1A5Y2wrMDdh?= =?utf-8?B?WmFTLzFPUWJ6RFIzVlBkcENwd3hVdGlaZDJMUERzZWdKUVg2Z1F3UkxqdUpK?= =?utf-8?B?UEE2QzgwTjYwWWZ1RGgyc25qTitlNG1STzFxTVk4bUx3TkFhVm9mNWVHWUEw?= =?utf-8?B?MjNWNkQ1dXYraHR1QWtiS3pMQUlMem1jTlJJOGFSZU1JcWI3cW1oVUk2Qi8x?= =?utf-8?B?a3VBNk4zVzFjZUJxa3RSb2NTZFVqUHp6VDY1MTExNWxYTy9Gd1pEeXg0VXhN?= =?utf-8?B?ZzExR0htTHkzbkFGUlF3Wnl0bmsyc25Dem15RVpFU1dxRitzQnEwTWV5UHd4?= =?utf-8?B?VkVPWVBOeDJIZ2NwdWtzTkxsS25mdFczSWlRdEhXQjVVdFFYY2lOVW1zdHk3?= =?utf-8?B?VEo5LzFkeCtSb0JpY3ZkVjhKNC9ZOE14TnZjdTJoRDZLRVR4YjRzaW1UUko3?= =?utf-8?B?b2U1cWZyOEFwcUMzcEV5ZTN0MkZBNURjejJYQTNqc2xZSVBwS3hUQzdyOEdP?= =?utf-8?B?dC92V1hSamdYOGRaNmJja0pvaE5yeDA0WVB6QTZqeVhlY0NaYzFFbDE3Z0tw?= =?utf-8?B?MmRDRHJFbERWNHd1Y05RTDZJMEhxc1k4TDBXYnNLMDhMRFQxUHI2RlJ1N2ZY?= =?utf-8?B?QjBQVGRKL0lxVkNaY21Va1lNQVpRNkFmd3FoNGZLc2lDOWI2czVUUEY5Y3du?= =?utf-8?B?U2ZYcStKb0ZGLytxYjFZVlVOSnBWN25INzV1R0txOUxsYnkvcDBUUGFVRm9U?= =?utf-8?B?Nm9SVjFZTk9JTXZpYkpaVE84M2JHY0srZm9yK0ZMeWdoVXZuRmdUZTZsL011?= =?utf-8?B?QThuYU1RRDlTeDAwcWdjenVnV1lCcUx3YWtvSGZUajhFOG1CNU9QdW9zRStT?= =?utf-8?B?NS82cVprRWptRHBqaVFXd0dRN2Z2MmhBR3VJTmQ1b3ZqS2lXaWw0eUR5dFVW?= =?utf-8?B?Q1dsUkloeG1MTFpaOFlmdVliVUM3UGdEOW83ODRGUy9SUjg4V0kzdXhwdUZ1?= =?utf-8?B?QmE1OWl4WklIS3M0MyttWk9yTlpKTEhtVXQ4dkZ6VldkOHZ2WlE2L0N3TWtT?= =?utf-8?B?S0NkaWZrVDJ5UjBDMm8xbjNTRzBxNmxFaktvTi8ybmE2cldweXEzR3ZSdUpN?= =?utf-8?B?b0JxUlZwcWprTzVXSzlpbiswYjFHdEhmZGlqQU9IVjJIcExWRzFCWXJocWJ5?= =?utf-8?B?cllrWnl5NUVQRTNBajlFcS9JMmpieTcrMm1UeFVXNE1JTmZnTEFUTFN6QklX?= =?utf-8?B?SXErbWdQU3NibmtGbTlxMGcxT3czb0FoS2FQVDU2MnZDYjM5b3d1ZzFLZytr?= =?utf-8?B?RzJ2Z3JVVVg0R253OVFMRU9tdTRZdzhqYlJMRnBGSFZRNHMxejNQVnZqZVVD?= =?utf-8?B?MEZFcWx2ZXZBbVpOdWRoczZFRG9YTmpoQnN5TlBDMzVicnJxT3VBaTdVUFgz?= =?utf-8?B?VzFYYjlzZm8yWFhhY2t0QW9WK003WE1ON3hoS3ptS3BPZ1hvTzNyZXpvVFBD?= =?utf-8?B?Q0lTeTMyMFcyNVgvcTQ5cThuRVJyN2JVWE9nSTNFazlPa0RpZWhuYytwMEJ6?= =?utf-8?B?bVgrQ2xuckViMmloZHZHN0dPVGZzdXByYmNlc2FPdks5M003OVhwaHhHek1k?= =?utf-8?B?VERqR0lqSnNSZmlBbmQyWHRQTjdENUMvS0U1WVk5Z0w1UmJwMG9zMjYwUHBG?= =?utf-8?B?UStJUVJHb2JBbjFlelhQbjRGV01IaVVyclFpS0lsK2o4WVYyS3dCaThjdWs3?= =?utf-8?B?aTNBSzFKTzBoc2U2dHlDbHJDMytqS2kxUDlJcGJHeGd5NGttNWRseC9hL2pY?= =?utf-8?B?eFFlSDhyb0YzYjBNeFM1cmxZSzVHRHBWRWc4bW9PTTBTdXNQcTZpcnVRZTEv?= =?utf-8?B?MllHWkNWZE1Oc3p4MTVFd2pJanJYbXZWMUJrSmNpajd5amF1SVRwN09NZjlm?= =?utf-8?B?TWtnbTBjVVRna0tjWTdTNEUzeFRqUWd3TTB6UHVidUZUZk5iOHBEMEpob0do?= =?utf-8?B?MUpJaXgxZ1Vkc3owTmxMbDJFVFRlWHQvbUp6eG5PdTZzYnQyeitQRG04WjRm?= =?utf-8?B?azdqN0VKYm41ZzZTY05ha3hEeVByYVQzUis2NnpheVQ0SzY4aWozeHFHNHQ1?= =?utf-8?B?YXNwTXYrZzVLSXhKRVVhYlpSVnExZDJMK3NDNGdVMm9ROEI4dHZ1TWNnNGF4?= =?utf-8?B?VnF5WlV0Mm5zd3FpNkpiNVRNejROYjBiSVpzVW91MUh2N2hyV1FUQU4zYkhk?= =?utf-8?B?d3EvOVM3bE9McUJkSGhtNzN0eGtVY0lHU2MzUW9tWExqeit1QzB0eUtlaXJj?= =?utf-8?B?SkVIcDlMc0ljSnZ2MkozS0h6bGVmU0Fsb3FOMVdZNUxyalBBaFQyQT09?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: b7dcede0-ef66-4302-870d-08de641803bb X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:44.1372 (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: 46zTXvMUweOGCbRxaLhRaFQNO8DjvmF06vbg97S8kQ7qD4VXBaIDyUkthscu8M8TTMXnnAyaMSmcKVcfWKXruA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 This patch introduces the data path implementation for the nxpwifi driver, covering both transmit (Tx) and receive (Rx) handling in station (STA) and access point (AP) modes. Key components added: - sta_rx.c: RX packet processing for STA mode - sta_tx.c: TX packet preparation and NULL frame handling for STA mode - uap_txrx.c: TX/RX logic for AP mode, including bridging and forwarding - txrx.c: Common TX/RX logic shared across roles Features include: - Ethernet frame reconstruction from 802.11 headers - TxPD/RxPD handling and alignment - WMM priority and packet delay calculation - 11n RX reordering support - Bridged packet queuing and forwarding in AP mode - TX status reporting and completion callbacks This is a foundational step toward enabling full data path support for nxpwifi in both client and AP roles. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/sta_rx.c | 242 ++++++++++ drivers/net/wireless/nxp/nxpwifi/sta_tx.c | 190 ++++++++ drivers/net/wireless/nxp/nxpwifi/txrx.c | 352 ++++++++++++++ drivers/net/wireless/nxp/nxpwifi/uap_txrx.c | 478 ++++++++++++++++++++ 4 files changed, 1262 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/wirele= ss/nxp/nxpwifi/sta_rx.c new file mode 100644 index 000000000000..d3864cc8a785 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_rx.c @@ -0,0 +1,242 @@ +// 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" + +/* + * Drop gratuitous IPv4 ARP and IPv6 neighbour advertisements when + * source and destination addresses are identical. + */ +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 =3D (struct ethhdr *)skb->data; + switch (ntohs(eth->h_proto)) { + case ETH_P_ARP: + arp =3D (void *)(skb->data + sizeof(struct ethhdr)); + if (arp->hdr.ar_op =3D=3D htons(ARPOP_REPLY) || + arp->hdr.ar_op =3D=3D htons(ARPOP_REQUEST)) { + if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) + return true; + } + break; + case ETH_P_IPV6: + ipv6 =3D (void *)(skb->data + sizeof(struct ethhdr)); + icmpv6 =3D (void *)(skb->data + sizeof(struct ethhdr) + + sizeof(struct ipv6hdr)); + if (icmpv6->icmp6_type =3D=3D NDISC_NEIGHBOUR_ADVERTISEMENT) { + if (!memcmp(&ipv6->saddr, &ipv6->daddr, + sizeof(struct in6_addr))) + return true; + } + break; + default: + break; + } + + return false; +} + +/* + * Process a received data packet. + * Convert 802.2/LLC/SNAP to Ethernet II when appropriate, trim the + * rxpd and extra headers, optionally drop gratuitous ARP/NA, cache + * RX rate for unicast, then deliver to the stack. + */ +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 =3D 0; + + local_rx_pd =3D (struct rxpd *)(skb->data); + + rx_pkt_off =3D le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_hdr =3D (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 <=3D 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 !=3D htons(ETH_P_AARP) && + rx_pkt_hdr->rfc1042_hdr.snap_type !=3D 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 =3D (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 =3D (u8 *)eth - (u8 *)local_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop =3D (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 =3D local_rx_pd->rx_rate; + priv->rxpd_htinfo =3D local_rx_pd->ht_info; + } + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + adj_rx_rate =3D 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 =3D nxpwifi_recv_packet(priv, skb); + if (ret) + nxpwifi_dbg(priv->adapter, ERROR, + "recv packet failed\n"); + + return ret; +} + +/* + * Process a received buffer on the STA path. + * Validate RxPD and lengths, handle monitor/mgmt frames, fast-path + * non-unicast frames, and feed unicast data into 11n reorder/BA logic. + */ +int nxpwifi_process_sta_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + int ret =3D 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 =3D (struct rxpd *)(skb->data); + rx_pkt_type =3D le16_to_cpu(local_rx_pd->rx_pkt_type); + rx_pkt_offset =3D le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_length =3D le16_to_cpu(local_rx_pd->rx_pkt_length); + seq_num =3D le16_to_cpu(local_rx_pd->seq_num); + + rx_pkt_hdr =3D (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=3D%d, rx_pkt_offset=3D%d, rx_pkt_length=3D%d\= n", + skb->len, rx_pkt_offset, rx_pkt_length); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->enable_net_mon && rx_pkt_type =3D=3D PKT_TYPE_802DOT11= ) { + ret =3D nxpwifi_recv_packet_to_monif(priv, skb); + if (ret) + dev_kfree_skb_any(skb); + return ret; + } + + if (rx_pkt_type =3D=3D PKT_TYPE_MGMT) { + ret =3D nxpwifi_process_mgmt_packet(priv, skb); + if (ret && (ret !=3D -EINPROGRESS)) + nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed"); + if (ret !=3D -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 !=3D PKT_TYPE_BAR && + local_rx_pd->priority < MAX_NUM_TID) + priv->rx_seq[local_rx_pd->priority] =3D seq_num; + memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + } + + /* Reorder and send to OS */ + ret =3D nxpwifi_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, + ta, (u8)rx_pkt_type, skb); + + if (ret || rx_pkt_type =3D=3D 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/wirele= ss/nxp/nxpwifi/sta_tx.c new file mode 100644 index 000000000000..5ad578223f1c --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sta_tx.c @@ -0,0 +1,190 @@ +// 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" + +/* + * Fill TxPD for TX packets by inserting it before payload and setting req= uired fields. + */ +void nxpwifi_process_sta_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct txpd *local_tx_pd; + struct nxpwifi_txinfo *tx_info =3D NXPWIFI_SKB_TXCB(skb); + unsigned int pad; + u16 pkt_type, pkt_length, pkt_offset; + int hroom =3D adapter->intf_hdr_len; + u32 tx_control; + + pkt_type =3D nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad =3D ((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 =3D (struct txpd *)skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + local_tx_pd->bss_num =3D priv->bss_num; + local_tx_pd->bss_type =3D priv->bss_type; + + pkt_length =3D (u16)(skb->len - (sizeof(struct txpd) + pad)); + if (pkt_type =3D=3D PKT_TYPE_MGMT) + pkt_length -=3D NXPWIFI_MGMT_FRAME_HEADER_SIZE; + local_tx_pd->tx_pkt_length =3D cpu_to_le16(pkt_length); + + local_tx_pd->priority =3D (u8)skb->priority; + local_tx_pd->pkt_delay_2ms =3D + 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 =3D tx_info->ack_frame_id; + local_tx_pd->flags |=3D 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 =3D + priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd->priority]; + local_tx_pd->tx_control =3D cpu_to_le32(tx_control); + } + + if (adapter->pps_uapsd_mode) { + if (nxpwifi_check_last_packet_indication(priv)) { + adapter->tx_lock_flag =3D true; + local_tx_pd->flags =3D + NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET; + } + } + + /* Offset of actual data */ + pkt_offset =3D sizeof(struct txpd) + pad; + if (pkt_type =3D=3D PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + local_tx_pd->tx_pkt_type =3D cpu_to_le16(pkt_type); + pkt_offset +=3D NXPWIFI_MGMT_FRAME_HEADER_SIZE; + } + + local_tx_pd->tx_pkt_offset =3D 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 =3D cpu_to_le32(priv->pkt_tx_ctrl); +} + +/* Send a NULL-data frame with TxPD at highest priority. */ +int nxpwifi_send_null_packet(struct nxpwifi_private *priv, u8 flags) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D NULL_PACKET_HDR; + struct sk_buff *skb; + int ret; + struct nxpwifi_txinfo *tx_info =3D 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 =3D dev_alloc_skb(data_len); + if (!skb) + return -ENOMEM; + + tx_info =3D NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num =3D priv->bss_num; + tx_info->bss_type =3D priv->bss_type; + tx_info->pkt_len =3D 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 =3D (struct txpd *)skb->data; + local_tx_pd->tx_control =3D cpu_to_le32(priv->pkt_tx_ctrl); + local_tx_pd->flags =3D flags; + local_tx_pd->priority =3D WMM_HIGHEST_PRIORITY; + local_tx_pd->tx_pkt_offset =3D cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->bss_num =3D priv->bss_num; + local_tx_pd->bss_type =3D priv->bss_type; + + skb_push(skb, adapter->intf_hdr_len); + tx_param.next_pkt_len =3D 0; + ret =3D 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=3D%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 =3D true; + break; + case -EINPROGRESS: + adapter->tx_lock_flag =3D true; + break; + default: + dev_kfree_skb_any(skb); + nxpwifi_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=3D%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + } + + return ret; +} + +/* Check whether a last=E2=80=91packet indication needs to be sent. */ +u8 nxpwifi_check_last_packet_indication(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + u8 ret =3D false; + + if (!adapter->sleep_period.period) + return ret; + if (nxpwifi_wmm_lists_empty(adapter)) + ret =3D true; + + if (ret && !adapter->cmd_sent && !adapter->curr_cmd && + !is_command_pending(adapter)) { + adapter->delay_null_pkt =3D false; + ret =3D true; + } else { + ret =3D false; + adapter->delay_null_pkt =3D 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..6e8b49138e57 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/txrx.c @@ -0,0 +1,352 @@ +// 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" + +/* + * Parse the RxPD, select the target interface, and dispatch the packet for + * handling. + */ +int nxpwifi_handle_rx_packet(struct nxpwifi_adapter *adapter, + struct sk_buff *skb) +{ + struct nxpwifi_private *priv =3D + nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY); + struct rxpd *local_rx_pd; + struct nxpwifi_rxinfo *rx_info =3D NXPWIFI_SKB_RXCB(skb); + int ret; + + local_rx_pd =3D (struct rxpd *)(skb->data); + /* Get the BSS number from rxpd, get corresponding priv */ + priv =3D nxpwifi_get_priv_by_id(adapter, local_rx_pd->bss_num & + BSS_NUM_MASK, local_rx_pd->bss_type); + if (!priv) + priv =3D 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 =3D priv->bss_num; + rx_info->bss_type =3D priv->bss_type; + + if (priv->bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP) + ret =3D nxpwifi_process_uap_rx_packet(priv, skb); + else + ret =3D nxpwifi_process_sta_rx_packet(priv, skb); + + return ret; +} +EXPORT_SYMBOL_GPL(nxpwifi_handle_rx_packet); + +/* + * Add TxPD, validate, send the packet to firmware, then run completion + * callback. + */ +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 =3D priv->adapter; + struct txpd *local_tx_pd =3D NULL; + struct nxpwifi_sta_node *dest_node; + struct ethhdr *hdr =3D (void *)skb->data; + + if (unlikely(!skb->len || + skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN)) { + ret =3D -EINVAL; + goto out; + } + + hroom =3D adapter->intf_hdr_len; + + if (priv->bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP) { + rcu_read_lock(); + dest_node =3D nxpwifi_get_sta_entry(priv, hdr->h_dest); + if (dest_node) { + dest_node->stats.tx_bytes +=3D skb->len; + dest_node->stats.tx_packets++; + } + rcu_read_unlock(); + 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) =3D=3D NXPWIFI_BSS_ROLE_STA) + local_tx_pd =3D (struct txpd *)(skb->data + hroom); + ret =3D 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) =3D=3D NXPWIFI_BSS_ROLE_STA) && + adapter->pps_uapsd_mode && adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag =3D false; + if (local_tx_pd) + local_tx_pd->flags =3D 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 =3D NULL; + u8 *head_ptr =3D skb->data; + int ret =3D 0; + struct nxpwifi_private *priv; + struct nxpwifi_txinfo *tx_info; + + tx_info =3D NXPWIFI_SKB_TXCB(skb); + priv =3D 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) =3D=3D NXPWIFI_BSS_ROLE_STA) + local_tx_pd =3D (struct txpd *)(head_ptr + adapter->intf_hdr_len); + + ret =3D 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) =3D=3D NXPWIFI_BSS_ROLE_STA) && + adapter->pps_uapsd_mode && + adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag =3D false; + if (local_tx_pd) + local_tx_pd->flags =3D 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 =3D skb_dequeue(&adapter->tx_data_q); + if (!skb) + return -ENOMEM; + + tx_info =3D 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 =3D skb_peek(&adapter->tx_data_q); + else + skb_next =3D NULL; + tx_param.next_pkt_len =3D ((skb_next) ? skb_next->len : 0); + if (!tx_param.next_pkt_len) { + if (!nxpwifi_wmm_lists_empty(adapter)) + tx_param.next_pkt_len =3D 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 =3D NXPWIFI_SKB_TXCB(skb); + priv =3D 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 +=3D tx_info->pkt_len; + if (priv->tx_timeout_cnt) + priv->tx_timeout_cnt =3D 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 =3D nxpwifi_1d_to_wmm_queue[skb->priority]; + if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { + txq =3D 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 =3D (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 =3D xa_erase(&priv->ack_status_frames, tx_status->tx_token_id); + spin_unlock_bh(&priv->ack_status_lock); + + if (ack_skb) { + tx_info =3D 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 =3D 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/wire= less/nxp/nxpwifi/uap_txrx.c new file mode 100644 index 000000000000..f3d24bf861ca --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c @@ -0,0 +1,478 @@ +// 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" + +/* + * Drop bridged pkts from RA list until pending <=3D low threshold; return= true if + * any. + */ +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 =3D false; + struct nxpwifi_txinfo *tx_info; + struct nxpwifi_adapter *adapter =3D 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 =3D 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 =3D true; + } + if ((atomic_read(&adapter->pending_bridged_pkts) <=3D + NXPWIFI_BRIDGED_PKTS_THR_LOW)) + break; + } + } + + return pkt_deleted; +} + +/* Delete bridged pkts from one RA list; rotate index to keep 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 =3D 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { + if (priv->del_list_idx =3D=3D MAX_NUM_TID) + priv->del_list_idx =3D 0; + ra_list =3D &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 =3D 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 =3D (struct uap_rxpd *)(skb->data); + rx_pkt_hdr =3D (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + if ((atomic_read(&adapter->pending_bridged_pkts) >=3D + 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 !=3D htons(ETH_P_AARP) && + rx_pkt_hdr->rfc1042_hdr.snap_type !=3D 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 =3D (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 =3D (u8 *)p_ethhdr - (u8 *)uap_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop =3D (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 =3D + 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 =3D new_skb; + nxpwifi_dbg(adapter, INFO, + "info: new skb headroom %d\n", + skb_headroom(skb)); + } + + tx_info =3D NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num =3D priv->bss_num; + tx_info->bss_type =3D priv->bss_type; + tx_info->flags |=3D NXPWIFI_BUF_FLAG_BRIDGED_PKT; + + rcu_read_lock(); + src_node =3D nxpwifi_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source); + if (src_node) { + src_node->stats.last_rx =3D jiffies; + src_node->stats.rx_bytes +=3D skb->len; + src_node->stats.rx_packets++; + src_node->stats.last_tx_rate =3D uap_rx_pd->rx_rate; + src_node->stats.last_tx_htinfo =3D uap_rx_pd->ht_info; + } + rcu_read_unlock(); + + 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 +=3D 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 =3D skb->len; + } + + __net_timestamp(skb); + + index =3D 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); +} + +/* AP fwd: mcast/bcast -> up + bridge; unicast -> bridge if RA assoc, else= up. */ +int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ra[ETH_ALEN]; + struct sk_buff *skb_uap; + struct nxpwifi_sta_node *node; + + uap_rx_pd =3D (struct uap_rxpd *)(skb->data); + rx_pkt_hdr =3D (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 =3D 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 { + node =3D nxpwifi_get_sta_entry_rcu(priv, ra); + if (node) { + /* 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 =3D priv->adapter; + struct nxpwifi_sta_node *src_node, *dst_node; + struct ethhdr *p_ethhdr; + struct sk_buff *skb_uap; + struct nxpwifi_txinfo *tx_info; + + if (!skb) + return -ENOMEM; + + p_ethhdr =3D (void *)skb->data; + rcu_read_lock(); + src_node =3D nxpwifi_get_sta_entry(priv, p_ethhdr->h_source); + if (src_node) { + src_node->stats.last_rx =3D jiffies; + src_node->stats.rx_bytes +=3D skb->len; + src_node->stats.rx_packets++; + } + dst_node =3D nxpwifi_get_sta_entry(priv, p_ethhdr->h_dest); + rcu_read_unlock(); + + if (is_multicast_ether_addr(p_ethhdr->h_dest) || dst_node) { + if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) + skb_uap =3D + skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN); + else + skb_uap =3D skb_copy(skb, GFP_ATOMIC); + + if (likely(skb_uap)) { + tx_info =3D NXPWIFI_SKB_TXCB(skb_uap); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num =3D priv->bss_num; + tx_info->bss_type =3D priv->bss_type; + tx_info->flags |=3D 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) >=3D + 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 (dst_node) + return 0; + } + + skb->dev =3D priv->netdev; + skb->protocol =3D eth_type_trans(skb, priv->netdev); + skb->ip_summed =3D CHECKSUM_NONE; + + /* Forward multicast/broadcast packet to upper layer*/ + netif_rx(skb); + return 0; +} + +/* Process AP RX: check RxPD/len, handle mgmt or 11n reorder/AMSDU, then f= orward. */ +int nxpwifi_process_uap_rx_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D (struct uap_rxpd *)(skb->data); + rx_pkt_type =3D le16_to_cpu(uap_rx_pd->rx_pkt_type); + rx_pkt_hdr =3D (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=3D%d, offset=3D%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=3D%d, offset=3D%d, length=3D%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++; + rcu_read_lock(); + node =3D nxpwifi_get_sta_entry(priv, ta); + if (node) + node->stats.tx_failed++; + rcu_read_unlock(); + + dev_kfree_skb_any(skb); + return 0; + } + + if (rx_pkt_type =3D=3D PKT_TYPE_MGMT) { + ret =3D nxpwifi_process_mgmt_packet(priv, skb); + if (ret && (ret !=3D -EINPROGRESS)) + nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed"); + if (ret !=3D -EINPROGRESS) + dev_kfree_skb_any(skb); + return ret; + } + + if (rx_pkt_type !=3D PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { + rcu_read_lock(); + node =3D nxpwifi_get_sta_entry(priv, ta); + if (node) + node->rx_seq[uap_rx_pd->priority] =3D + le16_to_cpu(uap_rx_pd->seq_num); + rcu_read_unlock(); + } + + 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) !=3D PKT_TYPE_AMSDU))) { + ret =3D nxpwifi_handle_uap_rx_forward(priv, skb); + return ret; + } + + /* Reorder and send to kernel */ + pkt_type =3D (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); + ret =3D 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 =3D=3D PKT_TYPE_BAR) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} + +/* + * Build TxPD for AP TX: push aligned TxPD; set bss, len/off, prio, delay,= txctl, + * flags. + */ +void nxpwifi_process_uap_txpd(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct uap_txpd *txpd; + struct nxpwifi_txinfo *tx_info =3D NXPWIFI_SKB_TXCB(skb); + int pad; + u16 pkt_type, pkt_offset; + int hroom =3D adapter->intf_hdr_len; + + pkt_type =3D nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad =3D ((uintptr_t)skb->data - (sizeof(*txpd) + hroom)) & + (NXPWIFI_DMA_ALIGN_SZ - 1); + + skb_push(skb, sizeof(*txpd) + pad); + + txpd =3D (struct uap_txpd *)skb->data; + memset(txpd, 0, sizeof(*txpd)); + txpd->bss_num =3D priv->bss_num; + txpd->bss_type =3D priv->bss_type; + txpd->tx_pkt_length =3D cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + + pad))); + txpd->priority =3D (u8)skb->priority; + + txpd->pkt_delay_2ms =3D 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 =3D tx_info->ack_frame_id; + txpd->flags |=3D 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 =3D + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); + + /* Offset of actual data */ + pkt_offset =3D sizeof(*txpd) + pad; + if (pkt_type =3D=3D PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + txpd->tx_pkt_type =3D cpu_to_le16(pkt_type); + pkt_offset +=3D NXPWIFI_MGMT_FRAME_HEADER_SIZE; + } + + txpd->tx_pkt_offset =3D 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 =3D cpu_to_le32(priv->pkt_tx_ctrl); +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010026.outbound.protection.outlook.com [52.101.69.26]) (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 3D590423A63; Wed, 4 Feb 2026 18:05:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.26 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228350; cv=fail; b=MczYhvg+d/OJ1FnE53xxtB2Pz6oEnn3+ryNrkMcGNOecOPr/uJlzBg0lcEHEQBwYPEss5qCg0GTj57BjUlNxT1jU9sIjhlclngTj2ajtvv7celq/wz7gS5TWcwxQQBlMFwRGL3UzoBPHWoUfsobsRSnErCOKlYBHldzZBHIuAgs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228350; c=relaxed/simple; bh=YtIjCP3eDtdXXAnqCcBOmeh3wunp6XFwWSZGQMmWwBE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=TH4KnvyoHk2I9y0o38+pn7+4JHYbjg0T3rcJwJYAC9Ab0flHMhdU8pmaG0PD0jdaJl9+NSTojn3oRAL9FuhUxDacJ0705bZwrkM+xAkJtH8MUbT4/u9MOm5OzWGLv34M/Ftcyc+O3Jm7gtpUBLxJlJEoIsahCaa/LQuGLAr0x2M= 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=jUJtcOMC; arc=fail smtp.client-ip=52.101.69.26 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="jUJtcOMC" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=t3MWeCKGOkb0oboOTUXvXqx1MDSlYm9JxV56T+WWFvKbD2tCW8onkApQNVxeWucxc6+dNE9wt/Ft9RZq/6zeW8aTt6NaZOaibmv8QW3B65Imu/P8MPro090f8vLV85F/Ze8BGo9dRb6/SER09r3sn/fCHcvRpESD8kD3n/SUb2glxpxvHdqWhff+Ml7RW8WXAbsqD3G7wk+Ma6nfHQQIa55JcnilctIUHiGoDBgUodnmBIOUYtwS/8qkyd2/TGQaFtYz832Qw3eNqMFt/hpmiXCCqGTZyX92rk8ioM6H99Z1A5D7PubniNd5Sfh9jZbh2KBr5dKPeQ7d7uSN0To+Sw== 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=1fFX0Kng3hL37t0WGx1Zii4MTD8f5KJvBDxvvKSSQY8=; b=fO/GwIO5SA76idHS3Ag53PSM81YJwhR+kBDlNuKsmwlxfiv0FbBYaHkiWFIdaiZ+qlKRnqoamU9CfLvvyLINLYXHk30BGxz5H9VMTonJ/Ul2ecSdDMwCfv2GHDlSvtQbSJaVOkXN7l0GeUsoakEtZ0Ol96qT7XxoERnaTfz5ZDoCg4mfyp6jcYCd6rjwpUzHe4UfICOpCpyX1BfM+7y8p99ErSLATKQzho5/yI7czgrDc8TyQKIr1QZ+P7MU0QdWCVXbVIF02NTuinH/rv/JyjusfvUP1YQiyDgO0J7RjhFEAHFFht5kg83QCAorcP35c6rG+aViOVTDoIkImgxfxw== 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=1fFX0Kng3hL37t0WGx1Zii4MTD8f5KJvBDxvvKSSQY8=; b=jUJtcOMCzLNrmkph3EXaJkqfacu2g+J88To/7d6gAArlHK45sS0rROw8tw+UE9/tQ1nfhBygCYVbWp/eKmHgqoDu4qUR0UuSZgJfr5uW6l+UI+WqQom4Jx6wEo2Dhc2/ln/g+T6GgI1SM2i8xx9YT+x2+cmpeiTQo3SvVgCZ9GYFflkIBaDjyS9BDJwvWDtL1hdslondP3gilixDYCgmqGXItf0FG6fd/n+4a4ASIMoIud249eS/ITS7oeuhAT9utlWEvStGhccgaUW4wG/PV+3OS9ALADxoPd35my/O/gJGv+FYgeXcUvddtuZXajeQrJw49wnuorcWpBYbFbIDbQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:47 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:46 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 14/21] wifi: nxpwifi: add debugfs support for diagnostics and testing Date: Thu, 5 Feb 2026 02:03:51 +0800 Message-Id: <20260204180358.632281-15-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 527d149e-e844-4a26-2283-08de64180576 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?O5Fn9pq/cGxnBu5tc25udvl43lGduTTtRHDvjrKtM6IQhOUr74XYkhfW2vPj?= =?us-ascii?Q?iAxD92syNZ9+z4Hbph1mc00N0KWEkSWiS5ynqeD9Ffoi7wsulAztNPV738yb?= =?us-ascii?Q?5dI52L2bhfY5BHxKBMSq9yigKev5ca0FL6agFpcFbBtH30xRzu6oKORyyNNx?= =?us-ascii?Q?T+h85lXEaoHKNpuSaL/wWFsm4DI5B4QDy3oobyR/FLZ83DPN0TafUvq9bGhH?= =?us-ascii?Q?AE1mBR7tIQIGoT2xiA2McNeNXXTy8BP2TtfurSHcNYJafWpNUfZTt32w1hXI?= =?us-ascii?Q?HN8EPFndrQcUmubYWuy6H6SsAnPAi//JfgNAJW5x8wbIL3mRBPG9ZUYIzC35?= =?us-ascii?Q?hbdFdtoz27FL6mJ8Q6qskWctpUlqKNdpHkLLFys3jeQNC1c2rS82F2DKQGvd?= =?us-ascii?Q?xjIsC+efbzuFGcImhM0+UIHPFJm9K+k2D2sen5ktHZwFtvd/xtZ36bsuU3WE?= =?us-ascii?Q?mXBumwIO4+88KUmavXWI1Bh5bP/ytRDWD3ltubzhmyyzqr1AkQM4KAkOW/58?= =?us-ascii?Q?z5a4osJoBDfAqDc1A2YQUGPUIEC3/iTEegvaJ8IryGHH36HYCSM95DeXXry9?= =?us-ascii?Q?nk8SfRSC2iznopF07Je4kluljXcGdswwMU2/0JPfauh4YLdBVcVbd3s1fw9A?= =?us-ascii?Q?nKUK9Qii+qONTvYS892NAUQMAUqZ3MYDS52hQ0bFCO2vQilY3jibueIr4hh8?= =?us-ascii?Q?lf3aoe5RIv8KgksHXvyBtunwTLEHdV04ww87da87qYfm06IAN0X5RI/55CyK?= =?us-ascii?Q?aJyVpNKPxPlXqueRcwaDMWpcsF8hkfD1rNQ0r3X24gtvK0eRnCNsj4P+Eqbc?= =?us-ascii?Q?qUgA2FldJsixUCptR4npxujTpcy2/iyod7qlNWdOsFnZPs//CVtwsSdz9guh?= =?us-ascii?Q?3x3ho6yLfCvhuSvYC5ORa17m9daJksZuUGoNeDKEdJe22SMQfmVm1+bUZhUk?= =?us-ascii?Q?tIjKdtGvusDyPGRLU64Lpwj/CZHg3RKHt5eSFqukFk6tOhIzh2WY2bmmYDxi?= =?us-ascii?Q?z6NxL9fH1HQRajpJNreYklDzIznBg/bkWmBcQc19qJXNFoAI+hkDJKNenR8i?= =?us-ascii?Q?WFdKI0cndX6jdnJXRA4qf26V+Z3SoBm93Tp0ER4qcVyvdgNKV19IIa4nm0ey?= =?us-ascii?Q?MHTJDuJbQAPGfhVHGGr1dtoTCkjL99H7bvgua3RwYDEgzpn6wtyM1P2I+I9y?= =?us-ascii?Q?85m5httEjIdUbFMPdWVwnboiws5yP0J87nbj5nz87xf0lR9V0ek4JZNBkEh4?= =?us-ascii?Q?qFZWvsFqI4giSRjPwNOQQDoU2Rq8JUqdNi1qDIQRG8Mh9VGU2DIhPRUu6zQC?= =?us-ascii?Q?1wiMH1eoUljQca+9vNr+J6Fz2zrHKRw/jjlLb6RB02TETekMEmF145EQHg/M?= =?us-ascii?Q?+DnFjwnKsVXKCXttMZOY8JKAbvX3l7rHn2DXaND3z6VYuwAXCbFpZ1/Yga1Q?= =?us-ascii?Q?3wx8r30daM4RYvdvXpJlQIcZ+sMVFT7qNCG1IyNjsMPUfV+HtyXqwntLv4Pn?= =?us-ascii?Q?Ltdj/zoiWmE0uNTo9lQ9Fmz2ACIrRxaE+4kJ11oC58+vlFJ15b/U4RV+8zPy?= =?us-ascii?Q?dBALNmUqJQMf4bEUMsgx7F/0Yx3s3eIn0dkB/BjDIg9ZSFBmdHZRLoW+A+p5?= =?us-ascii?Q?ZTCX9QwDqwiTNOBXUCtPjFA=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?vZV+BZshRPSklksdwKCWs/nra20aMM7T80M2kJKmZMWfSCf4kJ4hSdsxlvRr?= =?us-ascii?Q?KqQ3Q+ce6Y64nLiwbkUnauOljgfH+pzxmGlnh3O2b3Bup2JXliRVPx6Qley4?= =?us-ascii?Q?keGKrpGSROQFLunoko0bxejTHJ0OyV23cLSXObIVvAstLukKAuhBUHgFJWB6?= =?us-ascii?Q?jvKmIE0hXZQvuRCG4McGehe/uukTut0aPDg4Ygq8m2ROUznkqgIyme46WCaP?= =?us-ascii?Q?JspVXIfhgqHbyaF+9ChK++EsGJYfJkcDxzdNQAwYPdebX192KghVSjUjMM0i?= =?us-ascii?Q?C5Z+SGcHC2fGgxeq9ktPXTQFdl03BZOYs2ym7tVk+ZB1ysiBKgffghfHMfrb?= =?us-ascii?Q?5jMvT3ukZnZ3IvwrMgSi12p9ieJBSTrha2ekpBBG6dHHbIqu2q1FMkAH5cAc?= =?us-ascii?Q?7vSNAo3eux9J3tMmINMlIA+VOhV8wzM5K7nOkbl8tuy8ANq0/C0B/gMLDd4o?= =?us-ascii?Q?7upK/ax4lGv2F7TOdP032Qf7f+1tOLjdHe2RdrCwA+hcu1eRfJXUW+jLLsZs?= =?us-ascii?Q?Zv83VofwJOq9X9+guf8nLt7mygVngQbHzmg/fGWTacYSPy9kvUeUz4Pgcb4w?= =?us-ascii?Q?uwDhXkijR9SPdxHevHvoUqFd6MdNvioRziPcIEnyCxJGadJg2lNlRbQVnKZR?= =?us-ascii?Q?OhFAvo0rmPuAWrG751wxFG3WUFRO8lNT+N+ocQOZq400r7VHsk9NaH1d1aiO?= =?us-ascii?Q?lLf1wB/stdswQnOv6jq7KRGnMl35B0QH4evs2I2p0mPS1ghUHiQXDrvHm5pr?= =?us-ascii?Q?wV1ccyRsmXrZS+L8SqIAwZrKqUPOSTeIegUGxg18dh+f8X4bZw+O0FqEDxse?= =?us-ascii?Q?Io01wJV74VoiFKHP5oLGvSGlcPHemUPWikjLhYbqi7mSSnRtOKLrYX1aAsCb?= =?us-ascii?Q?G5Viz+7qucHYWtpmZkuT/A8o0mGbraB+vuY0UWIpbTeIP2PMBZ/QajWB9GKd?= =?us-ascii?Q?5NF2Rkv1aTQbKOGbKMu6DSollOOIuXACcLj4Glx9EIijP429891c3qJK8oir?= =?us-ascii?Q?adzoyq6P9IFmUu4Exs+wGqITeFp8xwTjV1hdqIy48r+XWNltFAt12O4g2/RX?= =?us-ascii?Q?7yGwDliQdslhLJ1eJ785lnFiiRwJbiKQ83GFbQbe4hYfmyMUtvthDJ3vTRqz?= =?us-ascii?Q?+IyyWnWnEJPwygUJR25Veft1aKAiZ6HMmII+CbeBpaumcK5RkpfXZDBqm1gJ?= =?us-ascii?Q?WrcJPKqdPEeFbsuF1dA2s0YVuPM5uiurxqXA5gUeyxk+ab+nOI27gub0aIlb?= =?us-ascii?Q?kIbakA50hREYmrqdrQhMKbYIlOXj/DQ+2G/Wt3iQFnBEJICQoeU+35qIGSVG?= =?us-ascii?Q?4boZX6aDf0MxADSGwz7NWhSuhEdM9Y0PptNWsRzZmNoV7Mv/wiLPQVezj0TY?= =?us-ascii?Q?HZfohn4+I8Ad7V/sFR0s3M/Eqos0tx0De3sf8wY8CWADS4/tnWP7HgxuWCbY?= =?us-ascii?Q?SY0nXS+/Q/rUBpjGgLgz5k1gb5UM4Y50Gc1CohR/6rzDfsya74sWO4JPbWIT?= =?us-ascii?Q?CChQKgks44S2HOcTDTAP8tS5POcfFMYo98ju7y9INW2xX/OR6DCfBS8dN6C8?= =?us-ascii?Q?c5HtYwOsVi2PtaBWm2V0crMYhQ0D+xSMf5fOGPmiMIMnbMBbAuXjGlHU5SDE?= =?us-ascii?Q?IlrROLluGSroBGiuloXhDLWxvuN7Ljkl8zuWSXMqvV898iVgs7AWIPwBZed0?= =?us-ascii?Q?asbZl/vkZG5R1WFVHSxe6SOiZbV+C70KFJYajK6Ukx2fTJTDmPu4YudD5Qat?= =?us-ascii?Q?8mr4MYqhNw=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 527d149e-e844-4a26-2283-08de64180576 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:46.8987 (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: kY4Erme8/0MwmpUlPaJVXzAOCh5X9TiZ5INDjiXAfid6w3cVdDaEA/hPnsgulWT2gAkAHLwRyxm3mEOicf64sQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" This patch adds a comprehensive debugfs interface for the nxpwifi driver, enabling easier diagnostics, testing, and runtime inspection. These tools are useful for both development and field debugging, especially for validating DFS behavior, power management, and firmware interaction. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/debugfs.c | 1094 ++++++++++++++++++++ 1 file changed, 1094 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/debugfs.c diff --git a/drivers/net/wireless/nxp/nxpwifi/debugfs.c b/drivers/net/wirel= ess/nxp/nxpwifi/debugfs.c new file mode 100644 index 000000000000..ccaf0eae37e3 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/debugfs.c @@ -0,0 +1,1094 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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[] =3D { + "UNSPECIFIED", + "ADHOC", + "STATION", + "AP", + "AP_VLAN", + "WDS", + "MONITOR", + "MESH_POINT", + "P2P_CLIENT", + "P2P_GO", + "P2P_DEVICE", +}; + +/* + * debugfs "info" read handler: dump driver name/version, interface, BSS m= ode, + * link state, MAC, counters; STA adds SSID/BSSID/channel/country/region a= nd + * multicast list. + */ +static ssize_t +nxpwifi_info_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + struct net_device *netdev =3D priv->netdev; + struct netdev_hw_addr *ha; + struct netdev_queue *txq; + unsigned long page =3D get_zeroed_page(GFP_KERNEL); + char *p =3D (char *)page, fmt[64]; + struct nxpwifi_bss_info info; + ssize_t ret; + int i =3D 0; + + if (!p) + return -ENOMEM; + + memset(&info, 0, sizeof(info)); + ret =3D 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 +=3D sprintf(p, "driver_name =3D "); + p +=3D sprintf(p, "\"nxpwifi\"\n"); + p +=3D sprintf(p, "driver_version =3D %s", fmt); + p +=3D sprintf(p, "\nverext =3D %s", priv->version_str); + p +=3D sprintf(p, "\ninterface_name=3D\"%s\"\n", netdev->name); + + if (info.bss_mode >=3D ARRAY_SIZE(bss_modes)) + p +=3D sprintf(p, "bss_mode=3D\"%d\"\n", info.bss_mode); + else + p +=3D sprintf(p, "bss_mode=3D\"%s\"\n", bss_modes[info.bss_mode]); + + p +=3D sprintf(p, "media_state=3D\"%s\"\n", + (!priv->media_connected ? "Disconnected" : "Connected")); + p +=3D sprintf(p, "mac_address=3D\"%pM\"\n", netdev->dev_addr); + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA) { + p +=3D sprintf(p, "multicast_count=3D\"%d\"\n", + netdev_mc_count(netdev)); + p +=3D sprintf(p, "essid=3D\"%.*s\"\n", info.ssid.ssid_len, + info.ssid.ssid); + p +=3D sprintf(p, "bssid=3D\"%pM\"\n", info.bssid); + p +=3D sprintf(p, "channel=3D\"%d\"\n", (int)info.bss_chan); + p +=3D sprintf(p, "country_code =3D \"%s\"\n", info.country_code); + p +=3D sprintf(p, "region_code=3D\"0x%x\"\n", + priv->adapter->region_code); + + netdev_for_each_mc_addr(ha, netdev) + p +=3D sprintf(p, "multicast_address[%d]=3D\"%pM\"\n", + i++, ha->addr); + } + + p +=3D sprintf(p, "num_tx_bytes =3D %lu\n", priv->stats.tx_bytes); + p +=3D sprintf(p, "num_rx_bytes =3D %lu\n", priv->stats.rx_bytes); + p +=3D sprintf(p, "num_tx_pkts =3D %lu\n", priv->stats.tx_packets); + p +=3D sprintf(p, "num_rx_pkts =3D %lu\n", priv->stats.rx_packets); + p +=3D sprintf(p, "num_tx_pkts_dropped =3D %lu\n", priv->stats.tx_dropped= ); + p +=3D sprintf(p, "num_rx_pkts_dropped =3D %lu\n", priv->stats.rx_dropped= ); + p +=3D sprintf(p, "num_tx_pkts_err =3D %lu\n", priv->stats.tx_errors); + p +=3D sprintf(p, "num_rx_pkts_err =3D %lu\n", priv->stats.rx_errors); + p +=3D sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) + ? "on" : "off")); + p +=3D sprintf(p, "tx queue"); + for (i =3D 0; i < netdev->num_tx_queues; i++) { + txq =3D netdev_get_tx_queue(netdev, i); + p +=3D sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p +=3D sprintf(p, "\n"); + + ret =3D simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* + * debugfs "getlog" read handler: dump firmware/802.11 counters (retry, RT= S/ACK, dup, + * frag, mcast, FCS, beacon stats). + */ +static ssize_t +nxpwifi_getlog_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + unsigned long page =3D get_zeroed_page(GFP_KERNEL); + char *p =3D (char *)page; + ssize_t ret; + struct nxpwifi_ds_get_stats stats; + + if (!p) + return -ENOMEM; + + memset(&stats, 0, sizeof(stats)); + ret =3D nxpwifi_get_stats_info(priv, &stats); + if (ret) + goto free_and_exit; + + p +=3D 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 =3D simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* + * debugfs "histogram" read handler: report sample count and per-rate/SNR/= noise + * floor/signal strength histograms. + */ +static ssize_t +nxpwifi_histogram_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + ssize_t ret; + struct nxpwifi_histogram_data *phist_data; + int i, value; + unsigned long page =3D get_zeroed_page(GFP_KERNEL); + char *p =3D (char *)page; + + if (!p) + return -ENOMEM; + + if (!priv || !priv->hist_data) { + ret =3D -EFAULT; + goto free_and_exit; + } + + phist_data =3D priv->hist_data; + + p +=3D sprintf(p, "\n" + "total samples =3D %d\n", + atomic_read(&phist_data->num_samples)); + + p +=3D sprintf(p, + "rx rates (in Mbps): 0=3D1M 1=3D2M 2=3D5.5M 3=3D11M 4=3D6M 5= =3D9M 6=3D12M\n" + "7=3D18M 8=3D24M 9=3D36M 10=3D48M 11=3D54M 12-27=3DMCS0-15(BW20= ) 28-43=3DMCS0-15(BW40)\n"); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + p +=3D sprintf(p, + "44-53=3DMCS0-9(VHT:BW20) 54-63=3DMCS0-9(VHT:BW40) 64-73=3DMCS0-9(= VHT:BW80)\n\n"); + } else { + p +=3D sprintf(p, "\n"); + } + + for (i =3D 0; i < NXPWIFI_MAX_RX_RATES; i++) { + value =3D atomic_read(&phist_data->rx_rate[i]); + if (value) + p +=3D sprintf(p, "rx_rate[%02d] =3D %d\n", i, value); + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + for (i =3D NXPWIFI_MAX_RX_RATES; i < NXPWIFI_MAX_AC_RX_RATES; + i++) { + value =3D atomic_read(&phist_data->rx_rate[i]); + if (value) + p +=3D sprintf(p, "rx_rate[%02d] =3D %d\n", + i, value); + } + } + + for (i =3D 0; i < NXPWIFI_MAX_SNR; i++) { + value =3D atomic_read(&phist_data->snr[i]); + if (value) + p +=3D sprintf(p, "snr[%02ddB] =3D %d\n", i, value); + } + for (i =3D 0; i < NXPWIFI_MAX_NOISE_FLR; i++) { + value =3D atomic_read(&phist_data->noise_flr[i]); + if (value) + p +=3D sprintf(p, "noise_flr[%02ddBm] =3D %d\n", + (int)(i - 128), value); + } + for (i =3D 0; i < NXPWIFI_MAX_SIG_STRENGTH; i++) { + value =3D atomic_read(&phist_data->sig_str[i]); + if (value) + p +=3D sprintf(p, "sig_strength[-%02ddBm] =3D %d\n", + i, value); + } + + ret =3D 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 =3D (void *)file->private_data; + + if (priv && priv->hist_data) + nxpwifi_hist_data_reset(priv); + return 0; +} + +static struct nxpwifi_debug_info info; + +/* debugfs "debug" read handler: dump adapter debug info and BA/reorder ta= bles. */ +static ssize_t +nxpwifi_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + unsigned long page =3D get_zeroed_page(GFP_KERNEL); + char *p =3D (char *)page; + ssize_t ret; + + if (!p) + return -ENOMEM; + + ret =3D nxpwifi_get_debug_info(priv, &info); + if (ret) + goto free_and_exit; + + p +=3D nxpwifi_debug_info_to_buffer(priv, p, &info); + + ret =3D 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; + +/* + * debugfs "regrdwr" write handler: parse and store for + * readback/IO. + */ +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 =3D 0, reg_offset =3D 0, reg_value =3D UINT_MAX; + int rv; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + rv =3D sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); + + if (rv !=3D 3) { + ret =3D -EINVAL; + goto done; + } + + if (reg_type =3D=3D 0 || reg_offset =3D=3D 0) { + ret =3D -EINVAL; + goto done; + } else { + saved_reg_type =3D reg_type; + saved_reg_offset =3D reg_offset; + saved_reg_value =3D reg_value; + ret =3D count; + } +done: + kfree(buf); + return ret; +} + +/* + * debugfs "regrdwr" read handler: perform pending register read/write and= return + * . + */ +static ssize_t +nxpwifi_regrdwr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + unsigned long addr =3D get_zeroed_page(GFP_KERNEL); + char *buf =3D (char *)addr; + int pos =3D 0, ret =3D 0; + u32 reg_value; + + if (!buf) + return -ENOMEM; + + if (!saved_reg_type) { + /* No command has been given */ + pos +=3D snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + /* Set command has been given */ + if (saved_reg_value !=3D UINT_MAX) { + ret =3D nxpwifi_reg_write(priv, saved_reg_type, saved_reg_offset, + saved_reg_value); + + pos +=3D snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", + saved_reg_type, saved_reg_offset, + saved_reg_value); + + ret =3D simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + goto done; + } + /* Get command has been given */ + ret =3D nxpwifi_reg_read(priv, saved_reg_type, + saved_reg_offset, ®_value); + if (ret) { + ret =3D -EINVAL; + goto done; + } + + pos +=3D snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, + saved_reg_offset, reg_value); + + ret =3D simple_read_from_buffer(ubuf, count, ppos, buf, pos); + +done: + free_page(addr); + return ret; +} + +/* debugfs "debug_mask" read handler: show driver debug mask. */ + +static ssize_t +nxpwifi_debug_mask_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + unsigned long page =3D get_zeroed_page(GFP_KERNEL); + char *buf =3D (char *)page; + size_t ret =3D 0; + int pos =3D 0; + + if (!buf) + return -ENOMEM; + + pos +=3D snprintf(buf, PAGE_SIZE, "debug mask=3D0x%08x\n", + priv->adapter->debug_mask); + ret =3D simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(page); + return ret; +} + +/* debugfs "debug_mask" write handler: set driver debug mask. */ + +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 =3D (void *)file->private_data; + char *buf; + + buf =3D 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 =3D -EINVAL; + goto done; + } + + priv->adapter->debug_mask =3D debug_mask; + ret =3D count; +done: + kfree(buf); + return ret; +} + +/* debugfs "verext" write handler: select extended version string. */ +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 =3D (void *)file->private_data; + + ret =3D kstrtou32_from_user(ubuf, count, 10, &versionstrsel); + if (ret) + return ret; + + priv->versionstrsel =3D versionstrsel; + + return count; +} + +/* debugfs "verext" read handler: show extended version string. */ +static ssize_t +nxpwifi_verext_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + char buf[256]; + int ret; + + nxpwifi_get_ver_ext(priv, priv->versionstrsel); + ret =3D snprintf(buf, sizeof(buf), "version string: %s\n", + priv->version_str); + + return simple_read_from_buffer(ubuf, count, ppos, buf, ret); +} + +/* debugfs "memrw" write handler: read/write firmware memory (addr, value)= . */ +static ssize_t +nxpwifi_memrw_write(struct file *file, const char __user *ubuf, size_t cou= nt, + loff_t *ppos) +{ + int ret; + char cmd; + struct nxpwifi_ds_mem_rw mem_rw; + u16 cmd_action; + struct nxpwifi_private *priv =3D (void *)file->private_data; + char *buf; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret =3D sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); + if (ret !=3D 3) { + ret =3D -EINVAL; + goto done; + } + + if ((cmd =3D=3D 'r') || (cmd =3D=3D 'R')) { + cmd_action =3D HOST_ACT_GEN_GET; + mem_rw.value =3D 0; + } else if ((cmd =3D=3D 'w') || (cmd =3D=3D 'W')) { + cmd_action =3D HOST_ACT_GEN_SET; + } else { + ret =3D -EINVAL; + goto done; + } + + memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_MEM_ACCESS, cmd_action, 0, + &mem_rw, true); + if (!ret) + ret =3D count; + +done: + kfree(buf); + return ret; +} + +/* debugfs "memrw" read handler: show last memory access result. */ +static ssize_t +nxpwifi_memrw_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D (void *)file->private_data; + unsigned long addr =3D get_zeroed_page(GFP_KERNEL); + char *buf =3D (char *)addr; + int ret, pos =3D 0; + + if (!buf) + return -ENOMEM; + + pos +=3D snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, + priv->mem_rw.value); + ret =3D simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static u32 saved_offset =3D -1, saved_bytes =3D -1; + +/* debugfs "rdeeprom" write handler: set EEPROM offset/length to read. */ +static ssize_t +nxpwifi_rdeeprom_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + char *buf; + int ret =3D 0; + int offset =3D -1, bytes =3D -1; + int rv; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + rv =3D sscanf(buf, "%d %d", &offset, &bytes); + + if (rv !=3D 2) { + ret =3D -EINVAL; + goto done; + } + + if (offset =3D=3D -1 || bytes =3D=3D -1) { + ret =3D -EINVAL; + goto done; + } else { + saved_offset =3D offset; + saved_bytes =3D bytes; + ret =3D count; + } +done: + kfree(buf); + return ret; +} + +/* debugfs "rdeeprom" read handler: dump EEPROM bytes from saved offset/le= ngth. */ +static ssize_t +nxpwifi_rdeeprom_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + unsigned long addr =3D get_zeroed_page(GFP_KERNEL); + char *buf =3D (char *)addr; + int pos, ret, i; + u8 value[MAX_EEPROM_DATA]; + + if (!buf) + return -ENOMEM; + + if (saved_offset =3D=3D -1) { + /* No command has been given */ + pos =3D snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + + /* Get command has been given */ + ret =3D nxpwifi_eeprom_read(priv, (u16)saved_offset, + (u16)saved_bytes, value); + if (ret) { + ret =3D -EINVAL; + goto out_free; + } + + pos =3D snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); + + for (i =3D 0; i < saved_bytes; i++) + pos +=3D scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); + +done: + ret =3D simple_read_from_buffer(ubuf, count, ppos, buf, pos); +out_free: + free_page(addr); + return ret; +} + +/* + * debugfs "hscfg" write handler: configure host-sleep (conditions/gpio/ga= p) or + * cancel. + */ +static ssize_t +nxpwifi_hscfg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D (void *)file->private_data; + char *buf; + int ret, arg_num; + struct nxpwifi_ds_hs_cfg hscfg; + int conditions =3D HS_CFG_COND_DEF; + u32 gpio =3D HS_CFG_GPIO_DEF, gap =3D HS_CFG_GAP_DEF; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + arg_num =3D 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 =3D -EINVAL; + goto done; + } + + if (arg_num >=3D 1 && arg_num < 3) + nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET, + NXPWIFI_SYNC_CMD, &hscfg); + + if (arg_num) { + if (conditions =3D=3D HS_CFG_CANCEL) { + nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD); + ret =3D count; + goto done; + } + hscfg.conditions =3D conditions; + } + if (arg_num >=3D 2) + hscfg.gpio =3D gpio; + if (arg_num =3D=3D 3) + hscfg.gap =3D gap; + + hscfg.is_invoke_hostcmd =3D 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 =3D count; +done: + kfree(buf); + return ret; +} + +/* debugfs "hscfg" read handler: show current host-sleep configuration. */ +static ssize_t +nxpwifi_hscfg_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D (void *)file->private_data; + unsigned long addr =3D get_zeroed_page(GFP_KERNEL); + char *buf =3D (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 =3D snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, + hscfg.gpio, hscfg.gap); + + ret =3D 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 =3D file->private_data; + char buf[3]; + bool timeshare_coex; + int ret; + unsigned int len; + + if (priv->adapter->fw_api_ver !=3D NXPWIFI_FW_V15) + return -EOPNOTSUPP; + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX, + HOST_ACT_GEN_GET, 0, ×hare_coex, true); + if (ret) + return ret; + + len =3D 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 =3D file->private_data; + int ret; + + if (priv->adapter->fw_api_ver !=3D NXPWIFI_FW_V15) + return -EOPNOTSUPP; + + ret =3D kstrtobool_from_user(ubuf, count, ×hare_coex); + if (ret) + return ret; + + ret =3D 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 =3D file->private_data; + struct nxpwifi_adapter *adapter =3D priv->adapter; + bool result; + int rc; + + rc =3D kstrtobool_from_user(ubuf, count, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + if (adapter->if_ops.card_reset) { + nxpwifi_dbg(adapter, INFO, "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 =3D file->private_data; + struct nxpwifi_adapter *adapter =3D priv->adapter; + bool result; + int rc; + + rc =3D 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"); + wiphy_delayed_work_cancel(priv->adapter->wiphy, &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; +} + +static ssize_t +nxpwifi_netmon_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + struct nxpwifi_802_11_net_monitor netmon_cfg; + struct nxpwifi_private *priv =3D (void *)file->private_data; + char *buf; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + memset(&netmon_cfg, 0, sizeof(struct nxpwifi_802_11_net_monitor)); + ret =3D sscanf(buf, "%u %u %u %u %u", + &netmon_cfg.enable_net_mon, + &netmon_cfg.filter_flag, + &netmon_cfg.band, + &netmon_cfg.channel, + &netmon_cfg.chan_bandwidth); + + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_802_11_NET_MONITOR, + HOST_ACT_GEN_SET, 0, &netmon_cfg, true); + + if (!ret) + ret =3D count; + + kfree(buf); + return ret; +} + +static ssize_t +nxpwifi_twt_setup_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + struct nxpwifi_twt_cfg twt_cfg; + struct nxpwifi_private *priv =3D (void *)file->private_data; + char *buf; + u16 twt_mantissa, bcn_miss_threshold; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret =3D sscanf(buf, "%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hu %hh= u %hu", + &twt_cfg.param.twt_setup.implicit, + &twt_cfg.param.twt_setup.announced, + &twt_cfg.param.twt_setup.trigger_enabled, + &twt_cfg.param.twt_setup.twt_info_disabled, + &twt_cfg.param.twt_setup.negotiation_type, + &twt_cfg.param.twt_setup.twt_wakeup_duration, + &twt_cfg.param.twt_setup.flow_identifier, + &twt_cfg.param.twt_setup.hard_constraint, + &twt_cfg.param.twt_setup.twt_exponent, + &twt_mantissa, + &twt_cfg.param.twt_setup.twt_request, + &bcn_miss_threshold); + + twt_cfg.param.twt_setup.twt_mantissa =3D cpu_to_le16(twt_mantissa); + twt_cfg.param.twt_setup.bcn_miss_threshold =3D cpu_to_le16(bcn_miss_thres= hold); + twt_cfg.sub_id =3D NXPWIFI_11AX_TWT_SETUP_SUBID; + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_SET, 0, + &twt_cfg, true); + if (!ret) + ret =3D count; + + kfree(buf); + return ret; +} + +static ssize_t +nxpwifi_twt_teardown_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + struct nxpwifi_twt_cfg twt_cfg; + struct nxpwifi_private *priv =3D (void *)file->private_data; + char *buf; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret =3D sscanf(buf, "%hhu %hhu %hhu", + &twt_cfg.param.twt_teardown.flow_identifier, + &twt_cfg.param.twt_teardown.negotiation_type, + &twt_cfg.param.twt_teardown.teardown_all_twt); + + twt_cfg.sub_id =3D NXPWIFI_11AX_TWT_TEARDOWN_SUBID; + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_SET, 0, + &twt_cfg, true); + + if (!ret) + ret =3D count; + + kfree(buf); + return ret; +} + +static ssize_t +nxpwifi_twt_report_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct nxpwifi_private *priv =3D + (struct nxpwifi_private *)file->private_data; + unsigned long page =3D get_zeroed_page(GFP_KERNEL); + char *p =3D (char *)page; + ssize_t ret; + struct nxpwifi_twt_cfg twt_cfg; + u8 num, i, j; + + if (!p) + return -ENOMEM; + + twt_cfg.sub_id =3D NXPWIFI_11AX_TWT_REPORT_SUBID; + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_GET, 0, + &twt_cfg, true); + if (ret) + goto done; + num =3D twt_cfg.param.twt_report.length / NXPWIFI_BTWT_REPORT_LEN; + num =3D num <=3D NXPWIFI_BTWT_REPORT_MAX_NUM ? num : NXPWIFI_BTWT_REPORT_= MAX_NUM; + p +=3D sprintf(p, "\ntwt_report len %hhu, num %hhu, twt_report_info:\n", + twt_cfg.param.twt_report.length, num); + for (i =3D 0; i < num; i++) { + p +=3D sprintf(p, "id[%hu]:\r\n", i); + for (j =3D 0; j < NXPWIFI_BTWT_REPORT_LEN; j++) { + p +=3D sprintf(p, + " 0x%02x", + twt_cfg.param.twt_report.data[i * NXPWIFI_BTWT_REPORT_LEN + j]); + } + p +=3D sprintf(p, "\r\n"); + } + + ret =3D simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + +done: + free_page(page); + return ret; +} + +static ssize_t +nxpwifi_twt_information_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + struct nxpwifi_twt_cfg twt_cfg; + struct nxpwifi_private *priv =3D (void *)file->private_data; + char *buf; + u32 suspend_duration; + + buf =3D memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1))); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret =3D sscanf(buf, "%hhu %u", + &twt_cfg.param.twt_information.flow_identifier, &suspend_duration); + twt_cfg.param.twt_information.suspend_duration =3D cpu_to_le32(suspend_du= ration); + + twt_cfg.sub_id =3D NXPWIFI_11AX_TWT_INFORMATION_SUBID; + ret =3D nxpwifi_send_cmd(priv, HOST_CMD_TWT_CFG, HOST_ACT_GEN_SET, 0, + &twt_cfg, true); + + if (!ret) + ret =3D count; + + kfree(buf); + return ret; +} + +#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 =3D { \ + .read =3D nxpwifi_##name##_read, \ + .write =3D nxpwifi_##name##_write, \ + .open =3D simple_open, \ +} + +#define NXPWIFI_DFS_FILE_READ_OPS(name) \ +static const struct file_operations nxpwifi_dfs_##name##_fops =3D { \ + .read =3D nxpwifi_##name##_read, \ + .open =3D simple_open, \ +} + +#define NXPWIFI_DFS_FILE_WRITE_OPS(name) \ +static const struct file_operations nxpwifi_dfs_##name##_fops =3D { \ + .write =3D nxpwifi_##name##_write, \ + .open =3D 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); +NXPWIFI_DFS_FILE_WRITE_OPS(netmon); +NXPWIFI_DFS_FILE_WRITE_OPS(twt_setup); +NXPWIFI_DFS_FILE_WRITE_OPS(twt_teardown); +NXPWIFI_DFS_FILE_READ_OPS(twt_report); +NXPWIFI_DFS_FILE_WRITE_OPS(twt_information); + +/* Create per-netdev debugfs directory and files. */ +void +nxpwifi_dev_debugfs_init(struct nxpwifi_private *priv) +{ + if (!nxpwifi_dfs_dir || !priv) + return; + + priv->dfs_dev_dir =3D 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); + NXPWIFI_DFS_ADD_FILE(netmon); + NXPWIFI_DFS_ADD_FILE(twt_setup); + NXPWIFI_DFS_ADD_FILE(twt_teardown); + NXPWIFI_DFS_ADD_FILE(twt_report); + NXPWIFI_DFS_ADD_FILE(twt_information); +} + +/* Remove per-netdev debugfs directory and files. */ +void +nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv) +{ + if (!priv) + return; + + debugfs_remove_recursive(priv->dfs_dev_dir); +} + +/* Create top-level debugfs directory. */ +void +nxpwifi_debugfs_init(void) +{ + if (!nxpwifi_dfs_dir) + nxpwifi_dfs_dir =3D debugfs_create_dir("nxpwifi", NULL); +} + +/* Remove top-level debugfs directory. */ +void +nxpwifi_debugfs_remove(void) +{ + debugfs_remove(nxpwifi_dfs_dir); +} --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010026.outbound.protection.outlook.com [52.101.69.26]) (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 EA8BF42B73E; Wed, 4 Feb 2026 18:05:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.26 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228351; cv=fail; b=KH7YsBffv8SBb4ppWL2FJVscI302xRGspcKl2PtodOZqPamNv1cUHyv8IPprxK33XLEp7eLo7cGZw8OuFaPqxQtamYsHKivOUYiTdBZvye1qMEd7CEQncaEeLTOoPl4A3bOYnP/go/7WvA0XKD+yT6qPt6/fza72iW4q4L+j4fo= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228351; c=relaxed/simple; bh=mEjjwfnsePPETZ0HPiK0LC/yx8nIj3Y7Pdh0w5PaL9g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=l0tx9uhNXDLwskE6ui8my0rTn7CfQ5Ouf8gSAgN/QDFIuM+Zfu9YJd3fhT28ZTeA9HHmsgqBLo68Sfymt3/dyvpGzedRuK6YNDPu5WGL+o9LyrG9YtrhR8e6nnIESL1X1Eq/7H2ueKzPFWeQUZT6KrrMGjXkxH2+Fgqw7uznlP4= 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=LCM0S1fM; arc=fail smtp.client-ip=52.101.69.26 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="LCM0S1fM" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=mE0cOEol1KLFWr7mOI6oe/V8FsQ9O5RIfCy95EMVgUIo7YWYY4+/2EmgWpE8N+A63YvBzg3vLclpBt5IaEwnh00zf3cI/YemtX9KKhaqDOvPb7LIojB/YmjVZ54daySjrffYvcPZ0IeFvSTzopKYYmq3beF9oOL7qnc39UvHYd8Ei1ur2Lnq2DEiGC/vfJ803j+QGcv8xcAh4eU5z9bmtskM1zD1joW2dHW3DFbsovoo1OuPDwoQWbfuRjcdrinLU8/Htois1e5Qb8Fzv2gJYmBQgog8NJtXMTfZpqbFrqbekgaAidrP7MYV0pHriREzvoM49fIuYBWJvnmffBEvmw== 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=Swm7kH7INptV7rSBelGkREbbhZ1uNtigAXbjkRdbl4w=; b=tjXJ6LIUNCEG12tRdDf9AIWHkuQznv2h/myxQgS8KPc8mK7hCDE5GAQ8VqxUFKEn6oc/46hnGcvdXm53TDQo8vn4vwp0wQPEqDRP3O1uCBcSJjeLjmjsdPEcH6LeeIVGJ0pun2fzSK5OJZccVGnMlHj20AqhrwbD7RHGMHbMtgrrpFWQqtxkSWmZ/2ne94prH3tXzGllrUC8roI0w0G50no5nSqpLy4QULWeyZxwYX2eCAaWY+Ygd3Sb5HoqeLeCT4U47qDhE0UL8NHDyGPB5YKWhGEPE5AwcuhfEdsz0MMJX+XNjPPviXAW4xRYPP0uTV2sO9xiE73EbecQ5RUbHA== 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=Swm7kH7INptV7rSBelGkREbbhZ1uNtigAXbjkRdbl4w=; b=LCM0S1fM6vWJhVjrQ6VyMTDOKB++wNpAckU3AH07fvkWM82yn/5LdlyAjxWgu589nKKeAL5C+PiWj1N8GXadwe1CeuIwN8xQBKa3XDeXdEo5IiPp2ZwV9NriOjUxnVa05lSnl+1v7xo0VqddXQlZ4etCHxF8OQ6d9a8Ub6Jf8hdrZhldBcr92UVRK3jNWI2Vm8eO+JmSoMZ+xd5//aVRl9yhAduLEBSdBNFPAK4NNcYpGSrsZLNyoEHFulWAMvOhwaUUoHo2hzu9LCMJDPuwCzN/0YtjTfJ5XG/OfTsCYxUeI0dCOe2pdU3Ma4PDTTrmVtMJQ8i7i7DIK8wotey7Og== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:49 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:49 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 15/21] wifi: nxpwifi: add ethtool support for Wake-on-LAN Date: Thu, 5 Feb 2026 02:03:52 +0800 Message-Id: <20260204180358.632281-16-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: ce78a47f-aba8-47ed-73d9-08de641806e1 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?orfPyqGAj/5NNzBU8KH+3RYToMUVHN0ockgLZJxPrc+7rb1hwWWhuhcY1MzO?= =?us-ascii?Q?sPb5RVjmiCI7MrAA3+fIAakJo0HxKIUr+iAfgxqDSrUzgLfRJ7/VS2dHix03?= =?us-ascii?Q?aMd4qfssAz10Llupl0xZHlVQQW5ZHCzy2PYj6Lct/xkBSyo3u1gafm7ewOaw?= =?us-ascii?Q?eQTfFubde4ZV7YCZH3C3Kii+7NOyAbga4oeuDPRAco8d8iGQVjTSrt9hxc3Z?= =?us-ascii?Q?JQtwOxYNpit+aadhLNILAbA4mdIEVDMPmPBpfK1Id5fk2OVyT+VuROyo/25C?= =?us-ascii?Q?CK22LTJIcjw90m75butp9Ha5rVbxSENCRgzXWS1+p1v+cvyOG0WbhXaLlvng?= =?us-ascii?Q?nqHcGcQuOIPWS4fEagvRu+JQgD6Ewx5F+e8I7ec+zrUQMBLe453+U2a91WyW?= =?us-ascii?Q?veCgSItjLyY+3GEso7Z3BRx65QKQxhIFWYmTbjlKiqWsaATqT8fQ/HRVjO27?= =?us-ascii?Q?l2JSqHGABrdIz0VoYvAKSuCcNltpuRdxQCT2lAhDqSBmDqOOCX+9QpNGFDJs?= =?us-ascii?Q?J5PaBeM2ad/ErxCLAVm6BLMAjqZzbzo/rFypZbP+2jNjbsBAW+y+r9WnbOZw?= =?us-ascii?Q?NcfFYGDKZtSome3k44d13kMOCr465DUCsVDoZH2jZHRZLuG9ozBofgj7hUj1?= =?us-ascii?Q?87ssGYDAnac3fUo37fVogjMZWj107/sa1I6UKUdFZ6pMCaQsi/3496R2V+tO?= =?us-ascii?Q?tyvkATgBIAH77ndPV2NbXYk3tAYW2Lrm/qp0/+hAmS7kqc3OLpxBNYx8szwh?= =?us-ascii?Q?GMCkFDzzMSL9R4oZI8O5LDZMZbMz8pQuSpH/1JuOL1Nl3CdTfjjU5HsnFZre?= =?us-ascii?Q?DRT7Sii+awfRd1ldn5LRzpNmRVpBY/T9f8GpOZz273kEzvYN868WH4IeQ6ro?= =?us-ascii?Q?EQOV7HsncDTXpIXYgU2qXK46mmGqFX6/rkOQU51Gpz2eMA/FwB+TWkk+T7tk?= =?us-ascii?Q?1WWsMxoYzUUQyuzLNuZ4agjsml56icjht5ZkCAq85ZuzQ5k9wnyNlh6HdpqO?= =?us-ascii?Q?JjjQDvjLXFzmYboSktD6DPCywtY0NZImq9KsG4aPKWiAw8Co1R/NYL8cl9oh?= =?us-ascii?Q?e1R3Xoi88cezK1XM/DNH/f2brC6UTtCm8gG0jYHxJQltOW56A7QfL9v6c2Ix?= =?us-ascii?Q?j0MyaTYKE21lELbB956VTv3BHywrbpeFhZVNXIEghHNGDXuAiGimY1kPsIV7?= =?us-ascii?Q?uNRGe4C1xkM33hOEhjAdAIO2uD1TlqWsfiS4bW+z0STsUpldiUFVD2uimz2h?= =?us-ascii?Q?uJKRW7Y094yVSbsHtlnXao5q+S10DUaPMOt4vngPMYSK4+D1F9cbSl/RTpf1?= =?us-ascii?Q?adh11NoIayirv3RM71D6CncpQndbsaIFVn+ISpQpwkwhkQ2nSnGbePK1uB2a?= =?us-ascii?Q?yhXPm3omplVRW8Z5O8yDguMjkVkPr8iUjM85DrHDYoZwhj4hWNNnJWIslzNZ?= =?us-ascii?Q?hE/1Wyqr4RdMwQvZ/9LEz7oxadeQLh+5GpJdou7Rf5Ri323wJsw+Fu5+uyr7?= =?us-ascii?Q?8LZWcj5zAGiqGSBJbdoFR12HjwNFDF2TbucS5l4TSY9k3e1NcV3a1fk7tgHF?= =?us-ascii?Q?6wLM3023PXfMplifR7FdOrklIFT98eJ7t8beD3fQJ8Kpkd8lxspCkwS9tSRF?= =?us-ascii?Q?TDdOQntIEIGCMF+qmuZpeJc=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?/odYTl4y7g+A1s4jTo77ZT5RK6zb1ipZWv3VH9trzE5ds1ji7rNPzpzCBPfb?= =?us-ascii?Q?L7ox/rnuoCPns6zoCOt0rKlXnJls8Sxm3t5ILOfKXTjR8Y1TGl2iKJjavHyf?= =?us-ascii?Q?U6dVUhKBBEJw0CscLE8jQRXKf4h7VJOJ8PmFQVjxIJ31hUbQ5fufFy2H/gpT?= =?us-ascii?Q?41UxVf6lzLQHwSrZ7VWKiDiNBMD8uW6NucJn+A11JimC9fMM5vmmJcQUbV8w?= =?us-ascii?Q?6qXhePdi52HLY08SmAt5e7ffeTo5MpW5wcEsd/GOP/PwMcK5n2Ml0eco5+bY?= =?us-ascii?Q?kglkOhBBXA8c5ajfEbxfhj7o8L7E4J3KtAsDPb/ldMBaFpvqBI1qQMmxEur9?= =?us-ascii?Q?KonUbCtFHxcsjc20K6xogcDuJGvptPJe/Jk/Gq/9vCVOXiGff66rx03Lhno+?= =?us-ascii?Q?NcHAa0nst7c7ltsEHzGHdEF65tFTQl2DuYTcz2T9hk6b+hjpxPv6gJZkQvrk?= =?us-ascii?Q?/lTCMCnnHKAySkOTQ1Sp+fiLK0adL5jcl6JyCkvUrOVW0e0iSaiiWx4/W0Zx?= =?us-ascii?Q?LdzAQrdXHt1+/qDis81Di2EXn6ZBzSx5WTzej76fl+J+Qp8y/fdM/q5aQUKz?= =?us-ascii?Q?fjeZ/VEzjIkYoGCx9fGci8Ej5c9PLzZcWs5JXm50cJFBXgF6GanzfzP2W/By?= =?us-ascii?Q?sWWcwfXzAPnnQHhB/7tB+vmp6BXlfxg0BPYP6XrocEZAO08csR6lG3qQOjjt?= =?us-ascii?Q?RdFOd6iLbIT/ZUbVFZuURA+ey+/vBfso2bKRPeN56ATNq+lfJqkw9YL+wplI?= =?us-ascii?Q?3XnKAOLB2MMbpRM0AbQJDu0A9t9MY5uv8oaJHVxAu/jWTAJIhdSKPttTDRrA?= =?us-ascii?Q?k9JwjwxUvckBWW2nuLP8vRmA0SF4djCtWkiXm++tYDmLcQsrd6rd5Z9/A3kI?= =?us-ascii?Q?XbcAjUOVk89b2EJIz6gOSR6Ugrc7I+Z1dScP6oXv8az9l8tmFAWHAhOsxxeg?= =?us-ascii?Q?XPcDr4Ec746myLOxWmE8zW5BRuKO0R4ZNc+Gi81eOW1QTD3cdTPvROitScdi?= =?us-ascii?Q?sEbUo9uLNGDvFh+Qz+FsaCecLG96+S0wiWvUrb5rWaJ8wJ6LTCzXgxkItvjE?= =?us-ascii?Q?tI1CYrl0xULQdnyU3YmqikfcuZwN2n1Muhj8tOci6X83V8FtNeN2/pFkhGd9?= =?us-ascii?Q?RJvR/mvHy/irlWQTa+Sqtlkp1SdLNJjAE3P696OiQ+eXzIjB7aqWwaWf0ij2?= =?us-ascii?Q?P5GpBmA54BsFC3nSiZ2NjGMsHb9wex15xNUT6oQ8pTg+gvqWmEiTPTEdN++r?= =?us-ascii?Q?jzdH7sxfC/60S//9kswhClbqtxI/Yii6jvBP5qgGPkI46Z9bvE+r+JTExSlI?= =?us-ascii?Q?EavcC27VQ5GrqJGUV6awfRV+MuHaQ566LXftug5/6r1SQJP72//r+J4WEJnY?= =?us-ascii?Q?ZW6TfowDdfvt8szjrmPcjfSEL/DbWeKAuiI6sDyd6n/w4wDbmlqPgLNGpkYR?= =?us-ascii?Q?Pgo2ISsF1kM1O+zjYa9so9zmSovEAFKYGO4klyWFiMBcxJkWb/+5LgXx4eqg?= =?us-ascii?Q?pVtjeYU4OGBYfKrd3dCp/wa8/HgUwbZWi5lio9O8JG2ZiuzwpvPZ/eFbCn1/?= =?us-ascii?Q?/r7HFBs16u8gebiAYnvdBlG3Mpf5DTSgqSlYXkzKVHNx/SzEe4t6YqMyLkBF?= =?us-ascii?Q?eMrMReTBp73O8xuOubLpQ+YPTc153Rn3RVOGrPRP/ueousGJoEwO21XznGbt?= =?us-ascii?Q?oKrKBx1nd/bA7SZskWayA2bqBhmnKikSJZIJrIvPMthmpehEBhQ9j49J5PE2?= =?us-ascii?Q?mcOE4TTB9g=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: ce78a47f-aba8-47ed-73d9-08de641806e1 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:49.2049 (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: TJR3787gsarBBNkNUgKrMDQA0zqcXVpwwSwVFscpxn4zyuPXyZWCt25Z6zh62Mf+5aGKUialaTDKm8i5uQJHPw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" This patch adds basic ethtool support to the nxpwifi driver, specifically implementing Wake-on-LAN (WoL) configuration via ethtool. Supported WoL options: - WAKE_UCAST - WAKE_MCAST - WAKE_BCAST - WAKE_PHY The ethtool ops `get_wol` and `set_wol` are implemented to map ethtool WoL flags to the driver's internal host sleep configuration. This enables users to query and configure WoL behavior using standard ethtool commands. Signed-off-by: Jeff Chen --- 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/wirel= ess/nxp/nxpwifi/ethtool.c new file mode 100644 index 000000000000..aabb635afcf5 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/ethtool.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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 =3D nxpwifi_netdev_get_priv(dev); + u32 conditions =3D le32_to_cpu(priv->adapter->hs_cfg.conditions); + + wol->supported =3D WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY; + + if (conditions =3D=3D HS_CFG_COND_DEF) + return; + + if (conditions & HS_CFG_COND_UNICAST_DATA) + wol->wolopts |=3D WAKE_UCAST; + if (conditions & HS_CFG_COND_MULTICAST_DATA) + wol->wolopts |=3D WAKE_MCAST; + if (conditions & HS_CFG_COND_BROADCAST_DATA) + wol->wolopts |=3D WAKE_BCAST; + if (conditions & HS_CFG_COND_MAC_EVENT) + wol->wolopts |=3D WAKE_PHY; +} + +static int nxpwifi_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + u32 conditions =3D 0; + + if (wol->wolopts & ~(WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY)) + return -EOPNOTSUPP; + + if (wol->wolopts & WAKE_UCAST) + conditions |=3D HS_CFG_COND_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + conditions |=3D HS_CFG_COND_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + conditions |=3D HS_CFG_COND_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + conditions |=3D HS_CFG_COND_MAC_EVENT; + if (wol->wolopts =3D=3D 0) + conditions |=3D HS_CFG_COND_DEF; + priv->adapter->hs_cfg.conditions =3D cpu_to_le32(conditions); + + return 0; +} + +const struct ethtool_ops nxpwifi_ethtool_ops =3D { + .get_wol =3D nxpwifi_ethtool_get_wol, + .set_wol =3D nxpwifi_ethtool_set_wol, +}; --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DB3PR0202CU003.outbound.protection.outlook.com (mail-northeuropeazon11010007.outbound.protection.outlook.com [52.101.84.7]) (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 78ED242E009; Wed, 4 Feb 2026 18:05:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.84.7 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228355; cv=fail; b=jnk50Imaj22bw/K5eLxU42QFi+IkJR+Wa5ZYutHiKIYyRz2V2mH6Cs1R0jpItMcgk2DhNnIfE4uOq95Ao9wIwMOxwbbmQhZWUjXTbV+D7gGvRfHxCdhCL32EXkUVOwNgrJRlnvbTDKmoIMBs++OQEGqNTMbAWPCKq7Q/yi4MPmw= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228355; c=relaxed/simple; bh=nN+vwX/FsZSF/N8vu6GZQu6Cf46J2PxgDMMRl8GCHUM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=l1iwOxOgYnIiP5u0xj+DuGaT24SHLGE6yOoRyM45d0zWJPaIP5tD0d1JhpZsA/MHU8rXQqXGJh1eeBGclX21E005aUak3Zx1SFTwTy1KCnul2D1Yea5TV08ybUaJSHCwTG3myT4hkX/8THE8Prs14r3PCehB3D1ZqAjScoNuuwA= 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=SRmkPq9e; arc=fail smtp.client-ip=52.101.84.7 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="SRmkPq9e" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=EjcE62flZiF5SviVVMrTkPzRQi2X/Cf5T3pdHbRWY1O9SpFN8B5yKwRTYv1Rd9/fejRe67BKLmwW2NtajKzTH4NTtIua1K3QeT1+1rpbx2wNnNXIz5tg6kpMd33oIBXKK4RhgyjUZZxDGWO5JqsQqepQ25FcMV/19rDaGS3jyFIM7AtxVLsJrkJ4vEfDI3XGWCQm2ER1J3Md1GC0SGChHykT6KIDA5xC4dhXQVDmcOHGYyrOe4WdCfY606sLtYNso1sMCl8RjbmSqiC+B9oyJTbd9Kat90lQXkr7bp4QiWx0uJ0fIVSTT0R3IduMTeQ3vFlnrCnEQ1j+gjZeuW6GPw== 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=BVhLoCGcVc0L3PMC7EX2xdxScTC+2UKtQ/7APMK2E+s=; b=KtT0YetsLsPIb98GK2MZFvuy92azO++D1u8G0NsSUkRyYEbQmItvVdv8+iwk8dL1say03MM0tLIAuSzLC6HTOef6nPBQrcOk2QB7I9xY9CosGSsfMhpwPv0GI7mnHMedW6cJkSTgaI4BnlWM3p16of5f4gegEFurijJc94Hj5TbNEQybZlnsVoKG5H6Ym7r4G+Xt8wIRsi41bJSbpOQ1MgGsvbGt/GGNe6eoalUjUq7xWXW9DxJHtYX4kipWCkEXsA/vWyIzEzCIpa/FkenFtcusnqopERwpqaK2ZrMUIuo8fAlwNqSAfQwasM4UVF8k0FcxI/tra9pHDXhe+DsP9A== 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=BVhLoCGcVc0L3PMC7EX2xdxScTC+2UKtQ/7APMK2E+s=; b=SRmkPq9ek1fMqQ5GUQ2SY9x72rrenuRoS6zxo66KpJoJj6yCCFyVD6kVTePdXWPR6W+NvPuu97bqldElSvCcon5LjfbKfLL4mHc1IkIf0GC1/JOH6cEXLA4citCzNYFuFCYZarWEqQw53QFHI5Rqf9vO9Y5Ijmi4m2L4Pz6UhaM034RYkujy0GrqjrQR0svs3jehG4LC/mWoGLMyxDwWJtzuEPx9BbjctzezjDwcTwlhOtrVniNa8wa9mSrN218AJtlGV0Vgwr0b5XCDOlieJhxdBBobXImmx9nAeIE6uD74cJnfik8XomwjL/ExWUgJI0TIecmc/vAZJwUDPrgl3A== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:51 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:51 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 16/21] wifi: nxpwifi: add utility and IE handling support Date: Thu, 5 Feb 2026 02:03:53 +0800 Message-Id: <20260204180358.632281-17-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: e56aff11-4b79-487a-7d20-08de6418083e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?B?Z0ZvZlN5SmIwNjkzSEMwaHFFZlM0YUF2MHNFNFp5bFMxQXF4Vk5HdGdaTmVv?= =?utf-8?B?QWRsYU1CV2VsclBqbnRyNVFvOXhvSk1iRDdMZmJqRlB4UnZaakFWSzhlS2d6?= =?utf-8?B?ODNyRmtWSzZ5b2RRZHZROTdKQmVGendiZ0x5UmtZL095Vjl5WWNCMnJ2SHV0?= =?utf-8?B?aTNIaFJEc09HeG54SUZsbHhPWnd4TmFpMCt6a0V0UFo4VXdMOXpEVUgrbHUy?= =?utf-8?B?ZGtsU1ZTZTVQT3lUOS9XZjNja3prWDdqejNzeVIxM1ZSMjZ0ZXZPUHBOMXhO?= =?utf-8?B?WHYrZXlOdUlsT2JPdk9qRnZWZm1GZkhPdEs4TXB3WDQyaFIxRGRNaFc2VVJY?= =?utf-8?B?N3NZUHRxdGVPa1VBWXFQWEUxOER2Nm5jb29Dd01BRTl2QVFFdDRSWG1vOG1K?= =?utf-8?B?WExqY0pFQzR0cW9vU0NNUC9BOGJYV3JENUFVSCtaU29PMWZNRjZLdThTVld2?= =?utf-8?B?cHE0T0Q4ZjZmbktrUEZWcGJwTEVyQkp2d1BiWDhnaXNTRTF1eW14RFNoUDN5?= =?utf-8?B?cmNXdGo4ZDlqd2lWVGYxTmxmeFZPaVVLWk91cmxlbkdyTDVnK3hTM0ZHSUl5?= =?utf-8?B?ZTNPc2JWMWl6cUl6WDk4ZHNkMXZCNEtpeTBkSXFJSm1aVm1XUS9jK0oxaUtm?= =?utf-8?B?QnhFb2thd1NYSFQ1WGdpT1RmVlExUWhNakpIODRSdHlCN1JqTE9wcExWSytF?= =?utf-8?B?cEZ6S3ZvWjFHd01YcGYzd0xMRG4xeVFkMFV5NGN3VFd3bFJ3ZXFEcDF2SlBU?= =?utf-8?B?WjVlbWJSQmM2a2VwcjRJYXlVdmJIcWZsNSt5OTNXcjNKSmVkdnBqYWxBdTUr?= =?utf-8?B?dWpGTlVOemVwaHNRL1RnL2lSZ2N5SE00VE5uM2dvcFpIY1cvQW5JcEJwYVBS?= =?utf-8?B?L0VhS0ZMTUNqRjdKRGNUc1NIRENIV0lwNzUwd3JvbFNIcGZ4TEpHM2o3bmVt?= =?utf-8?B?a1dnV0R3RThIcW4xUzJ5NmcvdTE1R2g5TnkyeTRucVdBdWVjRzgva0gwZnQ1?= =?utf-8?B?RlZUVWFEMnBtUjR5T2lLMTRQN1Q5RjR6KzM2WVVNSVV6UmZpNE12bFVDZDdp?= =?utf-8?B?U01maVJ1K3U2emFtK0Rzc1VQZldMVlJJbnlGS1ExKzFVdkZqazJ4WTRFZUxv?= =?utf-8?B?MHdMeTgxQ1hVSkZqWTlRaDhaUVdnK2R4WWpsNVBINEdzR0gwbXljS3U2d3ZI?= =?utf-8?B?Z2tPbWFaYVlMQzBXTkZhT1JVUElNYTdWWWFkUmFzV2VtRklsN25remNrWGJX?= =?utf-8?B?LzRNOFJuQ2ZIblc0dHpmWFcrY2thanZsanBzblY1VGw3eVhqSUhZRkZsREtu?= =?utf-8?B?azM1aXc4S1NaZnNhaFpiWGtiREd1UURXVThFeCtIL04xMzZWVHVxSzAvUzZU?= =?utf-8?B?UnJzNHZQbVBDTnZ0STdsTnY2My9CVHpMdEV6aWdOWXFMTlBYT0xlb0JBUjVj?= =?utf-8?B?eDNFNlhkamN1QWozMmJnS1plbVlidTFwNk5GUWYrRHo3NHFIYTN2TWtIL2tw?= =?utf-8?B?Nklpczc4eUFmejdDbTN3VXFDaGtiLzdwb013OTNwWkx4NTNyUjgxcnk4Ym5J?= =?utf-8?B?bDNxMzVHV1R0elhIRkEwV1dYb0Iyd1VFbEFlVW5WR0lKTS9rM2tEQWtKK0o2?= =?utf-8?B?dkN6UG94VG84Rk5qK1lwSVcxVWhZVmFiclhabXE5VFdGa3RxK05Gd0hKVW9H?= =?utf-8?B?QWxJNVQyc0wxVlk2eE5EWE55YVRCenVac092U2VjQWZQVDRORlBUZzZzcVVm?= =?utf-8?B?TXMyaDJIWEFKNUhLNEswMnIxVDh4Z1liWHBtS1kyekNqdVQ5WVZ2aEVGUS8r?= =?utf-8?B?d0RacXBxelZ0VnM4UEp3QURHdllPMy94VFpHQTVYOXdmVWxaZmdqOFNBa0xC?= =?utf-8?B?bTh5Nkt5cTFDaFdNTnlpRG5PWlRpNFlOWnZSZUNmcFExdEFKOFM1c1E4YlVO?= =?utf-8?B?dzZWOWdhd0J2OG8vWTk0QmY4UnFMNHJ1aGRhMFJRUzREbHJMTkgwa29Nc2N5?= =?utf-8?B?VFV3YXd6UDgwQ256d0Y5N3QzK1pHTVBYQzVSK3RJY0Q3VTVUbXJVdHpzK0U0?= =?utf-8?B?bUVSUEFjSVdVTDViRWlwaFNtanA1eXlJMGUrOTBJcXVOQ3VYcG1pejlNRjdn?= =?utf-8?B?SzJ6bW04c0trRWI5d3hUTlZWVVZtdWRjdlBvejlBNUxmQ0pNZG9jZCt0d2l4?= =?utf-8?Q?iSebOn12Fn6GvNuIbBpZCFs=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?VmJGMkZBaXBNbllXWVZYeXdkQlZTT0hLMmN1ZkpUQVhzTkNIZEh1RVUxTis0?= =?utf-8?B?QVdTdEYzZFZWbW5xU243K0hicHk5OGZlZHV2Rm1IWE9OSFdPRmpiV0Z5WXlW?= =?utf-8?B?UTZyK2tvNmlMem9jeTQwK3lRZ05yMHNmaXpuOVRTVlBiSHRmVjUwejNMT0F6?= =?utf-8?B?S1dBYzRpSWtTdEwyT0Fham42TjFJa3FpRGR6MkxYV3R5SndTczRLUUU2cXMx?= =?utf-8?B?VjFYZVZ3Rm1yUHkyblc2U0V3TGxYZlU0NHZjUEhaNTdmY0xLUGozUjNDb3Rx?= =?utf-8?B?T0NyendQL21UUTJYU3B1aThTanZqR3A5WEVpM3IrUk1ybXRMZE0vakxaN0dN?= =?utf-8?B?SEMrWkRydkVvUjJsSHR1YUFDUTBodmRMR2ZxbjlXR1BkMGkvWWpKVmtreG84?= =?utf-8?B?R1hoRWo3d2xvYm5vanUvVTBPTDVBeHljUjlYVzZ2cWF4dk1xVjAvaFVubjBj?= =?utf-8?B?NEh2dHIvNDVIK21zeU5HQVpEaW9nSzBDZ3FWL1V2RnFaM3R3VHVHcEU1RlFz?= =?utf-8?B?VUU5SS96TWNxNDd3clNEVVZSOEpHSkhNdGE0ZXNWTTJHb2FTKzJCNUJWM2l1?= =?utf-8?B?QWVMTkw5bFRMblB4WnpoWUpyL2dLUXdvQkpBRDNMeDcrUkJmalBROGl4Njg2?= =?utf-8?B?NXdMcWxqQWJ1OVpxK3hKeFN4MnlvbTZPcmZvNjB5WkFHZW9tRnV3TkZrM2ll?= =?utf-8?B?SXJwMFdMdDF3eHJUUGs5TVl0bmtva1NFUGhhcERTczVuY0tPNWJCaGpvK1FX?= =?utf-8?B?Z3NvdjErWmMrbGYzYjFlYmQ5M0dSZEt5NXRHL3pRUUp4QWMwLzB3OTNBVnBJ?= =?utf-8?B?eHMrOFhaWGxlNE1IZlUxa0laRFQxRlk0RldhL3hBdEMzWlRBQkhoc0dGbEdX?= =?utf-8?B?R1NEcWJ2cTJWZjF0TUVZN1lVeVJHT0J0RG9TMktGSUg3VHBpOTFkaHEvQWRo?= =?utf-8?B?YmYwc09oSUZpL2lrbVl4OCtZM0RMZVRUZytJRGxBR3ZuVlhJSUFjaktrbXBD?= =?utf-8?B?bUF5LzQ4VEVid2tKNkpuQWdaMmVoZUVFT1QyYzZzWEYxNHROMnRzSWJ0RkVo?= =?utf-8?B?ZUpMWGE5djVrUmJYZEUrWXBOS0pMdGI0bnA5aUxKa081M1lrQjlaNUxjVnV4?= =?utf-8?B?Q2tWVWE5S3ZYMlNmUTNyQTZYU0JSWGNoQlhLTTRjUldpOHhseVpWdG9kbTFT?= =?utf-8?B?SXUzTEN5dzVHcnV5TkZjYVdFSm1tYXQrZkcwZnIwR2h5ajkzZUR3L2QveHZP?= =?utf-8?B?NVhOZjhPSTJGOTZOMkRicjBaVEpuQkZSSDJwRmM2d0Y4N3NQRDlnTlhvSHB6?= =?utf-8?B?bmhhck42LzNoNWhqNXc3dTUrMXJtY3k3dGYwMUM2Y0pQbVRrYWxkb25RWk1j?= =?utf-8?B?Wllwb0lTN2l5UnNMNmtCbkhUYVR4QXRFU2s5QXZRZzd3d3B1NGFiUlkraTVW?= =?utf-8?B?SjFhNVFYY21oNnhIM1lqM1BSV0tyVzcydmFJckhhaVphMk5WVDBVa3ByTnJw?= =?utf-8?B?cEN2VTRGZXBTbTgvUTNhb2lQUlFQVFZuREQ0SGtXMFdaVlhDVGttNzdUYUVV?= =?utf-8?B?akJxT2RFUlRRTmNqRS9YMFllSWJOcDMxRStKRGcwdnYzWkpEWVgySnNVSVhq?= =?utf-8?B?YWVrdWNBb0J4VVVJQ3YzR0FTZ2dUdEc5S2FBKzYyLzZKQVpnZUJFcnZ3ZTc5?= =?utf-8?B?NUFjVVdjTjl6OXBzN0k5b25MOENJNExoVDdEcDlpcGZhV2JKZzZOalhDK2F0?= =?utf-8?B?SXRmSEFhWWI3TjBFenNQTENNTk1YQ1Yxdk5GczhDWklvNmtqMTVpL2dYQ01j?= =?utf-8?B?cmFjMG9Tc29oU25vZHlrRWtxcjFjc2IrVGtjQkxKbkZxYU92cVViSkc0S2wv?= =?utf-8?B?Zjlsb3A4Nll5VG5Ya3ZkRHcwS0M5aTRqWE9rZFJUN2gvQVBUZGh5WkFyTG92?= =?utf-8?B?V3RWZTZvWUxaRXVrcGZ0N1BtUGp3cysxdDYzYzNzRWxuakFPN2Y0aytmZHoz?= =?utf-8?B?dTQ1MHFaSlUxZ1RvT29iQjNDRVlSeXc2MFdPWVVDMjRpY0RjMEdEejNYL2pz?= =?utf-8?B?SkN1d0ZUaGVHZ2ZldTZ0YUFNa0ljT0FtSTdBSTkzUjBNQkFOcmdLY25FdUJT?= =?utf-8?B?S1Y1SkNCSG9sdk9wNmdkZUMzeS80RGh0WmFiSDdaTzRXSi90RzZhM1JONW1Q?= =?utf-8?B?eUI1YTNGVWhTTjVqUTJUa0kwUGxYQXk0MnFBeE1OQ0wrbkRZeWxxMFVrOFE0?= =?utf-8?B?QSsySTQ5N0N4NFpaaWlBcGdzdTk4RXBtSWRrQzIraVhPYkE1SFZmSkJYeHYy?= =?utf-8?B?V254ZXV2SHo5VzNsczh3SHZGbzdzaTF0U3lhcVA4TWZrU2d0akdHdz09?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: e56aff11-4b79-487a-7d20-08de6418083e X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:51.7008 (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: oeisuKftNUUSSfF1dqzbC8CyaoGd4Rvym4/ge/gDrVqss84IGqT0BDK0TD4KXfrhyO/EN6HYmRMDSdoo/m9rBQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 This patch introduces utility infrastructure for the nxpwifi driver, including common helper functions and Information Element (IE) management. Key additions: - `util.c/h`: General-purpose utilities for memory handling, debug info, workqueue helpers, channel validation, histogram tracking, and VDLL firmware block download. - `ie.c`: Functions for setting, updating, and deleting management IEs (e.g., beacon, probe response, association response), including support for vendor-specific IEs (WPS, P2P). - Support for automatic IE index allocation and multi-interface IE tracking. These utilities are foundational for driver features such as AP mode, debugging, and firmware interaction. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/ie.c | 480 +++++++ drivers/net/wireless/nxp/nxpwifi/util.c | 1523 +++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/util.h | 128 ++ 3 files changed, 2131 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/n= xp/nxpwifi/ie.c new file mode 100644 index 000000000000..294545996420 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/ie.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: management element handling - set/delete elements. + * + * Copyright 2011-2024 NXP + */ + +#include "main.h" +#include "cmdevt.h" + +/* Return true if the IE index is used by another interface. */ +static bool +nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private *priv, u16 idx) +{ + int i; + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_ie *ie; + + for (i =3D 0; i < adapter->priv_num; i++) { + if (adapter->priv[i] !=3D priv) { + ie =3D &adapter->priv[i]->mgmt_ie[idx]; + if (ie->mgmt_subtype_mask && ie->ie_length) + return true; + } + } + + return false; +} + +/* Pick an unused IE index for a new element. */ +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 =3D 0; i < priv->adapter->max_mgmt_ie_index; i++) { + mask =3D le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); + len =3D le16_to_cpu(ie->ie_length); + + if (mask =3D=3D NXPWIFI_AUTO_IDX_MASK) + continue; + + if (mask =3D=3D subtype_mask) { + if (len > IEEE_MAX_IE_SIZE) + continue; + + *index =3D i; + return 0; + } + + if (!priv->mgmt_ie[i].ie_length) { + if (nxpwifi_ie_index_used_by_other_intf(priv, i)) + continue; + + *index =3D i; + return 0; + } + } + + return -ENOENT; +} + +/* Build IE list and resolve AUTO index before sending 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 =3D le16_to_cpu(ie_list->len); + travel_len =3D sizeof(struct nxpwifi_ie_types_header); + + ie_list->len =3D 0; + + while (input_len >=3D sizeof(struct nxpwifi_ie_types_header)) { + ie =3D (struct nxpwifi_ie *)(((u8 *)ie_list) + travel_len); + tlv_len =3D le16_to_cpu(ie->ie_length); + travel_len +=3D tlv_len + NXPWIFI_IE_HDR_SIZE; + + if (input_len < tlv_len + NXPWIFI_IE_HDR_SIZE) + return -EINVAL; + index =3D le16_to_cpu(ie->ie_index); + mask =3D le16_to_cpu(ie->mgmt_subtype_mask); + + if (index =3D=3D NXPWIFI_AUTO_IDX_MASK) { + /* automatic addition */ + if (nxpwifi_ie_get_autoidx(priv, mask, ie, &index)) + return -ENOENT; + if (index =3D=3D NXPWIFI_AUTO_IDX_MASK) + return -EINVAL; + + tmp =3D (u8 *)&priv->mgmt_ie[index].ie_buffer; + memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); + priv->mgmt_ie[index].ie_length =3D ie->ie_length; + priv->mgmt_ie[index].ie_index =3D cpu_to_le16(index); + priv->mgmt_ie[index].mgmt_subtype_mask =3D + cpu_to_le16(mask); + + ie->ie_index =3D cpu_to_le16(index); + } else { + if (mask !=3D 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 =3D 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 -=3D tlv_len + NXPWIFI_IE_HDR_SIZE; + } + + if (GET_BSS_ROLE(priv) =3D=3D 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; +} + +/* Pack beacon/probe/assoc IEs into one list and update auto-assigned indi= ces. */ +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 =3D kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); + if (!ap_custom_ie) + return -ENOMEM; + + ap_custom_ie->type =3D cpu_to_le16(TLV_TYPE_MGMT_IE); + pos =3D (u8 *)ap_custom_ie->ie_list; + + if (beacon_ie) { + len =3D sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + memcpy(pos, beacon_ie, len); + pos +=3D len; + le16_unaligned_add_cpu(&ap_custom_ie->len, len); + } + if (pr_ie) { + len =3D sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + memcpy(pos, pr_ie, len); + pos +=3D len; + le16_unaligned_add_cpu(&ap_custom_ie->len, len); + } + if (ar_ie) { + len =3D sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(ar_ie->ie_length); + memcpy(pos, ar_ie, len); + pos +=3D len; + le16_unaligned_add_cpu(&ap_custom_ie->len, len); + } + + ret =3D nxpwifi_update_autoindex_ies(priv, ap_custom_ie); + + pos =3D (u8 *)(&ap_custom_ie->ie_list[0].ie_index); + if (beacon_ie && *beacon_idx =3D=3D NXPWIFI_AUTO_IDX_MASK) { + /* save beacon element index after auto-indexing */ + *beacon_idx =3D le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); + len =3D sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + pos +=3D len; + } + if (pr_ie && le16_to_cpu(pr_ie->ie_index) =3D=3D NXPWIFI_AUTO_IDX_MASK) { + /* save probe resp element index after auto-indexing */ + *probe_idx =3D *((u16 *)pos); + len =3D sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + pos +=3D len; + } + if (ar_ie && le16_to_cpu(ar_ie->ie_index) =3D=3D NXPWIFI_AUTO_IDX_MASK) + /* save assoc resp element index after auto-indexing */ + *assoc_idx =3D *((u16 *)pos); + + kfree(ap_custom_ie); + return ret; +} + +/* Append vendor IE (if present) into nxpwifi_ie, allocating as needed. */ +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 =3D *ie_ptr; + const u8 *vendor_ie; + + vendor_ie =3D cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); + if (vendor_ie) { + if (!*ie_ptr) { + *ie_ptr =3D kzalloc(sizeof(*ie_ptr), GFP_KERNEL); + if (!*ie_ptr) + return -ENOMEM; + ie =3D *ie_ptr; + } + + vs_ie =3D (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 =3D cpu_to_le16(mask); + ie->ie_index =3D cpu_to_le16(NXPWIFI_AUTO_IDX_MASK); + } + + *ie_ptr =3D ie; + return 0; +} + +/* Parse beacon/probe/assoc IEs from cfg80211 and push them to FW. */ +static int nxpwifi_set_mgmt_beacon_data_ies(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *data) +{ + struct nxpwifi_ie *beacon_ie =3D NULL, *pr_ie =3D NULL, *ar_ie =3D NULL; + u16 beacon_idx =3D NXPWIFI_AUTO_IDX_MASK, pr_idx =3D NXPWIFI_AUTO_IDX_MAS= K; + u16 ar_idx =3D NXPWIFI_AUTO_IDX_MASK; + int ret =3D 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 =3D 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 =3D beacon_idx; + priv->proberesp_idx =3D pr_idx; + priv->assocresp_idx =3D ar_idx; + +done: + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} + +/* Parse head/tail IEs from cfg80211_beacon_data and send them 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 =3D NXPWIFI_AUTO_IDX_MASK, ie_len =3D 0; + int left_len, parsed_len =3D 0; + unsigned int token_len; + int ret =3D 0; + + if (!info->tail || !info->tail_len) + return 0; + + gen_ie =3D kzalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + left_len =3D info->tail_len; + + /* Skip IEs generated by FW from bss configuration to avoid duplicates. */ + while (left_len > sizeof(struct element)) { + hdr =3D (void *)(info->tail + parsed_len); + token_len =3D hdr->datalen + sizeof(struct element); + if (token_len > left_len) { + ret =3D -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: + break; + case WLAN_EID_VENDOR_SPECIFIC: + /* Skip only Microsoft WMM element */ + 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 =3D -EINVAL; + goto done; + } + memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len); + ie_len +=3D token_len; + break; + } + left_len -=3D token_len; + parsed_len +=3D token_len; + } + + /* + * parse only WPA vendor element from tail, WMM element is configured by + * bss_config command + */ + vendorhdr =3D (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + info->tail, info->tail_len); + if (vendorhdr) { + token_len =3D vendorhdr->len + sizeof(struct element); + if (ie_len + token_len > IEEE_MAX_IE_SIZE) { + ret =3D -EINVAL; + goto done; + } + memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len); + ie_len +=3D token_len; + } + + if (!ie_len) + goto done; + + gen_ie->ie_index =3D cpu_to_le16(gen_idx); + gen_ie->mgmt_subtype_mask =3D cpu_to_le16(MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP); + gen_ie->ie_length =3D cpu_to_le16(ie_len); + + ret =3D nxpwifi_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, + NULL, NULL, NULL); + + if (ret) + goto done; + + priv->gen_idx =3D gen_idx; + + done: + kfree(gen_ie); + return ret; +} + +/* Parse head/tail/beacon/probe/assoc IEs and program the FW. */ +int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv, + struct cfg80211_beacon_data *info) +{ + int ret; + + ret =3D nxpwifi_uap_parse_tail_ies(priv, info); + + if (ret) + return ret; + + return nxpwifi_set_mgmt_beacon_data_ies(priv, info); +} + +/* Remove previously set management IEs. */ +int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv) +{ + struct nxpwifi_ie *beacon_ie =3D NULL, *pr_ie =3D NULL; + struct nxpwifi_ie *ar_ie =3D NULL, *gen_ie =3D NULL; + int ret =3D 0; + + if (priv->gen_idx !=3D NXPWIFI_AUTO_IDX_MASK) { + gen_ie =3D kmalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + gen_ie->ie_index =3D cpu_to_le16(priv->gen_idx); + gen_ie->mgmt_subtype_mask =3D cpu_to_le16(NXPWIFI_DELETE_MASK); + gen_ie->ie_length =3D 0; + ret =3D 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 =3D NXPWIFI_AUTO_IDX_MASK; + } + + if (priv->beacon_idx !=3D NXPWIFI_AUTO_IDX_MASK) { + beacon_ie =3D kmalloc(sizeof(*beacon_ie), GFP_KERNEL); + if (!beacon_ie) { + ret =3D -ENOMEM; + goto done; + } + beacon_ie->ie_index =3D cpu_to_le16(priv->beacon_idx); + beacon_ie->mgmt_subtype_mask =3D cpu_to_le16(NXPWIFI_DELETE_MASK); + beacon_ie->ie_length =3D 0; + } + if (priv->proberesp_idx !=3D NXPWIFI_AUTO_IDX_MASK) { + pr_ie =3D kmalloc(sizeof(*pr_ie), GFP_KERNEL); + if (!pr_ie) { + ret =3D -ENOMEM; + goto done; + } + pr_ie->ie_index =3D cpu_to_le16(priv->proberesp_idx); + pr_ie->mgmt_subtype_mask =3D cpu_to_le16(NXPWIFI_DELETE_MASK); + pr_ie->ie_length =3D 0; + } + if (priv->assocresp_idx !=3D NXPWIFI_AUTO_IDX_MASK) { + ar_ie =3D kmalloc(sizeof(*ar_ie), GFP_KERNEL); + if (!ar_ie) { + ret =3D -ENOMEM; + goto done; + } + ar_ie->ie_index =3D cpu_to_le16(priv->assocresp_idx); + ar_ie->mgmt_subtype_mask =3D cpu_to_le16(NXPWIFI_DELETE_MASK); + ar_ie->ie_length =3D 0; + } + + if (beacon_ie || pr_ie || ar_ie) + ret =3D 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..1e67883fa007 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/util.c @@ -0,0 +1,1523 @@ +// 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[] =3D { + {"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}, + {"rx_pending", adapter_item_size(rx_pending), + adapter_item_addr(rx_pending), 1}, +}; + +static int num_of_items =3D ARRAY_SIZE(items); + +/* Send init or shutdown command to firmware. */ +int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv, + u32 func_init_shutdown) +{ + u16 cmd; + + if (func_init_shutdown =3D=3D NXPWIFI_FUNC_INIT) { + cmd =3D HOST_CMD_FUNC_INIT; + } else if (func_init_shutdown =3D=3D NXPWIFI_FUNC_SHUTDOWN) { + cmd =3D 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); + +/* Handle IOCTL get/set of debug info across driver structures. */ +int nxpwifi_get_debug_info(struct nxpwifi_private *priv, + struct nxpwifi_debug_info *info) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + + if (info) { + info->debug_mask =3D adapter->debug_mask; + memcpy(info->packets_out, + priv->wmm.packets_out, + sizeof(priv->wmm.packets_out)); + info->curr_tx_buf_size =3D (u32)adapter->curr_tx_buf_size; + info->tx_buf_size =3D (u32)adapter->tx_buf_size; + info->rx_tbl_num =3D nxpwifi_get_rx_reorder_tbl(priv, + info->rx_tbl); + info->tx_tbl_num =3D nxpwifi_get_tx_ba_stream_tbl(priv, + info->tx_tbl); + info->ps_mode =3D adapter->ps_mode; + info->ps_state =3D adapter->ps_state; + info->is_deep_sleep =3D adapter->is_deep_sleep; + info->pm_wakeup_card_req =3D adapter->pm_wakeup_card_req; + info->pm_wakeup_fw_try =3D adapter->pm_wakeup_fw_try; + info->is_hs_configured =3D test_bit(NXPWIFI_IS_HS_CONFIGURED, + &adapter->work_flags); + info->hs_activated =3D adapter->hs_activated; + info->is_cmd_timedout =3D test_bit(NXPWIFI_IS_CMD_TIMEDOUT, + &adapter->work_flags); + info->num_cmd_host_to_card_failure =3D + adapter->dbg.num_cmd_host_to_card_failure; + info->num_cmd_sleep_cfm_host_to_card_failure =3D + adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + info->num_tx_host_to_card_failure =3D + adapter->dbg.num_tx_host_to_card_failure; + info->num_event_deauth =3D adapter->dbg.num_event_deauth; + info->num_event_disassoc =3D adapter->dbg.num_event_disassoc; + info->num_event_link_lost =3D adapter->dbg.num_event_link_lost; + info->num_cmd_deauth =3D adapter->dbg.num_cmd_deauth; + info->num_cmd_assoc_success =3D + adapter->dbg.num_cmd_assoc_success; + info->num_cmd_assoc_failure =3D + adapter->dbg.num_cmd_assoc_failure; + info->num_tx_timeout =3D adapter->dbg.num_tx_timeout; + info->timeout_cmd_id =3D adapter->dbg.timeout_cmd_id; + info->timeout_cmd_act =3D 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 =3D 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 =3D adapter->dbg.last_cmd_resp_index; + memcpy(info->last_event, adapter->dbg.last_event, + sizeof(adapter->dbg.last_event)); + info->last_event_index =3D 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 =3D adapter->dbg.last_sdio_mp_index; + info->data_sent =3D adapter->data_sent; + info->cmd_sent =3D adapter->cmd_sent; + info->cmd_resp_received =3D 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 =3D buf; + struct nxpwifi_debug_data *d =3D &items[0]; + size_t size, addr; + long val; + int i, j; + + if (!info) + return 0; + + for (i =3D 0; i < num_of_items; i++) { + p +=3D sprintf(p, "%s=3D", d[i].name); + + size =3D d[i].size / d[i].num; + + if (i < (num_of_items - 3)) + addr =3D d[i].addr + (size_t)info; + else /* The last 3 items are struct nxpwifi_adapter variables */ + addr =3D d[i].addr + (size_t)priv->adapter; + + for (j =3D 0; j < d[i].num; j++) { + switch (size) { + case 1: + val =3D *((u8 *)addr); + break; + case 2: + val =3D get_unaligned((u16 *)addr); + break; + case 4: + val =3D get_unaligned((u32 *)addr); + break; + case 8: + val =3D get_unaligned((long long *)addr); + break; + default: + val =3D -1; + break; + } + + p +=3D sprintf(p, "%#lx ", val); + addr +=3D size; + } + + p +=3D sprintf(p, "\n"); + } + + if (info->tx_tbl_num) { + p +=3D sprintf(p, "Tx BA stream table:\n"); + for (i =3D 0; i < info->tx_tbl_num; i++) + p +=3D sprintf(p, "tid =3D %d, ra =3D %pM\n", + info->tx_tbl[i].tid, info->tx_tbl[i].ra); + } + + if (info->rx_tbl_num) { + p +=3D sprintf(p, "Rx reorder table:\n"); + for (i =3D 0; i < info->rx_tbl_num; i++) { + p +=3D sprintf(p, "tid =3D %d, ta =3D %pM, ", + info->rx_tbl[i].tid, + info->rx_tbl[i].ta); + p +=3D sprintf(p, "start_win =3D %d, ", + info->rx_tbl[i].start_win); + p +=3D sprintf(p, "win_size =3D %d, buffer: ", + info->rx_tbl[i].win_size); + + for (j =3D 0; j < info->rx_tbl[i].win_size; j++) + p +=3D sprintf(p, "%c ", + info->rx_tbl[i].buffer[j] ? + '1' : '0'); + + p +=3D 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 =3D priv->adapter; + int i; + struct nxpwifi_private *tmp_priv; + u8 bss_role =3D GET_BSS_ROLE(priv); + struct ieee80211_channel *set_chan; + + for (i =3D 0; i < adapter->priv_num; i++) { + tmp_priv =3D adapter->priv[i]; + if (tmp_priv =3D=3D priv) + continue; + + set_chan =3D NULL; + if (bss_role =3D=3D NXPWIFI_BSS_ROLE_STA) { + if (GET_BSS_ROLE(tmp_priv) =3D=3D NXPWIFI_BSS_ROLE_UAP && + netif_carrier_ok(tmp_priv->netdev) && + cfg80211_chandef_valid(&tmp_priv->bss_chandef)) + set_chan =3D tmp_priv->bss_chandef.chan; + } else if (bss_role =3D=3D NXPWIFI_BSS_ROLE_UAP) { + struct nxpwifi_current_bss_params *bss_params =3D + &tmp_priv->curr_bss_params; + int channel =3D bss_params->bss_descriptor.channel; + enum nl80211_band band =3D + nxpwifi_band_to_radio_type(bss_params->band); + int freq =3D + ieee80211_channel_to_frequency(channel, band); + + if (GET_BSS_ROLE(tmp_priv) =3D=3D NXPWIFI_BSS_ROLE_STA && + tmp_priv->media_connected) + set_chan =3D 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; +} + +void nxpwifi_convert_chan_to_band_cfg(struct nxpwifi_private *priv, + u8 *band_cfg, + struct cfg80211_chan_def *chan_def) +{ + u8 chan_band =3D 0, chan_width =3D 0, chan2_offset =3D 0; + + switch (chan_def->chan->band) { + case NL80211_BAND_2GHZ: + chan_band =3D BAND_2GHZ; + break; + case NL80211_BAND_5GHZ: + chan_band =3D BAND_5GHZ; + break; + default: + break; + } + + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + chan_width =3D CHAN_BW_20MHZ; + break; + case NL80211_CHAN_WIDTH_40: + chan_width =3D CHAN_BW_40MHZ; + if (chan_def->center_freq1 > chan_def->chan->center_freq) + chan2_offset =3D IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + chan2_offset =3D IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case NL80211_CHAN_WIDTH_80: + chan2_offset =3D + nxpwifi_get_sec_chan_offset(chan_def->chan->hw_value); + chan_width =3D CHAN_BW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + default: + nxpwifi_dbg(priv->adapter, + WARN, "Unknown channel width: %d\n", + chan_def->width); + break; + } + + *band_cfg =3D ((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); +} + +static int +nxpwifi_parse_mgmt_packet(struct nxpwifi_private *priv, u8 *payload, u16 l= en, + struct rxpd *rx_pd) +{ + u16 stype; + u8 category; + struct ieee80211_hdr *ieee_hdr =3D (void *)payload; + + stype =3D (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE); + + switch (stype) { + case IEEE80211_STYPE_ACTION: + category =3D *(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; +} + +/* Send deauth frame to cfg80211. */ +void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv, + u16 reason_code, u8 *sa) +{ + u8 frame_buf[100]; + struct ieee80211_mgmt *mgmt =3D (struct ieee80211_mgmt *)frame_buf; + + memset(frame_buf, 0, sizeof(frame_buf)); + mgmt->frame_control =3D cpu_to_le16(IEEE80211_STYPE_DEAUTH); + mgmt->duration =3D 0; + mgmt->seq_ctrl =3D 0; + mgmt->u.deauth.reason_code =3D cpu_to_le16(reason_code); + + if (GET_BSS_ROLE(priv) =3D=3D 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 =3D 0; + priv->auth_alg =3D 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) !=3D NXPWIFI_BSS_ROLE_UAP) { + cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26); + } else { + cfg80211_rx_mgmt(&priv->wdev, + priv->bss_chandef.chan->center_freq, + 0, frame_buf, 26, 0); + } +} + +/* Parse and forward received management packet to cfg80211. */ +int +nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =3D 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 =3D=3D NL80211_IFTYPE_UNSPECIFIED) { + nxpwifi_dbg(adapter, ERROR, + "do not receive mgmt frames on uninitialized intf"); + return -EINVAL; + } + + rx_pd =3D (struct rxpd *)skb->data; + pkt_len =3D 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 -=3D sizeof(pkt_len); + + ieee_hdr =3D (void *)skb->data; + if (ieee80211_is_mgmt(ieee_hdr->frame_control)) { + ret =3D 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 -=3D ETH_ALEN; + rx_pd->rx_pkt_length =3D cpu_to_le16(pkt_len); + + if (priv->host_mlme_reg && + (GET_BSS_ROLE(priv) !=3D 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 !=3D WLAN_AUTH_SAE) { + priv->auth_flag &=3D + ~HOST_MLME_AUTH_PENDING; + priv->auth_flag |=3D + 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 =3D 0; + priv->auth_alg =3D WLAN_AUTH_NONE; + } else { + nxpwifi_dbg(adapter, MSG, + "assoc: receive disassoc from %pM\n", + ieee_hdr->addr3); + } + } + + rx_info =3D NXPWIFI_SKB_RXCB(skb); + rx_info->pkt_len =3D pkt_len; + skb_queue_tail(&adapter->rx_mlme_q, skb); + nxpwifi_queue_wiphy_work(adapter, &adapter->host_mlme_work); + return -EINPROGRESS; + } + + if (GET_BSS_ROLE(priv) =3D=3D 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; +} + +#define DOT11_MAX_PRIORITY 8 +#define IEEE80211_RADIOTAP_HE 23 + +u8 ru_signal[16][9] =3D { +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, +{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x07}, +{0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x07}, +{0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x06}, +{0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07}, +{0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0x06}, +{0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0x06}, +{0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0x05}, +{0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, +{0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x06}, +{0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x06}, +{0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0xff, 0x01, 0x05}, +{0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06}, +{0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0x05}, +{0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0x00, 0x00, 0x05}, +{0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0x04}}; + +u8 ru_signal_106[14][9] =3D { +{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, +{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, +{0xff, 0x01, 0xff, 0x01, 0xff, 0xff, 0xff, 0x02, 0x03}, +{0xff, 0xff, 0xff, 0x02, 0xff, 0x01, 0xff, 0x01, 0x03}, +{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x02, 0x05}, +{0x00, 0x00, 0xff, 0x01, 0xff, 0xff, 0xff, 0x02, 0x04}, +{0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x02, 0x04}, +{0xff, 0x01, 0xff, 0x01, 0xff, 0xff, 0xff, 0x02, 0x03}, +{0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05}, +{0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0xff, 0x01, 0x04}, +{0xff, 0xff, 0xff, 0x02, 0xff, 0x01, 0x00, 0x00, 0x04}, +{0xff, 0xff, 0xff, 0x02, 0xff, 0x01, 0xff, 0x01, 0x03}, +{0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0x02, 0x02}, +{0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0x04}}; + +u8 ru_signal_52[9] =3D {0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0x= 04}; + +static int +nxpwifi_rxpdinfo_to_radiotapinfo(struct nxpwifi_private *priv, + struct rxpd *rx_pd, + struct rxpd_extra_info *extra_info, + struct radiotap_info *rt_info) +{ + struct radiotap_info rt_info_tmp; + u8 rx_rate_info =3D 0; + u8 mcs_index =3D 0; + u8 format =3D 0; + u8 bw =3D 0; + u8 gi =3D 0; + u8 ldpc =3D 0; + u8 nss =3D 0; + u8 dcm =3D 0; + u32 rx_info; + + memset(&rt_info_tmp, 0, sizeof(rt_info_tmp)); + rx_info =3D le32_to_cpu(rx_pd->rx_info); + rt_info_tmp.snr =3D rx_pd->snr; + rt_info_tmp.nf =3D rx_pd->nf; + rt_info_tmp.band_config =3D rx_info & 0xf; + rt_info_tmp.chan_num =3D (rx_info & RXPD_CHAN_MASK) >> 5; + + rt_info_tmp.antenna =3D rx_pd->antenna; + rx_rate_info =3D rx_pd->ht_info; + if ((rx_rate_info & 0x3) =3D=3D NXPWIFI_RATE_FORMAT_HE) { + u8 gi_he =3D 0; + /* HE rate */ + format =3D NXPWIFI_RATE_FORMAT_HE; + mcs_index =3D min(rx_pd->rx_rate & 0xF, 0xb); + nss =3D ((rx_pd->rx_rate & 0xF0) >> 4); + nss =3D min(nss + 1, 2); + /* 20M: bw=3D0, 40M: bw=3D1, 80M: bw=3D2, 160M: bw=3D3 */ + bw =3D (rx_rate_info & 0xC) >> 2; + gi =3D (rx_rate_info & 0x10) >> 4; + gi_he =3D (rx_rate_info & 0x80) >> 7; + gi =3D gi | gi_he; + dcm =3D (rx_info & RXPD_DCM_MASK) >> 16; + } else if ((rx_rate_info & 0x3) =3D=3D NXPWIFI_RATE_FORMAT_VHT) { + /* VHT rate */ + format =3D NXPWIFI_RATE_FORMAT_VHT; + mcs_index =3D min(rx_pd->rx_rate & 0xF, 9); + nss =3D ((rx_pd->rx_rate & 0xF0) >> 4); + nss =3D min(nss + 1, 2); + /* 20M: bw=3D0, 40M: bw=3D1, 80M: bw=3D2, 160M: bw=3D3 */ + bw =3D (rx_rate_info & 0xC) >> 2; + /* LGI: gi =3D0, SGI: gi =3D 1 */ + gi =3D (rx_rate_info & 0x10) >> 4; + } else if ((rx_rate_info & 0x3) =3D=3D NXPWIFI_RATE_FORMAT_HT) { + /* HT rate */ + format =3D NXPWIFI_RATE_FORMAT_HT; + mcs_index =3D rx_pd->rx_rate; + /* 20M: bw=3D0, 40M: bw=3D1 */ + bw =3D (rx_rate_info & 0xC) >> 2; + /* LGI: gi =3D0, SGI: gi =3D 1 */ + gi =3D (rx_rate_info & 0x10) >> 4; + } else { + /* LG rate */ + format =3D NXPWIFI_RATE_FORMAT_LG; + mcs_index =3D (rx_pd->rx_rate > NXPWIFI_RATE_INDEX_OFDM0) ? + rx_pd->rx_rate - 1 : rx_pd->rx_rate; + } + ldpc =3D rx_rate_info & 0x40; + + rt_info_tmp.rate_info.mcs_index =3D mcs_index; + rt_info_tmp.rate_info.nss_index =3D nss; + rt_info_tmp.rate_info.dcm =3D dcm; + if (format =3D=3D NXPWIFI_RATE_FORMAT_HE) + rt_info_tmp.rate_info.rate_info =3D + (ldpc << 5) | (format << 3) | (bw << 1) | (gi << 6); + else + rt_info_tmp.rate_info.rate_info =3D + (ldpc << 5) | (format << 3) | (bw << 1) | gi; + rt_info_tmp.rate_info.bitrate =3D + nxpwifi_index_to_acs_data_rate(priv, rx_pd->rx_rate, + rx_pd->ht_info); + + if (rx_pd->flags & RXPD_FLAG_EXTRA_HEADER) { + memcpy(&rt_info_tmp.extra_info, (u8 *)extra_info, + sizeof(struct rxpd_extra_info)); + } + + memcpy(rt_info, &rt_info_tmp, sizeof(*rt_info)); + + return 0; +} + +int +nxpwifi_recv_packet_to_monif(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct radiotap_header *rth; + struct radiotap_info rt_info; + struct rxpd *local_rx_pd; + struct rxpd rx_pd; + struct ieee80211_hdr *dot11_hdr; + struct rxpd_extra_info extra_info; + u16 rx_pkt_offset =3D 0, offset; + u8 format =3D 0, mcs =3D 0, nss =3D 0, bw =3D 0, gi =3D 0, ldpc =3D 0, ch= an_num =3D 0; + u8 band =3D 0; + u8 *payload =3D NULL; + u32 vht_sig1 =3D 0, vht_sig2 =3D 0, he_sig1 =3D 0, he_sig2 =3D 0, usr_idx= =3D 0, out =3D 0; + u32 tone =3D 0; + u8 dcm =3D 0; + int freq; + u64 ts_us; + u16 data1 =3D 0, data2 =3D 0, data3 =3D 0, data5 =3D 0, data6 =3D 0; + + if (!skb) + return -ENOMEM; + + local_rx_pd =3D (struct rxpd *)skb->data; + rx_pkt_offset =3D le16_to_cpu(local_rx_pd->rx_pkt_offset); + dot11_hdr =3D (struct ieee80211_hdr *)(local_rx_pd + rx_pkt_offset); + + if (skb_headroom(skb) + rx_pkt_offset < sizeof(struct radiotap_header)) { + nxpwifi_dbg(priv->adapter, ERROR, + "No space to add Radio TAP header\n"); + return -EINVAL; + } + + memcpy(&rx_pd, local_rx_pd, sizeof(struct rxpd)); + memcpy(&extra_info, local_rx_pd + sizeof(struct rxpd), + sizeof(struct rxpd_extra_info)); + + if (rx_pkt_offset > sizeof(struct radiotap_header)) { + offset =3D rx_pkt_offset - sizeof(struct radiotap_header); + skb_pull(skb, offset); + } else { + if (skb_headroom(skb) + rx_pkt_offset < + sizeof(struct radiotap_header)) { + nxpwifi_dbg(priv->adapter, ERROR, + "No space to add Radio TAP header\n"); + return -EINVAL; + } + offset =3D sizeof(struct radiotap_header) - rx_pkt_offset; + skb_push(skb, offset); + } + + rth =3D (struct radiotap_header *)skb->data; + memset(rth, 0, sizeof(struct radiotap_header)); + + nxpwifi_rxpdinfo_to_radiotapinfo(priv, &rx_pd, &extra_info, &rt_info); + + ldpc =3D (rt_info.rate_info.rate_info & 0x20) >> 5; + format =3D (rt_info.rate_info.rate_info & 0x18) >> 3; + bw =3D (rt_info.rate_info.rate_info & 0x06) >> 1; + dcm =3D rt_info.rate_info.dcm; + if (format =3D=3D NXPWIFI_RATE_FORMAT_HE) + gi =3D (rt_info.rate_info.rate_info & 0xC0) >> 6; + else + gi =3D rt_info.rate_info.rate_info & 0x01; + mcs =3D rt_info.rate_info.mcs_index; + nss =3D rt_info.rate_info.nss_index; + + rth->hdr.it_version =3D PKTHDR_RADIOTAP_VERSION; + rth->hdr.it_pad =3D 0; + rth->hdr.it_len =3D cpu_to_le16(sizeof(struct radiotap_header)); + rth->hdr.it_present =3D + cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | + (1 << IEEE80211_RADIOTAP_ANTENNA)); + /** Timestamp */ + ts_us =3D ktime_get_ns() / 1000; /* nanoseconds =E2=86=92 microseconds */ + rth->body.timestamp =3D cpu_to_le64(ts_us); + /** Flags */ + rth->body.flags =3D (rt_info.extra_info.flags & + ~(RADIOTAP_FLAGS_USE_SGI_HT | + RADIOTAP_FLAGS_WITH_FRAGMENT | + RADIOTAP_FLAGS_WEP_ENCRYPTION | + RADIOTAP_FLAGS_FAILED_FCS_CHECK)); + /* + * reverse fail fcs, 1 means pass FCS in FW, but means + * fail FCS in radiotap + */ + rth->body.flags |=3D (~rt_info.extra_info.flags) & + RADIOTAP_FLAGS_FAILED_FCS_CHECK; + + if (format =3D=3D NXPWIFI_RATE_FORMAT_HT && gi =3D=3D 1) + rth->body.flags |=3D RADIOTAP_FLAGS_USE_SGI_HT; + if (ieee80211_is_mgmt(dot11_hdr->frame_control) || + ieee80211_is_data(dot11_hdr->frame_control)) { + if (ieee80211_has_morefrags(dot11_hdr->frame_control) || + (!ieee80211_is_first_frag(dot11_hdr->seq_ctrl))) { + rth->body.flags |=3D RADIOTAP_FLAGS_WITH_FRAGMENT; + } + } + if (ieee80211_is_data(dot11_hdr->frame_control) && + ieee80211_has_protected(dot11_hdr->frame_control)) { + payload =3D (u8 *)dot11_hdr + + ieee80211_hdrlen(dot11_hdr->frame_control); + + /** ExtIV bit shall be 0 for WEP frame */ + if (!(*(payload + 3) & 0x20)) + rth->body.flags |=3D RADIOTAP_FLAGS_WEP_ENCRYPTION; + } + /** Rate, u8 only apply for LG mode */ + if (format =3D=3D NXPWIFI_RATE_FORMAT_LG) { + rth->hdr.it_present |=3D + cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); + rth->body.rate =3D rt_info.rate_info.bitrate; + } + + /** Channel */ + rth->body.channel.flags =3D 0; + chan_num =3D rt_info.chan_num; + + band =3D (chan_num <=3D 14) ? NL80211_BAND_2GHZ : + NL80211_BAND_5GHZ; + + freq =3D ieee80211_channel_to_frequency(chan_num, band); + + if (freq < 0) + return freq; + + rth->body.channel.frequency =3D cpu_to_le16((u16)freq); + + if (band =3D=3D NL80211_BAND_2GHZ) + rth->body.channel.flags |=3D + cpu_to_le16(CHANNEL_FLAGS_2GHZ | + CHANNEL_FLAGS_DYNAMIC_CCK_OFDM); + else + rth->body.channel.flags |=3D + cpu_to_le16(CHANNEL_FLAGS_5GHZ | CHANNEL_FLAGS_OFDM); + + /** Antenna */ + rth->body.antenna_signal =3D -(rt_info.nf - rt_info.snr); + rth->body.antenna_noise =3D -rt_info.nf; + /* Convert FW antenna value to radiotap spec */ + rth->body.antenna =3D rt_info.antenna >> 1; + /** MCS */ + if (format =3D=3D NXPWIFI_RATE_FORMAT_HT) { + rth->hdr.it_present |=3D + cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); + rth->body.u.mcs.known =3D + rt_info.extra_info.mcs_known; + rth->body.u.mcs.flags =3D + rt_info.extra_info.mcs_flags; + /** MCS mcs */ + rth->body.u.mcs.known |=3D + MCS_KNOWN_MCS_INDEX_KNOWN; + rth->body.u.mcs.mcs =3D + rt_info.rate_info.mcs_index; + /** MCS bw */ + rth->body.u.mcs.known |=3D MCS_KNOWN_BANDWIDTH; + /** Clear, 20MHz as default */ + rth->body.u.mcs.flags &=3D ~(0x03); + if (bw =3D=3D 1) + rth->body.u.mcs.flags |=3D RX_BW_40; + /** MCS gi */ + rth->body.u.mcs.known |=3D + MCS_KNOWN_GUARD_INTERVAL; + rth->body.u.mcs.flags &=3D ~(1 << 2); + if (gi) + rth->body.u.mcs.flags |=3D gi << 2; + /** MCS FEC */ + rth->body.u.mcs.known |=3D MCS_KNOWN_FEC_TYPE; + rth->body.u.mcs.flags &=3D ~(1 << 4); + if (ldpc) + rth->body.u.mcs.flags |=3D ldpc << 4; + } + /** VHT */ + if (format =3D=3D NXPWIFI_RATE_FORMAT_VHT) { + vht_sig1 =3D rt_info.extra_info.vht_he_sig1; + vht_sig2 =3D rt_info.extra_info.vht_he_sig2; + /** Present Flag */ + rth->hdr.it_present |=3D + cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); + /** STBC */ + rth->body.u.vht.known |=3D VHT_KNOWN_STBC; + + if (vht_sig1 & MBIT(3)) + rth->body.u.vht.flags |=3D VHT_FLAG_STBC; + /** TXOP_PS_NA */ + /** TODO: Not support now */ + /** GI */ + rth->body.u.vht.known |=3D VHT_KNOWN_GI; + if (vht_sig2 & MBIT(0)) + rth->body.u.vht.flags |=3D VHT_FLAG_SGI; + /** SGI NSYM DIS */ + rth->body.u.vht.known |=3D VHT_KNOWN_SGI_NSYM_DIS; + if (vht_sig2 & MBIT(1)) + rth->body.u.vht.flags |=3D + VHT_FLAG_SGI_NSYM_M10_9; + /** LDPC_EXTRA_OFDM_SYM */ + /** TODO: Not support now */ + /** BEAMFORMED */ + rth->body.u.vht.known |=3D VHT_KNOWN_BEAMFORMED; + if (vht_sig2 & MBIT(8)) + rth->body.u.vht.flags |=3D + VHT_FLAG_BEAMFORMED; + /** BANDWIDTH */ + rth->body.u.vht.known |=3D VHT_KNOWN_BANDWIDTH; + if (bw =3D=3D 1) + rth->body.u.vht.bandwidth =3D RX_BW_40; + else if (bw =3D=3D 2) + rth->body.u.vht.bandwidth =3D RX_BW_80; + /** GROUP_ID */ + rth->body.u.vht.known |=3D VHT_KNOWN_GROUP_ID; + rth->body.u.vht.group_id =3D + (vht_sig1 & (0x3F0)) >> 4; + /** PARTIAL_AID */ + /** TODO: Not support now */ + /** mcs_nss */ + rth->body.u.vht.mcs_nss[0] =3D + vht_sig2 & (0xF0); + /* Convert FW NSS value to radiotap spec */ + rth->body.u.vht.mcs_nss[0] |=3D + ((vht_sig1 & (0x1C00)) >> 10) + 1; + /** gi */ + rth->body.u.vht.known |=3D VHT_KNOWN_GI; + if (gi) + rth->body.u.vht.flags |=3D + VHT_FLAG_SGI; + /** coding */ + if (vht_sig2 & MBIT(2)) + rth->body.u.vht.coding |=3D + VHT_CODING_LDPC_USER0; + } + if (format =3D=3D NXPWIFI_RATE_FORMAT_HE) { + he_sig1 =3D rt_info.extra_info.vht_he_sig1; + he_sig2 =3D rt_info.extra_info.vht_he_sig2; + usr_idx =3D rt_info.extra_info.user_idx; + rth->hdr.it_present |=3D cpu_to_le32(1 << IEEE80211_RADIOTAP_HE); + rth->body.u.he.data1 |=3D cpu_to_le16(HE_CODING_KNOWN); + if (ldpc) + rth->body.u.he.data3 |=3D cpu_to_le16(HE_CODING_LDPC_USER0); + rth->body.u.he.data1 |=3D cpu_to_le16(HE_BW_KNOWN); + if (he_sig1) + rth->body.u.he.data1 |=3D cpu_to_le16(HE_MU_DATA); + if (bw =3D=3D 1) { + rth->body.u.he.data5 |=3D cpu_to_le16(RX_HE_BW_40); + if (he_sig2) { + NXPWIFI_DECODE_RU_SIGNALING_CH1(out, he_sig1, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + if (!tone) { + NXPWIFI_DECODE_RU_SIGNALING_CH3(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (tone) { + rth->body.u.he.data5 &=3D cpu_to_le16(~RX_HE_BW_40); + rth->body.u.he.data5 |=3D cpu_to_le16(tone); + } + } + } else if (bw =3D=3D 2) { + rth->body.u.he.data5 |=3D cpu_to_le16(RX_HE_BW_80); + if (he_sig2) { + NXPWIFI_DECODE_RU_SIGNALING_CH1(out, he_sig1, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + if (!tone) { + NXPWIFI_DECODE_RU_SIGNALING_CH2(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (!tone) { + if ((he_sig2 & NXPWIFI_80_CENTER_RU) && !usr_idx) + tone =3D RU_TONE_26; + else + usr_idx--; + } + if (!tone) { + NXPWIFI_DECODE_RU_SIGNALING_CH3(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (!tone) { + NXPWIFI_DECODE_RU_SIGNALING_CH4(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (tone) { + rth->body.u.he.data5 &=3D cpu_to_le16(~RX_HE_BW_80); + rth->body.u.he.data5 |=3D cpu_to_le16(tone); + } + } + } else if (bw =3D=3D 3) { + rth->body.u.he.data5 |=3D cpu_to_le16(RX_HE_BW_160); + if (he_sig2) { + NXPWIFI_DECODE_RU_SIGNALING_CH1(out, he_sig1, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + if (!tone) { + NXPWIFI_DECODE_RU_SIGNALING_CH2(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (!tone) { + if ((he_sig2 & NXPWIFI_160_CENTER_RU) && !usr_idx) + tone =3D RU_TONE_26; + else + usr_idx--; + } + if (!tone) { + NXPWIFI_DECODING_160_RU_CH3(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (!tone) { + NXPWIFI_DECODING_160_RU_CH3(out, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + } + if (tone !=3D 0) { + rth->body.u.he.data5 &=3D cpu_to_le16(~RX_HE_BW_160); + rth->body.u.he.data5 |=3D cpu_to_le16(tone); + } + } + } else { + if (he_sig2) { + NXPWIFI_DECODE_RU_SIGNALING_CH1(out, he_sig1, he_sig2); + nxpwifi_decode_ru_tone(&out, &usr_idx, &tone); + if (tone) + rth->body.u.he.data5 |=3D cpu_to_le16(tone); + } + } + + data2 |=3D HE_DATA_GI_KNOWN; + data5 |=3D ((gi & 3) << 4); + data1 |=3D HE_MCS_KNOWN; + data3 |=3D (mcs << 8); + data6 |=3D nss; + data1 |=3D HE_DCM_KNOWN; + data5 |=3D (dcm << 12); + rth->body.u.he.data1 =3D cpu_to_le16(data1); + rth->body.u.he.data2 =3D cpu_to_le16(data2); + rth->body.u.he.data3 =3D cpu_to_le16(data3); + rth->body.u.he.data5 =3D cpu_to_le16(data5); + rth->body.u.he.data6 =3D cpu_to_le16(data6); + } + + /* Trim off the last 4 bytes in skb data */ + skb_trim(skb, skb->len - 4); + + priv->stats.rx_bytes +=3D skb->len; + priv->stats.rx_packets++; + + skb->dev =3D priv->netdev; + skb->pkt_type =3D PACKET_OTHERHOST; + skb->protocol =3D eth_type_trans(skb, priv->netdev); + skb->ip_summed =3D CHECKSUM_NONE; + netif_rx(skb); + + return 0; +} + +/* Process received packet and pass to net stack; reuse or build skb as ne= eded. */ +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 +=3D skb->len; + priv->stats.rx_packets++; + + if (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + p_ethhdr =3D (void *)skb->data; + rcu_read_lock(); + src_node =3D nxpwifi_get_sta_entry(priv, p_ethhdr->h_source); + if (src_node) { + src_node->stats.last_rx =3D jiffies; + src_node->stats.rx_bytes +=3D skb->len; + src_node->stats.rx_packets++; + } + rcu_read_unlock(); + } + + skb->dev =3D priv->netdev; + skb->protocol =3D eth_type_trans(skb, priv->netdev); + skb->ip_summed =3D CHECKSUM_NONE; + + netif_rx(skb); + return 0; +} + +/* IOCTL completion callback: wake waiters or process response as needed. = */ +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=3D%d\n", + adapter->cmd_wait_q.status); + + *cmd_node->condition =3D true; + wake_up_interruptible(&adapter->cmd_wait_q.wait); + + return 0; +} + +/* Find STA entry by MAC under rcu_read_lock(); return NULL if not found. = */ +struct nxpwifi_sta_node * +nxpwifi_get_sta_entry(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + struct nxpwifi_sta_node *found =3D NULL; + + if (!mac) + return NULL; + list_for_each_entry_rcu(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) { + found =3D node; + break; + } + } + + return found; +} + +struct nxpwifi_sta_node * +nxpwifi_get_sta_entry_rcu(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + + rcu_read_lock(); + node =3D nxpwifi_get_sta_entry(priv, mac); + rcu_read_unlock(); + + return node; +} + +/* Add STA entry by MAC; return existing entry or NULL on invalid MAC. */ +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 =3D nxpwifi_get_sta_entry_rcu(priv, mac); + + if (node) + goto done; + + node =3D kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + goto done; + + memcpy(node->mac_addr, mac, ETH_ALEN); + list_add_tail_rcu(&node->list, &priv->sta_list); + +done: + spin_unlock_bh(&priv->sta_list_spinlock); + return node; +} + +/* Parse HT cap IE from association IEs and set STA HT parameters. */ +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 =3D (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, + ies_len); + if (ht_cap_ie) { + ht_cap =3D (void *)(ht_cap_ie + 1); + node->is_11n_enabled =3D 1; + node->max_amsdu =3D 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 =3D 0; + } +} + +/* Delete a station from list; called under cfg80211 mutex. */ + +void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + + list_for_each_entry_rcu(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) { + list_del_rcu(&node->list); + kfree_rcu(node, rcu); + break; + } + } +} + +/* Delete all stations from 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_rcu(&node->list); + kfree_rcu(node, rcu); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_bh(&priv->sta_list_spinlock); +} + +/* Add one histogram sample. */ +void nxpwifi_hist_data_add(struct nxpwifi_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct nxpwifi_histogram_data *phist_data =3D 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 sn= r, + s8 nflr) +{ + struct nxpwifi_histogram_data *phist_data =3D priv->hist_data; + s8 nf =3D -nflr; + s8 rssi =3D 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 =3D priv->hist_data; + + atomic_set(&phist_data->num_samples, 0); + for (ix =3D 0; ix < NXPWIFI_MAX_AC_RX_RATES; ix++) + atomic_set(&phist_data->rx_rate[ix], 0); + for (ix =3D 0; ix < NXPWIFI_MAX_SNR; ix++) + atomic_set(&phist_data->snr[ix], 0); + for (ix =3D 0; ix < NXPWIFI_MAX_NOISE_FLR; ix++) + atomic_set(&phist_data->noise_flr[ix], 0); + for (ix =3D 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 =3D rx_len + NXPWIFI_RX_HEADROOM + NXPWIFI_DMA_ALIGN_SZ; + + skb =3D __dev_alloc_skb(buf_len, flags); + + if (!skb) + return NULL; + + skb_reserve(skb, NXPWIFI_RX_HEADROOM); + + pad =3D 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_en= d) +{ + struct nxpwifi_ie_types_data *tlv; + u16 header_len =3D sizeof(struct nxpwifi_ie_types_header); + + tlv =3D (struct nxpwifi_ie_types_data *)pos; + tlv->header.len =3D cpu_to_le16(len); + + if (id =3D=3D WLAN_EID_EXT_HE_CAPABILITY) { + if ((pos + header_len + len + 1) > cmd_end) + return 0; + + tlv->header.type =3D cpu_to_le16(WLAN_EID_EXTENSION); + tlv->data[0] =3D WLAN_EID_EXT_HE_CAPABILITY; + memcpy(tlv->data + 1, data, len); + } else { + if ((pos + header_len + len) > cmd_end) + return 0; + + tlv->header.type =3D cpu_to_le16(id); + memcpy(tlv->data, data, len); + } + + return (header_len + len); +} + +static int nxpwifi_get_vdll_image(struct nxpwifi_adapter *adapter, u32 vdl= l_len) +{ + struct vdll_dnld_ctrl *ctrl =3D &adapter->vdll_ctrl; + bool req_fw =3D false; + u32 offset; + + if (ctrl->vdll_mem) { + nxpwifi_dbg(adapter, EVENT, + "VDLL mem is not empty: %p old_len=3D%d new_len=3D%d\n", + ctrl->vdll_mem, ctrl->vdll_len, vdll_len); + vfree(ctrl->vdll_mem); + ctrl->vdll_mem =3D NULL; + ctrl->vdll_len =3D 0; + } + + ctrl->vdll_mem =3D vmalloc(vdll_len); + if (!ctrl->vdll_mem) + return -ENOMEM; + + if (!adapter->firmware) { + req_fw =3D true; + if (request_firmware(&adapter->firmware, adapter->fw_name, + adapter->dev)) + return -ENOENT; + } + + if (adapter->firmware) { + if (vdll_len < adapter->firmware->size) { + offset =3D adapter->firmware->size - vdll_len; + memcpy(ctrl->vdll_mem, adapter->firmware->data + offset, + vdll_len); + } else { + nxpwifi_dbg(adapter, ERROR, + "Invalid VDLL length =3D %d, fw_len=3D%d\n", + vdll_len, (int)adapter->firmware->size); + return -EINVAL; + } + if (req_fw) { + release_firmware(adapter->firmware); + adapter->firmware =3D NULL; + } + } + + ctrl->vdll_len =3D vdll_len; + nxpwifi_dbg(adapter, MSG, "VDLL image: len=3D%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 =3D &adapter->vdll_ctrl; + struct host_cmd_ds_command *host_cmd; + u16 msg_len =3D block_len + S_DS_GEN; + int ret =3D 0; + + skb_trim(ctrl->skb, 0); + skb_put_zero(ctrl->skb, msg_len); + + host_cmd =3D (struct host_cmd_ds_command *)(ctrl->skb->data); + + host_cmd->command =3D cpu_to_le16(HOST_CMD_VDLL); + host_cmd->seq_num =3D cpu_to_le16(0xFF00); + host_cmd->size =3D cpu_to_le16(msg_len); + memcpy(ctrl->skb->data + S_DS_GEN, block, block_len); + + skb_push(ctrl->skb, adapter->intf_hdr_len); + ret =3D 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 =3D priv->adapter; + struct vdll_ind_event *vdll_evt =3D + (struct vdll_ind_event *)(skb->data + sizeof(u32)); + u16 type =3D le16_to_cpu(vdll_evt->type); + u16 vdll_id =3D le16_to_cpu(vdll_evt->vdll_id); + u32 offset =3D le32_to_cpu(vdll_evt->offset); + u16 block_len =3D le16_to_cpu(vdll_evt->block_len); + struct vdll_dnld_ctrl *ctrl =3D &adapter->vdll_ctrl; + int ret =3D 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 <=3D ctrl->vdll_len) { + block_len =3D + min((u32)block_len, ctrl->vdll_len - offset); + if (!adapter->cmd_sent) { + ret =3D 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 =3D block_len; + ctrl->pending_block =3D ctrl->vdll_mem + offset; + } + } else { + nxpwifi_dbg(adapter, ERROR, + "Err Req: offset=3D%#x, len=3D%d, vdll_len=3D%d\n", + offset, block_len, ctrl->vdll_len); + ret =3D -EINVAL; + } + break; + case VDLL_IND_TYPE_OFFSET: + nxpwifi_dbg(adapter, EVENT, + "VDLL IND (OFFSET): offset: %#x\n", offset); + ret =3D 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 =3D -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 =3D=3D 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); + +void nxpwifi_queue_wiphy_work(struct nxpwifi_adapter *adapter, + struct wiphy_work *work) +{ + if (!nxpwifi_can_queue_work(adapter)) + return; + + wiphy_work_queue(adapter->wiphy, work); +} + +void nxpwifi_queue_delayed_wiphy_work(struct nxpwifi_adapter *adapter, + struct wiphy_delayed_work *dwork, + unsigned long delay) +{ + if (!nxpwifi_can_queue_work(adapter)) + return; + + wiphy_delayed_work_queue(adapter->wiphy, dwork, delay); +} diff --git a/drivers/net/wireless/nxp/nxpwifi/util.h b/drivers/net/wireless= /nxp/nxpwifi/util.h new file mode 100644 index 000000000000..a8ec1ceee646 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/util.h @@ -0,0 +1,128 @@ +/* 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 =3D (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 =3D (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 =3D (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 =3D (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_en= d); + +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); + +void nxpwifi_queue_wiphy_work(struct nxpwifi_adapter *adapter, + struct wiphy_work *work); + +void nxpwifi_queue_delayed_wiphy_work(struct nxpwifi_adapter *adapter, + struct wiphy_delayed_work *dwork, + unsigned long delay); + +/* + * Firmware cannot run AP and STA on different channels simultaneously, + * and doing so may trigger a crash. Check whether check_chan can be set + * safely; return true if allowed, false if another channel is already + * active in firmware. + */ +bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv, + struct ieee80211_channel *check_chan); + +void nxpwifi_convert_chan_to_band_cfg(struct nxpwifi_private *priv, + u8 *band_cfg, + struct cfg80211_chan_def *chan_def); + +#endif /* !_NXPWIFI_UTIL_H_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from DB3PR0202CU003.outbound.protection.outlook.com (mail-northeuropeazon11010007.outbound.protection.outlook.com [52.101.84.7]) (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 5F53B42E016; Wed, 4 Feb 2026 18:05:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.84.7 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228355; cv=fail; b=SCtN/tAD8bws3GCsnsy4mpjRddqjndOyAeSd9LyEa2KcCMEYgNGUa6pJdOB/249ATMnBBgVXPhZeuLFcCYK7WJJtbJmvR/+qY8sttcGlPbQyGlYHqE48PrH1erDS5G9jSa8l5lWC2hmsz1yxdr0Z4657+INRMTYN8hM0TWnLEz4= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228355; c=relaxed/simple; bh=7wQRYFWZAR5rkvbbJaiRIxmeNP9OshPOEfK9wx/otOA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=MzVRFXvtDgMU8796lY1rBwtYAS69ziHxYiLNF5linhpqpIlAOonbyWBEYlvv1eBTb8lk2SUP4ub4/pYnoW7CiEab1m+xqmocCSZVTIoIKMmAsjMGvqcpOLeoWeLERAADTVKdyZMg6dlWNIe2lT6qoua8t9HwMesGZRaqEzlFFaQ= 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=NgGwkD+f; arc=fail smtp.client-ip=52.101.84.7 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="NgGwkD+f" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=FXueZWpqhm43OpnTc4GGkk4veKJ2cv6orBUErqlv1xsK2HnANPtdla3m+yz+dWprkKSz1aF1/+Y5C1LQ8xMMnMVmGMT03Lk2DTcOpn4nbdCjF0OaQM0JgLeHvAv/M68HEdefoPVhisq0a30OW8SkoMyCqvW5BfG1FQSYUF3OGedgNnWy9lcG+EofytHttjdUFsEyK94l5Z62OEQYOaIhuJT7KQriBm3GycFCPj99F1Ql34dESCn7Ab32F73s2jA1bPpziz/AzJbpX8GX7nr4XY0Z8/qJgeeA8aZdwNWW7keCLS3JvFFvN1/0A+0hn883VggWgrInLaQZZVEFlkZdCA== 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=sp6AW69/tFsD6huikmfSpEe162rEFCGjP2gKlgOxcj0=; b=zDKZfKDlmDFO3HnfTK5kqbfiuX6T29pvbE+BaKRf6xpXgx87tz4ZgoSJjWQ35QL4t5eJnacI2uss93TxA1hgZIeGScwCZRFiQi172+gqmhlJdKG6gCwHIVpK7cJA/LIGf//m8/fvSFBIl9F/UByUv50Mg2WIiyZbYNzC2kZztASFRVHF+BE6nZkUzd+JNvPeq6gxegqTBusRNxjdDwY2+trNx+a5xOUbuMF0a5m6buZC2B3tt/L5EfR3hmZ+z7XJnxiP1VW8/TSWq8hgZ/IT8RVCPBACFfiX6DagtlQS4hLxCL3itQvIplY7OUERhpS38N9iDNp3gX3ZF9eW1VnU5g== 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=sp6AW69/tFsD6huikmfSpEe162rEFCGjP2gKlgOxcj0=; b=NgGwkD+fwbibVyijgxD9tZ2liuoRfp2gzFAoDrxkCI3GWOos7hm2kfpBZUHSdzfs8BgKAE4bbc29zoBF3OoWB24V4xGMz92FcD0dINAU/l1OjxL6L9JKD7WydMdFri9n9UPivsF3/jIXnbuscZFb19c6dbVX0Qw5cMwU5dwBZN2/r9k41BiJbZQ/CqC6RZ58wrwy6WKu579ucsB5HXHZNEeMOy/zC9sxEHVvn5IrszhymOiYrFtToZkaqtJEiDHLciEMwyusiLjM7SmxwHWv9pEskZb0g6Wz41UTpwQysA8ZWcSeiaCpBeSDQMEPJSo4ZExsfnsWaDY5U7Kb6nCLqg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:54 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:54 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 17/21] wifi: nxpwifi: add driver initialization and shutdown support Date: Thu, 5 Feb 2026 02:03:54 +0800 Message-Id: <20260204180358.632281-18-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 68a83459-2a2f-47fc-1b62-08de641809bc X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?C4+3B46ly5rwPjDH8A55CHYoT9UJFwW2fPddFXKS3lC8OgEV5bh23l2arhO+?= =?us-ascii?Q?qWdS36JYRnFIfsg157dIOaVIrauqReIdeQDiSLJEUJEj4GYdJoQGWlTdy69L?= =?us-ascii?Q?5KgP1lfH3J7QiK5Nn9CtDtdwOhtCqv9zu1x9z8Nruv3lds21Cz3tDRA7h2Qy?= =?us-ascii?Q?zNh8SmBdRC9FBWMgmrQvu4s1L0Joz/vET02ReyYMy2SP8g9GHyOiIrUDv+dp?= =?us-ascii?Q?+WbJyW8oHr3+CL+G0JMdbiwLorQlFJqfRB0clVxeoLCbrWZX16sq+q+zyL7S?= =?us-ascii?Q?DSGzUiHYLKvUUwQ6bJ8GhQE1BupWa9I5b1ZqEOFWVtJonhd+NtsmIw0Av6Nw?= =?us-ascii?Q?xacX1AYkw9SKZYtdUO1NlEkGcPagNeZhJ7T7vXA7NSwplvqJtdiiHFUrNGdD?= =?us-ascii?Q?UpsoqSnqavlGHN2YE1HHJAAsreVJQVflvR6HAiuQWZKQmmXGuETw7emOrSPI?= =?us-ascii?Q?yD57EyUR55qcQ6heiY94rLn4GZVnnv2jRtkaB7B/e/GpOkafpcNxJArTwvpE?= =?us-ascii?Q?b8jvmc2+OQDyu3A6wVR0hT6dNn5fD3Kc8ooEOFkcca34w2xCK7Hmg4ObOaAZ?= =?us-ascii?Q?/91nFyrkI46M9HjEWFGcTWmLAb82Oi5e2G7lHrtRiGasdOPDnioAEAtQpnXj?= =?us-ascii?Q?C9+Ia1Cd5xGc9BnEqWqSZzC42V0xqtWkvRhIe6SMHuMrfwicDeE2vR5rEUOD?= =?us-ascii?Q?nlWL/jeBxLhP3zOxGcW1FYhR4EuSixWA2GD/Hl/n2aauI4f7We9/hRCZ7XOP?= =?us-ascii?Q?ajeMLE2N5QJpwYyUqX/xveS5D+IZIKOPKvRv9d89N6jjMcNx0O1DnLofj18o?= =?us-ascii?Q?74zaBeFEMlmTox3EWc9DwfrDKPnJuRhpHCFHHgDy6Q8Td1O1/pKsHWhhzSlj?= =?us-ascii?Q?82WqnycRe6Rv6TN9bZW438GT7ju1UR6KA4sg0LpkqgEyweXh1CSiuxWWqL6I?= =?us-ascii?Q?SgkY1oTWX59BNJl9ydy33NUEn1AuDd0P5P9c6PIaBzxvn2VAdG+OrX62tean?= =?us-ascii?Q?iaP8ALcvgQK+iSRXQE/adSIH+j5Qqqs2sX35B93TCqG5Wp94m8zhrrfDjuHx?= =?us-ascii?Q?8uM3DuQ6BF61RLUQOJ0fuqccpqJj8AIEtZMUa9DkYwm0dRBWkzpleO8xVbRO?= =?us-ascii?Q?hP6Si/AOhGtnajxdlUH+Wqhrp5jJPHwfNz6+aYkKyCJdeI3jse6D9T9DsDLV?= =?us-ascii?Q?Ap7WeICPVKr4eB39TBKKOr83DZUiacvNCkb0WhTRMew5PXGCIK63xWLTPjMY?= =?us-ascii?Q?bdeNTBOtis1qmp8KuUTFpxRTMt3c5zf6Huj+qgFdK5bF2PoQUeEbPXLw8C6v?= =?us-ascii?Q?bn335ebWfko09QN5x5jy/kIr2yDW4dmeUiF82L3Av+qh9RfyP0HhgVhVNCK1?= =?us-ascii?Q?g4cYbT8ulgJo48YfwlN7eTqNP7M11IoxJou9jMdPiNSXOXx1X3IJajqHyFHI?= =?us-ascii?Q?bv1csoRS/jbHnk46axsthfyEISfN/+Ypbyig1TJcHz/lMNTzB1y1yaaltkGf?= =?us-ascii?Q?do1zv/LWMGR4sWsxybx8bA+aa6c9Lu01hsl262y9PlQEAsNs2asQUX2XvZ/Z?= =?us-ascii?Q?WE08X4sedka3F9/so6Ye91KvHveCNvKcgeoNq3Ftc4WL+9xUqVh5TD0/rA3r?= =?us-ascii?Q?B85FQ8uqeSi5/l4lOX5YO2M=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?o/i3RhhZMt+jTp1quUMiYUaMGI2JdXytjlyNr/P9kr3SZFZHCFotLjp8DNaF?= =?us-ascii?Q?mBX14GT7kJpdkZBQNM2w20O4ogYJQuNFnlAxbEmCFW1qYIp8QFAWmqwJKY32?= =?us-ascii?Q?lhJjWPumnCTQ7ytPFRj78r25PLOh3LQWDT7B/VW7CciOCoxppi1FGbqcA/c6?= =?us-ascii?Q?NoC7oDzKXa9HQiMCAPMNKWRA6z8uYoPXIDFS2chtebwgLaGT3F7lSnh7FD9s?= =?us-ascii?Q?IKOGZEC1QDATuf1jogJsD66eqP+MTwQHYfomHHNJFYEyEOLM/lq77KA270M1?= =?us-ascii?Q?vpFCZTm9AuI64rFtmZtbN1FDwT//z5TcJG5KNxValPDMCkWFzFI5N2iguOjk?= =?us-ascii?Q?5ask2H0Fvvw5lZV1GcT44ph+znBDkSJ2c13LaD9Qx03QHsStQ7Z9HExdWUt3?= =?us-ascii?Q?RDAqUyWSzCZKFGZ4sTYydQHfqYsZ2MVfTpnzu6g88kmwJ3GCI7Muk108dZp8?= =?us-ascii?Q?FTJakLDOi/swpGV5p0A9XSjHOPska/yR6+Wb6yt8zLKRkRgFwD6iWsUStsRk?= =?us-ascii?Q?pi8cZ5VCn984LwBqQlDQyAIJW2euFTM37t5PVBcl47xmJCrARWiFTxJFmRFf?= =?us-ascii?Q?ztbB3UhmyKFcbLNkBuM2uqM19E+48lR4rpq1uymoivfAYf1MYGuTAuw6u28T?= =?us-ascii?Q?hiLok54SCXYv3gN6jUaTvn2PwPG2MhOwjXb2jIzlw1+4/biiNzuPTdHHE9Af?= =?us-ascii?Q?mIUVN0Wvmxi6ycN3/TcV05sD030CVotyYaAGAWtZMeFTQ8kVadvgfOQuFQ57?= =?us-ascii?Q?M5/yBll0g3sMEig1l5mDJQdmrO2oX9KWlZjsnClt9ba1afi/YvjVxp3aYI4D?= =?us-ascii?Q?545C3DwvcXeBKybL5eZp/3xNYq7AIQqpHCC5O5cCO7h/8uT2bUDJn0Uxr5GM?= =?us-ascii?Q?ycr+liC9xzovlm67wlYxLi32sVuM6FvUwnhOFJufuG6NbX/UBBkjmCajULvU?= =?us-ascii?Q?f+5vXw0aX52g9cw2S4fZxChKSm5bqKQikuoasyxcajUrxRUv4ZAUN3pxEhNr?= =?us-ascii?Q?9cC+ptXkdomgdiotK6Kr7vEMhE8xKYcpWHNIjJTV/o9nRIHw8LjM4AIDE4ly?= =?us-ascii?Q?NkrAM598PhMHAI2lilL4yieFKCM/RGULiP/MFwer+ppK5syeNgTzAu7EAy2z?= =?us-ascii?Q?Zsp+I2+hIQ0DU3ugc6C2KVgx5LyCHDv2MRC0qwksOLN7wTlT0/TM8dNaN5Jf?= =?us-ascii?Q?2RNAVvedDTbW2nwO1DmbLejAKoXwvfrT5p/PEEqsp8FDBULFfhTt470qudgM?= =?us-ascii?Q?+dC10qvMo7EdOcpOjOd43pRA9pt47Q5D5AHVt5tvbfJN4hzMHy9LL8rWu0tI?= =?us-ascii?Q?TPaechPYfT5/32+f9oTi1EFPtsiTPNV3Kws3yi1DjG40ecbpT1bSBQ51KNOU?= =?us-ascii?Q?VXZtmKI+8Jj6vGqE4lG5A9q1iKxov0OfbQCuGvLGte6X+9Bo5wbzz5LbUL1o?= =?us-ascii?Q?na0F9nczKsOrotnt70OxgU0WkBorPpVZ445t64rhfY3NuBZ3TfsuVyZMwl7D?= =?us-ascii?Q?AdsHo9nAdtzB+94UeyAE5xt+ldDmqOOywZ17aAxX0htIE57j4mLSxsTSdrmn?= =?us-ascii?Q?2rRVAwSDkkAopSCte0sefbN5AMPna8sIJF1+vVsh6jEyogmHE7P94/rGRvoX?= =?us-ascii?Q?Fz8COD3URUXM40BxC51yPJfuqSEoNJQEpFRlFZ+CtsSD4fO8TeFw3E8B+sB7?= =?us-ascii?Q?8wYmG4vNhyIJjirINYTrW0CBf6sLeA5ZlQJfWx8HG6fGJxX61GrBHQ59JeUw?= =?us-ascii?Q?lnKvqc7vMw=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 68a83459-2a2f-47fc-1b62-08de641809bc X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:54.0112 (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: XShx4/WXEpbXL6FyuLzxke9drpk86iBBe7TDFkDzhL03KUgzkpjZGV98y4kWLBCqZ9uxYbefYDl13VqVJXy6lA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" This patch introduces the initialization and shutdown logic for the nxpwifi driver, including setup of adapter and private structures, firmware download,and resource cleanup. Key features: - Initializes adapter and private structures with default values - Allocates and initializes command buffers, queues, and timers - Sets up BSS priority tables for multi-interface coordination - Implements firmware download and initialization sequence - Provides shutdown and cleanup routines for safe driver removal - Adds helpers for managing netdev queues and synchronization primitives These foundational components are essential for bringing up the driver and ensuring proper resource management across its lifecycle. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/init.c | 607 ++++++++++++++++++++++++ 1 file changed, 607 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..7e472dd194a1 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/init.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nxpwifi: 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" + +/* Add a BSS priority node to the adapter list. */ +static int nxpwifi_add_bss_prio_tbl(struct nxpwifi_private *priv) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_bss_prio_node *bss_prio; + struct nxpwifi_bss_prio_tbl *tbl =3D adapter->bss_prio_tbl; + + bss_prio =3D kzalloc(sizeof(*bss_prio), GFP_KERNEL); + if (!bss_prio) + return -ENOMEM; + + bss_prio->priv =3D 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 =3D timer_container_of(adapter, t, wakeup= _timer); + + nxpwifi_dbg(adapter, ERROR, "Firmware wakeup failed\n"); + adapter->hw_status =3D NXPWIFI_HW_STATUS_RESET; + nxpwifi_cancel_all_pending_cmd(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +/* Initialize priv defaults and lists. */ +int nxpwifi_init_priv(struct nxpwifi_private *priv) +{ + u32 i; + + priv->media_connected =3D false; + eth_broadcast_addr(priv->curr_addr); + priv->port_open =3D false; + priv->usb_port =3D NXPWIFI_USB_EP_DATA; + priv->pkt_tx_ctrl =3D 0; + priv->bss_mode =3D NL80211_IFTYPE_UNSPECIFIED; + priv->data_rate =3D 0; /* Initially indicate the rate as auto */ + priv->is_data_rate_auto =3D true; + priv->bcn_avg_factor =3D DEFAULT_BCN_AVG_FACTOR; + priv->data_avg_factor =3D DEFAULT_DATA_AVG_FACTOR; + + priv->auth_flag =3D 0; + priv->auth_alg =3D WLAN_AUTH_NONE; + + priv->sec_info.wep_enabled =3D 0; + priv->sec_info.authentication_mode =3D NL80211_AUTHTYPE_OPEN_SYSTEM; + priv->sec_info.encryption_mode =3D 0; + for (i =3D 0; i < ARRAY_SIZE(priv->wep_key); i++) + memset(&priv->wep_key[i], 0, sizeof(struct nxpwifi_wep_key)); + priv->wep_key_curr_index =3D 0; + priv->curr_pkt_filter =3D HOST_ACT_MAC_DYNAMIC_BW_ENABLE | + HOST_ACT_MAC_RX_ON | HOST_ACT_MAC_TX_ON | + HOST_ACT_MAC_ETHERNETII_ENABLE; + + priv->beacon_period =3D 100; /* beacon interval */ + priv->attempted_bss_desc =3D NULL; + memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); + priv->listen_interval =3D 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 =3D 0; + priv->atim_window =3D 0; + priv->tx_power_level =3D 0; + priv->max_tx_power_level =3D 0; + priv->min_tx_power_level =3D 0; + priv->tx_ant =3D 0; + priv->rx_ant =3D 0; + priv->tx_rate =3D 0; + priv->rxpd_htinfo =3D 0; + priv->rxpd_rate =3D 0; + priv->rate_bitmap =3D 0; + priv->data_rssi_last =3D 0; + priv->data_rssi_avg =3D 0; + priv->data_nf_avg =3D 0; + priv->data_nf_last =3D 0; + priv->bcn_rssi_last =3D 0; + priv->bcn_rssi_avg =3D 0; + priv->bcn_nf_avg =3D 0; + priv->bcn_nf_last =3D 0; + memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + memset(&priv->aes_key, 0, sizeof(priv->aes_key)); + priv->wpa_ie_len =3D 0; + priv->wpa_is_gtk_set =3D false; + + memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); + priv->assoc_tlv_buf_len =3D 0; + memset(&priv->wps, 0, sizeof(priv->wps)); + memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); + priv->gen_ie_buf_len =3D 0; + memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); + + priv->wmm_required =3D true; + priv->wmm_enabled =3D false; + priv->wmm_qosinfo =3D 0; + priv->curr_bcn_buf =3D NULL; + priv->curr_bcn_size =3D 0; + priv->wps_ie =3D NULL; + priv->wps_ie_len =3D 0; + priv->ap_11n_enabled =3D 0; + memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); + + priv->scan_block =3D false; + + priv->csa_chan =3D 0; + priv->csa_expire_time =3D 0; + priv->del_list_idx =3D 0; + priv->hs2_enabled =3D 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); +} + +/* Allocate command buffer and sleep-confirm skb. */ +static int nxpwifi_allocate_adapter(struct nxpwifi_adapter *adapter) +{ + int ret; + + /* Allocate command buffer */ + ret =3D nxpwifi_alloc_cmd_buffer(adapter); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "%s: failed to alloc cmd buffer\n", + __func__); + return ret; + } + + adapter->sleep_cfm =3D + 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; +} + +/* Initialize adapter defaults and WMM parameters. */ +static void nxpwifi_init_adapter(struct nxpwifi_adapter *adapter) +{ + struct nxpwifi_opt_sleep_confirm *sleep_cfm_buf =3D NULL; + + skb_put(adapter->sleep_cfm, sizeof(struct nxpwifi_opt_sleep_confirm)); + + adapter->cmd_sent =3D false; + adapter->data_sent =3D true; + + adapter->intf_hdr_len =3D INTF_HEADER_LEN; + + adapter->cmd_resp_received =3D false; + adapter->event_received =3D false; + adapter->data_received =3D false; + adapter->assoc_resp_received =3D false; + adapter->priv_link_lost =3D NULL; + adapter->host_mlme_link_lost =3D false; + + clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + + adapter->hw_status =3D NXPWIFI_HW_STATUS_INITIALIZING; + + adapter->ps_mode =3D NXPWIFI_802_11_POWER_MODE_CAM; + adapter->ps_state =3D PS_STATE_AWAKE; + adapter->need_to_wakeup =3D false; + + adapter->scan_mode =3D HOST_BSS_MODE_ANY; + adapter->specific_scan_time =3D NXPWIFI_SPECIFIC_SCAN_CHAN_TIME; + adapter->active_scan_time =3D NXPWIFI_ACTIVE_SCAN_CHAN_TIME; + adapter->passive_scan_time =3D NXPWIFI_PASSIVE_SCAN_CHAN_TIME; + adapter->scan_chan_gap_time =3D NXPWIFI_DEF_SCAN_CHAN_GAP_TIME; + + adapter->scan_probes =3D 1; + + adapter->multiple_dtim =3D 1; + + /* default value in firmware will be used */ + adapter->local_listen_interval =3D 0; + + adapter->is_deep_sleep =3D false; + + adapter->delay_null_pkt =3D false; + adapter->delay_to_ps =3D 1000; + adapter->enhanced_ps_mode =3D PS_MODE_AUTO; + + /* Disable NULL Pkg generation by default */ + adapter->gen_null_pkt =3D false; + /* Disable pps/uapsd mode by default */ + adapter->pps_uapsd_mode =3D false; + adapter->pm_wakeup_card_req =3D false; + + adapter->pm_wakeup_fw_try =3D false; + + adapter->curr_tx_buf_size =3D NXPWIFI_TX_DATA_BUF_SIZE_2K; + + clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags); + adapter->hs_cfg.conditions =3D cpu_to_le32(HS_CFG_COND_DEF); + adapter->hs_cfg.gpio =3D HS_CFG_GPIO_DEF; + adapter->hs_cfg.gap =3D HS_CFG_GAP_DEF; + adapter->hs_activated =3D false; + + memset(adapter->event_body, 0, sizeof(adapter->event_body)); + adapter->hw_dot_11n_dev_cap =3D 0; + adapter->hw_dev_mcs_support =3D 0; + adapter->sec_chan_offset =3D 0; + + nxpwifi_wmm_init(adapter); + atomic_set(&adapter->tx_hw_pending, 0); + + sleep_cfm_buf =3D (struct nxpwifi_opt_sleep_confirm *) + adapter->sleep_cfm->data; + memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); + sleep_cfm_buf->command =3D cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->size =3D cpu_to_le16(adapter->sleep_cfm->len); + sleep_cfm_buf->result =3D 0; + sleep_cfm_buf->action =3D cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->resp_ctrl =3D cpu_to_le16(RESP_NEEDED); + + memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); + adapter->tx_lock_flag =3D false; + adapter->null_pkt_interval =3D 0; + adapter->fw_bands =3D 0; + adapter->fw_release_number =3D 0; + adapter->fw_cap_info =3D 0; + memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); + adapter->event_cause =3D 0; + adapter->region_code =3D 0; + adapter->bcn_miss_time_out =3D DEFAULT_BCN_MISS_TIMEOUT; + memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + adapter->arp_filter_size =3D 0; + adapter->max_mgmt_ie_index =3D MAX_MGMT_IE_INDEX; + adapter->key_api_major_ver =3D 0; + adapter->key_api_minor_ver =3D 0; + eth_broadcast_addr(adapter->perm_addr); + adapter->iface_limit.sta_intf =3D NXPWIFI_MAX_STA_NUM; + adapter->iface_limit.uap_intf =3D NXPWIFI_MAX_UAP_NUM; + adapter->active_scan_triggered =3D false; + timer_setup(&adapter->wakeup_timer, wakeup_timer_fn, 0); + adapter->devdump_len =3D 0; + memset(&adapter->vdll_ctrl, 0, sizeof(adapter->vdll_ctrl)); + adapter->vdll_ctrl.skb =3D dev_alloc_skb(NXPWIFI_SIZE_OF_CMD_BUFFER); + atomic_set(&adapter->iface_changing, 0); +} + +/* Update trans_start for each Tx queue. */ +void nxpwifi_set_trans_start(struct net_device *dev) +{ + int i; + + for (i =3D 0; i < dev->num_tx_queues; i++) + txq_trans_cond_update(netdev_get_tx_queue(dev, i)); + + netif_trans_update(dev); +} + +/* Wake all netdev Tx queues. */ +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); +} + +/* Stop all netdev Tx queues. */ +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); +} + +/* Invalidate 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 =3D 0; i < adapter->priv_num; i++) + list_del(&adapter->bss_prio_tbl[i].bss_prio_head); + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + for (j =3D 0; j < MAX_NUM_TID; ++j) { + list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); + list_del(&priv->tx_ba_stream_tbl_ptr[j]); + list_del(&priv->rx_reorder_tbl_ptr[j]); + } + list_del(&priv->sta_list); + } +} + +/* Cancel pending work, stop timers, and free adapter buffers. */ +static void +nxpwifi_adapter_cleanup(struct nxpwifi_adapter *adapter) +{ + timer_delete(&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 =3D NULL; + adapter->vdll_ctrl.vdll_len =3D 0; + } + if (adapter->vdll_ctrl.skb) { + dev_kfree_skb_any(adapter->vdll_ctrl.skb); + adapter->vdll_ctrl.skb =3D 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); +} + +/* Initialize locks and 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->nxpwifi_cmd_lock); + spin_lock_init(&adapter->queue_lock); + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D 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 =3D 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 =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + for (j =3D 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[j]); + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr[j]); + spin_lock_init(&priv->tx_ba_stream_tbl_lock[j]); + spin_lock_init(&priv->rx_reorder_tbl_lock[j]); + } + INIT_LIST_HEAD(&priv->sta_list); + skb_queue_head_init(&priv->bypass_txq); + + spin_lock_init(&priv->ack_status_lock); + xa_init(&priv->ack_status_frames); + } +} + +/* Init firmware: alloc resources, init adapter/privs, send STA init. */ +int nxpwifi_init_fw(struct nxpwifi_adapter *adapter) +{ + int ret; + struct nxpwifi_private *priv; + u8 i; + bool first_sta =3D true; + + adapter->hw_status =3D NXPWIFI_HW_STATUS_INITIALIZING; + + /* Allocate memory for member of adapter structure */ + ret =3D nxpwifi_allocate_adapter(adapter); + if (ret) + return ret; + + /* Initialize adapter structure */ + nxpwifi_init_adapter(adapter); + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + + /* Initialize private structure */ + ret =3D nxpwifi_init_priv(priv); + if (ret) + return ret; + } + + for (i =3D 0; i < adapter->priv_num; i++) { + ret =3D nxpwifi_sta_init_cmd(adapter->priv[i], + first_sta, true); + if (ret) + return ret; + + first_sta =3D false; + } + spin_lock_bh(&adapter->cmd_pending_q_lock); + WARN_ON(!list_empty(&adapter->cmd_pending_q)); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + adapter->hw_status =3D NXPWIFI_HW_STATUS_READY; + + return 0; +} + +/* Remove all BSS priority nodes for this priv. */ +static void nxpwifi_delete_bss_prio_tbl(struct nxpwifi_private *priv) +{ + int i; + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct nxpwifi_bss_prio_node *bssprio_node, *tmp_node; + struct list_head *head; + spinlock_t *lock; /* bss priority lock */ + + for (i =3D 0; i < adapter->priv_num; ++i) { + head =3D &adapter->bss_prio_tbl[i].bss_prio_head; + lock =3D &adapter->bss_prio_tbl[i].bss_prio_lock; + nxpwifi_dbg(adapter, INFO, + "info: delete BSS priority table,\t" + "bss_type =3D %d, bss_num =3D %d, i =3D %d,\t" + "head =3D %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 =3D=3D priv) { + nxpwifi_dbg(adapter, INFO, + "info: Delete\t" + "node %p, next =3D %p\n", + bssprio_node, tmp_node); + list_del(&bssprio_node->list); + kfree(bssprio_node); + } + } + spin_unlock_bh(lock); + } + } +} + +/* Free per-priv resources and BSS priority entries. */ +void nxpwifi_free_priv(struct nxpwifi_private *priv) +{ + nxpwifi_clean_txrx(priv); + nxpwifi_delete_bss_prio_tbl(priv); + nxpwifi_free_curr_bcn(priv); +} + +/* Shutdown driver: stop work, drain queues, free resources. */ +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 =3D=3D NXPWIFI_HW_STATUS_NOT_READY) + return; + + /* cancel current command */ + if (adapter->curr_cmd) { + nxpwifi_dbg(adapter, WARN, + "curr_cmd is still in processing\n"); + timer_delete_sync(&adapter->cmd_timer); + nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd); + adapter->curr_cmd =3D NULL; + } + + /* shut down nxpwifi */ + nxpwifi_dbg(adapter, MSG, + "info: shutdown nxpwifi...\n"); + + /* Clean up Tx/Rx queues and delete BSS priority table */ + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + + nxpwifi_abort_cac(priv); + nxpwifi_free_priv(priv); + } + + atomic_set(&adapter->tx_queued, 0); + while ((skb =3D skb_dequeue(&adapter->tx_data_q))) + nxpwifi_write_data_complete(adapter, skb, 0, 0); + + while ((skb =3D skb_dequeue(&adapter->rx_mlme_q))) + dev_kfree_skb_any(skb); + + while ((skb =3D skb_dequeue(&adapter->rx_data_q))) { + struct nxpwifi_rxinfo *rx_info =3D NXPWIFI_SKB_RXCB(skb); + + atomic_dec(&adapter->rx_pending); + priv =3D adapter->priv[rx_info->bss_num]; + if (priv) + priv->stats.rx_dropped++; + + dev_kfree_skb_any(skb); + } + + nxpwifi_adapter_cleanup(adapter); + + adapter->hw_status =3D NXPWIFI_HW_STATUS_NOT_READY; +} + +/* Download FW if needed; check winner and wait until ready. */ +int nxpwifi_dnld_fw(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *pmfw) +{ + int ret; + u32 poll_num =3D 1; + + /* check if firmware is already running */ + ret =3D 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 =3D 0; + ret =3D adapter->if_ops.check_winner_status(adapter); + + poll_num =3D 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 =3D adapter->if_ops.prog_fw(adapter, pmfw); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "prog_fw failed ret=3D%#x\n", ret); + return ret; + } + } + +poll_fw: + /* Check if the firmware is downloaded successfully or not */ + ret =3D 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); --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010011.outbound.protection.outlook.com [52.101.69.11]) (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 593E442EEC7; Wed, 4 Feb 2026 18:05:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.11 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228360; cv=fail; b=I+/kdLMtfMQfw5g0IcqQc0YwLV3KlA1dyXgOagtneM9qFbKs1cPcqhxEd7yV48PTih9Ub/OSOsj8IkPaR3Wo5rofVGy1EqKHs0nYESvmN0gFIfd2npGz2pj2caKfxGJLQjy0bVfInsgeGrC/M4TbFHhxta6hno2z9dldnpjhvd0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228360; c=relaxed/simple; bh=ksf70I8RtuJY/Q0fDLEB3H2imW4EJm9ZOD8bIrDFy2s=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=kV/Qt69R1obVuWRe6Aror3Wfh14tC/W/LK1+ujFRpwUbOuT9mGIXVPT0w2gkWymmds49ga6tyuRnOxtFGTc1XJ0K8rKa5kI57sUwtxSVGoBlVasGQl8LoKg93Y6O4FytOrFMcPm5vS+g8MeDs6dElxLrCgtKrXN5a8muL8NA5b8= 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=FVvLtftj; arc=fail smtp.client-ip=52.101.69.11 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="FVvLtftj" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=RvW59epDhZQctLQXNS4kmjox2Z20H9dZRGm/eNzpobXJXQIHfB3k+DRPYqsfrdjzl44CZHIChDkY0Wcucc5HO49dAfHrL1QL3l7PpLT/7N5bgCHndHzTRQxyAQqY4B8lU9ayM6tuhTYij1kD8jNnGqb0UE/dnmJCgjsFJMda2tnDqNgVFKhDfdWpXwhspd+COjMIO0Lpz35rqPH16jcHGs2myWNafCrX7ID43exWcE+rjf3fEgTqMd9DRnvt3d/MrVOdkXsAUJcZ60ontWqnFDG7tlx2zGMyv25YAMUOc43vGZ6J1EZm38JECr7BD+Y7U1ht5zasv7sw5+TghoX5bg== 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=bjAFq6YjcchuYN1odMs4yleVun5gWPg582kfeIPb/Kk=; b=N5AG+MM3/YMtMWuN7opRSezja7LOOcbYtZJQ/z8u/lhy9xL/anBSvQ43NLDNVRgRWn+0qdRZyx3gqz9efEiMmXRUAJfpp9OIUanfPzq+K1hphjQzP9cVU0oNajdxuHrOD/OWhhhevvoH88m937vFuy7Wa53kZgO1KVv1EKN4vFSi6XLzWhw+Idxjyv5QcDasKaF7OxdWEiHmWqcqP7Tn998JQGqh66b9m6aXAclKTt2ZWGgSVd5Sw3W6Xbo5cZw9IwDuow1reSJY/wlQ8YtccNo4lcGQLRO+tpdb+MNs/tUevQTChp/hai/2Y+8lrNygjbCKyUB4dbmIJTrmMabqlw== 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=bjAFq6YjcchuYN1odMs4yleVun5gWPg582kfeIPb/Kk=; b=FVvLtftj4uLXQ1BGve6W/3C5kloUZG0EJ3DaPFHutwIQPU8W2n1G1wqh3UU6BAGoGosST6WBpyA0z0eN0ylT2+DCY49dKBwYnat+qO+tTZBxxPMpY/Dy8kp2MWi3V9iyy2o0sGPf9KwSgS3fXXP9wDWN3IypGcCbcFpllqz7KNh0OcKFpbVhl/1Or6CZzCutjXkwdjHEgTlXIUtIP3AN6n8wievaz3QL+vPb8HmD5jgEHgUdJvWVWId9MKARX73o/lt3my5Ww+2yYvGnRwMKmIbzPWpLeSZWjx2tm+ICNRz7Fqh3zdXnFq27XwWixOIBWEml7TgpF2sYRfmnYp0khg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:56 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:56 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 18/21] wifi: nxpwifi: add core driver implementation Date: Thu, 5 Feb 2026 02:03:55 +0800 Message-Id: <20260204180358.632281-19-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 4ab943ee-23ae-478a-5888-08de64180b1e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014|7142099003; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?11taEzatCrsKVk81THJmuPaxm/E6TJnmjTwzumF5f+nVCE5qrcW6rTZdkn4K?= =?us-ascii?Q?ev0E/86IkiWbJeuWAdvC7e5wzVg6rePl0+26SJ8rVBv4kaqdQjupZk1VGInM?= =?us-ascii?Q?7++1QgMJ5y7Qbia2PJJHJ4DIQTGvKsIhwWvIdVbfeEUh98WJ8+TChy/Dr0wg?= =?us-ascii?Q?FbYMmce5IFsFNtYpZmk/5SbIYD6J+70vyGVPyaAqLyp3oNi75IpKL8sTNXPr?= =?us-ascii?Q?68GDjWHJv0uu0Vo9M22OYFJbr0hQX7Xr+9WUuiNrnmP9SNLCK0jJb6bj2AZh?= =?us-ascii?Q?HDUz8zs3gMJLFjR+dr3LaBK2oPAg27GoTQ3vbFBmsK8aoOwNKvzAKRI+07+V?= =?us-ascii?Q?Ho3mg0BVIUyziXBRCf5f3/xtFbzpz8JnI3bw7utU5/LkmrlGItVYIR1wa1Lv?= =?us-ascii?Q?wV/GZzoHjHMM7e0OlBQgDHf4OJNvb/iSuSHSUtncxIDmewpXFrEfYSYqDa/A?= =?us-ascii?Q?bWm+VqU8uIBdMAi/IZ0Fa9nFdJyk/6TD5UxFhIF/xBmM37ZGMT9IomKHIBn3?= =?us-ascii?Q?gUlkC0y9Ae12p5D1msEbbTkRWDyg0ej6PHpMYKFUwSzy3Cya5cIWwfMWDUxB?= =?us-ascii?Q?kTGQDcCqJzj6ppBrEY11akBxZ2HYo3VYeRlGCL7cfb5I1ocdt8FllstC/JT8?= =?us-ascii?Q?xfq/GRtd9/lbI/qf9jDxcUemblalT8w2UbrVlsQ+ghl0Fg4ek3RCoYev7KD8?= =?us-ascii?Q?kbp/dC7J5uvlzG1IdQZZvPAF9oDAahXxkEofHdVw8G1T3+bH9iY8aYIOLPN2?= =?us-ascii?Q?0Ss5jx5oH38tv1JqPiqLXnc9h73ENxlLYH6y5aUTIDBWpuKHCF6n5dFA4TZh?= =?us-ascii?Q?1drZyvWf9tKVG/TcV3bxZRrVirwla2xlvGQJkERtI0nCRI/IEA2Szoy8IPCf?= =?us-ascii?Q?lgYY2pG0Kt0XUaR7c09U7m1jqsfDUZz+D87IVc47GIrqZ3FD9fde+Q+oE+S/?= =?us-ascii?Q?6fEPAlgE11PdkRMnPYwxplFBUIDwmJX0zheq5awdBxTFYPNl+ap27K5EGl8j?= =?us-ascii?Q?Td/DA5iN6BlZ8oStHt9f6Z/NQ8Me3oOkdEyQnxXFgYMAU42y8QdT5zX+IFU8?= =?us-ascii?Q?H96fz/Ef2Rtk5Z0nPwXbPQaPS7MiboS6CiefbSftECyWePT6UFNNO+q/vpNU?= =?us-ascii?Q?hkXeRfKomCmkccvz5K//GhO10/ZZVQblZ/FRdDaV2dxhFdBJfWszse8jvmDD?= =?us-ascii?Q?13FkzimL5wlGLlaVjNUuMKjhN/8tWOOdvd4tS57rVKeKzM+Ry4mLWWS/IwiC?= =?us-ascii?Q?vTpV1CaJpvIjXgBbmqawd3SJDss+3Z3Bg+WNJ95wSoFDadpjJwHjKmZBgsK5?= =?us-ascii?Q?62RceGz3t6X3bfeVuiQezWvgSqayqepUSbbACFuY1Cbd8qmKVbDcuOX19zch?= =?us-ascii?Q?/inQiANTKtlS7ebj+I7PoJuD3Pcm5UN0SvAJQ1Dy2P4aBd+30elNJvqqqDkS?= =?us-ascii?Q?KmwoLx2U7z5mathINv0EJ0G2KKCuO3qI91QOZp/mOz6JuKkrewA7M/ZGtazf?= =?us-ascii?Q?+vwrgrOIexEWK/BjgniHuNvOIR9mvqdUBVM7IWAEgaw9Ozfi2cZkkFhyMR2W?= =?us-ascii?Q?P6x/R55d7OTWKMvKK7Ni1tuhNqv2tq2SwZ/UZcvN1Ja+H/2tYojAkNPbwQG3?= =?us-ascii?Q?Evd9otaAESpDcAE901SYAuM=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014)(7142099003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?zn1MSlYGldxSykh4r0B+/yi06EayoQuqEneN0ewLRc0ha8UGRtzSabPGTYI1?= =?us-ascii?Q?/SoKyU82muu9EUY0zxWGAn8FvjAihbmkArhzRTXbzo31o0h3Tt9pUjji8dVR?= =?us-ascii?Q?ff5JSej8iy0RRNRBO343vzfGUBSKkIaLTFD+RPOGl3GzOb33sQoKp2nRuoS4?= =?us-ascii?Q?vFly84HJeJafmRri3aC4sfSzH2a0+9wLj5uLfUbsN6QG8OkAkFD7k4nor686?= =?us-ascii?Q?uSHgcjjj5azcpp9fUUerdFBXutCqSehEggoeyVRlcr1+IAVVRO4gyEveCb7t?= =?us-ascii?Q?qPM6Z+MU7p+PuyWZALA+i/FEhTdHC+xyKKo90F+Z5W0xBciWW/6+sIDKHSrs?= =?us-ascii?Q?/XgBe40ld1nrUOJzip5YMpKf3HjGa9JD8fuVkktiFoY/vH68wpDEg/6As4Jq?= =?us-ascii?Q?felQWWUYDT5JKatkbljb7v+JG/rrd1rmGX6iM8MlDtE5dMBZDT9nJ58gNcRj?= =?us-ascii?Q?/zUPOiYOjdn/FpiWwUfie5FdPraQoAJW/Eb6Q5SM5yRtgCRpxEjwBrSIzTip?= =?us-ascii?Q?0az6bvgu4SxK+a5Noo8FJIuR0f0zudHh/gA+ZAvdGIiw2RzjJjROzZtQFII7?= =?us-ascii?Q?Tqznbn5XTjypTWrWS2jrKw6xm9OISztHW/wX0fiec2JbAIuktNQzYurdQGj3?= =?us-ascii?Q?wsRM1XrdV6/4xuvQh0vr3otkG8fNCjcGN/fiJfX1BoTcWzmFV2gVhpiQHhxl?= =?us-ascii?Q?hUAE8upWe9n+FhW2SixqDHBW5tIvmS/++qXc8VkuR4ltA1WvpOBNLqvrox/O?= =?us-ascii?Q?lTXhDOgzYgDjWdKzlDpJyc0FUy8M1yFfRWSLjMJSiXsORtOwECkk1v+Y7o0r?= =?us-ascii?Q?lNfD5EU3W0gbPYF1M4+EZo++8cq5y/ql9H+vHoK/B+UILI8FJNhh4o7QCjhG?= =?us-ascii?Q?rofQKt0xngxV8R9wFrExSVXcHxZRR3V0n/JcMksifj+hMItoU+VLuA8HgWXR?= =?us-ascii?Q?6q0ss6JODSTDuG6mFHhfnmhSEcnSNoJsv7yXdpbTGeFPnvos96P0DJ06MMoA?= =?us-ascii?Q?dW/1Efq9Y+5bxNNsDZu6Js/TOdHr685iHREPUIsVbG+iVN5ibFb7dX6FcMKX?= =?us-ascii?Q?0Mqwq+DoggFHuABYndasS/K8PjperzAW6t03CIh0MCDTkNckDIHVjXCRB7Sp?= =?us-ascii?Q?cUobdTFsIB0Wc7MyuxXjdItayV4EpSmlVV3KJUVDWAqQ5n8XubCbcTx0YIrK?= =?us-ascii?Q?2crXYX0tp3Iy7wuLmx8Dr/1j+M9C+dRDRzAkFBjIComJwIvsEOIDExx4yC2M?= =?us-ascii?Q?3gDft6YFZFgQXsUdKtGtMyOt96mimc0zx3KLVCeW6cG3ZY3qLsP89N2bi86D?= =?us-ascii?Q?yLuxwbXTWh0k48G3q7Km6atP4UtBzVoldbxwEYXR6Oa0j4jjP7WkThy5v/JZ?= =?us-ascii?Q?0JAeRxzv+9pvEIEps94XmDp0+oDuGQvpAsb+dSVZMakMfefakyjeeuj5TpNn?= =?us-ascii?Q?0M24CSfBXfaNTz/oj+x0dH3rKxgie3geAmchy6cAsFZOehoGaJocsVK4gjn2?= =?us-ascii?Q?mieIOYexIPDgSIX9cJJfP9r2F+R8E2BI4OKoOTsxLz3C6hYx3SekLDFj8IvT?= =?us-ascii?Q?2lb/C1P5mh1rMaLP2fkYo3mNWUI3PdZCtAS5u953ohSfCNvmWU9FD2kPpwmE?= =?us-ascii?Q?cUb5p44AUn2PU6G9o/b1GlWyGSGgQJPPxMePJpO0/YuR3e+tc2La8f354clF?= =?us-ascii?Q?mMEUA48ViyuoGs2nQoZilk2DRMAxALQUzLdbCLE8X0cTfddmAnkZ6AeU3C8S?= =?us-ascii?Q?OrIsK4xSEg=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4ab943ee-23ae-478a-5888-08de64180b1e X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:56.3864 (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: HE306m7Mtc94ccJk02ZvStdDTccsN5LLoV41TR/G7k15arbDU0UZBZxZxWqX24SpqA72rNt+gG7q3J0+rLNGLw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" This patch introduces the core layer of the nxpwifi driver, which provides the foundational logic and infrastructure for managing the wireless adapter. Highlights: - Implements adapter registration, initialization, and teardown - Defines the main driver loop for handling TX/RX, command, and event flows - Adds firmware download and wakeup handling logic - Introduces power management, workqueue, and interrupt handling - Provides debug and device dump support for diagnostics - Defines core data structures and interfaces in main.h - Establishes the interface abstraction via nxpwifi_if_ops for bus drivers This core layer enables the bus-specific driver to instantiate and operate the Wi-Fi device through a well-defined callback interface. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/main.c | 1721 ++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/main.h | 1800 +++++++++++++++++++++++ 2 files changed, 3521 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..af21a9ef3e9e --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/main.c @@ -0,0 +1,1721 @@ +// 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 =3D NXPWIFI_DEFAULT_DEBUG_MASK; + +char driver_version[] =3D "nxpwifi " VERSION " (%s) "; + +const u16 nxpwifi_1d_to_wmm_queue[8] =3D { 1, 0, 0, 1, 2, 2, 3, 3 }; + +/* Register device; init adapter/privs/if_ops/locks; cleanup on fail. */ +static struct nxpwifi_adapter *nxpwifi_register(void *card, struct device = *dev, + struct nxpwifi_if_ops *if_ops) +{ + struct nxpwifi_adapter *adapter; + int ret =3D 0; + int i; + + adapter =3D kzalloc(sizeof(*adapter), GFP_KERNEL); + if (!adapter) + return ERR_PTR(-ENOMEM); + + adapter->dev =3D dev; + adapter->card =3D card; + + /* Save interface specific operations in adapter */ + memmove(&adapter->if_ops, if_ops, sizeof(struct nxpwifi_if_ops)); + adapter->debug_mask =3D debug_mask; + + /* card specific initialization has been deferred until now .. */ + if (adapter->if_ops.init_if) { + ret =3D adapter->if_ops.init_if(adapter); + if (ret) + goto error; + } + + adapter->priv_num =3D 0; + + for (i =3D 0; i < NXPWIFI_MAX_BSS_NUM; i++) { + /* Allocate memory for private structure */ + adapter->priv[i] =3D + kzalloc(sizeof(struct nxpwifi_private), GFP_KERNEL); + if (!adapter->priv[i]) { + ret =3D -ENOMEM; + goto error; + } + + adapter->priv[i]->adapter =3D 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 =3D 0; i < adapter->priv_num; i++) + kfree(adapter->priv[i]); + + kfree(adapter); + + return ERR_PTR(ret); +} + +/* Unregister device; free timers, beacons, privs, nd_info, adapter. */ +static void nxpwifi_unregister(struct nxpwifi_adapter *adapter) +{ + s32 i; + + if (adapter->if_ops.cleanup_if) + adapter->if_ops.cleanup_if(adapter); + + timer_delete_sync(&adapter->cmd_timer); + + /* Free private structures */ + for (i =3D 0; i < adapter->priv_num; i++) { + nxpwifi_free_curr_bcn(adapter->priv[i]); + kfree(adapter->priv[i]); + } + + if (adapter->nd_info) { + for (i =3D 0 ; i < adapter->nd_info->n_matches ; i++) + kfree(adapter->nd_info->matches[i]); + kfree(adapter->nd_info); + adapter->nd_info =3D NULL; + } + + kfree(adapter->regd); + + kfree(adapter); +} + +static void nxpwifi_queue_rx_work(struct nxpwifi_adapter *adapter) +{ + queue_work(adapter->rx_workqueue, &adapter->rx_work); +} + +static void nxpwifi_process_rx(struct nxpwifi_adapter *adapter) +{ + struct sk_buff *skb; + struct nxpwifi_rxinfo *rx_info; + + if (atomic_read(&adapter->iface_changing) || + atomic_read(&adapter->rx_ba_teardown_pending)) + return; + + /* Check for Rx data */ + while ((skb =3D skb_dequeue(&adapter->rx_data_q))) { + atomic_dec(&adapter->rx_pending); + if (adapter->delay_main_work && + (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) { + adapter->delay_main_work =3D false; + nxpwifi_queue_work(adapter, &adapter->main_work); + } + rx_info =3D NXPWIFI_SKB_RXCB(skb); + if (rx_info->buf_type =3D=3D 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 =3D nxpwifi_get_priv(adapter, NXPWIFI_BSS_RO= LE_STA); + struct nxpwifi_ver_ext ver_ext; + + if (test_and_set_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &adapter->work_flag= s)) + return; + + memset(&ver_ext, 0, sizeof(ver_ext)); + ver_ext.version_str_sel =3D 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"); + } +} + +static inline void nxpwifi_handle_irq_status(struct nxpwifi_adapter *adapt= er, u8 istat) +{ + if (adapter->hs_activated) + nxpwifi_process_hs_config(adapter); + if (adapter->if_ops.process_int_status) + adapter->if_ops.process_int_status(adapter, istat); +} + +static inline bool nxpwifi_drain_tx(struct nxpwifi_adapter *adapter) +{ + bool ret =3D false; + + 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 =3D 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); + } + ret =3D true; + } + + 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 =3D 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); + } + ret =3D true; + } + + 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 =3D 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); + } + ret =3D true; + } + + return ret; +} + +static inline bool nxpwifi_handle_rx(struct nxpwifi_adapter *adapter) +{ + if (adapter->rx_work_enabled && adapter->data_received) { + nxpwifi_queue_rx_work(adapter); + return true; + } + + return false; +} + +static inline bool nxpwifi_handle_cmd_response(struct nxpwifi_adapter *ada= pter) +{ + /* Check for Cmd Resp */ + if (adapter->cmd_resp_received) { + adapter->cmd_resp_received =3D false; + nxpwifi_process_cmdresp(adapter); + return true; + } + + return false; +} + +static inline bool nxpwifi_handle_events(struct nxpwifi_adapter *adapter) +{ + if (adapter->event_received) { + adapter->event_received =3D false; + nxpwifi_process_event(adapter); + return true; + } + + return false; +} + +static inline bool nxpwifi_tx_has_pending(struct nxpwifi_adapter *adapter) +{ + return !skb_queue_empty(&adapter->tx_data_q) || + !nxpwifi_bypass_txlist_empty(adapter) || + !nxpwifi_wmm_lists_empty(adapter); +} + +static inline bool nxpwifi_cmd_has_pending(struct nxpwifi_adapter *adapter) +{ + return !list_empty(&adapter->cmd_pending_q); +} + +static inline bool nxpwifi_events_has_pending(struct nxpwifi_adapter *adap= ter) +{ + return adapter->event_received; +} + +static inline bool nxpwifi_should_wakeup_card(struct nxpwifi_adapter *adap= ter) +{ + if (adapter->ps_state !=3D PS_STATE_SLEEP) + return false; + + if (!adapter->pm_wakeup_card_req || adapter->pm_wakeup_fw_try) + return false; + + return is_command_pending(adapter) || nxpwifi_tx_has_pending(adapter); +} + +static inline bool nxpwifi_should_exit_main_loop(struct nxpwifi_adapter *a= dapter) +{ + if (adapter->pm_wakeup_fw_try) + return true; + + if (adapter->ps_state =3D=3D PS_STATE_PRE_SLEEP) + nxpwifi_check_ps_cond(adapter); + + if (adapter->ps_state !=3D PS_STATE_AWAKE) + return true; + + if (adapter->tx_lock_flag) + return true; + + if ((!adapter->scan_chan_gap_enabled && adapter->scan_processing) || + adapter->data_sent || !nxpwifi_tx_has_pending(adapter)) { + if (adapter->cmd_sent || adapter->curr_cmd || + !is_command_pending(adapter)) + return true; + } + + return false; +} + +static inline void nxpwifi_wakeup_card(struct nxpwifi_adapter *adapter) +{ + adapter->pm_wakeup_fw_try =3D true; + mod_timer(&adapter->wakeup_timer, jiffies + (HZ * 3)); + adapter->if_ops.wakeup(adapter); +} + +static inline void nxpwifi_handle_vdll_download(struct nxpwifi_adapter *ad= apter) +{ + if (!adapter->cmd_sent && adapter->vdll_ctrl.pending_block) { + struct vdll_dnld_ctrl *ctrl =3D &adapter->vdll_ctrl; + + nxpwifi_download_vdll_block(adapter, ctrl->pending_block, + ctrl->pending_block_len); + ctrl->pending_block =3D NULL; + } +} + +static inline void nxpwifi_finish_delayed_null_pkt(struct nxpwifi_adapter = *adapter) +{ + if (!adapter->delay_null_pkt) + return; + + if (adapter->cmd_sent || adapter->curr_cmd || is_command_pending(adapter)) + return; + + if (nxpwifi_tx_has_pending(adapter)) + return; + + 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 =3D false; + adapter->ps_state =3D PS_STATE_SLEEP; + } +} + +static inline bool nxpwifi_pump_command(struct nxpwifi_adapter *adapter) +{ + if (!adapter->cmd_sent && !adapter->curr_cmd) { + if (!nxpwifi_exec_next_cmd(adapter)) + return true; + } + + return false; +} + +/* Main loop: IRQ/RX/CMD/EVENT; wake card; TX; PS null; exit if idle. */ +void nxpwifi_main_process(struct nxpwifi_adapter *adapter) +{ + unsigned long flags; + + /* Check if virtual interface changing */ + if (atomic_read(&adapter->iface_changing)) { + nxpwifi_dbg(adapter, + INFO, "main_process skipped due to iface_changing"); + return; + } + + for (;;) { + bool did_work =3D false; + u8 istat =3D 0; + + if (adapter->hw_status =3D=3D NXPWIFI_HW_STATUS_NOT_READY) + break; + + /* + * For non-USB interfaces, If we process interrupts first, it + * would increase RX pending even further. Avoid this by + * checking if rx_pending has crossed high threshold and + * schedule rx work queue and then process interrupts. + * For USB interface, there are no interrupts. We already have + * HIGH_RX_PENDING check in usb.c + */ + if (atomic_read(&adapter->rx_pending) >=3D HIGH_RX_PENDING) { + adapter->delay_main_work =3D true; + nxpwifi_queue_rx_work(adapter); + break; + } + + /* + * Snapshot-and-clear the interrupt status. + * + * Take the same lock as the producer (nxpwifi_sdio_interrupt()) uses + * when OR-ing new bits into adapter->int_status. We atomically grab + * what has accumulated and clear it, so this consumer owns this batch. + */ + spin_lock_irqsave(&adapter->int_lock, flags); + istat =3D adapter->int_status; + adapter->int_status =3D 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + /* Handle pending interrupt if any */ + if (istat) { + nxpwifi_handle_irq_status(adapter, istat); + did_work =3D true; + } + + did_work |=3D nxpwifi_handle_rx(adapter); + + if (nxpwifi_should_wakeup_card(adapter)) { + nxpwifi_wakeup_card(adapter); + continue; + } + + if (IS_CARD_RX_RCVD(adapter)) { + /* Card has responded, clear wakeup state and update power state */ + adapter->data_received =3D false; + adapter->pm_wakeup_fw_try =3D false; + timer_delete(&adapter->wakeup_timer); + if (adapter->ps_state =3D=3D PS_STATE_SLEEP) + adapter->ps_state =3D PS_STATE_AWAKE; + } else { + if (nxpwifi_should_exit_main_loop(adapter)) + break; + } + + did_work |=3D nxpwifi_handle_events(adapter); + + did_work |=3D nxpwifi_handle_cmd_response(adapter); + + /* Check if we need to confirm Sleep Request received previously */ + if (adapter->ps_state =3D=3D 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 !=3D PS_STATE_AWAKE) + continue; + + if (adapter->tx_lock_flag) + continue; + + nxpwifi_handle_vdll_download(adapter); + + did_work |=3D nxpwifi_pump_command(adapter); + + did_work |=3D nxpwifi_drain_tx(adapter); + + /* + * Attempt to send delayed null packet. + * If successful, firmware will enter sleep and ps_state will be updated. + * We check ps_state here to determine if main loop can safely exit. + */ + nxpwifi_finish_delayed_null_pkt(adapter); + + if (adapter->ps_state =3D=3D PS_STATE_SLEEP) + break; + /* + * Step 3) Cooperative preemption point. + * cond_resched() yields ONLY if need_resched() is set. Placing it + * BEFORE the final check improves fairness: it lets ksdioirqd (RT/FIFO) + * or other producers run and set new int_status bits. Immediately + * after we return here, we perform the final "net cast" (Step 4) to + * decide if we should continue or return. + */ + + cond_resched(); + + /* + * Step 4) Exit decision with lost-kick closure. + * + * We consider exiting ONLY when this round did no real work. + * Rationale: + * - If did_work =3D=3D true: we will loop anyway; at Step 1 we will + * re-snapshot int_status, so there's no need to re-check now. + * - If did_work =3D=3D false: we appear idle and may return. But duri= ng + * our execution window, a producer may have just set int_status and + * queue_work(); since this work is still running, queue_work() + * returns false (no second instance queued). If we return now, + * we'd leave unprocessed status with no pending work =3D> lost-kick. + * + * Therefore, perform a single final check: if *anything* is pending, + * continue looping; otherwise, break and return. + */ + if (!did_work) { + bool more =3D false; + unsigned long flags; + /* 4a) New IRQ bits raced in while we were running? */ + spin_lock_irqsave(&adapter->int_lock, flags); + more |=3D adapter->int_status !=3D 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + /* 4b) Any other sources still pending? (driver-specific) */ + more |=3D nxpwifi_tx_has_pending(adapter); + more |=3D nxpwifi_cmd_has_pending(adapter); + more |=3D nxpwifi_events_has_pending(adapter); + + if (!more) + break; /* Truly quiescent now: safe to return. */ + /* else: loop back to Step 1 to consume what just arrived. */ + } + /* If did_work =3D=3D true, we loop unconditionally and re-snapshot. */ + }; +} + +/* Free adapter via nxpwifi_unregister(). */ +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__); +} + +/* Destroy main and RX workqueues. */ +static void nxpwifi_terminate_workqueue(struct nxpwifi_adapter *adapter) +{ + if (adapter->workqueue) { + destroy_workqueue(adapter->workqueue); + adapter->workqueue =3D NULL; + } + + if (adapter->rx_workqueue) { + destroy_workqueue(adapter->rx_workqueue); + adapter->rx_workqueue =3D NULL; + } +} + +/* FW bring-up: download, enable IRQ, init FW; cfg80211+ifaces; cleanup. */ +static int _nxpwifi_fw_dpc(const struct firmware *firmware, void *context) +{ + int ret =3D 0; + char fmt[64]; + struct nxpwifi_adapter *adapter =3D context; + struct nxpwifi_fw_image fw; + bool init_failed =3D false; + struct wireless_dev *wdev; + struct completion *fw_done =3D adapter->fw_done; + + if (!firmware) { + nxpwifi_dbg(adapter, ERROR, + "Failed to get firmware %s\n", adapter->fw_name); + ret =3D -EINVAL; + goto err_dnld_fw; + } + + memset(&fw, 0, sizeof(struct nxpwifi_fw_image)); + adapter->firmware =3D firmware; + fw.fw_buf =3D (u8 *)adapter->firmware->data; + fw.fw_len =3D adapter->firmware->size; + + if (adapter->if_ops.dnld_fw) + ret =3D adapter->if_ops.dnld_fw(adapter, &fw); + else + ret =3D 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 =3D adapter->if_ops.enable_int(adapter); + if (ret) + goto err_dnld_fw; + } + + ret =3D nxpwifi_init_fw(adapter); + if (ret) + goto err_init_fw; + + maybe_quirk_fw_disable_ds(adapter); + + 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(); + /* Create station interface by default */ + wdev =3D 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"); + rtnl_unlock(); + goto err_add_intf; + } + + wdev =3D 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"); + rtnl_unlock(); + goto err_add_intf; + } + + rtnl_unlock(); + + nxpwifi_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); + nxpwifi_dbg(adapter, MSG, "driver_version =3D %s\n", fmt); + adapter->is_up =3D 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); + nxpwifi_terminate_workqueue(adapter); + + if (adapter->hw_status =3D=3D NXPWIFI_HW_STATUS_READY) { + pr_debug("info: %s: shutdown nxpwifi\n", __func__); + nxpwifi_shutdown_drv(adapter); + nxpwifi_free_cmd_buffers(adapter); + } + + init_failed =3D true; +done: + if (adapter->cal_data) { + release_firmware(adapter->cal_data); + adapter->cal_data =3D NULL; + } + if (adapter->firmware) { + release_firmware(adapter->firmware); + adapter->firmware =3D NULL; + } + if (init_failed) { + if (adapter->irq_wakeup >=3D 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); +} + +/* Request firmware (sync/async) and start HW init. */ +static int nxpwifi_init_hw_fw(struct nxpwifi_adapter *adapter, + bool req_fw_nowait) +{ + int ret; + + if (req_fw_nowait) { + ret =3D request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, + adapter->dev, GFP_KERNEL, adapter, + nxpwifi_fw_dpc); + } else { + ret =3D 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; +} + +/* ndo_open: bring carrier down. */ +static int +nxpwifi_open(struct net_device *dev) +{ + netif_carrier_off(dev); + + return 0; +} + +/* ndo_stop: abort scan/sched-scan if running. */ +static int +nxpwifi_close(struct net_device *dev) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + + if (priv->scan_request) { + struct cfg80211_scan_info info =3D { + .aborted =3D true, + }; + + nxpwifi_dbg(priv->adapter, INFO, + "aborting scan on ndo_stop\n"); + cfg80211_scan_done(priv->scan_request, &info); + priv->scan_request =3D NULL; + priv->scan_aborting =3D 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 =3D (struct ethhdr *)skb->data; + + if (eth_hdr->h_proto =3D=3D 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 =3D=3D htons(ETH_P_PAE)) + nxpwifi_dbg(priv->adapter, MSG, + "key: send EAPOL to %pM\n", + eth_hdr->h_dest); + return true; + } + + return false; +} + +/* Queue SKB (WMM or bypass) and schedule main work. */ +void nxpwifi_queue_tx_pkt(struct nxpwifi_private *priv, struct sk_buff *sk= b) +{ + struct nxpwifi_adapter *adapter =3D priv->adapter; + struct netdev_queue *txq; + int index =3D nxpwifi_1d_to_wmm_queue[skb->priority]; + + if (atomic_inc_return(&priv->wmm_tx_pending[index]) >=3D MAX_TX_PENDING) { + txq =3D 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 =3D skb; + struct nxpwifi_txinfo *tx_info, *orig_tx_info; + u32 id32 =3D 0; + int ret; + + skb =3D skb_clone(skb, GFP_ATOMIC); + if (skb) { + spin_lock_bh(&priv->ack_status_lock); + /* + * Use XArray to allocate IDs in the range 1..0x0F. + * Limit ensures the allocated token ID is always within this + * range. + */ + ret =3D xa_alloc(&priv->ack_status_frames, &id32, orig_skb, + XA_LIMIT(1, 0x0f), GFP_ATOMIC); + spin_unlock_bh(&priv->ack_status_lock); + + if (ret =3D=3D 0) { + tx_info =3D NXPWIFI_SKB_TXCB(skb); + tx_info->ack_frame_id =3D id32; + tx_info->flags |=3D flag; + orig_tx_info =3D NXPWIFI_SKB_TXCB(orig_skb); + orig_tx_info->ack_frame_id =3D id32; + orig_tx_info->flags |=3D flag; + + if (flag =3D=3D NXPWIFI_BUF_FLAG_ACTION_TX_STATUS && cookie) + orig_tx_info->cookie =3D *cookie; + + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb =3D orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb =3D orig_skb; + } + + return skb; +} + +/* ndo_start_xmit: fix headroom, fill TXCB, timestamp, enqueue. */ +static netdev_tx_t +nxpwifi_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct nxpwifi_private *priv =3D 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 <=3D 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 =3D + 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 =3D new_skb; + nxpwifi_dbg(priv->adapter, INFO, + "info: new skb headroomd %d\n", + skb_headroom(skb)); + } + + tx_info =3D NXPWIFI_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num =3D priv->bss_num; + tx_info->bss_type =3D priv->bss_type; + tx_info->pkt_len =3D skb->len; + + multicast =3D is_multicast_ether_addr(skb->data); + + if (unlikely(!multicast && sk_requests_wifi_status(skb->sk) && + priv->adapter->fw_api_ver =3D=3D NXPWIFI_FW_V15)) + skb =3D 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 =3D ether_addr_to_u64(priv->curr_addr); + + if (external) { + mac_addr =3D ether_addr_to_u64(new_mac); + } else { + /* Internal mac address change */ + if (priv->bss_type =3D=3D NXPWIFI_BSS_TYPE_ANY) + return -EOPNOTSUPP; + + mac_addr =3D old_mac_addr; + + if (priv->adapter->priv[0] !=3D priv) { + /* Set mac address based on bss_type/bss_num */ + mac_addr ^=3D BIT_ULL(priv->bss_type + 8); + mac_addr +=3D priv->bss_num; + } + } + + u64_to_ether_addr(mac_addr, priv->curr_addr); + + /* Send request to firmware */ + ret =3D 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=3D%d\n", ret); + return ret; + } + + eth_hw_addr_set(dev, priv->curr_addr); + return 0; +} + +/* ndo_set_mac_address: set MAC via firmware. */ +static int +nxpwifi_ndo_set_mac_address(struct net_device *dev, void *addr) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + struct sockaddr *hw_addr =3D addr; + + return nxpwifi_set_mac_address(priv, dev, true, hw_addr->sa_data); +} + +/* ndo_set_rx_mode: promisc/allmulti or program multicast. */ +static void nxpwifi_set_multicast_list(struct net_device *dev) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + struct nxpwifi_multicast_list mcast_list; + + if (dev->flags & IFF_PROMISC) { + mcast_list.mode =3D NXPWIFI_PROMISC_MODE; + } else if (dev->flags & IFF_ALLMULTI || + netdev_mc_count(dev) > NXPWIFI_MAX_MULTICAST_LIST_SIZE) { + mcast_list.mode =3D NXPWIFI_ALL_MULTI_MODE; + } else { + mcast_list.mode =3D NXPWIFI_MULTICAST_MODE; + mcast_list.num_multicast_addr =3D + nxpwifi_copy_mcast_addr(&mcast_list, dev); + } + nxpwifi_request_set_multicast_list(priv, &mcast_list); +} + +/* ndo_tx_timeout: account; reset card on threshold. */ +static void +nxpwifi_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct nxpwifi_private *priv =3D 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 =3D %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, + "=3D=3D 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, + "=3D=3D 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 =3D NULL; + adapter->devdump_len =3D 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, "=3D=3D=3Dnxpwifi driverinfo dump start=3D=3D= =3D\n"); + + p =3D adapter->devdump_data; + strscpy(p, "=3D=3D=3D=3D=3D=3D=3D=3DStart dump driverinfo=3D=3D=3D=3D=3D= =3D=3D=3D\n", NXPWIFI_FW_DUMP_SIZE); + p +=3D strlen("=3D=3D=3D=3D=3D=3D=3D=3DStart dump driverinfo=3D=3D=3D=3D= =3D=3D=3D=3D\n"); + p +=3D sprintf(p, "driver_name =3D "); + p +=3D sprintf(p, "\"nxpwifi\"\n"); + + nxpwifi_drv_get_driver_version(adapter, drv_version, + sizeof(drv_version) - 1); + p +=3D sprintf(p, "driver_version =3D %s\n", drv_version); + + p +=3D sprintf(p, "tx_pending =3D %d\n", + atomic_read(&adapter->tx_pending)); + p +=3D sprintf(p, "rx_pending =3D %d\n", + atomic_read(&adapter->rx_pending)); + + if (adapter->iface_type =3D=3D NXPWIFI_SDIO) { + sdio_card =3D (struct sdio_mmc_card *)adapter->card; + p +=3D sprintf(p, "\nmp_rd_bitmap=3D0x%x curr_rd_port=3D0x%x\n", + sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); + p +=3D sprintf(p, "mp_wr_bitmap=3D0x%x curr_wr_port=3D0x%x\n", + sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); + } + + for (i =3D 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]->netdev) + continue; + priv =3D adapter->priv[i]; + p +=3D sprintf(p, "\n[interface : \"%s\"]\n", + priv->netdev->name); + p +=3D sprintf(p, "wmm_tx_pending[0] =3D %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + p +=3D sprintf(p, "wmm_tx_pending[1] =3D %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + p +=3D sprintf(p, "wmm_tx_pending[2] =3D %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + p +=3D sprintf(p, "wmm_tx_pending[3] =3D %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + p +=3D sprintf(p, "media_state=3D\"%s\"\n", !priv->media_connected ? + "Disconnected" : "Connected"); + p +=3D sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) + ? "on" : "off")); + for (idx =3D 0; idx < priv->netdev->num_tx_queues; idx++) { + txq =3D netdev_get_tx_queue(priv->netdev, idx); + p +=3D sprintf(p, "tx queue %d:%s ", idx, + netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p +=3D sprintf(p, "\n%s: num_tx_timeout =3D %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (adapter->iface_type =3D=3D NXPWIFI_SDIO) { + p +=3D sprintf(p, "\n=3D=3D=3D %s register dump=3D=3D=3D\n", "SDIO"); + if (adapter->if_ops.reg_dump) + p +=3D adapter->if_ops.reg_dump(adapter, p); + } + p +=3D sprintf(p, "\n=3D=3D=3D more debug information\n"); + debug_info =3D kzalloc(sizeof(*debug_info), GFP_KERNEL); + if (debug_info) { + for (i =3D 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]->netdev) + continue; + priv =3D adapter->priv[i]; + nxpwifi_get_debug_info(priv, debug_info); + p +=3D nxpwifi_debug_info_to_buffer(priv, p, debug_info); + break; + } + kfree(debug_info); + } + + p +=3D sprintf(p, "\n=3D=3D=3D=3D=3D=3D=3D=3DEnd dump=3D=3D=3D=3D=3D=3D= =3D=3D\n"); + nxpwifi_dbg(adapter, MSG, "=3D=3D=3Dnxpwifi driverinfo dump end=3D=3D=3D\= n"); + adapter->devdump_len =3D 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 =3D 0; + + for (idx =3D 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry =3D + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + dump_len +=3D (strlen("=3D=3D=3D=3D=3D=3D=3D=3DStart dump ") + + strlen(entry->mem_name) + + strlen("=3D=3D=3D=3D=3D=3D=3D=3D\n") + + (entry->mem_size + 1) + + strlen("\n=3D=3D=3D=3D=3D=3D=3D=3DEnd dump=3D=3D=3D=3D=3D=3D=3D=3D\n"= )); + } + } + + if (dump_len + 1 + adapter->devdump_len > NXPWIFI_FW_DUMP_SIZE) { + /* Realloc in case buffer overflow */ + fw_dump_ptr =3D 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 =3D fw_dump_ptr; + } + + fw_dump_ptr =3D (char *)adapter->devdump_data + adapter->devdump_len; + + for (idx =3D 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry =3D + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + fw_dump_ptr +=3D sprintf(fw_dump_ptr, "=3D=3D=3D=3D=3D=3D=3D=3DStart du= mp "); + fw_dump_ptr +=3D sprintf(fw_dump_ptr, "%s", entry->mem_name); + fw_dump_ptr +=3D sprintf(fw_dump_ptr, "=3D=3D=3D=3D=3D=3D=3D=3D\n"); + memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); + fw_dump_ptr +=3D entry->mem_size; + fw_dump_ptr +=3D sprintf(fw_dump_ptr, "\n=3D=3D=3D=3D=3D=3D=3D=3DEnd du= mp=3D=3D=3D=3D=3D=3D=3D=3D\n"); + } + } + + adapter->devdump_len =3D fw_dump_ptr - (char *)adapter->devdump_data; + + for (idx =3D 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry =3D + &adapter->mem_type_mapping_tbl[idx]; + + vfree(entry->mem_ptr); + entry->mem_ptr =3D NULL; + entry->mem_size =3D 0; + } +} +EXPORT_SYMBOL_GPL(nxpwifi_prepare_fw_dump_info); + +/* ndo_get_stats: return netdev stats. */ +static struct net_device_stats *nxpwifi_get_stats(struct net_device *dev) +{ + struct nxpwifi_private *priv =3D nxpwifi_netdev_get_priv(dev); + + return &priv->stats; +} + +static u16 +nxpwifi_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *sk= b, + struct net_device *sb_dev) +{ + skb->priority =3D cfg80211_classify8021d(skb, NULL); + return nxpwifi_1d_to_wmm_queue[skb->priority]; +} + +/* Network device handlers */ +static const struct net_device_ops nxpwifi_netdev_ops =3D { + .ndo_open =3D nxpwifi_open, + .ndo_stop =3D nxpwifi_close, + .ndo_start_xmit =3D nxpwifi_hard_start_xmit, + .ndo_set_mac_address =3D nxpwifi_ndo_set_mac_address, + .ndo_validate_addr =3D eth_validate_addr, + .ndo_tx_timeout =3D nxpwifi_tx_timeout, + .ndo_get_stats =3D nxpwifi_get_stats, + .ndo_set_rx_mode =3D nxpwifi_set_multicast_list, + .ndo_select_queue =3D nxpwifi_netdev_select_wmm_queue, +}; + +/* Init per-interface defaults: ops, addrs, mgmt IEs, stats. */ +void nxpwifi_init_priv_params(struct nxpwifi_private *priv, + struct net_device *dev) +{ + dev->netdev_ops =3D &nxpwifi_netdev_ops; + dev->needs_free_netdev =3D true; + /* Initialize private structure */ + priv->current_key_index =3D 0; + priv->media_connected =3D false; + memset(priv->mgmt_ie, 0, + sizeof(struct nxpwifi_ie) * MAX_MGMT_IE_INDEX); + priv->beacon_idx =3D NXPWIFI_AUTO_IDX_MASK; + priv->proberesp_idx =3D NXPWIFI_AUTO_IDX_MASK; + priv->assocresp_idx =3D NXPWIFI_AUTO_IDX_MASK; + priv->gen_idx =3D NXPWIFI_AUTO_IDX_MASK; + priv->num_tx_timeout =3D 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) =3D=3D NXPWIFI_BSS_ROLE_STA || + GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_UAP) { + priv->hist_data =3D kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); + if (priv->hist_data) + nxpwifi_hist_data_reset(priv); + } +} + +/* Return true if any 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 =3D list_empty(&adapter->cmd_pending_q); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + + return !is_cmd_pend_q_empty; +} + +/* Host MLME work: deliver RX; handle assoc/link-loss. */ +static void nxpwifi_host_mlme_work(struct wiphy *wiphy, struct wiphy_work = *work) +{ + struct nxpwifi_adapter *adapter =3D + 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 =3D skb_dequeue(&adapter->rx_mlme_q))) { + rx_info =3D NXPWIFI_SKB_RXCB(skb); + priv =3D adapter->priv[rx_info->bss_num]; + cfg80211_rx_mlme_mgmt(priv->netdev, + skb->data, + rx_info->pkt_len); + } + + /* 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 =3D NULL; + } + adapter->host_mlme_link_lost =3D false; + } + + /* Check for host mlme Assoc Resp */ + if (adapter->assoc_resp_received) { + nxpwifi_process_assoc_resp(adapter); + adapter->assoc_resp_received =3D false; + } +} + +/* RX work: process RX queue. */ +static void nxpwifi_rx_work(struct work_struct *work) +{ + struct nxpwifi_adapter *adapter =3D + container_of(work, struct nxpwifi_adapter, rx_work); + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) + return; + nxpwifi_process_rx(adapter); +} + +/* Main work: run nxpwifi_main_process(). */ +static void nxpwifi_main_work(struct work_struct *work) +{ + struct nxpwifi_adapter *adapter =3D + container_of(work, struct nxpwifi_adapter, main_work); + + if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) + return; + nxpwifi_main_process(adapter); +} + +/* Teardown: disable IRQs, stop queues, shutdown, remove ifaces, unreg. */ +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); + nxpwifi_terminate_workqueue(adapter); + adapter->int_status =3D 0; + + /* Stop data */ + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D 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->rx_pending) || + atomic_read(&adapter->tx_pending) || + atomic_read(&adapter->cmd_pending)) { + nxpwifi_dbg(adapter, ERROR, + "rx_pending=3D%d, tx_pending=3D%d,\t" + "cmd_pending=3D%d\n", + atomic_read(&adapter->rx_pending), + atomic_read(&adapter->tx_pending), + atomic_read(&adapter->cmd_pending)); + } + + for (i =3D 0; i < adapter->priv_num; i++) { + priv =3D adapter->priv[i]; + rtnl_lock(); + if (priv->netdev && + priv->wdev.iftype !=3D 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 =3D NULL; + + vfree(adapter->chan_stats); + nxpwifi_free_cmd_buffers(adapter); +} + +/* Shut down SW/FW and mark device down. */ +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 =3D 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 =3D false; +} +EXPORT_SYMBOL_GPL(nxpwifi_shutdown_sw); + +/* Re-init adapter SW and bring device up. */ +int +nxpwifi_reinit_sw(struct nxpwifi_adapter *adapter) +{ + int ret =3D 0; + + nxpwifi_init_lock_list(adapter); + if (adapter->if_ops.up_dev) + adapter->if_ops.up_dev(adapter); + + adapter->hw_status =3D NXPWIFI_HW_STATUS_INITIALIZING; + clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + adapter->hs_activated =3D 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 =3D 0; + adapter->scan_wait_q_woken =3D false; + + if (num_possible_cpus() > 1) + adapter->rx_work_enabled =3D true; + + adapter->workqueue =3D + alloc_workqueue("NXPWIFI_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0); + if (!adapter->workqueue) { + ret =3D -ENOMEM; + goto err_kmalloc; + } + + INIT_WORK(&adapter->main_work, nxpwifi_main_work); + + if (adapter->rx_work_enabled) { + adapter->rx_workqueue =3D alloc_workqueue("NXPWIFI_RX_WORK_QUEUE", + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 0); + if (!adapter->rx_workqueue) { + ret =3D -ENOMEM; + goto err_kmalloc; + } + INIT_WORK(&adapter->rx_work, nxpwifi_rx_work); + } + + wiphy_work_init(&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 =3D 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 =3D _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__); + + 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); + nxpwifi_terminate_workqueue(adapter); + if (adapter->hw_status =3D=3D 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 =3D priv; + + nxpwifi_dbg(adapter, INFO, "%s: wake by wifi", __func__); + adapter->wake_by_wifi =3D 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 =3D adapter->dev; + + if (!dev->of_node) + goto err_exit; + + adapter->dt_node =3D dev->of_node; + adapter->irq_wakeup =3D irq_of_parse_and_map(adapter->dt_node, 0); + if (!adapter->irq_wakeup) { + nxpwifi_dbg(adapter, ERROR, "fail to parse irq_wakeup from device tree\n= "); + goto err_exit; + } + + ret =3D devm_request_irq(dev, adapter->irq_wakeup, + nxpwifi_irq_wakeup_handler, + IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN, + "wifi_wake", adapter); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "Failed to request irq_wakeup %d (%d)\n", + adapter->irq_wakeup, ret); + goto err_exit; + } + + if (device_init_wakeup(dev, true)) { + nxpwifi_dbg(adapter, ERROR, "fail to init wakeup for nxpwifi\n"); + goto err_exit; + } + return; + +err_exit: + adapter->irq_wakeup =3D -1; +} + +/* Add card: register adapter, workqueues, device; request FW (async). */ +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 =3D 0; + + adapter =3D nxpwifi_register(card, dev, if_ops); + if (IS_ERR(adapter)) { + ret =3D PTR_ERR(adapter); + pr_err("%s: adapter register failed %d\n", __func__, ret); + goto err_init_sw; + } + + nxpwifi_probe_of(adapter); + + adapter->iface_type =3D iface_type; + adapter->fw_done =3D fw_done; + + adapter->hw_status =3D NXPWIFI_HW_STATUS_INITIALIZING; + clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags); + clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags); + adapter->hs_activated =3D false; + init_waitqueue_head(&adapter->hs_activate_wait_q); + init_waitqueue_head(&adapter->cmd_wait_q.wait); + adapter->cmd_wait_q.status =3D 0; + adapter->scan_wait_q_woken =3D false; + + if (num_possible_cpus() > 1) + adapter->rx_work_enabled =3D true; + + adapter->workqueue =3D + alloc_workqueue("NXPWIFI_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0); + if (!adapter->workqueue) { + ret =3D -ENOMEM; + goto err_kmalloc; + } + + INIT_WORK(&adapter->main_work, nxpwifi_main_work); + + if (adapter->rx_work_enabled) { + adapter->rx_workqueue =3D alloc_workqueue("NXPWIFI_RX_WORK_QUEUE", + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 0); + if (!adapter->rx_workqueue) { + ret =3D -ENOMEM; + goto err_kmalloc; + } + + INIT_WORK(&adapter->rx_work, nxpwifi_rx_work); + } + + wiphy_work_init(&adapter->host_mlme_work, nxpwifi_host_mlme_work); + + /* + * Register the device. Fill up the private data structure with relevant + * information from the card. + */ + ret =3D adapter->if_ops.register_dev(adapter); + if (ret) { + pr_err("%s: failed to register nxpwifi device\n", __func__); + goto err_registerdev; + } + + ret =3D nxpwifi_init_hw_fw(adapter, true); + if (ret) { + pr_err("%s: firmware init failed\n", __func__); + goto err_init_fw; + } + + 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); + +err_kmalloc: + nxpwifi_terminate_workqueue(adapter); + + if (adapter->hw_status =3D=3D NXPWIFI_HW_STATUS_READY) { + pr_debug("info: %s: shutdown nxpwifi\n", __func__); + nxpwifi_shutdown_drv(adapter); + nxpwifi_free_cmd_buffers(adapter); + } + + if (adapter->irq_wakeup >=3D 0) + device_init_wakeup(adapter->dev, false); + nxpwifi_free_adapter(adapter); + +err_init_sw: + + return ret; +} +EXPORT_SYMBOL_GPL(nxpwifi_add_card); + +/* Remove card: teardown SW, unregister device, free adapter. */ +void nxpwifi_remove_card(struct nxpwifi_adapter *adapter) +{ + if (!adapter) + return; + + if (adapter->is_up) + nxpwifi_uninit_sw(adapter); + + if (adapter->irq_wakeup >=3D 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 =3D fmt; + vaf.va =3D &args; + + if (adapter->dev) + dev_info(adapter->dev, "%pV", &vaf); + else + pr_info("%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(_nxpwifi_dbg); + +/* Module init: init debugfs if enabled. */ +static int +nxpwifi_init_module(void) +{ +#ifdef CONFIG_DEBUG_FS + nxpwifi_debugfs_init(); +#endif + return 0; +} + +/* Module exit: remove debugfs if enabled. */ +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..3b1f7f3d2df7 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/main.h @@ -0,0 +1,1800 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * nxpwifi: main 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; + +/* command type */ +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 +#define PKT_TYPE_802DOT11 0x05 +/* check if any data / resp / event is received from card */ +#define IS_CARD_RX_RCVD(adapter) ({ \ + typeof(adapter) (_adapter) =3D 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 + +/* tx_timeout threshold to trigger card reset */ +#define TX_TIMEOUT_THRESHOLD 6 + +#define NXPWIFI_DRV_INFO_SIZE_MAX 0x40000 + +/* address alignment helper */ +#define NXPWIFI_ALIGN_ADDR(p, a) ({ \ + typeof(a) (_a) =3D a; \ + (((long)(p) + (_a) - 1) & ~((_a) - 1)); \ + }) + +#define NXPWIFI_MAC_LOCAL_ADMIN_BIT 41 + +/* bit helper */ +#define MBIT(x) (((u32)1) << (x)) + +/* enum nxpwifi_debug_level - nxp wifi debug level */ +enum NXPWIFI_DEBUG_LEVEL { + NXPWIFI_DBG_MSG =3D 0x00000001, + NXPWIFI_DBG_FATAL =3D 0x00000002, + NXPWIFI_DBG_ERROR =3D 0x00000004, + NXPWIFI_DBG_DATA =3D 0x00000008, + NXPWIFI_DBG_CMD =3D 0x00000010, + NXPWIFI_DBG_EVENT =3D 0x00000020, + NXPWIFI_DBG_INTR =3D 0x00000040, + NXPWIFI_DBG_IOCTL =3D 0x00000080, + NXPWIFI_DBG_MPA_D =3D 0x00008000, + NXPWIFI_DBG_DAT_D =3D 0x00010000, + NXPWIFI_DBG_CMD_D =3D 0x00020000, + NXPWIFI_DBG_EVT_D =3D 0x00040000, + NXPWIFI_DBG_FW_D =3D 0x00080000, + NXPWIFI_DBG_IF_D =3D 0x00100000, + NXPWIFI_DBG_ENTRY =3D 0x10000000, + NXPWIFI_DBG_WARN =3D 0x20000000, + NXPWIFI_DBG_INFO =3D 0x40000000, + NXPWIFI_DBG_DUMP =3D 0x80000000, + NXPWIFI_DBG_ANY =3D 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 +/* bgscan interval (ms) and 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_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 =3D 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]; + /* protects 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 queued TX packets */ + atomic_t tx_pkts_queued; + /* highest priority currently 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 =3D 0, + ENABLE_11D =3D 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[MAX_NUM_TID]; + /* spin lock for tx_ba_stream_tbl_ptr queue */ + struct spinlock tx_ba_stream_tbl_lock[MAX_NUM_TID]; + 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[MAX_NUM_TID]; + /* spin lock for rx_reorder_tbl_ptr queue */ + struct spinlock rx_reorder_tbl_lock[MAX_NUM_TID]; +#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 xarray 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 wiphy_work reset_conn_state_work; + struct wiphy_delayed_work dfs_cac_work; + struct wiphy_delayed_work dfs_chan_sw_work; + bool uap_stop_tx; + struct cfg80211_ap_update ap_update_info; + 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; + u16 last_deauth_reason; +}; + +struct nxpwifi_tx_ba_stream_tbl { + struct list_head list; + struct rcu_head rcu; + 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; + struct list_head tmp_list; + struct rcu_head rcu; + 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; + spinlock_t bss_prio_lock; /* protects BSS priority */ + 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; +}; + +/*AP - side structure tracking associated STA info */ +struct nxpwifi_sta_node { + struct list_head list; + struct rcu_head rcu; + 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, u8 istat); + 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 operations */ + 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; + void *card; + struct nxpwifi_if_ops if_ops; + atomic_t bypass_tx_pending; + atomic_t rx_pending; + atomic_t tx_pending; + atomic_t cmd_pending; + atomic_t tx_hw_pending; + struct workqueue_struct *workqueue; + struct work_struct main_work; + struct workqueue_struct *rx_workqueue; + struct work_struct rx_work; + struct wiphy_work host_mlme_work; + bool rx_work_enabled; + bool rx_processing; + bool delay_main_work; + atomic_t rx_ba_teardown_pending; + atomic_t iface_changing; + struct nxpwifi_bss_prio_tbl bss_prio_tbl[NXPWIFI_MAX_BSS_NUM]; + u32 nxpwifi_processing; + 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; + /* interrupt lock */ + 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; + bool 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; + struct timer_list cmd_timer; + struct list_head cmd_free_q; + spinlock_t cmd_free_q_lock; /* protects cmd_free_q */ + struct list_head cmd_pending_q; + spinlock_t cmd_pending_q_lock; /* protects cmd_pending_q */ + struct list_head scan_pending_q; + spinlock_t scan_pending_q_lock; /* protects scan_pending_q */ + 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 hw_mpdu_density; + 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; /* protects 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 capability fields */ + 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 capability fields */ + 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; + struct completion *fw_done; /* FW init completion */ + 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; + void *devdump_data; /* device dump storage */ + int devdump_len; /* device dump length */ + bool ignore_btcoex_events; + struct vdll_dnld_ctrl vdll_ctrl; + u64 roc_cookie_counter; + u32 enable_net_mon; +}; + +struct mix_rate_info { + /* bit0: LGI: gi=3D0, SGI: gi=3D 1 */ + /* bit1-2: 20M: bw=3D0, 40M: bw=3D1, 80M: bw=3D2, 160M: bw=3D3 */ + /* bit3-4: LG: format=3D0, HT: format=3D1, VHT: format=3D2 */ + /* bit5: LDPC: 0-not support, 1-support */ + /* bit6-7:reserved */ + u8 rate_info; + /* MCS index */ + u8 mcs_index; + /* bitrate, in 500Kbps */ + u16 bitrate; + /* NSS */ + u8 nss_index; + /* DCM */ + u8 dcm; +}; + +struct rxpd_extra_info { + /* flags */ + u8 flags; + /* channel.flags */ + u16 channel_flags; + /* mcs.known */ + u8 mcs_known; + /* mcs.flags */ + u8 mcs_flags; + /* vht/he sig1 */ + u32 vht_he_sig1; + /* vht/he sig2 */ + u32 vht_he_sig2; + /* HE user idx */ + u32 user_idx; +}; + +struct radiotap_info { + /* Rate Info */ + struct mix_rate_info rate_info; + /* SNR */ + s8 snr; + /* Noise Floor */ + s8 nf; + /* band config */ + u8 band_config; + /* chan number */ + u8 chan_num; + u8 antenna; + /* extra rxpd info from FW */ + struct rxpd_extra_info extra_info; +}; + +/* channel_field.flags */ +#define CHANNEL_FLAGS_TURBO 0x0010 +#define CHANNEL_FLAGS_CCK 0x0020 +#define CHANNEL_FLAGS_OFDM 0x0040 +#define CHANNEL_FLAGS_2GHZ 0x0080 +#define CHANNEL_FLAGS_5GHZ 0x0100 +#define CHANNEL_FLAGS_ONLY_PASSIVSCAN_ALLOW 0x0200 +#define CHANNEL_FLAGS_DYNAMIC_CCK_OFDM 0x0400 +#define CHANNEL_FLAGS_GFSK 0x0800 +struct channel_field { + /* frequency */ + __le16 frequency; + /* flags */ + __le16 flags; +} __packed; + +/* mcs_field.known */ +#define MCS_KNOWN_BANDWIDTH 0x01 +#define MCS_KNOWN_MCS_INDEX_KNOWN 0x02 +#define MCS_KNOWN_GUARD_INTERVAL 0x04 +#define MCS_KNOWN_HT_FORMAT 0x08 +#define MCS_KNOWN_FEC_TYPE 0x10 +#define MCS_KNOWN_STBC_KNOWN 0x20 +#define MCS_KNOWN_NESS_KNOWN 0x40 +#define MCS_KNOWN_NESS_DATA 0x80 +/* bandwidth */ +#define RX_BW_20 0 +#define RX_BW_40 1 +#define RX_BW_20L 2 +#define RX_BW_20U 3 +#define RX_BW_80 4 +#define RX_HE_BW_20 0 +#define RX_HE_BW_40 1 +#define RX_HE_BW_80 2 +#define RX_HE_BW_160 3 + +/* + * mcs_field.flags + * The flags field is any combination of the following: + * 0x03 bandwidth - 0: 20, 1: 40, 2: 20L, 3: 20U + * 0x04 guard interval - 0: long GI, 1: short GI + * 0x08 HT format - 0: mixed, 1: greenfield + * 0x10 FEC type - 0: BCC, 1: LDPC + * 0x60 Number of STBC streams + * 0x80 Ness - bit 0 (LSB) of Number of extension spatial streams + */ +struct mcs_field { + /* known */ + u8 known; + /* flags */ + u8 flags; + /* mcs */ + u8 mcs; +} __packed; + +/* vht_field.known */ +#define VHT_KNOWN_STBC 0x0001 +#define VHT_KNOWN_TXOP_PS_NA 0x0002 +#define VHT_KNOWN_GI 0x0004 +#define VHT_KNOWN_SGI_NSYM_DIS 0x0008 +#define VHT_KNOWN_LDPC_EXTRA_OFDM_SYM 0x0010 +#define VHT_KNOWN_BEAMFORMED 0x0020 +#define VHT_KNOWN_BANDWIDTH 0x0040 +#define VHT_KNOWN_GROUP_ID 0x0080 +#define VHT_KNOWN_PARTIAL_AID 0x0100 + +/* vht_field.flags */ +#define VHT_FLAG_STBC 0x01 +#define VHT_FLAG_TXOP_PS_NA 0x02 +#define VHT_FLAG_SGI 0x04 +#define VHT_FLAG_SGI_NSYM_M10_9 0x08 +#define VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10 +#define VHT_FLAG_BEAMFORMED 0x20 + +/* vht_field.coding */ +#define VHT_CODING_LDPC_USER0 0x01 +#define VHT_CODING_LDPC_USER1 0x02 +#define VHT_CODING_LDPC_USER2 0x04 +#define VHT_CODING_LDPC_USER3 0x08 + +/* vht_field */ +struct vht_field { + /* pad: for vht field require 2 bytes alignment */ + u8 pad; + /* known */ + u16 known; + /* flags */ + u8 flags; + /* bandwidth */ + u8 bandwidth; + /* mcs_nss for up to 4 users */ + u8 mcs_nss[4]; + /* coding for up to 4 users */ + u8 coding; + /* group_id */ + u8 group_id; + /* partial_aid */ + u16 partial_aid; +} __packed; + +#define HE_BSS_COLOR_KNOWN 0x0002 +#define HE_BEAM_CHANGE_KNOWN 0x0004 +#define HE_UL_DL_KNOWN 0x0008 +#define HE_MCS_KNOWN 0x0020 +#define HE_DCM_KNOWN 0x0040 +#define HE_CODING_KNOWN 0x0080 +#define HE_BW_KNOWN 0x4000 +#define HE_DATA_GI_KNOWN 0x0002 +#define HE_MU_DATA 0x0002 +#define HE_CODING_LDPC_USER0 0x2000 +/* he_field - COCO */ +struct he_field { + u8 pad; + __le16 data1; + __le16 data2; + __le16 data3; + __le16 data4; + __le16 data5; + __le16 data6; +} __packed; + +extern u8 ru_signal[16][9]; +extern u8 ru_signal_106[14][9]; +extern u8 ru_signal_52[9]; + +#define NXPWIFI_20_BIT_CH1P 0xC0000000 +#define NXPWIFI_20_BIT_CH1S 0x0000003F +#define NXPWIFI_20_BIT_CH2 0x007F8000 +#define NXPWIFI_80_CENTER_RU 0x00004000 +#define NXPWIFI_160_CENTER_RU 0x40000000 +#define NXPWIFI_20_BIT_CH3 0x00003FC0 +#define NXPWIFI_20_BIT_CH4 0x7F800000 +#define NXPWIFI_BIT_160_CH3 0x003FC000 +#define NXPWIFI_BIT_160_CH4 0x03FC0000 + +#define NXPWIFI_DECODE_RU_SIGNALING_CH1(out, x, y) \ +{ \ + out =3D ((((x) << 8) & NXPWIFI_20_BIT_CH1P) >> 30) | \ + (((y) & NXPWIFI_20_BIT_CH1S) << 2); \ +} + +#define NXPWIFI_DECODE_RU_SIGNALING_CH3(out, y) \ +{ \ + out =3D ((y) & NXPWIFI_20_BIT_CH3) >> 6; \ +} + +#define NXPWIFI_DECODE_RU_SIGNALING_CH2(out, y) \ +{ \ + out =3D ((y) & NXPWIFI_20_BIT_CH2) >> 15; \ +} + +#define NXPWIFI_DECODE_RU_SIGNALING_CH4(out, y) \ +{ \ + out =3D ((y) & NXPWIFI_20_BIT_CH4) >> 23; \ +} + +#define NXPWIFI_DECODING_160_RU_CH3(out, y) \ +{ \ + out =3D ((y) & NXPWIFI_BIT_160_CH3) >> 5; \ +} + +#define NXPWIFI_DECODING_160_RU_CH4(out, y) \ +{ \ + out =3D ((y) & NXPWIFI_BIT_160_CH4) >> 22; \ +} + +#define RU_SIGNAL_52_TONE 112 +#define TONE_MAX_USERS_52 4 +#define TONE_MAX_USERS_242 3 +#define RU_SIGNAL_26_TONE 0 +#define TONE_MAX_USERS_26 8 +#define RU_26_TONE_LIMIT 15 +#define RU_TONE_LIMIT 96 +#define RU_80_106_TONE 128 +#define RU_40_242_TONE 192 +#define RU_80_484_TONE 200 +#define RU_160_996_TONE 208 +#define RU_TONE_26 4 +#define RU_TONE_52 5 +#define RU_TONE_106 6 +#define RU_TONE_242 7 +#define RU_TONE_484 8 +#define RU_TONE_996 9 + +static inline void nxpwifi_decode_ru_tone(u32 *x, u32 *y, u32 *tone) +{ + u32 x1, y1, t1; + + x1 =3D *x; + y1 =3D *y; + t1 =3D *tone; + + if (x1 =3D=3D RU_SIGNAL_52_TONE) { + if ((y1 + 1) <=3D TONE_MAX_USERS_52) + t1 =3D RU_TONE_52; + else + y1 =3D (y1 + 1) - TONE_MAX_USERS_52; + } else if (x1 =3D=3D RU_SIGNAL_26_TONE) { + if ((y1 + 1) <=3D TONE_MAX_USERS_26) + t1 =3D RU_TONE_26; + else + y1 =3D (y1 + 1) - TONE_MAX_USERS_26; + } else if (x1 <=3D RU_TONE_LIMIT) { + u32 ru_arr_idx; + + ru_arr_idx =3D x1 > RU_26_TONE_LIMIT ? 1 : 0; + if ((y1 + 1) > (ru_arr_idx ? + ru_signal_106[x1 / 8][8] : ru_signal[x1][8])) { + y1 =3D (y1 + 1) - (ru_arr_idx ? + ru_signal_106[x1 / 8][8] : ru_signal[x1][8]); + } else { + u32 ind =3D 0; + u32 idx =3D 0; + u32 tn; + + while (ind < 8) { + tn =3D ru_arr_idx ? + ru_signal_106[x1 / 8][7 - ind] : ru_signal[x1][7 - ind]; + ind++; + if (tn =3D=3D 0x1 || tn =3D=3D 0x0 || tn =3D=3D 0x2) { + if (idx =3D=3D y1) { + t1 =3D tn ? (tn =3D=3D 2) ? + RU_TONE_106 : RU_TONE_52 : RU_TONE_26; + break; + } + idx++; + } + } + } + } else if (x1 =3D=3D RU_80_106_TONE) { + if ((y1 + 1) > TONE_MAX_USERS_242) + y1 =3D (y1 + 1) - TONE_MAX_USERS_242; + else + t1 =3D (y1 =3D=3D 2) ? RU_TONE_106 : (y1 =3D=3D 1) ? 0 : RU_TONE_106; + } else if (x1 =3D=3D RU_40_242_TONE) { + if (!y1) + t1 =3D RU_TONE_242; + else + y1--; + } else if (x1 =3D=3D RU_80_484_TONE) { + if (!y1) + t1 =3D RU_TONE_484; + else + y1--; + } else if (x1 =3D=3D RU_160_996_TONE) { + if (!y1) + t1 =3D RU_TONE_996; + else + y1--; + } + + *y =3D y1; + *tone =3D t1; +} + +/* radiotap_body.flags */ +#define RADIOTAP_FLAGS_DURING_CFG 0x01 +#define RADIOTAP_FLAGS_SHORT_PREAMBLE 0x02 +#define RADIOTAP_FLAGS_WEP_ENCRYPTION 0x04 +#define RADIOTAP_FLAGS_WITH_FRAGMENT 0x08 +#define RADIOTAP_FLAGS_INCLUDE_FCS 0x10 +#define RADIOTAP_FLAGS_PAD_BTW_HEADER_PAYLOAD 0x20 +#define RADIOTAP_FLAGS_FAILED_FCS_CHECK 0x40 +#define RADIOTAP_FLAGS_USE_SGI_HT 0x80 +struct radiotap_body { + /* timestamp */ + __le64 timestamp; + /* flags */ + u8 flags; + /* + * rate for LG pkt, RATE flag will be present, it shows datarate in + * 500Kbps. For HT/VHT pkt, RATE flag will not be present, it is not + * used. + */ + u8 rate; + /* channel */ + struct channel_field channel; + /* antenna_signal */ + s8 antenna_signal; + /* antenna_noise */ + s8 antenna_noise; + /* antenna */ + u8 antenna; + /* union for HT/VHT pkt */ + union { + /* mcs field */ + struct mcs_field mcs; + /* vht field */ + struct vht_field vht; + /* he field */ + struct he_field he; + } u; +} __packed; + +struct radiotap_header { + struct ieee80211_radiotap_header hdr; + struct radiotap_body body; +} __packed; + +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_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_recv_packet_to_monif(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 *pr= iv, + 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_mas= k, + 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); + +/* check if RA-based queuing */ +static inline u8 +nxpwifi_queuing_ra_based(struct nxpwifi_private *priv) +{ + /* In STA mode DA=3D=3DRA; subject to future revision */ + if (priv->bss_mode =3D=3D NL80211_IFTYPE_STATION && + (GET_BSS_ROLE(priv) =3D=3D NXPWIFI_BSS_ROLE_STA)) + return false; + + return true; +} + +/* copy rates from src to dest */ +static inline u32 +nxpwifi_copy_rates(u8 *dest, u32 pos, u8 *src, int len) +{ + int i; + + for (i =3D 0; i < len && src[i]; i++, pos++) { + if (pos >=3D NXPWIFI_SUPPORTED_RATES) + break; + dest[pos] =3D src[i]; + } + + return pos; +} + +/* return priv matching the given BSS type and 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 =3D 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]->bss_mode =3D=3D + NL80211_IFTYPE_UNSPECIFIED) + continue; + if (adapter->priv[i]->bss_num =3D=3D bss_num && + adapter->priv[i]->bss_type =3D=3D bss_type) + break; + } + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* return first priv matching BSS role */ +static inline struct nxpwifi_private * +nxpwifi_get_priv(struct nxpwifi_adapter *adapter, + enum nxpwifi_bss_role bss_role) +{ + int i; + + for (i =3D 0; i < adapter->priv_num; i++) { + if (bss_role =3D=3D NXPWIFI_BSS_ROLE_ANY || + GET_BSS_ROLE(adapter->priv[i]) =3D=3D bss_role) + break; + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* find unused BSS number for new interface */ +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 =3D 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->bss_type =3D=3D bss_type && + !(adapter->priv[i]->bss_mode =3D=3D + NL80211_IFTYPE_UNSPECIFIED)) { + index[adapter->priv[i]->bss_num] =3D 1; + } + for (j =3D 0; j < NXPWIFI_MAX_BSS_NUM; j++) + if (!index[j]) + return j; + return -ENOENT; +} + +/* return unused private entry for requested bss type */ +static inline struct nxpwifi_private * +nxpwifi_get_unused_priv_by_bss_type(struct nxpwifi_adapter *adapter, + u8 bss_type) +{ + u8 i; + + for (i =3D 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->bss_mode =3D=3D + NL80211_IFTYPE_UNSPECIFIED) { + adapter->priv[i]->bss_num =3D + nxpwifi_get_unused_bss_num(adapter, bss_type); + break; + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* return private structure attached to netdev */ +static inline struct nxpwifi_private * +nxpwifi_netdev_get_priv(struct net_device *dev) +{ + return (struct nxpwifi_private *)(*(unsigned long *)netdev_priv(dev)); +} + +/* return true if skb contains a management frame */ +static inline bool nxpwifi_is_skb_mgmt_frame(struct sk_buff *skb) +{ + return (get_unaligned_le32(skb->data) =3D=3D PKT_TYPE_MGMT); +} + +/* channel closed by CSA */ +static inline u8 +nxpwifi_11h_get_csa_closed_channel(struct nxpwifi_private *priv) +{ + if (!priv->csa_chan) + return 0; + + /* clear CSA if DFS switch timeout expired */ + if (time_after(jiffies, priv->csa_expire_time)) { + priv->csa_chan =3D 0; + priv->csa_expire_time =3D 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 =3D 0; i < priv->adapter->priv_num; i++) { + priv_tmp =3D priv->adapter->priv[i]; + if ((GET_BSS_ROLE(priv_tmp) =3D=3D NXPWIFI_BSS_ROLE_UAP && + priv_tmp->bss_started) || + (GET_BSS_ROLE(priv_tmp) =3D=3D NXPWIFI_BSS_ROLE_STA && + priv_tmp->media_connected)) + return 1; + } + + return 0; +} + +/* disable wake IRQ */ +static inline void nxpwifi_disable_wake(struct nxpwifi_adapter *adapter) +{ + if (adapter->irq_wakeup >=3D 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 wake IRQ */ +static inline void nxpwifi_enable_wake(struct nxpwifi_adapter *adapter) +{ + /* Enable platform specific wakeup interrupt */ + if (adapter->irq_wakeup >=3D 0) { + adapter->wake_by_wifi =3D 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 *b= ss, + 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 byte= s, + 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_inde= x); + +int nxpwifi_get_tx_rate_cfg(struct nxpwifi_private *priv, int *tx_rate_ind= ex); + +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 *sk= b); + +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_typ= e); +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 *wde= v); + +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); +struct nxpwifi_sta_node * +nxpwifi_get_sta_entry_rcu(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_reset_conn_state_work(struct wiphy *wiphy, struct wiphy_work = *work); +void nxpwifi_dfs_cac_work(struct wiphy *wiphy, struct wiphy_work *work); +void nxpwifi_dfs_chan_sw_work(struct wiphy *wiphy, struct wiphy_work *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 sn= r, + 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_ */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010011.outbound.protection.outlook.com [52.101.69.11]) (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 8630642EED3; Wed, 4 Feb 2026 18:06:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.11 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228360; cv=fail; b=ONzvbg4rwWgf5fYGRCreuMBTA+z7Kqj12fu/AUVcp65sU/m6wAaxsc0uTx9Nl2MCFqtp4DfueCKlt1z3JEWVXNIwY7jKU4+Scqhfc3bn2c8pTttz5yOzGD8RFYchaQ8nU98gVafD5GQsYtNquBD0hy8SMUPyf0AtSNOrFwPnbq0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228360; c=relaxed/simple; bh=rdqh430lF8y1soT4TLvZJ0WulF+xqVEZP6ROF5gHlfQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=mbJ3zWRxhzh5sErC3Omsk/e2aY5kvwpoNeQHFhEdWFd9Ye3gH8U8z3UfRATrg3olsqRbUY3N0oIffeiODdQ4W518wFlqNl0PosZcPWK6zzln7XjYVexagDgoMUbkUsZbFw4nvQ3ca5HVcAyGmoej3ClzR8IKppNmZne3pWgyO2U= 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=iwvKZ5ls; arc=fail smtp.client-ip=52.101.69.11 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="iwvKZ5ls" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=BaZcCI7FeSoVy563ita3c/N6SrOr7IHS6cXpTXRAFHYlxg87p3Nu3j6eevIJa3adrXkbtqp2q63CmgrFF14/0APl5OhU6zqtGUisildTZKYDEnSKqrUjakDkbgX+9kHxlK9mHE8hlx52MQLPcRKNfwTwF0h3RhWWC14QP8G+b9GeH2qr083f3HeFKOoIPOyKD8+mEBwA6Qf1xoiuFa1P8vsVgMVK6c9Aoz//Tqrs30E97QfpZ8Ze6+i9DBYDdMfAN2ePiLMJbl9eZbXSj7WiTjGwrtEjQPTr4qq8s1nS13viS/5fDJKLfj8ABQDHKaid+RzKv7UMoyWCXjzgGBnOOw== 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=l8HkqCWBj0gBUcM2KWyduNFdjlFpfnXeGT6gx6CiNt0=; b=vM9Zy0J01RAblh7d2mqJWbbmhyNnsnJ97d5fNSNGhcN22qcWAqiUpXGVrV8zz5qQlZQrSCgcnyTcUMJkdo+XHEq87K2M8socTEkR7oeoTEMwCyLLTqoyH7BUe1bmLo2Fb61ItFuLcsO2bcx9nsBZbBw6bfsclTIhtP+SpJ4EE47oepH4MMO7zbX9cJEOCWpTcIP+KTNX/ecR5Oya5fndl/SbFw8dWlvvgYd/d+OjUk0buv8wdW5PmMmC5dYeJxpo1xSzPcnxzfN0J12B/22PZlgL0d9SFIgLdwNdwDILOEUUD4tlQ8wRrPqTZ5eVdDPlS1XBJeM5OCsz6y5JfinK6g== 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=l8HkqCWBj0gBUcM2KWyduNFdjlFpfnXeGT6gx6CiNt0=; b=iwvKZ5ls1z9RwAsd3/MDzakTIyz5UY44hdZU7CU4zM7gSyHy05bPcB7uITfwowiDfsYENpYs5JTM07b+mfpexaN+ZykfjDmZhTiggP3TZ0BWkkfzSi7Q9WPCNwW3QmDll2tuGqk4Fs9ZxR+wzo+5yMrTzeJoeiJFnyJNElwMVbK6N4GyAOojsuwd8q1klxlh6QU4DBoUDe4vC5YCZcJ4ucjiK8dV84VqQPh++culMJ0D2eYQmc31AQgIrP5/YcZtIotxu6U3sxo/ZP1G8FXKhkfkKMQpgvwXPm/6pbnrWdGbEfD6a0q0zmZSdEFd54C5z3N9+mU+SymaiF0P8NMvvQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:05:58 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:05:58 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 19/21] wifi: nxpwifi: add initial SDIO bus driver support Date: Thu, 5 Feb 2026 02:03:56 +0800 Message-Id: <20260204180358.632281-20-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 39ad9b6d-bc09-485f-0883-08de64180c80 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?AS1mtxNODDh4l3EFhC67BIgO/XL9NpthfnIgie6SVmUWIY61ItCIBA3yYWqY?= =?us-ascii?Q?16XGVr03y2kj4VAdfKy/+9yErws6H+JRZ3LdXUqKCFhxRBfhyg8h/749wl27?= =?us-ascii?Q?bW719+z73qqMpiEHTtyQM2lFlL5Hi4iP/3UyuD0MFNx5ZzOn4ua0oJo8sJN+?= =?us-ascii?Q?Z/n+Rp4LNt3LE1NKRsIUzYSQ1fzzfaZi57MptEiSIesptz4ayVfXC4WXmyyW?= =?us-ascii?Q?eeEwuB/ghW3ZYjiAN5p8OEya0UTwmd9s+iV4g1+nIX8zd6n0e18ohAn7MYyK?= =?us-ascii?Q?rGbJozk/e+H+yiknNqClgAG6o830/Rw5W1bmeCTBybKMlkGCUM+BEpLjJIwe?= =?us-ascii?Q?Gnlp5KzOAs1BYawFxm2CPkxThjXjn806W+BXk/OP3oUtMqJyEQ21lOkb/yCd?= =?us-ascii?Q?ziNrVI2VevEdZhoVRhSK69maRLDJ6n2VGahBzK4qSdOqJjSg0iFTvdieAbM/?= =?us-ascii?Q?drccgVeI5EF8+elWXKokSTQY5rZPeIPhxi39mpN8fxJSDc1iak0Vf0vsqJXh?= =?us-ascii?Q?EvGkdIzV+td6ysvuoNFom55mTNRLmoHyG0cebgVI4MVeLtJAdMXEQ+noiuXh?= =?us-ascii?Q?k7+MuhsQcPpweM3648rguqKExaZo0zq69gDQ2/YlBC37mxy8NEUFfCL4f4Rl?= =?us-ascii?Q?vmhRbBMTgHe7CWJIkLHnjNA+BFw/DVujYb3o5rkShUYm0OA7rvEHQpOOXY3q?= =?us-ascii?Q?seQs+z8rCNxGJluIMU5Wzq4dxnJ90N9HCfbgSLdz+NUbW27VMndy0XyN/wld?= =?us-ascii?Q?Hflu0O7mWSXUdMRlvv3F4/h8Zn/2SuoXdTolLeTXnuhbErjZbAZ1f5D6zH4f?= =?us-ascii?Q?OrJbWXyz6x8xV5uhAZNQQYbj9bM7A8eHLqMm58vLKgQ0kjHeBYCSZqrYkrz0?= =?us-ascii?Q?Lc6IznWMS1tMlEkMdbca2vJpVdEuC80dbHT+4GLLRlXVYyf69Vasbz+MYPDR?= =?us-ascii?Q?MWpGrjlpzgcIXgdRWO+X3I29o9EiGJ6XHtsS/AUtGIrWQgXnAXpAVneZd3H4?= =?us-ascii?Q?Khd9IvpVIt6xvr4ThXetlFUMHJXKUHN+WNy94WZb5QYAn8SKdqq2qz7ctvIV?= =?us-ascii?Q?d/HQ/UAOPkIOCdUZa+JuyxvhyMc2EJ9OIfq59lZikM3dn/uIVHafjytibILk?= =?us-ascii?Q?xTgH6eHbeuW+/AhQF9dR6Pi0xihzeJo4+k/hOCOLwHoXl7CAAocpTcnbJTdm?= =?us-ascii?Q?jEJzxwGyyTqArSbUD2uBBVSmnJfT0DIUkuPui/Ow0WzQAZu4xOsgvSySztDq?= =?us-ascii?Q?A2OAWEonk5KZaL8pqaCgX4VX0DnBrKRGoQJYn1sny2dVAJwVxuqmo3Nv9agA?= =?us-ascii?Q?l0vYGlMNfTT3LBadPmoHFaxXtFzkvIFzLBGBsVvZlvQcSOkNFpXkrJWUNPwt?= =?us-ascii?Q?v3qqI4takj4+zFsGoYyJJ3KXAG8ROQ3GkIe9a/XHyxOD/OVNhgu9MaqxDj7z?= =?us-ascii?Q?EZ208tCT7mXLCi3zzHyNFJ9B3/sw2KJiReLKnNFlx6xnS3fNq9vD8RNmWH79?= =?us-ascii?Q?IbUhRPhWX9YXiAvA8cnKDPmkjfKJMf7JimXDskvifwovwBIyM9ZlbOSQyOsz?= =?us-ascii?Q?4RlQbpUBopxh2Me4qzIDjmAe+kQi3jQwbsAfPM4Dp+SM/6V2QZ5dcUXtBLxh?= =?us-ascii?Q?lr2lv47GF8TiqZiHzAXPtS8=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?09BXC17s+d0TolfOZqrgFdkKbHq7hLsi9UFa+4fNudVZ4SoEDtF9KK/OEoYX?= =?us-ascii?Q?B7eD5TZs0tXdrm/OlQOXD8XecG6bsK/y6PoqD4I2oBE+NIadncn/YA1A9uzI?= =?us-ascii?Q?xVGpT5biX/9L/e4rzPoN5sNOFiDcvEzMfpeGl5p5z302Z5OIYBWqUoFTCf6o?= =?us-ascii?Q?r5dTxXUeFwYQsUyHJ6MML2Wr9x0mdA0b/U+jrKfyQJw9hcEE7Wyqxcq62Oa+?= =?us-ascii?Q?LDSYYZjT8hFNZ6jsr24YxTHhTAA9tC7iQZbXZq0xceX0bITzRLdJWLylxxBl?= =?us-ascii?Q?D4Yucu/kG3FFP/S6fmh/AQUMElAsAEqHMlkLjcYnHN/dhkTmCc7r+cXO2PDu?= =?us-ascii?Q?/ihpCGj50Kyf5l0wU7QG9Ex7zDjgvbVF62oIfPJPu6Aji9Xet+HM7aincGA7?= =?us-ascii?Q?F6jU2NuMWqoWUwWWQx/i52Ff11s4lWZU/0VHi/qC3hPkAlNmzf71Zv4P9UOH?= =?us-ascii?Q?XAXuA6SqwYUD1jM/sMGo9xng+wjmPpR3wS/b2xPQNmlf+4U12iNBGHthJkVR?= =?us-ascii?Q?gzhvviIeYzwaKEDs89kDUBysfwM2Oix21KhYKKNEv2UdcybQ7keiiipKMbfy?= =?us-ascii?Q?33sjQh1P/Ra49fO0PYXtGOlRavf9SP510Yy21DLMgQ86edh1anB5okNIrcJR?= =?us-ascii?Q?B0WQFo9exREUKNAfDrNdQLvAONdcmxrqLNRrKpl65WdNrebqCI44HQxGrMeR?= =?us-ascii?Q?H44zlMaulDJECDbA1087NaFkeViqSK5HZJ4NFZfuxfpyZpNqkUUKNIjKNyjs?= =?us-ascii?Q?3yLZib24caJntbnyTAPK2jiVW5dSzGOmpArkG83YSjANXG0iB4sB72+n8fNT?= =?us-ascii?Q?u4yzwI1WRM85IBWAUqCyT+YB1BZYwei2YTFpABAWYueysyPvn63QlMTxT1l4?= =?us-ascii?Q?Df12oFnpSbHuOEHcfrBem0RDlMG3sYgKk4CQfZVxhThdH+9FTBgQN3IpqgkV?= =?us-ascii?Q?JskdTqxb+Nkw2x6+3zp+rciNQ9O3OgQvAY3BGaqfqLxkpvH1M8y+aSOXzY2q?= =?us-ascii?Q?dXk/ok9qP2J9HQPHlpkxjb4NRouhcT5xzEBewpICFDqKNb7SUSm7tvadtWDl?= =?us-ascii?Q?2wk1wPOYjdETN+xRus9JOI8v0bkHmSoejT8Vl9Z6psALEI+sOzI4/8taAQ24?= =?us-ascii?Q?e2dvtnK1w6ytzrsqsACmSy3aQb2olFgMXoOO1b6eZ9ggihG29ZN/22nh6aa6?= =?us-ascii?Q?6VL6IAPaSdzRNX4JRfI3c5jtjYWgbVDqRI6kho5V74fwEhuP86WEdBFysxjm?= =?us-ascii?Q?ZHCq5Yla7PcAbQDLKtaXCUK/q/8OpRGg2LNm0/GHIRZkINOz7tF8VTxH0lTL?= =?us-ascii?Q?/sc+YiMEsNSfexhOCEgAgO3PeYKCCaigc/N2zdD7vEgI9m6eWlgyiiwUpJkc?= =?us-ascii?Q?GlHAIaDSqzOjBunBKOOPC6uEZS0j0qExNoLuqZ/MAArm6rRYiihK+gSkxf/6?= =?us-ascii?Q?0U3sCmkD2qHPEm+IOxlIGfhqhbopk/dhZoi8mStAt8kKZf7nDq0/voGl7BMk?= =?us-ascii?Q?m7tHmMs4ldAX/zrxe6M+jTzx/Tue7akzvFnveP+a7YgqGTfzqt/emHbyEBwJ?= =?us-ascii?Q?BGSc1rSoOj7pAAcwxERKzG608TS2wCsTooK5Wtd7wdIh0n7PT5cQ1oDXePyx?= =?us-ascii?Q?mR5Qv5uRqhSb04DTVgHnDaWW7QINlmq1HPgHXlsKb4TeGgE8TA6qOWAydotB?= =?us-ascii?Q?LgTwrtA6n/9xXg7k+yeIKs6aljVGQXA6iPaInf5Yn9KTlaPNBx8YzhvtMQuQ?= =?us-ascii?Q?hVYBnNAu2g=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 39ad9b6d-bc09-485f-0883-08de64180c80 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:05:58.6607 (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: XeLCyAj2ftfBqg6z8AYUoR07LtcrGiY8D/qlAoHkCNgUXtUvPstu+76hey1b/vzNB9DlDTC7wScwX0p4MRcbeg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" Introduce SDIO bus support for NXP WiFi chips (e.g., IW61x series). This driver integrates with the core layer to enable both STA and AP modes over the SDIO interface. Key features: - Implements SDIO probe, suspend/resume, and interrupt handling - Supports firmware download and wakeup/sleep mechanisms - Adds multi-port aggregation (MP-A) for TX/RX performance - Provides firmware dump and register dump utilities for debugging This is the SDIO transport layer for nxpwifi, laying the foundation for full feature parity with PCIe. Signed-off-by: Jeff Chen --- drivers/net/wireless/nxp/nxpwifi/sdio.c | 2326 +++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/sdio.h | 340 ++++ 2 files changed, 2666 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..d5ac82b9033a --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/sdio.c @@ -0,0 +1,2326 @@ +// 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 +#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" + +/* Process deferred SDIO work items. */ +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 =3D { + .start_rd_port =3D 0, + .start_wr_port =3D 0, + .base_0_reg =3D 0xF8, + .base_1_reg =3D 0xF9, + .poll_reg =3D 0x5C, + .host_int_enable =3D UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_rsr_reg =3D 0x4, + .host_int_status_reg =3D 0x0C, + .host_int_mask_reg =3D 0x08, + .host_strap_reg =3D 0xF4, + .host_strap_mask =3D 0x01, + .host_strap_value =3D 0x00, + .status_reg_0 =3D 0xE8, + .status_reg_1 =3D 0xE9, + .sdio_int_mask =3D 0xff, + .data_port_mask =3D 0xffffffff, + .io_port_0_reg =3D 0xE4, + .io_port_1_reg =3D 0xE5, + .io_port_2_reg =3D 0xE6, + .max_mp_regs =3D 196, + .rd_bitmap_l =3D 0x10, + .rd_bitmap_u =3D 0x11, + .rd_bitmap_1l =3D 0x12, + .rd_bitmap_1u =3D 0x13, + .wr_bitmap_l =3D 0x14, + .wr_bitmap_u =3D 0x15, + .wr_bitmap_1l =3D 0x16, + .wr_bitmap_1u =3D 0x17, + .rd_len_p0_l =3D 0x18, + .rd_len_p0_u =3D 0x19, + .card_misc_cfg_reg =3D 0xd8, + .card_cfg_2_1_reg =3D 0xd9, + .cmd_rd_len_0 =3D 0xc0, + .cmd_rd_len_1 =3D 0xc1, + .cmd_rd_len_2 =3D 0xc2, + .cmd_rd_len_3 =3D 0xc3, + .cmd_cfg_0 =3D 0xc4, + .cmd_cfg_1 =3D 0xc5, + .cmd_cfg_2 =3D 0xc6, + .cmd_cfg_3 =3D 0xc7, + .fw_dump_host_ready =3D 0xcc, + .fw_dump_ctrl =3D 0xf9, + .fw_dump_start =3D 0xf1, + .fw_dump_end =3D 0xf8, + .func1_dump_reg_start =3D 0x10, + .func1_dump_reg_end =3D 0x17, + .func1_scratch_reg =3D 0xE8, + .func1_spec_reg_num =3D 13, + .func1_spec_reg_table =3D {0x08, 0x58, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x64, 0x65, 0x66, + 0x68, 0x69, 0x6a}, +}; + +static const struct nxpwifi_sdio_device nxpwifi_sdio_iw61x =3D { + .firmware =3D IW61X_SDIO_FW_NAME, + .reg =3D &nxpwifi_reg_iw61x, + .max_ports =3D 32, + .mp_agg_pkt_limit =3D 16, + .tx_buf_size =3D NXPWIFI_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size =3D NXPWIFI_MP_AGGR_BSIZE_MAX, + .mp_rx_agg_buf_size =3D NXPWIFI_MP_AGGR_BSIZE_MAX, + .can_dump_fw =3D true, + .fw_dump_enh =3D true, + .can_ext_scan =3D true, +}; + +static struct memory_type_mapping generic_mem_type_map[] =3D { + {"DUMP", NULL, 0, 0xDD}, +}; + +static struct memory_type_mapping mem_type_mapping_tbl[] =3D { + {"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}, +}; + +/* Bind the SDIO function and start device registration. */ +static int +nxpwifi_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret; + struct sdio_mmc_card *card =3D NULL; + + card =3D devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + init_completion(&card->fw_done); + + card->func =3D func; + + if (id->driver_data) { + struct nxpwifi_sdio_device *data =3D (void *)id->driver_data; + + card->firmware =3D data->firmware; + card->firmware_sdiouart =3D data->firmware_sdiouart; + card->reg =3D data->reg; + card->max_ports =3D data->max_ports; + card->mp_agg_pkt_limit =3D data->mp_agg_pkt_limit; + card->tx_buf_size =3D data->tx_buf_size; + card->mp_tx_agg_buf_size =3D data->mp_tx_agg_buf_size; + card->mp_rx_agg_buf_size =3D data->mp_rx_agg_buf_size; + card->can_dump_fw =3D data->can_dump_fw; + card->fw_dump_enh =3D data->fw_dump_enh; + card->can_ext_scan =3D data->can_ext_scan; + INIT_WORK(&card->work, nxpwifi_sdio_work); + } + + sdio_claim_host(func); + ret =3D sdio_enable_func(func); + sdio_release_host(func); + + if (ret) { + dev_err(&func->dev, "failed to enable function\n"); + return ret; + } + + ret =3D 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; +} + +/* Resume the SDIO function and cancel host sleep. */ +static int nxpwifi_sdio_resume(struct device *dev) +{ + struct sdio_func *func =3D dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct nxpwifi_adapter *adapter; + + card =3D sdio_get_drvdata(func); + + if (unlikely(!card || !card->adapter)) { + dev_dbg(dev, "resume: %s not ready\n", !card ? "card" : "adapter"); + return -ENODEV; + } + + adapter =3D card->adapter; + + if (!test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) + 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; +} + +static int +nxpwifi_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) +{ + int ret; + + sdio_writeb(func, data, reg, &ret); + return ret; +} + +static int +nxpwifi_write_reg(struct nxpwifi_adapter *adapter, u32 reg, u8 data) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret; + + sdio_claim_host(card->func); + ret =3D nxpwifi_write_reg_locked(card->func, reg, data); + sdio_release_host(card->func); + + return ret; +} + +static int +nxpwifi_read_reg(struct nxpwifi_adapter *adapter, u32 reg, u8 *data) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret; + u8 val; + + sdio_claim_host(card->func); + val =3D sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + + *data =3D val; + + return ret; +} + +static int +nxpwifi_write_data_sync(struct nxpwifi_adapter *adapter, + u8 *buffer, u32 pkt_len, u32 port) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret; + u8 blk_mode =3D + (port & NXPWIFI_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + u32 blk_size =3D (blk_mode =3D=3D BLOCK_MODE) ? NXPWIFI_SDIO_BLOCK_SIZE := 1; + u32 blk_cnt =3D + (blk_mode =3D=3D + BLOCK_MODE) ? (pkt_len / + NXPWIFI_SDIO_BLOCK_SIZE) : pkt_len; + u32 ioport =3D (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 =3D sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); + + sdio_release_host(card->func); + + return ret; +} + +static int nxpwifi_read_data_sync(struct nxpwifi_adapter *adapter, u8 *buf= fer, + u32 len, u32 port, u8 claim) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret; + u8 blk_mode =3D (port & NXPWIFI_SDIO_BYTE_MODE_MASK) ? BYTE_MODE + : BLOCK_MODE; + u32 blk_size =3D (blk_mode =3D=3D BLOCK_MODE) ? NXPWIFI_SDIO_BLOCK_SIZE := 1; + u32 blk_cnt =3D (blk_mode =3D=3D BLOCK_MODE) ? (len / NXPWIFI_SDIO_BLOCK_= SIZE) + : len; + u32 ioport =3D (port & NXPWIFI_SDIO_IO_PORT_MASK); + + if (claim) + sdio_claim_host(card->func); + + ret =3D sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); + + if (claim) + sdio_release_host(card->func); + + return ret; +} + +static int +nxpwifi_sdio_read_fw_status(struct nxpwifi_adapter *adapter, u16 *dat) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D card->reg; + u8 fws0, fws1; + int ret; + + ret =3D nxpwifi_read_reg(adapter, reg->status_reg_0, &fws0); + if (ret) + return ret; + + ret =3D nxpwifi_read_reg(adapter, reg->status_reg_1, &fws1); + if (ret) + return ret; + + *dat =3D (u16)((fws1 << 8) | fws0); + return ret; +} + +static int nxpwifi_check_fw_status(struct nxpwifi_adapter *adapter, + u32 poll_num) +{ + int ret =3D 0; + u16 firmware_stat =3D 0; + + unsigned int timeout_us =3D poll_num * 100000; /* 100 ms * poll_num */ + /* + * Poll every 100 ms until firmware reports FIRMWARE_READY_SDIO. + * On timeout, read_poll_timeout() returns -ETIMEDOUT. + */ + ret =3D read_poll_timeout(nxpwifi_sdio_read_fw_status, ret, + (!ret && firmware_stat =3D=3D FIRMWARE_READY_SDIO), + 100000, timeout_us, true, /* sleep */ + adapter, &firmware_stat); + + /* FW may appear ready; wait a bit to avoid early races. */ + if (firmware_stat =3D=3D FIRMWARE_READY_SDIO) + msleep(100); + + return ret; +} + +static int nxpwifi_check_winner_status(struct nxpwifi_adapter *adapter) +{ + int ret; + u8 winner =3D 0; + struct sdio_mmc_card *card =3D adapter->card; + + ret =3D nxpwifi_read_reg(adapter, card->reg->status_reg_0, &winner); + if (ret) + return ret; + + if (winner) + adapter->winner =3D 0; + else + adapter->winner =3D 1; + + return ret; +} + +/* Remove the SDIO function and tear down the adapter. */ +static void +nxpwifi_sdio_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + struct nxpwifi_adapter *adapter; + struct nxpwifi_private *priv; + int ret =3D 0; + u16 firmware_stat; + + card =3D sdio_get_drvdata(func); + if (!card) + return; + + wait_for_completion(&card->fw_done); + + adapter =3D card->adapter; + if (!adapter || !adapter->priv_num) + return; + + ret =3D nxpwifi_sdio_read_fw_status(adapter, &firmware_stat); + if (!ret && firmware_stat =3D=3D FIRMWARE_READY_SDIO) { + nxpwifi_deauthenticate_all(adapter); + + priv =3D 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); +} + +/* Suspend the SDIO function while keeping SDIO power. */ +static int nxpwifi_sdio_suspend(struct device *dev) +{ + struct sdio_func *func =3D dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct nxpwifi_adapter *adapter; + mmc_pm_flag_t pm_flag =3D 0; + int ret =3D 0; + + pm_flag =3D sdio_get_host_pm_caps(func); + + if (!(pm_flag & MMC_PM_KEEP_POWER)) { + /* host lacks keep-power capability */ + dev_warn(dev, "suspend: host does not support MMC_PM_KEEP_POWER\n"); + return -EOPNOTSUPP; + } + + card =3D sdio_get_drvdata(func); + + if (!card) { + dev_warn(dev, "suspend: card not ready\n"); + return -ENODEV; + } + + /* Might still be loading firmware */ + wait_for_completion(&card->fw_done); + + adapter =3D card->adapter; + if (!adapter) { + dev_warn(dev, "suspend: adapter not ready\n"); + return -ENODEV; + } + + nxpwifi_enable_wake(adapter); + + /* Enable the Host Sleep */ + if (!nxpwifi_enable_hs(adapter)) { + nxpwifi_dbg(adapter, ERROR, "suspend: enable host sleep failed\n"); + clear_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags); + nxpwifi_disable_wake(adapter); + return -ETIMEDOUT; + } + + ret =3D 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 =3D dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + + card =3D 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[] =3D { + {SDIO_DEVICE(SDIO_VENDOR_ID_NXP, SDIO_DEVICE_ID_NXP_IW61X), + .driver_data =3D (unsigned long)&nxpwifi_sdio_iw61x}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, nxpwifi_ids); + +static const struct dev_pm_ops nxpwifi_sdio_pm_ops =3D { + .suspend =3D nxpwifi_sdio_suspend, + .resume =3D nxpwifi_sdio_resume, +}; + +static struct sdio_driver nxpwifi_sdio =3D { + .name =3D "nxpwifi_sdio", + .id_table =3D nxpwifi_ids, + .probe =3D nxpwifi_sdio_probe, + .remove =3D nxpwifi_sdio_remove, + .drv =3D { + .coredump =3D nxpwifi_sdio_coredump, + .pm =3D &nxpwifi_sdio_pm_ops, + } +}; + +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); +} + +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); +} + +/* SDIO wrapper for firmware download (claims host). */ +static int nxpwifi_sdio_dnld_fw(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret; + + sdio_claim_host(card->func); + ret =3D nxpwifi_dnld_fw(adapter, fw); + sdio_release_host(card->func); + + return ret; +} + +static int nxpwifi_init_sdio_new_mode(struct nxpwifi_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card =3D adapter->card; + int ret; + + adapter->ioport =3D MEM_PORT; + + /* enable sdio new mode */ + ret =3D nxpwifi_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®); + if (ret) + return ret; + ret =3D 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 =3D nxpwifi_read_reg(adapter, card->reg->cmd_cfg_0, ®); + if (ret) + return ret; + ret =3D 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 =3D nxpwifi_read_reg(adapter, card->reg->cmd_cfg_1, ®); + if (ret) + return ret; + ret =3D nxpwifi_write_reg(adapter, card->reg->cmd_cfg_1, + reg | CMD_PORT_AUTO_EN); + + return ret; +} + +/* Initialize SDIO IO ports and host-int behavior. */ +static int nxpwifi_init_sdio_ioport(struct nxpwifi_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card =3D adapter->card; + int ret; + + ret =3D nxpwifi_init_sdio_new_mode(adapter); + if (ret) + return ret; + + /* Set Host interrupt reset to read to clear */ + ret =3D nxpwifi_read_reg(adapter, card->reg->host_int_rsr_reg, ®); + if (ret) + return ret; + ret =3D 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 =3D nxpwifi_read_reg(adapter, card->reg->card_misc_cfg_reg, ®); + if (ret) + return ret; + ret =3D nxpwifi_write_reg(adapter, card->reg->card_misc_cfg_reg, + reg | AUTO_RE_ENABLE_INT); + + return ret; +} + +static int nxpwifi_write_data_to_card(struct nxpwifi_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port) +{ + u32 i =3D 0; + int ret; + + do { + ret =3D 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; +} + +static int nxpwifi_get_rd_port(struct nxpwifi_adapter *adapter, u8 *port) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D card->reg; + u32 rd_bitmap =3D card->mp_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 &=3D (u32)(~(1 << card->curr_rd_port)); + *port =3D card->curr_rd_port; + + if (++card->curr_rd_port =3D=3D card->max_ports) + card->curr_rd_port =3D reg->start_rd_port; + + return 0; +} + +static int nxpwifi_get_wr_port_data(struct nxpwifi_adapter *adapter, u32 *= port) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D card->reg; + u32 wr_bitmap =3D card->mp_wr_bitmap; + + if (!(wr_bitmap & card->mp_data_port_mask)) { + adapter->data_sent =3D true; + return -EBUSY; + } + + if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { + card->mp_wr_bitmap &=3D (u32)(~(1 << card->curr_wr_port)); + *port =3D card->curr_wr_port; + if (++card->curr_wr_port =3D=3D card->mp_end_port) + card->curr_wr_port =3D reg->start_wr_port; + } else { + adapter->data_sent =3D true; + return -EBUSY; + } + + return 0; +} + +static int +nxpwifi_sdio_poll_card_status(struct nxpwifi_adapter *adapter, u8 bits) +{ + struct sdio_mmc_card *card =3D adapter->card; + u32 tries; + u8 cs; + int ret; + + for (tries =3D 0; tries < MAX_POLL_TRIES; tries++) { + ret =3D nxpwifi_read_reg(adapter, card->reg->poll_reg, &cs); + if (ret) + break; + else if ((cs & bits) =3D=3D bits) + return 0; + + usleep_range(10, 20); + } + + nxpwifi_dbg(adapter, ERROR, "poll card status failed, tries =3D %d\n", tr= ies); + + return ret; +} + +/* Disable SDIO host interrupt and release IRQ. */ +static void nxpwifi_sdio_disable_host_int(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + struct sdio_func *func =3D 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); +} + +static void nxpwifi_interrupt_status(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D 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 =3D card->mp_regs[card->reg->host_int_status_reg]; + if (sdio_ireg) { + nxpwifi_dbg(adapter, INTR, "intr: sdio_ireg =3D %#x\n", sdio_ireg); + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |=3D sdio_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + } +} + +/* SDIO IRQ handler: snapshot status and schedule main work. */ +static void +nxpwifi_sdio_interrupt(struct sdio_func *func) +{ + struct nxpwifi_adapter *adapter; + struct sdio_mmc_card *card; + + card =3D sdio_get_drvdata(func); + + if (!card || !card->adapter) { + /* device-scoped error logging (rate-limited to avoid flood) */ + dev_err_ratelimited(&func->dev, "interrupt: missing card/adapter\n"); + return; + } + + adapter =3D card->adapter; + + if (!adapter->pps_uapsd_mode && adapter->ps_state =3D=3D PS_STATE_SLEEP) + adapter->ps_state =3D PS_STATE_AWAKE; + + nxpwifi_interrupt_status(adapter); + nxpwifi_queue_work(adapter, &adapter->main_work); +} + +/* Enable SDIO host interrupt and claim IRQ. */ +static int nxpwifi_sdio_enable_host_int(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + struct sdio_func *func =3D card->func; + int ret; + + sdio_claim_host(func); + + /* Request the SDIO IRQ */ + ret =3D sdio_claim_irq(func, nxpwifi_sdio_interrupt); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "claim irq failed: ret=3D%d\n", ret); + goto done; + } + + /* Simply write the mask to the register */ + ret =3D 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; +} + +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) + return -EINVAL; + + ret =3D nxpwifi_read_data_sync(adapter, buffer, npayload, ioport, 1); + + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "read iomem failed (ioport=3D%#x, len=3D%u): %d", + ioport, npayload, ret); + + return ret; + } + + nb =3D get_unaligned_le16((buffer)); + if (nb > npayload) { + nxpwifi_dbg(adapter, ERROR, + "invalid packet len: nb=3D%u > npayload=3D%u (ioport=3D%#x)", + nb, npayload, ioport); + return -EINVAL; + } + + *type =3D get_unaligned_le16((buffer + 2)); + + return ret; +} + +/* Download firmware using the helper protocol. */ +static int nxpwifi_prog_fw_w_helper(struct nxpwifi_adapter *adapter, + struct nxpwifi_fw_image *fw) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D card->reg; + int ret; + u8 *firmware =3D fw->fw_buf; + u32 firmware_len =3D fw->fw_len; + u32 offset =3D 0; + u8 base0, base1; + u8 *fwbuf; + u16 len =3D 0; + u32 txlen, tx_blocks =3D 0, tries; + u32 i =3D 0; + + if (!firmware_len) { + nxpwifi_dbg(adapter, ERROR, + "firmware image not found! Terminating download\n"); + return -EINVAL; + } + + /* Assume that the allocated buffer is 8-byte aligned */ + fwbuf =3D 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 =3D 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 >=3D firmware_len) + break; + + for (tries =3D 0; tries < MAX_POLL_TRIES; tries++) { + ret =3D nxpwifi_read_reg(adapter, reg->base_0_reg, + &base0); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "dev BASE0 register read failed:\t" + "base0=3D%#04X(%d). Terminating dnld\n", + base0, base0); + goto done; + } + ret =3D nxpwifi_read_reg(adapter, reg->base_1_reg, + &base1); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "dev BASE1 register read failed:\t" + "base1=3D%#04X(%d). Terminating dnld\n", + base1, base1); + goto done; + } + len =3D (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 =3D -EINVAL; + goto done; + } + + txlen =3D 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 =3D -EIO; + goto done; + } + nxpwifi_dbg(adapter, ERROR, + "CRC indicated by the helper:\t" + "len =3D 0x%04X, txlen =3D %d\n", len, txlen); + len &=3D ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen =3D 0; + } else { + i =3D 0; + + /* + * Set blocksize to transfer - checking for last + * block + */ + if (firmware_len - offset < txlen) + txlen =3D firmware_len - offset; + + tx_blocks =3D (txlen + NXPWIFI_SDIO_BLOCK_SIZE - 1) + / NXPWIFI_SDIO_BLOCK_SIZE; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + ret =3D 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 +=3D txlen; + } while (true); + + nxpwifi_dbg(adapter, MSG, "FW download complete (%u bytes)\n", offset); + + ret =3D 0; +done: + sdio_release_host(card->func); + kfree(fwbuf); + return ret; +} + +/* Deaggregate an SDIO RX aggregation packet. */ +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 =3D skb->data; + total_pkt_len =3D skb->len; + + while (total_pkt_len >=3D (SDIO_HEADER_OFFSET + adapter->intf_hdr_len)) { + if (total_pkt_len < adapter->sdio_rx_block_size) + break; + blk_num =3D *(data + BLOCK_NUMBER_OFFSET); + blk_size =3D 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=3D%d, blk_size=3D%d, total_pkt_len=3D%d\n", + __func__, blk_num, blk_size, total_pkt_len); + break; + } + pkt_len =3D 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=3D%d, blk_size=3D%d\n", + __func__, pkt_len, blk_size); + break; + } + + skb_deaggr =3D 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 +=3D blk_size; + total_pkt_len -=3D blk_size; + } +} + +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 =3D get_unaligned_le16(skb->data); + + if (upld_typ !=3D 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, DATA, + "Rx Aggr Data packet\n"); + rx_info =3D NXPWIFI_SKB_RXCB(skb); + rx_info->buf_type =3D NXPWIFI_TYPE_AGGR_DATA; + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + atomic_inc(&adapter->rx_pending); + adapter->data_received =3D true; + } else { + /* Deaggregate an SDIO RX aggregation packet. */ + nxpwifi_deaggr_sdio_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } + break; + + case NXPWIFI_TYPE_DATA: + nxpwifi_dbg(adapter, DATA, "Rx Data packet\n"); + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received =3D true; + atomic_inc(&adapter->rx_pending); + } else { + nxpwifi_handle_rx_packet(adapter, skb); + } + break; + + case NXPWIFI_TYPE_CMD: + nxpwifi_dbg(adapter, CMD, "Rx Cmd Response\n"); + /* take care of curr_cmd =3D NULL case */ + if (!adapter->curr_cmd) { + cmd_buf =3D adapter->upld_buf; + + if (adapter->ps_state =3D=3D 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 =3D true; + adapter->curr_cmd->resp_skb =3D skb; + } + break; + + case NXPWIFI_TYPE_EVENT: + nxpwifi_dbg(adapter, EVENT, "Rx Event\n"); + adapter->event_cause =3D get_unaligned_le32(skb->data); + + if (skb->len > NXPWIFI_EVENT_HEADER_LEN) { + u32 body_len =3D min_t(u32, skb->len - NXPWIFI_EVENT_HEADER_LEN, + MAX_EVENT_SIZE); + memcpy(adapter->event_body, skb->data + NXPWIFI_EVENT_HEADER_LEN, + body_len); + } + + /* event cause has been saved to adapter->event_cause */ + adapter->event_received =3D true; + adapter->event_skb =3D skb; + + break; + + default: + nxpwifi_dbg(adapter, ERROR, "unknown upload type %#x\n", upld_typ); + dev_kfree_skb_any(skb); + break; + } +} + +/* Receive path with SDIO multi-port aggregation. */ +static int nxpwifi_sdio_card_to_host_mp_aggr(struct nxpwifi_adapter *adapt= er, + u16 rx_len, u8 port) +{ + struct sdio_mmc_card *card =3D adapter->card; + s32 f_do_rx_aggr =3D 0; + s32 f_do_rx_cur =3D 0; + s32 f_aggr_cur =3D 0; + s32 f_post_aggr_cur =3D 0; + struct sk_buff *skb_deaggr; + struct sk_buff *skb =3D NULL; + u32 pkt_len, pkt_type, mport, pind; + u8 *curr_ptr; + int ret =3D 0; + + if (!card->mpa_rx.enabled) { + nxpwifi_dbg(adapter, WARN, "rx aggregation disabled\n"); + f_do_rx_cur =3D 1; + goto rx_curr_single; + } + + if (card->mp_rd_bitmap & card->reg->data_port_mask) { + /* Some more data RX pending */ + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { + f_aggr_cur =3D 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_aggr =3D 1; + f_post_aggr_cur =3D 1; + } + } else { + /* Rx aggr not in progress */ + f_aggr_cur =3D 1; + } + + } else { + /* No more data RX pending */ + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + f_do_rx_aggr =3D 1; + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) + f_aggr_cur =3D 1; + else + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_cur =3D 1; + } else { + f_do_rx_cur =3D 1; + } + } + + if (f_aggr_cur) { + /* 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)) { + /* No more pkts allowed in Aggr buf, rx it */ + f_do_rx_aggr =3D 1; + } + } + + if (f_do_rx_aggr) { + u32 port_count; + int i; + + /* do aggr RX now */ + for (i =3D 0, port_count =3D 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 =3D (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_rx.start_port; + + if (card->mpa_rx.pkt_cnt =3D=3D 1) + mport =3D adapter->ioport + card->mpa_rx.start_port; + + ret =3D nxpwifi_read_data_sync(adapter, card->mpa_rx.buf, + card->mpa_rx.buf_len, mport, 1); + if (ret) + goto error; + + curr_ptr =3D card->mpa_rx.buf; + + for (pind =3D 0; pind < card->mpa_rx.pkt_cnt; pind++) { + u32 *len_arr =3D card->mpa_rx.len_arr; + + /* get curr PKT len & type */ + pkt_len =3D get_unaligned_le16(&curr_ptr[0]); + pkt_type =3D get_unaligned_le16(&curr_ptr[2]); + + /* copy pkt to deaggr buf */ + skb_deaggr =3D nxpwifi_alloc_dma_align_buf(len_arr[pind], + GFP_KERNEL); + if (!skb_deaggr) { + nxpwifi_dbg(adapter, ERROR, "skb allocation failure\t" + "drop pkt len=3D%d type=3D%d\n", + pkt_len, pkt_type); + curr_ptr +=3D len_arr[pind]; + continue; + } + + skb_put(skb_deaggr, len_arr[pind]); + + if ((pkt_type =3D=3D NXPWIFI_TYPE_DATA || + (pkt_type =3D=3D NXPWIFI_TYPE_AGGR_DATA && + adapter->sdio_rx_aggr_enable)) && + pkt_len <=3D len_arr[pind]) { + memcpy(skb_deaggr->data, curr_ptr, pkt_len); + + skb_trim(skb_deaggr, pkt_len); + + nxpwifi_decode_rx_packet(adapter, skb_deaggr, + pkt_type); + } else { + nxpwifi_dbg(adapter, ERROR, + "drop wrong aggr pkt:\t" + "sdio_single_port_rx_aggr=3D%d\t" + "type=3D%d len=3D%d max_len=3D%d\n", + adapter->sdio_rx_aggr_enable, + pkt_type, pkt_len, len_arr[pind]); + dev_kfree_skb_any(skb_deaggr); + } + curr_ptr +=3D len_arr[pind]; + } + MP_RX_AGGR_BUF_RESET(card); + } + +rx_curr_single: + if (f_do_rx_cur) { + skb =3D nxpwifi_alloc_dma_align_buf(rx_len, GFP_KERNEL); + if (!skb) { + nxpwifi_dbg(adapter, ERROR, + "single skb allocated fail,\t" + "drop pkt port=3D%d len=3D%d\n", port, rx_len); + ret =3D 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 =3D 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 =3D=3D 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) /* 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; +} + +static int nxpwifi_process_int_status(struct nxpwifi_adapter *adapter, u8 = sdio_ireg) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D card->reg; + int ret =3D 0; + struct sk_buff *skb; + u8 port; + u32 len_reg_l, len_reg_u; + u32 rx_blocks; + u16 rx_len; + u32 bitmap; + u8 cr; + + if (!sdio_ireg) + return ret; + + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) + adapter->cmd_sent =3D false; + + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + u32 pkt_type; + + /* read the len of control packet */ + rx_len =3D card->mp_regs[reg->cmd_rd_len_1] << 8; + rx_len |=3D (u16)card->mp_regs[reg->cmd_rd_len_0]; + rx_blocks =3D DIV_ROUND_UP(rx_len, NXPWIFI_SDIO_BLOCK_SIZE); + if (rx_len <=3D adapter->intf_hdr_len || + (rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE) > + NXPWIFI_RX_DATA_BUF_SIZE) + return -EINVAL; + rx_len =3D (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE); + + skb =3D nxpwifi_alloc_dma_align_buf(rx_len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_put(skb, rx_len); + + ret =3D nxpwifi_sdio_card_to_host(adapter, &pkt_type, skb->data, + skb->len, adapter->ioport | + CMD_PORT_SLCT); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "failed to card_to_host"); + dev_kfree_skb_any(skb); + goto term_cmd; + } + + if (pkt_type !=3D NXPWIFI_TYPE_CMD && + pkt_type !=3D NXPWIFI_TYPE_EVENT) + nxpwifi_dbg(adapter, ERROR, "Received wrong packet on cmd port"); + + nxpwifi_decode_rx_packet(adapter, skb, pkt_type); + } + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + bitmap =3D (u32)card->mp_regs[reg->wr_bitmap_l]; + bitmap |=3D ((u32)card->mp_regs[reg->wr_bitmap_u]) << 8; + bitmap |=3D ((u32)card->mp_regs[reg->wr_bitmap_1l]) << 16; + bitmap |=3D ((u32)card->mp_regs[reg->wr_bitmap_1u]) << 24; + card->mp_wr_bitmap =3D bitmap; + + nxpwifi_dbg(adapter, INTR, "intr: wr_bitmap=3D0x%x\n", card->mp_wr_bitma= p); + if (adapter->data_sent && + (card->mp_wr_bitmap & card->mp_data_port_mask)) { + nxpwifi_dbg(adapter, INTR, "Tx DONE\n"); + adapter->data_sent =3D false; + } + } + + nxpwifi_dbg(adapter, INTR, "cmd_sent=3D%d data_sent=3D%d\n", + adapter->cmd_sent, adapter->data_sent); + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + bitmap =3D (u32)card->mp_regs[reg->rd_bitmap_l]; + bitmap |=3D ((u32)card->mp_regs[reg->rd_bitmap_u]) << 8; + bitmap |=3D ((u32)card->mp_regs[reg->rd_bitmap_1l]) << 16; + bitmap |=3D ((u32)card->mp_regs[reg->rd_bitmap_1u]) << 24; + card->mp_rd_bitmap =3D bitmap; + nxpwifi_dbg(adapter, INTR, "intr: rd_bitmap=3D0x%x\n", card->mp_rd_bitma= p); + + while (true) { + ret =3D nxpwifi_get_rd_port(adapter, &port); + + if (ret) + break; + + len_reg_l =3D reg->rd_len_p0_l + (port << 1); + len_reg_u =3D reg->rd_len_p0_u + (port << 1); + rx_len =3D ((u16)card->mp_regs[len_reg_u]) << 8; + rx_len |=3D (u16)card->mp_regs[len_reg_l]; + rx_blocks =3D + (rx_len + NXPWIFI_SDIO_BLOCK_SIZE - + 1) / NXPWIFI_SDIO_BLOCK_SIZE; + if (rx_len <=3D 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=3D%d\n", rx_len); + return -EINVAL; + } + + rx_len =3D (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE); + + ret =3D nxpwifi_sdio_card_to_host_mp_aggr(adapter, rx_len, + port); + if (ret) { + nxpwifi_dbg(adapter, ERROR, + "card_to_host_mpa failed: int status=3D%#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 =3D %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 =3D%x\n", cr); + + return ret; +} + +/* Transmit using SDIO multi-port aggregation. */ +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 =3D adapter->card; + int ret =3D 0; + s32 f_send_aggr_buf =3D 0; + s32 f_send_cur_buf =3D 0; + s32 f_precopy_cur_buf =3D 0; + s32 f_postcopy_cur_buf =3D 0; + u32 mport; + int index; + + if (!card->mpa_tx.enabled || port =3D=3D CMD_PORT_SLCT) { + nxpwifi_dbg(adapter, WARN, "tx aggregation disabled\n"); + f_send_cur_buf =3D 1; + goto tx_curr_single; + } + + if (next_pkt_len) { + /* More pkt in TX queue */ + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { + f_precopy_cur_buf =3D 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 =3D 1; + } else { + /* No room in Aggr buf, send it */ + f_send_aggr_buf =3D 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port))) + f_send_cur_buf =3D 1; + else + f_postcopy_cur_buf =3D 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 =3D 1; + else + f_send_cur_buf =3D 1; + } + } else { + /* Last pkt in TX queue */ + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + /* some packs in Aggr buf already */ + f_send_aggr_buf =3D 1; + + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) + f_precopy_cur_buf =3D 1; + else + /* No room in Aggr buf, send it */ + f_send_cur_buf =3D 1; + } else { + f_send_cur_buf =3D 1; + } + } + + if (f_precopy_cur_buf) { + 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 =3D 1; + } + + if (f_send_aggr_buf) { + u32 port_count; + int i; + + for (i =3D 0, port_count =3D 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 =3D (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_tx.start_port; + + if (card->mpa_tx.pkt_cnt =3D=3D 1) + mport =3D adapter->ioport + card->mpa_tx.start_port; + + ret =3D 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 =3D adapter->dbg.last_sdio_mp_index; + index =3D (index + 1) % NXPWIFI_DBG_SDIO_MP_NUM; + adapter->dbg.last_sdio_mp_index =3D index; + adapter->dbg.last_mp_wr_ports[index] =3D mport; + adapter->dbg.last_mp_wr_bitmap[index] =3D card->mp_wr_bitmap; + adapter->dbg.last_mp_wr_len[index] =3D card->mpa_tx.buf_len; + adapter->dbg.last_mp_curr_wr_port[index] =3D card->curr_wr_port; + + MP_TX_AGGR_BUF_RESET(card); + } + +tx_curr_single: + if (f_send_cur_buf) + ret =3D nxpwifi_write_data_to_card(adapter, payload, pkt_len, + adapter->ioport + port); + + if (f_postcopy_cur_buf) + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + + return ret; +} + +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 =3D adapter->card; + int ret; + u32 buf_block_len; + u32 blk_size; + u32 port; + u8 *payload =3D (u8 *)skb->data; + u32 pkt_len =3D skb->len; + + /* Allocate buffer and copy payload */ + blk_size =3D NXPWIFI_SDIO_BLOCK_SIZE; + buf_block_len =3D (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 =3D 0, NXPWIFI_TYPE_CMD =3D 1, + * NXPWIFI_TYPE_EVENT =3D 3) + */ + if (type =3D=3D NXPWIFI_TYPE_DATA) { + ret =3D nxpwifi_get_wr_port_data(adapter, &port); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "no wr_port available\n"); + return ret; + } + } else { + adapter->cmd_sent =3D true; + + if (pkt_len <=3D adapter->intf_hdr_len || + pkt_len > NXPWIFI_UPLD_SIZE) { + nxpwifi_dbg(adapter, ERROR, + "invalid upld pkt_len=3D%u (hdr_len=3D%u, max=3D%u)\n", + pkt_len, adapter->intf_hdr_len, NXPWIFI_UPLD_SIZE); + return -EINVAL; + } + + port =3D CMD_PORT_SLCT; + } + + /* Transfer data to card */ + pkt_len =3D buf_block_len * blk_size; + + if (tx_param) + ret =3D nxpwifi_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, tx_param->next_pkt_len + ); + else + ret =3D nxpwifi_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, 0); + + if (ret) { + if (type =3D=3D NXPWIFI_TYPE_CMD || + type =3D=3D NXPWIFI_TYPE_VDLL) + adapter->cmd_sent =3D false; + if (type =3D=3D NXPWIFI_TYPE_DATA) { + adapter->data_sent =3D false; + /* restore curr_wr_port in error cases */ + card->curr_wr_port =3D port; + card->mp_wr_bitmap |=3D (u32)(1 << card->curr_wr_port); + } + } else { + if (type =3D=3D NXPWIFI_TYPE_DATA) { + if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) + adapter->data_sent =3D true; + else + adapter->data_sent =3D false; + } + } + + return ret; +} + +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 =3D adapter->card; + u32 rx_buf_size; + int ret =3D 0; + + card->mpa_tx.buf =3D kzalloc(mpa_tx_buf_size, GFP_KERNEL); + if (!card->mpa_tx.buf) { + ret =3D -ENOMEM; + goto error; + } + + card->mpa_tx.buf_size =3D mpa_tx_buf_size; + + rx_buf_size =3D max_t(u32, mpa_rx_buf_size, + (u32)SDIO_MAX_AGGR_BUF_SIZE); + card->mpa_rx.buf =3D kzalloc(rx_buf_size, GFP_KERNEL); + if (!card->mpa_rx.buf) { + ret =3D -ENOMEM; + goto error; + } + + card->mpa_rx.buf_size =3D rx_buf_size; + +error: + if (ret) { + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); + card->mpa_tx.buf_size =3D 0; + card->mpa_rx.buf_size =3D 0; + card->mpa_tx.buf =3D NULL; + card->mpa_rx.buf =3D NULL; + } + + return ret; +} + +static void +nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + + if (adapter->card) { + card->adapter =3D NULL; + sdio_claim_host(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + } +} + +static int nxpwifi_register_dev(struct nxpwifi_adapter *adapter) +{ + int ret; + struct sdio_mmc_card *card =3D adapter->card; + struct sdio_func *func =3D card->func; + const char *firmware =3D card->firmware; + + /* save adapter pointer in card */ + card->adapter =3D adapter; + adapter->tx_buf_size =3D card->tx_buf_size; + + sdio_claim_host(func); + + /* Set block size */ + ret =3D 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 strap= ping + * option + */ + if (card->firmware_sdiouart) { + u8 val; + + nxpwifi_read_reg(adapter, card->reg->host_strap_reg, &val); + if ((val & card->reg->host_strap_mask) =3D=3D card->reg->host_strap_valu= e) + firmware =3D card->firmware_sdiouart; + } + strscpy(adapter->fw_name, firmware, sizeof(adapter->fw_name)); + + if (card->fw_dump_enh) { + adapter->mem_type_mapping_tbl =3D generic_mem_type_map; + adapter->num_mem_types =3D 1; + } else { + adapter->mem_type_mapping_tbl =3D mem_type_mapping_tbl; + adapter->num_mem_types =3D ARRAY_SIZE(mem_type_mapping_tbl); + } + + return 0; +} + +static int nxpwifi_init_sdio(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D 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 =3D 0; + card->mp_wr_bitmap =3D 0; + card->curr_rd_port =3D reg->start_rd_port; + card->curr_wr_port =3D reg->start_wr_port; + + card->mp_data_port_mask =3D reg->data_port_mask; + + card->mpa_tx.buf_len =3D 0; + card->mpa_tx.pkt_cnt =3D 0; + card->mpa_tx.start_port =3D 0; + + card->mpa_tx.enabled =3D 1; + card->mpa_tx.pkt_aggr_limit =3D card->mp_agg_pkt_limit; + + card->mpa_rx.buf_len =3D 0; + card->mpa_rx.pkt_cnt =3D 0; + card->mpa_rx.start_port =3D 0; + + card->mpa_rx.enabled =3D 1; + card->mpa_rx.pkt_aggr_limit =3D card->mp_agg_pkt_limit; + + /* Allocate buffers for SDIO MP-A */ + card->mp_regs =3D devm_kzalloc(&card->func->dev, reg->max_mp_regs, GFP_KE= RNEL); + + if (!card->mp_regs) + return -ENOMEM; + + card->mpa_rx.len_arr =3D + devm_kcalloc(&card->func->dev, card->mp_agg_pkt_limit, + sizeof(*card->mpa_rx.len_arr), GFP_KERNEL); + + if (!card->mpa_rx.len_arr) + return -ENOMEM; + + ret =3D 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 =3D=3D NXPWIFI_MP_AGGR_BSIZE_MAX || + card->mp_rx_agg_buf_size =3D=3D NXPWIFI_MP_AGGR_BSIZE_MAX)) { + /* Disable rx single port aggregation */ + adapter->host_disable_sdio_rx_aggr =3D true; + + ret =3D 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 =3D 0; + card->mpa_rx.enabled =3D 0; + } + } + + adapter->ext_scan =3D card->can_ext_scan; + return ret; +} + +static void nxpwifi_cleanup_mpa_buf(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + + MP_TX_AGGR_BUF_RESET(card); + MP_RX_AGGR_BUF_RESET(card); +} + +static void nxpwifi_cleanup_sdio(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + + cancel_work_sync(&card->work); + + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); +} + +static void +nxpwifi_update_mp_end_port(struct nxpwifi_adapter *adapter, u16 port) +{ + struct sdio_mmc_card *card =3D adapter->card; + const struct nxpwifi_sdio_card_reg *reg =3D card->reg; + int i; + + card->mp_end_port =3D port; + + card->mp_data_port_mask =3D reg->data_port_mask; + + if (reg->start_wr_port) { + for (i =3D 1; i <=3D card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &=3D + ~(1 << (card->max_ports - i)); + } + + card->curr_wr_port =3D reg->start_wr_port; +} + +/* Perform an SDIO card reset in workqueue context. */ +static void nxpwifi_sdio_card_reset_work(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + struct sdio_func *func =3D 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 =3D mmc_hw_reset(func->card); + sdio_release_host(func); + + switch (ret) { + case 1: + nxpwifi_dbg(adapter, MSG, "SDIO HW reset asynchronous\n"); + complete_all(adapter->fw_done); + break; + case 0: + ret =3D 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; + } +} + +static enum +rdwr_status nxpwifi_sdio_rdwr_firmware(struct nxpwifi_adapter *adapter, + u8 doneflag) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret, tries; + u8 ctrl_data =3D 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 =3D 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data =3D 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 =3D=3D FW_DUMP_DONE) + break; + if (doneflag && ctrl_data =3D=3D doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data !=3D 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 =3D=3D card->reg->fw_dump_host_ready) { + nxpwifi_dbg(adapter, ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/* Dump firmware memories for post-mortem analysis. */ +static void nxpwifi_sdio_fw_dump(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + int ret =3D 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag =3D 0; + enum rdwr_status stat; + u32 memory_size; + + if (!card->can_dump_fw) + return; + + for (idx =3D 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry =3D &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr =3D NULL; + } + entry->mem_size =3D 0; + } + + nxpwifi_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + nxpwifi_dbg(adapter, MSG, "=3D=3D nxpwifi firmware dump start =3D=3D\n"); + + stat =3D nxpwifi_sdio_rdwr_firmware(adapter, doneflag); + if (stat =3D=3D RDWR_STATUS_FAILURE) + goto done; + + reg =3D card->reg->fw_dump_start; + /* Read the number of the memories which will dump */ + dump_num =3D 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 =3D 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry =3D &mem_type_mapping_tbl[idx]; + + stat =3D nxpwifi_sdio_rdwr_firmware(adapter, doneflag); + if (stat =3D=3D RDWR_STATUS_FAILURE) + goto done; + + memory_size =3D 0; + reg =3D card->reg->fw_dump_start; + for (i =3D 0; i < 4; i++) { + read_reg =3D sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO read err\n"); + goto done; + } + memory_size |=3D (read_reg << i * 8); + reg++; + } + + if (memory_size =3D=3D 0) { + nxpwifi_dbg(adapter, DUMP, "Firmware dump Finished!\n"); + ret =3D 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=3D0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr =3D vmalloc(memory_size + 1); + entry->mem_size =3D memory_size; + if (!entry->mem_ptr) + goto done; + dbg_ptr =3D entry->mem_ptr; + end_ptr =3D dbg_ptr + memory_size; + + doneflag =3D entry->done_flag; + nxpwifi_dbg(adapter, DUMP, "Start %s output, please wait...\n", + entry->mem_name); + + do { + stat =3D nxpwifi_sdio_rdwr_firmware(adapter, doneflag); + if (stat =3D=3D RDWR_STATUS_FAILURE) + goto done; + + reg_start =3D card->reg->fw_dump_start; + reg_end =3D card->reg->fw_dump_end; + for (reg =3D reg_start; reg <=3D reg_end; reg++) { + *dbg_ptr =3D 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 !=3D RDWR_STATUS_DONE) + continue; + + nxpwifi_dbg(adapter, DUMP, "%s done: size=3D0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (1); + } + nxpwifi_dbg(adapter, MSG, "=3D=3D nxpwifi firmware dump end =3D=3D\n"); + +done: + sdio_release_host(card->func); +} + +/* Generic firmware dump flow for enhanced devices. */ +static void nxpwifi_sdio_generic_fw_dump(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + struct memory_type_mapping *entry =3D &generic_mem_type_map[0]; + unsigned int reg, reg_start, reg_end; + u8 start_flag =3D 0, done_flag =3D 0; + u8 *dbg_ptr, *end_ptr; + enum rdwr_status stat; + int ret =3D -EPERM, tries; + + if (!card->fw_dump_enh) + return; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr =3D NULL; + } + entry->mem_size =3D 0; + + nxpwifi_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + nxpwifi_dbg(adapter, MSG, "=3D=3D nxpwifi firmware dump start =3D=3D\n"); + + stat =3D nxpwifi_sdio_rdwr_firmware(adapter, done_flag); + if (stat =3D=3D RDWR_STATUS_FAILURE) + goto done; + + reg_start =3D card->reg->fw_dump_start; + reg_end =3D card->reg->fw_dump_end; + for (reg =3D reg_start; reg <=3D reg_end; reg++) { + for (tries =3D 0; tries < MAX_POLL_TRIES; tries++) { + start_flag =3D sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO read err\n"); + goto done; + } + if (start_flag =3D=3D 0) + break; + if (tries =3D=3D MAX_POLL_TRIES) { + nxpwifi_dbg(adapter, ERROR, "FW not ready to dump\n"); + ret =3D -EPERM; + goto done; + } + } + usleep_range(100, 200); + } + + entry->mem_ptr =3D vmalloc(0xf0000 + 1); + if (!entry->mem_ptr) { + ret =3D -ENOMEM; + goto done; + } + dbg_ptr =3D entry->mem_ptr; + entry->mem_size =3D 0xf0000; + end_ptr =3D dbg_ptr + entry->mem_size; + + done_flag =3D entry->done_flag; + nxpwifi_dbg(adapter, DUMP, + "Start %s output, please wait...\n", entry->mem_name); + + while (true) { + stat =3D nxpwifi_sdio_rdwr_firmware(adapter, done_flag); + if (stat =3D=3D RDWR_STATUS_FAILURE) + goto done; + for (reg =3D reg_start; reg <=3D reg_end; reg++) { + *dbg_ptr =3D sdio_readb(card->func, reg, &ret); + if (ret) { + nxpwifi_dbg(adapter, ERROR, "SDIO read err\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >=3D end_ptr) { + u8 *tmp_ptr; + + tmp_ptr =3D 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 =3D tmp_ptr; + tmp_ptr =3D NULL; + dbg_ptr =3D entry->mem_ptr + entry->mem_size; + entry->mem_size +=3D 0x4000; + end_ptr =3D entry->mem_ptr + entry->mem_size; + } + } + if (stat =3D=3D RDWR_STATUS_DONE) { + entry->mem_size =3D dbg_ptr - entry->mem_ptr; + nxpwifi_dbg(adapter, DUMP, "dump %s done size=3D0x%x\n", + entry->mem_name, entry->mem_size); + ret =3D 0; + break; + } + } + nxpwifi_dbg(adapter, MSG, "=3D=3D nxpwifi firmware dump end =3D=3D\n"); + +done: + if (ret) { + nxpwifi_dbg(adapter, ERROR, "firmware dump failed\n"); + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr =3D NULL; + } + entry->mem_size =3D 0; + } + sdio_release_host(card->func); +} + +/* Build and upload consolidated device dump. */ +static void nxpwifi_sdio_device_dump_work(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + + adapter->devdump_data =3D vzalloc(NXPWIFI_FW_DUMP_SIZE); + if (!adapter->devdump_data) + return; + + nxpwifi_drv_info_dump(adapter); + + /* Generic firmware dump flow for enhanced devices. */ + if (card->fw_dump_enh) + nxpwifi_sdio_generic_fw_dump(adapter); + /* Dump firmware memories for post-mortem analysis. */ + else + nxpwifi_sdio_fw_dump(adapter); + + nxpwifi_prepare_fw_dump_info(adapter); + nxpwifi_upload_device_dump(adapter); +} + +/* Process deferred SDIO work items. */ +static void nxpwifi_sdio_work(struct work_struct *work) +{ + struct sdio_mmc_card *card =3D + container_of(work, struct sdio_mmc_card, work); + + /* Build and upload consolidated device dump. */ + if (test_and_clear_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, + &card->work_flags)) + nxpwifi_sdio_device_dump_work(card->adapter); + + /* Perform an SDIO card reset in workqueue context. */ + if (test_and_clear_bit(NXPWIFI_IFACE_WORK_CARD_RESET, + &card->work_flags)) + nxpwifi_sdio_card_reset_work(card->adapter); +} + +/* Schedule SDIO card reset. */ +static void nxpwifi_sdio_card_reset(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + + if (!test_and_set_bit(NXPWIFI_IFACE_WORK_CARD_RESET, &card->work_flags)) + nxpwifi_queue_work(adapter, &card->work); +} + +static void nxpwifi_sdio_device_dump(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + + if (!test_and_set_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, + &card->work_flags)) + nxpwifi_queue_work(adapter, &card->work); +} + +/* Dump SDIO function and scratch registers into drv_buf. */ +static int +nxpwifi_sdio_reg_dump(struct nxpwifi_adapter *adapter, char *drv_buf) +{ + char *p =3D drv_buf; + struct sdio_mmc_card *cardp =3D adapter->card; + int ret =3D 0; + u8 count, func, data, index =3D 0, size =3D 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 =3D 0; count < 5; count++) { + memset(buf, 0, sizeof(buf)); + ptr =3D buf; + + switch (count) { + case 0: + /* Read the registers of SDIO function0 */ + func =3D count; + reg_start =3D 0; + reg_end =3D 9; + break; + case 1: + /* Read the registers of SDIO function1 */ + func =3D count; + reg_start =3D cardp->reg->func1_dump_reg_start; + reg_end =3D cardp->reg->func1_dump_reg_end; + break; + case 2: + index =3D 0; + func =3D 1; + reg_start =3D cardp->reg->func1_spec_reg_table[index++]; + size =3D cardp->reg->func1_spec_reg_num; + reg_end =3D cardp->reg->func1_spec_reg_table[size - 1]; + break; + default: + /* Read the scratch registers of SDIO function1 */ + if (count =3D=3D 4) + msleep(100); + func =3D 1; + reg_start =3D cardp->reg->func1_scratch_reg; + reg_end =3D reg_start + NXPWIFI_SDIO_SCRATCH_SIZE; + } + + if (count !=3D 2) + ptr +=3D scnprintf(ptr, sizeof(buf) - (ptr - buf), + "SDIO Func%d (%#x-%#x): ", func, reg_start, + reg_end); + else + ptr +=3D scnprintf(ptr, sizeof(buf) - (ptr - buf), + "SDIO Func%d: ", func); + + for (reg =3D reg_start; reg <=3D reg_end;) { + if (func =3D=3D 0) + data =3D sdio_f0_readb(cardp->func, reg, &ret); + else + data =3D sdio_readb(cardp->func, reg, &ret); + + if (count =3D=3D 2) + ptr +=3D scnprintf(ptr, sizeof(buf) - (ptr - buf), "(%#x) ", reg); + if (!ret) { + ptr +=3D scnprintf(ptr, sizeof(buf) - (ptr - buf), "%02x ", data); + } else { + ptr +=3D scnprintf(ptr, sizeof(buf) - (ptr - buf), "ERR"); + break; + } + + if (count =3D=3D 2 && reg < reg_end) + reg =3D cardp->reg->func1_spec_reg_table[index++]; + else + reg++; + } + + nxpwifi_dbg(adapter, MSG, "%s\n", buf); + p +=3D sprintf(p, "%s\n", buf); + } + + sdio_release_host(cardp->func); + + nxpwifi_dbg(adapter, MSG, "SDIO register dump end\n"); + + return p - drv_buf; +} + +static void nxpwifi_sdio_up_dev(struct nxpwifi_adapter *adapter) +{ + struct sdio_mmc_card *card =3D adapter->card; + u8 sdio_ireg; + int ret =3D 0; + + sdio_claim_host(card->func); + ret =3D sdio_enable_func(card->func); + + if (ret) + nxpwifi_dbg(adapter, ERROR, "sdio_enable_func failed: %d\n", ret); + + ret =3D sdio_set_block_size(card->func, NXPWIFI_SDIO_BLOCK_SIZE); + + if (ret) + nxpwifi_dbg(adapter, ERROR, "sdio_set_block_size failed: %d\n", ret); + + 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 =3D 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)) + nxpwifi_dbg(adapter, ERROR, "error enabling SDIO port\n"); +} + +static struct nxpwifi_if_ops sdio_ops =3D { + .init_if =3D nxpwifi_init_sdio, + .cleanup_if =3D nxpwifi_cleanup_sdio, + .check_fw_status =3D nxpwifi_check_fw_status, + .check_winner_status =3D nxpwifi_check_winner_status, + .prog_fw =3D nxpwifi_prog_fw_w_helper, + .register_dev =3D nxpwifi_register_dev, + .unregister_dev =3D nxpwifi_unregister_dev, + .enable_int =3D nxpwifi_sdio_enable_host_int, + .disable_int =3D nxpwifi_sdio_disable_host_int, + .process_int_status =3D nxpwifi_process_int_status, + .host_to_card =3D nxpwifi_sdio_host_to_card, + .wakeup =3D nxpwifi_pm_wakeup_card, + .wakeup_complete =3D nxpwifi_pm_wakeup_card_complete, + + /* SDIO specific */ + .update_mp_end_port =3D nxpwifi_update_mp_end_port, + .cleanup_mpa_buf =3D nxpwifi_cleanup_mpa_buf, + .cmdrsp_complete =3D nxpwifi_sdio_cmdrsp_complete, + .event_complete =3D nxpwifi_sdio_event_complete, + .dnld_fw =3D nxpwifi_sdio_dnld_fw, + .card_reset =3D nxpwifi_sdio_card_reset, + .reg_dump =3D nxpwifi_sdio_reg_dump, + .device_dump =3D nxpwifi_sdio_device_dump, + .deaggr_pkt =3D nxpwifi_deaggr_sdio_pkt, + .up_dev =3D 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) =3D a; \ + (((_a)->mpa_tx.buf_len + (len)) <=3D (_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) =3D (a); \ + typeof(pkt_len) (_pkt_len) =3D pkt_len; \ + typeof(port) (_port) =3D port; \ + memmove(&(_a)->mpa_tx.buf[(_a)->mpa_tx.buf_len], \ + payload, (_pkt_len)); \ + (_a)->mpa_tx.buf_len +=3D (_pkt_len); \ + if (!(_a)->mpa_tx.pkt_cnt) \ + (_a)->mpa_tx.start_port =3D (_port); \ + if ((_a)->mpa_tx.start_port <=3D (_port)) \ + (_a)->mpa_tx.ports |=3D (1 << ((_a)->mpa_tx.pkt_cnt)); \ + else \ + (_a)->mpa_tx.ports |=3D (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) =3D a; \ + ((_a)->mpa_tx.pkt_cnt =3D=3D (_a)->mpa_tx.pkt_aggr_limit); \ + }) + +/* Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) do { \ + typeof(a) (_a) =3D (a); \ + (_a)->mpa_tx.pkt_cnt =3D 0; \ + (_a)->mpa_tx.buf_len =3D 0; \ + (_a)->mpa_tx.ports =3D 0; \ + (_a)->mpa_tx.start_port =3D 0; \ +} while (0) + +/* SDIO Rx aggregation limit ? */ +#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) ({ \ + typeof(a) (_a) =3D a; \ + ((_a)->mpa_rx.pkt_cnt =3D=3D (_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) =3D a; \ + ((((_a)->mpa_rx.buf_len + (rx_len))) <=3D (_a)->mpa_rx.buf_size); \ + }) + +/* Reset SDIO Rx aggregation buffer parameters */ +#define MP_RX_AGGR_BUF_RESET(a) do { \ + typeof(a) (_a) =3D (a); \ + (_a)->mpa_rx.pkt_cnt =3D 0; \ + (_a)->mpa_rx.buf_len =3D 0; \ + (_a)->mpa_rx.ports =3D 0; \ + (_a)->mpa_rx.start_port =3D 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 *ada= pter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +/* .event_complete handler + */ +static inline int nxpwifi_sdio_event_complete(struct nxpwifi_adapter *adap= ter, + 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 =3D card->mp_end_port >> 1; + + if (((card->max_ports - card->mpa_rx.start_port) + + card->curr_rd_port) >=3D tmp) + return true; + } + + if ((card->curr_rd_port - card->mpa_rx.start_port) >=3D + (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 =3D card->mp_end_port >> 1; + + if (((card->max_ports - card->mpa_tx.start_port) + + card->curr_wr_port) >=3D tmp) + return true; + } + + if ((card->curr_wr_port - card->mpa_tx.start_port) >=3D + (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 +=3D rx_len; + + if (!card->mpa_rx.pkt_cnt) + card->mpa_rx.start_port =3D port; + + card->mpa_rx.ports |=3D (1 << port); + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] =3D rx_len; + card->mpa_rx.pkt_cnt++; +} +#endif /* _NXPWIFI_SDIO_H */ --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010066.outbound.protection.outlook.com [52.101.69.66]) (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 ABF5042846F; Wed, 4 Feb 2026 18:06:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.66 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228364; cv=fail; b=gMmulm5BlGg7J1wnQxqPQQYiZWb6+m8yrtBQmflkQ/JCPSyWSPsXhERXYMQJ6vsIuJMAQwUDGYEFIGjexs8ve4UrSh4vE5MPARnMf0N2jm1MJl5UuhFOJ8giGZ/6s3Yh1QbcSqnRaFQ/L/xSL+E85as+Hzyr4CzsBnuptUt/aro= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228364; c=relaxed/simple; bh=rjFAqmGAunq+A2rVobUf29rfxL8J2FaKZJh3XTvufAY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=r207TjxXhZ+CkLk6g5zi522bmIVHNFA0Mywjpa3iIT/vr/1poE2kpmwIIAtRKDlLizo81Tk+LkroNnLbNqEgRugfUTAfyM26FcClF57cTmuD4SUsv1lf2Av24kvc7PUnqVIIBZUEXo1E4u5bpqeikmqOmcV4SvlF2vpNxPlXSg0= 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=DIlPRvWU; arc=fail smtp.client-ip=52.101.69.66 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="DIlPRvWU" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Cxk+0DoliBmzDIZa88DzyTIgeGajeDwSN0pfh390LrpD3PKOr+6iwb35ole4p0nDHV1rDC6ymIPfH83vHX6p3heqehbSRFV/w5IIKHbJWvsNUcrMfg71zZ7SBOTv5V/N0upftbOe2zZCkoKXxoLGINitRhNc2KZ3luD92yMi5RCB9btiU3EjWtYzhvb99xK3DSQxqi1db0zeEypbWoMqrhAD21AeU4CXmK4svMMVIZ639HMplZKkpkzD+pcEMHdVrd4RAfPJJRuNoPPSDraQ/flUoC4Vb1scXiSEOTRH5j2cSGyRvXDBV64cC+Wcn1vYij2JMTZzx0qsMyXJEPeChg== 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=sziBrxCTUL3VebxBPrHbiNIG5zbwEbusRVwj6Nhm1Qc=; b=RmqM9edeDe2ZZ3I6nzUF4gnT+P7peRPP2bU5c/QzSo7DPxV2cnvTSlxoLnLPGX8vJ2TCUQV+JFjG/dWmBdBoYN/a2vWcGVGhMXd0H+TdVb8tD3cx09OjNRORxmryLy3YWKn6B+5T4VB/6pcWvBSPULbHKwxZQ5cv7oI4LDV7JrpXcnWBWtZYmM5mfzyoAomo9GAY+1RSPcGBTHe9bksaSl3/qCmg5M+uk3Y4bvf2S1TIzh10m1bG0r1SwneqGX8XgOFVfhXz+1nzLJzU3puZHm8hb+UVeNnWbFHVvk0tkJaJhyZv+S9MH40bcqQLiqxAjOVTL7Q+DkHDrRlrVinMIA== 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=sziBrxCTUL3VebxBPrHbiNIG5zbwEbusRVwj6Nhm1Qc=; b=DIlPRvWU+QV7JgZum3WR0JECBkGiBAmWXypY6ofkg+UWODJzZACtKUGeOQiShcxHo3WZ27bZmnYE1oYdOFW5d8SWGvBx4yOIokPaZ/f1+3LX1zFHzzSYJsq3FUw0mOJL082nWd/P05XJz3ll48X1o65xfeuKnnajz8pMUsxS9ycgDfD0vuRdUjie+h1DhKO4teqX6iPgw8sfq8bugsyxIjui1mwu4jXkctILDw19Stf8U3dLH2xzZDZf4rY15iDfkZUoi5notDjtKHNeMMCuvYzca4eJox2PbV6vjK+ptwfb/xenMtkrCIUMbitN3p4twX2pM+09vMm5D1hcyQB9kA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:06:01 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:06:01 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 20/21] wifi: nxpwifi: add Kconfig and Makefile for kernel integration Date: Thu, 5 Feb 2026 02:03:57 +0800 Message-Id: <20260204180358.632281-21-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 728d029b-f9fc-4069-d0c1-08de64180ddb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?m1FaU6cm98KvmGBBJPmuC6te7U6GNXWpuilOzWuHjlWaQYx+4J0Z6dQYxq61?= =?us-ascii?Q?bWKKmrmurG7sHGbIcQbLTmpkqartb01MykP+N/gcCo6P3dRk18bbPeigOWtA?= =?us-ascii?Q?HFVHWV9jCHae9VCIhL+c/szEBIKI2yy24zrbzydRE2RHCT+9TGNjn0oDA6SN?= =?us-ascii?Q?kUkzx7GJqvmobl+brJFonF1xtGAsjewTkTTWecEEvPoSteRaHwjNFUsM9fYr?= =?us-ascii?Q?tBhnRTmxE5mLt60sCbELpgEp2T5JuQMu4EeZ3GQix00gIiCZsVYJ1Chp5EC1?= =?us-ascii?Q?LVe6NVqwrRD+W1e2CBuOjNZ+TW61m5aWnePtpvPpEmZkJJDh4l0QZYZ+R6Gr?= =?us-ascii?Q?O6v1h8Wk5AXO5KFrleeKtKi/f3i4fHpOT5zVuk47PVE7YzCHzJA3s/+DwYEm?= =?us-ascii?Q?NIfF3HzS5G0jeVrSwPY//gGY2mrofgSEPx1Cw9fGsLhWOHnPuY+dMpPw6vj5?= =?us-ascii?Q?GfNBSQq80ODnI/ksRm+JaDBWEgLsXdKk93oAapodLwvVJ4hGNA2E8RcoKs8F?= =?us-ascii?Q?PT6Yr9pkN/Ctw0jfgMjji5dnYbfZcjaHbTMWfby373X8xLNo/qzbnS0sdSdw?= =?us-ascii?Q?JkhwCIgOFiLxSS3KOIYU5TYHYbF10GU11/empCyR6rGPM4vHo+qhfuxOHDVc?= =?us-ascii?Q?AFpnasrP3JKflRnN/h7Jr2yu7cqO70jjjptP0HMJJZZ7NYcI8JwuWcVI3KEO?= =?us-ascii?Q?Vo8KsXIm1XacYb6EGso8JKBUj3lghNoWDI+ssShBAAUwfz5bWKPfB2aX1+vm?= =?us-ascii?Q?94VC2kjNB3RTixZ7ofnKDD8+SZp4HSDUmh/f1FtLTH0hyIwOTJmOxawNazLT?= =?us-ascii?Q?gK5ncBev7lTQKST9EjxuuN/DKBLP7Y/jyrJ8UzKx5WDV5ZfU0GMVD01+WIGp?= =?us-ascii?Q?pIqSlg/95YJYY/pacn+cybm1xZjSb2VfYg+E9hNWgz/xHzURyijWdRG5ssXw?= =?us-ascii?Q?PIJWz4wb+GxpSUzfHh8P07S4DKDFC8s8YNdTjA6BdDpNEFhcMhb/C3MoMmv0?= =?us-ascii?Q?gJiRPizBzk1JNIyXptHCwN0zM6KAMIK9HhAZz3keFqtNiYAanA4EW2oXtt4v?= =?us-ascii?Q?2M0xx2BIby6ZNHNgJV9A9ajE0gvg2TN/nS888GIkhvAU0DGsc2Xql1Qizb7r?= =?us-ascii?Q?XE1Nh1bcSV/VaEWlDjwXcJLE7uJVR5JTvaA6Y8BChfIJnzxZB8L3W1oX2O/H?= =?us-ascii?Q?k6ZdJFc6mo649fzThi49xqSjJAu6Aix8CNsr25iNsgS1HLhPISpAim90MIV2?= =?us-ascii?Q?Znn4gLwALqvKXAxxaBkUw2TgO3Xf4kDrDcF3Rep+lUZGq9zUTLy+DqxPd61U?= =?us-ascii?Q?HVe4MJfOfcaIQ1XDa43MqsovgL/6Vs/Nc+vSaSfcnrOrMfHDlXiLtobF6pGi?= =?us-ascii?Q?U5MDjXWC3oNwLlYI9zoCRSuJC/LfdvSQNG5vcnaph4cB5rHCDD+dglAHTla7?= =?us-ascii?Q?MIqBmLz8H6WN2bO81BnSxa1Se1tWLGOW7SMEO36A0VaRM9EaBK/Ao+P8DpiL?= =?us-ascii?Q?sKZ6A1gOwU9RbcfnbZAnr5VT2bzKetehRXocy045kmnu7KaRKjNqEJwvaUvn?= =?us-ascii?Q?UIOiHU/s9sd5LqOUIKFVR89nCW41ljZPxha+WrcpzRzKbHs7DOaPInIdGyl9?= =?us-ascii?Q?nAI9tVj7TKwBhAEv8ympjdo=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?qYv6RlWEImp790lWEkmUh0P04aGmkjEDt5y69OS4uSXQChkKBcxTAMHhHi5D?= =?us-ascii?Q?byodh84FbEdAo/fuhCOp2DiRL7UDKlSuujsaLXT7G65phQWBWSjPvra5ANb2?= =?us-ascii?Q?MuAv+GQ4uCTM/ZdT/cHggIArqDbEOG7mtzQ0qMN7rfPdIKTEd7/gIDU7QWAg?= =?us-ascii?Q?/jqR7ElYt8svGgF9p8p4eDybhgTE16qOLSisoEFlBSg3OdHejKsqpNJ6qANz?= =?us-ascii?Q?RQCR1r2DOwmtDGhJ6ilJu4RC75WZRPl+0hIMaZicJgNAKE7VW+QwSWhcNu/r?= =?us-ascii?Q?oIK179bhoV6nIyzchkNwwrrgT9LVWeJ6wFZiJ2etk2xjxZtbe48/WGQ1Jpt7?= =?us-ascii?Q?s/s/jbJFdgzGeif8LRWhZRXjM4d1Zp/XVYeLaYjTP147TKTLd198crCEAG0j?= =?us-ascii?Q?6fzeHEArjGJbhcrtyuVG/ytmmhK+B/hekQbZ1TU67Vv3HWgZvF+fFuvS1tLJ?= =?us-ascii?Q?AGzt45oMqKrSri1reJCQeOhArYs4f+Wrsb0uXL+OFYCqyEQf9Q+i/MgWcAIy?= =?us-ascii?Q?eItVtR974wQpSwLfN/bOJyv3KvT373qmNSnWBdrByBVKUgGuGblD6i/fgk7D?= =?us-ascii?Q?VTDtPAlnSpKINegS065+/5SJ4bv+GOWvu2xtG03U0yPkx81ms9lw6Fc6DJzg?= =?us-ascii?Q?friabeJvwlxUQu4nZ0am6/IXdHqddZwLVcrzdOiqtthZ3UGq3UaI/PBX3QJx?= =?us-ascii?Q?tKuKnKVRl6GT5+kr5E5EpXXLLkGKqR/Eo5o4G49XHnP7yP3ro+uxqGwT69dP?= =?us-ascii?Q?zx/E4ZfeFtgH9BJp2318YHsmHpRwnxrtyhOfY/tbjGacrt+0FP8Ppj2LBCqf?= =?us-ascii?Q?e7JG2zXHTjMTSR7JXyadvR7hMdt3HBEO6ZrrIUE8UIDn//r5LhMCtu44A8zy?= =?us-ascii?Q?cBoCOXnr80xu/vnjj0Fk1Y6UW3LavP8NcHjAF5fLRlKWNx2MViJc0ZPVVoH0?= =?us-ascii?Q?lbe7jtCkt+1od4gU7NgPVIAuKEZMDNFva3gY3NMBZihod7HwO4S5tDPhjFQf?= =?us-ascii?Q?pkM3LKKtZcmvMZanvPGwcfMspuX+taAp5amvD/D0uosE6EmdkRKv8OXfc9su?= =?us-ascii?Q?aKX3ESlKUyqT9eeNkji3ilE6571JYh5wVM2QiDu8tKQTZdNE+rrnjkaTY+cW?= =?us-ascii?Q?jlIZfe6H9Fn21Ckf9SE+j08r3VJCEvOmyjJ9/ko1+zfYc6UNBBDp6yck8sNU?= =?us-ascii?Q?4rgT4Db1kbWzmgBGynq1bpnoRbDC8E8P7c5kcmMg5RTm1MkGSV56yp2kbDKI?= =?us-ascii?Q?zpT+oGZL79xnJvBsrQF7Ih5seUSJhDDI4rbwZ8xLfJYP1cKCg3zh+dbGK7LF?= =?us-ascii?Q?9qtdKEmMfw2GEO/GfejRGFkApnctai16mtTUUE2nRYEU/GJoTfgEXv9Toqgz?= =?us-ascii?Q?0Ed4I9gYByJDe3ao9+WICEK0TChfuYutaROaLG0utnYvouDqxYczI9NxHfPJ?= =?us-ascii?Q?jT45xhBhtVS5TBuNm4OR5P5eKq7c0EJcxdu6R5O+yHZJdcUyexWPUJZmALrn?= =?us-ascii?Q?nYpg7HzbOlnDaKXLcKimKdfBjP4YwGacYT0A6fGnf+PJKytxMAUmxOErgDY3?= =?us-ascii?Q?X9wZDg4QOxQDzRcaeeUdphmlBEfL6XCKbhXYRXSmjNF9P1uabmfgEFEUVJpl?= =?us-ascii?Q?gx695XOlX1CdcNn82F20HO1UvNZMd5EMhILnDtaE9tzbsN0O+UYjHEMoBLRg?= =?us-ascii?Q?P8R2ABba1f0YZ4xdAnNixVCusjIShSvpCK8CGFKT4bRfyW2vmfkzwtpnzXIu?= =?us-ascii?Q?yOkYoppy+A=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 728d029b-f9fc-4069-d0c1-08de64180ddb X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:06:00.9852 (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: sjO14VvGt9+g69q9biDGND4AIUyOTfU3oF4O6mB6GVZ0j3N+Pmju8f54BrO9amdktTMiESQL1L6IMzfJa9oOOQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" Introduce Kconfig and Makefile entries to integrate the nxpwifi driver into the kernel build system. This allows the driver to be configured and compiled as part of the kernel tree. Changes include: - Adding WLAN_VENDOR_NXP entry under drivers/net/wireless - Defining NXPWIFI and NXPWIFI_SDIO config options - Creating Makefiles for nxp/ and nxpwifi/ directories - Registering nxpwifi and nxpwifi_sdio as build targets Signed-off-by: Jeff Chen --- 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) +=3D intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) +=3D marvell/ obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) +=3D mediatek/ obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) +=3D microchip/ +obj-$(CONFIG_WLAN_VENDOR_NXP) +=3D nxp/ obj-$(CONFIG_WLAN_VENDOR_PURELIFI) +=3D purelifi/ obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) +=3D quantenna/ obj-$(CONFIG_WLAN_VENDOR_RALINK) +=3D ralink/ diff --git a/drivers/net/wireless/nxp/Kconfig b/drivers/net/wireless/nxp/Kc= onfig 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/M= akefile 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) +=3D nxpwifi/ diff --git a/drivers/net/wireless/nxp/nxpwifi/Kconfig b/drivers/net/wireles= s/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/wirele= ss/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 +=3D main.o +nxpwifi-y +=3D init.o +nxpwifi-y +=3D cfp.o +nxpwifi-y +=3D cmdevt.o +nxpwifi-y +=3D util.o +nxpwifi-y +=3D txrx.o +nxpwifi-y +=3D wmm.o +nxpwifi-y +=3D 11n.o +nxpwifi-y +=3D 11ac.o +nxpwifi-y +=3D 11ax.o +nxpwifi-y +=3D 11n_aggr.o +nxpwifi-y +=3D 11n_rxreorder.o +nxpwifi-y +=3D scan.o +nxpwifi-y +=3D join.o +nxpwifi-y +=3D sta_cfg.o +nxpwifi-y +=3D sta_cmd.o +nxpwifi-y +=3D uap_cmd.o +nxpwifi-y +=3D ie.o +nxpwifi-y +=3D sta_event.o +nxpwifi-y +=3D uap_event.o +nxpwifi-y +=3D sta_tx.o +nxpwifi-y +=3D sta_rx.o +nxpwifi-y +=3D uap_txrx.o +nxpwifi-y +=3D cfg80211.o +nxpwifi-y +=3D ethtool.o +nxpwifi-y +=3D 11h.o +nxpwifi-$(CONFIG_DEBUG_FS) +=3D debugfs.o +obj-$(CONFIG_NXPWIFI) +=3D nxpwifi.o + +nxpwifi_sdio-y +=3D sdio.o +obj-$(CONFIG_NXPWIFI_SDIO) +=3D nxpwifi_sdio.o + +ccflags-y +=3D -D__CHECK_ENDIAN --=20 2.34.1 From nobody Sat Feb 7 06:20:54 2026 Received: from AM0PR83CU005.outbound.protection.outlook.com (mail-westeuropeazon11010010.outbound.protection.outlook.com [52.101.69.10]) (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 93F7D436373; Wed, 4 Feb 2026 18:06:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.69.10 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228366; cv=fail; b=u/o1mPYdOoXUI/XZL+IeYdxBwk0vQYfD08TFlDlzWiAyyRTluZklm+/eGdq3NBkwJE6itwgbTzGqkhCLhUViR7IJvlfhlueZz/gE475VLPBvWdhLiv8lapn10uZ07UpfBQGf2ziH+gyKmcEDOxhTx2695+RlxHoGGvZihY8qjzg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770228366; c=relaxed/simple; bh=9eJCmduRaCTiTdZ4+1e7D2VE2W2yQKH3F4JK/mE1ZWs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=hLONeo9wc0TqxFYhk53s/mmjN6ueU9/Cj+ZXZn8WZYX8glIuiO2Xr+tHQ5V8umG4/tEY2Vfw6XkEv5914Dj0lUhUqDX1Q7qXIgTCy+FM/MxclBoHIEAm1yp1lT/HOGbH68xM/XGxtVmC8qYp2ChQj18TXX+KaJKoUzbXt0O2mfA= 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=G9D/zXhY; arc=fail smtp.client-ip=52.101.69.10 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="G9D/zXhY" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=R2kU7nkQUKLgo897h4PDamf9DwydOzW+3//qUv9IePgvTU/0qf1ylHppYzzHTnnmNGWZ1/6uBy/985SJ/jba3SSeQIB20HICQUxLbJy/RRQM5u06HPge9P8TKJuWGuC8Gx9+9i9rAaDe4xnDGJL6JvXn2zlFi/7CyWhVFSv1Rl/dd8acY+QV3ITTb6sQJ2cAQkn1E9JX1FEnh3bIa4WLcKtWbmdo9v6p4TAmWJPw7x2M3K/J2itpjWjPssSZUuKMf/9I8a0/xTy86Szl6aMhP4ZMH31ms83cyEv/qWhZ4BEMC3nW/2w367J5rWY08kbFqskVMcBR+89CC/pznJ4uJQ== 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=pTRbrXRLrhpCjjK0Ju+yUfsytWF5c6IoeQXDLU90rc4=; b=dCRck8CE31vYzZoVkYGLNjTJBJJ4ey8KIZi1BCmcIffg4/DjJ2EPshWWHk8w1qGysETxGmFsJ6EiSnB/JoUkxPTePvAFofisAAACVYIpAuE+ymF7O6yoh7j9hwlnje1G2FAcIhinGG9Ho0oy0RD/eJIv4iZc2iNWW+boMaMBqb9PesdPjuBUzBqNBxeGtM6C+KrKuqljMT8tELZ8Hk5+w1X695sATpUAwQxo02mAy0FUTXUO4XMmpIef/+22p+zIkyFg3YwqAiMRFyHf7HLIxbN0UAR+6iomDxZDDF6oMK2Pcz1O10ODX3nqrn0SvjNP8e3JFlG8utGSvywdosNfVw== 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=pTRbrXRLrhpCjjK0Ju+yUfsytWF5c6IoeQXDLU90rc4=; b=G9D/zXhYP8IjSaa9MHDrqi/DBbwwINPyb8S+as6DekHKDyRaF/UDWmuyHP8xZ7J36/f0G8I/Cf2AtKC/HMHDq0zeNuMO9V6sDGroz5TYDyt1RTyVhYibpaNtG8y4DKIoV5+lDC43w3ttXAaiGnqmdtxi06mF11Gz9ZAztk6lm9ywxSZiMTykRrexYnnkbuWnXTQUVmxFccop35kFXe6lYm1jmVbZsGKJvNq9OJI6RYm5YrBKkDC5UEqt0Joualif++XRc13+jpyERM1pZJmuf8An2Ln4czOywbxLPdblkcfiFtSz6GXKuceQysMOfPyclQhJsPxlYj6JWHlts6wRCA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) by GV2PR04MB11980.eurprd04.prod.outlook.com (2603:10a6:150:2f3::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9587.12; Wed, 4 Feb 2026 18:06:03 +0000 Received: from PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b]) by PAXPR04MB9255.eurprd04.prod.outlook.com ([fe80::1eb5:3ebc:9f11:f20b%4]) with mapi id 15.20.9564.016; Wed, 4 Feb 2026 18:06:03 +0000 From: Jeff Chen To: linux-wireless@vger.kernel.org Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org, johannes@sipsolutions.net, francesco@dolcini.it, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v9 21/21] wifi: nxpwifi: add MAINTAINERS entry for nxpwifi driver Date: Thu, 5 Feb 2026 02:03:58 +0800 Message-Id: <20260204180358.632281-22-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260204180358.632281-1-jeff.chen_1@nxp.com> References: <20260204180358.632281-1-jeff.chen_1@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SI2P153CA0015.APCP153.PROD.OUTLOOK.COM (2603:1096:4:140::21) To PAXPR04MB9255.eurprd04.prod.outlook.com (2603:10a6:102:2bb::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9255:EE_|GV2PR04MB11980:EE_ X-MS-Office365-Filtering-Correlation-Id: 602d1703-a1f0-4fdf-c4e5-08de64180f3e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|1800799024|52116014|376014|19092799006|38350700014|7053199007; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?L5n6gnYQVXSbreUFVe+fezVizAxTbNqs6wdmlnXW8RtNFJB5KUdz7dv/bDId?= =?us-ascii?Q?GS+GDuweVYlJF4O9gYZGbzE8QR7iVvSsi/2c7dc7W0f9wve4DxfG7M69TIJz?= =?us-ascii?Q?bNX8jgJFMTOGtUTqw439SDwc+RWd3PNBFLbdN+JsU2S6OK9wbt9Ic1UhNj/r?= =?us-ascii?Q?0THHah5yJkvUAVqQe/kMJ72lSamDIXF6xzxncKcPoHF9AKF+v6Z6FOn2eCZj?= =?us-ascii?Q?Xz7KEG7VYM9cIaiklZ2xJ2oampL1keaOqJztMcwBylb5o6Q0SD8ieA1U+W4e?= =?us-ascii?Q?LDEsVRGeH917JJ16X8tEsk/cOfCRWMpKLuydLypVFdl0sIhfaoUmhwXpUaMR?= =?us-ascii?Q?Vnu3gd9yhkfw3Fm0wW/lTdTVnIGu8OuVL2/5KgSa0V1JMX7Bwc5EtcSDsHeZ?= =?us-ascii?Q?he/kzjE6MnnO3iUBrR4mL1EqmTqEk2N6d6HSKBA10oMwGLNHTzpug7fE+s4I?= =?us-ascii?Q?oZllMLuinQHo/3MtvVt5wpWrd20LVf/tfUvcTnpoQwl3DwkAMoNwR/jGxeEV?= =?us-ascii?Q?oeWHPQRmtefZ72r57/sjB3ulFuyOD/R+rjpQ4cIizMVYEbFJjQyyqh/qplFa?= =?us-ascii?Q?Wfv29/Czu6q9Mng2RDMcmJsac8ZAuAkDOox1VB2qSscYxOKwxgCpDkaU98oa?= =?us-ascii?Q?yR3GaszUpxGEasFmixhm2dDtTzkmK0qsLPxrNTfYp8WlyGVre3j+wVeSF5EA?= =?us-ascii?Q?rp4k8sIGz16Lcv6PuS73F1bxd388LJtjhcm+nwoOpv4HUwOdf6QfGcP0R8BF?= =?us-ascii?Q?Qgq8bRRoCFZo+TfARtCcEmQ1pIj6zKvE91N9b3tqLYjaBeSEDzrfQGnMyN5i?= =?us-ascii?Q?SnnmbyvGGthZp7+SMC0rwtOAXIg3grbwwN1QbvYaLd21PgE1CdOA5FFzH0Sa?= =?us-ascii?Q?wpkq3jotLJWJ6GV4T1aHC5PlUPRWgvvcz11ldGBv4Apx0+qE/sUcE7tXnfgX?= =?us-ascii?Q?I6r7QVvz8HW973QSIgN60WbKbsoyM/DRll5dDiKXiIsGsd0Ily7aBBet7B/v?= =?us-ascii?Q?+XRxtk9CMeLa9lKT91w2lRCmSo9v2sf5p4V9HqpcR2SAr6QRaLM/JlMK5geS?= =?us-ascii?Q?Hd2nx6rcUiXdPh765XBkfzteELrndo8NcXGYONmYDWgGvf9t1HUlb7HvApxk?= =?us-ascii?Q?OE9SpDlU6dJeLGcksDNLdJ+mqJ3Vr6GGZF0fNmXJpxPz5cF2YULhrOpdntwm?= =?us-ascii?Q?bpU8ZFO8ybNTlU/1AGOHbJX+AugSGFQr6yZQ/iI22cMUj9IVWzePqrCGtOVm?= =?us-ascii?Q?VNi6XRs0puBeq9chheRLWVGRSAlxZwLYP247fytymaahO0AGaV+7aBxM0Duh?= =?us-ascii?Q?Aoto6qo+K9fhpzhC/kS9PoinIL/AKpfORC868v2OF9/nTAuSbP47cbKkhl5d?= =?us-ascii?Q?WC/WdOrEBaqCGwAxj2aTw50rkXy9NjjaI92cv12G0LeczkPLwW7cW9oZ6wf9?= =?us-ascii?Q?mKfR1vHay2N7eFAFznTL70o13zha7+Z9jxfQWqBUXbR5DeHknPa+OBs7IMkz?= =?us-ascii?Q?4wt7edOPVCXke0CWPS81fi2tXpwGlRqlniZqAik8ElAcnXMxDEQotlPy3bLz?= =?us-ascii?Q?ORv0RwgoUIku0Vusw6VBRWKoGUpcSHdahBgHn2kbKww3W/YwA884W0lJFkdS?= =?us-ascii?Q?XBIdVa968ILrh9JbwJmtnD8=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9255.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(52116014)(376014)(19092799006)(38350700014)(7053199007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?oAMIXQhRXCDWQaFbUZqUfL5zNx7tvvHhn4vSrtmfwKaaw1ssGBo1zZc3jjlQ?= =?us-ascii?Q?wSkHeR6TJszBHC4BD1LMVu4dhoGGdzhqvEgiQI+VRYP5q+CtPZk4rx0Ne1y/?= =?us-ascii?Q?opdgRphI1zy2ISA8TYGxzUHVibfOcrj9rZDIS7DGt8Sm44RMjGAMkooNdpr1?= =?us-ascii?Q?qydj1UsiGnTDMQBDg1IC8o8QtrrvGpczO73j7WWWKer7P/MQYDtooD8LPK3g?= =?us-ascii?Q?ilexr3iged3o2gz7dKpz2SjRIn3XBbXBjAqWHyhRiG61wTkcTQ88B6l8MqOI?= =?us-ascii?Q?nxpjUTA95/6kM6PZayP+JY0hHeJ2BbsV2LSNYmk2TZP9sAbCsrtvuMvIbKnU?= =?us-ascii?Q?bREC1/jrVSmqueBOVMd2tS6zly4sXJN/imT76MhvGUaKNzlJzotWQV4cQfxz?= =?us-ascii?Q?sskrJ7aGDzSA8czU4S0gV3boCwYtIGXQf0kcmRXqwsPsPtqkP5QAcD+qFYkI?= =?us-ascii?Q?/57LPjibkLZmR8V7U57zJ6T3ys4bUSH6WpH+QjEzxaKJgEw2dh0k5KG4Qz9M?= =?us-ascii?Q?uG6ff4GhZ3+Q1pclBkmc5/7QgQ4h4WPMHBsw9L3wu0eopzHyAbiDvEJKKJRw?= =?us-ascii?Q?4jnYNV8qne0OOnHRtiicNXNLpKgv59p2QXdkbporw+k2XyPjxmNnNkflIrbL?= =?us-ascii?Q?PvcoCoezEzebZN++b1B7kUhEJWSSUQnULtv/rbydIHUqqvO9E6QC2NCJd2Yu?= =?us-ascii?Q?0713i7hCNEHlS63LLaTAU7MGLoGM/PMd9Gp4+T9qUOXE3zuXZJ0/siCaY77k?= =?us-ascii?Q?dDxz9bYq0m08g0kiresXM2CKQZP9RbD0b1CBHoWQ2esEXm9DqFcNDg0PHP/H?= =?us-ascii?Q?Vu/LOpA/B/6lEq+GO4w2+CVZ48tMoAB8bQP4VnvonXUc7jFyXRM4xGeiQhWf?= =?us-ascii?Q?SeppEXmhd9+sVut3tbGPtR7lSXp+hy0ECR23x46+BdwI2ug8jcueJHMyFU12?= =?us-ascii?Q?Bc8oZyEkAL8o0lce3JRFDNb8J175DwDMsV+wdfpfGhS+QtfpM7rAb/KH89ph?= =?us-ascii?Q?zc4twArnIeLlg106wxDvImoB+HuVIbzpxhE1VArTmwU5IY26JEh07Yry0DPw?= =?us-ascii?Q?QQ9x1jxm+zZiQo93eIVNPcEjryEP8donWsz8P4gTFnM+ruM2lnOsJ70OxLoR?= =?us-ascii?Q?4oKSGQxSFSXQR6DBU53ptT+cfhurIo+BMwiMtyJhY798WlPrywLpKHonezzS?= =?us-ascii?Q?4IIH8RxiX0oHWkU6WiAZ7UdVCJPwLBC1Rf4NE+MVM0rpu6BTDxGEvv4t52M7?= =?us-ascii?Q?FiRIkHdh981v2QEWH7D/I1bO/qrC7hnuwaXesdZ8fZ3g0uXEXWY9m58uHWv3?= =?us-ascii?Q?B/2hAyNqQ+LsdEombWapYShL2jktQO5fBGteW7l5msHxGt/v5l0Ta1G4NxGE?= =?us-ascii?Q?P2xJ/oh/IffzJqhQiMzlr79LC1h/zzHI9snz/L11gp6zUaIopWQfmvDAsGBQ?= =?us-ascii?Q?5Zrer97PtJ1j0yC552uMeiPlzyrXbWKtvZNPQmN2/CY9ssEvKCjOySHd6bYC?= =?us-ascii?Q?pmu13goJgYC8H8F12AQDDMV+DBXCB+ToQSLpQpJnIHCptojcDLrM6EpuBBMH?= =?us-ascii?Q?fB7+QsQAYVmq3/CpWsRrIKvYtyBHqwKqkeZ7oDL1ilDoKFpJBxHke/96D/K3?= =?us-ascii?Q?cc6nEiz/d/tQ951j6eV+OtNkH/y6Bx4rC0Q2n4L2hWSF/umdS2OGL75vhvNd?= =?us-ascii?Q?GLq5af+jdrwzvnRDREP+o32RO5kkxsRH7yw22zm8j5RZvLYvxJJZLx838y/g?= =?us-ascii?Q?nj9kzbDyCg=3D=3D?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 602d1703-a1f0-4fdf-c4e5-08de64180f3e X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Feb 2026 18:06:03.3924 (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: v9eqiTTSZOG5g6gruwOtdCg0yIlx/Zb6hMZ6BJjRAPTk4Wz59gQyi+Am5Uddk+BwuS6FnDp/ydy2sPT3Fj06Vg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: GV2PR04MB11980 Content-Type: text/plain; charset="utf-8" Add a new section to the MAINTAINERS file for the nxpwifi driver, including primary maintainer, reviewers, mailing list, and file path patterns. This ensures proper tracking, patch routing, and community visibility for the NXP Wi-Fi SDIO driver. Signed-off-by: Jeff Chen --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0caa8aee5840..d43fea4fe79c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18919,6 +18919,13 @@ F: drivers/power/supply/pf1550-charger.c F: drivers/regulator/pf1550-regulator.c F: include/linux/mfd/pf1550.h =20 +NXP NXPWIFI WIRELESS DRIVER +M: Jeff Chen +R: Francesco Dolcini +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 --=20 2.34.1