From nobody Fri Dec 19 17:54:41 2025 Received: from PA4PR04CU001.outbound.protection.outlook.com (mail-francecentralazon11013041.outbound.protection.outlook.com [40.107.162.41]) (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 D05942F60CD; Fri, 5 Dec 2025 06:58:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.162.41 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764917887; cv=fail; b=BNUNEUm3eHhYgl5nsQL/ZJgAkemMotOp8PlE06Pu1UVyXdE+ZN9ZiOh0R5DTU5LBHjegaqjNJNICwneUAr8Q+JAW/Cmz4YuKSX0S8IP592+3CVVZLvAXVvmVrSvaUuXRn4zTEx9DinZoNVoy9orQLCwE2fb8Mtt9om2A46MPv/o= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764917887; c=relaxed/simple; bh=AGQxJIgyewBgxAIzzjs24SFbXcqBXSIAw9UL+NT6yjw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=b7ZfwcuTcW1DDYCqxFhvM3g1OeRndlRwp2K0RQ1KKpPhwQ4RKUyl31CmCq/8OL4vb0TwseIlRrxNODAF+S+eKLaikhjX4R4+OLBOZl6/GdyK+kZ5UeCb2HvhebR/u2tMf5Br+KpXRDm1iMu6Qwk2dxyW2SvA1Kd4PZpV5XjIIQs= 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=J55cQXEx; arc=fail smtp.client-ip=40.107.162.41 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="J55cQXEx" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=E9iU+42mw2fOZdzJpt8PSEQu3wdN+BXyI0W1LHPH4VfvvMseOp07MEf9cCUx1KMFR7U3rglxaHlPODtG8pSFRMd4VnRvzTqUzyqcjxq7I7F5M6GdirKa0DEhNExf9PrrasxA1COFTysLtyBOKUWvdnsoYOiW6B9jybtBFzCY9ijxvMpaspBvGEOq+XL90M2kaQajdaP6vC1g5jnx5KS5Uhz9/OvXC1Ky9OND5rNkdH1fS488xmz1qUH43+DIqanc3+x7XqvVgDb/MRpG08cMAXV8KiMm9Iea3AX2omnIA6CcF3SME1M93LMKPGfGVQq6DWNtqphs7GzBlEz4STCWCQ== 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=LxQ5aaRvN5d1cRPziWCcpqw3lS+E9J5dcaA3w0OFvic=; b=lMZpkUFW2JB4Fh4yjvJCTxwHHhpvArvcfZIhJJwZComx0qbv9nQpHdXaeE0EhLp5ksl5/OJphy5GosA4uoagfIipGWulCL9up9PLdixRzDo+1j/ekhrbr5O/vNDdkkv2OVGxc8RQP+sufCXbanCun1xNytm3Eh27MNeM19XZJWXPmDrhNf5Ae8ejmgiFcWV9EcSnRQz220d/svZFC5s8AIcYYz9x88l+qhPREnR7lc2bcqEouzC4UWkPpNxPwR8Jc6EdzQUVPmdTEhKsVWvN8PngXK1N9n3MCKk+RTznOwFhlxEWGvIfm9jlDDsuwcbvMnUoaUZN4tx2W/vQAiCL7A== 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=LxQ5aaRvN5d1cRPziWCcpqw3lS+E9J5dcaA3w0OFvic=; b=J55cQXExkk7thV6lyHZudEhWrjkwi2BSI2smOWqbcfxB9J34n46D7VbQ5oUbpgDLiHS+pKYge18yNdcoJ77BzTSI51Ccb39Elp/W2Or99AuOeXDRE4mD0KNCK+CQmvnb1xZZWVbreVkBM3yyE3hdwn66cLfU2xjyien/d1vvXUyQKJhmWVzPId8IdIVk4a8Pged5uxQc+VlYFw7fCiV5fntKSB+Dl9hdAhrcKOCKCaOriirYs16di+yey+SFZ31YGdsXDZNugCK16C2mDL6SpfmMBxKQ12DM8oEAl0NMrjJdq23DAMvuuXelQSLx0IUYpNPQp/0L+S6x1FUi3fVrDw== 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 PAXPR04MB8144.eurprd04.prod.outlook.com (2603:10a6:102:1cc::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9388.9; Fri, 5 Dec 2025 06:57: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%2]) with mapi id 15.20.9388.009; Fri, 5 Dec 2025 06:57: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, tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de, Jeff Chen Subject: [PATCH v8 16/22] wifi: nxpwifi: add utility and IE handling support Date: Fri, 5 Dec 2025 14:55:39 +0800 Message-Id: <20251205065545.3325032-17-jeff.chen_1@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251205065545.3325032-1-jeff.chen_1@nxp.com> References: <20251205065545.3325032-1-jeff.chen_1@nxp.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: AS4P190CA0022.EURP190.PROD.OUTLOOK.COM (2603:10a6:20b:5d0::14) 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_|PAXPR04MB8144:EE_ X-MS-Office365-Filtering-Correlation-Id: 3f8cc1a3-841a-4ae0-e975-08de33cb9aea X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|19092799006|1800799024|52116014|366016|376014|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?B?emtydVV3ZkFnang1amV0L1NyaWs0WkRON25sR1Q2WWFmdFlZL24xK05FTzVZ?= =?utf-8?B?cjNNVkR4WTJJakxMc0RGYUQ2OGt0R3dMUTBWeHZVVkx5dDFZNUlCSWdraUIx?= =?utf-8?B?N3VZRHhiQTZqYzI0U25rei9QUEdDS29DRllydk5CUzB1VlQ1TXhsTmRKcm9I?= =?utf-8?B?RmpuY2VkQko0enJESWVVL2JpRDFISTd0eXFvbjJHVUtwMGRxU3dXZGVEMmJY?= =?utf-8?B?SjRCNSs2Vm53MlZnTi9ZWkFaWnBnVUdPSE15cGEzUlBuRTFuWXBMS2xhL2dq?= =?utf-8?B?bGh4OEpsaTN4TDhFNjYySlFha2xITjNEbmd3Vnc4eTAzNjZnV0Jjc1JuekJM?= =?utf-8?B?TFIzR1VodzhHMDFaWnlCaUpFTUFuaG1ONUJ5VmREQXhXSVp3RjUwY1BvQ202?= =?utf-8?B?UDYzYWZmOFZpV1VEQnBHYSsvcmJDRzlMRFk1UHVNelJkNnd1QUxBWVd3d3lB?= =?utf-8?B?czlZaTZPUm1mYlZoTnk4Mm41Kzh1ZmZHQ3NuUUVmMXpVWHNFRGVidnVzRHRE?= =?utf-8?B?VHlpK01HVUV5M0U4K0RzMGFGU2dTZ3NzeDI1ckE0WnNlTmVwTklDSStUcEdt?= =?utf-8?B?Z3lGQWo4bDFhTWU5cUd6R0JCUjhSM21kWFpkM2huanAxVVVrNW83cEVDSXN1?= =?utf-8?B?TzFHY2lXY3kwK2ZUTWVZRXBPeUVBbnJSUVVHSUl4WDBrY3FuWXJQR3NGbmx3?= =?utf-8?B?aGtsdWNjTytKMCs0TmhidVB6NTJpNm15MGpMMEJiSFRZUG1Uaks4NDNrTXNX?= =?utf-8?B?T0pqcG95S2VSc3lNN2s0ZlNMRC9jSndLRFh1V2s0OHVzSlhpK2FIa3JjY08r?= =?utf-8?B?eG9PanZCaGNrSE9ZTVRKNU9VRmNqZmxFVWhobmJySDZvRm9mUm9zQjR3YmhJ?= =?utf-8?B?djAvN0UxSWpNcHhzV0tueDgrOEwvQzhqT0htbmlJdHhvQXFya1p1NFpLTGNF?= =?utf-8?B?b0xOMUU0cUVkWmNZQTRtSC9BUW9nYmYzQ3JUUHNUc3cyVjF5RnJtaXEySFVP?= =?utf-8?B?eHZjOUJNWEwvN1pTdmRQZXFxRnROcGtTR1hkUXdqUWlHdURLdmRFeXUrZ0RS?= =?utf-8?B?NHdKYmxucVhGRTA2aXIvekcyQ3NVVTNaczl4dk9DVytWTDJhcjNNOGJGWFNT?= =?utf-8?B?Sis1bmVqRXFWUUdFbFJ3SlNsUllQbE0rTzRJMWJQL2R5MEY5ZTA3N01FcnhF?= =?utf-8?B?bXVxMmlrYUZabGZJTWtRQU5ZSWNEenFhanBZTnNrYmVXR0pQbDMrNU50MTUy?= =?utf-8?B?a3RaM2dsY1FNVGtjdWtEeEwwYnJUcXNMZytjTnhzOWVHZUsvdWhUMVZ2andU?= =?utf-8?B?MUNLdm9aWGxIZU9QQ3VXOCt1RG1GUUFUUG1HOEE5YXM0aHcxc29idk1Ga292?= =?utf-8?B?bUZsR3FJU0crY2g4ZmJZN0hndk5kWVBvQTQ1Uy9zMkhiNEI5Z0U5d1VvcWxF?= =?utf-8?B?YktlWEhmNUxaanpIQ3RPQ2J6b01GVENISSs5NnN1cy8wbGZnQllTdnYxT0xK?= =?utf-8?B?MUhwK1JKMks1TUFhUVFPRGlTU1dSNk1MVS9qQmcwOER3enE3dmpwb1loM2I1?= =?utf-8?B?bXFFMzNWcytibDQ2WWV5ZjEvNnZJbkZiV3Mrd2R0VEFnRGF4QTROWW1PSWNX?= =?utf-8?B?a01iUTcrQTJmZnpBUUdPUFdndHVXVmozTVl3U3d1R0xxWlR3YjA4OFliQmxV?= =?utf-8?B?YVg0TDhCbjRsb3NrNWpPZE5CdXpxbjZDS3JqT3JEMkhiakJNYTcyQkx3eXEr?= =?utf-8?B?WkppamVINnRweW40UmU5OHlaVzR3MS85Y0YybkxZbjR3cTJkUXJHSkYrQzdE?= =?utf-8?B?bVQzU3BHd3k3YndiTjdvQXRzS0dIRlFRNmRBTjVYOWR6aFQyVGxVWk4yR0Zn?= =?utf-8?B?WlV6b2JPQTdPQ2MxdGFRN0lYK0lpUnUyQ1d4c2dXUUgwWkdHdjhMaVZreEdy?= =?utf-8?B?OVlpUTRFa0p4L2tYNHpWbG9YRWJJNVpqZUVFRHpPcUxEaEZULzA4cWUyamY3?= =?utf-8?B?ZkVDYlRNRXdITTRodDN0a1lKdlZ3VkZEZGcwQjlUSkYzdnBWVUMvSlQvMnpo?= =?utf-8?Q?ekYPXL?= 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)(19092799006)(1800799024)(52116014)(366016)(376014)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?QTFibENlNUlqZlpPbEp0UmpmZDFzS2JYdHg3Q2ZJd0hxZDI5UUlNUG9wZmt4?= =?utf-8?B?R3N4SlZlK2k0TldMR2JNUU1TTjBKZzdXMGVNUHcxL0tnUGJybzBDTmJpQmVJ?= =?utf-8?B?NTByajR6VEFlSmFWQWRHbGhRV3RsNlNtS0R1YUF2ZjE4bUxzQUlQc2FiRGlJ?= =?utf-8?B?Tzcyc2VoVHdZVW1DR1BIdUZ3VllhTmd3Y3ltbmRiR1ZaZkdMREZxVG1YTkF5?= =?utf-8?B?T0hod1BqYWlKREJEbkdsMUJ3MXVSNVZtb05tSEhESndQOWIzM2ZxMnI2a1hk?= =?utf-8?B?VnE1S2NObDBIZlJPOVQyN1U1ZHpRcGhXRWNTRzRyTzU1eU9laEtlT0tVS01V?= =?utf-8?B?QXI4T2dVWjRqYVlYbk9mNHAzNHBRcTJDbzlRUUZVbzhXaVN5MVdFOVZneHgz?= =?utf-8?B?b0hiV1dYREE4a1FTM1p2d290ZVIrMy9vYnNPUzJNWWJkNmxQanE0VzBBMXh4?= =?utf-8?B?S3lFQ3pWU2d2UG1MRHl0c0Q0MVFMTEk5cmVUQTgzeU1oQWNha0s3N3FyclFv?= =?utf-8?B?aXZuV2Nva3A0ZEtUNWZsQkwwakczQm9zaDJocFRNMm93c1kyNDJDVngwSW5P?= =?utf-8?B?OXJacHEyZHNDY1RtNVcwMjF5eVZvREN2S1M4Q2cwOW54ak5QUUtSMFhuOTZO?= =?utf-8?B?NzZkNXJNOWNCSnVNUHNIZnBWNm1xTTB2YmtNK2VVTjQwaDloWHovbS84bW8w?= =?utf-8?B?LytkUGg0OFZSY3EvSDNhbDZueWVycjVUbzllQWVWOEZ0VTE5TFIvNTd3akJa?= =?utf-8?B?QWkrNEVQY0pSNW9mcDZMbVExbTNOSi9mNXJRQXdpdHNwOGR5bEdDWmFhK2F4?= =?utf-8?B?ZzROdjVlYW8yUmI3RFF3ZGJoMjVpQ0pXajhla2dabDRSbE16VDBaeExoNTR0?= =?utf-8?B?M3BDLzA4WVlMUnZNL3M1bE5ublBFSjlkclJaRnpETncrREFwVGxpRUZXSFEr?= =?utf-8?B?cUZuYUpPZ3lIMjNIN3hiVnhwNlJ4eWpvOENVdHlQNzhia3paRkx5RUNsZ0VK?= =?utf-8?B?d3MwdzRKMVVXZkYzVTVRQmh6M1g1ZXJHQTJ3ZWRETHMwVXprL05xZktrRTJ6?= =?utf-8?B?QzNUc0xENlhIYlVxMjBpUjQ1eEpYSk52UWpabzZYT0taK09pdjdxMUtBMnFR?= =?utf-8?B?dCtMc3ZsV1FUejJzVG1UcEJWSDlpSU1mZlFtRFZMcy96Ylo5cFVHZlQwdEUr?= =?utf-8?B?UVhCZ0NhQ1ljMGhnNU8zSWllVnNvRlpGMXB1MGFpS0crcmtkOEYxZ3M0Wm45?= =?utf-8?B?Ny9MYTZnM2lqZ2s0Qk12VWszeWNLcVNZcytGN3ExZjdxYlY5NHViU1pSWjBB?= =?utf-8?B?N2YxYlVRT0VjbEZaam1HM0hmNmY5RUVnaW1TR3Q3TGQwQ3R6QzBQa3A2ejNy?= =?utf-8?B?NHRnZ0U5aFVISjNsaEsvYXhjRlo3VVFnaDRWcHI5eHNBaTNlUlVrVUxXWEwr?= =?utf-8?B?NjVpR2VUY05PbU1MSTd3QUlWRTc5ZUhaVDk2NWZ3Lyt5WjhpelJhbURlc3d2?= =?utf-8?B?eUlJK3lsY2p3RWRNbEQ1QkhEREx5VGhDZEt3OFRReGxIQlc3S0tLRmhvZ3Rv?= =?utf-8?B?OTVTS2VPQU11bDBaUGFIT1psMmF0cGNhaWtUTmhxUXdUWlNuMXMyVkZVSjBC?= =?utf-8?B?OXBHZmVPdmV1Zi95bzNRMUF0VDFMWTNJZUxFRHhla3gzYlRwTUpEZUlvS1Zs?= =?utf-8?B?eldFVmhmeTlaU1NJUG0rY2oydFR2M0p2dS9QejQvTjB4NmdNeUd5ZHh0YTNh?= =?utf-8?B?TmZqRWlYc1hCdXh2SXBsb1pXWkF0enNSaXJOQU1lVElEUEJNSFhERlhJU1N0?= =?utf-8?B?dm80enVMWmJXTTU3NG15WGt2OFhXMGdCT2FwVFhiMFllVVN3TlEyL0NWNlNs?= =?utf-8?B?dk1OL1Uydlg5S3lHT2E0UUtHOEZWR2pydTRibFRINkg2S2xDeDNpMEU4QTND?= =?utf-8?B?L1VRckZPaUhyMkZEMDNHMnNXSS9FQ1I3VXVKTm1uQWVxNEtIVlZMdVdqbzBT?= =?utf-8?B?Z3VseEVEOHNrRi84L3pxZUxkY3dObUk4SkJGTExIVkMxd3NwTFN5dGRWMUN1?= =?utf-8?B?NGV4ZUZCWjd4RGdiWXVmMFMxYTR2OWt4a3VTZUVDVVpvZlprUG96TzVabGNa?= =?utf-8?Q?L0ou9XsZiHSh53p+T7rppQXAm?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 3f8cc1a3-841a-4ae0-e975-08de33cb9aea X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9255.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 Dec 2025 06:57:50.9814 (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: 4mv934fq/RJljJ4Kq6HfNw2hf3KMMv/h0rZOcxjtgiCXNbrc4XpJ2IvAL4ZBbG73nIMKVV6xVRFzsbzxbvZYxg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR04MB8144 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 | 509 ++++++++ drivers/net/wireless/nxp/nxpwifi/util.c | 1581 +++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/util.h | 133 ++ 3 files changed, 2223 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..9daf63fc93b9 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/ie.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NXP Wireless LAN device driver: management element handling- setting and + * deleting element. + * + * Copyright 2011-2024 NXP + */ + +#include "main.h" +#include "cmdevt.h" + +/* + * This function checks if current element index is used by any on other i= nterface. + * Return: true: yes, current element index is used by someone else. + * false: no, current element index is NOT used by other interface. + */ +static bool +nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private *priv, u16 idx) +{ + int i; + struct nxpwifi_adapter *adapter =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; +} + +/* Get unused element index. This index will be used for setting new eleme= nt */ +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; +} + +/* This function prepares element data buffer for command to be sent to FW= */ +static int +nxpwifi_update_autoindex_ies(struct nxpwifi_private *priv, + struct nxpwifi_ie_list *ie_list) +{ + u16 travel_len, index, mask; + s16 input_len, tlv_len; + struct nxpwifi_ie *ie; + u8 *tmp; + + input_len =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; +} + +/* + * Copy individual custom IEs for beacon, probe response and assoc response + * and prepare single structure for element setting. + * This function also updates allocated element indices from driver. + */ +static int +nxpwifi_update_uap_custom_ie(struct nxpwifi_private *priv, + struct nxpwifi_ie *beacon_ie, u16 *beacon_idx, + struct nxpwifi_ie *pr_ie, u16 *probe_idx, + struct nxpwifi_ie *ar_ie, u16 *assoc_idx) +{ + struct nxpwifi_ie_list *ap_custom_ie; + u8 *pos; + u16 len; + int ret; + + ap_custom_ie =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; +} + +/* + * This function checks if the vendor specified element is present in pass= ed buffer + * and copies it to nxpwifi_ie structure. + * Function takes pointer to struct nxpwifi_ie pointer as argument. + * If the vendor specified element is present then memory is allocated for + * nxpwifi_ie pointer and filled in with element. Caller should take care = of freeing + * this memory. + */ +static int nxpwifi_update_vs_ie(const u8 *ies, int ies_len, + struct nxpwifi_ie **ie_ptr, u16 mask, + unsigned int oui, u8 oui_type) +{ + struct element *vs_ie; + struct nxpwifi_ie *ie =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; +} + +/* + * This function parses beacon IEs, probe response IEs, association respon= se IEs + * from cfg80211_ap_settings->beacon and sets these element 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; +} + +/* + * This function parses head and tail IEs, from cfg80211_beacon_data and = sets + * these element 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; + + /* + * Many IEs are generated in FW by parsing bss configuration. + * Let's not add them here; else we may end up duplicating these IEs + */ + while (left_len > sizeof(struct element)) { + hdr =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; +} + +/* + * This function parses different IEs-head & tail IEs, beacon IEs, + * probe response IEs, association response IEs from cfg80211_ap_settings + * function and sets these element to 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); +} + +/* This function removes management element set */ +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..98c7ebfd07ab --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/util.c @@ -0,0 +1,1581 @@ +// 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); + +/* + * Firmware initialization complete callback handler. + * + * This function wakes up the function waiting on the init + * wait queue for the firmware initialization to complete. + */ +void nxpwifi_init_fw_complete(struct nxpwifi_adapter *adapter) +{ + adapter->init_wait_q_woken =3D true; + wake_up_interruptible(&adapter->init_wait_q); +} + +/* + * This function sends init/shutdown command + * to firmware. + */ +int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv, + u32 func_init_shutdown) +{ + u16 cmd; + + if (func_init_shutdown =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); + +/* + * IOCTL request handler to set/get debug information. + * + * This function collates/sets the information from/to different driver + * structures. + */ +int nxpwifi_get_debug_info(struct nxpwifi_private *priv, + struct nxpwifi_debug_info *info) +{ + struct nxpwifi_adapter *adapter =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; +} + +/* This function sends deauth packet to the kernel. */ +void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv, + u16 reason_code, u8 *sa) +{ + u8 frame_buf[100]; + struct ieee80211_mgmt *mgmt =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); + } +} + +/* + * This function processes the received management packet and send it + * to the kernel. + */ +int +nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv, + struct sk_buff *skb) +{ + struct nxpwifi_adapter *adapter =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; +} + +/* + * This function processes the received packet before sending it to the + * kernel. + * + * It extracts the SKB from the received buffer and sends it to kernel. + * In case the received buffer does not contain the data in SKB format, + * the function creates a blank SKB, fills it with the data from the + * received buffer and then sends this new SKB to the kernel. + */ +int nxpwifi_recv_packet(struct nxpwifi_private *priv, struct sk_buff *skb) +{ + struct nxpwifi_sta_node *src_node; + struct ethhdr *p_ethhdr; + + if (!skb) + return -ENOMEM; + + priv->stats.rx_bytes +=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 handler. + * + * This function is called when a pending IOCTL is completed. + * + * If work queue support is enabled, the function wakes up the + * corresponding waiting function. Otherwise, it processes the + * IOCTL response and frees the response buffer. + */ +int nxpwifi_complete_cmd(struct nxpwifi_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + WARN_ON(!cmd_node->wait_q_enabled); + nxpwifi_dbg(adapter, CMD, "cmd completed: status=3D%d\n", + adapter->cmd_wait_q.status); + + *cmd_node->condition =3D true; + wake_up_interruptible(&adapter->cmd_wait_q.wait); + + return 0; +} + +/* + * This function will return the pointer to station entry in station list + * table which matches specified mac address. + * This function should be called after acquiring RA list spinlock. + * NULL is returned if station entry is not found in associated STA list. + * It must be called under rcu_read_lock(). + */ +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; +} + +/* + * This function will add a sta_node entry to associated station list + * table with the given mac address. + * If entry exist already, existing entry is returned. + * If received mac address is NULL, NULL is returned. + */ +struct nxpwifi_sta_node * +nxpwifi_add_sta_entry(struct nxpwifi_private *priv, const u8 *mac) +{ + struct nxpwifi_sta_node *node; + + if (!mac) + return NULL; + + spin_lock_bh(&priv->sta_list_spinlock); + node =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; +} + +/* + * This function will search for HT element in association request IEs + * and set station HT parameters accordingly. + */ +void +nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies, + int ies_len, struct nxpwifi_sta_node *node) +{ + struct element *ht_cap_ie; + const struct ieee80211_ht_cap *ht_cap; + + if (!ies) + return; + + ht_cap_ie =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; + } +} + +/* + * This function will delete a station entry from station list. + * It is called only from cfg80211_ops.del_station, which is + * protected by cfg80211's internal mutex. + * Therefore, no additional spinlock is needed here. + */ + +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; + } + } +} + +/* This function will delete all stations from associated station list. */ +void nxpwifi_del_all_sta_list(struct nxpwifi_private *priv) +{ + struct nxpwifi_sta_node *node, *tmp; + + spin_lock_bh(&priv->sta_list_spinlock); + + list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { + list_del_rcu(&node->list); + kfree_rcu(node, rcu); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_bh(&priv->sta_list_spinlock); +} + +/* This function adds histogram data to histogram array*/ +void nxpwifi_hist_data_add(struct nxpwifi_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct nxpwifi_histogram_data *phist_data =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..1a921b920406 --- /dev/null +++ b/drivers/net/wireless/nxp/nxpwifi/util.h @@ -0,0 +1,133 @@ +/* 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); + +/* + * Current firmware doesn't support AP and STA running on different + * channels simultaneously in normal mode. + * FW crash would occur in such case. + * This function is used to check if check_chan can be set to FW or not. + * + * Return: + * %true if check_chan can be set to FW without issues. + * %false there is already other channel is set to FW, setting of + * check_chan is not allowable. + */ +bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv, + struct ieee80211_channel *check_chan); + +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